Compare commits
1225 Commits
release-7.
...
develop
Author | SHA1 | Date | |
---|---|---|---|
|
baa988ec81 | ||
|
8eac0d34bd | ||
|
0b1dcb3ea7 | ||
|
3d529b0c3e | ||
|
a2ab4804dd | ||
|
ca226d2ab5 | ||
|
670e91cb60 | ||
|
cc291219f9 | ||
|
0eda2447dc | ||
|
867e6223ce | ||
|
3e7f9083ca | ||
|
902e861fc6 | ||
|
36d2109a15 | ||
|
bd9d5e0990 | ||
|
9a8667e99c | ||
|
710a48fcaf | ||
|
52807ab279 | ||
|
d2a85cb580 | ||
|
d13db1109f | ||
|
64bbcb2834 | ||
|
070d83d887 | ||
|
cf4144cb50 | ||
|
40ebae394a | ||
|
0f95bf1e42 | ||
|
b446bdf4a9 | ||
|
ef40ba8439 | ||
|
1bd69ecbb2 | ||
|
b18082376d | ||
|
127a31ba3a | ||
|
220a1de99a | ||
|
2349f29be0 | ||
|
f8bb1a872a | ||
|
86e2f6c58c | ||
|
2526275c5e | ||
|
5a54d80c6e | ||
|
ea7dd9cfcc | ||
|
eb3f1ee27a | ||
|
55ac17c45b | ||
|
54305397e4 | ||
|
736246e6c8 | ||
|
9dcb7e9759 | ||
|
69c2720872 | ||
|
364cb4ff6a | ||
|
df2a92d5f4 | ||
|
3719ea21b0 | ||
|
bd11b93a0c | ||
|
12d567a9b8 | ||
|
38d77b58cd | ||
|
a161f5cfe2 | ||
|
5be80df1ee | ||
|
87f2f88d4c | ||
|
b376888f43 | ||
|
0fe3241df6 | ||
|
7d5206818b | ||
|
db19362a97 | ||
|
cefb96e812 | ||
|
e7e80e704f | ||
|
2c4af7e793 | ||
|
f0fd1bd5e5 | ||
|
5d926582c7 | ||
|
7b62d14683 | ||
|
80723eeca1 | ||
|
e9fd02baca | ||
|
7ad48d8677 | ||
|
9a0cfbc648 | ||
|
1f2483c39e | ||
|
b9c1dc7325 | ||
|
cf33927f21 | ||
|
c638a63a03 | ||
|
2081fcd3f5 | ||
|
90ebbd8e91 | ||
|
165d218300 | ||
|
069bc887f1 | ||
|
5aa2b88861 | ||
|
d348f20dd9 | ||
|
b1bd0ceade | ||
|
8f2f99801e | ||
|
aae65e3e4e | ||
|
436db23ffc | ||
|
6c9856b9dc | ||
|
0927035a26 | ||
|
5985d8b118 | ||
|
cc990c4f94 | ||
|
c4882e7162 | ||
|
9d15255e0a | ||
|
ca9b593483 | ||
|
ef0813754b | ||
|
6bbff310bc | ||
|
6fb7a98901 | ||
|
9705e6325a | ||
|
7112d04df5 | ||
|
2ab48a43dd | ||
|
682134435d | ||
|
05d7cc31bf | ||
|
c498ad1787 | ||
|
4939a33456 | ||
|
023b3e3c39 | ||
|
7015b8f7b2 | ||
|
32eaaa25d9 | ||
|
ca4b58d5d7 | ||
|
4eb8db16f1 | ||
|
a7842ecc33 | ||
|
6150908025 | ||
|
5cb6d93136 | ||
|
7e6a08548d | ||
|
11a0241b5c | ||
|
8eb8f167ab | ||
|
5f5214e5dd | ||
|
04a7275280 | ||
|
05caa874bc | ||
|
dc74ea7382 | ||
|
8d0a53273f | ||
|
bca5e8f395 | ||
|
d49f81d132 | ||
|
82461b1113 | ||
|
4f1ee85424 | ||
|
b67c2c71d1 | ||
|
a7bdfb5f6b | ||
|
c6c17036e3 | ||
|
e913f61305 | ||
|
c78d4e7bb6 | ||
|
024c474658 | ||
|
03ad430699 | ||
|
ac38585a2a | ||
|
3cba5adb5a | ||
|
d186c34385 | ||
|
77979b4c95 | ||
|
8069de29b2 | ||
|
540fc7d2a8 | ||
|
d612ab94a7 | ||
|
bfa9613373 | ||
|
93be8b0735 | ||
|
2cea7454ef | ||
|
eab1180f68 | ||
|
e3a147f56d | ||
|
898a719d3e | ||
|
be8a9f6541 | ||
|
589ab24f7e | ||
|
317c7b5544 | ||
|
00bbbdc6e9 | ||
|
84269f9596 | ||
|
34294383eb | ||
|
4802f6950e | ||
|
531b58093e | ||
|
2ff9ef0620 | ||
|
5b7c5607a9 | ||
|
9acbf98f94 | ||
|
bef701eba8 | ||
|
742384909f | ||
|
606c12ffeb | ||
|
498c9db1ce | ||
|
59e565ef96 | ||
|
f370f0d749 | ||
|
b8d9c3d779 | ||
|
ebde563648 | ||
|
4ee6a6711d | ||
|
86b47b3421 | ||
|
7c636d4608 | ||
|
0406e168e7 | ||
|
1684cff7ac | ||
|
ad9fb7349f | ||
|
14af7044be | ||
|
fa65ac3ff7 | ||
|
f3c089792a | ||
|
81a3ba18bc | ||
|
98bc102f5b | ||
|
97da8892b9 | ||
|
97ced1f459 | ||
|
a49851cb40 | ||
|
d72c3aba80 | ||
|
9afe80b0e0 | ||
|
00af385619 | ||
|
bf6dc10f35 | ||
|
4cb424470b | ||
|
9445a555e4 | ||
|
47d1f23568 | ||
|
82a0a5bca1 | ||
|
10192c540b | ||
|
6aa821df9c | ||
|
89e434bd5b | ||
|
78f8a31cd6 | ||
|
e7d6be531f | ||
|
ba3a1b6280 | ||
|
d483b613b1 | ||
|
cf599865f6 | ||
|
546d49e183 | ||
|
7dd2530027 | ||
|
e9ecdd05b9 | ||
|
ad6f15f293 | ||
|
94c1cd6bcf | ||
|
e155844671 | ||
|
2918d61b16 | ||
|
bbf4e3b04d | ||
|
caf925cbad | ||
|
88f3e19f81 | ||
|
19b5a5e00b | ||
|
347228a246 | ||
|
1213a5ab46 | ||
|
17f696bffc | ||
|
e095780cc3 | ||
|
c01e95b79c | ||
|
984e870bca | ||
|
eb66e37b83 | ||
|
30ff417074 | ||
|
7e65aea2ef | ||
|
6cfe2dbc50 | ||
|
25ce11d85d | ||
|
707da5a25a | ||
|
584e800279 | ||
|
befa3baa6a | ||
|
f23a8e4b4b | ||
|
8c30cb3706 | ||
|
c1855c6ce3 | ||
|
b83fdbd1b7 | ||
|
0e67686295 | ||
|
e98bca4749 | ||
|
bdc2fca711 | ||
|
211865a1e1 | ||
|
311e36b5d6 | ||
|
fbf6833124 | ||
|
e4e4c4a430 | ||
|
97d932db62 | ||
|
5d70f4dbca | ||
|
66cbbfec0c | ||
|
07d8598638 | ||
|
79652799bd | ||
|
630145a1d0 | ||
|
0680d2dceb | ||
|
9110fbf282 | ||
|
e3b04d10cf | ||
|
8c607ae734 | ||
|
c5aac24a93 | ||
|
4036cecfc0 | ||
|
df14f88307 | ||
|
29736b3b1f | ||
|
9735e46a7e | ||
|
479335dfe0 | ||
|
d1566f2610 | ||
|
a2d44744fe | ||
|
288d0d1fd4 | ||
|
abfd1a4205 | ||
|
094cee8ec6 | ||
|
c54fecf5d9 | ||
|
172680abf8 | ||
|
5f3e9672cd | ||
|
f897b14e3e | ||
|
bb4b89470d | ||
|
2990c5d0c9 | ||
|
ea384d59fb | ||
|
3e448904a3 | ||
|
783af2c06a | ||
|
6c0492c0d1 | ||
|
1515607060 | ||
|
41bd008f5d | ||
|
d7b6450613 | ||
|
85c23b26de | ||
|
095de5ed4b | ||
|
6178e5a975 | ||
|
aa065f2b51 | ||
|
59ec0135d7 | ||
|
2283498ccb | ||
|
e6ba2f4970 | ||
|
b93cd88292 | ||
|
f8f9ffa118 | ||
|
4704c522e0 | ||
|
f7951f6082 | ||
|
eed7e996da | ||
|
06dbd381f8 | ||
|
3574d89e0f | ||
|
0e96111116 | ||
|
09aca7a0b5 | ||
|
b1783d8fb1 | ||
|
c3d03f0c33 | ||
|
7acfe36a62 | ||
|
45dad27a6d | ||
|
fff378b643 | ||
|
d7dadabfbf | ||
|
1eb75b6852 | ||
|
492bf373c6 | ||
|
02264f67f3 | ||
|
fcb436f1f4 | ||
|
e2e0a7af45 | ||
|
c50c3eaa94 | ||
|
3638fc0a7d | ||
|
8092c321a9 | ||
|
8e5be6f420 | ||
|
19316e22b4 | ||
|
5c95448f1f | ||
|
254444470f | ||
|
0aaea9bf5d | ||
|
5b4dcae7d9 | ||
|
963627fe98 | ||
|
232bb568da | ||
|
d3c7850f53 | ||
|
ebbc8838a8 | ||
|
d25e89a4c1 | ||
|
f99b04bd16 | ||
|
acf586ef82 | ||
|
0138cd65cb | ||
|
ab6301d5a1 | ||
|
3098aecf97 | ||
|
05094b7382 | ||
|
58efd3e9e2 | ||
|
4c52b18bdd | ||
|
44ff247f5f | ||
|
fecc1e087a | ||
|
d3acac16e6 | ||
|
28ffa8bf44 | ||
|
eb4efa5a9c | ||
|
6d1c67663d | ||
|
f4ebeedc21 | ||
|
be2888d6fb | ||
|
7ab391904a | ||
|
aac734d174 | ||
|
ffd8ed550f | ||
|
c5bac475e8 | ||
|
e88418ab7f | ||
|
bc0934a19c | ||
|
044ce4df32 | ||
|
758999fa64 | ||
|
4a1d85f999 | ||
|
6da2e7d3f6 | ||
|
85f36ebed7 | ||
|
f8ffb14619 | ||
|
5e2d1ffdfb | ||
|
3e2733d840 | ||
|
bad44ea264 | ||
|
f0da16a758 | ||
|
b0e197de38 | ||
|
6c2c724bd9 | ||
|
c88088c91a | ||
|
f919d363b7 | ||
|
589d8b6923 | ||
|
a5e7aedb4f | ||
|
07f25d6cca | ||
|
c22eec8f27 | ||
|
1faf741004 | ||
|
8cff7c4de6 | ||
|
cf27d2f9ab | ||
|
6c362afc21 | ||
|
c94ee67077 | ||
|
62c14cea2a | ||
|
01c3750835 | ||
|
52e5ee7111 | ||
|
3a0aa353cc | ||
|
8f5bb982cd | ||
|
a360ddbe0b | ||
|
06aba530d7 | ||
|
c2d6a137ab | ||
|
b927e58126 | ||
|
f1c3da6583 | ||
|
4c446ccd50 | ||
|
83aa0062c7 | ||
|
ff67fd10c3 | ||
|
7ba1e7d356 | ||
|
f533173b95 | ||
|
14d5fd7edb | ||
|
740ca54c87 | ||
|
6ce7e426d2 | ||
|
0c753619a4 | ||
|
4b06255cc2 | ||
|
1939e3e6ed | ||
|
9546c77113 | ||
|
0ba88ff138 | ||
|
215d7dbb74 | ||
|
91ba4cf75e | ||
|
779f70057b | ||
|
a44cb6430e | ||
|
1aadaa208d | ||
|
6d070e8607 | ||
|
5056a51c18 | ||
|
3c209ba502 | ||
|
71890707c0 | ||
|
b3b2e9df35 | ||
|
3aba7f8fec | ||
|
d6e4fe7c1f | ||
|
c9d628bf18 | ||
|
35358f8180 | ||
|
019e5ca3e8 | ||
|
6423edaa76 | ||
|
44153a28e3 | ||
|
bcf0635efd | ||
|
92dbe1684e | ||
|
7879091cea | ||
|
73adac2501 | ||
|
d801151d5c | ||
|
ea43d0f687 | ||
|
396f812496 | ||
|
f6d8c9659c | ||
|
e2d77f21ba | ||
|
ef6f9487f4 | ||
|
8140374e26 | ||
|
d67277a668 | ||
|
97ef8e8d0e | ||
|
2090f958c8 | ||
|
8731f4ba27 | ||
|
b3da35be74 | ||
|
1c3449e5a2 | ||
|
a351d1834a | ||
|
31ffe79a29 | ||
|
d9cd6f9c50 | ||
|
e079cbb055 | ||
|
7004388105 | ||
|
9afa7cc91f | ||
|
9a9a4fcfd4 | ||
|
74fe2fb2a6 | ||
|
eeb5fdbc9f | ||
|
a83e5be8f2 | ||
|
75c7df46a7 | ||
|
efaf4024ab | ||
|
99fd1e622c | ||
|
37e5f6af48 | ||
|
5b8c997180 | ||
|
7c547f6452 | ||
|
f7e8ec1855 | ||
|
72de2d5254 | ||
|
f25d0a4e0e | ||
|
1d3f2674d1 | ||
|
f9ebcb5d2e | ||
|
b2fdd83594 | ||
|
91eb30f037 | ||
|
d936b25868 | ||
|
e7c2274893 | ||
|
377fc0352a | ||
|
939a2d67ed | ||
|
304e294ea7 | ||
|
ae793f6cf1 | ||
|
1d638e018a | ||
|
50c7d39e08 | ||
|
c5bffed21e | ||
|
d48dd7eb6a | ||
|
b013133cc1 | ||
|
1bafa36c17 | ||
|
afaf6f894c | ||
|
8b3c465a50 | ||
|
40c614b3a5 | ||
|
ac36a2289f | ||
|
fa2b784f7e | ||
|
01e17806f6 | ||
|
f561e7febf | ||
|
39d4f0df66 | ||
|
1d468ac35a | ||
|
ce2ca13815 | ||
|
f256b836f4 | ||
|
71fb38c91f | ||
|
076c189948 | ||
|
9f66f6495a | ||
|
c07a857359 | ||
|
76df836378 | ||
|
a545f67a21 | ||
|
9d5818916a | ||
|
ea56ff255d | ||
|
01d3eea379 | ||
|
f13eccb03d | ||
|
39d7bc6c24 | ||
|
6a7f63166d | ||
|
bae59a8c07 | ||
|
bb039d4bc7 | ||
|
92b2f463e3 | ||
|
16e6a01881 | ||
|
81bc9e488f | ||
|
e64e4d7ef7 | ||
|
f19e8dd086 | ||
|
b4ae6e9774 | ||
|
5a9f780cf8 | ||
|
2d1197f59f | ||
|
d8f2a1354f | ||
|
b572f75dba | ||
|
14e16db01e | ||
|
7091bc81c2 | ||
|
1e947ca893 | ||
|
b9fe37aec1 | ||
|
5d68a4c992 | ||
|
760b80934f | ||
|
6d564628b7 | ||
|
f03924478b | ||
|
7c1a465ec9 | ||
|
a7ef663735 | ||
|
186211244d | ||
|
5740ee0444 | ||
|
945939ba68 | ||
|
9137721e8e | ||
|
566425166b | ||
|
d1513732be | ||
|
1467072f3d | ||
|
d133b40b25 | ||
|
c3eb17db74 | ||
|
4789708cc2 | ||
|
83fce74bb1 | ||
|
fcac98b367 | ||
|
97ff7afbe7 | ||
|
7e58b965b7 | ||
|
1ca7e5efe9 | ||
|
d460986de0 | ||
|
bdec11c501 | ||
|
b1aa9e5846 | ||
|
51bfda937d | ||
|
4a9ea832ff | ||
|
a8498b0dab | ||
|
48e5040196 | ||
|
928e18b66d | ||
|
62aa7a52c4 | ||
|
3c472fd7e0 | ||
|
b15f689ccf | ||
|
d960effb99 | ||
|
1e82cb6a7c | ||
|
31b62203a7 | ||
|
af59c41713 | ||
|
f7ffbcca6c | ||
|
342a0d0091 | ||
|
1974bb7e59 | ||
|
e6b4fc9182 | ||
|
c778dcbc9c | ||
|
a0944dab7a | ||
|
719d87de3b | ||
|
816acc9c76 | ||
|
a9fefb2eeb | ||
|
4ad448993c | ||
|
bcadffa0e3 | ||
|
ce2d2c9f32 | ||
|
6f652e1d2f | ||
|
361583edfc | ||
|
fbf29274e8 | ||
|
3c35d647b8 | ||
|
b4159d30fc | ||
|
f843d0732b | ||
|
280f041acb | ||
|
4f00012268 | ||
|
4ab630b832 | ||
|
a32ca43288 | ||
|
f505d43fc0 | ||
|
6a01c277e8 | ||
|
684c2ec47a | ||
|
273d75fd51 | ||
|
6f6b0b9661 | ||
|
2253100ac6 | ||
|
b31644e228 | ||
|
57430fd189 | ||
|
6ffcfcd7e9 | ||
|
516ddb22ae | ||
|
0b74b3833a | ||
|
ef21879df4 | ||
|
ad92c30472 | ||
|
bfafbdc51e | ||
|
1a7c5693cc | ||
|
f8b935ab37 | ||
|
8705e88e91 | ||
|
0be4db30eb | ||
|
821dd8400b | ||
|
c37e1fb9f1 | ||
|
5088d33fd2 | ||
|
39f7bea53e | ||
|
842f08dcfc | ||
|
f8d9cd9a03 | ||
|
960093700a | ||
|
54d393632d | ||
|
c633c6d083 | ||
|
96ebdfc9a8 | ||
|
f55120654a | ||
|
44ca7c7d22 | ||
|
58c2059e10 | ||
|
b7bccb9058 | ||
|
f046bd7d50 | ||
|
593f452986 | ||
|
97662f5c8e | ||
|
f8d88cc83d | ||
|
f393aa684e | ||
|
4c319c8f4f | ||
|
477b72ad33 | ||
|
3728cbb62a | ||
|
015f88bf83 | ||
|
02082cda09 | ||
|
5eec0f5901 | ||
|
3fbc25155b | ||
|
ecac05b2fc | ||
|
5b54475d3e | ||
|
391497645f | ||
|
f8fcb98c68 | ||
|
2eff1de560 | ||
|
1fbb41f5e2 | ||
|
01afa8957f | ||
|
5162203c7d | ||
|
b0940d696b | ||
|
fe73d696cb | ||
|
2ea4a78541 | ||
|
f5891940e0 | ||
|
cd9310afed | ||
|
64041a84a2 | ||
|
78ee63af38 | ||
|
455c495338 | ||
|
0bf70d677e | ||
|
2be630904f | ||
|
49cebc2da4 | ||
|
049abc9b1a | ||
|
e8c44e700d | ||
|
54a091ca59 | ||
|
ec32618e11 | ||
|
aaf1726965 | ||
|
6b970954d6 | ||
|
fb5aefffb2 | ||
|
20e2c70464 | ||
|
ed2f680202 | ||
|
5e0df9b696 | ||
|
ec41252535 | ||
|
4df9df03ab | ||
|
7441974d9d | ||
|
79222a56e3 | ||
|
cadb7142f0 | ||
|
251055302e | ||
|
b54d1651ab | ||
|
9d0175b81a | ||
|
002430db46 | ||
|
de30a72c4e | ||
|
0a956bbc73 | ||
|
1e9a596908 | ||
|
b9568279dc | ||
|
fcdd9ea986 | ||
|
c145bfa524 | ||
|
18ebc858fd | ||
|
6597a5c860 | ||
|
a3ffa64550 | ||
|
a6305ac891 | ||
|
06fc8358d9 | ||
|
c346d875a2 | ||
|
a2a09ffe01 | ||
|
21ffed3bf5 | ||
|
fd7aab466b | ||
|
da87e825a1 | ||
|
7befd63cce | ||
|
2c2c39b42c | ||
|
0692cbe701 | ||
|
ab10524cd7 | ||
|
1ccfba13eb | ||
|
b9ed8283b7 | ||
|
1495bfb73e | ||
|
9b02c31f8d | ||
|
50dae9d4f3 | ||
|
9a3931dac6 | ||
|
515197fba2 | ||
|
149b6d59cf | ||
|
37fb52aada | ||
|
db9f5f44e0 | ||
|
094af0f78f | ||
|
cebb4dd17a | ||
|
3487e1cb64 | ||
|
440afcedb0 | ||
|
b0a21c9389 | ||
|
fc4a1ef193 | ||
|
c8533c0b0d | ||
|
b0a018d823 | ||
|
3becd4386b | ||
|
3099a70edc | ||
|
cde85947c7 | ||
|
5d10045cc8 | ||
|
520594e529 | ||
|
95b300f1ea | ||
|
bd6e8533ad | ||
|
843c2d67eb | ||
|
89aaedc06c | ||
|
94d4d12ee0 | ||
|
997a3709d4 | ||
|
76cc8ce043 | ||
|
9499066375 | ||
|
25f7cf23d3 | ||
|
9ee68b9268 | ||
|
1208300ddf | ||
|
06a7d447f6 | ||
|
766e833a73 | ||
|
6d5a2ceefe | ||
|
149bc8e9ce | ||
|
9957aeb003 | ||
|
634612ae81 | ||
|
f0d2aab784 | ||
|
4c69290381 | ||
|
640aaa8c23 | ||
|
1aa5fa03f9 | ||
|
99ba02afb6 | ||
|
9aedb5afff | ||
|
e21756ea8c | ||
|
35ccfdb799 | ||
|
91c9082d19 | ||
|
4f4ccdaa59 | ||
|
01e058d8c1 | ||
|
f816e3cd3f | ||
|
49109b9213 | ||
|
d43399c2b4 | ||
|
308877aa8f | ||
|
159f14c076 | ||
|
8e4de055b8 | ||
|
d7f4e40f85 | ||
|
6e2fcc9e11 | ||
|
0d31e31282 | ||
|
4dc4c589ba | ||
|
d53d58a5d4 | ||
|
e704072892 | ||
|
a54bbae622 | ||
|
08c140b9b4 | ||
|
0c6362f28d | ||
|
67d473aab7 | ||
|
20c781b23b | ||
|
7f138e3538 | ||
|
3211b265d7 | ||
|
bc2940e16a | ||
|
1131038fdb | ||
|
d1528c1044 | ||
|
51d7a6289e | ||
|
10678096e5 | ||
|
51e62761ee | ||
|
d1cceac7d1 | ||
|
b8482a5d89 | ||
|
3139af8487 | ||
|
6fcdac0d36 | ||
|
073cb91f88 | ||
|
3154c2644a | ||
|
e225a72180 | ||
|
ec85266860 | ||
|
4c25e3ce75 | ||
|
8ae67b84db | ||
|
440dcdf022 | ||
|
22327bbe71 | ||
|
b8b8c8d4ac | ||
|
93870c315f | ||
|
13d67c6524 | ||
|
e8dc1564b6 | ||
|
f9578066d6 | ||
|
71e73bb6f8 | ||
|
8638076aa1 | ||
|
965ee5687a | ||
|
2cb22ad280 | ||
|
4dbcedd03f | ||
|
5ba13297c6 | ||
|
5fe9a7a6c3 | ||
|
a028894855 | ||
|
183ed7b90b | ||
|
24b9ed106f | ||
|
7c8a010378 | ||
|
817ecf8225 | ||
|
4509fde410 | ||
|
34cf28712c | ||
|
1fbc17d275 | ||
|
908ac813e0 | ||
|
3c96d5e0d5 | ||
|
c5705705d5 | ||
|
03a1d68b74 | ||
|
50eff80ca1 | ||
|
96c118779d | ||
|
64c591b234 | ||
|
0e5c377680 | ||
|
6d758795af | ||
|
dedc9e4edc | ||
|
fa7cfc77d8 | ||
|
14692eed40 | ||
|
f0aab541f8 | ||
|
026293f773 | ||
|
5d5f1b86fd | ||
|
ba159ba971 | ||
|
e5b9bfb2e7 | ||
|
8cb8273e67 | ||
|
73d8343914 | ||
|
3960eb7d32 | ||
|
4004e0faee | ||
|
b47993b736 | ||
|
ca061080c1 | ||
|
f1bd9700f7 | ||
|
5f63c781b4 | ||
|
43e6f05ed5 | ||
|
aad5ca5474 | ||
|
cf2393a509 | ||
|
81c0a1c4bd | ||
|
6fc745e98c | ||
|
7579fff532 | ||
|
7ac2a23bc8 | ||
|
6c4cf085e0 | ||
|
b51f1f1d41 | ||
|
8d7dcdfc5b | ||
|
3fe518ff2b | ||
|
98d6904e4a | ||
|
d7d2d9f612 | ||
|
2a5d291bd9 | ||
|
e2a65a7077 | ||
|
671d3c1c80 | ||
|
6cc3587da2 | ||
|
9f9b5254a2 | ||
|
284e374ae8 | ||
|
4cdf669154 | ||
|
4332b62a62 | ||
|
7138425c85 | ||
|
b67844e74c | ||
|
cea285f5f5 | ||
|
1fd90e9dd0 | ||
|
c523765c19 | ||
|
cd527c44a4 | ||
|
6476023cf7 | ||
|
a01a48793c | ||
|
18e628e873 | ||
|
00f75e2d54 | ||
|
81207c6502 | ||
|
8dd3a02747 | ||
|
3db83a189c | ||
|
b6e9a54951 | ||
|
6c374f5f08 | ||
|
4320830a86 | ||
|
603e3e7e2e | ||
|
109ae5bae0 | ||
|
41cb8d7ec6 | ||
|
494483fd2b | ||
|
a6c8a37a5d | ||
|
87efa700ab | ||
|
e38adf6006 | ||
|
25ffc4c4b0 | ||
|
8211befc29 | ||
|
1dc7f80034 | ||
|
cb7ff81ade | ||
|
400a2f7201 | ||
|
c123558314 | ||
|
f287d9deac | ||
|
534d156b12 | ||
|
54d88e4dbf | ||
|
95969e87a1 | ||
|
e29126ca26 | ||
|
1b5c78b066 | ||
|
1c3402d081 | ||
|
6b3b119db0 | ||
|
66461ac500 | ||
|
46e840fdf1 | ||
|
7033e28572 | ||
|
0008b22d8b | ||
|
8f9bd9617f | ||
|
0f64ee6a5f | ||
|
c23bf2fd22 | ||
|
3ad559ab22 | ||
|
5f1074471d | ||
|
c04cee7ff7 | ||
|
4123343130 | ||
|
faec21d572 | ||
|
cb35fb8d0e | ||
|
213f03351f | ||
|
00be211169 | ||
|
90fc720190 | ||
|
23b3711f96 | ||
|
6aa30d334c | ||
|
54cb077b40 | ||
|
92847b9774 | ||
|
6e5716f097 | ||
|
6f7454243e | ||
|
45c39d078f | ||
|
385babb458 | ||
|
904b128afe | ||
|
dffffc784e | ||
|
40fb387185 | ||
|
63acf0a7b4 | ||
|
fce000206f | ||
|
f5e4171df4 | ||
|
0aaec9ae4f | ||
|
d25452a64e | ||
|
41d0f74750 | ||
|
22bb260ae3 | ||
|
d82ae31fc1 | ||
|
cf5c01a5b1 | ||
|
f2015eee80 | ||
|
c0f0462550 | ||
|
1f70589deb | ||
|
f8f1c3cf23 | ||
|
ed4dce2fb6 | ||
|
4d49486cc9 | ||
|
b7dd32274c | ||
|
d8e0b14dc4 | ||
|
8bebd7f042 | ||
|
90da57a806 | ||
|
b142407b21 | ||
|
10266f65e4 | ||
|
c49ee87857 | ||
|
5b04a17730 | ||
|
4ea0a567bc | ||
|
8aeec1d52d | ||
|
f39d2de0cc | ||
|
1ba08f4641 | ||
|
4b65315cdc | ||
|
903fae94be | ||
|
275351e19c | ||
|
35c233d4c5 | ||
|
e099a3f7e8 | ||
|
4313466589 | ||
|
d2a3acd493 | ||
|
516dd6bd1a | ||
|
44bc60021d | ||
|
50d5eb0621 | ||
|
3d3acb7a92 | ||
|
bee88b1c7f | ||
|
98174b7a37 | ||
|
5d03919b59 | ||
|
2357d167b8 | ||
|
013a26aafe | ||
|
fe9927d10d | ||
|
df4fd7df7f | ||
|
c8ff812ab8 | ||
|
6d0e255ca1 | ||
|
3105f314cb | ||
|
6db906d623 | ||
|
418677ef31 | ||
|
9c10965997 | ||
|
1bd778d0ae | ||
|
87155e346c | ||
|
52dd09bdf7 | ||
|
d344ffe370 | ||
|
4745ab64cd | ||
|
288ea3d19b | ||
|
8ade44c9a3 | ||
|
fa3a46498f | ||
|
953a2590e2 | ||
|
02b628653b | ||
|
3546f57a42 | ||
|
8d3bc6b6b9 | ||
|
c75ba0f855 | ||
|
d1603f1945 | ||
|
84c63f4f01 | ||
|
514080653f | ||
|
603ed22015 | ||
|
ef0752972a | ||
|
fd5b155ee7 | ||
|
40fbae8ff6 | ||
|
ce4a86fbcd | ||
|
54d7477679 | ||
|
6f7d901a1f | ||
|
5eb71fc6a9 | ||
|
8a3aba1634 | ||
|
bb8e6ef35e | ||
|
529e2054ea | ||
|
158b07a39e | ||
|
932531c8ba | ||
|
69c21454ec | ||
|
564e394ec8 | ||
|
f0e4e07c05 | ||
|
df932c6587 | ||
|
81c1a95166 | ||
|
bcf45c74a1 | ||
|
8b576fd2bd | ||
|
7fdd68d768 | ||
|
f825d7753a | ||
|
19cb6ad588 | ||
|
4a84084d9d | ||
|
25579fbedc | ||
|
59e1e51903 | ||
|
30ef5475c7 | ||
|
9804996db6 | ||
|
b774817ada | ||
|
8939096db6 | ||
|
df6d46de9e | ||
|
a325d814e2 | ||
|
cd1e8dc8cc | ||
|
377707d3db | ||
|
7ebc8c48a8 | ||
|
42bc04a0d2 | ||
|
4322222acc | ||
|
cce6a54701 | ||
|
68865353cf | ||
|
0e7f435921 | ||
|
20ba6e5fb5 | ||
|
cf94adb363 | ||
|
d74a23d5b2 | ||
|
750209b8bb | ||
|
31aaa6d1da | ||
|
823cd3862d | ||
|
741c23a72a | ||
|
28de461067 | ||
|
69c709b05a | ||
|
222a10891c | ||
|
cb2671a02c | ||
|
0a56631831 | ||
|
046d510134 | ||
|
90df092174 | ||
|
85bbab0e92 | ||
|
67db141203 | ||
|
c5ea8367aa | ||
|
f8adb508ab | ||
|
6613deca0f | ||
|
763b3c3236 | ||
|
718aca3d06 | ||
|
bf95cfb30e | ||
|
8df5ab8aa7 | ||
|
83efdccfe1 | ||
|
f1ebec641a | ||
|
a4521ac0bb | ||
|
c5f16276d7 | ||
|
03361e51ef | ||
|
85495c794d | ||
|
049b02cee4 | ||
|
05d2c1f0b0 | ||
|
3c9c39cb89 | ||
|
377f27b16f | ||
|
58321f3491 | ||
|
4e07f9574a | ||
|
da6f846a49 | ||
|
9ad356d66f | ||
|
836e8d2e28 | ||
|
b84dc8551a | ||
|
f7d502c68c | ||
|
9d2516a199 | ||
|
e7ad373d89 | ||
|
dea33a3c1d | ||
|
1f16380efc | ||
|
05a8232a8f | ||
|
0063d52952 | ||
|
2e82c1d40c | ||
|
820892d7cc | ||
|
0d2105dec4 | ||
|
480faca559 | ||
|
0975dbc3dd | ||
|
8aa02320e0 | ||
|
470518eb3a | ||
|
ba609f3600 | ||
|
ca659136e2 | ||
|
f1a6dc5332 | ||
|
4eaa7dc8b1 | ||
|
94510edd72 | ||
|
9ad29e8d85 | ||
|
07f3d27fb8 | ||
|
f2692e60f3 | ||
|
009623823d | ||
|
a32a3e25ad | ||
|
3e3be9ae6f | ||
|
3c648ff9fd | ||
|
f769b0b4c6 | ||
|
75bd626f33 | ||
|
f2471f0f68 | ||
|
d9b24f7705 | ||
|
a389983d7d | ||
|
82d3755e25 | ||
|
6ccc7e77f9 | ||
|
b62e4c0cc7 | ||
|
f6f0fbbd9f | ||
|
c13a90540c | ||
|
6fd729e285 | ||
|
8ad9692daa | ||
|
d02858040e | ||
|
3ee0fec729 | ||
|
ec063470d7 | ||
|
b2ed2bf810 | ||
|
f8a02a5c9b | ||
|
6685810081 | ||
|
53d40df292 | ||
|
af6bf11793 | ||
|
d5b5f0503c | ||
|
555cb40efd | ||
|
9d22fce53f | ||
|
aee0999daa | ||
|
1bdde1f947 | ||
|
319ce8c19f | ||
|
4e66f55d84 | ||
|
6826f1d605 | ||
|
0161520b33 | ||
|
12cd8a7bea | ||
|
2d00a727f6 | ||
|
b123f4f948 | ||
|
fd9a8d1551 | ||
|
6bc1150ad0 | ||
|
45cce1d19a | ||
|
147366bc0a | ||
|
6667ff9330 | ||
|
5bf091149d | ||
|
be0df38453 | ||
|
fcfb3d2df7 | ||
|
1ff8136f98 | ||
|
182e19eae3 | ||
|
e8b0a7c6f0 | ||
|
d4f2059b78 | ||
|
180ed0cc1a | ||
|
b7d82354cb | ||
|
95f6860005 | ||
|
e6872aba75 | ||
|
11cbeb8f96 | ||
|
886b372ade | ||
|
534328f16d | ||
|
318d11481d | ||
|
5d425ecc02 | ||
|
c225ecbb55 | ||
|
0b4807dc1f | ||
|
d33de2e427 | ||
|
1191c33c2b | ||
|
52054469cd | ||
|
a90eaafa70 | ||
|
bbd9e4de9b | ||
|
8abe6b6732 | ||
|
e936bc4c60 | ||
|
f724059b88 | ||
|
1e702ee40f | ||
|
a2d0d5a71d | ||
|
c343036d3b | ||
|
7c5047b2ac | ||
|
961285d6ab | ||
|
df18d8560d | ||
|
3531c5bb8c | ||
|
cc41b039e6 | ||
|
7e0e1ec51d | ||
|
ab41816265 | ||
|
2c95973446 | ||
|
89d8f9b829 | ||
|
0bda885bbf | ||
|
9ca74cd009 | ||
|
74d4a98864 | ||
|
f619a04fe7 | ||
|
5627b4a9c5 | ||
|
46a13c8767 | ||
|
3bbe33132a | ||
|
b48540dc32 | ||
|
4e0a08345d | ||
|
9b1e0eb57d | ||
|
4cbae0a751 | ||
|
68c2833552 | ||
|
dbe14c6be5 | ||
|
7c85462ff3 | ||
|
8db4a2a5b9 | ||
|
2dce08caf1 | ||
|
f5729d9a7c | ||
|
0b2044e9a4 | ||
|
1c91d2f242 | ||
|
5fe9e30f39 | ||
|
4eb9083ddc | ||
|
b28f682ad9 | ||
|
eb079c8060 | ||
|
c0f9ccc5b5 | ||
|
51c39ec681 | ||
|
10436ed70c | ||
|
775236a1b3 | ||
|
b9503ff15f | ||
|
b4dff181f7 | ||
|
737fc1a2a4 | ||
|
bdff8591aa | ||
|
27c3775f99 | ||
|
bf0a577fb9 | ||
|
7d79abb607 | ||
|
2680dba0aa | ||
|
832a61f886 | ||
|
cf4df19986 | ||
|
c156034069 | ||
|
086a7e19f0 | ||
|
2f37cb31d9 | ||
|
e5534cd1f3 | ||
|
4f0ec908ec | ||
|
bd1ea64d02 | ||
|
649753e97e | ||
|
fc656b6927 | ||
|
94cd831e8d | ||
|
ffcc58cb3e | ||
|
8c7fd3327e | ||
|
b1ffc8ddab | ||
|
3e11d94829 | ||
|
82287e0407 | ||
|
ef6cbdfa2a | ||
|
a3173b5371 | ||
|
54fb799d95 | ||
|
565202c990 | ||
|
63c21c53af | ||
|
74e7c13a17 | ||
|
3cfcc83ea9 | ||
|
ee94be624e | ||
|
2fe3dc5960 | ||
|
fd7338d3f3 | ||
|
ed185f047f | ||
|
d384d991fa | ||
|
9913080a82 | ||
|
ec157b766e | ||
|
469ef3e06d | ||
|
107b470289 | ||
|
8b14b946e2 | ||
|
f7b912fc9d | ||
|
9fbec3793f | ||
|
e4449a0ba3 | ||
|
42dc3ed03f | ||
|
f6ed2036b3 | ||
|
f8bf71e152 | ||
|
61a2355618 | ||
|
248920a221 | ||
|
b2ecb9ac09 | ||
|
f738d7566e | ||
|
1d167f8fda | ||
|
460e83207f | ||
|
75116364c6 | ||
|
f7931c2ee2 | ||
|
dfc2b9c76a | ||
|
22bbf1bd1f | ||
|
10aac4fe17 | ||
|
42bc91463e | ||
|
b4fa6e120a | ||
|
2c744da9f7 | ||
|
ffaa47bf54 | ||
|
63c4469475 | ||
|
c1490cd627 | ||
|
1a390b6043 | ||
|
fac33498db | ||
|
31e84780a8 | ||
|
f3f8f3574a | ||
|
7bd26ce468 | ||
|
bcea19b957 | ||
|
5079ce8d64 | ||
|
f231a33f6e | ||
|
4fe497cd68 | ||
|
5655a33515 | ||
|
87db723008 | ||
|
11f8d25d94 | ||
|
4fbd5abe41 | ||
|
d524935b67 | ||
|
6d5c629b43 | ||
|
3390e84a9f | ||
|
2e1c123c05 | ||
|
83c5264598 | ||
|
6a5e9a59df | ||
|
bb62c62a03 | ||
|
9cc3d9d4ce | ||
|
d4b346b7ae | ||
|
52b3a54e4b | ||
|
be7f81421a | ||
|
17317ea308 | ||
|
cb32711077 | ||
|
5c96b1c628 | ||
|
884bd85495 | ||
|
94067f34cf | ||
|
87bcefd08a | ||
|
8a6776731a | ||
|
bfa5fe1598 | ||
|
ead59c0246 | ||
|
cfeadf858e | ||
|
7534eaf006 | ||
|
8a81aaaa0a | ||
|
b544661e81 |
@ -1,16 +1,17 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Chromium
|
||||
BasedOnStyle: Chromium
|
||||
IndentWidth: 4
|
||||
AlignConsecutiveMacros: false
|
||||
AlignConsecutiveAssignments: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
ColumnLimit: 140
|
||||
---
|
||||
Language: Cpp
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
AfterFunction: true
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeBraces: Custom
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
ColumnLimit: 140
|
||||
Cpp11BracedListStyle: false
|
||||
|
4
.clang-tidy
Normal file
4
.clang-tidy
Normal file
@ -0,0 +1,4 @@
|
||||
Checks:
|
||||
- modernize-use-using
|
||||
|
||||
SystemHeaders: false
|
8
.editorconfig
Normal file
8
.editorconfig
Normal file
@ -0,0 +1,8 @@
|
||||
# EditorConfig specs and documentation: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# C++ Code Style settings
|
||||
[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
|
||||
cpp_generate_documentation_comments = doxygen_slash_star
|
4
.git-blame-ignore-revs
Normal file
4
.git-blame-ignore-revs
Normal file
@ -0,0 +1,4 @@
|
||||
# .git-blame-ignore-revs
|
||||
|
||||
# tabs -> spaces
|
||||
bbb3b3e6f6e3c0f95873f22e6d0a4aaf350f49d9
|
32
.github/workflows/backport.yml
vendored
Normal file
32
.github/workflows/backport.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed, labeled]
|
||||
|
||||
# WARNING:
|
||||
# When extending this action, be aware that $GITHUB_TOKEN allows write access to
|
||||
# the GitHub repository. This means that it should not evaluate user input in a
|
||||
# way that allows code injection.
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
backport:
|
||||
permissions:
|
||||
contents: write # for korthout/backport-action to create branch
|
||||
pull-requests: write # for korthout/backport-action to create PR to backport
|
||||
name: Backport Pull Request
|
||||
if: github.repository_owner == 'PrismLauncher' && github.event.pull_request.merged == true && (github.event_name != 'labeled' || startsWith('backport', github.event.label.name))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@v2.1.0
|
||||
with:
|
||||
# Config README: https://github.com/korthout/backport-action#backport-action
|
||||
pull_description: |-
|
||||
Bot-based backport to `${target_branch}`, triggered by a label in #${pull_number}.
|
||||
|
207
.github/workflows/build.yml
vendored
207
.github/workflows/build.yml
vendored
@ -24,6 +24,12 @@ on:
|
||||
CACHIX_AUTH_TOKEN:
|
||||
description: Private token for authenticating against Cachix cache
|
||||
required: false
|
||||
GPG_PRIVATE_KEY:
|
||||
description: Private key for AppImage signing
|
||||
required: false
|
||||
GPG_PRIVATE_KEY_ID:
|
||||
description: ID for the GPG_PRIVATE_KEY, to select the signing key
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -31,56 +37,43 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 5
|
||||
|
||||
- os: ubuntu-20.04
|
||||
qt_ver: 6
|
||||
qt_host: linux
|
||||
qt_arch: ''
|
||||
qt_version: '6.2.4'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
qt_arch: ""
|
||||
qt_version: "6.2.4"
|
||||
qt_modules: "qt5compat qtimageformats"
|
||||
qt_tools: ""
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MinGW-w64"
|
||||
msystem: clang64
|
||||
vcvars_arch: 'amd64_x86'
|
||||
|
||||
- os: windows-2022
|
||||
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'
|
||||
vcvars_arch: "amd64_x86"
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC"
|
||||
msystem: ''
|
||||
architecture: 'x64'
|
||||
vcvars_arch: 'amd64'
|
||||
msystem: ""
|
||||
architecture: "x64"
|
||||
vcvars_arch: "amd64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: ''
|
||||
qt_version: '6.5.1'
|
||||
qt_version: '6.6.0'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
|
||||
- os: windows-2022
|
||||
name: "Windows-MSVC-arm64"
|
||||
msystem: ''
|
||||
architecture: 'arm64'
|
||||
vcvars_arch: 'amd64_arm64'
|
||||
msystem: ""
|
||||
architecture: "arm64"
|
||||
vcvars_arch: "amd64_arm64"
|
||||
qt_ver: 6
|
||||
qt_host: windows
|
||||
qt_arch: 'win64_msvc2019_arm64'
|
||||
qt_version: '6.5.1'
|
||||
qt_version: '6.6.0'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
|
||||
@ -90,7 +83,7 @@ jobs:
|
||||
qt_ver: 6
|
||||
qt_host: mac
|
||||
qt_arch: ''
|
||||
qt_version: '6.5.0'
|
||||
qt_version: '6.6.0'
|
||||
qt_modules: 'qt5compat qtimageformats'
|
||||
qt_tools: ''
|
||||
|
||||
@ -99,9 +92,9 @@ jobs:
|
||||
macosx_deployment_target: 10.13
|
||||
qt_ver: 5
|
||||
qt_host: mac
|
||||
qt_version: '5.15.2'
|
||||
qt_modules: ''
|
||||
qt_tools: ''
|
||||
qt_version: "5.15.2"
|
||||
qt_modules: ""
|
||||
qt_tools: ""
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@ -119,11 +112,11 @@ jobs:
|
||||
# PREPARE
|
||||
##
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: "true"
|
||||
|
||||
- name: 'Setup MSYS2'
|
||||
- name: "Setup MSYS2"
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@ -152,18 +145,18 @@ jobs:
|
||||
|
||||
- name: Setup ccache
|
||||
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||
uses: hendrikmuhs/ccache-action@v1.2.9
|
||||
uses: hendrikmuhs/ccache-action@v1.2.10
|
||||
with:
|
||||
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||
|
||||
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||
uses: actions/cache@v3.3.1
|
||||
uses: actions/cache@v3.3.2
|
||||
with:
|
||||
path: '${{ github.workspace }}\.ccache'
|
||||
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
${{ matrix.os }}-mingw-w64-ccache
|
||||
${{ matrix.os }}-mingw-w64-ccache
|
||||
|
||||
- name: Setup ccache (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||
@ -191,7 +184,7 @@ jobs:
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc
|
||||
sudo apt-get -y install ninja-build extra-cmake-modules scdoc appstream
|
||||
|
||||
- name: Install Dependencies (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
@ -208,35 +201,35 @@ jobs:
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: '==3.1.*'
|
||||
py7zrversion: '>=0.20.2'
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: 'windows'
|
||||
target: 'desktop'
|
||||
arch: ''
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
cache-key-prefix: host-qt-arm64-windows
|
||||
dir: ${{ github.workspace }}\HostQt
|
||||
set-env: false
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: "windows"
|
||||
target: "desktop"
|
||||
arch: ""
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
cache-key-prefix: host-qt-arm64-windows
|
||||
dir: ${{ github.workspace }}\HostQt
|
||||
set-env: false
|
||||
|
||||
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: '==3.1.*'
|
||||
py7zrversion: '>=0.20.2'
|
||||
version: ${{ matrix.qt_version }}
|
||||
host: ${{ matrix.qt_host }}
|
||||
target: 'desktop'
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
modules: ${{ matrix.qt_modules }}
|
||||
tools: ${{ matrix.qt_tools }}
|
||||
cache: ${{ inputs.is_qt_cached }}
|
||||
aqtversion: "==3.1.*"
|
||||
py7zrversion: ">=0.20.2"
|
||||
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' # We want this for MinGW builds as well, as we need SignTool
|
||||
if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
vsversion: 2022
|
||||
@ -249,7 +242,10 @@ jobs:
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||
|
||||
wget "https://github.com/AppImageCommunity/AppImageUpdate/releases/download/continuous/AppImageUpdate-x86_64.AppImage"
|
||||
|
||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
||||
sudo apt install libopengl0
|
||||
|
||||
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||
@ -263,23 +259,23 @@ jobs:
|
||||
- name: Configure CMake (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 -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
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||
|
||||
- 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
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
||||
|
||||
- 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 }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -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=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
- name: Configure CMake (Windows MSVC)
|
||||
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
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON -DLauncher_BUILD_ARTIFACT=${{ matrix.name }}-Qt${{ matrix.qt_ver }}
|
||||
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
||||
if ("${{ env.CCACHE_VAR }}")
|
||||
{
|
||||
@ -294,7 +290,7 @@ jobs:
|
||||
- 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 }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=official -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DLauncher_BUILD_ARTIFACT=Linux-Qt${{ matrix.qt_ver }} -G Ninja
|
||||
|
||||
##
|
||||
# BUILD
|
||||
@ -386,17 +382,16 @@ jobs:
|
||||
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
|
||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
||||
Copy-Item ${{ runner.workspace }}/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
||||
}
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||
|
||||
|
||||
- name: Fetch codesign certificate (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
||||
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
||||
run: |
|
||||
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
|
||||
|
||||
@ -406,7 +401,7 @@ jobs:
|
||||
if (Get-Content ./codesign.pfx){
|
||||
cd ${{ env.INSTALL_DIR }}
|
||||
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
||||
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_filelink.exe
|
||||
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_updater.exe prismlauncher_filelink.exe
|
||||
} else {
|
||||
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||
}
|
||||
@ -465,10 +460,15 @@ jobs:
|
||||
- name: Package AppImage (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
shell: bash
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
run: |
|
||||
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||
|
||||
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||
mv ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.metainfo.xml ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/metainfo/org.prismlauncher.PrismLauncher.appdata.xml
|
||||
export "NO_APPSTREAM=1" # we have to skip appstream checking because appstream on ubuntu 20.04 is outdated
|
||||
|
||||
export OUTPUT="PrismLauncher-Linux-x86_64.AppImage"
|
||||
|
||||
chmod +x linuxdeploy-*.AppImage
|
||||
|
||||
@ -479,10 +479,11 @@ jobs:
|
||||
|
||||
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 -r ${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||
|
||||
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
cp /usr/lib/x86_64-linux-gnu/libssl.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/
|
||||
cp /usr/lib/x86_64-linux-gnu/libOpenGL.so.0* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
||||
@ -491,8 +492,25 @@ jobs:
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
||||
export LD_LIBRARY_PATH
|
||||
|
||||
chmod +x AppImageUpdate-x86_64.AppImage
|
||||
cp AppImageUpdate-x86_64.AppImage ${{ env.INSTALL_APPIMAGE_DIR }}/usr/bin
|
||||
|
||||
export UPDATE_INFORMATION="gh-releases-zsync|${{ github.repository_owner }}|${{ github.event.repository.name }}|latest|PrismLauncher-Linux-x86_64.AppImage.zsync"
|
||||
|
||||
if [ '${{ secrets.GPG_PRIVATE_KEY_ID }}' != '' ]; then
|
||||
export SIGN=1
|
||||
export SIGN_KEY=${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
mkdir -p ~/.gnupg/
|
||||
echo "$GPG_PRIVATE_KEY" > ~/.gnupg/private.key
|
||||
gpg --import ~/.gnupg/private.key
|
||||
else
|
||||
echo ":warning: Skipped code signing for Linux AppImage, as gpg key was not present." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
./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
|
||||
|
||||
mv "PrismLauncher-Linux-x86_64.AppImage" "PrismLauncher-Linux-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||
|
||||
##
|
||||
# UPLOAD BUILDS
|
||||
##
|
||||
@ -529,14 +547,14 @@ jobs:
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
name: PrismLauncher-${{ runner.os }}-Qt5-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
name: PrismLauncher-${{ runner.os }}-Qt5-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||
path: PrismLauncher-portable.tar.gz
|
||||
|
||||
- name: Upload binary tarball (Linux, Qt 6)
|
||||
@ -560,6 +578,13 @@ jobs:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||
|
||||
- name: Upload AppImage Zsync (Linux)
|
||||
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage.zsync
|
||||
path: PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
|
||||
- name: ccache stats (Windows MinGW-w64)
|
||||
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||
shell: msys2 {0}
|
||||
@ -573,43 +598,13 @@ jobs:
|
||||
options: --privileged
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
if: inputs.build_type == 'Debug'
|
||||
with:
|
||||
submodules: 'true'
|
||||
submodules: "true"
|
||||
- name: Build Flatpak (Linux)
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: "Prism Launcher.flatpak"
|
||||
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||
|
||||
nix:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package:
|
||||
- prismlauncher
|
||||
- prismlauncher-qt5
|
||||
steps:
|
||||
- name: Clone repository
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: 'true'
|
||||
- name: Install nix
|
||||
if: inputs.build_type == 'Debug'
|
||||
uses: cachix/install-nix-action@v21
|
||||
with:
|
||||
install_url: https://nixos.org/nix/install
|
||||
extra_nix_config: |
|
||||
auto-optimise-store = true
|
||||
experimental-features = nix-command flakes
|
||||
- uses: cachix/cachix-action@v12
|
||||
if: inputs.build_type == 'Debug'
|
||||
with:
|
||||
name: prismlauncher
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- name: Build
|
||||
if: inputs.build_type == 'Debug'
|
||||
run: nix build .#${{ matrix.package }} --print-build-logs
|
||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
|
29
.github/workflows/trigger_builds.yml
vendored
29
.github/workflows/trigger_builds.yml
vendored
@ -3,26 +3,25 @@ name: Build Application
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'renovate/**'
|
||||
- "renovate/**"
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- "flake.lock"
|
||||
- "packages/**"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- '**/LICENSE'
|
||||
- 'flake.lock'
|
||||
- 'packages/**'
|
||||
- '.github/ISSUE_TEMPLATE/**'
|
||||
- '.markdownlint**'
|
||||
- "**.md"
|
||||
- "**/LICENSE"
|
||||
- "flake.lock"
|
||||
- "packages/**"
|
||||
- ".github/ISSUE_TEMPLATE/**"
|
||||
- ".markdownlint**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
build_debug:
|
||||
name: Build Debug
|
||||
uses: ./.github/workflows/build.yml
|
||||
@ -34,3 +33,5 @@ jobs:
|
||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
|
31
.github/workflows/trigger_release.yml
vendored
31
.github/workflows/trigger_release.yml
vendored
@ -3,10 +3,9 @@ name: Build Application and Make Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
|
||||
build_release:
|
||||
name: Build Release
|
||||
uses: ./.github/workflows/build.yml
|
||||
@ -18,6 +17,8 @@ jobs:
|
||||
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PRIVATE_KEY_ID: ${{ secrets.GPG_PRIVATE_KEY_ID }}
|
||||
|
||||
create_release:
|
||||
needs: build_release
|
||||
@ -26,10 +27,10 @@ jobs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'true'
|
||||
path: 'PrismLauncher-source'
|
||||
submodules: "true"
|
||||
path: "PrismLauncher-source"
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Grab and store version
|
||||
@ -41,9 +42,10 @@ jobs:
|
||||
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-Linux-Qt5-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-Linux-Qt5*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-x86_64.AppImage
|
||||
mv PrismLauncher-*.AppImage.zsync/PrismLauncher-*.AppImage.zsync PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||
|
||||
@ -78,25 +80,22 @@ jobs:
|
||||
- name: Create release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag_name: ${{ github.ref }}
|
||||
name: Prism Launcher ${{ env.VERSION }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: |
|
||||
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||
PrismLauncher-Linux-Qt5-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-Qt5-Portable-${{ env.VERSION }}.tar.gz
|
||||
PrismLauncher-Linux-x86_64.AppImage
|
||||
PrismLauncher-Linux-x86_64.AppImage.zsync
|
||||
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
|
||||
|
30
.github/workflows/update-flake.yml
vendored
Normal file
30
.github/workflows/update-flake.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: Update Flake Lockfile
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# run weekly on sunday
|
||||
- cron: "0 0 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
update-flake:
|
||||
if: github.repository == 'PrismLauncher/PrismLauncher'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@6a9a9e84a173d90b3ffb42c5ddaf9ea033fad011 # v23
|
||||
|
||||
- uses: DeterminateSystems/update-flake-lock@v20
|
||||
with:
|
||||
commit-msg: "chore(nix): update lockfile"
|
||||
pr-title: "chore(nix): update lockfile"
|
||||
pr-labels: |
|
||||
Linux
|
||||
packaging
|
||||
simple change
|
||||
changelog:omit
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -19,3 +19,6 @@
|
||||
[submodule "libraries/cmark"]
|
||||
path = libraries/cmark
|
||||
url = https://github.com/commonmark/cmark.git
|
||||
[submodule "flatpak/shared-modules"]
|
||||
path = flatpak/shared-modules
|
||||
url = https://github.com/flathub/shared-modules.git
|
||||
|
@ -33,6 +33,13 @@ if(MSVC)
|
||||
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
||||
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
|
||||
|
||||
# /EHs Enables stack unwind semantics for standard C++ exceptions to ensure stackframes are unwound
|
||||
# and object deconstructors are called when an exception is caught.
|
||||
# without it memory leaks and a warning is printed
|
||||
# /EHc tells the compiler to assume that functions declared as extern "C" never throw a C++ exception
|
||||
# This appears to not always be a defualt compiler option in CMAKE
|
||||
set(CMAKE_CXX_FLAGS "/EHsc ${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
|
||||
@ -85,6 +92,39 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
|
||||
# set CXXFLAGS for build targets
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
|
||||
option(DEBUG_ADDRESS_SANITIZER "Enable Address Sanitizer in Debug builds" OFF)
|
||||
|
||||
# If this is a Debug build turn on address sanitiser
|
||||
if ((CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") AND DEBUG_ADDRESS_SANITIZER)
|
||||
message(STATUS "Address Sanitizer enabled for Debug builds, Turn it off with -DDEBUG_ADDRESS_SANITIZER=off")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
# using clang with clang-cl front end
|
||||
message(STATUS "Address Sanitizer available on Clang MSVC frontend")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
else()
|
||||
# AppleClang and Clang
|
||||
message(STATUS "Address Sanitizer available on Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
endif()
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# GCC
|
||||
message(STATUS "Address Sanitizer available on GCC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
|
||||
link_libraries("asan")
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
message(STATUS "Address Sanitizer available on MSVC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /Oy-")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /Oy-")
|
||||
else()
|
||||
message(STATUS "Address Sanitizer not available on compiler ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||
|
||||
if(ENABLE_LTO)
|
||||
@ -138,7 +178,7 @@ set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL th
|
||||
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 7)
|
||||
set(Launcher_VERSION_MAJOR 8)
|
||||
set(Launcher_VERSION_MINOR 0)
|
||||
|
||||
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||
@ -146,10 +186,13 @@ set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.
|
||||
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.")
|
||||
set(Launcher_BUILD_PLATFORM "unknown" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
||||
|
||||
# Channel list URL
|
||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||
# Github repo URL with releases for updater
|
||||
set(Launcher_UPDATER_GITHUB_REPO "https://github.com/PrismLauncher/PrismLauncher" CACHE STRING "Base github URL for the updater.")
|
||||
|
||||
# Name to help updater identify valid artifacts
|
||||
set(Launcher_BUILD_ARTIFACT "" CACHE STRING "Artifact name to help the updater identify valid artifacts.")
|
||||
|
||||
# The metadata server
|
||||
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||
@ -176,6 +219,18 @@ set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL
|
||||
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
||||
|
||||
# Native libraries
|
||||
if(UNIX AND APPLE)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.dylib" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.dylib" CACHE STRING "Name of native openal library")
|
||||
elseif(UNIX)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "libglfw.so" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "libopenal.so" CACHE STRING "Name of native openal library")
|
||||
elseif(WIN32)
|
||||
set(Launcher_GLFW_LIBRARY_NAME "glfw.dll" CACHE STRING "Name of native glfw library")
|
||||
set(Launcher_OPENAL_LIBRARY_NAME "OpenAL.dll" CACHE STRING "Name of native openal library")
|
||||
endif()
|
||||
|
||||
# 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.
|
||||
@ -193,6 +248,11 @@ set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "
|
||||
# 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")
|
||||
|
||||
set(Launcher_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID})
|
||||
set(Launcher_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION})
|
||||
set(Launcher_COMPILER_TARGET_SYSTEM ${CMAKE_SYSTEM_NAME})
|
||||
set(Launcher_COMPILER_TARGET_SYSTEM_VERSION ${CMAKE_SYSTEM_VERSION})
|
||||
set(Launcher_COMPILER_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
|
||||
|
||||
#### Check the current Git commit and branch
|
||||
include(GetGitRevisionDescription)
|
||||
@ -286,6 +346,13 @@ add_subdirectory(program_info)
|
||||
|
||||
####################################### Install layout #######################################
|
||||
|
||||
set(Launcher_ENABLE_UPDATER NO)
|
||||
set(Launcher_BUILD_UPDATER NO)
|
||||
|
||||
if (NOT APPLE AND (NOT Launcher_UPDATER_GITHUB_REPO STREQUAL "" AND NOT Launcher_BUILD_ARTIFACT STREQUAL ""))
|
||||
set(Launcher_BUILD_UPDATER YES)
|
||||
endif()
|
||||
|
||||
if(NOT (UNIX AND APPLE))
|
||||
# Install "portable.txt" if selected component is "portable"
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL)
|
||||
@ -310,9 +377,9 @@ if(UNIX AND APPLE)
|
||||
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 "© 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_BUNDLE_COPYRIGHT "© 2022-2023 ${Launcher_Copyright_Mac}")
|
||||
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=" CACHE STRING "Public key for Sparkle update feed")
|
||||
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml" CACHE STRING "URL for Sparkle update feed")
|
||||
|
||||
set(MACOSX_SPARKLE_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")
|
||||
@ -321,8 +388,12 @@ if(UNIX AND APPLE)
|
||||
# directories to look for dependencies
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR})
|
||||
|
||||
if(NOT MACOSX_SPARKLE_UPDATE_PUBLIC_KEY STREQUAL "" AND NOT MACOSX_SPARKLE_UPDATE_FEED_URL STREQUAL "")
|
||||
set(Launcher_ENABLE_UPDATER YES)
|
||||
endif()
|
||||
|
||||
# install as bundle
|
||||
set(INSTALL_BUNDLE "full")
|
||||
set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies")
|
||||
|
||||
# Add the icon
|
||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||
@ -332,10 +403,10 @@ elseif(UNIX)
|
||||
|
||||
set(BINARY_DEST_DIR "bin")
|
||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
|
||||
set(JARS_DEST_DIR "share/${Launcher_Name}")
|
||||
|
||||
# install as bundle with no dependencies included
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
set(INSTALL_BUNDLE "nodeps" CACHE STRING "Use fixup_bundle to bundle dependencies")
|
||||
|
||||
# Set RPATH
|
||||
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
||||
@ -345,7 +416,7 @@ elseif(UNIX)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
||||
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "${KDE_INSTALL_DATADIR}/${Launcher_Name}")
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "share/${Launcher_Name}")
|
||||
|
||||
if(Launcher_ManPage)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||
@ -369,7 +440,7 @@ elseif(WIN32)
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
|
||||
# install as bundle
|
||||
set(INSTALL_BUNDLE "full")
|
||||
set(INSTALL_BUNDLE "full" CACHE STRING "Use fixup_bundle to bundle dependencies")
|
||||
else()
|
||||
message(FATAL_ERROR "Platform not supported")
|
||||
endif()
|
||||
|
@ -19,7 +19,7 @@ In an effort to ensure that the code you contribute is actually compatible with
|
||||
|
||||
This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message:
|
||||
|
||||
```
|
||||
```text
|
||||
<commit message>
|
||||
|
||||
Signed-off-by: Author name <Author email>
|
||||
@ -27,7 +27,7 @@ Signed-off-by: Author name <Author email>
|
||||
|
||||
By signing off your work, you agree to the terms below:
|
||||
|
||||
```
|
||||
```text
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
@ -61,3 +61,9 @@ As a bonus, you can also [cryptographically sign your commits][gh-signing-commit
|
||||
|
||||
[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
|
||||
|
||||
## Backporting to Release Branches
|
||||
|
||||
We use [automated backports](https://github.com/PrismLauncher/PrismLauncher/blob/develop/.github/workflows/backport.yml) to merge specific contributions from develop into `release` branches.
|
||||
|
||||
This is done when pull requests are merged and have labels such as `backport release-7.x` - which should be added along with the milestone for the release.
|
||||
|
27
README.md
27
README.md
@ -18,11 +18,18 @@
|
||||
</a>
|
||||
|
||||
- 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).
|
||||
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) tab (this also includes the pull requests status).
|
||||
|
||||
### Development Builds
|
||||
|
||||
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
||||
Please understand that these builds are not intended for most users. There may be bugs, and other instabilities. You have been warned.
|
||||
|
||||
There are development builds available through:
|
||||
|
||||
- [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions) (includes builds from pull requests opened by contribuitors)
|
||||
- [nightly.link](https://nightly.link/PrismLauncher/PrismLauncher/workflows/trigger_builds/develop) (this will always point only to the latest version of develop)
|
||||
|
||||
These have debug information in the binaries, so their file sizes are relatively larger.
|
||||
|
||||
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
|
||||
|
||||
@ -30,7 +37,7 @@ For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**,
|
||||
|
||||
[![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-git) [![prismlauncher-git](https://img.shields.io/badge/aur-prismlauncher--qt5--git-1793D1?label=AUR&logo=archlinux&logoColor=white)](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [![prismlauncher-git](https://img.shields.io/badge/mpr-prismlauncher--git-A80030?label=MPR&logo=debian&logoColor=white)](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[![prismlauncher-nightly](https://img.shields.io/badge/copr-prismlauncher--nightly-51A2DA?label=COPR&logo=fedora&logoColor=white)](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [![prismlauncher-nightly](https://img.shields.io/badge/OBS-prismlauncher--nightly-3AB6A9?logo=opensuse&logoColor=white)](https://build.opensuse.org/project/show/home:getchoo) [![prismlauncher-9999](https://img.shields.io/badge/gentoo-prismlauncher--9999-4D4270?label=Gentoo&logo=gentoo&logoColor=white)](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
||||
|
||||
These packages are also availiable to all the distributions based on the ones mentioned above.
|
||||
These packages are also available to all the distributions based on the ones mentioned above.
|
||||
|
||||
## Community & Support
|
||||
|
||||
@ -42,7 +49,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
|
||||
|
||||
- **Our Matrix space:**
|
||||
|
||||
[![PrismLauncher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix)
|
||||
[![Prism Launcher Space](https://img.shields.io/matrix/prismlauncher:matrix.org?style=for-the-badge&label=Matrix%20Space&logo=matrix&color=purple)](https://prismlauncher.org/matrix)
|
||||
|
||||
- **Our Subreddit:**
|
||||
|
||||
@ -50,7 +57,7 @@ Feel free to create a GitHub issue if you find a bug or want to suggest a new fe
|
||||
|
||||
## Translations
|
||||
|
||||
The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
|
||||
The translation effort for Prism Launcher 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>.
|
||||
|
||||
## Building
|
||||
|
||||
@ -82,14 +89,16 @@ Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/),
|
||||
|
||||
## Forking/Redistributing/Custom builds policy
|
||||
|
||||
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
||||
You are free to fork, redistribute and provide custom builds 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).
|
||||
- Make it clear that your fork is not Prism Launcher and is not endorsed by or affiliated with the Prism Launcher project (<https://prismlauncher.org>).
|
||||
- Go through [CMakeLists.txt](CMakeLists.txt) and change Prism Launcher'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:
|
||||
If you are just building Prism Launcher for your distribution, please make sure to set the `Launcher_BUILD_PLATFORM` to a slug representing your distribution. Examples are `archlinux`, `fedora` and `nixpkgs`.
|
||||
|
||||
Note 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)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -33,6 +33,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <qstringliteral.h>
|
||||
#include "BuildConfig.h"
|
||||
#include <QObject>
|
||||
|
||||
@ -59,15 +60,25 @@ Config::Config()
|
||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||
|
||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||
BUILD_ARTIFACT = "@Launcher_BUILD_ARTIFACT@";
|
||||
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
|
||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||
UPDATER_GITHUB_REPO = "@Launcher_UPDATER_GITHUB_REPO@";
|
||||
|
||||
COMPILER_NAME = "@Launcher_COMPILER_NAME@";
|
||||
COMPILER_VERSION = "@Launcher_COMPILER_VERSION@";
|
||||
|
||||
COMPILER_TARGET_SYSTEM = "@Launcher_COMPILER_TARGET_SYSTEM@";
|
||||
COMPILER_TARGET_SYSTEM_VERSION = "@Launcher_COMPILER_TARGET_SYSTEM_VERSION@";
|
||||
COMPILER_TARGET_SYSTEM_PROCESSOR = "@Launcher_COMPILER_TARGET_PROCESSOR@";
|
||||
|
||||
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())
|
||||
if (!MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||
{
|
||||
UPDATER_ENABLED = true;
|
||||
} else if(!UPDATER_GITHUB_REPO.isEmpty() && !BUILD_ARTIFACT.isEmpty()) {
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
|
||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||
@ -82,15 +93,13 @@ Config::Config()
|
||||
{
|
||||
GIT_REFSPEC = "refs/heads/stable";
|
||||
GIT_TAG = versionString();
|
||||
GIT_COMMIT = "";
|
||||
}
|
||||
|
||||
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
VERSION_CHANNEL.remove("refs/heads/");
|
||||
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) {
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
}
|
||||
else if (!GIT_COMMIT.isEmpty())
|
||||
{
|
||||
@ -109,6 +118,9 @@ Config::Config()
|
||||
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||
META_URL = "@Launcher_META_URL@";
|
||||
|
||||
GLFW_LIBRARY_NAME = "@Launcher_GLFW_LIBRARY_NAME@";
|
||||
OPENAL_LIBRARY_NAME = "@Launcher_OPENAL_LIBRARY_NAME@";
|
||||
|
||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||
TRANSLATIONS_URL = "@Launcher_TRANSLATIONS_URL@";
|
||||
MATRIX_URL = "@Launcher_MATRIX_URL@";
|
||||
@ -132,3 +144,16 @@ QString Config::printableVersionString() const
|
||||
}
|
||||
return vstr;
|
||||
}
|
||||
|
||||
QString Config::compilerID() const
|
||||
{
|
||||
if (COMPILER_VERSION.isEmpty())
|
||||
return COMPILER_NAME;
|
||||
return QStringLiteral("%1 - %2").arg(COMPILER_NAME).arg(COMPILER_VERSION);
|
||||
}
|
||||
|
||||
QString Config::systemID() const
|
||||
{
|
||||
return QStringLiteral("%1 %2 %3").arg(COMPILER_TARGET_SYSTEM, COMPILER_TARGET_SYSTEM_VERSION, COMPILER_TARGET_SYSTEM_PROCESSOR);
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,8 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* \brief The Config class holds all the build-time information passed from the build system.
|
||||
@ -68,14 +68,32 @@ class Config {
|
||||
|
||||
bool UPDATER_ENABLED = false;
|
||||
|
||||
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||
/// A short string identifying this build's platform or distribution.
|
||||
QString BUILD_PLATFORM;
|
||||
|
||||
/// A short string identifying this build's valid artifacts int he updater. For example, "lin64" or "win32".
|
||||
QString BUILD_ARTIFACT;
|
||||
|
||||
/// A string containing the build timestamp
|
||||
QString BUILD_DATE;
|
||||
|
||||
/// A string identifying the compiler use to build
|
||||
QString COMPILER_NAME;
|
||||
|
||||
/// A string identifying the compiler version used to build
|
||||
QString COMPILER_VERSION;
|
||||
|
||||
/// A string identifying the compiler target system os
|
||||
QString COMPILER_TARGET_SYSTEM;
|
||||
|
||||
/// A String identifying the compiler target system version
|
||||
QString COMPILER_TARGET_SYSTEM_VERSION;
|
||||
|
||||
/// A String identifying the compiler target processor
|
||||
QString COMPILER_TARGET_SYSTEM_PROCESSOR;
|
||||
|
||||
/// URL for the updater's channel
|
||||
QString UPDATER_BASE;
|
||||
QString UPDATER_GITHUB_REPO;
|
||||
|
||||
/// The public key used to sign releases for the Sparkle updater appcast
|
||||
QString MAC_SPARKLE_PUB_KEY;
|
||||
@ -134,6 +152,9 @@ class Config {
|
||||
*/
|
||||
QString META_URL;
|
||||
|
||||
QString GLFW_LIBRARY_NAME;
|
||||
QString OPENAL_LIBRARY_NAME;
|
||||
|
||||
QString BUG_TRACKER_URL;
|
||||
QString TRANSLATIONS_URL;
|
||||
QString MATRIX_URL;
|
||||
@ -145,7 +166,7 @@ class Config {
|
||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||
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 TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
||||
|
||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||
|
||||
@ -162,7 +183,7 @@ class Config {
|
||||
|
||||
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
|
||||
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
|
||||
QStringList MODRINTH_MRPACK_HOSTS{"cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com"};
|
||||
QStringList MODRINTH_MRPACK_HOSTS{ "cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com" };
|
||||
|
||||
QString FLAME_BASE_URL = "https://api.curseforge.com/v1";
|
||||
|
||||
@ -172,6 +193,18 @@ class Config {
|
||||
* \return The version number in string format (major.minor.revision.build).
|
||||
*/
|
||||
QString printableVersionString() const;
|
||||
|
||||
/**
|
||||
* \brief Compiler ID String
|
||||
* \return a string of the form "Name - Version" of just "Name" if the version is empty
|
||||
*/
|
||||
QString compilerID() const;
|
||||
|
||||
/**
|
||||
* \brief System ID String
|
||||
* \return a string of the form "OS Verison Processor"
|
||||
*/
|
||||
QString systemID() const;
|
||||
};
|
||||
|
||||
extern const Config BuildConfig;
|
||||
|
155
cmake/CompilerWarnings.cmake
Normal file
155
cmake/CompilerWarnings.cmake
Normal file
@ -0,0 +1,155 @@
|
||||
#
|
||||
# Function to set compiler warnings with reasonable defaults at the project level.
|
||||
# Taken from https://github.com/aminya/project_options/blob/main/src/CompilerWarnings.cmake
|
||||
# under the folowing license:
|
||||
#
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2022-2100 Amin Yahyaabadi
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
include_guard()
|
||||
|
||||
function(_set_project_warnings_add_target_link_option TARGET OPTIONS)
|
||||
target_link_options(${_project_name} INTERFACE ${OPTIONS})
|
||||
endfunction()
|
||||
|
||||
# Set the compiler warnings
|
||||
#
|
||||
# https://clang.llvm.org/docs/DiagnosticsReference.html
|
||||
# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md
|
||||
function(
|
||||
set_project_warnings
|
||||
_project_name
|
||||
MSVC_WARNINGS
|
||||
CLANG_WARNINGS
|
||||
GCC_WARNINGS
|
||||
)
|
||||
if("${MSVC_WARNINGS}" STREQUAL "")
|
||||
set(MSVC_WARNINGS
|
||||
/W4 # Baseline reasonable warnings
|
||||
/w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
|
||||
/w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/w14263 # 'function': member function does not override any base class virtual member function
|
||||
/w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not
|
||||
# be destructed correctly
|
||||
/w14287 # 'operator': unsigned/negative constant mismatch
|
||||
/we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside
|
||||
# the for-loop scope
|
||||
/w14296 # 'operator': expression is always 'boolean_value'
|
||||
/w14311 # 'variable': pointer truncation from 'type1' to 'type2'
|
||||
/w14545 # expression before comma evaluates to a function which is missing an argument list
|
||||
/w14546 # function call before comma missing argument list
|
||||
/w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||
/w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
|
||||
/w14555 # expression has no effect; expected expression with side- effect
|
||||
/w14619 # pragma warning: there is no warning number 'number'
|
||||
/w14640 # Enable warning on thread un-safe static member initialization
|
||||
/w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
|
||||
/w14905 # wide string literal cast to 'LPSTR'
|
||||
/w14906 # string literal cast to 'LPWSTR'
|
||||
/w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied
|
||||
/permissive- # standards conformance mode for MSVC compiler.
|
||||
)
|
||||
endif()
|
||||
|
||||
if("${CLANG_WARNINGS}" STREQUAL "")
|
||||
set(CLANG_WARNINGS
|
||||
-Wall
|
||||
-Wextra # reasonable and standard
|
||||
-Wshadow # warn the user if a variable declaration shadows one from a parent context
|
||||
-Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps
|
||||
# catch hard to track down memory errors
|
||||
-Wold-style-cast # warn for c-style casts
|
||||
-Wcast-align # warn for potential performance problem casts
|
||||
-Wunused # warn on anything being unused
|
||||
-Woverloaded-virtual # warn if you overload (not override) a virtual function
|
||||
-Wpedantic # warn if non-standard C++ is used
|
||||
-Wconversion # warn on type conversions that may lose data
|
||||
-Wsign-conversion # warn on sign conversions
|
||||
-Wnull-dereference # warn if a null dereference is detected
|
||||
-Wdouble-promotion # warn if float is implicit promoted to double
|
||||
-Wformat=2 # warn on security issues around functions that format output (ie printf)
|
||||
-Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation
|
||||
# -Wgnu-zero-variadic-macro-arguments (part of -pedantic) is triggered by every qCDebug() call and therefore results
|
||||
# in a lot of noise. This warning is only notifying us that clang is emulating the GCC behaviour
|
||||
# instead of the exact standard wording so we can safely ignore it
|
||||
-Wno-gnu-zero-variadic-macro-arguments
|
||||
)
|
||||
endif()
|
||||
|
||||
if("${GCC_WARNINGS}" STREQUAL "")
|
||||
set(GCC_WARNINGS
|
||||
${CLANG_WARNINGS}
|
||||
-Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist
|
||||
-Wduplicated-cond # warn if if / else chain has duplicated conditions
|
||||
-Wduplicated-branches # warn if if / else branches have duplicated code
|
||||
-Wlogical-op # warn about logical operations being used where bitwise were probably wanted
|
||||
-Wuseless-cast # warn if you perform a cast to the same type
|
||||
)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS})
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
|
||||
set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS})
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS})
|
||||
else()
|
||||
message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'")
|
||||
# TODO support Intel compiler
|
||||
endif()
|
||||
|
||||
# Add C warnings
|
||||
set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}")
|
||||
list(
|
||||
REMOVE_ITEM
|
||||
PROJECT_WARNINGS_C
|
||||
-Wnon-virtual-dtor
|
||||
-Wold-style-cast
|
||||
-Woverloaded-virtual
|
||||
-Wuseless-cast
|
||||
-Wextra-semi
|
||||
)
|
||||
|
||||
target_compile_options(
|
||||
${_project_name}
|
||||
INTERFACE # C++ warnings
|
||||
$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>
|
||||
# C warnings
|
||||
$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>
|
||||
)
|
||||
|
||||
# If we are using the compiler as a linker driver pass the warnings to it
|
||||
# (most useful when using LTO or warnings as errors)
|
||||
if(CMAKE_CXX_LINK_EXECUTABLE MATCHES "^<CMAKE_CXX_COMPILER>")
|
||||
_set_project_warnings_add_target_link_option(
|
||||
${_project_name} "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_WARNINGS_CXX}>"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_LINK_EXECUTABLE MATCHES "^<CMAKE_C_COMPILER>")
|
||||
_set_project_warnings_add_target_link_option(
|
||||
${_project_name} "$<$<COMPILE_LANGUAGE:C>:${PROJECT_WARNINGS_C}>"
|
||||
)
|
||||
endif()
|
||||
|
||||
endfunction()
|
@ -67,5 +67,16 @@
|
||||
<string>Alternate</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>Curseforge</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>curseforge</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
76
flake.lock
76
flake.lock
@ -3,11 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -21,11 +21,11 @@
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1683560683,
|
||||
"narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=",
|
||||
"lastModified": 1698882062,
|
||||
"narHash": "sha256-HkhafUayIqxXyHH1X8d9RDl1M2CkFgZLjKD3MzabiEo=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "006c75898cf814ef9497252b022e91c946ba8e17",
|
||||
"rev": "8c9fa2545007b49a5db5f650ae91f227672c3877",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -35,12 +35,15 @@
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"lastModified": 1685518550,
|
||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -73,11 +76,11 @@
|
||||
"libnbtplusplus": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1650031308,
|
||||
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
||||
"lastModified": 1690036783,
|
||||
"narHash": "sha256-A5kTgICnx+Qdq3Fir/bKTfdTt/T1NQP2SC+nhN1ENug=",
|
||||
"owner": "PrismLauncher",
|
||||
"repo": "libnbtplusplus",
|
||||
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
||||
"rev": "a5e8fd52b8bf4ab5d5bcc042b2a247867589985f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -86,13 +89,28 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-filter": {
|
||||
"locked": {
|
||||
"lastModified": 1694857738,
|
||||
"narHash": "sha256-bxxNyLHjhu0N8T3REINXQ2ZkJco0ABFPn6PIe2QUfqo=",
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"rev": "41fd48e00c22b4ced525af521ead8792402de0ea",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "nix-filter",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1685012353,
|
||||
"narHash": "sha256-U3oOge4cHnav8OLGdRVhL45xoRj4Ppd+It6nPC9nNIU=",
|
||||
"lastModified": 1699094435,
|
||||
"narHash": "sha256-YLZ5/KKZ1PyLrm2MO8UxRe4H3M0/oaYqNhSlq6FDeeA=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "aeb75dba965e790de427b73315d5addf91a54955",
|
||||
"rev": "9d5d25bbfe8c0297ebe85324addcb5020ed1a454",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -105,11 +123,11 @@
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1682879489,
|
||||
"narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=",
|
||||
"lastModified": 1698611440,
|
||||
"narHash": "sha256-jPjHjrerhYDy3q9+s5EAsuhyhuknNfowY6yt6pjn9pc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0",
|
||||
"rev": "0cbe9f69c234a7700596e943bfae7ef27a31b735",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -135,11 +153,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1684842236,
|
||||
"narHash": "sha256-rYWsIXHvNhVQ15RQlBUv67W3YnM+Pd+DuXGMvCBq2IE=",
|
||||
"lastModified": 1698852633,
|
||||
"narHash": "sha256-Hsc/cCHud8ZXLvmm8pxrXpuaPEeNaaUttaCvtdX/Wug=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "61e567d6497bc9556f391faebe5e410e6623217f",
|
||||
"rev": "dec10399e5b56aa95fcd530e0338be72ad6462a0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -153,9 +171,25 @@
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-parts": "flake-parts",
|
||||
"libnbtplusplus": "libnbtplusplus",
|
||||
"nix-filter": "nix-filter",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
25
flake.nix
25
flake.nix
@ -4,6 +4,7 @@
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
nix-filter.url = "github:numtide/nix-filter";
|
||||
pre-commit-hooks = {
|
||||
url = "github:cachix/pre-commit-hooks.nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
@ -20,8 +21,24 @@
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs:
|
||||
inputs.flake-parts.lib.mkFlake
|
||||
{inherit inputs;}
|
||||
{imports = [./nix];};
|
||||
outputs = {
|
||||
flake-parts,
|
||||
pre-commit-hooks,
|
||||
...
|
||||
} @ inputs:
|
||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
||||
imports = [
|
||||
pre-commit-hooks.flakeModule
|
||||
|
||||
./nix/dev.nix
|
||||
./nix/distribution.nix
|
||||
];
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
22
flatpak/libdecor.json
Normal file
22
flatpak/libdecor.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "libdecor",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [
|
||||
"-Ddemo=false"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.freedesktop.org/libdecor/libdecor.git",
|
||||
"commit": "73260393a97291c887e1074ab7f318e031be0ac6"
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"path": "patches/weird_libdecor.patch"
|
||||
}
|
||||
],
|
||||
"cleanup": [
|
||||
"/include",
|
||||
"/lib/pkgconfig"
|
||||
]
|
||||
}
|
@ -5,13 +5,6 @@ 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:
|
||||
@ -25,22 +18,34 @@ finish-args:
|
||||
- --filesystem=xdg-run/app/com.discordapp.Discord:create
|
||||
# Mod drag&drop
|
||||
- --filesystem=xdg-download:ro
|
||||
# FTBApp import
|
||||
- --filesystem=~/.ftba:ro
|
||||
|
||||
cleanup:
|
||||
- /lib/libGLU*
|
||||
|
||||
modules:
|
||||
# Might be needed by some Controller mods (see https://github.com/isXander/Controlify/issues/31)
|
||||
- shared-modules/libusb/libusb.json
|
||||
|
||||
# Needed for proper Wayland support
|
||||
- libdecor.json
|
||||
|
||||
- name: prismlauncher
|
||||
buildsystem: cmake-ninja
|
||||
builddir: true
|
||||
config-opts:
|
||||
- -DLauncher_BUILD_PLATFORM=flatpak
|
||||
- -DCMAKE_BUILD_TYPE=Debug
|
||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
- -DLauncher_QT_VERSION_MAJOR=5
|
||||
build-options:
|
||||
env:
|
||||
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
||||
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
||||
sources:
|
||||
- type: dir
|
||||
path: ../
|
||||
builddir: true
|
||||
- type: dir
|
||||
path: ../
|
||||
|
||||
- name: openjdk
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
@ -49,14 +54,45 @@ modules:
|
||||
- mv /app/jre /app/jdk/17
|
||||
- /usr/lib/sdk/openjdk8/install.sh
|
||||
- mv /app/jre /app/jdk/8
|
||||
cleanup: [/jre]
|
||||
cleanup:
|
||||
- /jre
|
||||
|
||||
- name: glfw
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
- -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
- -DBUILD_SHARED_LIBS:BOOL=ON
|
||||
- -DGLFW_USE_WAYLAND=ON
|
||||
sources:
|
||||
- type: git
|
||||
url: https://github.com/glfw/glfw.git
|
||||
commit: 3fa2360720eeba1964df3c0ecf4b5df8648a8e52
|
||||
- type: patch
|
||||
path: patches/0003-Don-t-crash-on-calls-to-focus-or-icon.patch
|
||||
- type: patch
|
||||
path: patches/0005-Add-warning-about-being-an-unofficial-patch.patch
|
||||
- type: patch
|
||||
path: patches/0007-Platform-Prefer-Wayland-over-X11.patch
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/cmake
|
||||
- /lib/pkgconfig
|
||||
|
||||
- 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]
|
||||
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.2.tar.xz
|
||||
sha256: c8bee4790d9058bacc4b6246456c58021db58a87ddda1a9d0139bf5f18f1f240
|
||||
x-checker-data:
|
||||
type: anitya
|
||||
project-id: 14957
|
||||
stable-only: true
|
||||
url-template: https://xorg.freedesktop.org/archive/individual/app/xrandr-$version.tar.xz
|
||||
cleanup:
|
||||
- /share/man
|
||||
- /bin/xkeystone
|
||||
|
||||
- name: gamemode
|
||||
buildsystem: meson
|
||||
config-opts:
|
||||
@ -67,19 +103,56 @@ modules:
|
||||
# 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
|
||||
- type: archive
|
||||
archive-type: tar-gzip
|
||||
url: https://api.github.com/repos/FeralInteractive/gamemode/tarball/1.7
|
||||
sha256: 57ce73ba605d1cf12f8d13725006a895182308d93eba0f69f285648449641803
|
||||
x-checker-data:
|
||||
type: json
|
||||
url: https://api.github.com/repos/FeralInteractive/gamemode/releases/latest
|
||||
version-query: .tag_name
|
||||
url-query: .tarball_url
|
||||
timestamp-query: .published_at
|
||||
cleanup:
|
||||
- /include
|
||||
- /lib/pkgconfig
|
||||
- /lib/libgamemodeauto.a
|
||||
|
||||
- name: glxinfo
|
||||
buildsystem: meson
|
||||
config-opts:
|
||||
- --bindir=/app/mesa-demos
|
||||
- -Degl=disabled
|
||||
- -Dglut=disabled
|
||||
- -Dosmesa=disabled
|
||||
- -Dvulkan=disabled
|
||||
- -Dwayland=disabled
|
||||
post-install:
|
||||
- mv -v /app/mesa-demos/glxinfo /app/bin
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://archive.mesa3d.org/demos/mesa-demos-9.0.0.tar.xz
|
||||
sha256: 3046a3d26a7b051af7ebdd257a5f23bfeb160cad6ed952329cdff1e9f1ed496b
|
||||
x-checker-data:
|
||||
type: anitya
|
||||
project-id: 16781
|
||||
stable-only: true
|
||||
url-template: https://archive.mesa3d.org/demos/mesa-demos-$version.tar.xz
|
||||
cleanup:
|
||||
- /include
|
||||
- /mesa-demos
|
||||
- /share
|
||||
modules:
|
||||
- shared-modules/glu/glu-9.json
|
||||
|
||||
- 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
|
||||
path: prime-run
|
||||
- type: file
|
||||
path: ../flatpak/prismlauncher
|
||||
path: prismlauncher
|
||||
|
@ -0,0 +1,24 @@
|
||||
diff --git a/src/wl_window.c b/src/wl_window.c
|
||||
index 52d3b9eb..4ac4eb5d 100644
|
||||
--- a/src/wl_window.c
|
||||
+++ b/src/wl_window.c
|
||||
@@ -2117,8 +2117,7 @@ void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title)
|
||||
void _glfwSetWindowIconWayland(_GLFWwindow* window,
|
||||
int count, const GLFWimage* images)
|
||||
{
|
||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
- "Wayland: The platform does not support setting the window icon");
|
||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the window icon\n");
|
||||
}
|
||||
|
||||
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
|
||||
@@ -2361,8 +2360,7 @@ void _glfwRequestWindowAttentionWayland(_GLFWwindow* window)
|
||||
|
||||
void _glfwFocusWindowWayland(_GLFWwindow* window)
|
||||
{
|
||||
- _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
- "Wayland: The platform does not support setting the input focus");
|
||||
+ fprintf(stderr, "!!! Ignoring Error: Wayland: The platform does not support setting the input focus\n");
|
||||
}
|
||||
|
||||
void _glfwSetWindowMonitorWayland(_GLFWwindow* window,
|
@ -0,0 +1,17 @@
|
||||
diff --git a/src/init.c b/src/init.c
|
||||
index 06dbb3f2..a7c6da86 100644
|
||||
--- a/src/init.c
|
||||
+++ b/src/init.c
|
||||
@@ -449,6 +449,12 @@ GLFWAPI int glfwInit(void)
|
||||
_glfw.initialized = GLFW_TRUE;
|
||||
|
||||
glfwDefaultWindowHints();
|
||||
+
|
||||
+ fprintf(stderr, "!!! Patched GLFW from https://github.com/Admicos/minecraft-wayland\n"
|
||||
+ "!!! If any issues with the window, or some issues with rendering, occur, "
|
||||
+ "first try with the built-in GLFW, and if that solves the issue, report there first.\n"
|
||||
+ "!!! Use outside Minecraft is untested, and things might break.\n");
|
||||
+
|
||||
return GLFW_TRUE;
|
||||
}
|
||||
|
20
flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
Normal file
20
flatpak/patches/0007-Platform-Prefer-Wayland-over-X11.patch
Normal file
@ -0,0 +1,20 @@
|
||||
diff --git a/src/platform.c b/src/platform.c
|
||||
index c5966ae7..3e7442f9 100644
|
||||
--- a/src/platform.c
|
||||
+++ b/src/platform.c
|
||||
@@ -49,12 +49,12 @@ static const struct
|
||||
#if defined(_GLFW_COCOA)
|
||||
{ GLFW_PLATFORM_COCOA, _glfwConnectCocoa },
|
||||
#endif
|
||||
-#if defined(_GLFW_X11)
|
||||
- { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
||||
-#endif
|
||||
#if defined(_GLFW_WAYLAND)
|
||||
{ GLFW_PLATFORM_WAYLAND, _glfwConnectWayland },
|
||||
#endif
|
||||
+#if defined(_GLFW_X11)
|
||||
+ { GLFW_PLATFORM_X11, _glfwConnectX11 },
|
||||
+#endif
|
||||
};
|
||||
|
||||
GLFWbool _glfwSelectPlatform(int desiredID, _GLFWplatform* platform)
|
40
flatpak/patches/weird_libdecor.patch
Normal file
40
flatpak/patches/weird_libdecor.patch
Normal file
@ -0,0 +1,40 @@
|
||||
diff --git a/src/libdecor.c b/src/libdecor.c
|
||||
index a9c1106..1aa38b3 100644
|
||||
--- a/src/libdecor.c
|
||||
+++ b/src/libdecor.c
|
||||
@@ -1391,22 +1391,32 @@ calculate_priority(const struct libdecor_plugin_description *plugin_description)
|
||||
static bool
|
||||
check_symbol_conflicts(const struct libdecor_plugin_description *plugin_description)
|
||||
{
|
||||
+ bool ret = true;
|
||||
char * const *symbol;
|
||||
+ void* main_prog = dlopen(NULL, RTLD_LAZY);
|
||||
+ if (!main_prog) {
|
||||
+ fprintf(stderr, "Plugin \"%s\" couldn't check conflicting symbols: \"%s\".\n",
|
||||
+ plugin_description->description, dlerror());
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
|
||||
symbol = plugin_description->conflicting_symbols;
|
||||
while (*symbol) {
|
||||
dlerror();
|
||||
- dlsym (RTLD_DEFAULT, *symbol);
|
||||
+ dlsym (main_prog, *symbol);
|
||||
if (!dlerror()) {
|
||||
fprintf(stderr, "Plugin \"%s\" uses conflicting symbol \"%s\".\n",
|
||||
plugin_description->description, *symbol);
|
||||
- return false;
|
||||
+ ret = false;
|
||||
+ break;
|
||||
}
|
||||
|
||||
symbol++;
|
||||
}
|
||||
|
||||
- return true;
|
||||
+ dlclose(main_prog);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static struct plugin_loader *
|
@ -5,7 +5,7 @@ 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/"
|
||||
export PATH="${PATH}${PATH:+:}/usr/lib/extensions/vulkan/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
|
||||
export VK_LAYER_PATH="/usr/lib/extensions/vulkan/share/vulkan/implicit_layer.d/"
|
||||
|
||||
exec /app/bin/prismrun "$@"
|
||||
|
1
flatpak/shared-modules
Submodule
1
flatpak/shared-modules
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 45094ca570be383d06df729b6972830ec63bd3df
|
6
garnix.yaml
Normal file
6
garnix.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
builds:
|
||||
exclude: []
|
||||
include:
|
||||
- "checks.x86_64-linux.*"
|
||||
- "devShells.*.*"
|
||||
- "packages.*.*"
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
* Copyright (C) 2022 Tayou <tayou@gmx.net>
|
||||
* Copyright (C) 2022 Tayou <git@tayou.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -38,16 +38,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <QApplication>
|
||||
#include <memory>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QFlag>
|
||||
#include <QIcon>
|
||||
#include <QDateTime>
|
||||
#include <QUrl>
|
||||
#include <memory>
|
||||
|
||||
#include <BaseInstance.h>
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "ui/themes/CatPack.h"
|
||||
|
||||
class LaunchController;
|
||||
class LocalPeer;
|
||||
@ -70,27 +71,22 @@ class TranslationsModel;
|
||||
class ITheme;
|
||||
class MCEditTool;
|
||||
class ThemeManager;
|
||||
class IconTheme;
|
||||
|
||||
namespace Meta {
|
||||
class Index;
|
||||
class Index;
|
||||
}
|
||||
|
||||
#if defined(APPLICATION)
|
||||
#undef APPLICATION
|
||||
#endif
|
||||
#define APPLICATION (static_cast<Application *>(QCoreApplication::instance()))
|
||||
#define APPLICATION (static_cast<Application*>(QCoreApplication::instance()))
|
||||
|
||||
class Application : public QApplication
|
||||
{
|
||||
class Application : public QApplication {
|
||||
// friends for the purpose of limiting access to deprecated stuff
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Status {
|
||||
StartingUp,
|
||||
Failed,
|
||||
Succeeded,
|
||||
Initialized
|
||||
};
|
||||
public:
|
||||
enum Status { StartingUp, Failed, Succeeded, Initialized };
|
||||
|
||||
enum Capability {
|
||||
None = 0,
|
||||
@ -102,33 +98,21 @@ public:
|
||||
};
|
||||
Q_DECLARE_FLAGS(Capabilities, Capability)
|
||||
|
||||
public:
|
||||
Application(int &argc, char **argv);
|
||||
public:
|
||||
Application(int& argc, char** argv);
|
||||
virtual ~Application();
|
||||
|
||||
bool event(QEvent* event) override;
|
||||
|
||||
std::shared_ptr<SettingsObject> settings() const {
|
||||
return m_settings;
|
||||
}
|
||||
std::shared_ptr<SettingsObject> settings() const { return m_settings; }
|
||||
|
||||
qint64 timeSinceStart() const {
|
||||
return startTime.msecsTo(QDateTime::currentDateTime());
|
||||
}
|
||||
qint64 timeSinceStart() const { return startTime.msecsTo(QDateTime::currentDateTime()); }
|
||||
|
||||
QIcon getThemedIcon(const QString& name);
|
||||
|
||||
void setIconTheme(const QString& name);
|
||||
ThemeManager* themeManager() { return m_themeManager.get(); }
|
||||
|
||||
void applyCurrentlySelectedTheme(bool initial = false);
|
||||
|
||||
QList<ITheme*> getValidApplicationThemes();
|
||||
|
||||
void setApplicationTheme(const QString& name);
|
||||
|
||||
shared_qobject_ptr<ExternalUpdater> updater() {
|
||||
return m_updater;
|
||||
}
|
||||
shared_qobject_ptr<ExternalUpdater> updater() { return m_updater; }
|
||||
|
||||
void triggerUpdateCheck();
|
||||
|
||||
@ -136,29 +120,17 @@ public:
|
||||
|
||||
std::shared_ptr<JavaInstallList> javalist();
|
||||
|
||||
std::shared_ptr<InstanceList> instances() const {
|
||||
return m_instances;
|
||||
}
|
||||
std::shared_ptr<InstanceList> instances() const { return m_instances; }
|
||||
|
||||
std::shared_ptr<IconList> icons() const {
|
||||
return m_icons;
|
||||
}
|
||||
std::shared_ptr<IconList> icons() const { return m_icons; }
|
||||
|
||||
MCEditTool *mcedit() const {
|
||||
return m_mcedit.get();
|
||||
}
|
||||
MCEditTool* mcedit() const { return m_mcedit.get(); }
|
||||
|
||||
shared_qobject_ptr<AccountList> accounts() const {
|
||||
return m_accounts;
|
||||
}
|
||||
shared_qobject_ptr<AccountList> accounts() const { return m_accounts; }
|
||||
|
||||
Status status() const {
|
||||
return m_status;
|
||||
}
|
||||
Status status() const { return m_status; }
|
||||
|
||||
const QMap<QString, std::shared_ptr<BaseProfilerFactory>> &profilers() const {
|
||||
return m_profilers;
|
||||
}
|
||||
const QMap<QString, std::shared_ptr<BaseProfilerFactory>>& profilers() const { return m_profilers; }
|
||||
|
||||
void updateProxySettings(QString proxyTypeStr, QString addr, int port, QString user, QString password);
|
||||
|
||||
@ -170,6 +142,8 @@ public:
|
||||
|
||||
void updateCapabilities();
|
||||
|
||||
void detectLibraries();
|
||||
|
||||
/*!
|
||||
* Finds and returns the full path to a jar file.
|
||||
* Returns a null-string if it could not be found.
|
||||
@ -183,35 +157,37 @@ public:
|
||||
QString getUserAgentUncached();
|
||||
|
||||
/// this is the root of the 'installation'. Used for automatic updates
|
||||
const QString &root() {
|
||||
return m_rootPath;
|
||||
}
|
||||
const QString& root() { return m_rootPath; }
|
||||
|
||||
bool isPortable() {
|
||||
return m_portable;
|
||||
}
|
||||
/// the data path the application is using
|
||||
const QString& dataRoot() { return m_dataPath; }
|
||||
|
||||
const Capabilities capabilities() {
|
||||
return m_capabilities;
|
||||
}
|
||||
bool isPortable() { return m_portable; }
|
||||
|
||||
const Capabilities capabilities() { return m_capabilities; }
|
||||
|
||||
/*!
|
||||
* Opens a json file using either a system default editor, or, if not empty, the editor
|
||||
* specified in the settings
|
||||
*/
|
||||
bool openJsonEditor(const QString &filename);
|
||||
bool openJsonEditor(const QString& filename);
|
||||
|
||||
InstanceWindow *showInstanceWindow(InstancePtr instance, QString page = QString());
|
||||
MainWindow *showMainWindow(bool minimized = false);
|
||||
InstanceWindow* showInstanceWindow(InstancePtr instance, QString page = QString());
|
||||
MainWindow* showMainWindow(bool minimized = false);
|
||||
|
||||
void updateIsRunning(bool running);
|
||||
bool updatesAreAllowed();
|
||||
|
||||
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
||||
void ShowGlobalSettings(class QWidget* parent, QString open_page = QString());
|
||||
|
||||
int suitableMaxMem();
|
||||
|
||||
signals:
|
||||
bool updaterEnabled();
|
||||
QString updaterBinaryName();
|
||||
|
||||
QUrl normalizeImportUrl(QString const& url);
|
||||
|
||||
signals:
|
||||
void updateAllowedChanged(bool status);
|
||||
void globalSettingsAboutToOpen();
|
||||
void globalSettingsClosed();
|
||||
@ -221,39 +197,36 @@ signals:
|
||||
void clickedOnDock();
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
bool launch(
|
||||
InstancePtr instance,
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
BaseProfilerFactory *profiler = nullptr,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr
|
||||
);
|
||||
public slots:
|
||||
bool launch(InstancePtr instance,
|
||||
bool online = true,
|
||||
bool demo = false,
|
||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||
MinecraftAccountPtr accountToUse = nullptr);
|
||||
bool kill(InstancePtr instance);
|
||||
void closeCurrentWindow();
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void on_windowClose();
|
||||
void messageReceived(const QByteArray & message);
|
||||
void messageReceived(const QByteArray& message);
|
||||
void controllerSucceeded();
|
||||
void controllerFailed(const QString & error);
|
||||
void controllerFailed(const QString& error);
|
||||
void setupWizardFinished(int status);
|
||||
|
||||
private:
|
||||
bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const;
|
||||
private:
|
||||
bool handleDataMigration(const QString& currentData, const QString& oldData, const QString& name, const QString& configFile) const;
|
||||
bool createSetupWizard();
|
||||
void performMainStartupAction();
|
||||
|
||||
// sets the fatal error message and m_status to Failed.
|
||||
void showFatalErrorMessage(const QString & title, const QString & content);
|
||||
void showFatalErrorMessage(const QString& title, const QString& content);
|
||||
|
||||
private:
|
||||
private:
|
||||
void addRunningInstance();
|
||||
void subRunningInstance();
|
||||
bool shouldExitNow() const;
|
||||
|
||||
private:
|
||||
private:
|
||||
QDateTime startTime;
|
||||
|
||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||
@ -277,6 +250,7 @@ private:
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||
|
||||
QString m_rootPath;
|
||||
QString m_dataPath;
|
||||
Status m_status = Application::StartingUp;
|
||||
Capabilities m_capabilities;
|
||||
bool m_portable = false;
|
||||
@ -292,7 +266,7 @@ private:
|
||||
|
||||
// FIXME: attach to instances instead.
|
||||
struct InstanceXtras {
|
||||
InstanceWindow * window = nullptr;
|
||||
InstanceWindow* window = nullptr;
|
||||
shared_qobject_ptr<LaunchController> controller;
|
||||
};
|
||||
std::map<QString, InstanceXtras> m_instanceExtras;
|
||||
@ -303,18 +277,21 @@ private:
|
||||
bool m_updateRunning = false;
|
||||
|
||||
// main window, if any
|
||||
MainWindow * m_mainWindow = nullptr;
|
||||
MainWindow* m_mainWindow = nullptr;
|
||||
|
||||
// peer launcher instance connector - used to implement single instance launcher and signalling
|
||||
LocalPeer * m_peerInstance = nullptr;
|
||||
LocalPeer* m_peerInstance = nullptr;
|
||||
|
||||
SetupWizard * m_setupWizard = nullptr;
|
||||
public:
|
||||
SetupWizard* m_setupWizard = nullptr;
|
||||
|
||||
public:
|
||||
QString m_detectedGLFWPath;
|
||||
QString m_detectedOpenALPath;
|
||||
QString m_instanceIdToLaunch;
|
||||
QString m_serverToJoin;
|
||||
QString m_profileToUse;
|
||||
bool m_liveCheck = false;
|
||||
QList<QUrl> m_zipsToImport;
|
||||
QList<QUrl> m_urlsToImport;
|
||||
QString m_instanceIdToShowWindowOf;
|
||||
std::unique_ptr<QFile> logFile;
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -39,7 +39,8 @@
|
||||
#include <QJsonObject>
|
||||
#include "Json.h"
|
||||
|
||||
void ApplicationMessage::parse(const QByteArray & input) {
|
||||
void ApplicationMessage::parse(const QByteArray& input)
|
||||
{
|
||||
auto doc = Json::requireDocument(input, "ApplicationMessage");
|
||||
auto root = Json::requireObject(doc, "ApplicationMessage");
|
||||
|
||||
@ -47,12 +48,13 @@ void ApplicationMessage::parse(const QByteArray & input) {
|
||||
args.clear();
|
||||
|
||||
auto parsedArgs = root.value("args").toObject();
|
||||
for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
|
||||
for (auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
|
||||
args.insert(iter.key(), iter.value().toString());
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray ApplicationMessage::serialize() {
|
||||
QByteArray ApplicationMessage::serialize()
|
||||
{
|
||||
QJsonObject root;
|
||||
root.insert("command", command);
|
||||
QJsonObject outArgs;
|
||||
|
@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
struct ApplicationMessage {
|
||||
QString command;
|
||||
QHash<QString, QString> args;
|
||||
|
||||
QByteArray serialize();
|
||||
void parse(const QByteArray & input);
|
||||
void parse(const QByteArray& input);
|
||||
};
|
||||
|
@ -18,27 +18,21 @@
|
||||
#include "BaseInstaller.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
|
||||
BaseInstaller::BaseInstaller()
|
||||
{
|
||||
BaseInstaller::BaseInstaller() {}
|
||||
|
||||
}
|
||||
|
||||
bool BaseInstaller::isApplied(MinecraftInstance *on)
|
||||
bool BaseInstaller::isApplied(MinecraftInstance* on)
|
||||
{
|
||||
return QFile::exists(filename(on->instanceRoot()));
|
||||
}
|
||||
|
||||
bool BaseInstaller::add(MinecraftInstance *to)
|
||||
bool BaseInstaller::add(MinecraftInstance* to)
|
||||
{
|
||||
if (!patchesDir(to->instanceRoot()).exists())
|
||||
{
|
||||
if (!patchesDir(to->instanceRoot()).exists()) {
|
||||
QDir(to->instanceRoot()).mkdir("patches");
|
||||
}
|
||||
|
||||
if (isApplied(to))
|
||||
{
|
||||
if (!remove(to))
|
||||
{
|
||||
if (isApplied(to)) {
|
||||
if (!remove(to)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -46,16 +40,16 @@ bool BaseInstaller::add(MinecraftInstance *to)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseInstaller::remove(MinecraftInstance *from)
|
||||
bool BaseInstaller::remove(MinecraftInstance* from)
|
||||
{
|
||||
return QFile::remove(filename(from->instanceRoot()));
|
||||
}
|
||||
|
||||
QString BaseInstaller::filename(const QString &root) const
|
||||
QString BaseInstaller::filename(const QString& root) const
|
||||
{
|
||||
return patchesDir(root).absoluteFilePath(id() + ".json");
|
||||
}
|
||||
QDir BaseInstaller::patchesDir(const QString &root) const
|
||||
QDir BaseInstaller::patchesDir(const QString& root) const
|
||||
{
|
||||
return QDir(root + "/patches/");
|
||||
}
|
||||
|
@ -26,20 +26,19 @@ class QObject;
|
||||
class Task;
|
||||
class BaseVersion;
|
||||
|
||||
class BaseInstaller
|
||||
{
|
||||
public:
|
||||
class BaseInstaller {
|
||||
public:
|
||||
BaseInstaller();
|
||||
virtual ~BaseInstaller(){};
|
||||
bool isApplied(MinecraftInstance *on);
|
||||
bool isApplied(MinecraftInstance* on);
|
||||
|
||||
virtual bool add(MinecraftInstance *to);
|
||||
virtual bool remove(MinecraftInstance *from);
|
||||
virtual bool add(MinecraftInstance* to);
|
||||
virtual bool remove(MinecraftInstance* from);
|
||||
|
||||
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersion::Ptr version, QObject *parent) = 0;
|
||||
virtual Task* createInstallTask(MinecraftInstance* instance, BaseVersion::Ptr version, QObject* parent) = 0;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
virtual QString id() const = 0;
|
||||
QString filename(const QString &root) const;
|
||||
QDir patchesDir(const QString &root) const;
|
||||
QString filename(const QString& root) const;
|
||||
QDir patchesDir(const QString& root) const;
|
||||
};
|
||||
|
@ -1,8 +1,9 @@
|
||||
// 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 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -36,23 +37,22 @@
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "settings/Setting.h"
|
||||
#include "settings/OverrideSetting.h"
|
||||
#include "settings/Setting.h"
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "Commandline.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "Commandline.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir)
|
||||
: QObject()
|
||||
BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir) : QObject()
|
||||
{
|
||||
m_settings = settings;
|
||||
m_global_settings = globalSettings;
|
||||
@ -79,7 +79,7 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
m_settings->registerSetting("InstanceType", "");
|
||||
|
||||
// Custom Commands
|
||||
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
||||
auto commandSetting = m_settings->registerSetting({ "OverrideCommands", "OverrideLaunchCmd" }, false);
|
||||
m_settings->registerOverride(globalSettings->getSetting("PreLaunchCommand"), commandSetting);
|
||||
m_settings->registerOverride(globalSettings->getSetting("WrapperCommand"), commandSetting);
|
||||
m_settings->registerOverride(globalSettings->getSetting("PostExitCommand"), commandSetting);
|
||||
@ -101,6 +101,8 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
||||
m_settings->registerSetting("ManagedPackName", "");
|
||||
m_settings->registerSetting("ManagedPackVersionID", "");
|
||||
m_settings->registerSetting("ManagedPackVersionName", "");
|
||||
|
||||
m_settings->registerSetting("Profiler", "");
|
||||
}
|
||||
|
||||
QString BaseInstance::getPreLaunchCommand()
|
||||
@ -148,7 +150,11 @@ 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)
|
||||
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);
|
||||
@ -173,8 +179,7 @@ int BaseInstance::getConsoleMaxLines() const
|
||||
auto lineSetting = m_settings->getSetting("ConsoleMaxLines");
|
||||
bool conversionOk = false;
|
||||
int maxLines = lineSetting->get().toInt(&conversionOk);
|
||||
if(!conversionOk)
|
||||
{
|
||||
if (!conversionOk) {
|
||||
maxLines = lineSetting->defValue().toInt();
|
||||
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
|
||||
}
|
||||
@ -220,8 +225,7 @@ bool BaseInstance::isLinkedToInstanceId(const QString& id) const
|
||||
|
||||
void BaseInstance::iconUpdated(QString key)
|
||||
{
|
||||
if(iconKey() == key)
|
||||
{
|
||||
if (iconKey() == key) {
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
}
|
||||
@ -235,8 +239,7 @@ void BaseInstance::invalidate()
|
||||
void BaseInstance::changeStatus(BaseInstance::Status newStatus)
|
||||
{
|
||||
Status status = currentStatus();
|
||||
if(status != newStatus)
|
||||
{
|
||||
if (status != newStatus) {
|
||||
m_status = newStatus;
|
||||
emit statusChanged(status, newStatus);
|
||||
}
|
||||
@ -259,23 +262,19 @@ bool BaseInstance::isRunning() const
|
||||
|
||||
void BaseInstance::setRunning(bool running)
|
||||
{
|
||||
if(running == m_isRunning)
|
||||
if (running == m_isRunning)
|
||||
return;
|
||||
|
||||
m_isRunning = running;
|
||||
|
||||
if(!m_settings->get("RecordGameTime").toBool())
|
||||
{
|
||||
if (!m_settings->get("RecordGameTime").toBool()) {
|
||||
emit runningStatusChanged(running);
|
||||
return;
|
||||
}
|
||||
|
||||
if(running)
|
||||
{
|
||||
if (running) {
|
||||
m_timeStarted = QDateTime::currentDateTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
QDateTime timeEnded = QDateTime::currentDateTime();
|
||||
|
||||
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
||||
@ -291,8 +290,7 @@ void BaseInstance::setRunning(bool running)
|
||||
int64_t BaseInstance::totalTimePlayed() const
|
||||
{
|
||||
qint64 current = m_settings->get("totalTimePlayed").toLongLong();
|
||||
if(m_isRunning)
|
||||
{
|
||||
if (m_isRunning) {
|
||||
QDateTime timeNow = QDateTime::currentDateTime();
|
||||
return current + m_timeStarted.secsTo(timeNow);
|
||||
}
|
||||
@ -301,8 +299,7 @@ int64_t BaseInstance::totalTimePlayed() const
|
||||
|
||||
int64_t BaseInstance::lastTimePlayed() const
|
||||
{
|
||||
if(m_isRunning)
|
||||
{
|
||||
if (m_isRunning) {
|
||||
QDateTime timeNow = QDateTime::currentDateTime();
|
||||
return m_timeStarted.secsTo(timeNow);
|
||||
}
|
||||
@ -349,14 +346,14 @@ qint64 BaseInstance::lastLaunch() const
|
||||
|
||||
void BaseInstance::setLastLaunch(qint64 val)
|
||||
{
|
||||
//FIXME: if no change, do not set. setting involves saving a file.
|
||||
// FIXME: if no change, do not set. setting involves saving a file.
|
||||
m_settings->set("lastLaunchTime", val);
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
|
||||
void BaseInstance::setNotes(QString val)
|
||||
{
|
||||
//FIXME: if no change, do not set. setting involves saving a file.
|
||||
// FIXME: if no change, do not set. setting involves saving a file.
|
||||
m_settings->set("notes", val);
|
||||
}
|
||||
|
||||
@ -367,7 +364,7 @@ QString BaseInstance::notes() const
|
||||
|
||||
void BaseInstance::setIconKey(QString val)
|
||||
{
|
||||
//FIXME: if no change, do not set. setting involves saving a file.
|
||||
// FIXME: if no change, do not set. setting involves saving a file.
|
||||
m_settings->set("iconKey", val);
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
@ -379,7 +376,7 @@ QString BaseInstance::iconKey() const
|
||||
|
||||
void BaseInstance::setName(QString val)
|
||||
{
|
||||
//FIXME: if no change, do not set. setting involves saving a file.
|
||||
// FIXME: if no change, do not set. setting involves saving a file.
|
||||
m_settings->set("name", val);
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
@ -391,7 +388,7 @@ QString BaseInstance::name() const
|
||||
|
||||
QString BaseInstance::windowTitle() const
|
||||
{
|
||||
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
|
||||
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name();
|
||||
}
|
||||
|
||||
// FIXME: why is this here? move it to MinecraftInstance!!!
|
||||
|
@ -1,8 +1,9 @@
|
||||
// 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 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -37,24 +38,25 @@
|
||||
#pragma once
|
||||
#include <cassert>
|
||||
|
||||
#include <QObject>
|
||||
#include "QObjectPtr.h"
|
||||
#include <QDateTime>
|
||||
#include <QSet>
|
||||
#include <QMenu>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QSet>
|
||||
#include "QObjectPtr.h"
|
||||
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
#include "settings/INIFile.h"
|
||||
#include "BaseVersionList.h"
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "MessageLevel.h"
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "pathmatcher/IPathMatcher.h"
|
||||
#include "settings/INIFile.h"
|
||||
|
||||
#include "net/Mode.h"
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "RuntimeContext.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
|
||||
class QDir;
|
||||
class Task;
|
||||
@ -62,7 +64,7 @@ class LaunchTask;
|
||||
class BaseInstance;
|
||||
|
||||
// pointer for lazy people
|
||||
typedef std::shared_ptr<BaseInstance> InstancePtr;
|
||||
using InstancePtr = std::shared_ptr<BaseInstance>;
|
||||
|
||||
/*!
|
||||
* \brief Base class for instances.
|
||||
@ -72,23 +74,21 @@ typedef std::shared_ptr<BaseInstance> InstancePtr;
|
||||
* To create a new instance type, create a new class inheriting from this class
|
||||
* and implement the pure virtual functions.
|
||||
*/
|
||||
class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance>
|
||||
{
|
||||
class BaseInstance : public QObject, public std::enable_shared_from_this<BaseInstance> {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
protected:
|
||||
/// no-touchy!
|
||||
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString &rootDir);
|
||||
BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir);
|
||||
|
||||
public: /* types */
|
||||
enum class Status
|
||||
{
|
||||
public: /* types */
|
||||
enum class Status {
|
||||
Present,
|
||||
Gone // either nuked or invalidated
|
||||
Gone // either nuked or invalidated
|
||||
};
|
||||
|
||||
public:
|
||||
public:
|
||||
/// virtual destructor to make sure the destruction is COMPLETE
|
||||
virtual ~BaseInstance() {};
|
||||
virtual ~BaseInstance() {}
|
||||
|
||||
virtual void saveNow() = 0;
|
||||
|
||||
@ -117,10 +117,7 @@ public:
|
||||
QString instanceRoot() const;
|
||||
|
||||
/// Path to the instance's game root directory.
|
||||
virtual QString gameRoot() const
|
||||
{
|
||||
return instanceRoot();
|
||||
}
|
||||
virtual QString gameRoot() const { return instanceRoot(); }
|
||||
|
||||
/// Path to the instance's mods directory.
|
||||
virtual QString modsRoot() const = 0;
|
||||
@ -151,15 +148,12 @@ public:
|
||||
void copyManagedPack(BaseInstance& other);
|
||||
|
||||
/// guess log level from a line of game log
|
||||
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level)
|
||||
{
|
||||
return level;
|
||||
};
|
||||
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString& line, MessageLevel::Enum level) { return level; }
|
||||
|
||||
virtual QStringList extraArguments();
|
||||
|
||||
/// Traits. Normally inside the version, depends on instance implementation.
|
||||
virtual QSet <QString> traits() const = 0;
|
||||
virtual QSet<QString> traits() const = 0;
|
||||
|
||||
/**
|
||||
* Gets the time that the instance was last launched.
|
||||
@ -189,8 +183,7 @@ public:
|
||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||
|
||||
/// returns a valid launcher (task container)
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(
|
||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
|
||||
/// returns the current launch task (if any)
|
||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
||||
@ -222,45 +215,30 @@ public:
|
||||
virtual QString typeName() const = 0;
|
||||
|
||||
void updateRuntimeContext();
|
||||
RuntimeContext runtimeContext() const
|
||||
{
|
||||
return m_runtimeContext;
|
||||
}
|
||||
RuntimeContext runtimeContext() const { return m_runtimeContext; }
|
||||
|
||||
bool hasVersionBroken() const
|
||||
{
|
||||
return m_hasBrokenVersion;
|
||||
}
|
||||
bool hasVersionBroken() const { return m_hasBrokenVersion; }
|
||||
void setVersionBroken(bool value)
|
||||
{
|
||||
if(m_hasBrokenVersion != value)
|
||||
{
|
||||
if (m_hasBrokenVersion != value) {
|
||||
m_hasBrokenVersion = value;
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasUpdateAvailable() const
|
||||
{
|
||||
return m_hasUpdate;
|
||||
}
|
||||
bool hasUpdateAvailable() const { return m_hasUpdate; }
|
||||
void setUpdateAvailable(bool value)
|
||||
{
|
||||
if(m_hasUpdate != value)
|
||||
{
|
||||
if (m_hasUpdate != value) {
|
||||
m_hasUpdate = value;
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasCrashed() const
|
||||
{
|
||||
return m_crashed;
|
||||
}
|
||||
bool hasCrashed() const { return m_crashed; }
|
||||
void setCrashed(bool value)
|
||||
{
|
||||
if(m_crashed != value)
|
||||
{
|
||||
if (m_crashed != value) {
|
||||
m_crashed = value;
|
||||
emit propertiesChanged(this);
|
||||
}
|
||||
@ -270,6 +248,8 @@ public:
|
||||
virtual bool canEdit() const = 0;
|
||||
virtual bool canExport() const = 0;
|
||||
|
||||
virtual void populateLaunchMenu(QMenu* menu) = 0;
|
||||
|
||||
bool reloadSettings();
|
||||
|
||||
/**
|
||||
@ -288,30 +268,32 @@ public:
|
||||
bool removeLinkedInstanceId(const QString& id);
|
||||
bool isLinkedToInstanceId(const QString& id) const;
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void changeStatus(Status newStatus);
|
||||
|
||||
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); };
|
||||
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); }
|
||||
|
||||
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
|
||||
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
|
||||
|
||||
signals:
|
||||
signals:
|
||||
/*!
|
||||
* \brief Signal emitted when properties relevant to the instance view change
|
||||
*/
|
||||
void propertiesChanged(BaseInstance *inst);
|
||||
void propertiesChanged(BaseInstance* inst);
|
||||
|
||||
void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
|
||||
|
||||
void runningStatusChanged(bool running);
|
||||
|
||||
void profilerChanged();
|
||||
|
||||
void statusChanged(Status from, Status to);
|
||||
|
||||
protected slots:
|
||||
protected slots:
|
||||
void iconUpdated(QString key);
|
||||
|
||||
protected: /* data */
|
||||
protected: /* data */
|
||||
QString m_rootDir;
|
||||
SettingsObjectPtr m_settings;
|
||||
// InstanceFlags m_flags;
|
||||
@ -320,7 +302,7 @@ protected: /* data */
|
||||
QDateTime m_timeStarted;
|
||||
RuntimeContext m_runtimeContext;
|
||||
|
||||
private: /* data */
|
||||
private: /* data */
|
||||
Status m_status = Status::Present;
|
||||
bool m_crashed = false;
|
||||
bool m_hasUpdate = false;
|
||||
@ -328,9 +310,8 @@ private: /* data */
|
||||
|
||||
SettingsObjectWeakPtr m_global_settings;
|
||||
bool m_specific_settings_loaded = false;
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
||||
//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
|
||||
//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
|
||||
// Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
|
||||
// Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)
|
||||
|
@ -15,16 +15,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <QString>
|
||||
#include <QMetaType>
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
|
||||
/*!
|
||||
* An abstract base class for versions.
|
||||
*/
|
||||
class BaseVersion
|
||||
{
|
||||
public:
|
||||
class BaseVersion {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<BaseVersion>;
|
||||
virtual ~BaseVersion() {}
|
||||
/*!
|
||||
@ -44,15 +43,8 @@ public:
|
||||
* the kind of version this is (Stable, Beta, Snapshot, whatever)
|
||||
*/
|
||||
virtual QString typeString() const = 0;
|
||||
|
||||
virtual bool operator<(BaseVersion &a)
|
||||
{
|
||||
return name() < a.name();
|
||||
};
|
||||
virtual bool operator>(BaseVersion &a)
|
||||
{
|
||||
return name() > a.name();
|
||||
};
|
||||
virtual bool operator<(BaseVersion& a) { return name() < a.name(); }
|
||||
virtual bool operator>(BaseVersion& a) { return name() > a.name(); }
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -36,14 +36,11 @@
|
||||
#include "BaseVersionList.h"
|
||||
#include "BaseVersion.h"
|
||||
|
||||
BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
BaseVersionList::BaseVersionList(QObject* parent) : QAbstractListModel(parent) {}
|
||||
|
||||
BaseVersion::Ptr BaseVersionList::findVersion(const QString &descriptor)
|
||||
BaseVersion::Ptr BaseVersionList::findVersion(const QString& descriptor)
|
||||
{
|
||||
for (int i = 0; i < count(); i++)
|
||||
{
|
||||
for (int i = 0; i < count(); i++) {
|
||||
if (at(i)->descriptor() == descriptor)
|
||||
return at(i);
|
||||
}
|
||||
@ -58,7 +55,7 @@ BaseVersion::Ptr BaseVersionList::getRecommended() const
|
||||
return at(0);
|
||||
}
|
||||
|
||||
QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
||||
QVariant BaseVersionList::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
@ -68,37 +65,36 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
||||
|
||||
BaseVersion::Ptr version = at(index.row());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case VersionPointerRole:
|
||||
return QVariant::fromValue(version);
|
||||
switch (role) {
|
||||
case VersionPointerRole:
|
||||
return QVariant::fromValue(version);
|
||||
|
||||
case VersionRole:
|
||||
return version->name();
|
||||
case VersionRole:
|
||||
return version->name();
|
||||
|
||||
case VersionIdRole:
|
||||
return version->descriptor();
|
||||
case VersionIdRole:
|
||||
return version->descriptor();
|
||||
|
||||
case TypeRole:
|
||||
return version->typeString();
|
||||
case TypeRole:
|
||||
return version->typeString();
|
||||
|
||||
default:
|
||||
return QVariant();
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
BaseVersionList::RoleList BaseVersionList::providesRoles() const
|
||||
{
|
||||
return {VersionPointerRole, VersionRole, VersionIdRole, TypeRole};
|
||||
return { VersionPointerRole, VersionRole, VersionIdRole, TypeRole };
|
||||
}
|
||||
|
||||
int BaseVersionList::rowCount(const QModelIndex &parent) const
|
||||
int BaseVersionList::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
// Return count
|
||||
return parent.isValid() ? 0 : count();
|
||||
}
|
||||
|
||||
int BaseVersionList::columnCount(const QModelIndex &parent) const
|
||||
int BaseVersionList::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : 1;
|
||||
}
|
||||
|
@ -15,13 +15,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include "BaseVersion.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
/*!
|
||||
* \brief Class that each instance type's version list derives from.
|
||||
@ -35,12 +35,10 @@
|
||||
* all have a default implementation, but they can be overridden by plugins to
|
||||
* change the behavior of the list.
|
||||
*/
|
||||
class BaseVersionList : public QAbstractListModel
|
||||
{
|
||||
class BaseVersionList : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ModelRoles
|
||||
{
|
||||
public:
|
||||
enum ModelRoles {
|
||||
VersionPointerRole = Qt::UserRole,
|
||||
VersionRole,
|
||||
VersionIdRole,
|
||||
@ -53,9 +51,9 @@ public:
|
||||
ArchitectureRole,
|
||||
SortRole
|
||||
};
|
||||
typedef QList<int> RoleList;
|
||||
using RoleList = QList<int>;
|
||||
|
||||
explicit BaseVersionList(QObject *parent = 0);
|
||||
explicit BaseVersionList(QObject* parent = 0);
|
||||
|
||||
/*!
|
||||
* \brief Gets a task that will reload the version list.
|
||||
@ -66,7 +64,7 @@ public:
|
||||
virtual Task::Ptr getLoadTask() = 0;
|
||||
|
||||
//! Checks whether or not the list is loaded. If this returns false, the list should be
|
||||
//loaded.
|
||||
// loaded.
|
||||
virtual bool isLoaded() = 0;
|
||||
|
||||
//! Gets the version at the given index.
|
||||
@ -76,9 +74,9 @@ public:
|
||||
virtual int count() const = 0;
|
||||
|
||||
//////// List Model Functions ////////
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
int rowCount(const QModelIndex& parent) const override;
|
||||
int columnCount(const QModelIndex& parent) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
//! which roles are provided by this version list?
|
||||
@ -90,7 +88,7 @@ public:
|
||||
* \return A const pointer to the version with the given descriptor. NULL if
|
||||
* one doesn't exist.
|
||||
*/
|
||||
virtual BaseVersion::Ptr findVersion(const QString &descriptor);
|
||||
virtual BaseVersion::Ptr findVersion(const QString& descriptor);
|
||||
|
||||
/*!
|
||||
* \brief Gets the recommended version from this list
|
||||
@ -103,8 +101,7 @@ public:
|
||||
*/
|
||||
virtual void sortVersions() = 0;
|
||||
|
||||
protected
|
||||
slots:
|
||||
protected slots:
|
||||
/*!
|
||||
* Updates this list with the given list of versions.
|
||||
* This is done by copying each version in the given list and inserting it
|
||||
|
@ -136,6 +136,16 @@ set(NET_SOURCES
|
||||
net/Validator.h
|
||||
net/Upload.cpp
|
||||
net/Upload.h
|
||||
net/HeaderProxy.h
|
||||
net/RawHeaderProxy.h
|
||||
net/ApiHeaderProxy.h
|
||||
net/StaticHeaderProxy.h
|
||||
net/ApiDownload.h
|
||||
net/ApiDownload.cpp
|
||||
net/ApiUpload.cpp
|
||||
net/ApiUpload.h
|
||||
net/NetRequest.cpp
|
||||
net/NetRequest.h
|
||||
)
|
||||
|
||||
# Game launch logic
|
||||
@ -172,6 +182,11 @@ set(MAC_UPDATE_SOURCES
|
||||
updater/MacSparkleUpdater.mm
|
||||
)
|
||||
|
||||
set(PRISM_UPDATE_SOURCES
|
||||
updater/PrismExternalUpdater.h
|
||||
updater/PrismExternalUpdater.cpp
|
||||
)
|
||||
|
||||
# Backend for the news bar... there's usually no news.
|
||||
set(NEWS_SOURCES
|
||||
# News System
|
||||
@ -207,13 +222,9 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/auth/MinecraftAccount.h
|
||||
minecraft/auth/Parsers.cpp
|
||||
minecraft/auth/Parsers.h
|
||||
minecraft/auth/Yggdrasil.cpp
|
||||
minecraft/auth/Yggdrasil.h
|
||||
|
||||
minecraft/auth/flows/AuthFlow.cpp
|
||||
minecraft/auth/flows/AuthFlow.h
|
||||
minecraft/auth/flows/Mojang.cpp
|
||||
minecraft/auth/flows/Mojang.h
|
||||
minecraft/auth/flows/MSA.cpp
|
||||
minecraft/auth/flows/MSA.h
|
||||
minecraft/auth/flows/Offline.cpp
|
||||
@ -227,12 +238,8 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/auth/steps/GetSkinStep.h
|
||||
minecraft/auth/steps/LauncherLoginStep.cpp
|
||||
minecraft/auth/steps/LauncherLoginStep.h
|
||||
minecraft/auth/steps/MigrationEligibilityStep.cpp
|
||||
minecraft/auth/steps/MigrationEligibilityStep.h
|
||||
minecraft/auth/steps/MinecraftProfileStep.cpp
|
||||
minecraft/auth/steps/MinecraftProfileStep.h
|
||||
minecraft/auth/steps/MinecraftProfileStepMojang.cpp
|
||||
minecraft/auth/steps/MinecraftProfileStepMojang.h
|
||||
minecraft/auth/steps/MSAStep.cpp
|
||||
minecraft/auth/steps/MSAStep.h
|
||||
minecraft/auth/steps/XboxAuthorizationStep.cpp
|
||||
@ -241,8 +248,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/auth/steps/XboxProfileStep.h
|
||||
minecraft/auth/steps/XboxUserStep.cpp
|
||||
minecraft/auth/steps/XboxUserStep.h
|
||||
minecraft/auth/steps/YggdrasilStep.cpp
|
||||
minecraft/auth/steps/YggdrasilStep.h
|
||||
|
||||
minecraft/gameoptions/GameOptions.h
|
||||
minecraft/gameoptions/GameOptions.cpp
|
||||
@ -262,8 +267,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/launch/CreateGameFolders.h
|
||||
minecraft/launch/ModMinecraftJar.cpp
|
||||
minecraft/launch/ModMinecraftJar.h
|
||||
minecraft/launch/DirectJavaLaunch.cpp
|
||||
minecraft/launch/DirectJavaLaunch.h
|
||||
minecraft/launch/ExtractNatives.cpp
|
||||
minecraft/launch/ExtractNatives.h
|
||||
minecraft/launch/LauncherPartLaunch.cpp
|
||||
@ -362,6 +365,8 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/mod/tasks/LocalWorldSaveParseTask.cpp
|
||||
minecraft/mod/tasks/LocalResourceParse.h
|
||||
minecraft/mod/tasks/LocalResourceParse.cpp
|
||||
minecraft/mod/tasks/GetModDependenciesTask.h
|
||||
minecraft/mod/tasks/GetModDependenciesTask.cpp
|
||||
|
||||
# Assets
|
||||
minecraft/AssetsUtils.h
|
||||
@ -375,8 +380,6 @@ set(MINECRAFT_SOURCES
|
||||
minecraft/services/SkinDelete.cpp
|
||||
minecraft/services/SkinDelete.h
|
||||
|
||||
mojang/PackageManifest.h
|
||||
mojang/PackageManifest.cpp
|
||||
minecraft/Agent.h)
|
||||
|
||||
# the screenshots feature
|
||||
@ -487,6 +490,9 @@ set(API_SOURCES
|
||||
modplatform/helpers/HashUtils.cpp
|
||||
modplatform/helpers/OverrideUtils.h
|
||||
modplatform/helpers/OverrideUtils.cpp
|
||||
|
||||
modplatform/helpers/ExportToModList.h
|
||||
modplatform/helpers/ExportToModList.cpp
|
||||
)
|
||||
|
||||
set(FTB_SOURCES
|
||||
@ -498,6 +504,11 @@ set(FTB_SOURCES
|
||||
modplatform/legacy_ftb/PrivatePackManager.cpp
|
||||
|
||||
modplatform/legacy_ftb/PackHelpers.h
|
||||
|
||||
modplatform/import_ftb/PackInstallTask.h
|
||||
modplatform/import_ftb/PackInstallTask.cpp
|
||||
modplatform/import_ftb/PackHelpers.h
|
||||
modplatform/import_ftb/PackHelpers.cpp
|
||||
)
|
||||
|
||||
set(FLAME_SOURCES
|
||||
@ -514,6 +525,8 @@ set(FLAME_SOURCES
|
||||
modplatform/flame/FlameCheckUpdate.h
|
||||
modplatform/flame/FlameInstanceCreationTask.h
|
||||
modplatform/flame/FlameInstanceCreationTask.cpp
|
||||
modplatform/flame/FlamePackExportTask.h
|
||||
modplatform/flame/FlamePackExportTask.cpp
|
||||
)
|
||||
|
||||
set(MODRINTH_SOURCES
|
||||
@ -558,6 +571,9 @@ set(ATLAUNCHER_SOURCES
|
||||
)
|
||||
|
||||
set(LINKEXE_SOURCES
|
||||
WindowsConsole.cpp
|
||||
WindowsConsole.h
|
||||
|
||||
filelink/FileLink.h
|
||||
filelink/FileLink.cpp
|
||||
FileSystem.h
|
||||
@ -569,6 +585,63 @@ set(LINKEXE_SOURCES
|
||||
DesktopServices.cpp
|
||||
)
|
||||
|
||||
set(PRISMUPDATER_SOURCES
|
||||
updater/prismupdater/PrismUpdater.h
|
||||
updater/prismupdater/PrismUpdater.cpp
|
||||
updater/prismupdater/UpdaterDialogs.h
|
||||
updater/prismupdater/UpdaterDialogs.cpp
|
||||
updater/prismupdater/GitHubRelease.h
|
||||
updater/prismupdater/GitHubRelease.cpp
|
||||
|
||||
Json.h
|
||||
Json.cpp
|
||||
FileSystem.h
|
||||
FileSystem.cpp
|
||||
StringUtils.h
|
||||
StringUtils.cpp
|
||||
DesktopServices.h
|
||||
DesktopServices.cpp
|
||||
Version.h
|
||||
Version.cpp
|
||||
Markdown.h
|
||||
Markdown.cpp
|
||||
|
||||
# Zip
|
||||
MMCZip.h
|
||||
MMCZip.cpp
|
||||
|
||||
# Time
|
||||
MMCTime.h
|
||||
MMCTime.cpp
|
||||
|
||||
net/ByteArraySink.h
|
||||
net/ChecksumValidator.h
|
||||
net/Download.cpp
|
||||
net/Download.h
|
||||
net/FileSink.cpp
|
||||
net/FileSink.h
|
||||
net/HttpMetaCache.cpp
|
||||
net/HttpMetaCache.h
|
||||
net/Logging.h
|
||||
net/Logging.cpp
|
||||
net/NetAction.h
|
||||
net/NetRequest.cpp
|
||||
net/NetRequest.h
|
||||
net/NetJob.cpp
|
||||
net/NetJob.h
|
||||
net/NetUtils.h
|
||||
net/Sink.h
|
||||
net/Validator.h
|
||||
net/HeaderProxy.h
|
||||
net/RawHeaderProxy.h
|
||||
|
||||
ui/dialogs/ProgressDialog.cpp
|
||||
ui/dialogs/ProgressDialog.h
|
||||
ui/widgets/SubTaskProgressBar.h
|
||||
ui/widgets/SubTaskProgressBar.cpp
|
||||
|
||||
)
|
||||
|
||||
######## Logging categories ########
|
||||
|
||||
ecm_qt_declare_logging_category(CORE_SOURCES
|
||||
@ -663,8 +736,10 @@ set(LOGIC_SOURCES
|
||||
${ATLAUNCHER_SOURCES}
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
if(APPLE AND Launcher_ENABLE_UPDATER)
|
||||
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${MAC_UPDATE_SOURCES})
|
||||
else()
|
||||
set (LOGIC_SOURCES ${LOGIC_SOURCES} ${PRISM_UPDATE_SOURCES})
|
||||
endif()
|
||||
|
||||
SET(LAUNCHER_SOURCES
|
||||
@ -682,6 +757,7 @@ SET(LAUNCHER_SOURCES
|
||||
VersionProxyModel.h
|
||||
VersionProxyModel.cpp
|
||||
Markdown.h
|
||||
Markdown.cpp
|
||||
|
||||
# Super secret!
|
||||
KonamiCode.h
|
||||
@ -753,8 +829,12 @@ SET(LAUNCHER_SOURCES
|
||||
ui/themes/ITheme.h
|
||||
ui/themes/SystemTheme.cpp
|
||||
ui/themes/SystemTheme.h
|
||||
ui/themes/IconTheme.cpp
|
||||
ui/themes/IconTheme.h
|
||||
ui/themes/ThemeManager.cpp
|
||||
ui/themes/ThemeManager.h
|
||||
ui/themes/CatPack.cpp
|
||||
ui/themes/CatPack.h
|
||||
|
||||
# Processes
|
||||
LaunchController.h
|
||||
@ -809,6 +889,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/global/AccountListPage.h
|
||||
ui/pages/global/CustomCommandsPage.cpp
|
||||
ui/pages/global/CustomCommandsPage.h
|
||||
ui/pages/global/EnvironmentVariablesPage.cpp
|
||||
ui/pages/global/EnvironmentVariablesPage.h
|
||||
ui/pages/global/ExternalToolsPage.cpp
|
||||
ui/pages/global/ExternalToolsPage.h
|
||||
ui/pages/global/JavaPage.cpp
|
||||
@ -825,8 +907,8 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/global/APIPage.h
|
||||
|
||||
# GUI - platform pages
|
||||
ui/pages/modplatform/VanillaPage.cpp
|
||||
ui/pages/modplatform/VanillaPage.h
|
||||
ui/pages/modplatform/CustomPage.cpp
|
||||
ui/pages/modplatform/CustomPage.h
|
||||
|
||||
ui/pages/modplatform/ResourcePage.cpp
|
||||
ui/pages/modplatform/ResourcePage.h
|
||||
@ -864,6 +946,11 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/legacy_ftb/ListModel.h
|
||||
ui/pages/modplatform/legacy_ftb/ListModel.cpp
|
||||
|
||||
ui/pages/modplatform/import_ftb/ImportFTBPage.cpp
|
||||
ui/pages/modplatform/import_ftb/ImportFTBPage.h
|
||||
ui/pages/modplatform/import_ftb/ListModel.h
|
||||
ui/pages/modplatform/import_ftb/ListModel.cpp
|
||||
|
||||
ui/pages/modplatform/flame/FlameModel.cpp
|
||||
ui/pages/modplatform/flame/FlameModel.h
|
||||
ui/pages/modplatform/flame/FlamePage.cpp
|
||||
@ -886,6 +973,9 @@ SET(LAUNCHER_SOURCES
|
||||
ui/pages/modplatform/ImportPage.cpp
|
||||
ui/pages/modplatform/ImportPage.h
|
||||
|
||||
ui/pages/modplatform/OptionalModDialog.cpp
|
||||
ui/pages/modplatform/OptionalModDialog.h
|
||||
|
||||
ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp
|
||||
ui/pages/modplatform/modrinth/ModrinthResourceModels.h
|
||||
ui/pages/modplatform/modrinth/ModrinthResourcePages.cpp
|
||||
@ -906,14 +996,14 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/EditAccountDialog.h
|
||||
ui/dialogs/ExportInstanceDialog.cpp
|
||||
ui/dialogs/ExportInstanceDialog.h
|
||||
ui/dialogs/ExportMrPackDialog.cpp
|
||||
ui/dialogs/ExportMrPackDialog.h
|
||||
ui/dialogs/ExportPackDialog.cpp
|
||||
ui/dialogs/ExportPackDialog.h
|
||||
ui/dialogs/ExportToModListDialog.cpp
|
||||
ui/dialogs/ExportToModListDialog.h
|
||||
ui/dialogs/IconPickerDialog.cpp
|
||||
ui/dialogs/IconPickerDialog.h
|
||||
ui/dialogs/ImportResourceDialog.cpp
|
||||
ui/dialogs/ImportResourceDialog.h
|
||||
ui/dialogs/LoginDialog.cpp
|
||||
ui/dialogs/LoginDialog.h
|
||||
ui/dialogs/MSALoginDialog.cpp
|
||||
ui/dialogs/MSALoginDialog.h
|
||||
ui/dialogs/OfflineLoginDialog.cpp
|
||||
@ -944,12 +1034,16 @@ SET(LAUNCHER_SOURCES
|
||||
ui/dialogs/ChooseProviderDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.cpp
|
||||
ui/dialogs/ModUpdateDialog.h
|
||||
ui/dialogs/InstallLoaderDialog.cpp
|
||||
ui/dialogs/InstallLoaderDialog.h
|
||||
|
||||
# GUI - widgets
|
||||
ui/widgets/Common.cpp
|
||||
ui/widgets/Common.h
|
||||
ui/widgets/CustomCommands.cpp
|
||||
ui/widgets/CustomCommands.h
|
||||
ui/widgets/EnvironmentVariables.cpp
|
||||
ui/widgets/EnvironmentVariables.h
|
||||
ui/widgets/DropLabel.cpp
|
||||
ui/widgets/DropLabel.h
|
||||
ui/widgets/FocusLineEdit.cpp
|
||||
@ -1008,6 +1102,23 @@ SET(LAUNCHER_SOURCES
|
||||
ui/instanceview/VisualGroup.h
|
||||
)
|
||||
|
||||
if (NOT Apple)
|
||||
set(LAUNCHER_SOURCES
|
||||
${LAUNCHER_SOURCES}
|
||||
|
||||
ui/dialogs/UpdateAvailableDialog.h
|
||||
ui/dialogs/UpdateAvailableDialog.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(LAUNCHER_SOURCES
|
||||
WindowsConsole.cpp
|
||||
WindowsConsole.h
|
||||
${LAUNCHER_SOURCES}
|
||||
)
|
||||
endif()
|
||||
|
||||
qt_wrap_ui(LAUNCHER_UI
|
||||
ui/MainWindow.ui
|
||||
ui/setupwizard/PasteWizardPage.ui
|
||||
@ -1032,15 +1143,18 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/pages/instance/ScreenshotsPage.ui
|
||||
ui/pages/modplatform/atlauncher/AtlOptionalModDialog.ui
|
||||
ui/pages/modplatform/atlauncher/AtlPage.ui
|
||||
ui/pages/modplatform/VanillaPage.ui
|
||||
ui/pages/modplatform/CustomPage.ui
|
||||
ui/pages/modplatform/ResourcePage.ui
|
||||
ui/pages/modplatform/flame/FlamePage.ui
|
||||
ui/pages/modplatform/legacy_ftb/Page.ui
|
||||
ui/pages/modplatform/import_ftb/ImportFTBPage.ui
|
||||
ui/pages/modplatform/ImportPage.ui
|
||||
ui/pages/modplatform/OptionalModDialog.ui
|
||||
ui/pages/modplatform/modrinth/ModrinthPage.ui
|
||||
ui/pages/modplatform/technic/TechnicPage.ui
|
||||
ui/widgets/InstanceCardWidget.ui
|
||||
ui/widgets/CustomCommands.ui
|
||||
ui/widgets/EnvironmentVariables.ui
|
||||
ui/widgets/InfoFrame.ui
|
||||
ui/widgets/ModFilterWidget.ui
|
||||
ui/widgets/SubTaskProgressBar.ui
|
||||
@ -1054,13 +1168,13 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/dialogs/ProfileSelectDialog.ui
|
||||
ui/dialogs/SkinUploadDialog.ui
|
||||
ui/dialogs/ExportInstanceDialog.ui
|
||||
ui/dialogs/ExportMrPackDialog.ui
|
||||
ui/dialogs/ExportPackDialog.ui
|
||||
ui/dialogs/ExportToModListDialog.ui
|
||||
ui/dialogs/IconPickerDialog.ui
|
||||
ui/dialogs/ImportResourceDialog.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
|
||||
@ -1068,6 +1182,14 @@ qt_wrap_ui(LAUNCHER_UI
|
||||
ui/dialogs/ChooseProviderDialog.ui
|
||||
)
|
||||
|
||||
qt_wrap_ui(PRISM_UPDATE_UI
|
||||
ui/dialogs/UpdateAvailableDialog.ui
|
||||
)
|
||||
|
||||
if (NOT Apple)
|
||||
set (LAUNCHER_UI ${LAUNCHER_UI} ${PRISM_UPDATE_UI})
|
||||
endif()
|
||||
|
||||
qt_add_resources(LAUNCHER_RESOURCES
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
@ -1084,14 +1206,31 @@ qt_add_resources(LAUNCHER_RESOURCES
|
||||
../${Launcher_Branding_LogoQRC}
|
||||
)
|
||||
|
||||
qt_wrap_ui(PRISMUPDATER_UI
|
||||
updater/prismupdater/SelectReleaseDialog.ui
|
||||
ui/widgets/SubTaskProgressBar.ui
|
||||
ui/dialogs/ProgressDialog.ui
|
||||
)
|
||||
|
||||
######## Windows resource files ########
|
||||
if(WIN32)
|
||||
set(LAUNCHER_RCS ${CMAKE_CURRENT_BINARY_DIR}/../${Launcher_Branding_WindowsRC})
|
||||
endif()
|
||||
|
||||
include(CompilerWarnings)
|
||||
|
||||
# Add executable
|
||||
add_library(Launcher_logic STATIC ${LOGIC_SOURCES} ${LAUNCHER_SOURCES} ${LAUNCHER_UI} ${LAUNCHER_RESOURCES})
|
||||
if(BUILD_TESTING)
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_TEST)
|
||||
endif()
|
||||
set_project_warnings(Launcher_logic
|
||||
"${Launcher_MSVC_WARNINGS}"
|
||||
"${Launcher_CLANG_WARNINGS}"
|
||||
"${Launcher_GCC_WARNINGS}")
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_include_directories(Launcher_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(Launcher_logic PUBLIC LAUNCHER_APPLICATION)
|
||||
target_link_libraries(Launcher_logic
|
||||
systeminfo
|
||||
Launcher_murmur2
|
||||
@ -1130,17 +1269,23 @@ 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)
|
||||
if(Launcher_ENABLE_UPDATER)
|
||||
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")
|
||||
add_compile_definitions(SPARKLE_ENABLED)
|
||||
endif()
|
||||
|
||||
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})
|
||||
if(Launcher_ENABLE_UPDATER)
|
||||
target_link_libraries(Launcher_logic ${SPARKLE_FRAMEWORK})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries(Launcher_logic)
|
||||
@ -1167,8 +1312,51 @@ install(TARGETS ${Launcher_Name}
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
if(Launcher_BUILD_UPDATER)
|
||||
# Updater
|
||||
add_library(prism_updater_logic STATIC ${PRISMUPDATER_SOURCES} ${TASKS_SOURCES} ${PRISMUPDATER_UI})
|
||||
target_include_directories(prism_updater_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(prism_updater_logic
|
||||
QuaZip::QuaZip
|
||||
${ZLIB_LIBRARIES}
|
||||
systeminfo
|
||||
BuildConfig
|
||||
ghcFilesystem::ghc_filesystem
|
||||
Qt${QT_VERSION_MAJOR}::Widgets
|
||||
Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
${Launcher_QT_LIBS}
|
||||
cmark::cmark
|
||||
Katabasis
|
||||
)
|
||||
|
||||
add_executable("${Launcher_Name}_updater" WIN32 updater/prismupdater/updater_main.cpp)
|
||||
target_sources("${Launcher_Name}_updater" PRIVATE updater/prismupdater/updater.exe.manifest)
|
||||
target_link_libraries("${Launcher_Name}_updater" prism_updater_logic)
|
||||
|
||||
if(DEFINED Launcher_APP_BINARY_NAME)
|
||||
set_target_properties("${Launcher_Name}_updater" PROPERTIES OUTPUT_NAME "${Launcher_APP_BINARY_NAME}_updater")
|
||||
endif()
|
||||
if(DEFINED Launcher_BINARY_RPATH)
|
||||
SET_TARGET_PROPERTIES("${Launcher_Name}_updater" PROPERTIES INSTALL_RPATH "${Launcher_BINARY_RPATH}")
|
||||
endif()
|
||||
|
||||
install(TARGETS "${Launcher_Name}_updater"
|
||||
BUNDLE DESTINATION "." COMPONENT Runtime
|
||||
LIBRARY DESTINATION ${LIBRARY_DEST_DIR} COMPONENT Runtime
|
||||
RUNTIME DESTINATION ${BINARY_DEST_DIR} COMPONENT Runtime
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_DEST_DIR} COMPONENT Runtime
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WIN32 OR (DEFINED Launcher_BUILD_FILELINKER AND Launcher_BUILD_FILELINKER))
|
||||
# File link
|
||||
add_library(filelink_logic STATIC ${LINKEXE_SOURCES})
|
||||
set_project_warnings(filelink_logic
|
||||
"${Launcher_MSVC_WARNINGS}"
|
||||
"${Launcher_CLANG_WARNINGS}"
|
||||
"${Launcher_GCC_WARNINGS}")
|
||||
|
||||
target_include_directories(filelink_logic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(filelink_logic
|
||||
systeminfo
|
||||
@ -1181,7 +1369,7 @@ if(WIN32)
|
||||
${Launcher_QT_LIBS}
|
||||
)
|
||||
|
||||
add_executable("${Launcher_Name}_filelink" WIN32 filelink/main.cpp)
|
||||
add_executable("${Launcher_Name}_filelink" WIN32 filelink/filelink_main.cpp)
|
||||
|
||||
target_sources("${Launcher_Name}_filelink" PRIVATE filelink/filelink.exe.manifest)
|
||||
|
||||
@ -1202,7 +1390,7 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
if (UNIX AND APPLE)
|
||||
if (UNIX AND APPLE AND Launcher_ENABLE_UPDATER)
|
||||
# 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
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -41,8 +41,7 @@
|
||||
* @file libutil/src/cmdutils.cpp
|
||||
*/
|
||||
|
||||
namespace Commandline
|
||||
{
|
||||
namespace Commandline {
|
||||
|
||||
// commandline splitter
|
||||
QStringList splitArgs(QString args)
|
||||
@ -51,19 +50,15 @@ QStringList splitArgs(QString args)
|
||||
QString current;
|
||||
bool escape = false;
|
||||
QChar inquotes;
|
||||
for (int i = 0; i < args.length(); i++)
|
||||
{
|
||||
for (int i = 0; i < args.length(); i++) {
|
||||
QChar cchar = args.at(i);
|
||||
|
||||
// \ escaped
|
||||
if (escape)
|
||||
{
|
||||
if (escape) {
|
||||
current += cchar;
|
||||
escape = false;
|
||||
// in "quotes"
|
||||
}
|
||||
else if (!inquotes.isNull())
|
||||
{
|
||||
} else if (!inquotes.isNull()) {
|
||||
if (cchar == '\\')
|
||||
escape = true;
|
||||
else if (cchar == inquotes)
|
||||
@ -71,18 +66,13 @@ QStringList splitArgs(QString args)
|
||||
else
|
||||
current += cchar;
|
||||
// otherwise
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cchar == ' ')
|
||||
{
|
||||
if (!current.isEmpty())
|
||||
{
|
||||
} else {
|
||||
if (cchar == ' ') {
|
||||
if (!current.isEmpty()) {
|
||||
argv << current;
|
||||
current.clear();
|
||||
}
|
||||
}
|
||||
else if (cchar == '"' || cchar == '\'')
|
||||
} else if (cchar == '"' || cchar == '\'')
|
||||
inquotes = cchar;
|
||||
else
|
||||
current += cchar;
|
||||
@ -92,4 +82,4 @@ QStringList splitArgs(QString args)
|
||||
argv << current;
|
||||
return argv;
|
||||
}
|
||||
}
|
||||
} // namespace Commandline
|
||||
|
@ -25,8 +25,7 @@
|
||||
* @brief commandline parsing and processing utilities
|
||||
*/
|
||||
|
||||
namespace Commandline
|
||||
{
|
||||
namespace Commandline {
|
||||
|
||||
/**
|
||||
* @brief split a string into argv items like a shell would do
|
||||
@ -34,4 +33,4 @@ namespace Commandline
|
||||
* @return a QStringList containing all arguments
|
||||
*/
|
||||
QStringList splitArgs(QString args);
|
||||
}
|
||||
} // namespace Commandline
|
||||
|
@ -1,33 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
template <typename T>
|
||||
class DefaultVariable
|
||||
{
|
||||
public:
|
||||
DefaultVariable(const T & value)
|
||||
{
|
||||
defaultValue = value;
|
||||
}
|
||||
DefaultVariable<T> & operator =(const T & value)
|
||||
class DefaultVariable {
|
||||
public:
|
||||
DefaultVariable(const T& value) { defaultValue = value; }
|
||||
DefaultVariable<T>& operator=(const T& value)
|
||||
{
|
||||
currentValue = value;
|
||||
is_default = currentValue == defaultValue;
|
||||
is_explicit = true;
|
||||
return *this;
|
||||
}
|
||||
operator const T &() const
|
||||
{
|
||||
return is_default ? defaultValue : currentValue;
|
||||
}
|
||||
bool isDefault() const
|
||||
{
|
||||
return is_default;
|
||||
}
|
||||
bool isExplicit() const
|
||||
{
|
||||
return is_explicit;
|
||||
}
|
||||
private:
|
||||
operator const T&() const { return is_default ? defaultValue : currentValue; }
|
||||
bool isDefault() const { return is_default; }
|
||||
bool isExplicit() const { return is_explicit; }
|
||||
|
||||
private:
|
||||
T currentValue;
|
||||
T defaultValue;
|
||||
bool is_default = true;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -33,40 +33,37 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "DesktopServices.h"
|
||||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
#include <QProcess>
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
|
||||
/**
|
||||
* This shouldn't exist, but until QTBUG-9328 and other unreported bugs are fixed, it needs to be a thing.
|
||||
*/
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
template <typename T>
|
||||
bool IndirectOpen(T callable, qint64 *pid_forked = nullptr)
|
||||
bool IndirectOpen(T callable, qint64* pid_forked = nullptr)
|
||||
{
|
||||
auto pid = fork();
|
||||
if(pid_forked)
|
||||
{
|
||||
if(pid > 0)
|
||||
if (pid_forked) {
|
||||
if (pid > 0)
|
||||
*pid_forked = pid;
|
||||
else
|
||||
*pid_forked = 0;
|
||||
}
|
||||
if(pid == -1)
|
||||
{
|
||||
if (pid == -1) {
|
||||
qWarning() << "IndirectOpen failed to fork: " << errno;
|
||||
return false;
|
||||
}
|
||||
// child - do the stuff
|
||||
if(pid == 0)
|
||||
{
|
||||
if (pid == 0) {
|
||||
// unset all this garbage so it doesn't get passed to the child process
|
||||
qunsetenv("LD_PRELOAD");
|
||||
qunsetenv("LD_LIBRARY_PATH");
|
||||
@ -82,19 +79,14 @@ bool IndirectOpen(T callable, qint64 *pid_forked = nullptr)
|
||||
|
||||
// die. now. do not clean up anything, it would just hang forever.
|
||||
_exit(status ? 0 : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
//parent - assume it worked.
|
||||
} else {
|
||||
// parent - assume it worked.
|
||||
int status;
|
||||
while (waitpid(pid, &status, 0))
|
||||
{
|
||||
if(WIFEXITED(status))
|
||||
{
|
||||
while (waitpid(pid, &status, 0)) {
|
||||
if (WIFEXITED(status)) {
|
||||
return WEXITSTATUS(status) == 0;
|
||||
}
|
||||
if(WIFSIGNALED(status))
|
||||
{
|
||||
if (WIFSIGNALED(status)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -104,26 +96,19 @@ bool IndirectOpen(T callable, qint64 *pid_forked = nullptr)
|
||||
#endif
|
||||
|
||||
namespace DesktopServices {
|
||||
bool openDirectory(const QString &path, bool ensureExists)
|
||||
bool openDirectory(const QString& path, [[maybe_unused]] bool ensureExists)
|
||||
{
|
||||
qDebug() << "Opening directory" << path;
|
||||
QDir parentPath;
|
||||
QDir dir(path);
|
||||
if (!dir.exists())
|
||||
{
|
||||
if (ensureExists && !dir.exists()) {
|
||||
parentPath.mkpath(dir.absolutePath());
|
||||
}
|
||||
auto f = [&]()
|
||||
{
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||
};
|
||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath())); };
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!isFlatpak())
|
||||
{
|
||||
if (!isSandbox()) {
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return f();
|
||||
}
|
||||
#else
|
||||
@ -131,20 +116,14 @@ bool openDirectory(const QString &path, bool ensureExists)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool openFile(const QString &path)
|
||||
bool openFile(const QString& path)
|
||||
{
|
||||
qDebug() << "Opening file" << path;
|
||||
auto f = [&]()
|
||||
{
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
};
|
||||
auto f = [&]() { return QDesktopServices::openUrl(QUrl::fromLocalFile(path)); };
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!isFlatpak())
|
||||
{
|
||||
if (!isSandbox()) {
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return f();
|
||||
}
|
||||
#else
|
||||
@ -152,41 +131,29 @@ bool openFile(const QString &path)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool openFile(const QString &application, const QString &path, const QString &workingDirectory, qint64 *pid)
|
||||
bool openFile(const QString& application, const QString& path, const QString& workingDirectory, qint64* pid)
|
||||
{
|
||||
qDebug() << "Opening file" << path << "using" << application;
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
if(!isFlatpak())
|
||||
{
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory);
|
||||
}, pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||
if (!isSandbox()) {
|
||||
return IndirectOpen([&]() { return QProcess::startDetached(application, QStringList() << path, workingDirectory); }, pid);
|
||||
} else {
|
||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||
}
|
||||
#else
|
||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool run(const QString &application, const QStringList &args, const QString &workingDirectory, qint64 *pid)
|
||||
bool run(const QString& application, const QStringList& args, const QString& workingDirectory, qint64* pid)
|
||||
{
|
||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!isFlatpak())
|
||||
{
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
return IndirectOpen([&]()
|
||||
{
|
||||
return QProcess::startDetached(application, args, workingDirectory);
|
||||
}, pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isSandbox()) {
|
||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||
return IndirectOpen([&]() { return QProcess::startDetached(application, args, workingDirectory); }, pid);
|
||||
} else {
|
||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
||||
}
|
||||
#else
|
||||
@ -194,20 +161,14 @@ bool run(const QString &application, const QStringList &args, const QString &wor
|
||||
#endif
|
||||
}
|
||||
|
||||
bool openUrl(const QUrl &url)
|
||||
bool openUrl(const QUrl& url)
|
||||
{
|
||||
qDebug() << "Opening URL" << url.toString();
|
||||
auto f = [&]()
|
||||
{
|
||||
return QDesktopServices::openUrl(url);
|
||||
};
|
||||
auto f = [&]() { return QDesktopServices::openUrl(url); };
|
||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||
if(!isFlatpak())
|
||||
{
|
||||
if (!isSandbox()) {
|
||||
return IndirectOpen(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return f();
|
||||
}
|
||||
#else
|
||||
@ -224,4 +185,18 @@ bool isFlatpak()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isSnap()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return getenv("SNAP");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isSandbox()
|
||||
{
|
||||
return isSnap() || isFlatpak();
|
||||
}
|
||||
|
||||
} // namespace DesktopServices
|
||||
|
@ -1,38 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <QUrl>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
/**
|
||||
* This wraps around QDesktopServices and adds workarounds where needed
|
||||
* Use this instead of QDesktopServices!
|
||||
*/
|
||||
namespace DesktopServices
|
||||
{
|
||||
/**
|
||||
* Open a file in whatever application is applicable
|
||||
*/
|
||||
bool openFile(const QString &path);
|
||||
namespace DesktopServices {
|
||||
/**
|
||||
* Open a file in whatever application is applicable
|
||||
*/
|
||||
bool openFile(const QString& path);
|
||||
|
||||
/**
|
||||
* Open a file in the specified application
|
||||
*/
|
||||
bool openFile(const QString &application, const QString &path, const QString & workingDirectory = QString(), qint64 *pid = 0);
|
||||
/**
|
||||
* Open a file in the specified application
|
||||
*/
|
||||
bool openFile(const QString& application, const QString& path, const QString& workingDirectory = QString(), qint64* pid = 0);
|
||||
|
||||
/**
|
||||
* Run an application
|
||||
*/
|
||||
bool run(const QString &application,const QStringList &args, const QString & workingDirectory = QString(), qint64 *pid = 0);
|
||||
/**
|
||||
* Run an application
|
||||
*/
|
||||
bool run(const QString& application, const QStringList& args, const QString& workingDirectory = QString(), qint64* pid = 0);
|
||||
|
||||
/**
|
||||
* Open a directory
|
||||
*/
|
||||
bool openDirectory(const QString &path, bool ensureExists = false);
|
||||
/**
|
||||
* Open a directory
|
||||
*/
|
||||
bool openDirectory(const QString& path, bool ensureExists = false);
|
||||
|
||||
/**
|
||||
* Open the URL, most likely in a browser. Maybe.
|
||||
*/
|
||||
bool openUrl(const QUrl &url);
|
||||
/**
|
||||
* Open the URL, most likely in a browser. Maybe.
|
||||
*/
|
||||
bool openUrl(const QUrl& url);
|
||||
|
||||
bool isFlatpak();
|
||||
}
|
||||
/**
|
||||
* Determine whether the launcher is running in a Flatpak environment
|
||||
*/
|
||||
bool isFlatpak();
|
||||
|
||||
/**
|
||||
* Determine whether the launcher is running in a Snap environment
|
||||
*/
|
||||
bool isSnap();
|
||||
|
||||
/**
|
||||
* Determine whether the launcher is running in a sandboxed (Flatpak or Snap) environment
|
||||
*/
|
||||
bool isSandbox();
|
||||
} // namespace DesktopServices
|
||||
|
@ -2,31 +2,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <exception>
|
||||
|
||||
class Exception : public std::exception
|
||||
{
|
||||
public:
|
||||
Exception(const QString &message) : std::exception(), m_message(message)
|
||||
{
|
||||
qCritical() << "Exception:" << message;
|
||||
}
|
||||
Exception(const Exception &other)
|
||||
: std::exception(), m_message(other.cause())
|
||||
{
|
||||
}
|
||||
class Exception : public std::exception {
|
||||
public:
|
||||
Exception(const QString& message) : std::exception(), m_message(message) { qCritical() << "Exception:" << message; }
|
||||
Exception(const Exception& other) : std::exception(), m_message(other.cause()) {}
|
||||
virtual ~Exception() noexcept {}
|
||||
const char *what() const noexcept
|
||||
{
|
||||
return m_message.toLatin1().constData();
|
||||
}
|
||||
QString cause() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
const char* what() const noexcept { return m_message.toLatin1().constData(); }
|
||||
QString cause() const { return m_message; }
|
||||
|
||||
private:
|
||||
private:
|
||||
QString m_message;
|
||||
};
|
||||
|
@ -4,31 +4,24 @@
|
||||
template <typename T>
|
||||
inline void clamp(T& current, T min, T max)
|
||||
{
|
||||
if (current < min)
|
||||
{
|
||||
if (current < min) {
|
||||
current = min;
|
||||
}
|
||||
else if(current > max)
|
||||
{
|
||||
} else if (current > max) {
|
||||
current = max;
|
||||
}
|
||||
}
|
||||
|
||||
// List of numbers from min to max. Next is exponent times bigger than previous.
|
||||
|
||||
class ExponentialSeries
|
||||
{
|
||||
public:
|
||||
class ExponentialSeries {
|
||||
public:
|
||||
ExponentialSeries(unsigned min, unsigned max, unsigned exponent = 2)
|
||||
{
|
||||
m_current = m_min = min;
|
||||
m_max = max;
|
||||
m_exponent = exponent;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
m_current = m_min;
|
||||
}
|
||||
void reset() { m_current = m_min; }
|
||||
unsigned operator()()
|
||||
{
|
||||
unsigned retval = m_current;
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <QFileSystemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QStack>
|
||||
#include <algorithm>
|
||||
#include "FileSystem.h"
|
||||
#include "SeparatorPrefixTree.h"
|
||||
#include "StringUtils.h"
|
||||
@ -254,3 +255,22 @@ bool FileIgnoreProxy::filterAcceptsColumn(int source_column, const QModelIndex&
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileIgnoreProxy::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
|
||||
{
|
||||
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||
|
||||
auto fileInfo = fsm->fileInfo(index);
|
||||
return !ignoreFile(fileInfo);
|
||||
}
|
||||
|
||||
bool FileIgnoreProxy::ignoreFile(QFileInfo fileInfo) const
|
||||
{
|
||||
return m_ignoreFiles.contains(fileInfo.fileName()) || m_ignoreFilePaths.covers(relPath(fileInfo.absoluteFilePath()));
|
||||
}
|
||||
|
||||
bool FileIgnoreProxy::filterFile(const QString& fileName) const
|
||||
{
|
||||
return blocked.covers(fileName) || ignoreFile(QFileInfo(QDir(root), fileName));
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include "SeparatorPrefixTree.h"
|
||||
|
||||
@ -63,10 +64,22 @@ class FileIgnoreProxy : public QSortFilterProxyModel {
|
||||
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
|
||||
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
|
||||
|
||||
// list of file names that need to be removed completely from model
|
||||
inline QStringList& ignoreFilesWithName() { return m_ignoreFiles; }
|
||||
// list of relative paths that need to be removed completely from model
|
||||
inline SeparatorPrefixTree<'/'>& ignoreFilesWithPath() { return m_ignoreFilePaths; }
|
||||
|
||||
bool filterFile(const QString& fileName) const;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
||||
|
||||
bool ignoreFile(QFileInfo file) const;
|
||||
|
||||
private:
|
||||
const QString root;
|
||||
SeparatorPrefixTree<'/'> blocked;
|
||||
QStringList m_ignoreFiles;
|
||||
SeparatorPrefixTree<'/'> m_ignoreFilePaths;
|
||||
};
|
||||
|
@ -36,6 +36,7 @@
|
||||
*/
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include <QPair>
|
||||
|
||||
#include "BuildConfig.h"
|
||||
|
||||
@ -102,7 +103,7 @@ namespace fs = ghc::filesystem;
|
||||
#include <linux/fs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
#elif defined(Q_OS_MACOS)
|
||||
#include <sys/attr.h>
|
||||
#include <sys/clonefile.h>
|
||||
#elif defined(Q_OS_WIN)
|
||||
@ -122,26 +123,35 @@ namespace fs = ghc::filesystem;
|
||||
|
||||
#if defined(__MINGW32__)
|
||||
|
||||
typedef struct _DUPLICATE_EXTENTS_DATA {
|
||||
struct _DUPLICATE_EXTENTS_DATA {
|
||||
HANDLE FileHandle;
|
||||
LARGE_INTEGER SourceFileOffset;
|
||||
LARGE_INTEGER TargetFileOffset;
|
||||
LARGE_INTEGER ByteCount;
|
||||
} DUPLICATE_EXTENTS_DATA, *PDUPLICATE_EXTENTS_DATA;
|
||||
};
|
||||
|
||||
typedef struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER {
|
||||
using DUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA;
|
||||
using PDUPLICATE_EXTENTS_DATA = _DUPLICATE_EXTENTS_DATA*;
|
||||
|
||||
struct _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER {
|
||||
WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32
|
||||
WORD Reserved; // Must be 0
|
||||
DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx
|
||||
DWORD ChecksumChunkSizeInBytes;
|
||||
DWORD ClusterSizeInBytes;
|
||||
} FSCTL_GET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER;
|
||||
};
|
||||
|
||||
typedef struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER {
|
||||
using FSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER;
|
||||
using PFSCTL_GET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_GET_INTEGRITY_INFORMATION_BUFFER*;
|
||||
|
||||
struct _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER {
|
||||
WORD ChecksumAlgorithm; // Checksum algorithm. e.g. CHECKSUM_TYPE_UNCHANGED, CHECKSUM_TYPE_NONE, CHECKSUM_TYPE_CRC32
|
||||
WORD Reserved; // Must be 0
|
||||
DWORD Flags; // FSCTL_INTEGRITY_FLAG_xxx
|
||||
} FSCTL_SET_INTEGRITY_INFORMATION_BUFFER, *PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER;
|
||||
};
|
||||
|
||||
using FSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER;
|
||||
using PFSCTL_SET_INTEGRITY_INFORMATION_BUFFER = _FSCTL_SET_INTEGRITY_INFORMATION_BUFFER*;
|
||||
|
||||
#endif
|
||||
|
||||
@ -193,6 +203,40 @@ void write(const QString& filename, const QByteArray& data)
|
||||
}
|
||||
}
|
||||
|
||||
void appendSafe(const QString& filename, const QByteArray& data)
|
||||
{
|
||||
ensureExists(QFileInfo(filename).dir());
|
||||
QByteArray buffer;
|
||||
try {
|
||||
buffer = read(filename);
|
||||
} catch (FileSystemException&) {
|
||||
buffer = QByteArray();
|
||||
}
|
||||
buffer.append(data);
|
||||
QSaveFile file(filename);
|
||||
if (!file.open(QSaveFile::WriteOnly)) {
|
||||
throw FileSystemException("Couldn't open " + filename + " for writing: " + file.errorString());
|
||||
}
|
||||
if (buffer.size() != file.write(buffer)) {
|
||||
throw FileSystemException("Error writing data to " + filename + ": " + file.errorString());
|
||||
}
|
||||
if (!file.commit()) {
|
||||
throw FileSystemException("Error while committing data to " + filename + ": " + file.errorString());
|
||||
}
|
||||
}
|
||||
|
||||
void append(const QString& filename, const QByteArray& data)
|
||||
{
|
||||
ensureExists(QFileInfo(filename).dir());
|
||||
QFile file(filename);
|
||||
if (!file.open(QFile::Append)) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray read(const QString& filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
@ -237,6 +281,28 @@ bool ensureFolderPathExists(QString foldernamepath)
|
||||
return success;
|
||||
}
|
||||
|
||||
bool copyFileAttributes(QString src, QString dst)
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
auto attrs = GetFileAttributesW(src.toStdWString().c_str());
|
||||
if (attrs == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
return SetFileAttributesW(dst.toStdWString().c_str(), attrs);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// needs folders to exists
|
||||
void copyFolderAttributes(QString src, QString dst, QString relative)
|
||||
{
|
||||
auto path = PathCombine(src, relative);
|
||||
QDir dsrc(src);
|
||||
while ((path = QFileInfo(path).path()).length() >= src.length()) {
|
||||
auto dst_path = PathCombine(dst, dsrc.relativeFilePath(path));
|
||||
copyFileAttributes(path, dst_path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copies a directory and it's contents from src to dest
|
||||
* @param offset subdirectory form src to copy to dest
|
||||
@ -246,6 +312,7 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
||||
{
|
||||
using copy_opts = fs::copy_options;
|
||||
m_copied = 0; // reset counter
|
||||
m_failedPaths.clear();
|
||||
|
||||
// NOTE always deep copy on windows. the alternatives are too messy.
|
||||
#if defined Q_OS_WIN32
|
||||
@ -263,6 +330,9 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
||||
if (!m_followSymlinks)
|
||||
opt |= copy_opts::copy_symlinks;
|
||||
|
||||
if (m_overwrite)
|
||||
opt |= copy_opts::overwrite_existing;
|
||||
|
||||
// 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))
|
||||
@ -271,12 +341,18 @@ bool copy::operator()(const QString& offset, bool dryRun)
|
||||
auto dst_path = PathCombine(dst, relative_dst_path);
|
||||
if (!dryRun) {
|
||||
ensureFilePathExists(dst_path);
|
||||
#ifdef Q_OS_WIN32
|
||||
copyFolderAttributes(src, dst, relative_dst_path);
|
||||
#endif
|
||||
fs::copy(StringUtils::toStdString(src_path), StringUtils::toStdString(dst_path), opt, err);
|
||||
}
|
||||
if (err) {
|
||||
qWarning() << "Failed to copy files:" << QString::fromStdString(err.message());
|
||||
qDebug() << "Source file:" << src_path;
|
||||
qDebug() << "Destination file:" << dst_path;
|
||||
m_failedPaths.append(dst_path);
|
||||
emit copyFailed(relative_dst_path);
|
||||
return;
|
||||
}
|
||||
m_copied++;
|
||||
emit fileCopied(relative_dst_path);
|
||||
@ -372,7 +448,7 @@ void create_link::make_link_list(const QString& offset)
|
||||
auto src_path = source_it.next();
|
||||
auto relative_path = src_dir.relativeFilePath(src_path);
|
||||
|
||||
if (m_max_depth >= 0 && pathDepth(relative_path) > m_max_depth){
|
||||
if (m_max_depth >= 0 && pathDepth(relative_path) > m_max_depth) {
|
||||
relative_path = pathTruncate(relative_path, m_max_depth);
|
||||
src_path = src_dir.filePath(relative_path);
|
||||
if (linkedPaths.contains(src_path)) {
|
||||
@ -663,7 +739,7 @@ QString pathTruncate(const QString& path, int depth)
|
||||
|
||||
QString trunc = QFileInfo(path).path();
|
||||
|
||||
if (pathDepth(trunc) > depth ) {
|
||||
if (pathDepth(trunc) > depth) {
|
||||
return pathTruncate(trunc, depth);
|
||||
}
|
||||
|
||||
@ -769,10 +845,48 @@ QString getDesktopDir()
|
||||
// Cross-platform Shortcut creation
|
||||
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon)
|
||||
{
|
||||
if (destination.isEmpty()) {
|
||||
destination = PathCombine(getDesktopDir(), RemoveInvalidFilenameChars(name));
|
||||
}
|
||||
#if defined(Q_OS_MACOS)
|
||||
destination += ".command";
|
||||
// Create the Application
|
||||
QDir applicationDirectory =
|
||||
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + "/" + BuildConfig.LAUNCHER_NAME + " Instances/";
|
||||
|
||||
QFile f(destination);
|
||||
if (!applicationDirectory.mkpath(".")) {
|
||||
qWarning() << "Couldn't create application directory";
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir application = applicationDirectory.path() + "/" + name + ".app/";
|
||||
|
||||
if (application.exists()) {
|
||||
qWarning() << "Application already exists!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!application.mkpath(".")) {
|
||||
qWarning() << "Couldn't create application";
|
||||
return false;
|
||||
}
|
||||
|
||||
QDir content = application.path() + "/Contents/";
|
||||
QDir resources = content.path() + "/Resources/";
|
||||
QDir binaryDir = content.path() + "/MacOS/";
|
||||
QFile info = content.path() + "/Info.plist";
|
||||
|
||||
if (!(content.mkpath(".") && resources.mkpath(".") && binaryDir.mkpath("."))) {
|
||||
qWarning() << "Couldn't create directories within application";
|
||||
return false;
|
||||
}
|
||||
info.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
|
||||
QFile(icon).rename(resources.path() + "/Icon.icns");
|
||||
|
||||
// Create the Command file
|
||||
QString exec = binaryDir.path() + "/Run.command";
|
||||
|
||||
QFile f(exec);
|
||||
f.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
QTextStream stream(&f);
|
||||
|
||||
@ -789,8 +903,34 @@ bool createShortcut(QString destination, QString target, QStringList args, QStri
|
||||
|
||||
f.setPermissions(f.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeGroup | QFileDevice::ExeOther);
|
||||
|
||||
// Generate the Info.plist
|
||||
QTextStream infoStream(&info);
|
||||
infoStream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \n"
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
|
||||
"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
|
||||
"<plist version=\"1.0\">\n"
|
||||
"<dict>\n"
|
||||
" <key>CFBundleExecutable</key>\n"
|
||||
" <string>Run.command</string>\n" // The path to the executable
|
||||
" <key>CFBundleIconFile</key>\n"
|
||||
" <string>Icon.icns</string>\n"
|
||||
" <key>CFBundleName</key>\n"
|
||||
" <string>"
|
||||
<< name
|
||||
<< "</string>\n" // Name of the application
|
||||
" <key>CFBundlePackageType</key>\n"
|
||||
" <string>APPL</string>\n"
|
||||
" <key>CFBundleShortVersionString</key>\n"
|
||||
" <string>1.0</string>\n"
|
||||
" <key>CFBundleVersion</key>\n"
|
||||
" <string>1.0</string>\n"
|
||||
"</dict>\n"
|
||||
"</plist>";
|
||||
|
||||
return true;
|
||||
#elif defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
if (!destination.endsWith(".desktop")) // in case of isFlatpak destination is already populated
|
||||
destination += ".desktop";
|
||||
QFile f(destination);
|
||||
f.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
QTextStream stream(&f);
|
||||
@ -974,7 +1114,7 @@ FilesystemType getFilesystemType(const QString& name)
|
||||
{
|
||||
for (auto iter = s_filesystem_type_names.constBegin(); iter != s_filesystem_type_names.constEnd(); ++iter) {
|
||||
auto fs_names = iter.value();
|
||||
if(fs_names.contains(name.toUpper()))
|
||||
if (fs_names.contains(name.toUpper()))
|
||||
return iter.key();
|
||||
}
|
||||
return FilesystemType::UNKNOWN;
|
||||
@ -1072,6 +1212,7 @@ bool clone::operator()(const QString& offset, bool dryRun)
|
||||
}
|
||||
|
||||
m_cloned = 0; // reset counter
|
||||
m_failedClones.clear();
|
||||
|
||||
auto src = PathCombine(m_src.absolutePath(), offset);
|
||||
auto dst = PathCombine(m_dst.absolutePath(), offset);
|
||||
@ -1092,6 +1233,9 @@ bool clone::operator()(const QString& offset, bool dryRun)
|
||||
qDebug() << "Failed to clone files: error" << err.value() << "message" << QString::fromStdString(err.message());
|
||||
qDebug() << "Source file:" << src_path;
|
||||
qDebug() << "Destination file:" << dst_path;
|
||||
m_failedClones.append(qMakePair(src_path, dst_path));
|
||||
emit cloneFailed(src_path, dst_path);
|
||||
return;
|
||||
}
|
||||
m_cloned++;
|
||||
emit fileCloned(src_path, dst_path);
|
||||
@ -1151,7 +1295,7 @@ bool clone_file(const QString& src, const QString& dst, std::error_code& ec)
|
||||
return false;
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
#elif defined(Q_OS_MACOS)
|
||||
|
||||
if (!macos_bsd_clonefile(src_path, dst_path, ec)) {
|
||||
qDebug() << "failed macos_bsd_clonefile:";
|
||||
@ -1380,7 +1524,7 @@ bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||
#elif defined(Q_OS_MACOS)
|
||||
|
||||
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec)
|
||||
{
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include <QFlags>
|
||||
#include <QLocalServer>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QThread>
|
||||
|
||||
namespace FS {
|
||||
@ -60,6 +61,16 @@ class FileSystemException : public ::Exception {
|
||||
*/
|
||||
void write(const QString& filename, const QByteArray& data);
|
||||
|
||||
/**
|
||||
* append data to a file safely
|
||||
*/
|
||||
void appendSafe(const QString& filename, const QByteArray& data);
|
||||
|
||||
/**
|
||||
* append data to a file
|
||||
*/
|
||||
void append(const QString& filename, const QByteArray& data);
|
||||
|
||||
/**
|
||||
* read data from a file safely\
|
||||
*/
|
||||
@ -108,13 +119,21 @@ class copy : public QObject {
|
||||
m_whitelist = whitelist;
|
||||
return *this;
|
||||
}
|
||||
copy& overwrite(const bool overwrite)
|
||||
{
|
||||
m_overwrite = overwrite;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||
|
||||
int totalCopied() { return m_copied; }
|
||||
qsizetype totalCopied() { return m_copied; }
|
||||
qsizetype totalFailed() { return m_failedPaths.length(); }
|
||||
QStringList failed() { return m_failedPaths; }
|
||||
|
||||
signals:
|
||||
void fileCopied(const QString& relativeName);
|
||||
void copyFailed(const QString& relativeName);
|
||||
// TODO: maybe add a "shouldCopy" signal in the future?
|
||||
|
||||
private:
|
||||
@ -124,9 +143,11 @@ class copy : public QObject {
|
||||
bool m_followSymlinks = true;
|
||||
const IPathMatcher* m_matcher = nullptr;
|
||||
bool m_whitelist = false;
|
||||
bool m_overwrite = false;
|
||||
QDir m_src;
|
||||
QDir m_dst;
|
||||
int m_copied;
|
||||
qsizetype m_copied;
|
||||
QStringList m_failedPaths;
|
||||
};
|
||||
|
||||
struct LinkPair {
|
||||
@ -360,25 +381,24 @@ enum class FilesystemType {
|
||||
* QMap is ordered
|
||||
*
|
||||
*/
|
||||
static const QMap<FilesystemType, QStringList> s_filesystem_type_names = {
|
||||
{FilesystemType::FAT, { "FAT" }},
|
||||
{FilesystemType::NTFS, { "NTFS" }},
|
||||
{FilesystemType::REFS, { "REFS" }},
|
||||
{FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" }},
|
||||
{FilesystemType::EXT_2_3_4, { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" }},
|
||||
{FilesystemType::EXT, { "EXT" }},
|
||||
{FilesystemType::XFS, { "XFS" }},
|
||||
{FilesystemType::BTRFS, { "BTRFS" }},
|
||||
{FilesystemType::NFS, { "NFS" }},
|
||||
{FilesystemType::ZFS, { "ZFS" }},
|
||||
{FilesystemType::APFS, { "APFS" }},
|
||||
{FilesystemType::HFS, { "HFS" }},
|
||||
{FilesystemType::HFSPLUS, { "HFSPLUS" }},
|
||||
{FilesystemType::HFSX, { "HFSX" }},
|
||||
{FilesystemType::FUSEBLK, { "FUSEBLK" }},
|
||||
{FilesystemType::F2FS, { "F2FS" }},
|
||||
{FilesystemType::UNKNOWN, { "UNKNOWN" }}
|
||||
};
|
||||
static const QMap<FilesystemType, QStringList> s_filesystem_type_names = { { FilesystemType::FAT, { "FAT" } },
|
||||
{ FilesystemType::NTFS, { "NTFS" } },
|
||||
{ FilesystemType::REFS, { "REFS" } },
|
||||
{ FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" } },
|
||||
{ FilesystemType::EXT_2_3_4,
|
||||
{ "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" } },
|
||||
{ FilesystemType::EXT, { "EXT" } },
|
||||
{ FilesystemType::XFS, { "XFS" } },
|
||||
{ FilesystemType::BTRFS, { "BTRFS" } },
|
||||
{ FilesystemType::NFS, { "NFS" } },
|
||||
{ FilesystemType::ZFS, { "ZFS" } },
|
||||
{ FilesystemType::APFS, { "APFS" } },
|
||||
{ FilesystemType::HFS, { "HFS" } },
|
||||
{ FilesystemType::HFSPLUS, { "HFSPLUS" } },
|
||||
{ FilesystemType::HFSX, { "HFSX" } },
|
||||
{ FilesystemType::FUSEBLK, { "FUSEBLK" } },
|
||||
{ FilesystemType::F2FS, { "F2FS" } },
|
||||
{ FilesystemType::UNKNOWN, { "UNKNOWN" } } };
|
||||
|
||||
/**
|
||||
* @brief Get the string name of Filesystem enum object
|
||||
@ -470,7 +490,10 @@ class clone : public QObject {
|
||||
|
||||
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||
|
||||
int totalCloned() { return m_cloned; }
|
||||
qsizetype totalCloned() { return m_cloned; }
|
||||
qsizetype totalFailed() { return m_failedClones.length(); }
|
||||
|
||||
QList<QPair<QString, QString>> failed() { return m_failedClones; }
|
||||
|
||||
signals:
|
||||
void fileCloned(const QString& src, const QString& dst);
|
||||
@ -484,7 +507,8 @@ class clone : public QObject {
|
||||
bool m_whitelist = false;
|
||||
QDir m_src;
|
||||
QDir m_dst;
|
||||
int m_cloned;
|
||||
qsizetype m_cloned;
|
||||
QList<QPair<QString, QString>> m_failedClones;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,28 +1,33 @@
|
||||
#include "Filter.h"
|
||||
|
||||
Filter::~Filter(){}
|
||||
Filter::~Filter() {}
|
||||
|
||||
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern){}
|
||||
ContainsFilter::~ContainsFilter(){}
|
||||
ContainsFilter::ContainsFilter(const QString& pattern) : pattern(pattern) {}
|
||||
ContainsFilter::~ContainsFilter() {}
|
||||
bool ContainsFilter::accepts(const QString& value)
|
||||
{
|
||||
return value.contains(pattern);
|
||||
}
|
||||
|
||||
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern){}
|
||||
ExactFilter::~ExactFilter(){}
|
||||
ExactFilter::ExactFilter(const QString& pattern) : pattern(pattern) {}
|
||||
ExactFilter::~ExactFilter() {}
|
||||
bool ExactFilter::accepts(const QString& value)
|
||||
{
|
||||
return value == pattern;
|
||||
}
|
||||
|
||||
RegexpFilter::RegexpFilter(const QString& regexp, bool invert)
|
||||
:invert(invert)
|
||||
ExactIfPresentFilter::ExactIfPresentFilter(const QString& pattern) : pattern(pattern) {}
|
||||
bool ExactIfPresentFilter::accepts(const QString& value)
|
||||
{
|
||||
return value.isEmpty() || value == pattern;
|
||||
}
|
||||
|
||||
RegexpFilter::RegexpFilter(const QString& regexp, bool invert) : invert(invert)
|
||||
{
|
||||
pattern.setPattern(regexp);
|
||||
pattern.optimize();
|
||||
}
|
||||
RegexpFilter::~RegexpFilter(){}
|
||||
RegexpFilter::~RegexpFilter() {}
|
||||
bool RegexpFilter::accepts(const QString& value)
|
||||
{
|
||||
auto match = pattern.match(value);
|
||||
|
@ -1,42 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
|
||||
class Filter
|
||||
{
|
||||
public:
|
||||
class Filter {
|
||||
public:
|
||||
virtual ~Filter();
|
||||
virtual bool accepts(const QString & value) = 0;
|
||||
virtual bool accepts(const QString& value) = 0;
|
||||
};
|
||||
|
||||
class ContainsFilter: public Filter
|
||||
{
|
||||
public:
|
||||
ContainsFilter(const QString &pattern);
|
||||
class ContainsFilter : public Filter {
|
||||
public:
|
||||
ContainsFilter(const QString& pattern);
|
||||
virtual ~ContainsFilter();
|
||||
bool accepts(const QString & value) override;
|
||||
private:
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
QString pattern;
|
||||
};
|
||||
|
||||
class ExactFilter: public Filter
|
||||
{
|
||||
public:
|
||||
ExactFilter(const QString &pattern);
|
||||
class ExactFilter : public Filter {
|
||||
public:
|
||||
ExactFilter(const QString& pattern);
|
||||
virtual ~ExactFilter();
|
||||
bool accepts(const QString & value) override;
|
||||
private:
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
QString pattern;
|
||||
};
|
||||
|
||||
class RegexpFilter: public Filter
|
||||
{
|
||||
public:
|
||||
RegexpFilter(const QString ®exp, bool invert);
|
||||
class ExactIfPresentFilter : public Filter {
|
||||
public:
|
||||
ExactIfPresentFilter(const QString& pattern);
|
||||
~ExactIfPresentFilter() override = default;
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
QString pattern;
|
||||
};
|
||||
|
||||
class RegexpFilter : public Filter {
|
||||
public:
|
||||
RegexpFilter(const QString& regexp, bool invert);
|
||||
virtual ~RegexpFilter();
|
||||
bool accepts(const QString & value) override;
|
||||
private:
|
||||
bool accepts(const QString& value) override;
|
||||
|
||||
private:
|
||||
QRegularExpression pattern;
|
||||
bool invert = false;
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -37,10 +37,9 @@
|
||||
#include <zlib.h>
|
||||
#include <QByteArray>
|
||||
|
||||
bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes)
|
||||
bool GZip::unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes)
|
||||
{
|
||||
if (compressedBytes.size() == 0)
|
||||
{
|
||||
if (compressedBytes.size() == 0) {
|
||||
uncompressedBytes = compressedBytes;
|
||||
return true;
|
||||
}
|
||||
@ -51,42 +50,37 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
|
||||
|
||||
z_stream strm;
|
||||
memset(&strm, 0, sizeof(strm));
|
||||
strm.next_in = (Bytef *)compressedBytes.data();
|
||||
strm.next_in = (Bytef*)compressedBytes.data();
|
||||
strm.avail_in = compressedBytes.size();
|
||||
|
||||
bool done = false;
|
||||
|
||||
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK)
|
||||
{
|
||||
if (inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int err = Z_OK;
|
||||
|
||||
while (!done)
|
||||
{
|
||||
while (!done) {
|
||||
// If our output buffer is too small
|
||||
if (strm.total_out >= uncompLength)
|
||||
{
|
||||
if (strm.total_out >= uncompLength) {
|
||||
uncompressedBytes.resize(uncompLength * 2);
|
||||
uncompLength *= 2;
|
||||
}
|
||||
|
||||
strm.next_out = reinterpret_cast<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.
|
||||
err = inflate(&strm, Z_SYNC_FLUSH);
|
||||
if (err == Z_STREAM_END)
|
||||
done = true;
|
||||
else if (err != Z_OK)
|
||||
{
|
||||
else if (err != Z_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inflateEnd(&strm) != Z_OK || !done)
|
||||
{
|
||||
if (inflateEnd(&strm) != Z_OK || !done) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -94,10 +88,9 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
bool GZip::zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes)
|
||||
{
|
||||
if (uncompressedBytes.size() == 0)
|
||||
{
|
||||
if (uncompressedBytes.size() == 0) {
|
||||
compressedBytes = uncompressedBytes;
|
||||
return true;
|
||||
}
|
||||
@ -109,8 +102,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
z_stream zs;
|
||||
memset(&zs, 0, sizeof(zs));
|
||||
|
||||
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
{
|
||||
if (deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (16 + MAX_WBITS), 8, Z_DEFAULT_STRATEGY) != Z_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -122,11 +114,9 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
|
||||
unsigned offset = 0;
|
||||
unsigned temp = 0;
|
||||
do
|
||||
{
|
||||
do {
|
||||
auto remaining = compressedBytes.size() - offset;
|
||||
if(remaining < 1)
|
||||
{
|
||||
if (remaining < 1) {
|
||||
compressedBytes.resize(compressedBytes.size() * 2);
|
||||
}
|
||||
zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset));
|
||||
@ -137,13 +127,11 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
||||
|
||||
compressedBytes.resize(offset);
|
||||
|
||||
if (deflateEnd(&zs) != Z_OK)
|
||||
{
|
||||
if (deflateEnd(&zs) != Z_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ret != Z_STREAM_END)
|
||||
{
|
||||
if (ret != Z_STREAM_END) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -1,10 +1,8 @@
|
||||
#pragma once
|
||||
#include <QByteArray>
|
||||
|
||||
class GZip
|
||||
{
|
||||
public:
|
||||
static bool unzip(const QByteArray &compressedBytes, QByteArray &uncompressedBytes);
|
||||
static bool zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes);
|
||||
class GZip {
|
||||
public:
|
||||
static bool unzip(const QByteArray& compressedBytes, QByteArray& uncompressedBytes);
|
||||
static bool zip(const QByteArray& uncompressedBytes, QByteArray& compressedBytes);
|
||||
};
|
||||
|
||||
|
@ -6,17 +6,10 @@
|
||||
|
||||
bool InstanceCopyPrefs::allTrue() const
|
||||
{
|
||||
return copySaves &&
|
||||
keepPlaytime &&
|
||||
copyGameOptions &&
|
||||
copyResourcePacks &&
|
||||
copyShaderPacks &&
|
||||
copyServers &&
|
||||
copyMods &&
|
||||
copyScreenshots;
|
||||
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
|
||||
{
|
||||
@ -26,25 +19,30 @@ QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& addition
|
||||
{
|
||||
QStringList filters;
|
||||
|
||||
if(!copySaves)
|
||||
if (!copySaves)
|
||||
filters << "saves";
|
||||
|
||||
if(!copyGameOptions)
|
||||
if (!copyGameOptions)
|
||||
filters << "options.txt";
|
||||
|
||||
if(!copyResourcePacks)
|
||||
filters << "resourcepacks" << "texturepacks";
|
||||
if (!copyResourcePacks)
|
||||
filters << "resourcepacks"
|
||||
<< "texturepacks";
|
||||
|
||||
if(!copyShaderPacks)
|
||||
if (!copyShaderPacks)
|
||||
filters << "shaderpacks";
|
||||
|
||||
if(!copyServers)
|
||||
filters << "servers.dat" << "servers.dat_old" << "server-resource-packs";
|
||||
if (!copyServers)
|
||||
filters << "servers.dat"
|
||||
<< "servers.dat_old"
|
||||
<< "server-resource-packs";
|
||||
|
||||
if(!copyMods)
|
||||
filters << "coremods" << "mods" << "config";
|
||||
if (!copyMods)
|
||||
filters << "coremods"
|
||||
<< "mods"
|
||||
<< "config";
|
||||
|
||||
if(!copyScreenshots)
|
||||
if (!copyScreenshots)
|
||||
filters << "screenshots";
|
||||
|
||||
for (auto filter : additionalFilters) {
|
||||
|
@ -40,7 +40,7 @@ struct InstanceCopyPrefs {
|
||||
void enableDontLinkSaves(bool b);
|
||||
void enableUseClone(bool b);
|
||||
|
||||
protected: // data
|
||||
protected: // data
|
||||
bool copySaves = true;
|
||||
bool keepPlaytime = true;
|
||||
bool copyGameOptions = true;
|
||||
|
@ -156,8 +156,9 @@ void InstanceCopyTask::copyFinished()
|
||||
allowed_symlinks.append(m_origInstance->gameRoot().toUtf8());
|
||||
allowed_symlinks.append("\n");
|
||||
if (allowed_symlinks_file.isSymLink())
|
||||
FS::deletePath(allowed_symlinks_file
|
||||
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
||||
FS::deletePath(
|
||||
allowed_symlinks_file
|
||||
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
||||
|
||||
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
||||
}
|
||||
|
@ -11,19 +11,18 @@
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
class InstanceCopyTask : public InstanceTask
|
||||
{
|
||||
class InstanceCopyTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
void copyFinished();
|
||||
void copyAborted();
|
||||
|
||||
private:
|
||||
private:
|
||||
/* data */
|
||||
InstancePtr m_origInstance;
|
||||
QFuture<bool> m_copyFuture;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// 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 flowln <flowlnlnln@gmail.com>
|
||||
*
|
||||
@ -45,11 +45,14 @@
|
||||
#include "icons/IconList.h"
|
||||
#include "icons/IconUtils.h"
|
||||
|
||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
|
||||
#include "modplatform/flame/FlameInstanceCreationTask.h"
|
||||
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
|
||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
#include <QtConcurrentRun>
|
||||
#include <algorithm>
|
||||
@ -88,25 +91,27 @@ void InstanceImportTask::executeTask()
|
||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||
m_downloadRequired = true;
|
||||
|
||||
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.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||
|
||||
m_filesNetJob->start();
|
||||
downloadFromUrl();
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadFromUrl()
|
||||
{
|
||||
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||
entry->setStale(true);
|
||||
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeCached(m_sourceUrl, entry));
|
||||
m_archivePath = entry->getFullPath();
|
||||
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||
m_filesNetJob->start();
|
||||
}
|
||||
|
||||
void InstanceImportTask::downloadSucceeded()
|
||||
{
|
||||
processZipPack();
|
||||
@ -138,8 +143,7 @@ void InstanceImportTask::processZipPack()
|
||||
|
||||
// open the zip and find relevant files in it
|
||||
m_packZip.reset(new QuaZip(m_archivePath));
|
||||
if (!m_packZip->open(QuaZip::mdUnzip))
|
||||
{
|
||||
if (!m_packZip->open(QuaZip::mdUnzip)) {
|
||||
emitFailed(tr("Unable to open supplied modpack zip file."));
|
||||
return;
|
||||
}
|
||||
@ -153,44 +157,40 @@ void InstanceImportTask::processZipPack()
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (modrinthFound) {
|
||||
// process as Modrinth pack
|
||||
qDebug() << "Modrinth:" << modrinthFound;
|
||||
m_modpackType = ModpackType::Modrinth;
|
||||
}
|
||||
else if (technicFound)
|
||||
{
|
||||
} else if (technicFound) {
|
||||
// process as Technic pack
|
||||
qDebug() << "Technic:" << technicFound;
|
||||
extractDir.mkpath(".minecraft");
|
||||
extractDir.cd(".minecraft");
|
||||
m_modpackType = ModpackType::Technic;
|
||||
}
|
||||
else
|
||||
{
|
||||
QStringList paths_to_ignore { "overrides/" };
|
||||
} else {
|
||||
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()) {
|
||||
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore);
|
||||
!flameRoot.isNull()) {
|
||||
// process as Flame pack
|
||||
qDebug() << "Flame:" << flameRoot;
|
||||
root = flameRoot;
|
||||
m_modpackType = ModpackType::Flame;
|
||||
}
|
||||
}
|
||||
if(m_modpackType == ModpackType::Unknown)
|
||||
{
|
||||
if (m_modpackType == ModpackType::Unknown) {
|
||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure we extract just the pack
|
||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
||||
m_extractFuture =
|
||||
QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||
}
|
||||
@ -210,37 +210,28 @@ void InstanceImportTask::extractFinished()
|
||||
|
||||
qDebug() << "Fixing permissions for extracted pack files...";
|
||||
QDirIterator it(extractDir, QDirIterator::Subdirectories);
|
||||
while (it.hasNext())
|
||||
{
|
||||
while (it.hasNext()) {
|
||||
auto filepath = it.next();
|
||||
QFileInfo file(filepath);
|
||||
auto permissions = QFile::permissions(filepath);
|
||||
auto origPermissions = permissions;
|
||||
if(file.isDir())
|
||||
{
|
||||
if (file.isDir()) {
|
||||
// Folder +rwx for current user
|
||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// File +rw for current user
|
||||
permissions |= QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser;
|
||||
}
|
||||
if(origPermissions != permissions)
|
||||
{
|
||||
if(!QFile::setPermissions(filepath, permissions))
|
||||
{
|
||||
if (origPermissions != permissions) {
|
||||
if (!QFile::setPermissions(filepath, permissions)) {
|
||||
logWarning(tr("Could not fix permissions for %1").arg(filepath));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
qDebug() << "Fixed" << filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(m_modpackType)
|
||||
{
|
||||
switch (m_modpackType) {
|
||||
case ModpackType::MultiMC:
|
||||
processMultiMC();
|
||||
return;
|
||||
@ -276,7 +267,8 @@ void InstanceImportTask::processFlame()
|
||||
if (original_instance_id_it != m_extra_info.constEnd())
|
||||
original_instance_id = original_instance_id_it.value();
|
||||
|
||||
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
inst_creation_task =
|
||||
makeShared<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 = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
|
||||
@ -293,7 +285,7 @@ void InstanceImportTask::processFlame()
|
||||
});
|
||||
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||
|
||||
@ -362,7 +354,8 @@ void InstanceImportTask::processModrinth()
|
||||
if (original_instance_id_it != m_extra_info.constEnd())
|
||||
original_instance_id = original_instance_id_it.value();
|
||||
|
||||
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||
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()) {
|
||||
@ -385,7 +378,7 @@ void InstanceImportTask::processModrinth()
|
||||
});
|
||||
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
||||
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propagateStepProgress);
|
||||
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
||||
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
|
||||
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -35,54 +35,49 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "InstanceTask.h"
|
||||
#include "net/NetJob.h"
|
||||
#include <QUrl>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include "settings/SettingsObject.h"
|
||||
#include <QUrl>
|
||||
#include "InstanceTask.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "modplatform/flame/PackManifest.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
class QuaZip;
|
||||
namespace Flame
|
||||
{
|
||||
class FileResolvingTask;
|
||||
namespace Flame {
|
||||
class FileResolvingTask;
|
||||
}
|
||||
|
||||
class InstanceImportTask : public InstanceTask
|
||||
{
|
||||
class InstanceImportTask : public InstanceTask {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||
|
||||
bool abort() override;
|
||||
const QVector<Flame::File> &getBlockedFiles() const
|
||||
{
|
||||
return m_blockedMods;
|
||||
}
|
||||
const QVector<Flame::File>& getBlockedFiles() const { return m_blockedMods; }
|
||||
|
||||
protected:
|
||||
protected:
|
||||
//! Entry point for tasks.
|
||||
virtual void executeTask() override;
|
||||
|
||||
private:
|
||||
private:
|
||||
void processZipPack();
|
||||
void processMultiMC();
|
||||
void processTechnic();
|
||||
void processFlame();
|
||||
void processModrinth();
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void downloadSucceeded();
|
||||
void downloadFailed(QString reason);
|
||||
void downloadProgressChanged(qint64 current, qint64 total);
|
||||
void downloadAborted();
|
||||
void extractFinished();
|
||||
|
||||
private: /* data */
|
||||
private: /* data */
|
||||
NetJob::Ptr m_filesNetJob;
|
||||
shared_qobject_ptr<Flame::FileResolvingTask> m_modIdResolver;
|
||||
QUrl m_sourceUrl;
|
||||
@ -92,7 +87,7 @@ private: /* data */
|
||||
QFuture<std::optional<QStringList>> m_extractFuture;
|
||||
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||
QVector<Flame::File> m_blockedMods;
|
||||
enum class ModpackType{
|
||||
enum class ModpackType {
|
||||
Unknown,
|
||||
MultiMC,
|
||||
Technic,
|
||||
@ -104,6 +99,7 @@ private: /* data */
|
||||
// the source URL / the resource it points to alone.
|
||||
QMap<QString, QString> m_extra_info;
|
||||
|
||||
//FIXME: nuke
|
||||
// FIXME: nuke
|
||||
QWidget* m_parent;
|
||||
void downloadFromUrl();
|
||||
};
|
||||
|
@ -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) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -41,9 +42,9 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QMimeData>
|
||||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include <QStack>
|
||||
#include <QPair>
|
||||
#include <QTextStream>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
@ -96,7 +97,11 @@ Qt::DropActions InstanceList::supportedDropActions() const
|
||||
return Qt::MoveAction;
|
||||
}
|
||||
|
||||
bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
|
||||
bool InstanceList::canDropMimeData(const QMimeData* data,
|
||||
[[maybe_unused]] Qt::DropAction action,
|
||||
[[maybe_unused]] int row,
|
||||
[[maybe_unused]] int column,
|
||||
[[maybe_unused]] const QModelIndex& parent) const
|
||||
{
|
||||
if (data && data->hasFormat("application/x-instanceid")) {
|
||||
return true;
|
||||
@ -104,7 +109,11 @@ bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
|
||||
bool InstanceList::dropMimeData(const QMimeData* data,
|
||||
[[maybe_unused]] Qt::DropAction action,
|
||||
[[maybe_unused]] int row,
|
||||
[[maybe_unused]] int column,
|
||||
[[maybe_unused]] const QModelIndex& parent)
|
||||
{
|
||||
if (data && data->hasFormat("application/x-instanceid")) {
|
||||
return true;
|
||||
@ -129,7 +138,7 @@ QMimeData* InstanceList::mimeData(const QModelIndexList& indexes) const
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
QStringList InstanceList::getLinkedInstancesById(const QString &id) const
|
||||
QStringList InstanceList::getLinkedInstancesById(const QString& id) const
|
||||
{
|
||||
QStringList linkedInstances;
|
||||
for (auto inst : m_instances) {
|
||||
@ -158,42 +167,34 @@ QVariant InstanceList::data(const QModelIndex& index, int role) const
|
||||
if (!index.isValid()) {
|
||||
return QVariant();
|
||||
}
|
||||
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
|
||||
switch (role)
|
||||
{
|
||||
case InstancePointerRole:
|
||||
{
|
||||
QVariant v = QVariant::fromValue((void *)pdata);
|
||||
return v;
|
||||
}
|
||||
case InstanceIDRole:
|
||||
{
|
||||
return pdata->id();
|
||||
}
|
||||
case Qt::EditRole:
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
return pdata->name();
|
||||
}
|
||||
case Qt::AccessibleTextRole:
|
||||
{
|
||||
return tr("%1 Instance").arg(pdata->name());
|
||||
}
|
||||
case Qt::ToolTipRole:
|
||||
{
|
||||
return pdata->instanceRoot();
|
||||
}
|
||||
case Qt::DecorationRole:
|
||||
{
|
||||
return pdata->iconKey();
|
||||
}
|
||||
// HACK: see InstanceView.h in gui!
|
||||
case GroupRole:
|
||||
{
|
||||
return getInstanceGroup(pdata->id());
|
||||
}
|
||||
default:
|
||||
break;
|
||||
BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer());
|
||||
switch (role) {
|
||||
case InstancePointerRole: {
|
||||
QVariant v = QVariant::fromValue((void*)pdata);
|
||||
return v;
|
||||
}
|
||||
case InstanceIDRole: {
|
||||
return pdata->id();
|
||||
}
|
||||
case Qt::EditRole:
|
||||
case Qt::DisplayRole: {
|
||||
return pdata->name();
|
||||
}
|
||||
case Qt::AccessibleTextRole: {
|
||||
return tr("%1 Instance").arg(pdata->name());
|
||||
}
|
||||
case Qt::ToolTipRole: {
|
||||
return pdata->instanceRoot();
|
||||
}
|
||||
case Qt::DecorationRole: {
|
||||
return pdata->iconKey();
|
||||
}
|
||||
// HACK: see InstanceView.h in gui!
|
||||
case GroupRole: {
|
||||
return getInstanceGroup(pdata->id());
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
@ -237,8 +238,11 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
||||
return GroupId();
|
||||
}
|
||||
|
||||
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
||||
void InstanceList::setInstanceGroup(const InstanceId& id, GroupId name)
|
||||
{
|
||||
if (name.isEmpty() && !name.isNull())
|
||||
name = QString();
|
||||
|
||||
auto inst = getInstanceById(id);
|
||||
if (!inst) {
|
||||
qDebug() << "Attempt to set a null instance's group";
|
||||
@ -249,6 +253,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
||||
auto iter = m_instanceGroupIndex.find(inst->id());
|
||||
if (iter != m_instanceGroupIndex.end()) {
|
||||
if (*iter != name) {
|
||||
decreaseGroupCount(*iter);
|
||||
*iter = name;
|
||||
changed = true;
|
||||
}
|
||||
@ -258,7 +263,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
m_groupNameCache.insert(name);
|
||||
increaseGroupCount(name);
|
||||
auto idx = getInstIndex(inst.get());
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
saveGroupList();
|
||||
@ -267,29 +272,55 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
||||
|
||||
QStringList InstanceList::getGroups()
|
||||
{
|
||||
return m_groupNameCache.values();
|
||||
return m_groupNameCache.keys();
|
||||
}
|
||||
|
||||
void InstanceList::deleteGroup(const QString& name)
|
||||
void InstanceList::deleteGroup(const GroupId& name)
|
||||
{
|
||||
m_groupNameCache.remove(name);
|
||||
m_collapsedGroups.remove(name);
|
||||
|
||||
bool removed = false;
|
||||
qDebug() << "Delete group" << name;
|
||||
for (auto& instance : m_instances) {
|
||||
const auto& instID = instance->id();
|
||||
auto instGroupName = getInstanceGroup(instID);
|
||||
const QString& instID = instance->id();
|
||||
const QString instGroupName = getInstanceGroup(instID);
|
||||
if (instGroupName == name) {
|
||||
m_instanceGroupIndex.remove(instID);
|
||||
qDebug() << "Remove" << instID << "from group" << name;
|
||||
removed = true;
|
||||
auto idx = getInstIndex(instance.get());
|
||||
if (idx > 0) {
|
||||
if (idx >= 0)
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed) {
|
||||
if (removed)
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
void InstanceList::renameGroup(const QString& src, const QString& dst)
|
||||
{
|
||||
m_groupNameCache.remove(src);
|
||||
if (m_collapsedGroups.remove(src))
|
||||
m_collapsedGroups.insert(dst);
|
||||
|
||||
bool modified = false;
|
||||
qDebug() << "Rename group" << src << "to" << dst;
|
||||
for (auto& instance : m_instances) {
|
||||
const QString& instID = instance->id();
|
||||
const QString instGroupName = getInstanceGroup(instID);
|
||||
if (instGroupName == src) {
|
||||
m_instanceGroupIndex[instID] = dst;
|
||||
increaseGroupCount(dst);
|
||||
qDebug() << "Set" << instID << "group to" << dst;
|
||||
modified = true;
|
||||
auto idx = getInstIndex(instance.get());
|
||||
if (idx >= 0)
|
||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||
}
|
||||
}
|
||||
if (modified)
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
bool InstanceList::isGroupCollapsed(const QString& group)
|
||||
@ -305,12 +336,13 @@ bool InstanceList::trashInstance(const InstanceId& id)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cachedGroupId = m_instanceGroupIndex[id];
|
||||
QString cachedGroupId = m_instanceGroupIndex[id];
|
||||
|
||||
qDebug() << "Will trash instance" << id;
|
||||
QString trashedLoc;
|
||||
|
||||
if (m_instanceGroupIndex.remove(id)) {
|
||||
decreaseGroupCount(cachedGroupId);
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
@ -320,16 +352,18 @@ bool InstanceList::trashInstance(const InstanceId& id)
|
||||
}
|
||||
|
||||
qDebug() << "Instance" << id << "has been trashed by the launcher.";
|
||||
m_trashHistory.push({id, inst->instanceRoot(), trashedLoc, cachedGroupId});
|
||||
m_trashHistory.push({ id, inst->instanceRoot(), trashedLoc, cachedGroupId });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InstanceList::trashedSomething() {
|
||||
bool InstanceList::trashedSomething()
|
||||
{
|
||||
return !m_trashHistory.empty();
|
||||
}
|
||||
|
||||
void InstanceList::undoTrashInstance() {
|
||||
void InstanceList::undoTrashInstance()
|
||||
{
|
||||
if (m_trashHistory.empty()) {
|
||||
qWarning() << "Nothing to recover from trash.";
|
||||
return;
|
||||
@ -346,7 +380,7 @@ void InstanceList::undoTrashInstance() {
|
||||
QFile(top.trashPath).rename(top.polyPath);
|
||||
|
||||
m_instanceGroupIndex[top.id] = top.groupName;
|
||||
m_groupNameCache.insert(top.groupName);
|
||||
increaseGroupCount(top.groupName);
|
||||
|
||||
saveGroupList();
|
||||
emit instancesChanged();
|
||||
@ -360,7 +394,10 @@ void InstanceList::deleteInstance(const InstanceId& id)
|
||||
return;
|
||||
}
|
||||
|
||||
QString cachedGroupId = m_instanceGroupIndex[id];
|
||||
|
||||
if (m_instanceGroupIndex.remove(id)) {
|
||||
decreaseGroupCount(cachedGroupId);
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
@ -558,7 +595,7 @@ InstancePtr InstanceList::getInstanceByManagedName(const QString& managed_name)
|
||||
return {};
|
||||
}
|
||||
|
||||
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
|
||||
QModelIndex InstanceList::getInstanceIndexById(const QString& id) const
|
||||
{
|
||||
return index(getInstIndex(getInstanceById(id).get()));
|
||||
}
|
||||
@ -597,19 +634,36 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
||||
|
||||
QString inst_type = instanceSettings->get("InstanceType").toString();
|
||||
|
||||
// NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix instance
|
||||
if (inst_type == "OneSix" || inst_type.isEmpty())
|
||||
{
|
||||
// NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix
|
||||
// instance
|
||||
if (inst_type == "OneSix" || inst_type.isEmpty()) {
|
||||
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||
}
|
||||
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
|
||||
return inst;
|
||||
}
|
||||
|
||||
void InstanceList::increaseGroupCount(const QString& group)
|
||||
{
|
||||
if (group.isEmpty())
|
||||
return;
|
||||
|
||||
++m_groupNameCache[group];
|
||||
}
|
||||
|
||||
void InstanceList::decreaseGroupCount(const QString& group)
|
||||
{
|
||||
if (group.isEmpty())
|
||||
return;
|
||||
|
||||
if (--m_groupNameCache[group] < 1) {
|
||||
m_groupNameCache.remove(group);
|
||||
m_collapsedGroups.remove(group);
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceList::saveGroupList()
|
||||
{
|
||||
qDebug() << "Will save group list now.";
|
||||
@ -621,7 +675,7 @@ void InstanceList::saveGroupList()
|
||||
QString groupFileName = m_instDir + "/instgroups.json";
|
||||
QMap<QString, QSet<QString>> reverseGroupMap;
|
||||
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
|
||||
QString id = iter.key();
|
||||
const QString& id = iter.key();
|
||||
QString group = iter.value();
|
||||
if (group.isEmpty())
|
||||
continue;
|
||||
@ -711,17 +765,22 @@ void InstanceList::loadGroupList()
|
||||
return;
|
||||
}
|
||||
|
||||
QSet<QString> groupSet;
|
||||
m_instanceGroupIndex.clear();
|
||||
m_groupNameCache.clear();
|
||||
|
||||
// Iterate through all the groups.
|
||||
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
||||
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
|
||||
QString groupName = iter.key();
|
||||
|
||||
if (iter.key().isEmpty()) {
|
||||
qWarning() << "Redundant empty group found";
|
||||
continue;
|
||||
}
|
||||
|
||||
// If not an object, complain and skip to the next one.
|
||||
if (!iter.value().isObject()) {
|
||||
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8();
|
||||
qWarning() << QString("Group '%1' in the group list should be an object").arg(groupName).toUtf8();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -733,23 +792,19 @@ void InstanceList::loadGroupList()
|
||||
continue;
|
||||
}
|
||||
|
||||
// keep a list/set of groups for choosing
|
||||
groupSet.insert(groupName);
|
||||
|
||||
auto hidden = groupObj.value("hidden").toBool(false);
|
||||
if (hidden) {
|
||||
if (hidden)
|
||||
m_collapsedGroups.insert(groupName);
|
||||
}
|
||||
|
||||
// Iterate through the list of instances in the group.
|
||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||
|
||||
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) {
|
||||
m_instanceGroupIndex[(*iter2).toString()] = groupName;
|
||||
for (auto value : instancesArray) {
|
||||
m_instanceGroupIndex[value.toString()] = groupName;
|
||||
increaseGroupCount(groupName);
|
||||
}
|
||||
}
|
||||
m_groupsLoaded = true;
|
||||
m_groupNameCache.unite(groupSet);
|
||||
qDebug() << "Group list loaded.";
|
||||
}
|
||||
|
||||
@ -759,7 +814,7 @@ void InstanceList::instanceDirContentsChanged(const QString& path)
|
||||
emit instancesChanged();
|
||||
}
|
||||
|
||||
void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value)
|
||||
void InstanceList::on_InstFolderChanged([[maybe_unused]] const Setting& setting, QVariant value)
|
||||
{
|
||||
QString newInstDir = QDir(value.toString()).canonicalPath();
|
||||
if (newInstDir != m_instDir) {
|
||||
@ -787,20 +842,25 @@ class InstanceStaging : public Task {
|
||||
Q_OBJECT
|
||||
const unsigned minBackoff = 1;
|
||||
const unsigned maxBackoff = 16;
|
||||
|
||||
public:
|
||||
InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName)
|
||||
: m_parent(parent), backoff(minBackoff, maxBackoff), m_stagingPath(std::move(stagingPath)), m_instance_name(std::move(instanceName)), m_groupName(std::move(groupName))
|
||||
: m_parent(parent)
|
||||
, backoff(minBackoff, maxBackoff)
|
||||
, m_stagingPath(std::move(stagingPath))
|
||||
, m_instance_name(std::move(instanceName))
|
||||
, m_groupName(std::move(groupName))
|
||||
{
|
||||
m_child.reset(child);
|
||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded);
|
||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceeded);
|
||||
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
||||
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
||||
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
|
||||
connect(child, &Task::status, this, &InstanceStaging::setStatus);
|
||||
connect(child, &Task::details, this, &InstanceStaging::setDetails);
|
||||
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
|
||||
connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress);
|
||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
|
||||
connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress);
|
||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded);
|
||||
}
|
||||
|
||||
virtual ~InstanceStaging(){};
|
||||
@ -815,21 +875,17 @@ class InstanceStaging : public Task {
|
||||
|
||||
return Task::abort();
|
||||
}
|
||||
bool canAbort() const override
|
||||
{
|
||||
return (m_child && m_child->canAbort());
|
||||
}
|
||||
bool canAbort() const override { return (m_child && m_child->canAbort()); }
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override { m_child->start(); }
|
||||
QStringList warnings() const override { return m_child->warnings(); }
|
||||
|
||||
private slots:
|
||||
void childSucceded()
|
||||
void childSucceeded()
|
||||
{
|
||||
unsigned sleepTime = backoff();
|
||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get()))
|
||||
{
|
||||
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get())) {
|
||||
emitSucceeded();
|
||||
return;
|
||||
}
|
||||
@ -847,13 +903,10 @@ class InstanceStaging : public Task {
|
||||
emitFailed(reason);
|
||||
}
|
||||
|
||||
void childAborted()
|
||||
{
|
||||
emitAborted();
|
||||
}
|
||||
void childAborted() { emitAborted(); }
|
||||
|
||||
private:
|
||||
InstanceList * m_parent;
|
||||
private:
|
||||
InstanceList* m_parent;
|
||||
/*
|
||||
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
|
||||
* Basically, it starts messing things up while the launcher is extracting/creating instances
|
||||
@ -892,8 +945,14 @@ QString InstanceList::getStagedInstancePath()
|
||||
return path;
|
||||
}
|
||||
|
||||
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, InstanceTask const& commiting)
|
||||
bool InstanceList::commitStagedInstance(const QString& path,
|
||||
InstanceName const& instanceName,
|
||||
QString groupName,
|
||||
InstanceTask const& commiting)
|
||||
{
|
||||
if (groupName.isEmpty() && !groupName.isNull())
|
||||
groupName = QString();
|
||||
|
||||
QDir dir;
|
||||
QString instID;
|
||||
InstancePtr inst;
|
||||
@ -924,7 +983,7 @@ bool InstanceList::commitStagedInstance(const QString& path, InstanceName const&
|
||||
}
|
||||
|
||||
m_instanceGroupIndex[instID] = groupName;
|
||||
m_groupNameCache.insert(groupName);
|
||||
increaseGroupCount(groupName);
|
||||
}
|
||||
|
||||
instanceSet.insert(instID);
|
||||
|
@ -1,26 +1,46 @@
|
||||
/* Copyright 2013-2021 MultiMC Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* 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 <QObject>
|
||||
#include <QAbstractListModel>
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
#include <QStack>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include <QStack>
|
||||
|
||||
#include "BaseInstance.h"
|
||||
|
||||
@ -32,21 +52,9 @@ using InstanceId = QString;
|
||||
using GroupId = QString;
|
||||
using InstanceLocator = std::pair<InstancePtr, int>;
|
||||
|
||||
enum class InstCreateError
|
||||
{
|
||||
NoCreateError = 0,
|
||||
NoSuchVersion,
|
||||
UnknownCreateError,
|
||||
InstExists,
|
||||
CantCreateDir
|
||||
};
|
||||
enum class InstCreateError { NoCreateError = 0, NoSuchVersion, UnknownCreateError, InstExists, CantCreateDir };
|
||||
|
||||
enum class GroupsState
|
||||
{
|
||||
NotLoaded,
|
||||
Steady,
|
||||
Dirty
|
||||
};
|
||||
enum class GroupsState { NotLoaded, Steady, Dirty };
|
||||
|
||||
struct TrashHistoryItem {
|
||||
QString id;
|
||||
@ -55,48 +63,36 @@ struct TrashHistoryItem {
|
||||
QString groupName;
|
||||
};
|
||||
|
||||
class InstanceList : public QAbstractListModel
|
||||
{
|
||||
class InstanceList : public QAbstractListModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InstanceList(SettingsObjectPtr settings, const QString & instDir, QObject *parent = 0);
|
||||
public:
|
||||
explicit InstanceList(SettingsObjectPtr settings, const QString& instDir, QObject* parent = 0);
|
||||
virtual ~InstanceList();
|
||||
|
||||
public:
|
||||
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
public:
|
||||
QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
bool setData(const QModelIndex & index, const QVariant & value, int role) override;
|
||||
bool setData(const QModelIndex& index, const QVariant& value, int role) override;
|
||||
|
||||
enum AdditionalRoles
|
||||
{
|
||||
enum AdditionalRoles {
|
||||
GroupRole = Qt::UserRole,
|
||||
InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
|
||||
InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
|
||||
InstancePointerRole = 0x34B1CB48, ///< Return pointer to real instance
|
||||
InstanceIDRole = 0x34B1CB49 ///< Return id if the instance
|
||||
};
|
||||
/*!
|
||||
* \brief Error codes returned by functions in the InstanceList class.
|
||||
* NoError Indicates that no error occurred.
|
||||
* UnknownError indicates that an unspecified error occurred.
|
||||
*/
|
||||
enum InstListError
|
||||
{
|
||||
NoError = 0,
|
||||
UnknownError
|
||||
};
|
||||
enum InstListError { NoError = 0, UnknownError };
|
||||
|
||||
InstancePtr at(int i) const
|
||||
{
|
||||
return m_instances.at(i);
|
||||
}
|
||||
InstancePtr at(int i) const { return m_instances.at(i); }
|
||||
|
||||
int count() const
|
||||
{
|
||||
return m_instances.count();
|
||||
}
|
||||
int count() const { return m_instances.count(); }
|
||||
|
||||
InstListError loadList();
|
||||
void saveNow();
|
||||
@ -105,21 +101,22 @@ public:
|
||||
InstancePtr getInstanceById(QString id) const;
|
||||
/* O(n) */
|
||||
InstancePtr getInstanceByManagedName(const QString& managed_name) const;
|
||||
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||
QModelIndex getInstanceIndexById(const QString& id) const;
|
||||
QStringList getGroups();
|
||||
bool isGroupCollapsed(const QString &groupName);
|
||||
bool isGroupCollapsed(const QString& groupName);
|
||||
|
||||
GroupId getInstanceGroup(const InstanceId & id) const;
|
||||
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
||||
GroupId getInstanceGroup(const InstanceId& id) const;
|
||||
void setInstanceGroup(const InstanceId& id, GroupId name);
|
||||
|
||||
void deleteGroup(const GroupId & name);
|
||||
bool trashInstance(const InstanceId &id);
|
||||
void deleteGroup(const GroupId& name);
|
||||
void renameGroup(const GroupId& src, const GroupId& dst);
|
||||
bool trashInstance(const InstanceId& id);
|
||||
bool trashedSomething();
|
||||
void undoTrashInstance();
|
||||
void deleteInstance(const InstanceId & id);
|
||||
void deleteInstance(const InstanceId& id);
|
||||
|
||||
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
||||
Task * wrapInstanceTask(InstanceTask * task);
|
||||
Task* wrapInstanceTask(InstanceTask* task);
|
||||
|
||||
/**
|
||||
* Create a new empty staging area for instance creation and @return a path/key top commit it later.
|
||||
@ -133,13 +130,13 @@ public:
|
||||
* 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 InstanceName& instanceName, const QString& groupName, const InstanceTask&);
|
||||
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, QString groupName, const InstanceTask&);
|
||||
|
||||
/**
|
||||
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
||||
* Used by instance manipulation tasks.
|
||||
*/
|
||||
bool destroyStagingPath(const QString & keyPath);
|
||||
bool destroyStagingPath(const QString& keyPath);
|
||||
|
||||
int getTotalPlayTime();
|
||||
|
||||
@ -147,51 +144,55 @@ public:
|
||||
|
||||
Qt::DropActions supportedDropActions() const override;
|
||||
|
||||
bool canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const override;
|
||||
bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override;
|
||||
|
||||
bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override;
|
||||
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override;
|
||||
|
||||
QStringList mimeTypes() const override;
|
||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||
QMimeData* mimeData(const QModelIndexList& indexes) const override;
|
||||
|
||||
QStringList getLinkedInstancesById(const QString &id) const;
|
||||
QStringList getLinkedInstancesById(const QString& id) const;
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void dataIsInvalid();
|
||||
void instancesChanged();
|
||||
void instanceSelectRequest(QString instanceId);
|
||||
void groupsChanged(QSet<QString> groups);
|
||||
|
||||
public slots:
|
||||
void on_InstFolderChanged(const Setting &setting, QVariant value);
|
||||
void on_GroupStateChanged(const QString &group, bool collapsed);
|
||||
public slots:
|
||||
void on_InstFolderChanged(const Setting& setting, QVariant value);
|
||||
void on_GroupStateChanged(const QString& group, bool collapsed);
|
||||
|
||||
private slots:
|
||||
void propertiesChanged(BaseInstance *inst);
|
||||
private slots:
|
||||
void propertiesChanged(BaseInstance* inst);
|
||||
void providerUpdated();
|
||||
void instanceDirContentsChanged(const QString &path);
|
||||
void instanceDirContentsChanged(const QString& path);
|
||||
|
||||
private:
|
||||
int getInstIndex(BaseInstance *inst) const;
|
||||
private:
|
||||
int getInstIndex(BaseInstance* inst) const;
|
||||
void updateTotalPlayTime();
|
||||
void suspendWatch();
|
||||
void resumeWatch();
|
||||
void add(const QList<InstancePtr> &list);
|
||||
void add(const QList<InstancePtr>& list);
|
||||
void loadGroupList();
|
||||
void saveGroupList();
|
||||
QList<InstanceId> discoverInstances();
|
||||
InstancePtr loadInstance(const InstanceId& id);
|
||||
|
||||
private:
|
||||
void increaseGroupCount(const QString& group);
|
||||
void decreaseGroupCount(const QString& group);
|
||||
|
||||
private:
|
||||
int m_watchLevel = 0;
|
||||
int totalPlayTime = 0;
|
||||
bool m_dirty = false;
|
||||
QList<InstancePtr> m_instances;
|
||||
QSet<QString> m_groupNameCache;
|
||||
// id -> refs
|
||||
QMap<QString, int> m_groupNameCache;
|
||||
|
||||
SettingsObjectPtr m_globalSettings;
|
||||
QString m_instDir;
|
||||
QFileSystemWatcher * m_watcher;
|
||||
QFileSystemWatcher* m_watcher;
|
||||
// FIXME: this is so inefficient that looking at it is almost painful.
|
||||
QSet<QString> m_collapsedGroups;
|
||||
QMap<InstanceId, GroupId> m_instanceGroupIndex;
|
||||
|
@ -1,36 +1,31 @@
|
||||
#pragma once
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include <FileSystem.h>
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "ui/pages/BasePage.h"
|
||||
#include "ui/pages/BasePageProvider.h"
|
||||
#include "ui/pages/instance/InstanceSettingsPage.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"
|
||||
#include "ui/pages/instance/ShaderPackPage.h"
|
||||
#include "ui/pages/instance/NotesPage.h"
|
||||
#include "ui/pages/instance/ScreenshotsPage.h"
|
||||
#include "ui/pages/instance/InstanceSettingsPage.h"
|
||||
#include "ui/pages/instance/OtherLogsPage.h"
|
||||
#include "ui/pages/instance/WorldListPage.h"
|
||||
#include "ui/pages/instance/ResourcePackPage.h"
|
||||
#include "ui/pages/instance/ScreenshotsPage.h"
|
||||
#include "ui/pages/instance/ServersPage.h"
|
||||
#include "ui/pages/instance/GameOptionsPage.h"
|
||||
#include "ui/pages/instance/ShaderPackPage.h"
|
||||
#include "ui/pages/instance/TexturePackPage.h"
|
||||
#include "ui/pages/instance/VersionPage.h"
|
||||
#include "ui/pages/instance/WorldListPage.h"
|
||||
|
||||
class InstancePageProvider : public QObject, public BasePageProvider
|
||||
{
|
||||
class InstancePageProvider : protected QObject, public BasePageProvider {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstancePageProvider(InstancePtr parent)
|
||||
{
|
||||
inst = parent;
|
||||
}
|
||||
public:
|
||||
explicit InstancePageProvider(InstancePtr parent) { inst = parent; }
|
||||
|
||||
virtual ~InstancePageProvider() {};
|
||||
virtual QList<BasePage *> getPages() override
|
||||
virtual ~InstancePageProvider(){};
|
||||
virtual QList<BasePage*> getPages() override
|
||||
{
|
||||
QList<BasePage *> values;
|
||||
QList<BasePage*> values;
|
||||
values.append(new LogPage(inst));
|
||||
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
||||
values.append(new VersionPage(onesix.get()));
|
||||
@ -50,18 +45,14 @@ public:
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->gameRoot(), "screenshots")));
|
||||
values.append(new InstanceSettingsPage(onesix.get()));
|
||||
auto logMatcher = inst->getLogFileMatcher();
|
||||
if(logMatcher)
|
||||
{
|
||||
if (logMatcher) {
|
||||
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
virtual QString dialogTitle() override
|
||||
{
|
||||
return tr("Edit Instance (%1)").arg(inst->name());
|
||||
}
|
||||
protected:
|
||||
virtual QString dialogTitle() override { return tr("Edit Instance (%1)").arg(inst->name()); }
|
||||
|
||||
protected:
|
||||
InstancePtr inst;
|
||||
};
|
||||
|
||||
|
@ -18,13 +18,14 @@ InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& ol
|
||||
return InstanceNameChange::ShouldKeep;
|
||||
}
|
||||
|
||||
ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name)
|
||||
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).")
|
||||
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"));
|
||||
@ -38,7 +39,6 @@ ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name)
|
||||
if (info->clickedButton() == info->button(QMessageBox::Abort))
|
||||
return ShouldUpdate::SkipUpdating;
|
||||
return ShouldUpdate::Cancel;
|
||||
|
||||
}
|
||||
|
||||
QString InstanceName::name() const
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -39,43 +39,39 @@
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget* parent)
|
||||
{
|
||||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]"))
|
||||
|| jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize"))
|
||||
{
|
||||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]")) || jvmargs.contains("-XX-MaxHeapSize") ||
|
||||
jvmargs.contains("-XX:InitialHeapSize")) {
|
||||
auto warnStr = QObject::tr(
|
||||
"You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" or \"-Xms\").\n"
|
||||
"You tried to manually set a JVM memory option (using \"-XX:PermSize\", \"-XX-MaxHeapSize\", \"-XX:InitialHeapSize\", \"-Xmx\" "
|
||||
"or \"-Xms\").\n"
|
||||
"There are dedicated boxes for these in the settings (Java tab, in the Memory group at the top).\n"
|
||||
"This message will be displayed until you remove them from the JVM arguments.");
|
||||
CustomMessageBox::selectable(
|
||||
parent, QObject::tr("JVM arguments warning"),
|
||||
warnStr,
|
||||
QMessageBox::Warning)->exec();
|
||||
CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec();
|
||||
return false;
|
||||
}
|
||||
// block lunacy with passing required version to the JVM
|
||||
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"
|
||||
"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.");
|
||||
CustomMessageBox::selectable(
|
||||
parent, QObject::tr("JVM arguments warning"),
|
||||
warnStr,
|
||||
QMessageBox::Warning)->exec();
|
||||
CustomMessageBox::selectable(parent, QObject::tr("JVM arguments warning"), warnStr, QMessageBox::Warning)->exec();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result)
|
||||
void JavaCommon::javaWasOk(QWidget* parent, const JavaCheckResult& result)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr("Java test succeeded!<br />Platform reported: %1<br />Java version "
|
||||
"reported: %2<br />Java vendor "
|
||||
"reported: %3<br />").arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor);
|
||||
if (result.errorLog.size())
|
||||
{
|
||||
text += QObject::tr(
|
||||
"Java test succeeded!<br />Platform reported: %1<br />Java version "
|
||||
"reported: %2<br />Java vendor "
|
||||
"reported: %3<br />")
|
||||
.arg(result.realPlatform, result.javaVersion.toString(), result.javaVendor);
|
||||
if (result.errorLog.size()) {
|
||||
auto htmlError = result.errorLog;
|
||||
htmlError.replace('\n', "<br />");
|
||||
text += QObject::tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").arg(htmlError);
|
||||
@ -83,7 +79,7 @@ void JavaCommon::javaWasOk(QWidget *parent, JavaCheckResult result)
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test success"), text, QMessageBox::Information)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::javaArgsWereBad(QWidget *parent, JavaCheckResult result)
|
||||
void JavaCommon::javaArgsWereBad(QWidget* parent, const JavaCheckResult& result)
|
||||
{
|
||||
auto htmlError = result.errorLog;
|
||||
QString text;
|
||||
@ -93,7 +89,7 @@ void JavaCommon::javaArgsWereBad(QWidget *parent, JavaCheckResult result)
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result)
|
||||
void JavaCommon::javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr(
|
||||
@ -102,7 +98,7 @@ void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result)
|
||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::javaCheckNotFound(QWidget *parent)
|
||||
void JavaCommon::javaCheckNotFound(QWidget* parent)
|
||||
{
|
||||
QString text;
|
||||
text += QObject::tr("Java checker library could not be found. Please check your installation.");
|
||||
@ -111,8 +107,7 @@ void JavaCommon::javaCheckNotFound(QWidget *parent)
|
||||
|
||||
void JavaCommon::TestCheck::run()
|
||||
{
|
||||
if (!JavaCommon::checkJVMArgs(m_args, m_parent))
|
||||
{
|
||||
if (!JavaCommon::checkJVMArgs(m_args, m_parent)) {
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
@ -129,8 +124,7 @@ void JavaCommon::TestCheck::run()
|
||||
|
||||
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
||||
{
|
||||
if (result.validity != JavaCheckResult::Validity::Valid)
|
||||
{
|
||||
if (result.validity != JavaCheckResult::Validity::Valid) {
|
||||
javaBinaryWasBad(m_parent, result);
|
||||
emit finished();
|
||||
return;
|
||||
@ -141,8 +135,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
||||
checker->m_args = m_args;
|
||||
checker->m_minMem = m_minMem;
|
||||
checker->m_maxMem = m_maxMem;
|
||||
if (result.javaVersion.requiresPermGen())
|
||||
{
|
||||
if (result.javaVersion.requiresPermGen()) {
|
||||
checker->m_permGen = m_permGen;
|
||||
}
|
||||
checker->performCheck();
|
||||
@ -150,8 +143,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
||||
|
||||
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
||||
{
|
||||
if (result.validity == JavaCheckResult::Validity::Valid)
|
||||
{
|
||||
if (result.validity == JavaCheckResult::Validity::Valid) {
|
||||
javaWasOk(m_parent, result);
|
||||
emit finished();
|
||||
return;
|
||||
@ -159,4 +151,3 @@ void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
||||
javaArgsWereBad(m_parent, result);
|
||||
emit finished();
|
||||
}
|
||||
|
||||
|
@ -6,45 +6,42 @@ class QWidget;
|
||||
/**
|
||||
* Common UI bits for the java pages to use.
|
||||
*/
|
||||
namespace JavaCommon
|
||||
{
|
||||
bool checkJVMArgs(QString args, QWidget *parent);
|
||||
namespace JavaCommon {
|
||||
bool checkJVMArgs(QString args, QWidget* parent);
|
||||
|
||||
// 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);
|
||||
// Show a dialog saying that the Java binary was usable
|
||||
void javaWasOk(QWidget* parent, const JavaCheckResult& result);
|
||||
// Show a dialog saying that the Java binary was not usable because of bad options
|
||||
void javaArgsWereBad(QWidget* parent, const JavaCheckResult& result);
|
||||
// Show a dialog saying that the Java binary was not usable
|
||||
void javaBinaryWasBad(QWidget* parent, const JavaCheckResult& result);
|
||||
// Show a dialog if we couldn't find Java Checker
|
||||
void javaCheckNotFound(QWidget* parent);
|
||||
|
||||
class TestCheck : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestCheck(QWidget *parent, QString path, QString args, int minMem, int maxMem, int permGen)
|
||||
:m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
|
||||
{
|
||||
}
|
||||
virtual ~TestCheck() {};
|
||||
class TestCheck : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestCheck(QWidget* parent, QString path, QString args, int minMem, int maxMem, int permGen)
|
||||
: m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
|
||||
{}
|
||||
virtual ~TestCheck(){};
|
||||
|
||||
void run();
|
||||
void run();
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private slots:
|
||||
void checkFinished(JavaCheckResult result);
|
||||
void checkFinishedWithArgs(JavaCheckResult result);
|
||||
private slots:
|
||||
void checkFinished(JavaCheckResult result);
|
||||
void checkFinishedWithArgs(JavaCheckResult result);
|
||||
|
||||
private:
|
||||
std::shared_ptr<JavaChecker> checker;
|
||||
QWidget *m_parent = nullptr;
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
int m_minMem = 0;
|
||||
int m_maxMem = 0;
|
||||
int m_permGen = 64;
|
||||
};
|
||||
}
|
||||
private:
|
||||
std::shared_ptr<JavaChecker> checker;
|
||||
QWidget* m_parent = nullptr;
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
int m_minMem = 0;
|
||||
int m_maxMem = 0;
|
||||
int m_permGen = 64;
|
||||
};
|
||||
} // namespace JavaCommon
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -37,257 +37,246 @@
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include <math.h>
|
||||
#include "FileSystem.h"
|
||||
|
||||
namespace Json
|
||||
{
|
||||
void write(const QJsonDocument &doc, const QString &filename)
|
||||
namespace Json {
|
||||
void write(const QJsonDocument& doc, const QString& filename)
|
||||
{
|
||||
FS::write(filename, doc.toJson());
|
||||
}
|
||||
void write(const QJsonObject &object, const QString &filename)
|
||||
void write(const QJsonObject& object, const QString& filename)
|
||||
{
|
||||
write(QJsonDocument(object), filename);
|
||||
}
|
||||
void write(const QJsonArray &array, const QString &filename)
|
||||
void write(const QJsonArray& array, const QString& filename)
|
||||
{
|
||||
write(QJsonDocument(array), filename);
|
||||
}
|
||||
|
||||
QByteArray toText(const QJsonObject &obj)
|
||||
QByteArray toText(const QJsonObject& obj)
|
||||
{
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
QByteArray toText(const QJsonArray &array)
|
||||
QByteArray toText(const QJsonArray& array)
|
||||
{
|
||||
return QJsonDocument(array).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
|
||||
static bool isBinaryJson(const QByteArray &data)
|
||||
static bool isBinaryJson(const QByteArray& data)
|
||||
{
|
||||
decltype(QJsonDocument::BinaryFormatTag) tag = QJsonDocument::BinaryFormatTag;
|
||||
return memcmp(data.constData(), &tag, sizeof(QJsonDocument::BinaryFormatTag)) == 0;
|
||||
}
|
||||
QJsonDocument requireDocument(const QByteArray &data, const QString &what)
|
||||
QJsonDocument requireDocument(const QByteArray& data, const QString& what)
|
||||
{
|
||||
if (isBinaryJson(data))
|
||||
{
|
||||
if (isBinaryJson(data)) {
|
||||
// FIXME: Is this needed?
|
||||
throw JsonException(what + ": Invalid JSON. Binary JSON unsupported");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
QJsonParseError error;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &error);
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
{
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
throw JsonException(what + ": Error parsing JSON: " + error.errorString());
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
QJsonDocument requireDocument(const QString &filename, const QString &what)
|
||||
QJsonDocument requireDocument(const QString& filename, const QString& what)
|
||||
{
|
||||
return requireDocument(FS::read(filename), what);
|
||||
}
|
||||
QJsonObject requireObject(const QJsonDocument &doc, const QString &what)
|
||||
QJsonObject requireObject(const QJsonDocument& doc, const QString& what)
|
||||
{
|
||||
if (!doc.isObject())
|
||||
{
|
||||
if (!doc.isObject()) {
|
||||
throw JsonException(what + " is not an object");
|
||||
}
|
||||
return doc.object();
|
||||
}
|
||||
QJsonArray requireArray(const QJsonDocument &doc, const QString &what)
|
||||
QJsonArray requireArray(const QJsonDocument& doc, const QString& what)
|
||||
{
|
||||
if (!doc.isArray())
|
||||
{
|
||||
if (!doc.isArray()) {
|
||||
throw JsonException(what + " is not an array");
|
||||
}
|
||||
return doc.array();
|
||||
}
|
||||
|
||||
void writeString(QJsonObject &to, const QString &key, const QString &value)
|
||||
void writeString(QJsonObject& to, const QString& key, const QString& value)
|
||||
{
|
||||
if (!value.isEmpty())
|
||||
{
|
||||
if (!value.isEmpty()) {
|
||||
to.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
void writeStringList(QJsonObject &to, const QString &key, const QStringList &values)
|
||||
void writeStringList(QJsonObject& to, const QString& key, const QStringList& values)
|
||||
{
|
||||
if (!values.isEmpty())
|
||||
{
|
||||
if (!values.isEmpty()) {
|
||||
QJsonArray array;
|
||||
for(auto value: values)
|
||||
{
|
||||
for (auto value : values) {
|
||||
array.append(value);
|
||||
}
|
||||
to.insert(key, array);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
QJsonValue toJson<QUrl>(const QUrl &url)
|
||||
template <>
|
||||
QJsonValue toJson<QUrl>(const QUrl& url)
|
||||
{
|
||||
return QJsonValue(url.toString(QUrl::FullyEncoded));
|
||||
}
|
||||
template<>
|
||||
QJsonValue toJson<QByteArray>(const QByteArray &data)
|
||||
template <>
|
||||
QJsonValue toJson<QByteArray>(const QByteArray& data)
|
||||
{
|
||||
return QJsonValue(QString::fromLatin1(data.toHex()));
|
||||
}
|
||||
template<>
|
||||
QJsonValue toJson<QDateTime>(const QDateTime &datetime)
|
||||
template <>
|
||||
QJsonValue toJson<QDateTime>(const QDateTime& datetime)
|
||||
{
|
||||
return QJsonValue(datetime.toString(Qt::ISODate));
|
||||
}
|
||||
template<>
|
||||
QJsonValue toJson<QDir>(const QDir &dir)
|
||||
template <>
|
||||
QJsonValue toJson<QDir>(const QDir& dir)
|
||||
{
|
||||
return QDir::current().relativeFilePath(dir.absolutePath());
|
||||
}
|
||||
template<>
|
||||
QJsonValue toJson<QUuid>(const QUuid &uuid)
|
||||
template <>
|
||||
QJsonValue toJson<QUuid>(const QUuid& uuid)
|
||||
{
|
||||
return uuid.toString();
|
||||
}
|
||||
template<>
|
||||
QJsonValue toJson<QVariant>(const QVariant &variant)
|
||||
template <>
|
||||
QJsonValue toJson<QVariant>(const QVariant& variant)
|
||||
{
|
||||
return QJsonValue::fromVariant(variant);
|
||||
}
|
||||
|
||||
|
||||
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
const QString string = ensureIsType<QString>(value, what);
|
||||
// ensure that the string can be safely cast to Latin1
|
||||
if (string != QString::fromLatin1(string.toLatin1()))
|
||||
{
|
||||
if (string != QString::fromLatin1(string.toLatin1())) {
|
||||
throw JsonException(what + " is not encodable as Latin1");
|
||||
}
|
||||
return QByteArray::fromHex(string.toLatin1());
|
||||
}
|
||||
|
||||
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
if (!value.isArray())
|
||||
{
|
||||
if (!value.isArray()) {
|
||||
throw JsonException(what + " is not an array");
|
||||
}
|
||||
return value.toArray();
|
||||
}
|
||||
|
||||
|
||||
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QString requireIsType<QString>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
if (!value.isString())
|
||||
{
|
||||
if (!value.isString()) {
|
||||
throw JsonException(what + " is not a string");
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
bool requireIsType<bool>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
if (!value.isBool())
|
||||
{
|
||||
if (!value.isBool()) {
|
||||
throw JsonException(what + " is not a bool");
|
||||
}
|
||||
return value.toBool();
|
||||
}
|
||||
|
||||
template<> double requireIsType<double>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
double requireIsType<double>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
if (!value.isDouble())
|
||||
{
|
||||
if (!value.isDouble()) {
|
||||
throw JsonException(what + " is not a double");
|
||||
}
|
||||
return value.toDouble();
|
||||
}
|
||||
|
||||
template<> int requireIsType<int>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
int requireIsType<int>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
const double doubl = requireIsType<double>(value, what);
|
||||
if (fmod(doubl, 1) != 0)
|
||||
{
|
||||
if (fmod(doubl, 1) != 0) {
|
||||
throw JsonException(what + " is not an integer");
|
||||
}
|
||||
return int(doubl);
|
||||
}
|
||||
|
||||
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
const QString string = requireIsType<QString>(value, what);
|
||||
const QDateTime datetime = QDateTime::fromString(string, Qt::ISODate);
|
||||
if (!datetime.isValid())
|
||||
{
|
||||
if (!datetime.isValid()) {
|
||||
throw JsonException(what + " is not a ISO formatted date/time value");
|
||||
}
|
||||
return datetime;
|
||||
}
|
||||
|
||||
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
const QString string = ensureIsType<QString>(value, what);
|
||||
if (string.isEmpty())
|
||||
{
|
||||
if (string.isEmpty()) {
|
||||
return QUrl();
|
||||
}
|
||||
const QUrl url = QUrl(string, QUrl::StrictMode);
|
||||
if (!url.isValid())
|
||||
{
|
||||
if (!url.isValid()) {
|
||||
throw JsonException(what + " is not a correctly formatted URL");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QDir requireIsType<QDir>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
const QString string = requireIsType<QString>(value, what);
|
||||
// FIXME: does not handle invalid characters!
|
||||
return QDir::current().absoluteFilePath(string);
|
||||
}
|
||||
|
||||
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
const QString string = requireIsType<QString>(value, what);
|
||||
const QUuid uuid = QUuid(string);
|
||||
if (uuid.toString() != string) // converts back => valid
|
||||
if (uuid.toString() != string) // converts back => valid
|
||||
{
|
||||
throw JsonException(what + " is not a valid UUID");
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
if (!value.isObject())
|
||||
{
|
||||
if (!value.isObject()) {
|
||||
throw JsonException(what + " is not an object");
|
||||
}
|
||||
return value.toObject();
|
||||
}
|
||||
|
||||
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QVariant requireIsType<QVariant>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
if (value.isNull() || value.isUndefined())
|
||||
{
|
||||
if (value.isNull() || value.isUndefined()) {
|
||||
throw JsonException(what + " is null or undefined");
|
||||
}
|
||||
return value.toVariant();
|
||||
}
|
||||
|
||||
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what)
|
||||
template <>
|
||||
QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, const QString& what)
|
||||
{
|
||||
if (value.isNull() || value.isUndefined())
|
||||
{
|
||||
if (value.isNull() || value.isUndefined()) {
|
||||
throw JsonException(what + " is null or undefined");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Json
|
||||
|
204
launcher/Json.h
204
launcher/Json.h
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -35,74 +35,71 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QDateTime>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QUrl>
|
||||
#include <QUuid>
|
||||
#include <QVariant>
|
||||
#include <memory>
|
||||
|
||||
#include "Exception.h"
|
||||
|
||||
namespace Json
|
||||
{
|
||||
class JsonException : public ::Exception
|
||||
{
|
||||
public:
|
||||
JsonException(const QString &message) : Exception(message) {}
|
||||
namespace Json {
|
||||
class JsonException : public ::Exception {
|
||||
public:
|
||||
JsonException(const QString& message) : Exception(message) {}
|
||||
};
|
||||
|
||||
/// @throw FileSystemException
|
||||
void write(const QJsonDocument &doc, const QString &filename);
|
||||
void write(const QJsonDocument& doc, const QString& filename);
|
||||
/// @throw FileSystemException
|
||||
void write(const QJsonObject &object, const QString &filename);
|
||||
void write(const QJsonObject& object, const QString& filename);
|
||||
/// @throw FileSystemException
|
||||
void write(const QJsonArray &array, const QString &filename);
|
||||
void write(const QJsonArray& array, const QString& filename);
|
||||
|
||||
QByteArray toText(const QJsonObject &obj);
|
||||
QByteArray toText(const QJsonArray &array);
|
||||
QByteArray toText(const QJsonObject& obj);
|
||||
QByteArray toText(const QJsonArray& array);
|
||||
|
||||
/// @throw JsonException
|
||||
QJsonDocument requireDocument(const QByteArray &data, const QString &what = "Document");
|
||||
QJsonDocument requireDocument(const QByteArray& data, const QString& what = "Document");
|
||||
/// @throw JsonException
|
||||
QJsonDocument requireDocument(const QString &filename, const QString &what = "Document");
|
||||
QJsonDocument requireDocument(const QString& filename, const QString& what = "Document");
|
||||
/// @throw JsonException
|
||||
QJsonObject requireObject(const QJsonDocument &doc, const QString &what = "Document");
|
||||
QJsonObject requireObject(const QJsonDocument& doc, const QString& what = "Document");
|
||||
/// @throw JsonException
|
||||
QJsonArray requireArray(const QJsonDocument &doc, const QString &what = "Document");
|
||||
QJsonArray requireArray(const QJsonDocument& doc, const QString& what = "Document");
|
||||
|
||||
/////////////////// WRITING ////////////////////
|
||||
|
||||
void writeString(QJsonObject & to, const QString &key, const QString &value);
|
||||
void writeStringList(QJsonObject & to, const QString &key, const QStringList &values);
|
||||
void writeString(QJsonObject& to, const QString& key, const QString& value);
|
||||
void writeStringList(QJsonObject& to, const QString& key, const QStringList& values);
|
||||
|
||||
template<typename T>
|
||||
QJsonValue toJson(const T &t)
|
||||
template <typename T>
|
||||
QJsonValue toJson(const T& t)
|
||||
{
|
||||
return QJsonValue(t);
|
||||
}
|
||||
template<>
|
||||
QJsonValue toJson<QUrl>(const QUrl &url);
|
||||
template<>
|
||||
QJsonValue toJson<QByteArray>(const QByteArray &data);
|
||||
template<>
|
||||
QJsonValue toJson<QDateTime>(const QDateTime &datetime);
|
||||
template<>
|
||||
QJsonValue toJson<QDir>(const QDir &dir);
|
||||
template<>
|
||||
QJsonValue toJson<QUuid>(const QUuid &uuid);
|
||||
template<>
|
||||
QJsonValue toJson<QVariant>(const QVariant &variant);
|
||||
template <>
|
||||
QJsonValue toJson<QUrl>(const QUrl& url);
|
||||
template <>
|
||||
QJsonValue toJson<QByteArray>(const QByteArray& data);
|
||||
template <>
|
||||
QJsonValue toJson<QDateTime>(const QDateTime& datetime);
|
||||
template <>
|
||||
QJsonValue toJson<QDir>(const QDir& dir);
|
||||
template <>
|
||||
QJsonValue toJson<QUuid>(const QUuid& uuid);
|
||||
template <>
|
||||
QJsonValue toJson<QVariant>(const QVariant& variant);
|
||||
|
||||
template<typename T>
|
||||
QJsonArray toJsonArray(const QList<T> &container)
|
||||
template <typename T>
|
||||
QJsonArray toJsonArray(const QList<T>& container)
|
||||
{
|
||||
QJsonArray array;
|
||||
for (const T item : container)
|
||||
{
|
||||
for (const T item : container) {
|
||||
array.append(toJson<T>(item));
|
||||
}
|
||||
return array;
|
||||
@ -112,106 +109,110 @@ QJsonArray toJsonArray(const QList<T> &container)
|
||||
|
||||
/// @throw JsonException
|
||||
template <typename T>
|
||||
T requireIsType(const QJsonValue &value, const QString &what = "Value");
|
||||
T requireIsType(const QJsonValue& value, const QString& what = "Value");
|
||||
|
||||
/// @throw JsonException
|
||||
template<> double requireIsType<double>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
double requireIsType<double>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> bool requireIsType<bool>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
bool requireIsType<bool>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> int requireIsType<int>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
int requireIsType<int>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QJsonObject requireIsType<QJsonObject>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QJsonObject requireIsType<QJsonObject>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QJsonArray requireIsType<QJsonArray>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QJsonArray requireIsType<QJsonArray>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QJsonValue requireIsType<QJsonValue>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QJsonValue requireIsType<QJsonValue>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QByteArray requireIsType<QByteArray>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QByteArray requireIsType<QByteArray>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QDateTime requireIsType<QDateTime>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QDateTime requireIsType<QDateTime>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QVariant requireIsType<QVariant>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QVariant requireIsType<QVariant>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QString requireIsType<QString>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QString requireIsType<QString>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QUuid requireIsType<QUuid>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QUuid requireIsType<QUuid>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QDir requireIsType<QDir>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QDir requireIsType<QDir>(const QJsonValue& value, const QString& what);
|
||||
/// @throw JsonException
|
||||
template<> QUrl requireIsType<QUrl>(const QJsonValue &value, const QString &what);
|
||||
template <>
|
||||
QUrl requireIsType<QUrl>(const QJsonValue& value, const QString& what);
|
||||
|
||||
// the following functions are higher level functions, that make use of the above functions for
|
||||
// type conversion
|
||||
template <typename T>
|
||||
T ensureIsType(const QJsonValue &value, const T default_ = T(), const QString &what = "Value")
|
||||
T ensureIsType(const QJsonValue& value, const T default_ = T(), const QString& what = "Value")
|
||||
{
|
||||
if (value.isUndefined() || value.isNull())
|
||||
{
|
||||
if (value.isUndefined() || value.isNull()) {
|
||||
return default_;
|
||||
}
|
||||
try
|
||||
{
|
||||
try {
|
||||
return requireIsType<T>(value, what);
|
||||
}
|
||||
catch (const JsonException &)
|
||||
{
|
||||
} catch (const JsonException&) {
|
||||
return default_;
|
||||
}
|
||||
}
|
||||
|
||||
/// @throw JsonException
|
||||
template <typename T>
|
||||
T requireIsType(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
|
||||
T requireIsType(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
|
||||
{
|
||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||
if (!parent.contains(key))
|
||||
{
|
||||
if (!parent.contains(key)) {
|
||||
throw JsonException(localWhat + "s parent does not contain " + localWhat);
|
||||
}
|
||||
return requireIsType<T>(parent.value(key), localWhat);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ensureIsType(const QJsonObject &parent, const QString &key, const T default_ = T(), const QString &what = "__placeholder__")
|
||||
T ensureIsType(const QJsonObject& parent, const QString& key, const T default_ = T(), const QString& what = "__placeholder__")
|
||||
{
|
||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||
if (!parent.contains(key))
|
||||
{
|
||||
if (!parent.contains(key)) {
|
||||
return default_;
|
||||
}
|
||||
return ensureIsType<T>(parent.value(key), default_, localWhat);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QVector<T> requireIsArrayOf(const QJsonDocument &doc)
|
||||
QVector<T> requireIsArrayOf(const QJsonDocument& doc)
|
||||
{
|
||||
const QJsonArray array = requireArray(doc);
|
||||
QVector<T> out;
|
||||
for (const QJsonValue val : array)
|
||||
{
|
||||
for (const QJsonValue val : array) {
|
||||
out.append(requireIsType<T>(val, "Document"));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QString &what = "Value")
|
||||
QVector<T> ensureIsArrayOf(const QJsonValue& value, const QString& what = "Value")
|
||||
{
|
||||
const QJsonArray array = ensureIsType<QJsonArray>(value, QJsonArray(), what);
|
||||
QVector<T> out;
|
||||
for (const QJsonValue val : array)
|
||||
{
|
||||
for (const QJsonValue val : array) {
|
||||
out.append(requireIsType<T>(val, what));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, const QString &what = "Value")
|
||||
QVector<T> ensureIsArrayOf(const QJsonValue& value, const QVector<T> default_, const QString& what = "Value")
|
||||
{
|
||||
if (value.isUndefined())
|
||||
{
|
||||
if (value.isUndefined()) {
|
||||
return default_;
|
||||
}
|
||||
return ensureIsArrayOf<T>(value, what);
|
||||
@ -219,45 +220,46 @@ QVector<T> ensureIsArrayOf(const QJsonValue &value, const QVector<T> default_, c
|
||||
|
||||
/// @throw JsonException
|
||||
template <typename T>
|
||||
QVector<T> requireIsArrayOf(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__")
|
||||
QVector<T> requireIsArrayOf(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__")
|
||||
{
|
||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||
if (!parent.contains(key))
|
||||
{
|
||||
if (!parent.contains(key)) {
|
||||
throw JsonException(localWhat + "s parent does not contain " + localWhat);
|
||||
}
|
||||
return ensureIsArrayOf<T>(parent.value(key), localWhat);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QVector<T> ensureIsArrayOf(const QJsonObject &parent, const QString &key,
|
||||
const QVector<T> &default_ = QVector<T>(), const QString &what = "__placeholder__")
|
||||
QVector<T> ensureIsArrayOf(const QJsonObject& parent,
|
||||
const QString& key,
|
||||
const QVector<T>& default_ = QVector<T>(),
|
||||
const QString& what = "__placeholder__")
|
||||
{
|
||||
const QString localWhat = QString(what).replace("__placeholder__", '\'' + key + '\'');
|
||||
if (!parent.contains(key))
|
||||
{
|
||||
if (!parent.contains(key)) {
|
||||
return default_;
|
||||
}
|
||||
return ensureIsArrayOf<T>(parent.value(key), default_, localWhat);
|
||||
}
|
||||
|
||||
// this macro part could be replaced by variadic functions that just pass on their arguments, but that wouldn't work well with IDE helpers
|
||||
#define JSON_HELPERFUNCTIONS(NAME, TYPE) \
|
||||
inline TYPE require##NAME(const QJsonValue &value, const QString &what = "Value") \
|
||||
{ \
|
||||
return requireIsType<TYPE>(value, what); \
|
||||
} \
|
||||
inline TYPE ensure##NAME(const QJsonValue &value, const TYPE default_ = TYPE(), const QString &what = "Value") \
|
||||
{ \
|
||||
return ensureIsType<TYPE>(value, default_, what); \
|
||||
} \
|
||||
inline TYPE require##NAME(const QJsonObject &parent, const QString &key, const QString &what = "__placeholder__") \
|
||||
{ \
|
||||
return requireIsType<TYPE>(parent, key, what); \
|
||||
} \
|
||||
inline TYPE ensure##NAME(const QJsonObject &parent, const QString &key, const TYPE default_ = TYPE(), const QString &what = "__placeholder") \
|
||||
{ \
|
||||
return ensureIsType<TYPE>(parent, key, default_, what); \
|
||||
#define JSON_HELPERFUNCTIONS(NAME, TYPE) \
|
||||
inline TYPE require##NAME(const QJsonValue& value, const QString& what = "Value") \
|
||||
{ \
|
||||
return requireIsType<TYPE>(value, what); \
|
||||
} \
|
||||
inline TYPE ensure##NAME(const QJsonValue& value, const TYPE default_ = TYPE(), const QString& what = "Value") \
|
||||
{ \
|
||||
return ensureIsType<TYPE>(value, default_, what); \
|
||||
} \
|
||||
inline TYPE require##NAME(const QJsonObject& parent, const QString& key, const QString& what = "__placeholder__") \
|
||||
{ \
|
||||
return requireIsType<TYPE>(parent, key, what); \
|
||||
} \
|
||||
inline TYPE ensure##NAME(const QJsonObject& parent, const QString& key, const TYPE default_ = TYPE(), \
|
||||
const QString& what = "__placeholder") \
|
||||
{ \
|
||||
return ensureIsType<TYPE>(parent, key, default_, what); \
|
||||
}
|
||||
|
||||
JSON_HELPERFUNCTIONS(Array, QJsonArray)
|
||||
@ -276,5 +278,5 @@ JSON_HELPERFUNCTIONS(Variant, QVariant)
|
||||
|
||||
#undef JSON_HELPERFUNCTIONS
|
||||
|
||||
}
|
||||
} // namespace Json
|
||||
using JSONValidationError = Json::JsonException;
|
||||
|
@ -1,42 +1,26 @@
|
||||
#include "KonamiCode.h"
|
||||
|
||||
#include <array>
|
||||
#include <QDebug>
|
||||
#include <array>
|
||||
|
||||
namespace {
|
||||
const std::array<Qt::Key, 10> konamiCode =
|
||||
{
|
||||
{
|
||||
Qt::Key_Up, Qt::Key_Up,
|
||||
Qt::Key_Down, Qt::Key_Down,
|
||||
Qt::Key_Left, Qt::Key_Right,
|
||||
Qt::Key_Left, Qt::Key_Right,
|
||||
Qt::Key_B, Qt::Key_A
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
KonamiCode::KonamiCode(QObject* parent) : QObject(parent)
|
||||
{
|
||||
const std::array<Qt::Key, 10> konamiCode = { { Qt::Key_Up, Qt::Key_Up, Qt::Key_Down, Qt::Key_Down, Qt::Key_Left, Qt::Key_Right,
|
||||
Qt::Key_Left, Qt::Key_Right, Qt::Key_B, Qt::Key_A } };
|
||||
}
|
||||
|
||||
KonamiCode::KonamiCode(QObject* parent) : QObject(parent) {}
|
||||
|
||||
void KonamiCode::input(QEvent* event)
|
||||
{
|
||||
if( event->type() == QEvent::KeyPress )
|
||||
{
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent*>( event );
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||
auto key = Qt::Key(keyEvent->key());
|
||||
if(key == konamiCode[m_progress])
|
||||
{
|
||||
m_progress ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (key == konamiCode[m_progress]) {
|
||||
m_progress++;
|
||||
} else {
|
||||
m_progress = 0;
|
||||
}
|
||||
if(m_progress == static_cast<int>(konamiCode.size()))
|
||||
{
|
||||
if (m_progress == static_cast<int>(konamiCode.size())) {
|
||||
m_progress = 0;
|
||||
emit triggered();
|
||||
}
|
||||
|
@ -2,16 +2,15 @@
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
class KonamiCode : public QObject
|
||||
{
|
||||
class KonamiCode : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
KonamiCode(QObject *parent = 0);
|
||||
void input(QEvent *event);
|
||||
public:
|
||||
KonamiCode(QObject* parent = 0);
|
||||
void input(QEvent* event);
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void triggered();
|
||||
|
||||
private:
|
||||
private:
|
||||
int m_progress = 0;
|
||||
};
|
||||
|
@ -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) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -34,44 +35,41 @@
|
||||
*/
|
||||
|
||||
#include "LaunchController.h"
|
||||
#include "minecraft/auth/AccountList.h"
|
||||
#include "Application.h"
|
||||
#include "minecraft/auth/AccountList.h"
|
||||
|
||||
#include "ui/MainWindow.h"
|
||||
#include "ui/InstanceWindow.h"
|
||||
#include "ui/MainWindow.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProfileSelectDialog.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/dialogs/EditAccountDialog.h"
|
||||
#include "ui/dialogs/ProfileSelectDialog.h"
|
||||
#include "ui/dialogs/ProfileSetupDialog.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QInputDialog>
|
||||
#include <QStringList>
|
||||
#include <QHostInfo>
|
||||
#include <QList>
|
||||
#include <QHostAddress>
|
||||
#include <QHostInfo>
|
||||
#include <QInputDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QList>
|
||||
#include <QPushButton>
|
||||
#include <QStringList>
|
||||
|
||||
#include "BuildConfig.h"
|
||||
#include "JavaCommon.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "minecraft/auth/AccountTask.h"
|
||||
#include "launch/steps/TextPrint.h"
|
||||
#include "minecraft/auth/AccountTask.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
LaunchController::LaunchController(QObject *parent) : Task(parent)
|
||||
{
|
||||
}
|
||||
LaunchController::LaunchController(QObject* parent) : Task(parent) {}
|
||||
|
||||
void LaunchController::executeTask()
|
||||
{
|
||||
if (!m_instance)
|
||||
{
|
||||
if (!m_instance) {
|
||||
emitFailed(tr("No instance specified!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) {
|
||||
if (!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) {
|
||||
emitFailed(tr("Invalid Java arguments specified. Please fix this first."));
|
||||
return;
|
||||
}
|
||||
@ -81,32 +79,25 @@ void LaunchController::executeTask()
|
||||
|
||||
void LaunchController::decideAccount()
|
||||
{
|
||||
if(m_accountToUse) {
|
||||
if (m_accountToUse) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find an account to use.
|
||||
auto accounts = APPLICATION->accounts();
|
||||
if (accounts->count() <= 0)
|
||||
{
|
||||
if (accounts->count() <= 0) {
|
||||
// Tell the user they need to log in at least one account in order to play.
|
||||
auto reply = CustomMessageBox::selectable(
|
||||
m_parentWidget,
|
||||
tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one 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
|
||||
)->exec();
|
||||
auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Microsoft "
|
||||
"account which owns Minecraft logged in. "
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)
|
||||
->exec();
|
||||
|
||||
if (reply == QMessageBox::Yes)
|
||||
{
|
||||
if (reply == QMessageBox::Yes) {
|
||||
// Open the account manager.
|
||||
APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts");
|
||||
}
|
||||
else if (reply == QMessageBox::No)
|
||||
{
|
||||
} else if (reply == QMessageBox::No) {
|
||||
// Do not open "profile select" dialog.
|
||||
return;
|
||||
}
|
||||
@ -115,20 +106,16 @@ void LaunchController::decideAccount()
|
||||
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
|
||||
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
|
||||
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
|
||||
if (instanceAccountIndex == -1) {
|
||||
if (instanceAccountIndex == -1 || instanceAccountId.isEmpty()) {
|
||||
m_accountToUse = accounts->defaultAccount();
|
||||
} else {
|
||||
m_accountToUse = accounts->at(instanceAccountIndex);
|
||||
}
|
||||
|
||||
if (!m_accountToUse)
|
||||
{
|
||||
if (!m_accountToUse) {
|
||||
// If no default account is set, ask the user which one to use.
|
||||
ProfileSelectDialog selectDialog(
|
||||
tr("Which account would you like to use?"),
|
||||
ProfileSelectDialog::GlobalDefaultCheckbox,
|
||||
m_parentWidget
|
||||
);
|
||||
ProfileSelectDialog selectDialog(tr("Which account would you like to use?"), ProfileSelectDialog::GlobalDefaultCheckbox,
|
||||
m_parentWidget);
|
||||
|
||||
selectDialog.exec();
|
||||
|
||||
@ -142,13 +129,12 @@ void LaunchController::decideAccount()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LaunchController::login() {
|
||||
void LaunchController::login()
|
||||
{
|
||||
decideAccount();
|
||||
|
||||
// if no account is selected, we bail
|
||||
if (!m_accountToUse)
|
||||
{
|
||||
if (!m_accountToUse) {
|
||||
emitFailed(tr("No account selected for launch."));
|
||||
return;
|
||||
}
|
||||
@ -157,15 +143,11 @@ void LaunchController::login() {
|
||||
bool tryagain = true;
|
||||
unsigned int tries = 0;
|
||||
|
||||
while (tryagain)
|
||||
{
|
||||
while (tryagain) {
|
||||
if (tries > 0 && tries % 3 == 0) {
|
||||
auto result = QMessageBox::question(
|
||||
m_parentWidget,
|
||||
tr("Continue launch?"),
|
||||
tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?")
|
||||
.arg(tries)
|
||||
);
|
||||
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();
|
||||
@ -179,60 +161,48 @@ void LaunchController::login() {
|
||||
m_accountToUse->fillSession(m_session);
|
||||
|
||||
// Launch immediately in true offline mode
|
||||
if(m_accountToUse->isOffline()) {
|
||||
if (m_accountToUse->isOffline()) {
|
||||
launchInstance();
|
||||
return;
|
||||
}
|
||||
|
||||
switch(m_accountToUse->accountState()) {
|
||||
switch (m_accountToUse->accountState()) {
|
||||
case AccountState::Offline: {
|
||||
m_session->wants_online = false;
|
||||
// NOTE: fallthrough is intentional
|
||||
}
|
||||
/* fallthrough */
|
||||
case AccountState::Online: {
|
||||
if(!m_session->wants_online) {
|
||||
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) {
|
||||
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"),
|
||||
message,
|
||||
QLineEdit::Normal,
|
||||
usedname,
|
||||
&ok
|
||||
);
|
||||
if (!ok)
|
||||
{
|
||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"), message, QLineEdit::Normal, usedname, &ok);
|
||||
if (!ok) {
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
if (name.length())
|
||||
{
|
||||
if (name.length()) {
|
||||
usedname = name;
|
||||
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||
}
|
||||
m_session->MakeOffline(usedname);
|
||||
// offline flavored game from here :3
|
||||
}
|
||||
if(m_accountToUse->ownsMinecraft()) {
|
||||
if(!m_accountToUse->hasProfile()) {
|
||||
if (m_accountToUse->ownsMinecraft()) {
|
||||
if (!m_accountToUse->hasProfile()) {
|
||||
// Now handle setting up a profile name here...
|
||||
ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
tryagain = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
emitFailed(tr("Received undetermined session status during login."));
|
||||
return;
|
||||
}
|
||||
@ -240,24 +210,24 @@ void LaunchController::login() {
|
||||
// we own Minecraft, there is a profile, it's all ready to go!
|
||||
launchInstance();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// play demo ?
|
||||
QMessageBox box(m_parentWidget);
|
||||
box.setWindowTitle(tr("Play demo?"));
|
||||
box.setText(tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play the demo?"));
|
||||
box.setText(
|
||||
tr("This account does not own Minecraft.\nYou need to purchase the game first to play it.\n\nDo you want to play "
|
||||
"the demo?"));
|
||||
box.setIcon(QMessageBox::Warning);
|
||||
auto demoButton = box.addButton(tr("Play Demo"), QMessageBox::ButtonRole::YesRole);
|
||||
auto cancelButton = box.addButton(tr("Cancel"), QMessageBox::ButtonRole::NoRole);
|
||||
box.setDefaultButton(cancelButton);
|
||||
|
||||
box.exec();
|
||||
if(box.clickedButton() == demoButton) {
|
||||
if (box.clickedButton() == demoButton) {
|
||||
// play demo here
|
||||
m_session->MakeDemo();
|
||||
launchInstance();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
emitFailed(tr("Launch cancelled - account does not own Minecraft."));
|
||||
}
|
||||
}
|
||||
@ -267,13 +237,12 @@ void LaunchController::login() {
|
||||
// This means some sort of soft error that we can fix with a refresh ... so let's refresh.
|
||||
case AccountState::Unchecked: {
|
||||
m_accountToUse->refresh();
|
||||
// NOTE: fallthrough intentional
|
||||
}
|
||||
/* fallthrough */
|
||||
case AccountState::Working: {
|
||||
// refresh is in progress, we need to wait for it to finish to proceed.
|
||||
ProgressDialog progDialog(m_parentWidget);
|
||||
if (m_online)
|
||||
{
|
||||
if (m_online) {
|
||||
progDialog.setSkipButton(true, tr("Play Offline"));
|
||||
}
|
||||
auto task = m_accountToUse->currentTask();
|
||||
@ -288,37 +257,24 @@ void LaunchController::login() {
|
||||
*/
|
||||
case AccountState::Expired: {
|
||||
auto errorString = tr("The account has expired and needs to be logged into manually again.");
|
||||
QMessageBox::warning(
|
||||
m_parentWidget,
|
||||
tr("Account refresh failed"),
|
||||
errorString,
|
||||
QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok
|
||||
);
|
||||
QMessageBox::warning(m_parentWidget, tr("Account refresh failed"), errorString, QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok);
|
||||
emitFailed(errorString);
|
||||
return;
|
||||
}
|
||||
case AccountState::Disabled: {
|
||||
auto errorString = tr("The launcher's client identification has changed. Please remove this account and add it again.");
|
||||
QMessageBox::warning(
|
||||
m_parentWidget,
|
||||
tr("Client identification changed"),
|
||||
errorString,
|
||||
QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok
|
||||
);
|
||||
QMessageBox::warning(m_parentWidget, tr("Client identification changed"), errorString, QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok);
|
||||
emitFailed(errorString);
|
||||
return;
|
||||
}
|
||||
case AccountState::Gone: {
|
||||
auto errorString = tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account you migrated this one to.");
|
||||
QMessageBox::warning(
|
||||
m_parentWidget,
|
||||
tr("Account gone"),
|
||||
errorString,
|
||||
QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok
|
||||
);
|
||||
auto errorString =
|
||||
tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account "
|
||||
"you migrated this one to.");
|
||||
QMessageBox::warning(m_parentWidget, tr("Account gone"), errorString, QMessageBox::StandardButton::Ok,
|
||||
QMessageBox::StandardButton::Ok);
|
||||
emitFailed(errorString);
|
||||
return;
|
||||
}
|
||||
@ -332,48 +288,45 @@ void LaunchController::launchInstance()
|
||||
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
||||
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL");
|
||||
|
||||
if(!m_instance->reloadSettings())
|
||||
{
|
||||
if (!m_instance->reloadSettings()) {
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile."));
|
||||
emitFailed(tr("Couldn't load the instance profile."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin);
|
||||
if (!m_launcher)
|
||||
{
|
||||
if (!m_launcher) {
|
||||
emitFailed(tr("Couldn't instantiate a launcher."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto console = qobject_cast<InstanceWindow *>(m_parentWidget);
|
||||
auto console = qobject_cast<InstanceWindow*>(m_parentWidget);
|
||||
auto showConsole = m_instance->settings()->get("ShowConsole").toBool();
|
||||
if(!console && showConsole)
|
||||
{
|
||||
if (!console && showConsole) {
|
||||
APPLICATION->showInstanceWindow(m_instance);
|
||||
}
|
||||
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
|
||||
connect(m_launcher.get(), &LaunchTask::succeeded, this, &LaunchController::onSucceeded);
|
||||
connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed);
|
||||
connect(m_launcher.get(), &LaunchTask::failed, this, &LaunchController::onFailed);
|
||||
connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
|
||||
|
||||
// Prepend Online and Auth Status
|
||||
QString online_mode;
|
||||
if(m_session->wants_online) {
|
||||
if (m_session->wants_online) {
|
||||
online_mode = "online";
|
||||
|
||||
// Prepend Server Status
|
||||
QStringList servers = {"authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com"};
|
||||
QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" };
|
||||
QString resolved_servers = "";
|
||||
QHostInfo host_info;
|
||||
|
||||
for(QString server : servers) {
|
||||
for (QString server : servers) {
|
||||
host_info = QHostInfo::fromName(server);
|
||||
resolved_servers = resolved_servers + server + " resolves to:\n [";
|
||||
if(!host_info.addresses().isEmpty()) {
|
||||
for(QHostAddress address : host_info.addresses()) {
|
||||
if (!host_info.addresses().isEmpty()) {
|
||||
for (QHostAddress address : host_info.addresses()) {
|
||||
resolved_servers = resolved_servers + address.toString();
|
||||
if(!host_info.addresses().endsWith(address)) {
|
||||
if (!host_info.addresses().endsWith(address)) {
|
||||
resolved_servers = resolved_servers + ", ";
|
||||
}
|
||||
}
|
||||
@ -387,46 +340,47 @@ void LaunchController::launchInstance()
|
||||
online_mode = m_demo ? "demo" : "offline";
|
||||
}
|
||||
|
||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
||||
m_launcher->prependStep(
|
||||
makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
||||
|
||||
// Prepend Version
|
||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
||||
{
|
||||
auto versionString = QString("%1 version: %2 (%3)")
|
||||
.arg(BuildConfig.LAUNCHER_DISPLAYNAME, BuildConfig.printableVersionString(), BuildConfig.BUILD_PLATFORM);
|
||||
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), versionString + "\n\n", MessageLevel::Launcher));
|
||||
}
|
||||
m_launcher->start();
|
||||
}
|
||||
|
||||
void LaunchController::readyForLaunch()
|
||||
{
|
||||
if (!m_profiler)
|
||||
{
|
||||
if (!m_profiler) {
|
||||
m_launcher->proceed();
|
||||
return;
|
||||
}
|
||||
|
||||
QString error;
|
||||
if (!m_profiler->check(&error))
|
||||
{
|
||||
if (!m_profiler->check(&error)) {
|
||||
m_launcher->abort();
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
|
||||
emitFailed("Profiler startup failed!");
|
||||
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Profiler check for %1 failed: %2").arg(m_profiler->name(), error));
|
||||
return;
|
||||
}
|
||||
BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||
BaseProfiler* profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||
|
||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString & message)
|
||||
{
|
||||
QMessageBox msg;
|
||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString& message) {
|
||||
QMessageBox msg(m_parentWidget);
|
||||
msg.setText(tr("The game launch is delayed until you press the "
|
||||
"button. This is the right time to setup the profiler, as the "
|
||||
"profiler server is running now.\n\n%1").arg(message));
|
||||
"button. This is the right time to setup the profiler, as the "
|
||||
"profiler server is running now.\n\n%1")
|
||||
.arg(message));
|
||||
msg.setWindowTitle(tr("Waiting."));
|
||||
msg.setIcon(QMessageBox::Information);
|
||||
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
|
||||
msg.setModal(true);
|
||||
msg.addButton(tr("&Launch"), QMessageBox::AcceptRole);
|
||||
msg.exec();
|
||||
m_launcher->proceed();
|
||||
});
|
||||
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message)
|
||||
{
|
||||
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString& message) {
|
||||
QMessageBox msg;
|
||||
msg.setText(tr("Couldn't start the profiler: %1").arg(message));
|
||||
msg.setWindowTitle(tr("Error"));
|
||||
@ -447,8 +401,7 @@ void LaunchController::onSucceeded()
|
||||
|
||||
void LaunchController::onFailed(QString reason)
|
||||
{
|
||||
if(m_instance->settings()->get("ShowConsoleOnError").toBool())
|
||||
{
|
||||
if (m_instance->settings()->get("ShowConsoleOnError").toBool()) {
|
||||
APPLICATION->showInstanceWindow(m_instance, "console");
|
||||
}
|
||||
emitFailed(reason);
|
||||
@ -464,21 +417,18 @@ void LaunchController::onProgressRequested(Task* task)
|
||||
|
||||
bool LaunchController::abort()
|
||||
{
|
||||
if(!m_launcher)
|
||||
{
|
||||
if (!m_launcher) {
|
||||
return true;
|
||||
}
|
||||
if(!m_launcher->canAbort())
|
||||
{
|
||||
if (!m_launcher->canAbort()) {
|
||||
return false;
|
||||
}
|
||||
auto response = CustomMessageBox::selectable(
|
||||
m_parentWidget, tr("Kill Minecraft?"),
|
||||
tr("This can cause the instance to get corrupted and should only be used if Minecraft "
|
||||
"is frozen for some reason"),
|
||||
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
|
||||
if (response == QMessageBox::Yes)
|
||||
{
|
||||
auto response = CustomMessageBox::selectable(m_parentWidget, tr("Kill Minecraft?"),
|
||||
tr("This can cause the instance to get corrupted and should only be used if Minecraft "
|
||||
"is frozen for some reason"),
|
||||
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)
|
||||
->exec();
|
||||
if (response == QMessageBox::Yes) {
|
||||
return m_launcher->abort();
|
||||
}
|
||||
return false;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
@ -34,81 +34,61 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
#include <BaseInstance.h>
|
||||
#include <tools/BaseProfiler.h>
|
||||
#include <QObject>
|
||||
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
#include "minecraft/auth/MinecraftAccount.h"
|
||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||
|
||||
class InstanceWindow;
|
||||
class LaunchController: public Task
|
||||
{
|
||||
class LaunchController : public Task {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
void executeTask() override;
|
||||
|
||||
LaunchController(QObject * parent = nullptr);
|
||||
LaunchController(QObject* parent = nullptr);
|
||||
virtual ~LaunchController(){};
|
||||
|
||||
void setInstance(InstancePtr instance) {
|
||||
m_instance = instance;
|
||||
}
|
||||
void setInstance(InstancePtr instance) { m_instance = instance; }
|
||||
|
||||
InstancePtr instance() {
|
||||
return m_instance;
|
||||
}
|
||||
InstancePtr instance() { return m_instance; }
|
||||
|
||||
void setOnline(bool online) {
|
||||
m_online = online;
|
||||
}
|
||||
void setOnline(bool online) { m_online = online; }
|
||||
|
||||
void setDemo(bool demo) {
|
||||
m_demo = demo;
|
||||
}
|
||||
void setDemo(bool demo) { m_demo = demo; }
|
||||
|
||||
void setProfiler(BaseProfilerFactory *profiler) {
|
||||
m_profiler = profiler;
|
||||
}
|
||||
void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; }
|
||||
|
||||
void setParentWidget(QWidget * widget) {
|
||||
m_parentWidget = widget;
|
||||
}
|
||||
void setParentWidget(QWidget* widget) { m_parentWidget = widget; }
|
||||
|
||||
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) {
|
||||
m_serverToJoin = std::move(serverToJoin);
|
||||
}
|
||||
void setServerToJoin(MinecraftServerTargetPtr serverToJoin) { m_serverToJoin = std::move(serverToJoin); }
|
||||
|
||||
void setAccountToUse(MinecraftAccountPtr accountToUse) {
|
||||
m_accountToUse = std::move(accountToUse);
|
||||
}
|
||||
void setAccountToUse(MinecraftAccountPtr accountToUse) { m_accountToUse = std::move(accountToUse); }
|
||||
|
||||
QString id()
|
||||
{
|
||||
return m_instance->id();
|
||||
}
|
||||
QString id() { return m_instance->id(); }
|
||||
|
||||
bool abort() override;
|
||||
|
||||
private:
|
||||
private:
|
||||
void login();
|
||||
void launchInstance();
|
||||
void decideAccount();
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void readyForLaunch();
|
||||
|
||||
void onSucceeded();
|
||||
void onFailed(QString reason);
|
||||
void onProgressRequested(Task *task);
|
||||
void onProgressRequested(Task* task);
|
||||
|
||||
private:
|
||||
BaseProfilerFactory *m_profiler = nullptr;
|
||||
private:
|
||||
BaseProfilerFactory* m_profiler = nullptr;
|
||||
bool m_online = true;
|
||||
bool m_demo = false;
|
||||
InstancePtr m_instance;
|
||||
QWidget * m_parentWidget = nullptr;
|
||||
InstanceWindow *m_console = nullptr;
|
||||
QWidget* m_parentWidget = nullptr;
|
||||
InstanceWindow* m_console = nullptr;
|
||||
MinecraftAccountPtr m_accountToUse = nullptr;
|
||||
AuthSessionPtr m_session;
|
||||
shared_qobject_ptr<LaunchTask> m_launcher;
|
||||
|
@ -39,7 +39,7 @@
|
||||
#include <QTextDecoder>
|
||||
#include "MessageLevel.h"
|
||||
|
||||
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
|
||||
LoggedProcess::LoggedProcess(QObject* parent) : QProcess(parent)
|
||||
{
|
||||
// QProcess has a strange interface... let's map a lot of those into a few.
|
||||
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
|
||||
@ -51,8 +51,7 @@ LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
|
||||
|
||||
LoggedProcess::~LoggedProcess()
|
||||
{
|
||||
if(m_is_detachable)
|
||||
{
|
||||
if (m_is_detachable) {
|
||||
setProcessState(QProcess::NotRunning);
|
||||
}
|
||||
}
|
||||
@ -66,14 +65,9 @@ QStringList LoggedProcess::reprocess(const QByteArray& data, QTextDecoder& decod
|
||||
m_leftover_line = "";
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts);
|
||||
#else
|
||||
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts);
|
||||
#endif
|
||||
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed);
|
||||
|
||||
if (!str.endsWith(QChar::LineFeed))
|
||||
m_leftover_line = lines.takeLast();
|
||||
m_leftover_line = lines.takeLast();
|
||||
return lines;
|
||||
}
|
||||
|
||||
@ -95,39 +89,31 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
|
||||
m_exit_code = exit_code;
|
||||
|
||||
// based on state, send signals
|
||||
if (!m_is_aborting)
|
||||
{
|
||||
if (status == QProcess::NormalExit)
|
||||
{
|
||||
if (!m_is_aborting) {
|
||||
if (status == QProcess::NormalExit) {
|
||||
//: Message displayed on instance exit
|
||||
emit log({tr("Process exited with code %1.").arg(exit_code)}, MessageLevel::Launcher);
|
||||
emit log({ tr("Process exited with code %1.").arg(exit_code) }, MessageLevel::Launcher);
|
||||
changeState(LoggedProcess::Finished);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
//: Message displayed on instance crashed
|
||||
if(exit_code == -1)
|
||||
emit log({tr("Process crashed.")}, MessageLevel::Launcher);
|
||||
if (exit_code == -1)
|
||||
emit log({ tr("Process crashed.") }, MessageLevel::Launcher);
|
||||
else
|
||||
emit log({tr("Process crashed with exitcode %1.").arg(exit_code)}, MessageLevel::Launcher);
|
||||
emit log({ tr("Process crashed with exitcode %1.").arg(exit_code) }, MessageLevel::Launcher);
|
||||
changeState(LoggedProcess::Crashed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
//: Message displayed after the instance exits due to kill request
|
||||
emit log({tr("Process was killed by user.")}, MessageLevel::Error);
|
||||
emit log({ tr("Process was killed by user.") }, MessageLevel::Error);
|
||||
changeState(LoggedProcess::Aborted);
|
||||
}
|
||||
}
|
||||
|
||||
void LoggedProcess::on_error(QProcess::ProcessError error)
|
||||
{
|
||||
switch(error)
|
||||
{
|
||||
case QProcess::FailedToStart:
|
||||
{
|
||||
emit log({tr("The process failed to start.")}, MessageLevel::Fatal);
|
||||
switch (error) {
|
||||
case QProcess::FailedToStart: {
|
||||
emit log({ tr("The process failed to start.") }, MessageLevel::Fatal);
|
||||
changeState(LoggedProcess::FailedToStart);
|
||||
break;
|
||||
}
|
||||
@ -154,7 +140,7 @@ int LoggedProcess::exitCode() const
|
||||
|
||||
void LoggedProcess::changeState(LoggedProcess::State state)
|
||||
{
|
||||
if(state == m_state)
|
||||
if (state == m_state)
|
||||
return;
|
||||
m_state = state;
|
||||
emit stateChanged(m_state);
|
||||
@ -167,24 +153,19 @@ LoggedProcess::State LoggedProcess::state() const
|
||||
|
||||
void LoggedProcess::on_stateChange(QProcess::ProcessState state)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
switch (state) {
|
||||
case QProcess::NotRunning:
|
||||
break; // let's not - there are too many that handle this already.
|
||||
case QProcess::Starting:
|
||||
{
|
||||
if(m_state != LoggedProcess::NotRunning)
|
||||
{
|
||||
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Starting;
|
||||
break; // let's not - there are too many that handle this already.
|
||||
case QProcess::Starting: {
|
||||
if (m_state != LoggedProcess::NotRunning) {
|
||||
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Starting;
|
||||
}
|
||||
changeState(LoggedProcess::Starting);
|
||||
return;
|
||||
}
|
||||
case QProcess::Running:
|
||||
{
|
||||
if(m_state != LoggedProcess::Starting)
|
||||
{
|
||||
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int) LoggedProcess::Running;
|
||||
case QProcess::Running: {
|
||||
if (m_state != LoggedProcess::Starting) {
|
||||
qWarning() << "Wrong state change for process from state" << m_state << "to" << (int)LoggedProcess::Running;
|
||||
}
|
||||
changeState(LoggedProcess::Running);
|
||||
return;
|
||||
|
@ -43,22 +43,12 @@
|
||||
* This is a basic process.
|
||||
* It has line-based logging support and hides some of the nasty bits.
|
||||
*/
|
||||
class LoggedProcess : public QProcess
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum State
|
||||
{
|
||||
NotRunning,
|
||||
Starting,
|
||||
FailedToStart,
|
||||
Running,
|
||||
Finished,
|
||||
Crashed,
|
||||
Aborted
|
||||
};
|
||||
class LoggedProcess : public QProcess {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum State { NotRunning, Starting, FailedToStart, Running, Finished, Crashed, Aborted };
|
||||
|
||||
public:
|
||||
public:
|
||||
explicit LoggedProcess(QObject* parent = 0);
|
||||
virtual ~LoggedProcess();
|
||||
|
||||
@ -67,30 +57,29 @@ public:
|
||||
|
||||
void setDetachable(bool detachable);
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void log(QStringList lines, MessageLevel::Enum level);
|
||||
void stateChanged(LoggedProcess::State state);
|
||||
|
||||
public slots:
|
||||
public slots:
|
||||
/**
|
||||
* @brief kill the process - equivalent to kill -9
|
||||
*/
|
||||
void kill();
|
||||
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void on_stdErr();
|
||||
void on_stdOut();
|
||||
void on_exit(int exit_code, QProcess::ExitStatus status);
|
||||
void on_error(QProcess::ProcessError error);
|
||||
void on_stateChange(QProcess::ProcessState);
|
||||
|
||||
private:
|
||||
private:
|
||||
void changeState(LoggedProcess::State state);
|
||||
|
||||
QStringList reprocess(const QByteArray& data, QTextDecoder& decoder);
|
||||
|
||||
private:
|
||||
private:
|
||||
QTextDecoder m_err_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
||||
QTextDecoder m_out_decoder = QTextDecoder(QTextCodec::codecForLocale());
|
||||
QString m_leftover_line;
|
||||
|
@ -16,31 +16,31 @@
|
||||
*/
|
||||
|
||||
#include <MMCTime.h>
|
||||
#include <qobject.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QTextStream>
|
||||
|
||||
QString Time::prettifyDuration(int64_t duration) {
|
||||
int seconds = (int) (duration % 60);
|
||||
QString Time::prettifyDuration(int64_t duration, bool noDays)
|
||||
{
|
||||
int seconds = (int)(duration % 60);
|
||||
duration /= 60;
|
||||
int minutes = (int) (duration % 60);
|
||||
int minutes = (int)(duration % 60);
|
||||
duration /= 60;
|
||||
int hours = (int) (duration % 24);
|
||||
int days = (int) (duration / 24);
|
||||
if((hours == 0)&&(days == 0))
|
||||
{
|
||||
int hours = (int)(noDays ? duration : (duration % 24));
|
||||
int days = (int)(noDays ? 0 : (duration / 24));
|
||||
if ((hours == 0) && (days == 0)) {
|
||||
return QObject::tr("%1min %2s").arg(minutes).arg(seconds);
|
||||
}
|
||||
if (days == 0)
|
||||
{
|
||||
if (days == 0) {
|
||||
return QObject::tr("%1h %2min").arg(hours).arg(minutes);
|
||||
}
|
||||
return QObject::tr("%1d %2h %3min").arg(days).arg(hours).arg(minutes);
|
||||
}
|
||||
|
||||
QString Time::humanReadableDuration(double duration, int precision) {
|
||||
|
||||
QString Time::humanReadableDuration(double duration, int precision)
|
||||
{
|
||||
using days = std::chrono::duration<int, std::ratio<86400>>;
|
||||
|
||||
QString outStr;
|
||||
@ -48,8 +48,8 @@ QString Time::humanReadableDuration(double duration, int precision) {
|
||||
|
||||
bool neg = false;
|
||||
if (duration < 0) {
|
||||
neg = true; // flag
|
||||
duration *= -1; // invert
|
||||
neg = true; // flag
|
||||
duration *= -1; // invert
|
||||
}
|
||||
|
||||
auto std_duration = std::chrono::duration<double>(duration);
|
||||
@ -78,22 +78,22 @@ QString Time::humanReadableDuration(double duration, int precision) {
|
||||
if (hc) {
|
||||
if (dc)
|
||||
os << " ";
|
||||
os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours
|
||||
os << qSetFieldWidth(2) << hc << QObject::tr("h"); // hours
|
||||
}
|
||||
if (mc) {
|
||||
if (dc || hc)
|
||||
os << " ";
|
||||
os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes
|
||||
os << qSetFieldWidth(2) << mc << QObject::tr("m"); // minutes
|
||||
}
|
||||
if (dc || hc || mc || sc) {
|
||||
if (dc || hc || mc)
|
||||
os << " ";
|
||||
os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds
|
||||
os << qSetFieldWidth(2) << sc << QObject::tr("s"); // seconds
|
||||
}
|
||||
if ((msc && (precision > 0)) || !(dc || hc || mc || sc)) {
|
||||
if (dc || hc || mc || sc)
|
||||
os << " ";
|
||||
os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds
|
||||
os << qSetFieldWidth(0) << qSetRealNumberPrecision(precision) << msc << QObject::tr("ms"); // miliseconds
|
||||
}
|
||||
|
||||
os.flush();
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
namespace Time {
|
||||
|
||||
QString prettifyDuration(int64_t duration);
|
||||
QString prettifyDuration(int64_t duration, bool noDays = false);
|
||||
|
||||
/**
|
||||
* @brief Returns a string with short form time duration ie. `2days 1h3m4s56.0ms`.
|
||||
@ -31,4 +31,4 @@ QString prettifyDuration(int64_t duration);
|
||||
* @return QString
|
||||
*/
|
||||
QString humanReadableDuration(double duration, int precision = 0);
|
||||
}
|
||||
} // namespace Time
|
||||
|
@ -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) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -33,56 +34,54 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MMCZip.h"
|
||||
#include <quazip/quazip.h>
|
||||
#include <quazip/quazipdir.h>
|
||||
#include <quazip/quazipfile.h>
|
||||
#include "MMCZip.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include <QtConcurrentRun>
|
||||
#endif
|
||||
|
||||
namespace MMCZip {
|
||||
// ours
|
||||
bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained, const FilterFunction filter)
|
||||
bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter)
|
||||
{
|
||||
QuaZip modZip(from.filePath());
|
||||
modZip.open(QuaZip::mdUnzip);
|
||||
|
||||
QuaZipFile fileInsideMod(&modZip);
|
||||
QuaZipFile zipOutFile(into);
|
||||
for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile())
|
||||
{
|
||||
for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) {
|
||||
QString filename = modZip.getCurrentFileName();
|
||||
if (filter && !filter(filename))
|
||||
{
|
||||
qDebug() << "Skipping file " << filename << " from "
|
||||
<< from.fileName() << " - filtered";
|
||||
if (filter && !filter(filename)) {
|
||||
qDebug() << "Skipping file " << filename << " from " << from.fileName() << " - filtered";
|
||||
continue;
|
||||
}
|
||||
if (contained.contains(filename))
|
||||
{
|
||||
qDebug() << "Skipping already contained file " << filename << " from "
|
||||
<< from.fileName();
|
||||
if (contained.contains(filename)) {
|
||||
qDebug() << "Skipping already contained file " << filename << " from " << from.fileName();
|
||||
continue;
|
||||
}
|
||||
contained.insert(filename);
|
||||
|
||||
if (!fileInsideMod.open(QIODevice::ReadOnly))
|
||||
{
|
||||
if (!fileInsideMod.open(QIODevice::ReadOnly)) {
|
||||
qCritical() << "Failed to open " << filename << " from " << from.fileName();
|
||||
return false;
|
||||
}
|
||||
|
||||
QuaZipNewInfo info_out(fileInsideMod.getActualFileName());
|
||||
|
||||
if (!zipOutFile.open(QIODevice::WriteOnly, info_out))
|
||||
{
|
||||
if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) {
|
||||
qCritical() << "Failed to open " << filename << " in the jar";
|
||||
fileInsideMod.close();
|
||||
return false;
|
||||
}
|
||||
if (!JlCompress::copyData(fileInsideMod, zipOutFile))
|
||||
{
|
||||
if (!JlCompress::copyData(fileInsideMod, zipOutFile)) {
|
||||
zipOutFile.close();
|
||||
fileInsideMod.close();
|
||||
qCritical() << "Failed to copy data of " << filename << " into the jar";
|
||||
@ -94,10 +93,11 @@ bool MMCZip::mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &containe
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks)
|
||||
bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks)
|
||||
{
|
||||
QDir directory(dir);
|
||||
if (!directory.exists()) return false;
|
||||
if (!directory.exists())
|
||||
return false;
|
||||
|
||||
for (auto e : files) {
|
||||
auto filePath = directory.relativeFilePath(e.absoluteFilePath());
|
||||
@ -109,17 +109,18 @@ bool MMCZip::compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, boo
|
||||
srcPath = e.canonicalFilePath();
|
||||
}
|
||||
}
|
||||
if( !JlCompress::compressFile(zip, srcPath, filePath)) return false;
|
||||
if (!JlCompress::compressFile(zip, srcPath, filePath))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
|
||||
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks)
|
||||
{
|
||||
QuaZip zip(fileCompressed);
|
||||
QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
|
||||
if(!zip.open(QuaZip::mdCreate)) {
|
||||
if (!zip.open(QuaZip::mdCreate)) {
|
||||
QFile::remove(fileCompressed);
|
||||
return false;
|
||||
}
|
||||
@ -127,7 +128,7 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
|
||||
auto result = compressDirFiles(&zip, dir, files, followSymlinks);
|
||||
|
||||
zip.close();
|
||||
if(zip.getZipError()!=0) {
|
||||
if (zip.getZipError() != 0) {
|
||||
QFile::remove(fileCompressed);
|
||||
return false;
|
||||
}
|
||||
@ -135,12 +136,12 @@ bool MMCZip::compressDirFiles(QString fileCompressed, QString dir, QFileInfoList
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
// ours
|
||||
bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods)
|
||||
{
|
||||
QuaZip zipOut(targetJarPath);
|
||||
if (!zipOut.open(QuaZip::mdCreate))
|
||||
{
|
||||
if (!zipOut.open(QuaZip::mdCreate)) {
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to open the minecraft.jar for modding";
|
||||
return false;
|
||||
@ -151,37 +152,29 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
|
||||
// Modify the jar
|
||||
// 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++)
|
||||
{
|
||||
for (auto i = mods.crbegin(); i != mods.crend(); i++) {
|
||||
const auto* mod = *i;
|
||||
// do not merge disabled mods.
|
||||
if (!mod->enabled())
|
||||
continue;
|
||||
if (mod->type() == ResourceType::ZIPFILE)
|
||||
{
|
||||
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles))
|
||||
{
|
||||
if (mod->type() == ResourceType::ZIPFILE) {
|
||||
if (!mergeZipFiles(&zipOut, mod->fileinfo(), addedFiles)) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (mod->type() == ResourceType::SINGLEFILE)
|
||||
{
|
||||
} else if (mod->type() == ResourceType::SINGLEFILE) {
|
||||
// FIXME: buggy - does not work with addedFiles
|
||||
auto filename = mod->fileinfo();
|
||||
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName()))
|
||||
{
|
||||
if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
addedFiles.insert(filename.fileName());
|
||||
}
|
||||
else if (mod->type() == ResourceType::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->fileinfo();
|
||||
@ -190,25 +183,21 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
dir.cdUp();
|
||||
QString parent_dir = dir.absolutePath();
|
||||
auto files = QFileInfoList();
|
||||
MMCZip::collectFileListRecursively(what_to_zip, nullptr, &files, nullptr);
|
||||
collectFileListRecursively(what_to_zip, nullptr, &files, nullptr);
|
||||
|
||||
for (auto e : files) {
|
||||
if (addedFiles.contains(e.filePath()))
|
||||
files.removeAll(e);
|
||||
}
|
||||
|
||||
if (!MMCZip::compressDirFiles(&zipOut, parent_dir, files))
|
||||
{
|
||||
if (!compressDirFiles(&zipOut, parent_dir, files)) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to add" << mod->fileinfo().fileName() << "to the jar.";
|
||||
return false;
|
||||
}
|
||||
qDebug() << "Adding folder " << filename.fileName() << " from "
|
||||
<< filename.absoluteFilePath();
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath();
|
||||
} else {
|
||||
// Make sure we do not continue launching when something is missing or undefined...
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
@ -217,8 +206,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
}
|
||||
}
|
||||
|
||||
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key){return !key.contains("META-INF");}))
|
||||
{
|
||||
if (!mergeZipFiles(&zipOut, QFileInfo(sourceJarPath), addedFiles, [](const QString key) { return !key.contains("META-INF"); })) {
|
||||
zipOut.close();
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to insert minecraft.jar contents.";
|
||||
@ -227,17 +215,17 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const
|
||||
|
||||
// Recompress the jar
|
||||
zipOut.close();
|
||||
if (zipOut.getZipError() != 0)
|
||||
{
|
||||
if (zipOut.getZipError() != 0) {
|
||||
QFile::remove(targetJarPath);
|
||||
qCritical() << "Failed to finalize minecraft.jar!";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ours
|
||||
QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root)
|
||||
QString findFolderOfFileInZip(QuaZip* zip, const QString& what, const QStringList& ignore_paths, const QString& root)
|
||||
{
|
||||
QuaZipDir rootDir(zip, root);
|
||||
for (auto&& fileName : rootDir.entryList(QDir::Files)) {
|
||||
@ -261,27 +249,23 @@ QString MMCZip::findFolderOfFileInZip(QuaZip* zip, const QString& what, const QS
|
||||
}
|
||||
|
||||
// ours
|
||||
bool MMCZip::findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root)
|
||||
bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, 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) {
|
||||
result.append(root);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for(auto fileName: rootDir.entryList(QDir::Dirs))
|
||||
{
|
||||
for (auto fileName : rootDir.entryList(QDir::Dirs)) {
|
||||
findFilesInZip(zip, what, result, root + fileName);
|
||||
}
|
||||
return !result.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
// ours
|
||||
std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & subdir, const QString &target)
|
||||
std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, const QString& target)
|
||||
{
|
||||
auto target_top_dir = QUrl::fromLocalFile(target);
|
||||
|
||||
@ -289,16 +273,13 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
|
||||
|
||||
qDebug() << "Extracting subdir" << subdir << "from" << zip->getZipName() << "to" << target;
|
||||
auto numEntries = zip->getEntriesCount();
|
||||
if(numEntries < 0) {
|
||||
if (numEntries < 0) {
|
||||
qWarning() << "Failed to enumerate files in archive";
|
||||
return std::nullopt;
|
||||
}
|
||||
else if(numEntries == 0) {
|
||||
} else if (numEntries == 0) {
|
||||
qDebug() << "Extracting empty archives seems odd...";
|
||||
return extracted;
|
||||
}
|
||||
else if (!zip->goToFirstFile())
|
||||
{
|
||||
} else if (!zip->goToFirstFile()) {
|
||||
qWarning() << "Failed to seek to first file in zip";
|
||||
return std::nullopt;
|
||||
}
|
||||
@ -334,7 +315,8 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
|
||||
}
|
||||
|
||||
if (!target_top_dir.isParentOf(QUrl::fromLocalFile(target_file_path))) {
|
||||
qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path" << target;
|
||||
qWarning() << "Extracting" << relative_file_name << "was cancelled, because it was effectively outside of the target path"
|
||||
<< target;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -345,7 +327,8 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
|
||||
}
|
||||
|
||||
extracted.append(target_file_path);
|
||||
QFile::setPermissions(target_file_path, QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
|
||||
QFile::setPermissions(target_file_path,
|
||||
QFileDevice::Permission::ReadUser | QFileDevice::Permission::WriteUser | QFileDevice::Permission::ExeUser);
|
||||
|
||||
qDebug() << "Extracted file" << relative_file_name << "to" << target_file_path;
|
||||
} while (zip->goToNextFile());
|
||||
@ -354,66 +337,66 @@ std::optional<QStringList> MMCZip::extractSubDir(QuaZip *zip, const QString & su
|
||||
}
|
||||
|
||||
// ours
|
||||
bool MMCZip::extractRelFile(QuaZip *zip, const QString &file, const QString &target)
|
||||
bool extractRelFile(QuaZip* zip, const QString& file, const QString& target)
|
||||
{
|
||||
return JlCompress::extractFile(zip, file, target);
|
||||
}
|
||||
|
||||
// ours
|
||||
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString dir)
|
||||
std::optional<QStringList> extractDir(QString fileCompressed, QString dir)
|
||||
{
|
||||
QuaZip zip(fileCompressed);
|
||||
if (!zip.open(QuaZip::mdUnzip))
|
||||
{
|
||||
if (!zip.open(QuaZip::mdUnzip)) {
|
||||
// check if this is a minimum size empty zip file...
|
||||
QFileInfo fileInfo(fileCompressed);
|
||||
if(fileInfo.size() == 22) {
|
||||
if (fileInfo.size() == 22) {
|
||||
return QStringList();
|
||||
}
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
;
|
||||
return std::nullopt;
|
||||
}
|
||||
return MMCZip::extractSubDir(&zip, "", dir);
|
||||
return extractSubDir(&zip, "", dir);
|
||||
}
|
||||
|
||||
// ours
|
||||
std::optional<QStringList> MMCZip::extractDir(QString fileCompressed, QString subdir, QString dir)
|
||||
std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir)
|
||||
{
|
||||
QuaZip zip(fileCompressed);
|
||||
if (!zip.open(QuaZip::mdUnzip))
|
||||
{
|
||||
if (!zip.open(QuaZip::mdUnzip)) {
|
||||
// check if this is a minimum size empty zip file...
|
||||
QFileInfo fileInfo(fileCompressed);
|
||||
if(fileInfo.size() == 22) {
|
||||
if (fileInfo.size() == 22) {
|
||||
return QStringList();
|
||||
}
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();;
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
;
|
||||
return std::nullopt;
|
||||
}
|
||||
return MMCZip::extractSubDir(&zip, subdir, dir);
|
||||
return extractSubDir(&zip, subdir, dir);
|
||||
}
|
||||
|
||||
// ours
|
||||
bool MMCZip::extractFile(QString fileCompressed, QString file, QString target)
|
||||
bool extractFile(QString fileCompressed, QString file, QString target)
|
||||
{
|
||||
QuaZip zip(fileCompressed);
|
||||
if (!zip.open(QuaZip::mdUnzip))
|
||||
{
|
||||
if (!zip.open(QuaZip::mdUnzip)) {
|
||||
// check if this is a minimum size empty zip file...
|
||||
QFileInfo fileInfo(fileCompressed);
|
||||
if(fileInfo.size() == 22) {
|
||||
if (fileInfo.size() == 22) {
|
||||
return true;
|
||||
}
|
||||
qWarning() << "Could not open archive for unzipping:" << fileCompressed << "Error:" << zip.getZipError();
|
||||
return false;
|
||||
}
|
||||
return MMCZip::extractRelFile(&zip, file, target);
|
||||
return extractRelFile(&zip, file, target);
|
||||
}
|
||||
|
||||
bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList *files,
|
||||
MMCZip::FilterFunction excludeFilter) {
|
||||
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter)
|
||||
{
|
||||
QDir rootDirectory(rootDir);
|
||||
if (!rootDirectory.exists()) return false;
|
||||
if (!rootDirectory.exists())
|
||||
return false;
|
||||
|
||||
QDir directory;
|
||||
if (subDir == nullptr)
|
||||
@ -421,25 +404,109 @@ bool MMCZip::collectFileListRecursively(const QString& rootDir, const QString& s
|
||||
else
|
||||
directory = QDir(subDir);
|
||||
|
||||
if (!directory.exists()) return false; // shouldn't ever happen
|
||||
if (!directory.exists())
|
||||
return false; // shouldn't ever happen
|
||||
|
||||
// recurse directories
|
||||
QFileInfoList entries = directory.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::Hidden);
|
||||
for (const auto& e: entries) {
|
||||
for (const auto& e : entries) {
|
||||
if (!collectFileListRecursively(rootDir, e.filePath(), files, excludeFilter))
|
||||
return false;
|
||||
}
|
||||
|
||||
// collect files
|
||||
entries = directory.entryInfoList(QDir::Files);
|
||||
for (const auto& e: entries) {
|
||||
for (const auto& e : entries) {
|
||||
QString relativeFilePath = rootDirectory.relativeFilePath(e.absoluteFilePath());
|
||||
if (excludeFilter && excludeFilter(relativeFilePath)) {
|
||||
qDebug() << "Skipping file " << relativeFilePath;
|
||||
continue;
|
||||
}
|
||||
|
||||
files->append(e); // we want the original paths for MMCZip::compressDirFiles
|
||||
files->append(e); // we want the original paths for compressDirFiles
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
void ExportToZipTask::executeTask()
|
||||
{
|
||||
setStatus("Adding files...");
|
||||
setProgress(0, m_files.length());
|
||||
m_build_zip_future = QtConcurrent::run(QThreadPool::globalInstance(), [this]() { return exportZip(); });
|
||||
connect(&m_build_zip_watcher, &QFutureWatcher<ZipResult>::finished, this, &ExportToZipTask::finish);
|
||||
m_build_zip_watcher.setFuture(m_build_zip_future);
|
||||
}
|
||||
|
||||
auto ExportToZipTask::exportZip() -> ZipResult
|
||||
{
|
||||
if (!m_dir.exists()) {
|
||||
return ZipResult(tr("Folder doesn't exist"));
|
||||
}
|
||||
if (!m_output.isOpen() && !m_output.open(QuaZip::mdCreate)) {
|
||||
return ZipResult(tr("Could not create file"));
|
||||
}
|
||||
|
||||
for (auto fileName : m_extra_files.keys()) {
|
||||
if (m_build_zip_future.isCanceled())
|
||||
return ZipResult();
|
||||
QuaZipFile indexFile(&m_output);
|
||||
if (!indexFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileName))) {
|
||||
return ZipResult(tr("Could not create:") + fileName);
|
||||
}
|
||||
indexFile.write(m_extra_files[fileName]);
|
||||
}
|
||||
|
||||
for (const QFileInfo& file : m_files) {
|
||||
if (m_build_zip_future.isCanceled())
|
||||
return ZipResult();
|
||||
|
||||
auto absolute = file.absoluteFilePath();
|
||||
auto relative = m_dir.relativeFilePath(absolute);
|
||||
setStatus("Compresing: " + relative);
|
||||
setProgress(m_progress + 1, m_progressTotal);
|
||||
if (m_follow_symlinks) {
|
||||
if (file.isSymLink())
|
||||
absolute = file.symLinkTarget();
|
||||
else
|
||||
absolute = file.canonicalFilePath();
|
||||
}
|
||||
|
||||
if (!m_exclude_files.contains(relative) && !JlCompress::compressFile(&m_output, absolute, m_destination_prefix + relative)) {
|
||||
return ZipResult(tr("Could not read and compress %1").arg(relative));
|
||||
}
|
||||
}
|
||||
|
||||
m_output.close();
|
||||
if (m_output.getZipError() != 0) {
|
||||
return ZipResult(tr("A zip error occurred"));
|
||||
}
|
||||
return ZipResult();
|
||||
}
|
||||
|
||||
void ExportToZipTask::finish()
|
||||
{
|
||||
if (m_build_zip_future.isCanceled()) {
|
||||
QFile::remove(m_output_path);
|
||||
emitAborted();
|
||||
} else if (auto result = m_build_zip_future.result(); result.has_value()) {
|
||||
QFile::remove(m_output_path);
|
||||
emitFailed(result.value());
|
||||
} else {
|
||||
emitSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
bool ExportToZipTask::abort()
|
||||
{
|
||||
if (m_build_zip_future.isRunning()) {
|
||||
m_build_zip_future.cancel();
|
||||
// NOTE: Here we don't do `emitAborted()` because it will be done when `m_build_zip_future` actually cancels, which may not occur
|
||||
// immediately.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace MMCZip
|
||||
|
@ -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) 2023 Trial97 <alexandru.tripon97@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -35,110 +36,163 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QFileInfo>
|
||||
#include <QSet>
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include <functional>
|
||||
|
||||
#include <quazip.h>
|
||||
#include <quazip/JlCompress.h>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
namespace MMCZip
|
||||
{
|
||||
using FilterFunction = std::function<bool(const QString &)>;
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#endif
|
||||
#include "tasks/Task.h"
|
||||
|
||||
/**
|
||||
* Merge two zip files, using a filter function
|
||||
*/
|
||||
bool mergeZipFiles(QuaZip *into, QFileInfo from, QSet<QString> &contained,
|
||||
const FilterFunction filter = nullptr);
|
||||
namespace MMCZip {
|
||||
using FilterFunction = std::function<bool(const QString&)>;
|
||||
|
||||
/**
|
||||
* Compress directory, by providing a list of files to compress
|
||||
* \param zip target archive
|
||||
* \param dir directory that will be compressed (to compress with relative paths)
|
||||
* \param files list of files to compress
|
||||
* \param followSymlinks should follow symlinks when compressing file data
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool compressDirFiles(QuaZip *zip, QString dir, QFileInfoList files, bool followSymlinks = false);
|
||||
/**
|
||||
* Merge two zip files, using a filter function
|
||||
*/
|
||||
bool mergeZipFiles(QuaZip* into, QFileInfo from, QSet<QString>& contained, const FilterFunction filter = nullptr);
|
||||
|
||||
/**
|
||||
* Compress directory, by providing a list of files to compress
|
||||
* \param fileCompressed target archive file
|
||||
* \param dir directory that will be compressed (to compress with relative paths)
|
||||
* \param files list of files to compress
|
||||
* \param followSymlinks should follow symlinks when compressing file data
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
|
||||
/**
|
||||
* Compress directory, by providing a list of files to compress
|
||||
* \param zip target archive
|
||||
* \param dir directory that will be compressed (to compress with relative paths)
|
||||
* \param files list of files to compress
|
||||
* \param followSymlinks should follow symlinks when compressing file data
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool compressDirFiles(QuaZip* zip, QString dir, QFileInfoList files, bool followSymlinks = false);
|
||||
|
||||
/**
|
||||
* take a source jar, add mods to it, resulting in target jar
|
||||
*/
|
||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
|
||||
/**
|
||||
* Compress directory, by providing a list of files to compress
|
||||
* \param fileCompressed target archive file
|
||||
* \param dir directory that will be compressed (to compress with relative paths)
|
||||
* \param files list of files to compress
|
||||
* \param followSymlinks should follow symlinks when compressing file data
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool compressDirFiles(QString fileCompressed, QString dir, QFileInfoList files, bool followSymlinks = false);
|
||||
|
||||
/**
|
||||
* 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 QStringList& ignore_paths = {}, const QString &root = QString(""));
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
/**
|
||||
* take a source jar, add mods to it, resulting in target jar
|
||||
*/
|
||||
bool createModdedJar(QString sourceJarPath, QString targetJarPath, const QList<Mod*>& mods);
|
||||
#endif
|
||||
/**
|
||||
* 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 QStringList& ignore_paths = {}, const QString& root = QString(""));
|
||||
|
||||
/**
|
||||
* Find a multiple files of the same name in archive by file name
|
||||
* If a file is found in a path, no deeper paths are searched
|
||||
*
|
||||
* \return true if anything was found
|
||||
*/
|
||||
bool findFilesInZip(QuaZip * zip, const QString & what, QStringList & result, const QString &root = QString());
|
||||
/**
|
||||
* Find a multiple files of the same name in archive by file name
|
||||
* If a file is found in a path, no deeper paths are searched
|
||||
*
|
||||
* \return true if anything was found
|
||||
*/
|
||||
bool findFilesInZip(QuaZip* zip, const QString& what, QStringList& result, const QString& root = QString());
|
||||
|
||||
/**
|
||||
* Extract a subdirectory from an archive
|
||||
*/
|
||||
std::optional<QStringList> extractSubDir(QuaZip *zip, const QString & subdir, const QString &target);
|
||||
/**
|
||||
* Extract a subdirectory from an archive
|
||||
*/
|
||||
std::optional<QStringList> extractSubDir(QuaZip* zip, const QString& subdir, const QString& target);
|
||||
|
||||
bool extractRelFile(QuaZip *zip, const QString & file, const QString &target);
|
||||
bool extractRelFile(QuaZip* zip, const QString& file, const QString& target);
|
||||
|
||||
/**
|
||||
* Extract a whole archive.
|
||||
*
|
||||
* \param fileCompressed The name of the archive.
|
||||
* \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.
|
||||
*/
|
||||
std::optional<QStringList> extractDir(QString fileCompressed, QString dir);
|
||||
/**
|
||||
* Extract a whole archive.
|
||||
*
|
||||
* \param fileCompressed The name of the archive.
|
||||
* \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.
|
||||
*/
|
||||
std::optional<QStringList> extractDir(QString fileCompressed, QString dir);
|
||||
|
||||
/**
|
||||
* Extract a subdirectory from an archive
|
||||
*
|
||||
* \param fileCompressed The name of the archive.
|
||||
* \param subdir The directory within the archive to extract
|
||||
* \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.
|
||||
*/
|
||||
std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir);
|
||||
/**
|
||||
* Extract a subdirectory from an archive
|
||||
*
|
||||
* \param fileCompressed The name of the archive.
|
||||
* \param subdir The directory within the archive to extract
|
||||
* \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.
|
||||
*/
|
||||
std::optional<QStringList> extractDir(QString fileCompressed, QString subdir, QString dir);
|
||||
|
||||
/**
|
||||
* Extract a single file from an archive into a directory
|
||||
*
|
||||
* \param fileCompressed The name of the archive.
|
||||
* \param file The file within the archive to extract
|
||||
* \param dir The directory to extract to, the current directory if left empty.
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool extractFile(QString fileCompressed, QString file, QString dir);
|
||||
/**
|
||||
* Extract a single file from an archive into a directory
|
||||
*
|
||||
* \param fileCompressed The name of the archive.
|
||||
* \param file The file within the archive to extract
|
||||
* \param dir The directory to extract to, the current directory if left empty.
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool extractFile(QString fileCompressed, QString file, QString dir);
|
||||
|
||||
/**
|
||||
* Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included.
|
||||
* \param rootDir directory to start off
|
||||
* \param subDir subdirectory, should be nullptr for first invocation
|
||||
* \param files resulting list of QFileInfo
|
||||
* \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude)
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool collectFileListRecursively(const QString &rootDir, const QString &subDir, QFileInfoList *files, FilterFunction excludeFilter);
|
||||
}
|
||||
/**
|
||||
* Populate a QFileInfoList with a directory tree recursively, while allowing to excludeFilter what shouldn't be included.
|
||||
* \param rootDir directory to start off
|
||||
* \param subDir subdirectory, should be nullptr for first invocation
|
||||
* \param files resulting list of QFileInfo
|
||||
* \param excludeFilter function to excludeFilter which files shouldn't be included (returning true means to excude)
|
||||
* \return true for success or false for failure
|
||||
*/
|
||||
bool collectFileListRecursively(const QString& rootDir, const QString& subDir, QFileInfoList* files, FilterFunction excludeFilter);
|
||||
|
||||
#if defined(LAUNCHER_APPLICATION)
|
||||
class ExportToZipTask : public Task {
|
||||
public:
|
||||
ExportToZipTask(QString outputPath, QDir dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
||||
: m_output_path(outputPath)
|
||||
, m_output(outputPath)
|
||||
, m_dir(dir)
|
||||
, m_files(files)
|
||||
, m_destination_prefix(destinationPrefix)
|
||||
, m_follow_symlinks(followSymlinks)
|
||||
{
|
||||
setAbortable(true);
|
||||
};
|
||||
ExportToZipTask(QString outputPath, QString dir, QFileInfoList files, QString destinationPrefix = "", bool followSymlinks = false)
|
||||
: ExportToZipTask(outputPath, QDir(dir), files, destinationPrefix, followSymlinks){};
|
||||
|
||||
virtual ~ExportToZipTask() = default;
|
||||
|
||||
void setExcludeFiles(QStringList excludeFiles) { m_exclude_files = excludeFiles; }
|
||||
void addExtraFile(QString fileName, QByteArray data) { m_extra_files.insert(fileName, data); }
|
||||
|
||||
using ZipResult = std::optional<QString>;
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
bool abort() override;
|
||||
|
||||
ZipResult exportZip();
|
||||
void finish();
|
||||
|
||||
private:
|
||||
QString m_output_path;
|
||||
QuaZip m_output;
|
||||
QDir m_dir;
|
||||
QFileInfoList m_files;
|
||||
QString m_destination_prefix;
|
||||
bool m_follow_symlinks;
|
||||
QStringList m_exclude_files;
|
||||
QHash<QString, QByteArray> m_extra_files;
|
||||
|
||||
QFuture<ZipResult> m_build_zip_future;
|
||||
QFutureWatcher<ZipResult> m_build_zip_watcher;
|
||||
};
|
||||
#endif
|
||||
} // namespace MMCZip
|
||||
|
@ -1,8 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QPixmapCache>
|
||||
#include <QThread>
|
||||
#include <QTime>
|
||||
#include <limits>
|
||||
|
||||
#define GET_TYPE() \
|
||||
Qt::ConnectionType type; \
|
||||
@ -60,6 +63,8 @@ class PixmapCache final : public QObject {
|
||||
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)
|
||||
DEFINE_FUNC_NO_PARAM(markCacheMissByEviciton, bool)
|
||||
DEFINE_FUNC_ONE_PARAM(setFastEvictionThreshold, bool, int)
|
||||
|
||||
// NOTE: Every function returns something non-void to simplify the macros.
|
||||
private slots:
|
||||
@ -90,6 +95,53 @@ class PixmapCache final : public QObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark that a cache miss occurred because of a eviction if too many of these occur too fast the cache size is increased
|
||||
* @return if the cache size was increased
|
||||
*/
|
||||
bool _markCacheMissByEviciton()
|
||||
{
|
||||
static constexpr uint maxInt = static_cast<uint>(std::numeric_limits<int>::max());
|
||||
static constexpr uint step = 10240;
|
||||
static constexpr int oneSecond = 1000;
|
||||
|
||||
auto now = QTime::currentTime();
|
||||
if (!m_last_cache_miss_by_eviciton.isNull()) {
|
||||
auto diff = m_last_cache_miss_by_eviciton.msecsTo(now);
|
||||
if (diff < oneSecond) { // less than a second ago
|
||||
++m_consecutive_fast_evicitons;
|
||||
} else {
|
||||
m_consecutive_fast_evicitons = 0;
|
||||
}
|
||||
}
|
||||
m_last_cache_miss_by_eviciton = now;
|
||||
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
|
||||
// increase the cache size
|
||||
uint newSize = _cacheLimit() + step;
|
||||
if (newSize >= maxInt) { // increase it until you overflow :D
|
||||
newSize = maxInt;
|
||||
qDebug() << m_consecutive_fast_evicitons
|
||||
<< tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit");
|
||||
} else {
|
||||
qDebug() << m_consecutive_fast_evicitons
|
||||
<< tr("pixmap cache misses by eviction happened too fast, increasing cache size to") << static_cast<int>(newSize);
|
||||
}
|
||||
_setCacheLimit(static_cast<int>(newSize));
|
||||
m_consecutive_fast_evicitons = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool _setFastEvictionThreshold(int threshold)
|
||||
{
|
||||
m_consecutive_fast_evicitons_threshold = threshold;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static PixmapCache* s_instance;
|
||||
QTime m_last_cache_miss_by_eviciton;
|
||||
int m_consecutive_fast_evicitons = 0;
|
||||
int m_consecutive_fast_evicitons_threshold = 15;
|
||||
};
|
||||
|
@ -16,15 +16,25 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QSysInfo>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "MangoHud.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Json.h"
|
||||
#include "MangoHud.h"
|
||||
|
||||
#ifdef __GLIBC__
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#define UNDEF_GNU_SOURCE
|
||||
#endif
|
||||
#include <dlfcn.h>
|
||||
#include <linux/limits.h>
|
||||
#endif
|
||||
|
||||
namespace MangoHud {
|
||||
|
||||
@ -106,4 +116,37 @@ QString getLibraryString()
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString findLibrary(QString libName)
|
||||
{
|
||||
#ifdef __GLIBC__
|
||||
const char* library = libName.toLocal8Bit().constData();
|
||||
|
||||
void* handle = dlopen(library, RTLD_NOW);
|
||||
if (!handle) {
|
||||
qCritical() << "dlopen() failed:" << dlerror();
|
||||
return {};
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
if (dlinfo(handle, RTLD_DI_ORIGIN, path) == -1) {
|
||||
qCritical() << "dlinfo() failed:" << dlerror();
|
||||
dlclose(handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto fullPath = FS::PathCombine(QString(path), libName);
|
||||
|
||||
dlclose(handle);
|
||||
return fullPath;
|
||||
#else
|
||||
qWarning() << "MangoHud::findLibrary is not implemented on this platform";
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
} // namespace MangoHud
|
||||
|
||||
#ifdef UNDEF_GNU_SOURCE
|
||||
#undef _GNU_SOURCE
|
||||
#undef UNDEF_GNU_SOURCE
|
||||
#endif
|
||||
|
@ -24,4 +24,6 @@
|
||||
namespace MangoHud {
|
||||
|
||||
QString getLibraryString();
|
||||
}
|
||||
|
||||
QString findLibrary(QString libName);
|
||||
} // namespace MangoHud
|
||||
|
31
launcher/Markdown.cpp
Normal file
31
launcher/Markdown.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2023 Joshua Goins <josh@redstrate.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Markdown.h"
|
||||
|
||||
QString markdownToHTML(const QString& markdown)
|
||||
{
|
||||
const QByteArray markdownData = markdown.toUtf8();
|
||||
char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE);
|
||||
|
||||
QString htmlStr(buffer);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return htmlStr;
|
||||
}
|
@ -18,17 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <cmark.h>
|
||||
#include <QString>
|
||||
|
||||
static QString markdownToHTML(const QString& markdown)
|
||||
{
|
||||
const QByteArray markdownData = markdown.toUtf8();
|
||||
char* buffer = cmark_markdown_to_html(markdownData.constData(), markdownData.length(), CMARK_OPT_NOBREAKS | CMARK_OPT_UNSAFE);
|
||||
|
||||
QString htmlStr(buffer);
|
||||
|
||||
free(buffer);
|
||||
|
||||
return htmlStr;
|
||||
}
|
||||
QString markdownToHTML(const QString& markdown);
|
@ -22,12 +22,11 @@ MessageLevel::Enum MessageLevel::getLevel(const QString& levelName)
|
||||
return MessageLevel::Unknown;
|
||||
}
|
||||
|
||||
MessageLevel::Enum MessageLevel::fromLine(QString &line)
|
||||
MessageLevel::Enum MessageLevel::fromLine(QString& line)
|
||||
{
|
||||
// Level prefix
|
||||
int endmark = line.indexOf("]!");
|
||||
if (line.startsWith("!![") && endmark != -1)
|
||||
{
|
||||
if (line.startsWith("!![") && endmark != -1) {
|
||||
auto level = MessageLevel::getLevel(line.left(endmark).mid(3));
|
||||
line = line.mid(endmark + 2);
|
||||
return level;
|
||||
|
@ -6,23 +6,21 @@
|
||||
* @brief the MessageLevel Enum
|
||||
* defines what level a log message is
|
||||
*/
|
||||
namespace MessageLevel
|
||||
{
|
||||
enum Enum
|
||||
{
|
||||
Unknown, /**< No idea what this is or where it came from */
|
||||
StdOut, /**< Undetermined stderr messages */
|
||||
StdErr, /**< Undetermined stdout messages */
|
||||
namespace MessageLevel {
|
||||
enum Enum {
|
||||
Unknown, /**< No idea what this is or where it came from */
|
||||
StdOut, /**< Undetermined stderr messages */
|
||||
StdErr, /**< Undetermined stdout messages */
|
||||
Launcher, /**< Launcher Messages */
|
||||
Debug, /**< Debug Messages */
|
||||
Info, /**< Info Messages */
|
||||
Message, /**< Standard Messages */
|
||||
Warning, /**< Warnings */
|
||||
Error, /**< Errors */
|
||||
Fatal, /**< Fatal Errors */
|
||||
Debug, /**< Debug Messages */
|
||||
Info, /**< Info Messages */
|
||||
Message, /**< Standard Messages */
|
||||
Warning, /**< Warnings */
|
||||
Error, /**< Errors */
|
||||
Fatal, /**< Fatal Errors */
|
||||
};
|
||||
MessageLevel::Enum getLevel(const QString &levelName);
|
||||
MessageLevel::Enum getLevel(const QString& levelName);
|
||||
|
||||
/* Get message level from a line. Line is modified if it was successful. */
|
||||
MessageLevel::Enum fromLine(QString &line);
|
||||
}
|
||||
MessageLevel::Enum fromLine(QString& line);
|
||||
} // namespace MessageLevel
|
||||
|
@ -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) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -37,88 +38,39 @@
|
||||
#include "BaseInstance.h"
|
||||
#include "launch/LaunchTask.h"
|
||||
|
||||
class NullInstance: public BaseInstance
|
||||
{
|
||||
class NullInstance : public BaseInstance {
|
||||
Q_OBJECT
|
||||
public:
|
||||
public:
|
||||
NullInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr settings, const QString& rootDir)
|
||||
:BaseInstance(globalSettings, settings, rootDir)
|
||||
: BaseInstance(globalSettings, settings, rootDir)
|
||||
{
|
||||
setVersionBroken(true);
|
||||
}
|
||||
virtual ~NullInstance() {};
|
||||
void saveNow() override
|
||||
{
|
||||
}
|
||||
void loadSpecificSettings() override
|
||||
{
|
||||
setSpecificSettingsLoaded(true);
|
||||
}
|
||||
QString getStatusbarDescription() override
|
||||
{
|
||||
return tr("Unknown instance type");
|
||||
};
|
||||
QSet< QString > traits() const override
|
||||
{
|
||||
return {};
|
||||
};
|
||||
QString instanceConfigFolder() const override
|
||||
{
|
||||
return instanceRoot();
|
||||
};
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
QProcessEnvironment createEnvironment() override
|
||||
{
|
||||
return QProcessEnvironment();
|
||||
}
|
||||
QProcessEnvironment createLaunchEnvironment() override
|
||||
{
|
||||
return QProcessEnvironment();
|
||||
}
|
||||
QMap<QString, QString> getVariables() override
|
||||
{
|
||||
return QMap<QString, QString>();
|
||||
}
|
||||
IPathMatcher::Ptr getLogFileMatcher() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
QString getLogFileRoot() override
|
||||
{
|
||||
return instanceRoot();
|
||||
}
|
||||
QString typeName() const override
|
||||
{
|
||||
return "Null";
|
||||
}
|
||||
bool canExport() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool canEdit() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool canLaunch() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual ~NullInstance(){};
|
||||
void saveNow() override {}
|
||||
void loadSpecificSettings() override { setSpecificSettingsLoaded(true); }
|
||||
QString getStatusbarDescription() override { return tr("Unknown instance type"); };
|
||||
QSet<QString> traits() const override { return {}; };
|
||||
QString instanceConfigFolder() const override { return instanceRoot(); };
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override { return nullptr; }
|
||||
shared_qobject_ptr<Task> createUpdateTask([[maybe_unused]] Net::Mode mode) override { return nullptr; }
|
||||
QProcessEnvironment createEnvironment() override { return QProcessEnvironment(); }
|
||||
QProcessEnvironment createLaunchEnvironment() override { return QProcessEnvironment(); }
|
||||
QMap<QString, QString> getVariables() override { return QMap<QString, QString>(); }
|
||||
IPathMatcher::Ptr getLogFileMatcher() override { return nullptr; }
|
||||
QString getLogFileRoot() override { return instanceRoot(); }
|
||||
QString typeName() const override { return "Null"; }
|
||||
bool canExport() const override { return false; }
|
||||
bool canEdit() const override { return false; }
|
||||
bool canLaunch() const override { return false; }
|
||||
void populateLaunchMenu(QMenu* menu) override {}
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override
|
||||
{
|
||||
QStringList out;
|
||||
out << "Null instance - placeholder.";
|
||||
return out;
|
||||
}
|
||||
QString modsRoot() const override {
|
||||
return QString();
|
||||
}
|
||||
QString modsRoot() const override { return QString(); }
|
||||
void updateRuntimeContext()
|
||||
{
|
||||
// NOOP
|
||||
|
@ -1,47 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
enum class ProblemSeverity
|
||||
{
|
||||
None,
|
||||
Warning,
|
||||
Error
|
||||
};
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
struct PatchProblem
|
||||
{
|
||||
enum class ProblemSeverity { None, Warning, Error };
|
||||
|
||||
struct PatchProblem {
|
||||
ProblemSeverity m_severity;
|
||||
QString m_description;
|
||||
};
|
||||
|
||||
class ProblemProvider
|
||||
{
|
||||
public:
|
||||
virtual ~ProblemProvider() {};
|
||||
class ProblemProvider {
|
||||
public:
|
||||
virtual ~ProblemProvider() {}
|
||||
virtual const QList<PatchProblem> getProblems() const = 0;
|
||||
virtual ProblemSeverity getProblemSeverity() const = 0;
|
||||
};
|
||||
|
||||
class ProblemContainer : public ProblemProvider
|
||||
{
|
||||
public:
|
||||
const QList<PatchProblem> getProblems() const override
|
||||
class ProblemContainer : public ProblemProvider {
|
||||
public:
|
||||
const QList<PatchProblem> getProblems() const override { return m_problems; }
|
||||
ProblemSeverity getProblemSeverity() const override { return m_problemSeverity; }
|
||||
virtual void addProblem(ProblemSeverity severity, const QString& description)
|
||||
{
|
||||
return m_problems;
|
||||
}
|
||||
ProblemSeverity getProblemSeverity() const override
|
||||
{
|
||||
return m_problemSeverity;
|
||||
}
|
||||
virtual void addProblem(ProblemSeverity severity, const QString &description)
|
||||
{
|
||||
if(severity > m_problemSeverity)
|
||||
{
|
||||
if (severity > m_problemSeverity) {
|
||||
m_problemSeverity = severity;
|
||||
}
|
||||
m_problems.append({severity, description});
|
||||
m_problems.append({ severity, description });
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
QList<PatchProblem> m_problems;
|
||||
ProblemSeverity m_problemSeverity = ProblemSeverity::None;
|
||||
};
|
||||
|
@ -36,35 +36,34 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <QVariant>
|
||||
#include <QList>
|
||||
#include <QVariant>
|
||||
|
||||
namespace QVariantUtils {
|
||||
|
||||
template <typename T>
|
||||
inline QList<T> toList(QVariant src) {
|
||||
inline QList<T> toList(QVariant src)
|
||||
{
|
||||
QVariantList variantList = src.toList();
|
||||
|
||||
QList<T> list_t;
|
||||
list_t.reserve(variantList.size());
|
||||
for (const QVariant& v : variantList)
|
||||
{
|
||||
for (const QVariant& v : variantList) {
|
||||
list_t.append(v.value<T>());
|
||||
}
|
||||
return list_t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline QVariant fromList(QList<T> val) {
|
||||
inline QVariant fromList(QList<T> val)
|
||||
{
|
||||
QVariantList variantList;
|
||||
variantList.reserve(val.size());
|
||||
for (const T& v : val)
|
||||
{
|
||||
for (const T& v : val) {
|
||||
variantList.append(v);
|
||||
}
|
||||
|
||||
return variantList;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace QVariantUtils
|
@ -1,13 +1,12 @@
|
||||
#pragma once
|
||||
#include <QWriteLocker>
|
||||
#include <QReadLocker>
|
||||
#include <QMap>
|
||||
#include <QReadLocker>
|
||||
#include <QSet>
|
||||
#include <QWriteLocker>
|
||||
|
||||
template <typename K, typename V>
|
||||
class RWStorage
|
||||
{
|
||||
public:
|
||||
class RWStorage {
|
||||
public:
|
||||
void add(K key, V value)
|
||||
{
|
||||
QWriteLocker l(&lock);
|
||||
@ -17,21 +16,19 @@ public:
|
||||
V get(K key)
|
||||
{
|
||||
QReadLocker l(&lock);
|
||||
if(cache.contains(key))
|
||||
{
|
||||
if (cache.contains(key)) {
|
||||
return cache[key];
|
||||
}
|
||||
else return V();
|
||||
} else
|
||||
return V();
|
||||
}
|
||||
bool get(K key, V& value)
|
||||
{
|
||||
QReadLocker l(&lock);
|
||||
if(cache.contains(key))
|
||||
{
|
||||
if (cache.contains(key)) {
|
||||
value = cache[key];
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
bool has(K key)
|
||||
{
|
||||
@ -41,15 +38,14 @@ public:
|
||||
bool stale(K key)
|
||||
{
|
||||
QReadLocker l(&lock);
|
||||
if(!cache.contains(key))
|
||||
if (!cache.contains(key))
|
||||
return true;
|
||||
return stale_entries.contains(key);
|
||||
}
|
||||
void setStale(K key)
|
||||
{
|
||||
QWriteLocker l(&lock);
|
||||
if(cache.contains(key))
|
||||
{
|
||||
if (cache.contains(key)) {
|
||||
stale_entries.insert(key);
|
||||
}
|
||||
}
|
||||
@ -59,7 +55,8 @@ public:
|
||||
cache.clear();
|
||||
stale_entries.clear();
|
||||
}
|
||||
private:
|
||||
|
||||
private:
|
||||
QReadWriteLock lock;
|
||||
QMap<K, V> cache;
|
||||
QSet<K> stale_entries;
|
||||
|
@ -1,25 +1,21 @@
|
||||
#include "RecursiveFileSystemWatcher.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
|
||||
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject *parent)
|
||||
: QObject(parent), m_watcher(new QFileSystemWatcher(this))
|
||||
RecursiveFileSystemWatcher::RecursiveFileSystemWatcher(QObject* parent) : QObject(parent), m_watcher(new QFileSystemWatcher(this))
|
||||
{
|
||||
connect(m_watcher, &QFileSystemWatcher::fileChanged, this,
|
||||
&RecursiveFileSystemWatcher::fileChange);
|
||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this,
|
||||
&RecursiveFileSystemWatcher::directoryChange);
|
||||
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &RecursiveFileSystemWatcher::fileChange);
|
||||
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &RecursiveFileSystemWatcher::directoryChange);
|
||||
}
|
||||
|
||||
void RecursiveFileSystemWatcher::setRootDir(const QDir &root)
|
||||
void RecursiveFileSystemWatcher::setRootDir(const QDir& root)
|
||||
{
|
||||
bool wasEnabled = m_isEnabled;
|
||||
disable();
|
||||
m_root = root;
|
||||
setFiles(scanRecursive(m_root));
|
||||
if (wasEnabled)
|
||||
{
|
||||
if (wasEnabled) {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
@ -28,16 +24,14 @@ void RecursiveFileSystemWatcher::setWatchFiles(const bool watchFiles)
|
||||
bool wasEnabled = m_isEnabled;
|
||||
disable();
|
||||
m_watchFiles = watchFiles;
|
||||
if (wasEnabled)
|
||||
{
|
||||
if (wasEnabled) {
|
||||
enable();
|
||||
}
|
||||
}
|
||||
|
||||
void RecursiveFileSystemWatcher::enable()
|
||||
{
|
||||
if (m_isEnabled)
|
||||
{
|
||||
if (m_isEnabled) {
|
||||
return;
|
||||
}
|
||||
Q_ASSERT(m_root != QDir::root());
|
||||
@ -46,8 +40,7 @@ void RecursiveFileSystemWatcher::enable()
|
||||
}
|
||||
void RecursiveFileSystemWatcher::disable()
|
||||
{
|
||||
if (!m_isEnabled)
|
||||
{
|
||||
if (!m_isEnabled) {
|
||||
return;
|
||||
}
|
||||
m_isEnabled = false;
|
||||
@ -55,57 +48,49 @@ void RecursiveFileSystemWatcher::disable()
|
||||
m_watcher->removePaths(m_watcher->directories());
|
||||
}
|
||||
|
||||
void RecursiveFileSystemWatcher::setFiles(const QStringList &files)
|
||||
void RecursiveFileSystemWatcher::setFiles(const QStringList& files)
|
||||
{
|
||||
if (files != m_files)
|
||||
{
|
||||
if (files != m_files) {
|
||||
m_files = files;
|
||||
emit filesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir &dir)
|
||||
void RecursiveFileSystemWatcher::addFilesToWatcherRecursive(const QDir& dir)
|
||||
{
|
||||
m_watcher->addPath(dir.absolutePath());
|
||||
for (const QString &directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
|
||||
{
|
||||
for (const QString& directory : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||
addFilesToWatcherRecursive(dir.absoluteFilePath(directory));
|
||||
}
|
||||
if (m_watchFiles)
|
||||
{
|
||||
for (const QFileInfo &info : dir.entryInfoList(QDir::Files))
|
||||
{
|
||||
if (m_watchFiles) {
|
||||
for (const QFileInfo& info : dir.entryInfoList(QDir::Files)) {
|
||||
m_watcher->addPath(info.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir &directory)
|
||||
QStringList RecursiveFileSystemWatcher::scanRecursive(const QDir& directory)
|
||||
{
|
||||
QStringList ret;
|
||||
if(!m_matcher)
|
||||
{
|
||||
if (!m_matcher) {
|
||||
return {};
|
||||
}
|
||||
for (const QString &dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden))
|
||||
{
|
||||
for (const QString& dir : directory.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden)) {
|
||||
ret.append(scanRecursive(directory.absoluteFilePath(dir)));
|
||||
}
|
||||
for (const QString &file : directory.entryList(QDir::Files | QDir::Hidden))
|
||||
{
|
||||
for (const QString& file : directory.entryList(QDir::Files | QDir::Hidden)) {
|
||||
auto relPath = m_root.relativeFilePath(directory.absoluteFilePath(file));
|
||||
if (m_matcher->matches(relPath))
|
||||
{
|
||||
if (m_matcher->matches(relPath)) {
|
||||
ret.append(relPath);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RecursiveFileSystemWatcher::fileChange(const QString &path)
|
||||
void RecursiveFileSystemWatcher::fileChange(const QString& path)
|
||||
{
|
||||
emit fileChanged(path);
|
||||
}
|
||||
void RecursiveFileSystemWatcher::directoryChange(const QString &path)
|
||||
void RecursiveFileSystemWatcher::directoryChange([[maybe_unused]] const QString& path)
|
||||
{
|
||||
setFiles(scanRecursive(m_root));
|
||||
}
|
||||
|
@ -1,61 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QDir>
|
||||
#include <QFileSystemWatcher>
|
||||
#include "pathmatcher/IPathMatcher.h"
|
||||
|
||||
class RecursiveFileSystemWatcher : public QObject
|
||||
{
|
||||
class RecursiveFileSystemWatcher : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RecursiveFileSystemWatcher(QObject *parent);
|
||||
public:
|
||||
RecursiveFileSystemWatcher(QObject* parent);
|
||||
|
||||
void setRootDir(const QDir &root);
|
||||
QDir rootDir() const
|
||||
{
|
||||
return m_root;
|
||||
}
|
||||
void setRootDir(const QDir& root);
|
||||
QDir rootDir() const { return m_root; }
|
||||
|
||||
// WARNING: setting this to true may be bad for performance
|
||||
void setWatchFiles(const bool watchFiles);
|
||||
bool watchFiles() const
|
||||
{
|
||||
return m_watchFiles;
|
||||
}
|
||||
bool watchFiles() const { return m_watchFiles; }
|
||||
|
||||
void setMatcher(IPathMatcher::Ptr matcher)
|
||||
{
|
||||
m_matcher = matcher;
|
||||
}
|
||||
void setMatcher(IPathMatcher::Ptr matcher) { m_matcher = matcher; }
|
||||
|
||||
QStringList files() const
|
||||
{
|
||||
return m_files;
|
||||
}
|
||||
QStringList files() const { return m_files; }
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void filesChanged();
|
||||
void fileChanged(const QString &path);
|
||||
void fileChanged(const QString& path);
|
||||
|
||||
public slots:
|
||||
public slots:
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
private:
|
||||
private:
|
||||
QDir m_root;
|
||||
bool m_watchFiles = false;
|
||||
bool m_isEnabled = false;
|
||||
IPathMatcher::Ptr m_matcher;
|
||||
|
||||
QFileSystemWatcher *m_watcher;
|
||||
QFileSystemWatcher* m_watcher;
|
||||
|
||||
QStringList m_files;
|
||||
void setFiles(const QStringList &files);
|
||||
void setFiles(const QStringList& files);
|
||||
|
||||
void addFilesToWatcherRecursive(const QDir &dir);
|
||||
QStringList scanRecursive(const QDir &dir);
|
||||
void addFilesToWatcherRecursive(const QDir& dir);
|
||||
QStringList scanRecursive(const QDir& dir);
|
||||
|
||||
private slots:
|
||||
void fileChange(const QString &path);
|
||||
void directoryChange(const QString &path);
|
||||
private slots:
|
||||
void fileChange(const QString& path);
|
||||
void directoryChange(const QString& path);
|
||||
};
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "minecraft/mod/ModFolderModel.h"
|
||||
#include "minecraft/mod/ResourceFolderModel.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
||||
ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
ModPlatform::IndexedVersion version,
|
||||
const std::shared_ptr<ResourceFolderModel> packs,
|
||||
@ -51,10 +53,10 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack,
|
||||
}
|
||||
}
|
||||
|
||||
m_filesNetJob->addNetAction(Net::Download::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
||||
m_filesNetJob->addNetAction(Net::ApiDownload::makeFile(m_pack_version.downloadUrl, dir.absoluteFilePath(getFilename())));
|
||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ResourceDownloadTask::downloadSucceeded);
|
||||
connect(m_filesNetJob.get(), &NetJob::progress, this, &ResourceDownloadTask::downloadProgressChanged);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propogateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &ResourceDownloadTask::propagateStepProgress);
|
||||
connect(m_filesNetJob.get(), &NetJob::failed, this, &ResourceDownloadTask::downloadFailed);
|
||||
|
||||
addTask(m_filesNetJob);
|
||||
|
@ -38,6 +38,8 @@ class ResourceDownloadTask : public SequentialTask {
|
||||
const QString& getFilename() const { return m_pack_version.fileName; }
|
||||
const QString& getCustomPath() const { return m_custom_target_folder; }
|
||||
const QVariant& getVersionID() const { return m_pack_version.fileId; }
|
||||
const ModPlatform::IndexedVersion& getVersion() const { return m_pack_version; }
|
||||
const ModPlatform::ResourceProvider& getProvider() const { return m_pack->provider; }
|
||||
const QString& getName() const { return m_pack->name; }
|
||||
ModPlatform::IndexedPack::Ptr getPack() { return m_pack; }
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
/*
|
||||
* PolyMC - Minecraft Launcher
|
||||
* Prism Launcher - Minecraft Launcher
|
||||
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -1,44 +1,32 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
template <char Tseparator>
|
||||
class SeparatorPrefixTree
|
||||
{
|
||||
public:
|
||||
SeparatorPrefixTree(QStringList paths)
|
||||
{
|
||||
insert(paths);
|
||||
}
|
||||
class SeparatorPrefixTree {
|
||||
public:
|
||||
SeparatorPrefixTree(QStringList paths) { insert(paths); }
|
||||
|
||||
SeparatorPrefixTree(bool contained = false)
|
||||
{
|
||||
m_contained = contained;
|
||||
}
|
||||
SeparatorPrefixTree(bool contained = false) { m_contained = contained; }
|
||||
|
||||
void insert(QStringList paths)
|
||||
{
|
||||
for(auto &path: paths)
|
||||
{
|
||||
for (auto& path : paths) {
|
||||
insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
/// insert an exact path into the tree
|
||||
SeparatorPrefixTree & insert(QString path)
|
||||
SeparatorPrefixTree& insert(QString path)
|
||||
{
|
||||
auto sepIndex = path.indexOf(Tseparator);
|
||||
if(sepIndex == -1)
|
||||
{
|
||||
if (sepIndex == -1) {
|
||||
children[path] = SeparatorPrefixTree(true);
|
||||
return children[path];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
auto prefix = path.left(sepIndex);
|
||||
if(!children.contains(prefix))
|
||||
{
|
||||
if (!children.contains(prefix)) {
|
||||
children[prefix] = SeparatorPrefixTree(false);
|
||||
}
|
||||
return children[prefix].insert(path.mid(sepIndex + 1));
|
||||
@ -56,26 +44,20 @@ public:
|
||||
bool covers(QString path) const
|
||||
{
|
||||
// if we found some valid node, it's good enough. the tree covers the path
|
||||
if(m_contained)
|
||||
{
|
||||
if (m_contained) {
|
||||
return true;
|
||||
}
|
||||
auto sepIndex = path.indexOf(Tseparator);
|
||||
if(sepIndex == -1)
|
||||
{
|
||||
if (sepIndex == -1) {
|
||||
auto found = children.find(path);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return false;
|
||||
}
|
||||
return (*found).covers(QString());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
auto prefix = path.left(sepIndex);
|
||||
auto found = children.find(prefix);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return false;
|
||||
}
|
||||
return (*found).covers(path.mid(sepIndex + 1));
|
||||
@ -86,41 +68,33 @@ public:
|
||||
QString cover(QString path) const
|
||||
{
|
||||
// if we found some valid node, it's good enough. the tree covers the path
|
||||
if(m_contained)
|
||||
{
|
||||
if (m_contained) {
|
||||
return QString("");
|
||||
}
|
||||
auto sepIndex = path.indexOf(Tseparator);
|
||||
if(sepIndex == -1)
|
||||
{
|
||||
if (sepIndex == -1) {
|
||||
auto found = children.find(path);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return QString();
|
||||
}
|
||||
auto nested = (*found).cover(QString());
|
||||
if(nested.isNull())
|
||||
{
|
||||
if (nested.isNull()) {
|
||||
return nested;
|
||||
}
|
||||
if(nested.isEmpty())
|
||||
if (nested.isEmpty())
|
||||
return path;
|
||||
return path + Tseparator + nested;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
auto prefix = path.left(sepIndex);
|
||||
auto found = children.find(prefix);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return QString();
|
||||
}
|
||||
auto nested = (*found).cover(path.mid(sepIndex + 1));
|
||||
if(nested.isNull())
|
||||
{
|
||||
if (nested.isNull()) {
|
||||
return nested;
|
||||
}
|
||||
if(nested.isEmpty())
|
||||
if (nested.isEmpty())
|
||||
return prefix;
|
||||
return prefix + Tseparator + nested;
|
||||
}
|
||||
@ -130,21 +104,16 @@ public:
|
||||
bool exists(QString path) const
|
||||
{
|
||||
auto sepIndex = path.indexOf(Tseparator);
|
||||
if(sepIndex == -1)
|
||||
{
|
||||
if (sepIndex == -1) {
|
||||
auto found = children.find(path);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
auto prefix = path.left(sepIndex);
|
||||
auto found = children.find(prefix);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return false;
|
||||
}
|
||||
return (*found).exists(path.mid(sepIndex + 1));
|
||||
@ -152,24 +121,19 @@ public:
|
||||
}
|
||||
|
||||
/// find a node in the tree by name
|
||||
const SeparatorPrefixTree * find(QString path) const
|
||||
const SeparatorPrefixTree* find(QString path) const
|
||||
{
|
||||
auto sepIndex = path.indexOf(Tseparator);
|
||||
if(sepIndex == -1)
|
||||
{
|
||||
if (sepIndex == -1) {
|
||||
auto found = children.find(path);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &(*found);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
auto prefix = path.left(sepIndex);
|
||||
auto found = children.find(prefix);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return (*found).find(path.mid(sepIndex + 1));
|
||||
@ -177,70 +141,48 @@ public:
|
||||
}
|
||||
|
||||
/// is this a leaf node?
|
||||
bool leaf() const
|
||||
{
|
||||
return children.isEmpty();
|
||||
}
|
||||
bool leaf() const { return children.isEmpty(); }
|
||||
|
||||
/// is this node actually contained in the tree, or is it purely structural?
|
||||
bool contained() const
|
||||
{
|
||||
return m_contained;
|
||||
}
|
||||
bool contained() const { return m_contained; }
|
||||
|
||||
/// Remove a path from the tree
|
||||
bool remove(QString path)
|
||||
{
|
||||
return removeInternal(path) != Failed;
|
||||
}
|
||||
bool remove(QString path) { return removeInternal(path) != Failed; }
|
||||
|
||||
/// Clear all children of this node tree node
|
||||
void clear()
|
||||
{
|
||||
children.clear();
|
||||
}
|
||||
void clear() { children.clear(); }
|
||||
|
||||
QStringList toStringList() const
|
||||
{
|
||||
QStringList collected;
|
||||
// collecting these is more expensive.
|
||||
auto iter = children.begin();
|
||||
while(iter != children.end())
|
||||
{
|
||||
while (iter != children.end()) {
|
||||
QStringList list = iter.value().toStringList();
|
||||
for(int i = 0; i < list.size(); i++)
|
||||
{
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list[i] = iter.key() + Tseparator + list[i];
|
||||
}
|
||||
collected.append(list);
|
||||
if((*iter).m_contained)
|
||||
{
|
||||
if ((*iter).m_contained) {
|
||||
collected.append(iter.key());
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
return collected;
|
||||
}
|
||||
private:
|
||||
enum Removal
|
||||
{
|
||||
Failed,
|
||||
Succeeded,
|
||||
HasChildren
|
||||
};
|
||||
|
||||
private:
|
||||
enum Removal { Failed, Succeeded, HasChildren };
|
||||
Removal removeInternal(QString path = QString())
|
||||
{
|
||||
if(path.isEmpty())
|
||||
{
|
||||
if(!m_contained)
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
if (!m_contained) {
|
||||
// remove all children - we are removing a prefix
|
||||
clear();
|
||||
return Succeeded;
|
||||
}
|
||||
m_contained = false;
|
||||
if(children.size())
|
||||
{
|
||||
if (children.size()) {
|
||||
return HasChildren;
|
||||
}
|
||||
return Succeeded;
|
||||
@ -248,42 +190,32 @@ private:
|
||||
Removal remStatus = Failed;
|
||||
QString childToRemove;
|
||||
auto sepIndex = path.indexOf(Tseparator);
|
||||
if(sepIndex == -1)
|
||||
{
|
||||
if (sepIndex == -1) {
|
||||
childToRemove = path;
|
||||
auto found = children.find(childToRemove);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return Failed;
|
||||
}
|
||||
remStatus = (*found).removeInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
childToRemove = path.left(sepIndex);
|
||||
auto found = children.find(childToRemove);
|
||||
if(found == children.end())
|
||||
{
|
||||
if (found == children.end()) {
|
||||
return Failed;
|
||||
}
|
||||
remStatus = (*found).removeInternal(path.mid(sepIndex + 1));
|
||||
}
|
||||
switch (remStatus)
|
||||
{
|
||||
switch (remStatus) {
|
||||
case Failed:
|
||||
case HasChildren:
|
||||
{
|
||||
case HasChildren: {
|
||||
return remStatus;
|
||||
}
|
||||
case Succeeded:
|
||||
{
|
||||
case Succeeded: {
|
||||
children.remove(childToRemove);
|
||||
if(m_contained)
|
||||
{
|
||||
if (m_contained) {
|
||||
return HasChildren;
|
||||
}
|
||||
if(children.size())
|
||||
{
|
||||
if (children.size()) {
|
||||
return HasChildren;
|
||||
}
|
||||
return Succeeded;
|
||||
@ -292,7 +224,7 @@ private:
|
||||
return Failed;
|
||||
}
|
||||
|
||||
private:
|
||||
QMap<QString,SeparatorPrefixTree<Tseparator>> children;
|
||||
private:
|
||||
QMap<QString, SeparatorPrefixTree<Tseparator>> children;
|
||||
bool m_contained = false;
|
||||
};
|
||||
|
@ -14,17 +14,16 @@
|
||||
*/
|
||||
|
||||
#include "SkinUtils.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "Application.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QPainter>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QPainter>
|
||||
|
||||
namespace SkinUtils
|
||||
{
|
||||
namespace SkinUtils {
|
||||
/*
|
||||
* Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise
|
||||
*/
|
||||
@ -32,12 +31,15 @@ QPixmap getFaceFromCache(QString username, int height, int width)
|
||||
{
|
||||
QFile fskin(APPLICATION->metacache()->resolveEntry("skins", username + ".png")->getFullPath());
|
||||
|
||||
if (fskin.exists())
|
||||
{
|
||||
if (fskin.exists()) {
|
||||
QPixmap skinTexture(fskin.fileName());
|
||||
if(!skinTexture.isNull())
|
||||
{
|
||||
if (!skinTexture.isNull()) {
|
||||
QPixmap skin = QPixmap(8, 8);
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||
skin.fill(QColorConstants::Transparent);
|
||||
#else
|
||||
skin.fill(QColor(0, 0, 0, 0));
|
||||
#endif
|
||||
QPainter painter(&skin);
|
||||
painter.drawPixmap(0, 0, skinTexture.copy(8, 8, 8, 8));
|
||||
painter.drawPixmap(0, 0, skinTexture.copy(40, 8, 8, 8));
|
||||
@ -47,4 +49,4 @@ QPixmap getFaceFromCache(QString username, int height, int width)
|
||||
|
||||
return QPixmap();
|
||||
}
|
||||
}
|
||||
} // namespace SkinUtils
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
#include <QPixmap>
|
||||
|
||||
namespace SkinUtils
|
||||
{
|
||||
namespace SkinUtils {
|
||||
QPixmap getFaceFromCache(QString id, int height = 64, int width = 64);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user