Compare commits
1531 Commits
Author | SHA1 | Date | |
---|---|---|---|
54e03d602f | |||
d8a84d2aa3 | |||
a7ff74365d | |||
68c884f20d | |||
b7e96bdf62 | |||
16b48866f4 | |||
4827f7e317 | |||
92f6a34624 | |||
c5ce8bfb3e | |||
6429088472 | |||
bf9885dd7e | |||
d16b6fe634 | |||
4539d58d7d | |||
a8611a56fc | |||
c3d64aa984 | |||
3fc63fa196 | |||
4438684ce6 | |||
699fce4482 | |||
49060beae7 | |||
de561e4fd3 | |||
1de301752f | |||
cb8c389303 | |||
e1e0166c95 | |||
6f8e1ccf89 | |||
3751856a4e | |||
5203e72199 | |||
3b1ab3c974 | |||
3476bbebd9 | |||
45870497c6 | |||
bcf506488f | |||
040774d67b | |||
94410352f5 | |||
2a819f1ca0 | |||
25c63dd1e0 | |||
d7223972d8 | |||
0a6c1238eb | |||
c11575f5f5 | |||
783761ca2e | |||
15084c8d6d | |||
822c5a530e | |||
6eb641ae56 | |||
9eb14e6b9b | |||
7cc42269a9 | |||
c45442760c | |||
83f0611200 | |||
d95625ed88 | |||
5622bcc563 | |||
deb9c98630 | |||
2c1e887c8d | |||
4a13d72997 | |||
590875d022 | |||
8f30237765 | |||
cb0339b492 | |||
e427174ff0 | |||
fc4c57f0d9 | |||
9ad4ae5340 | |||
0f48e0fc06 | |||
37ad1b40d8 | |||
514e7ae6a0 | |||
34230bfcf4 | |||
c390e211ee | |||
b0d69db878 | |||
b373beee21 | |||
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 | |||
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 | |||
6aad750fe0 | |||
5e443ae347 | |||
c65f5f7728 | |||
74b62727af | |||
2e94562f79 | |||
061ea5648e | |||
439e7bbf4e | |||
0f1d51f866 | |||
75cfaf0672 | |||
ceaa732e5f | |||
deb2a2bd14 | |||
33853b6107 | |||
ab6e1b112b | |||
220e823c8d | |||
c666c3e251 | |||
9cc1773fa7 | |||
e78c7af715 | |||
b96e76134a | |||
fd2b206997 | |||
2d53c7c5b2 | |||
28ae5d710e | |||
7a13412ec7 | |||
c86610b917 | |||
502750492c | |||
8fc1653b0c | |||
6841ebc31d | |||
8757281467 | |||
20b1723e78 | |||
be78afeee5 | |||
3746a2566d | |||
0cb47cf7d7 | |||
dec81c4f27 | |||
54b335711a | |||
6bb8332b4b | |||
de9e304236 | |||
650af5eb64 | |||
47bdcb6050 | |||
58dc3e93d3 | |||
79b0a16f7a | |||
2b65ee433f | |||
001bbef9ee | |||
c4316e81e6 | |||
fac63541a4 | |||
dfd6cb29be | |||
5f75e531e6 | |||
a7648d60ce | |||
52c45c2d32 | |||
fd6755c93f | |||
a53ee2e35c | |||
4e6978ff6f | |||
91a5c4bdcb | |||
9a07ede615 | |||
dfab55112b | |||
1709b47bb7 | |||
2d10c246a8 | |||
6e2869834f | |||
b8b71c7dd2 | |||
c3f6c3dd82 | |||
844b245776 | |||
4bcf8e6975 | |||
0e52112016 | |||
32a9545360 | |||
9a44c92211 | |||
dd6aabf9ab | |||
91776311c7 | |||
882c82f82c | |||
43b9db6e45 | |||
032ceefa1d | |||
05fa266e6b | |||
56085310cb | |||
6a1d611fd1 | |||
c8a72c876d | |||
ec87a8ddfc | |||
33e34ebb83 | |||
dce435c882 | |||
396b3c3952 | |||
8cf42f4e15 | |||
17ea51ce27 | |||
04818ca626 | |||
6732b77594 | |||
3e4346e321 | |||
4dfc01899a | |||
b0b8ccfd4a | |||
4d35c66af3 | |||
509f7bd018 | |||
42b9b3d72a | |||
e280f9fe3e | |||
89493a2f1d | |||
e259bffca6 | |||
ba9164022d | |||
a4672ba00f | |||
c0bf267bae | |||
1a26a53659 | |||
8f4d7ac655 | |||
c5625d8d32 | |||
548a2a1d64 | |||
df01a58099 | |||
ac8ee9f981 | |||
b1805b70ea | |||
eae8a2914e | |||
03429db528 | |||
10f27250ee | |||
aed7963d11 | |||
2810413112 | |||
31fd92e071 | |||
b3b76d5d56 | |||
cd948dceae | |||
eb33a87ff5 | |||
bf560f4594 | |||
d77237ca5d | |||
211d596fdd | |||
f464b347b2 | |||
203c3ec233 | |||
e2a74dfc30 | |||
4e99da7b62 | |||
eb5ed50824 | |||
3b4539de79 | |||
3e4d1c04de | |||
fdf5748029 | |||
3562e94650 | |||
15c5bbcf22 | |||
c363423718 | |||
e58158c3cd | |||
ff2cd50bfa | |||
984692dc62 | |||
a7fc23dd96 | |||
c1bcbf8c63 | |||
9e69b8fe1b | |||
e0ae631d59 | |||
3b187b5246 | |||
20666763f0 | |||
f591c87665 | |||
e6fe701727 | |||
35a698ef72 | |||
998271414e | |||
1749f25420 | |||
4ab0e70a9a | |||
08989bde5e | |||
87cbff391c | |||
962923bbce | |||
311b081e60 | |||
4103948132 | |||
906f26698b | |||
8a1a583afe | |||
9e19b73ce6 | |||
e11706d99d | |||
301b811310 | |||
273cf3d565 | |||
f432cfd73a | |||
ffa756ccee | |||
e210a4b244 | |||
f1902a4471 | |||
04b865adae | |||
00df092a99 | |||
cad581388f | |||
e7ed3abb79 | |||
e5f6dc1b14 | |||
145da82cd8 | |||
64776d6bac | |||
64d123f524 | |||
0ec4ade683 | |||
4bfc445cf8 | |||
4232b1cedb | |||
474d77ac57 | |||
3c40355d7c | |||
cbc1aad58d | |||
89a30a47c5 | |||
881a5603dc | |||
68c48b2c8a | |||
0f9260869b | |||
412fdb0f7b | |||
81d52b6169 | |||
278d2169da | |||
5599b5a337 | |||
471d6d6031 | |||
b90d6b0f26 | |||
cd22da9c62 | |||
fb75c23f4e | |||
7f22994f68 | |||
7aba7b6064 | |||
e32030f364 | |||
b40619bcbd | |||
fe2e1d931f | |||
2e17e78052 | |||
f2fa82fd3c | |||
8cec4b60a6 | |||
bf9888099c | |||
02201631e7 | |||
8e80b4bfc1 | |||
63dfc0633f | |||
50f1847904 | |||
79840f0fca | |||
06bf7b0f31 | |||
dbb63b97bd | |||
3039cb5c02 | |||
b5d2570fe2 | |||
5f951e8f21 | |||
286f82cc99 | |||
ed5415aeb8 | |||
bef79df6bb | |||
2edabe0f99 | |||
91b5f0228d | |||
d639da741b | |||
84bd5ace6c | |||
63589d2ba9 | |||
d6f4ff26b5 | |||
b606a2e040 | |||
f685139d89 | |||
68d6ce60a9 | |||
2bba64fe3a | |||
774ed044fc | |||
9cc235cde0 | |||
91375e4c6d | |||
0ef8da64e0 | |||
9ef38171e2 | |||
455e4de6f3 | |||
4e319254dd | |||
04e8780dd0 | |||
0fe4384067 | |||
bdfcd0b99e | |||
5da87d1904 | |||
821edf0f51 | |||
04e822acfb | |||
c31fce3621 | |||
5c05cf2206 | |||
d4e544c62c | |||
d232248268 | |||
90025ed45d | |||
964151d9c7 | |||
409c9bf9d2 | |||
a135c06bcf | |||
efb1f4abf2 | |||
036a826b3a | |||
8d2b66a0fd | |||
31d6278b31 | |||
5558d7eef8 | |||
6103d86a47 | |||
b8899a534d | |||
fa6829a6a1 | |||
5335540c33 | |||
7f62de3854 | |||
0afa2e92d5 | |||
6d1b166ad7 | |||
4b6ddfb89b | |||
9ec260619b | |||
4c6ac9e4fe | |||
08fc3ea2e0 | |||
03e454b71d | |||
c527ea83fb | |||
8b9ac63657 | |||
0ba02f0830 | |||
1f6cef6f8a | |||
251942323e | |||
8e3efec40f | |||
3d0740f80e | |||
707a68cb4f | |||
e6b1a1fa76 | |||
3e8d450741 | |||
d0881b7420 | |||
27239b2dde | |||
3fbbaddece | |||
effe46db86 | |||
ba939c92ec | |||
e25cdd9d12 | |||
d394235ee0 | |||
349fc4143d | |||
4be9e6a0bc | |||
cb258146c4 | |||
9f039cef72 | |||
d08815bbc1 | |||
c04e38d011 | |||
794022d399 | |||
8e43190984 | |||
2212a6e40f | |||
63b69c0b0c | |||
278219b1d8 | |||
2ce4ce9064 | |||
4448418b63 | |||
9f1f37e780 | |||
40ccd1a469 | |||
a4ef0940ed | |||
2ff0aa09e3 | |||
32217a774f | |||
8856c8cd62 | |||
e843b8e188 | |||
c4f2e3a955 | |||
91301ec7fe | |||
13b03e7e50 | |||
8a0aa5a0c8 | |||
2bd8e7dca4 | |||
d75571ffa1 | |||
8683d529fc | |||
2ee4b6768d | |||
a9f0a85590 | |||
37160f973f | |||
9bbf50e864 | |||
51258ab28c | |||
29e5a213a5 | |||
2d261607df | |||
54144154f9 | |||
81daffe68e | |||
2ea20a8b29 | |||
2257c875f5 | |||
8a2e8ad953 | |||
fa5b1d9978 | |||
529fb07b42 | |||
4a261cac1a | |||
b3c8f9d508 | |||
1b878030aa | |||
46e403b20b | |||
1b1f728c58 | |||
309013efb3 | |||
705cf6499e | |||
f2ed4a92e2 | |||
0c8ca1b3c0 | |||
6ee5ee496e | |||
7f8aa2099c | |||
1d9797660b | |||
cd0d8a76c5 | |||
3f1251e78b | |||
89d4405563 | |||
8966364648 | |||
1c60e9b4fc | |||
843c860d98 | |||
6587e39992 | |||
757fa1410c | |||
dd6d8e0002 | |||
cd49406bfe | |||
7a3acc3249 | |||
db1c804812 | |||
493d58f358 | |||
fcd56dddc2 | |||
91b85f9919 | |||
4cecba8787 | |||
5930acc418 | |||
61d36c1723 | |||
c2a43c6f40 | |||
1ab00ca8b2 | |||
778baa6dbe | |||
25ab121e42 | |||
cf4949b4f5 | |||
4ca634d229 | |||
ca21b31696 | |||
38ff76d2b8 | |||
147bde5294 | |||
1a004f0c4d | |||
7d21bf15e8 | |||
a88ad8025b | |||
e06bf17d13 | |||
04a3669fc4 | |||
795075f90d | |||
2727df704f | |||
8ce8aadd9b | |||
2999e69f0f | |||
7ac16ed073 | |||
3585e4764b | |||
065e38c6aa | |||
9054ee18a1 | |||
9d8b95107d | |||
8731c86d0d | |||
d7fad4bd04 | |||
2746251dcd | |||
73e46e569f | |||
4f0f74d201 | |||
adf1e1982a | |||
f5405e835e | |||
85ee7fb3a4 | |||
ee00a5d8ee | |||
65d23fc9b9 | |||
3448d7cb70 | |||
db1abb02d6 | |||
36d78577e2 | |||
20832682ef | |||
8e6c592ad9 | |||
0b3115997a | |||
b07c5982e1 | |||
4a0b0d8735 | |||
7b9f67f4b4 | |||
577f17c916 | |||
d4c1d62781 | |||
80da1f1bb9 | |||
febdb85f96 | |||
96b76c8f5c | |||
411bf3be03 | |||
a98b6663e1 | |||
e7f35e6ca3 | |||
f0ec165d42 | |||
abd240468e | |||
b5e00027d1 | |||
1698554024 | |||
f4604bbf79 | |||
699ad316f0 | |||
fcbe233fdb | |||
4af8a9ed2d | |||
80627b4f89 | |||
83078cd49a | |||
53e0d13142 | |||
85901082a2 | |||
f19e27e875 | |||
c864046cee | |||
2d68235e55 | |||
2be8100e7c | |||
ab3e2562db | |||
123d6c72e4 | |||
0ea2135aa5 | |||
48e20cb5f7 | |||
0ffe0b6894 | |||
338156500b | |||
bfd9bd43c9 | |||
283e50e670 | |||
6fb5bb6a5e | |||
5d3bef32ca | |||
3ff26d5cfe | |||
88c1e504b0 | |||
0263677e1f | |||
940455f81c | |||
f541ea659c | |||
938cae1130 | |||
7f73e57c67 | |||
e50ec31351 | |||
a28fa219d7 | |||
8a1ba03bcb | |||
f8e7fb3d48 | |||
e8b7e70ec9 | |||
67c5aa0be9 | |||
e644380160 | |||
c5eb6fe6fb | |||
ae2ef324f2 | |||
d0337da8ea | |||
17b30b2ae2 | |||
5e17d53c7f | |||
22e0527502 | |||
ca3c6c5e8a | |||
4bd30f5e72 | |||
3cc26b15a1 | |||
9673dac22b | |||
0426149580 | |||
fce5c57548 | |||
4ee5264e24 | |||
101ca60b2b | |||
f28a0aa666 | |||
997bf91442 | |||
5a1de15332 | |||
42f8ec5b14 | |||
2fc1b99911 | |||
3a923060ce | |||
0985adfd74 | |||
59d628208b | |||
5c5699bba5 | |||
67e0214fa5 | |||
e17b6804a7 | |||
96e36f0604 | |||
a99858c64d | |||
ba50765c30 | |||
d7f6b36990 | |||
4439666e67 | |||
23febc6d94 | |||
fab4a7a602 | |||
092d2f8917 | |||
e9fb566c07 | |||
5a34e8fd7c | |||
fcfb2cfc3d | |||
e93b9560b5 | |||
8e4438b375 | |||
eaa5ce4467 | |||
c86c719e1a | |||
b30b88716e | |||
c3e6b1aa8a | |||
6d0ea13f97 | |||
2a0018e730 | |||
2be583ad4d | |||
c988b4d213 | |||
3bc02b9662 | |||
166f872712 | |||
8f2c485c92 | |||
613f2fc447 | |||
b1f486518e | |||
f3c72f4f08 | |||
05cd30ac06 | |||
34adcec616 | |||
f9f46609ee | |||
b5bdfa6c2e | |||
ea4ef1655b | |||
7eb61a28be | |||
92b913ca37 | |||
6961a39cd2 |
16
.clang-format
Normal file
16
.clang-format
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Chromium
|
||||
IndentWidth: 4
|
||||
AlignConsecutiveMacros: false
|
||||
AlignConsecutiveAssignments: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeBraces: Custom
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
ColumnLimit: 140
|
||||
Cpp11BracedListStyle: false
|
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.
|
||||
|
||||
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 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.
|
||||
- type: dropdown
|
||||
@ -25,9 +25,16 @@ body:
|
||||
- Other
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Version of PolyMC
|
||||
description: The version of PolyMC used in the bug report.
|
||||
placeholder: PolyMC 1.2.2
|
||||
label: Version of Prism Launcher
|
||||
description: The version of Prism Launcher used in the bug report.
|
||||
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:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: PolyMC Matrix Support Room
|
||||
url: https://matrix.to/#/#support:polymc.org
|
||||
- name: Prism Launcher Matrix Support Room
|
||||
url: https://matrix.to/#/#prism-support:matrix.org
|
||||
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
|
||||
attributes:
|
||||
value: |
|
||||
### Use this form to suggest a larger change for PolyMC.
|
||||
### Use this form to suggest a larger change for Prism Launcher.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Goal
|
||||
@ -18,7 +18,7 @@ body:
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: |
|
||||
Introduce the topic. If this is a not-well-known section of PolyMC, a detailed explanation of the background is recommended.
|
||||
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:
|
||||
- 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!).
|
||||
|
8
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
8
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
@ -5,25 +5,25 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### Use this form to suggest a feature for PolyMC.
|
||||
### Use this form to suggest a feature for Prism Launcher.
|
||||
- type: input
|
||||
attributes:
|
||||
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.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
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.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
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.
|
||||
validations:
|
||||
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
|
2
.github/dco.yml
vendored
Normal file
2
.github/dco.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
allowRemediationCommits:
|
||||
individual: true
|
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!
|
||||
-->
|
19
.github/workflows/backport.yml
vendored
19
.github/workflows/backport.yml
vendored
@ -1,19 +0,0 @@
|
||||
name: Backport PR to stable
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ develop ]
|
||||
types: [ closed ]
|
||||
jobs:
|
||||
release_pull_request:
|
||||
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'backport')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Backport PR by cherry-pick-ing
|
||||
uses: Nathanmalnoury/gh-backport-action@master
|
||||
with:
|
||||
pr_branch: 'stable'
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
441
.github/workflows/build.yml
vendored
441
.github/workflows/build.yml
vendored
@ -7,6 +7,17 @@ on:
|
||||
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
||||
type: string
|
||||
default: Debug
|
||||
is_qt_cached:
|
||||
description: Enable Qt caching or not
|
||||
type: string
|
||||
default: true
|
||||
secrets:
|
||||
SPARKLE_ED25519_KEY:
|
||||
description: Private key for signing Sparkle updates
|
||||
required: false
|
||||
CACHIX_AUTH_TOKEN:
|
||||
description: Private token for authenticating against Cachix cache
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -16,20 +27,74 @@ jobs:
|
||||
include:
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 5
|
||||
|
||||
- os: ubuntu-20.04
|
||||
appimage: true
|
||||
qt_ver: 6
|
||||
qt_host: linux
|
||||
qt_arch: ''
|
||||
qt_version: '6.2.4'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-i686"
|
||||
msystem: mingw32
|
||||
name: "Windows-MinGW-w64"
|
||||
msystem: clang64
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-x86_64"
|
||||
msystem: mingw64
|
||||
name: "Windows-MSVC-Legacy"
|
||||
msystem: ''
|
||||
architecture: 'win32'
|
||||
vcvars_arch: 'amd64_x86'
|
||||
qt_ver: 5
|
||||
qt_host: windows
|
||||
qt_arch: 'win32_msvc2019'
|
||||
qt_version: '5.15.2'
|
||||
qt_modules: ''
|
||||
qt_tools: 'tools_openssl_x86'
|
||||
|
||||
- os: macos-11
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC"
|
||||
msystem: ''
|
||||
architecture: 'x64'
|
||||
vcvars_arch: 'amd64'
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: ''
|
||||
qt_version: '6.4.2'
|
||||
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.4.2'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
|
||||
- os: macos-12
|
||||
name: macOS
|
||||
macosx_deployment_target: 10.15
|
||||
qt_ver: 6
|
||||
qt_host: mac
|
||||
qt_arch: ''
|
||||
qt_version: '6.3.0'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
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 }}
|
||||
|
||||
@ -40,6 +105,7 @@ jobs:
|
||||
INSTALL_APPIMAGE_DIR: "install-appdir"
|
||||
BUILD_DIR: "build"
|
||||
CCACHE_VAR: ""
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
||||
|
||||
steps:
|
||||
##
|
||||
@ -51,29 +117,39 @@ jobs:
|
||||
submodules: 'true'
|
||||
|
||||
- name: 'Setup MSYS2'
|
||||
if: runner.os == 'Windows'
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ${{ matrix.msystem }}
|
||||
update: true
|
||||
install: >-
|
||||
git
|
||||
mingw-w64-x86_64-binutils
|
||||
pacboy: >-
|
||||
toolchain:p
|
||||
cmake:p
|
||||
extra-cmake-modules:p
|
||||
ninja:p
|
||||
qt5:p
|
||||
qt6-base:p
|
||||
qt6-svg:p
|
||||
qt6-imageformats:p
|
||||
quazip-qt6:p
|
||||
ccache:p
|
||||
nsis:p
|
||||
qt6-5compat: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
|
||||
if: runner.os != 'Windows' && inputs.build_type == 'Debug'
|
||||
uses: hendrikmuhs/ccache-action@v1.2.1
|
||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||
uses: hendrikmuhs/ccache-action@v1.2.5
|
||||
with:
|
||||
key: ${{ matrix.os }}-${{ matrix.appimage }}
|
||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||
|
||||
- name: Setup ccache (Windows)
|
||||
if: runner.os == 'Windows' && inputs.build_type == 'Debug'
|
||||
- name: Setup ccache (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
|
||||
@ -88,14 +164,14 @@ jobs:
|
||||
run: |
|
||||
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
|
||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||
uses: actions/cache@v3.0.11
|
||||
with:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-${{ matrix.msystem }}
|
||||
key: ${{ matrix.os }}-mingw-w64
|
||||
restore-keys: |
|
||||
${{ matrix.os }}-${{ matrix.msystem }}
|
||||
${{ matrix.os }}-mingw-w64
|
||||
|
||||
- name: Set short version
|
||||
shell: bash
|
||||
@ -103,25 +179,59 @@ jobs:
|
||||
ver_short=`git rev-parse --short HEAD`
|
||||
echo "VERSION=$ver_short" >> $GITHUB_ENV
|
||||
|
||||
- name: Install Qt (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew update
|
||||
brew install qt@5 ninja
|
||||
|
||||
- name: Update Qt (AppImage)
|
||||
if: runner.os == 'Linux' && matrix.appimage == true
|
||||
run: |
|
||||
sudo add-apt-repository ppa:savoury1/qt-5-15
|
||||
|
||||
- name: Install Qt (Linux)
|
||||
- name: Install Dependencies (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 ninja-build
|
||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc
|
||||
|
||||
- name: Install Dependencies (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew update
|
||||
brew install ninja extra-cmake-modules
|
||||
|
||||
- name: Install Qt (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
run: |
|
||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||
|
||||
- name: Install host Qt (Windows MSVC arm64)
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
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:
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: ${{ matrix.qt_host }}
|
||||
target: 'desktop'
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
|
||||
- name: Install MSVC (Windows MSVC)
|
||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
vsversion: 2022
|
||||
arch: ${{ matrix.vcvars_arch }}
|
||||
|
||||
- name: Prepare AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.appimage == true
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
run: |
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
||||
@ -129,25 +239,50 @@ jobs:
|
||||
|
||||
${{ 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
|
||||
##
|
||||
|
||||
- name: Configure CMake (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DQt5_DIR=/usr/local/opt/qt@5 -DCMAKE_PREFIX_PATH=/usr/local/opt/qt@5 -DLauncher_BUILD_PLATFORM=macOS -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -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)
|
||||
if: runner.os == 'Windows'
|
||||
- name: Configure CMake (macOS-Legacy)
|
||||
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}
|
||||
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 }} -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)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
##
|
||||
# BUILD
|
||||
@ -158,12 +293,37 @@ jobs:
|
||||
run: |
|
||||
cmake --build ${{ env.BUILD_DIR }}
|
||||
|
||||
- name: Build (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
- name: Build (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
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
|
||||
##
|
||||
|
||||
- name: Test
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
ctest -E "^example64|example$" --test-dir build --output-on-failure
|
||||
|
||||
- name: Test (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
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
|
||||
##
|
||||
@ -174,70 +334,106 @@ jobs:
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
chmod +x "PolyMC.app/Contents/MacOS/polymc"
|
||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PolyMC.app/Contents/MacOS/polymc"
|
||||
tar -czf ../PolyMC.tar.gz *
|
||||
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||
mv "PrismLauncher.app" "Prism Launcher.app"
|
||||
tar -czf ../PrismLauncher.tar.gz *
|
||||
|
||||
- name: Package (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
- name: Make Sparkle signature (macOS)
|
||||
if: matrix.name == 'macOS'
|
||||
run: |
|
||||
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
||||
brew install openssl@3
|
||||
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
||||
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||
rm ed25519-priv.pem
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
### Artifact Information :information_source:
|
||||
- :memo: Sparkle Signature (ed25519): \`$signature\`
|
||||
EOF
|
||||
else
|
||||
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||
### Artifact Information :information_source:
|
||||
- :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
|
||||
EOF
|
||||
fi
|
||||
|
||||
- name: Package (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }}
|
||||
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
if [ "${{ matrix.msystem }}" == "mingw32" ]; then
|
||||
cp /mingw32/bin/libcrypto-1_1.dll /mingw32/bin/libssl-1_1.dll ./
|
||||
elif [ "${{ matrix.msystem }}" == "mingw64" ]; then
|
||||
cp /mingw64/bin/libcrypto-1_1-x64.dll /mingw64/bin/libssl-1_1-x64.dll ./
|
||||
fi
|
||||
- name: Package (Windows MSVC)
|
||||
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
||||
|
||||
- name: Package (Windows, portable)
|
||||
if: runner.os == 'Windows'
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
if ("${{ matrix.qt_ver }}" -eq "5")
|
||||
{
|
||||
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
||||
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
||||
}
|
||||
|
||||
- name: Package (Windows MinGW-w64, portable)
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
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
|
||||
|
||||
- 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
|
||||
|
||||
- name: Package (Windows, installer)
|
||||
if: runner.os == 'Windows'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
makensis -NOCD "-DVERSION=${{ env.VERSION }}" "-DMUI_ICON=${{ github.workspace }}/program_info/polymc.ico" "-XOutFile ${{ github.workspace }}/PolyMC-Setup.exe" "${{ github.workspace }}/program_info/win_install.nsi"
|
||||
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||
|
||||
- name: Package (Linux)
|
||||
if: runner.os == 'Linux' && matrix.appimage != true
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ 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)
|
||||
if: runner.os == 'Linux' && matrix.appimage != true
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||
|
||||
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||
tar -czf ../PolyMC-portable.tar.gz *
|
||||
tar -czf ../PrismLauncher-portable.tar.gz *
|
||||
|
||||
- name: Package AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.appimage == true
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
shell: bash
|
||||
run: |
|
||||
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
|
||||
|
||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
|
||||
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||
|
||||
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
|
||||
|
||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
||||
|
||||
cp -r /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/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"
|
||||
@ -245,7 +441,7 @@ jobs:
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
||||
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
|
||||
@ -255,49 +451,134 @@ jobs:
|
||||
if: runner.os == 'macOS'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC.tar.gz
|
||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher.tar.gz
|
||||
|
||||
- name: Upload binary zip (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: ${{ env.INSTALL_DIR }}/**
|
||||
|
||||
- name: Upload binary zip (Windows, portable)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
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 }}/**
|
||||
|
||||
- name: Upload installer (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC-Setup.exe
|
||||
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher-Setup.exe
|
||||
|
||||
- name: Upload binary tarball (Linux)
|
||||
if: runner.os == 'Linux' && matrix.appimage != true
|
||||
- name: Upload binary tarball (Linux, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC.tar.gz
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable)
|
||||
if: runner.os == 'Linux' && matrix.appimage != true
|
||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PolyMC-portable.tar.gz
|
||||
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher-portable.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, Qt 6)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver !=5
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable, Qt 6)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher-portable.tar.gz
|
||||
|
||||
- name: Upload AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.appimage == true
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
snap:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Set short version
|
||||
shell: bash
|
||||
if: inputs.build_type == 'Debug'
|
||||
run: |
|
||||
ver_short=`git rev-parse --short HEAD`
|
||||
echo "VERSION=$ver_short" >> $GITHUB_ENV
|
||||
- name: Package Snap (Linux)
|
||||
id: snapcraft
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: snapcore/action-build@v1
|
||||
- name: Upload Snap (Linux)
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: prismlauncher_${{ env.VERSION }}_amd64.snap
|
||||
path: ${{ steps.snapcraft.outputs.snap }}
|
||||
|
||||
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@v4
|
||||
with:
|
||||
bundle: "Prism Launcher.flatpak"
|
||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||
cache-key: flatpak-${{ github.sha }}-x86_64
|
||||
|
||||
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@v18
|
||||
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
|
61
.github/workflows/pr-comment.yml
vendored
61
.github/workflows/pr-comment.yml
vendored
@ -1,61 +0,0 @@
|
||||
name: Comment on pull request
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Build Application']
|
||||
types: [completed]
|
||||
jobs:
|
||||
pr_comment:
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v5
|
||||
with:
|
||||
# This snippet is public-domain, taken from
|
||||
# https://github.com/oprypin/nightly.link/blob/master/.github/workflows/pr-comment.yml
|
||||
script: |
|
||||
async function upsertComment(owner, repo, issue_number, purpose, body) {
|
||||
const {data: comments} = await github.rest.issues.listComments(
|
||||
{owner, repo, issue_number});
|
||||
|
||||
const marker = `<!-- bot: ${purpose} -->`;
|
||||
body = marker + "\n" + body;
|
||||
|
||||
const existing = comments.filter((c) => c.body.includes(marker));
|
||||
if (existing.length > 0) {
|
||||
const last = existing[existing.length - 1];
|
||||
core.info(`Updating comment ${last.id}`);
|
||||
await github.rest.issues.updateComment({
|
||||
owner, repo,
|
||||
body,
|
||||
comment_id: last.id,
|
||||
});
|
||||
} else {
|
||||
core.info(`Creating a comment in issue / PR #${issue_number}`);
|
||||
await github.rest.issues.createComment({issue_number, body, owner, repo});
|
||||
}
|
||||
}
|
||||
|
||||
const {owner, repo} = context.repo;
|
||||
const run_id = ${{github.event.workflow_run.id}};
|
||||
|
||||
const pull_requests = ${{ toJSON(github.event.workflow_run.pull_requests) }};
|
||||
if (!pull_requests.length) {
|
||||
return core.error("This workflow doesn't match any pull requests!");
|
||||
}
|
||||
|
||||
const artifacts = await github.paginate(
|
||||
github.rest.actions.listWorkflowRunArtifacts, {owner, repo, run_id});
|
||||
if (!artifacts.length) {
|
||||
return core.error(`No artifacts found`);
|
||||
}
|
||||
let body = `Download the artifacts for this pull request:\n`;
|
||||
for (const art of artifacts) {
|
||||
body += `\n* [${art.name}.zip](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||
}
|
||||
|
||||
core.info("Review thread message body:", body);
|
||||
|
||||
for (const pr of pull_requests) {
|
||||
await upsertComment(owner, repo, pr.number,
|
||||
"nightly-link", body);
|
||||
}
|
10
.github/workflows/trigger_builds.yml
vendored
10
.github/workflows/trigger_builds.yml
vendored
@ -3,22 +3,22 @@ name: Build Application
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'stable'
|
||||
- 'renovate/**'
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- '**.nix'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- '**.nix'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@ -28,3 +28,7 @@ jobs:
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
build_type: Debug
|
||||
is_qt_cached: true
|
||||
secrets:
|
||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
|
75
.github/workflows/trigger_release.yml
vendored
75
.github/workflows/trigger_release.yml
vendored
@ -12,6 +12,9 @@ jobs:
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
build_type: Release
|
||||
is_qt_cached: false
|
||||
secrets:
|
||||
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||
|
||||
create_release:
|
||||
needs: build_release
|
||||
@ -23,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
path: 'PolyMC-source'
|
||||
path: 'PrismLauncher-source'
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Grab and store version
|
||||
@ -32,22 +35,39 @@ jobs:
|
||||
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
||||
- name: Package artifacts properly
|
||||
run: |
|
||||
mv ${{ github.workspace }}/PolyMC-source PolyMC-${{ env.VERSION }}
|
||||
mv PolyMC-Linux-Portable*/PolyMC-portable.tar.gz PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||
mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
||||
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
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
|
||||
ARCH="$(echo -n ${d} | cut -d '-' -f 3)"
|
||||
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
|
||||
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
|
||||
INST="$(echo -n ${d} | grep -o Setup || true)"
|
||||
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
||||
NAME="PolyMC-Windows-${ARCH}"
|
||||
NAME="PrismLauncher-Windows-MSVC"
|
||||
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
||||
test -z "${ARM64}" || NAME="${NAME}-arm64"
|
||||
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" *
|
||||
cd ..
|
||||
done
|
||||
@ -59,18 +79,27 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
name: PolyMC ${{ env.VERSION }}
|
||||
name: Prism Launcher ${{ env.VERSION }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: |
|
||||
PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
PolyMC-Windows-i686-${{ env.VERSION }}.zip
|
||||
PolyMC-Windows-i686-Portable-${{ env.VERSION }}.zip
|
||||
PolyMC-Windows-i686-Setup-${{ env.VERSION }}.exe
|
||||
PolyMC-Windows-x86_64-${{ env.VERSION }}.zip
|
||||
PolyMC-Windows-x86_64-Portable-${{ env.VERSION }}.zip
|
||||
PolyMC-Windows-x86_64-Setup-${{ env.VERSION }}.exe
|
||||
PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
||||
PolyMC-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
||||
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
|
||||
PrismLauncher-Windows-MSVC-Legacy-${{ env.VERSION }}.zip
|
||||
PrismLauncher-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip
|
||||
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
|
||||
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
|
||||
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
|
||||
|
15
.github/workflows/winget.yml
vendored
Normal file
15
.github/workflows/winget.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
name: Publish to WinGet
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v1
|
||||
with:
|
||||
identifier: PrismLauncher.PrismLauncher
|
||||
version: ${{ github.event.release.tag_name }}
|
||||
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -15,7 +15,6 @@ CMakeLists.txt.user.*
|
||||
/.settings
|
||||
/.idea
|
||||
/.vscode
|
||||
.clang-format
|
||||
cmake-build-*/
|
||||
Debug
|
||||
|
||||
@ -48,3 +47,6 @@ result/
|
||||
# Flatpak
|
||||
.flatpak-builder
|
||||
flatbuild
|
||||
|
||||
# Snap
|
||||
*.snap
|
||||
|
20
.gitmodules
vendored
20
.gitmodules
vendored
@ -1,8 +1,18 @@
|
||||
[submodule "depends/libnbtplusplus"]
|
||||
path = libraries/libnbtplusplus
|
||||
url = https://github.com/PolyMC/libnbtplusplus.git
|
||||
pushurl = git@github.com:PolyMC/libnbtplusplus.git
|
||||
|
||||
[submodule "libraries/quazip"]
|
||||
path = libraries/quazip
|
||||
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
|
||||
|
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
|
52
BUILD.md
52
BUILD.md
@ -1,5 +1,53 @@
|
||||
# Build Instructions
|
||||
|
||||
Build instructions are available on [the website](https://polymc.org/wiki/development/build-instructions/).
|
||||
Full build instructions will be 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 will be able to do so [here](https://github.com/PrismLauncher/website/blob/master/src/wiki/development/build-instructions.md).
|
||||
|
||||
## Getting the source
|
||||
|
||||
Clone the source code using git, and grab all the submodules. This is generic for all platforms you want to build on.
|
||||
```
|
||||
git clone --recursive https://github.com/PrismLauncher/PrismLauncher
|
||||
cd PrismLauncher
|
||||
```
|
||||
|
||||
## Linux
|
||||
|
||||
This guide will mostly mention dependant packages by their Debian naming and commands are done by a user in the sudoers file.
|
||||
### Dependencies
|
||||
|
||||
- A C++ compiler capable of building C++17 code (can be found in the package `build-essential`).
|
||||
- Qt Development tools 5.12 or newer (on Debian 11 or Debian-based distributions, `qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5`).
|
||||
- `cmake` 3.15 or newer.
|
||||
- `extra-cmake-modules`.
|
||||
- zlib (`zlib1g-dev` on Debian 11 or Debian-based distributions).
|
||||
- Java Development Kit (Java JDK) (`openjdk-17-jdk` on Debian 11 or Debian-based distributions).
|
||||
- Mesa GL headers (`libgl1-mesa-dev` on Debian 11 or Debian-based distributions).
|
||||
- (Optional) `scdoc` to generate man pages.
|
||||
|
||||
In conclusion, to check if all you need is installed (including optional):
|
||||
|
||||
```
|
||||
sudo apt install build-essential qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5 cmake extra-cmake-modules zlib1g-dev openjdk-17-jdk libgl1-mesa-dev scdoc
|
||||
```
|
||||
|
||||
### Compiling
|
||||
#### Building and installing on the system
|
||||
This is usually the suggested way to build the client.
|
||||
|
||||
```
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="/usr" -DENABLE_LTO=ON
|
||||
cmake --build build -j$(nproc)
|
||||
sudo cmake --install build
|
||||
```
|
||||
|
||||
#### Building a portable binary
|
||||
|
||||
```
|
||||
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=install
|
||||
cmake --build build -j$(nproc)
|
||||
cmake --install build
|
||||
cmake --install build --component portable
|
||||
```
|
||||
|
||||
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).
|
||||
|
318
CMakeLists.txt
318
CMakeLists.txt
@ -1,19 +1,12 @@
|
||||
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)
|
||||
include(CTest)
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||
if(IS_IN_SOURCE_BUILD)
|
||||
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
|
||||
endif()
|
||||
|
||||
|
||||
##################################### Set CMake options #####################################
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
@ -31,12 +24,54 @@ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
|
||||
######## Set compiler flags ########
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED true)
|
||||
set(CMAKE_C_STANDARD_REQUIRED true)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
include(GenerateExportHeader)
|
||||
set(CMAKE_CXX_FLAGS "-Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
|
||||
if(UNIX AND APPLE)
|
||||
set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
||||
if(MSVC)
|
||||
# /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag
|
||||
# /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()
|
||||
|
||||
# Fix build with Qt 5.13
|
||||
@ -44,9 +79,11 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
|
||||
|
||||
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(CMAKE_CXX_FLAGS_DEBUG "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
|
||||
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||
|
||||
@ -54,30 +91,59 @@ if(ENABLE_LTO)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
||||
|
||||
if(ipo_supported AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel"))
|
||||
message(STATUS "IPO / LTO enabled")
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
elseif(ipo_supported)
|
||||
message(STATUS "Not enabling IPO / LTO on debug builds")
|
||||
if(ipo_supported)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE)
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
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()
|
||||
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(BUILD_TESTING "Build the testing tree." ON)
|
||||
|
||||
find_package(ECM QUIET NO_MODULE)
|
||||
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(ECMAddTests)
|
||||
if(BUILD_TESTING)
|
||||
enable_testing()
|
||||
endif()
|
||||
|
||||
##################################### Set Application options #####################################
|
||||
|
||||
######## 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_OPEN_URL "https://polymc.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||
set(Launcher_HELP_URL "https://polymc.org/wiki/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_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
||||
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||
set(Launcher_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(Launcher_VERSION_MAJOR 1)
|
||||
set(Launcher_VERSION_MINOR 3)
|
||||
set(Launcher_VERSION_HOTFIX 0)
|
||||
set(Launcher_VERSION_MAJOR 6)
|
||||
set(Launcher_VERSION_MINOR 2)
|
||||
|
||||
# Build number
|
||||
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||
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.
|
||||
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.")
|
||||
@ -86,89 +152,133 @@ set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the plat
|
||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||
|
||||
# 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
|
||||
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
||||
|
||||
# MSA Client ID
|
||||
set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
||||
|
||||
# CurseForge API Key
|
||||
# CHANGE THIS IF YOU FORK THIS PROJECT!
|
||||
set(Launcher_CURSEFORGE_API_KEY "$2a$10$1Oqr2MX3O4n/ilhFGc597u8tfI3L2Hyr9/rtWDAMRjghSQV2QUuxq" CACHE STRING "CurseForge API Key")
|
||||
|
||||
# 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
|
||||
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
|
||||
set(Launcher_MATRIX_URL "https://matrix.to/#/#polymc:matrix.org" CACHE STRING "URL to the Matrix Space")
|
||||
set(Launcher_MATRIX_URL "https://matrix.to/#/#prismlauncher:matrix.org" CACHE STRING "URL to the Matrix Space")
|
||||
|
||||
# Discord URL
|
||||
set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
|
||||
|
||||
|
||||
set(Launcher_DISCORD_URL "https://discord.gg/prismlauncher" CACHE STRING "URL for the Discord guild.")
|
||||
|
||||
# Subreddit URL
|
||||
set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PolyMCLauncher/" CACHE STRING "URL for the subreddit.")
|
||||
set(Launcher_SUBREDDIT_URL "https://www.reddit.com/r/PrismLauncher/" CACHE STRING "URL for the subreddit.")
|
||||
|
||||
# Builds
|
||||
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")
|
||||
|
||||
# API Keys
|
||||
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
||||
# of these platforms, please change these API keys beforehand.
|
||||
# Be aware that if you were to use these API keys for malicious purposes they might get revoked, which might cause
|
||||
# breakage to thousands of users.
|
||||
# If you don't plan to use these features of this software, you can just remove these values.
|
||||
|
||||
# By using this key in your builds you accept the terms of use laid down in
|
||||
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
|
||||
set(Launcher_MSA_CLIENT_ID "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
|
||||
# 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.
|
||||
# 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
|
||||
include(GetGitRevisionDescription)
|
||||
git_get_exact_tag(Launcher_GIT_TAG)
|
||||
get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
|
||||
|
||||
message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
|
||||
message(STATUS "Git tag: ${Launcher_GIT_TAG}")
|
||||
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
||||
|
||||
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||
string(TIMESTAMP TODAY "%Y-%m-%d")
|
||||
set(Launcher_RELEASE_TIMESTAMP "${TODAY}")
|
||||
|
||||
#### Custom target to just print the version.
|
||||
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
|
||||
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
|
||||
set(Launcher_BUILD_TIMESTAMP "${TODAY}")
|
||||
|
||||
################################ 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
|
||||
include(QtVersionlessBackport)
|
||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||
set(QT_VERSION_MAJOR 5)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
find_package(QuaZip-Qt5 1.3)
|
||||
find_package(QuaZip-Qt5 1.3 QUIET)
|
||||
endif()
|
||||
if (NOT QuaZip-Qt5_FOUND)
|
||||
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
|
||||
set(FORCE_BUNDLED_QUAZIP 1)
|
||||
endif()
|
||||
|
||||
# Qt 6 sets these by default. Notably causes Windows APIs to use UNICODE strings.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
|
||||
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
|
||||
set(QT_VERSION_MAJOR 6)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat)
|
||||
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
find_package(QuaZip-Qt6 1.3 QUIET)
|
||||
endif()
|
||||
if (NOT QuaZip-Qt6_FOUND)
|
||||
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
|
||||
set(FORCE_BUNDLED_QUAZIP 1)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
|
||||
endif()
|
||||
|
||||
# The Qt5 cmake files don't provide its install paths, so ask qmake.
|
||||
include(QMakeQuery)
|
||||
query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
|
||||
query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
|
||||
query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR)
|
||||
query_qmake(QT_INSTALL_LIBEXECS QT_LIBEXECS_DIR)
|
||||
query_qmake(QT_HOST_DATA QT_DATA_DIR)
|
||||
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
|
||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||
include(ECMQueryQt)
|
||||
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
|
||||
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
|
||||
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
|
||||
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
|
||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||
# Find toml++
|
||||
find_package(tomlplusplus 3.2.0 QUIET)
|
||||
|
||||
# Find ghc_filesystem
|
||||
find_package(ghc_filesystem QUIET)
|
||||
endif()
|
||||
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
|
||||
####################################### 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)
|
||||
|
||||
####################################### Install layout #######################################
|
||||
@ -182,6 +292,7 @@ if(UNIX AND APPLE)
|
||||
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||
set(FRAMEWORK_DEST_DIR "${Launcher_Name}.app/Contents/Frameworks")
|
||||
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
|
||||
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
|
||||
|
||||
@ -189,17 +300,23 @@ if(UNIX AND APPLE)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
||||
|
||||
# Mac bundle settings
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
|
||||
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_GUI_IDENTIFIER "org.polymc.${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
||||
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.prismlauncher.${Launcher_Name}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2021-2022 ${Launcher_Copyright}")
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}")
|
||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=")
|
||||
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_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||
|
||||
# directories to look for dependencies
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR})
|
||||
|
||||
# install as bundle
|
||||
set(INSTALL_BUNDLE "full")
|
||||
@ -208,13 +325,11 @@ if(UNIX AND APPLE)
|
||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||
|
||||
elseif(UNIX)
|
||||
include(KDEInstallDirs)
|
||||
|
||||
set(BINARY_DEST_DIR "bin")
|
||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||
set(JARS_DEST_DIR "share/jars")
|
||||
set(LAUNCHER_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
|
||||
set(LAUNCHER_METAINFO_DEST_DIR "share/metainfo" CACHE STRING "Path to the metainfo directory")
|
||||
set(LAUNCHER_ICON_DEST_DIR "share/icons/hicolor/scalable/apps" CACHE STRING "Path to the scalable icon directory")
|
||||
set(LAUNCHER_MAN_DEST_DIR "share/man/man6" CACHE STRING "Path to the man page directory")
|
||||
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
|
||||
|
||||
# install as bundle with no dependencies included
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
@ -222,13 +337,14 @@ elseif(UNIX)
|
||||
# Set RPATH
|
||||
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
||||
|
||||
# jars path is determined on runtime, relative to "Application root path", generally /usr or the root of the portable bundle
|
||||
set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_JARS_LOCATION=${JARS_DEST_DIR}")
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${KDE_INSTALL_APPDIR})
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR})
|
||||
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_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_ManPage} DESTINATION ${LAUNCHER_MAN_DEST_DIR} RENAME "${Launcher_APP_BINARY_NAME}.6")
|
||||
if(Launcher_ManPage)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||
endif()
|
||||
|
||||
# Install basic runner script if component "portable" is selected
|
||||
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
|
||||
@ -267,7 +383,32 @@ 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/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)
|
||||
message(STATUS "Using bundled QuaZip")
|
||||
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
||||
@ -277,16 +418,31 @@ else()
|
||||
message(STATUS "Using system QuaZip")
|
||||
endif()
|
||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||
add_subdirectory(libraries/classparser) # class parser library
|
||||
add_subdirectory(libraries/optional-bare)
|
||||
add_subdirectory(libraries/tomlc99) # toml parser
|
||||
if(NOT tomlplusplus_FOUND)
|
||||
message(STATUS "Using bundled tomlplusplus")
|
||||
add_subdirectory(libraries/tomlplusplus) # toml parser
|
||||
else()
|
||||
message(STATUS "Using system tomlplusplus")
|
||||
endif()
|
||||
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
||||
add_subdirectory(libraries/gamemode)
|
||||
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
|
||||
if (NOT ghc_filesystem_FOUND)
|
||||
message(STATUS "Using bundled ghc_filesystem")
|
||||
set(GHC_FILESYSTEM_WITH_INSTALL OFF) # Workaround ghc::filesystem bug
|
||||
add_subdirectory(libraries/filesystem) # Implementation of std::filesystem for old C++, for usage in old macOS
|
||||
add_library(ghcFilesystem::ghc_filesystem ALIAS ghc_filesystem)
|
||||
else()
|
||||
message(STATUS "Using system ghc_filesystem")
|
||||
endif()
|
||||
|
||||
############################### Built Artifacts ###############################
|
||||
|
||||
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.
|
||||
add_subdirectory(launcher)
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
This is a modified version of the Contributor Covenant.
|
||||
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
|
||||
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).
|
||||
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
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
||||
|
63
CONTRIBUTING.md
Normal file
63
CONTRIBUTING.md
Normal file
@ -0,0 +1,63 @@
|
||||
# Contributions Guidelines
|
||||
|
||||
## Code formatting
|
||||
|
||||
Try to follow the existing formatting.
|
||||
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
||||
|
||||
In general, in order of importance:
|
||||
|
||||
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
||||
- Prefer readability over dogma.
|
||||
- Keep to the existing formatting.
|
||||
- Indent with 4 space unless it's in a submodule.
|
||||
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
|
||||
|
||||
## Signing your work
|
||||
|
||||
In an effort to ensure that the code you contribute is actually compatible with the licenses in this codebase, we require you to sign-off all your contributions.
|
||||
|
||||
This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message:
|
||||
|
||||
```
|
||||
<commit message>
|
||||
|
||||
Signed-off-by: Author name <Author email>
|
||||
```
|
||||
|
||||
By signing off your work, you agree to the terms below:
|
||||
|
||||
```
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(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
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
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.
|
||||
|
||||
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-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits
|
416
COPYING.md
416
COPYING.md
@ -1,64 +1,121 @@
|
||||
# PolyMC
|
||||
## Prism Launcher
|
||||
|
||||
Copyright (C) 2012-2021 MultiMC Contributors
|
||||
Copyright (C) 2021-2022 PolyMC Contributors
|
||||
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 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.
|
||||
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/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Launcher (https://github.com/MultiMC/Launcher)
|
||||
Copyright 2012-2021 MultiMC Contributors
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Copyright 2013-2021 MultiMC Contributors
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
# MinGW runtime (Windows)
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Copyright (c) 2012 MinGW.org project
|
||||
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.
|
||||
|
||||
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:
|
||||
## PolyMC
|
||||
|
||||
The above copyright notice, this permission notice and the below disclaimer
|
||||
shall be included in all copies or substantial portions of the Software.
|
||||
PolyMC - Minecraft Launcher
|
||||
Copyright (C) 2021-2022 PolyMC Contributors
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
# Qt 5
|
||||
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.
|
||||
|
||||
Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
Contact: http://www.qt-project.org/legal
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Licensed under LGPL v2.1
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
# libnbt++
|
||||
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.
|
||||
|
||||
## MinGW-w64 runtime (Windows)
|
||||
|
||||
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
|
||||
|
||||
This license has been certified as open source. It has also been designated
|
||||
as GPL compatible by the Free Software Foundation (FSF).
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions in source code must retain the accompanying copyright
|
||||
notice, this list of conditions, and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the accompanying
|
||||
copyright notice, this list of conditions, and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
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.
|
||||
|
||||
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.
|
||||
Contact: https://www.qt.io/licensing
|
||||
|
||||
Licensed under LGPL v3
|
||||
|
||||
## libnbt++
|
||||
|
||||
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
||||
Copyright (C) 2013, 2015 ljfa-ag
|
||||
@ -76,7 +133,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
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 Olaf Schmidt <ojschmidt@kde.org>
|
||||
@ -99,7 +156,7 @@
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
|
||||
# Hoedown
|
||||
## Hoedown
|
||||
|
||||
Copyright (c) 2008, Natacha Porté
|
||||
Copyright (c) 2011, Vicent Martí
|
||||
@ -117,7 +174,7 @@
|
||||
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
|
||||
## Batch icon set
|
||||
|
||||
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
|
||||
@ -133,7 +190,7 @@
|
||||
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
||||
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/),
|
||||
with Reserved Font Name Material Design Icons.
|
||||
@ -144,76 +201,82 @@
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
# Quazip
|
||||
## Quazip
|
||||
|
||||
Copyright (C) 2005-2011 Sergey A. Tachenov
|
||||
Copyright (C) 2005-2021 Sergey A. Tachenov
|
||||
|
||||
This program 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 2 of the License, or (at
|
||||
your option) any later version.
|
||||
The QuaZip library is licensed under the GNU Lesser General Public
|
||||
License V2.1 plus a static linking exception.
|
||||
|
||||
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 Lesser
|
||||
General Public License for more details.
|
||||
The original ZIP/UNZIP package (MiniZip) is copyrighted by Gilles
|
||||
Vollant and contributors, see quazip/(un)zip.h files for details.
|
||||
Basically it's the zlib license.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
STATIC LINKING EXCEPTION
|
||||
|
||||
The copyright holders give you permission to link this library with
|
||||
independent modules to produce an executable, regardless of the license
|
||||
terms of these independent modules, and to copy and distribute the
|
||||
resulting executable under terms of your choice, provided that you also
|
||||
meet, for each linked independent module, the terms and conditions of
|
||||
the license of that module. An independent module is a module which is
|
||||
not derived from or based on this library. If you modify this library,
|
||||
you must extend this exception to your version of the library.
|
||||
|
||||
See COPYING file for the full LGPL text.
|
||||
|
||||
Original ZIP package is copyrighted by Gilles Vollant, see
|
||||
quazip/(un)zip.h files for details, basically it's zlib license.
|
||||
## launcher (`libraries/launcher`)
|
||||
|
||||
# xz-minidec
|
||||
PolyMC - Minecraft Launcher
|
||||
Copyright (C) 2021-2022 PolyMC Contributors
|
||||
|
||||
XZ decompressor
|
||||
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.
|
||||
|
||||
Authors: Lasse Collin <lasse.collin@tukaani.org>
|
||||
Igor Pavlov <http://7-zip.org/>
|
||||
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.
|
||||
|
||||
This file has been put into the public domain.
|
||||
You can do whatever you want with this file.
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
# ColumnResizer
|
||||
As a special exception, the copyright holders of this library give
|
||||
you permission to link this library with independent modules to
|
||||
produce an executable, regardless of the license terms of these
|
||||
independent modules, and to copy and distribute the resulting
|
||||
executable under terms of your choice, provided that you also meet,
|
||||
for each linked independent module, the terms and conditions of the
|
||||
license of that module. An independent module is a module which is
|
||||
not derived from or based on this library. If you modify this
|
||||
library, you may extend this exception to your version of the
|
||||
library, but you are not obliged to do so. If you do not wish to do
|
||||
so, delete this exception statement from your version.
|
||||
|
||||
Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
All rights reserved.
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
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:
|
||||
Copyright 2013-2021 MultiMC Contributors
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
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
|
||||
|
||||
* 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.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
* The name of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific prior
|
||||
written permission.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
# lionshead
|
||||
## lionshead
|
||||
|
||||
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
||||
translated to C++ laced with Qt.
|
||||
@ -240,60 +303,26 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
# optional-bare
|
||||
|
||||
Code from https://github.com/martinmoene/optional-bare/
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# tomlc99
|
||||
## tomlplusplus
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 CK Tan
|
||||
https://github.com/cktan/tomlc99
|
||||
Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||
|
||||
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:
|
||||
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 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.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
# O2 (Katabasis fork)
|
||||
## O2 (Katabasis fork)
|
||||
|
||||
Copyright (c) 2012, Akos Polster
|
||||
All rights reserved.
|
||||
@ -318,3 +347,96 @@
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
## Gamemode
|
||||
|
||||
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/>.
|
||||
|
133
README.md
133
README.md
@ -1,89 +1,102 @@
|
||||
<p align="center">
|
||||
<img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo"/>
|
||||
<img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo"/>
|
||||
<p align="left">
|
||||
<picture>
|
||||
<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="50%">
|
||||
</picture>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
|
||||
|
||||
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC.
|
||||
If you want to read about why this fork was created, check out [our FAQ page](https://polymc.org/wiki/overview/faq/).
|
||||
<br>
|
||||
|
||||
# Installation
|
||||
|
||||
- All downloads and instructions for PolyMC can be found [here](https://polymc.org/download/)
|
||||
- Last build status: https://github.com/PolyMC/PolyMC/actions
|
||||
|
||||
|
||||
## Development Builds
|
||||
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
|
||||
|
||||
There are per-commit development builds available [here](https://github.com/PolyMC/PolyMC/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
||||
Portable builds are provided for AppImage on Linux, Windows, and macOS.
|
||||
This is a **fork** of the MultiMC Launcher and is not endorsed by MultiMC.
|
||||
|
||||
For Debian and Arch, you can use these packages for the latest development versions:
|
||||
[](https://aur.archlinux.org/packages/polymc-git/)
|
||||
[](https://mpr.makedeb.org/packages/polymc-git)
|
||||
For flatpak, you can use [flathub-beta](https://discourse.flathub.org/t/how-to-use-flathub-beta/2111)
|
||||
|
||||
# Help & Support
|
||||
## Installation
|
||||
|
||||
Feel free to create an issue if you need help. However, you might find it easier to ask in the Discord server.
|
||||
<a href="https://repology.org/project/prismlauncher/versions">
|
||||
<img src="https://repology.org/badge/vertical-allrepos/prismlauncher.svg" alt="Packaging status" align="right">
|
||||
</a>
|
||||
|
||||
[](https://discord.gg/xq7fxrgtMP)
|
||||
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download/).
|
||||
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions).
|
||||
|
||||
For people who don't want to use Discord, we have a Matrix Space which is bridged to the Discord server:
|
||||
### Development Builds
|
||||
|
||||
[](https://matrix.to/#/#polymc:matrix.org)
|
||||
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.
|
||||
|
||||
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:
|
||||
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
|
||||
|
||||
[](https://matrix.to/#/#polymc-development:matrix.org)
|
||||
[](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)
|
||||
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
|
||||
|
||||
we also have a subreddit you can post your issues and suggestions on:
|
||||
[](https://aur.archlinux.org/packages/prismlauncher-git/) [](https://aur.archlinux.org/packages/prismlauncher-qt5-git/) [](https://mpr.makedeb.org/packages/prismlauncher-git) [](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [](https://build.opensuse.org/project/show/home:getchoo) [](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
||||
|
||||
[r/PolyMCLauncher](https://www.reddit.com/r/PolyMCLauncher/)
|
||||
## Community & Support
|
||||
|
||||
# Development
|
||||
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.
|
||||
|
||||
If you want to contribute to PolyMC you might find it useful to join our Discord Server or Matrix Space.
|
||||
#### Join our Discord server:
|
||||
[](https://discord.gg/prismlauncher)
|
||||
|
||||
#### Join our Matrix space:
|
||||
[](https://matrix.to/#/#prismlauncher:matrix.org)
|
||||
|
||||
#### Join our Subreddit:
|
||||
[](https://www.reddit.com/r/PrismLauncher/)
|
||||
|
||||
## Building
|
||||
|
||||
If you want to build PolyMC yourself, check [Build Instructions](https://polymc.org/wiki/development/build-instructions/) for build instructions.
|
||||
|
||||
## Code formatting
|
||||
|
||||
Just follow the existing formatting.
|
||||
|
||||
In general, in order of importance:
|
||||
|
||||
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
||||
- Prefer readability over dogma.
|
||||
- Keep to the existing formatting.
|
||||
- Indent with 4 space unless it's in a submodule.
|
||||
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
|
||||
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
|
||||
|
||||
## Translations
|
||||
|
||||
The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations
|
||||
|
||||
## Download information
|
||||
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)
|
||||
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>
|
||||
|
||||
## Forking/Redistributing/Custom builds policy
|
||||
|
||||
Do whatever you want, we don't care. Just follow the license. If you have any questions about this feel free to ask in an issue.
|
||||
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 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.
|
||||
|
||||
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)
|
||||
|
||||
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 (`""`).
|
||||
|
||||
## 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>
|
||||
|
||||
|
||||
## License
|
||||
|
||||
All launcher code is available under the GPL-3.0-only license.
|
||||
|
||||
[Source for the website](https://github.com/PolyMC/polymc.github.io) is hosted under the AGPL-3.0-or-later License.
|
||||
|
||||

|
||||
|
||||
The logo and related assets are under the CC BY-SA 4.0 license.
|
||||
|
@ -42,12 +42,14 @@ Config::Config()
|
||||
{
|
||||
// Name and copyright
|
||||
LAUNCHER_NAME = "@Launcher_Name@";
|
||||
LAUNCHER_APP_BINARY_NAME = "@Launcher_APP_BINARY_NAME@";
|
||||
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
||||
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
||||
LAUNCHER_GIT = "@Launcher_Git@";
|
||||
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
||||
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
|
||||
|
||||
USER_AGENT = "@Launcher_UserAgent@";
|
||||
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
||||
@ -55,23 +57,38 @@ Config::Config()
|
||||
// Version information
|
||||
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||
VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
|
||||
VERSION_BUILD = @Launcher_VERSION_BUILD@;
|
||||
|
||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
|
||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND"))
|
||||
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
||||
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
||||
|
||||
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||
{
|
||||
VERSION_CHANNEL = QStringLiteral("stable");
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
else if(GIT_REFSPEC.startsWith("refs/heads/"))
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
GIT_TAG = "@Launcher_GIT_TAG@";
|
||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||
|
||||
// Assume that builds outside of Git repos are "stable"
|
||||
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|
||||
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND")
|
||||
|| GIT_REFSPEC == QStringLiteral("")
|
||||
|| GIT_TAG == QStringLiteral("GIT-NOTFOUND"))
|
||||
{
|
||||
GIT_REFSPEC = "refs/heads/stable";
|
||||
GIT_TAG = versionString();
|
||||
}
|
||||
|
||||
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -81,16 +98,15 @@ Config::Config()
|
||||
}
|
||||
else
|
||||
{
|
||||
VERSION_CHANNEL = QObject::tr("unknown");
|
||||
VERSION_CHANNEL = "unknown";
|
||||
}
|
||||
|
||||
VERSION_STR = "@Launcher_VERSION_STRING@";
|
||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
||||
HELP_URL = "@Launcher_HELP_URL@";
|
||||
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
||||
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
||||
CURSEFORGE_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||
META_URL = "@Launcher_META_URL@";
|
||||
|
||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||
@ -100,20 +116,19 @@ Config::Config()
|
||||
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
||||
}
|
||||
|
||||
QString Config::versionString() const
|
||||
{
|
||||
return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
|
||||
}
|
||||
|
||||
QString Config::printableVersionString() const
|
||||
{
|
||||
QString vstr = QString("%1.%2.%3").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR), QString::number(VERSION_HOTFIX));
|
||||
QString vstr = versionString();
|
||||
|
||||
// If the build is not a main release, append the channel
|
||||
if(VERSION_CHANNEL != "stable")
|
||||
if(VERSION_CHANNEL != "stable" && GIT_TAG != vstr)
|
||||
{
|
||||
vstr += "-" + VERSION_CHANNEL;
|
||||
}
|
||||
|
||||
// if a build number is set, also add it to the end
|
||||
if(VERSION_BUILD >= 0)
|
||||
{
|
||||
vstr += "-" + QString::number(VERSION_BUILD);
|
||||
}
|
||||
return vstr;
|
||||
}
|
||||
|
@ -44,21 +44,19 @@ class Config {
|
||||
public:
|
||||
Config();
|
||||
QString LAUNCHER_NAME;
|
||||
QString LAUNCHER_APP_BINARY_NAME;
|
||||
QString LAUNCHER_DISPLAYNAME;
|
||||
QString LAUNCHER_COPYRIGHT;
|
||||
QString LAUNCHER_DOMAIN;
|
||||
QString LAUNCHER_CONFIGFILE;
|
||||
QString LAUNCHER_GIT;
|
||||
QString LAUNCHER_DESKTOPFILENAME;
|
||||
QString LAUNCHER_SVGFILENAME;
|
||||
|
||||
/// The major version number.
|
||||
int VERSION_MAJOR;
|
||||
/// The minor version number.
|
||||
int VERSION_MINOR;
|
||||
/// The hotfix number.
|
||||
int VERSION_HOTFIX;
|
||||
/// The build number.
|
||||
int VERSION_BUILD;
|
||||
|
||||
/**
|
||||
* The version channel
|
||||
@ -71,9 +69,18 @@ class Config {
|
||||
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||
QString BUILD_PLATFORM;
|
||||
|
||||
/// A string containing the build timestamp
|
||||
QString BUILD_DATE;
|
||||
|
||||
/// URL for the updater's channel
|
||||
QString UPDATER_BASE;
|
||||
|
||||
/// The public key used to sign releases for the Sparkle updater appcast
|
||||
QString MAC_SPARKLE_PUB_KEY;
|
||||
|
||||
/// URL for the Sparkle updater's appcast
|
||||
QString MAC_SPARKLE_APPCAST_URL;
|
||||
|
||||
/// User-Agent to use.
|
||||
QString USER_AGENT;
|
||||
|
||||
@ -83,12 +90,12 @@ class Config {
|
||||
/// The git commit hash of this build
|
||||
QString GIT_COMMIT;
|
||||
|
||||
/// The git tag of this build
|
||||
QString GIT_TAG;
|
||||
|
||||
/// The git refspec of this build
|
||||
QString GIT_REFSPEC;
|
||||
|
||||
/// This is printed on start to standard output
|
||||
QString VERSION_STR;
|
||||
|
||||
/**
|
||||
* This is used to fetch the news RSS feed.
|
||||
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
|
||||
@ -118,7 +125,7 @@ class Config {
|
||||
/**
|
||||
* Client API key for CurseForge
|
||||
*/
|
||||
QString CURSEFORGE_API_KEY;
|
||||
QString FLAME_API_KEY;
|
||||
|
||||
/**
|
||||
* Metadata repository URL prefix
|
||||
@ -135,8 +142,8 @@ class Config {
|
||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||
QString FMLLIBS_BASE_URL = "https://files.polymc.org/fmllibs/";
|
||||
QString TRANSLATIONS_BASE_URL = "https://i18n.polymc.org/";
|
||||
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
||||
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
||||
|
||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||
|
||||
@ -154,6 +161,9 @@ class Config {
|
||||
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
|
||||
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
|
||||
|
||||
QString FLAME_BASE_URL = "https://api.curseforge.com/v1";
|
||||
|
||||
QString versionString() const;
|
||||
/**
|
||||
* \brief Converts the Version to a string.
|
||||
* \return The version number in string format (major.minor.revision.build).
|
||||
|
@ -7,5 +7,5 @@ add_library(BuildConfig STATIC
|
||||
${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(BuildConfig Qt5::Core)
|
||||
target_link_libraries(BuildConfig Qt${QT_VERSION_MAJOR}::Core)
|
||||
target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
100
cmake/ECMQueryQt.cmake
Normal file
100
cmake/ECMQueryQt.cmake
Normal file
@ -0,0 +1,100 @@
|
||||
# SPDX-FileCopyrightText: 2014 Rohan Garg <rohan16garg@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014-2016 Aleix Pol <aleixpol@kde.org>
|
||||
# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
# SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samir78@gmail.com>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#[=======================================================================[.rst:
|
||||
ECMQueryQt
|
||||
---------------
|
||||
This module can be used to query the installation paths used by Qt.
|
||||
|
||||
For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in
|
||||
support to query the paths of a target platform when cross-compiling).
|
||||
|
||||
This module defines the following function:
|
||||
::
|
||||
|
||||
ecm_query_qt(<result_variable> <qt_variable> [TRY])
|
||||
|
||||
Passing ``TRY`` will result in the method not making the build fail if the executable
|
||||
used for querying has not been found, but instead simply print a warning message and
|
||||
return an empty string.
|
||||
|
||||
Example usage:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
include(ECMQueryQt)
|
||||
ecm_query_qt(bin_dir QT_INSTALL_BINS)
|
||||
|
||||
If the call succeeds ``${bin_dir}`` will be set to ``<prefix>/path/to/bin/dir`` (e.g.
|
||||
``/usr/lib64/qt/bin/``).
|
||||
|
||||
Since: 5.93
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
|
||||
include(CheckLanguage)
|
||||
check_language(CXX)
|
||||
if (CMAKE_CXX_COMPILER)
|
||||
# Enable the CXX language to let CMake look for config files in library dirs.
|
||||
# See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266
|
||||
enable_language(CXX)
|
||||
endif()
|
||||
|
||||
if (QT_MAJOR_VERSION STREQUAL "5")
|
||||
# QUIET to accommodate the TRY option
|
||||
find_package(Qt${QT_MAJOR_VERSION}Core QUIET)
|
||||
if(TARGET Qt5::qmake)
|
||||
get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
|
||||
|
||||
set(QUERY_EXECUTABLE ${_qmake_executable_default}
|
||||
CACHE FILEPATH "Location of the Qt5 qmake executable")
|
||||
set(_exec_name_text "Qt5 qmake")
|
||||
set(_cli_option "-query")
|
||||
endif()
|
||||
elseif(QT_MAJOR_VERSION STREQUAL "6")
|
||||
# QUIET to accommodate the TRY option
|
||||
find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG)
|
||||
if (TARGET Qt6::qtpaths)
|
||||
get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION)
|
||||
|
||||
set(QUERY_EXECUTABLE ${_qtpaths_executable}
|
||||
CACHE FILEPATH "Location of the Qt6 qtpaths executable")
|
||||
set(_exec_name_text "Qt6 qtpaths")
|
||||
set(_cli_option "--query")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
function(ecm_query_qt result_variable qt_variable)
|
||||
set(options TRY)
|
||||
set(oneValueArgs)
|
||||
set(multiValueArgs)
|
||||
|
||||
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT QUERY_EXECUTABLE)
|
||||
if(ARGS_TRY)
|
||||
set(${result_variable} "" PARENT_SCOPE)
|
||||
message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}")
|
||||
return()
|
||||
else()
|
||||
message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required")
|
||||
endif()
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}"
|
||||
RESULT_VARIABLE return_code
|
||||
OUTPUT_VARIABLE output
|
||||
)
|
||||
if(return_code EQUAL 0)
|
||||
string(STRIP "${output}" output)
|
||||
file(TO_CMAKE_PATH "${output}" output_path)
|
||||
set(${result_variable} "${output_path}" PARENT_SCOPE)
|
||||
else()
|
||||
message(WARNING "Failed call: ${_command} \"${qt_variable}\"")
|
||||
message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}")
|
||||
endif()
|
||||
endfunction()
|
@ -3,7 +3,7 @@
|
||||
# These functions force a re-configure on each git commit so that you can
|
||||
# trust the values of the variables in your build system.
|
||||
#
|
||||
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
|
||||
# get_git_head_revision(<refspecvar> <hashvar> [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR])
|
||||
#
|
||||
# Returns the refspec and sha hash of the current head revision
|
||||
#
|
||||
@ -12,20 +12,33 @@
|
||||
# Returns the results of git describe on the source tree, and adjusting
|
||||
# the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_describe_working_tree(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe on the working tree (--dirty option),
|
||||
# and adjusting the output so that it tests false if an error occurs.
|
||||
#
|
||||
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||
#
|
||||
# Returns the results of git describe --exact-match on the source tree,
|
||||
# and adjusting the output so that it tests false if there was no exact
|
||||
# matching tag.
|
||||
#
|
||||
# git_local_changes(<var>)
|
||||
#
|
||||
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
|
||||
# Uses the return code of "git diff-index --quiet HEAD --".
|
||||
# Does not regard untracked files.
|
||||
#
|
||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||
#
|
||||
# Original Author:
|
||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
||||
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Copyright 2009-2013, Iowa State University.
|
||||
# Copyright 2013-2020, Ryan Pavlik
|
||||
# Copyright 2013-2020, Contributors
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
@ -39,45 +52,124 @@ set(__get_git_revision_description YES)
|
||||
# to find the path to this module rather than the path to a calling list file
|
||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
|
||||
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
|
||||
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
|
||||
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
|
||||
# Function _git_find_closest_git_dir finds the next closest .git directory
|
||||
# that is part of any directory in the path defined by _start_dir.
|
||||
# The result is returned in the parent scope variable whose name is passed
|
||||
# as variable _git_dir_var. If no .git directory can be found, the
|
||||
# function returns an empty string via _git_dir_var.
|
||||
#
|
||||
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||
# neither foo nor bar contain a file/directory .git. This wil return
|
||||
# C:/bla/.git
|
||||
#
|
||||
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||
set(cur_dir "${_start_dir}")
|
||||
set(git_dir "${_start_dir}/.git")
|
||||
while(NOT EXISTS "${git_dir}")
|
||||
# .git dir not found, search parent directories
|
||||
set(git_previous_parent "${cur_dir}")
|
||||
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||
if(cur_dir STREQUAL git_previous_parent)
|
||||
# We have reached the root directory, we are not in git
|
||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_git_dir_var}
|
||||
""
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
||||
set(git_dir "${cur_dir}/.git")
|
||||
endwhile()
|
||||
# check if this is a submodule
|
||||
set(${_git_dir_var}
|
||||
"${git_dir}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(get_git_head_revision _refspecvar _hashvar)
|
||||
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
|
||||
|
||||
if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
|
||||
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
|
||||
else()
|
||||
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
|
||||
endif()
|
||||
if(NOT "${GIT_DIR}" STREQUAL "")
|
||||
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
|
||||
"${GIT_DIR}")
|
||||
if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
|
||||
# We've gone above the CMake root dir.
|
||||
set(GIT_DIR "")
|
||||
endif()
|
||||
endif()
|
||||
if("${GIT_DIR}" STREQUAL "")
|
||||
set(${_refspecvar}
|
||||
"GITDIR-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
set(${_hashvar}
|
||||
"GITDIR-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check if the current source dir is a git submodule or a worktree.
|
||||
# In both cases .git is a file instead of a directory.
|
||||
#
|
||||
if(NOT IS_DIRECTORY ${GIT_DIR})
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
||||
# The following git command will return a non empty string that
|
||||
# points to the super project working tree if the current
|
||||
# source dir is inside a git submodule.
|
||||
# Otherwise the command will return an empty string.
|
||||
#
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" rev-parse
|
||||
--show-superproject-working-tree
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT "${out}" STREQUAL "")
|
||||
# If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||
file(READ ${GIT_DIR} submodule)
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
|
||||
${submodule})
|
||||
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
|
||||
ABSOLUTE)
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
else()
|
||||
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||
file(READ ${GIT_DIR} worktree_ref)
|
||||
# The .git directory contains a path to the worktree information directory
|
||||
# inside the parent git repo of the worktree.
|
||||
#
|
||||
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
|
||||
${worktree_ref})
|
||||
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||
endif()
|
||||
else()
|
||||
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||
endif()
|
||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||
if(NOT EXISTS "${GIT_DATA}")
|
||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${GIT_DIR}/HEAD")
|
||||
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||
return()
|
||||
endif()
|
||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
|
||||
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||
|
||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
|
||||
"${GIT_DATA}/grabRef.cmake"
|
||||
@ONLY)
|
||||
"${GIT_DATA}/grabRef.cmake" @ONLY)
|
||||
include("${GIT_DATA}/grabRef.cmake")
|
||||
|
||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
||||
set(${_refspecvar}
|
||||
"${HEAD_REF}"
|
||||
PARENT_SCOPE)
|
||||
set(${_hashvar}
|
||||
"${HEAD_HASH}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe _var)
|
||||
@ -86,45 +178,107 @@ function(git_describe _var)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
|
||||
set(${_var}
|
||||
"HEAD-HASH-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# TODO sanitize
|
||||
#if((${ARGN}" MATCHES "&&") OR
|
||||
# (ARGN MATCHES "||") OR
|
||||
# (ARGN MATCHES "\\;"))
|
||||
# message("Please report the following error to the project!")
|
||||
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
|
||||
# (ARGN MATCHES "||") OR
|
||||
# (ARGN MATCHES "\\;"))
|
||||
# message("Please report the following error to the project!")
|
||||
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
|
||||
#endif()
|
||||
|
||||
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||
|
||||
execute_process(COMMAND
|
||||
"${GIT_EXECUTABLE}"
|
||||
describe
|
||||
${hash}
|
||||
${ARGN}
|
||||
WORKING_DIRECTORY
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE
|
||||
res
|
||||
OUTPUT_VARIABLE
|
||||
out
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_describe_working_tree _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(NOT res EQUAL 0)
|
||||
set(out "${out}-${res}-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_get_exact_tag _var)
|
||||
git_describe(out --exact-match ${ARGN})
|
||||
set(${_var} "${out}" PARENT_SCOPE)
|
||||
set(${_var}
|
||||
"${out}"
|
||||
PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(git_local_changes _var)
|
||||
if(NOT GIT_FOUND)
|
||||
find_package(Git QUIET)
|
||||
endif()
|
||||
get_git_head_revision(refspec hash)
|
||||
if(NOT GIT_FOUND)
|
||||
set(${_var}
|
||||
"GIT-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(NOT hash)
|
||||
set(${_var}
|
||||
"HEAD-HASH-NOTFOUND"
|
||||
PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE res
|
||||
OUTPUT_VARIABLE out
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(res EQUAL 0)
|
||||
set(${_var}
|
||||
"CLEAN"
|
||||
PARENT_SCOPE)
|
||||
else()
|
||||
set(${_var}
|
||||
"DIRTY"
|
||||
PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
@ -8,10 +8,12 @@
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2009-2010.
|
||||
# Copyright 2009-2012, Iowa State University
|
||||
# Copyright 2011-2015, Contributors
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
set(HEAD_HASH)
|
||||
|
||||
@ -19,23 +21,23 @@ file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
|
||||
|
||||
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
|
||||
if(HEAD_CONTENTS MATCHES "ref")
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
# named branch
|
||||
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
|
||||
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
|
||||
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
else()
|
||||
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
|
||||
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
|
||||
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
|
||||
set(HEAD_HASH "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
# detached HEAD
|
||||
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
|
||||
endif()
|
||||
|
||||
if(NOT HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
|
||||
string(STRIP "${HEAD_HASH}" HEAD_HASH)
|
||||
endif()
|
||||
|
@ -40,5 +40,32 @@
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>SUPublicEDKey</key>
|
||||
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
|
||||
<key>SUFeedURL</key>
|
||||
<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>
|
||||
</plist>
|
||||
|
@ -1,14 +0,0 @@
|
||||
if(__QMAKEQUERY_CMAKE__)
|
||||
return()
|
||||
endif()
|
||||
set(__QMAKEQUERY_CMAKE__ TRUE)
|
||||
|
||||
get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
|
||||
|
||||
function(QUERY_QMAKE VAR RESULT)
|
||||
exec_program(${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output )
|
||||
if(NOT return_code)
|
||||
file(TO_CMAKE_PATH "${output}" output)
|
||||
set(${RESULT} ${output} PARENT_SCOPE)
|
||||
endif(NOT return_code)
|
||||
endfunction(QUERY_QMAKE)
|
38
cmake/QtVersionOption.cmake
Normal file
38
cmake/QtVersionOption.cmake
Normal file
@ -0,0 +1,38 @@
|
||||
#.rst:
|
||||
# QtVersionOption
|
||||
# ---------------
|
||||
#
|
||||
# Adds a build option to select the major Qt version if necessary,
|
||||
# that is, if the major Qt version has not yet been determined otherwise
|
||||
# (e.g. by a corresponding find_package() call).
|
||||
#
|
||||
# This module is typically included by other modules requiring knowledge
|
||||
# about the major Qt version.
|
||||
#
|
||||
# ``QT_MAJOR_VERSION`` is defined to either be "5" or "6".
|
||||
#
|
||||
#
|
||||
# Since 5.82.0.
|
||||
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if (DEFINED QT_MAJOR_VERSION)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if (TARGET Qt5::Core)
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
elseif (TARGET Qt6::Core)
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
else()
|
||||
option(BUILD_WITH_QT6 "Build against Qt 6" OFF)
|
||||
|
||||
if (BUILD_WITH_QT6)
|
||||
set(QT_MAJOR_VERSION 6)
|
||||
else()
|
||||
set(QT_MAJOR_VERSION 5)
|
||||
endif()
|
||||
endif()
|
97
cmake/QtVersionlessBackport.cmake
Normal file
97
cmake/QtVersionlessBackport.cmake
Normal file
@ -0,0 +1,97 @@
|
||||
#=============================================================================
|
||||
# Copyright 2005-2011 Kitware, Inc.
|
||||
# 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 Kitware, Inc. 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
|
||||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#=============================================================================
|
||||
|
||||
# From Qt5CoreMacros.cmake
|
||||
|
||||
function(qt_generate_moc)
|
||||
if(QT_VERSION_MAJOR EQUAL 5)
|
||||
qt5_generate_moc(${ARGV})
|
||||
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt6_generate_moc(${ARGV})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(qt_wrap_cpp outfiles)
|
||||
if(QT_VERSION_MAJOR EQUAL 5)
|
||||
qt5_wrap_cpp("${outfiles}" ${ARGN})
|
||||
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt6_wrap_cpp("${outfiles}" ${ARGN})
|
||||
endif()
|
||||
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(qt_add_binary_resources)
|
||||
if(QT_VERSION_MAJOR EQUAL 5)
|
||||
qt5_add_binary_resources(${ARGV})
|
||||
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt6_add_binary_resources(${ARGV})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(qt_add_resources outfiles)
|
||||
if(QT_VERSION_MAJOR EQUAL 5)
|
||||
qt5_add_resources("${outfiles}" ${ARGN})
|
||||
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt6_add_resources("${outfiles}" ${ARGN})
|
||||
endif()
|
||||
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(qt_add_big_resources outfiles)
|
||||
if(QT_VERSION_MAJOR EQUAL 5)
|
||||
qt5_add_big_resources(${outfiles} ${ARGN})
|
||||
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt6_add_big_resources(${outfiles} ${ARGN})
|
||||
endif()
|
||||
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(qt_import_plugins)
|
||||
if(QT_VERSION_MAJOR EQUAL 5)
|
||||
qt5_import_plugins(${ARGV})
|
||||
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt6_import_plugins(${ARGV})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
# From Qt5WidgetsMacros.cmake
|
||||
|
||||
function(qt_wrap_ui outfiles)
|
||||
if(QT_VERSION_MAJOR EQUAL 5)
|
||||
qt5_wrap_ui("${outfiles}" ${ARGN})
|
||||
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt6_wrap_ui("${outfiles}" ${ARGN})
|
||||
endif()
|
||||
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
@ -1,50 +0,0 @@
|
||||
find_package(Qt5Test REQUIRED)
|
||||
|
||||
set(TEST_RESOURCE_PATH ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
message(${TEST_RESOURCE_PATH})
|
||||
|
||||
function(add_unit_test name)
|
||||
if(BUILD_TESTING)
|
||||
set(options "")
|
||||
set(oneValueArgs DATA)
|
||||
set(multiValueArgs SOURCES LIBS)
|
||||
|
||||
cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
|
||||
|
||||
if(WIN32)
|
||||
add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc)
|
||||
else()
|
||||
add_executable(${name}_test ${OPT_SOURCES})
|
||||
endif()
|
||||
|
||||
if(NOT "${OPT_DATA}" STREQUAL "")
|
||||
set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
|
||||
set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}")
|
||||
message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}")
|
||||
string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}")
|
||||
if(UNIX)
|
||||
# on unix we get the third / from the filename
|
||||
set(TEST_DATA_URL "file://${TEST_DATA_PATH}")
|
||||
else()
|
||||
# we don't on windows, so we have to add it ourselves
|
||||
set(TEST_DATA_URL "file:///${TEST_DATA_PATH}")
|
||||
endif()
|
||||
if(NOT TARGET "${DATA_TARGET_NAME}")
|
||||
add_custom_target(${DATA_TARGET_NAME})
|
||||
add_dependencies(${name}_test ${DATA_TARGET_NAME})
|
||||
add_custom_command(
|
||||
TARGET ${DATA_TARGET_NAME}
|
||||
COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS})
|
||||
|
||||
target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/")
|
||||
|
||||
add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
endfunction()
|
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFile>
|
||||
#include <QCoreApplication>
|
||||
#include <QTest>
|
||||
#include <QDir>
|
||||
|
||||
#define expandstr(s) expandstr2(s)
|
||||
#define expandstr2(s) #s
|
||||
|
||||
class TestsInternal
|
||||
{
|
||||
public:
|
||||
static QByteArray readFile(const QString &fileName)
|
||||
{
|
||||
QFile f(fileName);
|
||||
f.open(QFile::ReadOnly);
|
||||
return f.readAll();
|
||||
}
|
||||
static QString readFileUtf8(const QString &fileName)
|
||||
{
|
||||
return QString::fromUtf8(readFile(fileName));
|
||||
}
|
||||
};
|
||||
|
||||
#define GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA(file))
|
||||
#define GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA(file))
|
||||
|
@ -1,23 +0,0 @@
|
||||
# Copy files from source directory to destination directory, substituting any
|
||||
# variables. Create destination directory if it does not exist.
|
||||
|
||||
function(configure_files srcDir destDir)
|
||||
make_directory(${destDir})
|
||||
|
||||
file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*)
|
||||
foreach(templateFile ${templateFiles})
|
||||
set(srcTemplatePath ${srcDir}/${templateFile})
|
||||
if(NOT IS_DIRECTORY ${srcTemplatePath})
|
||||
configure_file(
|
||||
${srcTemplatePath}
|
||||
${destDir}/${templateFile}
|
||||
@ONLY
|
||||
NEWLINE_STYLE LF
|
||||
)
|
||||
else()
|
||||
configure_files("${srcTemplatePath}" "${destDir}/${templateFile}")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
configure_files(${SOURCE} ${DESTINATION})
|
@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<assemblyIdentity name="Launcher.Test.0" type="win32" version="5.0.0.0" />
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<description>Custom Minecraft launcher for managing multiple installs.</description>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!--The ID below indicates app support for Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!--The ID below indicates app support for Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
@ -1,28 +0,0 @@
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
1 RT_MANIFEST "test.manifest"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,0,0,0
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_APP
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "000004b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "MultiMC & PolyMC Contributors"
|
||||
VALUE "FileDescription", "Testcase"
|
||||
VALUE "FileVersion", "1.0.0.0"
|
||||
VALUE "ProductName", "Launcher Testcase"
|
||||
VALUE "ProductVersion", "5"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0000, 0x04b0 // Unicode
|
||||
END
|
||||
END
|
@ -1 +1 @@
|
||||
(import packages/nix/flake-compat.nix).defaultNix
|
||||
(import nix/flake-compat.nix).defaultNix
|
||||
|
41
flake.lock
generated
41
flake.lock
generated
@ -3,11 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1648199409,
|
||||
"narHash": "sha256-JwPKdC2PoVBkG6E+eWw3j6BMR6sL3COpYWfif7RVb8Y=",
|
||||
"lastModified": 1668681692,
|
||||
"narHash": "sha256-Ht91NGdewz8IQLtWZ9LCeNXMSXHUss+9COoqu6JLmXU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "64a525ee38886ab9028e6f61790de0832aa3ef03",
|
||||
"rev": "009399224d5e398d03b22badca40a37ac85412a1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -19,26 +19,26 @@
|
||||
"libnbtplusplus": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1591558203,
|
||||
"narHash": "sha256-QgvNvaoFflCXEPCCFBCeZvYTpuiwScBG7EosUgFwFNQ=",
|
||||
"owner": "multimc",
|
||||
"lastModified": 1650031308,
|
||||
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
||||
"owner": "PrismLauncher",
|
||||
"repo": "libnbtplusplus",
|
||||
"rev": "dc72a20b7efd304d12af2025223fad07b4b78464",
|
||||
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "multimc",
|
||||
"owner": "PrismLauncher",
|
||||
"repo": "libnbtplusplus",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1648219316,
|
||||
"narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=",
|
||||
"lastModified": 1671417167,
|
||||
"narHash": "sha256-JkHam6WQOwZN1t2C2sbp1TqMv3TVRjzrdoejqfefwrM=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634",
|
||||
"rev": "bb31220cca6d044baa6dc2715b07497a2a7c4bc7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -48,28 +48,11 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"quazip": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1643049383,
|
||||
"narHash": "sha256-LcJY6yd6GyeL7X5MP4L94diceM1TYespWByliBsjK98=",
|
||||
"owner": "stachenov",
|
||||
"repo": "quazip",
|
||||
"rev": "09ec1d10c6d627f895109b21728dda000cbfa7d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "stachenov",
|
||||
"repo": "quazip",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"quazip": "quazip"
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
27
flake.nix
27
flake.nix
@ -4,31 +4,34 @@
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
|
||||
libnbtplusplus = { url = "github:multimc/libnbtplusplus"; flake = false; };
|
||||
quazip = { url = "github:stachenov/quazip"; flake = false; };
|
||||
libnbtplusplus = { url = "github:PrismLauncher/libnbtplusplus"; flake = false; };
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, libnbtplusplus, quazip, ... }:
|
||||
outputs = { self, nixpkgs, libnbtplusplus, ... }:
|
||||
let
|
||||
# Generate a user-friendly version number.
|
||||
# User-friendly version number.
|
||||
version = builtins.substring 0 8 self.lastModifiedDate;
|
||||
|
||||
# System types to support (qtbase is currently broken for "aarch64-darwin")
|
||||
# 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 system types.
|
||||
# Nixpkgs instantiated for supported systems.
|
||||
pkgs = forAllSystems (system: nixpkgs.legacyPackages.${system});
|
||||
|
||||
packagesFn = pkgs: rec {
|
||||
prismlauncher-qt5 = pkgs.libsForQt5.callPackage ./nix { inherit version self libnbtplusplus; };
|
||||
prismlauncher = pkgs.qt6Packages.callPackage ./nix { inherit version self libnbtplusplus; };
|
||||
};
|
||||
in
|
||||
{
|
||||
packages = forAllSystems (system: { polymc = pkgs.${system}.libsForQt5.callPackage ./packages/nix/polymc { inherit version self quazip libnbtplusplus; }; });
|
||||
defaultPackage = forAllSystems (system: self.packages.${system}.polymc);
|
||||
packages = forAllSystems (system:
|
||||
let packages = packagesFn pkgs.${system}; in
|
||||
packages // { default = packages.prismlauncher; }
|
||||
);
|
||||
|
||||
apps = forAllSystems (system: { polymc = { type = "app"; program = "${self.defaultPackage.${system}}/bin/polymc"; }; });
|
||||
defaultApp = forAllSystems (system: self.apps.${system}.polymc);
|
||||
|
||||
overlay = final: prev: { polymc = self.defaultPackage.${final.system}; };
|
||||
overlay = final: packagesFn;
|
||||
};
|
||||
}
|
||||
|
83
flatpak/org.prismlauncher.PrismLauncher.yml
Normal file
83
flatpak/org.prismlauncher.PrismLauncher.yml
Normal file
@ -0,0 +1,83 @@
|
||||
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
|
||||
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: ../
|
||||
- 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,12 @@
|
||||
// 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 Lenny McLennington <lenny@sneed.church>
|
||||
* Copyright (C) 2022 Tayou <tayou@gmx.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
|
||||
@ -37,10 +41,14 @@
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "DataMigrationTask.h"
|
||||
#include "net/PasteUpload.h"
|
||||
#include "pathmatcher/MultiMatcher.h"
|
||||
#include "pathmatcher/SimplePrefixMatcher.h"
|
||||
#include "ui/MainWindow.h"
|
||||
#include "ui/InstanceWindow.h"
|
||||
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/instanceview/AccessibleInstanceView.h"
|
||||
|
||||
#include "ui/pages/BasePageProvider.h"
|
||||
@ -54,12 +62,6 @@
|
||||
#include "ui/pages/global/APIPage.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/LanguageWizardPage.h"
|
||||
#include "ui/setupwizard/JavaWizardPage.h"
|
||||
@ -69,11 +71,14 @@
|
||||
|
||||
#include "ui/pagedialog/PageDialog.h"
|
||||
|
||||
#include "ui/themes/ThemeManager.h"
|
||||
|
||||
#include "ApplicationMessage.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <QAccessible>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkAccessManager>
|
||||
@ -84,8 +89,10 @@
|
||||
#include <QDebug>
|
||||
#include <QStyleFactory>
|
||||
#include <QWindow>
|
||||
#include <QIcon>
|
||||
|
||||
#include "InstanceList.h"
|
||||
#include "MTPixmapCache.h"
|
||||
|
||||
#include <minecraft/auth/AccountList.h>
|
||||
#include "icons/IconList.h"
|
||||
@ -99,20 +106,24 @@
|
||||
#include "tools/JVisualVM.h"
|
||||
#include "tools/MCEditTool.h"
|
||||
|
||||
#include <xdgicon.h>
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "settings/Setting.h"
|
||||
|
||||
#include "translations/TranslationsModel.h"
|
||||
#include "meta/Index.h"
|
||||
|
||||
#include <Commandline.h>
|
||||
#include <FileSystem.h>
|
||||
#include <DesktopServices.h>
|
||||
#include <LocalPeer.h>
|
||||
|
||||
#include <sys.h>
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <dlfcn.h>
|
||||
#include "gamemode_client.h"
|
||||
#include "MangoHud.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
@ -127,26 +138,15 @@
|
||||
|
||||
static const QLatin1String liveCheckFile("live.check");
|
||||
|
||||
using namespace Commandline;
|
||||
|
||||
#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"
|
||||
PixmapCache* PixmapCache::s_instance = nullptr;
|
||||
|
||||
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)
|
||||
{
|
||||
const char *levels = "DWCFIS";
|
||||
const QString format("%1 %2 %3\n");
|
||||
|
||||
qint64 msecstotal = APPLICATION->timeSinceStart();
|
||||
qint64 seconds = msecstotal / 1000;
|
||||
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);
|
||||
QString out = qFormatLogMessage(type, context, msg);
|
||||
out += QChar::LineFeed;
|
||||
|
||||
APPLICATION->logFile->write(out.toUtf8());
|
||||
APPLICATION->logFile->flush();
|
||||
@ -224,8 +224,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
setOrganizationName(BuildConfig.LAUNCHER_NAME);
|
||||
setOrganizationDomain(BuildConfig.LAUNCHER_DOMAIN);
|
||||
setApplicationName(BuildConfig.LAUNCHER_NAME);
|
||||
setApplicationDisplayName(BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||
setApplicationVersion(BuildConfig.printableVersionString());
|
||||
setApplicationDisplayName(QString("%1 %2").arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString()));
|
||||
setApplicationVersion(BuildConfig.printableVersionString() + "\n" + BuildConfig.GIT_COMMIT);
|
||||
setDesktopFileName(BuildConfig.LAUNCHER_DESKTOPFILENAME);
|
||||
startTime = QDateTime::currentDateTime();
|
||||
|
||||
@ -233,80 +233,29 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
this->setQuitOnLastWindowClosed(false);
|
||||
|
||||
// Commandline parsing
|
||||
QHash<QString, QVariant> args;
|
||||
{
|
||||
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(BuildConfig.LAUNCHER_DISPLAYNAME);
|
||||
|
||||
// --help
|
||||
parser.addSwitch("help");
|
||||
parser.addShortOpt("help", 'h');
|
||||
parser.addDocumentation("help", "Display this help and exit.");
|
||||
// --version
|
||||
parser.addSwitch("version");
|
||||
parser.addShortOpt("version", 'V');
|
||||
parser.addDocumentation("version", "Display program version and exit.");
|
||||
// --dir
|
||||
parser.addOption("dir");
|
||||
parser.addShortOpt("dir", 'd');
|
||||
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)");
|
||||
parser.addOptions({
|
||||
{{"d", "dir"}, "Use a custom path as application root (use '.' for current directory)", "directory"},
|
||||
{{"l", "launch"}, "Launch the specified instance (by instance ID)", "instance"},
|
||||
{{"s", "server"}, "Join the specified server on launch (only valid in combination with --launch)", "address"},
|
||||
{{"a", "profile"}, "Use the account specified by its profile name (only valid in combination with --launch)", "profile"},
|
||||
{"alive", "Write a small '" + liveCheckFile + "' file after the launcher starts"},
|
||||
{{"I", "import"}, "Import instance from specified zip (local path or URL)", "file"},
|
||||
{"show", "Opens the window for the specified instance (by instance ID)", "show"}
|
||||
});
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
|
||||
// parse the 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;
|
||||
}
|
||||
parser.process(arguments());
|
||||
|
||||
// display help and exit
|
||||
if (args["help"].toBool())
|
||||
{
|
||||
std::cout << qPrintable(parser.compileHelp(arguments()[0]));
|
||||
m_status = Application::Succeeded;
|
||||
return;
|
||||
}
|
||||
|
||||
// display version and exit
|
||||
if (args["version"].toBool())
|
||||
{
|
||||
std::cout << "Version " << BuildConfig.printableVersionString().toStdString() << std::endl;
|
||||
std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
|
||||
m_status = Application::Succeeded;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_instanceIdToLaunch = args["launch"].toString();
|
||||
m_serverToJoin = args["server"].toString();
|
||||
m_profileToUse = args["profile"].toString();
|
||||
m_liveCheck = args["alive"].toBool();
|
||||
m_zipToImport = args["import"].toUrl();
|
||||
m_instanceIdToLaunch = parser.value("launch");
|
||||
m_serverToJoin = parser.value("server");
|
||||
m_profileToUse = parser.value("profile");
|
||||
m_liveCheck = parser.isSet("alive");
|
||||
m_zipToImport = parser.value("import");
|
||||
m_instanceIdToShowWindowOf = parser.value("show");
|
||||
|
||||
// error if --launch is missing with --server or --profile
|
||||
if((!m_serverToJoin.isEmpty() || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty())
|
||||
@ -321,7 +270,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
{
|
||||
// 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
|
||||
m_rootPath = foo.absolutePath();
|
||||
#elif defined(Q_OS_WIN32)
|
||||
@ -332,16 +281,12 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
// on macOS, touch the root to force Finder to reload the .app metadata (and fix any icon change issues)
|
||||
FS::updateTimestamp(m_rootPath);
|
||||
#endif
|
||||
|
||||
#ifdef LAUNCHER_JARS_LOCATION
|
||||
m_jarsPath = TOSTRING(LAUNCHER_JARS_LOCATION);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString adjustedBy;
|
||||
QString dataPath;
|
||||
// change folder
|
||||
QString dirParam = args["dir"].toString();
|
||||
QString dirParam = parser.value("dir");
|
||||
if (!dirParam.isEmpty())
|
||||
{
|
||||
// the dir param. it makes multimc data path point to whatever the user specified
|
||||
@ -355,16 +300,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
dataPath = foo.absolutePath();
|
||||
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
|
||||
if (QFile::exists(FS::PathCombine(m_rootPath, "portable.txt"))) {
|
||||
dataPath = m_rootPath;
|
||||
@ -380,9 +315,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
QString(
|
||||
"The launcher data folder could not be created.\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"
|
||||
"(%1)\n"
|
||||
"\n"
|
||||
@ -398,9 +330,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
QString(
|
||||
"The launcher data folder could not be opened.\n"
|
||||
"\n"
|
||||
#if defined(Q_OS_MAC)
|
||||
MACOS_HINT
|
||||
#endif
|
||||
"Make sure you have the right permissions to the launcher data folder.\n"
|
||||
"(%1)\n"
|
||||
"\n"
|
||||
@ -481,9 +410,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
QString(
|
||||
"The launcher couldn't create a log file - the data folder is not writable.\n"
|
||||
"\n"
|
||||
#if defined(Q_OS_MAC)
|
||||
MACOS_HINT
|
||||
#endif
|
||||
"Make sure you have write permissions to the data folder.\n"
|
||||
"(%1)\n"
|
||||
"\n"
|
||||
@ -493,9 +419,26 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
return;
|
||||
}
|
||||
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}");
|
||||
|
||||
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;
|
||||
@ -545,14 +488,17 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
// Initialize application settings
|
||||
{
|
||||
m_settings.reset(new INISettingsObject(BuildConfig.LAUNCHER_CONFIGFILE, this));
|
||||
// Provide a fallback for migration from PolyMC
|
||||
m_settings.reset(new INISettingsObject({ BuildConfig.LAUNCHER_CONFIGFILE, "polymc.cfg", "multimc.cfg" }, this));
|
||||
// Updates
|
||||
// Multiple channels are separated by spaces
|
||||
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
||||
m_settings->registerSetting("AutoUpdate", true);
|
||||
|
||||
// Theming
|
||||
m_settings->registerSetting("IconTheme", QString("pe_colored"));
|
||||
m_settings->registerSetting("ApplicationTheme", QString("system"));
|
||||
m_settings->registerSetting("BackgroundCat", QString("kitteh"));
|
||||
|
||||
// Remembered state
|
||||
m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
|
||||
@ -617,13 +563,14 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
// Memory
|
||||
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
|
||||
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 4096);
|
||||
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, suitableMaxMem());
|
||||
m_settings->registerSetting("PermGen", 128);
|
||||
|
||||
// Java Settings
|
||||
m_settings->registerSetting("JavaPath", "");
|
||||
m_settings->registerSetting("JavaTimestamp", 0);
|
||||
m_settings->registerSetting("JavaArchitecture", "");
|
||||
m_settings->registerSetting("JavaRealArchitecture", "");
|
||||
m_settings->registerSetting("JavaVersion", "");
|
||||
m_settings->registerSetting("JavaVendor", "");
|
||||
m_settings->registerSetting("LastHostname", "");
|
||||
@ -635,6 +582,11 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("UseNativeOpenAL", false);
|
||||
m_settings->registerSetting("UseNativeGLFW", false);
|
||||
|
||||
// Peformance related options
|
||||
m_settings->registerSetting("EnableFeralGamemode", false);
|
||||
m_settings->registerSetting("EnableMangoHud", false);
|
||||
m_settings->registerSetting("UseDiscreteGpu", false);
|
||||
|
||||
// Game time
|
||||
m_settings->registerSetting("ShowGameTime", true);
|
||||
m_settings->registerSetting("ShowGlobalGameTime", true);
|
||||
@ -643,6 +595,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
// Minecraft launch method
|
||||
m_settings->registerSetting("MCLaunchMethod", "LauncherPart");
|
||||
|
||||
// Minecraft mods
|
||||
m_settings->registerSetting("ModMetadataDisabled", false);
|
||||
|
||||
// Minecraft offline player name
|
||||
m_settings->registerSetting("LastOfflinePlayerName", "");
|
||||
|
||||
@ -656,6 +611,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
// The cat
|
||||
m_settings->registerSetting("TheCat", false);
|
||||
|
||||
m_settings->registerSetting("ToolbarsLocked", false);
|
||||
|
||||
m_settings->registerSetting("InstSortMode", "Name");
|
||||
m_settings->registerSetting("SelectedInstance", QString());
|
||||
|
||||
@ -674,6 +631,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
m_settings->registerSetting("UpdateDialogGeometry", "");
|
||||
|
||||
m_settings->registerSetting("ModDownloadGeometry", "");
|
||||
|
||||
// HACK: This code feels so stupid is there a less stupid way of doing this?
|
||||
{
|
||||
m_settings->registerSetting("PastebinURL", "");
|
||||
@ -705,9 +664,21 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_settings->registerSetting("CloseAfterLaunch", false);
|
||||
m_settings->registerSetting("QuitAfterGameStop", false);
|
||||
|
||||
// Custom MSA credentials
|
||||
// Custom Microsoft Authentication Client ID
|
||||
m_settings->registerSetting("MSAClientIDOverride", "");
|
||||
m_settings->registerSetting("CFKeyOverride", "");
|
||||
|
||||
// Custom Flame API Key
|
||||
{
|
||||
m_settings->registerSetting("CFKeyOverride", "");
|
||||
m_settings->registerSetting("FlameKeyOverride", "");
|
||||
|
||||
QString flameKey = m_settings->get("CFKeyOverride").toString();
|
||||
|
||||
if (!flameKey.isEmpty())
|
||||
m_settings->set("FlameKeyOverride", flameKey);
|
||||
m_settings->reset("CFKeyOverride");
|
||||
}
|
||||
m_settings->registerSetting("UserAgentOverride", "");
|
||||
|
||||
// Init page provider
|
||||
{
|
||||
@ -722,6 +693,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_globalSettingsProvider->addPage<AccountListPage>();
|
||||
m_globalSettingsProvider->addPage<APIPage>();
|
||||
}
|
||||
|
||||
PixmapCache::setInstance(new PixmapCache(this));
|
||||
|
||||
qDebug() << "<> Settings loaded.";
|
||||
}
|
||||
|
||||
@ -756,7 +730,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
auto platform = getIdealPlatform(BuildConfig.BUILD_PLATFORM);
|
||||
auto channelUrl = BuildConfig.UPDATER_BASE + platform + "/channels.json";
|
||||
qDebug() << "Initializing updater with platform: " << platform << " -- " << channelUrl;
|
||||
m_updateChecker.reset(new UpdateChecker(m_network, channelUrl, BuildConfig.VERSION_CHANNEL, BuildConfig.VERSION_BUILD));
|
||||
m_updateChecker.reset(new UpdateChecker(m_network, channelUrl, BuildConfig.VERSION_CHANNEL));
|
||||
qDebug() << "<> Updater started.";
|
||||
}
|
||||
|
||||
@ -778,29 +752,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
qDebug() << "<> Instance icons intialized.";
|
||||
}
|
||||
|
||||
// Icon themes
|
||||
{
|
||||
// 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.";
|
||||
}
|
||||
// Themes
|
||||
m_themeManager = std::make_unique<ThemeManager>(m_mainWindow);
|
||||
|
||||
// initialize and load all instances
|
||||
{
|
||||
@ -846,7 +799,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_metacache->addBase("ModpacksCHPacks", QDir("cache/ModpacksCHPacks").absolutePath());
|
||||
m_metacache->addBase("TechnicPacks", QDir("cache/TechnicPacks").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("ModrinthModpacks", QDir("cache/ModrinthModpacks").absolutePath());
|
||||
m_metacache->addBase("root", QDir::currentPath());
|
||||
m_metacache->addBase("translations", QDir("translations").absolutePath());
|
||||
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
|
||||
@ -871,6 +826,12 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
m_mcedit.reset(new MCEditTool(m_settings));
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
connect(this, &Application::clickedOnDock, [this]() {
|
||||
this->showMainWindow();
|
||||
});
|
||||
#endif
|
||||
|
||||
connect(this, &Application::aboutToQuit, [this](){
|
||||
if(m_instances)
|
||||
{
|
||||
@ -891,10 +852,13 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
qDebug() << "<> Application theme set.";
|
||||
}
|
||||
|
||||
updateCapabilities();
|
||||
|
||||
if(createSetupWizard())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
performMainStartupAction();
|
||||
}
|
||||
|
||||
@ -954,6 +918,27 @@ bool Application::createSetupWizard()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Application::event(QEvent* event)
|
||||
{
|
||||
#ifdef Q_OS_MACOS
|
||||
if (event->type() == QEvent::ApplicationStateChange) {
|
||||
auto ev = static_cast<QApplicationStateChangeEvent*>(event);
|
||||
|
||||
if (m_prevAppState == Qt::ApplicationActive && ev->applicationState() == Qt::ApplicationActive) {
|
||||
emit clickedOnDock();
|
||||
}
|
||||
m_prevAppState = ev->applicationState();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (event->type() == QEvent::FileOpen) {
|
||||
auto ev = static_cast<QFileOpenEvent*>(event);
|
||||
m_mainWindow->droppedURLs({ ev->url() });
|
||||
}
|
||||
|
||||
return QApplication::event(event);
|
||||
}
|
||||
|
||||
void Application::setupWizardFinished(int status)
|
||||
{
|
||||
qDebug() << "Wizard result =" << status;
|
||||
@ -988,7 +973,17 @@ void Application::performMainStartupAction()
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -1092,6 +1087,7 @@ void Application::messageReceived(const QByteArray& message)
|
||||
launch(
|
||||
instance,
|
||||
true,
|
||||
false,
|
||||
nullptr,
|
||||
serverObject,
|
||||
accountObject
|
||||
@ -1117,53 +1113,27 @@ std::shared_ptr<JavaInstallList> Application::javalist()
|
||||
return m_javalist;
|
||||
}
|
||||
|
||||
std::vector<ITheme *> Application::getValidApplicationThemes()
|
||||
QList<ITheme*> Application::getValidApplicationThemes()
|
||||
{
|
||||
std::vector<ITheme *> ret;
|
||||
auto iter = m_themes.cbegin();
|
||||
while (iter != m_themes.cend())
|
||||
{
|
||||
ret.push_back((*iter).second.get());
|
||||
iter++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Application::isFlatpak()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return QFile::exists("/.flatpak-info");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
return m_themeManager->getValidApplicationThemes();
|
||||
}
|
||||
|
||||
void Application::setApplicationTheme(const QString& name, bool initial)
|
||||
{
|
||||
auto systemPalette = qApp->palette();
|
||||
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;
|
||||
}
|
||||
m_themeManager->setApplicationTheme(name, initial);
|
||||
}
|
||||
|
||||
void Application::setIconTheme(const QString& name)
|
||||
{
|
||||
XdgIcon::setThemeName(name);
|
||||
m_themeManager->setIconTheme(name);
|
||||
}
|
||||
|
||||
QIcon Application::getThemedIcon(const QString& name)
|
||||
{
|
||||
if(name == "logo") {
|
||||
return QIcon(":/org.polymc.PolyMC.svg");
|
||||
return QIcon(":/" + BuildConfig.LAUNCHER_SVGFILENAME);
|
||||
}
|
||||
return XdgIcon::fromTheme(name);
|
||||
return QIcon::fromTheme(name);
|
||||
}
|
||||
|
||||
bool Application::openJsonEditor(const QString &filename)
|
||||
@ -1183,6 +1153,7 @@ bool Application::openJsonEditor(const QString &filename)
|
||||
bool Application::launch(
|
||||
InstancePtr instance,
|
||||
bool online,
|
||||
bool demo,
|
||||
BaseProfilerFactory *profiler,
|
||||
MinecraftServerTargetPtr serverToJoin,
|
||||
MinecraftAccountPtr accountToUse
|
||||
@ -1206,6 +1177,7 @@ bool Application::launch(
|
||||
controller.reset(new LaunchController());
|
||||
controller->setInstance(instance);
|
||||
controller->setOnline(online);
|
||||
controller->setDemo(demo);
|
||||
controller->setProfiler(profiler);
|
||||
controller->setServerToJoin(serverToJoin);
|
||||
controller->setAccountToUse(accountToUse);
|
||||
@ -1219,6 +1191,9 @@ bool Application::launch(
|
||||
}
|
||||
connect(controller.get(), &LaunchController::succeeded, this, &Application::controllerSucceeded);
|
||||
connect(controller.get(), &LaunchController::failed, this, &Application::controllerFailed);
|
||||
connect(controller.get(), &LaunchController::aborted, this, [this] {
|
||||
controllerFailed(tr("Aborted"));
|
||||
});
|
||||
addRunningInstance();
|
||||
controller->start();
|
||||
return true;
|
||||
@ -1373,6 +1348,7 @@ MainWindow* Application::showMainWindow(bool minimized)
|
||||
m_mainWindow = new MainWindow();
|
||||
m_mainWindow->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowState").toByteArray()));
|
||||
m_mainWindow->restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("MainWindowGeometry").toByteArray()));
|
||||
|
||||
if(minimized)
|
||||
{
|
||||
m_mainWindow->showMinimized();
|
||||
@ -1525,13 +1501,40 @@ shared_qobject_ptr<Meta::Index> Application::metadataIndex()
|
||||
return m_metadataIndex;
|
||||
}
|
||||
|
||||
QString Application::getJarsPath()
|
||||
void Application::updateCapabilities()
|
||||
{
|
||||
if(m_jarsPath.isEmpty())
|
||||
m_capabilities = None;
|
||||
if (!getMSAClientID().isEmpty())
|
||||
m_capabilities |= SupportsMSA;
|
||||
if (!getFlameAPIKey().isEmpty())
|
||||
m_capabilities |= SupportsFlame;
|
||||
|
||||
#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)
|
||||
{
|
||||
QStringList potentialPaths = {
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
FS::PathCombine(m_rootPath, "share/" + BuildConfig.LAUNCHER_APP_BINARY_NAME),
|
||||
#endif
|
||||
FS::PathCombine(m_rootPath, "jars"),
|
||||
FS::PathCombine(applicationDirPath(), "jars"),
|
||||
FS::PathCombine(applicationDirPath(), "..", "jars") // from inside build dir, for debuging
|
||||
};
|
||||
for(QString p : potentialPaths)
|
||||
{
|
||||
return FS::PathCombine(QCoreApplication::applicationDirPath(), "jars");
|
||||
QString jarPath = FS::PathCombine(p, jarFile);
|
||||
if (QFileInfo(jarPath).isFile())
|
||||
return jarPath;
|
||||
}
|
||||
return FS::PathCombine(m_rootPath, m_jarsPath);
|
||||
return {};
|
||||
}
|
||||
|
||||
QString Application::getMSAClientID()
|
||||
@ -1544,12 +1547,132 @@ QString Application::getMSAClientID()
|
||||
return BuildConfig.MSA_CLIENT_ID;
|
||||
}
|
||||
|
||||
QString Application::getCurseKey()
|
||||
QString Application::getFlameAPIKey()
|
||||
{
|
||||
QString keyOverride = m_settings->get("CFKeyOverride").toString();
|
||||
QString keyOverride = m_settings->get("FlameKeyOverride").toString();
|
||||
if (!keyOverride.isEmpty()) {
|
||||
return keyOverride;
|
||||
}
|
||||
|
||||
return BuildConfig.CURSEFORGE_API_KEY;
|
||||
return BuildConfig.FLAME_API_KEY;
|
||||
}
|
||||
|
||||
QString Application::getUserAgent()
|
||||
{
|
||||
QString uaOverride = m_settings->get("UserAgentOverride").toString();
|
||||
if (!uaOverride.isEmpty()) {
|
||||
return uaOverride.replace("$LAUNCHER_VER", BuildConfig.printableVersionString());
|
||||
}
|
||||
|
||||
return BuildConfig.USER_AGENT;
|
||||
}
|
||||
|
||||
QString Application::getUserAgentUncached()
|
||||
{
|
||||
QString uaOverride = m_settings->get("UserAgentOverride").toString();
|
||||
if (!uaOverride.isEmpty()) {
|
||||
uaOverride += " (Uncached)";
|
||||
return uaOverride.replace("$LAUNCHER_VER", BuildConfig.printableVersionString());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
// 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 Tayou <tayou@gmx.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
|
||||
@ -68,6 +69,7 @@ class BaseDetachedToolFactory;
|
||||
class TranslationsModel;
|
||||
class ITheme;
|
||||
class MCEditTool;
|
||||
class ThemeManager;
|
||||
|
||||
namespace Meta {
|
||||
class Index;
|
||||
@ -90,10 +92,22 @@ public:
|
||||
Initialized
|
||||
};
|
||||
|
||||
enum Capability {
|
||||
None = 0,
|
||||
|
||||
SupportsMSA = 1 << 0,
|
||||
SupportsFlame = 1 << 1,
|
||||
SupportsGameMode = 1 << 2,
|
||||
SupportsMangoHud = 1 << 3,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Capabilities, Capability)
|
||||
|
||||
public:
|
||||
Application(int &argc, char **argv);
|
||||
virtual ~Application();
|
||||
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
std::shared_ptr<SettingsObject> settings() const {
|
||||
return m_settings;
|
||||
}
|
||||
@ -104,11 +118,9 @@ public:
|
||||
|
||||
QIcon getThemedIcon(const QString& name);
|
||||
|
||||
bool isFlatpak();
|
||||
|
||||
void setIconTheme(const QString& name);
|
||||
|
||||
std::vector<ITheme *> getValidApplicationThemes();
|
||||
QList<ITheme*> getValidApplicationThemes();
|
||||
|
||||
void setApplicationTheme(const QString& name, bool initial);
|
||||
|
||||
@ -152,16 +164,28 @@ public:
|
||||
|
||||
shared_qobject_ptr<Meta::Index> metadataIndex();
|
||||
|
||||
QString getJarsPath();
|
||||
void updateCapabilities();
|
||||
|
||||
/*!
|
||||
* Finds and returns the full path to a jar file.
|
||||
* Returns a null-string if it could not be found.
|
||||
*/
|
||||
QString getJarPath(QString jarFile);
|
||||
|
||||
QString getMSAClientID();
|
||||
QString getCurseKey();
|
||||
QString getFlameAPIKey();
|
||||
QString getUserAgent();
|
||||
QString getUserAgentUncached();
|
||||
|
||||
/// this is the root of the 'installation'. Used for automatic updates
|
||||
const QString &root() {
|
||||
return m_rootPath;
|
||||
}
|
||||
|
||||
const Capabilities capabilities() {
|
||||
return m_capabilities;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Opens a json file using either a system default editor, or, if not empty, the editor
|
||||
* specified in the settings
|
||||
@ -176,15 +200,22 @@ public:
|
||||
|
||||
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
||||
|
||||
int suitableMaxMem();
|
||||
|
||||
signals:
|
||||
void updateAllowedChanged(bool status);
|
||||
void globalSettingsAboutToOpen();
|
||||
void globalSettingsClosed();
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
void clickedOnDock();
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
bool launch(
|
||||
InstancePtr instance,
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
BaseProfilerFactory *profiler = nullptr,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr
|
||||
@ -200,6 +231,7 @@ private slots:
|
||||
void setupWizardFinished(int status);
|
||||
|
||||
private:
|
||||
bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const;
|
||||
bool createSetupWizard();
|
||||
void performMainStartupAction();
|
||||
|
||||
@ -228,15 +260,19 @@ private:
|
||||
std::shared_ptr<JavaInstallList> m_javalist;
|
||||
std::shared_ptr<TranslationsModel> m_translations;
|
||||
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
||||
std::map<QString, std::unique_ptr<ITheme>> m_themes;
|
||||
std::unique_ptr<MCEditTool> m_mcedit;
|
||||
QString m_jarsPath;
|
||||
QSet<QString> m_features;
|
||||
std::unique_ptr<ThemeManager> m_themeManager;
|
||||
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||
|
||||
QString m_rootPath;
|
||||
Status m_status = Application::StartingUp;
|
||||
Capabilities m_capabilities;
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
|
||||
#endif
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
// used on Windows to attach the standard IO streams
|
||||
@ -268,6 +304,7 @@ public:
|
||||
QString m_profileToUse;
|
||||
bool m_liveCheck = false;
|
||||
QUrl m_zipToImport;
|
||||
QString m_instanceIdToShowWindowOf;
|
||||
std::unique_ptr<QFile> logFile;
|
||||
};
|
||||
|
||||
|
@ -1,18 +1,54 @@
|
||||
// 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.
|
||||
*/
|
||||
|
||||
#include "ApplicationMessage.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include "Json.h"
|
||||
|
||||
void ApplicationMessage::parse(const QByteArray & input) {
|
||||
auto doc = QJsonDocument::fromBinaryData(input);
|
||||
auto root = doc.object();
|
||||
auto doc = Json::requireDocument(input, "ApplicationMessage");
|
||||
auto root = Json::requireObject(doc, "ApplicationMessage");
|
||||
|
||||
command = root.value("command").toString();
|
||||
args.clear();
|
||||
|
||||
auto parsedArgs = root.value("args").toObject();
|
||||
for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
|
||||
args[iter.key()] = iter.value().toString();
|
||||
for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
|
||||
args.insert(iter.key(), iter.value().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,12 +56,10 @@ QByteArray ApplicationMessage::serialize() {
|
||||
QJsonObject root;
|
||||
root.insert("command", command);
|
||||
QJsonObject outArgs;
|
||||
for (auto iter = args.begin(); iter != args.end(); iter++) {
|
||||
outArgs[iter.key()] = iter.value();
|
||||
for (auto iter = args.constBegin(); iter != args.constEnd(); iter++) {
|
||||
outArgs.insert(iter.key(), iter.value());
|
||||
}
|
||||
root.insert("args", outArgs);
|
||||
|
||||
QJsonDocument out;
|
||||
out.setObject(root);
|
||||
return out.toBinaryData();
|
||||
return Json::toText(root);
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
#include <QByteArray>
|
||||
|
||||
struct ApplicationMessage {
|
||||
QString command;
|
||||
QMap<QString, QString> args;
|
||||
QHash<QString, QString> args;
|
||||
|
||||
QByteArray serialize();
|
||||
void parse(const QByteArray & input);
|
||||
|
@ -17,13 +17,14 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "BaseVersion.h"
|
||||
|
||||
class MinecraftInstance;
|
||||
class QDir;
|
||||
class QString;
|
||||
class QObject;
|
||||
class Task;
|
||||
class BaseVersion;
|
||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
||||
|
||||
class BaseInstaller
|
||||
{
|
||||
@ -35,7 +36,7 @@ public:
|
||||
virtual bool add(MinecraftInstance *to);
|
||||
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:
|
||||
virtual QString id() const = 0;
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* 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
|
||||
@ -38,6 +39,7 @@
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "settings/Setting.h"
|
||||
@ -51,15 +53,26 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
: QObject()
|
||||
{
|
||||
m_settings = settings;
|
||||
m_global_settings = globalSettings;
|
||||
m_rootDir = rootDir;
|
||||
|
||||
m_settings->registerSetting("name", "Unnamed Instance");
|
||||
m_settings->registerSetting("iconKey", "default");
|
||||
m_settings->registerSetting("notes", "");
|
||||
|
||||
m_settings->registerSetting("lastLaunchTime", 0);
|
||||
m_settings->registerSetting("totalTimePlayed", 0);
|
||||
m_settings->registerSetting("lastTimePlayed", 0);
|
||||
m_settings->registerSetting("InstanceType", "OneSix");
|
||||
|
||||
// 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
|
||||
// a locally stored instance
|
||||
if (!m_settings->getSetting("InstanceType"))
|
||||
m_settings->registerSetting("InstanceType", "");
|
||||
|
||||
// Custom Commands
|
||||
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
||||
@ -76,6 +89,14 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
|
||||
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
|
||||
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
|
||||
|
||||
// Managed Packs
|
||||
m_settings->registerSetting("ManagedPack", false);
|
||||
m_settings->registerSetting("ManagedPackType", "");
|
||||
m_settings->registerSetting("ManagedPackID", "");
|
||||
m_settings->registerSetting("ManagedPackName", "");
|
||||
m_settings->registerSetting("ManagedPackVersionID", "");
|
||||
m_settings->registerSetting("ManagedPackVersionName", "");
|
||||
}
|
||||
|
||||
QString BaseInstance::getPreLaunchCommand()
|
||||
@ -93,9 +114,59 @@ QString BaseInstance::getPostExitCommand()
|
||||
return settings()->get("PostExitCommand").toString();
|
||||
}
|
||||
|
||||
bool BaseInstance::isManagedPack() const
|
||||
{
|
||||
return m_settings->get("ManagedPack").toBool();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackType() const
|
||||
{
|
||||
return m_settings->get("ManagedPackType").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackID() const
|
||||
{
|
||||
return m_settings->get("ManagedPackID").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackName() const
|
||||
{
|
||||
return m_settings->get("ManagedPackName").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackVersionID() const
|
||||
{
|
||||
return m_settings->get("ManagedPackVersionID").toString();
|
||||
}
|
||||
|
||||
QString BaseInstance::getManagedPackVersionName() const
|
||||
{
|
||||
return m_settings->get("ManagedPackVersionName").toString();
|
||||
}
|
||||
|
||||
void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version)
|
||||
{
|
||||
m_settings->set("ManagedPack", true);
|
||||
m_settings->set("ManagedPackType", type);
|
||||
m_settings->set("ManagedPackID", id);
|
||||
m_settings->set("ManagedPackName", name);
|
||||
m_settings->set("ManagedPackVersionID", versionId);
|
||||
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
|
||||
{
|
||||
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
|
||||
auto lineSetting = m_settings->getSetting("ConsoleMaxLines");
|
||||
bool conversionOk = false;
|
||||
int maxLines = lineSetting->get().toInt(&conversionOk);
|
||||
if(!conversionOk)
|
||||
@ -108,7 +179,7 @@ int BaseInstance::getConsoleMaxLines() const
|
||||
|
||||
bool BaseInstance::shouldStopOnConsoleOverflow() const
|
||||
{
|
||||
return settings()->get("ConsoleOverflowStop").toBool();
|
||||
return m_settings->get("ConsoleOverflowStop").toBool();
|
||||
}
|
||||
|
||||
void BaseInstance::iconUpdated(QString key)
|
||||
@ -183,7 +254,7 @@ void BaseInstance::setRunning(bool running)
|
||||
|
||||
int64_t BaseInstance::totalTimePlayed() const
|
||||
{
|
||||
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
||||
qint64 current = m_settings->get("totalTimePlayed").toLongLong();
|
||||
if(m_isRunning)
|
||||
{
|
||||
QDateTime timeNow = QDateTime::currentDateTime();
|
||||
@ -199,7 +270,7 @@ int64_t BaseInstance::lastTimePlayed() const
|
||||
QDateTime timeNow = QDateTime::currentDateTime();
|
||||
return m_timeStarted.secsTo(timeNow);
|
||||
}
|
||||
return settings()->get("lastTimePlayed").toLongLong();
|
||||
return m_settings->get("lastTimePlayed").toLongLong();
|
||||
}
|
||||
|
||||
void BaseInstance::resetTimePlayed()
|
||||
@ -218,8 +289,10 @@ QString BaseInstance::instanceRoot() const
|
||||
return m_rootDir;
|
||||
}
|
||||
|
||||
SettingsObjectPtr BaseInstance::settings() const
|
||||
SettingsObjectPtr BaseInstance::settings()
|
||||
{
|
||||
loadSpecificSettings();
|
||||
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
@ -282,11 +355,11 @@ QString BaseInstance::name() const
|
||||
|
||||
QString BaseInstance::windowTitle() const
|
||||
{
|
||||
return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegExp("[ \n\r\t]+"), " ");
|
||||
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
|
||||
}
|
||||
|
||||
// FIXME: why is this here? move it to MinecraftInstance!!!
|
||||
QStringList BaseInstance::extraArguments() const
|
||||
QStringList BaseInstance::extraArguments()
|
||||
{
|
||||
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
||||
}
|
||||
@ -295,3 +368,8 @@ shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
|
||||
{
|
||||
return m_launchProcess;
|
||||
}
|
||||
|
||||
void BaseInstance::updateRuntimeContext()
|
||||
{
|
||||
// NOOP
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
*
|
||||
* 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
|
||||
@ -53,6 +54,7 @@
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "RuntimeContext.h"
|
||||
|
||||
class QDir;
|
||||
class Task;
|
||||
@ -139,13 +141,22 @@ public:
|
||||
QString getPostExitCommand();
|
||||
QString getWrapperCommand();
|
||||
|
||||
bool isManagedPack() const;
|
||||
QString getManagedPackType() const;
|
||||
QString getManagedPackID() const;
|
||||
QString getManagedPackName() const;
|
||||
QString getManagedPackVersionID() const;
|
||||
QString getManagedPackVersionName() const;
|
||||
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
|
||||
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
|
||||
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level)
|
||||
{
|
||||
return level;
|
||||
};
|
||||
|
||||
virtual QStringList extraArguments() const;
|
||||
virtual QStringList extraArguments();
|
||||
|
||||
/// Traits. Normally inside the version, depends on instance implementation.
|
||||
virtual QSet <QString> traits() const = 0;
|
||||
@ -161,9 +172,18 @@ public:
|
||||
/*!
|
||||
* \brief Gets this instance's settings object.
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||
@ -179,6 +199,7 @@ public:
|
||||
* Create envrironment variables for running the instance
|
||||
*/
|
||||
virtual QProcessEnvironment createEnvironment() = 0;
|
||||
virtual QProcessEnvironment createLaunchEnvironment() = 0;
|
||||
|
||||
/*!
|
||||
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
|
||||
@ -196,10 +217,16 @@ public:
|
||||
virtual QString instanceConfigFolder() const = 0;
|
||||
|
||||
/// get variables this instance exports
|
||||
virtual QMap<QString, QString> getVariables() const = 0;
|
||||
virtual QMap<QString, QString> getVariables() = 0;
|
||||
|
||||
virtual QString typeName() const = 0;
|
||||
|
||||
void updateRuntimeContext();
|
||||
RuntimeContext runtimeContext() const
|
||||
{
|
||||
return m_runtimeContext;
|
||||
}
|
||||
|
||||
bool hasVersionBroken() const
|
||||
{
|
||||
return m_hasBrokenVersion;
|
||||
@ -258,6 +285,11 @@ public:
|
||||
protected:
|
||||
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:
|
||||
/*!
|
||||
* \brief Signal emitted when properties relevant to the instance view change
|
||||
@ -280,12 +312,17 @@ protected: /* data */
|
||||
bool m_isRunning = false;
|
||||
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
||||
QDateTime m_timeStarted;
|
||||
RuntimeContext m_runtimeContext;
|
||||
|
||||
private: /* data */
|
||||
Status m_status = Status::Present;
|
||||
bool m_crashed = false;
|
||||
bool m_hasUpdate = false;
|
||||
bool m_hasBrokenVersion = false;
|
||||
|
||||
SettingsObjectWeakPtr m_global_settings;
|
||||
bool m_specific_settings_loaded = false;
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
||||
|
@ -25,6 +25,7 @@
|
||||
class BaseVersion
|
||||
{
|
||||
public:
|
||||
using Ptr = std::shared_ptr<BaseVersion>;
|
||||
virtual ~BaseVersion() {}
|
||||
/*!
|
||||
* A string used to identify this version in config files.
|
||||
@ -54,6 +55,4 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
||||
|
||||
Q_DECLARE_METATYPE(BaseVersionPtr)
|
||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||
|
@ -1,16 +1,36 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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 "BaseVersionList.h"
|
||||
@ -20,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++)
|
||||
{
|
||||
if (at(i)->descriptor() == descriptor)
|
||||
return at(i);
|
||||
}
|
||||
return BaseVersionPtr();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BaseVersionPtr BaseVersionList::getRecommended() const
|
||||
BaseVersion::Ptr BaseVersionList::getRecommended() const
|
||||
{
|
||||
if (count() <= 0)
|
||||
return BaseVersionPtr();
|
||||
return nullptr;
|
||||
else
|
||||
return at(0);
|
||||
}
|
||||
@ -46,12 +66,12 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
||||
if (index.row() > count())
|
||||
return QVariant();
|
||||
|
||||
BaseVersionPtr version = at(index.row());
|
||||
BaseVersion::Ptr version = at(index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case VersionPointerRole:
|
||||
return qVariantFromValue(version);
|
||||
return QVariant::fromValue(version);
|
||||
|
||||
case VersionRole:
|
||||
return version->name();
|
||||
@ -75,12 +95,12 @@ BaseVersionList::RoleList BaseVersionList::providesRoles() const
|
||||
int BaseVersionList::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
// Return count
|
||||
return count();
|
||||
return parent.isValid() ? 0 : count();
|
||||
}
|
||||
|
||||
int BaseVersionList::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return 1;
|
||||
return parent.isValid() ? 0 : 1;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> BaseVersionList::roleNames() const
|
||||
|
@ -70,7 +70,7 @@ public:
|
||||
virtual bool isLoaded() = 0;
|
||||
|
||||
//! 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.
|
||||
virtual int count() const = 0;
|
||||
@ -90,13 +90,13 @@ public:
|
||||
* \return A const pointer to the version with the given descriptor. NULL if
|
||||
* 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
|
||||
* 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.
|
||||
@ -117,5 +117,5 @@ slots:
|
||||
* then copies the versions and sets their parents correctly.
|
||||
* \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;
|
||||
};
|
||||
|
@ -4,8 +4,6 @@ project(application)
|
||||
|
||||
######## Sources and headers ########
|
||||
|
||||
include (UnitTest)
|
||||
|
||||
set(CORE_SOURCES
|
||||
# LOGIC - Base classes and infrastructure
|
||||
BaseInstaller.h
|
||||
@ -26,12 +24,15 @@ set(CORE_SOURCES
|
||||
NullInstance.h
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
MMCStrings.h
|
||||
MMCStrings.cpp
|
||||
StringUtils.h
|
||||
StringUtils.cpp
|
||||
RuntimeContext.h
|
||||
|
||||
# Basic instance manipulation tasks (derived from InstanceTask)
|
||||
InstanceCreationTask.h
|
||||
InstanceCreationTask.cpp
|
||||
InstanceCopyPrefs.h
|
||||
InstanceCopyPrefs.cpp
|
||||
InstanceCopyTask.h
|
||||
InstanceCopyTask.cpp
|
||||
InstanceImportTask.h
|
||||
@ -88,18 +89,18 @@ set(CORE_SOURCES
|
||||
# Time
|
||||
MMCTime.h
|
||||
MMCTime.cpp
|
||||
|
||||
MTPixmapCache.h
|
||||
)
|
||||
if (UNIX AND NOT CYGWIN AND NOT APPLE)
|
||||
set(CORE_SOURCES
|
||||
${CORE_SOURCES}
|
||||
|
||||
add_unit_test(FileSystem
|
||||
SOURCES FileSystem_test.cpp
|
||||
LIBS Launcher_logic
|
||||
DATA testdata
|
||||
)
|
||||
|
||||
add_unit_test(GZip
|
||||
SOURCES GZip_test.cpp
|
||||
LIBS Launcher_logic
|
||||
# MangoHud
|
||||
MangoHud.h
|
||||
MangoHud.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(PATHMATCHER_SOURCES
|
||||
# Path matchers
|
||||
@ -107,6 +108,7 @@ set(PATHMATCHER_SOURCES
|
||||
pathmatcher/IPathMatcher.h
|
||||
pathmatcher/MultiMatcher.h
|
||||
pathmatcher/RegexpMatcher.h
|
||||
pathmatcher/SimplePrefixMatcher.h
|
||||
)
|
||||
|
||||
set(NET_SOURCES
|
||||
@ -124,10 +126,13 @@ set(NET_SOURCES
|
||||
net/NetAction.h
|
||||
net/NetJob.cpp
|
||||
net/NetJob.h
|
||||
net/NetUtils.h
|
||||
net/PasteUpload.cpp
|
||||
net/PasteUpload.h
|
||||
net/Sink.h
|
||||
net/Validator.h
|
||||
net/Upload.cpp
|
||||
net/Upload.h
|
||||
)
|
||||
|
||||
# Game launch logic
|
||||
@ -162,19 +167,13 @@ set(UPDATE_SOURCES
|
||||
updater/UpdateChecker.cpp
|
||||
updater/DownloadTask.h
|
||||
updater/DownloadTask.cpp
|
||||
updater/ExternalUpdater.h
|
||||
)
|
||||
|
||||
add_unit_test(UpdateChecker
|
||||
SOURCES updater/UpdateChecker_test.cpp
|
||||
LIBS Launcher_logic
|
||||
DATA updater/testdata
|
||||
)
|
||||
|
||||
add_unit_test(DownloadTask
|
||||
SOURCES updater/DownloadTask_test.cpp
|
||||
LIBS Launcher_logic
|
||||
DATA updater/testdata
|
||||
)
|
||||
set(MAC_UPDATE_SOURCES
|
||||
updater/MacSparkleUpdater.h
|
||||
updater/MacSparkleUpdater.mm
|
||||
)
|
||||
|
||||
# Backend for the news bar... there's usually no news.
|
||||
set(NEWS_SOURCES
|
||||
@ -304,8 +303,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/Rule.h
|
||||
minecraft/OneSixVersionFormat.cpp
|
||||
minecraft/OneSixVersionFormat.h
|
||||
minecraft/OpSys.cpp
|
||||
minecraft/OpSys.h
|
||||
minecraft/ParseUtils.cpp
|
||||
minecraft/ParseUtils.h
|
||||
minecraft/ProfileUtils.cpp
|
||||
@ -313,6 +310,8 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/Library.cpp
|
||||
minecraft/Library.h
|
||||
minecraft/MojangDownloadInfo.h
|
||||
minecraft/VanillaInstanceCreationTask.cpp
|
||||
minecraft/VanillaInstanceCreationTask.h
|
||||
minecraft/VersionFile.cpp
|
||||
minecraft/VersionFile.h
|
||||
minecraft/VersionFilterData.h
|
||||
@ -322,19 +321,36 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/WorldList.h
|
||||
minecraft/WorldList.cpp
|
||||
|
||||
minecraft/mod/MetadataHandler.h
|
||||
minecraft/mod/Mod.h
|
||||
minecraft/mod/Mod.cpp
|
||||
minecraft/mod/ModDetails.h
|
||||
minecraft/mod/ModFolderModel.h
|
||||
minecraft/mod/ModFolderModel.cpp
|
||||
minecraft/mod/ModFolderLoadTask.h
|
||||
minecraft/mod/ModFolderLoadTask.cpp
|
||||
minecraft/mod/LocalModParseTask.h
|
||||
minecraft/mod/LocalModParseTask.cpp
|
||||
minecraft/mod/Resource.h
|
||||
minecraft/mod/Resource.cpp
|
||||
minecraft/mod/ResourceFolderModel.h
|
||||
minecraft/mod/ResourceFolderModel.cpp
|
||||
minecraft/mod/ResourcePack.h
|
||||
minecraft/mod/ResourcePack.cpp
|
||||
minecraft/mod/ResourcePackFolderModel.h
|
||||
minecraft/mod/ResourcePackFolderModel.cpp
|
||||
minecraft/mod/TexturePack.h
|
||||
minecraft/mod/TexturePack.cpp
|
||||
minecraft/mod/TexturePackFolderModel.h
|
||||
minecraft/mod/TexturePackFolderModel.cpp
|
||||
minecraft/mod/ShaderPackFolderModel.h
|
||||
minecraft/mod/tasks/BasicFolderLoadTask.h
|
||||
minecraft/mod/tasks/ModFolderLoadTask.h
|
||||
minecraft/mod/tasks/ModFolderLoadTask.cpp
|
||||
minecraft/mod/tasks/LocalModParseTask.h
|
||||
minecraft/mod/tasks/LocalModParseTask.cpp
|
||||
minecraft/mod/tasks/LocalModUpdateTask.h
|
||||
minecraft/mod/tasks/LocalModUpdateTask.cpp
|
||||
minecraft/mod/tasks/LocalResourcePackParseTask.h
|
||||
minecraft/mod/tasks/LocalResourcePackParseTask.cpp
|
||||
minecraft/mod/tasks/LocalTexturePackParseTask.h
|
||||
minecraft/mod/tasks/LocalTexturePackParseTask.cpp
|
||||
|
||||
# Assets
|
||||
minecraft/AssetsUtils.h
|
||||
@ -352,52 +368,6 @@ set(MINECRAFT_SOURCES
|
||||
mojang/PackageManifest.cpp
|
||||
minecraft/Agent.h)
|
||||
|
||||
add_unit_test(GradleSpecifier
|
||||
SOURCES minecraft/GradleSpecifier_test.cpp
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(PackageManifest
|
||||
mojang/PackageManifest_test.cpp
|
||||
)
|
||||
target_link_libraries(PackageManifest
|
||||
Launcher_logic
|
||||
Qt5::Test
|
||||
)
|
||||
target_include_directories(PackageManifest
|
||||
PRIVATE ../cmake/UnitTest/
|
||||
)
|
||||
add_test(
|
||||
NAME PackageManifest
|
||||
COMMAND PackageManifest
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
add_unit_test(MojangVersionFormat
|
||||
SOURCES minecraft/MojangVersionFormat_test.cpp
|
||||
LIBS Launcher_logic
|
||||
DATA minecraft/testdata
|
||||
)
|
||||
|
||||
add_unit_test(Library
|
||||
SOURCES minecraft/Library_test.cpp
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
# FIXME: shares data with FileSystem test
|
||||
add_unit_test(ModFolderModel
|
||||
SOURCES minecraft/mod/ModFolderModel_test.cpp
|
||||
DATA testdata
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
add_unit_test(ParseUtils
|
||||
SOURCES minecraft/ParseUtils_test.cpp
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
# the screenshots feature
|
||||
set(SCREENSHOTS_SOURCES
|
||||
screenshots/Screenshot.h
|
||||
@ -411,15 +381,14 @@ set(TASKS_SOURCES
|
||||
# Tasks
|
||||
tasks/Task.h
|
||||
tasks/Task.cpp
|
||||
tasks/ConcurrentTask.h
|
||||
tasks/ConcurrentTask.cpp
|
||||
tasks/SequentialTask.h
|
||||
tasks/SequentialTask.cpp
|
||||
tasks/MultipleOptionsTask.h
|
||||
tasks/MultipleOptionsTask.cpp
|
||||
)
|
||||
|
||||
add_unit_test(Task
|
||||
SOURCES tasks/Task_test.cpp
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
set(SETTINGS_SOURCES
|
||||
# Settings
|
||||
settings/INIFile.cpp
|
||||
@ -436,11 +405,6 @@ set(SETTINGS_SOURCES
|
||||
settings/SettingsObject.h
|
||||
)
|
||||
|
||||
add_unit_test(INIFile
|
||||
SOURCES settings/INIFile_test.cpp
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
set(JAVA_SOURCES
|
||||
java/JavaChecker.h
|
||||
java/JavaChecker.cpp
|
||||
@ -456,11 +420,6 @@ set(JAVA_SOURCES
|
||||
java/JavaVersion.cpp
|
||||
)
|
||||
|
||||
add_unit_test(JavaVersion
|
||||
SOURCES java/JavaVersion_test.cpp
|
||||
LIBS Launcher_logic
|
||||
)
|
||||
|
||||
set(TRANSLATIONS_SOURCES
|
||||
translations/TranslationsModel.h
|
||||
translations/TranslationsModel.cpp
|
||||
@ -497,13 +456,26 @@ set(META_SOURCES
|
||||
)
|
||||
|
||||
set(API_SOURCES
|
||||
modplatform/ModIndex.h
|
||||
modplatform/ModIndex.cpp
|
||||
|
||||
modplatform/ModAPI.h
|
||||
|
||||
modplatform/EnsureMetadataTask.h
|
||||
modplatform/EnsureMetadataTask.cpp
|
||||
|
||||
modplatform/CheckUpdateTask.h
|
||||
|
||||
modplatform/flame/FlameAPI.h
|
||||
modplatform/flame/FlameAPI.cpp
|
||||
modplatform/modrinth/ModrinthAPI.h
|
||||
|
||||
modplatform/modrinth/ModrinthAPI.cpp
|
||||
modplatform/helpers/NetworkModAPI.h
|
||||
modplatform/helpers/NetworkModAPI.cpp
|
||||
modplatform/helpers/HashUtils.h
|
||||
modplatform/helpers/HashUtils.cpp
|
||||
modplatform/helpers/OverrideUtils.h
|
||||
modplatform/helpers/OverrideUtils.cpp
|
||||
)
|
||||
|
||||
set(FTB_SOURCES
|
||||
@ -527,6 +499,10 @@ set(FLAME_SOURCES
|
||||
modplatform/flame/PackManifest.cpp
|
||||
modplatform/flame/FileResolvingTask.h
|
||||
modplatform/flame/FileResolvingTask.cpp
|
||||
modplatform/flame/FlameCheckUpdate.cpp
|
||||
modplatform/flame/FlameCheckUpdate.h
|
||||
modplatform/flame/FlameInstanceCreationTask.h
|
||||
modplatform/flame/FlameInstanceCreationTask.cpp
|
||||
)
|
||||
|
||||
set(MODRINTH_SOURCES
|
||||
@ -534,6 +510,10 @@ set(MODRINTH_SOURCES
|
||||
modplatform/modrinth/ModrinthPackIndex.h
|
||||
modplatform/modrinth/ModrinthPackManifest.cpp
|
||||
modplatform/modrinth/ModrinthPackManifest.h
|
||||
modplatform/modrinth/ModrinthCheckUpdate.cpp
|
||||
modplatform/modrinth/ModrinthCheckUpdate.h
|
||||
modplatform/modrinth/ModrinthInstanceCreationTask.cpp
|
||||
modplatform/modrinth/ModrinthInstanceCreationTask.h
|
||||
)
|
||||
|
||||
set(MODPACKSCH_SOURCES
|
||||
@ -543,6 +523,12 @@ set(MODPACKSCH_SOURCES
|
||||
modplatform/modpacksch/FTBPackManifest.cpp
|
||||
)
|
||||
|
||||
set(PACKWIZ_SOURCES
|
||||
modplatform/packwiz/Packwiz.h
|
||||
modplatform/packwiz/Packwiz.cpp
|
||||
)
|
||||
|
||||
|
||||
set(TECHNIC_SOURCES
|
||||
modplatform/technic/SingleZipPackInstallTask.h
|
||||
modplatform/technic/SingleZipPackInstallTask.cpp
|
||||
@ -565,16 +551,26 @@ set(ATLAUNCHER_SOURCES
|
||||
modplatform/atlauncher/ATLShareCode.h
|
||||
)
|
||||
|
||||
add_unit_test(Index
|
||||
SOURCES meta/Index_test.cpp
|
||||
LIBS Launcher_logic
|
||||
######## 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}"
|
||||
)
|
||||
|
||||
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 ################################
|
||||
|
||||
# we need zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
set(LOGIC_SOURCES
|
||||
${CORE_SOURCES}
|
||||
${PATHMATCHER_SOURCES}
|
||||
@ -596,14 +592,21 @@ set(LOGIC_SOURCES
|
||||
${FLAME_SOURCES}
|
||||
${MODRINTH_SOURCES}
|
||||
${MODPACKSCH_SOURCES}
|
||||
${PACKWIZ_SOURCES}
|
||||
${TECHNIC_SOURCES}
|
||||
${ATLAUNCHER_SOURCES}
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
|
||||
endif()
|
||||
|
||||
SET(LAUNCHER_SOURCES
|
||||
# Application base
|
||||
Application.h
|
||||
Application.cpp
|
||||
DataMigrationTask.h
|
||||
DataMigrationTask.cpp
|
||||
UpdateController.cpp
|
||||
UpdateController.h
|
||||
ApplicationMessage.h
|
||||
@ -627,9 +630,12 @@ SET(LAUNCHER_SOURCES
|
||||
resources/pe_light/pe_light.qrc
|
||||
resources/pe_colored/pe_colored.qrc
|
||||
resources/pe_blue/pe_blue.qrc
|
||||
resources/breeze_dark/breeze_dark.qrc
|
||||
resources/breeze_light/breeze_light.qrc
|
||||
resources/OSX/OSX.qrc
|
||||
resources/iOS/iOS.qrc
|
||||
resources/flat/flat.qrc
|
||||
resources/flat_white/flat_white.qrc
|
||||
resources/documents/documents.qrc
|
||||
../${Launcher_Branding_LogoQRC}
|
||||
|
||||
@ -677,6 +683,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/themes/ITheme.h
|
||||
ui/themes/SystemTheme.cpp
|
||||
ui/themes/SystemTheme.h
|
||||
ui/themes/ThemeManager.cpp
|
||||
ui/themes/ThemeManager.h
|
||||
|
||||
# Processes
|
||||
LaunchController.h
|
||||
@ -695,10 +703,14 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/BasePageProvider.h
|
||||
|
||||
# GUI - instance pages
|
||||
ui/pages/instance/ExternalResourcesPage.cpp
|
||||
ui/pages/instance/ExternalResourcesPage.h
|
||||
ui/pages/instance/GameOptionsPage.cpp
|
||||
ui/pages/instance/GameOptionsPage.h
|
||||
ui/pages/instance/VersionPage.cpp
|
||||
ui/pages/instance/VersionPage.h
|
||||
ui/pages/instance/ManagedPackPage.cpp
|
||||
ui/pages/instance/ManagedPackPage.h
|
||||
ui/pages/instance/TexturePackPage.h
|
||||
ui/pages/instance/ResourcePackPage.h
|
||||
ui/pages/instance/ShaderPackPage.h
|
||||
@ -756,6 +768,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.h
|
||||
ui/pages/modplatform/atlauncher/AtlPage.cpp
|
||||
ui/pages/modplatform/atlauncher/AtlPage.h
|
||||
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.cpp
|
||||
ui/pages/modplatform/atlauncher/AtlUserInteractionSupportImpl.h
|
||||
|
||||
ui/pages/modplatform/ftb/FtbFilterModel.cpp
|
||||
ui/pages/modplatform/ftb/FtbFilterModel.h
|
||||
@ -813,6 +827,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/ExportInstanceDialog.h
|
||||
ui/dialogs/IconPickerDialog.cpp
|
||||
ui/dialogs/IconPickerDialog.h
|
||||
ui/dialogs/ImportResourcePackDialog.cpp
|
||||
ui/dialogs/ImportResourcePackDialog.h
|
||||
ui/dialogs/LoginDialog.cpp
|
||||
ui/dialogs/LoginDialog.h
|
||||
ui/dialogs/MSALoginDialog.cpp
|
||||
@ -823,6 +839,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/NewComponentDialog.h
|
||||
ui/dialogs/NewInstanceDialog.cpp
|
||||
ui/dialogs/NewInstanceDialog.h
|
||||
ui/dialogs/NewsDialog.cpp
|
||||
ui/dialogs/NewsDialog.h
|
||||
ui/pagedialog/PageDialog.cpp
|
||||
ui/pagedialog/PageDialog.h
|
||||
ui/dialogs/ProgressDialog.cpp
|
||||
@ -837,6 +855,14 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/SkinUploadDialog.h
|
||||
ui/dialogs/ModDownloadDialog.cpp
|
||||
ui/dialogs/ModDownloadDialog.h
|
||||
ui/dialogs/ScrollMessageBox.cpp
|
||||
ui/dialogs/ScrollMessageBox.h
|
||||
ui/dialogs/BlockedModsDialog.cpp
|
||||
ui/dialogs/BlockedModsDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.h
|
||||
ui/dialogs/ChooseProviderDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.h
|
||||
|
||||
# GUI - widgets
|
||||
ui/widgets/Common.cpp
|
||||
@ -859,8 +885,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/LineSeparator.h
|
||||
ui/widgets/LogView.cpp
|
||||
ui/widgets/LogView.h
|
||||
ui/widgets/MCModInfoFrame.cpp
|
||||
ui/widgets/MCModInfoFrame.h
|
||||
ui/widgets/InfoFrame.cpp
|
||||
ui/widgets/InfoFrame.h
|
||||
ui/widgets/ModFilterWidget.cpp
|
||||
ui/widgets/ModFilterWidget.h
|
||||
ui/widgets/ModListView.cpp
|
||||
@ -868,6 +894,12 @@ SET(LAUNCHER_SOURCES
|
||||
ui/widgets/PageContainer.cpp
|
||||
ui/widgets/PageContainer.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/VersionListView.cpp
|
||||
ui/widgets/VersionListView.h
|
||||
ui/widgets/VersionSelectWidget.cpp
|
||||
@ -891,7 +923,7 @@ SET(LAUNCHER_SOURCES
|
||||
ui/instanceview/VisualGroup.h
|
||||
)
|
||||
|
||||
qt5_wrap_ui(LAUNCHER_UI
|
||||
qt_wrap_ui(LAUNCHER_UI
|
||||
ui/setupwizard/PasteWizardPage.ui
|
||||
ui/pages/global/AccountListPage.ui
|
||||
ui/pages/global/JavaPage.ui
|
||||
@ -900,7 +932,7 @@ qt5_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/global/ProxyPage.ui
|
||||
ui/pages/global/MinecraftPage.ui
|
||||
ui/pages/global/ExternalToolsPage.ui
|
||||
ui/pages/instance/ModFolderPage.ui
|
||||
ui/pages/instance/ExternalResourcesPage.ui
|
||||
ui/pages/instance/NotesPage.ui
|
||||
ui/pages/instance/LogPage.ui
|
||||
ui/pages/instance/ServersPage.ui
|
||||
@ -908,6 +940,7 @@ qt5_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/instance/OtherLogsPage.ui
|
||||
ui/pages/instance/InstanceSettingsPage.ui
|
||||
ui/pages/instance/VersionPage.ui
|
||||
ui/pages/instance/ManagedPackPage.ui
|
||||
ui/pages/instance/WorldListPage.ui
|
||||
ui/pages/instance/ScreenshotsPage.ui
|
||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
||||
@ -922,7 +955,7 @@ qt5_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/modplatform/technic/TechnicPage.ui
|
||||
ui/widgets/InstanceCardWidget.ui
|
||||
ui/widgets/CustomCommands.ui
|
||||
ui/widgets/MCModInfoFrame.ui
|
||||
ui/widgets/InfoFrame.ui
|
||||
ui/widgets/ModFilterWidget.ui
|
||||
ui/dialogs/CopyInstanceDialog.ui
|
||||
ui/dialogs/ProfileSetupDialog.ui
|
||||
@ -930,25 +963,32 @@ qt5_wrap_ui(LAUNCHER_UI
|
||||
ui/dialogs/NewInstanceDialog.ui
|
||||
ui/dialogs/UpdateDialog.ui
|
||||
ui/dialogs/NewComponentDialog.ui
|
||||
ui/dialogs/NewsDialog.ui
|
||||
ui/dialogs/ProfileSelectDialog.ui
|
||||
ui/dialogs/SkinUploadDialog.ui
|
||||
ui/dialogs/ExportInstanceDialog.ui
|
||||
ui/dialogs/IconPickerDialog.ui
|
||||
ui/dialogs/ImportResourcePackDialog.ui
|
||||
ui/dialogs/MSALoginDialog.ui
|
||||
ui/dialogs/OfflineLoginDialog.ui
|
||||
ui/dialogs/AboutDialog.ui
|
||||
ui/dialogs/LoginDialog.ui
|
||||
ui/dialogs/EditAccountDialog.ui
|
||||
ui/dialogs/ReviewMessageBox.ui
|
||||
ui/dialogs/ScrollMessageBox.ui
|
||||
ui/dialogs/BlockedModsDialog.ui
|
||||
ui/dialogs/ChooseProviderDialog.ui
|
||||
)
|
||||
|
||||
qt5_add_resources(LAUNCHER_RESOURCES
|
||||
qt_add_resources(LAUNCHER_RESOURCES
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
resources/pe_dark/pe_dark.qrc
|
||||
resources/pe_light/pe_light.qrc
|
||||
resources/pe_colored/pe_colored.qrc
|
||||
resources/pe_blue/pe_blue.qrc
|
||||
resources/breeze_dark/breeze_dark.qrc
|
||||
resources/breeze_light/breeze_light.qrc
|
||||
resources/OSX/OSX.qrc
|
||||
resources/iOS/iOS.qrc
|
||||
resources/flat/flat.qrc
|
||||
@ -958,35 +998,61 @@ qt5_add_resources(LAUNCHER_RESOURCES
|
||||
|
||||
######## Windows resource files ########
|
||||
if(WIN32)
|
||||
set(LAUNCHER_RCS ../${Launcher_Branding_WindowsRC})
|
||||
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
|
||||
endif()
|
||||
|
||||
# Add executable
|
||||
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
|
||||
systeminfo
|
||||
Launcher_classparser
|
||||
Launcher_murmur2
|
||||
nbt++
|
||||
${ZLIB_LIBRARIES}
|
||||
optional-bare
|
||||
tomlc99
|
||||
tomlplusplus::tomlplusplus
|
||||
BuildConfig
|
||||
Katabasis
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
ghcFilesystem::ghc_filesystem
|
||||
)
|
||||
|
||||
if (UNIX AND NOT CYGWIN AND NOT APPLE)
|
||||
target_link_libraries(Launcher_logic
|
||||
gamemode
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(Launcher_logic
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Xml
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
${Launcher_QT_LIBS}
|
||||
)
|
||||
target_link_libraries(Launcher_logic
|
||||
Qt5::Core
|
||||
Qt5::Xml
|
||||
Qt5::Network
|
||||
Qt5::Concurrent
|
||||
Qt5::Gui
|
||||
)
|
||||
target_link_libraries(Launcher_logic
|
||||
Launcher_iconfix
|
||||
QuaZip::QuaZip
|
||||
hoedown
|
||||
LocalPeer
|
||||
Launcher_rainbow
|
||||
)
|
||||
if(APPLE)
|
||||
set(CMAKE_MACOSX_RPATH 1)
|
||||
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks/")
|
||||
|
||||
file(DOWNLOAD ${MACOSX_SPARKLE_DOWNLOAD_URL} ${CMAKE_BINARY_DIR}/Sparkle.tar.xz EXPECTED_HASH SHA256=${MACOSX_SPARKLE_SHA256})
|
||||
file(ARCHIVE_EXTRACT INPUT ${CMAKE_BINARY_DIR}/Sparkle.tar.xz DESTINATION ${CMAKE_BINARY_DIR}/frameworks/Sparkle)
|
||||
|
||||
find_library(SPARKLE_FRAMEWORK Sparkle "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||
target_link_libraries(Launcher_logic
|
||||
"-framework AppKit"
|
||||
"-framework Carbon"
|
||||
"-framework Foundation"
|
||||
"-framework ApplicationServices"
|
||||
)
|
||||
target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
|
||||
endif()
|
||||
|
||||
target_link_libraries(Launcher_logic)
|
||||
|
||||
@ -1009,8 +1075,16 @@ install(TARGETS ${Launcher_Name}
|
||||
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
|
||||
)
|
||||
|
||||
if (UNIX AND APPLE)
|
||||
# Add Sparkle updater
|
||||
# It has to be copied here instead of just allowing fixup_bundle to install it, otherwise essential parts of
|
||||
# the framework aren't installed
|
||||
install(DIRECTORY ${MACOSX_SPARKLE_DIR}/Sparkle.framework DESTINATION ${FRAMEWORK_DEST_DIR} USE_SOURCE_PERMISSIONS)
|
||||
endif()
|
||||
|
||||
#### The bundle mess! ####
|
||||
# Bundle utilities are used to complete the portable packages - they add all the libraries that would otherwise be missing on the target system.
|
||||
# NOTE: it seems that this absolutely has to be here, and nowhere else.
|
||||
@ -1021,78 +1095,99 @@ if(INSTALL_BUNDLE STREQUAL "full")
|
||||
COMPONENT Runtime
|
||||
)
|
||||
# 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(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "tga|tiff|mng" EXCLUDE
|
||||
)
|
||||
# Icon engines
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
||||
CONFIGURATIONS Release MinSizeRel
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "fontawesome" EXCLUDE
|
||||
)
|
||||
# Platform plugins
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||
)
|
||||
# Style plugins
|
||||
if(EXISTS "${QT_PLUGINS_DIR}/styles")
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/styles"
|
||||
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 "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
)
|
||||
# Icon engines
|
||||
endif()
|
||||
# TLS plugins (Qt 6 only)
|
||||
if(EXISTS "${QT_PLUGINS_DIR}/tls")
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
||||
CONFIGURATIONS Debug RelWithDebInfo ""
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "fontawesome" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
REGEX "\\.dSYM" EXCLUDE
|
||||
PATTERN "*qopensslbackend*" EXCLUDE
|
||||
PATTERN "*qcertonlybackend*" EXCLUDE
|
||||
)
|
||||
# Platform plugins
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/tls"
|
||||
CONFIGURATIONS Release MinSizeRel
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "dd\\." EXCLUDE
|
||||
REGEX "_debug\\." 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()
|
||||
endif()
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||
|
@ -1,18 +1,38 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* Authors: Orochimarufan <orochimarufan.x3@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.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
*
|
||||
* Copyright 2013-2021 MultiMC Contributors
|
||||
*
|
||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Commandline.h"
|
||||
@ -47,7 +67,7 @@ QStringList splitArgs(QString args)
|
||||
if (cchar == '\\')
|
||||
escape = true;
|
||||
else if (cchar == inquotes)
|
||||
inquotes = 0;
|
||||
inquotes = QChar::Null;
|
||||
else
|
||||
current += cchar;
|
||||
// otherwise
|
||||
@ -72,412 +92,4 @@ QStringList splitArgs(QString args)
|
||||
argv << current;
|
||||
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
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QHash>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
@ -39,212 +34,4 @@ namespace Commandline
|
||||
* @return a QStringList containing all arguments
|
||||
*/
|
||||
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;
|
||||
};
|
@ -119,7 +119,7 @@ bool openDirectory(const QString &path, bool ensureExists)
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -140,7 +140,7 @@ bool openFile(const QString &path)
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -158,7 +158,7 @@ bool openFile(const QString &application, const QString &path, const QString &wo
|
||||
qDebug() << "Opening file" << path << "using" << application;
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
@ -178,7 +178,7 @@ bool run(const QString &application, const QStringList &args, const QString &wor
|
||||
{
|
||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||
#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
|
||||
return IndirectOpen([&]()
|
||||
@ -203,7 +203,7 @@ bool openUrl(const QUrl &url)
|
||||
return QDesktopServices::openUrl(url);
|
||||
};
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!APPLICATION->isFlatpak())
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
@ -216,4 +216,13 @@ bool openUrl(const QUrl &url)
|
||||
#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.
|
||||
*/
|
||||
bool openUrl(const QUrl &url);
|
||||
|
||||
bool isFlatpak();
|
||||
}
|
||||
|
@ -1,77 +1,123 @@
|
||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
||||
// 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.
|
||||
*/
|
||||
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QSaveFile>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QSaveFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
#include <QUrl>
|
||||
|
||||
#include "DesktopServices.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <sys/utime.h>
|
||||
#include <winnls.h>
|
||||
#include <shobjidl.h>
|
||||
#include <objbase.h>
|
||||
#include <objidl.h>
|
||||
#include <shlguid.h>
|
||||
#include <shlobj.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <objbase.h>
|
||||
#include <objidl.h>
|
||||
#include <shlguid.h>
|
||||
#include <shlobj.h>
|
||||
#include <shobjidl.h>
|
||||
#include <sys/utime.h>
|
||||
#include <versionhelpers.h>
|
||||
#include <windows.h>
|
||||
#include <winnls.h>
|
||||
#include <string>
|
||||
#else
|
||||
#include <utime.h>
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
// Snippet from https://github.com/gulrak/filesystem#using-it-as-single-file-header
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <Availability.h> // for deployment target to support pre-catalina targets without std::fs
|
||||
#endif // __APPLE__
|
||||
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
|
||||
#if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
|
||||
#define GHC_USE_STD_FS
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
#endif // MacOS min version check
|
||||
#endif // Other OSes version check
|
||||
|
||||
#ifndef GHC_USE_STD_FS
|
||||
#include <ghc/filesystem.hpp>
|
||||
namespace fs = ghc::filesystem;
|
||||
#endif
|
||||
|
||||
namespace FS {
|
||||
|
||||
void ensureExists(const QDir &dir)
|
||||
void ensureExists(const QDir& dir)
|
||||
{
|
||||
if (!QDir().mkpath(dir.absolutePath()))
|
||||
{
|
||||
throw FileSystemException("Unable to create folder " + dir.dirName() + " (" +
|
||||
dir.absolutePath() + ")");
|
||||
if (!QDir().mkpath(dir.absolutePath())) {
|
||||
throw FileSystemException("Unable to create folder " + dir.dirName() + " (" + dir.absolutePath() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
void write(const QString &filename, const QByteArray &data)
|
||||
void write(const QString& filename, const QByteArray& data)
|
||||
{
|
||||
ensureExists(QFileInfo(filename).dir());
|
||||
QSaveFile file(filename);
|
||||
if (!file.open(QSaveFile::WriteOnly))
|
||||
{
|
||||
throw FileSystemException("Couldn't open " + filename + " for writing: " +
|
||||
file.errorString());
|
||||
if (!file.open(QSaveFile::WriteOnly)) {
|
||||
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||
}
|
||||
if (data.size() != file.write(data))
|
||||
{
|
||||
throw FileSystemException("Error writing data to " + filename + ": " +
|
||||
file.errorString());
|
||||
if (data.size() != file.write(data)) {
|
||||
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
|
||||
}
|
||||
if (!file.commit())
|
||||
{
|
||||
throw FileSystemException("Error while committing data to " + filename + ": " +
|
||||
file.errorString());
|
||||
if (!file.commit()) {
|
||||
throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray read(const QString &filename)
|
||||
QByteArray read(const QString& filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (!file.open(QFile::ReadOnly))
|
||||
{
|
||||
throw FileSystemException("Unable to open " + filename + " for reading: " +
|
||||
file.errorString());
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
throw FileSystemException("Unable to open " + filename + " for reading: " + file.errorString());
|
||||
}
|
||||
const qint64 size = file.size();
|
||||
QByteArray data(int(size), 0);
|
||||
const qint64 ret = file.read(data.data(), size);
|
||||
if (ret == -1 || ret != size)
|
||||
{
|
||||
throw FileSystemException("Error reading data from " + filename + ": " +
|
||||
file.errorString());
|
||||
if (ret == -1 || ret != size) {
|
||||
throw FileSystemException("Error reading data from " + filename + ": " + file.errorString());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@ -105,149 +151,113 @@ bool ensureFolderPathExists(QString foldernamepath)
|
||||
return success;
|
||||
}
|
||||
|
||||
bool copy::operator()(const QString &offset)
|
||||
/// @brief Copies a directory and it's contents from src to dest
|
||||
/// @param offset subdirectory form src to copy to dest
|
||||
/// @return if there was an error during the filecopy
|
||||
bool copy::operator()(const QString& offset, bool dryRun)
|
||||
{
|
||||
//NOTE always deep copy on windows. the alternatives are too messy.
|
||||
#if defined Q_OS_WIN32
|
||||
using copy_opts = fs::copy_options;
|
||||
m_copied = 0; // reset counter
|
||||
|
||||
// NOTE always deep copy on windows. the alternatives are too messy.
|
||||
#if defined Q_OS_WIN32
|
||||
m_followSymlinks = true;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
auto src = PathCombine(m_src.absolutePath(), offset);
|
||||
auto dst = PathCombine(m_dst.absolutePath(), offset);
|
||||
|
||||
QFileInfo currentSrc(src);
|
||||
if (!currentSrc.exists())
|
||||
return false;
|
||||
std::error_code err;
|
||||
|
||||
if(!m_followSymlinks && currentSrc.isSymLink())
|
||||
{
|
||||
qDebug() << "creating symlink" << src << " - " << dst;
|
||||
if (!ensureFilePathExists(dst))
|
||||
{
|
||||
qWarning() << "Cannot create path!";
|
||||
return false;
|
||||
fs::copy_options opt = copy_opts::none;
|
||||
|
||||
// The default behavior is to follow symlinks
|
||||
if (!m_followSymlinks)
|
||||
opt |= copy_opts::copy_symlinks;
|
||||
|
||||
// Function that'll do the actual copying
|
||||
auto copy_file = [&](QString src_path, QString relative_dst_path) {
|
||||
if (m_matcher && (m_matcher->matches(relative_dst_path) != m_whitelist))
|
||||
return;
|
||||
|
||||
auto dst_path = PathCombine(dst, relative_dst_path);
|
||||
if (!dryRun) {
|
||||
ensureFilePathExists(dst_path);
|
||||
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
|
||||
}
|
||||
return QFile::link(currentSrc.symLinkTarget(), dst);
|
||||
}
|
||||
else if(currentSrc.isFile())
|
||||
{
|
||||
qDebug() << "copying file" << src << " - " << dst;
|
||||
if (!ensureFilePathExists(dst))
|
||||
{
|
||||
qWarning() << "Cannot create path!";
|
||||
return false;
|
||||
if (err) {
|
||||
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
||||
qDebug() << "Source file:" << src_path;
|
||||
qDebug() << "Destination file:" << dst_path;
|
||||
}
|
||||
return QFile::copy(src, dst);
|
||||
m_copied++;
|
||||
emit fileCopied(relative_dst_path);
|
||||
};
|
||||
|
||||
// We can't use copy_opts::recursive because we need to take into account the
|
||||
// blacklisted paths, so we iterate over the source directory, and if there's no blacklist
|
||||
// match, we copy the file.
|
||||
QDir src_dir(src);
|
||||
QDirIterator source_it(src, QDir::Filter::Files | QDir::Filter::Hidden, QDirIterator::Subdirectories);
|
||||
|
||||
while (source_it.hasNext()) {
|
||||
auto src_path = source_it.next();
|
||||
auto relative_path = src_dir.relativeFilePath(src_path);
|
||||
|
||||
copy_file(src_path, relative_path);
|
||||
}
|
||||
else if(currentSrc.isDir())
|
||||
{
|
||||
qDebug() << "recursing" << offset;
|
||||
if (!ensureFolderPathExists(dst))
|
||||
{
|
||||
qWarning() << "Cannot create path!";
|
||||
return false;
|
||||
}
|
||||
QDir currentDir(src);
|
||||
for(auto & f : currentDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System))
|
||||
{
|
||||
auto inner_offset = PathCombine(offset, f);
|
||||
// ignore and skip stuff that matches the blacklist.
|
||||
if(m_blacklist && m_blacklist->matches(inner_offset))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if(!operator()(inner_offset))
|
||||
{
|
||||
qWarning() << "Failed to copy" << inner_offset;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qCritical() << "Copy ERROR: Unknown filesystem object:" << src;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
// If the root src is not a directory, the previous iterator won't run.
|
||||
if (!fs::is_directory(StringUtils::toStdString(src)))
|
||||
copy_file(src, "");
|
||||
|
||||
return err.value() == 0;
|
||||
}
|
||||
|
||||
bool deletePath(QString path)
|
||||
{
|
||||
bool OK = true;
|
||||
QFileInfo finfo(path);
|
||||
if(finfo.isFile()) {
|
||||
return QFile::remove(path);
|
||||
std::error_code err;
|
||||
|
||||
fs::remove_all(StringUtils::toStdString(path), err);
|
||||
|
||||
if (err) {
|
||||
qWarning() << "Failed to remove files:" << QString::fromStdString(err.message());
|
||||
}
|
||||
|
||||
QDir dir(path);
|
||||
|
||||
if (!dir.exists())
|
||||
{
|
||||
return OK;
|
||||
}
|
||||
auto allEntries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden |
|
||||
QDir::AllDirs | QDir::Files,
|
||||
QDir::DirsFirst);
|
||||
|
||||
for(auto & info: allEntries)
|
||||
{
|
||||
#if defined Q_OS_WIN32
|
||||
QString nativePath = QDir::toNativeSeparators(info.absoluteFilePath());
|
||||
auto wString = nativePath.toStdWString();
|
||||
DWORD dwAttrs = GetFileAttributesW(wString.c_str());
|
||||
// Windows: check for junctions, reparse points and other nasty things of that sort
|
||||
if(dwAttrs & FILE_ATTRIBUTE_REPARSE_POINT)
|
||||
{
|
||||
if (info.isFile())
|
||||
{
|
||||
OK &= QFile::remove(info.absoluteFilePath());
|
||||
}
|
||||
else if (info.isDir())
|
||||
{
|
||||
OK &= dir.rmdir(info.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
#else
|
||||
// We do not trust Qt with reparse points, but do trust it with unix symlinks.
|
||||
if(info.isSymLink())
|
||||
{
|
||||
OK &= QFile::remove(info.absoluteFilePath());
|
||||
}
|
||||
#endif
|
||||
else if (info.isDir())
|
||||
{
|
||||
OK &= deletePath(info.absoluteFilePath());
|
||||
}
|
||||
else if (info.isFile())
|
||||
{
|
||||
OK &= QFile::remove(info.absoluteFilePath());
|
||||
}
|
||||
else
|
||||
{
|
||||
OK = false;
|
||||
qCritical() << "Delete ERROR: Unknown filesystem object:" << info.absoluteFilePath();
|
||||
}
|
||||
}
|
||||
OK &= dir.rmdir(dir.absolutePath());
|
||||
return OK;
|
||||
return err.value() == 0;
|
||||
}
|
||||
|
||||
|
||||
QString PathCombine(const QString & path1, const QString & path2)
|
||||
bool trash(QString path, QString *pathInTrash = nullptr)
|
||||
{
|
||||
if(!path1.size())
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
return false;
|
||||
#else
|
||||
// FIXME: Figure out trash in Flatpak. Qt seemingly doesn't use the Trash portal
|
||||
if (DesktopServices::isFlatpak())
|
||||
return false;
|
||||
#if defined Q_OS_WIN32
|
||||
if (IsWindowsServer())
|
||||
return false;
|
||||
#endif
|
||||
return QFile::moveToTrash(path, pathInTrash);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString PathCombine(const QString& path1, const QString& path2)
|
||||
{
|
||||
if (!path1.size())
|
||||
return path2;
|
||||
if(!path2.size())
|
||||
if (!path2.size())
|
||||
return path1;
|
||||
return QDir::cleanPath(path1 + QDir::separator() + path2);
|
||||
}
|
||||
|
||||
QString PathCombine(const QString & path1, const QString & path2, const QString & path3)
|
||||
QString PathCombine(const QString& path1, const QString& path2, const QString& path3)
|
||||
{
|
||||
return PathCombine(PathCombine(path1, path2), path3);
|
||||
}
|
||||
|
||||
QString PathCombine(const QString & path1, const QString & path2, const QString & path3, const QString & path4)
|
||||
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4)
|
||||
{
|
||||
return PathCombine(PathCombine(path1, path2, path3), path4);
|
||||
}
|
||||
@ -259,17 +269,14 @@ QString AbsolutePath(QString path)
|
||||
|
||||
QString ResolveExecutable(QString path)
|
||||
{
|
||||
if (path.isEmpty())
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
if(!path.contains('/'))
|
||||
{
|
||||
if (!path.contains('/')) {
|
||||
path = QStandardPaths::findExecutable(path);
|
||||
}
|
||||
QFileInfo pathInfo(path);
|
||||
if(!pathInfo.exists() || !pathInfo.isExecutable())
|
||||
{
|
||||
if (!pathInfo.exists() || !pathInfo.isExecutable()) {
|
||||
return QString();
|
||||
}
|
||||
return pathInfo.absoluteFilePath();
|
||||
@ -289,12 +296,9 @@ QString NormalizePath(QString path)
|
||||
QDir b(path);
|
||||
QString newAbsolute = b.absolutePath();
|
||||
|
||||
if (newAbsolute.startsWith(currentAbsolute))
|
||||
{
|
||||
if (newAbsolute.startsWith(currentAbsolute)) {
|
||||
return a.relativeFilePath(newAbsolute);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return newAbsolute;
|
||||
}
|
||||
}
|
||||
@ -303,10 +307,8 @@ QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
|
||||
|
||||
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
|
||||
{
|
||||
for (int i = 0; i < string.length(); i++)
|
||||
{
|
||||
if (badFilenameChars.contains(string[i]))
|
||||
{
|
||||
for (int i = 0; i < string.length(); i++) {
|
||||
if (badFilenameChars.contains(string[i])) {
|
||||
string[i] = replaceWith;
|
||||
}
|
||||
}
|
||||
@ -318,15 +320,11 @@ QString DirNameFromString(QString string, QString inDir)
|
||||
int num = 0;
|
||||
QString baseName = RemoveInvalidFilenameChars(string, '-');
|
||||
QString dirName;
|
||||
do
|
||||
{
|
||||
if(num == 0)
|
||||
{
|
||||
do {
|
||||
if (num == 0) {
|
||||
dirName = baseName;
|
||||
}
|
||||
else
|
||||
{
|
||||
dirName = baseName + QString::number(num);;
|
||||
} else {
|
||||
dirName = baseName + "(" + QString::number(num) + ")";
|
||||
}
|
||||
|
||||
// If it's over 9000
|
||||
@ -345,68 +343,41 @@ bool checkProblemticPathJava(QDir folder)
|
||||
return pathfoldername.contains("!", Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
// Win32 crap
|
||||
#if defined Q_OS_WIN
|
||||
|
||||
bool called_coinit = false;
|
||||
|
||||
HRESULT CreateLink(LPCSTR linkPath, LPCSTR targetPath, LPCSTR args)
|
||||
{
|
||||
HRESULT hres;
|
||||
|
||||
if (!called_coinit)
|
||||
{
|
||||
hres = CoInitialize(NULL);
|
||||
called_coinit = true;
|
||||
|
||||
if (!SUCCEEDED(hres))
|
||||
{
|
||||
qWarning("Failed to initialize COM. Error 0x%08lX", hres);
|
||||
return hres;
|
||||
}
|
||||
}
|
||||
|
||||
IShellLink *link;
|
||||
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink,
|
||||
(LPVOID *)&link);
|
||||
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
IPersistFile *persistFile;
|
||||
|
||||
link->SetPath(targetPath);
|
||||
link->SetArguments(args);
|
||||
|
||||
hres = link->QueryInterface(IID_IPersistFile, (LPVOID *)&persistFile);
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
WCHAR wstr[MAX_PATH];
|
||||
|
||||
MultiByteToWideChar(CP_ACP, 0, linkPath, -1, wstr, MAX_PATH);
|
||||
|
||||
hres = persistFile->Save(wstr, TRUE);
|
||||
persistFile->Release();
|
||||
}
|
||||
link->Release();
|
||||
}
|
||||
return hres;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
QString getDesktopDir()
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
}
|
||||
|
||||
// Cross-platform Shortcut creation
|
||||
bool createShortCut(QString location, QString dest, QStringList args, QString name,
|
||||
QString icon)
|
||||
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
|
||||
{
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
location = PathCombine(location, name + ".desktop");
|
||||
#if defined(Q_OS_MACOS)
|
||||
destination += ".command";
|
||||
|
||||
QFile f(location);
|
||||
QFile f(destination);
|
||||
f.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
QTextStream stream(&f);
|
||||
|
||||
QString argstring;
|
||||
if (!args.empty())
|
||||
argstring = " \"" + args.join("\" \"") + "\"";
|
||||
|
||||
stream << "#!/bin/bash"
|
||||
<< "\n";
|
||||
stream << "\""
|
||||
<< target
|
||||
<< "\" "
|
||||
<< argstring
|
||||
<< "\n";
|
||||
|
||||
stream.flush();
|
||||
f.close();
|
||||
|
||||
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
|
||||
|
||||
return true;
|
||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
QFile f(destination);
|
||||
f.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
QTextStream stream(&f);
|
||||
|
||||
@ -418,40 +389,170 @@ bool createShortCut(QString location, QString dest, QStringList args, QString na
|
||||
<< "\n";
|
||||
stream << "Type=Application"
|
||||
<< "\n";
|
||||
stream << "TryExec=" << dest.toLocal8Bit() << "\n";
|
||||
stream << "Exec=" << dest.toLocal8Bit() << argstring.toLocal8Bit() << "\n";
|
||||
stream << "Exec=\"" << target.toLocal8Bit() << "\"" << argstring.toLocal8Bit() << "\n";
|
||||
stream << "Name=" << name.toLocal8Bit() << "\n";
|
||||
stream << "Icon=" << icon.toLocal8Bit() << "\n";
|
||||
if (!icon.isEmpty())
|
||||
{
|
||||
stream << "Icon=" << icon.toLocal8Bit() << "\n";
|
||||
}
|
||||
|
||||
stream.flush();
|
||||
f.close();
|
||||
|
||||
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup |
|
||||
QFileDevice::ExeOther);
|
||||
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
|
||||
|
||||
return true;
|
||||
#elif defined Q_OS_WIN
|
||||
// TODO: Fix
|
||||
// QFile file(PathCombine(location, name + ".lnk"));
|
||||
// WCHAR *file_w;
|
||||
// WCHAR *dest_w;
|
||||
// WCHAR *args_w;
|
||||
// file.fileName().toWCharArray(file_w);
|
||||
// dest.toWCharArray(dest_w);
|
||||
#elif defined(Q_OS_WIN)
|
||||
QFileInfo targetInfo(target);
|
||||
|
||||
// QString argStr;
|
||||
// for (int i = 0; i < args.count(); i++)
|
||||
// {
|
||||
// argStr.append(args[i]);
|
||||
// argStr.append(" ");
|
||||
// }
|
||||
// argStr.toWCharArray(args_w);
|
||||
if (!targetInfo.exists())
|
||||
{
|
||||
qWarning() << "Target file does not exist!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// return SUCCEEDED(CreateLink(file_w, dest_w, args_w));
|
||||
return false;
|
||||
target = targetInfo.absoluteFilePath();
|
||||
|
||||
if (target.length() >= MAX_PATH)
|
||||
{
|
||||
qWarning() << "Target file path is too long!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!icon.isEmpty() && icon.length() >= MAX_PATH)
|
||||
{
|
||||
qWarning() << "Icon path is too long!";
|
||||
return false;
|
||||
}
|
||||
|
||||
destination += ".lnk";
|
||||
|
||||
if (destination.length() >= MAX_PATH)
|
||||
{
|
||||
qWarning() << "Destination path is too long!";
|
||||
return false;
|
||||
}
|
||||
|
||||
QString argStr;
|
||||
int argCount = args.count();
|
||||
for (int i = 0; i < argCount; i++)
|
||||
{
|
||||
if (args[i].contains(' '))
|
||||
{
|
||||
argStr.append('"').append(args[i]).append('"');
|
||||
}
|
||||
else
|
||||
{
|
||||
argStr.append(args[i]);
|
||||
}
|
||||
|
||||
if (i < argCount - 1)
|
||||
{
|
||||
argStr.append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
if (argStr.length() >= MAX_PATH)
|
||||
{
|
||||
qWarning() << "Arguments string is too long!";
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT hres;
|
||||
|
||||
// ...yes, you need to initialize the entire COM stack just to make a shortcut
|
||||
hres = CoInitialize(nullptr);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
qWarning() << "Failed to initialize COM!";
|
||||
return false;
|
||||
}
|
||||
|
||||
WCHAR wsz[MAX_PATH];
|
||||
|
||||
IShellLink* psl;
|
||||
|
||||
// create an IShellLink instance - this stores the shortcut's attributes
|
||||
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
wmemset(wsz, 0, MAX_PATH);
|
||||
target.toWCharArray(wsz);
|
||||
psl->SetPath(wsz);
|
||||
|
||||
wmemset(wsz, 0, MAX_PATH);
|
||||
argStr.toWCharArray(wsz);
|
||||
psl->SetArguments(wsz);
|
||||
|
||||
wmemset(wsz, 0, MAX_PATH);
|
||||
targetInfo.absolutePath().toWCharArray(wsz);
|
||||
psl->SetWorkingDirectory(wsz); // "Starts in" attribute
|
||||
|
||||
if (!icon.isEmpty())
|
||||
{
|
||||
wmemset(wsz, 0, MAX_PATH);
|
||||
icon.toWCharArray(wsz);
|
||||
psl->SetIconLocation(wsz, 0);
|
||||
}
|
||||
|
||||
// query an IPersistFile interface from our IShellLink instance
|
||||
// this is the interface that will actually let us save the shortcut to disk!
|
||||
IPersistFile* ppf;
|
||||
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
wmemset(wsz, 0, MAX_PATH);
|
||||
destination.toWCharArray(wsz);
|
||||
hres = ppf->Save(wsz, TRUE);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
qWarning() << "IPresistFile->Save() failed";
|
||||
qWarning() << "hres = " << hres;
|
||||
}
|
||||
ppf->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Failed to query IPersistFile interface from IShellLink instance";
|
||||
qWarning() << "hres = " << hres;
|
||||
}
|
||||
psl->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Failed to create IShellLink instance";
|
||||
qWarning() << "hres = " << hres;
|
||||
}
|
||||
|
||||
// go away COM, nobody likes you
|
||||
CoUninitialize();
|
||||
|
||||
return SUCCEEDED(hres);
|
||||
#else
|
||||
qWarning("Desktop Shortcuts not supported on your platform!");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool overrideFolder(QString overwritten_path, QString override_path)
|
||||
{
|
||||
using copy_opts = fs::copy_options;
|
||||
|
||||
if (!FS::ensureFolderPathExists(overwritten_path))
|
||||
return false;
|
||||
|
||||
std::error_code err;
|
||||
fs::copy_options opt = copy_opts::recursive | copy_opts::overwrite_existing;
|
||||
|
||||
// FIXME: hello traveller! Apparently std::copy does NOT overwrite existing files on GNU libstdc++ on Windows?
|
||||
fs::copy(StringUtils::toStdString(override_path), StringUtils::toStdString(overwritten_path), opt, err);
|
||||
|
||||
if (err) {
|
||||
qCritical() << QString("Failed to apply override from %1 to %2").arg(override_path, overwritten_path);
|
||||
qCritical() << "Reason:" << QString::fromStdString(err.message());
|
||||
}
|
||||
|
||||
return err.value() == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,37 @@
|
||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
||||
// 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
|
||||
|
||||
@ -7,30 +40,29 @@
|
||||
|
||||
#include <QDir>
|
||||
#include <QFlags>
|
||||
#include <QObject>
|
||||
|
||||
namespace FS
|
||||
{
|
||||
namespace FS {
|
||||
|
||||
class FileSystemException : public ::Exception
|
||||
{
|
||||
public:
|
||||
FileSystemException(const QString &message) : Exception(message) {}
|
||||
class FileSystemException : public ::Exception {
|
||||
public:
|
||||
FileSystemException(const QString& message) : Exception(message) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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\
|
||||
*/
|
||||
QByteArray read(const QString &filename);
|
||||
QByteArray read(const QString& filename);
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -44,37 +76,49 @@ bool ensureFilePathExists(QString filenamepath);
|
||||
*/
|
||||
bool ensureFolderPathExists(QString filenamepath);
|
||||
|
||||
class copy
|
||||
{
|
||||
public:
|
||||
copy(const QString & src, const QString & dst)
|
||||
/// @brief Copies a directory and it's contents from src to dest
|
||||
class copy : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
copy(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
||||
{
|
||||
m_src = src;
|
||||
m_dst = dst;
|
||||
m_src.setPath(src);
|
||||
m_dst.setPath(dst);
|
||||
}
|
||||
copy & followSymlinks(const bool follow)
|
||||
copy& followSymlinks(const bool follow)
|
||||
{
|
||||
m_followSymlinks = follow;
|
||||
return *this;
|
||||
}
|
||||
copy & blacklist(const IPathMatcher * filter)
|
||||
copy& matcher(const IPathMatcher* filter)
|
||||
{
|
||||
m_blacklist = filter;
|
||||
m_matcher = filter;
|
||||
return *this;
|
||||
}
|
||||
bool operator()()
|
||||
copy& whitelist(bool whitelist)
|
||||
{
|
||||
return operator()(QString());
|
||||
m_whitelist = whitelist;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
bool operator()(const QString &offset);
|
||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||
|
||||
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;
|
||||
const IPathMatcher * m_blacklist = nullptr;
|
||||
const IPathMatcher* m_matcher = nullptr;
|
||||
bool m_whitelist = false;
|
||||
QDir m_src;
|
||||
QDir m_dst;
|
||||
int m_copied;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -82,9 +126,14 @@ private:
|
||||
*/
|
||||
bool deletePath(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);
|
||||
/**
|
||||
* Trash a folder / file
|
||||
*/
|
||||
bool trash(QString path, QString *pathInTrash);
|
||||
|
||||
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(QString path);
|
||||
|
||||
@ -120,8 +169,12 @@ bool checkProblemticPathJava(QDir folder);
|
||||
// Get the Directory representing the User's Desktop
|
||||
QString getDesktopDir();
|
||||
|
||||
// Create a shortcut at *location*, pointing to *dest* called with the arguments *args*
|
||||
// call it *name* and assign it the icon *icon*
|
||||
// return true if operation succeeded
|
||||
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
|
||||
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
|
||||
// Equivalent to doing QDir::rename, but allowing for overrides
|
||||
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);
|
||||
}
|
||||
|
@ -1,164 +0,0 @@
|
||||
#include <QTest>
|
||||
#include <QTemporaryDir>
|
||||
#include <QStandardPaths>
|
||||
#include "TestUtil.h"
|
||||
|
||||
#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("data/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));
|
||||
}
|
||||
|
||||
// this is only valid on linux
|
||||
// FIXME: implement on windows, OSX, then test.
|
||||
#if defined(Q_OS_LINUX)
|
||||
void test_createShortcut_data()
|
||||
{
|
||||
QTest::addColumn<QString>("location");
|
||||
QTest::addColumn<QString>("dest");
|
||||
QTest::addColumn<QStringList>("args");
|
||||
QTest::addColumn<QString>("name");
|
||||
QTest::addColumn<QString>("iconLocation");
|
||||
QTest::addColumn<QByteArray>("result");
|
||||
|
||||
QTest::newRow("unix") << QDir::currentPath()
|
||||
<< "asdfDest"
|
||||
<< (QStringList() << "arg1" << "arg2")
|
||||
<< "asdf"
|
||||
<< QString()
|
||||
#if defined(Q_OS_LINUX)
|
||||
<< GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
|
||||
#elif defined(Q_OS_WIN)
|
||||
<< QByteArray()
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
void test_createShortcut()
|
||||
{
|
||||
QFETCH(QString, location);
|
||||
QFETCH(QString, dest);
|
||||
QFETCH(QStringList, args);
|
||||
QFETCH(QString, name);
|
||||
QFETCH(QString, iconLocation);
|
||||
QFETCH(QByteArray, result);
|
||||
|
||||
QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
|
||||
QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
|
||||
|
||||
//QDir().remove(location);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(FileSystemTest)
|
||||
|
||||
#include "FileSystem_test.moc"
|
@ -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.
|
||||
*/
|
||||
|
||||
#include "GZip.h"
|
||||
#include <zlib.h>
|
||||
#include <QByteArray>
|
||||
@ -37,7 +72,7 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
|
||||
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;
|
||||
|
||||
// Inflate another chunk.
|
||||
@ -67,7 +102,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned compLength = std::min(uncompressedBytes.size(), 16);
|
||||
unsigned compLength = qMin(uncompressedBytes.size(), 16);
|
||||
compressedBytes.clear();
|
||||
compressedBytes.resize(compLength);
|
||||
|
||||
@ -94,7 +129,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
{
|
||||
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;
|
||||
ret = deflate(&zs, Z_FINISH);
|
||||
offset += temp - zs.avail_out;
|
||||
@ -112,4 +147,4 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public:
|
||||
}
|
||||
void put(QByteArray input)
|
||||
{
|
||||
hoedown_buffer_put(buf, (uint8_t *) input.data(), input.size());
|
||||
hoedown_buffer_put(buf, reinterpret_cast<uint8_t *>(input.data()), input.size());
|
||||
}
|
||||
const uint8_t * data() const
|
||||
{
|
||||
|
135
launcher/InstanceCopyPrefs.cpp
Normal file
135
launcher/InstanceCopyPrefs.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
//
|
||||
// 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
|
||||
{
|
||||
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";
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// ======= 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;
|
||||
}
|
41
launcher/InstanceCopyPrefs.h
Normal file
41
launcher/InstanceCopyPrefs.h
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// Created by marcelohdez on 10/22/22.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
struct InstanceCopyPrefs {
|
||||
public:
|
||||
[[nodiscard]] bool allTrue() const;
|
||||
[[nodiscard]] QString getSelectedFiltersAsRegex() 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;
|
||||
// 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);
|
||||
|
||||
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;
|
||||
};
|
@ -5,15 +5,17 @@
|
||||
#include "pathmatcher/RegexpMatcher.h"
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
|
||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
|
||||
{
|
||||
m_origInstance = origInstance;
|
||||
m_keepPlaytime = keepPlaytime;
|
||||
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
|
||||
|
||||
if(!copySaves)
|
||||
QString filters = prefs.getSelectedFiltersAsRegex();
|
||||
if (!filters.isEmpty())
|
||||
{
|
||||
// Set regex filter:
|
||||
// FIXME: get this from the original instance type...
|
||||
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
|
||||
auto matcherReal = new RegexpMatcher(filters);
|
||||
matcherReal->caseSensitive(false);
|
||||
m_matcher.reset(matcherReal);
|
||||
}
|
||||
@ -23,10 +25,12 @@ void InstanceCopyTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
||||
|
||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this]{
|
||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||
folderCopy.followSymlinks(false).matcher(m_matcher.get());
|
||||
|
||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
|
||||
return folderCopy();
|
||||
});
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
|
||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
|
||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||
@ -44,7 +48,7 @@ void InstanceCopyTask::copyFinished()
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
|
||||
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||
inst->setName(m_instName);
|
||||
inst->setName(name());
|
||||
inst->setIconKey(m_instIcon);
|
||||
if(!m_keepPlaytime) {
|
||||
inst->resetTimePlayed();
|
||||
|
@ -1,20 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include "net/NetJob.h"
|
||||
#include <QUrl>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "BaseVersion.h"
|
||||
#include <QUrl>
|
||||
#include "BaseInstance.h"
|
||||
#include "BaseVersion.h"
|
||||
#include "InstanceCopyPrefs.h"
|
||||
#include "InstanceTask.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class InstanceCopyTask : public InstanceTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
|
||||
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
@ -22,7 +23,8 @@ protected:
|
||||
void copyFinished();
|
||||
void copyAborted();
|
||||
|
||||
private: /* data */
|
||||
private:
|
||||
/* data */
|
||||
InstancePtr m_origInstance;
|
||||
QFuture<bool> m_copyFuture;
|
||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||
|
@ -1,40 +1,60 @@
|
||||
#include "InstanceCreationTask.h"
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
//FIXME: remove this
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
||||
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
|
||||
{
|
||||
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;
|
||||
}
|
||||
InstanceCreationTask::InstanceCreationTask() = default;
|
||||
|
||||
void InstanceCreationTask::executeTask()
|
||||
{
|
||||
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
||||
{
|
||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||
instanceSettings->suspendSave();
|
||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
||||
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();
|
||||
setAbortable(true);
|
||||
|
||||
if (updateInstance()) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
return;
|
||||
}
|
||||
|
@ -1,26 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include "net/NetJob.h"
|
||||
#include <QUrl>
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "BaseVersion.h"
|
||||
#include "InstanceTask.h"
|
||||
|
||||
class InstanceCreationTask : public InstanceTask
|
||||
{
|
||||
class InstanceCreationTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstanceCreationTask(BaseVersionPtr version);
|
||||
explicit InstanceCreationTask(BaseVersionPtr version, QString loader, BaseVersionPtr loaderVersion);
|
||||
public:
|
||||
InstanceCreationTask();
|
||||
virtual ~InstanceCreationTask() = default;
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
protected:
|
||||
void executeTask() final override;
|
||||
|
||||
private: /* data */
|
||||
BaseVersionPtr m_version;
|
||||
bool m_usingLoader;
|
||||
QString m_loader;
|
||||
BaseVersionPtr m_loaderVersion;
|
||||
/**
|
||||
* Tries to update an already existing instance.
|
||||
*
|
||||
* This can be implemented by subclasses to provide a way of updating an already existing
|
||||
* 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(QString message) { m_error_message = message; };
|
||||
|
||||
protected:
|
||||
bool m_abort = false;
|
||||
|
||||
QStringList m_files_to_remove;
|
||||
|
||||
private:
|
||||
QString m_error_message;
|
||||
};
|
||||
|
@ -35,71 +35,67 @@
|
||||
*/
|
||||
|
||||
#include "InstanceImportTask.h"
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "FileSystem.h"
|
||||
#include "MMCZip.h"
|
||||
#include "NullInstance.h"
|
||||
|
||||
#include "icons/IconList.h"
|
||||
#include "icons/IconUtils.h"
|
||||
|
||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
|
||||
#include "modplatform/flame/FlameInstanceCreationTask.h"
|
||||
|
||||
#include "settings/INISettingsObject.h"
|
||||
|
||||
// FIXME: this does not belong here, it's Minecraft/Flame specific
|
||||
#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 <QtConcurrentRun>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent)
|
||||
{
|
||||
m_sourceUrl = sourceUrl;
|
||||
m_parent = parent;
|
||||
}
|
||||
#include <quazip/quazipdir.h>
|
||||
|
||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
|
||||
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
|
||||
{}
|
||||
|
||||
bool InstanceImportTask::abort()
|
||||
{
|
||||
m_filesNetJob->abort();
|
||||
if (!canAbort())
|
||||
return false;
|
||||
|
||||
if (m_filesNetJob)
|
||||
m_filesNetJob->abort();
|
||||
m_extractFuture.cancel();
|
||||
|
||||
return false;
|
||||
return Task::abort();
|
||||
}
|
||||
|
||||
void InstanceImportTask::executeTask()
|
||||
{
|
||||
if (m_sourceUrl.isLocalFile())
|
||||
{
|
||||
setAbortable(true);
|
||||
|
||||
if (m_sourceUrl.isLocalFile()) {
|
||||
m_archivePath = m_sourceUrl.toLocalFile();
|
||||
processZipPack();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||
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);
|
||||
entry->setStale(true);
|
||||
m_archivePath = entry->getFullPath();
|
||||
|
||||
m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network());
|
||||
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
||||
m_archivePath = entry->getFullPath();
|
||||
auto job = m_filesNetJob.get();
|
||||
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
}
|
||||
@ -118,7 +114,13 @@ void InstanceImportTask::downloadFailed(QString reason)
|
||||
|
||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current / 2, total);
|
||||
setProgress(current, total);
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadAborted()
|
||||
{
|
||||
emitAborted();
|
||||
m_filesNetJob.reset();
|
||||
}
|
||||
|
||||
void InstanceImportTask::processZipPack()
|
||||
@ -135,18 +137,20 @@ void InstanceImportTask::processZipPack()
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList blacklist = {"instance.cfg", "manifest.json"};
|
||||
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
|
||||
bool technicFound = QuaZipDir(m_packZip.get()).exists("/bin/modpack.jar") || QuaZipDir(m_packZip.get()).exists("/bin/version.json");
|
||||
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
|
||||
QString modrinthFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "modrinth.index.json");
|
||||
QuaZipDir packZipDir(m_packZip.get());
|
||||
|
||||
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
|
||||
bool modrinthFound = packZipDir.exists("/modrinth.index.json");
|
||||
bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json");
|
||||
QString root;
|
||||
if(!mmcFound.isNull())
|
||||
|
||||
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
|
||||
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
|
||||
if(modrinthFound)
|
||||
{
|
||||
// process as MultiMC instance/pack
|
||||
qDebug() << "MultiMC:" << mmcFound;
|
||||
root = mmcFound;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
// process as Modrinth pack
|
||||
qDebug() << "Modrinth:" << modrinthFound;
|
||||
m_modpackType = ModpackType::Modrinth;
|
||||
}
|
||||
else if (technicFound)
|
||||
{
|
||||
@ -156,19 +160,21 @@ void InstanceImportTask::processZipPack()
|
||||
extractDir.cd(".minecraft");
|
||||
m_modpackType = ModpackType::Technic;
|
||||
}
|
||||
else if(!flameFound.isNull())
|
||||
else
|
||||
{
|
||||
// process as Flame pack
|
||||
qDebug() << "Flame:" << flameFound;
|
||||
root = flameFound;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
}
|
||||
else if(!modrinthFound.isNull())
|
||||
{
|
||||
// process as Modrinth pack
|
||||
qDebug() << "Modrinth:" << modrinthFound;
|
||||
root = modrinthFound;
|
||||
m_modpackType = ModpackType::Modrinth;
|
||||
QStringList paths_to_ignore { "overrides/" };
|
||||
|
||||
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
|
||||
// process as MultiMC instance/pack
|
||||
qDebug() << "MultiMC:" << mmcRoot;
|
||||
root = mmcRoot;
|
||||
m_modpackType = ModpackType::MultiMC;
|
||||
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) {
|
||||
// process as Flame pack
|
||||
qDebug() << "Flame:" << flameRoot;
|
||||
root = flameRoot;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
}
|
||||
}
|
||||
if(m_modpackType == ModpackType::Unknown)
|
||||
{
|
||||
@ -246,216 +252,51 @@ void InstanceImportTask::extractFinished()
|
||||
|
||||
void InstanceImportTask::extractAborted()
|
||||
{
|
||||
emitFailed(tr("Instance import has been aborted."));
|
||||
return;
|
||||
emitAborted();
|
||||
}
|
||||
|
||||
void InstanceImportTask::processFlame()
|
||||
{
|
||||
const static QMap<QString,QString> forgemap = {
|
||||
{"1.2.5", "3.4.9.171"},
|
||||
{"1.4.2", "6.0.1.355"},
|
||||
{"1.4.7", "6.6.2.534"},
|
||||
{"1.5.2", "7.8.1.737"}
|
||||
};
|
||||
Flame::Manifest pack;
|
||||
try
|
||||
{
|
||||
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
|
||||
Flame::loadManifest(pack, configPath);
|
||||
QFile::remove(configPath);
|
||||
}
|
||||
catch (const JSONValidationError &e)
|
||||
{
|
||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
||||
return;
|
||||
}
|
||||
if(!pack.overrides.isEmpty())
|
||||
{
|
||||
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
|
||||
if (QFile::exists(overridePath))
|
||||
{
|
||||
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
||||
if (!QFile::rename(overridePath, mcPath))
|
||||
{
|
||||
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
|
||||
}
|
||||
FlameCreationTask* inst_creation_task = nullptr;
|
||||
if (!m_extra_info.isEmpty()) {
|
||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||
auto pack_id = pack_id_it.value();
|
||||
|
||||
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
||||
Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
|
||||
auto pack_version_id = pack_version_id_it.value();
|
||||
|
||||
QString original_instance_id;
|
||||
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
||||
if (original_instance_id_it != m_extra_info.constEnd())
|
||||
original_instance_id = original_instance_id_it.value();
|
||||
|
||||
inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
} else {
|
||||
// FIXME: Find a way to get IDs in directly imported ZIPs
|
||||
inst_creation_task = new FlameCreationTask(m_stagingPath, m_globalSettings, m_parent, {}, {});
|
||||
}
|
||||
|
||||
QString forgeVersion;
|
||||
QString fabricVersion;
|
||||
// TODO: is Quilt relevant here?
|
||||
for(auto &loader: pack.minecraft.modLoaders)
|
||||
{
|
||||
auto id = loader.id;
|
||||
if(id.startsWith("forge-"))
|
||||
{
|
||||
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(QRegExp("[.]+$"));
|
||||
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();
|
||||
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
||||
for(auto result: results.files)
|
||||
{
|
||||
QString filename = result.fileName;
|
||||
if(!result.required)
|
||||
{
|
||||
filename += ".disabled";
|
||||
}
|
||||
|
||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
||||
auto path = FS::PathCombine(m_stagingPath , relpath);
|
||||
|
||||
switch(result.type)
|
||||
{
|
||||
case Flame::File::Type::Folder:
|
||||
{
|
||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
||||
}
|
||||
case Flame::File::Type::SingleFile:
|
||||
case Flame::File::Type::Mod:
|
||||
{
|
||||
qDebug() << "Will download" << result.url << "to" << path;
|
||||
auto dl = Net::Download::makeFile(result.url, path);
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
break;
|
||||
}
|
||||
case Flame::File::Type::Modpack:
|
||||
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
|
||||
break;
|
||||
case Flame::File::Type::Cmod2:
|
||||
case Flame::File::Type::Ctoc:
|
||||
case Flame::File::Type::Unknown:
|
||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_modIdResolver.reset();
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
|
||||
{
|
||||
m_filesNetJob.reset();
|
||||
emitSucceeded();
|
||||
}
|
||||
);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
|
||||
{
|
||||
m_filesNetJob.reset();
|
||||
emitFailed(reason);
|
||||
});
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
});
|
||||
setStatus(tr("Downloading mods..."));
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
);
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
|
||||
{
|
||||
m_modIdResolver.reset();
|
||||
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
|
||||
inst_creation_task->setName(*this);
|
||||
inst_creation_task->setIcon(m_instIcon);
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||
|
||||
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
});
|
||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
|
||||
{
|
||||
setStatus(status);
|
||||
});
|
||||
m_modIdResolver->start();
|
||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
||||
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();
|
||||
}
|
||||
|
||||
void InstanceImportTask::processTechnic()
|
||||
@ -463,7 +304,7 @@ void InstanceImportTask::processTechnic()
|
||||
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
|
||||
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
|
||||
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()
|
||||
@ -477,7 +318,7 @@ void InstanceImportTask::processMultiMC()
|
||||
instance.resetTimePlayed();
|
||||
|
||||
// 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 (m_instIcon != "default") {
|
||||
@ -500,154 +341,51 @@ void InstanceImportTask::processMultiMC()
|
||||
|
||||
void InstanceImportTask::processModrinth()
|
||||
{
|
||||
std::vector<Modrinth::File> files;
|
||||
QString minecraftVersion, fabricVersion, quiltVersion, forgeVersion;
|
||||
try {
|
||||
QString indexPath = FS::PathCombine(m_stagingPath, "modrinth.index.json");
|
||||
auto doc = Json::requireDocument(indexPath);
|
||||
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);
|
||||
}
|
||||
ModrinthCreationTask* inst_creation_task = nullptr;
|
||||
if (!m_extra_info.isEmpty()) {
|
||||
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||
auto pack_id = pack_id_it.value();
|
||||
|
||||
auto jsonFiles = Json::requireIsArrayOf<QJsonObject>(obj, "files", "modrinth.index.json");
|
||||
bool had_optional = false;
|
||||
for (auto& obj : jsonFiles) {
|
||||
Modrinth::File file;
|
||||
file.path = Json::requireString(obj, "path");
|
||||
QString pack_version_id;
|
||||
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
||||
if (pack_version_id_it != m_extra_info.constEnd())
|
||||
pack_version_id = pack_version_id_it.value();
|
||||
|
||||
auto env = Json::ensureObject(obj, "env");
|
||||
QString support = Json::ensureString(env, "client", "unsupported");
|
||||
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();
|
||||
}
|
||||
QString original_instance_id;
|
||||
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
||||
if (original_instance_id_it != m_extra_info.constEnd())
|
||||
original_instance_id = original_instance_id_it.value();
|
||||
|
||||
if (file.path.endsWith(".jar"))
|
||||
file.path += ".disabled";
|
||||
}
|
||||
|
||||
QJsonObject hashes = Json::requireObject(obj, "hashes");
|
||||
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)
|
||||
file.download = Json::requireString(Json::ensureArray(obj, "downloads").first(), "Download URL for " + file.path);
|
||||
if (!file.download.isValid() || !Modrinth::validateDownloadUrl(file.download)) {
|
||||
throw JSONValidationError("Download URL for " + file.path + " is not a correctly formatted 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));
|
||||
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
} else {
|
||||
QString pack_id;
|
||||
if (!m_sourceUrl.isEmpty()) {
|
||||
QRegularExpression regex(R"(data\/(.*)\/versions)");
|
||||
pack_id = regex.match(m_sourceUrl.toString()).captured(1);
|
||||
}
|
||||
QFile::remove(indexPath);
|
||||
} catch (const JSONValidationError& e) {
|
||||
emitFailed(tr("Could not understand pack index:\n") + e.cause());
|
||||
return;
|
||||
|
||||
// FIXME: Find a way to get the ID in directly imported ZIPs
|
||||
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
||||
}
|
||||
|
||||
QString overridePath = FS::PathCombine(m_stagingPath, "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") + "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, true);
|
||||
if (!quiltVersion.isEmpty())
|
||||
components->setComponentVersion("org.quiltmc.quilt-loader", quiltVersion, true);
|
||||
if (!forgeVersion.isEmpty())
|
||||
components->setComponentVersion("net.minecraftforge", forgeVersion, true);
|
||||
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 download" << file.download << "to" << path;
|
||||
auto dl = Net::Download::makeFile(file.download, path);
|
||||
dl->addValidator(new Net::ChecksumValidator(file.hashAlgorithm, file.hash));
|
||||
m_filesNetJob->addNetAction(dl);
|
||||
}
|
||||
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);
|
||||
inst_creation_task->setName(*this);
|
||||
inst_creation_task->setIcon(m_instIcon);
|
||||
inst_creation_task->setGroup(m_instGroup);
|
||||
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||
|
||||
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
||||
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||
emitSucceeded();
|
||||
});
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
||||
{
|
||||
setProgress(current, total);
|
||||
});
|
||||
setStatus(tr("Downloading mods..."));
|
||||
m_filesNetJob->start();
|
||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
||||
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();
|
||||
}
|
||||
|
@ -42,8 +42,9 @@
|
||||
#include <QFutureWatcher>
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "modplatform/flame/PackManifest.h"
|
||||
|
||||
#include <nonstd/optional>
|
||||
#include <optional>
|
||||
|
||||
class QuaZip;
|
||||
namespace Flame
|
||||
@ -55,10 +56,13 @@ class InstanceImportTask : public InstanceTask
|
||||
{
|
||||
Q_OBJECT
|
||||
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;
|
||||
const QVector<Flame::File> &getBlockedFiles() const
|
||||
{
|
||||
return m_blockedMods;
|
||||
}
|
||||
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
@ -75,6 +79,7 @@ private slots:
|
||||
void downloadSucceeded();
|
||||
void downloadFailed(QString reason);
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
void downloadAborted();
|
||||
void extractFinished();
|
||||
void extractAborted();
|
||||
|
||||
@ -85,8 +90,9 @@ private: /* data */
|
||||
QString m_archivePath;
|
||||
bool m_downloadRequired = false;
|
||||
std::unique_ptr<QuaZip> m_packZip;
|
||||
QFuture<nonstd::optional<QStringList>> m_extractFuture;
|
||||
QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
|
||||
QFuture<std::optional<QStringList>> m_extractFuture;
|
||||
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||
QVector<Flame::File> m_blockedMods;
|
||||
enum class ModpackType{
|
||||
Unknown,
|
||||
MultiMC,
|
||||
@ -95,6 +101,10 @@ private: /* data */
|
||||
Modrinth,
|
||||
} 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
|
||||
QWidget* m_parent;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,13 +19,15 @@
|
||||
#include <QAbstractListModel>
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
#include <QStack>
|
||||
#include <QPair>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
class QFileSystemWatcher;
|
||||
class InstanceTask;
|
||||
struct InstanceName;
|
||||
|
||||
using InstanceId = QString;
|
||||
using GroupId = QString;
|
||||
using InstanceLocator = std::pair<InstancePtr, int>;
|
||||
@ -46,6 +48,12 @@ enum class GroupsState
|
||||
Dirty
|
||||
};
|
||||
|
||||
struct TrashHistoryItem {
|
||||
QString id;
|
||||
QString polyPath;
|
||||
QString trashPath;
|
||||
QString groupName;
|
||||
};
|
||||
|
||||
class InstanceList : public QAbstractListModel
|
||||
{
|
||||
@ -93,7 +101,10 @@ public:
|
||||
InstListError loadList();
|
||||
void saveNow();
|
||||
|
||||
/* O(n) */
|
||||
InstancePtr getInstanceById(QString id) const;
|
||||
/* O(n) */
|
||||
InstancePtr getInstanceByManagedName(const QString& managed_name) const;
|
||||
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||
QStringList getGroups();
|
||||
bool isGroupCollapsed(const QString &groupName);
|
||||
@ -102,6 +113,9 @@ public:
|
||||
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
||||
|
||||
void deleteGroup(const GroupId & name);
|
||||
bool trashInstance(const InstanceId &id);
|
||||
bool trashedSomething();
|
||||
void undoTrashInstance();
|
||||
void deleteInstance(const InstanceId & id);
|
||||
|
||||
// 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.
|
||||
* 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.
|
||||
@ -180,4 +196,6 @@ private:
|
||||
QSet<InstanceId> instanceSet;
|
||||
bool m_groupsLoaded = false;
|
||||
bool m_instancesProbed = false;
|
||||
|
||||
QStack<TrashHistoryItem> m_trashHistory;
|
||||
};
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "ui/pages/BasePageProvider.h"
|
||||
#include "ui/pages/instance/LogPage.h"
|
||||
#include "ui/pages/instance/VersionPage.h"
|
||||
#include "ui/pages/instance/ManagedPackPage.h"
|
||||
#include "ui/pages/instance/ModFolderPage.h"
|
||||
#include "ui/pages/instance/ResourcePackPage.h"
|
||||
#include "ui/pages/instance/TexturePackPage.h"
|
||||
@ -33,13 +34,14 @@ public:
|
||||
values.append(new LogPage(inst));
|
||||
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
||||
values.append(new VersionPage(onesix.get()));
|
||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods");
|
||||
values.append(ManagedPackPage::createPage(onesix.get()));
|
||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
|
||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
||||
values.append(modsPage);
|
||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
|
||||
values.append(new ResourcePackPage(onesix.get()));
|
||||
values.append(new TexturePackPage(onesix.get()));
|
||||
values.append(new ShaderPackPage(onesix.get()));
|
||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
|
||||
values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList()));
|
||||
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 WorldListPage(onesix.get(), onesix->worldList()));
|
||||
values.append(new ServersPage(onesix));
|
||||
|
@ -1,9 +1,75 @@
|
||||
#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
|
||||
|
||||
#include "tasks/Task.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
|
||||
public:
|
||||
explicit InstanceTask();
|
||||
virtual ~InstanceTask();
|
||||
public:
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
protected: /* data */
|
||||
SettingsObjectPtr m_globalSettings;
|
||||
QString m_instName;
|
||||
QString m_instIcon;
|
||||
QString m_instGroup;
|
||||
QString m_stagingPath;
|
||||
|
||||
bool m_override_existing = false;
|
||||
bool m_confirm_update = true;
|
||||
|
||||
QString m_original_instance_id;
|
||||
};
|
||||
|
@ -1,10 +1,47 @@
|
||||
// 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.
|
||||
*/
|
||||
|
||||
#include "JavaCommon.h"
|
||||
#include "java/JavaUtils.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include <MMCStrings.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||
{
|
||||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegExp("-Xm[sx]"))
|
||||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]"))
|
||||
|| jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize"))
|
||||
{
|
||||
auto warnStr = QObject::tr(
|
||||
@ -18,7 +55,7 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||
return false;
|
||||
}
|
||||
// block lunacy with passing required version to the JVM
|
||||
if (jvmargs.contains(QRegExp("-version:.*"))) {
|
||||
if (jvmargs.contains(QRegularExpression("-version:.*"))) {
|
||||
auto warnStr = QObject::tr(
|
||||
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n"
|
||||
"This message will be displayed until you remove this from the JVM arguments.");
|
||||
@ -65,6 +102,13 @@ void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result)
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::javaCheckNotFound(QWidget *parent)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr("Java checker library could not be found. Please check your installation.");
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::run()
|
||||
{
|
||||
if (!JavaCommon::checkJVMArgs(m_args, m_parent))
|
||||
@ -72,6 +116,11 @@ void JavaCommon::TestCheck::run()
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
if (JavaUtils::getJavaCheckPath().isEmpty()) {
|
||||
javaCheckNotFound(m_parent);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
checker.reset(new JavaChecker());
|
||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
||||
SLOT(checkFinished(JavaCheckResult)));
|
||||
|
@ -10,12 +10,14 @@ namespace JavaCommon
|
||||
{
|
||||
bool checkJVMArgs(QString args, QWidget *parent);
|
||||
|
||||
// Show a dialog saying that the Java binary was not usable
|
||||
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result);
|
||||
// Show a dialog saying that the Java binary was not usable because of bad options
|
||||
void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
|
||||
// Show a dialog saying that the Java binary was usable
|
||||
void javaWasOk(QWidget *parent, JavaCheckResult result);
|
||||
// Show a dialog saying that the Java binary was not usable because of bad options
|
||||
void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
|
||||
// Show a dialog saying that the Java binary was not usable
|
||||
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result);
|
||||
// Show a dialog if we couldn't find Java Checker
|
||||
void javaCheckNotFound(QWidget *parent);
|
||||
|
||||
class TestCheck : public QObject
|
||||
{
|
||||
|
@ -1,4 +1,37 @@
|
||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
||||
// 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.
|
||||
*/
|
||||
|
||||
#include "Json.h"
|
||||
|
||||
@ -22,14 +55,6 @@ void write(const QJsonArray &array, const QString &filename)
|
||||
write(QJsonDocument(array), filename);
|
||||
}
|
||||
|
||||
QByteArray toBinary(const QJsonObject &obj)
|
||||
{
|
||||
return QJsonDocument(obj).toBinaryData();
|
||||
}
|
||||
QByteArray toBinary(const QJsonArray &array)
|
||||
{
|
||||
return QJsonDocument(array).toBinaryData();
|
||||
}
|
||||
QByteArray toText(const QJsonObject &obj)
|
||||
{
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
@ -48,12 +73,8 @@ QJsonDocument requireDocument(const QByteArray &data, const QString &what)
|
||||
{
|
||||
if (isBinaryJson(data))
|
||||
{
|
||||
QJsonDocument doc = QJsonDocument::fromBinaryData(data);
|
||||
if (doc.isNull())
|
||||
{
|
||||
throw JsonException(what + ": Invalid JSON (binary JSON detected)");
|
||||
}
|
||||
return doc;
|
||||
// FIXME: Is this needed?
|
||||
throw JsonException(what + ": Invalid JSON. Binary JSON unsupported");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,4 +1,37 @@
|
||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
||||
// 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
|
||||
|
||||
@ -29,8 +62,6 @@ void write(const QJsonObject &object, const QString &filename);
|
||||
/// @throw FileSystemException
|
||||
void write(const QJsonArray &array, const QString &filename);
|
||||
|
||||
QByteArray toBinary(const QJsonObject &obj);
|
||||
QByteArray toBinary(const QJsonArray &array);
|
||||
QByteArray toText(const QJsonObject &obj);
|
||||
QByteArray toText(const QJsonArray &array);
|
||||
|
||||
|
@ -93,8 +93,8 @@ void LaunchController::decideAccount()
|
||||
auto reply = CustomMessageBox::selectable(
|
||||
m_parentWidget,
|
||||
tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Microsoft "
|
||||
"account logged in. "
|
||||
tr("In order to play Minecraft, you must have at least one Microsoft or Mojang "
|
||||
"account logged in. Mojang accounts can only be used offline. "
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information,
|
||||
QMessageBox::Yes | QMessageBox::No
|
||||
@ -105,6 +105,11 @@ void LaunchController::decideAccount()
|
||||
// Open the account manager.
|
||||
APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts");
|
||||
}
|
||||
else if (reply == QMessageBox::No)
|
||||
{
|
||||
// Do not open "profile select" dialog.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_accountToUse = accounts->defaultAccount();
|
||||
@ -140,18 +145,29 @@ void LaunchController::login() {
|
||||
return;
|
||||
}
|
||||
|
||||
// we try empty password first :)
|
||||
QString password;
|
||||
// we loop until the user succeeds in logging in or gives up
|
||||
bool tryagain = true;
|
||||
// the failure. the default failure.
|
||||
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter your password to log in again. <br /> <br /> This could be caused by a password change.");
|
||||
QString failReason = needLoginAgain;
|
||||
unsigned int tries = 0;
|
||||
|
||||
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->wants_online = m_online;
|
||||
m_session->demo = m_demo;
|
||||
m_accountToUse->fillSession(m_session);
|
||||
|
||||
// Launch immediately in true offline mode
|
||||
@ -169,12 +185,18 @@ void LaunchController::login() {
|
||||
if(!m_session->wants_online) {
|
||||
// we ask the user for a player name
|
||||
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 usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
||||
QString name = QInputDialog::getText(
|
||||
m_parentWidget,
|
||||
tr("Player name"),
|
||||
tr("Choose your offline mode player name."),
|
||||
message,
|
||||
QLineEdit::Normal,
|
||||
usedname,
|
||||
&ok
|
||||
@ -354,13 +376,13 @@ void LaunchController::launchInstance()
|
||||
}
|
||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
||||
} 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));
|
||||
|
||||
// Prepend Version
|
||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_NAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
||||
m_launcher->start();
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,10 @@ public:
|
||||
m_online = online;
|
||||
}
|
||||
|
||||
void setDemo(bool demo) {
|
||||
m_demo = demo;
|
||||
}
|
||||
|
||||
void setProfiler(BaseProfilerFactory *profiler) {
|
||||
m_profiler = profiler;
|
||||
}
|
||||
@ -101,6 +105,7 @@ private slots:
|
||||
private:
|
||||
BaseProfilerFactory *m_profiler = nullptr;
|
||||
bool m_online = true;
|
||||
bool m_demo = false;
|
||||
InstancePtr m_instance;
|
||||
QWidget * m_parentWidget = nullptr;
|
||||
InstanceWindow *m_console = nullptr;
|
||||
|
@ -18,13 +18,17 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
|
||||
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
echo "Launcher Dir: ${LAUNCHER_DIR}"
|
||||
|
||||
# Set up env - filter out input LD_ variables but pass them in under different names
|
||||
export GAME_LIBRARY_PATH=${GAME_LIBRARY_PATH-${LD_LIBRARY_PATH}}
|
||||
export GAME_PRELOAD=${GAME_PRELOAD-${LD_PRELOAD}}
|
||||
export LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@":$LAUNCHER_LIBRARY_PATH
|
||||
export LD_PRELOAD=$LAUNCHER_PRELOAD
|
||||
export QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
|
||||
export QT_FONTPATH="${LAUNCHER_DIR}/fonts"
|
||||
# Set up env.
|
||||
# Pass our custom variables separately so that the launcher can remove them for child processes
|
||||
export LAUNCHER_LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@"
|
||||
export LAUNCHER_LD_PRELOAD=""
|
||||
export LAUNCHER_QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
|
||||
export LAUNCHER_QT_FONTPATH="${LAUNCHER_DIR}/fonts"
|
||||
|
||||
export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH"
|
||||
export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD"
|
||||
export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH"
|
||||
export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH"
|
||||
|
||||
# Detect missing dependencies...
|
||||
DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'`
|
||||
|
@ -1,6 +1,42 @@
|
||||
// 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.
|
||||
*/
|
||||
|
||||
#include "LoggedProcess.h"
|
||||
#include "MessageLevel.h"
|
||||
#include <QDebug>
|
||||
#include <QTextDecoder>
|
||||
#include "MessageLevel.h"
|
||||
|
||||
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
|
||||
{
|
||||
@ -8,7 +44,11 @@ LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
|
||||
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
|
||||
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
|
||||
connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -20,25 +60,26 @@ LoggedProcess::~LoggedProcess()
|
||||
}
|
||||
}
|
||||
|
||||
QStringList reprocess(const QByteArray & data, QString & leftover)
|
||||
QStringList reprocess(const QByteArray& data, QTextDecoder& decoder)
|
||||
{
|
||||
QString str = leftover + QString::fromLocal8Bit(data);
|
||||
|
||||
str.remove('\r');
|
||||
QStringList lines = str.split("\n");
|
||||
leftover = lines.takeLast();
|
||||
auto str = decoder.toUnicode(data);
|
||||
#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
|
||||
return lines;
|
||||
}
|
||||
|
||||
void LoggedProcess::on_stdErr()
|
||||
{
|
||||
auto lines = reprocess(readAllStandardError(), m_err_leftover);
|
||||
auto lines = reprocess(readAllStandardError(), m_err_decoder);
|
||||
emit log(lines, MessageLevel::StdErr);
|
||||
}
|
||||
|
||||
void LoggedProcess::on_stdOut()
|
||||
{
|
||||
auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
|
||||
auto lines = reprocess(readAllStandardOutput(), m_out_decoder);
|
||||
emit log(lines, MessageLevel::StdOut);
|
||||
}
|
||||
|
||||
@ -47,18 +88,6 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
|
||||
// save the 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
|
||||
if (!m_is_aborting)
|
||||
{
|
||||
@ -157,19 +186,6 @@ void LoggedProcess::on_stateChange(QProcess::ProcessState state)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
qint64 LoggedProcess::processId() const
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
return pid() ? pid()->dwProcessId : 0;
|
||||
#else
|
||||
return pid();
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoggedProcess::setDetachable(bool detachable)
|
||||
{
|
||||
m_is_detachable = detachable;
|
||||
|
@ -1,21 +1,42 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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 <QProcess>
|
||||
#include <QTextDecoder>
|
||||
#include "MessageLevel.h"
|
||||
|
||||
/*
|
||||
@ -43,7 +64,6 @@ public:
|
||||
|
||||
State state() const;
|
||||
int exitCode() const;
|
||||
qint64 processId() const;
|
||||
|
||||
void setDetachable(bool detachable);
|
||||
|
||||
@ -69,8 +89,8 @@ private:
|
||||
void changeState(LoggedProcess::State state);
|
||||
|
||||
private:
|
||||
QString m_err_leftover;
|
||||
QString m_out_leftover;
|
||||
QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
||||
QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
||||
bool m_killed = false;
|
||||
State m_state = NotRunning;
|
||||
int m_exit_code = 0;
|
||||
|
@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace Strings
|
||||
{
|
||||
int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs);
|
||||
}
|
@ -28,11 +28,11 @@ QString Time::prettifyDuration(int64_t duration) {
|
||||
int days = (int) (duration / 24);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "MMCZip.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
||||
// ours
|
||||
@ -127,7 +128,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
|
||||
}
|
||||
|
||||
// ours
|
||||
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods)
|
||||
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
||||
{
|
||||
QuaZip zipOut(targetJarPath);
|
||||
if (!zipOut.open(QuaZip::mdCreate))
|
||||
@ -141,42 +142,41 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
QSet<QString> addedFiles;
|
||||
|
||||
// Modify the jar
|
||||
QListIterator<Mod> i(mods);
|
||||
i.toBack();
|
||||
while (i.hasPrevious())
|
||||
// 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.previous();
|
||||
const auto* mod = *i;
|
||||
// do not merge disabled mods.
|
||||
if (!mod.enabled())
|
||||
if (!mod->enabled())
|
||||
continue;
|
||||
if (mod.type() == Mod::MOD_ZIPFILE)
|
||||
if (mod->type() == ResourceType::ZIPFILE)
|
||||
{
|
||||
if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles))
|
||||
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles))
|
||||
{
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (mod.type() == Mod::MOD_SINGLEFILE)
|
||||
else if (mod->type() == ResourceType::SINGLEFILE)
|
||||
{
|
||||
// FIXME: buggy - does not work with addedFiles
|
||||
auto filename = mod.filename();
|
||||
auto filename = mod->fileinfo();
|
||||
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
|
||||
{
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
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
|
||||
// FIXME: buggy - does not work with addedFiles
|
||||
auto filename = mod.filename();
|
||||
auto filename = mod->fileinfo();
|
||||
QString what_to_zip = filename.absoluteFilePath();
|
||||
QDir dir(what_to_zip);
|
||||
dir.cdUp();
|
||||
@ -193,7 +193,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
{
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar.";
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
qDebug() << "Adding folder " << filename.fileName() << " from "
|
||||
@ -204,7 +204,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
// Make sure we do not continue launching when something is missing or undefined...
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar.";
|
||||
qCritical() << "Failed to add unknown mod type" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -229,23 +229,27 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
}
|
||||
|
||||
// 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);
|
||||
for(auto fileName: rootDir.entryList(QDir::Files))
|
||||
{
|
||||
if(fileName == what)
|
||||
for (auto&& fileName : rootDir.entryList(QDir::Files)) {
|
||||
if (fileName == what)
|
||||
return root;
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
for(auto fileName: rootDir.entryList(QDir::Dirs))
|
||||
{
|
||||
QString result = findFolderOfFileInZip(zip, what, root + fileName);
|
||||
if(!result.isEmpty())
|
||||
{
|
||||
|
||||
// Recurse the search to non-ignored subfolders
|
||||
for (auto&& fileName : rootDir.entryList(QDir::Dirs)) {
|
||||
if (ignore_paths.contains(fileName))
|
||||
continue;
|
||||
|
||||
QString result = findFolderOfFileInZip(zip, what, ignore_paths, root + fileName);
|
||||
if (!result.isEmpty())
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// ours
|
||||
@ -269,7 +273,7 @@ bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & re
|
||||
|
||||
|
||||
// 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);
|
||||
QStringList extracted;
|
||||
@ -278,7 +282,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
|
||||
auto numEntries = zip->getEntriesCount();
|
||||
if(numEntries < 0) {
|
||||
qWarning() << "Failed to enumerate files in archive";
|
||||
return nonstd::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
else if(numEntries == 0) {
|
||||
qDebug() << "Extracting empty archives seems odd...";
|
||||
@ -287,7 +291,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
|
||||
else if (!zip->goToFirstFile())
|
||||
{
|
||||
qWarning() << "Failed to seek to first file in zip";
|
||||
return nonstd::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
do
|
||||
@ -305,7 +309,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
|
||||
QString path;
|
||||
if(name.contains('/') && !name.endsWith('/')){
|
||||
path = name.section('/', 0, -2) + "/";
|
||||
FS::ensureFolderPathExists(path);
|
||||
FS::ensureFolderPathExists(FS::PathCombine(target, path));
|
||||
|
||||
name = name.split('/').last();
|
||||
}
|
||||
@ -324,7 +328,7 @@ nonstd::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString &
|
||||
{
|
||||
qWarning() << "Failed to extract file" << original_name << "to" << absFilePath;
|
||||
JlCompress::removeFile(extracted);
|
||||
return nonstd::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
extracted.append(absFilePath);
|
||||
@ -342,7 +346,7 @@ bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &tar
|
||||
}
|
||||
|
||||
// ours
|
||||
nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
|
||||
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
|
||||
{
|
||||
QuaZip zip(fileCompressed);
|
||||
if (!zip.open(QuaZip::mdUnzip))
|
||||
@ -353,13 +357,13 @@ nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString
|
||||
return QStringList();
|
||||
}
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
|
||||
return nonstd::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
return MMCZip::extractSubDir(&zip, "", dir);
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!zip.open(QuaZip::mdUnzip))
|
||||
@ -370,7 +374,7 @@ nonstd::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString
|
||||
return QStringList();
|
||||
}
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
|
||||
return nonstd::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
return MMCZip::extractSubDir(&zip, subdir, dir);
|
||||
}
|
||||
@ -421,7 +425,7 @@ bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& s
|
||||
continue;
|
||||
}
|
||||
|
||||
files->append(e.filePath()); // we want the original paths for MMCZip::compressDirFiles
|
||||
files->append(e); // we want the original paths for MMCZip::compressDirFiles
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -42,7 +42,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include <quazip/JlCompress.h>
|
||||
#include <nonstd/optional>
|
||||
#include <optional>
|
||||
|
||||
namespace MMCZip
|
||||
{
|
||||
@ -75,14 +75,16 @@ namespace MMCZip
|
||||
/**
|
||||
* take a source jar, add mods to it, resulting in target jar
|
||||
*/
|
||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod>& mods);
|
||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
@ -95,7 +97,7 @@ namespace MMCZip
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@ -106,7 +108,7 @@ namespace MMCZip
|
||||
* \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.
|
||||
*/
|
||||
nonstd::optional<QStringList> extractDir(QString fileCompressed, QString dir);
|
||||
std::optional<QStringList> extractDir(QString fileCompressed, QString dir);
|
||||
|
||||
/**
|
||||
* Extract a subdirectory from an archive
|
||||
@ -116,7 +118,7 @@ namespace MMCZip
|
||||
* \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.
|
||||
*/
|
||||
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
|
||||
|
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;
|
||||
};
|
90
launcher/MangoHud.cpp
Normal file
90
launcher/MangoHud.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
// 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 <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) {
|
||||
QString filePath = FS::PathCombine(vkLayer, "MangoHud.json");
|
||||
if (!QFile::exists(filePath))
|
||||
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();
|
||||
}
|
@ -1,25 +1,56 @@
|
||||
// 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(const QUrl sourceUrl,const QString filename, const std::shared_ptr<ModFolderModel> mods)
|
||||
: m_sourceUrl(sourceUrl), mods(mods), filename(filename) {
|
||||
}
|
||||
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);
|
||||
|
||||
void ModDownloadTask::executeTask() {
|
||||
setStatus(tr("Downloading mod:\n%1").arg(m_sourceUrl.toString()));
|
||||
addTask(m_update_task);
|
||||
}
|
||||
|
||||
m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network()));
|
||||
m_filesNetJob->addNetAction(Net::Download::makeFile(m_sourceUrl, mods->dir().absoluteFilePath(filename)));
|
||||
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);
|
||||
m_filesNetJob->start();
|
||||
|
||||
addTask(m_filesNetJob);
|
||||
}
|
||||
|
||||
void ModDownloadTask::downloadSucceeded()
|
||||
{
|
||||
emitSucceeded();
|
||||
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)
|
||||
@ -33,7 +64,9 @@ void ModDownloadTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||
emit progress(current, total);
|
||||
}
|
||||
|
||||
bool ModDownloadTask::abort() {
|
||||
return m_filesNetJob->abort();
|
||||
// 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,34 +1,56 @@
|
||||
// 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 "QObjectPtr.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
|
||||
#include "net/NetJob.h"
|
||||
#include <QUrl>
|
||||
#include "tasks/SequentialTask.h"
|
||||
|
||||
#include "modplatform/ModIndex.h"
|
||||
#include "minecraft/mod/tasks/LocalModUpdateTask.h"
|
||||
|
||||
class ModDownloadTask : public Task {
|
||||
class ModFolderModel;
|
||||
|
||||
class ModDownloadTask : public SequentialTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModDownloadTask(const QUrl sourceUrl, const QString filename, const std::shared_ptr<ModFolderModel> mods);
|
||||
const QString& getFilename() const { return filename; }
|
||||
|
||||
public slots:
|
||||
bool abort() override;
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
void executeTask() override;
|
||||
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:
|
||||
QUrl m_sourceUrl;
|
||||
NetJob::Ptr m_filesNetJob;
|
||||
ModPlatform::IndexedPack m_mod;
|
||||
ModPlatform::IndexedVersion m_mod_version;
|
||||
const std::shared_ptr<ModFolderModel> mods;
|
||||
const QString filename;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user