Compare commits
1631 Commits
Author | SHA1 | Date | |
---|---|---|---|
bfe7e3afed | |||
75b1eaed0c | |||
7fb4c8358b | |||
5b3431b268 | |||
9b9d439fce | |||
62d1bc87ae | |||
51ed39804f | |||
d12110b47b | |||
accf0cd40e | |||
a9302468e7 | |||
3b9d822d72 | |||
f3a7de6b76 | |||
a807b231a7 | |||
d59a06344a | |||
e884341795 | |||
3a068970f9 | |||
1043d29dd5 | |||
6505a62801 | |||
7e016c44c1 | |||
e1b37f3cd3 | |||
0c24827a52 | |||
e6eee82fb9 | |||
c5cf78205a | |||
37b4f606c8 | |||
5824047ffa | |||
3dccc38f42 | |||
f613b03efd | |||
3c87e5d31e | |||
e26827b849 | |||
8eb10e991f | |||
f6f32914de | |||
1840505a0f | |||
29e532c096 | |||
0f0cbd4c1f | |||
0ece0b5b27 | |||
6a4fb6a271 | |||
4593538fc8 | |||
03b66ba7a5 | |||
954d4d701a | |||
ce2d58bb7d | |||
7af116fb00 | |||
0357921284 | |||
86974b046e | |||
a04a6f1e0d | |||
37420405c7 | |||
acf1946dac | |||
a52574b026 | |||
5d14724e66 | |||
d582bf7f1f | |||
c81cb59b4b | |||
ff03dd22fe | |||
aae892dfd1 | |||
f24211e8b5 | |||
e61d8e4dc8 | |||
403279e926 | |||
70983c7269 | |||
42f9eccb17 | |||
3c937532f2 | |||
863027cbe8 | |||
6b8fe283f0 | |||
7983977156 | |||
21cb459899 | |||
1b3ff96ffd | |||
42ceaa0015 | |||
90b330d4ba | |||
3be18b58bb | |||
22aaf56855 | |||
7537ea1ef5 | |||
2078133493 | |||
f60562c5a2 | |||
18cfe219fe | |||
129e959a3b | |||
e1b6020b76 | |||
d5c6a1b4d1 | |||
5b8d025440 | |||
6b6d6a01dc | |||
c5aff7cc1e | |||
b16829b0f9 | |||
f27716656c | |||
30cf73a22f | |||
884ac73078 | |||
718abaae0e | |||
62a402d05a | |||
ce5bb29c44 | |||
d535f73a58 | |||
d38696f411 | |||
d0b6f0124b | |||
b266068644 | |||
e0635955df | |||
5ec4cbf1cb | |||
e0380960fd | |||
64ba5e4ed1 | |||
733619ca74 | |||
9f9c829bc5 | |||
236764adf6 | |||
a80b425551 | |||
d7032d975c | |||
96decbac27 | |||
0fb6a2836b | |||
fe36471b8d | |||
6b28af6cc5 | |||
6306fb564b | |||
cdccb25fe3 | |||
b6452215c1 | |||
f1028fa66d | |||
9d2f0e4dc8 | |||
f997529cd4 | |||
495103f72e | |||
d80dee2a54 | |||
788fa40c2a | |||
ae75585b52 | |||
ff07714e8c | |||
f54fc16718 | |||
7298f9c273 | |||
672f5cf160 | |||
92cda68480 | |||
f41426f394 | |||
8dc3267925 | |||
709736d3f9 | |||
12f0d51c0c | |||
2e9403a324 | |||
ba17efa381 | |||
b8e0c8ebc6 | |||
1b05303250 | |||
51095c5a27 | |||
a02f67ed0e | |||
3a7961834a | |||
95842ee25f | |||
371c839573 | |||
1b8b044b52 | |||
a988111507 | |||
c56db0408b | |||
2321d9c065 | |||
ed085630db | |||
74d5c7c1a4 | |||
813ccc1381 | |||
b65f4c9536 | |||
012d8bb468 | |||
d7a137ad13 | |||
073aaf9b3b | |||
871d647c93 | |||
0ce3049579 | |||
de20258aa7 | |||
41c5e523b2 | |||
197be9cfd0 | |||
5b50b806ec | |||
62c59820cf | |||
0cf1fc72a3 | |||
5ce7874280 | |||
ba2b5c3a65 | |||
0a9081cb87 | |||
eea1e3b800 | |||
70364884a9 | |||
ea7f03770c | |||
bc1a7d2890 | |||
732bc53692 | |||
4055e34320 | |||
81b1406290 | |||
538092b727 | |||
4df4b43900 | |||
3e3b92d4c1 | |||
c7dc115365 | |||
e42050cc8a | |||
df17f5e899 | |||
a0045ece07 | |||
4c013e59f0 | |||
09d607f39c | |||
9418c62d95 | |||
ef50e5595e | |||
02bf086c09 | |||
77932061bc | |||
44bf32e729 | |||
0c986ba4d0 | |||
a28193430c | |||
0bec0046bb | |||
a96519cbdc | |||
458c2f38bc | |||
dc5402349e | |||
ae289c923c | |||
e0ef86340f | |||
1ca2c59f2e | |||
3ec92acfe7 | |||
656bfd36f6 | |||
562ae676a5 | |||
72292f4e03 | |||
536da704fc | |||
a0e03c41c0 | |||
a1053a4c5a | |||
9f441a9678 | |||
7870cf28e5 | |||
9939367db7 | |||
1210c3256d | |||
2e8d04aad0 | |||
3a0e4546c2 | |||
cd2419137d | |||
34ac8b3ec3 | |||
2837236d81 | |||
bc8336a4b1 | |||
397e7f0363 | |||
c5bbe42b57 | |||
c9105e525e | |||
1bed7754e0 | |||
5978882378 | |||
8ba51c7900 | |||
6d160a7b7e | |||
32409a361b | |||
2ceefea5f3 | |||
485f156e57 | |||
f794e49bb6 | |||
46f448dfba | |||
710156b9f1 | |||
6dcf34acdc | |||
d2f674e08f | |||
ec8cb056bf | |||
1893871a99 | |||
8837f06e4e | |||
5346dfc782 | |||
ddca838e46 | |||
45b0367d14 | |||
dce86edff0 | |||
84e91ec3b5 | |||
05b6969ee5 | |||
af949f5cdd | |||
7707af08e3 | |||
5db4fabcad | |||
cfca82ceb3 | |||
a1130dace0 | |||
95a0bd61a9 | |||
b324778be7 | |||
950f921c09 | |||
9dff1bac83 | |||
c17f6e37e5 | |||
e0e26a37b5 | |||
15a0c03664 | |||
7642e75b28 | |||
4b36d1e3ea | |||
20525bec29 | |||
430369feb8 | |||
5ac3e5c958 | |||
abc2a529fb | |||
c638fd9056 | |||
1dd96898e1 | |||
2cc9b0df06 | |||
3bd4ad7c93 | |||
a8ffdeca2b | |||
59b15c5f08 | |||
59cf8f678f | |||
088be050b2 | |||
5d5fcae501 | |||
970ec8187c | |||
76f66148db | |||
6bdd6ce571 | |||
dd96e1819d | |||
07702d3be7 | |||
b9dfcf6d2f | |||
f3f0652d2b | |||
aae60334d1 | |||
b38af948b9 | |||
06de728aa7 | |||
87384182a1 | |||
f583e617ec | |||
a2716f5cf6 | |||
55f928f845 | |||
2343aad088 | |||
dcaa907fed | |||
adcdf28d64 | |||
f28a7b9d08 | |||
6505b0c065 | |||
88ef02474f | |||
9ec32b2561 | |||
a5dd6b6cd7 | |||
46cc325f7c | |||
0fadb5a2be | |||
8b897ac714 | |||
a80b820e94 | |||
4efbf22089 | |||
5c913149c8 | |||
9265c319a9 | |||
cbe6eff2fa | |||
1f3d18ec12 | |||
89255e34bd | |||
a7b8092296 | |||
1feb630520 | |||
9f202caf93 | |||
651e511ff0 | |||
b46aca52ee | |||
0eae9355e6 | |||
4878f1a438 | |||
f986b4cd56 | |||
d5c6704475 | |||
fda9ca1e34 | |||
01c4ed232e | |||
dc8109658c | |||
8ce8055aeb | |||
a71b743ee3 | |||
c50e9ac418 | |||
7973b01e81 | |||
b977ac6df5 | |||
256fc322a8 | |||
33bf85a387 | |||
1f0fc61b53 | |||
89c945ecc8 | |||
c15962c6c1 | |||
9c2a3231c5 | |||
80840f1fdb | |||
7d02e1f866 | |||
d93cb751b0 | |||
c07fff7503 | |||
7896dd19c1 | |||
f5f2d33f93 | |||
4331eaae2e | |||
d5d4f87236 | |||
5d49433326 | |||
381d7413c8 | |||
e70a5a47ee | |||
6be7eed878 | |||
a6ac832f8f | |||
104863846b | |||
5883c30b84 | |||
cebaae76b4 | |||
d886d32bd8 | |||
8440c2819b | |||
8114d8778f | |||
f36c3a3f6c | |||
46c6cc2d2b | |||
ada5e88eb9 | |||
b724607e31 | |||
c3ea303a37 | |||
e89a10945c | |||
8ddc89fa01 | |||
a47bf72b07 | |||
34460dd77a | |||
c125c96e88 | |||
d2fd0fd8e8 | |||
74a875bdce | |||
edaa66f622 | |||
c392db7e0d | |||
6ac073e779 | |||
58d9ceda4b | |||
3781c64d41 | |||
4166d9ab7b | |||
435273e08a | |||
35a62d9787 | |||
75683039c5 | |||
1a609612f2 | |||
121a7a9e23 | |||
e593faf245 | |||
ec5bb944b2 | |||
deed49574a | |||
7cc39cd357 | |||
a714d5966e | |||
2b0252d4ae | |||
c78db5459e | |||
a27564ed70 | |||
df8df41621 | |||
d5a0d4b452 | |||
357b6ee991 | |||
4d2b5c2f42 | |||
f705cf09bc | |||
29f7ea752f | |||
9b7c8ea22d | |||
199a7df807 | |||
5186ad95d3 | |||
0546345182 | |||
90feaaf2df | |||
6d27ef5eea | |||
e4e8a51e5c | |||
3ddf413332 | |||
6eb36fb053 | |||
58239ff98f | |||
6e841a3b7e | |||
085e067fc1 | |||
c45fa016c0 | |||
16477a8f6c | |||
04e4900415 | |||
322f317a5b | |||
ea5020e188 | |||
cefa1662ef | |||
2a949fcb86 | |||
445f9e5f71 | |||
5a25ce8c1b | |||
670cf8ee07 | |||
6c5f6e8900 | |||
4ed4fb2314 | |||
f16989bea9 | |||
55d4064335 | |||
3b38a4c690 | |||
ada595663d | |||
b2de01b076 | |||
f3acf35aea | |||
3da1d6a464 | |||
b9df10c8bd | |||
ec1f73c827 | |||
cdc9f93f71 | |||
bcebb1920f | |||
81848e05f1 | |||
5ae69c079a | |||
0199d8a74f | |||
f49ad2ee03 | |||
7ed993b54e | |||
9934537e19 | |||
00d42d296e | |||
ad74fedfba | |||
730f714e97 | |||
3bec4a80b3 | |||
a84e4b0e07 | |||
198139feb4 | |||
de11017552 | |||
6fb837c529 | |||
c0c3892064 | |||
1a35fec134 | |||
f1247d2224 | |||
7992b7eb89 | |||
cd893e18d2 | |||
a5c8b166fe | |||
72a9b98ef0 | |||
cf022524d1 | |||
ebb0596c1a | |||
3de681d2d6 | |||
f7b0ba88da | |||
c95c81d42f | |||
9407596b12 | |||
c294c2d1df | |||
bd36f8e220 | |||
3a168ba6dd | |||
1919069b12 | |||
ba677a8cb7 | |||
e62e1d9701 | |||
b3330cb0da | |||
7d128c79a3 | |||
3cff23dae2 | |||
c3f0139f76 | |||
563fe8d515 | |||
38e20eb148 | |||
36571c5e22 | |||
c8eca4fb85 | |||
0e207aba6c | |||
45d1319891 | |||
39b7ac90d4 | |||
ef87bdf18a | |||
433a802c6e | |||
6a18079953 | |||
b937d33436 | |||
f7244fbf68 | |||
4e80d1fc79 | |||
b2f48eaeb8 | |||
160dd09fc2 | |||
80eea05deb | |||
5e89ba7079 | |||
ff7878217d | |||
807da6a035 | |||
4e2a958896 | |||
3ee0ec7cd0 | |||
22a2b7ac46 | |||
aa7c910e26 | |||
24a4bd3a1c | |||
fff52cb247 | |||
219c5b323d | |||
14278a9e35 | |||
dd013209e8 | |||
96d5438633 | |||
391ef64c22 | |||
d45a62b3a6 | |||
976e550aa7 | |||
668b19d119 | |||
5c48f0b458 | |||
689fe1e2c7 | |||
7d440402ad | |||
6daa457838 | |||
49d317b19a | |||
1b80ae0fca | |||
a113ecca8b | |||
61144f7a21 | |||
574af2c795 | |||
a4870d4834 | |||
78bbcac0ea | |||
ca9b2525c8 | |||
bb1c387a98 | |||
38ce37b6fd | |||
7fdc81236e | |||
fca40c1c6b | |||
d5d5d66e03 | |||
0481ae187a | |||
9de6927c3f | |||
30b01ef053 | |||
03b75bf2a9 | |||
f3f628410d | |||
9901ecda49 | |||
39becf607e | |||
f5955a4738 | |||
e018b30875 | |||
2dbd775cf3 | |||
a5051327db | |||
8140f5136d | |||
f04703f09b | |||
2faf8332ee | |||
e0e3d4d8fe | |||
ba81ad1ac3 | |||
eefb259ddf | |||
9b8add1961 | |||
e186523878 | |||
021e6c02d7 | |||
cba3d68063 | |||
7e2d78bab5 | |||
4b12c85d91 | |||
0ebf04a021 | |||
d55f47077a | |||
11df4845b7 | |||
7f438425aa | |||
c470f05abf | |||
141e94369e | |||
257970c27d | |||
67ac3da8a0 | |||
c1e5179b5f | |||
7a651bdc53 | |||
f33f596584 | |||
c8d8046412 | |||
4d334b645d | |||
58d3779efb | |||
3691f3a296 | |||
463b4fbe0c | |||
9f1c79a5ec | |||
70573b6f31 | |||
6ea1234a3b | |||
434f639b0c | |||
bf04becc9e | |||
b2082bfde7 | |||
78984eea3a | |||
8422e3ac01 | |||
cfce54fe46 | |||
a7c9b2f172 | |||
eb31a951a1 | |||
ccfe605920 | |||
878614ff68 | |||
25e23e50ca | |||
64c51a70a3 | |||
e4296c48c8 | |||
dd3848d7b1 | |||
56ac1a0813 | |||
cbe5af235c | |||
f932ffcc5b | |||
0a50c5bf40 | |||
e416e5d301 | |||
3227859992 | |||
01139c3b50 | |||
ee3e65d759 | |||
c85867395d | |||
bf2ce54076 | |||
07de285299 | |||
8c194cd245 | |||
8cbec3d5a0 | |||
cdf05e74c8 | |||
b98b4f1027 | |||
fc11dfd6b4 | |||
483a5b6cae | |||
df1b7f1656 | |||
a566d1c5de | |||
11c8237d8b | |||
6dc19c85a8 | |||
6e07c11f65 | |||
8da6667816 | |||
81fedbf03c | |||
0516055b31 | |||
e3f8d99087 | |||
22aebc2215 | |||
3653e9d5e3 | |||
40f16b6d62 | |||
a61daa40dc | |||
ee67b5f1ad | |||
aa3633d2d7 | |||
7b805a38b9 | |||
c8d8dda79a | |||
c440f33122 | |||
b0c866bfaa | |||
dd578354c4 | |||
fdac978c84 | |||
3b81ada1c3 | |||
4ee29b388d | |||
52dc9068e5 | |||
4f1ea712da | |||
849b92665e | |||
d193ed9eeb | |||
ee003cd9ee | |||
127b094c41 | |||
b2285be5bc | |||
6ede02bb84 | |||
821c424b66 | |||
5450e0edf3 | |||
e05ccc0e55 | |||
4f3786c2e1 | |||
107977f3d1 | |||
813663485d | |||
64585d8f78 | |||
756d933d1a | |||
83deccf266 | |||
aaef448959 | |||
7e96077ed7 | |||
c44f931059 | |||
15084c8d6d | |||
822c5a530e | |||
6eb641ae56 | |||
9eb14e6b9b | |||
7cc42269a9 | |||
c45442760c | |||
83f0611200 | |||
d95625ed88 | |||
5622bcc563 | |||
deb9c98630 | |||
2c1e887c8d | |||
4a13d72997 | |||
2d5820e910 | |||
6cb857b895 | |||
590875d022 | |||
8f30237765 | |||
cb0339b492 | |||
e427174ff0 | |||
fc4c57f0d9 | |||
9ad4ae5340 | |||
0f48e0fc06 | |||
37ad1b40d8 | |||
514e7ae6a0 | |||
ab2f3bb5bc | |||
34230bfcf4 | |||
c390e211ee | |||
b0d69db878 | |||
b373beee21 | |||
8c99edd3c5 | |||
8036272e87 | |||
6f50809457 | |||
bb386a1162 | |||
80054e4db2 | |||
1630a23fb0 | |||
4e75419e08 | |||
089018015a | |||
feb6f285ce | |||
cb12c51afc | |||
c5c426ecbc | |||
74f7039abf | |||
57b905be24 | |||
34794cc4af | |||
38f59fdf39 | |||
527c1113f1 | |||
04dbe28793 | |||
968366c2ae | |||
7f5dea28bb | |||
d4979974b4 | |||
9539230915 | |||
25cfa26e7b | |||
0ff8891c66 | |||
58d2c15ffa | |||
82699cc297 | |||
08d008a5aa | |||
9e17ff884f | |||
1c567232e3 | |||
cba2608c1c | |||
e5c42f68c2 | |||
02b4468bcd | |||
746f6945da | |||
5cab4d8864 | |||
5cedfcbfaf | |||
c06abd9b6b | |||
e08d97825d | |||
d61323aa6d | |||
5ae044db93 | |||
c6e4fdac7e | |||
9669377b06 | |||
c8c3fe1023 | |||
85bddbed86 | |||
318a2a429c | |||
07359865c6 | |||
2fd7338cd3 | |||
ce3cfa212c | |||
b29c99656e | |||
45b0762096 | |||
aceafb28f6 | |||
928939cbe3 | |||
2462b8e524 | |||
263a72879d | |||
70620d5137 | |||
79d5beff8d | |||
fa3caf091a | |||
93707560cb | |||
5c6f325071 | |||
b9a17f5999 | |||
ab23f542c6 | |||
fa98bf1ee7 | |||
3cc987a5b4 | |||
d1a1b8b6e8 | |||
0d08e082be | |||
386fb28a35 | |||
79b79b752c | |||
a116778402 | |||
4c1c26a0e6 | |||
9e1653ebb4 | |||
99dd9874df | |||
24193163e1 | |||
94d164c0f2 | |||
efef2e95ab | |||
5efa725e92 | |||
42c00213ce | |||
b7d1b9ac61 | |||
52d06ed814 | |||
46a1c855a9 | |||
31dc82b1a6 | |||
4e27e132a0 | |||
a5b0b4800a | |||
e6244c937a | |||
3cbf8d4993 | |||
f3279a0697 | |||
8cac61f0be | |||
236c196e68 | |||
6c09efd630 | |||
cc92e1b0b6 | |||
c90f2c3ac6 | |||
6bdd27a08e | |||
80ff0b1418 | |||
a8943ce1ee | |||
71d35dc70b | |||
9daf2a2996 | |||
f5d6aea942 | |||
5850447fd0 | |||
e12a7a4415 | |||
81d84b8d02 | |||
bae0a0530b | |||
cbf4159c7e | |||
4a1d082614 | |||
fd8b4c5368 | |||
5872c34315 | |||
b60fe08d44 | |||
6a51eda361 | |||
96e8217b00 | |||
e6cb6cae11 | |||
980f03dd20 | |||
20c281d6f8 | |||
66f9fed2f2 | |||
68b7aa0a4d | |||
f916ce8752 | |||
fdbd8d9d2b | |||
4208c2c72a | |||
5cc91965d0 | |||
c74f852364 | |||
c1b3a3adb4 | |||
7096f02b88 | |||
70a11935a8 | |||
f601135cc0 | |||
884fe0d574 | |||
ffcbf97dcc | |||
f55fc51e9b | |||
df0f9259c0 | |||
bb7a321c6e | |||
2d69d63efe | |||
479843f56b | |||
6e1639551b | |||
2367903ac6 | |||
30607c34a1 | |||
817947c928 | |||
29926a59bb | |||
d92ae530d7 | |||
e0e428ce38 | |||
d1db7a0e23 | |||
c27ebde575 | |||
a99cd16422 | |||
1f6b8f9d2b | |||
a33b804923 | |||
f527958cb3 | |||
b1bdc6f745 | |||
8dacbafc8b | |||
70fa92f22c | |||
3482076a20 | |||
f7239f7f8a | |||
20ba787c00 | |||
669eef92eb | |||
c6d9edb78f | |||
347ae0a9ad | |||
dbd01d35e6 | |||
b24c09665f | |||
fa96ebd382 | |||
52d43f843b | |||
90376749f0 | |||
5cf2a8b5fc | |||
f72ac94c11 | |||
bd3a693e70 | |||
298871e207 | |||
ce958f4ffd | |||
2de150bfc8 | |||
40970a1a87 | |||
8aae652be1 | |||
37a117d2ef | |||
debc1659e1 | |||
23e411c42b | |||
88feebe499 | |||
e8b871ac72 | |||
9f30c6d94b | |||
c4c1e75de8 | |||
c1763cc4b0 | |||
ee096edfff | |||
464b9ebc95 | |||
7705f290ca | |||
7439fd6bcb | |||
6fe626ab9a | |||
a12f892841 | |||
aa3ea79f94 | |||
ef53455b66 | |||
ba46ff6692 | |||
0e916244f0 | |||
1e4ee02cbc | |||
cedc7754d9 | |||
fece9e207b | |||
cca052ccc9 | |||
5be9472912 | |||
f60b09568c | |||
be765f8d88 | |||
8d6aecd944 | |||
aef5349aee | |||
ac993aa31f | |||
fce323b945 | |||
5558f3d2cc | |||
08f8623cb7 | |||
f8a137a26e | |||
3cba359d38 | |||
b8d7aedb2c | |||
55c7b291e1 | |||
472d931b4b | |||
ce892c9e77 | |||
e14b998da3 | |||
2c9452efaf | |||
97a7af855f | |||
5be8545edc | |||
0a04c3a2a7 | |||
dcfc15a0a1 | |||
841c2e9166 | |||
07392d493c | |||
acd50969e0 | |||
074b53eb6b | |||
c4cfec1e94 | |||
b0269e6dfc | |||
43b9d9484d | |||
69bbb29328 | |||
577069cfb4 | |||
fe94c3609e | |||
173aed7fd8 | |||
335bec68fb | |||
bbb7e9f5c7 | |||
086304f7f2 | |||
bd7065eece | |||
15aaff7c1c | |||
e048bce13e | |||
ddfb449b28 | |||
5322155b19 | |||
45ce72593a | |||
b813c867b5 | |||
f7d7d76ee8 | |||
7e5076b068 | |||
dbe0553b4a | |||
9be48f1dc4 | |||
6043444e4e | |||
487e352642 | |||
cc5f82bfac | |||
4d4dfab388 | |||
b83f9be1bd | |||
6ae3b183fd | |||
f12117c532 | |||
70768189ba | |||
30b266622c | |||
25b306b7e1 | |||
9af1b00df5 | |||
51c27e2748 | |||
b9e2c3524c | |||
3f6cc17818 | |||
5909af9878 | |||
64576f4c4c | |||
21dbd28a2f | |||
b57fee1a44 | |||
cabd887866 | |||
863a168cb5 | |||
5ee4fb3522 | |||
bb8ac9b99a | |||
2f10fa8b61 | |||
46a8e18841 | |||
6c53e68a52 | |||
392452d422 | |||
99ed0b6c2c | |||
e6d057fe6d | |||
9ad6eb11a3 | |||
32c2ad2bbd | |||
f2ca9a6b31 | |||
50d40257fe | |||
d90eff64d0 | |||
e6dc34fe10 | |||
745c331311 | |||
1ea2e85415 | |||
4a2df30f92 | |||
922220c11e | |||
56d5035c63 | |||
4abf3a20c6 | |||
5912ef3b45 | |||
9931c9a286 | |||
1da834f650 | |||
fb677a7489 | |||
404796d4b2 | |||
5b9bfe8891 | |||
779bc2c63d | |||
cae1ba7cd8 | |||
8ce78dcc54 | |||
afe088dba1 | |||
e899e31745 | |||
dabb84f62a | |||
9b8096c699 | |||
ac5b74301e | |||
609b30110b | |||
35d200356f | |||
0cd1d7aa7e | |||
ade7526f5f | |||
a7b1700d42 | |||
c0b8c53e69 | |||
e86fbc697f | |||
e5622ce824 | |||
99d9868116 | |||
107fa6b4f7 | |||
9062d28704 | |||
e68dcea6bc | |||
787c5a515f | |||
ce2df4b36f | |||
fcb9733df0 | |||
245928a064 | |||
589d160515 | |||
380e76a2e3 | |||
9ac6114b63 | |||
d558ff305a | |||
89aaac3a70 | |||
456999eee6 | |||
16e3b786fc | |||
126bbd67f7 | |||
38e1d44dbb | |||
9fb80a2687 | |||
d5d224d89a | |||
4708ce4226 | |||
a0580946dc | |||
e68fd7c1e3 | |||
369419870e | |||
0938f80b41 | |||
9f462dcc27 | |||
96008d3bb2 | |||
c05f744ec2 | |||
3df31579a1 | |||
f1d3481a29 | |||
cf3aad9c41 | |||
7b1e68bfa8 | |||
82256a2fe8 | |||
016b3448e8 | |||
2dde26e485 | |||
2999afe781 | |||
bfc5a6488b | |||
4af22d663a | |||
ba8322aa5c | |||
e4bd82190a | |||
695734636f | |||
bdac2a5f4d | |||
f8ea6212c7 | |||
5e9b26dbef | |||
67c99369ab | |||
d35c2db41e | |||
dff5fea976 | |||
ab6c7244fc | |||
353b51f11e | |||
76050880f1 | |||
396a7994f9 | |||
e29259cd55 | |||
79a728d7d2 | |||
98444bd865 | |||
3f75e1cb1d | |||
4f327120ac | |||
cc8456ec6b | |||
f8ad1a052f | |||
372ce588cd | |||
12632ec404 | |||
e777c26a14 | |||
1d38248194 | |||
d52868c6c5 | |||
29c1639b1d | |||
653a953d2f | |||
9f5537d936 | |||
21a7af535c | |||
a241d3f187 | |||
8f045af867 | |||
6d4efc0443 | |||
e2e6d00064 | |||
e682c3e9b8 | |||
fe347262e9 | |||
c993209b76 | |||
2cf4d5f8ec | |||
c6ab9718ae | |||
3e3ef03855 | |||
c2f37716e5 | |||
e9bfd43ff7 | |||
2958ffa73f | |||
3d11d044d2 | |||
7eb0b1c69a | |||
8d39a9ea61 | |||
cb796dbdfb | |||
4254c20eda | |||
094b57bb23 | |||
9ca4f19aea | |||
a29d88c313 | |||
a1ed8154f7 | |||
6c45a990ef | |||
8dfa3393dc | |||
d12323396f | |||
48d34741f3 | |||
76203018e9 | |||
7956e6f04e | |||
668342b470 | |||
0a0ce74105 | |||
f6a3fa55b1 | |||
718a9a3559 | |||
5485c5aaed | |||
afcdf4b9cf | |||
a3f5ea3598 | |||
fef60a9da0 | |||
576867605d | |||
d1626d20bd | |||
c890aa18f7 | |||
6c7d040439 | |||
d03ae34b61 | |||
b9547adce7 | |||
7f32c6464d | |||
209a1650e4 | |||
b142e2a7a7 | |||
6010ce0dc5 | |||
a7a331a26e | |||
fda2c116be | |||
d2f3dbaa29 | |||
e9d4793b1e | |||
13c7efa058 | |||
1598d65824 | |||
028e086960 | |||
04b39294ba | |||
3a446c410c | |||
35edea2141 | |||
ce1a4857cc | |||
dfdf739282 | |||
7086b1e65c | |||
f2d445d27b | |||
29d362a6e0 | |||
c8adc4e356 | |||
8467daa0ec | |||
13e14b712e | |||
27dfe30fe6 | |||
d89a541a8d | |||
42350e689d | |||
b7d5fee995 | |||
fd099166c6 | |||
b4be28b9bb | |||
19a8d51bf3 | |||
5d1aac3c53 | |||
87b0d3fe11 | |||
98891a036d | |||
b142a6da5b | |||
1c0139c5f4 | |||
1b7ae4321d | |||
7f991ed897 | |||
5ed7eb9d53 | |||
c00f96c7ca | |||
81333515e0 | |||
be3c9abcfe | |||
177f17450a | |||
150a3adaaf | |||
76146e0f17 | |||
4500e345b3 | |||
e885f3c26a | |||
e6e92f2b0e | |||
03b48554b1 | |||
f229574758 | |||
7b446dd30f | |||
a5be974f1a | |||
952615eadb | |||
93894f62ff | |||
2051de51b5 | |||
2ba3de79d8 | |||
d35c985d2e | |||
b18466da84 | |||
90714a93f3 | |||
990cfb7b33 | |||
aacf7938ae | |||
2e0f818905 | |||
63b6c6685c | |||
610b971117 | |||
318f657c31 | |||
482d3a1d76 | |||
b516dd970f | |||
d9463620e6 | |||
385c452ddf | |||
a89df42561 | |||
08cd5d52fa | |||
d0e668e1d8 | |||
eeb95f89e1 | |||
e4e0c27e1c | |||
b638a6ae95 | |||
74b98af8e5 | |||
6a474a0125 | |||
224304813a | |||
4caf06bc99 | |||
15593b5c09 | |||
e7e56eb1e3 | |||
43bf601f12 | |||
d5109f024b | |||
7f441f5b87 | |||
0aac85dda1 | |||
4777a45722 | |||
29e6e170cc | |||
01db80d19a | |||
8b28ed67ae | |||
f35a30d371 | |||
1a8de111a3 | |||
9bb62e1bba | |||
b2789fef0f | |||
46fe7e77b3 | |||
99e1c13e80 | |||
a6e65dfc7a | |||
1471eddc7e | |||
89fd84d916 | |||
0e17eee096 | |||
81f1305270 | |||
2ec4310104 | |||
24e4db676a | |||
6e21a9829d | |||
4e99e10cbd | |||
500a7eceab | |||
1dad35ca76 | |||
8b3093e758 | |||
a406aeb3ea | |||
970e4b020e | |||
a4f4f2891c | |||
a6b13487f0 | |||
56ae4e5b6a | |||
54c26b05fa | |||
bd376d5217 | |||
2592c69021 | |||
4a2b25e841 | |||
ba4af1a890 | |||
9b17cde019 | |||
7a8c963722 | |||
82d1dbf173 | |||
aefb8b353e | |||
5b75869c0b | |||
1288b92615 | |||
7eecf454e8 | |||
ccf7d1e0c4 | |||
e4b5cd23e6 | |||
819296cc9c | |||
67c7d80f41 | |||
919f8c54d7 | |||
cbaf71f581 | |||
f118c602e6 | |||
ff2f40f5e1 | |||
9758c9b2a2 | |||
09e4e100a2 | |||
bdd5c180d3 | |||
749b085063 | |||
c5a796ad8e | |||
12d8409e65 | |||
8db89b4cfb | |||
31dc92dafe | |||
01b90809e8 | |||
0a7383a4e1 | |||
5e86a34925 | |||
ece206b81c | |||
88341b923a | |||
5d27633821 | |||
a9d4370ad4 | |||
8a0318c779 | |||
8e754d9a56 | |||
0e209dcb02 | |||
6f2d6425a2 | |||
b46c4a81e0 | |||
b6aa33f3a6 | |||
ab68d9198d | |||
32eebd3ca7 | |||
3f2e5633b4 | |||
a99b9e80b8 | |||
42ed7e4626 | |||
ae910eea62 | |||
d35920f356 | |||
c29e7477f0 | |||
86bbc7939c | |||
dab091a7b7 | |||
513d93cfc7 | |||
d7478c14fd | |||
297c593edd | |||
6439ef11aa | |||
d6479e133d | |||
50bbf2aacc | |||
41032aaac2 | |||
4e08f28246 | |||
46c57e120f | |||
6b52ee61ae | |||
61fbc5a791 | |||
22365205f9 | |||
c6515c1dad | |||
8201d1df02 | |||
ddf168c536 | |||
c9f81f56e9 | |||
570264e1e9 | |||
325e58d98c | |||
23a2960aa3 | |||
888a87463e | |||
2ebaf46095 | |||
3a95a3b7c1 | |||
c4edffb388 | |||
5eec7cc788 | |||
fb4cf0b75d | |||
5584884fe6 | |||
3bb1068ef0 | |||
b1b85313ae | |||
92dfd659f1 | |||
d87f743a2b | |||
aec3e7b0fc | |||
243600b75b | |||
3d89d126d0 | |||
54281e53a1 | |||
6befd2be81 | |||
1f0ca9ed92 | |||
f3db9c3920 | |||
83ceb26151 | |||
32cdfb871c | |||
801e7da5ee | |||
2cf1ab7ec5 | |||
d049c1afaf | |||
9f45389bc1 | |||
d875481969 | |||
17a1e1245c | |||
06d28c3eec | |||
684982c25c | |||
421522a61a | |||
f3b29d67f4 | |||
2ee5c6b2a1 | |||
5083772c6f | |||
fd51e5df47 | |||
3405fd91c6 | |||
194822f11e | |||
98963d4cdf | |||
804ef36b20 | |||
3c6ff8fddf | |||
511893d535 | |||
e05fe77bfe | |||
2b7b9a2abb | |||
6db3f68ae1 | |||
3773f2e7a5 | |||
1f8bffc15a | |||
afaef4e83b | |||
b746f723cb | |||
cfaf44f57b | |||
eed541201e | |||
a8bcb85f7b | |||
981e9cf290 | |||
3ac398ac49 | |||
2d0728395f | |||
722194405a | |||
0868a5e534 | |||
a86b9c82ce | |||
f8642548c8 | |||
27873f7ed4 | |||
b3eda6bcbb | |||
1c7799e292 | |||
aecd158d3c | |||
87d35f0d16 | |||
c089f9b59f | |||
82d7f9f5a4 | |||
8bc529be3d | |||
924c1634d3 | |||
03d077e915 | |||
303628bb05 | |||
3b92ec8e82 | |||
545944cb0d | |||
c90a88b6b6 | |||
89e45a61b3 | |||
2aff7bac4a | |||
124097d3a5 | |||
c520faed6d | |||
5bdb3703ed | |||
2901039a48 | |||
dfa220ef02 | |||
f26be00571 | |||
83654a193e | |||
b2a5d8daf4 | |||
19ee736e1d | |||
fda3f1352e | |||
d194b02e28 | |||
aaba99dc10 | |||
93a2e0f777 | |||
71f3c6b461 | |||
aabcca5059 | |||
7e67fd8c79 | |||
fecf1ffcb9 | |||
fafc9cf9ca | |||
3111e6a721 | |||
e436f471a0 | |||
28f84902f6 | |||
ebee50eedc | |||
e0ef09dfe1 | |||
d7992ab29d | |||
60f19f305e | |||
d99976f5d7 | |||
db158a5735 | |||
ea3be17220 | |||
f26049009e | |||
787234a53a | |||
fc3a64056c | |||
6ebc9abb80 | |||
80e9eed35a | |||
23b3990f99 | |||
41276403df | |||
e3e9e39498 | |||
f315025a8d | |||
8e43d97133 | |||
c97a47dc62 | |||
7ccafdc993 | |||
0dca9cb6b8 | |||
be3d780720 | |||
0f59a1dde1 | |||
7c6bb80cee | |||
62841c5b23 | |||
aad4f8d1f7 | |||
11c44c676c | |||
370c3aa598 | |||
1cdadafdf8 | |||
dd6f670dec | |||
9ff364b0d3 | |||
58a5331f7b | |||
ed261f0af9 | |||
c01b475cbf | |||
3d4feeec8d | |||
a1800ec23f | |||
8a4f1c66f8 | |||
b187231b0e | |||
60b38de69f | |||
600c49f7f0 | |||
e7380e70a3 | |||
3df8594f19 | |||
ee4a829293 | |||
1862f3c124 | |||
777ab3416f | |||
ecf5ab75e7 | |||
4f6d964217 | |||
06019f01e3 | |||
ddde885084 | |||
be8c6f218c | |||
9eb35ea7c8 | |||
d2fdbec41d | |||
2dd372600c | |||
eda6cf11ef | |||
68facd6b93 | |||
87002fb8f8 | |||
6a50fa35ec | |||
6541570969 | |||
4b0ceea894 | |||
8c0816c166 | |||
be769d07f1 | |||
3a9d58e31c | |||
7024acac06 | |||
6131346e2f | |||
eed73c9078 | |||
795d6f35ee | |||
ebd46705d5 | |||
72d2ca234e | |||
2246c3359b | |||
242fb156a2 | |||
208ed73e59 | |||
4441b37338 | |||
941d75824a | |||
ec9ddc4f22 | |||
98b6f90172 | |||
7e280de361 | |||
7bd8bd13fe | |||
09e85e948c | |||
9ec1c00887 | |||
c6bcb6228b | |||
a24d589845 | |||
ebbcc9f6da | |||
23fc453fae | |||
aad6f74db6 | |||
07dcefabcb | |||
40c68595d7 | |||
fe9a4fece4 | |||
098327f128 | |||
0873b8d304 | |||
c9eb584ac8 | |||
10493bd44a | |||
9e35230467 | |||
81e326571b | |||
1b2a7de4e2 | |||
11216d200c | |||
777be6a48d | |||
5765a1fdf1 | |||
29dcb9d274 | |||
8674ac4f68 | |||
684b8f24f3 | |||
1ca2be0039 | |||
8c41ff68f7 | |||
931d6c280a | |||
ee0fb2d0e0 | |||
c496ad1237 | |||
277fa21f5f | |||
1cf949226e | |||
be3fae6511 | |||
5932f36285 | |||
30abb65368 | |||
64a06b5ed6 | |||
69d18f17a5 | |||
4c7d3a103c | |||
10320bdeb4 | |||
4261dcff39 | |||
a091245793 | |||
ca282f9fb3 | |||
7cf2c3be0f | |||
f65d506f26 | |||
333dbca01e | |||
42eb265624 | |||
8a65726e9d | |||
1b0ca47682 | |||
8e3f5c3305 | |||
3c0a6987cd | |||
e37f70b9f7 | |||
03c148ce50 | |||
4817f0312d | |||
b70a82c609 | |||
25d1e0c4e6 | |||
8e3356f11a | |||
1b559c7776 | |||
d5583f0f02 | |||
bedd3c50b6 | |||
43a7af3f44 | |||
9db27c6acc | |||
42c81395b3 | |||
3b13e692d2 | |||
0331f5a1eb | |||
8a7e117f6b | |||
9b984cedac | |||
dd9e30b24a | |||
6a93688b2e | |||
3ab17a97a8 | |||
f21ae66265 | |||
afa1a5e932 | |||
050768c266 | |||
cda2bfc240 | |||
2f167b1512 | |||
ba3ac85356 | |||
ec29cedeb7 | |||
064ae49d2b | |||
247f99ce2f | |||
7b6d269904 | |||
87a0482b8b | |||
e899699918 | |||
bdf464e792 | |||
c410bb4ecb | |||
a720bcc637 | |||
369a8cdc74 | |||
3f4e55be4f | |||
9171f471ab | |||
2186b134a4 | |||
f371ec210c | |||
afcd669d2f | |||
fbf542d205 | |||
13184eb8e9 | |||
ddf1e1ccee | |||
5ac4e73697 | |||
6be59b53f1 | |||
bb54fec907 | |||
0b81b283bf | |||
e2ab2aea32 | |||
c3ceefbafb | |||
e7cf9932a9 | |||
0c9d03f5df | |||
92aa24ae34 | |||
97a74d5c1f | |||
256f8094f5 | |||
1e2f0ab308 | |||
af2cf2734d | |||
ec62d8e973 | |||
3225f514f6 | |||
2d63c86022 | |||
2dcff83be7 | |||
afb9ebcd99 | |||
92d7e44525 | |||
a517f442ea | |||
311758233b | |||
6e086eb808 | |||
7e8644430c | |||
70a8f6743a | |||
69eafd0e11 | |||
6b6a095b91 | |||
d5a2185030 | |||
26f31e9288 | |||
e654e66839 | |||
bb4861cf0d | |||
01505910f4 | |||
ab766a0598 | |||
51c664a678 | |||
3c4b45c9e7 | |||
93507a263b | |||
1175461030 | |||
2f5e55bea0 | |||
c375e7b4df | |||
5d188c69ed | |||
6bff7751d0 | |||
1a5986abe0 | |||
cebac3c10e | |||
a5da3db966 | |||
cd30f75173 | |||
94df4ceb36 | |||
a14476c5fb | |||
d82bb29919 | |||
33af0c6a7c | |||
68f3f98bc3 | |||
f873cd5b1a | |||
fa8df286d9 | |||
76aa0c434a | |||
78dc0cfdf3 | |||
9654728bfb | |||
123d1864f4 | |||
96a91e988d | |||
19ecb1701e | |||
8085b2728d | |||
56cad31a36 | |||
3275bc4e93 | |||
8963378039 | |||
c7d435bb7a | |||
06cc7e4ea0 | |||
fba20e2cfb | |||
358f080c76 | |||
8cea57ff0f | |||
7b27f200b1 | |||
f4b207220c | |||
d835e1d14e | |||
75f92de8f8 | |||
293c1deee5 | |||
4dee05a967 | |||
8d6414d74a | |||
362ecdb583 | |||
058b9f9272 | |||
355762aa30 | |||
be4fb65470 | |||
4ed296bad4 | |||
a8aa862919 | |||
31ba1de53b | |||
bca1fef2b2 | |||
f33b31e048 | |||
0d10ebb7ca | |||
4a8abc948e | |||
6fd3672618 | |||
abd090bd48 | |||
bd9140f1f3 | |||
9d78b2d259 | |||
9c9528838a | |||
77b640b76b | |||
a8dfe98b1a | |||
cee41b87f7 | |||
7a95314e42 | |||
6aaf1f4f21 | |||
368a0ddd44 | |||
0808a10b7b | |||
6f052baa94 | |||
5bc67d3f6b | |||
74c6c5cfbc | |||
158b7fd166 | |||
c3f647dc96 | |||
5936c7b65c | |||
a8bcd85c93 | |||
127b558f95 | |||
6e9a27f40f | |||
4a13dbe3bb | |||
0f61f5ba03 | |||
5f1efbeb67 | |||
9c105914f0 | |||
579582740e | |||
b15544c163 | |||
94a63e3859 | |||
842b7e6c39 | |||
22f5011451 | |||
74120fe1f3 | |||
b4e8abd0ad | |||
336f1f4f50 | |||
1c256d8876 | |||
1ce0f0e7a5 | |||
fbf1901d86 | |||
0382f33c46 | |||
13372f3f99 | |||
e741cb7f0a | |||
fb289c6b17 | |||
75a7ea55d4 | |||
86573a5ccd | |||
6fe55a79f1 | |||
1a6cb9ee99 | |||
a495d9eca5 | |||
65a945f968 | |||
a9e8ed5087 | |||
00520b6a0e | |||
e6f2a3893a | |||
24c034ff6a | |||
631a93bcd8 | |||
b1763353ea | |||
f95bcf45ad | |||
15ec1abb6a | |||
cfda8dbb2b | |||
3aa2003951 | |||
0e473f4570 | |||
bfa824ee71 | |||
1157436a24 | |||
4596e78df0 | |||
813dfbd2d3 | |||
ba7dfb360c | |||
ab6e1b112b | |||
c666c3e251 | |||
20b1723e78 | |||
be78afeee5 | |||
6a1d611fd1 | |||
c8a72c876d | |||
ec87a8ddfc | |||
33e34ebb83 | |||
509f7bd018 | |||
10f27250ee | |||
aed7963d11 | |||
2810413112 | |||
31fd92e071 | |||
bf560f4594 | |||
a7fc23dd96 | |||
9e69b8fe1b | |||
e0ae631d59 | |||
273cf3d565 | |||
f432cfd73a | |||
6961a39cd2 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@ -1 +1 @@
|
|||||||
open_collective: polymc
|
open_collective: prismlauncher
|
||||||
|
17
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
17
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -8,9 +8,9 @@ body:
|
|||||||
If you need help with running Minecraft, please visit us on our Discord before making a bug report.
|
If you need help with running Minecraft, please visit us on our Discord before making a bug report.
|
||||||
|
|
||||||
Before submitting a bug report, please make sure you have read this *entire* form, and that:
|
Before submitting a bug report, please make sure you have read this *entire* form, and that:
|
||||||
* You have read the [PolyMC wiki](https://polymc.org/wiki/) and it has not answered your question.
|
* You have read the [Prism Launcher wiki](https://prismlauncher.org/wiki/) and it has not answered your question.
|
||||||
* Your bug is not caused by Minecraft or any mods you have installed.
|
* Your bug is not caused by Minecraft or any mods you have installed.
|
||||||
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/PolyMC/PolyMC/issues)
|
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/PrismLauncher/PrismLauncher/issues)
|
||||||
|
|
||||||
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
|
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
@ -25,9 +25,16 @@ body:
|
|||||||
- Other
|
- Other
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Version of PolyMC
|
label: Version of Prism Launcher
|
||||||
description: The version of PolyMC used in the bug report.
|
description: The version of Prism Launcher used in the bug report.
|
||||||
placeholder: PolyMC 1.3.2
|
placeholder: Prism Launcher 5.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Version of Qt
|
||||||
|
description: The version of Qt used in the bug report. You can find it in Help -> About Prism Launcher -> About Qt.
|
||||||
|
placeholder: Qt 6.3.0
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: PolyMC Matrix Support Room
|
- name: Prism Launcher Matrix Support Room
|
||||||
url: https://matrix.to/#/#support:polymc.org
|
url: https://matrix.to/#/#prism-support:matrix.org
|
||||||
about: Please ask for support here before opening an issue.
|
about: Please ask for support here before opening an issue.
|
||||||
|
4
.github/ISSUE_TEMPLATE/rfc.yml
vendored
4
.github/ISSUE_TEMPLATE/rfc.yml
vendored
@ -6,7 +6,7 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
### Use this form to suggest a larger change for PolyMC.
|
### Use this form to suggest a larger change for Prism Launcher.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Goal
|
label: Goal
|
||||||
@ -18,7 +18,7 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Motivation
|
label: Motivation
|
||||||
description: |
|
description: |
|
||||||
Introduce the topic. If this is a not-well-known section of PolyMC, a detailed explanation of the background is recommended.
|
Introduce the topic. If this is a not-well-known section of Prism Launcher, a detailed explanation of the background is recommended.
|
||||||
Some example points of discussion:
|
Some example points of discussion:
|
||||||
- What specific problems are you facing right now that you're trying to address?
|
- What specific problems are you facing right now that you're trying to address?
|
||||||
- Are there any previous discussions? Link to them and summarize them (don't force your readers to read them though!).
|
- Are there any previous discussions? Link to them and summarize them (don't force your readers to read them though!).
|
||||||
|
8
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
8
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
@ -5,25 +5,25 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
### Use this form to suggest a feature for PolyMC.
|
### Use this form to suggest a feature for Prism Launcher.
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Role
|
label: Role
|
||||||
description: In what way do you use PolyMC that needs this feature?
|
description: In what way do you use Prism Launcher that needs this feature?
|
||||||
placeholder: I play modded Minecraft.
|
placeholder: I play modded Minecraft.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Suggestion
|
label: Suggestion
|
||||||
description: What do you want PolyMC to do?
|
description: What do you want Prism Launcher to do?
|
||||||
placeholder: I want the cat button to meow.
|
placeholder: I want the cat button to meow.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Benefit
|
label: Benefit
|
||||||
description: Why do you need PolyMC to do this?
|
description: Why do you need Prism Launcher to do this?
|
||||||
placeholder: so that I can always hear a cat when I need to.
|
placeholder: so that I can always hear a cat when I need to.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
3
.github/codeql/codeql-config.yml
vendored
Normal file
3
.github/codeql/codeql-config.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
query-filters:
|
||||||
|
- exclude:
|
||||||
|
id: cpp/fixme-comment
|
9
.github/pull_request_template.md
vendored
Normal file
9
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!--
|
||||||
|
Hey there! Thanks for your contribution.
|
||||||
|
|
||||||
|
Please make sure that your commits are signed off first.
|
||||||
|
If you don't know how that works, check out our contribution guidelines: https://github.com/PrismLauncher/PrismLauncher/blob/develop/CONTRIBUTING.md#signing-your-work
|
||||||
|
If you already created your commits, you can run `git rebase --signoff develop` to retroactively sign-off all your commits and `git push --force` to override what you have pushed already.
|
||||||
|
|
||||||
|
Note that signing and signing-off are two different things!
|
||||||
|
-->
|
397
.github/workflows/build.yml
vendored
397
.github/workflows/build.yml
vendored
@ -7,10 +7,23 @@ on:
|
|||||||
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
||||||
type: string
|
type: string
|
||||||
default: Debug
|
default: Debug
|
||||||
|
is_qt_cached:
|
||||||
|
description: Enable Qt caching or not
|
||||||
|
type: string
|
||||||
|
default: true
|
||||||
secrets:
|
secrets:
|
||||||
SPARKLE_ED25519_KEY:
|
SPARKLE_ED25519_KEY:
|
||||||
description: Private key for signing Sparkle updates
|
description: Private key for signing Sparkle updates
|
||||||
required: false
|
required: false
|
||||||
|
WINDOWS_CODESIGN_CERT:
|
||||||
|
description: Certificate for signing Windows builds
|
||||||
|
required: false
|
||||||
|
WINDOWS_CODESIGN_PASSWORD:
|
||||||
|
description: Password for signing Windows builds
|
||||||
|
required: false
|
||||||
|
CACHIX_AUTH_TOKEN:
|
||||||
|
description: Private token for authenticating against Cachix cache
|
||||||
|
required: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -25,27 +38,70 @@ jobs:
|
|||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: linux
|
qt_host: linux
|
||||||
|
qt_arch: ''
|
||||||
qt_version: '6.2.4'
|
qt_version: '6.2.4'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_path: /home/runner/work/PolyMC/Qt
|
qt_tools: ''
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows-Legacy"
|
name: "Windows-MinGW-w64"
|
||||||
msystem: mingw32
|
msystem: clang64
|
||||||
|
vcvars_arch: 'amd64_x86'
|
||||||
|
|
||||||
|
- os: windows-2022
|
||||||
|
name: "Windows-MSVC-Legacy"
|
||||||
|
msystem: ''
|
||||||
|
architecture: 'win32'
|
||||||
|
vcvars_arch: 'amd64_x86'
|
||||||
qt_ver: 5
|
qt_ver: 5
|
||||||
|
qt_host: windows
|
||||||
|
qt_arch: 'win32_msvc2019'
|
||||||
|
qt_version: '5.15.2'
|
||||||
|
qt_modules: ''
|
||||||
|
qt_tools: 'tools_openssl_x86'
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
name: "Windows"
|
name: "Windows-MSVC"
|
||||||
msystem: mingw32
|
msystem: ''
|
||||||
|
architecture: 'x64'
|
||||||
|
vcvars_arch: 'amd64'
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
|
qt_host: windows
|
||||||
|
qt_arch: ''
|
||||||
|
qt_version: '6.5.1'
|
||||||
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
|
qt_tools: ''
|
||||||
|
|
||||||
|
- os: windows-2022
|
||||||
|
name: "Windows-MSVC-arm64"
|
||||||
|
msystem: ''
|
||||||
|
architecture: 'arm64'
|
||||||
|
vcvars_arch: 'amd64_arm64'
|
||||||
|
qt_ver: 6
|
||||||
|
qt_host: windows
|
||||||
|
qt_arch: 'win64_msvc2019_arm64'
|
||||||
|
qt_version: '6.5.1'
|
||||||
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
|
qt_tools: ''
|
||||||
|
|
||||||
- os: macos-12
|
- os: macos-12
|
||||||
macosx_deployment_target: 10.14
|
name: macOS
|
||||||
|
macosx_deployment_target: 11.0
|
||||||
qt_ver: 6
|
qt_ver: 6
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
qt_version: '6.3.1'
|
qt_arch: ''
|
||||||
|
qt_version: '6.5.0'
|
||||||
qt_modules: 'qt5compat qtimageformats'
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
qt_path: /Users/runner/work/PolyMC/Qt
|
qt_tools: ''
|
||||||
|
|
||||||
|
- os: macos-12
|
||||||
|
name: macOS-Legacy
|
||||||
|
macosx_deployment_target: 10.13
|
||||||
|
qt_ver: 5
|
||||||
|
qt_host: mac
|
||||||
|
qt_version: '5.15.2'
|
||||||
|
qt_modules: ''
|
||||||
|
qt_tools: ''
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
@ -56,6 +112,7 @@ jobs:
|
|||||||
INSTALL_APPIMAGE_DIR: "install-appdir"
|
INSTALL_APPIMAGE_DIR: "install-appdir"
|
||||||
BUILD_DIR: "build"
|
BUILD_DIR: "build"
|
||||||
CCACHE_VAR: ""
|
CCACHE_VAR: ""
|
||||||
|
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
##
|
##
|
||||||
@ -67,34 +124,49 @@ jobs:
|
|||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
|
|
||||||
- name: 'Setup MSYS2'
|
- name: 'Setup MSYS2'
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
msystem: ${{ matrix.msystem }}
|
msystem: ${{ matrix.msystem }}
|
||||||
update: true
|
update: true
|
||||||
install: >-
|
install: >-
|
||||||
git
|
git
|
||||||
|
mingw-w64-x86_64-binutils
|
||||||
pacboy: >-
|
pacboy: >-
|
||||||
toolchain:p
|
toolchain:p
|
||||||
cmake:p
|
cmake:p
|
||||||
extra-cmake-modules:p
|
extra-cmake-modules:p
|
||||||
ninja:p
|
ninja:p
|
||||||
qt${{ matrix.qt_ver }}-base:p
|
qt6-base:p
|
||||||
qt${{ matrix.qt_ver }}-svg:p
|
qt6-svg:p
|
||||||
qt${{ matrix.qt_ver }}-imageformats:p
|
qt6-imageformats:p
|
||||||
quazip-qt${{ matrix.qt_ver }}:p
|
quazip-qt6:p
|
||||||
ccache:p
|
ccache:p
|
||||||
nsis:p
|
qt6-5compat:p
|
||||||
${{ matrix.qt_ver == 6 && 'qt6-5compat:p' || '' }}
|
cmark:p
|
||||||
|
|
||||||
|
- name: Force newer ccache
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == '' && inputs.build_type == 'Debug'
|
||||||
|
run: |
|
||||||
|
choco install ccache --version 4.7.1
|
||||||
|
|
||||||
- name: Setup ccache
|
- name: Setup ccache
|
||||||
if: runner.os != 'Windows' && inputs.build_type == 'Debug'
|
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||||
uses: hendrikmuhs/ccache-action@v1.2.1
|
uses: hendrikmuhs/ccache-action@v1.2.9
|
||||||
with:
|
with:
|
||||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||||
|
|
||||||
- name: Setup ccache (Windows)
|
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
|
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||||
|
uses: actions/cache@v3.3.1
|
||||||
|
with:
|
||||||
|
path: '${{ github.workspace }}\.ccache'
|
||||||
|
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ matrix.os }}-mingw-w64-ccache
|
||||||
|
|
||||||
|
- name: Setup ccache (Windows MinGW-w64)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
|
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
|
||||||
@ -109,15 +181,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
|
echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Retrieve ccache cache (Windows)
|
|
||||||
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
|
|
||||||
uses: actions/cache@v3.0.2
|
|
||||||
with:
|
|
||||||
path: '${{ github.workspace }}\.ccache'
|
|
||||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ matrix.os }}-qt${{ matrix.qt_ver }}
|
|
||||||
|
|
||||||
- name: Set short version
|
- name: Set short version
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@ -140,25 +203,44 @@ jobs:
|
|||||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||||
|
|
||||||
- name: Cache Qt (macOS and AppImage)
|
|
||||||
id: cache-qt
|
|
||||||
if: matrix.qt_ver == 6 && runner.os != 'Windows'
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: '${{ matrix.qt_path }}/${{ matrix.qt_version }}'
|
|
||||||
key: ${{ matrix.qt_host }}-${{ matrix.qt_version }}-"${{ matrix.qt_modules }}"-qt_cache
|
|
||||||
|
|
||||||
- name: Install Qt (macOS and AppImage)
|
- name: Install host Qt (Windows MSVC arm64)
|
||||||
if: matrix.qt_ver == 6 && runner.os != 'Windows'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
uses: jurplel/install-qt-action@v2
|
uses: jurplel/install-qt-action@v3
|
||||||
with:
|
with:
|
||||||
|
aqtversion: '==3.1.*'
|
||||||
|
py7zrversion: '>=0.20.2'
|
||||||
|
version: ${{ matrix.qt_version }}
|
||||||
|
host: 'windows'
|
||||||
|
target: 'desktop'
|
||||||
|
arch: ''
|
||||||
|
modules: ${{ matrix.qt_modules }}
|
||||||
|
tools: ${{ matrix.qt_tools }}
|
||||||
|
cache: ${{ inputs.is_qt_cached }}
|
||||||
|
cache-key-prefix: host-qt-arm64-windows
|
||||||
|
dir: ${{ github.workspace }}\HostQt
|
||||||
|
set-env: false
|
||||||
|
|
||||||
|
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
aqtversion: '==3.1.*'
|
||||||
|
py7zrversion: '>=0.20.2'
|
||||||
version: ${{ matrix.qt_version }}
|
version: ${{ matrix.qt_version }}
|
||||||
host: ${{ matrix.qt_host }}
|
host: ${{ matrix.qt_host }}
|
||||||
target: 'desktop'
|
target: 'desktop'
|
||||||
|
arch: ${{ matrix.qt_arch }}
|
||||||
modules: ${{ matrix.qt_modules }}
|
modules: ${{ matrix.qt_modules }}
|
||||||
cached: ${{ steps.cache-qt.outputs.cache-hit }}
|
tools: ${{ matrix.qt_tools }}
|
||||||
aqtversion: ==2.1.*
|
cache: ${{ inputs.is_qt_cached }}
|
||||||
|
|
||||||
|
- name: Install MSVC (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
with:
|
||||||
|
vsversion: 2022
|
||||||
|
arch: ${{ matrix.vcvars_arch }}
|
||||||
|
|
||||||
- name: Prepare AppImage (Linux)
|
- name: Prepare AppImage (Linux)
|
||||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
@ -169,20 +251,45 @@ jobs:
|
|||||||
|
|
||||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
||||||
|
|
||||||
|
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
||||||
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
|
run: |
|
||||||
|
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
##
|
##
|
||||||
# CONFIGURE
|
# CONFIGURE
|
||||||
##
|
##
|
||||||
|
|
||||||
- name: Configure CMake (macOS)
|
- name: Configure CMake (macOS)
|
||||||
if: runner.os == 'macOS'
|
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 -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ 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_BUILD_PLATFORM=${{ matrix.name }} -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 (Windows)
|
- name: Configure CMake (macOS-Legacy)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
||||||
|
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=${{ matrix.name }} -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
|
||||||
|
|
||||||
|
- name: Configure CMake (Windows MinGW-w64)
|
||||||
|
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=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ 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_BUILD_PLATFORM=${{ matrix.name }} -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 -G Ninja
|
||||||
|
|
||||||
|
- name: Configure CMake (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
|
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=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
|
||||||
|
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
||||||
|
if ("${{ env.CCACHE_VAR }}")
|
||||||
|
{
|
||||||
|
Copy-Item C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/ccache.exe -Destination C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/cl.exe
|
||||||
|
echo "CLToolExe=cl.exe" >> $env:GITHUB_ENV
|
||||||
|
echo "CLToolPath=C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/" >> $env:GITHUB_ENV
|
||||||
|
echo "TrackFileAccess=false" >> $env:GITHUB_ENV
|
||||||
|
}
|
||||||
|
# Needed for ccache, but also speeds up compile
|
||||||
|
echo "UseMultiToolTask=true" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
- name: Configure CMake (Linux)
|
- name: Configure CMake (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
@ -198,12 +305,17 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake --build ${{ env.BUILD_DIR }}
|
cmake --build ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
- name: Build (Windows)
|
- name: Build (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake --build ${{ env.BUILD_DIR }}
|
cmake --build ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
|
- name: Build (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
|
run: |
|
||||||
|
cmake --build ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
||||||
|
|
||||||
##
|
##
|
||||||
# TEST
|
# TEST
|
||||||
##
|
##
|
||||||
@ -211,13 +323,18 @@ jobs:
|
|||||||
- name: Test
|
- name: Test
|
||||||
if: runner.os != 'Windows'
|
if: runner.os != 'Windows'
|
||||||
run: |
|
run: |
|
||||||
ctest --test-dir build --output-on-failure
|
ctest -E "^example64|example$" --test-dir build --output-on-failure
|
||||||
|
|
||||||
- name: Test (Windows)
|
- name: Test (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
ctest --test-dir build --output-on-failure
|
ctest -E "^example64|example$" --test-dir build --output-on-failure
|
||||||
|
|
||||||
|
- name: Test (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64'
|
||||||
|
run: |
|
||||||
|
ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }}
|
||||||
|
|
||||||
##
|
##
|
||||||
# PACKAGE BUILDS
|
# PACKAGE BUILDS
|
||||||
@ -229,17 +346,18 @@ jobs:
|
|||||||
cmake --install ${{ env.BUILD_DIR }}
|
cmake --install ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
chmod +x "PolyMC.app/Contents/MacOS/polymc"
|
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PolyMC.app/Contents/MacOS/polymc"
|
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||||
tar -czf ../PolyMC.tar.gz *
|
mv "PrismLauncher.app" "Prism Launcher.app"
|
||||||
|
tar -czf ../PrismLauncher.tar.gz *
|
||||||
|
|
||||||
- name: Make Sparkle signature (macOS)
|
- name: Make Sparkle signature (macOS)
|
||||||
if: runner.os == 'macOS'
|
if: matrix.name == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
||||||
brew install openssl@3
|
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 }}/PolyMC.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
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)
|
||||||
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:
|
||||||
@ -252,49 +370,97 @@ jobs:
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Package (Windows)
|
- name: Package (Windows MinGW-w64)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }}
|
cmake --install ${{ env.BUILD_DIR }}
|
||||||
|
touch ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
|
- name: Package (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
|
run: |
|
||||||
|
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
||||||
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
if [ "${{ matrix.msystem }}" == "mingw32" ]; then
|
if ("${{ matrix.qt_ver }}" -eq "5")
|
||||||
cp /mingw32/bin/libcrypto-1_1.dll /mingw32/bin/libssl-1_1.dll ./
|
{
|
||||||
elif [ "${{ matrix.msystem }}" == "mingw64" ]; then
|
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
||||||
cp /mingw64/bin/libcrypto-1_1-x64.dll /mingw64/bin/libssl-1_1-x64.dll ./
|
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
||||||
fi
|
}
|
||||||
|
cd ${{ github.workspace }}
|
||||||
|
|
||||||
- name: Package (Windows, portable)
|
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
|
||||||
|
|
||||||
|
|
||||||
|
- name: Fetch codesign certificate (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
|
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
||||||
|
run: |
|
||||||
|
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
|
||||||
|
|
||||||
|
- name: Sign executable (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
if (Get-Content ./codesign.pfx){
|
||||||
|
cd ${{ env.INSTALL_DIR }}
|
||||||
|
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
||||||
|
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_filelink.exe
|
||||||
|
} else {
|
||||||
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Package (Windows MinGW-w64, portable)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
|
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||||
|
|
||||||
|
- name: Package (Windows MSVC, portable)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
|
run: |
|
||||||
|
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||||
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
|
|
||||||
|
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
- name: Package (Windows, installer)
|
- name: Package (Windows, installer)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
shell: msys2 {0}
|
|
||||||
run: |
|
run: |
|
||||||
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"
|
||||||
|
|
||||||
|
- name: Sign installer (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
if (Get-Content ./codesign.pfx){
|
||||||
|
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
|
||||||
|
} else {
|
||||||
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
}
|
||||||
|
|
||||||
- name: Package (Linux)
|
- name: Package (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
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 }}
|
cd ${{ env.INSTALL_DIR }}
|
||||||
tar --owner root --group root -czf ../PolyMC.tar.gz *
|
tar --owner root --group root -czf ../PrismLauncher.tar.gz *
|
||||||
|
|
||||||
- name: Package (Linux, portable)
|
- name: Package (Linux, portable)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
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 }}
|
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
tar -czf ../PolyMC-portable.tar.gz *
|
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
|
||||||
@ -302,7 +468,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||||
|
|
||||||
export OUTPUT="PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||||
|
|
||||||
chmod +x linuxdeploy-*.AppImage
|
chmod +x linuxdeploy-*.AppImage
|
||||||
|
|
||||||
@ -313,7 +479,10 @@ jobs:
|
|||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
||||||
|
|
||||||
cp -r /home/runner/work/PolyMC/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
cp -r /home/runner/work/PrismLauncher/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/libssl.so.1.1 ${{ 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/server"
|
||||||
@ -322,7 +491,7 @@ jobs:
|
|||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
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
|
||||||
|
|
||||||
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.polymc.PolyMC.svg
|
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
|
||||||
|
|
||||||
##
|
##
|
||||||
# UPLOAD BUILDS
|
# UPLOAD BUILDS
|
||||||
@ -332,63 +501,115 @@ jobs:
|
|||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PolyMC.tar.gz
|
path: PrismLauncher.tar.gz
|
||||||
|
|
||||||
- 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@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ 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@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ 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@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PolyMC-Setup.exe
|
path: PrismLauncher-Setup.exe
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 5)
|
- name: Upload binary tarball (Linux, 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@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PolyMC.tar.gz
|
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@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PolyMC-portable.tar.gz
|
path: PrismLauncher-portable.tar.gz
|
||||||
|
|
||||||
- name: Upload binary tarball (Linux, Qt 6)
|
- name: Upload binary tarball (Linux, 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@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PolyMC.tar.gz
|
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@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PolyMC-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@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
|
|
||||||
|
- name: ccache stats (Windows MinGW-w64)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
ccache -s
|
||||||
|
|
||||||
|
flatpak:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
|
||||||
|
options: --privileged
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
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
|
||||||
|
|
||||||
|
nix:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
package:
|
||||||
|
- prismlauncher
|
||||||
|
- prismlauncher-qt5
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
- name: Install nix
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
uses: cachix/install-nix-action@v21
|
||||||
|
with:
|
||||||
|
install_url: https://nixos.org/nix/install
|
||||||
|
extra_nix_config: |
|
||||||
|
auto-optimise-store = true
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
- uses: cachix/cachix-action@v12
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
with:
|
||||||
|
name: prismlauncher
|
||||||
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
- name: Build
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
run: nix build .#${{ matrix.package }} --print-build-logs
|
||||||
|
35
.github/workflows/codeql.yml
vendored
Normal file
35
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
name: "CodeQL Code Scanning"
|
||||||
|
|
||||||
|
on: [ push, pull_request, workflow_dispatch ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
CodeQL:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
config-file: ./.github/codeql/codeql-config.yml
|
||||||
|
queries: security-and-quality
|
||||||
|
languages: cpp, java
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run:
|
||||||
|
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
|
||||||
|
|
||||||
|
- name: Configure and Build
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr -DLauncher_QT_VERSION_MAJOR=5 -G Ninja
|
||||||
|
|
||||||
|
cmake --build build
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
10
.github/workflows/trigger_builds.yml
vendored
10
.github/workflows/trigger_builds.yml
vendored
@ -3,22 +3,22 @@ name: Build Application
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'stable'
|
- 'renovate/**'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
- '**/LICENSE'
|
- '**/LICENSE'
|
||||||
- 'flake.lock'
|
- 'flake.lock'
|
||||||
- '**.nix'
|
|
||||||
- 'packages/**'
|
- 'packages/**'
|
||||||
- '.github/ISSUE_TEMPLATE/**'
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
|
- '.markdownlint**'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
- '**/LICENSE'
|
- '**/LICENSE'
|
||||||
- 'flake.lock'
|
- 'flake.lock'
|
||||||
- '**.nix'
|
|
||||||
- 'packages/**'
|
- 'packages/**'
|
||||||
- '.github/ISSUE_TEMPLATE/**'
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
|
- '.markdownlint**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -28,5 +28,9 @@ jobs:
|
|||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
with:
|
with:
|
||||||
build_type: Debug
|
build_type: Debug
|
||||||
|
is_qt_cached: true
|
||||||
secrets:
|
secrets:
|
||||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
77
.github/workflows/trigger_release.yml
vendored
77
.github/workflows/trigger_release.yml
vendored
@ -12,8 +12,12 @@ jobs:
|
|||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
with:
|
with:
|
||||||
build_type: Release
|
build_type: Release
|
||||||
|
is_qt_cached: false
|
||||||
secrets:
|
secrets:
|
||||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
|
||||||
create_release:
|
create_release:
|
||||||
needs: build_release
|
needs: build_release
|
||||||
@ -25,7 +29,7 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
path: 'PolyMC-source'
|
path: 'PrismLauncher-source'
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
- name: Grab and store version
|
- name: Grab and store version
|
||||||
@ -34,25 +38,39 @@ jobs:
|
|||||||
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
||||||
- name: Package artifacts properly
|
- name: Package artifacts properly
|
||||||
run: |
|
run: |
|
||||||
mv ${{ github.workspace }}/PolyMC-source PolyMC-${{ env.VERSION }}
|
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||||
mv PolyMC-Linux-Qt6-Portable*/PolyMC-portable.tar.gz PolyMC-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 PolyMC-Linux-Qt6*/PolyMC.tar.gz PolyMC-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||||
mv PolyMC-Linux-Portable*/PolyMC-portable.tar.gz PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||||
mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||||
mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||||
mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||||
|
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||||
|
|
||||||
tar -czf PolyMC-${{ env.VERSION }}.tar.gz PolyMC-${{ env.VERSION }}
|
tar -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
|
||||||
|
|
||||||
for d in PolyMC-Windows-*; do
|
for d in PrismLauncher-Windows-MSVC*; do
|
||||||
cd "${d}" || continue
|
cd "${d}" || continue
|
||||||
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
|
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
|
||||||
|
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
|
||||||
INST="$(echo -n ${d} | grep -o Setup || true)"
|
INST="$(echo -n ${d} | grep -o Setup || true)"
|
||||||
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
||||||
NAME="PolyMC-Windows"
|
NAME="PrismLauncher-Windows-MSVC"
|
||||||
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
||||||
|
test -z "${ARM64}" || NAME="${NAME}-arm64"
|
||||||
test -z "${PORT}" || NAME="${NAME}-Portable"
|
test -z "${PORT}" || NAME="${NAME}-Portable"
|
||||||
test -z "${INST}" || mv PolyMC-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
||||||
|
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
||||||
|
cd ..
|
||||||
|
done
|
||||||
|
|
||||||
|
for d in PrismLauncher-Windows-MinGW-w64*; do
|
||||||
|
cd "${d}" || continue
|
||||||
|
INST="$(echo -n ${d} | grep -o Setup || true)"
|
||||||
|
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
||||||
|
NAME="PrismLauncher-Windows-MinGW-w64"
|
||||||
|
test -z "${PORT}" || NAME="${NAME}-Portable"
|
||||||
|
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
||||||
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
||||||
cd ..
|
cd ..
|
||||||
done
|
done
|
||||||
@ -64,20 +82,27 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
name: PolyMC ${{ env.VERSION }}
|
name: Prism Launcher ${{ env.VERSION }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
files: |
|
files: |
|
||||||
PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||||
PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||||
PolyMC-Windows-Legacy-${{ env.VERSION }}.zip
|
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||||
PolyMC-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
PolyMC-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||||
PolyMC-Windows-Legacy-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
||||||
PolyMC-Windows-Legacy-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
|
||||||
PolyMC-Windows-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-Legacy-${{ env.VERSION }}.zip
|
||||||
PolyMC-Windows-Portable-${{ env.VERSION }}.zip
|
PrismLauncher-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip
|
||||||
PolyMC-Windows-Setup-${{ env.VERSION }}.exe
|
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
|
||||||
PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
|
||||||
PolyMC-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
|
||||||
|
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
|
||||||
|
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
||||||
|
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
||||||
|
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
|
||||||
|
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||||
|
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||||
|
PrismLauncher-${{ env.VERSION }}.tar.gz
|
||||||
|
7
.github/workflows/winget.yml
vendored
7
.github/workflows/winget.yml
vendored
@ -7,8 +7,9 @@ jobs:
|
|||||||
publish:
|
publish:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: vedantmgoyal2009/winget-releaser@latest
|
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||||
with:
|
with:
|
||||||
identifier: PolyMC.PolyMC
|
identifier: PrismLauncher.PrismLauncher
|
||||||
installers-regex: '\.exe$'
|
version: ${{ github.event.release.tag_name }}
|
||||||
|
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
||||||
token: ${{ secrets.WINGET_TOKEN }}
|
token: ${{ secrets.WINGET_TOKEN }}
|
||||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -11,10 +11,14 @@ html/
|
|||||||
*.pro.user
|
*.pro.user
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
CMakeLists.txt.user.*
|
CMakeLists.txt.user.*
|
||||||
|
CMakeSettings.json
|
||||||
|
/CMakeFiles
|
||||||
|
CMakeCache.txt
|
||||||
/.project
|
/.project
|
||||||
/.settings
|
/.settings
|
||||||
/.idea
|
/.idea
|
||||||
/.vscode
|
/.vscode
|
||||||
|
/.vs
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
Debug
|
Debug
|
||||||
|
|
||||||
@ -42,8 +46,13 @@ run/
|
|||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
# Nix/NixOS
|
# Nix/NixOS
|
||||||
result/
|
.direnv/
|
||||||
|
.pre-commit-config.yaml
|
||||||
|
result
|
||||||
|
|
||||||
# Flatpak
|
# Flatpak
|
||||||
.flatpak-builder
|
.flatpak-builder
|
||||||
flatbuild
|
flatbuild
|
||||||
|
|
||||||
|
# Snap
|
||||||
|
*.snap
|
||||||
|
23
.gitmodules
vendored
23
.gitmodules
vendored
@ -1,8 +1,21 @@
|
|||||||
[submodule "depends/libnbtplusplus"]
|
|
||||||
path = libraries/libnbtplusplus
|
|
||||||
url = https://github.com/PolyMC/libnbtplusplus.git
|
|
||||||
pushurl = git@github.com:PolyMC/libnbtplusplus.git
|
|
||||||
|
|
||||||
[submodule "libraries/quazip"]
|
[submodule "libraries/quazip"]
|
||||||
path = libraries/quazip
|
path = libraries/quazip
|
||||||
url = https://github.com/stachenov/quazip.git
|
url = https://github.com/stachenov/quazip.git
|
||||||
|
[submodule "libraries/tomlplusplus"]
|
||||||
|
path = libraries/tomlplusplus
|
||||||
|
url = https://github.com/marzer/tomlplusplus.git
|
||||||
|
[submodule "libraries/filesystem"]
|
||||||
|
path = libraries/filesystem
|
||||||
|
url = https://github.com/gulrak/filesystem
|
||||||
|
[submodule "libraries/libnbtplusplus"]
|
||||||
|
path = libraries/libnbtplusplus
|
||||||
|
url = https://github.com/PrismLauncher/libnbtplusplus.git
|
||||||
|
[submodule "libraries/zlib"]
|
||||||
|
path = libraries/zlib
|
||||||
|
url = https://github.com/madler/zlib.git
|
||||||
|
[submodule "libraries/extra-cmake-modules"]
|
||||||
|
path = libraries/extra-cmake-modules
|
||||||
|
url = https://github.com/KDE/extra-cmake-modules
|
||||||
|
[submodule "libraries/cmark"]
|
||||||
|
path = libraries/cmark
|
||||||
|
url = https://github.com/commonmark/cmark.git
|
||||||
|
12
.markdownlint.yaml
Normal file
12
.markdownlint.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# MD013/line-length - Line length
|
||||||
|
MD013: false
|
||||||
|
|
||||||
|
# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
|
||||||
|
MD024:
|
||||||
|
siblings-only: true
|
||||||
|
|
||||||
|
# MD033/no-inline-html Inline HTML
|
||||||
|
MD033: false
|
||||||
|
|
||||||
|
# MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading
|
||||||
|
MD041: false
|
2
.markdownlintignore
Normal file
2
.markdownlintignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
libraries/nbtplusplus
|
||||||
|
libraries/quazip
|
4
BUILD.md
4
BUILD.md
@ -1,5 +1,3 @@
|
|||||||
# Build Instructions
|
# Build Instructions
|
||||||
|
|
||||||
Build instructions are available on [the website](https://polymc.org/wiki/development/build-instructions/).
|
Full build instructions are available on [the website](https://prismlauncher.org/wiki/development/build-instructions/).
|
||||||
|
|
||||||
If you would like to contribute or fix an issue with the Build instructions you can do so [here](https://github.com/PolyMC/polymc.github.io/blob/master/src/wiki/development/build-instructions.md).
|
|
||||||
|
276
CMakeLists.txt
276
CMakeLists.txt
@ -1,10 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.15) # minimum version required by QuaZip
|
cmake_minimum_required(VERSION 3.15) # minimum version required by QuaZip
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
|
|
||||||
cmake_policy(SET CMP0020 OLD)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(Launcher)
|
project(Launcher)
|
||||||
|
|
||||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||||
@ -29,12 +24,54 @@ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
|
|||||||
######## Set compiler flags ########
|
######## Set compiler flags ########
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED true)
|
set(CMAKE_CXX_STANDARD_REQUIRED true)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED true)
|
set(CMAKE_C_STANDARD_REQUIRED true)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
set(CMAKE_CXX_FLAGS "-Wall -pedantic -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
|
if(MSVC)
|
||||||
if(UNIX AND APPLE)
|
# /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag
|
||||||
set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
# /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20
|
||||||
|
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
||||||
|
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
|
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
|
||||||
|
# This implicitly selects an entrypoint specific to the subsystem selected
|
||||||
|
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
|
||||||
|
# Additinaly LINK autodetects we use a GUI so we can omit /SUBSYSTEM
|
||||||
|
# This allows tests to still use have console without using seperate linker flags
|
||||||
|
# /LTCG allows for linking wholy optimizated programs
|
||||||
|
# /MANIFEST:NO disables generating a manifest file, we instead provide our own
|
||||||
|
# /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "/LTCG /MANIFEST:NO /STACK:8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
|
||||||
|
# /GL enables whole program optimizations
|
||||||
|
# /Gw helps reduce binary size
|
||||||
|
# /Gy allows the compiler to package individual functions
|
||||||
|
# /guard:cf enables control flow guard
|
||||||
|
foreach(lang C CXX)
|
||||||
|
set("CMAKE_${lang}_FLAGS_RELEASE" "/GL /Gw /Gy /guard:cf")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# See https://github.com/ccache/ccache/issues/1040
|
||||||
|
# Note, CMake 3.25 replaces this with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
|
||||||
|
# See https://cmake.org/cmake/help/v3.25/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.html
|
||||||
|
foreach(config DEBUG RELWITHDEBINFO)
|
||||||
|
foreach(lang C CXX)
|
||||||
|
set(flags_var "CMAKE_${lang}_FLAGS_${config}")
|
||||||
|
string(REGEX REPLACE "/Z[Ii]" "/Z7" ${flags_var} "${${flags_var}}")
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(CMAKE_MSVC_RUNTIME_LIBRARY STREQUAL "MultiThreadedDLL")
|
||||||
|
set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Release "")
|
||||||
|
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release "")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
|
# ATL's pack list needs more than the default 1 Mib stack on windows
|
||||||
|
if(WIN32)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Fix build with Qt 5.13
|
# Fix build with Qt 5.13
|
||||||
@ -42,6 +79,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
|
|||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00")
|
||||||
|
|
||||||
|
# Fix aarch64 build for toml++
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
|
||||||
|
|
||||||
# set CXXFLAGS for build targets
|
# set CXXFLAGS for build targets
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
|
||||||
@ -51,11 +91,18 @@ if(ENABLE_LTO)
|
|||||||
include(CheckIPOSupported)
|
include(CheckIPOSupported)
|
||||||
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
||||||
|
|
||||||
if(ipo_supported AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel"))
|
if(ipo_supported)
|
||||||
message(STATUS "IPO / LTO enabled")
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
|
||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE)
|
||||||
elseif(ipo_supported)
|
if(CMAKE_BUILD_TYPE)
|
||||||
message(STATUS "Not enabling IPO / LTO on debug builds")
|
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
|
||||||
|
message(STATUS "IPO / LTO enabled")
|
||||||
|
else()
|
||||||
|
message(STATUS "Not enabling IPO / LTO on debug builds")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "IPO / LTO will only be enabled for release builds")
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
||||||
endif()
|
endif()
|
||||||
@ -63,28 +110,40 @@ endif()
|
|||||||
|
|
||||||
option(BUILD_TESTING "Build the testing tree." ON)
|
option(BUILD_TESTING "Build the testing tree." ON)
|
||||||
|
|
||||||
find_package(ECM REQUIRED NO_MODULE)
|
find_package(ECM QUIET NO_MODULE)
|
||||||
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
|
if(NOT ECM_FOUND)
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/CMakeLists.txt")
|
||||||
|
message(STATUS "Using bundled ECM")
|
||||||
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/modules;${CMAKE_MODULE_PATH}")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
" Could not find ECM\n \n"
|
||||||
|
" Either install ECM using the system package manager or clone submodules\n"
|
||||||
|
" Submodules can be cloned with 'git submodule update --init --recursive'")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
|
||||||
|
endif()
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(ECMAddTests)
|
include(ECMAddTests)
|
||||||
if (BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
##################################### Set Application options #####################################
|
##################################### Set Application options #####################################
|
||||||
|
|
||||||
######## Set URLs ########
|
######## Set URLs ########
|
||||||
set(Launcher_NEWS_RSS_URL "https://polymc.org/feed/feed.xml" CACHE STRING "URL to fetch PolyMC's news RSS feed from.")
|
set(Launcher_NEWS_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://polymc.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://polymc.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
set(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 version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 1)
|
set(Launcher_VERSION_MAJOR 7)
|
||||||
set(Launcher_VERSION_MINOR 4)
|
set(Launcher_VERSION_MINOR 0)
|
||||||
set(Launcher_VERSION_HOTFIX 0)
|
|
||||||
|
|
||||||
# Build number
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||||
|
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
|
||||||
|
|
||||||
# Build platform.
|
# Build platform.
|
||||||
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
||||||
@ -93,29 +152,29 @@ set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the plat
|
|||||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||||
|
|
||||||
# The metadata server
|
# The metadata server
|
||||||
set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||||
|
|
||||||
# Imgur API Client ID
|
# Imgur API Client ID
|
||||||
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
||||||
|
|
||||||
# Bug tracker URL
|
# Bug tracker URL
|
||||||
set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.")
|
set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/issues" CACHE STRING "URL for the bug tracker.")
|
||||||
|
|
||||||
# Translations Platform URL
|
# Translations Platform URL
|
||||||
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/polymc/polymc/" 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.")
|
||||||
|
|
||||||
# Matrix Space
|
# Matrix Space
|
||||||
set(Launcher_MATRIX_URL "https://matrix.to/#/#polymc:matrix.org" CACHE STRING "URL to the Matrix Space")
|
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
|
||||||
|
|
||||||
# Discord URL
|
# Discord URL
|
||||||
set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
|
set(Launcher_DISCORD_URL "https://prismlauncher.org/discord" CACHE STRING "URL for the Discord guild.")
|
||||||
|
|
||||||
# Subreddit URL
|
# Subreddit URL
|
||||||
set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRING "URL for the subreddit.")
|
set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL for the subreddit.")
|
||||||
|
|
||||||
# Builds
|
# Builds
|
||||||
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 "5" CACHE STRING "Major Qt version to build against")
|
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
||||||
|
|
||||||
# API Keys
|
# API Keys
|
||||||
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
||||||
@ -126,12 +185,13 @@ set(Launcher_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build agains
|
|||||||
|
|
||||||
# By using this key in your builds you accept the terms of use laid down in
|
# By using this key in your builds you accept the terms of use laid down in
|
||||||
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
|
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
|
||||||
set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
||||||
|
|
||||||
# By using this key in your builds you accept the terms and conditions laid down in
|
# By using this key in your builds you accept the terms and conditions laid down in
|
||||||
# https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions
|
# https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions
|
||||||
# NOTE: CurseForge requires you to change this if you make any kind of derivative work.
|
# NOTE: CurseForge requires you to change this if you make any kind of derivative work.
|
||||||
set(Launcher_CURSEFORGE_API_KEY "$2a$10$1Oqr2MX3O4n/ilhFGc597u8tfI3L2Hyr9/rtWDAMRjghSQV2QUuxq" CACHE STRING "API key for the CurseForge platform")
|
# This key was issued specifically for Prism Launcher
|
||||||
|
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
|
||||||
|
|
||||||
|
|
||||||
#### Check the current Git commit and branch
|
#### Check the current Git commit and branch
|
||||||
@ -143,18 +203,21 @@ message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
|
|||||||
message(STATUS "Git tag: ${Launcher_GIT_TAG}")
|
message(STATUS "Git tag: ${Launcher_GIT_TAG}")
|
||||||
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
||||||
|
|
||||||
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
|
||||||
set(Launcher_RELEASE_VERSION_NAME4 "${Launcher_RELEASE_VERSION_NAME}.0")
|
|
||||||
set(Launcher_RELEASE_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},${Launcher_VERSION_HOTFIX},0")
|
|
||||||
string(TIMESTAMP TODAY "%Y-%m-%d")
|
string(TIMESTAMP TODAY "%Y-%m-%d")
|
||||||
set(Launcher_RELEASE_TIMESTAMP "${TODAY}")
|
set(Launcher_BUILD_TIMESTAMP "${TODAY}")
|
||||||
|
|
||||||
#### Custom target to just print the version.
|
|
||||||
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
|
|
||||||
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
|
|
||||||
|
|
||||||
################################ 3rd Party Libs ################################
|
################################ 3rd Party Libs ################################
|
||||||
|
|
||||||
|
# Successive configurations of cmake without cleaning the build dir will cause zlib fallback to fail due to cached values
|
||||||
|
# Record when fallback triggered and skip this find_package
|
||||||
|
if(NOT Launcher_FORCE_BUNDLED_LIBS AND NOT FORCE_BUNDLED_ZLIB)
|
||||||
|
find_package(ZLIB QUIET)
|
||||||
|
endif()
|
||||||
|
if(NOT ZLIB_FOUND)
|
||||||
|
set(FORCE_BUNDLED_ZLIB TRUE CACHE BOOL "")
|
||||||
|
mark_as_advanced(FORCE_BUNDLED_ZLIB)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Find the required Qt parts
|
# Find the required Qt parts
|
||||||
include(QtVersionlessBackport)
|
include(QtVersionlessBackport)
|
||||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||||
@ -173,7 +236,7 @@ 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 Widgets Concurrent Network Test Xml Core5Compat)
|
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat)
|
||||||
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)
|
||||||
@ -187,21 +250,38 @@ else()
|
|||||||
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
|
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include(ECMQueryQt)
|
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||||
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
|
include(ECMQueryQt)
|
||||||
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
|
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
|
||||||
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
|
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
|
||||||
ecm_query_qt(QT_DATA_DIR QT_HOST_DATA)
|
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
|
||||||
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
|
else()
|
||||||
|
set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS})
|
||||||
|
set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS})
|
||||||
|
set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS})
|
||||||
|
endif()
|
||||||
|
|
||||||
# NOTE: Qt 6 already sets this by default
|
# NOTE: Qt 6 already sets this by default
|
||||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||||
|
# Find toml++
|
||||||
|
find_package(tomlplusplus 3.2.0 QUIET)
|
||||||
|
|
||||||
|
# Find ghc_filesystem
|
||||||
|
find_package(ghc_filesystem QUIET)
|
||||||
|
|
||||||
|
# Find cmark
|
||||||
|
find_package(cmark QUIET)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(ECMQtDeclareLoggingCategory)
|
||||||
|
|
||||||
####################################### Program Info #######################################
|
####################################### Program Info #######################################
|
||||||
|
|
||||||
set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
|
set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary")
|
||||||
add_subdirectory(program_info)
|
add_subdirectory(program_info)
|
||||||
|
|
||||||
####################################### Install layout #######################################
|
####################################### Install layout #######################################
|
||||||
@ -223,16 +303,16 @@ if(UNIX AND APPLE)
|
|||||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
||||||
|
|
||||||
# Mac bundle settings
|
# Mac bundle settings
|
||||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
|
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
||||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
||||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.polymc.${Launcher_Name}")
|
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}")
|
||||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
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 "Copyright 2021-2022 ${Launcher_Copyright}")
|
set(MACOSX_BUNDLE_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}")
|
||||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "idALcUIazingvKSSsEa9U7coDVxZVx/ORpOEE/QtJfg=")
|
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=")
|
||||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://polymc.org/feed/appcast.xml")
|
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml")
|
||||||
|
|
||||||
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.1.0/Sparkle-2.1.0.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 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||||
@ -248,13 +328,11 @@ if(UNIX AND APPLE)
|
|||||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||||
|
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
|
include(KDEInstallDirs)
|
||||||
|
|
||||||
set(BINARY_DEST_DIR "bin")
|
set(BINARY_DEST_DIR "bin")
|
||||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||||
set(JARS_DEST_DIR "share/jars")
|
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
|
||||||
set(LAUNCHER_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
|
|
||||||
set(LAUNCHER_METAINFO_DEST_DIR "share/metainfo" CACHE STRING "Path to the metainfo directory")
|
|
||||||
set(LAUNCHER_ICON_DEST_DIR "share/icons/hicolor/scalable/apps" CACHE STRING "Path to the scalable icon directory")
|
|
||||||
set(LAUNCHER_MAN_DEST_DIR "share/man/man6" CACHE STRING "Path to the man page directory")
|
|
||||||
|
|
||||||
# install as bundle with no dependencies included
|
# install as bundle with no dependencies included
|
||||||
set(INSTALL_BUNDLE "nodeps")
|
set(INSTALL_BUNDLE "nodeps")
|
||||||
@ -262,12 +340,15 @@ elseif(UNIX)
|
|||||||
# Set RPATH
|
# Set RPATH
|
||||||
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${KDE_INSTALL_APPDIR})
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR})
|
||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
|
||||||
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
||||||
|
|
||||||
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "${KDE_INSTALL_DATADIR}/${Launcher_Name}")
|
||||||
|
|
||||||
if(Launcher_ManPage)
|
if(Launcher_ManPage)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION ${LAUNCHER_MAN_DEST_DIR})
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Install basic runner script if component "portable" is selected
|
# Install basic runner script if component "portable" is selected
|
||||||
@ -293,6 +374,8 @@ else()
|
|||||||
message(FATAL_ERROR "Platform not supported")
|
message(FATAL_ERROR "Platform not supported")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
################################ Included Libs ################################
|
################################ Included Libs ################################
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
@ -304,10 +387,34 @@ option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
|
|||||||
add_subdirectory(libraries/libnbtplusplus)
|
add_subdirectory(libraries/libnbtplusplus)
|
||||||
|
|
||||||
add_subdirectory(libraries/systeminfo) # system information library
|
add_subdirectory(libraries/systeminfo) # system information library
|
||||||
add_subdirectory(libraries/hoedown) # markdown parser
|
|
||||||
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
||||||
add_subdirectory(libraries/javacheck) # java compatibility checker
|
add_subdirectory(libraries/javacheck) # java compatibility checker
|
||||||
add_subdirectory(libraries/xz-embedded) # xz compression
|
if(FORCE_BUNDLED_ZLIB)
|
||||||
|
message(STATUS "Using bundled zlib")
|
||||||
|
|
||||||
|
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # Suppress cmake warnings and allow INTERPROCEDURAL_OPTIMIZATION for zlib
|
||||||
|
set(SKIP_INSTALL_ALL ON)
|
||||||
|
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.
|
||||||
|
# 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)
|
||||||
|
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" AND NOT NEED_GENERATED_ZCONF)
|
||||||
|
# zlib's cmake script renames a file, dirtying the submodule, see https://github.com/madler/zlib/issues/162
|
||||||
|
message(STATUS "Undoing Rename")
|
||||||
|
message(STATUS " ${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
|
||||||
|
file(RENAME "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" CACHE STRING "" FORCE)
|
||||||
|
set_target_properties(zlibstatic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}")
|
||||||
|
add_library(ZLIB::ZLIB ALIAS zlibstatic)
|
||||||
|
set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "zlib library name")
|
||||||
|
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system zlib")
|
||||||
|
endif()
|
||||||
if (FORCE_BUNDLED_QUAZIP)
|
if (FORCE_BUNDLED_QUAZIP)
|
||||||
message(STATUS "Using bundled QuaZip")
|
message(STATUS "Using bundled QuaZip")
|
||||||
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
||||||
@ -318,16 +425,39 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||||
add_subdirectory(libraries/classparser) # class parser library
|
if(NOT tomlplusplus_FOUND)
|
||||||
add_subdirectory(libraries/optional-bare)
|
message(STATUS "Using bundled tomlplusplus")
|
||||||
add_subdirectory(libraries/tomlc99) # toml parser
|
add_subdirectory(libraries/tomlplusplus) # toml parser
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system tomlplusplus")
|
||||||
|
endif()
|
||||||
|
if(NOT cmark_FOUND)
|
||||||
|
message(STATUS "Using bundled cmark")
|
||||||
|
set(CMARK_STATIC ON CACHE BOOL "Build static libcmark library" FORCE)
|
||||||
|
set(CMARK_SHARED OFF CACHE BOOL "Build shared libcmark library" FORCE)
|
||||||
|
set(CMARK_TESTS OFF CACHE BOOL "Build cmark tests and enable testing" FORCE)
|
||||||
|
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
|
||||||
|
add_library(cmark::cmark ALIAS cmark_static)
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system cmark")
|
||||||
|
endif()
|
||||||
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
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)
|
||||||
|
message(STATUS "Using bundled ghc_filesystem")
|
||||||
|
add_subdirectory(libraries/filesystem) # Implementation of std::filesystem for old C++, for usage in old macOS
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system ghc_filesystem")
|
||||||
|
endif()
|
||||||
|
add_subdirectory(libraries/qdcss) # css parser
|
||||||
|
|
||||||
############################### Built Artifacts ###############################
|
############################### Built Artifacts ###############################
|
||||||
|
|
||||||
add_subdirectory(buildconfig)
|
add_subdirectory(buildconfig)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
||||||
add_subdirectory(launcher)
|
add_subdirectory(launcher)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
This is a modified version of the Contributor Covenant.
|
This is a modified version of the Contributor Covenant.
|
||||||
See commit history to see our changes.
|
See commit history to see our changes.
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ representative at an online or offline event.
|
|||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported to the community leaders responsible for enforcement via email at
|
reported to the community leaders responsible for enforcement via email at
|
||||||
[polymc-enforcement@scrumplex.net](mailto:polymc-enforcement@scrumplex.net) (Email
|
[coc@scrumplex.net](mailto:coc@scrumplex.net) (Email
|
||||||
address subject to change).
|
address subject to change).
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
@ -133,4 +134,3 @@ For answers to common questions about this code of conduct, see the FAQ at
|
|||||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||||
[FAQ]: https://www.contributor-covenant.org/faq
|
[FAQ]: https://www.contributor-covenant.org/faq
|
||||||
[translations]: https://www.contributor-covenant.org/translations
|
[translations]: https://www.contributor-covenant.org/translations
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ Try to follow the existing formatting.
|
|||||||
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
||||||
|
|
||||||
In general, in order of importance:
|
In general, in order of importance:
|
||||||
|
|
||||||
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
||||||
- Prefer readability over dogma.
|
- Prefer readability over dogma.
|
||||||
- Keep to the existing formatting.
|
- Keep to the existing formatting.
|
||||||
@ -26,37 +27,37 @@ Signed-off-by: Author name <Author email>
|
|||||||
|
|
||||||
By signing off your work, you agree to the terms below:
|
By signing off your work, you agree to the terms below:
|
||||||
|
|
||||||
Developer's Certificate of Origin 1.1
|
```
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
By making a contribution to this project, I certify that:
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
(a) The contribution was created in whole or in part by me and I
|
(a) The contribution was created in whole or in part by me and I
|
||||||
have the right to submit it under the open source license
|
have the right to submit it under the open source license
|
||||||
indicated in the file; or
|
indicated in the file; or
|
||||||
|
|
||||||
(b) The contribution is based upon previous work that, to the best
|
(b) The contribution is based upon previous work that, to the best
|
||||||
of my knowledge, is covered under an appropriate open source
|
of my knowledge, is covered under an appropriate open source
|
||||||
license and I have the right under that license to submit that
|
license and I have the right under that license to submit that
|
||||||
work with modifications, whether created in whole or in part
|
work with modifications, whether created in whole or in part
|
||||||
by me, under the same open source license (unless I am
|
by me, under the same open source license (unless I am
|
||||||
permitted to submit under a different license), as indicated
|
permitted to submit under a different license), as indicated
|
||||||
in the file; or
|
in the file; or
|
||||||
|
|
||||||
(c) The contribution was provided directly to me by some other
|
(c) The contribution was provided directly to me by some other
|
||||||
person who certified (a), (b) or (c) and I have not modified
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
it.
|
it.
|
||||||
|
|
||||||
(d) I understand and agree that this project and the contribution
|
(d) I understand and agree that this project and the contribution
|
||||||
are public and that a record of the contribution (including all
|
are public and that a record of the contribution (including all
|
||||||
personal information I submit with it, including my sign-off) is
|
personal information I submit with it, including my sign-off) is
|
||||||
maintained indefinitely and may be redistributed consistent with
|
maintained indefinitely and may be redistributed consistent with
|
||||||
this project or the open source license(s) involved.
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
These terms will be enforced once you create a pull request, and you will be informed automatically if any of your commits aren't signed-off by you.
|
These terms will be enforced once you create a pull request, and you will be informed automatically if any of your commits aren't signed-off by you.
|
||||||
|
|
||||||
As a bonus, you can also [cryptographically sign your commits][gh-signing-commits] and enable [vigilant mode][gh-vigilant-mode] on GitHub.
|
As a bonus, you can also [cryptographically sign your commits][gh-signing-commits] and enable [vigilant mode][gh-vigilant-mode] on GitHub.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits
|
[gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits
|
||||||
[gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits
|
[gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits
|
||||||
|
344
COPYING.md
344
COPYING.md
@ -1,4 +1,38 @@
|
|||||||
# PolyMC
|
## Prism Launcher
|
||||||
|
|
||||||
|
Prism Launcher - Minecraft Launcher
|
||||||
|
Copyright (C) 2022-2023 Prism Launcher Contributors
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
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.
|
||||||
|
|
||||||
|
## PolyMC
|
||||||
|
|
||||||
PolyMC - Minecraft Launcher
|
PolyMC - Minecraft Launcher
|
||||||
Copyright (C) 2021-2022 PolyMC Contributors
|
Copyright (C) 2021-2022 PolyMC Contributors
|
||||||
@ -32,36 +66,56 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
# MinGW runtime (Windows)
|
## MinGW-w64 runtime (Windows)
|
||||||
|
|
||||||
Copyright (c) 2012 MinGW.org project
|
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
This license has been certified as open source. It has also been designated
|
||||||
copy of this software and associated documentation files (the "Software"),
|
as GPL compatible by the Free Software Foundation (FSF).
|
||||||
to deal in the Software without restriction, including without limitation
|
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice, this permission notice and the below disclaimer
|
Redistribution and use in source and binary forms, with or without
|
||||||
shall be included in all copies or substantial portions of the Software.
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
1. Redistributions in source code must retain the accompanying copyright
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
notice, this list of conditions, and the following disclaimer.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
2. Redistributions in binary form must reproduce the accompanying
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
copyright notice, this list of conditions, and the following disclaimer
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
in the documentation and/or other materials provided with the
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
distribution.
|
||||||
DEALINGS IN THE SOFTWARE.
|
3. Names of the copyright holders must not be used to endorse or promote
|
||||||
|
products derived from this software without prior written permission
|
||||||
|
from the copyright holders.
|
||||||
|
4. The right to distribute this software or to use it for any purpose does
|
||||||
|
not give you the right to use Servicemarks (sm) or Trademarks (tm) of
|
||||||
|
the copyright holders. Use of them is covered by separate agreement
|
||||||
|
with the copyright holders.
|
||||||
|
5. If any files are modified, you must cause the modified files to carry
|
||||||
|
prominent notices stating that you changed the files and the date of
|
||||||
|
any change.
|
||||||
|
|
||||||
# Qt 5/6
|
Disclaimer
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
|
||||||
|
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 HOLDERS 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.
|
||||||
|
|
||||||
|
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
|
||||||
|
|
||||||
|
## Qt 5/6
|
||||||
|
|
||||||
Copyright (C) 2022 The Qt Company Ltd and other contributors.
|
Copyright (C) 2022 The Qt Company Ltd and other contributors.
|
||||||
Contact: https://www.qt.io/licensing
|
Contact: https://www.qt.io/licensing
|
||||||
|
|
||||||
Licensed under LGPL v3
|
Licensed under LGPL v3
|
||||||
|
|
||||||
# libnbt++
|
## libnbt++
|
||||||
|
|
||||||
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
||||||
Copyright (C) 2013, 2015 ljfa-ag
|
Copyright (C) 2013, 2015 ljfa-ag
|
||||||
@ -79,7 +133,7 @@
|
|||||||
You should have received a copy of the GNU Lesser General Public License
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# rainbow (KGuiAddons)
|
## rainbow (KGuiAddons)
|
||||||
|
|
||||||
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
||||||
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
|
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
|
||||||
@ -102,25 +156,36 @@
|
|||||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
Boston, MA 02110-1301, USA.
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
# Hoedown
|
## cmark
|
||||||
|
|
||||||
Copyright (c) 2008, Natacha Porté
|
Copyright (c) 2014, John MacFarlane
|
||||||
Copyright (c) 2011, Vicent Martí
|
|
||||||
Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software for any
|
All rights reserved.
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
Redistribution and use in source and binary forms, with or without
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
modification, are permitted provided that the following conditions are met:
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
# Batch icon set
|
* 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
|
||||||
|
OWNER 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.
|
||||||
|
|
||||||
|
## Batch icon set
|
||||||
|
|
||||||
You are free to use Batch (the "icon set") or any part thereof (the "icons")
|
You are free to use Batch (the "icon set") or any part thereof (the "icons")
|
||||||
in any personal, open-source or commercial work without obligation of payment
|
in any personal, open-source or commercial work without obligation of payment
|
||||||
@ -136,7 +201,7 @@
|
|||||||
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
||||||
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
# Material Design Icons
|
## Material Design Icons
|
||||||
|
|
||||||
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
|
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
|
||||||
with Reserved Font Name Material Design Icons.
|
with Reserved Font Name Material Design Icons.
|
||||||
@ -147,7 +212,7 @@
|
|||||||
This license is copied below, and is also available with a FAQ at:
|
This license is copied below, and is also available with a FAQ at:
|
||||||
http://scripts.sil.org/OFL
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
# Quazip
|
## Quazip
|
||||||
|
|
||||||
Copyright (C) 2005-2021 Sergey A. Tachenov
|
Copyright (C) 2005-2021 Sergey A. Tachenov
|
||||||
|
|
||||||
@ -171,53 +236,7 @@
|
|||||||
|
|
||||||
See COPYING file for the full LGPL text.
|
See COPYING file for the full LGPL text.
|
||||||
|
|
||||||
# xz-minidec
|
## launcher (`libraries/launcher`)
|
||||||
|
|
||||||
XZ decompressor
|
|
||||||
|
|
||||||
Authors: Lasse Collin <lasse.collin@tukaani.org>
|
|
||||||
Igor Pavlov <http://7-zip.org/>
|
|
||||||
|
|
||||||
This file has been put into the public domain.
|
|
||||||
You can do whatever you want with this file.
|
|
||||||
|
|
||||||
# ColumnResizer
|
|
||||||
|
|
||||||
Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted (subject to the limitations in the
|
|
||||||
disclaimer below) 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.
|
|
||||||
|
|
||||||
* The name of the contributors may not be used to endorse or
|
|
||||||
promote products derived from this software without specific prior
|
|
||||||
written permission.
|
|
||||||
|
|
||||||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
|
||||||
GRANTED BY THIS LICENSE. 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 OWNER 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.
|
|
||||||
|
|
||||||
# launcher (`libraries/launcher`)
|
|
||||||
|
|
||||||
PolyMC - Minecraft Launcher
|
PolyMC - Minecraft Launcher
|
||||||
Copyright (C) 2021-2022 PolyMC Contributors
|
Copyright (C) 2021-2022 PolyMC Contributors
|
||||||
@ -268,7 +287,7 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
# lionshead
|
## lionshead
|
||||||
|
|
||||||
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
||||||
translated to C++ laced with Qt.
|
translated to C++ laced with Qt.
|
||||||
@ -295,60 +314,26 @@
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
# optional-bare
|
## tomlplusplus
|
||||||
|
|
||||||
Code from https://github.com/martinmoene/optional-bare/
|
|
||||||
|
|
||||||
Boost Software License - Version 1.0 - August 17th, 2003
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person or organization
|
|
||||||
obtaining a copy of the software and accompanying documentation covered by
|
|
||||||
this license (the "Software") to use, reproduce, display, distribute,
|
|
||||||
execute, and transmit the Software, and to prepare derivative works of the
|
|
||||||
Software, and to permit third-parties to whom the Software is furnished to
|
|
||||||
do so, all subject to the following:
|
|
||||||
|
|
||||||
The copyright notices in the Software and this entire statement, including
|
|
||||||
the above license grant, this restriction and the following disclaimer,
|
|
||||||
must be included in all copies of the Software, in whole or in part, and
|
|
||||||
all derivative works of the Software, unless such copies or derivative
|
|
||||||
works are solely in the form of machine-executable object code generated by
|
|
||||||
a source language processor.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
||||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
||||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
# tomlc99
|
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 CK Tan
|
Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||||
https://github.com/cktan/tomlc99
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
||||||
in the Software without restriction, including without limitation the rights
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||||
copies or substantial portions of the Software.
|
Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
# O2 (Katabasis fork)
|
## O2 (Katabasis fork)
|
||||||
|
|
||||||
Copyright (c) 2012, Akos Polster
|
Copyright (c) 2012, Akos Polster
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
@ -373,3 +358,96 @@
|
|||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
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
|
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.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
## Gamemode
|
||||||
|
|
||||||
|
Copyright (c) 2017-2022, Feral Interactive
|
||||||
|
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.
|
||||||
|
* Neither the name of Feral Interactive nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
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 OWNER 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.
|
||||||
|
|
||||||
|
## gulrak/filesystem
|
||||||
|
|
||||||
|
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
## Breeze icons
|
||||||
|
|
||||||
|
Copyright (C) 2014 Uri Herrera <uri_herrera@nitrux.in> and others
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 3 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
## Oxygen Icons
|
||||||
|
|
||||||
|
The Oxygen Icon Theme
|
||||||
|
Copyright (C) 2007 Nuno Pinheiro <nuno@oxygen-icons.org>
|
||||||
|
Copyright (C) 2007 David Vignoni <david@icon-king.com>
|
||||||
|
Copyright (C) 2007 David Miller <miller@oxygen-icons.org>
|
||||||
|
Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
|
||||||
|
Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
|
||||||
|
Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
|
||||||
|
|
||||||
|
and others
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 3 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
127
README.md
127
README.md
@ -1,98 +1,103 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo" width="50%"/>
|
<picture>
|
||||||
<img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo" width="50%"/>
|
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo.svg">
|
||||||
|
<img alt="Prism Launcher" src="/program_info/org.prismlauncher.PrismLauncher.logo.svg" width="40%">
|
||||||
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
|
<p align="center">
|
||||||
|
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.<br />
|
||||||
|
<br />This is a <b>fork</b> of the MultiMC Launcher and is <b>not</b> endorsed by it.
|
||||||
|
</p>
|
||||||
|
|
||||||
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC.
|
## Installation
|
||||||
If you want to read about why this fork was created, check out [our FAQ page](https://polymc.org/wiki/overview/faq/).
|
|
||||||
<br>
|
|
||||||
|
|
||||||
# Installation
|
<a href="https://repology.org/project/prismlauncher/versions">
|
||||||
|
<img src="https://repology.org/badge/vertical-allrepos/prismlauncher.svg" alt="Packaging status" align="right">
|
||||||
|
</a>
|
||||||
|
|
||||||
- All downloads and instructions for PolyMC can be found [here](https://polymc.org/download/)
|
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download).
|
||||||
- Last build status: https://github.com/PolyMC/PolyMC/actions
|
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions).
|
||||||
|
|
||||||
|
### Development Builds
|
||||||
|
|
||||||
## Development Builds
|
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
||||||
|
|
||||||
There are per-commit development builds available [here](https://github.com/PolyMC/PolyMC/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
|
||||||
Portable builds are provided for AppImage on Linux, Windows, and macOS.
|
|
||||||
|
|
||||||
For Debian and Arch, you can use these packages for the latest development versions:
|
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
|
||||||
[](https://aur.archlinux.org/packages/polymc-git/)
|
|
||||||
[](https://mpr.makedeb.org/packages/polymc-git)
|
|
||||||
For flatpak, you can use [flathub-beta](https://discourse.flathub.org/t/how-to-use-flathub-beta/2111)
|
|
||||||
|
|
||||||
# Help & Support
|
[](https://aur.archlinux.org/packages/prismlauncher-git) [](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [](https://build.opensuse.org/project/show/home:getchoo) [](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
||||||
|
|
||||||
Feel free to create an issue if you need help. However, you might find it easier to ask in the Discord server.
|
These packages are also availiable to all the distributions based on the ones mentioned above.
|
||||||
|
|
||||||
[](https://discord.gg/xq7fxrgtMP)
|
## Community & Support
|
||||||
|
|
||||||
For people who don't want to use Discord, we have a Matrix Space which is bridged to the Discord server:
|
Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple community spaces where other community members can help you:
|
||||||
|
|
||||||
[](https://matrix.to/#/#polymc:matrix.org)
|
- **Our Discord server:**
|
||||||
|
|
||||||
If there are any issues with the space or you are using a client that does not support the feature here are the individual rooms:
|
[](https://prismlauncher.org/discord)
|
||||||
|
|
||||||
[](https://matrix.to/#/#polymc-development:matrix.org)
|
- **Our Matrix space:**
|
||||||
[](https://matrix.to/#/#polymc-discussion:matrix.org)
|
|
||||||
[](https://matrix.to/#/#polymc-github:matrix.org)
|
|
||||||
[](https://matrix.to/#/#polymc-maintainers:matrix.org)
|
|
||||||
[](https://matrix.to/#/#polymc-news:matrix.org)
|
|
||||||
[](https://matrix.to/#/#polymc-offtopic:matrix.org)
|
|
||||||
[](https://matrix.to/#/#polymc-support:matrix.org)
|
|
||||||
[](https://matrix.to/#/#polymc-voice:matrix.org)
|
|
||||||
|
|
||||||
We also have a subreddit you can post your issues and suggestions on:
|
[](https://prismlauncher.org/matrix)
|
||||||
|
|
||||||
[r/PolyMCLauncher](https://www.reddit.com/r/PolyMCLauncher/)
|
- **Our Subreddit:**
|
||||||
|
|
||||||
# Development
|
[](https://prismlauncher.org/reddit)
|
||||||
|
|
||||||
If you want to contribute to PolyMC you might find it useful to join our Discord Server or Matrix Space.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
If you want to build PolyMC yourself, check [Build Instructions](https://polymc.org/wiki/development/build-instructions/) for build instructions.
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations
|
The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
|
||||||
|
|
||||||
## Download information
|
## Building
|
||||||
|
|
||||||
To modify download information or change packaging information send a pull request or issue to the website [Here](https://github.com/PolyMC/polymc.github.io/blob/master/src/download.md)
|
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
|
||||||
|
|
||||||
|
## Sponsors & Partners
|
||||||
|
|
||||||
|
We thank all the wonderful backers over at Open Collective! Support Prism Launcher by [becoming a backer](https://opencollective.com/prismlauncher).
|
||||||
|
|
||||||
|
[](https://opencollective.com/prismlauncher#backers)
|
||||||
|
|
||||||
|
Thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
|
||||||
|
|
||||||
|
[](https://www.jetbrains.com/opensource/)
|
||||||
|
|
||||||
|
Thanks to Weblate for hosting our translation efforts.
|
||||||
|
|
||||||
|
<a href="https://hosted.weblate.org/engage/prismlauncher/">
|
||||||
|
<img src="https://hosted.weblate.org/widgets/prismlauncher/-/open-graph.png" alt="Translation status" width="300" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
Thanks to Netlify for providing us their excellent web services, as part of their [Open Source program](https://www.netlify.com/open-source/).
|
||||||
|
|
||||||
|
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" /> </a>
|
||||||
|
|
||||||
|
Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
|
||||||
|
|
||||||
|
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
|
||||||
|
|
||||||
## Forking/Redistributing/Custom builds policy
|
## Forking/Redistributing/Custom builds policy
|
||||||
|
|
||||||
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
||||||
- Make it clear that your fork is not PolyMC and is not endorsed by or affiliated with the PolyMC project (https://polymc.org).
|
|
||||||
- Go through [CMakeLists.txt](CMakeLists.txt) and change PolyMC's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>).
|
||||||
|
- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
||||||
|
|
||||||
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
||||||
|
|
||||||
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
||||||
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
|
||||||
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
||||||
|
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
||||||
|
|
||||||
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
|
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
|
||||||
|
|
||||||
|
## License [](LICENSE)
|
||||||
|
|
||||||
All launcher code is available under the GPL-3.0-only license.
|
All launcher code is available under the GPL-3.0-only license.
|
||||||
|
|
||||||
The logo and related assets are under the CC BY-SA 4.0 license.
|
The logo and related assets are under the CC BY-SA 4.0 license.
|
||||||
|
|
||||||
## Sponsors
|
|
||||||
Thank you to all our generous backers over at Open Collective! Support PolyMC by [becoming a backer](https://opencollective.com/polymc).
|
|
||||||
|
|
||||||
[](https://opencollective.com/polymc#backers)
|
|
||||||
|
|
||||||
Also, thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
|
|
||||||
|
|
||||||
[](https://www.jetbrains.com/opensource/)
|
|
||||||
|
|
||||||
Additionally, thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
|
|
||||||
|
|
||||||
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
|
|
||||||
|
@ -42,12 +42,14 @@ Config::Config()
|
|||||||
{
|
{
|
||||||
// Name and copyright
|
// Name and copyright
|
||||||
LAUNCHER_NAME = "@Launcher_Name@";
|
LAUNCHER_NAME = "@Launcher_Name@";
|
||||||
|
LAUNCHER_APP_BINARY_NAME = "@Launcher_APP_BINARY_NAME@";
|
||||||
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
||||||
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
||||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||||
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
||||||
LAUNCHER_GIT = "@Launcher_Git@";
|
LAUNCHER_GIT = "@Launcher_Git@";
|
||||||
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
||||||
|
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
|
||||||
|
|
||||||
USER_AGENT = "@Launcher_UserAgent@";
|
USER_AGENT = "@Launcher_UserAgent@";
|
||||||
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
||||||
@ -55,10 +57,9 @@ Config::Config()
|
|||||||
// Version information
|
// Version information
|
||||||
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
||||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||||
VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
|
|
||||||
VERSION_BUILD = @Launcher_VERSION_BUILD@;
|
|
||||||
|
|
||||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||||
|
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
|
||||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||||
|
|
||||||
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
||||||
@ -75,7 +76,9 @@ Config::Config()
|
|||||||
|
|
||||||
// Assume that builds outside of Git repos are "stable"
|
// Assume that builds outside of Git repos are "stable"
|
||||||
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|
||||||
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND"))
|
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND")
|
||||||
|
|| GIT_REFSPEC == QStringLiteral("")
|
||||||
|
|| GIT_TAG == QStringLiteral("GIT-NOTFOUND"))
|
||||||
{
|
{
|
||||||
GIT_REFSPEC = "refs/heads/stable";
|
GIT_REFSPEC = "refs/heads/stable";
|
||||||
GIT_TAG = versionString();
|
GIT_TAG = versionString();
|
||||||
@ -85,7 +88,7 @@ Config::Config()
|
|||||||
{
|
{
|
||||||
VERSION_CHANNEL = GIT_REFSPEC;
|
VERSION_CHANNEL = GIT_REFSPEC;
|
||||||
VERSION_CHANNEL.remove("refs/heads/");
|
VERSION_CHANNEL.remove("refs/heads/");
|
||||||
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0) {
|
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) {
|
||||||
UPDATER_ENABLED = true;
|
UPDATER_ENABLED = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +101,6 @@ Config::Config()
|
|||||||
VERSION_CHANNEL = "unknown";
|
VERSION_CHANNEL = "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
VERSION_STR = "@Launcher_VERSION_STRING@";
|
|
||||||
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@";
|
||||||
@ -116,7 +118,7 @@ Config::Config()
|
|||||||
|
|
||||||
QString Config::versionString() const
|
QString Config::versionString() const
|
||||||
{
|
{
|
||||||
return QString("%1.%2.%3").arg(VERSION_MAJOR).arg(VERSION_MINOR).arg(VERSION_HOTFIX);
|
return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Config::printableVersionString() const
|
QString Config::printableVersionString() const
|
||||||
@ -128,11 +130,5 @@ QString Config::printableVersionString() const
|
|||||||
{
|
{
|
||||||
vstr += "-" + VERSION_CHANNEL;
|
vstr += "-" + VERSION_CHANNEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if a build number is set, also add it to the end
|
|
||||||
if(VERSION_BUILD >= 0)
|
|
||||||
{
|
|
||||||
vstr += "+build." + QString::number(VERSION_BUILD);
|
|
||||||
}
|
|
||||||
return vstr;
|
return vstr;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* 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
|
||||||
@ -36,6 +37,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief The Config class holds all the build-time information passed from the build system.
|
* \brief The Config class holds all the build-time information passed from the build system.
|
||||||
@ -44,21 +46,19 @@ class Config {
|
|||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
QString LAUNCHER_NAME;
|
QString LAUNCHER_NAME;
|
||||||
|
QString LAUNCHER_APP_BINARY_NAME;
|
||||||
QString LAUNCHER_DISPLAYNAME;
|
QString LAUNCHER_DISPLAYNAME;
|
||||||
QString LAUNCHER_COPYRIGHT;
|
QString LAUNCHER_COPYRIGHT;
|
||||||
QString LAUNCHER_DOMAIN;
|
QString LAUNCHER_DOMAIN;
|
||||||
QString LAUNCHER_CONFIGFILE;
|
QString LAUNCHER_CONFIGFILE;
|
||||||
QString LAUNCHER_GIT;
|
QString LAUNCHER_GIT;
|
||||||
QString LAUNCHER_DESKTOPFILENAME;
|
QString LAUNCHER_DESKTOPFILENAME;
|
||||||
|
QString LAUNCHER_SVGFILENAME;
|
||||||
|
|
||||||
/// The major version number.
|
/// The major version number.
|
||||||
int VERSION_MAJOR;
|
int VERSION_MAJOR;
|
||||||
/// The minor version number.
|
/// The minor version number.
|
||||||
int VERSION_MINOR;
|
int VERSION_MINOR;
|
||||||
/// The hotfix number.
|
|
||||||
int VERSION_HOTFIX;
|
|
||||||
/// The build number.
|
|
||||||
int VERSION_BUILD;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version channel
|
* The version channel
|
||||||
@ -71,6 +71,9 @@ class Config {
|
|||||||
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||||
QString BUILD_PLATFORM;
|
QString BUILD_PLATFORM;
|
||||||
|
|
||||||
|
/// A string containing the build timestamp
|
||||||
|
QString BUILD_DATE;
|
||||||
|
|
||||||
/// URL for the updater's channel
|
/// URL for the updater's channel
|
||||||
QString UPDATER_BASE;
|
QString UPDATER_BASE;
|
||||||
|
|
||||||
@ -95,9 +98,6 @@ class Config {
|
|||||||
/// The git refspec of this build
|
/// The git refspec of this build
|
||||||
QString GIT_REFSPEC;
|
QString GIT_REFSPEC;
|
||||||
|
|
||||||
/// This is printed on start to standard output
|
|
||||||
QString VERSION_STR;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to fetch the news RSS feed.
|
* This is used to fetch the news RSS feed.
|
||||||
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
|
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
|
||||||
@ -144,8 +144,8 @@ class Config {
|
|||||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
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.polymc.org/fmllibs/";
|
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
||||||
QString TRANSLATIONS_BASE_URL = "https://i18n.polymc.org/";
|
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
||||||
|
|
||||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||||
|
|
||||||
@ -162,6 +162,9 @@ class Config {
|
|||||||
|
|
||||||
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
|
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
|
||||||
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
|
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
|
||||||
|
QStringList MODRINTH_MRPACK_HOSTS{"cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com"};
|
||||||
|
|
||||||
|
QString FLAME_BASE_URL = "https://api.curseforge.com/v1";
|
||||||
|
|
||||||
QString versionString() const;
|
QString versionString() const;
|
||||||
/**
|
/**
|
||||||
|
@ -44,5 +44,28 @@
|
|||||||
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
|
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
|
||||||
<key>SUFeedURL</key>
|
<key>SUFeedURL</key>
|
||||||
<string>${MACOSX_SPARKLE_UPDATE_FEED_URL}</string>
|
<string>${MACOSX_SPARKLE_UPDATE_FEED_URL}</string>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>zip</string>
|
||||||
|
<string>mrpack</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Prism Launcher instance</string>
|
||||||
|
<key>CFBundleTypeOSTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>TEXT</string>
|
||||||
|
<string>utxt</string>
|
||||||
|
<string>TUTX</string>
|
||||||
|
<string>****</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Alternate</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
15
default.nix
15
default.nix
@ -1 +1,14 @@
|
|||||||
(import nix/flake-compat.nix).defaultNix
|
(
|
||||||
|
import
|
||||||
|
(
|
||||||
|
let
|
||||||
|
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||||
|
in
|
||||||
|
fetchTarball {
|
||||||
|
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
|
||||||
|
120
flake.lock
generated
120
flake.lock
generated
@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1650374568,
|
"lastModified": 1673956053,
|
||||||
"narHash": "sha256-Z+s0J8/r907g149rllvwhb4pKi8Wam5ij0st8PwAh+E=",
|
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "b4a34015c698c7793d592d66adbab377907a2be8",
|
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -16,29 +16,83 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1683560683,
|
||||||
|
"narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "006c75898cf814ef9497252b022e91c946ba8e17",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1667395993,
|
||||||
|
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||||
|
"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": {
|
||||||
"lastModified": 1650031308,
|
"lastModified": 1650031308,
|
||||||
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
||||||
"owner": "PolyMC",
|
"owner": "PrismLauncher",
|
||||||
"repo": "libnbtplusplus",
|
"repo": "libnbtplusplus",
|
||||||
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "PolyMC",
|
"owner": "PrismLauncher",
|
||||||
"repo": "libnbtplusplus",
|
"repo": "libnbtplusplus",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1658119717,
|
"lastModified": 1685012353,
|
||||||
"narHash": "sha256-4upOZIQQ7Bc4CprqnHsKnqYfw+arJeAuU+QcpjYBXW0=",
|
"narHash": "sha256-U3oOge4cHnav8OLGdRVhL45xoRj4Ppd+It6nPC9nNIU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9eb60f25aff0d2218c848dd4574a0ab5e296cabe",
|
"rev": "aeb75dba965e790de427b73315d5addf91a54955",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -48,11 +102,59 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "lib",
|
||||||
|
"lastModified": 1682879489,
|
||||||
|
"narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "lib",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pre-commit-hooks": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1684842236,
|
||||||
|
"narHash": "sha256-rYWsIXHvNhVQ15RQlBUv67W3YnM+Pd+DuXGMvCBq2IE=",
|
||||||
|
"owner": "cachix",
|
||||||
|
"repo": "pre-commit-hooks.nix",
|
||||||
|
"rev": "61e567d6497bc9556f391faebe5e410e6623217f",
|
||||||
|
"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",
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs",
|
||||||
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
48
flake.nix
48
flake.nix
@ -3,35 +3,25 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
libnbtplusplus = { url = "github:PolyMC/libnbtplusplus"; flake = false; };
|
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 = {
|
||||||
|
url = "github:PrismLauncher/libnbtplusplus";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, libnbtplusplus, ... }:
|
outputs = inputs:
|
||||||
let
|
inputs.flake-parts.lib.mkFlake
|
||||||
# User-friendly version number.
|
{inherit inputs;}
|
||||||
version = builtins.substring 0 8 self.lastModifiedDate;
|
{imports = [./nix];};
|
||||||
|
|
||||||
# Supported systems (qtbase is currently broken for "aarch64-darwin")
|
|
||||||
supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" ];
|
|
||||||
|
|
||||||
# Helper function to generate an attrset '{ x86_64-linux = f "x86_64-linux"; ... }'.
|
|
||||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
|
||||||
|
|
||||||
# Nixpkgs instantiated for supported systems.
|
|
||||||
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
|
||||||
|
|
||||||
packagesFn = pkgs: rec {
|
|
||||||
polymc = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
|
|
||||||
polymc-qt6 = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
packages = forAllSystems (system:
|
|
||||||
let packages = packagesFn pkgs.${system}; in
|
|
||||||
packages // { default = packages.polymc; }
|
|
||||||
);
|
|
||||||
|
|
||||||
overlay = final: packagesFn;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
85
flatpak/org.prismlauncher.PrismLauncher.yml
Normal file
85
flatpak/org.prismlauncher.PrismLauncher.yml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
id: org.prismlauncher.PrismLauncher
|
||||||
|
runtime: org.kde.Platform
|
||||||
|
runtime-version: "5.15-22.08"
|
||||||
|
sdk: org.kde.Sdk
|
||||||
|
sdk-extensions:
|
||||||
|
- org.freedesktop.Sdk.Extension.openjdk17
|
||||||
|
- org.freedesktop.Sdk.Extension.openjdk8
|
||||||
|
add-extensions:
|
||||||
|
com.valvesoftware.Steam.Utility.gamescope:
|
||||||
|
version: stable
|
||||||
|
add-ld-path: lib
|
||||||
|
no-autodownload: true
|
||||||
|
autodelete: false
|
||||||
|
directory: utils/gamescope
|
||||||
|
|
||||||
|
command: prismlauncher
|
||||||
|
finish-args:
|
||||||
|
- --share=ipc
|
||||||
|
- --socket=x11
|
||||||
|
- --socket=wayland
|
||||||
|
- --device=all
|
||||||
|
- --share=network
|
||||||
|
- --socket=pulseaudio
|
||||||
|
# for Discord RPC mods
|
||||||
|
- --filesystem=xdg-run/app/com.discordapp.Discord:create
|
||||||
|
# Mod drag&drop
|
||||||
|
- --filesystem=xdg-download:ro
|
||||||
|
|
||||||
|
modules:
|
||||||
|
- name: prismlauncher
|
||||||
|
buildsystem: cmake-ninja
|
||||||
|
config-opts:
|
||||||
|
- -DLauncher_BUILD_PLATFORM=flatpak
|
||||||
|
- -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
- -DLauncher_QT_VERSION_MAJOR=5
|
||||||
|
build-options:
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
||||||
|
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
||||||
|
sources:
|
||||||
|
- type: dir
|
||||||
|
path: ../
|
||||||
|
builddir: true
|
||||||
|
- 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: xrandr
|
||||||
|
buildsystem: autotools
|
||||||
|
sources:
|
||||||
|
- type: archive
|
||||||
|
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz
|
||||||
|
sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762
|
||||||
|
cleanup: [/share/man, /bin/xkeystone]
|
||||||
|
- name: gamemode
|
||||||
|
buildsystem: meson
|
||||||
|
config-opts:
|
||||||
|
- -Dwith-sd-bus-provider=no-daemon
|
||||||
|
- -Dwith-examples=false
|
||||||
|
post-install:
|
||||||
|
# gamemoderun is installed for users who want to use wrapper commands
|
||||||
|
# post-install is running inside the build dir, we need it from the source though
|
||||||
|
- install -Dm755 ../data/gamemoderun -t /app/bin
|
||||||
|
sources:
|
||||||
|
- type: git
|
||||||
|
url: https://github.com/FeralInteractive/gamemode
|
||||||
|
tag: "1.7"
|
||||||
|
commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9
|
||||||
|
- name: enhance
|
||||||
|
buildsystem: simple
|
||||||
|
build-commands:
|
||||||
|
- mkdir -p /app/utils/gamescope
|
||||||
|
- install -Dm755 prime-run /app/bin/prime-run
|
||||||
|
- mv /app/bin/prismlauncher /app/bin/prismrun
|
||||||
|
- install -Dm755 prismlauncher /app/bin/prismlauncher
|
||||||
|
sources:
|
||||||
|
- type: file
|
||||||
|
path: ../flatpak/prime-run
|
||||||
|
- type: file
|
||||||
|
path: ../flatpak/prismlauncher
|
4
flatpak/prime-run
Normal file
4
flatpak/prime-run
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia
|
||||||
|
exec "$@"
|
11
flatpak/prismlauncher
Normal file
11
flatpak/prismlauncher
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# discord RPC
|
||||||
|
for i in {0..9}; do
|
||||||
|
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
|
||||||
|
done
|
||||||
|
|
||||||
|
export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
|
||||||
|
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/"
|
||||||
|
|
||||||
|
exec /app/bin/prismrun "$@"
|
@ -1,8 +1,14 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only AND Apache-2.0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
|
* Copyright (C) 2022 Lenny McLennington <lenny@sneed.church>
|
||||||
|
* Copyright (C) 2022 Tayou <tayou@gmx.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (C) 2023 Rachel Powers <508861+Ryex@users.noreply.github.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
|
||||||
@ -37,10 +43,15 @@
|
|||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
|
|
||||||
|
#include "DataMigrationTask.h"
|
||||||
#include "net/PasteUpload.h"
|
#include "net/PasteUpload.h"
|
||||||
|
#include "pathmatcher/MultiMatcher.h"
|
||||||
|
#include "pathmatcher/SimplePrefixMatcher.h"
|
||||||
|
#include "settings/INIFile.h"
|
||||||
#include "ui/MainWindow.h"
|
#include "ui/MainWindow.h"
|
||||||
#include "ui/InstanceWindow.h"
|
#include "ui/InstanceWindow.h"
|
||||||
|
|
||||||
|
#include "ui/dialogs/ProgressDialog.h"
|
||||||
#include "ui/instanceview/AccessibleInstanceView.h"
|
#include "ui/instanceview/AccessibleInstanceView.h"
|
||||||
|
|
||||||
#include "ui/pages/BasePageProvider.h"
|
#include "ui/pages/BasePageProvider.h"
|
||||||
@ -54,26 +65,26 @@
|
|||||||
#include "ui/pages/global/APIPage.h"
|
#include "ui/pages/global/APIPage.h"
|
||||||
#include "ui/pages/global/CustomCommandsPage.h"
|
#include "ui/pages/global/CustomCommandsPage.h"
|
||||||
|
|
||||||
#include "ui/themes/ITheme.h"
|
|
||||||
#include "ui/themes/SystemTheme.h"
|
|
||||||
#include "ui/themes/DarkTheme.h"
|
|
||||||
#include "ui/themes/BrightTheme.h"
|
|
||||||
#include "ui/themes/CustomTheme.h"
|
|
||||||
|
|
||||||
#include "ui/setupwizard/SetupWizard.h"
|
#include "ui/setupwizard/SetupWizard.h"
|
||||||
#include "ui/setupwizard/LanguageWizardPage.h"
|
#include "ui/setupwizard/LanguageWizardPage.h"
|
||||||
#include "ui/setupwizard/JavaWizardPage.h"
|
#include "ui/setupwizard/JavaWizardPage.h"
|
||||||
#include "ui/setupwizard/PasteWizardPage.h"
|
#include "ui/setupwizard/PasteWizardPage.h"
|
||||||
|
#include "ui/setupwizard/ThemeWizardPage.h"
|
||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
#include "ui/pagedialog/PageDialog.h"
|
#include "ui/pagedialog/PageDialog.h"
|
||||||
|
|
||||||
|
#include "ui/themes/ThemeManager.h"
|
||||||
|
|
||||||
#include "ApplicationMessage.h"
|
#include "ApplicationMessage.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <QFileOpenEvent>
|
||||||
#include <QAccessible>
|
#include <QAccessible>
|
||||||
|
#include <QCommandLineParser>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
@ -87,6 +98,7 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
|
|
||||||
#include "InstanceList.h"
|
#include "InstanceList.h"
|
||||||
|
#include "MTPixmapCache.h"
|
||||||
|
|
||||||
#include <minecraft/auth/AccountList.h>
|
#include <minecraft/auth/AccountList.h>
|
||||||
#include "icons/IconList.h"
|
#include "icons/IconList.h"
|
||||||
@ -94,7 +106,7 @@
|
|||||||
|
|
||||||
#include "java/JavaUtils.h"
|
#include "java/JavaUtils.h"
|
||||||
|
|
||||||
#include "updater/UpdateChecker.h"
|
#include "updater/ExternalUpdater.h"
|
||||||
|
|
||||||
#include "tools/JProfiler.h"
|
#include "tools/JProfiler.h"
|
||||||
#include "tools/JVisualVM.h"
|
#include "tools/JVisualVM.h"
|
||||||
@ -106,13 +118,22 @@
|
|||||||
#include "translations/TranslationsModel.h"
|
#include "translations/TranslationsModel.h"
|
||||||
#include "meta/Index.h"
|
#include "meta/Index.h"
|
||||||
|
|
||||||
#include <Commandline.h>
|
|
||||||
#include <FileSystem.h>
|
#include <FileSystem.h>
|
||||||
#include <DesktopServices.h>
|
#include <DesktopServices.h>
|
||||||
#include <LocalPeer.h>
|
#include <LocalPeer.h>
|
||||||
|
|
||||||
#include <sys.h>
|
#include <sys.h>
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include "gamemode_client.h"
|
||||||
|
#include "MangoHud.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
#include "updater/MacSparkleUpdater.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
@ -127,26 +148,18 @@
|
|||||||
|
|
||||||
static const QLatin1String liveCheckFile("live.check");
|
static const QLatin1String liveCheckFile("live.check");
|
||||||
|
|
||||||
using namespace Commandline;
|
PixmapCache* PixmapCache::s_instance = nullptr;
|
||||||
|
|
||||||
#define MACOS_HINT "If you are on macOS Sierra, you might have to move the app to your /Applications or ~/Applications folder. "\
|
|
||||||
"This usually fixes the problem and you can move the application elsewhere afterwards.\n"\
|
|
||||||
"\n"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/** This is used so that we can output to the log file in addition to the CLI. */
|
||||||
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
{
|
{
|
||||||
const char *levels = "DWCFIS";
|
static std::mutex loggerMutex;
|
||||||
const QString format("%1 %2 %3\n");
|
const std::lock_guard<std::mutex> lock(loggerMutex); // synchronized, QFile logFile is not thread-safe
|
||||||
|
|
||||||
qint64 msecstotal = APPLICATION->timeSinceStart();
|
QString out = qFormatLogMessage(type, context, msg);
|
||||||
qint64 seconds = msecstotal / 1000;
|
out += QChar::LineFeed;
|
||||||
qint64 msecs = msecstotal % 1000;
|
|
||||||
QString foo;
|
|
||||||
char buf[1025] = {0};
|
|
||||||
::snprintf(buf, 1024, "%5lld.%03lld", seconds, msecs);
|
|
||||||
|
|
||||||
QString out = format.arg(buf).arg(levels[type]).arg(msg);
|
|
||||||
|
|
||||||
APPLICATION->logFile->write(out.toUtf8());
|
APPLICATION->logFile->write(out.toUtf8());
|
||||||
APPLICATION->logFile->flush();
|
APPLICATION->logFile->flush();
|
||||||
@ -154,45 +167,6 @@ void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QSt
|
|||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getIdealPlatform(QString currentPlatform) {
|
|
||||||
auto info = Sys::getKernelInfo();
|
|
||||||
switch(info.kernelType) {
|
|
||||||
case Sys::KernelType::Darwin: {
|
|
||||||
if(info.kernelMajor >= 17) {
|
|
||||||
// macOS 10.13 or newer
|
|
||||||
return "osx64-5.15.2";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// macOS 10.12 or older
|
|
||||||
return "osx64";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case Sys::KernelType::Windows: {
|
|
||||||
// FIXME: 5.15.2 is not stable on Windows, due to a large number of completely unpredictable and hard to reproduce issues
|
|
||||||
break;
|
|
||||||
/*
|
|
||||||
if(info.kernelMajor == 6 && info.kernelMinor >= 1) {
|
|
||||||
// Windows 7
|
|
||||||
return "win32-5.15.2";
|
|
||||||
}
|
|
||||||
else if (info.kernelMajor > 6) {
|
|
||||||
// Above Windows 7
|
|
||||||
return "win32-5.15.2";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Below Windows 7
|
|
||||||
return "win32";
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
case Sys::KernelType::Undetermined:
|
|
||||||
case Sys::KernelType::Linux: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentPlatform;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||||
@ -225,7 +199,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
|
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
|
||||||
setApplicationName(BuildConfig.LAUNCHER_NAME);
|
setApplicationName(BuildConfig.LAUNCHER_NAME);
|
||||||
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
|
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
|
||||||
setApplicationVersion(BuildConfig.printableVersionString());
|
setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
|
||||||
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
|
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
|
||||||
startTime = QDateTime::currentDateTime();
|
startTime = QDateTime::currentDateTime();
|
||||||
|
|
||||||
@ -233,80 +207,39 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
this->setQuitOnLastWindowClosed(false);
|
this->setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
// Commandline parsing
|
// Commandline parsing
|
||||||
QHash<QString, QVariant> args;
|
QCommandLineParser parser;
|
||||||
{
|
parser.setApplicationDescription(BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||||
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
|
|
||||||
|
|
||||||
// --help
|
parser.addOptions({
|
||||||
parser.addSwitch("help");
|
{{"d", "dir"}, "Use a custom path as application root (use '.' for current directory)", "directory"},
|
||||||
parser.addShortOpt("help", 'h');
|
{{"l", "launch"}, "Launch the specified instance (by instance ID)", "instance"},
|
||||||
parser.addDocumentation("help", "Display this help and exit.");
|
{{"s", "server"}, "Join the specified server on launch (only valid in combination with --launch)", "address"},
|
||||||
// --version
|
{{"a", "profile"}, "Use the account specified by its profile name (only valid in combination with --launch)", "profile"},
|
||||||
parser.addSwitch("version");
|
{"alive", "Write a small '" + liveCheckFile + "' file after the launcher starts"},
|
||||||
parser.addShortOpt("version", 'V');
|
{{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"},
|
||||||
parser.addDocumentation("version", "Display program version and exit.");
|
{"show", "Opens the window for the specified instance (by instance ID)", "show"}
|
||||||
// --dir
|
});
|
||||||
parser.addOption("dir");
|
parser.addHelpOption();
|
||||||
parser.addShortOpt("dir", 'd');
|
parser.addVersionOption();
|
||||||
parser.addDocumentation("dir", "Use the supplied folder as application root instead of the binary location (use '.' for current)");
|
|
||||||
// --launch
|
|
||||||
parser.addOption("launch");
|
|
||||||
parser.addShortOpt("launch", 'l');
|
|
||||||
parser.addDocumentation("launch", "Launch the specified instance (by instance ID)");
|
|
||||||
// --server
|
|
||||||
parser.addOption("server");
|
|
||||||
parser.addShortOpt("server", 's');
|
|
||||||
parser.addDocumentation("server", "Join the specified server on launch (only valid in combination with --launch)");
|
|
||||||
// --profile
|
|
||||||
parser.addOption("profile");
|
|
||||||
parser.addShortOpt("profile", 'a');
|
|
||||||
parser.addDocumentation("profile", "Use the account specified by its profile name (only valid in combination with --launch)");
|
|
||||||
// --alive
|
|
||||||
parser.addSwitch("alive");
|
|
||||||
parser.addDocumentation("alive", "Write a small '" + liveCheckFile + "' file after the launcher starts");
|
|
||||||
// --import
|
|
||||||
parser.addOption("import");
|
|
||||||
parser.addShortOpt("import", 'I');
|
|
||||||
parser.addDocumentation("import", "Import instance from specified zip (local path or URL)");
|
|
||||||
|
|
||||||
// parse the arguments
|
parser.process(arguments());
|
||||||
try
|
|
||||||
{
|
|
||||||
args = parser.parse(arguments());
|
|
||||||
}
|
|
||||||
catch (const ParsingError &e)
|
|
||||||
{
|
|
||||||
std::cerr << "CommandLineError: " << e.what() << std::endl;
|
|
||||||
if(argc > 0)
|
|
||||||
std::cerr << "Try '" << argv[0] << " -h' to get help on command line parameters."
|
|
||||||
<< std::endl;
|
|
||||||
m_status = Application::Failed;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// display help and exit
|
m_instanceIdToLaunch = parser.value("launch");
|
||||||
if (args["help"].toBool())
|
m_serverToJoin = parser.value("server");
|
||||||
{
|
m_profileToUse = parser.value("profile");
|
||||||
std::cout << qPrintable(parser.compileHelp(arguments()[0]));
|
m_liveCheck = parser.isSet("alive");
|
||||||
m_status = Application::Succeeded;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// display version and exit
|
m_instanceIdToShowWindowOf = parser.value("show");
|
||||||
if (args["version"].toBool())
|
|
||||||
{
|
for (auto zip_path : parser.values("import")){
|
||||||
std::cout << "Version " << BuildConfig.printableVersionString().toStdString() << std::endl;
|
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
||||||
std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
|
}
|
||||||
m_status = Application::Succeeded;
|
|
||||||
return;
|
// treat unspecified positional arguments as import urls
|
||||||
}
|
for (auto zip_path : parser.positionalArguments()) {
|
||||||
|
m_zipsToImport.append(QUrl::fromLocalFile(QFileInfo(zip_path).absoluteFilePath()));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_instanceIdToLaunch = args["launch"].toString();
|
|
||||||
m_serverToJoin = args["server"].toString();
|
|
||||||
m_profileToUse = args["profile"].toString();
|
|
||||||
m_liveCheck = args["alive"].toBool();
|
|
||||||
m_zipToImport = args["import"].toUrl();
|
|
||||||
|
|
||||||
// error if --launch is missing with --server or --profile
|
// error if --launch is missing with --server or --profile
|
||||||
if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty())
|
if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty())
|
||||||
@ -321,7 +254,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Root path is used for updates and portable data
|
// Root path is used for updates and portable data
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr
|
QDir foo(FS::PathCombine(binPath, "..")); // typically portable-root or /usr
|
||||||
m_rootPath = foo.absolutePath();
|
m_rootPath = foo.absolutePath();
|
||||||
#elif defined(Q_OS_WIN32)
|
#elif defined(Q_OS_WIN32)
|
||||||
@ -337,7 +270,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
QString adjustedBy;
|
QString adjustedBy;
|
||||||
QString dataPath;
|
QString dataPath;
|
||||||
// change folder
|
// change folder
|
||||||
QString dirParam = args["dir"].toString();
|
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
|
||||||
@ -351,20 +284,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
dataPath = foo.absolutePath();
|
dataPath = foo.absolutePath();
|
||||||
adjustedBy = "Persistent data path";
|
adjustedBy = "Persistent data path";
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
|
||||||
// TODO: this should be removed in a future version
|
|
||||||
// TODO: provide a migration path similar to macOS migration
|
|
||||||
QDir bar(FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), "polymc"));
|
|
||||||
if (bar.exists()) {
|
|
||||||
dataPath = bar.absolutePath();
|
|
||||||
adjustedBy = "Legacy data path";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef Q_OS_MACOS
|
#ifndef Q_OS_MACOS
|
||||||
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
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;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -376,9 +300,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
QString(
|
QString(
|
||||||
"The launcher data folder could not be created.\n"
|
"The launcher data folder could not be created.\n"
|
||||||
"\n"
|
"\n"
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
MACOS_HINT
|
|
||||||
#endif
|
|
||||||
"Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n"
|
"Make sure you have the right permissions to the launcher data folder and any folder needed to access it.\n"
|
||||||
"(%1)\n"
|
"(%1)\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -394,9 +315,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
QString(
|
QString(
|
||||||
"The launcher data folder could not be opened.\n"
|
"The launcher data folder could not be opened.\n"
|
||||||
"\n"
|
"\n"
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
MACOS_HINT
|
|
||||||
#endif
|
|
||||||
"Make sure you have the right permissions to the launcher data folder.\n"
|
"Make sure you have the right permissions to the launcher data folder.\n"
|
||||||
"(%1)\n"
|
"(%1)\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -407,7 +325,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Establish the mechanism for communication with an already running PolyMC that uses the same data path.
|
* Establish the mechanism for communication with an already running PrismLauncher that uses the same data path.
|
||||||
* If there is one, tell it what the user actually wanted to do and exit.
|
* If there is one, tell it what the user actually wanted to do and exit.
|
||||||
* We want to initialize this before logging to avoid messing with the log of a potential already running copy.
|
* We want to initialize this before logging to avoid messing with the log of a potential already running copy.
|
||||||
*/
|
*/
|
||||||
@ -425,12 +343,14 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
activate.command = "activate";
|
activate.command = "activate";
|
||||||
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
m_peerInstance->sendMessage(activate.serialize(), timeout);
|
||||||
|
|
||||||
if(!m_zipToImport.isEmpty())
|
if(!m_zipsToImport.isEmpty())
|
||||||
{
|
{
|
||||||
ApplicationMessage import;
|
for (auto zip_url : m_zipsToImport) {
|
||||||
import.command = "import";
|
ApplicationMessage import;
|
||||||
import.args.insert("path", m_zipToImport.toString());
|
import.command = "import";
|
||||||
m_peerInstance->sendMessage(import.serialize(), timeout);
|
import.args.insert("path", zip_url.toString());
|
||||||
|
m_peerInstance->sendMessage(import.serialize(), timeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -477,9 +397,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
QString(
|
QString(
|
||||||
"The launcher couldn't create a log file - the data folder is not writable.\n"
|
"The launcher couldn't create a log file - the data folder is not writable.\n"
|
||||||
"\n"
|
"\n"
|
||||||
#if defined(Q_OS_MAC)
|
|
||||||
MACOS_HINT
|
|
||||||
#endif
|
|
||||||
"Make sure you have write permissions to the data folder.\n"
|
"Make sure you have write permissions to the data folder.\n"
|
||||||
"(%1)\n"
|
"(%1)\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -489,9 +406,67 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qInstallMessageHandler(appDebugOutput);
|
qInstallMessageHandler(appDebugOutput);
|
||||||
|
|
||||||
|
qSetMessagePattern(
|
||||||
|
"%{time process}" " "
|
||||||
|
"%{if-debug}D%{endif}" "%{if-info}I%{endif}" "%{if-warning}W%{endif}" "%{if-critical}C%{endif}" "%{if-fatal}F%{endif}"
|
||||||
|
" " "|" " "
|
||||||
|
"%{if-category}[%{category}]: %{endif}"
|
||||||
|
"%{message}");
|
||||||
|
|
||||||
|
bool foundLoggingRules = false;
|
||||||
|
|
||||||
|
auto logRulesFile = QStringLiteral("qtlogging.ini");
|
||||||
|
auto logRulesPath = FS::PathCombine(dataPath, logRulesFile);
|
||||||
|
|
||||||
|
qDebug() << "Testing" << logRulesPath << "...";
|
||||||
|
foundLoggingRules = QFile::exists(logRulesPath);
|
||||||
|
|
||||||
|
// search the dataPath()
|
||||||
|
// seach app data standard path
|
||||||
|
if(!foundLoggingRules && !isPortable() && dirParam.isEmpty()) {
|
||||||
|
logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile));
|
||||||
|
if(!logRulesPath.isEmpty()) {
|
||||||
|
qDebug() << "Found" << logRulesPath << "...";
|
||||||
|
foundLoggingRules = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// seach root path
|
||||||
|
if(!foundLoggingRules) {
|
||||||
|
logRulesPath = FS::PathCombine(m_rootPath, logRulesFile);
|
||||||
|
qDebug() << "Testing" << logRulesPath << "...";
|
||||||
|
foundLoggingRules = QFile::exists(logRulesPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(foundLoggingRules) {
|
||||||
|
// load and set logging rules
|
||||||
|
qDebug() << "Loading logging rules from:" << logRulesPath;
|
||||||
|
QSettings loggingRules(logRulesPath, QSettings::IniFormat);
|
||||||
|
loggingRules.beginGroup("Rules");
|
||||||
|
QStringList rule_names = loggingRules.childKeys();
|
||||||
|
QStringList rules;
|
||||||
|
qDebug() << "Setting log rules:";
|
||||||
|
for (auto rule_name : rule_names) {
|
||||||
|
auto rule = QString("%1=%2").arg(rule_name).arg(loggingRules.value(rule_name).toString());
|
||||||
|
rules.append(rule);
|
||||||
|
qDebug() << " " << rule;
|
||||||
|
}
|
||||||
|
auto rules_str = rules.join("\n");
|
||||||
|
QLoggingCategory::setFilterRules(rules_str);
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "<> Log initialized.";
|
qDebug() << "<> Log initialized.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
bool migrated = false;
|
||||||
|
|
||||||
|
if (!migrated)
|
||||||
|
migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../PolyMC"), "PolyMC", "polymc.cfg");
|
||||||
|
if (!migrated)
|
||||||
|
migrated = handleDataMigration(dataPath, FS::PathCombine(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation), "../../multimc"), "MultiMC", "multimc.cfg");
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
|
qDebug() << BuildConfig.LAUNCHER_DISPLAYNAME << ", (c) 2013-2021 " << BuildConfig.LAUNCHER_COPYRIGHT;
|
||||||
@ -541,15 +516,13 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// Initialize application settings
|
// Initialize application settings
|
||||||
{
|
{
|
||||||
m_settings.reset(new INISettingsObject(BuildConfig.LAUNCHER_CONFIGFILE, this));
|
// Provide a fallback for migration from PolyMC
|
||||||
// Updates
|
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
|
||||||
// Multiple channels are separated by spaces
|
|
||||||
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
|
||||||
m_settings->registerSetting("AutoUpdate", true);
|
|
||||||
|
|
||||||
// Theming
|
// Theming
|
||||||
m_settings->registerSetting("IconTheme", QString("pe_colored"));
|
m_settings->registerSetting("IconTheme", QString("pe_colored"));
|
||||||
m_settings->registerSetting("ApplicationTheme", QString("system"));
|
m_settings->registerSetting("ApplicationTheme", QString());
|
||||||
|
m_settings->registerSetting("BackgroundCat", QString("kitteh"));
|
||||||
|
|
||||||
// Remembered state
|
// Remembered state
|
||||||
m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
|
m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
|
||||||
@ -587,6 +560,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
m_settings->registerSetting("InstanceDir", "instances");
|
m_settings->registerSetting("InstanceDir", "instances");
|
||||||
m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods");
|
m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods");
|
||||||
m_settings->registerSetting("IconsDir", "icons");
|
m_settings->registerSetting("IconsDir", "icons");
|
||||||
|
m_settings->registerSetting("DownloadsDir", QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||||
|
m_settings->registerSetting("DownloadsDirWatchRecursive", false);
|
||||||
|
|
||||||
// Editors
|
// Editors
|
||||||
m_settings->registerSetting("JsonEditor", QString());
|
m_settings->registerSetting("JsonEditor", QString());
|
||||||
@ -614,7 +589,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"}, 4096);
|
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, suitableMaxMem());
|
||||||
m_settings->registerSetting("PermGen", 128);
|
m_settings->registerSetting("PermGen", 128);
|
||||||
|
|
||||||
// Java Settings
|
// Java Settings
|
||||||
@ -662,6 +637,8 @@ 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("ToolbarsLocked", false);
|
||||||
|
|
||||||
m_settings->registerSetting("InstSortMode", "Name");
|
m_settings->registerSetting("InstSortMode", "Name");
|
||||||
m_settings->registerSetting("SelectedInstance", QString());
|
m_settings->registerSetting("SelectedInstance", QString());
|
||||||
|
|
||||||
@ -680,6 +657,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
m_settings->registerSetting("UpdateDialogGeometry", "");
|
m_settings->registerSetting("UpdateDialogGeometry", "");
|
||||||
|
|
||||||
|
m_settings->registerSetting("ModDownloadGeometry", "");
|
||||||
|
m_settings->registerSetting("RPDownloadGeometry", "");
|
||||||
|
m_settings->registerSetting("TPDownloadGeometry", "");
|
||||||
|
m_settings->registerSetting("ShaderDownloadGeometry", "");
|
||||||
|
|
||||||
// HACK: This code feels so stupid is there a less stupid way of doing this?
|
// HACK: This code feels so stupid is there a less stupid way of doing this?
|
||||||
{
|
{
|
||||||
m_settings->registerSetting("PastebinURL", "");
|
m_settings->registerSetting("PastebinURL", "");
|
||||||
@ -725,6 +707,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
m_settings->set("FlameKeyOverride", flameKey);
|
m_settings->set("FlameKeyOverride", flameKey);
|
||||||
m_settings->reset("CFKeyOverride");
|
m_settings->reset("CFKeyOverride");
|
||||||
}
|
}
|
||||||
|
m_settings->registerSetting("ModrinthToken", "");
|
||||||
m_settings->registerSetting("UserAgentOverride", "");
|
m_settings->registerSetting("UserAgentOverride", "");
|
||||||
|
|
||||||
// Init page provider
|
// Init page provider
|
||||||
@ -740,6 +723,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
m_globalSettingsProvider->addPage<AccountListPage>();
|
m_globalSettingsProvider->addPage<AccountListPage>();
|
||||||
m_globalSettingsProvider->addPage<APIPage>();
|
m_globalSettingsProvider->addPage<APIPage>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PixmapCache::setInstance(new PixmapCache(this));
|
||||||
|
|
||||||
qDebug() << "<> Settings loaded.";
|
qDebug() << "<> Settings loaded.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -749,7 +735,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
|
|
||||||
// initialize network access and proxy setup
|
// initialize network access and proxy setup
|
||||||
{
|
{
|
||||||
m_network = new QNetworkAccessManager();
|
m_network.reset(new QNetworkAccessManager());
|
||||||
QString proxyTypeStr = settings()->get("ProxyType").toString();
|
QString proxyTypeStr = settings()->get("ProxyType").toString();
|
||||||
QString addr = settings()->get("ProxyAddr").toString();
|
QString addr = settings()->get("ProxyAddr").toString();
|
||||||
int port = settings()->get("ProxyPort").value<qint16>();
|
int port = settings()->get("ProxyPort").value<qint16>();
|
||||||
@ -771,10 +757,10 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
// initialize the updater
|
// initialize the updater
|
||||||
if(BuildConfig.UPDATER_ENABLED)
|
if(BuildConfig.UPDATER_ENABLED)
|
||||||
{
|
{
|
||||||
auto platform = getIdealPlatform(BuildConfig.BUILD_PLATFORM);
|
qDebug() << "Initializing updater";
|
||||||
auto channelUrl = BuildConfig.UPDATER_BASE + platform + "/channels.json";
|
#ifdef Q_OS_MAC
|
||||||
qDebug() << "Initializing updater with platform: " << platform << " -- " << channelUrl;
|
m_updater.reset(new MacSparkleUpdater());
|
||||||
m_updateChecker.reset(new UpdateChecker(m_network, channelUrl, BuildConfig.VERSION_CHANNEL, BuildConfig.VERSION_BUILD));
|
#endif
|
||||||
qDebug() << "<> Updater started.";
|
qDebug() << "<> Updater started.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -796,29 +782,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
qDebug() << "<> Instance icons intialized.";
|
qDebug() << "<> Instance icons intialized.";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Icon themes
|
// Themes
|
||||||
{
|
m_themeManager = std::make_unique<ThemeManager>(m_mainWindow);
|
||||||
// TODO: icon themes and instance icons do not mesh well together. Rearrange and fix discrepancies!
|
|
||||||
// set icon theme search path!
|
|
||||||
auto searchPaths = QIcon::themeSearchPaths();
|
|
||||||
searchPaths.append("iconthemes");
|
|
||||||
QIcon::setThemeSearchPaths(searchPaths);
|
|
||||||
qDebug() << "<> Icon themes initialized.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize widget themes
|
|
||||||
{
|
|
||||||
auto insertTheme = [this](ITheme * theme)
|
|
||||||
{
|
|
||||||
m_themes.insert(std::make_pair(theme->id(), std::unique_ptr<ITheme>(theme)));
|
|
||||||
};
|
|
||||||
auto darkTheme = new DarkTheme();
|
|
||||||
insertTheme(new SystemTheme());
|
|
||||||
insertTheme(darkTheme);
|
|
||||||
insertTheme(new BrightTheme());
|
|
||||||
insertTheme(new CustomTheme(darkTheme, "custom"));
|
|
||||||
qDebug() << "<> Widget themes initialized.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize and load all instances
|
// initialize and load all instances
|
||||||
{
|
{
|
||||||
@ -864,7 +829,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").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("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
|
m_metacache->addBase("ModrinthPacks", QDir("cache/ModrinthPacks").absolutePath());
|
||||||
|
m_metacache->addBase("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||||
m_metacache->addBase("root", QDir::currentPath());
|
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("icons", QDir("cache/icons").absolutePath());
|
||||||
@ -908,17 +875,15 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
{
|
applyCurrentlySelectedTheme(true);
|
||||||
setIconTheme(settings()->get("IconTheme").toString());
|
|
||||||
qDebug() << "<> Icon theme set.";
|
updateCapabilities();
|
||||||
setApplicationTheme(settings()->get("ApplicationTheme").toString(), true);
|
|
||||||
qDebug() << "<> Application theme set.";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(createSetupWizard())
|
if(createSetupWizard())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
performMainStartupAction();
|
performMainStartupAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -952,7 +917,8 @@ bool Application::createSetupWizard()
|
|||||||
return false;
|
return false;
|
||||||
}();
|
}();
|
||||||
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
bool pasteInterventionRequired = settings()->get("PastebinURL") != "";
|
||||||
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired;
|
bool themeInterventionRequired = settings()->get("ApplicationTheme") == "";
|
||||||
|
bool wizardRequired = javaRequired || languageRequired || pasteInterventionRequired || themeInterventionRequired;
|
||||||
|
|
||||||
if(wizardRequired)
|
if(wizardRequired)
|
||||||
{
|
{
|
||||||
@ -971,6 +937,12 @@ bool Application::createSetupWizard()
|
|||||||
{
|
{
|
||||||
m_setupWizard->addPage(new PasteWizardPage(m_setupWizard));
|
m_setupWizard->addPage(new PasteWizardPage(m_setupWizard));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (themeInterventionRequired)
|
||||||
|
{
|
||||||
|
settings()->set("ApplicationTheme", QString("system")); // set default theme after going into theme wizard
|
||||||
|
m_setupWizard->addPage(new ThemeWizardPage(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 true;
|
||||||
@ -978,18 +950,24 @@ bool Application::createSetupWizard()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::event(QEvent* event) {
|
bool Application::event(QEvent* event)
|
||||||
|
{
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
if (event->type() == QEvent::ApplicationStateChange) {
|
if (event->type() == QEvent::ApplicationStateChange) {
|
||||||
auto ev = static_cast<QApplicationStateChangeEvent*>(event);
|
auto ev = static_cast<QApplicationStateChangeEvent*>(event);
|
||||||
|
|
||||||
if (m_prevAppState == Qt::ApplicationActive
|
if (m_prevAppState == Qt::ApplicationActive && ev->applicationState() == Qt::ApplicationActive) {
|
||||||
&& ev->applicationState() == Qt::ApplicationActive) {
|
|
||||||
emit clickedOnDock();
|
emit clickedOnDock();
|
||||||
}
|
}
|
||||||
m_prevAppState = ev->applicationState();
|
m_prevAppState = ev->applicationState();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (event->type() == QEvent::FileOpen) {
|
||||||
|
auto ev = static_cast<QFileOpenEvent*>(event);
|
||||||
|
m_mainWindow->processURLs({ ev->url() });
|
||||||
|
}
|
||||||
|
|
||||||
return QApplication::event(event);
|
return QApplication::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1027,7 +1005,17 @@ void Application::performMainStartupAction()
|
|||||||
qDebug() << " Launching with account" << m_profileToUse;
|
qDebug() << " Launching with account" << m_profileToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(inst, true, nullptr, serverToJoin, accountToUse);
|
launch(inst, true, false, nullptr, serverToJoin, accountToUse);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!m_instanceIdToShowWindowOf.isEmpty())
|
||||||
|
{
|
||||||
|
auto inst = instances()->getInstanceById(m_instanceIdToShowWindowOf);
|
||||||
|
if(inst)
|
||||||
|
{
|
||||||
|
qDebug() << "<> Showing window of instance " << m_instanceIdToShowWindowOf;
|
||||||
|
showInstanceWindow(inst);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1037,10 +1025,10 @@ void Application::performMainStartupAction()
|
|||||||
showMainWindow(false);
|
showMainWindow(false);
|
||||||
qDebug() << "<> Main window shown.";
|
qDebug() << "<> Main window shown.";
|
||||||
}
|
}
|
||||||
if(!m_zipToImport.isEmpty())
|
if(!m_zipsToImport.isEmpty())
|
||||||
{
|
{
|
||||||
qDebug() << "<> Importing instance from zip:" << m_zipToImport;
|
qDebug() << "<> Importing from zip:" << m_zipsToImport;
|
||||||
m_mainWindow->droppedURLs({ m_zipToImport });
|
m_mainWindow->processURLs( m_zipsToImport );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1093,7 +1081,7 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
qWarning() << "Received" << command << "message without a zip path/URL.";
|
qWarning() << "Received" << command << "message without a zip path/URL.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_mainWindow->droppedURLs({ QUrl(path) });
|
m_mainWindow->processURLs({ QUrl::fromLocalFile(QFileInfo(path).absoluteFilePath()) });
|
||||||
}
|
}
|
||||||
else if(command == "launch")
|
else if(command == "launch")
|
||||||
{
|
{
|
||||||
@ -1131,6 +1119,7 @@ void Application::messageReceived(const QByteArray& message)
|
|||||||
launch(
|
launch(
|
||||||
instance,
|
instance,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
nullptr,
|
nullptr,
|
||||||
serverObject,
|
serverObject,
|
||||||
accountObject
|
accountObject
|
||||||
@ -1156,51 +1145,30 @@ std::shared_ptr<JavaInstallList> Application::javalist()
|
|||||||
return m_javalist;
|
return m_javalist;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ITheme *> Application::getValidApplicationThemes()
|
QList<ITheme*> Application::getValidApplicationThemes()
|
||||||
{
|
{
|
||||||
std::vector<ITheme *> ret;
|
return m_themeManager->getValidApplicationThemes();
|
||||||
auto iter = m_themes.cbegin();
|
|
||||||
while (iter != m_themes.cend())
|
|
||||||
{
|
|
||||||
ret.push_back((*iter).second.get());
|
|
||||||
iter++;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Application::isFlatpak()
|
void Application::applyCurrentlySelectedTheme(bool initial)
|
||||||
{
|
{
|
||||||
#ifdef Q_OS_LINUX
|
m_themeManager->applyCurrentlySelectedTheme(initial);
|
||||||
return QFile::exists("/.flatpak-info");
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setApplicationTheme(const QString& name, bool initial)
|
void Application::setApplicationTheme(const QString& name)
|
||||||
{
|
{
|
||||||
auto systemPalette = qApp->palette();
|
m_themeManager->setApplicationTheme(name);
|
||||||
auto themeIter = m_themes.find(name);
|
|
||||||
if(themeIter != m_themes.end())
|
|
||||||
{
|
|
||||||
auto & theme = (*themeIter).second;
|
|
||||||
theme->apply(initial);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qWarning() << "Tried to set invalid theme:" << name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setIconTheme(const QString& name)
|
void Application::setIconTheme(const QString& name)
|
||||||
{
|
{
|
||||||
QIcon::setThemeName(name);
|
m_themeManager->setIconTheme(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
QIcon Application::getThemedIcon(const QString& name)
|
QIcon Application::getThemedIcon(const QString& name)
|
||||||
{
|
{
|
||||||
if(name == "logo") {
|
if(name == "logo") {
|
||||||
return QIcon(":/org.polymc.PolyMC.svg");
|
return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME);
|
||||||
}
|
}
|
||||||
return QIcon::fromTheme(name);
|
return QIcon::fromTheme(name);
|
||||||
}
|
}
|
||||||
@ -1222,6 +1190,7 @@ bool Application::openJsonEditor(const QString &filename)
|
|||||||
bool Application::launch(
|
bool Application::launch(
|
||||||
InstancePtr instance,
|
InstancePtr instance,
|
||||||
bool online,
|
bool online,
|
||||||
|
bool demo,
|
||||||
BaseProfilerFactory *profiler,
|
BaseProfilerFactory *profiler,
|
||||||
MinecraftServerTargetPtr serverToJoin,
|
MinecraftServerTargetPtr serverToJoin,
|
||||||
MinecraftAccountPtr accountToUse
|
MinecraftAccountPtr accountToUse
|
||||||
@ -1245,6 +1214,7 @@ bool Application::launch(
|
|||||||
controller.reset(new LaunchController());
|
controller.reset(new LaunchController());
|
||||||
controller->setInstance(instance);
|
controller->setInstance(instance);
|
||||||
controller->setOnline(online);
|
controller->setOnline(online);
|
||||||
|
controller->setDemo(demo);
|
||||||
controller->setProfiler(profiler);
|
controller->setProfiler(profiler);
|
||||||
controller->setServerToJoin(serverToJoin);
|
controller->setServerToJoin(serverToJoin);
|
||||||
controller->setAccountToUse(accountToUse);
|
controller->setAccountToUse(accountToUse);
|
||||||
@ -1258,6 +1228,9 @@ bool Application::launch(
|
|||||||
}
|
}
|
||||||
connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded);
|
connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded);
|
||||||
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"));
|
||||||
|
});
|
||||||
addRunningInstance();
|
addRunningInstance();
|
||||||
controller->start();
|
controller->start();
|
||||||
return true;
|
return true;
|
||||||
@ -1412,6 +1385,7 @@ MainWindow* Application::showMainWindow(bool minimized)
|
|||||||
m_mainWindow = new MainWindow();
|
m_mainWindow = new MainWindow();
|
||||||
m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
|
m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
|
||||||
m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
|
m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
|
||||||
|
|
||||||
if(minimized)
|
if(minimized)
|
||||||
{
|
{
|
||||||
m_mainWindow->showMinimized();
|
m_mainWindow->showMinimized();
|
||||||
@ -1564,24 +1538,32 @@ shared_qobject_ptr<Meta::Index> Application::metadataIndex()
|
|||||||
return m_metadataIndex;
|
return m_metadataIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::Capabilities Application::currentCapabilities()
|
void Application::updateCapabilities()
|
||||||
{
|
{
|
||||||
Capabilities c;
|
m_capabilities = None;
|
||||||
if (!getMSAClientID().isEmpty())
|
if (!getMSAClientID().isEmpty())
|
||||||
c |= SupportsMSA;
|
m_capabilities |= SupportsMSA;
|
||||||
if (!getFlameAPIKey().isEmpty())
|
if (!getFlameAPIKey().isEmpty())
|
||||||
c |= SupportsFlame;
|
m_capabilities |= SupportsFlame;
|
||||||
return c;
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
if (gamemode_query_status() >= 0)
|
||||||
|
m_capabilities |= SupportsGameMode;
|
||||||
|
|
||||||
|
if (!MangoHud::getLibraryString().isEmpty())
|
||||||
|
m_capabilities |= SupportsMangoHud;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Application::getJarPath(QString jarFile)
|
QString Application::getJarPath(QString jarFile)
|
||||||
{
|
{
|
||||||
QStringList potentialPaths = {
|
QStringList potentialPaths = {
|
||||||
#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/jars"),
|
FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_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
|
||||||
};
|
};
|
||||||
for(QString p : potentialPaths)
|
for(QString p : potentialPaths)
|
||||||
{
|
{
|
||||||
@ -1612,6 +1594,15 @@ QString Application::getFlameAPIKey()
|
|||||||
return BuildConfig.FLAME_API_KEY;
|
return BuildConfig.FLAME_API_KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Application::getModrinthAPIToken()
|
||||||
|
{
|
||||||
|
QString tokenOverride = m_settings->get("ModrinthToken").toString();
|
||||||
|
if (!tokenOverride.isEmpty())
|
||||||
|
return tokenOverride;
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
QString Application::getUserAgent()
|
QString Application::getUserAgent()
|
||||||
{
|
{
|
||||||
QString uaOverride = m_settings->get("UserAgentOverride").toString();
|
QString uaOverride = m_settings->get("UserAgentOverride").toString();
|
||||||
@ -1632,3 +1623,113 @@ 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,
|
||||||
|
const QString& oldData,
|
||||||
|
const QString& name,
|
||||||
|
const QString& configFile) const
|
||||||
|
{
|
||||||
|
QString nomigratePath = FS::PathCombine(currentData, name + "_nomigrate.txt");
|
||||||
|
QStringList configPaths = { FS::PathCombine(oldData, configFile), FS::PathCombine(oldData, BuildConfig.LAUNCHER_CONFIGFILE) };
|
||||||
|
|
||||||
|
QLocale locale;
|
||||||
|
|
||||||
|
// Is there a valid config at the old location?
|
||||||
|
bool configExists = false;
|
||||||
|
for (QString configPath : configPaths) {
|
||||||
|
configExists |= QFileInfo::exists(configPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!configExists || QFileInfo::exists(nomigratePath)) {
|
||||||
|
qDebug() << "<> No migration needed from" << name;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString message;
|
||||||
|
bool currentExists = QFileInfo::exists(FS::PathCombine(currentData, BuildConfig.LAUNCHER_CONFIGFILE));
|
||||||
|
|
||||||
|
if (currentExists) {
|
||||||
|
message = tr("Old data from %1 was found, but you already have existing data for %2. Sadly you will need to migrate yourself. Do "
|
||||||
|
"you want to be reminded of the pending data migration next time you start %2?")
|
||||||
|
.arg(name, BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||||
|
} else {
|
||||||
|
message = tr("It looks like you used %1 before. Do you want to migrate your data to the new location of %2?")
|
||||||
|
.arg(name, BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||||
|
|
||||||
|
QFileInfo logInfo(FS::PathCombine(oldData, name + "-0.log"));
|
||||||
|
if (logInfo.exists()) {
|
||||||
|
QString lastModified = logInfo.lastModified().toString(locale.dateFormat());
|
||||||
|
message = tr("It looks like you used %1 on %2 before. Do you want to migrate your data to the new location of %3?")
|
||||||
|
.arg(name, lastModified, BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::StandardButton askMoveDialogue =
|
||||||
|
QMessageBox::question(nullptr, BuildConfig.LAUNCHER_DISPLAYNAME, message, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||||
|
|
||||||
|
auto setDoNotMigrate = [&nomigratePath] {
|
||||||
|
QFile file(nomigratePath);
|
||||||
|
file.open(QIODevice::WriteOnly);
|
||||||
|
};
|
||||||
|
|
||||||
|
// create no-migrate file if user doesn't want to migrate
|
||||||
|
if (askMoveDialogue != QMessageBox::Yes) {
|
||||||
|
qDebug() << "<> Migration declined for" << name;
|
||||||
|
setDoNotMigrate();
|
||||||
|
return currentExists; // cancel further migrations, if we already have a data directory
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentExists) {
|
||||||
|
// Migrate!
|
||||||
|
auto matcher = std::make_shared<MultiMatcher>();
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>(configFile));
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>(
|
||||||
|
BuildConfig.LAUNCHER_CONFIGFILE)); // it's possible that we already used that directory before
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>("accounts.json"));
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>("accounts/"));
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>("assets/"));
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>("icons/"));
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>("instances/"));
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>("libraries/"));
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>("mods/"));
|
||||||
|
matcher->add(std::make_shared<SimplePrefixMatcher>("themes/"));
|
||||||
|
|
||||||
|
ProgressDialog diag;
|
||||||
|
DataMigrationTask task(nullptr, oldData, currentData, matcher);
|
||||||
|
if (diag.execWithTask(&task)) {
|
||||||
|
qDebug() << "<> Migration succeeded";
|
||||||
|
setDoNotMigrate();
|
||||||
|
} else {
|
||||||
|
QString reason = task.failReason();
|
||||||
|
QMessageBox::critical(nullptr, BuildConfig.LAUNCHER_DISPLAYNAME, tr("Migration failed! Reason: %1").arg(reason));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qWarning() << "<> Migration was skipped, due to existing data";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::triggerUpdateCheck()
|
||||||
|
{
|
||||||
|
if (m_updater) {
|
||||||
|
qDebug() << "Checking for updates.";
|
||||||
|
m_updater->setBetaAllowed(false); // There are no other channels than stable
|
||||||
|
m_updater->checkForUpdates();
|
||||||
|
} else {
|
||||||
|
qDebug() << "Updater not available.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2022 Tayou <tayou@gmx.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
*
|
*
|
||||||
* 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,7 +44,6 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <updater/GoUpdate.h>
|
|
||||||
|
|
||||||
#include <BaseInstance.h>
|
#include <BaseInstance.h>
|
||||||
|
|
||||||
@ -62,12 +63,13 @@ class AccountList;
|
|||||||
class IconList;
|
class IconList;
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
class JavaInstallList;
|
class JavaInstallList;
|
||||||
class UpdateChecker;
|
class ExternalUpdater;
|
||||||
class BaseProfilerFactory;
|
class BaseProfilerFactory;
|
||||||
class BaseDetachedToolFactory;
|
class BaseDetachedToolFactory;
|
||||||
class TranslationsModel;
|
class TranslationsModel;
|
||||||
class ITheme;
|
class ITheme;
|
||||||
class MCEditTool;
|
class MCEditTool;
|
||||||
|
class ThemeManager;
|
||||||
|
|
||||||
namespace Meta {
|
namespace Meta {
|
||||||
class Index;
|
class Index;
|
||||||
@ -95,6 +97,8 @@ public:
|
|||||||
|
|
||||||
SupportsMSA = 1 << 0,
|
SupportsMSA = 1 << 0,
|
||||||
SupportsFlame = 1 << 1,
|
SupportsFlame = 1 << 1,
|
||||||
|
SupportsGameMode = 1 << 2,
|
||||||
|
SupportsMangoHud = 1 << 3,
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(Capabilities, Capability)
|
Q_DECLARE_FLAGS(Capabilities, Capability)
|
||||||
|
|
||||||
@ -114,18 +118,20 @@ public:
|
|||||||
|
|
||||||
QIcon getThemedIcon(const QString& name);
|
QIcon getThemedIcon(const QString& name);
|
||||||
|
|
||||||
bool isFlatpak();
|
|
||||||
|
|
||||||
void setIconTheme(const QString& name);
|
void setIconTheme(const QString& name);
|
||||||
|
|
||||||
std::vector<ITheme *> getValidApplicationThemes();
|
void applyCurrentlySelectedTheme(bool initial = false);
|
||||||
|
|
||||||
void setApplicationTheme(const QString& name, bool initial);
|
QList<ITheme*> getValidApplicationThemes();
|
||||||
|
|
||||||
shared_qobject_ptr<UpdateChecker> updateChecker() {
|
void setApplicationTheme(const QString& name);
|
||||||
return m_updateChecker;
|
|
||||||
|
shared_qobject_ptr<ExternalUpdater> updater() {
|
||||||
|
return m_updater;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void triggerUpdateCheck();
|
||||||
|
|
||||||
std::shared_ptr<TranslationsModel> translations();
|
std::shared_ptr<TranslationsModel> translations();
|
||||||
|
|
||||||
std::shared_ptr<JavaInstallList> javalist();
|
std::shared_ptr<JavaInstallList> javalist();
|
||||||
@ -162,7 +168,7 @@ public:
|
|||||||
|
|
||||||
shared_qobject_ptr<Meta::Index> metadataIndex();
|
shared_qobject_ptr<Meta::Index> metadataIndex();
|
||||||
|
|
||||||
Capabilities currentCapabilities();
|
void updateCapabilities();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Finds and returns the full path to a jar file.
|
* Finds and returns the full path to a jar file.
|
||||||
@ -172,6 +178,7 @@ public:
|
|||||||
|
|
||||||
QString getMSAClientID();
|
QString getMSAClientID();
|
||||||
QString getFlameAPIKey();
|
QString getFlameAPIKey();
|
||||||
|
QString getModrinthAPIToken();
|
||||||
QString getUserAgent();
|
QString getUserAgent();
|
||||||
QString getUserAgentUncached();
|
QString getUserAgentUncached();
|
||||||
|
|
||||||
@ -180,6 +187,14 @@ public:
|
|||||||
return m_rootPath;
|
return m_rootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPortable() {
|
||||||
|
return m_portable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Capabilities capabilities() {
|
||||||
|
return m_capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Opens a json file using either a system default editor, or, if not empty, the editor
|
* Opens a json file using either a system default editor, or, if not empty, the editor
|
||||||
* specified in the settings
|
* specified in the settings
|
||||||
@ -194,10 +209,13 @@ public:
|
|||||||
|
|
||||||
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
||||||
|
|
||||||
|
int suitableMaxMem();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateAllowedChanged(bool status);
|
void updateAllowedChanged(bool status);
|
||||||
void globalSettingsAboutToOpen();
|
void globalSettingsAboutToOpen();
|
||||||
void globalSettingsClosed();
|
void globalSettingsClosed();
|
||||||
|
int currentCatChanged(int index);
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
void clickedOnDock();
|
void clickedOnDock();
|
||||||
@ -207,6 +225,7 @@ public slots:
|
|||||||
bool launch(
|
bool launch(
|
||||||
InstancePtr instance,
|
InstancePtr instance,
|
||||||
bool online = true,
|
bool online = true,
|
||||||
|
bool demo = false,
|
||||||
BaseProfilerFactory *profiler = nullptr,
|
BaseProfilerFactory *profiler = nullptr,
|
||||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||||
MinecraftAccountPtr accountToUse = nullptr
|
MinecraftAccountPtr accountToUse = nullptr
|
||||||
@ -222,6 +241,7 @@ private slots:
|
|||||||
void setupWizardFinished(int status);
|
void setupWizardFinished(int status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const;
|
||||||
bool createSetupWizard();
|
bool createSetupWizard();
|
||||||
void performMainStartupAction();
|
void performMainStartupAction();
|
||||||
|
|
||||||
@ -238,7 +258,7 @@ private:
|
|||||||
|
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
|
|
||||||
shared_qobject_ptr<UpdateChecker> m_updateChecker;
|
shared_qobject_ptr<ExternalUpdater> m_updater;
|
||||||
shared_qobject_ptr<AccountList> m_accounts;
|
shared_qobject_ptr<AccountList> m_accounts;
|
||||||
|
|
||||||
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
||||||
@ -250,14 +270,16 @@ private:
|
|||||||
std::shared_ptr<JavaInstallList> m_javalist;
|
std::shared_ptr<JavaInstallList> m_javalist;
|
||||||
std::shared_ptr<TranslationsModel> m_translations;
|
std::shared_ptr<TranslationsModel> m_translations;
|
||||||
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
||||||
std::map<QString, std::unique_ptr<ITheme>> m_themes;
|
|
||||||
std::unique_ptr<MCEditTool> m_mcedit;
|
std::unique_ptr<MCEditTool> m_mcedit;
|
||||||
QSet<QString> m_features;
|
QSet<QString> m_features;
|
||||||
|
std::unique_ptr<ThemeManager> m_themeManager;
|
||||||
|
|
||||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||||
|
|
||||||
QString m_rootPath;
|
QString m_rootPath;
|
||||||
Status m_status = Application::StartingUp;
|
Status m_status = Application::StartingUp;
|
||||||
|
Capabilities m_capabilities;
|
||||||
|
bool m_portable = false;
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
|
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
|
||||||
@ -292,7 +314,7 @@ public:
|
|||||||
QString m_serverToJoin;
|
QString m_serverToJoin;
|
||||||
QString m_profileToUse;
|
QString m_profileToUse;
|
||||||
bool m_liveCheck = false;
|
bool m_liveCheck = false;
|
||||||
QUrl m_zipToImport;
|
QList<QUrl> m_zipsToImport;
|
||||||
|
QString m_instanceIdToShowWindowOf;
|
||||||
std::unique_ptr<QFile> logFile;
|
std::unique_ptr<QFile> logFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,8 +47,8 @@ void ApplicationMessage::parse(const QByteArray & input) {
|
|||||||
args.clear();
|
args.clear();
|
||||||
|
|
||||||
auto parsedArgs = root.value("args").toObject();
|
auto parsedArgs = root.value("args").toObject();
|
||||||
for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
|
for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
|
||||||
args[iter.key()] = iter.value().toString();
|
args.insert(iter.key(), iter.value().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,8 +56,8 @@ QByteArray ApplicationMessage::serialize() {
|
|||||||
QJsonObject root;
|
QJsonObject root;
|
||||||
root.insert("command", command);
|
root.insert("command", command);
|
||||||
QJsonObject outArgs;
|
QJsonObject outArgs;
|
||||||
for (auto iter = args.begin(); iter != args.end(); iter++) {
|
for (auto iter = args.constBegin(); iter != args.constEnd(); iter++) {
|
||||||
outArgs[iter.key()] = iter.value();
|
outArgs.insert(iter.key(), iter.value());
|
||||||
}
|
}
|
||||||
root.insert("args", outArgs);
|
root.insert("args", outArgs);
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QMap>
|
#include <QHash>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
struct ApplicationMessage {
|
struct ApplicationMessage {
|
||||||
QString command;
|
QString command;
|
||||||
QMap<QString, QString> args;
|
QHash<QString, QString> args;
|
||||||
|
|
||||||
QByteArray serialize();
|
QByteArray serialize();
|
||||||
void parse(const QByteArray & input);
|
void parse(const QByteArray & input);
|
||||||
|
@ -17,13 +17,14 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "BaseVersion.h"
|
||||||
|
|
||||||
class MinecraftInstance;
|
class MinecraftInstance;
|
||||||
class QDir;
|
class QDir;
|
||||||
class QString;
|
class QString;
|
||||||
class QObject;
|
class QObject;
|
||||||
class Task;
|
class Task;
|
||||||
class BaseVersion;
|
class BaseVersion;
|
||||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
|
||||||
|
|
||||||
class BaseInstaller
|
class BaseInstaller
|
||||||
{
|
{
|
||||||
@ -35,7 +36,7 @@ public:
|
|||||||
virtual bool add(MinecraftInstance *to);
|
virtual bool add(MinecraftInstance *to);
|
||||||
virtual bool remove(MinecraftInstance *from);
|
virtual bool remove(MinecraftInstance *from);
|
||||||
|
|
||||||
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
|
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersion::Ptr version, QObject *parent) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QString id() const = 0;
|
virtual QString id() const = 0;
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
#include "settings/Setting.h"
|
#include "settings/Setting.h"
|
||||||
@ -53,15 +55,24 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
|||||||
: QObject()
|
: QObject()
|
||||||
{
|
{
|
||||||
m_settings = settings;
|
m_settings = settings;
|
||||||
|
m_global_settings = globalSettings;
|
||||||
m_rootDir = rootDir;
|
m_rootDir = rootDir;
|
||||||
|
|
||||||
m_settings->registerSetting("name", "Unnamed Instance");
|
m_settings->registerSetting("name", "Unnamed Instance");
|
||||||
m_settings->registerSetting("iconKey", "default");
|
m_settings->registerSetting("iconKey", "default");
|
||||||
m_settings->registerSetting("notes", "");
|
m_settings->registerSetting("notes", "");
|
||||||
|
|
||||||
m_settings->registerSetting("lastLaunchTime", 0);
|
m_settings->registerSetting("lastLaunchTime", 0);
|
||||||
m_settings->registerSetting("totalTimePlayed", 0);
|
m_settings->registerSetting("totalTimePlayed", 0);
|
||||||
m_settings->registerSetting("lastTimePlayed", 0);
|
m_settings->registerSetting("lastTimePlayed", 0);
|
||||||
|
|
||||||
|
m_settings->registerSetting("linkedInstances", "[]");
|
||||||
|
|
||||||
|
// Game time override
|
||||||
|
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
|
||||||
|
m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
|
||||||
|
m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride);
|
||||||
|
|
||||||
// NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of
|
// NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of
|
||||||
// a locally stored instance
|
// a locally stored instance
|
||||||
if (!m_settings->getSetting("InstanceType"))
|
if (!m_settings->getSetting("InstanceType"))
|
||||||
@ -107,49 +118,59 @@ QString BaseInstance::getPostExitCommand()
|
|||||||
return settings()->get("PostExitCommand").toString();
|
return settings()->get("PostExitCommand").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseInstance::isManagedPack()
|
bool BaseInstance::isManagedPack() const
|
||||||
{
|
{
|
||||||
return settings()->get("ManagedPack").toBool();
|
return m_settings->get("ManagedPack").toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackType()
|
QString BaseInstance::getManagedPackType() const
|
||||||
{
|
{
|
||||||
return settings()->get("ManagedPackType").toString();
|
return m_settings->get("ManagedPackType").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackID()
|
QString BaseInstance::getManagedPackID() const
|
||||||
{
|
{
|
||||||
return settings()->get("ManagedPackID").toString();
|
return m_settings->get("ManagedPackID").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackName()
|
QString BaseInstance::getManagedPackName() const
|
||||||
{
|
{
|
||||||
return settings()->get("ManagedPackName").toString();
|
return m_settings->get("ManagedPackName").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackVersionID()
|
QString BaseInstance::getManagedPackVersionID() const
|
||||||
{
|
{
|
||||||
return settings()->get("ManagedPackVersionID").toString();
|
return m_settings->get("ManagedPackVersionID").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getManagedPackVersionName()
|
QString BaseInstance::getManagedPackVersionName() const
|
||||||
{
|
{
|
||||||
return settings()->get("ManagedPackVersionName").toString();
|
return m_settings->get("ManagedPackVersionName").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version)
|
void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version)
|
||||||
{
|
{
|
||||||
settings()->set("ManagedPack", true);
|
m_settings->set("ManagedPack", true);
|
||||||
settings()->set("ManagedPackType", type);
|
m_settings->set("ManagedPackType", type);
|
||||||
settings()->set("ManagedPackID", id);
|
m_settings->set("ManagedPackID", id);
|
||||||
settings()->set("ManagedPackName", name);
|
m_settings->set("ManagedPackName", name);
|
||||||
settings()->set("ManagedPackVersionID", versionId);
|
m_settings->set("ManagedPackVersionID", versionId);
|
||||||
settings()->set("ManagedPackVersionName", version);
|
m_settings->set("ManagedPackVersionName", version);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseInstance::copyManagedPack(BaseInstance& other)
|
||||||
|
{
|
||||||
|
m_settings->set("ManagedPack", other.isManagedPack());
|
||||||
|
m_settings->set("ManagedPackType", other.getManagedPackType());
|
||||||
|
m_settings->set("ManagedPackID", other.getManagedPackID());
|
||||||
|
m_settings->set("ManagedPackName", other.getManagedPackName());
|
||||||
|
m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID());
|
||||||
|
m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName());
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseInstance::getConsoleMaxLines() const
|
int BaseInstance::getConsoleMaxLines() const
|
||||||
{
|
{
|
||||||
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
|
auto lineSetting = m_settings->getSetting("ConsoleMaxLines");
|
||||||
bool conversionOk = false;
|
bool conversionOk = false;
|
||||||
int maxLines = lineSetting->get().toInt(&conversionOk);
|
int maxLines = lineSetting->get().toInt(&conversionOk);
|
||||||
if(!conversionOk)
|
if(!conversionOk)
|
||||||
@ -162,7 +183,39 @@ int BaseInstance::getConsoleMaxLines() const
|
|||||||
|
|
||||||
bool BaseInstance::shouldStopOnConsoleOverflow() const
|
bool BaseInstance::shouldStopOnConsoleOverflow() const
|
||||||
{
|
{
|
||||||
return settings()->get("ConsoleOverflowStop").toBool();
|
return m_settings->get("ConsoleOverflowStop").toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList BaseInstance::getLinkedInstances() const
|
||||||
|
{
|
||||||
|
return m_settings->get("linkedInstances").toStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseInstance::setLinkedInstances(const QStringList& list)
|
||||||
|
{
|
||||||
|
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
||||||
|
m_settings->set("linkedInstances", list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseInstance::addLinkedInstanceId(const QString& id)
|
||||||
|
{
|
||||||
|
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
||||||
|
linkedInstances.append(id);
|
||||||
|
setLinkedInstances(linkedInstances);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseInstance::removeLinkedInstanceId(const QString& id)
|
||||||
|
{
|
||||||
|
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
||||||
|
int numRemoved = linkedInstances.removeAll(id);
|
||||||
|
setLinkedInstances(linkedInstances);
|
||||||
|
return numRemoved > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseInstance::isLinkedToInstanceId(const QString& id) const
|
||||||
|
{
|
||||||
|
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
||||||
|
return linkedInstances.contains(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::iconUpdated(QString key)
|
void BaseInstance::iconUpdated(QString key)
|
||||||
@ -237,7 +290,7 @@ void BaseInstance::setRunning(bool running)
|
|||||||
|
|
||||||
int64_t BaseInstance::totalTimePlayed() const
|
int64_t BaseInstance::totalTimePlayed() const
|
||||||
{
|
{
|
||||||
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
qint64 current = m_settings->get("totalTimePlayed").toLongLong();
|
||||||
if(m_isRunning)
|
if(m_isRunning)
|
||||||
{
|
{
|
||||||
QDateTime timeNow = QDateTime::currentDateTime();
|
QDateTime timeNow = QDateTime::currentDateTime();
|
||||||
@ -253,7 +306,7 @@ int64_t BaseInstance::lastTimePlayed() const
|
|||||||
QDateTime timeNow = QDateTime::currentDateTime();
|
QDateTime timeNow = QDateTime::currentDateTime();
|
||||||
return m_timeStarted.secsTo(timeNow);
|
return m_timeStarted.secsTo(timeNow);
|
||||||
}
|
}
|
||||||
return settings()->get("lastTimePlayed").toLongLong();
|
return m_settings->get("lastTimePlayed").toLongLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::resetTimePlayed()
|
void BaseInstance::resetTimePlayed()
|
||||||
@ -272,8 +325,10 @@ QString BaseInstance::instanceRoot() const
|
|||||||
return m_rootDir;
|
return m_rootDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsObjectPtr BaseInstance::settings() const
|
SettingsObjectPtr BaseInstance::settings()
|
||||||
{
|
{
|
||||||
|
loadSpecificSettings();
|
||||||
|
|
||||||
return m_settings;
|
return m_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,11 +391,11 @@ QString BaseInstance::name() const
|
|||||||
|
|
||||||
QString BaseInstance::windowTitle() const
|
QString BaseInstance::windowTitle() const
|
||||||
{
|
{
|
||||||
return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
|
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: why is this here? move it to MinecraftInstance!!!
|
// FIXME: why is this here? move it to MinecraftInstance!!!
|
||||||
QStringList BaseInstance::extraArguments() const
|
QStringList BaseInstance::extraArguments()
|
||||||
{
|
{
|
||||||
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
||||||
}
|
}
|
||||||
@ -349,3 +404,8 @@ shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
|
|||||||
{
|
{
|
||||||
return m_launchProcess;
|
return m_launchProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseInstance::updateRuntimeContext()
|
||||||
|
{
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
|
||||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||||
|
#include "RuntimeContext.h"
|
||||||
|
|
||||||
class QDir;
|
class QDir;
|
||||||
class Task;
|
class Task;
|
||||||
@ -140,21 +141,22 @@ public:
|
|||||||
QString getPostExitCommand();
|
QString getPostExitCommand();
|
||||||
QString getWrapperCommand();
|
QString getWrapperCommand();
|
||||||
|
|
||||||
bool isManagedPack();
|
bool isManagedPack() const;
|
||||||
QString getManagedPackType();
|
QString getManagedPackType() const;
|
||||||
QString getManagedPackID();
|
QString getManagedPackID() const;
|
||||||
QString getManagedPackName();
|
QString getManagedPackName() const;
|
||||||
QString getManagedPackVersionID();
|
QString getManagedPackVersionID() const;
|
||||||
QString getManagedPackVersionName();
|
QString getManagedPackVersionName() const;
|
||||||
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
|
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
|
||||||
|
void copyManagedPack(BaseInstance& other);
|
||||||
|
|
||||||
/// guess log level from a line of game log
|
/// guess log level from a line of game log
|
||||||
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
|
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level)
|
||||||
{
|
{
|
||||||
return level;
|
return level;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual QStringList extraArguments() const;
|
virtual QStringList extraArguments();
|
||||||
|
|
||||||
/// Traits. Normally inside the version, depends on instance implementation.
|
/// Traits. Normally inside the version, depends on instance implementation.
|
||||||
virtual QSet <QString> traits() const = 0;
|
virtual QSet <QString> traits() const = 0;
|
||||||
@ -170,9 +172,18 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
* \brief Gets this instance's settings object.
|
* \brief Gets this instance's settings object.
|
||||||
* This settings object stores instance-specific settings.
|
* This settings object stores instance-specific settings.
|
||||||
|
*
|
||||||
|
* Note that this method is not const.
|
||||||
|
* It may call loadSpecificSettings() to ensure those are loaded.
|
||||||
|
*
|
||||||
* \return A pointer to this instance's settings object.
|
* \return A pointer to this instance's settings object.
|
||||||
*/
|
*/
|
||||||
virtual SettingsObjectPtr settings() const;
|
virtual SettingsObjectPtr settings();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Loads settings specific to an instance type if they're not already loaded.
|
||||||
|
*/
|
||||||
|
virtual void loadSpecificSettings() = 0;
|
||||||
|
|
||||||
/// returns a valid update task
|
/// returns a valid update task
|
||||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||||
@ -206,10 +217,16 @@ public:
|
|||||||
virtual QString instanceConfigFolder() const = 0;
|
virtual QString instanceConfigFolder() const = 0;
|
||||||
|
|
||||||
/// get variables this instance exports
|
/// get variables this instance exports
|
||||||
virtual QMap<QString, QString> getVariables() const = 0;
|
virtual QMap<QString, QString> getVariables() = 0;
|
||||||
|
|
||||||
virtual QString typeName() const = 0;
|
virtual QString typeName() const = 0;
|
||||||
|
|
||||||
|
void updateRuntimeContext();
|
||||||
|
RuntimeContext runtimeContext() const
|
||||||
|
{
|
||||||
|
return m_runtimeContext;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasVersionBroken() const
|
bool hasVersionBroken() const
|
||||||
{
|
{
|
||||||
return m_hasBrokenVersion;
|
return m_hasBrokenVersion;
|
||||||
@ -265,9 +282,20 @@ public:
|
|||||||
int getConsoleMaxLines() const;
|
int getConsoleMaxLines() const;
|
||||||
bool shouldStopOnConsoleOverflow() const;
|
bool shouldStopOnConsoleOverflow() const;
|
||||||
|
|
||||||
|
QStringList getLinkedInstances() const;
|
||||||
|
void setLinkedInstances(const QStringList& list);
|
||||||
|
void addLinkedInstanceId(const QString& id);
|
||||||
|
bool removeLinkedInstanceId(const QString& id);
|
||||||
|
bool isLinkedToInstanceId(const QString& id) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void changeStatus(Status newStatus);
|
void changeStatus(Status newStatus);
|
||||||
|
|
||||||
|
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); };
|
||||||
|
|
||||||
|
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
|
||||||
|
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/*!
|
/*!
|
||||||
* \brief Signal emitted when properties relevant to the instance view change
|
* \brief Signal emitted when properties relevant to the instance view change
|
||||||
@ -290,12 +318,17 @@ protected: /* data */
|
|||||||
bool m_isRunning = false;
|
bool m_isRunning = false;
|
||||||
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
||||||
QDateTime m_timeStarted;
|
QDateTime m_timeStarted;
|
||||||
|
RuntimeContext m_runtimeContext;
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
Status m_status = Status::Present;
|
Status m_status = Status::Present;
|
||||||
bool m_crashed = false;
|
bool m_crashed = false;
|
||||||
bool m_hasUpdate = false;
|
bool m_hasUpdate = false;
|
||||||
bool m_hasBrokenVersion = false;
|
bool m_hasBrokenVersion = false;
|
||||||
|
|
||||||
|
SettingsObjectWeakPtr m_global_settings;
|
||||||
|
bool m_specific_settings_loaded = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
class BaseVersion
|
class BaseVersion
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using Ptr = std::shared_ptr<BaseVersion>;
|
||||||
virtual ~BaseVersion() {}
|
virtual ~BaseVersion() {}
|
||||||
/*!
|
/*!
|
||||||
* A string used to identify this version in config files.
|
* A string used to identify this version in config files.
|
||||||
@ -54,6 +55,4 @@ public:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BaseVersionPtr)
|
|
||||||
|
@ -40,20 +40,20 @@ BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
|
BaseVersion::Ptr BaseVersionList::findVersion(const QString &descriptor)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count(); i++)
|
for (int i = 0; i < count(); i++)
|
||||||
{
|
{
|
||||||
if (at(i)->descriptor() == descriptor)
|
if (at(i)->descriptor() == descriptor)
|
||||||
return at(i);
|
return at(i);
|
||||||
}
|
}
|
||||||
return BaseVersionPtr();
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseVersionPtr BaseVersionList::getRecommended() const
|
BaseVersion::Ptr BaseVersionList::getRecommended() const
|
||||||
{
|
{
|
||||||
if (count() <= 0)
|
if (count() <= 0)
|
||||||
return BaseVersionPtr();
|
return nullptr;
|
||||||
else
|
else
|
||||||
return at(0);
|
return at(0);
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
|||||||
if (index.row() > count())
|
if (index.row() > count())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
BaseVersionPtr version = at(index.row());
|
BaseVersion::Ptr version = at(index.row());
|
||||||
|
|
||||||
switch (role)
|
switch (role)
|
||||||
{
|
{
|
||||||
@ -95,12 +95,12 @@ BaseVersionList::RoleList BaseVersionList::providesRoles() const
|
|||||||
int BaseVersionList::rowCount(const QModelIndex &parent) const
|
int BaseVersionList::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
// Return count
|
// Return count
|
||||||
return count();
|
return parent.isValid() ? 0 : count();
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseVersionList::columnCount(const QModelIndex &parent) const
|
int BaseVersionList::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return 1;
|
return parent.isValid() ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> BaseVersionList::roleNames() const
|
QHash<int, QByteArray> BaseVersionList::roleNames() const
|
||||||
|
@ -70,7 +70,7 @@ public:
|
|||||||
virtual bool isLoaded() = 0;
|
virtual bool isLoaded() = 0;
|
||||||
|
|
||||||
//! Gets the version at the given index.
|
//! Gets the version at the given index.
|
||||||
virtual const BaseVersionPtr at(int i) const = 0;
|
virtual const BaseVersion::Ptr at(int i) const = 0;
|
||||||
|
|
||||||
//! Returns the number of versions in the list.
|
//! Returns the number of versions in the list.
|
||||||
virtual int count() const = 0;
|
virtual int count() const = 0;
|
||||||
@ -90,13 +90,13 @@ public:
|
|||||||
* \return A const pointer to the version with the given descriptor. NULL if
|
* \return A const pointer to the version with the given descriptor. NULL if
|
||||||
* one doesn't exist.
|
* one doesn't exist.
|
||||||
*/
|
*/
|
||||||
virtual BaseVersionPtr findVersion(const QString &descriptor);
|
virtual BaseVersion::Ptr findVersion(const QString &descriptor);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets the recommended version from this list
|
* \brief Gets the recommended version from this list
|
||||||
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
||||||
*/
|
*/
|
||||||
virtual BaseVersionPtr getRecommended() const;
|
virtual BaseVersion::Ptr getRecommended() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Sorts the version list.
|
* Sorts the version list.
|
||||||
@ -117,5 +117,5 @@ slots:
|
|||||||
* then copies the versions and sets their parents correctly.
|
* then copies the versions and sets their parents correctly.
|
||||||
* \param versions List of versions whose parents should be set.
|
* \param versions List of versions whose parents should be set.
|
||||||
*/
|
*/
|
||||||
virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
|
virtual void updateListData(QList<BaseVersion::Ptr> versions) = 0;
|
||||||
};
|
};
|
||||||
|
@ -24,20 +24,24 @@ set(CORE_SOURCES
|
|||||||
NullInstance.h
|
NullInstance.h
|
||||||
MMCZip.h
|
MMCZip.h
|
||||||
MMCZip.cpp
|
MMCZip.cpp
|
||||||
MMCStrings.h
|
StringUtils.h
|
||||||
MMCStrings.cpp
|
StringUtils.cpp
|
||||||
|
QVariantUtils.h
|
||||||
|
RuntimeContext.h
|
||||||
|
|
||||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
# Basic instance manipulation tasks (derived from InstanceTask)
|
||||||
InstanceCreationTask.h
|
InstanceCreationTask.h
|
||||||
InstanceCreationTask.cpp
|
InstanceCreationTask.cpp
|
||||||
|
InstanceCopyPrefs.h
|
||||||
|
InstanceCopyPrefs.cpp
|
||||||
InstanceCopyTask.h
|
InstanceCopyTask.h
|
||||||
InstanceCopyTask.cpp
|
InstanceCopyTask.cpp
|
||||||
InstanceImportTask.h
|
InstanceImportTask.h
|
||||||
InstanceImportTask.cpp
|
InstanceImportTask.cpp
|
||||||
|
|
||||||
# Mod downloading task
|
# Resource downloading task
|
||||||
ModDownloadTask.h
|
ResourceDownloadTask.h
|
||||||
ModDownloadTask.cpp
|
ResourceDownloadTask.cpp
|
||||||
|
|
||||||
# Use tracking separate from memory management
|
# Use tracking separate from memory management
|
||||||
Usable.h
|
Usable.h
|
||||||
@ -86,13 +90,18 @@ set(CORE_SOURCES
|
|||||||
# Time
|
# Time
|
||||||
MMCTime.h
|
MMCTime.h
|
||||||
MMCTime.cpp
|
MMCTime.cpp
|
||||||
|
|
||||||
|
MTPixmapCache.h
|
||||||
)
|
)
|
||||||
|
if (UNIX AND NOT CYGWIN AND NOT APPLE)
|
||||||
|
set(CORE_SOURCES
|
||||||
|
${CORE_SOURCES}
|
||||||
|
|
||||||
ecm_add_test(FileSystem_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
# MangoHud
|
||||||
TEST_NAME FileSystem) # TODO: needs testdata
|
MangoHud.h
|
||||||
|
MangoHud.cpp
|
||||||
ecm_add_test(GZip_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
)
|
||||||
TEST_NAME GZip)
|
endif()
|
||||||
|
|
||||||
set(PATHMATCHER_SOURCES
|
set(PATHMATCHER_SOURCES
|
||||||
# Path matchers
|
# Path matchers
|
||||||
@ -100,6 +109,7 @@ set(PATHMATCHER_SOURCES
|
|||||||
pathmatcher/IPathMatcher.h
|
pathmatcher/IPathMatcher.h
|
||||||
pathmatcher/MultiMatcher.h
|
pathmatcher/MultiMatcher.h
|
||||||
pathmatcher/RegexpMatcher.h
|
pathmatcher/RegexpMatcher.h
|
||||||
|
pathmatcher/SimplePrefixMatcher.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(NET_SOURCES
|
set(NET_SOURCES
|
||||||
@ -114,6 +124,8 @@ set(NET_SOURCES
|
|||||||
net/HttpMetaCache.h
|
net/HttpMetaCache.h
|
||||||
net/MetaCacheSink.cpp
|
net/MetaCacheSink.cpp
|
||||||
net/MetaCacheSink.h
|
net/MetaCacheSink.h
|
||||||
|
net/Logging.h
|
||||||
|
net/Logging.cpp
|
||||||
net/NetAction.h
|
net/NetAction.h
|
||||||
net/NetJob.cpp
|
net/NetJob.cpp
|
||||||
net/NetJob.h
|
net/NetJob.h
|
||||||
@ -152,12 +164,6 @@ set(LAUNCH_SOURCES
|
|||||||
|
|
||||||
# Old update system
|
# Old update system
|
||||||
set(UPDATE_SOURCES
|
set(UPDATE_SOURCES
|
||||||
updater/GoUpdate.h
|
|
||||||
updater/GoUpdate.cpp
|
|
||||||
updater/UpdateChecker.h
|
|
||||||
updater/UpdateChecker.cpp
|
|
||||||
updater/DownloadTask.h
|
|
||||||
updater/DownloadTask.cpp
|
|
||||||
updater/ExternalUpdater.h
|
updater/ExternalUpdater.h
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -294,8 +300,6 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/Rule.h
|
minecraft/Rule.h
|
||||||
minecraft/OneSixVersionFormat.cpp
|
minecraft/OneSixVersionFormat.cpp
|
||||||
minecraft/OneSixVersionFormat.h
|
minecraft/OneSixVersionFormat.h
|
||||||
minecraft/OpSys.cpp
|
|
||||||
minecraft/OpSys.h
|
|
||||||
minecraft/ParseUtils.cpp
|
minecraft/ParseUtils.cpp
|
||||||
minecraft/ParseUtils.h
|
minecraft/ParseUtils.h
|
||||||
minecraft/ProfileUtils.cpp
|
minecraft/ProfileUtils.cpp
|
||||||
@ -303,6 +307,8 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/Library.cpp
|
minecraft/Library.cpp
|
||||||
minecraft/Library.h
|
minecraft/Library.h
|
||||||
minecraft/MojangDownloadInfo.h
|
minecraft/MojangDownloadInfo.h
|
||||||
|
minecraft/VanillaInstanceCreationTask.cpp
|
||||||
|
minecraft/VanillaInstanceCreationTask.h
|
||||||
minecraft/VersionFile.cpp
|
minecraft/VersionFile.cpp
|
||||||
minecraft/VersionFile.h
|
minecraft/VersionFile.h
|
||||||
minecraft/VersionFilterData.h
|
minecraft/VersionFilterData.h
|
||||||
@ -318,16 +324,44 @@ set(MINECRAFT_SOURCES
|
|||||||
minecraft/mod/ModDetails.h
|
minecraft/mod/ModDetails.h
|
||||||
minecraft/mod/ModFolderModel.h
|
minecraft/mod/ModFolderModel.h
|
||||||
minecraft/mod/ModFolderModel.cpp
|
minecraft/mod/ModFolderModel.cpp
|
||||||
|
minecraft/mod/Resource.h
|
||||||
|
minecraft/mod/Resource.cpp
|
||||||
|
minecraft/mod/ResourceFolderModel.h
|
||||||
|
minecraft/mod/ResourceFolderModel.cpp
|
||||||
|
minecraft/mod/DataPack.h
|
||||||
|
minecraft/mod/DataPack.cpp
|
||||||
|
minecraft/mod/ResourcePack.h
|
||||||
|
minecraft/mod/ResourcePack.cpp
|
||||||
minecraft/mod/ResourcePackFolderModel.h
|
minecraft/mod/ResourcePackFolderModel.h
|
||||||
minecraft/mod/ResourcePackFolderModel.cpp
|
minecraft/mod/ResourcePackFolderModel.cpp
|
||||||
|
minecraft/mod/TexturePack.h
|
||||||
|
minecraft/mod/TexturePack.cpp
|
||||||
|
minecraft/mod/ShaderPack.h
|
||||||
|
minecraft/mod/ShaderPack.cpp
|
||||||
|
minecraft/mod/WorldSave.h
|
||||||
|
minecraft/mod/WorldSave.cpp
|
||||||
minecraft/mod/TexturePackFolderModel.h
|
minecraft/mod/TexturePackFolderModel.h
|
||||||
minecraft/mod/TexturePackFolderModel.cpp
|
minecraft/mod/TexturePackFolderModel.cpp
|
||||||
|
minecraft/mod/ShaderPackFolderModel.h
|
||||||
|
minecraft/mod/tasks/BasicFolderLoadTask.h
|
||||||
minecraft/mod/tasks/ModFolderLoadTask.h
|
minecraft/mod/tasks/ModFolderLoadTask.h
|
||||||
minecraft/mod/tasks/ModFolderLoadTask.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/LocalModUpdateTask.h
|
||||||
minecraft/mod/tasks/LocalModUpdateTask.cpp
|
minecraft/mod/tasks/LocalModUpdateTask.cpp
|
||||||
|
minecraft/mod/tasks/LocalDataPackParseTask.h
|
||||||
|
minecraft/mod/tasks/LocalDataPackParseTask.cpp
|
||||||
|
minecraft/mod/tasks/LocalResourcePackParseTask.h
|
||||||
|
minecraft/mod/tasks/LocalResourcePackParseTask.cpp
|
||||||
|
minecraft/mod/tasks/LocalTexturePackParseTask.h
|
||||||
|
minecraft/mod/tasks/LocalTexturePackParseTask.cpp
|
||||||
|
minecraft/mod/tasks/LocalShaderPackParseTask.h
|
||||||
|
minecraft/mod/tasks/LocalShaderPackParseTask.cpp
|
||||||
|
minecraft/mod/tasks/LocalWorldSaveParseTask.h
|
||||||
|
minecraft/mod/tasks/LocalWorldSaveParseTask.cpp
|
||||||
|
minecraft/mod/tasks/LocalResourceParse.h
|
||||||
|
minecraft/mod/tasks/LocalResourceParse.cpp
|
||||||
|
|
||||||
# Assets
|
# Assets
|
||||||
minecraft/AssetsUtils.h
|
minecraft/AssetsUtils.h
|
||||||
@ -345,42 +379,6 @@ set(MINECRAFT_SOURCES
|
|||||||
mojang/PackageManifest.cpp
|
mojang/PackageManifest.cpp
|
||||||
minecraft/Agent.h)
|
minecraft/Agent.h)
|
||||||
|
|
||||||
ecm_add_test(minecraft/GradleSpecifier_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME GradleSpecifier)
|
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
|
||||||
add_executable(PackageManifest
|
|
||||||
mojang/PackageManifest_test.cpp
|
|
||||||
)
|
|
||||||
target_link_libraries(PackageManifest
|
|
||||||
Launcher_logic
|
|
||||||
Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
)
|
|
||||||
target_include_directories(PackageManifest
|
|
||||||
PRIVATE ../cmake/UnitTest/
|
|
||||||
)
|
|
||||||
add_test(
|
|
||||||
NAME PackageManifest
|
|
||||||
COMMAND PackageManifest
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# TODO: needs minecraft/testdata
|
|
||||||
ecm_add_test(minecraft/MojangVersionFormat_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME MojangVersionFormat)
|
|
||||||
|
|
||||||
ecm_add_test(minecraft/Library_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME Library)
|
|
||||||
|
|
||||||
# FIXME: shares data with FileSystem test
|
|
||||||
# TODO: needs testdata
|
|
||||||
ecm_add_test(minecraft/mod/ModFolderModel_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME ModFolderModel)
|
|
||||||
|
|
||||||
ecm_add_test(minecraft/ParseUtils_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME ParseUtils)
|
|
||||||
|
|
||||||
# the screenshots feature
|
# the screenshots feature
|
||||||
set(SCREENSHOTS_SOURCES
|
set(SCREENSHOTS_SOURCES
|
||||||
screenshots/Screenshot.h
|
screenshots/Screenshot.h
|
||||||
@ -402,9 +400,6 @@ set(TASKS_SOURCES
|
|||||||
tasks/MultipleOptionsTask.cpp
|
tasks/MultipleOptionsTask.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_add_test(tasks/Task_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME Task)
|
|
||||||
|
|
||||||
set(SETTINGS_SOURCES
|
set(SETTINGS_SOURCES
|
||||||
# Settings
|
# Settings
|
||||||
settings/INIFile.cpp
|
settings/INIFile.cpp
|
||||||
@ -421,9 +416,6 @@ set(SETTINGS_SOURCES
|
|||||||
settings/SettingsObject.h
|
settings/SettingsObject.h
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_add_test(settings/INIFile_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME INIFile)
|
|
||||||
|
|
||||||
set(JAVA_SOURCES
|
set(JAVA_SOURCES
|
||||||
java/JavaChecker.h
|
java/JavaChecker.h
|
||||||
java/JavaChecker.cpp
|
java/JavaChecker.cpp
|
||||||
@ -439,9 +431,6 @@ set(JAVA_SOURCES
|
|||||||
java/JavaVersion.cpp
|
java/JavaVersion.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_add_test(java/JavaVersion_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME JavaVersion)
|
|
||||||
|
|
||||||
set(TRANSLATIONS_SOURCES
|
set(TRANSLATIONS_SOURCES
|
||||||
translations/TranslationsModel.h
|
translations/TranslationsModel.h
|
||||||
translations/TranslationsModel.cpp
|
translations/TranslationsModel.cpp
|
||||||
@ -481,7 +470,7 @@ set(API_SOURCES
|
|||||||
modplatform/ModIndex.h
|
modplatform/ModIndex.h
|
||||||
modplatform/ModIndex.cpp
|
modplatform/ModIndex.cpp
|
||||||
|
|
||||||
modplatform/ModAPI.h
|
modplatform/ResourceAPI.h
|
||||||
|
|
||||||
modplatform/EnsureMetadataTask.h
|
modplatform/EnsureMetadataTask.h
|
||||||
modplatform/EnsureMetadataTask.cpp
|
modplatform/EnsureMetadataTask.cpp
|
||||||
@ -492,8 +481,12 @@ set(API_SOURCES
|
|||||||
modplatform/flame/FlameAPI.cpp
|
modplatform/flame/FlameAPI.cpp
|
||||||
modplatform/modrinth/ModrinthAPI.h
|
modplatform/modrinth/ModrinthAPI.h
|
||||||
modplatform/modrinth/ModrinthAPI.cpp
|
modplatform/modrinth/ModrinthAPI.cpp
|
||||||
modplatform/helpers/NetworkModAPI.h
|
modplatform/helpers/NetworkResourceAPI.h
|
||||||
modplatform/helpers/NetworkModAPI.cpp
|
modplatform/helpers/NetworkResourceAPI.cpp
|
||||||
|
modplatform/helpers/HashUtils.h
|
||||||
|
modplatform/helpers/HashUtils.cpp
|
||||||
|
modplatform/helpers/OverrideUtils.h
|
||||||
|
modplatform/helpers/OverrideUtils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(FTB_SOURCES
|
set(FTB_SOURCES
|
||||||
@ -519,6 +512,8 @@ set(FLAME_SOURCES
|
|||||||
modplatform/flame/FileResolvingTask.cpp
|
modplatform/flame/FileResolvingTask.cpp
|
||||||
modplatform/flame/FlameCheckUpdate.cpp
|
modplatform/flame/FlameCheckUpdate.cpp
|
||||||
modplatform/flame/FlameCheckUpdate.h
|
modplatform/flame/FlameCheckUpdate.h
|
||||||
|
modplatform/flame/FlameInstanceCreationTask.h
|
||||||
|
modplatform/flame/FlameInstanceCreationTask.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(MODRINTH_SOURCES
|
set(MODRINTH_SOURCES
|
||||||
@ -528,13 +523,10 @@ set(MODRINTH_SOURCES
|
|||||||
modplatform/modrinth/ModrinthPackManifest.h
|
modplatform/modrinth/ModrinthPackManifest.h
|
||||||
modplatform/modrinth/ModrinthCheckUpdate.cpp
|
modplatform/modrinth/ModrinthCheckUpdate.cpp
|
||||||
modplatform/modrinth/ModrinthCheckUpdate.h
|
modplatform/modrinth/ModrinthCheckUpdate.h
|
||||||
)
|
modplatform/modrinth/ModrinthInstanceCreationTask.cpp
|
||||||
|
modplatform/modrinth/ModrinthInstanceCreationTask.h
|
||||||
set(MODPACKSCH_SOURCES
|
modplatform/modrinth/ModrinthPackExportTask.cpp
|
||||||
modplatform/modpacksch/FTBPackInstallTask.h
|
modplatform/modrinth/ModrinthPackExportTask.h
|
||||||
modplatform/modpacksch/FTBPackInstallTask.cpp
|
|
||||||
modplatform/modpacksch/FTBPackManifest.h
|
|
||||||
modplatform/modpacksch/FTBPackManifest.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PACKWIZ_SOURCES
|
set(PACKWIZ_SOURCES
|
||||||
@ -542,9 +534,6 @@ set(PACKWIZ_SOURCES
|
|||||||
modplatform/packwiz/Packwiz.cpp
|
modplatform/packwiz/Packwiz.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: needs modplatform/packwiz/testdata
|
|
||||||
ecm_add_test(modplatform/packwiz/Packwiz_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
|
||||||
TEST_NAME Packwiz)
|
|
||||||
|
|
||||||
set(TECHNIC_SOURCES
|
set(TECHNIC_SOURCES
|
||||||
modplatform/technic/SingleZipPackInstallTask.h
|
modplatform/technic/SingleZipPackInstallTask.h
|
||||||
@ -568,14 +557,87 @@ set(ATLAUNCHER_SOURCES
|
|||||||
modplatform/atlauncher/ATLShareCode.h
|
modplatform/atlauncher/ATLShareCode.h
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_add_test(meta/Index_test.cpp LINK_LIBRARIES Launcher_logic Qt${QT_VERSION_MAJOR}::Test
|
set(LINKEXE_SOURCES
|
||||||
TEST_NAME Index)
|
filelink/FileLink.h
|
||||||
|
filelink/FileLink.cpp
|
||||||
|
FileSystem.h
|
||||||
|
FileSystem.cpp
|
||||||
|
Exception.h
|
||||||
|
StringUtils.h
|
||||||
|
StringUtils.cpp
|
||||||
|
DesktopServices.h
|
||||||
|
DesktopServices.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
######## Logging categories ########
|
||||||
|
|
||||||
|
ecm_qt_declare_logging_category(CORE_SOURCES
|
||||||
|
HEADER Logging.h
|
||||||
|
IDENTIFIER authCredentials
|
||||||
|
CATEGORY_NAME "launcher.auth.credentials"
|
||||||
|
DEFAULT_SEVERITY Warning
|
||||||
|
DESCRIPTION "Secrets and credentials for debugging purposes"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_qt_export_logging_category(
|
||||||
|
IDENTIFIER taskLogC
|
||||||
|
CATEGORY_NAME "launcher.task"
|
||||||
|
DEFAULT_SEVERITY Debug
|
||||||
|
DESCRIPTION "Task actions"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_qt_export_logging_category(
|
||||||
|
IDENTIFIER taskNetLogC
|
||||||
|
CATEGORY_NAME "launcher.task.net"
|
||||||
|
DEFAULT_SEVERITY Debug
|
||||||
|
DESCRIPTION "task network action"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_qt_export_logging_category(
|
||||||
|
IDENTIFIER taskDownloadLogC
|
||||||
|
CATEGORY_NAME "launcher.task.net.download"
|
||||||
|
DEFAULT_SEVERITY Debug
|
||||||
|
DESCRIPTION "task network download actions"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
ecm_qt_export_logging_category(
|
||||||
|
IDENTIFIER taskUploadLogC
|
||||||
|
CATEGORY_NAME "launcher.task.net.upload"
|
||||||
|
DEFAULT_SEVERITY Debug
|
||||||
|
DESCRIPTION "task network upload actions"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_qt_export_logging_category(
|
||||||
|
IDENTIFIER taskMetaCacheLogC
|
||||||
|
CATEGORY_NAME "launcher.task.net.metacache"
|
||||||
|
DEFAULT_SEVERITY Debug
|
||||||
|
DESCRIPTION "task network meta-cache actions"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
ecm_qt_export_logging_category(
|
||||||
|
IDENTIFIER taskHttpMetaCacheLogC
|
||||||
|
CATEGORY_NAME "launcher.task.net.metacache.http"
|
||||||
|
DEFAULT_SEVERITY Debug
|
||||||
|
DESCRIPTION "task network http meta-cache actions"
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(KDE_INSTALL_LOGGINGCATEGORIESDIR) # only install if there is a standard path for this
|
||||||
|
ecm_qt_install_logging_categories(
|
||||||
|
EXPORT "${Launcher_Name}"
|
||||||
|
DESTINATION "${KDE_INSTALL_LOGGINGCATEGORIESDIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
################################ COMPILE ################################
|
################################ COMPILE ################################
|
||||||
|
|
||||||
# we need zlib
|
|
||||||
find_package(ZLIB REQUIRED)
|
|
||||||
|
|
||||||
set(LOGIC_SOURCES
|
set(LOGIC_SOURCES
|
||||||
${CORE_SOURCES}
|
${CORE_SOURCES}
|
||||||
${PATHMATCHER_SOURCES}
|
${PATHMATCHER_SOURCES}
|
||||||
@ -596,7 +658,6 @@ set(LOGIC_SOURCES
|
|||||||
${FTB_SOURCES}
|
${FTB_SOURCES}
|
||||||
${FLAME_SOURCES}
|
${FLAME_SOURCES}
|
||||||
${MODRINTH_SOURCES}
|
${MODRINTH_SOURCES}
|
||||||
${MODPACKSCH_SOURCES}
|
|
||||||
${PACKWIZ_SOURCES}
|
${PACKWIZ_SOURCES}
|
||||||
${TECHNIC_SOURCES}
|
${TECHNIC_SOURCES}
|
||||||
${ATLAUNCHER_SOURCES}
|
${ATLAUNCHER_SOURCES}
|
||||||
@ -610,8 +671,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
# Application base
|
# Application base
|
||||||
Application.h
|
Application.h
|
||||||
Application.cpp
|
Application.cpp
|
||||||
UpdateController.cpp
|
DataMigrationTask.h
|
||||||
UpdateController.h
|
DataMigrationTask.cpp
|
||||||
ApplicationMessage.h
|
ApplicationMessage.h
|
||||||
ApplicationMessage.cpp
|
ApplicationMessage.cpp
|
||||||
|
|
||||||
@ -620,7 +681,7 @@ SET(LAUNCHER_SOURCES
|
|||||||
DesktopServices.cpp
|
DesktopServices.cpp
|
||||||
VersionProxyModel.h
|
VersionProxyModel.h
|
||||||
VersionProxyModel.cpp
|
VersionProxyModel.cpp
|
||||||
HoeDown.h
|
Markdown.h
|
||||||
|
|
||||||
# Super secret!
|
# Super secret!
|
||||||
KonamiCode.h
|
KonamiCode.h
|
||||||
@ -633,9 +694,12 @@ SET(LAUNCHER_SOURCES
|
|||||||
resources/pe_light/pe_light.qrc
|
resources/pe_light/pe_light.qrc
|
||||||
resources/pe_colored/pe_colored.qrc
|
resources/pe_colored/pe_colored.qrc
|
||||||
resources/pe_blue/pe_blue.qrc
|
resources/pe_blue/pe_blue.qrc
|
||||||
|
resources/breeze_dark/breeze_dark.qrc
|
||||||
|
resources/breeze_light/breeze_light.qrc
|
||||||
resources/OSX/OSX.qrc
|
resources/OSX/OSX.qrc
|
||||||
resources/iOS/iOS.qrc
|
resources/iOS/iOS.qrc
|
||||||
resources/flat/flat.qrc
|
resources/flat/flat.qrc
|
||||||
|
resources/flat_white/flat_white.qrc
|
||||||
resources/documents/documents.qrc
|
resources/documents/documents.qrc
|
||||||
../${Launcher_Branding_LogoQRC}
|
../${Launcher_Branding_LogoQRC}
|
||||||
|
|
||||||
@ -658,6 +722,10 @@ SET(LAUNCHER_SOURCES
|
|||||||
# FIXME: maybe find a better home for this.
|
# FIXME: maybe find a better home for this.
|
||||||
SkinUtils.cpp
|
SkinUtils.cpp
|
||||||
SkinUtils.h
|
SkinUtils.h
|
||||||
|
FileIgnoreProxy.cpp
|
||||||
|
FileIgnoreProxy.h
|
||||||
|
FastFileIconProvider.cpp
|
||||||
|
FastFileIconProvider.h
|
||||||
|
|
||||||
# GUI - setup wizard
|
# GUI - setup wizard
|
||||||
ui/setupwizard/SetupWizard.h
|
ui/setupwizard/SetupWizard.h
|
||||||
@ -669,6 +737,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/setupwizard/LanguageWizardPage.h
|
ui/setupwizard/LanguageWizardPage.h
|
||||||
ui/setupwizard/PasteWizardPage.cpp
|
ui/setupwizard/PasteWizardPage.cpp
|
||||||
ui/setupwizard/PasteWizardPage.h
|
ui/setupwizard/PasteWizardPage.h
|
||||||
|
ui/setupwizard/ThemeWizardPage.cpp
|
||||||
|
ui/setupwizard/ThemeWizardPage.h
|
||||||
|
|
||||||
# GUI - themes
|
# GUI - themes
|
||||||
ui/themes/FusionTheme.cpp
|
ui/themes/FusionTheme.cpp
|
||||||
@ -683,6 +753,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/themes/ITheme.h
|
ui/themes/ITheme.h
|
||||||
ui/themes/SystemTheme.cpp
|
ui/themes/SystemTheme.cpp
|
||||||
ui/themes/SystemTheme.h
|
ui/themes/SystemTheme.h
|
||||||
|
ui/themes/ThemeManager.cpp
|
||||||
|
ui/themes/ThemeManager.h
|
||||||
|
|
||||||
# Processes
|
# Processes
|
||||||
LaunchController.h
|
LaunchController.h
|
||||||
@ -707,9 +779,14 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/instance/GameOptionsPage.h
|
ui/pages/instance/GameOptionsPage.h
|
||||||
ui/pages/instance/VersionPage.cpp
|
ui/pages/instance/VersionPage.cpp
|
||||||
ui/pages/instance/VersionPage.h
|
ui/pages/instance/VersionPage.h
|
||||||
|
ui/pages/instance/ManagedPackPage.cpp
|
||||||
|
ui/pages/instance/ManagedPackPage.h
|
||||||
ui/pages/instance/TexturePackPage.h
|
ui/pages/instance/TexturePackPage.h
|
||||||
|
ui/pages/instance/TexturePackPage.cpp
|
||||||
ui/pages/instance/ResourcePackPage.h
|
ui/pages/instance/ResourcePackPage.h
|
||||||
|
ui/pages/instance/ResourcePackPage.cpp
|
||||||
ui/pages/instance/ShaderPackPage.h
|
ui/pages/instance/ShaderPackPage.h
|
||||||
|
ui/pages/instance/ShaderPackPage.cpp
|
||||||
ui/pages/instance/ModFolderPage.cpp
|
ui/pages/instance/ModFolderPage.cpp
|
||||||
ui/pages/instance/ModFolderPage.h
|
ui/pages/instance/ModFolderPage.h
|
||||||
ui/pages/instance/NotesPage.cpp
|
ui/pages/instance/NotesPage.cpp
|
||||||
@ -751,11 +828,26 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/modplatform/VanillaPage.cpp
|
ui/pages/modplatform/VanillaPage.cpp
|
||||||
ui/pages/modplatform/VanillaPage.h
|
ui/pages/modplatform/VanillaPage.h
|
||||||
|
|
||||||
|
ui/pages/modplatform/ResourcePage.cpp
|
||||||
|
ui/pages/modplatform/ResourcePage.h
|
||||||
|
ui/pages/modplatform/ResourceModel.cpp
|
||||||
|
ui/pages/modplatform/ResourceModel.h
|
||||||
|
|
||||||
ui/pages/modplatform/ModPage.cpp
|
ui/pages/modplatform/ModPage.cpp
|
||||||
ui/pages/modplatform/ModPage.h
|
ui/pages/modplatform/ModPage.h
|
||||||
ui/pages/modplatform/ModModel.cpp
|
ui/pages/modplatform/ModModel.cpp
|
||||||
ui/pages/modplatform/ModModel.h
|
ui/pages/modplatform/ModModel.h
|
||||||
|
|
||||||
|
ui/pages/modplatform/ResourcePackPage.cpp
|
||||||
|
ui/pages/modplatform/ResourcePackModel.cpp
|
||||||
|
|
||||||
|
# Needed for MOC to find them without a corresponding .cpp
|
||||||
|
ui/pages/modplatform/TexturePackPage.h
|
||||||
|
ui/pages/modplatform/TexturePackModel.cpp
|
||||||
|
|
||||||
|
ui/pages/modplatform/ShaderPackPage.cpp
|
||||||
|
ui/pages/modplatform/ShaderPackModel.cpp
|
||||||
|
|
||||||
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
|
||||||
@ -764,13 +856,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h
|
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h
|
||||||
ui/pages/modplatform/atlauncher/AtlPage.cpp
|
ui/pages/modplatform/atlauncher/AtlPage.cpp
|
||||||
ui/pages/modplatform/atlauncher/AtlPage.h
|
ui/pages/modplatform/atlauncher/AtlPage.h
|
||||||
|
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp
|
||||||
ui/pages/modplatform/ftb/FtbFilterModel.cpp
|
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h
|
||||||
ui/pages/modplatform/ftb/FtbFilterModel.h
|
|
||||||
ui/pages/modplatform/ftb/FtbListModel.cpp
|
|
||||||
ui/pages/modplatform/ftb/FtbListModel.h
|
|
||||||
ui/pages/modplatform/ftb/FtbPage.cpp
|
|
||||||
ui/pages/modplatform/ftb/FtbPage.h
|
|
||||||
|
|
||||||
ui/pages/modplatform/legacy_ftb/Page.cpp
|
ui/pages/modplatform/legacy_ftb/Page.cpp
|
||||||
ui/pages/modplatform/legacy_ftb/Page.h
|
ui/pages/modplatform/legacy_ftb/Page.h
|
||||||
@ -781,10 +868,10 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/modplatform/flame/FlameModel.h
|
ui/pages/modplatform/flame/FlameModel.h
|
||||||
ui/pages/modplatform/flame/FlamePage.cpp
|
ui/pages/modplatform/flame/FlamePage.cpp
|
||||||
ui/pages/modplatform/flame/FlamePage.h
|
ui/pages/modplatform/flame/FlamePage.h
|
||||||
ui/pages/modplatform/flame/FlameModModel.cpp
|
ui/pages/modplatform/flame/FlameResourceModels.cpp
|
||||||
ui/pages/modplatform/flame/FlameModModel.h
|
ui/pages/modplatform/flame/FlameResourceModels.h
|
||||||
ui/pages/modplatform/flame/FlameModPage.cpp
|
ui/pages/modplatform/flame/FlameResourcePages.cpp
|
||||||
ui/pages/modplatform/flame/FlameModPage.h
|
ui/pages/modplatform/flame/FlameResourcePages.h
|
||||||
|
|
||||||
ui/pages/modplatform/modrinth/ModrinthPage.cpp
|
ui/pages/modplatform/modrinth/ModrinthPage.cpp
|
||||||
ui/pages/modplatform/modrinth/ModrinthPage.h
|
ui/pages/modplatform/modrinth/ModrinthPage.h
|
||||||
@ -799,10 +886,10 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/pages/modplatform/ImportPage.cpp
|
ui/pages/modplatform/ImportPage.cpp
|
||||||
ui/pages/modplatform/ImportPage.h
|
ui/pages/modplatform/ImportPage.h
|
||||||
|
|
||||||
ui/pages/modplatform/modrinth/ModrinthModModel.cpp
|
ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp
|
||||||
ui/pages/modplatform/modrinth/ModrinthModModel.h
|
ui/pages/modplatform/modrinth/ModrinthResourceModels.h
|
||||||
ui/pages/modplatform/modrinth/ModrinthModPage.cpp
|
ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp
|
||||||
ui/pages/modplatform/modrinth/ModrinthModPage.h
|
ui/pages/modplatform/modrinth/ModrinthResourcePages.h
|
||||||
|
|
||||||
# GUI - dialogs
|
# GUI - dialogs
|
||||||
ui/dialogs/AboutDialog.cpp
|
ui/dialogs/AboutDialog.cpp
|
||||||
@ -819,8 +906,12 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/dialogs/EditAccountDialog.h
|
ui/dialogs/EditAccountDialog.h
|
||||||
ui/dialogs/ExportInstanceDialog.cpp
|
ui/dialogs/ExportInstanceDialog.cpp
|
||||||
ui/dialogs/ExportInstanceDialog.h
|
ui/dialogs/ExportInstanceDialog.h
|
||||||
|
ui/dialogs/ExportMrPackDialog.cpp
|
||||||
|
ui/dialogs/ExportMrPackDialog.h
|
||||||
ui/dialogs/IconPickerDialog.cpp
|
ui/dialogs/IconPickerDialog.cpp
|
||||||
ui/dialogs/IconPickerDialog.h
|
ui/dialogs/IconPickerDialog.h
|
||||||
|
ui/dialogs/ImportResourceDialog.cpp
|
||||||
|
ui/dialogs/ImportResourceDialog.h
|
||||||
ui/dialogs/LoginDialog.cpp
|
ui/dialogs/LoginDialog.cpp
|
||||||
ui/dialogs/LoginDialog.h
|
ui/dialogs/LoginDialog.h
|
||||||
ui/dialogs/MSALoginDialog.cpp
|
ui/dialogs/MSALoginDialog.cpp
|
||||||
@ -839,16 +930,16 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/dialogs/ProgressDialog.h
|
ui/dialogs/ProgressDialog.h
|
||||||
ui/dialogs/ReviewMessageBox.cpp
|
ui/dialogs/ReviewMessageBox.cpp
|
||||||
ui/dialogs/ReviewMessageBox.h
|
ui/dialogs/ReviewMessageBox.h
|
||||||
ui/dialogs/UpdateDialog.cpp
|
|
||||||
ui/dialogs/UpdateDialog.h
|
|
||||||
ui/dialogs/VersionSelectDialog.cpp
|
ui/dialogs/VersionSelectDialog.cpp
|
||||||
ui/dialogs/VersionSelectDialog.h
|
ui/dialogs/VersionSelectDialog.h
|
||||||
ui/dialogs/SkinUploadDialog.cpp
|
ui/dialogs/SkinUploadDialog.cpp
|
||||||
ui/dialogs/SkinUploadDialog.h
|
ui/dialogs/SkinUploadDialog.h
|
||||||
ui/dialogs/ModDownloadDialog.cpp
|
ui/dialogs/ResourceDownloadDialog.cpp
|
||||||
ui/dialogs/ModDownloadDialog.h
|
ui/dialogs/ResourceDownloadDialog.h
|
||||||
ui/dialogs/ScrollMessageBox.cpp
|
ui/dialogs/ScrollMessageBox.cpp
|
||||||
ui/dialogs/ScrollMessageBox.h
|
ui/dialogs/ScrollMessageBox.h
|
||||||
|
ui/dialogs/BlockedModsDialog.cpp
|
||||||
|
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/ModUpdateDialog.cpp
|
||||||
@ -875,8 +966,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/widgets/LineSeparator.h
|
ui/widgets/LineSeparator.h
|
||||||
ui/widgets/LogView.cpp
|
ui/widgets/LogView.cpp
|
||||||
ui/widgets/LogView.h
|
ui/widgets/LogView.h
|
||||||
ui/widgets/MCModInfoFrame.cpp
|
ui/widgets/InfoFrame.cpp
|
||||||
ui/widgets/MCModInfoFrame.h
|
ui/widgets/InfoFrame.h
|
||||||
ui/widgets/ModFilterWidget.cpp
|
ui/widgets/ModFilterWidget.cpp
|
||||||
ui/widgets/ModFilterWidget.h
|
ui/widgets/ModFilterWidget.h
|
||||||
ui/widgets/ModListView.cpp
|
ui/widgets/ModListView.cpp
|
||||||
@ -884,6 +975,14 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/widgets/PageContainer.cpp
|
ui/widgets/PageContainer.cpp
|
||||||
ui/widgets/PageContainer.h
|
ui/widgets/PageContainer.h
|
||||||
ui/widgets/PageContainer_p.h
|
ui/widgets/PageContainer_p.h
|
||||||
|
ui/widgets/ProjectDescriptionPage.h
|
||||||
|
ui/widgets/ProjectDescriptionPage.cpp
|
||||||
|
ui/widgets/VariableSizedImageObject.h
|
||||||
|
ui/widgets/VariableSizedImageObject.cpp
|
||||||
|
ui/widgets/ProjectItem.h
|
||||||
|
ui/widgets/ProjectItem.cpp
|
||||||
|
ui/widgets/SubTaskProgressBar.h
|
||||||
|
ui/widgets/SubTaskProgressBar.cpp
|
||||||
ui/widgets/VersionListView.cpp
|
ui/widgets/VersionListView.cpp
|
||||||
ui/widgets/VersionListView.h
|
ui/widgets/VersionListView.h
|
||||||
ui/widgets/VersionSelectWidget.cpp
|
ui/widgets/VersionSelectWidget.cpp
|
||||||
@ -892,6 +991,8 @@ SET(LAUNCHER_SOURCES
|
|||||||
ui/widgets/ProgressWidget.cpp
|
ui/widgets/ProgressWidget.cpp
|
||||||
ui/widgets/WideBar.h
|
ui/widgets/WideBar.h
|
||||||
ui/widgets/WideBar.cpp
|
ui/widgets/WideBar.cpp
|
||||||
|
ui/widgets/ThemeCustomizationWidget.h
|
||||||
|
ui/widgets/ThemeCustomizationWidget.cpp
|
||||||
|
|
||||||
# GUI - instance group view
|
# GUI - instance group view
|
||||||
ui/instanceview/InstanceProxyModel.cpp
|
ui/instanceview/InstanceProxyModel.cpp
|
||||||
@ -908,7 +1009,9 @@ SET(LAUNCHER_SOURCES
|
|||||||
)
|
)
|
||||||
|
|
||||||
qt_wrap_ui(LAUNCHER_UI
|
qt_wrap_ui(LAUNCHER_UI
|
||||||
|
ui/MainWindow.ui
|
||||||
ui/setupwizard/PasteWizardPage.ui
|
ui/setupwizard/PasteWizardPage.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
|
||||||
@ -924,33 +1027,36 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/pages/instance/OtherLogsPage.ui
|
ui/pages/instance/OtherLogsPage.ui
|
||||||
ui/pages/instance/InstanceSettingsPage.ui
|
ui/pages/instance/InstanceSettingsPage.ui
|
||||||
ui/pages/instance/VersionPage.ui
|
ui/pages/instance/VersionPage.ui
|
||||||
|
ui/pages/instance/ManagedPackPage.ui
|
||||||
ui/pages/instance/WorldListPage.ui
|
ui/pages/instance/WorldListPage.ui
|
||||||
ui/pages/instance/ScreenshotsPage.ui
|
ui/pages/instance/ScreenshotsPage.ui
|
||||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
||||||
ui/pages/modplatform/atlauncher/AtlPage.ui
|
ui/pages/modplatform/atlauncher/AtlPage.ui
|
||||||
ui/pages/modplatform/VanillaPage.ui
|
ui/pages/modplatform/VanillaPage.ui
|
||||||
ui/pages/modplatform/ModPage.ui
|
ui/pages/modplatform/ResourcePage.ui
|
||||||
ui/pages/modplatform/flame/FlamePage.ui
|
ui/pages/modplatform/flame/FlamePage.ui
|
||||||
ui/pages/modplatform/legacy_ftb/Page.ui
|
ui/pages/modplatform/legacy_ftb/Page.ui
|
||||||
ui/pages/modplatform/ImportPage.ui
|
ui/pages/modplatform/ImportPage.ui
|
||||||
ui/pages/modplatform/ftb/FtbPage.ui
|
|
||||||
ui/pages/modplatform/modrinth/ModrinthPage.ui
|
ui/pages/modplatform/modrinth/ModrinthPage.ui
|
||||||
ui/pages/modplatform/technic/TechnicPage.ui
|
ui/pages/modplatform/technic/TechnicPage.ui
|
||||||
ui/widgets/InstanceCardWidget.ui
|
ui/widgets/InstanceCardWidget.ui
|
||||||
ui/widgets/CustomCommands.ui
|
ui/widgets/CustomCommands.ui
|
||||||
ui/widgets/MCModInfoFrame.ui
|
ui/widgets/InfoFrame.ui
|
||||||
ui/widgets/ModFilterWidget.ui
|
ui/widgets/ModFilterWidget.ui
|
||||||
|
ui/widgets/SubTaskProgressBar.ui
|
||||||
|
ui/widgets/ThemeCustomizationWidget.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
|
||||||
ui/dialogs/NewInstanceDialog.ui
|
ui/dialogs/NewInstanceDialog.ui
|
||||||
ui/dialogs/UpdateDialog.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/SkinUploadDialog.ui
|
||||||
ui/dialogs/ExportInstanceDialog.ui
|
ui/dialogs/ExportInstanceDialog.ui
|
||||||
|
ui/dialogs/ExportMrPackDialog.ui
|
||||||
ui/dialogs/IconPickerDialog.ui
|
ui/dialogs/IconPickerDialog.ui
|
||||||
|
ui/dialogs/ImportResourceDialog.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
|
||||||
@ -958,6 +1064,7 @@ qt_wrap_ui(LAUNCHER_UI
|
|||||||
ui/dialogs/EditAccountDialog.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/ChooseProviderDialog.ui
|
ui/dialogs/ChooseProviderDialog.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -968,6 +1075,8 @@ qt_add_resources(LAUNCHER_RESOURCES
|
|||||||
resources/pe_light/pe_light.qrc
|
resources/pe_light/pe_light.qrc
|
||||||
resources/pe_colored/pe_colored.qrc
|
resources/pe_colored/pe_colored.qrc
|
||||||
resources/pe_blue/pe_blue.qrc
|
resources/pe_blue/pe_blue.qrc
|
||||||
|
resources/breeze_dark/breeze_dark.qrc
|
||||||
|
resources/breeze_light/breeze_light.qrc
|
||||||
resources/OSX/OSX.qrc
|
resources/OSX/OSX.qrc
|
||||||
resources/iOS/iOS.qrc
|
resources/iOS/iOS.qrc
|
||||||
resources/flat/flat.qrc
|
resources/flat/flat.qrc
|
||||||
@ -982,17 +1091,18 @@ endif()
|
|||||||
|
|
||||||
# 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})
|
||||||
|
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_link_libraries(Launcher_logic
|
target_link_libraries(Launcher_logic
|
||||||
systeminfo
|
systeminfo
|
||||||
Launcher_classparser
|
|
||||||
Launcher_murmur2
|
Launcher_murmur2
|
||||||
nbt++
|
nbt++
|
||||||
${ZLIB_LIBRARIES}
|
${ZLIB_LIBRARIES}
|
||||||
optional-bare
|
tomlplusplus::tomlplusplus
|
||||||
tomlc99
|
qdcss
|
||||||
BuildConfig
|
BuildConfig
|
||||||
Katabasis
|
Katabasis
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
ghcFilesystem::ghc_filesystem
|
||||||
)
|
)
|
||||||
|
|
||||||
if (UNIX AND NOT CYGWIN AND NOT APPLE)
|
if (UNIX AND NOT CYGWIN AND NOT APPLE)
|
||||||
@ -1012,7 +1122,7 @@ target_link_libraries(Launcher_logic
|
|||||||
)
|
)
|
||||||
target_link_libraries(Launcher_logic
|
target_link_libraries(Launcher_logic
|
||||||
QuaZip::QuaZip
|
QuaZip::QuaZip
|
||||||
hoedown
|
cmark::cmark
|
||||||
LocalPeer
|
LocalPeer
|
||||||
Launcher_rainbow
|
Launcher_rainbow
|
||||||
)
|
)
|
||||||
@ -1057,6 +1167,41 @@ install(TARGETS ${Launcher_Name}
|
|||||||
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
|
||||||
|
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_link_libraries(filelink_logic
|
||||||
|
systeminfo
|
||||||
|
BuildConfig
|
||||||
|
ghcFilesystem::ghc_filesystem
|
||||||
|
Qt${QT_VERSION_MAJOR}::Widgets
|
||||||
|
Qt${QT_VERSION_MAJOR}::Core
|
||||||
|
Qt${QT_VERSION_MAJOR}::Network
|
||||||
|
# Qt${QT_VERSION_MAJOR}::Concurrent
|
||||||
|
${Launcher_QT_LIBS}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable("${Launcher_Name}_filelink" WIN32 filelink/main.cpp)
|
||||||
|
|
||||||
|
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
|
||||||
|
|
||||||
|
target_link_libraries("${Launcher_Name}_filelink" filelink_logic)
|
||||||
|
|
||||||
|
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||||
|
set_target_properties("${Launcher_Name}_filelink" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_filelink")
|
||||||
|
endif()
|
||||||
|
if(DEFINED Launcher_BINARY_RPATH)
|
||||||
|
SET_TARGET_PROPERTIES("${Launcher_Name}_filelink" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(TARGETS "${Launcher_Name}_filelink"
|
||||||
|
BUNDLE DESTINATION "." COMPONENT Runtime
|
||||||
|
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
|
||||||
|
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
|
||||||
|
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (UNIX AND APPLE)
|
if (UNIX AND APPLE)
|
||||||
# Add Sparkle updater
|
# Add Sparkle updater
|
||||||
# It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of
|
# It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of
|
||||||
@ -1073,97 +1218,106 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
|||||||
CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \" \")"
|
CODE "file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}/qt.conf\" \" \")"
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
)
|
)
|
||||||
|
# add qtlogging.ini as a config file
|
||||||
|
install(
|
||||||
|
FILES "qtlogging.ini"
|
||||||
|
DESTINATION ${CMAKE_INSTALL_PREFIX}/${RESOURCES_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
)
|
||||||
# Bundle plugins
|
# Bundle plugins
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
# Image formats
|
||||||
# Image formats
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "tga|tiff|mng" EXCLUDE
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "tga|tiff|mng" EXCLUDE
|
||||||
|
REGEX "d\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
# Icon engines
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "fontawesome" EXCLUDE
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "fontawesome" EXCLUDE
|
||||||
|
REGEX "d\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
# Platform plugins
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
|
COMPONENT Runtime
|
||||||
|
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||||
|
REGEX "[^2]d\\." EXCLUDE
|
||||||
|
REGEX "_debug\\." EXCLUDE
|
||||||
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
)
|
||||||
|
# Style plugins
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/styles")
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
REGEX "tga|tiff|mng" EXCLUDE
|
|
||||||
)
|
)
|
||||||
# Icon engines
|
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
REGEX "fontawesome" EXCLUDE
|
|
||||||
)
|
|
||||||
# Platform plugins
|
|
||||||
install(
|
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
|
||||||
COMPONENT Runtime
|
|
||||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
|
||||||
)
|
|
||||||
# Style plugins
|
|
||||||
if(EXISTS "${QT_PLUGINS_DIR}/styles")
|
|
||||||
install(
|
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
|
||||||
COMPONENT Runtime
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
# TLS plugins (Qt 6 only)
|
|
||||||
if(EXISTS "${QT_PLUGINS_DIR}/tls")
|
|
||||||
install(
|
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
|
||||||
COMPONENT Runtime
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
# Image formats
|
|
||||||
install(
|
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
|
||||||
COMPONENT Runtime
|
|
||||||
REGEX "tga|tiff|mng" EXCLUDE
|
|
||||||
REGEX "d\\." EXCLUDE
|
REGEX "d\\." EXCLUDE
|
||||||
REGEX "_debug\\." EXCLUDE
|
REGEX "_debug\\." EXCLUDE
|
||||||
REGEX "\\.dSYM" EXCLUDE
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
)
|
)
|
||||||
# Icon engines
|
endif()
|
||||||
|
# TLS plugins (Qt 6 only)
|
||||||
|
if(EXISTS "${QT_PLUGINS_DIR}/tls")
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
||||||
|
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
REGEX "fontawesome" EXCLUDE
|
PATTERN "*qopensslbackend*" EXCLUDE
|
||||||
REGEX "d\\." EXCLUDE
|
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||||
REGEX "_debug\\." EXCLUDE
|
|
||||||
REGEX "\\.dSYM" EXCLUDE
|
|
||||||
)
|
)
|
||||||
# Platform plugins
|
|
||||||
install(
|
install(
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
||||||
|
CONFIGURATIONS Release MinSizeRel
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
DESTINATION ${PLUGIN_DEST_DIR}
|
||||||
COMPONENT Runtime
|
COMPONENT Runtime
|
||||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
REGEX "dd\\." EXCLUDE
|
||||||
REGEX "d\\." EXCLUDE
|
|
||||||
REGEX "_debug\\." EXCLUDE
|
REGEX "_debug\\." EXCLUDE
|
||||||
REGEX "\\.dSYM" EXCLUDE
|
REGEX "\\.dSYM" EXCLUDE
|
||||||
|
PATTERN "*qopensslbackend*" EXCLUDE
|
||||||
|
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||||
)
|
)
|
||||||
# Style plugins
|
|
||||||
if(EXISTS "${QT_PLUGINS_DIR}/styles")
|
|
||||||
install(
|
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
|
||||||
COMPONENT Runtime
|
|
||||||
REGEX "d\\." EXCLUDE
|
|
||||||
REGEX "_debug\\." EXCLUDE
|
|
||||||
REGEX "\\.dSYM" EXCLUDE
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
# TLS plugins (Qt 6 only)
|
|
||||||
if(EXISTS "${QT_PLUGINS_DIR}/tls")
|
|
||||||
install(
|
|
||||||
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
|
||||||
DESTINATION ${PLUGIN_DEST_DIR}
|
|
||||||
COMPONENT Runtime
|
|
||||||
REGEX "_debug\\." EXCLUDE
|
|
||||||
REGEX "\\.dSYM" EXCLUDE
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||||
|
@ -92,412 +92,4 @@ QStringList splitArgs(QString args)
|
|||||||
argv << current;
|
argv << current;
|
||||||
return argv;
|
return argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle)
|
|
||||||
{
|
|
||||||
m_flagStyle = flagStyle;
|
|
||||||
m_argStyle = argStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// styles setter/getter
|
|
||||||
void Parser::setArgumentStyle(ArgumentStyle::Enum style)
|
|
||||||
{
|
|
||||||
m_argStyle = style;
|
|
||||||
}
|
|
||||||
ArgumentStyle::Enum Parser::argumentStyle()
|
|
||||||
{
|
|
||||||
return m_argStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::setFlagStyle(FlagStyle::Enum style)
|
|
||||||
{
|
|
||||||
m_flagStyle = style;
|
|
||||||
}
|
|
||||||
FlagStyle::Enum Parser::flagStyle()
|
|
||||||
{
|
|
||||||
return m_flagStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup methods
|
|
||||||
void Parser::addSwitch(QString name, bool def)
|
|
||||||
{
|
|
||||||
if (m_params.contains(name))
|
|
||||||
throw "Name not unique";
|
|
||||||
|
|
||||||
OptionDef *param = new OptionDef;
|
|
||||||
param->type = otSwitch;
|
|
||||||
param->name = name;
|
|
||||||
param->metavar = QString("<%1>").arg(name);
|
|
||||||
param->def = def;
|
|
||||||
|
|
||||||
m_options[name] = param;
|
|
||||||
m_params[name] = (CommonDef *)param;
|
|
||||||
m_optionList.append(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::addOption(QString name, QVariant def)
|
|
||||||
{
|
|
||||||
if (m_params.contains(name))
|
|
||||||
throw "Name not unique";
|
|
||||||
|
|
||||||
OptionDef *param = new OptionDef;
|
|
||||||
param->type = otOption;
|
|
||||||
param->name = name;
|
|
||||||
param->metavar = QString("<%1>").arg(name);
|
|
||||||
param->def = def;
|
|
||||||
|
|
||||||
m_options[name] = param;
|
|
||||||
m_params[name] = (CommonDef *)param;
|
|
||||||
m_optionList.append(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::addArgument(QString name, bool required, QVariant def)
|
|
||||||
{
|
|
||||||
if (m_params.contains(name))
|
|
||||||
throw "Name not unique";
|
|
||||||
|
|
||||||
PositionalDef *param = new PositionalDef;
|
|
||||||
param->name = name;
|
|
||||||
param->def = def;
|
|
||||||
param->required = required;
|
|
||||||
param->metavar = name;
|
|
||||||
|
|
||||||
m_positionals.append(param);
|
|
||||||
m_params[name] = (CommonDef *)param;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::addDocumentation(QString name, QString doc, QString metavar)
|
|
||||||
{
|
|
||||||
if (!m_params.contains(name))
|
|
||||||
throw "Name does not exist";
|
|
||||||
|
|
||||||
CommonDef *param = m_params[name];
|
|
||||||
param->doc = doc;
|
|
||||||
if (!metavar.isNull())
|
|
||||||
param->metavar = metavar;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::addShortOpt(QString name, QChar flag)
|
|
||||||
{
|
|
||||||
if (!m_params.contains(name))
|
|
||||||
throw "Name does not exist";
|
|
||||||
if (!m_options.contains(name))
|
|
||||||
throw "Name is not an Option or Swtich";
|
|
||||||
|
|
||||||
OptionDef *param = m_options[name];
|
|
||||||
m_flags[flag] = param;
|
|
||||||
param->flag = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
// help methods
|
|
||||||
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
|
|
||||||
{
|
|
||||||
QStringList help;
|
|
||||||
help << compileUsage(progName, useFlags) << "\r\n";
|
|
||||||
|
|
||||||
// positionals
|
|
||||||
if (!m_positionals.isEmpty())
|
|
||||||
{
|
|
||||||
help << "\r\n";
|
|
||||||
help << "Positional arguments:\r\n";
|
|
||||||
QListIterator<PositionalDef *> it2(m_positionals);
|
|
||||||
while (it2.hasNext())
|
|
||||||
{
|
|
||||||
PositionalDef *param = it2.next();
|
|
||||||
help << " " << param->metavar;
|
|
||||||
help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
|
|
||||||
help << param->doc << "\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options
|
|
||||||
if (!m_optionList.isEmpty())
|
|
||||||
{
|
|
||||||
help << "\r\n";
|
|
||||||
QString optPrefix, flagPrefix;
|
|
||||||
getPrefix(optPrefix, flagPrefix);
|
|
||||||
|
|
||||||
help << "Options & Switches:\r\n";
|
|
||||||
QListIterator<OptionDef *> it(m_optionList);
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
OptionDef *option = it.next();
|
|
||||||
help << " ";
|
|
||||||
int nameLength = optPrefix.length() + option->name.length();
|
|
||||||
if (!option->flag.isNull())
|
|
||||||
{
|
|
||||||
nameLength += 3 + flagPrefix.length();
|
|
||||||
help << flagPrefix << option->flag << ", ";
|
|
||||||
}
|
|
||||||
help << optPrefix << option->name;
|
|
||||||
if (option->type == otOption)
|
|
||||||
{
|
|
||||||
QString arg = QString("%1%2").arg(
|
|
||||||
((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
|
|
||||||
nameLength += arg.length();
|
|
||||||
help << arg;
|
|
||||||
}
|
|
||||||
help << " " << QString(helpIndent - nameLength - 1, ' ');
|
|
||||||
help << option->doc << "\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return help.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Parser::compileUsage(QString progName, bool useFlags)
|
|
||||||
{
|
|
||||||
QStringList usage;
|
|
||||||
usage << "Usage: " << progName;
|
|
||||||
|
|
||||||
QString optPrefix, flagPrefix;
|
|
||||||
getPrefix(optPrefix, flagPrefix);
|
|
||||||
|
|
||||||
// options
|
|
||||||
QListIterator<OptionDef *> it(m_optionList);
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
OptionDef *option = it.next();
|
|
||||||
usage << " [";
|
|
||||||
if (!option->flag.isNull() && useFlags)
|
|
||||||
usage << flagPrefix << option->flag;
|
|
||||||
else
|
|
||||||
usage << optPrefix << option->name;
|
|
||||||
if (option->type == otOption)
|
|
||||||
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
|
|
||||||
usage << "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
// arguments
|
|
||||||
QListIterator<PositionalDef *> it2(m_positionals);
|
|
||||||
while (it2.hasNext())
|
|
||||||
{
|
|
||||||
PositionalDef *param = it2.next();
|
|
||||||
usage << " " << (param->required ? "<" : "[");
|
|
||||||
usage << param->metavar;
|
|
||||||
usage << (param->required ? ">" : "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
return usage.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsing
|
|
||||||
QHash<QString, QVariant> Parser::parse(QStringList argv)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> map;
|
|
||||||
|
|
||||||
QStringListIterator it(argv);
|
|
||||||
QString programName = it.next();
|
|
||||||
|
|
||||||
QString optionPrefix;
|
|
||||||
QString flagPrefix;
|
|
||||||
QListIterator<PositionalDef *> positionals(m_positionals);
|
|
||||||
QStringList expecting;
|
|
||||||
|
|
||||||
getPrefix(optionPrefix, flagPrefix);
|
|
||||||
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
QString arg = it.next();
|
|
||||||
|
|
||||||
if (!expecting.isEmpty())
|
|
||||||
// we were expecting an argument
|
|
||||||
{
|
|
||||||
QString name = expecting.first();
|
|
||||||
/*
|
|
||||||
if (map.contains(name))
|
|
||||||
throw ParsingError(
|
|
||||||
QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
|
|
||||||
*/
|
|
||||||
map[name] = QVariant(arg);
|
|
||||||
|
|
||||||
expecting.removeFirst();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.startsWith(optionPrefix))
|
|
||||||
// we have an option
|
|
||||||
{
|
|
||||||
// qDebug("Found option %s", qPrintable(arg));
|
|
||||||
|
|
||||||
QString name = arg.mid(optionPrefix.length());
|
|
||||||
QString equals;
|
|
||||||
|
|
||||||
if ((m_argStyle == ArgumentStyle::Equals ||
|
|
||||||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
|
|
||||||
name.contains("="))
|
|
||||||
{
|
|
||||||
int i = name.indexOf("=");
|
|
||||||
equals = name.mid(i + 1);
|
|
||||||
name = name.left(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_options.contains(name))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
if (map.contains(name))
|
|
||||||
throw ParsingError(QString("Option %2%1 was given multiple times")
|
|
||||||
.arg(name, optionPrefix));
|
|
||||||
*/
|
|
||||||
OptionDef *option = m_options[name];
|
|
||||||
if (option->type == otSwitch)
|
|
||||||
map[name] = true;
|
|
||||||
else // if (option->type == otOption)
|
|
||||||
{
|
|
||||||
if (m_argStyle == ArgumentStyle::Space)
|
|
||||||
expecting.append(name);
|
|
||||||
else if (!equals.isNull())
|
|
||||||
map[name] = equals;
|
|
||||||
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
|
|
||||||
expecting.append(name);
|
|
||||||
else
|
|
||||||
throw ParsingError(QString("Option %2%1 reqires an argument.")
|
|
||||||
.arg(name, optionPrefix));
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.startsWith(flagPrefix))
|
|
||||||
// we have (a) flag(s)
|
|
||||||
{
|
|
||||||
// qDebug("Found flags %s", qPrintable(arg));
|
|
||||||
|
|
||||||
QString flags = arg.mid(flagPrefix.length());
|
|
||||||
QString equals;
|
|
||||||
|
|
||||||
if ((m_argStyle == ArgumentStyle::Equals ||
|
|
||||||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
|
|
||||||
flags.contains("="))
|
|
||||||
{
|
|
||||||
int i = flags.indexOf("=");
|
|
||||||
equals = flags.mid(i + 1);
|
|
||||||
flags = flags.left(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < flags.length(); i++)
|
|
||||||
{
|
|
||||||
QChar flag = flags.at(i);
|
|
||||||
|
|
||||||
if (!m_flags.contains(flag))
|
|
||||||
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
|
|
||||||
|
|
||||||
OptionDef *option = m_flags[flag];
|
|
||||||
/*
|
|
||||||
if (map.contains(option->name))
|
|
||||||
throw ParsingError(QString("Option %2%1 was given multiple times")
|
|
||||||
.arg(option->name, optionPrefix));
|
|
||||||
*/
|
|
||||||
if (option->type == otSwitch)
|
|
||||||
map[option->name] = true;
|
|
||||||
else // if (option->type == otOption)
|
|
||||||
{
|
|
||||||
if (m_argStyle == ArgumentStyle::Space)
|
|
||||||
expecting.append(option->name);
|
|
||||||
else if (!equals.isNull())
|
|
||||||
if (i == flags.length() - 1)
|
|
||||||
map[option->name] = equals;
|
|
||||||
else
|
|
||||||
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
|
|
||||||
"%1 not last flag in %4%3")
|
|
||||||
.arg(option->name, flag, flags, flagPrefix));
|
|
||||||
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
|
|
||||||
expecting.append(option->name);
|
|
||||||
else
|
|
||||||
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
|
|
||||||
.arg(option->name, flag, flagPrefix));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be a positional argument
|
|
||||||
if (!positionals.hasNext())
|
|
||||||
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
|
|
||||||
|
|
||||||
PositionalDef *param = positionals.next();
|
|
||||||
|
|
||||||
map[param->name] = arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if we're missing something
|
|
||||||
if (!expecting.isEmpty())
|
|
||||||
throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
|
|
||||||
expecting.join(QString(", ") + optionPrefix), optionPrefix));
|
|
||||||
|
|
||||||
while (positionals.hasNext())
|
|
||||||
{
|
|
||||||
PositionalDef *param = positionals.next();
|
|
||||||
if (param->required)
|
|
||||||
throw ParsingError(
|
|
||||||
QString("Missing required positional argument '%1'").arg(param->name));
|
|
||||||
else
|
|
||||||
map[param->name] = param->def;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill out gaps
|
|
||||||
QListIterator<OptionDef *> iter(m_optionList);
|
|
||||||
while (iter.hasNext())
|
|
||||||
{
|
|
||||||
OptionDef *option = iter.next();
|
|
||||||
if (!map.contains(option->name))
|
|
||||||
map[option->name] = option->def;
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear defs
|
|
||||||
void Parser::clear()
|
|
||||||
{
|
|
||||||
m_flags.clear();
|
|
||||||
m_params.clear();
|
|
||||||
m_options.clear();
|
|
||||||
|
|
||||||
QMutableListIterator<OptionDef *> it(m_optionList);
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
OptionDef *option = it.next();
|
|
||||||
it.remove();
|
|
||||||
delete option;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMutableListIterator<PositionalDef *> it2(m_positionals);
|
|
||||||
while (it2.hasNext())
|
|
||||||
{
|
|
||||||
PositionalDef *arg = it2.next();
|
|
||||||
it2.remove();
|
|
||||||
delete arg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
Parser::~Parser()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPrefix
|
|
||||||
void Parser::getPrefix(QString &opt, QString &flag)
|
|
||||||
{
|
|
||||||
if (m_flagStyle == FlagStyle::Windows)
|
|
||||||
opt = flag = "/";
|
|
||||||
else if (m_flagStyle == FlagStyle::Unix)
|
|
||||||
opt = flag = "-";
|
|
||||||
// else if (m_flagStyle == FlagStyle::GNU)
|
|
||||||
else
|
|
||||||
{
|
|
||||||
opt = "--";
|
|
||||||
flag = "-";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParsingError
|
|
||||||
ParsingError::ParsingError(const QString &what) : std::runtime_error(what.toStdString())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVariant>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,212 +34,4 @@ namespace Commandline
|
|||||||
* @return a QStringList containing all arguments
|
* @return a QStringList containing all arguments
|
||||||
*/
|
*/
|
||||||
QStringList splitArgs(QString args);
|
QStringList splitArgs(QString args);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The FlagStyle enum
|
|
||||||
* Specifies how flags are decorated
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FlagStyle
|
|
||||||
{
|
|
||||||
enum Enum
|
|
||||||
{
|
|
||||||
GNU, /**< --option and -o (GNU Style) */
|
|
||||||
Unix, /**< -option and -o (Unix Style) */
|
|
||||||
Windows, /**< /option and /o (Windows Style) */
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
Default = Windows
|
|
||||||
#else
|
|
||||||
Default = GNU
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The ArgumentStyle enum
|
|
||||||
*/
|
|
||||||
namespace ArgumentStyle
|
|
||||||
{
|
|
||||||
enum Enum
|
|
||||||
{
|
|
||||||
Space, /**< --option value */
|
|
||||||
Equals, /**< --option=value */
|
|
||||||
SpaceAndEquals, /**< --option[= ]value */
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
Default = Equals
|
|
||||||
#else
|
|
||||||
Default = SpaceAndEquals
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The ParsingError class
|
|
||||||
*/
|
|
||||||
class ParsingError : public std::runtime_error
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ParsingError(const QString &what);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The Parser class
|
|
||||||
*/
|
|
||||||
class Parser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Parser constructor
|
|
||||||
* @param flagStyle the FlagStyle to use in this Parser
|
|
||||||
* @param argStyle the ArgumentStyle to use in this Parser
|
|
||||||
*/
|
|
||||||
Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
|
|
||||||
ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief set the flag style
|
|
||||||
* @param style
|
|
||||||
*/
|
|
||||||
void setFlagStyle(FlagStyle::Enum style);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief get the flag style
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
FlagStyle::Enum flagStyle();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief set the argument style
|
|
||||||
* @param style
|
|
||||||
*/
|
|
||||||
void setArgumentStyle(ArgumentStyle::Enum style);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief get the argument style
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
ArgumentStyle::Enum argumentStyle();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief define a boolean switch
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param def the default value
|
|
||||||
*/
|
|
||||||
void addSwitch(QString name, bool def = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief define an option that takes an additional argument
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param def the default value
|
|
||||||
*/
|
|
||||||
void addOption(QString name, QVariant def = QVariant());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief define a positional argument
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param required wether this argument is required
|
|
||||||
* @param def the default value
|
|
||||||
*/
|
|
||||||
void addArgument(QString name, bool required = true, QVariant def = QVariant());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief adds a flag to an existing parameter
|
|
||||||
* @param name the (existing) parameter name
|
|
||||||
* @param flag the flag character
|
|
||||||
* @see addSwitch addArgument addOption
|
|
||||||
* Note: any one parameter can only have one flag
|
|
||||||
*/
|
|
||||||
void addShortOpt(QString name, QChar flag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief adds documentation to a Parameter
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param metavar a string to be displayed as placeholder for the value
|
|
||||||
* @param doc a QString containing the documentation
|
|
||||||
* Note: on positional arguments, metavar replaces the name as displayed.
|
|
||||||
* on options , metavar replaces the value placeholder
|
|
||||||
*/
|
|
||||||
void addDocumentation(QString name, QString doc, QString metavar = QString());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief generate a help message
|
|
||||||
* @param progName the program name to use in the help message
|
|
||||||
* @param helpIndent how much the parameter documentation should be indented
|
|
||||||
* @param flagsInUsage whether we should use flags instead of options in the usage
|
|
||||||
* @return a help message
|
|
||||||
*/
|
|
||||||
QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief generate a short usage message
|
|
||||||
* @param progName the program name to use in the usage message
|
|
||||||
* @param useFlags whether we should use flags instead of options
|
|
||||||
* @return a usage message
|
|
||||||
*/
|
|
||||||
QString compileUsage(QString progName, bool useFlags = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief parse
|
|
||||||
* @param argv a QStringList containing the program ARGV
|
|
||||||
* @return a QHash mapping argument names to their values
|
|
||||||
*/
|
|
||||||
QHash<QString, QVariant> parse(QStringList argv);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief clear all definitions
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
~Parser();
|
|
||||||
|
|
||||||
private:
|
|
||||||
FlagStyle::Enum m_flagStyle;
|
|
||||||
ArgumentStyle::Enum m_argStyle;
|
|
||||||
|
|
||||||
enum OptionType
|
|
||||||
{
|
|
||||||
otSwitch,
|
|
||||||
otOption
|
|
||||||
};
|
|
||||||
|
|
||||||
// Important: the common part MUST BE COMMON ON ALL THREE structs
|
|
||||||
struct CommonDef
|
|
||||||
{
|
|
||||||
QString name;
|
|
||||||
QString doc;
|
|
||||||
QString metavar;
|
|
||||||
QVariant def;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct OptionDef
|
|
||||||
{
|
|
||||||
// common
|
|
||||||
QString name;
|
|
||||||
QString doc;
|
|
||||||
QString metavar;
|
|
||||||
QVariant def;
|
|
||||||
// option
|
|
||||||
OptionType type;
|
|
||||||
QChar flag;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PositionalDef
|
|
||||||
{
|
|
||||||
// common
|
|
||||||
QString name;
|
|
||||||
QString doc;
|
|
||||||
QString metavar;
|
|
||||||
QVariant def;
|
|
||||||
// positional
|
|
||||||
bool required;
|
|
||||||
};
|
|
||||||
|
|
||||||
QHash<QString, OptionDef *> m_options;
|
|
||||||
QHash<QChar, OptionDef *> m_flags;
|
|
||||||
QHash<QString, CommonDef *> m_params;
|
|
||||||
QList<PositionalDef *> m_positionals;
|
|
||||||
QList<OptionDef *> m_optionList;
|
|
||||||
|
|
||||||
void getPrefix(QString &opt, QString &flag);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
96
launcher/DataMigrationTask.cpp
Normal file
96
launcher/DataMigrationTask.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
#include "DataMigrationTask.h"
|
||||||
|
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
DataMigrationTask::DataMigrationTask(QObject* parent,
|
||||||
|
const QString& sourcePath,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::executeTask()
|
||||||
|
{
|
||||||
|
setStatus(tr("Scanning files..."));
|
||||||
|
|
||||||
|
// 1. Scan
|
||||||
|
// Check how many files we gotta copy
|
||||||
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
||||||
|
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>::canceled, this, &DataMigrationTask::dryRunAborted);
|
||||||
|
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::dryRunFinished()
|
||||||
|
{
|
||||||
|
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
|
||||||
|
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::dryRunAborted);
|
||||||
|
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
|
||||||
|
#else
|
||||||
|
if (!m_copyFuture.result()) {
|
||||||
|
#endif
|
||||||
|
emitFailed(tr("Failed to scan source path."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Copy
|
||||||
|
// Actually copy all files now.
|
||||||
|
m_toCopy = m_copy.totalCopied();
|
||||||
|
connect(&m_copy, &FS::copy::fileCopied, [&, this](const QString& relativeName) {
|
||||||
|
QString shortenedName = relativeName;
|
||||||
|
// shorten the filename to hopefully fit into one line
|
||||||
|
if (shortenedName.length() > 50)
|
||||||
|
shortenedName = relativeName.left(20) + "…" + relativeName.right(29);
|
||||||
|
setProgress(m_copy.totalCopied(), m_toCopy);
|
||||||
|
setStatus(tr("Copying %1…").arg(shortenedName));
|
||||||
|
});
|
||||||
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
||||||
|
return m_copy(false); // actually copy now
|
||||||
|
});
|
||||||
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
||||||
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::copyAborted);
|
||||||
|
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::dryRunAborted()
|
||||||
|
{
|
||||||
|
emitFailed(tr("Aborted"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::copyFinished()
|
||||||
|
{
|
||||||
|
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
||||||
|
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::copyAborted);
|
||||||
|
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
|
||||||
|
#else
|
||||||
|
if (!m_copyFuture.result()) {
|
||||||
|
#endif
|
||||||
|
emitFailed(tr("Some paths could not be copied!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::copyAborted()
|
||||||
|
{
|
||||||
|
emitFailed(tr("Aborted"));
|
||||||
|
}
|
42
launcher/DataMigrationTask.h
Normal file
42
launcher/DataMigrationTask.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "pathmatcher/IPathMatcher.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Migrate existing data from other MMC-like launchers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DataMigrationTask : public Task {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathmatcher);
|
||||||
|
~DataMigrationTask() override = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void executeTask() override;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void dryRunFinished();
|
||||||
|
void dryRunAborted();
|
||||||
|
void copyFinished();
|
||||||
|
void copyAborted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString& m_sourcePath;
|
||||||
|
const QString& m_targetPath;
|
||||||
|
const IPathMatcher::Ptr m_pathMatcher;
|
||||||
|
|
||||||
|
FS::copy m_copy;
|
||||||
|
int m_toCopy = 0;
|
||||||
|
QFuture<bool> m_copyFuture;
|
||||||
|
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||||
|
};
|
@ -37,7 +37,6 @@
|
|||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "Application.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
||||||
@ -119,7 +118,7 @@ bool openDirectory(const QString &path, bool ensureExists)
|
|||||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||||
};
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
if(!APPLICATION->isFlatpak())
|
if(!isFlatpak())
|
||||||
{
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
}
|
}
|
||||||
@ -140,7 +139,7 @@ bool openFile(const QString &path)
|
|||||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||||
};
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
if(!APPLICATION->isFlatpak())
|
if(!isFlatpak())
|
||||||
{
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
}
|
}
|
||||||
@ -158,7 +157,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo
|
|||||||
qDebug() << "Opening file" << path << "using" << application;
|
qDebug() << "Opening file" << path << "using" << application;
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#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
|
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||||
if(!APPLICATION->isFlatpak())
|
if(!isFlatpak())
|
||||||
{
|
{
|
||||||
return IndirectOpen([&]()
|
return IndirectOpen([&]()
|
||||||
{
|
{
|
||||||
@ -178,7 +177,7 @@ bool run(const QString &application, const QStringList &args, const QString &wor
|
|||||||
{
|
{
|
||||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
if(!APPLICATION->isFlatpak())
|
if(!isFlatpak())
|
||||||
{
|
{
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||||
return IndirectOpen([&]()
|
return IndirectOpen([&]()
|
||||||
@ -203,7 +202,7 @@ bool openUrl(const QUrl &url)
|
|||||||
return QDesktopServices::openUrl(url);
|
return QDesktopServices::openUrl(url);
|
||||||
};
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
if(!APPLICATION->isFlatpak())
|
if(!isFlatpak())
|
||||||
{
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
}
|
}
|
||||||
@ -216,4 +215,13 @@ bool openUrl(const QUrl &url)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isFlatpak()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
return QFile::exists("/.flatpak-info");
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,4 +33,6 @@ namespace DesktopServices
|
|||||||
* Open the URL, most likely in a browser. Maybe.
|
* Open the URL, most likely in a browser. Maybe.
|
||||||
*/
|
*/
|
||||||
bool openUrl(const QUrl &url);
|
bool openUrl(const QUrl &url);
|
||||||
|
|
||||||
|
bool isFlatpak();
|
||||||
}
|
}
|
||||||
|
47
launcher/FastFileIconProvider.cpp
Normal file
47
launcher/FastFileIconProvider.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FastFileIconProvider.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QStyle>
|
||||||
|
|
||||||
|
QIcon FastFileIconProvider::icon(const QFileInfo& info) const
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
|
||||||
|
bool link = info.isSymbolicLink() || info.isAlias() || info.isShortcut();
|
||||||
|
#else
|
||||||
|
// in versions prior to 6.4 we don't have access to isAlias
|
||||||
|
bool link = info.isSymLink();
|
||||||
|
#endif
|
||||||
|
QStyle::StandardPixmap icon;
|
||||||
|
|
||||||
|
if (info.isDir()) {
|
||||||
|
if (link)
|
||||||
|
icon = QStyle::SP_DirLinkIcon;
|
||||||
|
else
|
||||||
|
icon = QStyle::SP_DirIcon;
|
||||||
|
} else {
|
||||||
|
if (link)
|
||||||
|
icon = QStyle::SP_FileLinkIcon;
|
||||||
|
else
|
||||||
|
icon = QStyle::SP_FileIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QApplication::style()->standardIcon(icon);
|
||||||
|
}
|
26
launcher/FastFileIconProvider.h
Normal file
26
launcher/FastFileIconProvider.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFileIconProvider>
|
||||||
|
|
||||||
|
class FastFileIconProvider : public QFileIconProvider {
|
||||||
|
public:
|
||||||
|
QIcon icon(const QFileInfo& info) const override;
|
||||||
|
};
|
256
launcher/FileIgnoreProxy.cpp
Normal file
256
launcher/FileIgnoreProxy.cpp
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FileIgnoreProxy.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFileSystemModel>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QStack>
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "SeparatorPrefixTree.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
|
FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), root(root) {}
|
||||||
|
// NOTE: Sadly, we have to do sorting ourselves.
|
||||||
|
bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const
|
||||||
|
{
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
if (!fsm) {
|
||||||
|
return QSortFilterProxyModel::lessThan(left, right);
|
||||||
|
}
|
||||||
|
bool asc = sortOrder() == Qt::AscendingOrder ? true : false;
|
||||||
|
|
||||||
|
QFileInfo leftFileInfo = fsm->fileInfo(left);
|
||||||
|
QFileInfo rightFileInfo = fsm->fileInfo(right);
|
||||||
|
|
||||||
|
if (!leftFileInfo.isDir() && rightFileInfo.isDir()) {
|
||||||
|
return !asc;
|
||||||
|
}
|
||||||
|
if (leftFileInfo.isDir() && !rightFileInfo.isDir()) {
|
||||||
|
return asc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort and proxy model breaks the original model...
|
||||||
|
if (sortColumn() == 0) {
|
||||||
|
return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), Qt::CaseInsensitive) < 0;
|
||||||
|
}
|
||||||
|
if (sortColumn() == 1) {
|
||||||
|
auto leftSize = leftFileInfo.size();
|
||||||
|
auto rightSize = rightFileInfo.size();
|
||||||
|
if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir())) {
|
||||||
|
return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), Qt::CaseInsensitive) < 0 ? asc : !asc;
|
||||||
|
}
|
||||||
|
return leftSize < rightSize;
|
||||||
|
}
|
||||||
|
return QSortFilterProxyModel::lessThan(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags FileIgnoreProxy::flags(const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return Qt::NoItemFlags;
|
||||||
|
|
||||||
|
auto sourceIndex = mapToSource(index);
|
||||||
|
Qt::ItemFlags flags = sourceIndex.flags();
|
||||||
|
if (index.column() == 0) {
|
||||||
|
flags |= Qt::ItemIsUserCheckable;
|
||||||
|
if (sourceIndex.model()->hasChildren(sourceIndex)) {
|
||||||
|
flags |= Qt::ItemIsAutoTristate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant FileIgnoreProxy::data(const QModelIndex& index, int role) const
|
||||||
|
{
|
||||||
|
QModelIndex sourceIndex = mapToSource(index);
|
||||||
|
|
||||||
|
if (index.column() == 0 && role == Qt::CheckStateRole) {
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||||
|
auto cover = blocked.cover(blockedPath);
|
||||||
|
if (!cover.isNull()) {
|
||||||
|
return QVariant(Qt::Unchecked);
|
||||||
|
} else if (blocked.exists(blockedPath)) {
|
||||||
|
return QVariant(Qt::PartiallyChecked);
|
||||||
|
} else {
|
||||||
|
return QVariant(Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceIndex.data(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||||
|
{
|
||||||
|
if (index.column() == 0 && role == Qt::CheckStateRole) {
|
||||||
|
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
|
||||||
|
return setFilterState(index, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex sourceIndex = mapToSource(index);
|
||||||
|
return QSortFilterProxyModel::sourceModel()->setData(sourceIndex, value, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileIgnoreProxy::relPath(const QString& path) const
|
||||||
|
{
|
||||||
|
return QDir(root).relativeFilePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
|
||||||
|
{
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
|
||||||
|
if (!fsm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex sourceIndex = mapToSource(index);
|
||||||
|
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||||
|
bool changed = false;
|
||||||
|
if (state == Qt::Unchecked) {
|
||||||
|
// blocking a path
|
||||||
|
auto& node = blocked.insert(blockedPath);
|
||||||
|
// get rid of all blocked nodes below
|
||||||
|
node.clear();
|
||||||
|
changed = true;
|
||||||
|
} else if (state == Qt::Checked || state == Qt::PartiallyChecked) {
|
||||||
|
if (!blocked.remove(blockedPath)) {
|
||||||
|
auto cover = blocked.cover(blockedPath);
|
||||||
|
qDebug() << "Blocked by cover" << cover;
|
||||||
|
// uncover
|
||||||
|
blocked.remove(cover);
|
||||||
|
// block all contents, except for any cover
|
||||||
|
QModelIndex rootIndex = fsm->index(FS::PathCombine(root, cover));
|
||||||
|
QModelIndex doing = rootIndex;
|
||||||
|
int row = 0;
|
||||||
|
QStack<QModelIndex> todo;
|
||||||
|
while (1) {
|
||||||
|
auto node = fsm->index(row, 0, doing);
|
||||||
|
if (!node.isValid()) {
|
||||||
|
if (!todo.size()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
doing = todo.pop();
|
||||||
|
row = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto relpath = relPath(fsm->filePath(node));
|
||||||
|
if (blockedPath.startsWith(relpath)) // cover found?
|
||||||
|
{
|
||||||
|
// continue processing cover later
|
||||||
|
todo.push(node);
|
||||||
|
} else {
|
||||||
|
// or just block this one.
|
||||||
|
blocked.insert(relpath);
|
||||||
|
}
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
// update the thing
|
||||||
|
emit dataChanged(index, index, { Qt::CheckStateRole });
|
||||||
|
// update everything above index
|
||||||
|
QModelIndex up = index.parent();
|
||||||
|
while (1) {
|
||||||
|
if (!up.isValid())
|
||||||
|
break;
|
||||||
|
emit dataChanged(up, up, { Qt::CheckStateRole });
|
||||||
|
up = up.parent();
|
||||||
|
}
|
||||||
|
// and everything below the index
|
||||||
|
QModelIndex doing = index;
|
||||||
|
int row = 0;
|
||||||
|
QStack<QModelIndex> todo;
|
||||||
|
while (1) {
|
||||||
|
auto node = this->index(row, 0, doing);
|
||||||
|
if (!node.isValid()) {
|
||||||
|
if (!todo.size()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
doing = todo.pop();
|
||||||
|
row = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit dataChanged(node, node, { Qt::CheckStateRole });
|
||||||
|
todo.push(node);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
// siblings and unrelated nodes are ignored
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::shouldExpand(QModelIndex index)
|
||||||
|
{
|
||||||
|
QModelIndex sourceIndex = mapToSource(index);
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
if (!fsm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||||
|
auto found = blocked.find(blockedPath);
|
||||||
|
if (found) {
|
||||||
|
return !found->leaf();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileIgnoreProxy::setBlockedPaths(QStringList paths)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
blocked.clear();
|
||||||
|
blocked.insert(paths);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(source_parent)
|
||||||
|
|
||||||
|
// adjust the columns you want to filter out here
|
||||||
|
// return false for those that will be hidden
|
||||||
|
if (source_column == 2 || source_column == 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
72
launcher/FileIgnoreProxy.h
Normal file
72
launcher/FileIgnoreProxy.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 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
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include "SeparatorPrefixTree.h"
|
||||||
|
|
||||||
|
class FileIgnoreProxy : public QSortFilterProxyModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileIgnoreProxy(QString root, QObject* parent);
|
||||||
|
// NOTE: Sadly, we have to do sorting ourselves.
|
||||||
|
bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
|
||||||
|
|
||||||
|
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
|
|
||||||
|
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
|
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
|
|
||||||
|
QString relPath(const QString& path) const;
|
||||||
|
|
||||||
|
bool setFilterState(QModelIndex index, Qt::CheckState state);
|
||||||
|
|
||||||
|
bool shouldExpand(QModelIndex index);
|
||||||
|
|
||||||
|
void setBlockedPaths(QStringList paths);
|
||||||
|
|
||||||
|
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
|
||||||
|
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString root;
|
||||||
|
SeparatorPrefixTree<'/'> blocked;
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,9 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.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
|
||||||
@ -38,32 +40,35 @@
|
|||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "pathmatcher/IPathMatcher.h"
|
#include "pathmatcher/IPathMatcher.h"
|
||||||
|
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
|
#include <QLocalServer>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
namespace FS
|
namespace FS {
|
||||||
{
|
|
||||||
|
|
||||||
class FileSystemException : public ::Exception
|
class FileSystemException : public ::Exception {
|
||||||
{
|
public:
|
||||||
public:
|
FileSystemException(const QString& message) : Exception(message) {}
|
||||||
FileSystemException(const QString &message) : Exception(message) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* write data to a file safely
|
* write data to a file safely
|
||||||
*/
|
*/
|
||||||
void write(const QString &filename, const QByteArray &data);
|
void write(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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the last changed timestamp of an existing file
|
* Update the last changed timestamp of an existing file
|
||||||
*/
|
*/
|
||||||
bool updateTimestamp(const QString & filename);
|
bool updateTimestamp(const QString& filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates all the folders in a path for the specified path
|
* Creates all the folders in a path for the specified path
|
||||||
@ -77,49 +82,214 @@ bool ensureFilePathExists(QString filenamepath);
|
|||||||
*/
|
*/
|
||||||
bool ensureFolderPathExists(QString filenamepath);
|
bool ensureFolderPathExists(QString filenamepath);
|
||||||
|
|
||||||
class copy
|
/**
|
||||||
{
|
* @brief Copies a directory and it's contents from src to dest
|
||||||
public:
|
*/
|
||||||
copy(const QString & src, const QString & dst)
|
class copy : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
copy(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
||||||
{
|
{
|
||||||
m_src.setPath(src);
|
m_src.setPath(src);
|
||||||
m_dst.setPath(dst);
|
m_dst.setPath(dst);
|
||||||
}
|
}
|
||||||
copy & followSymlinks(const bool follow)
|
copy& followSymlinks(const bool follow)
|
||||||
{
|
{
|
||||||
m_followSymlinks = follow;
|
m_followSymlinks = follow;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
copy & blacklist(const IPathMatcher * filter)
|
copy& matcher(const IPathMatcher* filter)
|
||||||
{
|
{
|
||||||
m_blacklist = filter;
|
m_matcher = filter;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
bool operator()()
|
copy& whitelist(bool whitelist)
|
||||||
{
|
{
|
||||||
return operator()(QString());
|
m_whitelist = whitelist;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
bool operator()(const QString &offset);
|
|
||||||
|
|
||||||
private:
|
int totalCopied() { return m_copied; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fileCopied(const QString& relativeName);
|
||||||
|
// TODO: maybe add a "shouldCopy" signal in the future?
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool operator()(const QString& offset, bool dryRun = false);
|
||||||
|
|
||||||
|
private:
|
||||||
bool m_followSymlinks = true;
|
bool m_followSymlinks = true;
|
||||||
const IPathMatcher * m_blacklist = nullptr;
|
const IPathMatcher* m_matcher = nullptr;
|
||||||
|
bool m_whitelist = false;
|
||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
|
int m_copied;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LinkPair {
|
||||||
|
QString src;
|
||||||
|
QString dst;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LinkResult {
|
||||||
|
QString src;
|
||||||
|
QString dst;
|
||||||
|
QString err_msg;
|
||||||
|
int err_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExternalLinkFileProcess : public QThread {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ExternalLinkFileProcess(QString server, bool useHardLinks, QObject* parent = nullptr)
|
||||||
|
: QThread(parent), m_useHardLinks(useHardLinks), m_server(server)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
runLinkFile();
|
||||||
|
emit processExited();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void processExited();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void runLinkFile();
|
||||||
|
|
||||||
|
bool m_useHardLinks = false;
|
||||||
|
|
||||||
|
QString m_server;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief links (a file / a directory and it's contents) from src to dest
|
||||||
|
*/
|
||||||
|
class create_link : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
create_link(const QList<LinkPair> path_pairs, QObject* parent = nullptr) : QObject(parent) { m_path_pairs.append(path_pairs); }
|
||||||
|
create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
||||||
|
{
|
||||||
|
LinkPair pair = { src, dst };
|
||||||
|
m_path_pairs.append(pair);
|
||||||
|
}
|
||||||
|
create_link& useHardLinks(const bool useHard)
|
||||||
|
{
|
||||||
|
m_useHardLinks = useHard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& matcher(const IPathMatcher* filter)
|
||||||
|
{
|
||||||
|
m_matcher = filter;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& whitelist(bool whitelist)
|
||||||
|
{
|
||||||
|
m_whitelist = whitelist;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& linkRecursively(bool recursive)
|
||||||
|
{
|
||||||
|
m_recursive = recursive;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& setMaxDepth(int depth)
|
||||||
|
{
|
||||||
|
m_max_depth = depth;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& debug(bool d)
|
||||||
|
{
|
||||||
|
m_debug = d;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code getOSError() { return m_os_err; }
|
||||||
|
|
||||||
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
|
int totalLinked() { return m_linked; }
|
||||||
|
|
||||||
|
void runPrivileged() { runPrivileged(QString()); }
|
||||||
|
void runPrivileged(const QString& offset);
|
||||||
|
|
||||||
|
QList<LinkResult> getResults() { return m_path_results; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fileLinked(const QString& srcName, const QString& dstName);
|
||||||
|
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
|
||||||
|
void finished();
|
||||||
|
void finishedPrivileged(bool gotResults);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool operator()(const QString& offset, bool dryRun = false);
|
||||||
|
void make_link_list(const QString& offset);
|
||||||
|
bool make_links();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_useHardLinks = false;
|
||||||
|
const IPathMatcher* m_matcher = nullptr;
|
||||||
|
bool m_whitelist = false;
|
||||||
|
bool m_recursive = true;
|
||||||
|
|
||||||
|
/// @brief >= -1 = infinite, 0 = link files at src/* to dest/*, 1 = link files at src/*/* to dest/*/*, etc.
|
||||||
|
int m_max_depth = -1;
|
||||||
|
|
||||||
|
QList<LinkPair> m_path_pairs;
|
||||||
|
QList<LinkResult> m_path_results;
|
||||||
|
QList<LinkPair> m_links_to_make;
|
||||||
|
|
||||||
|
int m_linked;
|
||||||
|
bool m_debug = false;
|
||||||
|
std::error_code m_os_err;
|
||||||
|
|
||||||
|
QLocalServer m_linkServer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief moves a file by renaming it
|
||||||
|
* @param source source file path
|
||||||
|
* @param dest destination filepath
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool move(const QString& source, const QString& dest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a folder recursively
|
* Delete a folder recursively
|
||||||
*/
|
*/
|
||||||
bool deletePath(QString path);
|
bool deletePath(QString path);
|
||||||
|
|
||||||
QString PathCombine(const QString &path1, const QString &path2);
|
/**
|
||||||
QString PathCombine(const QString &path1, const QString &path2, const QString &path3);
|
* Trash a folder / file
|
||||||
QString PathCombine(const QString &path1, const QString &path2, const QString &path3, const QString &path4);
|
*/
|
||||||
|
bool trash(QString path, QString* pathInTrash = nullptr);
|
||||||
|
|
||||||
QString AbsolutePath(QString path);
|
QString PathCombine(const QString& path1, const QString& path2);
|
||||||
|
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
|
||||||
|
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4);
|
||||||
|
|
||||||
|
QString AbsolutePath(const QString& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief depth of path. "foo.txt" -> 0 , "bar/foo.txt" -> 1, /baz/bar/foo.txt -> 2, etc.
|
||||||
|
*
|
||||||
|
* @param path path to measure
|
||||||
|
* @return int number of components before base path
|
||||||
|
*/
|
||||||
|
int pathDepth(const QString& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief cut off segments of path until it is a max of length depth
|
||||||
|
*
|
||||||
|
* @param path path to truncate
|
||||||
|
* @param depth max depth of new path
|
||||||
|
* @return QString truncated path
|
||||||
|
*/
|
||||||
|
QString pathTruncate(const QString& path, int depth);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve an executable
|
* Resolve an executable
|
||||||
@ -156,4 +326,199 @@ QString getDesktopDir();
|
|||||||
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
|
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
|
||||||
// Equivalent to doing QDir::rename, but allowing for overrides
|
// Equivalent to doing QDir::rename, but allowing for overrides
|
||||||
bool overrideFolder(QString overwritten_path, QString override_path);
|
bool overrideFolder(QString overwritten_path, QString override_path);
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* Creates a shortcut to the specified target file at the specified destination path.
|
||||||
|
*/
|
||||||
|
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon);
|
||||||
|
|
||||||
|
enum class FilesystemType {
|
||||||
|
FAT,
|
||||||
|
NTFS,
|
||||||
|
REFS,
|
||||||
|
EXT,
|
||||||
|
EXT_2_OLD,
|
||||||
|
EXT_2_3_4,
|
||||||
|
XFS,
|
||||||
|
BTRFS,
|
||||||
|
NFS,
|
||||||
|
ZFS,
|
||||||
|
APFS,
|
||||||
|
HFS,
|
||||||
|
HFSPLUS,
|
||||||
|
HFSX,
|
||||||
|
FUSEBLK,
|
||||||
|
F2FS,
|
||||||
|
UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ordered Mapping of enum types to reported filesystem names
|
||||||
|
* this mapping is non exsaustive, it just attempts to capture the filesystems which could be reasonalbly be in use .
|
||||||
|
* all string values are in uppercase, use `QString.toUpper()` or equivalent during lookup.
|
||||||
|
*
|
||||||
|
* QMap is ordered
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static const QMap<FilesystemType, QStringList> s_filesystem_type_names = {
|
||||||
|
{FilesystemType::FAT, { "FAT" }},
|
||||||
|
{FilesystemType::NTFS, { "NTFS" }},
|
||||||
|
{FilesystemType::REFS, { "REFS" }},
|
||||||
|
{FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" }},
|
||||||
|
{FilesystemType::EXT_2_3_4, { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" }},
|
||||||
|
{FilesystemType::EXT, { "EXT" }},
|
||||||
|
{FilesystemType::XFS, { "XFS" }},
|
||||||
|
{FilesystemType::BTRFS, { "BTRFS" }},
|
||||||
|
{FilesystemType::NFS, { "NFS" }},
|
||||||
|
{FilesystemType::ZFS, { "ZFS" }},
|
||||||
|
{FilesystemType::APFS, { "APFS" }},
|
||||||
|
{FilesystemType::HFS, { "HFS" }},
|
||||||
|
{FilesystemType::HFSPLUS, { "HFSPLUS" }},
|
||||||
|
{FilesystemType::HFSX, { "HFSX" }},
|
||||||
|
{FilesystemType::FUSEBLK, { "FUSEBLK" }},
|
||||||
|
{FilesystemType::F2FS, { "F2FS" }},
|
||||||
|
{FilesystemType::UNKNOWN, { "UNKNOWN" }}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the string name of Filesystem enum object
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @return QString
|
||||||
|
*/
|
||||||
|
QString getFilesystemTypeName(FilesystemType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the Filesystem enum object from a name
|
||||||
|
* Does a lookup of the type name and returns an exact match
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @return FilesystemType
|
||||||
|
*/
|
||||||
|
FilesystemType getFilesystemType(const QString& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the Filesystem enum object from a name
|
||||||
|
* Does a fuzzy lookup of the type name and returns an apropreate match
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @return FilesystemType
|
||||||
|
*/
|
||||||
|
FilesystemType getFilesystemTypeFuzzy(const QString& name);
|
||||||
|
|
||||||
|
struct FilesystemInfo {
|
||||||
|
FilesystemType fsType = FilesystemType::UNKNOWN;
|
||||||
|
QString fsTypeName;
|
||||||
|
int blockSize;
|
||||||
|
qint64 bytesAvailable;
|
||||||
|
qint64 bytesFree;
|
||||||
|
qint64 bytesTotal;
|
||||||
|
QString name;
|
||||||
|
QString rootPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief path to the near ancestor that exists
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QString nearestExistentAncestor(const QString& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief colect information about the filesystem under a file
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
FilesystemInfo statFS(const QString& path);
|
||||||
|
|
||||||
|
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
|
||||||
|
FilesystemType::XFS, FilesystemType::REFS };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if the Filesystem is reflink/clone capable
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool canCloneOnFS(const QString& path);
|
||||||
|
bool canCloneOnFS(const FilesystemInfo& info);
|
||||||
|
bool canCloneOnFS(FilesystemType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if the Filesystems are reflink/clone capable and both are on the same device
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool canClone(const QString& src, const QString& dst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copies a directory and it's contents from src to dest
|
||||||
|
*/
|
||||||
|
class clone : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
clone(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
||||||
|
{
|
||||||
|
m_src.setPath(src);
|
||||||
|
m_dst.setPath(dst);
|
||||||
|
}
|
||||||
|
clone& matcher(const IPathMatcher* filter)
|
||||||
|
{
|
||||||
|
m_matcher = filter;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
clone& whitelist(bool whitelist)
|
||||||
|
{
|
||||||
|
m_whitelist = whitelist;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
|
int totalCloned() { return m_cloned; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fileCloned(const QString& src, const QString& dst);
|
||||||
|
void cloneFailed(const QString& src, const QString& dst);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool operator()(const QString& offset, bool dryRun = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const IPathMatcher* m_matcher = nullptr;
|
||||||
|
bool m_whitelist = false;
|
||||||
|
QDir m_src;
|
||||||
|
QDir m_dst;
|
||||||
|
int m_cloned;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief clone/reflink file from src to dst
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool clone_file(const QString& src, const QString& dst, std::error_code& ec);
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
bool win_ioctl_clone(const std::wstring& src_path, const std::wstring& dst_path, std::error_code& ec);
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std::error_code& ec);
|
||||||
|
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
|
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const QList<FilesystemType> s_non_link_filesystems = {
|
||||||
|
FilesystemType::FAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if the Filesystem is symlink capable
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool canLinkOnFS(const QString& path);
|
||||||
|
bool canLinkOnFS(const FilesystemInfo& info);
|
||||||
|
bool canLinkOnFS(FilesystemType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if the Filesystem is symlink capable on both ends
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool canLink(const QString& src, const QString& dst);
|
||||||
|
|
||||||
|
uintmax_t hardLinkCount(const QString& path);
|
||||||
|
|
||||||
|
} // namespace FS
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
#include <QTest>
|
|
||||||
#include <QTemporaryDir>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
|
|
||||||
#include "FileSystem.h"
|
|
||||||
|
|
||||||
class FileSystemTest : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
const QString bothSlash = "/foo/";
|
|
||||||
const QString trailingSlash = "foo/";
|
|
||||||
const QString leadingSlash = "/foo";
|
|
||||||
|
|
||||||
private
|
|
||||||
slots:
|
|
||||||
void test_pathCombine()
|
|
||||||
{
|
|
||||||
QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash));
|
|
||||||
QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash));
|
|
||||||
QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash));
|
|
||||||
|
|
||||||
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash));
|
|
||||||
QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash));
|
|
||||||
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash));
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_PathCombine1_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<QString>("result");
|
|
||||||
QTest::addColumn<QString>("path1");
|
|
||||||
QTest::addColumn<QString>("path2");
|
|
||||||
|
|
||||||
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
|
|
||||||
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
|
|
||||||
QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
|
|
||||||
QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_PathCombine1()
|
|
||||||
{
|
|
||||||
QFETCH(QString, result);
|
|
||||||
QFETCH(QString, path1);
|
|
||||||
QFETCH(QString, path2);
|
|
||||||
|
|
||||||
QCOMPARE(FS::PathCombine(path1, path2), result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_PathCombine2_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<QString>("result");
|
|
||||||
QTest::addColumn<QString>("path1");
|
|
||||||
QTest::addColumn<QString>("path2");
|
|
||||||
QTest::addColumn<QString>("path3");
|
|
||||||
|
|
||||||
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
|
|
||||||
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
|
|
||||||
QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
|
|
||||||
QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
|
|
||||||
QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
|
|
||||||
QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
|
|
||||||
QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_PathCombine2()
|
|
||||||
{
|
|
||||||
QFETCH(QString, result);
|
|
||||||
QFETCH(QString, path1);
|
|
||||||
QFETCH(QString, path2);
|
|
||||||
QFETCH(QString, path3);
|
|
||||||
|
|
||||||
QCOMPARE(FS::PathCombine(path1, path2, path3), result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_copy()
|
|
||||||
{
|
|
||||||
QString folder = QFINDTESTDATA("testdata/test_folder");
|
|
||||||
auto f = [&folder]()
|
|
||||||
{
|
|
||||||
QTemporaryDir tempDir;
|
|
||||||
tempDir.setAutoRemove(true);
|
|
||||||
qDebug() << "From:" << folder << "To:" << tempDir.path();
|
|
||||||
|
|
||||||
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
|
||||||
qDebug() << tempDir.path();
|
|
||||||
qDebug() << target_dir.path();
|
|
||||||
FS::copy c(folder, target_dir.path());
|
|
||||||
c();
|
|
||||||
|
|
||||||
for(auto entry: target_dir.entryList())
|
|
||||||
{
|
|
||||||
qDebug() << entry;
|
|
||||||
}
|
|
||||||
QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
|
|
||||||
QVERIFY(target_dir.entryList().contains("assets"));
|
|
||||||
};
|
|
||||||
|
|
||||||
// first try variant without trailing /
|
|
||||||
QVERIFY(!folder.endsWith('/'));
|
|
||||||
f();
|
|
||||||
|
|
||||||
// then variant with trailing /
|
|
||||||
folder.append('/');
|
|
||||||
QVERIFY(folder.endsWith('/'));
|
|
||||||
f();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_getDesktop()
|
|
||||||
{
|
|
||||||
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(FileSystemTest)
|
|
||||||
|
|
||||||
#include "FileSystem_test.moc"
|
|
@ -72,7 +72,7 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
|
|||||||
uncompLength *= 2;
|
uncompLength *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
|
strm.next_out = reinterpret_cast<Bytef *>((uncompressedBytes.data() + strm.total_out));
|
||||||
strm.avail_out = uncompLength - strm.total_out;
|
strm.avail_out = uncompLength - strm.total_out;
|
||||||
|
|
||||||
// Inflate another chunk.
|
// Inflate another chunk.
|
||||||
@ -129,7 +129,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
|||||||
{
|
{
|
||||||
compressedBytes.resize(compressedBytes.size() * 2);
|
compressedBytes.resize(compressedBytes.size() * 2);
|
||||||
}
|
}
|
||||||
zs.next_out = (Bytef *) (compressedBytes.data() + offset);
|
zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset));
|
||||||
temp = zs.avail_out = compressedBytes.size() - offset;
|
temp = zs.avail_out = compressedBytes.size() - offset;
|
||||||
ret = deflate(&zs, Z_FINISH);
|
ret = deflate(&zs, Z_FINISH);
|
||||||
offset += temp - zs.avail_out;
|
offset += temp - zs.avail_out;
|
||||||
|
@ -1,76 +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 <hoedown/html.h>
|
|
||||||
#include <hoedown/document.h>
|
|
||||||
#include <QString>
|
|
||||||
#include <QByteArray>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* hoedown wrapper, because dealing with resource lifetime in C is stupid
|
|
||||||
*/
|
|
||||||
class HoeDown
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
class buffer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
buffer(size_t unit = 4096)
|
|
||||||
{
|
|
||||||
buf = hoedown_buffer_new(unit);
|
|
||||||
}
|
|
||||||
~buffer()
|
|
||||||
{
|
|
||||||
hoedown_buffer_free(buf);
|
|
||||||
}
|
|
||||||
const char * cstr()
|
|
||||||
{
|
|
||||||
return hoedown_buffer_cstr(buf);
|
|
||||||
}
|
|
||||||
void put(QByteArray input)
|
|
||||||
{
|
|
||||||
hoedown_buffer_put(buf, (uint8_t *) input.data(), input.size());
|
|
||||||
}
|
|
||||||
const uint8_t * data() const
|
|
||||||
{
|
|
||||||
return buf->data;
|
|
||||||
}
|
|
||||||
size_t size() const
|
|
||||||
{
|
|
||||||
return buf->size;
|
|
||||||
}
|
|
||||||
hoedown_buffer * buf;
|
|
||||||
} ib, ob;
|
|
||||||
HoeDown()
|
|
||||||
{
|
|
||||||
renderer = hoedown_html_renderer_new((hoedown_html_flags) 0,0);
|
|
||||||
document = hoedown_document_new(renderer, (hoedown_extensions) 0, 8);
|
|
||||||
}
|
|
||||||
~HoeDown()
|
|
||||||
{
|
|
||||||
hoedown_document_free(document);
|
|
||||||
hoedown_html_renderer_free(renderer);
|
|
||||||
}
|
|
||||||
QString process(QByteArray input)
|
|
||||||
{
|
|
||||||
ib.put(input);
|
|
||||||
hoedown_document_render(document, ob.buf, ib.data(), ib.size());
|
|
||||||
return ob.cstr();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
hoedown_document * document;
|
|
||||||
hoedown_renderer * renderer;
|
|
||||||
};
|
|
194
launcher/InstanceCopyPrefs.cpp
Normal file
194
launcher/InstanceCopyPrefs.cpp
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
//
|
||||||
|
// Created by marcelohdez on 10/22/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "InstanceCopyPrefs.h"
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::allTrue() const
|
||||||
|
{
|
||||||
|
return copySaves &&
|
||||||
|
keepPlaytime &&
|
||||||
|
copyGameOptions &&
|
||||||
|
copyResourcePacks &&
|
||||||
|
copyShaderPacks &&
|
||||||
|
copyServers &&
|
||||||
|
copyMods &&
|
||||||
|
copyScreenshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat")
|
||||||
|
QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const
|
||||||
|
{
|
||||||
|
return getSelectedFiltersAsRegex({});
|
||||||
|
}
|
||||||
|
QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& additionalFilters) const
|
||||||
|
{
|
||||||
|
QStringList filters;
|
||||||
|
|
||||||
|
if(!copySaves)
|
||||||
|
filters << "saves";
|
||||||
|
|
||||||
|
if(!copyGameOptions)
|
||||||
|
filters << "options.txt";
|
||||||
|
|
||||||
|
if(!copyResourcePacks)
|
||||||
|
filters << "resourcepacks" << "texturepacks";
|
||||||
|
|
||||||
|
if(!copyShaderPacks)
|
||||||
|
filters << "shaderpacks";
|
||||||
|
|
||||||
|
if(!copyServers)
|
||||||
|
filters << "servers.dat" << "servers.dat_old" << "server-resource-packs";
|
||||||
|
|
||||||
|
if(!copyMods)
|
||||||
|
filters << "coremods" << "mods" << "config";
|
||||||
|
|
||||||
|
if(!copyScreenshots)
|
||||||
|
filters << "screenshots";
|
||||||
|
|
||||||
|
for (auto filter : additionalFilters) {
|
||||||
|
filters << filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have any filters to add, join them as a single regex string to return:
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
const QString MC_ROOT = "[.]?minecraft/";
|
||||||
|
// Ensure first filter starts with root, then join other filters with OR regex before root (ex: ".minecraft/saves|.minecraft/mods"):
|
||||||
|
return MC_ROOT + filters.join("|" + MC_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= Getters =======
|
||||||
|
bool InstanceCopyPrefs::isCopySavesEnabled() const
|
||||||
|
{
|
||||||
|
return copySaves;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isKeepPlaytimeEnabled() const
|
||||||
|
{
|
||||||
|
return keepPlaytime;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyGameOptionsEnabled() const
|
||||||
|
{
|
||||||
|
return copyGameOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyResourcePacksEnabled() const
|
||||||
|
{
|
||||||
|
return copyResourcePacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyShaderPacksEnabled() const
|
||||||
|
{
|
||||||
|
return copyShaderPacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyServersEnabled() const
|
||||||
|
{
|
||||||
|
return copyServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyModsEnabled() const
|
||||||
|
{
|
||||||
|
return copyMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const
|
||||||
|
{
|
||||||
|
return copyScreenshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isUseSymLinksEnabled() const
|
||||||
|
{
|
||||||
|
return useSymLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isUseHardLinksEnabled() const
|
||||||
|
{
|
||||||
|
return useHardLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isLinkRecursivelyEnabled() const
|
||||||
|
{
|
||||||
|
return linkRecursively;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
|
||||||
|
{
|
||||||
|
return dontLinkSaves;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isUseCloneEnabled() const
|
||||||
|
{
|
||||||
|
return useClone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= Setters =======
|
||||||
|
void InstanceCopyPrefs::enableCopySaves(bool b)
|
||||||
|
{
|
||||||
|
copySaves = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableKeepPlaytime(bool b)
|
||||||
|
{
|
||||||
|
keepPlaytime = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyGameOptions(bool b)
|
||||||
|
{
|
||||||
|
copyGameOptions = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyResourcePacks(bool b)
|
||||||
|
{
|
||||||
|
copyResourcePacks = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyShaderPacks(bool b)
|
||||||
|
{
|
||||||
|
copyShaderPacks = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyServers(bool b)
|
||||||
|
{
|
||||||
|
copyServers = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyMods(bool b)
|
||||||
|
{
|
||||||
|
copyMods = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyScreenshots(bool b)
|
||||||
|
{
|
||||||
|
copyScreenshots = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableUseSymLinks(bool b)
|
||||||
|
{
|
||||||
|
useSymLinks = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableLinkRecursively(bool b)
|
||||||
|
{
|
||||||
|
linkRecursively = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableUseHardLinks(bool b)
|
||||||
|
{
|
||||||
|
useHardLinks = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableDontLinkSaves(bool b)
|
||||||
|
{
|
||||||
|
dontLinkSaves = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableUseClone(bool b)
|
||||||
|
{
|
||||||
|
useClone = b;
|
||||||
|
}
|
57
launcher/InstanceCopyPrefs.h
Normal file
57
launcher/InstanceCopyPrefs.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// Created by marcelohdez on 10/22/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
struct InstanceCopyPrefs {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool allTrue() const;
|
||||||
|
[[nodiscard]] QString getSelectedFiltersAsRegex() const;
|
||||||
|
[[nodiscard]] QString getSelectedFiltersAsRegex(const QStringList& additionalFilters) const;
|
||||||
|
// Getters
|
||||||
|
[[nodiscard]] bool isCopySavesEnabled() const;
|
||||||
|
[[nodiscard]] bool isKeepPlaytimeEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyGameOptionsEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyResourcePacksEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyShaderPacksEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyServersEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyModsEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
|
||||||
|
[[nodiscard]] bool isUseSymLinksEnabled() const;
|
||||||
|
[[nodiscard]] bool isLinkRecursivelyEnabled() const;
|
||||||
|
[[nodiscard]] bool isUseHardLinksEnabled() const;
|
||||||
|
[[nodiscard]] bool isDontLinkSavesEnabled() const;
|
||||||
|
[[nodiscard]] bool isUseCloneEnabled() const;
|
||||||
|
// Setters
|
||||||
|
void enableCopySaves(bool b);
|
||||||
|
void enableKeepPlaytime(bool b);
|
||||||
|
void enableCopyGameOptions(bool b);
|
||||||
|
void enableCopyResourcePacks(bool b);
|
||||||
|
void enableCopyShaderPacks(bool b);
|
||||||
|
void enableCopyServers(bool b);
|
||||||
|
void enableCopyMods(bool b);
|
||||||
|
void enableCopyScreenshots(bool b);
|
||||||
|
void enableUseSymLinks(bool b);
|
||||||
|
void enableLinkRecursively(bool b);
|
||||||
|
void enableUseHardLinks(bool b);
|
||||||
|
void enableDontLinkSaves(bool b);
|
||||||
|
void enableUseClone(bool b);
|
||||||
|
|
||||||
|
protected: // data
|
||||||
|
bool copySaves = true;
|
||||||
|
bool keepPlaytime = true;
|
||||||
|
bool copyGameOptions = true;
|
||||||
|
bool copyResourcePacks = true;
|
||||||
|
bool copyShaderPacks = true;
|
||||||
|
bool copyServers = true;
|
||||||
|
bool copyMods = true;
|
||||||
|
bool copyScreenshots = true;
|
||||||
|
bool useSymLinks = false;
|
||||||
|
bool linkRecursively = false;
|
||||||
|
bool useHardLinks = false;
|
||||||
|
bool dontLinkSaves = false;
|
||||||
|
bool useClone = false;
|
||||||
|
};
|
@ -1,19 +1,34 @@
|
|||||||
#include "InstanceCopyTask.h"
|
#include "InstanceCopyTask.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include <QDebug>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
#include "pathmatcher/RegexpMatcher.h"
|
#include "pathmatcher/RegexpMatcher.h"
|
||||||
#include <QtConcurrentRun>
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
|
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
|
||||||
{
|
{
|
||||||
m_origInstance = origInstance;
|
m_origInstance = origInstance;
|
||||||
m_keepPlaytime = keepPlaytime;
|
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
|
||||||
|
m_useLinks = prefs.isUseSymLinksEnabled();
|
||||||
|
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
|
||||||
|
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
|
||||||
|
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
|
||||||
|
m_useClone = prefs.isUseCloneEnabled();
|
||||||
|
|
||||||
if(!copySaves)
|
QString filters = prefs.getSelectedFiltersAsRegex();
|
||||||
{
|
if (m_useLinks || m_useHardLinks) {
|
||||||
|
if (!filters.isEmpty())
|
||||||
|
filters += "|";
|
||||||
|
filters += "instance.cfg";
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "CopyFilters:" << filters;
|
||||||
|
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
// Set regex filter:
|
||||||
// FIXME: get this from the original instance type...
|
// FIXME: get this from the original instance type...
|
||||||
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
|
auto matcherReal = new RegexpMatcher(filters);
|
||||||
matcherReal->caseSensitive(false);
|
matcherReal->caseSensitive(false);
|
||||||
m_matcher.reset(matcherReal);
|
m_matcher.reset(matcherReal);
|
||||||
}
|
}
|
||||||
@ -23,10 +38,88 @@ void InstanceCopyTask::executeTask()
|
|||||||
{
|
{
|
||||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
||||||
|
|
||||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
auto copySaves = [&]() {
|
||||||
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
|
QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft"));
|
||||||
|
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
||||||
|
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
|
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) {
|
||||||
|
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
|
folderClone.matcher(m_matcher.get());
|
||||||
|
|
||||||
|
return folderClone();
|
||||||
|
} else if (m_useLinks || m_useHardLinks) {
|
||||||
|
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
|
||||||
|
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
|
||||||
|
|
||||||
|
bool there_were_errors = false;
|
||||||
|
|
||||||
|
if (!folderLink()) {
|
||||||
|
#if defined Q_OS_WIN32
|
||||||
|
if (!m_useHardLinks) {
|
||||||
|
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
||||||
|
|
||||||
|
qDebug() << "attempting to run with privelage";
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
bool got_priv_results = false;
|
||||||
|
|
||||||
|
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
|
||||||
|
if (!gotResults) {
|
||||||
|
qDebug() << "Privileged run exited without results!";
|
||||||
|
}
|
||||||
|
got_priv_results = gotResults;
|
||||||
|
loop.quit();
|
||||||
|
});
|
||||||
|
folderLink.runPrivileged();
|
||||||
|
|
||||||
|
loop.exec(); // wait for the finished signal
|
||||||
|
|
||||||
|
for (auto result : folderLink.getResults()) {
|
||||||
|
if (result.err_value != 0) {
|
||||||
|
there_were_errors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_copySaves) {
|
||||||
|
there_were_errors |= !copySaves();
|
||||||
|
}
|
||||||
|
|
||||||
|
return got_priv_results && !there_were_errors;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_copySaves) {
|
||||||
|
there_were_errors |= !copySaves();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !there_were_errors;
|
||||||
|
} else {
|
||||||
|
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
|
folderCopy.followSymlinks(false).matcher(m_matcher.get());
|
||||||
|
|
||||||
|
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);
|
||||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||||
@ -35,20 +128,40 @@ void InstanceCopyTask::executeTask()
|
|||||||
void InstanceCopyTask::copyFinished()
|
void InstanceCopyTask::copyFinished()
|
||||||
{
|
{
|
||||||
auto successful = m_copyFuture.result();
|
auto successful = m_copyFuture.result();
|
||||||
if(!successful)
|
if (!successful) {
|
||||||
{
|
|
||||||
emitFailed(tr("Instance folder copy failed."));
|
emitFailed(tr("Instance folder copy failed."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: shouldn't this be able to report errors?
|
// FIXME: shouldn't this be able to report errors?
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||||
|
|
||||||
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||||
inst->setName(m_instName);
|
inst->setName(name());
|
||||||
inst->setIconKey(m_instIcon);
|
inst->setIconKey(m_instIcon);
|
||||||
if(!m_keepPlaytime) {
|
if (!m_keepPlaytime) {
|
||||||
inst->resetTimePlayed();
|
inst->resetTimePlayed();
|
||||||
}
|
}
|
||||||
|
if (m_useLinks)
|
||||||
|
inst->addLinkedInstanceId(m_origInstance->id());
|
||||||
|
if (m_useLinks) {
|
||||||
|
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
|
||||||
|
|
||||||
|
QByteArray allowed_symlinks;
|
||||||
|
if (allowed_symlinks_file.exists()) {
|
||||||
|
allowed_symlinks.append(FS::read(allowed_symlinks_file.filePath()));
|
||||||
|
if (allowed_symlinks.right(1) != "\n")
|
||||||
|
allowed_symlinks.append("\n"); // we want to be on a new line
|
||||||
|
}
|
||||||
|
allowed_symlinks.append(m_origInstance->gameRoot().toUtf8());
|
||||||
|
allowed_symlinks.append("\n");
|
||||||
|
if (allowed_symlinks_file.isSymLink())
|
||||||
|
FS::deletePath(allowed_symlinks_file
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include "settings/SettingsObject.h"
|
#include <QUrl>
|
||||||
#include "BaseVersion.h"
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
#include "BaseVersion.h"
|
||||||
|
#include "InstanceCopyPrefs.h"
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
#include "settings/SettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
class InstanceCopyTask : public InstanceTask
|
class InstanceCopyTask : public InstanceTask
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
|
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
@ -22,10 +23,16 @@ protected:
|
|||||||
void copyFinished();
|
void copyFinished();
|
||||||
void copyAborted();
|
void copyAborted();
|
||||||
|
|
||||||
private: /* data */
|
private:
|
||||||
|
/* data */
|
||||||
InstancePtr m_origInstance;
|
InstancePtr m_origInstance;
|
||||||
QFuture<bool> m_copyFuture;
|
QFuture<bool> m_copyFuture;
|
||||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||||
std::unique_ptr<IPathMatcher> m_matcher;
|
std::unique_ptr<IPathMatcher> m_matcher;
|
||||||
bool m_keepPlaytime;
|
bool m_keepPlaytime;
|
||||||
|
bool m_useLinks = false;
|
||||||
|
bool m_useHardLinks = false;
|
||||||
|
bool m_copySaves = false;
|
||||||
|
bool m_linkRecursively = false;
|
||||||
|
bool m_useClone = false;
|
||||||
};
|
};
|
||||||
|
@ -1,40 +1,60 @@
|
|||||||
#include "InstanceCreationTask.h"
|
#include "InstanceCreationTask.h"
|
||||||
#include "settings/INISettingsObject.h"
|
|
||||||
#include "FileSystem.h"
|
|
||||||
|
|
||||||
//FIXME: remove this
|
#include <QDebug>
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include <QFile>
|
||||||
#include "minecraft/PackProfile.h"
|
|
||||||
|
|
||||||
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
|
InstanceCreationTask::InstanceCreationTask() = default;
|
||||||
{
|
|
||||||
m_version = version;
|
|
||||||
m_usingLoader = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion)
|
|
||||||
{
|
|
||||||
m_version = version;
|
|
||||||
m_usingLoader = true;
|
|
||||||
m_loader = loader;
|
|
||||||
m_loaderVersion = loaderVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCreationTask::executeTask()
|
void InstanceCreationTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
setAbortable(true);
|
||||||
{
|
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
if (updateInstance()) {
|
||||||
instanceSettings->suspendSave();
|
emitSucceeded();
|
||||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
return;
|
||||||
auto components = inst.getPackProfile();
|
|
||||||
components->buildingFromScratch();
|
|
||||||
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
|
||||||
if(m_usingLoader)
|
|
||||||
components->setComponentVersion(m_loader, m_loaderVersion->descriptor());
|
|
||||||
inst.setName(m_instName);
|
|
||||||
inst.setIconKey(m_instIcon);
|
|
||||||
instanceSettings->resumeSave();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When the user aborted in the update stage.
|
||||||
|
if (m_abort) {
|
||||||
|
emitAborted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!createInstance()) {
|
||||||
|
if (m_abort)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qWarning() << "Instance creation failed!";
|
||||||
|
if (!m_error_message.isEmpty()) {
|
||||||
|
qWarning() << "Reason: " << m_error_message;
|
||||||
|
emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message));
|
||||||
|
} else {
|
||||||
|
emitFailed(tr("Error while creating new instance."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is set, it means we're updating an instance. So, we now need to remove the
|
||||||
|
// 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.
|
||||||
|
if (shouldOverride()) {
|
||||||
|
setAbortable(false);
|
||||||
|
setStatus(tr("Removing old conflicting files..."));
|
||||||
|
qDebug() << "Removing old files";
|
||||||
|
|
||||||
|
for (auto path : m_files_to_remove) {
|
||||||
|
if (!QFile::exists(path))
|
||||||
|
continue;
|
||||||
|
qDebug() << "Removing" << path;
|
||||||
|
if (!QFile::remove(path)) {
|
||||||
|
qCritical() << "Couldn't remove the old conflicting files.";
|
||||||
|
emitFailed(tr("Failed to remove old conflicting files."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,46 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include <QUrl>
|
|
||||||
#include "settings/SettingsObject.h"
|
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
|
||||||
class InstanceCreationTask : public InstanceTask
|
class InstanceCreationTask : public InstanceTask {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceCreationTask(BaseVersionPtr version);
|
InstanceCreationTask();
|
||||||
explicit InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion);
|
virtual ~InstanceCreationTask() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
void executeTask() final override;
|
||||||
virtual void executeTask() override;
|
|
||||||
|
|
||||||
private: /* data */
|
/**
|
||||||
BaseVersionPtr m_version;
|
* Tries to update an already existing instance.
|
||||||
bool m_usingLoader;
|
*
|
||||||
QString m_loader;
|
* This can be implemented by subclasses to provide a way of updating an already existing
|
||||||
BaseVersionPtr m_loaderVersion;
|
* instance, according to that implementation's concept of 'identity' (i.e. instances that
|
||||||
|
* are updates / downgrades of one another).
|
||||||
|
*
|
||||||
|
* If this returns true, createInstance() will not run, so you should do all update steps in here.
|
||||||
|
* Otherwise, createInstance() is run as normal.
|
||||||
|
*/
|
||||||
|
virtual bool updateInstance() { return false; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* Returns whether the instance creation was successful (true) or not (false).
|
||||||
|
*/
|
||||||
|
virtual bool createInstance() { return false; };
|
||||||
|
|
||||||
|
QString getError() const { return m_error_message; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setError(const QString& message) { m_error_message = message; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_abort = false;
|
||||||
|
|
||||||
|
QStringList m_files_to_remove;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_error_message;
|
||||||
};
|
};
|
||||||
|
@ -35,72 +35,74 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "InstanceImportTask.h"
|
#include "InstanceImportTask.h"
|
||||||
#include <QtConcurrentRun>
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "BaseInstance.h"
|
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "MMCZip.h"
|
#include "MMCZip.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
|
|
||||||
|
#include "QObjectPtr.h"
|
||||||
#include "icons/IconList.h"
|
#include "icons/IconList.h"
|
||||||
#include "icons/IconUtils.h"
|
#include "icons/IconUtils.h"
|
||||||
|
|
||||||
|
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||||
|
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
|
||||||
|
#include "modplatform/flame/FlameInstanceCreationTask.h"
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
// FIXME: this does not belong here, it's Minecraft/Flame specific
|
#include <QtConcurrentRun>
|
||||||
#include <quazip/quazipdir.h>
|
|
||||||
#include "Json.h"
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
|
||||||
#include "minecraft/PackProfile.h"
|
|
||||||
#include "modplatform/flame/FileResolvingTask.h"
|
|
||||||
#include "modplatform/flame/PackManifest.h"
|
|
||||||
#include "modplatform/modrinth/ModrinthPackManifest.h"
|
|
||||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "icons/IconList.h"
|
|
||||||
#include "net/ChecksumValidator.h"
|
|
||||||
|
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
|
||||||
#include "ui/dialogs/ScrollMessageBox.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
|
#include <quazip/quazipdir.h>
|
||||||
{
|
|
||||||
m_sourceUrl = sourceUrl;
|
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
|
||||||
m_parent = parent;
|
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
|
||||||
}
|
{}
|
||||||
|
|
||||||
bool InstanceImportTask::abort()
|
bool InstanceImportTask::abort()
|
||||||
{
|
{
|
||||||
|
if (!canAbort())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (m_filesNetJob)
|
if (m_filesNetJob)
|
||||||
m_filesNetJob->abort();
|
m_filesNetJob->abort();
|
||||||
m_extractFuture.cancel();
|
if (m_extractFuture.isRunning()) {
|
||||||
|
// NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled,
|
||||||
|
// but we can use this call to check the state when the extraction finishes.
|
||||||
|
m_extractFuture.cancel();
|
||||||
|
m_extractFuture.waitForFinished();
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return Task::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::executeTask()
|
void InstanceImportTask::executeTask()
|
||||||
{
|
{
|
||||||
if (m_sourceUrl.isLocalFile())
|
setAbortable(true);
|
||||||
{
|
|
||||||
|
if (m_sourceUrl.isLocalFile()) {
|
||||||
m_archivePath = m_sourceUrl.toLocalFile();
|
m_archivePath = m_sourceUrl.toLocalFile();
|
||||||
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;
|
m_downloadRequired = true;
|
||||||
|
|
||||||
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 = new NetJob(tr("Modpack download"), APPLICATION->network());
|
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
|
||||||
m_archivePath = entry->getFullPath();
|
m_archivePath = entry->getFullPath();
|
||||||
auto job = m_filesNetJob.get();
|
|
||||||
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||||
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
||||||
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||||
|
|
||||||
m_filesNetJob->start();
|
m_filesNetJob->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -119,7 +121,13 @@ void InstanceImportTask::downloadFailed(QString reason)
|
|||||||
|
|
||||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||||
{
|
{
|
||||||
setProgress(current / 2, total);
|
setProgress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::downloadAborted()
|
||||||
|
{
|
||||||
|
emitAborted();
|
||||||
|
m_filesNetJob.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processZipPack()
|
void InstanceImportTask::processZipPack()
|
||||||
@ -161,18 +169,14 @@ void InstanceImportTask::processZipPack()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
|
QStringList paths_to_ignore { "overrides/" };
|
||||||
QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
|
|
||||||
|
|
||||||
if (!mmcRoot.isNull())
|
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
|
||||||
{
|
|
||||||
// process as MultiMC instance/pack
|
// process as MultiMC instance/pack
|
||||||
qDebug() << "MultiMC:" << mmcRoot;
|
qDebug() << "MultiMC:" << mmcRoot;
|
||||||
root = mmcRoot;
|
root = mmcRoot;
|
||||||
m_modpackType = ModpackType::MultiMC;
|
m_modpackType = ModpackType::MultiMC;
|
||||||
}
|
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) {
|
||||||
else if(!flameRoot.isNull())
|
|
||||||
{
|
|
||||||
// process as Flame pack
|
// process as Flame pack
|
||||||
qDebug() << "Flame:" << flameRoot;
|
qDebug() << "Flame:" << flameRoot;
|
||||||
root = flameRoot;
|
root = flameRoot;
|
||||||
@ -188,18 +192,20 @@ void InstanceImportTask::processZipPack()
|
|||||||
// make sure we extract just the pack
|
// make sure we extract just the pack
|
||||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
|
|
||||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::extractFinished()
|
void InstanceImportTask::extractFinished()
|
||||||
{
|
{
|
||||||
m_packZip.reset();
|
m_packZip.reset();
|
||||||
if (!m_extractFuture.result())
|
|
||||||
{
|
if (m_extractFuture.isCanceled())
|
||||||
|
return;
|
||||||
|
if (!m_extractFuture.result().has_value()) {
|
||||||
emitFailed(tr("Failed to extract modpack"));
|
emitFailed(tr("Failed to extract modpack"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
|
|
||||||
qDebug() << "Fixing permissions for extracted pack files...";
|
qDebug() << "Fixing permissions for extracted pack files...";
|
||||||
@ -253,300 +259,57 @@ void InstanceImportTask::extractFinished()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::extractAborted()
|
|
||||||
{
|
|
||||||
emitFailed(tr("Instance import has been aborted."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::processFlame()
|
void InstanceImportTask::processFlame()
|
||||||
{
|
{
|
||||||
const static QMap<QString,QString> forgemap = {
|
shared_qobject_ptr<FlameCreationTask> inst_creation_task = nullptr;
|
||||||
{"1.2.5", "3.4.9.171"},
|
if (!m_extra_info.isEmpty()) {
|
||||||
{"1.4.2", "6.0.1.355"},
|
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||||
{"1.4.7", "6.6.2.534"},
|
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||||
{"1.5.2", "7.8.1.737"}
|
auto pack_id = pack_id_it.value();
|
||||||
};
|
|
||||||
Flame::Manifest pack;
|
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
||||||
try
|
Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
|
||||||
{
|
auto pack_version_id = pack_version_id_it.value();
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
|
|
||||||
Flame::loadManifest(pack, configPath);
|
QString original_instance_id;
|
||||||
QFile::remove(configPath);
|
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
||||||
}
|
if (original_instance_id_it != m_extra_info.constEnd())
|
||||||
catch (const JSONValidationError &e)
|
original_instance_id = original_instance_id_it.value();
|
||||||
{
|
|
||||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||||
return;
|
} else {
|
||||||
}
|
// FIXME: Find a way to get IDs in directly imported ZIPs
|
||||||
if(!pack.overrides.isEmpty())
|
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
|
||||||
{
|
|
||||||
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
|
|
||||||
if (QFile::exists(overridePath))
|
|
||||||
{
|
|
||||||
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
|
||||||
if (!QFile::rename(overridePath, mcPath))
|
|
||||||
{
|
|
||||||
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString forgeVersion;
|
inst_creation_task->setName(*this);
|
||||||
QString fabricVersion;
|
inst_creation_task->setIcon(m_instIcon);
|
||||||
// TODO: is Quilt relevant here?
|
inst_creation_task->setGroup(m_instGroup);
|
||||||
for(auto &loader: pack.minecraft.modLoaders)
|
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||||
{
|
|
||||||
auto id = loader.id;
|
connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] {
|
||||||
if(id.startsWith("forge-"))
|
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||||
{
|
emitSucceeded();
|
||||||
id.remove("forge-");
|
|
||||||
forgeVersion = id;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(id.startsWith("fabric-"))
|
|
||||||
{
|
|
||||||
id.remove("fabric-");
|
|
||||||
fabricVersion = id;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
|
||||||
auto mcVersion = pack.minecraft.version;
|
|
||||||
// Hack to correct some 'special sauce'...
|
|
||||||
if(mcVersion.endsWith('.'))
|
|
||||||
{
|
|
||||||
mcVersion.remove(QRegularExpression("[.]+$"));
|
|
||||||
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
|
|
||||||
}
|
|
||||||
auto components = instance.getPackProfile();
|
|
||||||
components->buildingFromScratch();
|
|
||||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
|
||||||
if(!forgeVersion.isEmpty())
|
|
||||||
{
|
|
||||||
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
|
|
||||||
if(forgeVersion == "recommended")
|
|
||||||
{
|
|
||||||
if(forgemap.contains(mcVersion))
|
|
||||||
{
|
|
||||||
forgeVersion = forgemap[mcVersion];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logWarning(tr("Could not map recommended Forge version for Minecraft %1").arg(mcVersion));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
|
||||||
}
|
|
||||||
if(!fabricVersion.isEmpty())
|
|
||||||
{
|
|
||||||
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
|
|
||||||
}
|
|
||||||
if (m_instIcon != "default")
|
|
||||||
{
|
|
||||||
instance.setIconKey(m_instIcon);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(pack.name.contains("Direwolf20"))
|
|
||||||
{
|
|
||||||
instance.setIconKey("steve");
|
|
||||||
}
|
|
||||||
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
|
|
||||||
{
|
|
||||||
instance.setIconKey("ftb_logo");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// default to something other than the MultiMC default to distinguish these
|
|
||||||
instance.setIconKey("flame");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
|
|
||||||
QFileInfo jarmodsInfo(jarmodsPath);
|
|
||||||
if(jarmodsInfo.isDir())
|
|
||||||
{
|
|
||||||
// install all the jar mods
|
|
||||||
qDebug() << "Found jarmods:";
|
|
||||||
QDir jarmodsDir(jarmodsPath);
|
|
||||||
QStringList jarMods;
|
|
||||||
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
|
||||||
{
|
|
||||||
qDebug() << info.fileName();
|
|
||||||
jarMods.push_back(info.absoluteFilePath());
|
|
||||||
}
|
|
||||||
auto profile = instance.getPackProfile();
|
|
||||||
profile->installJarMods(jarMods);
|
|
||||||
// nuke the original files
|
|
||||||
FS::deletePath(jarmodsPath);
|
|
||||||
}
|
|
||||||
instance.setName(m_instName);
|
|
||||||
m_modIdResolver = new Flame::FileResolvingTask(APPLICATION->network(), pack);
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
|
|
||||||
{
|
|
||||||
auto results = m_modIdResolver->getResults();
|
|
||||||
//first check for blocked mods
|
|
||||||
QString text;
|
|
||||||
auto anyBlocked = false;
|
|
||||||
for(const auto& result: results.files.values()) {
|
|
||||||
if (!result.resolved || result.url.isEmpty()) {
|
|
||||||
text += QString("%1: <a href='%2'>%2</a><br/>").arg(result.fileName, result.websiteUrl);
|
|
||||||
anyBlocked = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(anyBlocked) {
|
|
||||||
qWarning() << "Blocked mods found, displaying mod list";
|
|
||||||
|
|
||||||
auto message_dialog = new ScrollMessageBox(m_parent,
|
|
||||||
tr("Blocked mods found"),
|
|
||||||
tr("The following mods were blocked on third party launchers.<br/>"
|
|
||||||
"You will need to manually download them and add them to the modpack"),
|
|
||||||
text);
|
|
||||||
message_dialog->setModal(true);
|
|
||||||
|
|
||||||
if (message_dialog->exec()) {
|
|
||||||
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
|
||||||
for (const auto &result: m_modIdResolver->getResults().files) {
|
|
||||||
QString filename = result.fileName;
|
|
||||||
if (!result.required) {
|
|
||||||
filename += ".disabled";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
|
||||||
auto path = FS::PathCombine(m_stagingPath, relpath);
|
|
||||||
|
|
||||||
switch (result.type) {
|
|
||||||
case Flame::File::Type::Folder: {
|
|
||||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
|
||||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
|
||||||
}
|
|
||||||
case Flame::File::Type::SingleFile:
|
|
||||||
case Flame::File::Type::Mod: {
|
|
||||||
if (!result.url.isEmpty()) {
|
|
||||||
qDebug() << "Will download" << result.url << "to" << path;
|
|
||||||
auto dl = Net::Download::makeFile(result.url, path);
|
|
||||||
m_filesNetJob->addNetAction(dl);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Flame::File::Type::Modpack:
|
|
||||||
logWarning(
|
|
||||||
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
|
|
||||||
relpath));
|
|
||||||
break;
|
|
||||||
case Flame::File::Type::Cmod2:
|
|
||||||
case Flame::File::Type::Ctoc:
|
|
||||||
case Flame::File::Type::Unknown:
|
|
||||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_modIdResolver.reset();
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitSucceeded();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitFailed(reason);
|
|
||||||
});
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
|
||||||
setProgress(current, total);
|
|
||||||
});
|
|
||||||
setStatus(tr("Downloading mods..."));
|
|
||||||
m_filesNetJob->start();
|
|
||||||
} else {
|
|
||||||
m_modIdResolver.reset();
|
|
||||||
emitFailed("Canceled");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//TODO extract to function ?
|
|
||||||
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
|
||||||
for (const auto &result: m_modIdResolver->getResults().files) {
|
|
||||||
QString filename = result.fileName;
|
|
||||||
if (!result.required) {
|
|
||||||
filename += ".disabled";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
|
||||||
auto path = FS::PathCombine(m_stagingPath, relpath);
|
|
||||||
|
|
||||||
switch (result.type) {
|
|
||||||
case Flame::File::Type::Folder: {
|
|
||||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
|
||||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
|
||||||
}
|
|
||||||
case Flame::File::Type::SingleFile:
|
|
||||||
case Flame::File::Type::Mod: {
|
|
||||||
if (!result.url.isEmpty()) {
|
|
||||||
qDebug() << "Will download" << result.url << "to" << path;
|
|
||||||
auto dl = Net::Download::makeFile(result.url, path);
|
|
||||||
m_filesNetJob->addNetAction(dl);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Flame::File::Type::Modpack:
|
|
||||||
logWarning(
|
|
||||||
tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(
|
|
||||||
relpath));
|
|
||||||
break;
|
|
||||||
case Flame::File::Type::Cmod2:
|
|
||||||
case Flame::File::Type::Ctoc:
|
|
||||||
case Flame::File::Type::Unknown:
|
|
||||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_modIdResolver.reset();
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]() {
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitSucceeded();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason) {
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitFailed(reason);
|
|
||||||
});
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total) {
|
|
||||||
setProgress(current, total);
|
|
||||||
});
|
|
||||||
setStatus(tr("Downloading mods..."));
|
|
||||||
m_filesNetJob->start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
|
|
||||||
{
|
|
||||||
m_modIdResolver.reset();
|
|
||||||
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
|
|
||||||
});
|
});
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
|
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||||
{
|
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||||
setProgress(current, total);
|
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||||
});
|
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
|
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||||
{
|
|
||||||
setStatus(status);
|
connect(this, &Task::aborted, inst_creation_task.get(), &InstanceCreationTask::abort);
|
||||||
});
|
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
|
||||||
m_modIdResolver->start();
|
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||||
|
|
||||||
|
inst_creation_task->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processTechnic()
|
void InstanceImportTask::processTechnic()
|
||||||
{
|
{
|
||||||
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
|
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor{ new Technic::TechnicPackProcessor };
|
||||||
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
|
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
|
||||||
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
|
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
|
||||||
packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath);
|
packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processMultiMC()
|
void InstanceImportTask::processMultiMC()
|
||||||
@ -560,7 +323,7 @@ void InstanceImportTask::processMultiMC()
|
|||||||
instance.resetTimePlayed();
|
instance.resetTimePlayed();
|
||||||
|
|
||||||
// set a new nice name
|
// set a new nice name
|
||||||
instance.setName(m_instName);
|
instance.setName(name());
|
||||||
|
|
||||||
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
||||||
if (m_instIcon != "default") {
|
if (m_instIcon != "default") {
|
||||||
@ -581,198 +344,55 @@ void InstanceImportTask::processMultiMC()
|
|||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.modrinth.com/docs/modpacks/format_definition/
|
|
||||||
void InstanceImportTask::processModrinth()
|
void InstanceImportTask::processModrinth()
|
||||||
{
|
{
|
||||||
std::vector<Modrinth::File> files;
|
ModrinthCreationTask* inst_creation_task = nullptr;
|
||||||
QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
|
if (!m_extra_info.isEmpty()) {
|
||||||
try {
|
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||||
QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
|
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||||
auto doc = Json::requireDocument(indexPath);
|
auto pack_id = pack_id_it.value();
|
||||||
auto obj = Json::requireObject(doc, "modrinth.index.json");
|
|
||||||
int formatVersion = Json::requireInteger(obj, "formatVersion", "modrinth.index.json");
|
|
||||||
if (formatVersion == 1) {
|
|
||||||
auto game = Json::requireString(obj, "game", "modrinth.index.json");
|
|
||||||
if (game != "minecraft") {
|
|
||||||
throw JSONValidationError("Unknown game: " + game);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
QString pack_version_id;
|
||||||
bool had_optional = false;
|
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
||||||
for (auto modInfo : jsonFiles) {
|
if (pack_version_id_it != m_extra_info.constEnd())
|
||||||
Modrinth::File file;
|
pack_version_id = pack_version_id_it.value();
|
||||||
file.path = Json::requireString(modInfo, "path");
|
|
||||||
|
|
||||||
auto env = Json::ensureObject(modInfo, "env");
|
QString original_instance_id;
|
||||||
// 'env' field is optional
|
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
||||||
if (!env.isEmpty()) {
|
if (original_instance_id_it != m_extra_info.constEnd())
|
||||||
QString support = Json::ensureString(env, "client", "unsupported");
|
original_instance_id = original_instance_id_it.value();
|
||||||
if (support == "unsupported") {
|
|
||||||
continue;
|
|
||||||
} else if (support == "optional") {
|
|
||||||
// TODO: Make a review dialog for choosing which ones the user wants!
|
|
||||||
if (!had_optional) {
|
|
||||||
had_optional = true;
|
|
||||||
auto info = CustomMessageBox::selectable(
|
|
||||||
m_parent, tr("Optional mod detected!"),
|
|
||||||
tr("One or more mods from this modpack are optional. They will be downloaded, but disabled by default!"),
|
|
||||||
QMessageBox::Information);
|
|
||||||
info->exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.path.endsWith(".jar"))
|
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||||
file.path += ".disabled";
|
} else {
|
||||||
}
|
QString pack_id;
|
||||||
}
|
if (!m_sourceUrl.isEmpty()) {
|
||||||
|
QRegularExpression regex(R"(data\/([^\/]*)\/versions)");
|
||||||
QJsonObject hashes = Json::requireObject(modInfo, "hashes");
|
pack_id = regex.match(m_sourceUrl.toString()).captured(1);
|
||||||
QString hash;
|
|
||||||
QCryptographicHash::Algorithm hashAlgorithm;
|
|
||||||
hash = Json::ensureString(hashes, "sha1");
|
|
||||||
hashAlgorithm = QCryptographicHash::Sha1;
|
|
||||||
if (hash.isEmpty()) {
|
|
||||||
hash = Json::ensureString(hashes, "sha512");
|
|
||||||
hashAlgorithm = QCryptographicHash::Sha512;
|
|
||||||
if (hash.isEmpty()) {
|
|
||||||
hash = Json::ensureString(hashes, "sha256");
|
|
||||||
hashAlgorithm = QCryptographicHash::Sha256;
|
|
||||||
if (hash.isEmpty()) {
|
|
||||||
throw JSONValidationError("No hash found for: " + file.path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file.hash = QByteArray::fromHex(hash.toLatin1());
|
|
||||||
file.hashAlgorithm = hashAlgorithm;
|
|
||||||
|
|
||||||
// Do not use requireUrl, which uses StrictMode, instead use QUrl's default TolerantMode
|
|
||||||
// (as Modrinth seems to incorrectly handle spaces)
|
|
||||||
|
|
||||||
auto download_arr = Json::ensureArray(modInfo, "downloads");
|
|
||||||
for(auto download : download_arr) {
|
|
||||||
qWarning() << download.toString();
|
|
||||||
bool is_last = download.toString() == download_arr.last().toString();
|
|
||||||
|
|
||||||
auto download_url = QUrl(download.toString());
|
|
||||||
|
|
||||||
if (!download_url.isValid()) {
|
|
||||||
qDebug() << QString("Download URL (%1) for %2 is not a correctly formatted URL")
|
|
||||||
.arg(download_url.toString(), file.path);
|
|
||||||
if(is_last && file.downloads.isEmpty())
|
|
||||||
throw JSONValidationError(tr("Download URL for %1 is not a correctly formatted URL").arg(file.path));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
file.downloads.push_back(download_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
files.push_back(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dependencies = Json::requireObject(obj, "dependencies", "modrinth.index.json");
|
|
||||||
for (auto it = dependencies.begin(), end = dependencies.end(); it != end; ++it) {
|
|
||||||
QString name = it.key();
|
|
||||||
if (name == "minecraft") {
|
|
||||||
minecraftVersion = Json::requireString(*it, "Minecraft version");
|
|
||||||
}
|
|
||||||
else if (name == "fabric-loader") {
|
|
||||||
fabricVersion = Json::requireString(*it, "Fabric Loader version");
|
|
||||||
}
|
|
||||||
else if (name == "quilt-loader") {
|
|
||||||
quiltVersion = Json::requireString(*it, "Quilt Loader version");
|
|
||||||
}
|
|
||||||
else if (name == "forge") {
|
|
||||||
forgeVersion = Json::requireString(*it, "Forge version");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw JSONValidationError("Unknown dependency type: " + name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw JSONValidationError(QStringLiteral("Unknown format version: %s").arg(formatVersion));
|
|
||||||
}
|
}
|
||||||
QFile::remove(indexPath);
|
|
||||||
} catch (const JSONValidationError& e) {
|
// FIXME: Find a way to get the ID in directly imported ZIPs
|
||||||
emitFailed(tr("Could not understand pack index:\n") + e.cause());
|
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inst_creation_task->setName(*this);
|
||||||
|
inst_creation_task->setIcon(m_instIcon);
|
||||||
|
inst_creation_task->setGroup(m_instGroup);
|
||||||
|
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||||
|
|
||||||
auto mcPath = FS::PathCombine(m_stagingPath, ".minecraft");
|
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
||||||
|
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||||
auto override_path = FS::PathCombine(m_stagingPath, "overrides");
|
emitSucceeded();
|
||||||
if (QFile::exists(override_path)) {
|
|
||||||
if (!QFile::rename(override_path, mcPath)) {
|
|
||||||
emitFailed(tr("Could not rename the overrides folder:\n") + "overrides");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do client overrides
|
|
||||||
auto client_override_path = FS::PathCombine(m_stagingPath, "client-overrides");
|
|
||||||
if (QFile::exists(client_override_path)) {
|
|
||||||
if (!FS::overrideFolder(mcPath, client_override_path)) {
|
|
||||||
emitFailed(tr("Could not rename the client overrides folder:\n") + "client overrides");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
|
||||||
auto components = instance.getPackProfile();
|
|
||||||
components->buildingFromScratch();
|
|
||||||
components->setComponentVersion("net.minecraft", minecraftVersion, true);
|
|
||||||
if (!fabricVersion.isEmpty())
|
|
||||||
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
|
|
||||||
if (!quiltVersion.isEmpty())
|
|
||||||
components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion);
|
|
||||||
if (!forgeVersion.isEmpty())
|
|
||||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
|
||||||
if (m_instIcon != "default")
|
|
||||||
{
|
|
||||||
instance.setIconKey(m_instIcon);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
instance.setIconKey("modrinth");
|
|
||||||
}
|
|
||||||
instance.setName(m_instName);
|
|
||||||
instance.saveNow();
|
|
||||||
|
|
||||||
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
|
||||||
for (auto file : files)
|
|
||||||
{
|
|
||||||
auto path = FS::PathCombine(m_stagingPath, ".minecraft", file.path);
|
|
||||||
qDebug() << "Will try to download" << file.downloads.front() << "to" << path;
|
|
||||||
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
|
|
||||||
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
|
||||||
m_filesNetJob->addNetAction(dl);
|
|
||||||
|
|
||||||
if (file.downloads.size() > 0) {
|
|
||||||
// FIXME: This really needs to be put into a ConcurrentTask of
|
|
||||||
// MultipleOptionsTask's , once those exist :)
|
|
||||||
connect(dl.get(), &NetAction::failed, [this, &file, path, dl]{
|
|
||||||
auto dl = Net::Download::makeFile(file.downloads.dequeue(), path);
|
|
||||||
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
|
||||||
m_filesNetJob->addNetAction(dl);
|
|
||||||
dl->succeeded();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
|
|
||||||
{
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitSucceeded();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](const QString &reason)
|
|
||||||
{
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitFailed(reason);
|
|
||||||
});
|
});
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||||
{
|
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
||||||
setProgress(current, total);
|
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||||
});
|
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
||||||
setStatus(tr("Downloading mods..."));
|
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
|
||||||
m_filesNetJob->start();
|
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
|
||||||
|
|
||||||
|
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
|
||||||
|
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
|
||||||
|
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||||
|
|
||||||
|
inst_creation_task->start();
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
#include "modplatform/flame/PackManifest.h"
|
#include "modplatform/flame/PackManifest.h"
|
||||||
|
|
||||||
#include <nonstd/optional>
|
#include <optional>
|
||||||
|
|
||||||
class QuaZip;
|
class QuaZip;
|
||||||
namespace Flame
|
namespace Flame
|
||||||
@ -56,9 +56,8 @@ class InstanceImportTask : public InstanceTask
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr);
|
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||||
|
|
||||||
bool canAbort() const override { return true; }
|
|
||||||
bool abort() override;
|
bool abort() override;
|
||||||
const QVector<Flame::File> &getBlockedFiles() const
|
const QVector<Flame::File> &getBlockedFiles() const
|
||||||
{
|
{
|
||||||
@ -80,8 +79,8 @@ private slots:
|
|||||||
void downloadSucceeded();
|
void downloadSucceeded();
|
||||||
void downloadFailed(QString reason);
|
void downloadFailed(QString reason);
|
||||||
void downloadProgressChanged(qint64 current, qint64 total);
|
void downloadProgressChanged(qint64 current, qint64 total);
|
||||||
|
void downloadAborted();
|
||||||
void extractFinished();
|
void extractFinished();
|
||||||
void extractAborted();
|
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
NetJob::Ptr m_filesNetJob;
|
NetJob::Ptr m_filesNetJob;
|
||||||
@ -90,8 +89,8 @@ private: /* data */
|
|||||||
QString m_archivePath;
|
QString m_archivePath;
|
||||||
bool m_downloadRequired = false;
|
bool m_downloadRequired = false;
|
||||||
std::unique_ptr<QuaZip> m_packZip;
|
std::unique_ptr<QuaZip> m_packZip;
|
||||||
QFuture<nonstd::optional<QStringList>> m_extractFuture;
|
QFuture<std::optional<QStringList>> m_extractFuture;
|
||||||
QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
|
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||||
QVector<Flame::File> m_blockedMods;
|
QVector<Flame::File> m_blockedMods;
|
||||||
enum class ModpackType{
|
enum class ModpackType{
|
||||||
Unknown,
|
Unknown,
|
||||||
@ -101,6 +100,10 @@ private: /* data */
|
|||||||
Modrinth,
|
Modrinth,
|
||||||
} m_modpackType = ModpackType::Unknown;
|
} m_modpackType = ModpackType::Unknown;
|
||||||
|
|
||||||
|
// Extra info we might need, that's available before, but can't be derived from
|
||||||
|
// the source URL / the resource it points to alone.
|
||||||
|
QMap<QString, QString> m_extra_info;
|
||||||
|
|
||||||
//FIXME: nuke
|
//FIXME: nuke
|
||||||
QWidget* m_parent;
|
QWidget* m_parent;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -19,13 +19,15 @@
|
|||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QStack>
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
|
|
||||||
class QFileSystemWatcher;
|
class QFileSystemWatcher;
|
||||||
class InstanceTask;
|
class InstanceTask;
|
||||||
|
struct InstanceName;
|
||||||
|
|
||||||
using InstanceId = QString;
|
using InstanceId = QString;
|
||||||
using GroupId = QString;
|
using GroupId = QString;
|
||||||
using InstanceLocator = std::pair<InstancePtr, int>;
|
using InstanceLocator = std::pair<InstancePtr, int>;
|
||||||
@ -46,6 +48,12 @@ enum class GroupsState
|
|||||||
Dirty
|
Dirty
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TrashHistoryItem {
|
||||||
|
QString id;
|
||||||
|
QString polyPath;
|
||||||
|
QString trashPath;
|
||||||
|
QString groupName;
|
||||||
|
};
|
||||||
|
|
||||||
class InstanceList : public QAbstractListModel
|
class InstanceList : public QAbstractListModel
|
||||||
{
|
{
|
||||||
@ -93,7 +101,10 @@ public:
|
|||||||
InstListError loadList();
|
InstListError loadList();
|
||||||
void saveNow();
|
void saveNow();
|
||||||
|
|
||||||
|
/* O(n) */
|
||||||
InstancePtr getInstanceById(QString id) const;
|
InstancePtr getInstanceById(QString id) const;
|
||||||
|
/* O(n) */
|
||||||
|
InstancePtr getInstanceByManagedName(const QString& managed_name) const;
|
||||||
QModelIndex getInstanceIndexById(const QString &id) const;
|
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||||
QStringList getGroups();
|
QStringList getGroups();
|
||||||
bool isGroupCollapsed(const QString &groupName);
|
bool isGroupCollapsed(const QString &groupName);
|
||||||
@ -102,6 +113,9 @@ public:
|
|||||||
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
||||||
|
|
||||||
void deleteGroup(const GroupId & name);
|
void deleteGroup(const GroupId & name);
|
||||||
|
bool trashInstance(const InstanceId &id);
|
||||||
|
bool trashedSomething();
|
||||||
|
void undoTrashInstance();
|
||||||
void deleteInstance(const InstanceId & id);
|
void deleteInstance(const InstanceId & id);
|
||||||
|
|
||||||
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
||||||
@ -116,8 +130,10 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
||||||
* Used by instance manipulation tasks.
|
* Used by instance manipulation tasks.
|
||||||
|
* should_override is used when another similar instance already exists, and we want to override it
|
||||||
|
* - for instance, when updating it.
|
||||||
*/
|
*/
|
||||||
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName);
|
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
||||||
@ -138,6 +154,8 @@ public:
|
|||||||
QStringList mimeTypes() const override;
|
QStringList mimeTypes() const override;
|
||||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||||
|
|
||||||
|
QStringList getLinkedInstancesById(const QString &id) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dataIsInvalid();
|
void dataIsInvalid();
|
||||||
void instancesChanged();
|
void instancesChanged();
|
||||||
@ -180,4 +198,6 @@ private:
|
|||||||
QSet<InstanceId> instanceSet;
|
QSet<InstanceId> instanceSet;
|
||||||
bool m_groupsLoaded = false;
|
bool m_groupsLoaded = false;
|
||||||
bool m_instancesProbed = false;
|
bool m_instancesProbed = false;
|
||||||
|
|
||||||
|
QStack<TrashHistoryItem> m_trashHistory;
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "ui/pages/BasePageProvider.h"
|
#include "ui/pages/BasePageProvider.h"
|
||||||
#include "ui/pages/instance/LogPage.h"
|
#include "ui/pages/instance/LogPage.h"
|
||||||
#include "ui/pages/instance/VersionPage.h"
|
#include "ui/pages/instance/VersionPage.h"
|
||||||
|
#include "ui/pages/instance/ManagedPackPage.h"
|
||||||
#include "ui/pages/instance/ModFolderPage.h"
|
#include "ui/pages/instance/ModFolderPage.h"
|
||||||
#include "ui/pages/instance/ResourcePackPage.h"
|
#include "ui/pages/instance/ResourcePackPage.h"
|
||||||
#include "ui/pages/instance/TexturePackPage.h"
|
#include "ui/pages/instance/TexturePackPage.h"
|
||||||
@ -33,13 +34,15 @@ public:
|
|||||||
values.append(new LogPage(inst));
|
values.append(new LogPage(inst));
|
||||||
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
||||||
values.append(new VersionPage(onesix.get()));
|
values.append(new VersionPage(onesix.get()));
|
||||||
|
values.append(ManagedPackPage::createPage(onesix.get()));
|
||||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
|
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
|
||||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
modsPage->setFilter("%1 (*.zip *.jar *.litemod *.nilmod)");
|
||||||
values.append(modsPage);
|
values.append(modsPage);
|
||||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
|
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
|
||||||
values.append(new ResourcePackPage(onesix.get()));
|
values.append(new NilModFolderPage(onesix.get(), onesix->nilModList()));
|
||||||
values.append(new TexturePackPage(onesix.get()));
|
values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList()));
|
||||||
values.append(new ShaderPackPage(onesix.get()));
|
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
|
||||||
|
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.get(), onesix->worldList()));
|
||||||
values.append(new ServersPage(onesix));
|
values.append(new ServersPage(onesix));
|
||||||
|
@ -1,9 +1,75 @@
|
|||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
|
||||||
InstanceTask::InstanceTask()
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
|
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
||||||
{
|
{
|
||||||
|
auto dialog =
|
||||||
|
CustomMessageBox::selectable(parent, QObject::tr("Change instance name"),
|
||||||
|
QObject::tr("The instance's name seems to include the old version. Would you like to update it?\n\n"
|
||||||
|
"Old name: %1\n"
|
||||||
|
"New name: %2")
|
||||||
|
.arg(old_name, new_name),
|
||||||
|
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes);
|
||||||
|
auto result = dialog->exec();
|
||||||
|
|
||||||
|
if (result == QMessageBox::Yes)
|
||||||
|
return InstanceNameChange::ShouldChange;
|
||||||
|
return InstanceNameChange::ShouldKeep;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceTask::~InstanceTask()
|
ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name)
|
||||||
{
|
{
|
||||||
|
auto info = CustomMessageBox::selectable(
|
||||||
|
parent, QObject::tr("Similar modpack was found!"),
|
||||||
|
QObject::tr("One or more of your instances are from this same modpack%1. Do you want to create a "
|
||||||
|
"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).")
|
||||||
|
.arg(original_version_name),
|
||||||
|
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
|
||||||
|
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance"));
|
||||||
|
info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance"));
|
||||||
|
info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
|
||||||
|
|
||||||
|
info->exec();
|
||||||
|
|
||||||
|
if (info->clickedButton() == info->button(QMessageBox::Ok))
|
||||||
|
return ShouldUpdate::Update;
|
||||||
|
if (info->clickedButton() == info->button(QMessageBox::Abort))
|
||||||
|
return ShouldUpdate::SkipUpdating;
|
||||||
|
return ShouldUpdate::Cancel;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString InstanceName::name() const
|
||||||
|
{
|
||||||
|
if (!m_modified_name.isEmpty())
|
||||||
|
return modifiedName();
|
||||||
|
return QString("%1 %2").arg(m_original_name, m_original_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString InstanceName::originalName() const
|
||||||
|
{
|
||||||
|
return m_original_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString InstanceName::modifiedName() const
|
||||||
|
{
|
||||||
|
if (!m_modified_name.isEmpty())
|
||||||
|
return m_modified_name;
|
||||||
|
return m_original_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString InstanceName::version() const
|
||||||
|
{
|
||||||
|
return m_original_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceName::setName(InstanceName& other)
|
||||||
|
{
|
||||||
|
m_original_name = other.m_original_name;
|
||||||
|
m_original_version = other.m_original_version;
|
||||||
|
m_modified_name = other.m_modified_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceTask::InstanceTask() : Task(), InstanceName() {}
|
||||||
|
@ -1,52 +1,72 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
class InstanceTask : public Task
|
/* Helpers */
|
||||||
{
|
enum class InstanceNameChange { ShouldChange, ShouldKeep };
|
||||||
|
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name);
|
||||||
|
enum class ShouldUpdate { Update, SkipUpdating, Cancel };
|
||||||
|
[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name);
|
||||||
|
|
||||||
|
struct InstanceName {
|
||||||
|
public:
|
||||||
|
InstanceName() = default;
|
||||||
|
InstanceName(QString name, QString version) : m_original_name(std::move(name)), m_original_version(std::move(version)) {}
|
||||||
|
|
||||||
|
[[nodiscard]] QString modifiedName() const;
|
||||||
|
[[nodiscard]] QString originalName() const;
|
||||||
|
[[nodiscard]] QString name() const;
|
||||||
|
[[nodiscard]] QString version() const;
|
||||||
|
|
||||||
|
void setName(QString name) { m_modified_name = name; }
|
||||||
|
void setName(InstanceName& other);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_original_name;
|
||||||
|
QString m_original_version;
|
||||||
|
|
||||||
|
QString m_modified_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InstanceTask : public Task, public InstanceName {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceTask();
|
InstanceTask();
|
||||||
virtual ~InstanceTask();
|
~InstanceTask() override = default;
|
||||||
|
|
||||||
void setParentSettings(SettingsObjectPtr settings)
|
void setParentSettings(SettingsObjectPtr settings) { m_globalSettings = settings; }
|
||||||
|
|
||||||
|
void setStagingPath(const QString& stagingPath) { m_stagingPath = stagingPath; }
|
||||||
|
|
||||||
|
void setIcon(const QString& icon) { m_instIcon = icon; }
|
||||||
|
|
||||||
|
void setGroup(const QString& group) { m_instGroup = group; }
|
||||||
|
QString group() const { return m_instGroup; }
|
||||||
|
|
||||||
|
[[nodiscard]] bool shouldConfirmUpdate() const { return m_confirm_update; }
|
||||||
|
void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; }
|
||||||
|
|
||||||
|
bool shouldOverride() const { return m_override_existing; }
|
||||||
|
|
||||||
|
[[nodiscard]] QString originalInstanceID() const { return m_original_instance_id; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setOverride(bool override, QString instance_id_to_override = {})
|
||||||
{
|
{
|
||||||
m_globalSettings = settings;
|
m_override_existing = override;
|
||||||
|
if (!instance_id_to_override.isEmpty())
|
||||||
|
m_original_instance_id = instance_id_to_override;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setStagingPath(const QString &stagingPath)
|
protected: /* data */
|
||||||
{
|
|
||||||
m_stagingPath = stagingPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setName(const QString &name)
|
|
||||||
{
|
|
||||||
m_instName = name;
|
|
||||||
}
|
|
||||||
QString name() const
|
|
||||||
{
|
|
||||||
return m_instName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setIcon(const QString &icon)
|
|
||||||
{
|
|
||||||
m_instIcon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGroup(const QString &group)
|
|
||||||
{
|
|
||||||
m_instGroup = group;
|
|
||||||
}
|
|
||||||
QString group() const
|
|
||||||
{
|
|
||||||
return m_instGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected: /* data */
|
|
||||||
SettingsObjectPtr m_globalSettings;
|
SettingsObjectPtr m_globalSettings;
|
||||||
QString m_instName;
|
|
||||||
QString m_instIcon;
|
QString m_instIcon;
|
||||||
QString m_instGroup;
|
QString m_instGroup;
|
||||||
QString m_stagingPath;
|
QString m_stagingPath;
|
||||||
|
|
||||||
|
bool m_override_existing = false;
|
||||||
|
bool m_confirm_update = true;
|
||||||
|
|
||||||
|
QString m_original_instance_id;
|
||||||
};
|
};
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
#include "JavaCommon.h"
|
#include "JavaCommon.h"
|
||||||
#include "java/JavaUtils.h"
|
#include "java/JavaUtils.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include <MMCStrings.h>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||||
@ -122,8 +122,7 @@ void JavaCommon::TestCheck::run()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker());
|
||||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
|
||||||
SLOT(checkFinished(JavaCheckResult)));
|
|
||||||
checker->m_path = m_path;
|
checker->m_path = m_path;
|
||||||
checker->performCheck();
|
checker->performCheck();
|
||||||
}
|
}
|
||||||
@ -137,8 +136,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker());
|
||||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
|
||||||
SLOT(checkFinishedWithArgs(JavaCheckResult)));
|
|
||||||
checker->m_path = m_path;
|
checker->m_path = m_path;
|
||||||
checker->m_args = m_args;
|
checker->m_args = m_args;
|
||||||
checker->m_minMem = m_minMem;
|
checker->m_minMem = m_minMem;
|
||||||
|
@ -93,8 +93,8 @@ void LaunchController::decideAccount()
|
|||||||
auto reply = CustomMessageBox::selectable(
|
auto reply = CustomMessageBox::selectable(
|
||||||
m_parentWidget,
|
m_parentWidget,
|
||||||
tr("No Accounts"),
|
tr("No Accounts"),
|
||||||
tr("In order to play Minecraft, you must have at least one Mojang or Microsoft "
|
tr("In order to play Minecraft, you must have at least one Microsoft or Mojang "
|
||||||
"account logged in. "
|
"account logged in. Mojang accounts can only be used offline. "
|
||||||
"Would you like to open the account manager to add an account now?"),
|
"Would you like to open the account manager to add an account now?"),
|
||||||
QMessageBox::Information,
|
QMessageBox::Information,
|
||||||
QMessageBox::Yes | QMessageBox::No
|
QMessageBox::Yes | QMessageBox::No
|
||||||
@ -112,7 +112,15 @@ void LaunchController::decideAccount()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_accountToUse = accounts->defaultAccount();
|
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
|
||||||
|
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
|
||||||
|
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
|
||||||
|
if (instanceAccountIndex == -1) {
|
||||||
|
m_accountToUse = accounts->defaultAccount();
|
||||||
|
} else {
|
||||||
|
m_accountToUse = accounts->at(instanceAccountIndex);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_accountToUse)
|
if (!m_accountToUse)
|
||||||
{
|
{
|
||||||
// If no default account is set, ask the user which one to use.
|
// If no default account is set, ask the user which one to use.
|
||||||
@ -145,18 +153,29 @@ void LaunchController::login() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we try empty password first :)
|
|
||||||
QString password;
|
|
||||||
// we loop until the user succeeds in logging in or gives up
|
// we loop until the user succeeds in logging in or gives up
|
||||||
bool tryagain = true;
|
bool tryagain = true;
|
||||||
// the failure. the default failure.
|
unsigned int tries = 0;
|
||||||
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter your password to log in again. <br /> <br /> This could be caused by a password change.");
|
|
||||||
QString failReason = needLoginAgain;
|
|
||||||
|
|
||||||
while (tryagain)
|
while (tryagain)
|
||||||
{
|
{
|
||||||
|
if (tries > 0 && tries % 3 == 0) {
|
||||||
|
auto result = QMessageBox::question(
|
||||||
|
m_parentWidget,
|
||||||
|
tr("Continue launch?"),
|
||||||
|
tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?")
|
||||||
|
.arg(tries)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == QMessageBox::No) {
|
||||||
|
emitAborted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tries++;
|
||||||
m_session = std::make_shared<AuthSession>();
|
m_session = std::make_shared<AuthSession>();
|
||||||
m_session->wants_online = m_online;
|
m_session->wants_online = m_online;
|
||||||
|
m_session->demo = m_demo;
|
||||||
m_accountToUse->fillSession(m_session);
|
m_accountToUse->fillSession(m_session);
|
||||||
|
|
||||||
// Launch immediately in true offline mode
|
// Launch immediately in true offline mode
|
||||||
@ -174,12 +193,18 @@ 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 message = tr("Choose your offline mode player name.");
|
||||||
|
if(m_session->demo) {
|
||||||
|
message = tr("Choose your demo mode player name.");
|
||||||
|
}
|
||||||
|
|
||||||
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
||||||
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
||||||
QString name = QInputDialog::getText(
|
QString name = QInputDialog::getText(
|
||||||
m_parentWidget,
|
m_parentWidget,
|
||||||
tr("Player name"),
|
tr("Player name"),
|
||||||
tr("Choose your offline mode player name."),
|
message,
|
||||||
QLineEdit::Normal,
|
QLineEdit::Normal,
|
||||||
usedname,
|
usedname,
|
||||||
&ok
|
&ok
|
||||||
@ -357,15 +382,15 @@ void LaunchController::launchInstance()
|
|||||||
}
|
}
|
||||||
resolved_servers = resolved_servers + "]\n\n";
|
resolved_servers = resolved_servers + "]\n\n";
|
||||||
}
|
}
|
||||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
||||||
} else {
|
} else {
|
||||||
online_mode = "offline";
|
online_mode = m_demo ? "demo" : "offline";
|
||||||
}
|
}
|
||||||
|
|
||||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
||||||
|
|
||||||
// Prepend Version
|
// Prepend Version
|
||||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_NAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
||||||
m_launcher->start();
|
m_launcher->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,10 @@ public:
|
|||||||
m_online = online;
|
m_online = online;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setDemo(bool demo) {
|
||||||
|
m_demo = demo;
|
||||||
|
}
|
||||||
|
|
||||||
void setProfiler(BaseProfilerFactory *profiler) {
|
void setProfiler(BaseProfilerFactory *profiler) {
|
||||||
m_profiler = profiler;
|
m_profiler = profiler;
|
||||||
}
|
}
|
||||||
@ -101,6 +105,7 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
BaseProfilerFactory *m_profiler = nullptr;
|
BaseProfilerFactory *m_profiler = nullptr;
|
||||||
bool m_online = true;
|
bool m_online = true;
|
||||||
|
bool m_demo = false;
|
||||||
InstancePtr m_instance;
|
InstancePtr m_instance;
|
||||||
QWidget * m_parentWidget = nullptr;
|
QWidget * m_parentWidget = nullptr;
|
||||||
InstanceWindow *m_console = nullptr;
|
InstanceWindow *m_console = nullptr;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# Basic start script for running the launcher with the libs packaged with it.
|
# Basic start script for running the launcher with the libs packaged with it.
|
||||||
|
|
||||||
function printerror {
|
function printerror {
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022,2023 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 flowln <flowlnlnln@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
|
||||||
@ -34,20 +35,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "LoggedProcess.h"
|
#include "LoggedProcess.h"
|
||||||
#include "MessageLevel.h"
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QTextDecoder>
|
||||||
|
#include "MessageLevel.h"
|
||||||
|
|
||||||
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
|
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
|
||||||
{
|
{
|
||||||
// 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);
|
||||||
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
|
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
|
||||||
connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
|
connect(this, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &LoggedProcess::on_exit);
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
connect(this, &QProcess::errorOccurred, this, &LoggedProcess::on_error);
|
||||||
connect(this, SIGNAL(errorOccurred(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
|
|
||||||
#else
|
|
||||||
connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
|
|
||||||
#endif
|
|
||||||
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
|
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,25 +57,35 @@ LoggedProcess::~LoggedProcess()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList reprocess(const QByteArray & data, QString & leftover)
|
QStringList LoggedProcess::reprocess(const QByteArray& data, QTextDecoder& decoder)
|
||||||
{
|
{
|
||||||
QString str = leftover + QString::fromLocal8Bit(data);
|
auto str = decoder.toUnicode(data);
|
||||||
|
|
||||||
str.remove('\r');
|
if (!m_leftover_line.isEmpty()) {
|
||||||
QStringList lines = str.split("\n");
|
str.prepend(m_leftover_line);
|
||||||
leftover = lines.takeLast();
|
m_leftover_line = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts);
|
||||||
|
#else
|
||||||
|
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!str.endsWith(QChar::LineFeed))
|
||||||
|
m_leftover_line = lines.takeLast();
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoggedProcess::on_stdErr()
|
void LoggedProcess::on_stdErr()
|
||||||
{
|
{
|
||||||
auto lines = reprocess(readAllStandardError(), m_err_leftover);
|
auto lines = reprocess(readAllStandardError(), m_err_decoder);
|
||||||
emit log(lines, MessageLevel::StdErr);
|
emit log(lines, MessageLevel::StdErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoggedProcess::on_stdOut()
|
void LoggedProcess::on_stdOut()
|
||||||
{
|
{
|
||||||
auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
|
auto lines = reprocess(readAllStandardOutput(), m_out_decoder);
|
||||||
emit log(lines, MessageLevel::StdOut);
|
emit log(lines, MessageLevel::StdOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,18 +94,6 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
|
|||||||
// save the exit code
|
// save the exit code
|
||||||
m_exit_code = exit_code;
|
m_exit_code = exit_code;
|
||||||
|
|
||||||
// Flush console window
|
|
||||||
if (!m_err_leftover.isEmpty())
|
|
||||||
{
|
|
||||||
emit log({m_err_leftover}, MessageLevel::StdErr);
|
|
||||||
m_err_leftover.clear();
|
|
||||||
}
|
|
||||||
if (!m_out_leftover.isEmpty())
|
|
||||||
{
|
|
||||||
emit log({m_err_leftover}, MessageLevel::StdOut);
|
|
||||||
m_out_leftover.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// based on state, send signals
|
// based on state, send signals
|
||||||
if (!m_is_aborting)
|
if (!m_is_aborting)
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - Minecraft Launcher
|
* Prism Launcher - Minecraft Launcher
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
* Copyright (C) 2022,2023 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
*
|
*
|
||||||
* 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
|
||||||
@ -36,6 +36,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QTextDecoder>
|
||||||
#include "MessageLevel.h"
|
#include "MessageLevel.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -87,9 +88,12 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
void changeState(LoggedProcess::State state);
|
void changeState(LoggedProcess::State state);
|
||||||
|
|
||||||
|
QStringList reprocess(const QByteArray& data, QTextDecoder& decoder);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_err_leftover;
|
QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
||||||
QString m_out_leftover;
|
QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
||||||
|
QString m_leftover_line;
|
||||||
bool m_killed = false;
|
bool m_killed = false;
|
||||||
State m_state = NotRunning;
|
State m_state = NotRunning;
|
||||||
int m_exit_code = 0;
|
int m_exit_code = 0;
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
#include "MMCStrings.h"
|
|
||||||
|
|
||||||
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
|
||||||
static inline QChar getNextChar(const QString &s, int location)
|
|
||||||
{
|
|
||||||
return (location < s.length()) ? s.at(location) : QChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
|
||||||
int Strings::naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
|
|
||||||
{
|
|
||||||
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2)
|
|
||||||
{
|
|
||||||
// skip spaces, tabs and 0's
|
|
||||||
QChar c1 = getNextChar(s1, l1);
|
|
||||||
while (c1.isSpace())
|
|
||||||
c1 = getNextChar(s1, ++l1);
|
|
||||||
QChar c2 = getNextChar(s2, l2);
|
|
||||||
while (c2.isSpace())
|
|
||||||
c2 = getNextChar(s2, ++l2);
|
|
||||||
|
|
||||||
if (c1.isDigit() && c2.isDigit())
|
|
||||||
{
|
|
||||||
while (c1.digitValue() == 0)
|
|
||||||
c1 = getNextChar(s1, ++l1);
|
|
||||||
while (c2.digitValue() == 0)
|
|
||||||
c2 = getNextChar(s2, ++l2);
|
|
||||||
|
|
||||||
int lookAheadLocation1 = l1;
|
|
||||||
int lookAheadLocation2 = l2;
|
|
||||||
int currentReturnValue = 0;
|
|
||||||
// find the last digit, setting currentReturnValue as we go if it isn't equal
|
|
||||||
for (QChar lookAhead1 = c1, lookAhead2 = c2;
|
|
||||||
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
|
|
||||||
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
|
|
||||||
lookAhead2 = getNextChar(s2, ++lookAheadLocation2))
|
|
||||||
{
|
|
||||||
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
|
|
||||||
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
|
|
||||||
if (!is1ADigit && !is2ADigit)
|
|
||||||
break;
|
|
||||||
if (!is1ADigit)
|
|
||||||
return -1;
|
|
||||||
if (!is2ADigit)
|
|
||||||
return 1;
|
|
||||||
if (currentReturnValue == 0)
|
|
||||||
{
|
|
||||||
if (lookAhead1 < lookAhead2)
|
|
||||||
{
|
|
||||||
currentReturnValue = -1;
|
|
||||||
}
|
|
||||||
else if (lookAhead1 > lookAhead2)
|
|
||||||
{
|
|
||||||
currentReturnValue = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (currentReturnValue != 0)
|
|
||||||
return currentReturnValue;
|
|
||||||
}
|
|
||||||
if (cs == Qt::CaseInsensitive)
|
|
||||||
{
|
|
||||||
if (!c1.isLower())
|
|
||||||
c1 = c1.toLower();
|
|
||||||
if (!c2.isLower())
|
|
||||||
c2 = c2.toLower();
|
|
||||||
}
|
|
||||||
int r = QString::localeAwareCompare(c1, c2);
|
|
||||||
if (r < 0)
|
|
||||||
return -1;
|
|
||||||
if (r > 0)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// The two strings are the same (02 == 2) so fall back to the normal sort
|
|
||||||
return QString::compare(s1, s2, cs);
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
namespace Strings
|
|
||||||
{
|
|
||||||
int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
|
|
||||||
}
|
|
@ -18,6 +18,8 @@
|
|||||||
#include <MMCTime.h>
|
#include <MMCTime.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
QString Time::prettifyDuration(int64_t duration) {
|
QString Time::prettifyDuration(int64_t duration) {
|
||||||
int seconds = (int) (duration % 60);
|
int seconds = (int) (duration % 60);
|
||||||
@ -28,11 +30,73 @@ QString Time::prettifyDuration(int64_t duration) {
|
|||||||
int days = (int) (duration / 24);
|
int days = (int) (duration / 24);
|
||||||
if((hours == 0)&&(days == 0))
|
if((hours == 0)&&(days == 0))
|
||||||
{
|
{
|
||||||
return QObject::tr("%1m %2s").arg(minutes).arg(seconds);
|
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
|
||||||
}
|
}
|
||||||
if (days == 0)
|
if (days == 0)
|
||||||
{
|
{
|
||||||
return QObject::tr("%1h %2m").arg(hours).arg(minutes);
|
return QObject::tr("%1h %2min").arg(hours).arg(minutes);
|
||||||
}
|
}
|
||||||
return QObject::tr("%1d %2h %3m").arg(days).arg(hours).arg(minutes);
|
return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Time::humanReadableDuration(double duration, int precision) {
|
||||||
|
|
||||||
|
using days = std::chrono::duration<int, std::ratio<86400>>;
|
||||||
|
|
||||||
|
QString outStr;
|
||||||
|
QTextStream os(&outStr);
|
||||||
|
|
||||||
|
bool neg = false;
|
||||||
|
if (duration < 0) {
|
||||||
|
neg = true; // flag
|
||||||
|
duration *= -1; // invert
|
||||||
|
}
|
||||||
|
|
||||||
|
auto std_duration = std::chrono::duration<double>(duration);
|
||||||
|
auto d = std::chrono::duration_cast<days>(std_duration);
|
||||||
|
std_duration -= d;
|
||||||
|
auto h = std::chrono::duration_cast<std::chrono::hours>(std_duration);
|
||||||
|
std_duration -= h;
|
||||||
|
auto m = std::chrono::duration_cast<std::chrono::minutes>(std_duration);
|
||||||
|
std_duration -= m;
|
||||||
|
auto s = std::chrono::duration_cast<std::chrono::seconds>(std_duration);
|
||||||
|
std_duration -= s;
|
||||||
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std_duration);
|
||||||
|
|
||||||
|
auto dc = d.count();
|
||||||
|
auto hc = h.count();
|
||||||
|
auto mc = m.count();
|
||||||
|
auto sc = s.count();
|
||||||
|
auto msc = ms.count();
|
||||||
|
|
||||||
|
if (neg) {
|
||||||
|
os << '-';
|
||||||
|
}
|
||||||
|
if (dc) {
|
||||||
|
os << dc << QObject::tr("days");
|
||||||
|
}
|
||||||
|
if (hc) {
|
||||||
|
if (dc)
|
||||||
|
os << " ";
|
||||||
|
os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours
|
||||||
|
}
|
||||||
|
if (mc) {
|
||||||
|
if (dc || hc)
|
||||||
|
os << " ";
|
||||||
|
os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes
|
||||||
|
}
|
||||||
|
if (dc || hc || mc || sc) {
|
||||||
|
if (dc || hc || mc)
|
||||||
|
os << " ";
|
||||||
|
os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds
|
||||||
|
}
|
||||||
|
if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) {
|
||||||
|
if (dc || hc || mc || sc)
|
||||||
|
os << " ";
|
||||||
|
os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
os.flush();
|
||||||
|
|
||||||
|
return outStr;
|
||||||
|
}
|
@ -22,4 +22,13 @@ namespace Time {
|
|||||||
|
|
||||||
QString prettifyDuration(int64_t duration);
|
QString prettifyDuration(int64_t duration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.
|
||||||
|
* miliseconds are only included if `precision` is greater than 0.
|
||||||
|
*
|
||||||
|
* @param duration a number of seconds as floating point
|
||||||
|
* @param precision number of decmial points to display on fractons of a second, defualts to 0.
|
||||||
|
* @return QString
|
||||||
|
*/
|
||||||
|
QString humanReadableDuration(double duration, int precision = 0);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#include "MMCZip.h"
|
#include "MMCZip.h"
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
// ours
|
// ours
|
||||||
@ -93,20 +94,28 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files)
|
bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks)
|
||||||
{
|
{
|
||||||
QDir directory(dir);
|
QDir directory(dir);
|
||||||
if (!directory.exists()) return false;
|
if (!directory.exists()) return false;
|
||||||
|
|
||||||
for (auto e : files) {
|
for (auto e : files) {
|
||||||
auto filePath = directory.relativeFilePath(e.absoluteFilePath());
|
auto filePath = directory.relativeFilePath(e.absoluteFilePath());
|
||||||
if( !JlCompress::compressFile(zip, e.absoluteFilePath(), filePath)) return false;
|
auto srcPath = e.absoluteFilePath();
|
||||||
|
if (followSymlinks) {
|
||||||
|
if (e.isSymLink()) {
|
||||||
|
srcPath = e.symLinkTarget();
|
||||||
|
} else {
|
||||||
|
srcPath = e.canonicalFilePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( !JlCompress::compressFile(zip, srcPath, filePath)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files)
|
bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
|
||||||
{
|
{
|
||||||
QuaZip zip(fileCompressed);
|
QuaZip zip(fileCompressed);
|
||||||
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
||||||
@ -115,7 +124,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = compressDirFiles(&zip, dir, files);
|
auto result = compressDirFiles(&zip, dir, files, followSymlinks);
|
||||||
|
|
||||||
zip.close();
|
zip.close();
|
||||||
if(zip.getZipError()!=0) {
|
if(zip.getZipError()!=0) {
|
||||||
@ -141,13 +150,14 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
QSet<QString> addedFiles;
|
QSet<QString> addedFiles;
|
||||||
|
|
||||||
// Modify the jar
|
// Modify the jar
|
||||||
for (auto i = mods.constEnd(); i != mods.constBegin(); --i)
|
// This needs to be done in reverse-order to ensure we respect the loading order of components
|
||||||
|
for (auto i = mods.crbegin(); i != mods.crend(); i++)
|
||||||
{
|
{
|
||||||
const Mod* mod = *i;
|
const auto* mod = *i;
|
||||||
// do not merge disabled mods.
|
// do not merge disabled mods.
|
||||||
if (!mod->enabled())
|
if (!mod->enabled())
|
||||||
continue;
|
continue;
|
||||||
if (mod->type() == Mod::MOD_ZIPFILE)
|
if (mod->type() == ResourceType::ZIPFILE)
|
||||||
{
|
{
|
||||||
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles))
|
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles))
|
||||||
{
|
{
|
||||||
@ -157,7 +167,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mod->type() == Mod::MOD_SINGLEFILE)
|
else if (mod->type() == ResourceType::SINGLEFILE)
|
||||||
{
|
{
|
||||||
// FIXME: buggy - does not work with addedFiles
|
// FIXME: buggy - does not work with addedFiles
|
||||||
auto filename = mod->fileinfo();
|
auto filename = mod->fileinfo();
|
||||||
@ -170,7 +180,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
}
|
}
|
||||||
addedFiles.insert(filename.fileName());
|
addedFiles.insert(filename.fileName());
|
||||||
}
|
}
|
||||||
else if (mod->type() == Mod::MOD_FOLDER)
|
else if (mod->type() == ResourceType::FOLDER)
|
||||||
{
|
{
|
||||||
// untested, but seems to be unused / not possible to reach
|
// untested, but seems to be unused / not possible to reach
|
||||||
// FIXME: buggy - does not work with addedFiles
|
// FIXME: buggy - does not work with addedFiles
|
||||||
@ -227,23 +237,27 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ours
|
// ours
|
||||||
QString MMCZip::findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root)
|
QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root)
|
||||||
{
|
{
|
||||||
QuaZipDir rootDir(zip, root);
|
QuaZipDir rootDir(zip, root);
|
||||||
for(auto fileName: rootDir.entryList(QDir::Files))
|
for (auto&& fileName : rootDir.entryList(QDir::Files)) {
|
||||||
{
|
if (fileName == what)
|
||||||
if(fileName == what)
|
|
||||||
return root;
|
return root;
|
||||||
|
|
||||||
|
QCoreApplication::processEvents();
|
||||||
}
|
}
|
||||||
for(auto fileName: rootDir.entryList(QDir::Dirs))
|
|
||||||
{
|
// Recurse the search to non-ignored subfolders
|
||||||
QString result = findFolderOfFileInZip(zip, what, root + fileName);
|
for (auto&& fileName : rootDir.entryList(QDir::Dirs)) {
|
||||||
if(!result.isEmpty())
|
if (ignore_paths.contains(fileName))
|
||||||
{
|
continue;
|
||||||
|
|
||||||
|
QString result = findFolderOfFileInZip(zip, what, ignore_paths, root + fileName);
|
||||||
|
if (!result.isEmpty())
|
||||||
return result;
|
return result;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return QString();
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ours
|
// ours
|
||||||
@ -267,16 +281,17 @@ bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & re
|
|||||||
|
|
||||||
|
|
||||||
// ours
|
// ours
|
||||||
nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
|
std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
|
||||||
{
|
{
|
||||||
QDir directory(target);
|
auto target_top_dir = QUrl::fromLocalFile(target);
|
||||||
|
|
||||||
QStringList extracted;
|
QStringList extracted;
|
||||||
|
|
||||||
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
|
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
|
||||||
auto numEntries = zip->getEntriesCount();
|
auto numEntries = zip->getEntriesCount();
|
||||||
if(numEntries < 0) {
|
if(numEntries < 0) {
|
||||||
qWarning() << "Failed to enumerate files in archive";
|
qWarning() << "Failed to enumerate files in archive";
|
||||||
return nonstd::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
else if(numEntries == 0) {
|
else if(numEntries == 0) {
|
||||||
qDebug() << "Extracting empty archives seems odd...";
|
qDebug() << "Extracting empty archives seems odd...";
|
||||||
@ -285,51 +300,56 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
|
|||||||
else if (!zip->goToFirstFile())
|
else if (!zip->goToFirstFile())
|
||||||
{
|
{
|
||||||
qWarning() << "Failed to seek to first file in zip";
|
qWarning() << "Failed to seek to first file in zip";
|
||||||
return nonstd::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
do
|
do {
|
||||||
{
|
QString file_name = zip->getCurrentFileName();
|
||||||
QString name = zip->getCurrentFileName();
|
if (!file_name.startsWith(subdir))
|
||||||
if(!name.startsWith(subdir))
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
name.remove(0, subdir.size());
|
auto relative_file_name = QDir::fromNativeSeparators(file_name.remove(0, subdir.size()));
|
||||||
auto original_name = name;
|
auto original_name = 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
|
// Fix weird "folders with a single file get squashed" thing
|
||||||
QString path;
|
QString sub_path;
|
||||||
if(name.contains('/') && !name.endsWith('/')){
|
if (relative_file_name.contains('/') && !relative_file_name.endsWith('/')) {
|
||||||
path = name.section('/', 0, -2) + "/";
|
sub_path = relative_file_name.section('/', 0, -2) + '/';
|
||||||
FS::ensureFolderPathExists(FS::PathCombine(target, path));
|
FS::ensureFolderPathExists(FS::PathCombine(target, sub_path));
|
||||||
|
|
||||||
name = name.split('/').last();
|
relative_file_name = relative_file_name.split('/').last();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString absFilePath;
|
QString target_file_path;
|
||||||
if(name.isEmpty())
|
if (relative_file_name.isEmpty()) {
|
||||||
{
|
target_file_path = target + '/';
|
||||||
absFilePath = directory.absoluteFilePath(name) + "/";
|
} else {
|
||||||
}
|
target_file_path = FS::PathCombine(target_top_dir.toLocalFile(), sub_path, relative_file_name);
|
||||||
else
|
if (relative_file_name.endsWith('/') && !target_file_path.endsWith('/'))
|
||||||
{
|
target_file_path += '/';
|
||||||
absFilePath = directory.absoluteFilePath(path + name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!JlCompress::extractFile(zip, "", absFilePath))
|
if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) {
|
||||||
{
|
qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" << target;
|
||||||
qWarning() << "Failed to extract file" << original_name << "to" << absFilePath;
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!JlCompress::extractFile(zip, "", target_file_path)) {
|
||||||
|
qWarning() << "Failed to extract file" << original_name << "to" << target_file_path;
|
||||||
JlCompress::removeFile(extracted);
|
JlCompress::removeFile(extracted);
|
||||||
return nonstd::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
extracted.append(absFilePath);
|
extracted.append(target_file_path);
|
||||||
QFile::setPermissions(absFilePath, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
|
QFile::setPermissions(target_file_path, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
|
||||||
|
|
||||||
qDebug() << "Extracted file" << name << "to" << absFilePath;
|
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
||||||
} while (zip->goToNextFile());
|
} while (zip->goToNextFile());
|
||||||
|
|
||||||
return extracted;
|
return extracted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +360,7 @@ bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &tar
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ours
|
// ours
|
||||||
nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
|
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
|
||||||
{
|
{
|
||||||
QuaZip zip(fileCompressed);
|
QuaZip zip(fileCompressed);
|
||||||
if (!zip.open(QuaZip::mdUnzip))
|
if (!zip.open(QuaZip::mdUnzip))
|
||||||
@ -351,13 +371,13 @@ nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString
|
|||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
|
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
|
||||||
return nonstd::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return MMCZip::extractSubDir(&zip, "", dir);
|
return MMCZip::extractSubDir(&zip, "", dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ours
|
// ours
|
||||||
nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir)
|
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir)
|
||||||
{
|
{
|
||||||
QuaZip zip(fileCompressed);
|
QuaZip zip(fileCompressed);
|
||||||
if (!zip.open(QuaZip::mdUnzip))
|
if (!zip.open(QuaZip::mdUnzip))
|
||||||
@ -368,7 +388,7 @@ nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString
|
|||||||
return QStringList();
|
return QStringList();
|
||||||
}
|
}
|
||||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
|
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
|
||||||
return nonstd::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return MMCZip::extractSubDir(&zip, subdir, dir);
|
return MMCZip::extractSubDir(&zip, subdir, dir);
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <quazip/JlCompress.h>
|
#include <quazip/JlCompress.h>
|
||||||
#include <nonstd/optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace MMCZip
|
namespace MMCZip
|
||||||
{
|
{
|
||||||
@ -59,18 +59,20 @@ namespace MMCZip
|
|||||||
* \param zip target archive
|
* \param zip target archive
|
||||||
* \param dir directory that will be compressed (to compress with relative paths)
|
* \param dir directory that will be compressed (to compress with relative paths)
|
||||||
* \param files list of files to compress
|
* \param files list of files to compress
|
||||||
|
* \param followSymlinks should follow symlinks when compressing file data
|
||||||
* \return true for success or false for failure
|
* \return true for success or false for failure
|
||||||
*/
|
*/
|
||||||
bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files);
|
bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compress directory, by providing a list of files to compress
|
* Compress directory, by providing a list of files to compress
|
||||||
* \param fileCompressed target archive file
|
* \param fileCompressed target archive file
|
||||||
* \param dir directory that will be compressed (to compress with relative paths)
|
* \param dir directory that will be compressed (to compress with relative paths)
|
||||||
* \param files list of files to compress
|
* \param files list of files to compress
|
||||||
|
* \param followSymlinks should follow symlinks when compressing file data
|
||||||
* \return true for success or false for failure
|
* \return true for success or false for failure
|
||||||
*/
|
*/
|
||||||
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files);
|
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* take a source jar, add mods to it, resulting in target jar
|
* take a source jar, add mods to it, resulting in target jar
|
||||||
@ -80,9 +82,11 @@ namespace MMCZip
|
|||||||
/**
|
/**
|
||||||
* Find a single file in archive by file name (not path)
|
* Find a single file in archive by file name (not path)
|
||||||
*
|
*
|
||||||
|
* \param ignore_paths paths to skip when recursing the search
|
||||||
|
*
|
||||||
* \return the path prefix where the file is
|
* \return the path prefix where the file is
|
||||||
*/
|
*/
|
||||||
QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QString &root = QString(""));
|
QString findFolderOfFileInZip(QuaZip * zip, const QString & what, const QStringList& ignore_paths = {}, const QString &root = QString(""));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a multiple files of the same name in archive by file name
|
* Find a multiple files of the same name in archive by file name
|
||||||
@ -95,7 +99,7 @@ namespace MMCZip
|
|||||||
/**
|
/**
|
||||||
* Extract a subdirectory from an archive
|
* Extract a subdirectory from an archive
|
||||||
*/
|
*/
|
||||||
nonstd::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
|
std::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
|
||||||
|
|
||||||
bool extractRelFile(QuaZip *zip, const QString & file, const QString &target);
|
bool extractRelFile(QuaZip *zip, const QString & file, const QString &target);
|
||||||
|
|
||||||
@ -106,7 +110,7 @@ namespace MMCZip
|
|||||||
* \param dir The directory to extract to, the current directory if left empty.
|
* \param dir The directory to extract to, the current directory if left empty.
|
||||||
* \return The list of the full paths of the files extracted, empty on failure.
|
* \return The list of the full paths of the files extracted, empty on failure.
|
||||||
*/
|
*/
|
||||||
nonstd::optional<QStringList> extractDir(QString fileCompressed, QString dir);
|
std::optional<QStringList> extractDir(QString fileCompressed, QString dir);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a subdirectory from an archive
|
* Extract a subdirectory from an archive
|
||||||
@ -116,7 +120,7 @@ namespace MMCZip
|
|||||||
* \param dir The directory to extract to, the current directory if left empty.
|
* \param dir The directory to extract to, the current directory if left empty.
|
||||||
* \return The list of the full paths of the files extracted, empty on failure.
|
* \return The list of the full paths of the files extracted, empty on failure.
|
||||||
*/
|
*/
|
||||||
nonstd::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir);
|
std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract a single file from an archive into a directory
|
* Extract a single file from an archive into a directory
|
||||||
|
95
launcher/MTPixmapCache.h
Normal file
95
launcher/MTPixmapCache.h
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QPixmapCache>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#define GET_TYPE() \
|
||||||
|
Qt::ConnectionType type; \
|
||||||
|
if (QThread::currentThread() != QCoreApplication::instance()->thread()) \
|
||||||
|
type = Qt::BlockingQueuedConnection; \
|
||||||
|
else \
|
||||||
|
type = Qt::DirectConnection;
|
||||||
|
|
||||||
|
#define DEFINE_FUNC_NO_PARAM(NAME, RET_TYPE) \
|
||||||
|
static RET_TYPE NAME() \
|
||||||
|
{ \
|
||||||
|
RET_TYPE ret; \
|
||||||
|
GET_TYPE() \
|
||||||
|
QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret)); \
|
||||||
|
return ret; \
|
||||||
|
}
|
||||||
|
#define DEFINE_FUNC_ONE_PARAM(NAME, RET_TYPE, PARAM_1_TYPE) \
|
||||||
|
static RET_TYPE NAME(PARAM_1_TYPE p1) \
|
||||||
|
{ \
|
||||||
|
RET_TYPE ret; \
|
||||||
|
GET_TYPE() \
|
||||||
|
QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret), Q_ARG(PARAM_1_TYPE, p1)); \
|
||||||
|
return ret; \
|
||||||
|
}
|
||||||
|
#define DEFINE_FUNC_TWO_PARAM(NAME, RET_TYPE, PARAM_1_TYPE, PARAM_2_TYPE) \
|
||||||
|
static RET_TYPE NAME(PARAM_1_TYPE p1, PARAM_2_TYPE p2) \
|
||||||
|
{ \
|
||||||
|
RET_TYPE ret; \
|
||||||
|
GET_TYPE() \
|
||||||
|
QMetaObject::invokeMethod(s_instance, "_" #NAME, type, Q_RETURN_ARG(RET_TYPE, ret), Q_ARG(PARAM_1_TYPE, p1), \
|
||||||
|
Q_ARG(PARAM_2_TYPE, p2)); \
|
||||||
|
return ret; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A wrapper around QPixmapCache with thread affinity with the main thread.
|
||||||
|
*/
|
||||||
|
class PixmapCache final : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PixmapCache(QObject* parent) : QObject(parent) {}
|
||||||
|
~PixmapCache() override = default;
|
||||||
|
|
||||||
|
static PixmapCache& instance() { return *s_instance; }
|
||||||
|
static void setInstance(PixmapCache* i) { s_instance = i; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
DEFINE_FUNC_NO_PARAM(cacheLimit, int)
|
||||||
|
DEFINE_FUNC_NO_PARAM(clear, bool)
|
||||||
|
DEFINE_FUNC_TWO_PARAM(find, bool, const QString&, QPixmap*)
|
||||||
|
DEFINE_FUNC_TWO_PARAM(find, bool, const QPixmapCache::Key&, QPixmap*)
|
||||||
|
DEFINE_FUNC_TWO_PARAM(insert, bool, const QString&, const QPixmap&)
|
||||||
|
DEFINE_FUNC_ONE_PARAM(insert, QPixmapCache::Key, const QPixmap&)
|
||||||
|
DEFINE_FUNC_ONE_PARAM(remove, bool, const QString&)
|
||||||
|
DEFINE_FUNC_ONE_PARAM(remove, bool, const QPixmapCache::Key&)
|
||||||
|
DEFINE_FUNC_TWO_PARAM(replace, bool, const QPixmapCache::Key&, const QPixmap&)
|
||||||
|
DEFINE_FUNC_ONE_PARAM(setCacheLimit, bool, int)
|
||||||
|
|
||||||
|
// NOTE: Every function returns something non-void to simplify the macros.
|
||||||
|
private slots:
|
||||||
|
int _cacheLimit() { return QPixmapCache::cacheLimit(); }
|
||||||
|
bool _clear()
|
||||||
|
{
|
||||||
|
QPixmapCache::clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool _find(const QString& key, QPixmap* pixmap) { return QPixmapCache::find(key, pixmap); }
|
||||||
|
bool _find(const QPixmapCache::Key& key, QPixmap* pixmap) { return QPixmapCache::find(key, pixmap); }
|
||||||
|
bool _insert(const QString& key, const QPixmap& pixmap) { return QPixmapCache::insert(key, pixmap); }
|
||||||
|
QPixmapCache::Key _insert(const QPixmap& pixmap) { return QPixmapCache::insert(pixmap); }
|
||||||
|
bool _remove(const QString& key)
|
||||||
|
{
|
||||||
|
QPixmapCache::remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool _remove(const QPixmapCache::Key& key)
|
||||||
|
{
|
||||||
|
QPixmapCache::remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool _replace(const QPixmapCache::Key& key, const QPixmap& pixmap) { return QPixmapCache::replace(key, pixmap); }
|
||||||
|
bool _setCacheLimit(int n)
|
||||||
|
{
|
||||||
|
QPixmapCache::setCacheLimit(n);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static PixmapCache* s_instance;
|
||||||
|
};
|
109
launcher/MangoHud.cpp
Normal file
109
launcher/MangoHud.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PrismLauncher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Jan Drögehoff <sentrycraft123@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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QString>
|
||||||
|
#include <QSysInfo>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include "MangoHud.h"
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "Json.h"
|
||||||
|
|
||||||
|
namespace MangoHud {
|
||||||
|
|
||||||
|
QString getLibraryString()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Check for vulkan layers in this order:
|
||||||
|
*
|
||||||
|
* $VK_LAYER_PATH
|
||||||
|
* $XDG_DATA_DIRS (/usr/local/share/:/usr/share/)
|
||||||
|
* $XDG_DATA_HOME (~/.local/share)
|
||||||
|
* /etc
|
||||||
|
* $XDG_CONFIG_DIRS (/etc/xdg)
|
||||||
|
* $XDG_CONFIG_HOME (~/.config)
|
||||||
|
*/
|
||||||
|
|
||||||
|
QStringList vkLayerList;
|
||||||
|
{
|
||||||
|
QString home = QDir::homePath();
|
||||||
|
|
||||||
|
QString vkLayerPath = qEnvironmentVariable("VK_LAYER_PATH");
|
||||||
|
if (!vkLayerPath.isEmpty()) {
|
||||||
|
vkLayerList << vkLayerPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList xdgDataDirs = qEnvironmentVariable("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/").split(QLatin1String(":"));
|
||||||
|
for (QString dir : xdgDataDirs) {
|
||||||
|
vkLayerList << FS::PathCombine(dir, "vulkan", "implicit_layer.d");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString xdgDataHome = qEnvironmentVariable("XDG_DATA_HOME");
|
||||||
|
if (xdgDataHome.isEmpty()) {
|
||||||
|
xdgDataHome = FS::PathCombine(home, ".local", "share");
|
||||||
|
}
|
||||||
|
vkLayerList << FS::PathCombine(xdgDataHome, "vulkan", "implicit_layer.d");
|
||||||
|
|
||||||
|
vkLayerList << "/etc";
|
||||||
|
|
||||||
|
QStringList xdgConfigDirs = qEnvironmentVariable("XDG_CONFIG_DIRS", "/etc/xdg").split(QLatin1String(":"));
|
||||||
|
for (QString dir : xdgConfigDirs) {
|
||||||
|
vkLayerList << FS::PathCombine(dir, "vulkan", "implicit_layer.d");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString xdgConfigHome = qEnvironmentVariable("XDG_CONFIG_HOME");
|
||||||
|
if (xdgConfigHome.isEmpty()) {
|
||||||
|
xdgConfigHome = FS::PathCombine(home, ".config");
|
||||||
|
}
|
||||||
|
vkLayerList << FS::PathCombine(xdgConfigHome, "vulkan", "implicit_layer.d");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QString vkLayer : vkLayerList) {
|
||||||
|
// prefer to use architecture specific vulkan layers
|
||||||
|
QString currentArch = QSysInfo::currentCpuArchitecture();
|
||||||
|
|
||||||
|
if (currentArch == "arm64") {
|
||||||
|
currentArch = "aarch64";
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList manifestNames = { QString("MangoHud.%1.json").arg(currentArch), "MangoHud.json" };
|
||||||
|
|
||||||
|
QString filePath = "";
|
||||||
|
for (QString manifestName : manifestNames) {
|
||||||
|
QString tryPath = FS::PathCombine(vkLayer, manifestName);
|
||||||
|
if (QFile::exists(tryPath)) {
|
||||||
|
filePath = tryPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filePath.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto conf = Json::requireDocument(filePath, vkLayer);
|
||||||
|
auto confObject = Json::requireObject(conf, vkLayer);
|
||||||
|
auto layer = Json::ensureObject(confObject, "layer");
|
||||||
|
return Json::ensureString(layer, "library_path");
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
} // namespace MangoHud
|
27
launcher/MangoHud.h
Normal file
27
launcher/MangoHud.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PrismLauncher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Jan Drögehoff <sentrycraft123@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 <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace MangoHud {
|
||||||
|
|
||||||
|
QString getLibraryString();
|
||||||
|
}
|
34
launcher/Markdown.h
Normal file
34
launcher/Markdown.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 Joshua Goins <josh@redstrate.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 <QString>
|
||||||
|
#include <cmark.h>
|
||||||
|
|
||||||
|
static QString markdownToHTML(const QString& markdown)
|
||||||
|
{
|
||||||
|
const QByteArray markdownData = markdown.toUtf8();
|
||||||
|
char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE);
|
||||||
|
|
||||||
|
QString htmlStr(buffer);
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return htmlStr;
|
||||||
|
}
|
@ -1,72 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
/*
|
|
||||||
* PolyMC - Minecraft Launcher
|
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ModDownloadTask.h"
|
|
||||||
|
|
||||||
#include "Application.h"
|
|
||||||
#include "minecraft/mod/ModFolderModel.h"
|
|
||||||
|
|
||||||
ModDownloadTask::ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr<ModFolderModel> mods, bool is_indexed)
|
|
||||||
: m_mod(mod), m_mod_version(version), mods(mods)
|
|
||||||
{
|
|
||||||
if (is_indexed) {
|
|
||||||
m_update_task.reset(new LocalModUpdateTask(mods->indexDir(), m_mod, m_mod_version));
|
|
||||||
connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ModDownloadTask::hasOldMod);
|
|
||||||
|
|
||||||
addTask(m_update_task);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
|
|
||||||
m_filesNetJob->setStatus(tr("Downloading mod:\n%1").arg(m_mod_version.downloadUrl));
|
|
||||||
|
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeFile(m_mod_version.downloadUrl, mods->dir().absoluteFilePath(getFilename())));
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed);
|
|
||||||
|
|
||||||
addTask(m_filesNetJob);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModDownloadTask::downloadSucceeded()
|
|
||||||
{
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
auto name = std::get<0>(to_delete);
|
|
||||||
auto filename = std::get<1>(to_delete);
|
|
||||||
if (!name.isEmpty() && filename != m_mod_version.fileName) {
|
|
||||||
mods->uninstallMod(filename, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModDownloadTask::downloadFailed(QString reason)
|
|
||||||
{
|
|
||||||
emitFailed(reason);
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
|
|
||||||
{
|
|
||||||
emit progress(current, total);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This indirection is done so that we don't delete a mod before being sure it was
|
|
||||||
// downloaded successfully!
|
|
||||||
void ModDownloadTask::hasOldMod(QString name, QString filename)
|
|
||||||
{
|
|
||||||
to_delete = {name, filename};
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
/*
|
|
||||||
* PolyMC - Minecraft Launcher
|
|
||||||
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
|
||||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include "tasks/SequentialTask.h"
|
|
||||||
|
|
||||||
#include "modplatform/ModIndex.h"
|
|
||||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
|
||||||
|
|
||||||
class ModFolderModel;
|
|
||||||
|
|
||||||
class ModDownloadTask : public SequentialTask {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr<ModFolderModel> mods, bool is_indexed = true);
|
|
||||||
const QString& getFilename() const { return m_mod_version.fileName; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ModPlatform::IndexedPack m_mod;
|
|
||||||
ModPlatform::IndexedVersion m_mod_version;
|
|
||||||
const std::shared_ptr<ModFolderModel> mods;
|
|
||||||
|
|
||||||
NetJob::Ptr m_filesNetJob;
|
|
||||||
LocalModUpdateTask::Ptr m_update_task;
|
|
||||||
|
|
||||||
void downloadProgressChanged(qint64 current, qint64 total);
|
|
||||||
|
|
||||||
void downloadFailed(QString reason);
|
|
||||||
|
|
||||||
void downloadSucceeded();
|
|
||||||
|
|
||||||
std::tuple<QString, QString> to_delete {"", ""};
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void hasOldMod(QString name, QString filename);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,3 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "launch/LaunchTask.h"
|
#include "launch/LaunchTask.h"
|
||||||
@ -15,6 +50,10 @@ public:
|
|||||||
void saveNow() override
|
void saveNow() override
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
void loadSpecificSettings() override
|
||||||
|
{
|
||||||
|
setSpecificSettingsLoaded(true);
|
||||||
|
}
|
||||||
QString getStatusbarDescription() override
|
QString getStatusbarDescription() override
|
||||||
{
|
{
|
||||||
return tr("Unknown instance type");
|
return tr("Unknown instance type");
|
||||||
@ -43,7 +82,7 @@ public:
|
|||||||
{
|
{
|
||||||
return QProcessEnvironment();
|
return QProcessEnvironment();
|
||||||
}
|
}
|
||||||
QMap<QString, QString> getVariables() const override
|
QMap<QString, QString> getVariables() override
|
||||||
{
|
{
|
||||||
return QMap<QString, QString>();
|
return QMap<QString, QString>();
|
||||||
}
|
}
|
||||||
@ -80,4 +119,8 @@ public:
|
|||||||
QString modsRoot() const override {
|
QString modsRoot() const override {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
void updateRuntimeContext()
|
||||||
|
{
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,89 +1,53 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
namespace details
|
|
||||||
{
|
|
||||||
struct DeleteQObjectLater
|
|
||||||
{
|
|
||||||
void operator()(QObject *obj) const
|
|
||||||
{
|
|
||||||
obj->deleteLater();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* A unique pointer class with unique pointer semantics intended for derivates of QObject
|
* A unique pointer class with unique pointer semantics intended for derivates of QObject
|
||||||
* Calls deleteLater() instead of destroying the contained object immediately
|
* Calls deleteLater() instead of destroying the contained object immediately
|
||||||
*/
|
*/
|
||||||
template<typename T> using unique_qobject_ptr = std::unique_ptr<T, details::DeleteQObjectLater>;
|
template <typename T>
|
||||||
|
using unique_qobject_ptr = QScopedPointer<T, QScopedPointerDeleteLater>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A shared pointer class with shared pointer semantics intended for derivates of QObject
|
* A shared pointer class with shared pointer semantics intended for derivates of QObject
|
||||||
* Calls deleteLater() instead of destroying the contained object immediately
|
* Calls deleteLater() instead of destroying the contained object immediately
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class shared_qobject_ptr
|
class shared_qobject_ptr : public QSharedPointer<T> {
|
||||||
{
|
public:
|
||||||
public:
|
constexpr explicit shared_qobject_ptr() : QSharedPointer<T>() {}
|
||||||
shared_qobject_ptr(){}
|
constexpr explicit shared_qobject_ptr(T* ptr) : QSharedPointer<T>(ptr, &QObject::deleteLater) {}
|
||||||
shared_qobject_ptr(T * wrap)
|
constexpr shared_qobject_ptr(std::nullptr_t null_ptr) : QSharedPointer<T>(null_ptr, &QObject::deleteLater) {}
|
||||||
{
|
|
||||||
reset(wrap);
|
|
||||||
}
|
|
||||||
shared_qobject_ptr(const shared_qobject_ptr<T>& other)
|
|
||||||
{
|
|
||||||
m_ptr = other.m_ptr;
|
|
||||||
}
|
|
||||||
template<typename Derived>
|
|
||||||
shared_qobject_ptr(const shared_qobject_ptr<Derived> &other)
|
|
||||||
{
|
|
||||||
m_ptr = other.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
template <typename Derived>
|
||||||
void reset(T * wrap)
|
constexpr shared_qobject_ptr(const shared_qobject_ptr<Derived>& other) : QSharedPointer<T>(other)
|
||||||
{
|
{}
|
||||||
using namespace std::placeholders;
|
|
||||||
m_ptr.reset(wrap, std::bind(&QObject::deleteLater, _1));
|
|
||||||
}
|
|
||||||
void reset(const shared_qobject_ptr<T> &other)
|
|
||||||
{
|
|
||||||
m_ptr = other.m_ptr;
|
|
||||||
}
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
m_ptr.reset();
|
|
||||||
}
|
|
||||||
T * get() const
|
|
||||||
{
|
|
||||||
return m_ptr.get();
|
|
||||||
}
|
|
||||||
T * operator->() const
|
|
||||||
{
|
|
||||||
return m_ptr.get();
|
|
||||||
}
|
|
||||||
T & operator*() const
|
|
||||||
{
|
|
||||||
return *m_ptr.get();
|
|
||||||
}
|
|
||||||
operator bool() const
|
|
||||||
{
|
|
||||||
return m_ptr.get() != nullptr;
|
|
||||||
}
|
|
||||||
const std::shared_ptr <T> unwrap() const
|
|
||||||
{
|
|
||||||
return m_ptr;
|
|
||||||
}
|
|
||||||
bool operator==(const shared_qobject_ptr<T>& other) {
|
|
||||||
return m_ptr == other.m_ptr;
|
|
||||||
}
|
|
||||||
bool operator!=(const shared_qobject_ptr<T>& other) {
|
|
||||||
return m_ptr != other.m_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
template <typename Derived>
|
||||||
std::shared_ptr <T> m_ptr;
|
constexpr shared_qobject_ptr(const QSharedPointer<Derived>& other) : QSharedPointer<T>(other)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void reset() { QSharedPointer<T>::reset(); }
|
||||||
|
void reset(T*&& other)
|
||||||
|
{
|
||||||
|
shared_qobject_ptr<T> t(other);
|
||||||
|
this->swap(t);
|
||||||
|
}
|
||||||
|
void reset(const shared_qobject_ptr<T>& other)
|
||||||
|
{
|
||||||
|
shared_qobject_ptr<T> t(other);
|
||||||
|
this->swap(t);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
shared_qobject_ptr<T> makeShared(Args... args)
|
||||||
|
{
|
||||||
|
auto obj = new T(args...);
|
||||||
|
return shared_qobject_ptr<T>(obj);
|
||||||
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
/*
|
/*
|
||||||
* PolyMC - 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 flowln <flowlnlnln@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
|
||||||
@ -35,27 +36,35 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "modplatform/ModAPI.h"
|
|
||||||
#include "ui/pages/modplatform/ModPage.h"
|
|
||||||
|
|
||||||
#include "modplatform/flame/FlameAPI.h"
|
#include <QVariant>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
class FlameModPage : public ModPage {
|
namespace QVariantUtils {
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
template <typename T>
|
||||||
explicit FlameModPage(ModDownloadDialog* dialog, BaseInstance* instance);
|
inline QList<T> toList(QVariant src) {
|
||||||
~FlameModPage() override = default;
|
QVariantList variantList = src.toList();
|
||||||
|
|
||||||
inline auto displayName() const -> QString override { return "CurseForge"; }
|
QList<T> list_t;
|
||||||
inline auto icon() const -> QIcon override { return APPLICATION->getThemedIcon("flame"); }
|
list_t.reserve(variantList.size());
|
||||||
inline auto id() const -> QString override { return "curseforge"; }
|
for (const QVariant& v : variantList)
|
||||||
inline auto helpPage() const -> QString override { return "Mod-platform"; }
|
{
|
||||||
|
list_t.append(v.value<T>());
|
||||||
|
}
|
||||||
|
return list_t;
|
||||||
|
}
|
||||||
|
|
||||||
inline auto debugName() const -> QString override { return "Flame"; }
|
template <typename T>
|
||||||
inline auto metaEntryBase() const -> QString override { return "FlameMods"; };
|
inline QVariant fromList(QList<T> val) {
|
||||||
|
QVariantList variantList;
|
||||||
|
variantList.reserve(val.size());
|
||||||
|
for (const T& v : val)
|
||||||
|
{
|
||||||
|
variantList.append(v);
|
||||||
|
}
|
||||||
|
|
||||||
auto validateVersion(ModPlatform::IndexedVersion& ver, QString mineVer, ModAPI::ModLoaderTypes loaders = ModAPI::Unspecified) const -> bool override;
|
return variantList;
|
||||||
|
}
|
||||||
|
|
||||||
auto shouldDisplay() const -> bool override;
|
}
|
||||||
};
|
|
92
launcher/ResourceDownloadTask.cpp
Normal file
92
launcher/ResourceDownloadTask.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2022-2023 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ResourceDownloadTask.h"
|
||||||
|
|
||||||
|
#include "Application.h"
|
||||||
|
|
||||||
|
#include "minecraft/mod/ModFolderModel.h"
|
||||||
|
#include "minecraft/mod/ResourceFolderModel.h"
|
||||||
|
|
||||||
|
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||||
|
ModPlatform::IndexedVersion version,
|
||||||
|
const std::shared_ptr<ResourceFolderModel> packs,
|
||||||
|
bool is_indexed,
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model && is_indexed) {
|
||||||
|
m_update_task.reset(new LocalModUpdateTask(model->indexDir(), *m_pack, m_pack_version));
|
||||||
|
connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource);
|
||||||
|
|
||||||
|
addTask(m_update_task);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_filesNetJob.reset(new NetJob(tr("Resource download"), APPLICATION->network()));
|
||||||
|
m_filesNetJob->setStatus(tr("Downloading resource:\n%1").arg(m_pack_version.downloadUrl));
|
||||||
|
|
||||||
|
QDir dir{ m_pack_model->dir() };
|
||||||
|
{
|
||||||
|
// FIXME: Make this more generic. May require adding additional info to IndexedVersion,
|
||||||
|
// or adquiring a reference to the base instance.
|
||||||
|
if (!m_custom_target_folder.isEmpty()) {
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cd(m_custom_target_folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed);
|
||||||
|
|
||||||
|
addTask(m_filesNetJob);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceDownloadTask::downloadSucceeded()
|
||||||
|
{
|
||||||
|
m_filesNetJob.reset();
|
||||||
|
auto name = std::get<0>(to_delete);
|
||||||
|
auto filename = std::get<1>(to_delete);
|
||||||
|
if (!name.isEmpty() && filename != m_pack_version.fileName) {
|
||||||
|
if (auto model = dynamic_cast<ModFolderModel*>(m_pack_model.get()); model)
|
||||||
|
model->uninstallMod(filename, true);
|
||||||
|
else
|
||||||
|
m_pack_model->uninstallResource(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceDownloadTask::downloadFailed(QString reason)
|
||||||
|
{
|
||||||
|
emitFailed(reason);
|
||||||
|
m_filesNetJob.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||||
|
{
|
||||||
|
emit progress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This indirection is done so that we don't delete a mod before being sure it was
|
||||||
|
// downloaded successfully!
|
||||||
|
void ResourceDownloadTask::hasOldResource(QString name, QString filename)
|
||||||
|
{
|
||||||
|
to_delete = { name, filename };
|
||||||
|
}
|
61
launcher/ResourceDownloadTask.h
Normal file
61
launcher/ResourceDownloadTask.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2022-2023 flowln <flowlnlnln@gmail.com>
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
#include "tasks/SequentialTask.h"
|
||||||
|
|
||||||
|
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||||
|
#include "modplatform/ModIndex.h"
|
||||||
|
|
||||||
|
class ResourceFolderModel;
|
||||||
|
|
||||||
|
class ResourceDownloadTask : public SequentialTask {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||||
|
ModPlatform::IndexedVersion version,
|
||||||
|
const std::shared_ptr<ResourceFolderModel> packs,
|
||||||
|
bool is_indexed = true,
|
||||||
|
QString custom_target_folder = {});
|
||||||
|
const QString& getFilename() const { return m_pack_version.fileName; }
|
||||||
|
const QString& getCustomPath() const { return m_custom_target_folder; }
|
||||||
|
const QVariant& getVersionID() const { return m_pack_version.fileId; }
|
||||||
|
const QString& getName() const { return m_pack->name; }
|
||||||
|
ModPlatform::IndexedPack::Ptr getPack() { return m_pack; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ModPlatform::IndexedPack::Ptr m_pack;
|
||||||
|
ModPlatform::IndexedVersion m_pack_version;
|
||||||
|
const std::shared_ptr<ResourceFolderModel> m_pack_model;
|
||||||
|
QString m_custom_target_folder;
|
||||||
|
|
||||||
|
NetJob::Ptr m_filesNetJob;
|
||||||
|
LocalModUpdateTask::Ptr m_update_task;
|
||||||
|
|
||||||
|
void downloadProgressChanged(qint64 current, qint64 total);
|
||||||
|
void downloadFailed(QString reason);
|
||||||
|
void downloadSucceeded();
|
||||||
|
|
||||||
|
std::tuple<QString, QString> to_delete{ "", "" };
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void hasOldResource(QString name, QString filename);
|
||||||
|
};
|
88
launcher/RuntimeContext.h
Normal file
88
launcher/RuntimeContext.h
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
|
#include "settings/SettingsObject.h"
|
||||||
|
|
||||||
|
struct RuntimeContext {
|
||||||
|
QString javaArchitecture;
|
||||||
|
QString javaRealArchitecture;
|
||||||
|
QString javaPath;
|
||||||
|
QString system;
|
||||||
|
|
||||||
|
QString mappedJavaRealArchitecture() const
|
||||||
|
{
|
||||||
|
if (javaRealArchitecture == "amd64")
|
||||||
|
return "x86_64";
|
||||||
|
if (javaRealArchitecture == "i386" || javaRealArchitecture == "i686")
|
||||||
|
return "x86";
|
||||||
|
if (javaRealArchitecture == "aarch64")
|
||||||
|
return "arm64";
|
||||||
|
if (javaRealArchitecture == "arm" || javaRealArchitecture == "armhf")
|
||||||
|
return "arm32";
|
||||||
|
return javaRealArchitecture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateFromInstanceSettings(SettingsObjectPtr instanceSettings)
|
||||||
|
{
|
||||||
|
javaArchitecture = instanceSettings->get("JavaArchitecture").toString();
|
||||||
|
javaRealArchitecture = instanceSettings->get("JavaRealArchitecture").toString();
|
||||||
|
javaPath = instanceSettings->get("JavaPath").toString();
|
||||||
|
system = currentSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getClassifier() const { return system + "-" + mappedJavaRealArchitecture(); }
|
||||||
|
|
||||||
|
// "Legacy" refers to the fact that Mojang assumed that these are the only two architectures
|
||||||
|
bool isLegacyArch() const
|
||||||
|
{
|
||||||
|
const QString mapped = mappedJavaRealArchitecture();
|
||||||
|
return mapped == "x86_64" || mapped == "x86";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool classifierMatches(QString target) const
|
||||||
|
{
|
||||||
|
// try to match precise classifier "[os]-[arch]"
|
||||||
|
bool x = target == getClassifier();
|
||||||
|
// try to match imprecise classifier on legacy architectures "[os]"
|
||||||
|
if (!x && isLegacyArch())
|
||||||
|
x = target == system;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
};
|
184
launcher/StringUtils.cpp
Normal file
184
launcher/StringUtils.cpp
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 flowln <flowlnlnln@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 "StringUtils.h"
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QUuid>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
/// If you're wondering where these came from exactly, then know you're not the only one =D
|
||||||
|
|
||||||
|
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
||||||
|
static inline QChar getNextChar(const QString& s, int location)
|
||||||
|
{
|
||||||
|
return (location < s.length()) ? s.at(location) : QChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TAKEN FROM Qt, because it doesn't expose it intelligently
|
||||||
|
int StringUtils::naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs)
|
||||||
|
{
|
||||||
|
int l1 = 0, l2 = 0;
|
||||||
|
while (l1 <= s1.count() && l2 <= s2.count()) {
|
||||||
|
// skip spaces, tabs and 0's
|
||||||
|
QChar c1 = getNextChar(s1, l1);
|
||||||
|
while (c1.isSpace())
|
||||||
|
c1 = getNextChar(s1, ++l1);
|
||||||
|
|
||||||
|
QChar c2 = getNextChar(s2, l2);
|
||||||
|
while (c2.isSpace())
|
||||||
|
c2 = getNextChar(s2, ++l2);
|
||||||
|
|
||||||
|
if (c1.isDigit() && c2.isDigit()) {
|
||||||
|
while (c1.digitValue() == 0)
|
||||||
|
c1 = getNextChar(s1, ++l1);
|
||||||
|
while (c2.digitValue() == 0)
|
||||||
|
c2 = getNextChar(s2, ++l2);
|
||||||
|
|
||||||
|
int lookAheadLocation1 = l1;
|
||||||
|
int lookAheadLocation2 = l2;
|
||||||
|
int currentReturnValue = 0;
|
||||||
|
// find the last digit, setting currentReturnValue as we go if it isn't equal
|
||||||
|
for (QChar lookAhead1 = c1, lookAhead2 = c2; (lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
|
||||||
|
lookAhead1 = getNextChar(s1, ++lookAheadLocation1), lookAhead2 = getNextChar(s2, ++lookAheadLocation2)) {
|
||||||
|
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
|
||||||
|
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
|
||||||
|
if (!is1ADigit && !is2ADigit)
|
||||||
|
break;
|
||||||
|
if (!is1ADigit)
|
||||||
|
return -1;
|
||||||
|
if (!is2ADigit)
|
||||||
|
return 1;
|
||||||
|
if (currentReturnValue == 0) {
|
||||||
|
if (lookAhead1 < lookAhead2) {
|
||||||
|
currentReturnValue = -1;
|
||||||
|
} else if (lookAhead1 > lookAhead2) {
|
||||||
|
currentReturnValue = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentReturnValue != 0)
|
||||||
|
return currentReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cs == Qt::CaseInsensitive) {
|
||||||
|
if (!c1.isLower())
|
||||||
|
c1 = c1.toLower();
|
||||||
|
if (!c2.isLower())
|
||||||
|
c2 = c2.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = QString::localeAwareCompare(c1, c2);
|
||||||
|
if (r < 0)
|
||||||
|
return -1;
|
||||||
|
if (r > 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
l1 += 1;
|
||||||
|
l2 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The two strings are the same (02 == 2) so fall back to the normal sort
|
||||||
|
return QString::compare(s1, s2, cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString StringUtils::truncateUrlHumanFriendly(QUrl& url, int max_len, bool hard_limit)
|
||||||
|
{
|
||||||
|
auto display_options = QUrl::RemoveUserInfo | QUrl::RemoveFragment | QUrl::NormalizePathSegments;
|
||||||
|
auto str_url = url.toDisplayString(display_options);
|
||||||
|
|
||||||
|
if (str_url.length() <= max_len)
|
||||||
|
return str_url;
|
||||||
|
|
||||||
|
auto url_path_parts = url.path().split('/');
|
||||||
|
QString last_path_segment = url_path_parts.takeLast();
|
||||||
|
|
||||||
|
if (url_path_parts.size() >= 1 && url_path_parts.first().isEmpty())
|
||||||
|
url_path_parts.removeFirst(); // drop empty first segment (from leading / )
|
||||||
|
|
||||||
|
if (url_path_parts.size() >= 1)
|
||||||
|
url_path_parts.removeLast(); // drop the next to last path segment
|
||||||
|
|
||||||
|
auto url_template = QStringLiteral("%1://%2/%3%4");
|
||||||
|
|
||||||
|
auto url_compact = url_path_parts.isEmpty()
|
||||||
|
? url_template.arg(url.scheme(), url.host(), QStringList({ "...", last_path_segment }).join('/'), url.query())
|
||||||
|
: url_template.arg(url.scheme(), url.host(),
|
||||||
|
QStringList({ url_path_parts.join('/'), "...", last_path_segment }).join('/'), url.query());
|
||||||
|
|
||||||
|
// remove url parts one by one if it's still too long
|
||||||
|
while (url_compact.length() > max_len && url_path_parts.size() >= 1) {
|
||||||
|
url_path_parts.removeLast(); // drop the next to last path segment
|
||||||
|
url_compact = url_path_parts.isEmpty()
|
||||||
|
? url_template.arg(url.scheme(), url.host(), QStringList({ "...", last_path_segment }).join('/'), url.query())
|
||||||
|
: url_template.arg(url.scheme(), url.host(),
|
||||||
|
QStringList({ url_path_parts.join('/'), "...", last_path_segment }).join('/'), url.query());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((url_compact.length() >= max_len) && hard_limit) {
|
||||||
|
// still too long, truncate normaly
|
||||||
|
url_compact = QString(str_url);
|
||||||
|
auto to_remove = url_compact.length() - max_len + 3;
|
||||||
|
url_compact.remove(url_compact.length() - to_remove - 1, to_remove);
|
||||||
|
url_compact.append("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
return url_compact;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const QStringList s_units_si{ "KB", "MB", "GB", "TB" };
|
||||||
|
static const QStringList s_units_kibi{ "KiB", "MiB", "GiB", "TiB" };
|
||||||
|
|
||||||
|
QString StringUtils::humanReadableFileSize(double bytes, bool use_si, int decimal_points)
|
||||||
|
{
|
||||||
|
const QStringList units = use_si ? s_units_si : s_units_kibi;
|
||||||
|
const int scale = use_si ? 1000 : 1024;
|
||||||
|
|
||||||
|
int u = -1;
|
||||||
|
double r = pow(10, decimal_points);
|
||||||
|
|
||||||
|
do {
|
||||||
|
bytes /= scale;
|
||||||
|
u++;
|
||||||
|
} while (round(abs(bytes) * r) / r >= scale && u < units.length() - 1);
|
||||||
|
|
||||||
|
return QString::number(bytes, 'f', 2) + " " + units[u];
|
||||||
|
}
|
||||||
|
|
||||||
|
QString StringUtils::getRandomAlphaNumeric()
|
||||||
|
{
|
||||||
|
return QUuid::createUuid().toString(QUuid::Id128);
|
||||||
|
}
|
82
launcher/StringUtils.h
Normal file
82
launcher/StringUtils.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 flowln <flowlnlnln@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 <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
namespace StringUtils {
|
||||||
|
|
||||||
|
#if defined Q_OS_WIN32
|
||||||
|
using string = std::wstring;
|
||||||
|
|
||||||
|
inline string toStdString(QString s)
|
||||||
|
{
|
||||||
|
return s.toStdWString();
|
||||||
|
}
|
||||||
|
inline QString fromStdString(string s)
|
||||||
|
{
|
||||||
|
return QString::fromStdWString(s);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
using string = std::string;
|
||||||
|
|
||||||
|
inline string toStdString(QString s)
|
||||||
|
{
|
||||||
|
return s.toStdString();
|
||||||
|
}
|
||||||
|
inline QString fromStdString(string s)
|
||||||
|
{
|
||||||
|
return QString::fromStdString(s);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int naturalCompare(const QString& s1, const QString& s2, Qt::CaseSensitivity cs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Truncate a url while keeping its readability py placing the `...` in the middle of the path
|
||||||
|
* @param url Url to truncate
|
||||||
|
* @param max_len max lenght of url in charaters
|
||||||
|
* @param hard_limit if truncating the path can't get the url short enough, truncate it normaly.
|
||||||
|
*/
|
||||||
|
QString truncateUrlHumanFriendly(QUrl &url, int max_len, bool hard_limit = false);
|
||||||
|
|
||||||
|
QString humanReadableFileSize(double bytes, bool use_si = false, int decimal_points = 1);
|
||||||
|
|
||||||
|
|
||||||
|
QString getRandomAlphaNumeric();
|
||||||
|
} // namespace StringUtils
|
@ -1,443 +0,0 @@
|
|||||||
#include <QFile>
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <FileSystem.h>
|
|
||||||
#include <updater/GoUpdate.h>
|
|
||||||
#include "UpdateController.h"
|
|
||||||
#include <QApplication>
|
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
|
||||||
#include <LocalPeer.h>
|
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
|
||||||
|
|
||||||
|
|
||||||
// from <sys/stat.h>
|
|
||||||
#ifndef S_IRUSR
|
|
||||||
#define __S_IREAD 0400 /* Read by owner. */
|
|
||||||
#define __S_IWRITE 0200 /* Write by owner. */
|
|
||||||
#define __S_IEXEC 0100 /* Execute by owner. */
|
|
||||||
#define S_IRUSR __S_IREAD /* Read by owner. */
|
|
||||||
#define S_IWUSR __S_IWRITE /* Write by owner. */
|
|
||||||
#define S_IXUSR __S_IEXEC /* Execute by owner. */
|
|
||||||
|
|
||||||
#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
|
|
||||||
#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
|
|
||||||
#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */
|
|
||||||
|
|
||||||
#define S_IROTH (S_IRGRP >> 3) /* Read by others. */
|
|
||||||
#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */
|
|
||||||
#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */
|
|
||||||
#endif
|
|
||||||
static QFile::Permissions unixModeToPermissions(const int mode)
|
|
||||||
{
|
|
||||||
QFile::Permissions perms;
|
|
||||||
|
|
||||||
if (mode & S_IRUSR)
|
|
||||||
{
|
|
||||||
perms |= QFile::ReadUser;
|
|
||||||
}
|
|
||||||
if (mode & S_IWUSR)
|
|
||||||
{
|
|
||||||
perms |= QFile::WriteUser;
|
|
||||||
}
|
|
||||||
if (mode & S_IXUSR)
|
|
||||||
{
|
|
||||||
perms |= QFile::ExeUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode & S_IRGRP)
|
|
||||||
{
|
|
||||||
perms |= QFile::ReadGroup;
|
|
||||||
}
|
|
||||||
if (mode & S_IWGRP)
|
|
||||||
{
|
|
||||||
perms |= QFile::WriteGroup;
|
|
||||||
}
|
|
||||||
if (mode & S_IXGRP)
|
|
||||||
{
|
|
||||||
perms |= QFile::ExeGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode & S_IROTH)
|
|
||||||
{
|
|
||||||
perms |= QFile::ReadOther;
|
|
||||||
}
|
|
||||||
if (mode & S_IWOTH)
|
|
||||||
{
|
|
||||||
perms |= QFile::WriteOther;
|
|
||||||
}
|
|
||||||
if (mode & S_IXOTH)
|
|
||||||
{
|
|
||||||
perms |= QFile::ExeOther;
|
|
||||||
}
|
|
||||||
return perms;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QLatin1String liveCheckFile("live.check");
|
|
||||||
|
|
||||||
UpdateController::UpdateController(QWidget * parent, const QString& root, const QString updateFilesDir, GoUpdate::OperationList operations)
|
|
||||||
{
|
|
||||||
m_parent = parent;
|
|
||||||
m_root = root;
|
|
||||||
m_updateFilesDir = updateFilesDir;
|
|
||||||
m_operations = operations;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void UpdateController::installUpdates()
|
|
||||||
{
|
|
||||||
qint64 pid = -1;
|
|
||||||
QStringList args;
|
|
||||||
bool started = false;
|
|
||||||
|
|
||||||
qDebug() << "Installing updates.";
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
QString finishCmd = QApplication::applicationFilePath();
|
|
||||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined (Q_OS_OPENBSD)
|
|
||||||
QString finishCmd = FS::PathCombine(m_root, BuildConfig.LAUNCHER_NAME);
|
|
||||||
#elif defined Q_OS_MAC
|
|
||||||
QString finishCmd = QApplication::applicationFilePath();
|
|
||||||
#else
|
|
||||||
#error Unsupported operating system.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QString backupPath = FS::PathCombine(m_root, "update", "backup");
|
|
||||||
QDir origin(m_root);
|
|
||||||
|
|
||||||
// clean up the backup folder. it should be empty before we start
|
|
||||||
if(!FS::deletePath(backupPath))
|
|
||||||
{
|
|
||||||
qWarning() << "couldn't remove previous backup folder" << backupPath;
|
|
||||||
}
|
|
||||||
// and it should exist.
|
|
||||||
if(!FS::ensureFolderPathExists(backupPath))
|
|
||||||
{
|
|
||||||
qWarning() << "couldn't create folder" << backupPath;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool useXPHack = false;
|
|
||||||
QString exePath;
|
|
||||||
QString exeOrigin;
|
|
||||||
QString exeBackup;
|
|
||||||
|
|
||||||
// perform the update operations
|
|
||||||
for(auto op: m_operations)
|
|
||||||
{
|
|
||||||
switch(op.type)
|
|
||||||
{
|
|
||||||
// replace = move original out to backup, if it exists, move the new file in its place
|
|
||||||
case GoUpdate::Operation::OP_REPLACE:
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
QString windowsExeName = BuildConfig.LAUNCHER_NAME + ".exe";
|
|
||||||
// hack for people renaming the .exe because ... reasons :)
|
|
||||||
if(op.destination == windowsExeName)
|
|
||||||
{
|
|
||||||
op.destination = QFileInfo(QApplication::applicationFilePath()).fileName();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
QFileInfo destination (FS::PathCombine(m_root, op.destination));
|
|
||||||
if(destination.exists())
|
|
||||||
{
|
|
||||||
QString backupName = op.destination;
|
|
||||||
backupName.replace('/', '_');
|
|
||||||
QString backupFilePath = FS::PathCombine(backupPath, backupName);
|
|
||||||
if(!QFile::rename(destination.absoluteFilePath(), backupFilePath))
|
|
||||||
{
|
|
||||||
qWarning() << "Couldn't move:" << destination.absoluteFilePath() << "to" << backupFilePath;
|
|
||||||
m_failedOperationType = Replace;
|
|
||||||
m_failedFile = op.destination;
|
|
||||||
fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BackupEntry be;
|
|
||||||
be.original = destination.absoluteFilePath();
|
|
||||||
be.backup = backupFilePath;
|
|
||||||
be.update = op.source;
|
|
||||||
m_replace_backups.append(be);
|
|
||||||
}
|
|
||||||
// make sure the folder we are putting this into exists
|
|
||||||
if(!FS::ensureFilePathExists(destination.absoluteFilePath()))
|
|
||||||
{
|
|
||||||
qWarning() << "REPLACE: Couldn't create folder:" << destination.absoluteFilePath();
|
|
||||||
m_failedOperationType = Replace;
|
|
||||||
m_failedFile = op.destination;
|
|
||||||
fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// now move the new file in
|
|
||||||
if(!QFile::rename(op.source, destination.absoluteFilePath()))
|
|
||||||
{
|
|
||||||
qWarning() << "REPLACE: Couldn't move:" << op.source << "to" << destination.absoluteFilePath();
|
|
||||||
m_failedOperationType = Replace;
|
|
||||||
m_failedFile = op.destination;
|
|
||||||
fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QFile::setPermissions(destination.absoluteFilePath(), unixModeToPermissions(op.destinationMode));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// delete = move original to backup
|
|
||||||
case GoUpdate::Operation::OP_DELETE:
|
|
||||||
{
|
|
||||||
QString destFilePath = FS::PathCombine(m_root, op.destination);
|
|
||||||
if(QFile::exists(destFilePath))
|
|
||||||
{
|
|
||||||
QString backupName = op.destination;
|
|
||||||
backupName.replace('/', '_');
|
|
||||||
QString trashFilePath = FS::PathCombine(backupPath, backupName);
|
|
||||||
|
|
||||||
if(!QFile::rename(destFilePath, trashFilePath))
|
|
||||||
{
|
|
||||||
qWarning() << "DELETE: Couldn't move:" << op.destination << "to" << trashFilePath;
|
|
||||||
m_failedFile = op.destination;
|
|
||||||
m_failedOperationType = Delete;
|
|
||||||
fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BackupEntry be;
|
|
||||||
be.original = destFilePath;
|
|
||||||
be.backup = trashFilePath;
|
|
||||||
m_delete_backups.append(be);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to start the new binary
|
|
||||||
args = qApp->arguments();
|
|
||||||
args.removeFirst();
|
|
||||||
|
|
||||||
// on old Windows, do insane things... no error checking here, this is just to have something.
|
|
||||||
if(useXPHack)
|
|
||||||
{
|
|
||||||
QString script;
|
|
||||||
auto nativePath = QDir::toNativeSeparators(exePath);
|
|
||||||
auto nativeOriginPath = QDir::toNativeSeparators(exeOrigin);
|
|
||||||
auto nativeBackupPath = QDir::toNativeSeparators(exeBackup);
|
|
||||||
|
|
||||||
// so we write this vbscript thing...
|
|
||||||
QTextStream out(&script);
|
|
||||||
out << "WScript.Sleep 1000\n";
|
|
||||||
out << "Set fso=CreateObject(\"Scripting.FileSystemObject\")\n";
|
|
||||||
out << "Set shell=CreateObject(\"WScript.Shell\")\n";
|
|
||||||
out << "fso.MoveFile \"" << nativePath << "\", \"" << nativeBackupPath << "\"\n";
|
|
||||||
out << "fso.MoveFile \"" << nativeOriginPath << "\", \"" << nativePath << "\"\n";
|
|
||||||
out << "shell.Run \"" << nativePath << "\"\n";
|
|
||||||
|
|
||||||
QString scriptPath = FS::PathCombine(m_root, "update", "update.vbs");
|
|
||||||
|
|
||||||
// we save it
|
|
||||||
QFile scriptFile(scriptPath);
|
|
||||||
scriptFile.open(QIODevice::WriteOnly);
|
|
||||||
scriptFile.write(script.toLocal8Bit().replace("\n", "\r\n"));
|
|
||||||
scriptFile.close();
|
|
||||||
|
|
||||||
// we run it
|
|
||||||
started = QProcess::startDetached("wscript", {scriptPath}, m_root);
|
|
||||||
|
|
||||||
// and we quit. conscious thought.
|
|
||||||
qApp->quit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool doLiveCheck = true;
|
|
||||||
bool startFailed = false;
|
|
||||||
|
|
||||||
// remove live check file, if any
|
|
||||||
if(QFile::exists(liveCheckFile))
|
|
||||||
{
|
|
||||||
if(!QFile::remove(liveCheckFile))
|
|
||||||
{
|
|
||||||
qWarning() << "Couldn't remove the" << liveCheckFile << "file! We will proceed without :(";
|
|
||||||
doLiveCheck = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(doLiveCheck)
|
|
||||||
{
|
|
||||||
if(!args.contains("--alive"))
|
|
||||||
{
|
|
||||||
args.append("--alive");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: reparse args and construct a safe variant from scratch. This is a workaround for GH-1874:
|
|
||||||
QStringList realargs;
|
|
||||||
int skip = 0;
|
|
||||||
for(auto & arg: args)
|
|
||||||
{
|
|
||||||
if(skip)
|
|
||||||
{
|
|
||||||
skip--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(arg == "-l")
|
|
||||||
{
|
|
||||||
skip = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
realargs.append(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// start the updated application
|
|
||||||
started = QProcess::startDetached(finishCmd, realargs, QDir::currentPath(), &pid);
|
|
||||||
// much dumber check - just find out if the call
|
|
||||||
if(!started || pid == -1)
|
|
||||||
{
|
|
||||||
qWarning() << "Couldn't start new process properly!";
|
|
||||||
startFailed = true;
|
|
||||||
}
|
|
||||||
if(!startFailed && doLiveCheck)
|
|
||||||
{
|
|
||||||
int attempts = 0;
|
|
||||||
while(attempts < 10)
|
|
||||||
{
|
|
||||||
attempts++;
|
|
||||||
QString key;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
|
||||||
if(!QFile::exists(liveCheckFile))
|
|
||||||
{
|
|
||||||
qWarning() << "Couldn't find the" << liveCheckFile << "file!";
|
|
||||||
startFailed = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
key = QString::fromUtf8(FS::read(liveCheckFile));
|
|
||||||
auto id = ApplicationId::fromRawString(key);
|
|
||||||
LocalPeer peer(nullptr, id);
|
|
||||||
if(peer.isClient())
|
|
||||||
{
|
|
||||||
startFailed = false;
|
|
||||||
qDebug() << "Found process started with key " << key;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
startFailed = true;
|
|
||||||
qDebug() << "Process started with key " << key << "apparently died or is not reponding...";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (const Exception &e)
|
|
||||||
{
|
|
||||||
qWarning() << "Couldn't read the" << liveCheckFile << "file!";
|
|
||||||
startFailed = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(startFailed)
|
|
||||||
{
|
|
||||||
m_failedOperationType = Start;
|
|
||||||
fail();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
origin.rmdir(m_updateFilesDir);
|
|
||||||
qApp->quit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateController::fail()
|
|
||||||
{
|
|
||||||
qWarning() << "Update failed!";
|
|
||||||
|
|
||||||
QString msg;
|
|
||||||
bool doRollback = false;
|
|
||||||
QString failTitle = QObject::tr("Update failed!");
|
|
||||||
QString rollFailTitle = QObject::tr("Rollback failed!");
|
|
||||||
switch (m_failedOperationType)
|
|
||||||
{
|
|
||||||
case Replace:
|
|
||||||
{
|
|
||||||
msg = QObject::tr(
|
|
||||||
"Couldn't replace file %1. Changes will be reverted.\n"
|
|
||||||
"See the %2 log file for details."
|
|
||||||
).arg(m_failedFile, BuildConfig.LAUNCHER_NAME);
|
|
||||||
doRollback = true;
|
|
||||||
QMessageBox::critical(m_parent, failTitle, msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Delete:
|
|
||||||
{
|
|
||||||
msg = QObject::tr(
|
|
||||||
"Couldn't remove file %1. Changes will be reverted.\n"
|
|
||||||
"See the %2 log file for details."
|
|
||||||
).arg(m_failedFile, BuildConfig.LAUNCHER_NAME);
|
|
||||||
doRollback = true;
|
|
||||||
QMessageBox::critical(m_parent, failTitle, msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Start:
|
|
||||||
{
|
|
||||||
msg = QObject::tr("The new version didn't start or is too old and doesn't respond to startup checks.\n"
|
|
||||||
"\n"
|
|
||||||
"Roll back to previous version?");
|
|
||||||
auto result = QMessageBox::critical(
|
|
||||||
m_parent,
|
|
||||||
failTitle,
|
|
||||||
msg,
|
|
||||||
QMessageBox::Yes | QMessageBox::No,
|
|
||||||
QMessageBox::Yes
|
|
||||||
);
|
|
||||||
doRollback = (result == QMessageBox::Yes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Nothing:
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(doRollback)
|
|
||||||
{
|
|
||||||
auto rollbackOK = rollback();
|
|
||||||
if(!rollbackOK)
|
|
||||||
{
|
|
||||||
msg = QObject::tr("The rollback failed too.\n"
|
|
||||||
"You will have to repair %1 manually.\n"
|
|
||||||
"Please let us know why and how this happened.").arg(BuildConfig.LAUNCHER_NAME);
|
|
||||||
QMessageBox::critical(m_parent, rollFailTitle, msg);
|
|
||||||
qApp->quit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
qApp->quit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UpdateController::rollback()
|
|
||||||
{
|
|
||||||
bool revertOK = true;
|
|
||||||
// if the above failed, roll back changes
|
|
||||||
for(auto backup:m_replace_backups)
|
|
||||||
{
|
|
||||||
qWarning() << "restoring" << backup.original << "from" << backup.backup;
|
|
||||||
if(!QFile::rename(backup.original, backup.update))
|
|
||||||
{
|
|
||||||
revertOK = false;
|
|
||||||
qWarning() << "moving new" << backup.original << "back to" << backup.update << "failed!";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!QFile::rename(backup.backup, backup.original))
|
|
||||||
{
|
|
||||||
revertOK = false;
|
|
||||||
qWarning() << "restoring" << backup.original << "failed!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(auto backup:m_delete_backups)
|
|
||||||
{
|
|
||||||
qWarning() << "restoring" << backup.original << "from" << backup.backup;
|
|
||||||
if(!QFile::rename(backup.backup, backup.original))
|
|
||||||
{
|
|
||||||
revertOK = false;
|
|
||||||
qWarning() << "restoring" << backup.original << "failed!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return revertOK;
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QList>
|
|
||||||
#include <updater/GoUpdate.h>
|
|
||||||
|
|
||||||
class QWidget;
|
|
||||||
|
|
||||||
class UpdateController
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
UpdateController(QWidget * parent, const QString &root, const QString updateFilesDir, GoUpdate::OperationList operations);
|
|
||||||
void installUpdates();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void fail();
|
|
||||||
bool rollback();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString m_root;
|
|
||||||
QString m_updateFilesDir;
|
|
||||||
GoUpdate::OperationList m_operations;
|
|
||||||
QWidget * m_parent;
|
|
||||||
|
|
||||||
struct BackupEntry
|
|
||||||
{
|
|
||||||
// path where we got the new file from
|
|
||||||
QString update;
|
|
||||||
// path of what is being actually updated
|
|
||||||
QString original;
|
|
||||||
// path where the backup of the updated file was placed
|
|
||||||
QString backup;
|
|
||||||
};
|
|
||||||
QList <BackupEntry> m_replace_backups;
|
|
||||||
QList <BackupEntry> m_delete_backups;
|
|
||||||
enum Failure
|
|
||||||
{
|
|
||||||
Replace,
|
|
||||||
Delete,
|
|
||||||
Start,
|
|
||||||
Nothing
|
|
||||||
} m_failedOperationType = Nothing;
|
|
||||||
QString m_failedFile;
|
|
||||||
};
|
|
@ -1,85 +1,128 @@
|
|||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
|
|
||||||
#include <QStringList>
|
#include <QDebug>
|
||||||
#include <QUrl>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QRegularExpressionMatch>
|
#include <QRegularExpressionMatch>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
Version::Version(const QString &str) : m_string(str)
|
Version::Version(QString str) : m_string(std::move(str))
|
||||||
{
|
{
|
||||||
parse();
|
parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Version::operator<(const Version &other) const
|
#define VERSION_OPERATOR(return_on_different) \
|
||||||
{
|
bool exclude_our_sections = false; \
|
||||||
const int size = qMax(m_sections.size(), other.m_sections.size());
|
bool exclude_their_sections = false; \
|
||||||
for (int i = 0; i < size; ++i)
|
\
|
||||||
{
|
const auto size = qMax(m_sections.size(), other.m_sections.size()); \
|
||||||
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
|
for (int i = 0; i < size; ++i) { \
|
||||||
const Section sec2 =
|
Section sec1 = (i >= m_sections.size()) ? Section() : m_sections.at(i); \
|
||||||
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
|
Section sec2 = (i >= other.m_sections.size()) ? Section() : other.m_sections.at(i); \
|
||||||
if (sec1 != sec2)
|
\
|
||||||
{
|
{ /* Don't include appendixes in the comparison */ \
|
||||||
return sec1 < sec2;
|
if (sec1.isAppendix()) \
|
||||||
}
|
exclude_our_sections = true; \
|
||||||
|
if (sec2.isAppendix()) \
|
||||||
|
exclude_their_sections = true; \
|
||||||
|
\
|
||||||
|
if (exclude_our_sections) { \
|
||||||
|
sec1 = Section(); \
|
||||||
|
if (sec2.m_isNull) \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (exclude_their_sections) { \
|
||||||
|
sec2 = Section(); \
|
||||||
|
if (sec1.m_isNull) \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
if (sec1 != sec2) \
|
||||||
|
return return_on_different; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Version::operator<(const Version& other) const
|
||||||
|
{
|
||||||
|
VERSION_OPERATOR(sec1 < sec2)
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool Version::operator<=(const Version &other) const
|
bool Version::operator==(const Version& other) const
|
||||||
{
|
{
|
||||||
return *this < other || *this == other;
|
VERSION_OPERATOR(false)
|
||||||
}
|
|
||||||
bool Version::operator>(const Version &other) const
|
|
||||||
{
|
|
||||||
const int size = qMax(m_sections.size(), other.m_sections.size());
|
|
||||||
for (int i = 0; i < size; ++i)
|
|
||||||
{
|
|
||||||
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
|
|
||||||
const Section sec2 =
|
|
||||||
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
|
|
||||||
if (sec1 != sec2)
|
|
||||||
{
|
|
||||||
return sec1 > sec2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool Version::operator>=(const Version &other) const
|
|
||||||
{
|
|
||||||
return *this > other || *this == other;
|
|
||||||
}
|
|
||||||
bool Version::operator==(const Version &other) const
|
|
||||||
{
|
|
||||||
const int size = qMax(m_sections.size(), other.m_sections.size());
|
|
||||||
for (int i = 0; i < size; ++i)
|
|
||||||
{
|
|
||||||
const Section sec1 = (i >= m_sections.size()) ? Section("0") : m_sections.at(i);
|
|
||||||
const Section sec2 =
|
|
||||||
(i >= other.m_sections.size()) ? Section("0") : other.m_sections.at(i);
|
|
||||||
if (sec1 != sec2)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Version::operator!=(const Version &other) const
|
bool Version::operator!=(const Version& other) const
|
||||||
{
|
{
|
||||||
return !operator==(other);
|
return !operator==(other);
|
||||||
}
|
}
|
||||||
|
bool Version::operator<=(const Version& other) const
|
||||||
|
{
|
||||||
|
return *this < other || *this == other;
|
||||||
|
}
|
||||||
|
bool Version::operator>(const Version& other) const
|
||||||
|
{
|
||||||
|
return !(*this <= other);
|
||||||
|
}
|
||||||
|
bool Version::operator>=(const Version& other) const
|
||||||
|
{
|
||||||
|
return !(*this < other);
|
||||||
|
}
|
||||||
|
|
||||||
void Version::parse()
|
void Version::parse()
|
||||||
{
|
{
|
||||||
m_sections.clear();
|
m_sections.clear();
|
||||||
|
QString currentSection;
|
||||||
|
|
||||||
// FIXME: this is bad. versions can contain a lot more separators...
|
if (m_string.isEmpty())
|
||||||
QStringList parts = m_string.split('.');
|
return;
|
||||||
|
|
||||||
for (const auto& part : parts)
|
auto classChange = [&](QChar lastChar, QChar currentChar) {
|
||||||
{
|
if (lastChar.isNull())
|
||||||
m_sections.append(Section(part));
|
return false;
|
||||||
|
if (lastChar.isDigit() != currentChar.isDigit())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const QList<QChar> s_separators{ '.', '-', '+' };
|
||||||
|
if (s_separators.contains(currentChar) && currentSection.at(0) != currentChar)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
currentSection += m_string.at(0);
|
||||||
|
for (int i = 1; i < m_string.size(); ++i) {
|
||||||
|
const auto& current_char = m_string.at(i);
|
||||||
|
if (classChange(m_string.at(i - 1), current_char)) {
|
||||||
|
if (!currentSection.isEmpty())
|
||||||
|
m_sections.append(Section(currentSection));
|
||||||
|
currentSection = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
currentSection += current_char;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!currentSection.isEmpty())
|
||||||
|
m_sections.append(Section(currentSection));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// qDebug print support for the Version class
|
||||||
|
QDebug operator<<(QDebug debug, const Version& v)
|
||||||
|
{
|
||||||
|
QDebugStateSaver saver(debug);
|
||||||
|
|
||||||
|
debug.nospace() << "Version{ string: " << v.toString() << ", sections: [ ";
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
for (auto s : v.m_sections) {
|
||||||
|
if (!first) debug.nospace() << ", ";
|
||||||
|
debug.nospace() << s.m_fullString;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.nospace() << " ]" << " }";
|
||||||
|
|
||||||
|
return debug;
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user