Compare commits
1753 Commits
5.2.2+1.18
...
1.20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
292a6aeab3 | ||
|
|
7fbfcf1a92 | ||
|
|
1bcb28a1ad | ||
|
|
d51b0f60a2 | ||
|
|
ab9880159e | ||
|
|
0f94634361 | ||
|
|
f1492cc829 | ||
|
|
0ecee529d7 | ||
|
|
e9bfd96dd9 | ||
|
|
fb9dcf77c6 | ||
|
|
33851c1cb6 | ||
|
|
494203ef5a | ||
|
|
74f76f7305 | ||
|
|
62dbbea083 | ||
|
|
538c52bc2a | ||
|
|
b62eb1845b | ||
|
|
7c45564979 | ||
|
|
f8d2425242 | ||
|
|
50cedfc699 | ||
|
|
f4f596ca0c | ||
|
|
85aab426c5 | ||
|
|
29ff5f152e | ||
|
|
8213a720a3 | ||
|
|
afe3e09a27 | ||
|
|
ae20fa17c9 | ||
|
|
a6c03e9928 | ||
|
|
864c751aea | ||
|
|
f931d5c442 | ||
|
|
55cec86e5f | ||
|
|
4ec8ef753a | ||
|
|
3f22e23565 | ||
|
|
a73dd5ef6a | ||
|
|
653a477060 | ||
|
|
44113d2536 | ||
|
|
1165d3bdd1 | ||
|
|
c73cdc49a4 | ||
|
|
4e3ecf9b6d | ||
|
|
a40363c1fb | ||
|
|
46dd5ecddd | ||
|
|
b765bcb51f | ||
|
|
26bd7116a1 | ||
|
|
4d2f0da1fc | ||
|
|
c2f585da95 | ||
|
|
327c3cd9ff | ||
|
|
c64ca2e54b | ||
|
|
85955ebf75 | ||
|
|
d749205427 | ||
|
|
438ceb1984 | ||
|
|
5acb5115b9 | ||
|
|
37dc9e60eb | ||
|
|
c2191df359 | ||
|
|
d08da1b3c8 | ||
|
|
36f425b8cd | ||
|
|
dc3c379049 | ||
|
|
4ff7d4c554 | ||
|
|
db13f39b30 | ||
|
|
5a9c49f8d4 | ||
|
|
8ee85f2c16 | ||
|
|
2081b63b56 | ||
|
|
94f1fbf4db | ||
|
|
ab8a8068e0 | ||
|
|
79d2b28d5b | ||
|
|
18dc488ab9 | ||
|
|
a9340b2642 | ||
|
|
670e06816b | ||
|
|
53349cbd1a | ||
|
|
1794c81b61 | ||
|
|
dbe9acb3d8 | ||
|
|
22915a91a1 | ||
|
|
1289897004 | ||
|
|
9692da12b4 | ||
|
|
e34a99b38c | ||
|
|
f79eae8b83 | ||
|
|
38288d5e6a | ||
|
|
2050516bf1 | ||
|
|
02f486ebf4 | ||
|
|
9edce9ad91 | ||
|
|
ac8d93d5b9 | ||
|
|
bee4536c1a | ||
|
|
da2206168b | ||
|
|
17f930ea6f | ||
|
|
f23348c6cb | ||
|
|
21cbcb0e04 | ||
|
|
925c7526ee | ||
|
|
30e3deb8e2 | ||
|
|
ee34dcf96e | ||
|
|
49d800ff27 | ||
|
|
15f30b532c | ||
|
|
df06010846 | ||
|
|
696b344ef5 | ||
|
|
e63d99763e | ||
|
|
60850610f9 | ||
|
|
e16179b797 | ||
|
|
784b914a43 | ||
|
|
b9933b1158 | ||
|
|
878b3798f3 | ||
|
|
bc0e9a09fc | ||
|
|
8c34c0de50 | ||
|
|
5a93bc6109 | ||
|
|
8125da7882 | ||
|
|
d699187006 | ||
|
|
cff29149db | ||
|
|
3926f27d33 | ||
|
|
9bc5f06a19 | ||
|
|
a70f76a34d | ||
|
|
4dcdf09a01 | ||
|
|
f26d35070e | ||
|
|
a04266df54 | ||
|
|
2ec6a6afbc | ||
|
|
49f5b527db | ||
|
|
00287612de | ||
|
|
4b18cc2cc6 | ||
|
|
b2ed5b9341 | ||
|
|
a30dd08cd1 | ||
|
|
7420a7c7ab | ||
|
|
3f9148fa62 | ||
|
|
8cc41fa222 | ||
|
|
f06fb8c32e | ||
|
|
6ee15122f9 | ||
|
|
c9843e08bd | ||
|
|
7b47c39e6b | ||
|
|
b26ab375b5 | ||
|
|
9c4da7fa68 | ||
|
|
d64a1c760b | ||
|
|
555213714f | ||
|
|
7f27141a16 | ||
|
|
a8227a964d | ||
|
|
a7a9aac23a | ||
|
|
1176cc98e3 | ||
|
|
a7622f58ae | ||
|
|
67814db6ad | ||
|
|
8959c2ff91 | ||
|
|
a6eb99d23c | ||
|
|
fd46baae21 | ||
|
|
273bab7856 | ||
|
|
8133198cc2 | ||
|
|
25976f3b87 | ||
|
|
f71277eb64 | ||
|
|
b30b319214 | ||
|
|
e411f11c0c | ||
|
|
e30a7fccf2 | ||
|
|
12a0414f61 | ||
|
|
44322a7d07 | ||
|
|
8d4a7c3374 | ||
|
|
29c1a479a4 | ||
|
|
ee6489fb69 | ||
|
|
87c977a3e6 | ||
|
|
54e55e7534 | ||
|
|
65ab37b819 | ||
|
|
873e3bd676 | ||
|
|
6b37051980 | ||
|
|
cf5c81f7e2 | ||
|
|
9d280f51df | ||
|
|
f26ed86973 | ||
|
|
38f1370d59 | ||
|
|
213e0ceb9a | ||
|
|
7688fc2b16 | ||
|
|
20b3d8bffa | ||
|
|
2e0ec6ae9e | ||
|
|
a170f072ff | ||
|
|
e7542afd1c | ||
|
|
71b444288b | ||
|
|
94271b866b | ||
|
|
6e591f5ace | ||
|
|
4802988bbb | ||
|
|
292b80cf37 | ||
|
|
5048e74c79 | ||
|
|
c3086e5249 | ||
|
|
bb658319cb | ||
|
|
14f1552ad7 | ||
|
|
61c8cfdca6 | ||
|
|
af526b9113 | ||
|
|
6df33005a7 | ||
|
|
e9cb602ab7 | ||
|
|
a13587e139 | ||
|
|
6979b56d8c | ||
|
|
3a2edf7ddd | ||
|
|
a643170426 | ||
|
|
4964cccff9 | ||
|
|
82535da861 | ||
|
|
f70fb212f7 | ||
|
|
588b56530e | ||
|
|
3ad4e2478e | ||
|
|
c118675369 | ||
|
|
8b0622ff5c | ||
|
|
900e5786b6 | ||
|
|
2535174e00 | ||
|
|
7398b48345 | ||
|
|
eed320b055 | ||
|
|
01d2582c44 | ||
|
|
536eb03b50 | ||
|
|
1b37c9da66 | ||
|
|
e253833b68 | ||
|
|
ae729bcd15 | ||
|
|
9be6134073 | ||
|
|
59e3b83d74 | ||
|
|
4bdddf1051 | ||
|
|
211e404687 | ||
|
|
4cde23f4fe | ||
|
|
dbb6e4713d | ||
|
|
8c2c33093b | ||
|
|
63aeef1ba0 | ||
|
|
51c4f3eae8 | ||
|
|
455b56749c | ||
|
|
673851ffbd | ||
|
|
20be8d0da3 | ||
|
|
967b0c1982 | ||
|
|
fcea407708 | ||
|
|
5168eb63aa | ||
|
|
4a8e0487bc | ||
|
|
e7f86f8687 | ||
|
|
f97766019a | ||
|
|
6aae0f952c | ||
|
|
b72959f257 | ||
|
|
64a427fa62 | ||
|
|
32a8800344 | ||
|
|
1c789111e8 | ||
|
|
0b8eddbf25 | ||
|
|
631b3eddfb | ||
|
|
98e6af87c1 | ||
|
|
639f0e2c1a | ||
|
|
ba6d0d20fc | ||
|
|
14a89f94a6 | ||
|
|
8307564336 | ||
|
|
fffb1031f7 | ||
|
|
0bda3c9f43 | ||
|
|
0f3bfd894e | ||
|
|
054d36baa0 | ||
|
|
8e1270dcdc | ||
|
|
fec7eb66ea | ||
|
|
7c550a1ce4 | ||
|
|
333147cfd5 | ||
|
|
d25781f36c | ||
|
|
75ff154eca | ||
|
|
49464451dd | ||
|
|
c0eaf29cb5 | ||
|
|
87d1aad3d1 | ||
|
|
6f4212ebc8 | ||
|
|
6a365be734 | ||
|
|
2e52db6e93 | ||
|
|
ece2897c37 | ||
|
|
4ee8c41abd | ||
|
|
66ef30449a | ||
|
|
edddba6991 | ||
|
|
bc7aa5539c | ||
|
|
7348737878 | ||
|
|
fd42c5b129 | ||
|
|
3bad8f5934 | ||
|
|
72fc1583be | ||
|
|
aac4334b37 | ||
|
|
6f4098a2d9 | ||
|
|
b50a7587da | ||
|
|
f7f871ffc1 | ||
|
|
2193aa11a4 | ||
|
|
1ec9aad47f | ||
|
|
fb51a31dd2 | ||
|
|
ef528a4f3a | ||
|
|
792f9790ea | ||
|
|
661c6c9ccf | ||
|
|
809c622ad4 | ||
|
|
a51e740040 | ||
|
|
c7befd1913 | ||
|
|
03e758a037 | ||
|
|
10f1365905 | ||
|
|
dfb9378297 | ||
|
|
94c848b0de | ||
|
|
5d7813bf3e | ||
|
|
3a190a56b4 | ||
|
|
b088fee0e8 | ||
|
|
e9ffa69412 | ||
|
|
5a7ba9ffea | ||
|
|
6bfe079175 | ||
|
|
def4c05dbc | ||
|
|
309870517e | ||
|
|
0d462268e9 | ||
|
|
1728de0f16 | ||
|
|
06878c69e4 | ||
|
|
831db874e4 | ||
|
|
fdac36df9f | ||
|
|
ad9ce1265e | ||
|
|
16f637329f | ||
|
|
d9a44ee061 | ||
|
|
1ce76470c2 | ||
|
|
4e3897636d | ||
|
|
965d105542 | ||
|
|
90848b8972 | ||
|
|
473c74e63b | ||
|
|
2e8c003572 | ||
|
|
60707e43b9 | ||
|
|
641649c1aa | ||
|
|
8b2fa7e554 | ||
|
|
67e3a863cc | ||
|
|
2915a88465 | ||
|
|
f8424a9fac | ||
|
|
237239d466 | ||
|
|
2f4836b045 | ||
|
|
c15b95e2be | ||
|
|
97e2a318f3 | ||
|
|
d7f2e90984 | ||
|
|
dcc0ed27d8 | ||
|
|
debfbdc017 | ||
|
|
dd45519d21 | ||
|
|
d36ac86b2a | ||
|
|
ecf3417b45 | ||
|
|
32b0216b19 | ||
|
|
9f521ed308 | ||
|
|
ab1da68da2 | ||
|
|
ec189b55a2 | ||
|
|
8873a055bc | ||
|
|
53e5eb6748 | ||
|
|
851febf61d | ||
|
|
dac091cea4 | ||
|
|
e1059e15e7 | ||
|
|
efc764020a | ||
|
|
9c7b482a20 | ||
|
|
8f6c76e93a | ||
|
|
eddb7fa166 | ||
|
|
0e48559109 | ||
|
|
34b9f8d755 | ||
|
|
8cca316fb5 | ||
|
|
4ddac48f1e | ||
|
|
e9c266cd16 | ||
|
|
b0ed60c2f0 | ||
|
|
099c62672b | ||
|
|
6c82d448f7 | ||
|
|
469b4b7093 | ||
|
|
033cfd34e5 | ||
|
|
c3eeb1e90f | ||
|
|
db105905f7 | ||
|
|
85aa352215 | ||
|
|
aa6b7fad1f | ||
|
|
43dbba1152 | ||
|
|
9e3e732b43 | ||
|
|
8dac16bfc4 | ||
|
|
72ba7d37e0 | ||
|
|
183429867a | ||
|
|
226e4a373b | ||
|
|
ddffe93f1a | ||
|
|
8a247bd8aa | ||
|
|
a198bfaecd | ||
|
|
c413a70c56 | ||
|
|
66abdcd9a7 | ||
|
|
1b52387cfa | ||
|
|
e5e8e6d9b5 | ||
|
|
f7b4c786d7 | ||
|
|
3bf8255581 | ||
|
|
7e9ace3fc6 | ||
|
|
fe3de55409 | ||
|
|
deb81ec6cd | ||
|
|
b85f1341c1 | ||
|
|
d2680cf29d | ||
|
|
ab077bc48f | ||
|
|
884bdb0fe2 | ||
|
|
f63d82ce9b | ||
|
|
4af1e27a89 | ||
|
|
3ad7a8ce9d | ||
|
|
b916ef5ae9 | ||
|
|
9c2fbfcae6 | ||
|
|
1814bd3e1f | ||
|
|
070b7b6d12 | ||
|
|
a0fdb3e6d7 | ||
|
|
94d37544b2 | ||
|
|
bf74ab5a80 | ||
|
|
e1ff64785e | ||
|
|
0e9d024c5d | ||
|
|
de63462363 | ||
|
|
e39cc1ca15 | ||
|
|
ebb4f26777 | ||
|
|
571d6c6865 | ||
|
|
5cc62f7e90 | ||
|
|
31cc0a0da0 | ||
|
|
280d14a595 | ||
|
|
3b0e7ce18f | ||
|
|
09e2e0e145 | ||
|
|
06726296fe | ||
|
|
d128ca12c9 | ||
|
|
fc2bcc3fb6 | ||
|
|
89badd662b | ||
|
|
d854fcffff | ||
|
|
7b2a0e39be | ||
|
|
3c17a5b4a2 | ||
|
|
89b07422e7 | ||
|
|
e6bb67cb05 | ||
|
|
095ba1905d | ||
|
|
36f2564d6a | ||
|
|
f1fc20d5a1 | ||
|
|
1747cb0b46 | ||
|
|
6531e605c2 | ||
|
|
40b9ac6002 | ||
|
|
263ed51369 | ||
|
|
a197120f57 | ||
|
|
49364f6f6d | ||
|
|
a45783647f | ||
|
|
e7632b7f0b | ||
|
|
56779b9a5b | ||
|
|
124112259e | ||
|
|
01a68a2453 | ||
|
|
a1ece7da38 | ||
|
|
c1f409f9ad | ||
|
|
ad1e12a3bb | ||
|
|
e1fcac1761 | ||
|
|
3e3b9128c3 | ||
|
|
73b0f13de0 | ||
|
|
f4f76cada3 | ||
|
|
51c31bf0b8 | ||
|
|
feaf363fd8 | ||
|
|
edda3b4e02 | ||
|
|
5fe5a484a3 | ||
|
|
e91220cdea | ||
|
|
e0516ebc51 | ||
|
|
2d2bc94747 | ||
|
|
2120774df5 | ||
|
|
e9c0f6df3b | ||
|
|
12d69bec45 | ||
|
|
e1d203ba10 | ||
|
|
e3a41d7416 | ||
|
|
8b0080256c | ||
|
|
f12d7723c0 | ||
|
|
81c4980738 | ||
|
|
df9d280b1c | ||
|
|
f4c7832964 | ||
|
|
a36ad8a4b4 | ||
|
|
2c963888b5 | ||
|
|
6207162a17 | ||
|
|
efdb1d475d | ||
|
|
ca3c02677e | ||
|
|
20c0f3dd6a | ||
|
|
c32fad10e0 | ||
|
|
0d4126df41 | ||
|
|
41596444a3 | ||
|
|
c749fc1aeb | ||
|
|
55cb94f1d1 | ||
|
|
94ca6ccdd6 | ||
|
|
a100622a03 | ||
|
|
27d831ef6a | ||
|
|
0a8a88047c | ||
|
|
d7146a0667 | ||
|
|
c6e01f8a59 | ||
|
|
1f7db94281 | ||
|
|
5d6abe46f5 | ||
|
|
03e7df5308 | ||
|
|
6fa3e64210 | ||
|
|
b2d80ada34 | ||
|
|
9d95a5363f | ||
|
|
c85f7e49d7 | ||
|
|
2e88482344 | ||
|
|
91602ae7d6 | ||
|
|
ac4ebd871c | ||
|
|
1d67197df1 | ||
|
|
c678ebbb91 | ||
|
|
7174ae1454 | ||
|
|
9375a4233b | ||
|
|
17fdccddfe | ||
|
|
3214311bb6 | ||
|
|
d0fd498682 | ||
|
|
a186c956f7 | ||
|
|
5e983ea936 | ||
|
|
8320c31f85 | ||
|
|
1da264e5a7 | ||
|
|
6d7b50bdd8 | ||
|
|
300ff4d5f8 | ||
|
|
3fa0c3923a | ||
|
|
db5a3234d2 | ||
|
|
8b97ebf945 | ||
|
|
6706715fb1 | ||
|
|
cc1be852bb | ||
|
|
35c82b81c8 | ||
|
|
357d421f5a | ||
|
|
5772661dfb | ||
|
|
f6c7b3109b | ||
|
|
e301d7d480 | ||
|
|
8d6a344ded | ||
|
|
4512bc6fbd | ||
|
|
4f49ba3f9b | ||
|
|
7e53a8bc73 | ||
|
|
06bfd71d07 | ||
|
|
ae8cfbaa3d | ||
|
|
0ea384f3e6 | ||
|
|
837a66befc | ||
|
|
6f0d6e473f | ||
|
|
a79ea9766a | ||
|
|
675c58a437 | ||
|
|
d1863cc66e | ||
|
|
11508fbe07 | ||
|
|
aee0b2a47d | ||
|
|
d76fd84b76 | ||
|
|
6578040e77 | ||
|
|
ecef37ac60 | ||
|
|
c96f7ecf11 | ||
|
|
d90a56ff40 | ||
|
|
bcd2e80821 | ||
|
|
152cdc4469 | ||
|
|
2406c1a338 | ||
|
|
4646b525f3 | ||
|
|
ae561db9e3 | ||
|
|
ebf1d93422 | ||
|
|
b5ef37a713 | ||
|
|
85928b5b9c | ||
|
|
12b0d352cd | ||
|
|
8aa04e13a6 | ||
|
|
6ef5654bb6 | ||
|
|
0abffa26ee | ||
|
|
7e57ad8279 | ||
|
|
97ba361867 | ||
|
|
a87399a10f | ||
|
|
9034688502 | ||
|
|
cef8abe247 | ||
|
|
60b372870b | ||
|
|
95496fc27c | ||
|
|
d8da6c08a0 | ||
|
|
7547a9227c | ||
|
|
8058d4caa9 | ||
|
|
f35d920801 | ||
|
|
7efe10a8a9 | ||
|
|
94fbefd901 | ||
|
|
88a276eed9 | ||
|
|
8bcd85d079 | ||
|
|
8729e50f1b | ||
|
|
ee8093fc50 | ||
|
|
a7c2424b14 | ||
|
|
5ff2951b5a | ||
|
|
4d0444ba2a | ||
|
|
f59aa5b19c | ||
|
|
00fa8227c3 | ||
|
|
bc2e84bacc | ||
|
|
ed74748994 | ||
|
|
0111102d25 | ||
|
|
c5afe3c769 | ||
|
|
1cfe0216ac | ||
|
|
ba6103bbc7 | ||
|
|
1f16ce835a | ||
|
|
7e2f3f3fb9 | ||
|
|
09939a551f | ||
|
|
af13e30f95 | ||
|
|
9c00f7297d | ||
|
|
aa53a7610b | ||
|
|
5fa94bf5e4 | ||
|
|
b5c4b79eb8 | ||
|
|
5e8a111fed | ||
|
|
fe65ffa4d1 | ||
|
|
240fa4b510 | ||
|
|
8677532629 | ||
|
|
559bdb612c | ||
|
|
5579743b6c | ||
|
|
81836a838d | ||
|
|
0056a57f30 | ||
|
|
e47bdbf6d0 | ||
|
|
845671ce07 | ||
|
|
07159b5eb7 | ||
|
|
a76c7f2505 | ||
|
|
cba08057cf | ||
|
|
b351febc82 | ||
|
|
9464f07a5a | ||
|
|
28ac5eaec4 | ||
|
|
11f313f6e7 | ||
|
|
ccfc282cfc | ||
|
|
46127b50f3 | ||
|
|
cb09af7e86 | ||
|
|
2c577da8fd | ||
|
|
2e9166c7df | ||
|
|
7be382a3ce | ||
|
|
7c57ffd2c6 | ||
|
|
bf43ba7bf4 | ||
|
|
c0f162716b | ||
|
|
2946b3a7b5 | ||
|
|
61ce2e7117 | ||
|
|
8ce41242ed | ||
|
|
c7a60f5112 | ||
|
|
9fb1464176 | ||
|
|
488be0c748 | ||
|
|
1002baa6b1 | ||
|
|
0b9b49c360 | ||
|
|
0aea884052 | ||
|
|
a1f3300a8a | ||
|
|
ef6c14a61e | ||
|
|
27167ea42c | ||
|
|
a531df53cb | ||
|
|
3fb3bb3c11 | ||
|
|
8bc92e051e | ||
|
|
adc3dbaf57 | ||
|
|
7c0f8b8bef | ||
|
|
193b0922cc | ||
|
|
abfaea19a5 | ||
|
|
3e52c75d0f | ||
|
|
552eea488d | ||
|
|
215b797e7b | ||
|
|
5983ed5bf3 | ||
|
|
ed9dc3719c | ||
|
|
8bb70222ce | ||
|
|
2f3df11220 | ||
|
|
134dc0ef20 | ||
|
|
bd55e16d6e | ||
|
|
e859ce8eb6 | ||
|
|
d11e9ac190 | ||
|
|
3551ab645a | ||
|
|
cc60cba1f2 | ||
|
|
fb84fda4de | ||
|
|
53b7d34649 | ||
|
|
2ecd0c654b | ||
|
|
4de9022e3c | ||
|
|
482c0ca503 | ||
|
|
b6a47da3b0 | ||
|
|
819cb13d43 | ||
|
|
32ad8f6df1 | ||
|
|
58f79e25e9 | ||
|
|
d358ea37a8 | ||
|
|
2a6077cb65 | ||
|
|
d557725c82 | ||
|
|
6b01f0450b | ||
|
|
6a8d63a2cd | ||
|
|
ced7f866d8 | ||
|
|
375276ea1f | ||
|
|
72a653e532 | ||
|
|
59235e6a21 | ||
|
|
09490df85d | ||
|
|
a677f6d571 | ||
|
|
d4f09f6ff9 | ||
|
|
fccbae33db | ||
|
|
621ecf6b3e | ||
|
|
81baeea35b | ||
|
|
f4f3eff242 | ||
|
|
0b5145f290 | ||
|
|
0a72abb519 | ||
|
|
5d984f653e | ||
|
|
7cb660f955 | ||
|
|
3eff8c787d | ||
|
|
b9d81f44e6 | ||
|
|
93fbbfe2d1 | ||
|
|
e36ba04921 | ||
|
|
7054c16f6c | ||
|
|
b7b510f807 | ||
|
|
4054bd8a23 | ||
|
|
89dab59980 | ||
|
|
f4ce7072a6 | ||
|
|
daea9dd83a | ||
|
|
b10e9de54c | ||
|
|
14abfef522 | ||
|
|
77760d7dd2 | ||
|
|
f391e97a62 | ||
|
|
fd68efec0c | ||
|
|
c0cdbd122e | ||
|
|
e397f0ca78 | ||
|
|
04a72f4d90 | ||
|
|
8b37a1dbfc | ||
|
|
b943dc1449 | ||
|
|
fd61abb375 | ||
|
|
735a626cf3 | ||
|
|
54c7dbd16e | ||
|
|
c5a5b01bfb | ||
|
|
6740857274 | ||
|
|
9f040b2c43 | ||
|
|
a5b5733965 | ||
|
|
b567658318 | ||
|
|
7cbea84706 | ||
|
|
5fbbacbe11 | ||
|
|
4b8ec822fe | ||
|
|
8059fc4672 | ||
|
|
49c1bc71ba | ||
|
|
e7277b89d5 | ||
|
|
cbfe1636ff | ||
|
|
cebe1c308a | ||
|
|
39fa555209 | ||
|
|
538d332310 | ||
|
|
ed1f2c5837 | ||
|
|
1d68dd797c | ||
|
|
da71fc1a2c | ||
|
|
40b04cbded | ||
|
|
504397bc23 | ||
|
|
e537cbe29a | ||
|
|
b1e91686da | ||
|
|
5007c5e286 | ||
|
|
bf6979e45b | ||
|
|
803aaba204 | ||
|
|
637c14fb6d | ||
|
|
ea918a44c4 | ||
|
|
5c73ddeaf8 | ||
|
|
b56fbc853d | ||
|
|
1a358582a5 | ||
|
|
2d14bc3b1b | ||
|
|
6593b6a5cf | ||
|
|
204c8b78c4 | ||
|
|
b1a0d0b94a | ||
|
|
d7d75e88d5 | ||
|
|
6c7a5c70f5 | ||
|
|
d877ab46f0 | ||
|
|
c45f83e896 | ||
|
|
ce0d054e6e | ||
|
|
c87dee4f3f | ||
|
|
fb720782b9 | ||
|
|
36673f275e | ||
|
|
385523c826 | ||
|
|
c387e91f0d | ||
|
|
9c02b2c393 | ||
|
|
9306743001 | ||
|
|
a1f867c26d | ||
|
|
2710a02b2a | ||
|
|
4090a91272 | ||
|
|
00144af296 | ||
|
|
96d01e5a1e | ||
|
|
225366ee02 | ||
|
|
cedcd94417 | ||
|
|
43ca13cdf2 | ||
|
|
8fd8efb2e3 | ||
|
|
f9856ec591 | ||
|
|
0dc068eabf | ||
|
|
c0815b787e | ||
|
|
aa25e3f05e | ||
|
|
b57eeb38de | ||
|
|
3011e40972 | ||
|
|
47ced6ee40 | ||
|
|
db49f73bf5 | ||
|
|
cf362047a6 | ||
|
|
b1a1d785de | ||
|
|
4529bf36d1 | ||
|
|
3150863b2c | ||
|
|
f59e003df1 | ||
|
|
f1ef1a6c41 | ||
|
|
d76e841761 | ||
|
|
c5fee81b8a | ||
|
|
3d1ead98c3 | ||
|
|
5de1518f76 | ||
|
|
dc4850502d | ||
|
|
8d057a2b55 | ||
|
|
a11e70c413 | ||
|
|
d32ba39c1d | ||
|
|
0c65c2cf17 | ||
|
|
7b1d9ff8bb | ||
|
|
5c21a98c7f | ||
|
|
88d56c451e | ||
|
|
ec6de638a2 | ||
|
|
17e3b40bf9 | ||
|
|
0aabad688b | ||
|
|
7ff300d8df | ||
|
|
bdd12f1802 | ||
|
|
bd1ca9ce94 | ||
|
|
eff07204ea | ||
|
|
4a90be9bc7 | ||
|
|
90f6e9b60e | ||
|
|
8b0158b044 | ||
|
|
fe942c90df | ||
|
|
7d9f110f27 | ||
|
|
e9f6f3d953 | ||
|
|
2a6be9f9e6 | ||
|
|
31fc5961d8 | ||
|
|
412a8c4aec | ||
|
|
a0c5c90c8d | ||
|
|
e94f1481ae | ||
|
|
60491a9c69 | ||
|
|
22559131cc | ||
|
|
8b0b0759ef | ||
|
|
375c978eb4 | ||
|
|
3beef4df08 | ||
|
|
dc42bda09d | ||
|
|
6198f0c9d3 | ||
|
|
5f45f612a3 | ||
|
|
adac1125e8 | ||
|
|
06f5441399 | ||
|
|
5fef7dc66b | ||
|
|
fcde6104eb | ||
|
|
20a15a587c | ||
|
|
4b560c7d3d | ||
|
|
ecb3863410 | ||
|
|
eac4ddac4c | ||
|
|
987c5dc321 | ||
|
|
ec99687702 | ||
|
|
501d24ddfd | ||
|
|
2aab73220c | ||
|
|
4f7a4cbf95 | ||
|
|
3bf2c86b07 | ||
|
|
eadb19d386 | ||
|
|
ce7b7cbaf2 | ||
|
|
fa5b947442 | ||
|
|
11f1be0d2c | ||
|
|
4418dd2eab | ||
|
|
8081e0db02 | ||
|
|
73d2a4405d | ||
|
|
8d766a8cc8 | ||
|
|
fe8d0434c5 | ||
|
|
ae59c2496e | ||
|
|
a19d519b4b | ||
|
|
f7097ec394 | ||
|
|
16e1e37fd9 | ||
|
|
48c9d401db | ||
|
|
37078ef962 | ||
|
|
5ad333703f | ||
|
|
54625c1a40 | ||
|
|
e1ea900ffb | ||
|
|
26bdb57972 | ||
|
|
c34974a555 | ||
|
|
ffa76b238e | ||
|
|
ab4ded89e3 | ||
|
|
1f7da1d650 | ||
|
|
29e3d38c55 | ||
|
|
0df87a9f83 | ||
|
|
26758e18b2 | ||
|
|
e8926c334c | ||
|
|
dcbc3e033a | ||
|
|
501e498175 | ||
|
|
f8a35c5884 | ||
|
|
0b685d843e | ||
|
|
89700c1e2e | ||
|
|
58b7b0da0f | ||
|
|
b171d4bf19 | ||
|
|
e827fba754 | ||
|
|
fac858e3cb | ||
|
|
8290964c31 | ||
|
|
383d40e420 | ||
|
|
37724d7696 | ||
|
|
9f5ccf28df | ||
|
|
6536c49c3c | ||
|
|
59abaf6a7f | ||
|
|
2a1685a2cb | ||
|
|
a35c605e73 | ||
|
|
4dcb94e6a5 | ||
|
|
ec66fbc05c | ||
|
|
e946cc896a | ||
|
|
a1e8838aee | ||
|
|
9390f7fca9 | ||
|
|
7490809a42 | ||
|
|
45f92f7fce | ||
|
|
bc7fb1e6d4 | ||
|
|
2ee1ef13ec | ||
|
|
7552199f5e | ||
|
|
73c80d0603 | ||
|
|
fa9103fb06 | ||
|
|
9964693050 | ||
|
|
c51133f2c3 | ||
|
|
da944c330b | ||
|
|
6b1f5232fa | ||
|
|
672ca92aab | ||
|
|
4aebee53ac | ||
|
|
7faa29e84b | ||
|
|
349f58b807 | ||
|
|
1f5763bdf0 | ||
|
|
744622b81d | ||
|
|
12aa14cc54 | ||
|
|
3acaa1dd49 | ||
|
|
2e8b11de93 | ||
|
|
2629cd484a | ||
|
|
74c9f684e6 | ||
|
|
18722beebd | ||
|
|
6b7a300a2e | ||
|
|
1ce0a833e9 | ||
|
|
b54c6751a4 | ||
|
|
6e00010e9a | ||
|
|
9325a8c892 | ||
|
|
00481145b6 | ||
|
|
a918c09543 | ||
|
|
a06fca791a | ||
|
|
ef341932a3 | ||
|
|
2e272f9b18 | ||
|
|
1580e757ed | ||
|
|
ed0460747c | ||
|
|
fa8d83a21a | ||
|
|
6908f14905 | ||
|
|
6fa24ec171 | ||
|
|
dcb72dfa62 | ||
|
|
5203985a25 | ||
|
|
351e4d3fc6 | ||
|
|
5079ce65ba | ||
|
|
47a04c66f1 | ||
|
|
53cc4584f4 | ||
|
|
3492f9a4ee | ||
|
|
d9184833ec | ||
|
|
0aef731a79 | ||
|
|
ca51ceba6a | ||
|
|
d7b2f5b75b | ||
|
|
33e43f5b8f | ||
|
|
f9ebcb6b98 | ||
|
|
fa47e923f0 | ||
|
|
97b6672610 | ||
|
|
05cc25c761 | ||
|
|
d13cfd5ca8 | ||
|
|
4399eeb67f | ||
|
|
dd4d316a66 | ||
|
|
2f92dbe12c | ||
|
|
1ad6ec4bd1 | ||
|
|
fab831f143 | ||
|
|
073b8cb9de | ||
|
|
b776587f2d | ||
|
|
70a2a2194f | ||
|
|
b87c615e5d | ||
|
|
d1c9be8900 | ||
|
|
b5b46888af | ||
|
|
df8da0823a | ||
|
|
21ee0dc1cf | ||
|
|
05e74f4141 | ||
|
|
507cc7eb14 | ||
|
|
fa4d659c5b | ||
|
|
74d28ed997 | ||
|
|
dcad671568 | ||
|
|
0a18e9c1dc | ||
|
|
ca8d290870 | ||
|
|
e949ed67ef | ||
|
|
a51b1563b1 | ||
|
|
478d003307 | ||
|
|
f656aea029 | ||
|
|
1e77cccf5a | ||
|
|
1a8681e15d | ||
|
|
721645f88f | ||
|
|
d92e48e1aa | ||
|
|
7645a43538 | ||
|
|
0c82223248 | ||
|
|
4d3aaceb9c | ||
|
|
8b4c5d4c7a | ||
|
|
cb6ff1d68b | ||
|
|
8b698452fd | ||
|
|
e9ff7d7ba1 | ||
|
|
34179fb313 | ||
|
|
be4a6abc81 | ||
|
|
770969ec5c | ||
|
|
24e014f326 | ||
|
|
a506f8785e | ||
|
|
f1c0d02bc3 | ||
|
|
b8a4e13284 | ||
|
|
d52e6467ac | ||
|
|
0779058ca5 | ||
|
|
6c85449b63 | ||
|
|
365eb80a28 | ||
|
|
fed0392ddc | ||
|
|
cac291df47 | ||
|
|
712409b4e1 | ||
|
|
571d3bd5e2 | ||
|
|
26bbe781a0 | ||
|
|
2844c97170 | ||
|
|
5e7d6642a7 | ||
|
|
fbfaa178e3 | ||
|
|
27c2a674cf | ||
|
|
f488df2d45 | ||
|
|
7b23053da0 | ||
|
|
590c939778 | ||
|
|
eec5ec7efb | ||
|
|
22f22b9317 | ||
|
|
32e246885b | ||
|
|
824deddb46 | ||
|
|
866fcebf2e | ||
|
|
27c7400f34 | ||
|
|
c1182944f3 | ||
|
|
180ac925f7 | ||
|
|
bb115f1d2f | ||
|
|
6c465c7182 | ||
|
|
46be9a801c | ||
|
|
b8d0e5fee2 | ||
|
|
e2aa482187 | ||
|
|
8f31048619 | ||
|
|
52b66db729 | ||
|
|
39e8e4a2ae | ||
|
|
8875710f3d | ||
|
|
858551411b | ||
|
|
4ed5e8a434 | ||
|
|
e0a170db92 | ||
|
|
fa17a5f745 | ||
|
|
dc59c9bf0c | ||
|
|
823fa259e9 | ||
|
|
47491fe749 | ||
|
|
c81327fdb5 | ||
|
|
a82b728d98 | ||
|
|
795aca19e0 | ||
|
|
ec5b92dd7a | ||
|
|
8a0ff999bd | ||
|
|
7b18bcafd6 | ||
|
|
1931101545 | ||
|
|
ae3eab8320 | ||
|
|
abb8cbd23f | ||
|
|
5165cf20c8 | ||
|
|
2cb5acb9f9 | ||
|
|
605058522e | ||
|
|
431231c4ee | ||
|
|
41ca9fc251 | ||
|
|
371e5119f1 | ||
|
|
1a25984e88 | ||
|
|
c1277a2bf5 | ||
|
|
3df41090e4 | ||
|
|
1b73ded110 | ||
|
|
6fe2674f5b | ||
|
|
c3eb1369ac | ||
|
|
9c380e52d1 | ||
|
|
e04b05dcc8 | ||
|
|
c2695b1180 | ||
|
|
d3884d764d | ||
|
|
f05d33969f | ||
|
|
f225e6cf75 | ||
|
|
14170ade1f | ||
|
|
c8749940f7 | ||
|
|
dbff17a1ff | ||
|
|
1989f122c6 | ||
|
|
4972081d8a | ||
|
|
331ad00758 | ||
|
|
b64fefa73c | ||
|
|
c5f73c5e26 | ||
|
|
022367c8d6 | ||
|
|
5853f9b034 | ||
|
|
eac9edb13a | ||
|
|
d5a9f68575 | ||
|
|
3caba87924 | ||
|
|
f469d591b8 | ||
|
|
72e3a115d6 | ||
|
|
df854f333c | ||
|
|
23605b2a80 | ||
|
|
33fa870f04 | ||
|
|
f1e2b21d8f | ||
|
|
fac9f6fac9 | ||
|
|
13aaab4798 | ||
|
|
0f08491f50 | ||
|
|
cfef1d62e6 | ||
|
|
d9e429bfb4 | ||
|
|
81ebcc8186 | ||
|
|
ac827a0f42 | ||
|
|
19593b5574 | ||
|
|
89abcdb17c | ||
|
|
89c7e58101 | ||
|
|
6d53262a9f | ||
|
|
24792b96cc | ||
|
|
bf5ca2f3cd | ||
|
|
23ffdabcc4 | ||
|
|
200cf00fe4 | ||
|
|
12c2214432 | ||
|
|
df9d2fbe2f | ||
|
|
fa6b93fad2 | ||
|
|
942b7cbb43 | ||
|
|
88e0016e31 | ||
|
|
54dda96849 | ||
|
|
acec6112cb | ||
|
|
7e777625a5 | ||
|
|
fe818eb7e6 | ||
|
|
82218ce18f | ||
|
|
5bd5c8289c | ||
|
|
26998e912b | ||
|
|
02fd57f9a4 | ||
|
|
f76ca049cc | ||
|
|
f4ab932924 | ||
|
|
d77946a5ab | ||
|
|
0d190a4c80 | ||
|
|
201db412a4 | ||
|
|
a1a441d397 | ||
|
|
ba9ba01dfe | ||
|
|
ad19e11213 | ||
|
|
2ff09ef382 | ||
|
|
62ea11ef7b | ||
|
|
2c1e036857 | ||
|
|
11fd512c5e | ||
|
|
7d6fa48a74 | ||
|
|
31b5114a8d | ||
|
|
1cd7b7e0c7 | ||
|
|
1d1e7b640a | ||
|
|
db5f99f3f6 | ||
|
|
25dc08eb79 | ||
|
|
11fe75578c | ||
|
|
c8c316a06f | ||
|
|
156f0a10cb | ||
|
|
9c0c2416d6 | ||
|
|
858325e40d | ||
|
|
c7604672fc | ||
|
|
49f9644714 | ||
|
|
49fa5bf14a | ||
|
|
e540c9d58d | ||
|
|
9d6f51695a | ||
|
|
2bc5b39895 | ||
|
|
3e4f1ab23a | ||
|
|
ec2f6e8490 | ||
|
|
472531d1b6 | ||
|
|
4fe235cdbd | ||
|
|
e3051d6427 | ||
|
|
467d4818d8 | ||
|
|
84bd756cdf | ||
|
|
1a2981e5f1 | ||
|
|
cedd9ac79b | ||
|
|
94f7b9295d | ||
|
|
78ff767b9e | ||
|
|
762728c0a6 | ||
|
|
377f59847e | ||
|
|
92a204c253 | ||
|
|
2b4199c0a6 | ||
|
|
ee75ef19aa | ||
|
|
106b3447eb | ||
|
|
3e3448adb1 | ||
|
|
3b3803f57e | ||
|
|
26f7ee56ab | ||
|
|
877eeeab5d | ||
|
|
ce7e796d93 | ||
|
|
3a4b8738fc | ||
|
|
eb1d16740e | ||
|
|
f08ca83440 | ||
|
|
4d0ac56bd8 | ||
|
|
01bbff42db | ||
|
|
4697704763 | ||
|
|
4a66802b37 | ||
|
|
edfa7f0c41 | ||
|
|
ea593d5002 | ||
|
|
88a2b25281 | ||
|
|
5da78f7565 | ||
|
|
f036350b0a | ||
|
|
c7b6a9ed9e | ||
|
|
5225d89210 | ||
|
|
eee22d7e70 | ||
|
|
1884996e53 | ||
|
|
451b26fa93 | ||
|
|
4fe3b93fb0 | ||
|
|
51bf5890d3 | ||
|
|
23e768d629 | ||
|
|
e1fdca5f41 | ||
|
|
d3ff2823dc | ||
|
|
4e3dc99479 | ||
|
|
e45acac6b6 | ||
|
|
3456b8cf23 | ||
|
|
7b02351f11 | ||
|
|
f3f66e1873 | ||
|
|
13cc103be2 | ||
|
|
ae8c062631 | ||
|
|
49c847f38a | ||
|
|
0070b8731d | ||
|
|
efd9feca0e | ||
|
|
2663f5e3f4 | ||
|
|
36ae73316a | ||
|
|
a467d853b0 | ||
|
|
7a648f0c5e | ||
|
|
dabf9fc9cb | ||
|
|
f1fa3a4c9c | ||
|
|
cb6926e821 | ||
|
|
213bcd1b2a | ||
|
|
33e437f865 | ||
|
|
055892cd62 | ||
|
|
4da012c871 | ||
|
|
accd6cb143 | ||
|
|
46fd26a879 | ||
|
|
0a9644a8a0 | ||
|
|
7ad09acec6 | ||
|
|
76d1c8e8bd | ||
|
|
5d33805a6f | ||
|
|
82445d2ba2 | ||
|
|
70fc67199f | ||
|
|
84e27a7c4d | ||
|
|
c41728b97c | ||
|
|
512ed02db2 | ||
|
|
5babe993df | ||
|
|
6ae1992329 | ||
|
|
7f44701670 | ||
|
|
00fb93a87f | ||
|
|
5338cca79b | ||
|
|
bb5e71c651 | ||
|
|
437eb9f793 | ||
|
|
23f5af93ce | ||
|
|
55f6e86a56 | ||
|
|
aa50c4466e | ||
|
|
1889ef2d8c | ||
|
|
3a96f63798 | ||
|
|
18db311712 | ||
|
|
000e9746ba | ||
|
|
7fa6c45f07 | ||
|
|
90f6f6d68d | ||
|
|
b24f82d257 | ||
|
|
bbcc58f103 | ||
|
|
0c3cade976 | ||
|
|
2246b79a85 | ||
|
|
d3bd8289ec | ||
|
|
7b670f229e | ||
|
|
b43e7902df | ||
|
|
7e2dc67c4e | ||
|
|
e56155f9a0 | ||
|
|
4cdf5e6b3d | ||
|
|
821f87b880 | ||
|
|
ecbcc6dcaa | ||
|
|
cd9adbdf7e | ||
|
|
6efa7f3c5b | ||
|
|
48409fc572 | ||
|
|
373a10f12c | ||
|
|
18cec4f88d | ||
|
|
4031b65099 | ||
|
|
bc09210ada | ||
|
|
affb24fc11 | ||
|
|
b5870b3ae4 | ||
|
|
edb3da4f74 | ||
|
|
19df78dbe2 | ||
|
|
4131f3b2fe | ||
|
|
4c80deae25 | ||
|
|
c6eb703648 | ||
|
|
24598ac463 | ||
|
|
b98377d5cb | ||
|
|
08bf8ecc89 | ||
|
|
7dbb926787 | ||
|
|
a5fb8575a6 | ||
|
|
f9315a127f | ||
|
|
22d0d55d79 | ||
|
|
50ab84af42 | ||
|
|
4397f274a5 | ||
|
|
374aac7ff5 | ||
|
|
e8aa88b317 | ||
|
|
5ed2faebc4 | ||
|
|
0767b6e9cf | ||
|
|
174e609526 | ||
|
|
63111ed938 | ||
|
|
42298947cc | ||
|
|
427a92f0f3 | ||
|
|
4a0219e9da | ||
|
|
b7a1f16a9d | ||
|
|
5994edcf17 | ||
|
|
754e7741c7 | ||
|
|
4f428b0e5d | ||
|
|
ad60b1dec6 | ||
|
|
d1a1fce7e1 | ||
|
|
d69f5ac18d | ||
|
|
72a45ef014 | ||
|
|
3162a531b7 | ||
|
|
02f6431109 | ||
|
|
af5ece3fe9 | ||
|
|
91980319d2 | ||
|
|
8383053b5c | ||
|
|
7d7e2f029d | ||
|
|
3827d8a5ce | ||
|
|
2941f11c54 | ||
|
|
9eed139cc9 | ||
|
|
ab55dde534 | ||
|
|
0bc372bf86 | ||
|
|
4c737e315e | ||
|
|
d8e515272a | ||
|
|
30f483245c | ||
|
|
8527594b3b | ||
|
|
12ec9b08bf | ||
|
|
3fed2ef08d | ||
|
|
d9b9586af3 | ||
|
|
6fda7393b3 | ||
|
|
ac436db456 | ||
|
|
e65f8fbb32 | ||
|
|
5b716dcdf2 | ||
|
|
e88d8bb7b7 | ||
|
|
94433b0107 | ||
|
|
8ac43d5617 | ||
|
|
90e63341bd | ||
|
|
36eb73b28e | ||
|
|
b499a054b9 | ||
|
|
ed7bd69116 | ||
|
|
e34beb5775 | ||
|
|
95c3044a40 | ||
|
|
ab7c982589 | ||
|
|
92643a244e | ||
|
|
c3e3dff805 | ||
|
|
5ec070843d | ||
|
|
9fbb97d0fa | ||
|
|
c51b0585a2 | ||
|
|
2f7bd0e083 | ||
|
|
594c47f08b | ||
|
|
94d5121a26 | ||
|
|
4f186c03f5 | ||
|
|
e86787c663 | ||
|
|
24ec6f154a | ||
|
|
c4abe1cf05 | ||
|
|
6f710f1f6b | ||
|
|
33d27ff04b | ||
|
|
9a5edb9431 | ||
|
|
2a328356cd | ||
|
|
48fdb94343 | ||
|
|
67343b5680 | ||
|
|
ad948f0ec1 | ||
|
|
c63a8fa21e | ||
|
|
2c53c01cbc | ||
|
|
a8fd35fd15 | ||
|
|
d9f93ec67e | ||
|
|
18e354d0ae | ||
|
|
09d24c542e | ||
|
|
9fcc1f5dc2 | ||
|
|
dff181accb | ||
|
|
ff292cfabc | ||
|
|
76d6ff14c2 | ||
|
|
89522a8197 | ||
|
|
a2eacc0fd3 | ||
|
|
565cc23c6c | ||
|
|
faccdb0ed7 | ||
|
|
b0815bcae6 | ||
|
|
c684b56124 | ||
|
|
dccde1a422 | ||
|
|
6cd99383b9 | ||
|
|
0116a31616 | ||
|
|
dc0ea4d828 | ||
|
|
ec1bacd1dd | ||
|
|
0ebb566645 | ||
|
|
7c3b01c911 | ||
|
|
ec02a77c19 | ||
|
|
1531762524 | ||
|
|
b79a66ce83 | ||
|
|
17d479bc93 | ||
|
|
5f59fdf471 | ||
|
|
a1ee34bb1c | ||
|
|
a799afc19c | ||
|
|
24130cde74 | ||
|
|
bd179f817f | ||
|
|
fd72aa4fac | ||
|
|
793fb5f388 | ||
|
|
eecf9f0726 | ||
|
|
9f212fc475 | ||
|
|
40ec3f14bf | ||
|
|
ad1df59c18 | ||
|
|
804cd04778 | ||
|
|
083664d858 | ||
|
|
c98721056d | ||
|
|
d2fe664fe5 | ||
|
|
ad35fc535e | ||
|
|
10bd3e1899 | ||
|
|
d14ae9d2c1 | ||
|
|
e4cce437d4 | ||
|
|
3a6b5c91d5 | ||
|
|
1b563c30c5 | ||
|
|
9cedaf18a8 | ||
|
|
5fd02ffc96 | ||
|
|
e5152439ef | ||
|
|
1a68035131 | ||
|
|
5294503483 | ||
|
|
f2d05c1355 | ||
|
|
8f806e6285 | ||
|
|
73e8631fcd | ||
|
|
29c260f7ce | ||
|
|
5318347ab2 | ||
|
|
40ddd2dfdb | ||
|
|
d36cc4acc1 | ||
|
|
6548d5da80 | ||
|
|
09aae6e34c | ||
|
|
aff4e0c2d0 | ||
|
|
08daaa38e9 | ||
|
|
55c9b8e322 | ||
|
|
9281344863 | ||
|
|
061806eb8f | ||
|
|
8067cb6e65 | ||
|
|
b31381ed4d | ||
|
|
2bf51c2034 | ||
|
|
cbf63b8c19 | ||
|
|
f715a0c8a3 | ||
|
|
f7259f745f | ||
|
|
423a550303 | ||
|
|
944e481f32 | ||
|
|
b409db3e29 | ||
|
|
6ae19f11ea | ||
|
|
eded461866 | ||
|
|
c1e40ebe1f | ||
|
|
406a4a3a91 | ||
|
|
896c8d1f5f | ||
|
|
816346e919 | ||
|
|
4c484f5125 | ||
|
|
c7aaa15a19 | ||
|
|
a5ae6e0631 | ||
|
|
0e248f8b83 | ||
|
|
2ca29efd77 | ||
|
|
3c9e5d2f0f | ||
|
|
61288a17ac | ||
|
|
fff28fdcbf | ||
|
|
84cc58d30b | ||
|
|
1f043c7088 | ||
|
|
9a966cfad9 | ||
|
|
e235e07bb2 | ||
|
|
1ba2a1fae0 | ||
|
|
e87c85b75c | ||
|
|
bd351197b2 | ||
|
|
4537f86bbc | ||
|
|
546d1df48f | ||
|
|
4ea7b864a8 | ||
|
|
489d3abe65 | ||
|
|
5dea99f139 | ||
|
|
11f107d60b | ||
|
|
829994f5b5 | ||
|
|
5315d80859 | ||
|
|
61a8000bf1 | ||
|
|
a189927b22 | ||
|
|
b3f00a0681 | ||
|
|
8cc5785e86 | ||
|
|
827d09289d | ||
|
|
0ce00531a6 | ||
|
|
4962d855af | ||
|
|
7e25e31d33 | ||
|
|
a1d56e2921 | ||
|
|
a34ac84e00 | ||
|
|
540b19c141 | ||
|
|
aa493c41d5 | ||
|
|
637f844601 | ||
|
|
05e66b2492 | ||
|
|
ae115c4908 | ||
|
|
28c456e4c5 | ||
|
|
7d8dd5ab09 | ||
|
|
adfbc7508e | ||
|
|
417eaa544f | ||
|
|
1a7909aaa4 | ||
|
|
0fea79a647 | ||
|
|
ed2d01df4d | ||
|
|
e56898fff4 | ||
|
|
7671066548 | ||
|
|
87cd49a291 | ||
|
|
60d06c92f6 | ||
|
|
f1cd8bac20 | ||
|
|
6020ff7ee1 | ||
|
|
cb55bd3612 | ||
|
|
e65727937c | ||
|
|
a1f3cb9270 | ||
|
|
3b85b20020 | ||
|
|
29f7badbfb | ||
|
|
5e7421ab24 | ||
|
|
8325a8ba02 | ||
|
|
9b93a28ddb | ||
|
|
92264ed37e | ||
|
|
9ce029b35d | ||
|
|
6300dd6737 | ||
|
|
ce8c6e0283 | ||
|
|
50e558644a | ||
|
|
1592e1c0ce | ||
|
|
ba7bda0b04 | ||
|
|
c7787d1390 | ||
|
|
a7d0d4892f | ||
|
|
463f1c540e | ||
|
|
079c348948 | ||
|
|
c50388a358 | ||
|
|
b637d79c50 | ||
|
|
2a16229056 | ||
|
|
7652a1dbc5 | ||
|
|
6d4071f42c | ||
|
|
5aeea48413 | ||
|
|
7632647e1d | ||
|
|
41a56fba20 | ||
|
|
1600dc4f84 | ||
|
|
c33fe2e928 | ||
|
|
cac4ca96d5 | ||
|
|
cc89986a94 | ||
|
|
3237de7e33 | ||
|
|
37d6f477c4 | ||
|
|
0f90f53f53 | ||
|
|
dc8d727bdb | ||
|
|
3be36f826c | ||
|
|
dc268285b0 | ||
|
|
7464cf7420 | ||
|
|
30c9956acd | ||
|
|
7cc579fd61 | ||
|
|
a6cce0c043 | ||
|
|
152e795c32 | ||
|
|
89a78bf43b | ||
|
|
840cad23b8 | ||
|
|
93817c5d3a | ||
|
|
470f8aed5b | ||
|
|
463bd4ef51 | ||
|
|
c8192ef7e3 | ||
|
|
6ed3c5d365 | ||
|
|
ca60a13bb4 | ||
|
|
96a83533dd | ||
|
|
99521e4d76 | ||
|
|
54f55ee257 | ||
|
|
84b08ef64a | ||
|
|
bd3a9eb345 | ||
|
|
7a2d38f2f1 | ||
|
|
370a0fad11 | ||
|
|
ec49868d27 | ||
|
|
af486998d2 | ||
|
|
5229643761 | ||
|
|
4fd4de7d52 | ||
|
|
8e467e8239 | ||
|
|
04c6ea2e66 | ||
|
|
b6182b4913 | ||
|
|
2bad1c0226 | ||
|
|
65506ba7e1 | ||
|
|
c93380dcf2 | ||
|
|
b755d745c5 | ||
|
|
c23a4be440 | ||
|
|
f89b5d0a34 | ||
|
|
99acb17d90 | ||
|
|
565dfb4b81 | ||
|
|
24f31dd92a | ||
|
|
63bcdb1019 | ||
|
|
5b2e70cda0 | ||
|
|
d97b18e633 | ||
|
|
1f589901d6 | ||
|
|
1afdfccf3b | ||
|
|
5bd745a188 | ||
|
|
9bc6b7b0f7 | ||
|
|
49bc941dd7 | ||
|
|
4037124c57 | ||
|
|
c1c0010126 | ||
|
|
9cdeeb4c94 | ||
|
|
a84fa8ac0c | ||
|
|
40be7c9fc1 | ||
|
|
6006026deb | ||
|
|
00b6261a1c | ||
|
|
e75aca9236 | ||
|
|
683f9fd13b | ||
|
|
18e1cec611 | ||
|
|
4d47ca26db | ||
|
|
42d41eb0f7 | ||
|
|
fea1d35d08 | ||
|
|
15ed114e87 | ||
|
|
f7037e3ecb | ||
|
|
75bd0a5ff7 | ||
|
|
c3a5964f17 | ||
|
|
cb6f595cc3 | ||
|
|
9aa96b4d03 | ||
|
|
0765b6770f | ||
|
|
761417833e | ||
|
|
e5ab4df3dc | ||
|
|
12345a67e8 | ||
|
|
173d71b04f | ||
|
|
6df837f9ba | ||
|
|
eea53ee9ef | ||
|
|
d352c9e8ca | ||
|
|
e5cc8ef97c | ||
|
|
af63a6ca09 | ||
|
|
a13e8d98e1 | ||
|
|
a7e5147104 | ||
|
|
017d81a8c6 | ||
|
|
77436644aa | ||
|
|
cc6191cef2 | ||
|
|
9aa28c3e3f | ||
|
|
055d27a9f6 | ||
|
|
002ff639a0 | ||
|
|
826694793c | ||
|
|
4699b9be7a | ||
|
|
22c52171d9 | ||
|
|
48924e3364 | ||
|
|
79c4b1cb27 | ||
|
|
b1bfcfb190 | ||
|
|
7ab2dac645 | ||
|
|
702fe0ba24 | ||
|
|
755bc35d09 | ||
|
|
7bbc10acc3 | ||
|
|
690e1b7f8a | ||
|
|
ca4f2a2aeb | ||
|
|
531b5d58ba | ||
|
|
aedbc88e3c | ||
|
|
f9cbd8fa61 | ||
|
|
a614c050e7 | ||
|
|
931bf29c47 | ||
|
|
673e6dec8d | ||
|
|
f8771a260c | ||
|
|
a635671276 | ||
|
|
144bdaa301 | ||
|
|
444f5ad446 | ||
|
|
618698aab3 | ||
|
|
830eb19420 | ||
|
|
cb22591182 | ||
|
|
4a9cd78d1d | ||
|
|
0f4e835f6c | ||
|
|
d51de3fa55 | ||
|
|
d89f8da5a2 | ||
|
|
f4b2db89ec | ||
|
|
214b39ce8f | ||
|
|
a327b460bb | ||
|
|
ddf2fc5a74 | ||
|
|
2bd2fd7ef7 | ||
|
|
baca029fd0 | ||
|
|
0f180ff979 | ||
|
|
bd94b5dd2e | ||
|
|
31feef398f | ||
|
|
49a8f72151 | ||
|
|
aae4db7ebf | ||
|
|
61eca643d4 | ||
|
|
d428f9c4c0 | ||
|
|
4cccd8f6d9 | ||
|
|
bd1182ab39 | ||
|
|
adbf61a06b | ||
|
|
b44739e33c | ||
|
|
340b026951 | ||
|
|
30bf3afc1d | ||
|
|
119f9ec6dd | ||
|
|
3a24d094c9 | ||
|
|
f633d9b3bf | ||
|
|
8ee985928e | ||
|
|
98def7b879 | ||
|
|
d2d5f81782 | ||
|
|
b1fb365f22 | ||
|
|
fc69d55314 | ||
|
|
ee3f29c569 | ||
|
|
8510d648aa | ||
|
|
33d7b6f67b | ||
|
|
a1f50a0c4f | ||
|
|
57947441ce | ||
|
|
f1515c5063 | ||
|
|
ec10f36839 | ||
|
|
05b28b8269 | ||
|
|
9c421218b8 | ||
|
|
2de8f02814 | ||
|
|
74dae9316e | ||
|
|
acdd18f8df | ||
|
|
6eddbd1ecf | ||
|
|
8c278d6555 | ||
|
|
127a85ea89 | ||
|
|
ed65d5da44 | ||
|
|
dcd30d34da | ||
|
|
9879420d31 | ||
|
|
fe7357bca2 | ||
|
|
28bbb79e60 | ||
|
|
78da1157ab | ||
|
|
21a9266c8f | ||
|
|
985b5a68fe | ||
|
|
b89e7639cd | ||
|
|
1760041e56 | ||
|
|
8e4bbeff28 | ||
|
|
df6127f528 | ||
|
|
60140bdc1e | ||
|
|
403e820f55 | ||
|
|
bcb1f5db6c | ||
|
|
20b85f7567 | ||
|
|
92c136d4c3 | ||
|
|
163ac96cd7 | ||
|
|
08c774e818 | ||
|
|
c229b1c60c | ||
|
|
422b6d56f7 | ||
|
|
e84360efe9 | ||
|
|
44882e5418 | ||
|
|
7eb702545b | ||
|
|
650bd0bfb9 | ||
|
|
9e021ce7e3 | ||
|
|
10c95c68d8 | ||
|
|
95ee0c60ca | ||
|
|
1f4e45eca6 | ||
|
|
b80afc2c79 | ||
|
|
12623012ed | ||
|
|
e6f30956db | ||
|
|
85442810f3 | ||
|
|
0f449d05aa | ||
|
|
0833d4dd56 | ||
|
|
170da33755 | ||
|
|
912a8f9f2c | ||
|
|
d4cf8503cd | ||
|
|
1fbf83c562 | ||
|
|
b3da731e1f | ||
|
|
65f151bbef | ||
|
|
545c68f122 | ||
|
|
d4bfe17a72 | ||
|
|
2a7733e61f | ||
|
|
d658c18566 | ||
|
|
74cf6ef598 | ||
|
|
2aacadaa82 | ||
|
|
ae33070507 | ||
|
|
e1b29f2985 | ||
|
|
969c3fd18e | ||
|
|
f62aac1c77 | ||
|
|
8d1a73c339 | ||
|
|
09c5cdb6a2 | ||
|
|
cae1026fee | ||
|
|
393a834891 | ||
|
|
260d2a5f42 | ||
|
|
f7d247fb2c | ||
|
|
4d91bde49c | ||
|
|
7ca647b763 | ||
|
|
85d54292b2 | ||
|
|
5edbe16e18 | ||
|
|
8a06ef1322 | ||
|
|
0568c8f841 | ||
|
|
0de67936c1 | ||
|
|
3f2a8dbb61 | ||
|
|
e830fd9bef | ||
|
|
ada0bd4012 | ||
|
|
a06a46b498 | ||
|
|
b746be4c67 | ||
|
|
45b4b3a6f8 | ||
|
|
fddac90db2 | ||
|
|
efb9f4ca2d | ||
|
|
ea25f2e854 | ||
|
|
1bdaf14eb5 | ||
|
|
d9c2e0f342 | ||
|
|
097bae35ab | ||
|
|
723bb9d942 | ||
|
|
e989411074 | ||
|
|
0ee41f262e | ||
|
|
d1f8560aaf | ||
|
|
56d73ea9a2 | ||
|
|
a1eb62a176 | ||
|
|
50dc476ceb | ||
|
|
1ae3f9f319 | ||
|
|
677a26cac1 | ||
|
|
cd069c016b | ||
|
|
6768662f1d | ||
|
|
f51808a000 | ||
|
|
117232687a | ||
|
|
4a3a555326 | ||
|
|
e6b7edc180 | ||
|
|
9284aa39d5 | ||
|
|
0d74ad8cbf | ||
|
|
8b84b97cf0 | ||
|
|
e77097de2d | ||
|
|
d350c18999 | ||
|
|
dc236d6ec1 | ||
|
|
90585fbb4f | ||
|
|
6c7cd80bb3 | ||
|
|
ba830aee5a | ||
|
|
57b7c4785a | ||
|
|
4a7bcbfd38 | ||
|
|
4f30edd960 | ||
|
|
d3bf2271fc | ||
|
|
0ae2225d9e | ||
|
|
a4515e8eae | ||
|
|
821a15ecaa | ||
|
|
9c172e2010 | ||
|
|
be75468100 | ||
|
|
f645d68ff2 | ||
|
|
14a8be793c | ||
|
|
932b401aa9 | ||
|
|
fb7664ea3c | ||
|
|
c0d27ee01d | ||
|
|
61050ec861 | ||
|
|
6191382a39 | ||
|
|
9c9e8269b6 | ||
|
|
a7f0c08a94 | ||
|
|
16269ee144 | ||
|
|
fdd1174d8b | ||
|
|
879fe24b62 | ||
|
|
ec31b0361e | ||
|
|
42398fcd17 | ||
|
|
877db870d2 | ||
|
|
a218d84931 | ||
|
|
7eaa286a7a | ||
|
|
d534dacf18 | ||
|
|
7c2fd66929 | ||
|
|
23ee5cf982 | ||
|
|
f3fd986512 | ||
|
|
d63b5acd6e | ||
|
|
44dc150f66 | ||
|
|
7a17c74762 | ||
|
|
c090476925 | ||
|
|
e6716613cc | ||
|
|
99ffd3dc95 | ||
|
|
8dbcac7afc | ||
|
|
f8f0e0ca45 | ||
|
|
c891501579 | ||
|
|
244873f2fd | ||
|
|
2db0b24002 | ||
|
|
05dde993fd | ||
|
|
af555a2089 | ||
|
|
491c62e8a7 | ||
|
|
8142eb8235 | ||
|
|
47959cddef | ||
|
|
357b9af52f | ||
|
|
bfa9d5b12e | ||
|
|
5f8f03137c | ||
|
|
5f08542009 | ||
|
|
6dd27c8799 | ||
|
|
fd53fe1cc3 | ||
|
|
9c284e6b8e | ||
|
|
75a977fc39 | ||
|
|
5cca594f7d | ||
|
|
0aeab7ef69 | ||
|
|
a8e92871c2 | ||
|
|
b988715c2c | ||
|
|
ba82be7a89 | ||
|
|
3906bf9725 | ||
|
|
da74b29f21 | ||
|
|
3728927586 | ||
|
|
55201311c7 | ||
|
|
bc1c70bd0c | ||
|
|
214015dfe9 | ||
|
|
fb6167ec6d | ||
|
|
d057ab061b | ||
|
|
f30b1b70f6 | ||
|
|
c9a48eb832 | ||
|
|
61c2b943cf | ||
|
|
2e3f43674f | ||
|
|
9eb3ed4273 | ||
|
|
ac261a0bfd | ||
|
|
4a15e277aa | ||
|
|
a2cbc49bb1 | ||
|
|
5d317b6d94 | ||
|
|
c269cbedac | ||
|
|
f36074376b |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
ko_fi: embeddedt
|
||||
78
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
78
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
name: Bug Report
|
||||
description: "For reporting bugs and other defects"
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >-
|
||||
**Need help?** Ask on [Discord](https://discord.gg/rN9Y7caguP) instead of opening an issue.
|
||||
|
||||
**Issues that do not meet the requirements below (or are otherwise impossible to address with the given info) will be closed without investigation.**
|
||||
- type: checkboxes
|
||||
id: confirmations
|
||||
attributes:
|
||||
label: Checklist
|
||||
options:
|
||||
- label: I am reporting a defect, not asking for help
|
||||
required: true
|
||||
- label: I have searched existing issues and this has not been reported
|
||||
required: true
|
||||
- label: I have reduced my mod list to the minimum required to reproduce this issue (see below)
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: >-
|
||||
Describe the issue in detail. Be sure to include what you expected to happen and what actually happened.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: minimal-mods
|
||||
attributes:
|
||||
label: Minimal Mod List
|
||||
description: >-
|
||||
List ONLY the mods required to reproduce this issue. Maintainers have debugging tools that help them
|
||||
locate problems quickly, but these generally don't work well in modpacks or large mod sets.
|
||||
A minimal list should typically contain fewer than 10 mods.
|
||||
|
||||
Reports with large mod lists will likely be closed without investigation, unless the problem is very clear.
|
||||
|
||||
If you don't know which mods are causing your problem, use binary search:
|
||||
|
||||
1. Remove half your mods
|
||||
|
||||
2. Test if the issue still occurs
|
||||
|
||||
3. If yes, remove half again. If no, restore the last removed half and repeat from step 1.
|
||||
|
||||
4. Repeat until only the necessary mods remain
|
||||
placeholder: "- ModernFix 5.x.x\n- SomeMod 1.2.3"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description-reproduction-steps
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: >-
|
||||
Provide clear steps to reproduce the bug. Each step should be a single concrete action.
|
||||
|
||||
Maintainers are busy and need to be able to quickly replicate your problem. Your reproduction steps should be
|
||||
clear enough for someone who is unfamiliar with your mods to follow in 5 minutes or less (not counting time
|
||||
to launch the game).
|
||||
|
||||
Providing vague steps is likely to result in the issue being closed.
|
||||
|
||||
placeholder: "1. \n2. \n3. "
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: diagnostic-info
|
||||
attributes:
|
||||
label: Diagnostic Info
|
||||
description: >-
|
||||
Drag and drop `latest.log` from `.minecraft/logs/` for the session where the issue occurred.
|
||||
Do not paste log text inline. Issues without a valid `latest.log` will be closed.
|
||||
|
||||
If a crash occurred, also attach the relevant file from `.minecraft/crash-reports/`.
|
||||
validations:
|
||||
required: true
|
||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
6
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: For help with other issues, join our Discord community
|
||||
url: https://discord.gg/rN9Y7caguP
|
||||
about: This is the best option for getting help with mod installation, performance issues, and any other support inquiries
|
||||
# Copied from https://github.com/CaffeineMC/sodium-fabric#community
|
||||
133
.github/workflows/gradle.yml
vendored
133
.github/workflows/gradle.yml
vendored
|
|
@ -1,7 +1,4 @@
|
|||
# This workflow will build a Java project with Gradle
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: Build mod
|
||||
name: Build ModernFix using Gradle
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -13,21 +10,127 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
issues: write
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v3
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
cache: 'gradle'
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build the mod
|
||||
run: ./gradlew --no-daemon build
|
||||
- uses: actions/upload-artifact@v2
|
||||
java-version: 21
|
||||
check-latest: true
|
||||
- name: Check if release branch
|
||||
id: check_branch
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" =~ ^refs/heads/[0-9]+\. ]]; then
|
||||
echo "is_release=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "is_release=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
with:
|
||||
cache-read-only: ${{ steps.check_branch.outputs.is_release != 'true' }}
|
||||
gradle-home-cache-cleanup: true
|
||||
- name: Remove tags for release on other versions
|
||||
if: steps.check_branch.outputs.is_release == 'true'
|
||||
run: ./scripts/tagcleaner.sh
|
||||
- name: Build ModernFix using Gradle
|
||||
run: ./gradlew build
|
||||
- name: Run mixin audit
|
||||
run: timeout 60 xvfb-run ./gradlew runAuditClient
|
||||
- name: Publish mod to CurseForge & Modrinth
|
||||
if: steps.check_branch.outputs.is_release == 'true'
|
||||
run: ./gradlew publishMods copyJarToBin
|
||||
env:
|
||||
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
- name: Capture mod version
|
||||
if: steps.check_branch.outputs.is_release == 'true'
|
||||
run: |
|
||||
echo "MOD_VERSION=$(./gradlew properties -q | grep '^version:' | awk '{print $2}')" >> $GITHUB_ENV
|
||||
echo "MC_VERSION=$(grep '^minecraft_version=' gradle.properties | cut -d= -f2)" >> $GITHUB_ENV
|
||||
- name: Comment on fixed issues
|
||||
if: steps.check_branch.outputs.is_release == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const branch = context.ref.replace('refs/heads/', '');
|
||||
const { data: runs } = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'gradle.yml',
|
||||
branch,
|
||||
status: 'success',
|
||||
per_page: 1
|
||||
});
|
||||
|
||||
const logArgs = runs.workflow_runs.length > 0
|
||||
? `${runs.workflow_runs[0].head_sha}..${context.sha}`
|
||||
: `-1 ${context.sha}`;
|
||||
const log = execSync(`git log ${logArgs} --format=%s%n%b`, { encoding: 'utf8' });
|
||||
|
||||
const issueNumbers = new Set();
|
||||
const pattern = /(?:fix(?:es|ed)?|close[sd]?|resolve[sd]?)\s+#(\d+)/gi;
|
||||
let match;
|
||||
while ((match = pattern.exec(log)) !== null) {
|
||||
issueNumbers.add(parseInt(match[1]));
|
||||
}
|
||||
|
||||
if (issueNumbers.size === 0) {
|
||||
console.log('No fixed issues found in commits');
|
||||
return;
|
||||
}
|
||||
|
||||
const MARKER = '<!-- modernfix-fix-tracker -->';
|
||||
const modVersion = process.env.MOD_VERSION;
|
||||
const mcVersion = process.env.MC_VERSION;
|
||||
const newLine = `- ${modVersion} for Minecraft ${mcVersion}`;
|
||||
|
||||
for (const issueNumber of issueNumbers) {
|
||||
try {
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
const existing = comments.find(c => c.body.includes(MARKER));
|
||||
if (existing) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existing.id,
|
||||
body: existing.body + `\n${newLine}`
|
||||
});
|
||||
console.log(`Updated comment on issue #${issueNumber}`);
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
body: `${MARKER}\nThe fix for this issue has been released in the following versions of ModernFix:\n${newLine}`
|
||||
});
|
||||
console.log(`Created comment on issue #${issueNumber}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Could not comment on #${issueNumber}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
- name: Upload Artifacts to GitHub
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Package
|
||||
path: bin
|
||||
path: bin
|
||||
|
|
|
|||
37
.github/workflows/release.yml
vendored
37
.github/workflows/release.yml
vendored
|
|
@ -1,37 +0,0 @@
|
|||
name: release-artifacts
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
- name: Build and publish mod to CurseForge & Modrinth
|
||||
run: ./gradlew forge:publishToModSites fabric:publishToModSites forge:copyJarToBin fabric:copyJarToBin
|
||||
env:
|
||||
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
- name: Upload assets to GitHub
|
||||
uses: AButler/upload-release-assets@v2.0
|
||||
with:
|
||||
files: 'bin/*'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Add changelog to release
|
||||
uses: irongut/EditRelease@v1.2.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: ${{ github.event.release.id }}
|
||||
replacebody: true
|
||||
files: "CHANGELOG.md"
|
||||
27
.github/workflows/wiki_update.yml
vendored
Normal file
27
.github/workflows/wiki_update.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
name: Update wiki using WikiGen
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '1.**'
|
||||
|
||||
jobs:
|
||||
wikigen:
|
||||
if: github.repository_owner == 'embeddedt'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Generate Markdown Patch-List
|
||||
run: python3 scripts/gen-markdown-patchlist.py
|
||||
- name: Very legitimate hack for wiki push race condition
|
||||
run: sleep $((1 + (RANDOM % 30)))
|
||||
shell: bash
|
||||
- name: Upload generated file to wiki
|
||||
uses: SwiftDocOrg/github-wiki-publish-action@v1
|
||||
with:
|
||||
path: "doc/generated"
|
||||
env:
|
||||
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.WIKI_TOKEN }}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -2,6 +2,8 @@ eclipse
|
|||
run
|
||||
libs
|
||||
media
|
||||
__pycache__
|
||||
*.pyc
|
||||
classes/
|
||||
.architectury-transformer/
|
||||
fabric/fabricloader.log
|
||||
|
|
|
|||
21
CONTRIBUTING.md
Normal file
21
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
ModernFix is a standard Minecraft-style Gradle project powered by Architectury Loom. To build the mod for all platforms,
|
||||
run the `build` task (e.g. via `./gradlew build`). You can also use `./gradlew forge:build` or `./gradlew fabric:build`
|
||||
to build for just one loader (e.g. when debugging and wanting to rebuild quickly).
|
||||
|
||||
You must use Java 21 to develop ModernFix as the toolchain requires it. Nonetheless, the built 1.20.1 JAR is still
|
||||
compatible with Java 17.
|
||||
|
||||
## Submitting pull requests
|
||||
|
||||
Code or documentation contributions are welcome. Please keep the following points in mind:
|
||||
|
||||
* This project supports many Minecraft versions. Ideally, contributions should be made to the oldest relevant MC version (currently 1.20)
|
||||
and then they will be ported forward.
|
||||
|
||||
This somewhat unconventional policy ensures that all supported versions are treated equal when it comes to development,
|
||||
rather than the onus being on other modders and players to backport changes that are needed. Changes to older versions are
|
||||
quickly ported up to the latest one as part of the regular development cycle. You are still welcome to open PRs against
|
||||
a newer branch if desired - but the change will likely be applied manually and not merged as a regular PR.
|
||||
|
||||
* Please ensure your code is reasonably neat and sufficiently documented. Remember that self-documenting code is always
|
||||
better.
|
||||
10
README.md
10
README.md
|
|
@ -6,11 +6,11 @@ Some fixes are based on prior work in various Forge PRs (check commit history an
|
|||
is directly derived from Sodium and used under the terms of the LGPL-3.0 license.
|
||||
|
||||
## Development builds (generally stable, but may occasionally have bugs)
|
||||
- 1.16.5, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.16/Package.zip
|
||||
- 1.18.2, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.18/Package.zip
|
||||
- 1.19.2, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.19.2/Package.zip
|
||||
- 1.19.4, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.19.4/Package.zip
|
||||
- 1.20.1, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.20/Package.zip
|
||||
- 1.16.5: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.16/Package.zip
|
||||
- 1.18.2: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.18/Package.zip
|
||||
- 1.19.2: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.19.2/Package.zip
|
||||
- 1.20.1: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.20/Package.zip
|
||||
- 1.20.2: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.20.2/Package.zip
|
||||
|
||||
------------
|
||||
|
||||
|
|
|
|||
54
annotation-processor/build.gradle
Normal file
54
annotation-processor/build.gradle
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
plugins {
|
||||
id 'com.gradleup.shadow' version '8.3.9'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url uri("https://maven.fabricmc.net") }
|
||||
maven { url "https://maven.neoforged.net/releases" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
|
||||
compileOnly 'com.google.auto.service:auto-service:1.1.1'
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
shadow 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'com.google.auto:auto-common:1.2.1'
|
||||
shadow 'com.google.auto:auto-common:1.2.1'
|
||||
implementation 'com.google.guava:guava:21.0'
|
||||
shadow 'com.google.guava:guava:21.0'
|
||||
|
||||
implementation project(":annotations")
|
||||
shadow project(":annotations")
|
||||
// Shadow annotations
|
||||
implementation 'net.fabricmc:sponge-mixin:0.12.5+'
|
||||
implementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||
implementation 'net.minecraftforge:mergetool:1.1.7'
|
||||
implementation 'net.neoforged:mergetool:2.0.2'
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.release = 17
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
dependencies {
|
||||
include(dependency('net.fabricmc:sponge-mixin:'))
|
||||
include(dependency('net.fabricmc:fabric-loader:'))
|
||||
include(dependency(':mergetool:'))
|
||||
}
|
||||
// shadowJar bug
|
||||
include '*.jar'
|
||||
include 'META-INF/services/javax.annotation.processing.Processor'
|
||||
include 'META-INF/gradle/incremental.annotation.processors'
|
||||
include 'org/spongepowered/asm/mixin/Mixin.class'
|
||||
include 'org/fury_phoenix/**/*'
|
||||
include {it.getName() == 'OnlyIn.class'}
|
||||
include {it.getName() == 'Dist.class'}
|
||||
include {it.getName() == 'Environment.class'}
|
||||
include {it.getName() == 'EnvType.class'}
|
||||
}
|
||||
|
||||
version = '1.1.4'
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
package org.fury_phoenix.mixinAp.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.annotation.IgnoreMixin;
|
||||
import org.fury_phoenix.mixinAp.util.TypedAccessorMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
|
||||
import static java.util.AbstractMap.SimpleImmutableEntry;
|
||||
|
||||
public class ClientMixinValidator {
|
||||
|
||||
private final Messager messager;
|
||||
|
||||
private final Elements elemUtils;
|
||||
|
||||
private final Types types;
|
||||
|
||||
private final boolean debug;
|
||||
|
||||
private static final TypedAccessorMap<Annotation> markers = new TypedAccessorMap<>();
|
||||
|
||||
private static final Map.Entry<Class<Environment>, Function<? super Environment, ?>>
|
||||
FabricAccessor = new SimpleImmutableEntry<>(Environment.class, Environment::value);
|
||||
|
||||
private static final Map.Entry<
|
||||
Class<net.minecraftforge.api.distmarker.OnlyIn>,
|
||||
Function<? super net.minecraftforge.api.distmarker.OnlyIn, ?>>
|
||||
ForgeAccessor = new SimpleImmutableEntry<>(
|
||||
net.minecraftforge.api.distmarker.OnlyIn.class,
|
||||
net.minecraftforge.api.distmarker.OnlyIn::value
|
||||
);
|
||||
|
||||
private static final Map.Entry<
|
||||
Class<net.neoforged.api.distmarker.OnlyIn>,
|
||||
Function<? super net.neoforged.api.distmarker.OnlyIn, ?>>
|
||||
NeoForgeAccessor = new SimpleImmutableEntry<>(
|
||||
net.neoforged.api.distmarker.OnlyIn.class,
|
||||
net.neoforged.api.distmarker.OnlyIn::value
|
||||
);
|
||||
|
||||
static {
|
||||
markers.put(FabricAccessor);
|
||||
markers.put(ForgeAccessor);
|
||||
markers.put(NeoForgeAccessor);
|
||||
}
|
||||
|
||||
private static final Collection<String> unannotatedClasses = new HashSet<>();
|
||||
|
||||
public ClientMixinValidator(ProcessingEnvironment env) {
|
||||
debug = Boolean.valueOf(env.getOptions().get("org.fury_phoenix.mixinAp.validator.debug"));
|
||||
messager = env.getMessager();
|
||||
elemUtils = env.getElementUtils();
|
||||
types = env.getTypeUtils();
|
||||
}
|
||||
|
||||
public boolean validateMixin(TypeElement annotatedMixinClass) {
|
||||
return targetsClient(annotatedMixinClass) &&
|
||||
(annotatedMixinClass.getAnnotation(ClientOnlyMixin.class) == null);
|
||||
}
|
||||
|
||||
public boolean targetsClient(TypeElement annotatedMixinClass) {
|
||||
return targetsClient(getTargets(annotatedMixinClass)) &&
|
||||
!isIgnored(annotatedMixinClass);
|
||||
}
|
||||
|
||||
private boolean targetsClient(Collection<?> classTargets) {
|
||||
return classTargets.stream().anyMatch(this::targetsClient);
|
||||
}
|
||||
|
||||
private boolean targetsClient(Object classTarget) {
|
||||
if (classTarget instanceof TypeElement te) {
|
||||
return isClientMarked(te);
|
||||
} else if (classTarget instanceof TypeMirror tm) {
|
||||
var el = types.asElement(tm);
|
||||
return el != null ? targetsClient(el) : warn("TypeMirror of " + tm);
|
||||
} else if (classTarget instanceof String s) {
|
||||
var te = elemUtils.getTypeElement(toSourceString(s.split("\\$")[0]));
|
||||
return te != null ? targetsClient(te) : warn(s);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled type: "
|
||||
+ classTarget.getClass() + "\n" + "Stringified contents: "
|
||||
+ classTarget.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isClientMarked(TypeElement te) {
|
||||
for (var entry : markers.entrySet()) {
|
||||
var marker = te.getAnnotation(entry.getKey());
|
||||
if(marker == null) continue;
|
||||
|
||||
return entry.getValue().apply(marker).toString().equals("CLIENT");
|
||||
}
|
||||
if(debug && unannotatedClasses.add(te.toString())) {
|
||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
||||
"No marker annotations present on " + te + "!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isIgnored(TypeElement te) {
|
||||
if(te.getAnnotation(IgnoreMixin.class) != null) {
|
||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
||||
toSourceString(te.toString()) + " is ignored!");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean warn(Object o) {
|
||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
||||
toSourceString(o.toString()) + " can't be loaded, so it is skipped!");
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map.Entry<? extends CharSequence, ? extends CharSequence>
|
||||
getClientMixinEntry(TypeElement annotatedMixinClass) {
|
||||
return new SimpleImmutableEntry<>(
|
||||
annotatedMixinClass.getQualifiedName(),
|
||||
getTargets(annotatedMixinClass)
|
||||
.stream()
|
||||
.filter(this::targetsClient)
|
||||
.map(Object::toString)
|
||||
.map(ClientMixinValidator::toSourceString)
|
||||
.collect(Collectors.joining(", "))
|
||||
);
|
||||
}
|
||||
|
||||
private Collection<Object> getTargets(TypeElement annotatedMixinClass) {
|
||||
Collection<? extends TypeMirror> clzsses = Set.of();
|
||||
Collection<? extends String> imaginaries = Set.of();
|
||||
TypeMirror MixinElement = elemUtils.getTypeElement(Mixin.class.getName()).asType();
|
||||
for (var mirror : annotatedMixinClass.getAnnotationMirrors()) {
|
||||
if(!types.isSameType(mirror.getAnnotationType(), MixinElement))
|
||||
continue;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
var wrappedClzss = (List<? extends AnnotationValue>)
|
||||
getAnnotationValue(mirror, "value").getValue();
|
||||
|
||||
clzsses = wrappedClzss.stream()
|
||||
.map(AnnotationValue::getValue)
|
||||
.map(TypeMirror.class::cast)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
var wrappedStrings = (List<? extends AnnotationValue>)
|
||||
getAnnotationValue(mirror, "targets").getValue();
|
||||
|
||||
imaginaries = wrappedStrings.stream()
|
||||
.map(AnnotationValue::getValue)
|
||||
.map(String.class::cast)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
return Stream.of(clzsses, imaginaries)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static String toSourceString(String bytecodeName) {
|
||||
return bytecodeName.replaceAll("\\/", ".");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
package org.fury_phoenix.mixinAp.annotation;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedOptions;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
import org.fury_phoenix.mixinAp.config.MixinConfig;
|
||||
|
||||
@SupportedAnnotationTypes({"org.spongepowered.asm.mixin.Mixin", "org.embeddedt.modernfix.annotation.ClientOnlyMixin"})
|
||||
@SupportedOptions({"rootProject.name", "project.name", "org.fury_phoenix.mixinAp.validator.debug"})
|
||||
@AutoService(Processor.class)
|
||||
public class MixinProcessor extends AbstractProcessor {
|
||||
|
||||
// Remember to call toString when using aliases
|
||||
private static final Map<String, String> aliases = Map.of(
|
||||
"Mixin", "mixins",
|
||||
"ClientOnlyMixin", "client"
|
||||
);
|
||||
|
||||
private final Map<String, List<String>> mixinConfigList = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
try {
|
||||
if(roundEnv.processingOver()){
|
||||
filterMixinSets();
|
||||
// create record for serialization, compute package name
|
||||
String packageName = Optional.ofNullable(mixinConfigList.get("mixins"))
|
||||
.orElse(mixinConfigList.get("client"))
|
||||
.get(0).split("(?<=mixin)")[0];
|
||||
finalizeMixinConfig();
|
||||
new MixinConfig(packageName,
|
||||
mixinConfigList.get("mixins"),
|
||||
mixinConfigList.get("client")
|
||||
).generateMixinConfig(processingEnv);
|
||||
} else {
|
||||
processMixins(annotations, roundEnv);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Fatal error:" +
|
||||
Throwables.getStackTraceAsString(e));
|
||||
throw new RuntimeException(e);
|
||||
// Halt the AP to prevent nonsense errors
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processMixins(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
for (TypeElement annotation : annotations) {
|
||||
Set<? extends Element> annotatedMixins = roundEnv.getElementsAnnotatedWith(annotation);
|
||||
|
||||
Stream<TypeElement> mixinStream =
|
||||
annotatedMixins.stream()
|
||||
.map(TypeElement.class::cast);
|
||||
|
||||
validateCommonMixins(annotation, mixinStream);
|
||||
|
||||
List<String> mixins =
|
||||
annotatedMixins.stream()
|
||||
.map(TypeElement.class::cast)
|
||||
.map(e -> processingEnv.getElementUtils().getBinaryName(e).toString())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
mixinConfigList.putIfAbsent(aliases.get(annotation.getSimpleName().toString()), mixins);
|
||||
}
|
||||
}
|
||||
|
||||
private void filterMixinSets() {
|
||||
List<String> commonSet = mixinConfigList.get("mixins");
|
||||
if(commonSet == null) return;
|
||||
commonSet.removeAll(mixinConfigList.get("client"));
|
||||
}
|
||||
|
||||
private void validateCommonMixins(TypeElement annotation, Stream<TypeElement> mixins) {
|
||||
if(!annotation.getSimpleName().toString().equals("Mixin"))
|
||||
return;
|
||||
ClientMixinValidator validator = new ClientMixinValidator(processingEnv);
|
||||
// The implementation may throw a CME
|
||||
mixins.sequential()
|
||||
.filter(validator::validateMixin)
|
||||
.map(validator::getClientMixinEntry)
|
||||
.forEach(this::logClientClassTarget);
|
||||
}
|
||||
|
||||
private void logClientClassTarget(Map.Entry<? extends CharSequence, ? extends CharSequence> mixin) {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
|
||||
"Mixin " + mixin.getKey() + " targets client-side classes: " + mixin.getValue());
|
||||
}
|
||||
|
||||
private void finalizeMixinConfig() {
|
||||
// relativize class names
|
||||
for(var list : mixinConfigList.values()) {
|
||||
list.replaceAll(className -> className.split("(?<=mixin.)")[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package org.fury_phoenix.mixinAp.config;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
public record MixinConfig(
|
||||
boolean required,
|
||||
String minVersion,
|
||||
@SerializedName("package")
|
||||
String packageName,
|
||||
String plugin,
|
||||
String compatibilityLevel,
|
||||
@SerializedName("mixins")
|
||||
List<String> commonMixins,
|
||||
@SerializedName("client")
|
||||
List<String> clientMixins,
|
||||
InjectorOptions injectors, OverwriteOptions overwrites
|
||||
) {
|
||||
public MixinConfig(String packageName, List<String> commonMixins, List<String> clientMixins) {
|
||||
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_17",
|
||||
commonMixins, clientMixins, InjectorOptions.DEFAULT, OverwriteOptions.DEFAULT);
|
||||
}
|
||||
public record InjectorOptions(int defaultRequire) {
|
||||
public static final InjectorOptions DEFAULT = new InjectorOptions(1);
|
||||
}
|
||||
public record OverwriteOptions(boolean conformVisibility) {
|
||||
public static final OverwriteOptions DEFAULT = new OverwriteOptions(true);
|
||||
}
|
||||
|
||||
public void generateMixinConfig(ProcessingEnvironment env) throws IOException {
|
||||
try (
|
||||
Writer mixinConfigWriter = env.getFiler()
|
||||
.createResource(StandardLocation.SOURCE_OUTPUT, "",
|
||||
MixinConfig.computeMixinConfigPath(
|
||||
Optional.of(env.getOptions().get("rootProject.name")),
|
||||
Optional.ofNullable(env.getOptions().get("project.name"))
|
||||
)
|
||||
).openWriter()
|
||||
) {
|
||||
String mixinConfig = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
.toJson(this);
|
||||
|
||||
mixinConfigWriter.write(mixinConfig);
|
||||
mixinConfigWriter.write("\n");
|
||||
} catch (IOException e) { throw e; }
|
||||
}
|
||||
|
||||
private static String computeMixinConfigPath(Optional<String> rootProjectName, Optional<String> projectName) {
|
||||
return "resources/" +
|
||||
rootProjectName.get() +
|
||||
(projectName.isPresent() ? "-" : "") +
|
||||
projectName.orElse("") +
|
||||
".mixins.json";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package org.fury_phoenix.mixinAp.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Type-safe heterogenous map of accessors
|
||||
* @author Fury_Phoenix
|
||||
* @reason Type-safety since K, V of Map are non-identical
|
||||
* @param <SuperType> The supertype of desired types.
|
||||
* This is useful in cases such as <A extends Annotation>.
|
||||
*/
|
||||
public class TypedAccessorMap<SuperType> {
|
||||
private final Map<Class<? extends SuperType>, Function<Object, ?>> typedAccessors = new HashMap<>();
|
||||
|
||||
public <T extends SuperType> void put(Class<T> key, Function<? super T, ?> func) {
|
||||
Objects.requireNonNull(func);
|
||||
typedAccessors.put(Objects.requireNonNull(key), o -> func.apply(key.cast(o)));
|
||||
}
|
||||
|
||||
public <T extends SuperType> void put(Entry<Class<T>, Function<? super T, ?>> entry) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
public <T extends SuperType> Function<Object, ?> get(Class<T> key) {
|
||||
return typedAccessors.get(key);
|
||||
}
|
||||
|
||||
public Set<Entry<Class<? extends SuperType>, Function<Object, ?>>> entrySet() {
|
||||
return typedAccessors.entrySet();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.fury_phoenix.mixinAp.annotation.MixinProcessor,aggregating
|
||||
10
annotations/build.gradle.kts
Normal file
10
annotations/build.gradle.kts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
plugins {
|
||||
id("java")
|
||||
}
|
||||
|
||||
version = "1.1.0"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
|
||||
public enum FeatureLevel {
|
||||
GA, BETA;
|
||||
|
||||
public boolean isAtLeast(FeatureLevel required) {
|
||||
return this.ordinal() >= required.ordinal();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface IgnoreMixin {
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* The config system will ignore mixins with this annotation when generating config options unless running
|
||||
* in a dev environment.
|
||||
*/
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface IgnoreOutsideDev {
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
||||
public @interface RequiresFeatureLevel {
|
||||
FeatureLevel value() default FeatureLevel.GA;
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target(ElementType.TYPE)
|
||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
||||
public @interface RequiresMod {
|
||||
String value() default "";
|
||||
}
|
||||
240
build.gradle
240
build.gradle
|
|
@ -1,240 +0,0 @@
|
|||
plugins {
|
||||
id "architectury-plugin" version "3.4-SNAPSHOT"
|
||||
id "dev.architectury.loom" version "1.2-SNAPSHOT" apply false
|
||||
id "maven-publish"
|
||||
id 'com.matthewprenger.cursegradle' version '1.4.0' apply false
|
||||
id 'com.palantir.git-version' version '1.0.0'
|
||||
id 'org.ajoberstar.grgit' version '5.2.0'
|
||||
id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0'
|
||||
id "com.modrinth.minotaur" version "2.+" apply false
|
||||
id("com.diffplug.spotless") version "6.18.0" apply false
|
||||
}
|
||||
|
||||
architectury {
|
||||
minecraft = rootProject.minecraft_version
|
||||
}
|
||||
|
||||
ext.archives_base_name = 'modernfix'
|
||||
|
||||
allprojects {
|
||||
apply plugin: "java"
|
||||
apply plugin: "architectury-plugin"
|
||||
apply plugin: "maven-publish"
|
||||
apply plugin: "com.diffplug.spotless"
|
||||
|
||||
spotless {
|
||||
java {
|
||||
removeUnusedImports()
|
||||
}
|
||||
}
|
||||
|
||||
group = 'org.embeddedt'
|
||||
// extract base version from tag, generate other metadata ourselves
|
||||
def details = versionDetails()
|
||||
def plusIndex = details.lastTag.indexOf("+")
|
||||
if(plusIndex == -1) {
|
||||
plusIndex = details.lastTag.length()
|
||||
}
|
||||
def baseVersion = details.lastTag.substring(0, plusIndex)
|
||||
def dirtyMarker = grgit.status().clean ? "" : ".dirty"
|
||||
def commitHashMarker = details.commitDistance > 0 ? ("." + details.gitHash) : ""
|
||||
def preMarker = (details.commitDistance > 0 || !details.isCleanTag) ? ("-beta." + details.commitDistance) : ""
|
||||
if(preMarker.length() > 0) {
|
||||
// bump to next patch release
|
||||
def versionParts = baseVersion.tokenize(".")
|
||||
baseVersion = "${versionParts[0]}.${versionParts[1]}.${versionParts[2].toInteger() + 1}"
|
||||
}
|
||||
def versionString = "${baseVersion}${preMarker}+mc${minecraft_version}${commitHashMarker}${dirtyMarker}"
|
||||
version = versionString
|
||||
archivesBaseName = rootProject.archives_base_name + '-' + project.name
|
||||
|
||||
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
repositories {
|
||||
maven { url 'https://modmaven.dev/' }
|
||||
maven {
|
||||
url "https://cursemaven.com"
|
||||
content {
|
||||
includeGroup "curse.maven"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
name = 'ParchmentMC'
|
||||
url = 'https://maven.parchmentmc.org'
|
||||
}
|
||||
maven {
|
||||
// Shedaniel's maven (Architectury API)
|
||||
url = "https://maven.architectury.dev"
|
||||
content {
|
||||
includeGroup "me.shedaniel"
|
||||
}
|
||||
}
|
||||
|
||||
maven {
|
||||
// saps.dev Maven (KubeJS and Rhino)
|
||||
url = "https://maven.saps.dev/minecraft"
|
||||
content {
|
||||
includeGroup "dev.latvian.mods"
|
||||
}
|
||||
}
|
||||
maven { // CTM
|
||||
url "https://maven.tterrag.com/"
|
||||
}
|
||||
maven { url 'https://maven.blamejared.com' }
|
||||
repositories {
|
||||
maven {
|
||||
name = "Fuzs Mod Resources"
|
||||
url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url 'https://maven.terraformersmc.com/releases'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure(subprojects.findAll {it.name == "common" || it.name == "forge" || it.name == "fabric"}) {
|
||||
apply plugin: "dev.architectury.loom"
|
||||
|
||||
loom {
|
||||
silentMojangMappingsLicense()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
||||
mappings loom.layered() {
|
||||
officialMojangMappings()
|
||||
parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
def mixinFileList = []
|
||||
def mixinDirectory = file("src/main/java/org/embeddedt/modernfix/" + project.name + "/mixin")
|
||||
fileTree(mixinDirectory).visit { FileVisitDetails details ->
|
||||
if(details.file.isFile()) {
|
||||
def fileName = mixinDirectory.relativePath(details.file).toString().replaceFirst(/\.java$/, "").replace('/', '.')
|
||||
mixinFileList << fileName
|
||||
}
|
||||
}
|
||||
|
||||
def mixinClassesStringB = new StringBuilder()
|
||||
for(int i = 0; i < mixinFileList.size(); i++) {
|
||||
mixinClassesStringB.append(" \"")
|
||||
mixinClassesStringB.append(mixinFileList.get(i))
|
||||
mixinClassesStringB.append('"')
|
||||
if(i < (mixinFileList.size() - 1))
|
||||
mixinClassesStringB.append(',')
|
||||
mixinClassesStringB.append('\n')
|
||||
}
|
||||
|
||||
def replacements = [
|
||||
mixin_classes: mixinClassesStringB.toString()
|
||||
]
|
||||
|
||||
inputs.properties replacements
|
||||
def filePattern = "modernfix-" + project.name + ".mixins.json"
|
||||
filesMatching(filePattern) {
|
||||
expand replacements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||
// this fixes some edge cases with special characters not displaying correctly
|
||||
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||
// If Javadoc is generated, this must be specified in that task too.
|
||||
options.encoding = "UTF-8"
|
||||
|
||||
// The Minecraft launcher currently installs Java 8 for users, so your mod probably wants to target Java 8 too
|
||||
// JDK 9 introduced a new way of specifying this that will make sure no newer classes or methods are used.
|
||||
// We'll use that if it's available, but otherwise we'll use the older option.
|
||||
def targetVersion = 8
|
||||
/*
|
||||
if (JavaVersion.current().isJava9Compatible()) {
|
||||
options.release = targetVersion
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
task generateChangelog(type: se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) {
|
||||
def details = versionDetails();
|
||||
if(details.commitDistance > 0) {
|
||||
fromRef = details.lastTag;
|
||||
} else {
|
||||
def secondLastTagCmd = "git describe --abbrev=0 " + details.lastTag + "^"
|
||||
def secondLastTag = secondLastTagCmd.execute().text.trim()
|
||||
fromRef = secondLastTag;
|
||||
}
|
||||
|
||||
file = new File("CHANGELOG.md");
|
||||
def otherTemplateContent = new File('gradle/changelog.mustache').getText('UTF-8');
|
||||
templateContent = "## Changes since " + fromRef + "\n" + otherTemplateContent;
|
||||
toCommit = "HEAD";
|
||||
}
|
||||
|
||||
tasks.register('checkCleanTag') {
|
||||
doLast {
|
||||
def details = versionDetails()
|
||||
if (!details.isCleanTag || versionDetails().commitDistance != 0) {
|
||||
throw new GradleException('Not a clean tree.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configure(subprojects.findAll {it.name == "forge" || it.name == "fabric"}) {
|
||||
apply plugin: 'com.matthewprenger.cursegradle'
|
||||
apply plugin: 'com.modrinth.minotaur'
|
||||
|
||||
def copyJarNameConsistent = tasks.register('copyJarNameConsistent', Copy) {
|
||||
from remapJar // shortcut for createJar.outputs.files
|
||||
into project.file("build/libs")
|
||||
rename { name -> "modernfix-" + project.name + "-latest.jar" }
|
||||
}
|
||||
|
||||
def copyJarToBin = tasks.register('copyJarToBin', Copy) {
|
||||
from remapJar // shortcut for createJar.outputs.files
|
||||
into rootProject.file("bin")
|
||||
mustRunAfter "copyJarNameConsistent"
|
||||
}
|
||||
|
||||
tasks.build.dependsOn(copyJarToBin, copyJarNameConsistent)
|
||||
|
||||
def isBeta = project.version.toString().contains("beta")
|
||||
|
||||
curseforge {
|
||||
if (System.getenv("CURSEFORGE_TOKEN") != null) {
|
||||
apiKey = System.getenv("CURSEFORGE_TOKEN")
|
||||
project {
|
||||
id = "790626"
|
||||
changelog = file('../CHANGELOG.md')
|
||||
changelogType = "markdown"
|
||||
releaseType = isBeta ? "beta" : "release"
|
||||
addGameVersion project.name.capitalize()
|
||||
gameVersionStrings.addAll(supported_minecraft_versions.tokenize(","))
|
||||
mainArtifact remapJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
token = System.getenv("MODRINTH_TOKEN")
|
||||
projectId = "modernfix" // This can be the project ID or the slug. Either will work!
|
||||
versionType = isBeta ? "beta" : "release" // This is the default -- can also be `beta` or `alpha`
|
||||
uploadFile = remapJar
|
||||
gameVersions = supported_minecraft_versions.tokenize(",")
|
||||
loaders = [project.name]
|
||||
changelog.set(provider { file("CHANGELOG.md").getText('UTF-8') })
|
||||
}
|
||||
|
||||
tasks.curseforge.dependsOn(rootProject.generateChangelog)
|
||||
tasks.modrinth.dependsOn(rootProject.generateChangelog)
|
||||
|
||||
tasks.register('publishToModSites') {
|
||||
publishToModSites.dependsOn(tasks.modrinth)
|
||||
publishToModSites.dependsOn(tasks.curseforge)
|
||||
}
|
||||
}
|
||||
|
||||
println "ModernFix: " + version
|
||||
210
build.gradle.kts
Normal file
210
build.gradle.kts
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
plugins {
|
||||
id("net.neoforged.moddev.legacyforge") version("2.0.134")
|
||||
id("me.modmuss50.mod-publish-plugin") version("1.1.0")
|
||||
}
|
||||
|
||||
val minecraft_version = rootProject.properties["minecraft_version"].toString()
|
||||
|
||||
group = "org.embeddedt"
|
||||
|
||||
val gitVersion = providers.of(GitVersionSource::class) {
|
||||
parameters {
|
||||
minecraftVersion.set(minecraft_version)
|
||||
projectDir.set(rootProject.layout.projectDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
version = gitVersion.get()
|
||||
|
||||
base.archivesName = "modernfix-forge"
|
||||
|
||||
legacyForge {
|
||||
enable {
|
||||
forgeVersion = rootProject.properties["forge_version"].toString()
|
||||
isDisableRecompilation = System.getenv("CI") == "true"
|
||||
}
|
||||
|
||||
rootProject.properties["parchment_version"]?.let { parchmentVer ->
|
||||
parchment {
|
||||
minecraftVersion = minecraft_version
|
||||
mappingsVersion = parchmentVer.toString()
|
||||
}
|
||||
}
|
||||
|
||||
runs {
|
||||
create("client") {
|
||||
client()
|
||||
}
|
||||
create("server") {
|
||||
server()
|
||||
}
|
||||
create("auditClient") {
|
||||
client()
|
||||
jvmArguments.addAll("-Dmodernfix.auditAndExit=true", "-Djava.awt.headless=true")
|
||||
}
|
||||
}
|
||||
|
||||
mods {
|
||||
create("modernfix") {
|
||||
sourceSet(sourceSets.main.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixin {
|
||||
add(sourceSets.main.get(), "modernfix.refmap.json")
|
||||
config("modernfix-modernfix.mixins.json")
|
||||
}
|
||||
|
||||
tasks.named<Jar>("jar") {
|
||||
manifest.attributes(mapOf(
|
||||
"MixinConfigs" to "modernfix-modernfix.mixins.json",
|
||||
"Specification-Version" to "1",
|
||||
"Implementation-Title" to project.name,
|
||||
"Implementation-Version" to version
|
||||
))
|
||||
}
|
||||
|
||||
java {
|
||||
val curSourceCompatLevel = JavaVersion.VERSION_17
|
||||
sourceCompatibility = curSourceCompatLevel
|
||||
targetCompatibility = curSourceCompatLevel
|
||||
}
|
||||
|
||||
repositories {
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
// location of the maven that hosts JEI files
|
||||
name = "Progwml6 maven"
|
||||
url = uri("https://dvs1.progwml6.com/files/maven/")
|
||||
}
|
||||
}
|
||||
forRepository {
|
||||
maven {
|
||||
name = "ModMaven"
|
||||
url = uri("https://modmaven.dev")
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("mezz.jei")
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://cursemaven.com")
|
||||
}
|
||||
filter {
|
||||
includeGroup("curse.maven")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val embed by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
isTransitive = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":annotations"))
|
||||
embed(project(":annotations"))
|
||||
"additionalRuntimeClasspath"(project(":annotations"))
|
||||
annotationProcessor(project(path = ":annotation-processor", configuration = "shadow"))
|
||||
|
||||
val mixinextrasVersion = rootProject.properties["mixinextras_version"].toString()
|
||||
implementation("io.github.llamalad7:mixinextras-common:${mixinextrasVersion}")
|
||||
annotationProcessor("net.fabricmc:sponge-mixin:0.12.5+mixin.0.8.5")
|
||||
annotationProcessor("io.github.llamalad7:mixinextras-common:${mixinextrasVersion}")
|
||||
implementation("io.github.llamalad7:mixinextras-forge:${mixinextrasVersion}")
|
||||
"jarJar"("io.github.llamalad7:mixinextras-forge:${mixinextrasVersion}")
|
||||
|
||||
val jei_version = rootProject.properties["jei_version"].toString()
|
||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
||||
modCompileOnly("curse.maven:spark-361579:${rootProject.properties["spark_version"].toString()}")
|
||||
modCompileOnly("curse.maven:ctm-267602:${rootProject.properties["ctm_version"].toString()}")
|
||||
modCompileOnly("curse.maven:ldlib-626676:${rootProject.properties["ldlib_version"].toString()}")
|
||||
modCompileOnly("curse.maven:supermartijncore-454372:4455391")
|
||||
modCompileOnly("curse.maven:patchouli-306770:6164575")
|
||||
modCompileOnly("curse.maven:cofhcore-69162:5374122")
|
||||
modCompileOnly("curse.maven:resourcefullib-570073:5659871")
|
||||
modCompileOnly("curse.maven:kubejs-238086:5853326")
|
||||
modCompileOnly("curse.maven:terrablender-563928:6290448")
|
||||
}
|
||||
|
||||
tasks.named<Jar>("jar") {
|
||||
from(embed.map { if (it.isDirectory) it else zipTree(it) })
|
||||
}
|
||||
|
||||
// For the AP
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
if (!name.lowercase().contains("test")) {
|
||||
options.compilerArgs.addAll(
|
||||
listOf(
|
||||
"-ArootProject.name=${rootProject.name}",
|
||||
"-Aproject.name=${project.name}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir(
|
||||
layout.buildDirectory.dir("generated/sources/annotationProcessor/java/main/resources")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<ProcessResources>("processResources") {
|
||||
dependsOn(tasks.named("compileJava"))
|
||||
|
||||
inputs.property("version", project.version)
|
||||
|
||||
filesMatching("META-INF/mods.toml") {
|
||||
expand("version" to project.version)
|
||||
}
|
||||
}
|
||||
|
||||
val finalJarTask = "reobfJar"
|
||||
|
||||
tasks.register<Copy>("copyJarNameConsistent") {
|
||||
from(tasks.named<Jar>(finalJarTask).get().outputs.files)
|
||||
into(project.file("build/libs"))
|
||||
rename { _ -> "modernfix-" + project.name + "-latest.jar" }
|
||||
}
|
||||
|
||||
tasks.register<Copy>("copyJarToBin") {
|
||||
from(tasks.named<Jar>(finalJarTask).get().outputs.files)
|
||||
into(rootProject.file("bin"))
|
||||
mustRunAfter(tasks.named("copyJarNameConsistent"))
|
||||
}
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn("copyJarToBin", "copyJarNameConsistent")
|
||||
}
|
||||
|
||||
publishMods {
|
||||
file.set(tasks.named<Jar>(finalJarTask).flatMap { it.archiveFile })
|
||||
displayName.set(tasks.named<Jar>(finalJarTask).flatMap { it.archiveFileName })
|
||||
changelog = "Please check the [GitHub wiki](https://github.com/embeddedt/ModernFix/wiki/Changelog) for major changes."
|
||||
type = STABLE
|
||||
|
||||
modLoaders.add("forge")
|
||||
|
||||
curseforge {
|
||||
projectId = "790626"
|
||||
projectSlug = "modernfix"
|
||||
accessToken = providers.environmentVariable("CURSEFORGE_TOKEN")
|
||||
minecraftVersions.add(minecraft_version)
|
||||
}
|
||||
modrinth {
|
||||
projectId = "nmDcB62a"
|
||||
accessToken = providers.environmentVariable("MODRINTH_TOKEN")
|
||||
minecraftVersions.add(minecraft_version)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("publishMods") {
|
||||
dependsOn(finalJarTask)
|
||||
}
|
||||
7
buildSrc/build.gradle.kts
Normal file
7
buildSrc/build.gradle.kts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
61
buildSrc/src/main/kotlin/GitVersionSource.kt
Normal file
61
buildSrc/src/main/kotlin/GitVersionSource.kt
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ValueSource
|
||||
import org.gradle.api.provider.ValueSourceParameters
|
||||
import org.gradle.process.ExecOperations
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class GitVersionSource : ValueSource<String, GitVersionSource.Parameters> {
|
||||
|
||||
interface Parameters : ValueSourceParameters {
|
||||
val minecraftVersion: Property<String>
|
||||
val projectDir: DirectoryProperty
|
||||
}
|
||||
|
||||
@get:Inject
|
||||
abstract val execOperations: ExecOperations
|
||||
|
||||
override fun obtain(): String {
|
||||
val minecraftVersion = parameters.minecraftVersion.get()
|
||||
val workDir = parameters.projectDir.get().asFile
|
||||
|
||||
val releaseLine = workDir.resolve("release_line.txt").readText().trim()
|
||||
|
||||
val patch = try {
|
||||
// Find the most recent first-parent commit that touched release_line.txt
|
||||
val lineStartCommit = git(workDir,
|
||||
"log", "--first-parent",
|
||||
"-n", "1",
|
||||
"--format=%H",
|
||||
"--",
|
||||
"release_line.txt"
|
||||
).trim()
|
||||
|
||||
if (lineStartCommit.isEmpty()) {
|
||||
// count all first-parent commits as a safe fallback
|
||||
git(workDir, "rev-list", "--count", "--first-parent", "HEAD")
|
||||
.trim().toIntOrNull() ?: 0
|
||||
} else {
|
||||
git(workDir, "rev-list", "--count", "--first-parent", "$lineStartCommit..HEAD")
|
||||
.trim().toIntOrNull() ?: 0
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
// Git is unavailable or this is not a git repository
|
||||
999
|
||||
}
|
||||
|
||||
return "$releaseLine.$patch+mc$minecraftVersion"
|
||||
}
|
||||
|
||||
private fun git(workDir: File, vararg args: String): String {
|
||||
val output = ByteArrayOutputStream()
|
||||
execOperations.exec {
|
||||
commandLine("git", *args)
|
||||
standardOutput = output
|
||||
workingDir(workDir)
|
||||
}
|
||||
return output.toString(Charsets.UTF_8)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
architectury {
|
||||
common(rootProject.enabled_platforms.split(","))
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath = file("src/main/resources/modernfix.accesswidener")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// We depend on fabric loader here to use the fabric @Environment annotations and get the mixin dependencies
|
||||
// Do NOT use other classes from fabric loader
|
||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||
|
||||
modApi("dev.latvian.mods:kubejs:${kubejs_version}") {
|
||||
transitive = false
|
||||
}
|
||||
modApi("dev.latvian.mods:rhino:${rhino_version}") {
|
||||
transitive = false
|
||||
}
|
||||
modApi("me.shedaniel:RoughlyEnoughItems-api:${rei_version}") {
|
||||
transitive = false
|
||||
}
|
||||
modCompileOnly("me.shedaniel:RoughlyEnoughItems-fabric:${rei_version}") {
|
||||
transitive = false
|
||||
}
|
||||
modCompileOnly("appeng:appliedenergistics2-fabric:${appeng_version}") {
|
||||
transitive = false
|
||||
}
|
||||
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||
// compile against the JEI API but do not include it at runtime
|
||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-common:${jei_version}")
|
||||
// Remove the next line if you don't want to depend on the API
|
||||
// modApi "me.shedaniel:architectury:${rootProject.architectury_version}"
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenCommon(MavenPublication) {
|
||||
artifactId = rootProject.archives_base_name
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
|
||||
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
|
||||
repositories {
|
||||
// Add repositories to publish to here.
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package org.embeddedt.modernfix;
|
||||
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FileWalker extends CacheLoader<Pair<Path, Integer>, List<Path>> {
|
||||
public static final FileWalker INSTANCE = new FileWalker();
|
||||
|
||||
@Override
|
||||
public List<Path> load(Pair<Path, Integer> key) throws Exception {
|
||||
try(Stream<Path> stream = Files.walk(key.getLeft(), key.getRight())) {
|
||||
return stream.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
package org.embeddedt.modernfix;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.ConnectScreen;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.MemoryReserve;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree;
|
||||
import org.embeddedt.modernfix.searchtree.REIBackedSearchTree;
|
||||
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
|
||||
import org.embeddedt.modernfix.util.ClassInfoManager;
|
||||
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class ModernFixClient {
|
||||
public static long worldLoadStartTime;
|
||||
private static int numRenderTicks;
|
||||
|
||||
public static float gameStartTimeSeconds = -1;
|
||||
|
||||
private static boolean recipesUpdated, tagsUpdated = false;
|
||||
|
||||
public String brandingString = null;
|
||||
|
||||
/**
|
||||
* The list of loaded client integrations.
|
||||
*/
|
||||
public static List<ModernFixClientIntegration> CLIENT_INTEGRATIONS = new CopyOnWriteArrayList<>();
|
||||
|
||||
public ModernFixClient() {
|
||||
// clear reserve as it's not needed
|
||||
MemoryReserve.release();
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
|
||||
brandingString = ModernFix.NAME + " " + ModernFixPlatformHooks.getVersionString();
|
||||
}
|
||||
SearchTreeProviderRegistry.register(JEIBackedSearchTree.PROVIDER);
|
||||
SearchTreeProviderRegistry.register(REIBackedSearchTree.PROVIDER);
|
||||
for(String className : ModernFixPlatformHooks.getCustomModOptions().get(IntegrationConstants.CLIENT_INTEGRATION_CLASS)) {
|
||||
try {
|
||||
CLIENT_INTEGRATIONS.add((ModernFixClientIntegration)Class.forName(className).getDeclaredConstructor().newInstance());
|
||||
} catch(ReflectiveOperationException | ClassCastException e) {
|
||||
ModernFix.LOGGER.error("Could not instantiate integration {}", className, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetWorldLoadStateMachine() {
|
||||
numRenderTicks = 0;
|
||||
worldLoadStartTime = -1;
|
||||
recipesUpdated = false;
|
||||
tagsUpdated = false;
|
||||
}
|
||||
|
||||
public void onScreenOpening(Screen openingScreen) {
|
||||
if(openingScreen instanceof ConnectScreen) {
|
||||
worldLoadStartTime = System.nanoTime();
|
||||
} else if (openingScreen instanceof TitleScreen && gameStartTimeSeconds < 0) {
|
||||
gameStartTimeSeconds = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.measure_time.GameLoad"))
|
||||
ModernFix.LOGGER.warn("Game took " + gameStartTimeSeconds + " seconds to start");
|
||||
ModernFixPlatformHooks.onLaunchComplete();
|
||||
ClassInfoManager.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void onRecipesUpdated() {
|
||||
recipesUpdated = true;
|
||||
}
|
||||
|
||||
public void onTagsUpdated() {
|
||||
tagsUpdated = true;
|
||||
}
|
||||
|
||||
public void onRenderTickEnd() {
|
||||
if(recipesUpdated
|
||||
&& tagsUpdated
|
||||
&& worldLoadStartTime != -1
|
||||
&& Minecraft.getInstance().player != null
|
||||
&& numRenderTicks++ >= 10) {
|
||||
float timeSpentLoading = ((float)(System.nanoTime() - worldLoadStartTime) / 1000000000f);
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.measure_time.WorldLoad")) {
|
||||
ModernFix.LOGGER.warn("Time from main menu to in-game was " + timeSpentLoading + " seconds");
|
||||
ModernFix.LOGGER.warn("Total time to load game and open world was " + (timeSpentLoading + gameStartTimeSeconds) + " seconds");
|
||||
}
|
||||
resetWorldLoadStateMachine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the IDs match and remap them if not.
|
||||
* @return true if ID remap was needed
|
||||
*/
|
||||
private static boolean compareAndSwitchIds(Class<? extends Entity> eClass, String fieldName, EntityDataAccessor<?> accessor, int newId) {
|
||||
if(accessor.id != newId) {
|
||||
ModernFix.LOGGER.warn("Corrected ID mismatch on {} field {}. Client had {} but server wants {}.",
|
||||
eClass,
|
||||
fieldName,
|
||||
accessor.id,
|
||||
newId);
|
||||
accessor.id = newId;
|
||||
return true;
|
||||
} else {
|
||||
ModernFix.LOGGER.debug("{} {} ID fine: {}", eClass, fieldName, newId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Horrendous hack to allow tracking every synced entity data manager.
|
||||
*
|
||||
* This is to ensure we can perform ID fixup on already constructed managers.
|
||||
*/
|
||||
public static final Set<SynchedEntityData> allEntityDatas = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
|
||||
private static final Field entriesArrayField;
|
||||
static {
|
||||
Field field;
|
||||
try {
|
||||
field = SynchedEntityData.class.getDeclaredField("entriesArray");
|
||||
field.setAccessible(true);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
field = null;
|
||||
}
|
||||
entriesArrayField = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extremely hacky method to detect and correct mismatched entity data parameter IDs on the client and server.
|
||||
*
|
||||
* The technique is far from ideal, but it should detect reliably and also not break already constructed entities.
|
||||
*/
|
||||
public static void handleEntityIDSync(EntityIDSyncPacket packet) {
|
||||
Map<Class<? extends Entity>, List<Pair<String, Integer>>> info = packet.getFieldInfo();
|
||||
boolean fixNeeded = false;
|
||||
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : info.entrySet()) {
|
||||
Class<? extends Entity> eClass = entry.getKey();
|
||||
for(Pair<String, Integer> field : entry.getValue()) {
|
||||
String fieldName = field.getFirst();
|
||||
int newId = field.getSecond();
|
||||
try {
|
||||
Field f = eClass.getDeclaredField(fieldName);
|
||||
f.setAccessible(true);
|
||||
EntityDataAccessor<?> accessor = (EntityDataAccessor<?>)f.get(null);
|
||||
if(compareAndSwitchIds(eClass, fieldName, accessor, newId))
|
||||
fixNeeded = true;
|
||||
} catch(NoSuchFieldException e) {
|
||||
ModernFix.LOGGER.warn("Couldn't find field on {}: {}", eClass, fieldName);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Unexpected exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now the ID mappings on synced entity data instances are probably all wrong. Fix that. */
|
||||
List<SynchedEntityData> dataEntries;
|
||||
synchronized (allEntityDatas) {
|
||||
if(fixNeeded) {
|
||||
dataEntries = new ArrayList<>(allEntityDatas);
|
||||
for(SynchedEntityData manager : dataEntries) {
|
||||
Int2ObjectOpenHashMap<SynchedEntityData.DataItem<?>> fixedMap = new Int2ObjectOpenHashMap<>();
|
||||
List<SynchedEntityData.DataItem<?>> items = new ArrayList<>(manager.itemsById.values());
|
||||
for(SynchedEntityData.DataItem<?> item : items) {
|
||||
fixedMap.put(item.getAccessor().id, item);
|
||||
}
|
||||
manager.lock.writeLock().lock();
|
||||
try {
|
||||
manager.itemsById.replaceAll((id, parameter) -> fixedMap.get((int)id));
|
||||
if(entriesArrayField != null) {
|
||||
try {
|
||||
SynchedEntityData.DataItem<?>[] dataArray = new SynchedEntityData.DataItem[items.size()];
|
||||
for(int i = 0; i < dataArray.length; i++) {
|
||||
dataArray[i] = fixedMap.get(i);
|
||||
}
|
||||
entriesArrayField.set(manager, dataArray);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
ModernFix.LOGGER.error(e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
manager.lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
allEntityDatas.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void onServerStarted(MinecraftServer server) {
|
||||
if(!ModernFixMixinPlugin.instance.isOptionEnabled("feature.integrated_server_watchdog.IntegratedWatchdog"))
|
||||
return;
|
||||
IntegratedWatchdog watchdog = new IntegratedWatchdog(server);
|
||||
watchdog.start();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.EmptyLevelChunk;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
@Mixin(value = ServerChunkCache.class, priority = 1100)
|
||||
public abstract class ServerChunkCacheMixin {
|
||||
@Shadow @Final private Thread mainThread;
|
||||
@Shadow @Final public ServerLevel level;
|
||||
|
||||
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread(int k, int l, ChunkStatus arg, boolean bl);
|
||||
|
||||
@Shadow @Final private ServerChunkCache.MainThreadExecutor mainThreadProcessor;
|
||||
private final boolean debugDeadServerAccess = Boolean.getBoolean("modernfix.debugBadChunkloading");
|
||||
|
||||
@Inject(method = "getChunk", at = @At("HEAD"), cancellable = true)
|
||||
private void bailIfServerDead(int chunkX, int chunkZ, ChunkStatus requiredStatus, boolean load, CallbackInfoReturnable<ChunkAccess> cir) {
|
||||
if(!this.level.getServer().isRunning() && !this.mainThread.isAlive()) {
|
||||
ModernFix.LOGGER.fatal("A mod is accessing chunks from a stopped server (this will also cause memory leaks)");
|
||||
if(debugDeadServerAccess) {
|
||||
new Exception().printStackTrace();
|
||||
}
|
||||
Holder<Biome> plains = this.level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getHolderOrThrow(Biomes.PLAINS);
|
||||
cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ), plains));
|
||||
} else if(Thread.currentThread() != this.mainThread) {
|
||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = CompletableFuture.supplyAsync(() -> this.getChunkFutureMainThread(chunkX, chunkZ, requiredStatus, false), this.mainThreadProcessor).join();
|
||||
if(!future.isDone()) {
|
||||
// Wait at least 500 milliseconds before printing anything
|
||||
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> resultingChunk = null;
|
||||
try {
|
||||
resultingChunk = future.get(500, TimeUnit.MILLISECONDS);
|
||||
} catch(InterruptedException | ExecutionException | TimeoutException ignored) {
|
||||
}
|
||||
if(resultingChunk != null && resultingChunk.left().isPresent()) {
|
||||
cir.setReturnValue(resultingChunk.left().get());
|
||||
return;
|
||||
}
|
||||
if(debugDeadServerAccess)
|
||||
ModernFix.LOGGER.warn("Async loading of a chunk was requested, this might not be desirable", new Exception());
|
||||
else
|
||||
ModernFix.LOGGER.warn("Suspicious async chunkload, pass -Dmodernfix.debugBadChunkloading=true for more details");
|
||||
try {
|
||||
resultingChunk = future.get(10, TimeUnit.SECONDS);
|
||||
if(resultingChunk.left().isPresent()) {
|
||||
cir.setReturnValue(resultingChunk.left().get());
|
||||
return;
|
||||
}
|
||||
} catch(InterruptedException | ExecutionException | TimeoutException e) {
|
||||
ModernFix.LOGGER.error("Async chunk load took way too long, this needs to be reported to the appropriate mod.", e);
|
||||
}
|
||||
//cir.setReturnValue(new EmptyLevelChunk(this.level, new ChunkPos(chunkX, chunkZ)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.concurrency;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
@ClientOnlyMixin
|
||||
public abstract class MinecraftMixin<R extends Runnable> extends BlockableEventLoop<R> {
|
||||
|
||||
protected MinecraftMixin(String p_i50403_1_) {
|
||||
super(p_i50403_1_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void managedBlock(BooleanSupplier pIsDone) {
|
||||
if(!this.isSameThread()) {
|
||||
ModernFix.LOGGER.warn("A mod is calling Minecraft.managedBlock from the wrong thread. This is most likely related to one of our parallelizations.");
|
||||
ModernFix.LOGGER.warn("ModernFix will work around this, however ideally the issue should be patched in the other mod.");
|
||||
ModernFix.LOGGER.warn("Stacktrace", new IllegalThreadStateException());
|
||||
while(!pIsDone.getAsBoolean()) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch(InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.managedBlock(pIsDone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import org.embeddedt.modernfix.duck.IPaperChunkHolder;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(ChunkHolder.class)
|
||||
public abstract class ChunkHolderMixin implements IPaperChunkHolder {
|
||||
|
||||
@Shadow public abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getFutureIfPresentUnchecked(ChunkStatus arg);
|
||||
|
||||
@Shadow @Final private static List<ChunkStatus> CHUNK_STATUSES;
|
||||
|
||||
public ChunkStatus mfix$getChunkHolderStatus() {
|
||||
for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) {
|
||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr);
|
||||
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null);
|
||||
if (either == null || !either.left().isPresent()) {
|
||||
continue;
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ChunkAccess mfix$getAvailableChunkNow() {
|
||||
// TODO can we just getStatusFuture(EMPTY)?
|
||||
for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) {
|
||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr);
|
||||
Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null);
|
||||
if (either == null || !either.left().isPresent()) {
|
||||
continue;
|
||||
}
|
||||
return either.left().get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ChunkStatus mfix$getNextStatus(ChunkStatus status) {
|
||||
if (status == ChunkStatus.FULL) {
|
||||
return status;
|
||||
}
|
||||
return CHUNK_STATUSES.get(status.getIndex() + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mfix$canAdvanceStatus() {
|
||||
ChunkStatus status = mfix$getChunkHolderStatus();
|
||||
ChunkAccess chunk = mfix$getAvailableChunkNow();
|
||||
return chunk != null && (status == null || chunk.getStatus().isOrAfter(mfix$getNextStatus(status)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.*;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.BlockableEventLoop;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
import org.embeddedt.modernfix.duck.IPaperChunkHolder;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
@Mixin(ChunkMap.class)
|
||||
public abstract class ChunkMapMixin {
|
||||
@Shadow @Final private BlockableEventLoop<Runnable> mainThreadExecutor;
|
||||
|
||||
@Shadow @Final private ChunkMap.DistanceManager distanceManager;
|
||||
|
||||
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> protoChunkToFullChunk(ChunkHolder arg);
|
||||
|
||||
@Shadow @Final private ServerLevel level;
|
||||
@Shadow @Final private StructureManager structureManager;
|
||||
@Shadow @Final private ThreadedLevelLightEngine lightEngine;
|
||||
@Shadow @Final private ChunkProgressListener progressListener;
|
||||
|
||||
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> scheduleChunkGeneration(ChunkHolder chunkHolder, ChunkStatus chunkStatus);
|
||||
|
||||
private Executor mainInvokingExecutor;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void setup(CallbackInfo ci) {
|
||||
MinecraftServer server = this.level.getServer();
|
||||
this.mainInvokingExecutor = (runnable) -> {
|
||||
if(server.isSameThread())
|
||||
runnable.run();
|
||||
else
|
||||
this.mainThreadExecutor.execute(runnable);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/* https://github.com/PaperMC/Paper/blob/ver/1.17.1/patches/server/0752-Fix-chunks-refusing-to-unload-at-low-TPS.patch */
|
||||
@ModifyArg(method = "prepareAccessibleChunk", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1)
|
||||
private Executor useMainThreadExecutor(Executor executor) {
|
||||
return this.mainThreadExecutor;
|
||||
}
|
||||
|
||||
/* https://github.com/PaperMC/Paper/blob/master/patches/removed/1.19.2-legacy-chunksystem/0482-Improve-Chunk-Status-Transition-Speed.patch */
|
||||
@ModifyArg(method = "prepareEntityTickingChunk", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenApplyAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"), index = 1)
|
||||
private Executor useMainInvokingExecutor(Executor executor) {
|
||||
return this.mainInvokingExecutor;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Redirect(method = "scheduleChunkGeneration", at = @At(value = "INVOKE", target = "Ljava/util/concurrent/CompletableFuture;thenComposeAsync(Ljava/util/function/Function;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
|
||||
private CompletableFuture skipWorkerIfPossible(CompletableFuture inputFuture, Function function, Executor executor, ChunkHolder holder) {
|
||||
Executor targetExecutor = (runnable) -> {
|
||||
if(((IPaperChunkHolder)holder).mfix$canAdvanceStatus()) {
|
||||
this.mainInvokingExecutor.execute(runnable);
|
||||
return;
|
||||
}
|
||||
executor.execute(runnable);
|
||||
};
|
||||
return inputFuture.thenComposeAsync(function, targetExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason revert 1.17 chunk system changes, significantly reduces time and RAM needed to load chunks
|
||||
*/
|
||||
@Inject(method = "schedule", at = @At("HEAD"), cancellable = true)
|
||||
private void useLegacySchedulingLogic(ChunkHolder holder, ChunkStatus requiredStatus, CallbackInfoReturnable<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> cir) {
|
||||
if(requiredStatus != ChunkStatus.EMPTY) {
|
||||
ChunkPos chunkpos = holder.getPos();
|
||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = holder.getOrScheduleFuture(requiredStatus.getParent(), (ChunkMap)(Object)this);
|
||||
cir.setReturnValue(future.thenComposeAsync((either) -> {
|
||||
Optional<ChunkAccess> optional = either.left();
|
||||
if(!optional.isPresent())
|
||||
return CompletableFuture.completedFuture(either);
|
||||
|
||||
if (requiredStatus == ChunkStatus.LIGHT) {
|
||||
this.distanceManager.addTicket(TicketType.LIGHT, chunkpos, 33 + ChunkStatus.getDistance(ChunkStatus.LIGHT), chunkpos);
|
||||
}
|
||||
|
||||
// from original method
|
||||
if (optional.get().getStatus().isOrAfter(requiredStatus)) {
|
||||
CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = requiredStatus.load(this.level, this.structureManager, this.lightEngine, (arg2) -> {
|
||||
return this.protoChunkToFullChunk(holder);
|
||||
}, (ChunkAccess)optional.get());
|
||||
this.progressListener.onStatusChange(chunkpos, requiredStatus);
|
||||
return completablefuture;
|
||||
} else {
|
||||
return this.scheduleChunkGeneration(holder, requiredStatus);
|
||||
}
|
||||
}, this.mainThreadExecutor).thenComposeAsync(CompletableFuture::completedFuture, this.mainThreadExecutor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.core;
|
||||
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.embeddedt.modernfix.ModernFixClient;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(SynchedEntityData.class)
|
||||
@ClientOnlyMixin
|
||||
public class SynchedEntityDataMixin {
|
||||
/**
|
||||
* Store this in our set of all entity data objects.
|
||||
*
|
||||
* Not an ideal solution, but it should guarantee compatibility with mods.
|
||||
*/
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void storeInSet(Entity arg, CallbackInfo ci) {
|
||||
synchronized (ModernFixClient.allEntityDatas) {
|
||||
ModernFixClient.allEntityDatas.add((SynchedEntityData)(Object)this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.devenv;
|
||||
|
||||
import com.mojang.text2speech.Narrator;
|
||||
import com.mojang.text2speech.NarratorDummy;
|
||||
import net.minecraft.client.gui.chat.NarratorChatListener;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(NarratorChatListener.class)
|
||||
@ClientOnlyMixin
|
||||
public class NarratorMixin {
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/text2speech/Narrator;getNarrator()Lcom/mojang/text2speech/Narrator;", remap = false))
|
||||
private Narrator useDummyNarrator() {
|
||||
return new NarratorDummy();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.direct_stack_trace;
|
||||
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.CrashReportCategory;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(CrashReport.class)
|
||||
public class CrashReportMixin {
|
||||
@Shadow @Final private Throwable exception;
|
||||
|
||||
@Inject(method = "addCategory(Ljava/lang/String;I)Lnet/minecraft/CrashReportCategory;", at = @At(value = "INVOKE", target = "Ljava/io/PrintStream;println(Ljava/lang/String;)V"))
|
||||
private void dumpStacktrace(String s, int i, CallbackInfoReturnable<CrashReportCategory> cir) {
|
||||
new Exception("ModernFix crash stacktrace").printStackTrace();
|
||||
if(this.exception != null)
|
||||
this.exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.server.WorldStem;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
|
||||
@Mixin(Minecraft.class)
|
||||
@ClientOnlyMixin
|
||||
public class MinecraftMixin {
|
||||
private long datapackReloadStartTime;
|
||||
|
||||
@Inject(method = "makeWorldStem(Lnet/minecraft/server/packs/repository/PackRepository;ZLnet/minecraft/server/WorldStem$DataPackConfigSupplier;Lnet/minecraft/server/WorldStem$WorldDataSupplier;)Lnet/minecraft/server/WorldStem;", at = @At(value = "HEAD"))
|
||||
private void recordReloadStart(CallbackInfoReturnable<WorldStem> cir) {
|
||||
datapackReloadStartTime = System.nanoTime();
|
||||
}
|
||||
|
||||
@Inject(method = "makeWorldStem(Lnet/minecraft/server/packs/repository/PackRepository;ZLnet/minecraft/server/WorldStem$DataPackConfigSupplier;Lnet/minecraft/server/WorldStem$WorldDataSupplier;)Lnet/minecraft/server/WorldStem;", at = @At(value = "RETURN"))
|
||||
private void recordReloadEnd(CallbackInfoReturnable<WorldStem> cir) {
|
||||
float timeSpentReloading = ((float)(System.nanoTime() - datapackReloadStartTime) / 1000000000f);
|
||||
ModernFix.LOGGER.warn("Datapack reload took " + timeSpentReloading + " seconds.");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
||||
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||
import net.minecraft.server.packs.resources.ProfiledReloadInstance;
|
||||
import org.embeddedt.modernfix.util.NamedPreparableResourceListener;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(ProfiledReloadInstance.class)
|
||||
public class ProfiledReloadInstanceMixin {
|
||||
@ModifyVariable(method = "<init>", at = @At("HEAD"), argsOnly = true, ordinal = 0)
|
||||
private static List<PreparableReloadListener> getWrappedListeners(List<PreparableReloadListener> listeners) {
|
||||
List<PreparableReloadListener> newList = new ArrayList<>(listeners.size());
|
||||
for(PreparableReloadListener listener : listeners) {
|
||||
newList.add(new NamedPreparableResourceListener(listener));
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
||||
|
||||
import net.minecraft.server.packs.resources.ReloadableResourceManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
@Mixin(ReloadableResourceManager.class)
|
||||
public class SimpleReloadableResourceManagerMixin {
|
||||
// TODO maybe expose as a mixin config
|
||||
private static final boolean ENABLE_DEBUG_RELOADER = Boolean.getBoolean("modernfix.debugReloader");
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason add ability to use this feature in modpacks
|
||||
*/
|
||||
@ModifyArg(method = "createReload", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/packs/resources/SimpleReloadInstance;create(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/List;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Z)Lnet/minecraft/server/packs/resources/ReloadInstance;"), index = 5)
|
||||
private boolean enableDebugReloader(boolean bl) {
|
||||
return bl || ENABLE_DEBUG_RELOADER;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_model_materials;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.ICachedMaterialsModel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(BlockModel.class)
|
||||
@ClientOnlyMixin
|
||||
public class BlockModelMixin {
|
||||
@Shadow @Final @Mutable public Map<String, Either<Material, String>> textureMap;
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason detect changes to the texture map, and clear the material cache as needed
|
||||
*/
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void useTrackingTextureMap(CallbackInfo ci) {
|
||||
Map<String, Either<Material, String>> backingMap = this.textureMap;
|
||||
ICachedMaterialsModel cacheHolder = (ICachedMaterialsModel)this;
|
||||
this.textureMap = new Map<String, Either<Material, String>>() {
|
||||
@Override
|
||||
public int size() {
|
||||
return backingMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return backingMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return backingMap.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return backingMap.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Either<Material, String> get(Object o) {
|
||||
return backingMap.get(o);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Either<Material, String> put(String s, Either<Material, String> materialStringEither) {
|
||||
Either<Material, String> old = backingMap.put(s, materialStringEither);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Either<Material, String> remove(Object o) {
|
||||
Either<Material, String> e = backingMap.remove(o);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends String, ? extends Either<Material, String>> map) {
|
||||
backingMap.putAll(map);
|
||||
cacheHolder.clearMaterialsCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
backingMap.clear();
|
||||
cacheHolder.clearMaterialsCache();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<Either<Material, String>> values() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<String, Either<Material, String>>> entrySet() {
|
||||
cacheHolder.clearMaterialsCache();
|
||||
return backingMap.entrySet();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_model_materials;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Mixin(MultiPart.class)
|
||||
@ClientOnlyMixin
|
||||
public class MultipartMixin {
|
||||
private Collection<ResourceLocation> dependencyCache = null;
|
||||
@Inject(method = "getDependencies", at = @At("HEAD"), cancellable = true)
|
||||
private void useDependencyCache(CallbackInfoReturnable<Collection<ResourceLocation>> cir) {
|
||||
if(dependencyCache != null)
|
||||
cir.setReturnValue(dependencyCache);
|
||||
}
|
||||
|
||||
@Inject(method = "getDependencies", at = @At("RETURN"))
|
||||
private void storeDependencyCache(CallbackInfoReturnable<Collection<ResourceLocation>> cir) {
|
||||
if(dependencyCache == null)
|
||||
dependencyCache = cir.getReturnValue();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_model_materials;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.duck.ICachedMaterialsModel;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/* only cache BlockModel to prevent issues with CTM on Fabric */
|
||||
@Mixin(value = {BlockModel.class})
|
||||
@ClientOnlyMixin
|
||||
public class VanillaModelMixin implements ICachedMaterialsModel {
|
||||
private Collection<Material> materialsCache = null;
|
||||
|
||||
@Inject(method = "getMaterials", at = @At("HEAD"), cancellable = true)
|
||||
private void useCachedMaterials(Function<ResourceLocation, UnbakedModel> pModelGetter, Set<Pair<String, String>> pMissingTextureErrors, CallbackInfoReturnable<Collection<Material>> cir) {
|
||||
if(materialsCache != null) {
|
||||
cir.setReturnValue(materialsCache);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "getMaterials", at = @At("RETURN"))
|
||||
private void storeCachedMaterials(Function<ResourceLocation, UnbakedModel> pModelGetter, Set<Pair<String, String>> pMissingTextureErrors, CallbackInfoReturnable<Collection<Material>> cir) {
|
||||
if(materialsCache == null)
|
||||
materialsCache = Collections.unmodifiableCollection(cir.getReturnValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearMaterialsCache() {
|
||||
materialsCache = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_strongholds;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.duck.IChunkGenerator;
|
||||
import org.embeddedt.modernfix.duck.IServerLevel;
|
||||
import org.embeddedt.modernfix.world.StrongholdLocationCache;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(ChunkGenerator.class)
|
||||
public class ChunkGeneratorMixin implements IChunkGenerator {
|
||||
private WeakReference<ServerLevel> mfix$serverLevel;
|
||||
|
||||
@Override
|
||||
public void mfix$setAssociatedServerLevel(ServerLevel level) {
|
||||
mfix$serverLevel = new WeakReference<>(level);
|
||||
}
|
||||
|
||||
@Inject(method = "generateRingPositions", at = @At("HEAD"), cancellable = true)
|
||||
private void useCachedDataIfAvailable(Holder<StructureSet> setHolder, ConcentricRingsStructurePlacement placement, CallbackInfoReturnable<CompletableFuture<List<ChunkPos>>> cir) {
|
||||
if(placement.count() == 0)
|
||||
return;
|
||||
ServerLevel level = searchLevel();
|
||||
if(level == null)
|
||||
return;
|
||||
StrongholdLocationCache cache = ((IServerLevel)level).mfix$getStrongholdCache();
|
||||
List<ChunkPos> positions = cache.getChunkPosList();
|
||||
if(positions.isEmpty())
|
||||
return;
|
||||
ModernFix.LOGGER.debug("Loaded stronghold cache for dimension {} with {} positions", level.dimension().location(), positions.size());
|
||||
cir.setReturnValue(CompletableFuture.completedFuture(positions));
|
||||
}
|
||||
|
||||
private ServerLevel searchLevel() {
|
||||
if(mfix$serverLevel != null)
|
||||
return mfix$serverLevel.get();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@Inject(method = "generateRingPositions", at = @At("RETURN"), cancellable = true)
|
||||
private void saveCachedData(Holder<StructureSet> setHolder, ConcentricRingsStructurePlacement placement, CallbackInfoReturnable<CompletableFuture<List<ChunkPos>>> cir) {
|
||||
cir.setReturnValue(cir.getReturnValue().thenApplyAsync(list -> {
|
||||
if(list.size() == 0)
|
||||
return list;
|
||||
ServerLevel level = searchLevel();
|
||||
if(level != null) {
|
||||
StrongholdLocationCache cache = ((IServerLevel)level).mfix$getStrongholdCache();
|
||||
cache.setChunkPosList(list);
|
||||
ModernFix.LOGGER.debug("Saved stronghold cache for dimension {}", level.dimension().location());
|
||||
}
|
||||
return list;
|
||||
}, Util.backgroundExecutor()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_strongholds;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.level.CustomSpawner;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.storage.DimensionDataStorage;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
import net.minecraft.world.level.storage.WritableLevelData;
|
||||
import org.embeddedt.modernfix.duck.IChunkGenerator;
|
||||
import org.embeddedt.modernfix.duck.IServerLevel;
|
||||
import org.embeddedt.modernfix.world.StrongholdLocationCache;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
public abstract class ServerLevelMixin extends Level implements IServerLevel {
|
||||
protected ServerLevelMixin(WritableLevelData arg, ResourceKey<Level> arg2, Holder<DimensionType> arg3, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l) {
|
||||
super(arg, arg2, arg3, supplier, bl, bl2, l);
|
||||
}
|
||||
|
||||
@Shadow public abstract DimensionDataStorage getDataStorage();
|
||||
|
||||
private StrongholdLocationCache mfix$strongholdCache;
|
||||
|
||||
/**
|
||||
* Initialize the stronghold cache but don't force any structure generation yet.
|
||||
*/
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/chunk/ChunkGenerator;ensureStructuresGenerated()V"))
|
||||
private void hookStrongholdCache(ChunkGenerator generator) {
|
||||
((IChunkGenerator)generator).mfix$setAssociatedServerLevel((ServerLevel)(Object)this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Now start the stronghold generation process.
|
||||
*/
|
||||
@Inject(method = "<init>", at = @At("TAIL"))
|
||||
private void ensureGeneration(MinecraftServer minecraftServer, Executor executor, LevelStorageSource.LevelStorageAccess arg, ServerLevelData arg2, ResourceKey<Level> arg3, Holder<DimensionType> arg4, ChunkProgressListener arg5, ChunkGenerator arg6, boolean bl, long l, List<CustomSpawner> list, boolean bl2, CallbackInfo ci) {
|
||||
mfix$strongholdCache = this.getDataStorage().computeIfAbsent(StrongholdLocationCache::load,
|
||||
StrongholdLocationCache::new,
|
||||
StrongholdLocationCache.getFileId(this.dimensionTypeRegistration()));
|
||||
arg6.ensureStructuresGenerated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StrongholdLocationCache mfix$getStrongholdCache() {
|
||||
return mfix$strongholdCache;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.cache_upgraded_structures;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
import org.embeddedt.modernfix.structure.CachingStructureManager;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@Mixin(StructureManager.class)
|
||||
public class StructureManagerMixin {
|
||||
@Shadow @Final private DataFixer fixerUpper;
|
||||
|
||||
@Redirect(method = "loadFromResource", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureManager;readStructure(Ljava/io/InputStream;)Lnet/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate;"))
|
||||
private StructureTemplate readViaCache(StructureManager manager, InputStream stream, ResourceLocation arg) throws IOException {
|
||||
return CachingStructureManager.readStructure(arg, this.fixerUpper, stream);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.deduplicate_climate_parameters;
|
||||
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
import org.embeddedt.modernfix.dedup.ClimateCache;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin({ Climate.Parameter.class, Climate.ParameterPoint.class })
|
||||
public class ParameterMixin {
|
||||
@Redirect(method = "*", at = @At(value = "NEW", target = "net/minecraft/world/level/biome/Climate$Parameter"), require = 0)
|
||||
private static Climate.Parameter internParameterStatic(long min, long max) {
|
||||
return ClimateCache.MFIX_INTERNER.intern(new Climate.Parameter(min, max));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.deduplicate_location;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.dedup.IdentifierCaches;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ResourceLocation.class)
|
||||
public class MixinResourceLocation {
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
protected String namespace;
|
||||
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
protected String path;
|
||||
|
||||
@Inject(method = "<init>([Ljava/lang/String;)V", at = @At("RETURN"))
|
||||
private void reinit(String[] id, CallbackInfo ci) {
|
||||
this.namespace = IdentifierCaches.NAMESPACES.deduplicate(this.namespace);
|
||||
this.path = IdentifierCaches.PATH.deduplicate(this.path);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_dfu;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(SharedConstants.class)
|
||||
public class SharedConstantsMixin {
|
||||
@Inject(method = "<clinit>", at = @At("RETURN"))
|
||||
private static void skipSchemaCheck(CallbackInfo ci) {
|
||||
SharedConstants.CHECK_DATA_FIXER_SCHEMA = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_entity_renderers;
|
||||
|
||||
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.entity.EntityRendererMap;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(EntityRenderDispatcher.class)
|
||||
@ClientOnlyMixin
|
||||
public class EntityRenderDispatcherMixin {
|
||||
@Shadow private Map<EntityType<?>, EntityRenderer<?>> renderers;
|
||||
|
||||
private EntityRendererMap mfix$dynamicRenderers;
|
||||
|
||||
@Inject(method = "getRenderer", at = @At("RETURN"), cancellable = true)
|
||||
private <T extends Entity> void checkNullness(T entity, CallbackInfoReturnable<EntityRenderer<? super T>> cir) {
|
||||
// apparently some mods yeet the renderers map and cause issues
|
||||
if(cir.getReturnValue() == null)
|
||||
cir.setReturnValue((EntityRenderer<? super T>)mfix$dynamicRenderers.get(entity.getType()));
|
||||
}
|
||||
|
||||
@Redirect(method = "onResourceManagerReload", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/client/renderer/entity/EntityRenderDispatcher;renderers:Ljava/util/Map;"))
|
||||
private void setRendererField(EntityRenderDispatcher instance, Map<EntityType<?>, EntityRenderer<?>> incomingMap) {
|
||||
this.renderers = incomingMap;
|
||||
this.mfix$dynamicRenderers = (EntityRendererMap)incomingMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_entity_renderers;
|
||||
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderers;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.entity.EntityRendererMap;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(EntityRenderers.class)
|
||||
@ClientOnlyMixin
|
||||
public class EntityRenderersMixin {
|
||||
@Shadow @Final private static Map<EntityType<?>, EntityRendererProvider<?>> PROVIDERS;
|
||||
|
||||
@Inject(method = "createEntityRenderers", at = @At("HEAD"), cancellable = true)
|
||||
private static void createDynamicRendererLoader(EntityRendererProvider.Context context, CallbackInfoReturnable<Map<EntityType<?>, EntityRenderer<?>>> cir) {
|
||||
cir.setReturnValue(new EntityRendererMap(PROVIDERS, context));
|
||||
ModernFix.LOGGER.info("Dynamic entity renderer hook setup");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.client.renderer.block.model.BlockElementFace;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.dynamicresources.UVController;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@Mixin(BlockElementFace.Deserializer.class)
|
||||
@ClientOnlyMixin
|
||||
public class BlockElementFaceDeserializerMixin {
|
||||
|
||||
@Redirect(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/client/renderer/block/model/BlockElementFace;",
|
||||
at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonDeserializationContext;deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;)Ljava/lang/Object;", ordinal = 0))
|
||||
private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) {
|
||||
return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources.ae2;
|
||||
|
||||
import appeng.core.AppEng;
|
||||
import appeng.init.client.InitAutoRotatingModel;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.annotation.RequiresMod;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.ModernFixClient;
|
||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(InitAutoRotatingModel.class)
|
||||
@RequiresMod("ae2")
|
||||
@ClientOnlyMixin
|
||||
public class RegistrationMixin {
|
||||
@Shadow @Final private static Map<String, Function<BakedModel, BakedModel>> CUSTOMIZERS;
|
||||
|
||||
@Inject(method = "init", at = @At("TAIL"), remap = false)
|
||||
private static void doRegisterDynBake(CallbackInfo ci) {
|
||||
ModernFixClient.CLIENT_INTEGRATIONS.add(new ModernFixClientIntegration() {
|
||||
@Override
|
||||
public BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery) {
|
||||
if(location.getNamespace().equals(AppEng.MOD_ID)) {
|
||||
BakedModel m = bakery.bake(ModelBakery.MISSING_MODEL_LOCATION, BlockModelRotation.X0_Y0);
|
||||
if(originalModel == m)
|
||||
return originalModel;
|
||||
Function<BakedModel, BakedModel> customizerFn = CUSTOMIZERS.get(location.getPath());
|
||||
if(customizerFn != null)
|
||||
originalModel = customizerFn.apply(originalModel);
|
||||
}
|
||||
return originalModel;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.faster_font_loading;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import net.minecraft.client.gui.font.providers.LegacyUnicodeBitmapsProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Objective: avoid recomputing locations many times, as well as loading all the font sheets in the constructor
|
||||
* only to do it again later.
|
||||
*/
|
||||
@Mixin(LegacyUnicodeBitmapsProvider.class)
|
||||
@ClientOnlyMixin
|
||||
public abstract class LegacyUnicodeBitmapsProviderMixin {
|
||||
@Shadow protected abstract ResourceLocation getSheetLocation(int i);
|
||||
|
||||
@Shadow @Final private Map<ResourceLocation, NativeImage> textures;
|
||||
private final ResourceLocation[] glyphLocations = new ResourceLocation[256];
|
||||
|
||||
private ResourceLocation currentCharIdx;
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/font/providers/LegacyUnicodeBitmapsProvider;getSheetLocation(I)Lnet/minecraft/resources/ResourceLocation;"))
|
||||
private ResourceLocation storeCurrentCharIdx(LegacyUnicodeBitmapsProvider provider, int i) {
|
||||
ResourceLocation location = getSheetLocation(i);
|
||||
currentCharIdx = location;
|
||||
return location;
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;read(Lcom/mojang/blaze3d/platform/NativeImage$Format;Ljava/io/InputStream;)Lcom/mojang/blaze3d/platform/NativeImage;"))
|
||||
private NativeImage storeLoadedFontSheet(NativeImage.Format format, InputStream stream) throws IOException {
|
||||
NativeImage image = NativeImage.read(format, stream);
|
||||
textures.put(currentCharIdx, image);
|
||||
return image;
|
||||
}
|
||||
|
||||
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/NativeImage;close()V"))
|
||||
private void skipCloseNativeImage(NativeImage image) {
|
||||
/* we can't close here, as the image has been stored for use later */
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void clearLocation(CallbackInfo ci) {
|
||||
currentCharIdx = null;
|
||||
}
|
||||
|
||||
@Inject(method = "getSheetLocation", at = @At("HEAD"), cancellable = true)
|
||||
private void useCachedLocation(int idx, CallbackInfoReturnable<ResourceLocation> cir) {
|
||||
int cachedIdx = idx / 256;
|
||||
if(cachedIdx >= 0 && cachedIdx < glyphLocations.length && glyphLocations[cachedIdx] != null)
|
||||
cir.setReturnValue(glyphLocations[cachedIdx]);
|
||||
}
|
||||
|
||||
@Inject(method = "getSheetLocation", at = @At("RETURN"))
|
||||
private void saveCachedLocation(int idx, CallbackInfoReturnable<ResourceLocation> cir) {
|
||||
int cachedIdx = idx / 256;
|
||||
if(cachedIdx >= 0 && cachedIdx < glyphLocations.length)
|
||||
glyphLocations[cachedIdx] = cir.getReturnValue();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.faster_texture_loading;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.metadata.animation.AnimationMetadataSection;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
@Mixin(value = TextureAtlas.class, priority = 600)
|
||||
@ClientOnlyMixin
|
||||
public abstract class TextureAtlasMixin {
|
||||
@Shadow protected abstract ResourceLocation getResourceLocation(ResourceLocation location);
|
||||
|
||||
@Shadow protected abstract Collection<TextureAtlasSprite.Info> getBasicSpriteInfos(ResourceManager resourceManager, Set<ResourceLocation> spriteLocations);
|
||||
|
||||
private Map<ResourceLocation, Pair<Resource, NativeImage>> loadedImages = new ConcurrentHashMap<>();
|
||||
private boolean usingFasterLoad;
|
||||
private Collection<TextureAtlasSprite.Info> storedResults;
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason simplify texture loading by loading whole image once, avoid slow PngInfo code
|
||||
*/
|
||||
@Inject(method = "getBasicSpriteInfos", at = @At("HEAD"))
|
||||
private void loadImages(ResourceManager manager, Set<ResourceLocation> imageLocations, CallbackInfoReturnable<Collection<TextureAtlasSprite.Info>> cir) {
|
||||
usingFasterLoad = ModernFixPlatformHooks.isLoadingNormally();
|
||||
}
|
||||
|
||||
@Redirect(method = "getBasicSpriteInfos", at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;", ordinal = 0))
|
||||
private Iterator<?> skipIteration(Set<?> instance, ResourceManager manager, Set<ResourceLocation> imageLocations) {
|
||||
// bail if Forge is erroring to avoid AT crashes
|
||||
if(!usingFasterLoad)
|
||||
return instance.iterator();
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
ConcurrentLinkedQueue<TextureAtlasSprite.Info> results = new ConcurrentLinkedQueue<>();
|
||||
for(ResourceLocation location : imageLocations) {
|
||||
if(MissingTextureAtlasSprite.getLocation().equals(location))
|
||||
continue;
|
||||
futures.add(CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
ResourceLocation fileLocation = this.getResourceLocation(location);
|
||||
Resource resource = manager.getResource(fileLocation);
|
||||
NativeImage image = NativeImage.read(resource.getInputStream());
|
||||
AnimationMetadataSection animData = resource.getMetadata(AnimationMetadataSection.SERIALIZER);
|
||||
if (animData == null) {
|
||||
animData = AnimationMetadataSection.EMPTY;
|
||||
}
|
||||
Pair<Integer, Integer> dimensions = animData.getFrameSize(image.getWidth(), image.getHeight());
|
||||
loadedImages.put(location, Pair.of(resource, image));
|
||||
results.add(new TextureAtlasSprite.Info(location, dimensions.getFirst(), dimensions.getSecond(), animData));
|
||||
} catch(IOException e) {
|
||||
ModernFix.LOGGER.error("Using missing texture, unable to load {} : {}", location, e);
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Unable to parse metadata from {} : {}", location, e);
|
||||
}
|
||||
}, ModernFix.resourceReloadExecutor()));
|
||||
}
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
storedResults = results;
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
@Inject(method = "getBasicSpriteInfos", at = @At("RETURN"))
|
||||
private void injectFastSprites(ResourceManager resourceManager, Set<ResourceLocation> spriteLocations, CallbackInfoReturnable<Collection<TextureAtlasSprite.Info>> cir) {
|
||||
if(usingFasterLoad)
|
||||
cir.getReturnValue().addAll(storedResults);
|
||||
}
|
||||
|
||||
@Inject(method = "prepareToStitch", at = @At("HEAD"))
|
||||
private void initMap(CallbackInfoReturnable<TextureAtlas.Preparations> cir) {
|
||||
loadedImages = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Inject(method = "prepareToStitch", at = @At("RETURN"))
|
||||
private void clearLoadedImages(CallbackInfoReturnable<TextureAtlas.Preparations> cir) {
|
||||
loadedImages = Collections.emptyMap();
|
||||
storedResults = null;
|
||||
}
|
||||
|
||||
@Inject(method = "load(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/client/renderer/texture/TextureAtlasSprite$Info;IIIII)Lnet/minecraft/client/renderer/texture/TextureAtlasSprite;",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/texture/TextureAtlas;getResourceLocation(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/resources/ResourceLocation;"), cancellable = true)
|
||||
private void loadFromExisting(ResourceManager resourceManager, TextureAtlasSprite.Info spriteInfo, int storageX, int storageY, int mipLevel, int x, int y, CallbackInfoReturnable<TextureAtlasSprite> cir) {
|
||||
if(!usingFasterLoad)
|
||||
return;
|
||||
Pair<Resource, NativeImage> pair = loadedImages.get(spriteInfo.name());
|
||||
if(pair == null) {
|
||||
ModernFix.LOGGER.error("Texture {} was not loaded in early stage", spriteInfo.name());
|
||||
cir.setReturnValue(null);
|
||||
} else {
|
||||
TextureAtlasSprite sprite = null;
|
||||
try {
|
||||
sprite = ModernFixPlatformHooks.loadTextureAtlasSprite((TextureAtlas)(Object)this, resourceManager, spriteInfo, pair.getFirst(), storageX, storageY, x, y, mipLevel, pair.getSecond());
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Error loading texture {}: {}", spriteInfo.name(), e);
|
||||
} finally {
|
||||
try {
|
||||
pair.getFirst().close();
|
||||
} catch(IOException ignored) {
|
||||
// not much we can do
|
||||
}
|
||||
}
|
||||
cir.setReturnValue(sprite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.nbt_memory_usage;
|
||||
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.embeddedt.modernfix.util.CanonizingStringMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(targets = "net/minecraft/nbt/CompoundTag$1")
|
||||
public class CompoundTag1Mixin {
|
||||
@ModifyVariable(method = "load(Ljava/io/DataInput;ILnet/minecraft/nbt/NbtAccounter;)Lnet/minecraft/nbt/CompoundTag;", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false))
|
||||
private Map<String, Tag> modifyMap(Map<String, Tag> map) {
|
||||
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
|
||||
if(map != null)
|
||||
newMap.putAll(map);
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.nbt_memory_usage;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.embeddedt.modernfix.util.CanonizingStringMap;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(CompoundTag.class)
|
||||
public class CompoundTagMixin {
|
||||
@Shadow @Final
|
||||
private Map<String, Tag> tags;
|
||||
|
||||
/**
|
||||
* Ensure that the default backing map is a CanonizingStringMap.
|
||||
*/
|
||||
@ModifyArg(method = "<init>()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;<init>(Ljava/util/Map;)V"), index = 0)
|
||||
private static Map<String, Tag> useCanonizingStringMap(Map<String, Tag> incoming) {
|
||||
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
|
||||
if(incoming != null)
|
||||
newMap.putAll(incoming);
|
||||
return newMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason use more efficient method when copying canonizing string map
|
||||
*/
|
||||
@Inject(method = "copy()Lnet/minecraft/nbt/CompoundTag;", at = @At("HEAD"), cancellable = true)
|
||||
public void copyEfficient(CallbackInfoReturnable<Tag> cir) {
|
||||
if(this.tags instanceof CanonizingStringMap) {
|
||||
cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap<Tag>)this.tags, Tag::copy)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
|
||||
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import org.embeddedt.modernfix.duck.IBlockState;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Dynamic;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
|
||||
@Mixin(BlockBehaviour.BlockStateBase.class)
|
||||
public abstract class BlockStateBaseMixin implements IBlockState {
|
||||
@Shadow public abstract void initCache();
|
||||
|
||||
@Shadow private BlockBehaviour.BlockStateBase.Cache cache;
|
||||
|
||||
private volatile boolean cacheInvalid = false;
|
||||
private static boolean buildingCache = false;
|
||||
@Override
|
||||
public void clearCache() {
|
||||
cacheInvalid = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCacheInvalid() {
|
||||
return cacheInvalid;
|
||||
}
|
||||
|
||||
private BlockBehaviour.BlockStateBase.Cache generateCache(BlockBehaviour.BlockStateBase base) {
|
||||
if(cacheInvalid) {
|
||||
// Ensure that only one block's cache is built at a time
|
||||
synchronized (BlockBehaviour.BlockStateBase.class) {
|
||||
if(cacheInvalid) {
|
||||
// Ensure that if we end up in here recursively, we just use the original cache
|
||||
if(!buildingCache) {
|
||||
buildingCache = true;
|
||||
try {
|
||||
this.initCache();
|
||||
cacheInvalid = false;
|
||||
} finally {
|
||||
buildingCache = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(
|
||||
value = "FIELD",
|
||||
opcode = Opcodes.GETFIELD,
|
||||
target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;cache:Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache;",
|
||||
ordinal = 0
|
||||
))
|
||||
private BlockBehaviour.BlockStateBase.Cache dynamicCacheGen(BlockBehaviour.BlockStateBase base) {
|
||||
return generateCache(base);
|
||||
}
|
||||
|
||||
@Dynamic
|
||||
@Inject(method = "getPathNodeType", at = @At("HEAD"), require = 0, remap = false)
|
||||
private void generateCacheLithium(CallbackInfoReturnable<?> cir) {
|
||||
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
|
||||
}
|
||||
|
||||
@Dynamic
|
||||
@Inject(method = "getNeighborPathNodeType", at = @At("HEAD"), require = 0, remap = false)
|
||||
private void generateCacheLithium2(CallbackInfoReturnable<?> cir) {
|
||||
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
|
||||
}
|
||||
|
||||
@Dynamic
|
||||
@Inject(method = "getAllFlags", at = @At("HEAD"), require = 0, remap = false)
|
||||
private void generateCacheLithium3(CallbackInfoReturnable<?> cir) {
|
||||
generateCache((BlockBehaviour.BlockStateBase)(Object)this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.reduce_blockstate_cache_rebuilds;
|
||||
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import org.embeddedt.modernfix.blockstate.BlockStateCacheHandler;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(value = Blocks.class, priority = 1100)
|
||||
public class BlocksMixin {
|
||||
@ModifyArg(method = "rebuildCache", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/IdMapper;forEach(Ljava/util/function/Consumer;)V"), index = 0)
|
||||
private static Consumer getEmptyConsumer(Consumer original) {
|
||||
BlockStateCacheHandler.rebuildParallel(true);
|
||||
return o -> {};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = MinecraftServer.class, priority = 1100)
|
||||
public class MinecraftServerMixin {
|
||||
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;addRegionTicket(Lnet/minecraft/server/level/TicketType;Lnet/minecraft/world/level/ChunkPos;ILjava/lang/Object;)V"))
|
||||
private void addSpawnChunkTicket(ServerChunkCache cache, TicketType<?> type, ChunkPos pos, int distance, Object o) {
|
||||
// load first chunk
|
||||
cache.getChunk(pos.x, pos.z, ChunkStatus.FULL, true);
|
||||
}
|
||||
|
||||
@Redirect(method = "prepareLevels", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerChunkCache;getTickingGenerated()I"), require = 0)
|
||||
private int getGenerated(ServerChunkCache cache) {
|
||||
return 441;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.remove_spawn_chunks;
|
||||
|
||||
import net.minecraft.server.level.DistanceManager;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ServerChunkCache.class)
|
||||
public interface ServerChunkCacheAccessor {
|
||||
@Accessor("distanceManager")
|
||||
DistanceManager getDistanceManager();
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.resourcepacks;
|
||||
|
||||
import net.minecraft.server.packs.resources.ReloadableResourceManager;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.resources.PackResourcesCacheEngine;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ReloadableResourceManager.class)
|
||||
public class ReloadableResourceManagerMixin {
|
||||
@Inject(method = "createReload", at = @At("HEAD"))
|
||||
private void invalidateResourceCaches(CallbackInfoReturnable<?> cir) {
|
||||
ModernFix.LOGGER.info("Invalidating pack caches");
|
||||
PackResourcesCacheEngine.invalidate();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.safety;
|
||||
|
||||
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.layers.RenderLayer;
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(LivingEntityRenderer.class)
|
||||
@ClientOnlyMixin
|
||||
public class LivingEntityRendererMixin {
|
||||
@Shadow @Final @Mutable
|
||||
protected List<RenderLayer<?, ?>> layers;
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void synchronizeLayerList(CallbackInfo ci) {
|
||||
/* allows buggy mods to call addLayer concurrently, order is not deterministic but can't fix that */
|
||||
this.layers = Collections.synchronizedList(layers);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
package org.embeddedt.modernfix.core;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig;
|
||||
import org.embeddedt.modernfix.core.config.Option;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.objectweb.asm.tree.*;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class ModernFixMixinPlugin implements IMixinConfigPlugin {
|
||||
private static final String MIXIN_PACKAGE_ROOT = "org.embeddedt.modernfix.mixin.";
|
||||
|
||||
public final Logger logger = LogManager.getLogger("ModernFix");
|
||||
public ModernFixEarlyConfig config = null;
|
||||
public static ModernFixMixinPlugin instance;
|
||||
|
||||
public ModernFixMixinPlugin() {
|
||||
/* invoke early to ensure it gets read on one thread */
|
||||
ModernFixPlatformHooks.getCustomModOptions();
|
||||
boolean firstConfig = instance == null;
|
||||
if(firstConfig) {
|
||||
instance = this;
|
||||
try {
|
||||
config = ModernFixEarlyConfig.load(new File("./config/modernfix-mixins.properties"));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not load configuration file for ModernFix", e);
|
||||
}
|
||||
|
||||
this.logger.info("Loaded configuration file for ModernFix: {} options available, {} override(s) found",
|
||||
config.getOptionCount(), config.getOptionOverrideCount());
|
||||
|
||||
config.getOptionMap().values().forEach(option -> {
|
||||
if (option.isOverridden()) {
|
||||
String source = "[unknown]";
|
||||
|
||||
if (option.isUserDefined()) {
|
||||
source = "user configuration";
|
||||
} else if (option.isModDefined()) {
|
||||
source = "mods [" + String.join(", ", option.getDefiningMods()) + "]";
|
||||
}
|
||||
this.logger.warn("Option '{}' overriden (by {}) to '{}'", option.getName(),
|
||||
source, option.isEnabled());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if(ModernFixEarlyConfig.OPTIFINE_PRESENT)
|
||||
this.logger.fatal("OptiFine detected. Use of ModernFix with OptiFine is not supported due to its impact on launch time and breakage of Forge features.");
|
||||
|
||||
try {
|
||||
Class.forName("sun.misc.Unsafe").getDeclaredMethod("defineAnonymousClass", Class.class, byte[].class, Object[].class);
|
||||
} catch(ReflectiveOperationException | NullPointerException e) {
|
||||
this.logger.info("Applying Nashorn fix");
|
||||
Properties properties = System.getProperties();
|
||||
properties.setProperty("nashorn.args", properties.getProperty("nashorn.args", "") + " --anonymous-classes=false");
|
||||
}
|
||||
|
||||
/* We abuse the constructor of a mixin plugin as a safe location to start modifying the classloader */
|
||||
ModernFixPlatformHooks.injectPlatformSpecificHacks();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onLoad(String mixinPackage) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRefMapperConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
|
||||
mixinClassName = ModernFixEarlyConfig.sanitize(mixinClassName);
|
||||
if (!mixinClassName.startsWith(MIXIN_PACKAGE_ROOT)) {
|
||||
this.logger.error("Expected mixin '{}' to start with package root '{}', treating as foreign and " +
|
||||
"disabling!", mixinClassName, MIXIN_PACKAGE_ROOT);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
String mixin = mixinClassName.substring(MIXIN_PACKAGE_ROOT.length());
|
||||
if(!instance.isOptionEnabled(mixin))
|
||||
return false;
|
||||
String disabledBecauseMod = instance.config.getPermanentlyDisabledMixins().get(mixin);
|
||||
return disabledBecauseMod == null;
|
||||
}
|
||||
|
||||
public boolean isOptionEnabled(String mixin) {
|
||||
Option option = instance.config.getEffectiveOptionForMixin(mixin);
|
||||
|
||||
if (option == null) {
|
||||
this.logger.error("No rules matched mixin '{}', treating as foreign and disabling!", mixin);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return option.isEnabled();
|
||||
}
|
||||
@Override
|
||||
public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
|
||||
ModernFixPlatformHooks.applyASMTransformers(mixinClassName, targetClass);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,389 +0,0 @@
|
|||
package org.embeddedt.modernfix.core.config;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.*;
|
||||
import com.google.gson.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.AnnotationNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ModernFixEarlyConfig {
|
||||
private static final Logger LOGGER = LogManager.getLogger("ModernFixConfig");
|
||||
|
||||
private final Map<String, Option> options = new HashMap<>();
|
||||
private final Multimap<String, Option> optionsByCategory = HashMultimap.create();
|
||||
|
||||
public static final boolean OPTIFINE_PRESENT;
|
||||
|
||||
private File configFile;
|
||||
|
||||
static {
|
||||
boolean hasOfClass = false;
|
||||
try {
|
||||
Class.forName("optifine.OptiFineTransformationService");
|
||||
hasOfClass = true;
|
||||
} catch(Throwable e) {
|
||||
}
|
||||
OPTIFINE_PRESENT = hasOfClass;
|
||||
}
|
||||
|
||||
private static boolean modPresent(String modId) {
|
||||
if(modId.equals("optifine"))
|
||||
return OPTIFINE_PRESENT;
|
||||
else
|
||||
return ModernFixPlatformHooks.modPresent(modId);
|
||||
}
|
||||
|
||||
private static final String MIXIN_DESC = "Lorg/spongepowered/asm/mixin/Mixin;";
|
||||
private static final String MIXIN_CLIENT_ONLY_DESC = "Lorg/embeddedt/modernfix/annotation/ClientOnlyMixin;";
|
||||
private static final String MIXIN_REQUIRES_MOD_DESC = "Lorg/embeddedt/modernfix/annotation/RequiresMod;";
|
||||
|
||||
private static final Pattern PLATFORM_PREFIX = Pattern.compile("(forge|fabric|common)\\.");
|
||||
|
||||
public static String sanitize(String mixinClassName) {
|
||||
return PLATFORM_PREFIX.matcher(mixinClassName).replaceFirst("");
|
||||
}
|
||||
|
||||
private final Set<String> mixinOptions = new ObjectOpenHashSet<>();
|
||||
private final Map<String, String> mixinsMissingMods = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
public static boolean isFabric = ModernFixEarlyConfig.class.getClassLoader().getResourceAsStream("modernfix-fabric.mixins.json") != null;
|
||||
|
||||
public Map<String, String> getPermanentlyDisabledMixins() {
|
||||
return mixinsMissingMods;
|
||||
}
|
||||
|
||||
private void scanForAndBuildMixinOptions() {
|
||||
List<String> configFiles = ImmutableList.of("modernfix-common.mixins.json", "modernfix-fabric.mixins.json", "modernfix-forge.mixins.json");
|
||||
List<String> mixinPaths = new ArrayList<>();
|
||||
for(String configFile : configFiles) {
|
||||
InputStream stream = ModernFixEarlyConfig.class.getClassLoader().getResourceAsStream(configFile);
|
||||
if(stream == null)
|
||||
continue;
|
||||
try(Reader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
|
||||
JsonObject configObject = (JsonObject)new JsonParser().parse(reader);
|
||||
JsonArray mixinList = configObject.getAsJsonArray("mixins");
|
||||
String packageName = configObject.get("package").getAsString().replace('.', '/');
|
||||
for(JsonElement mixin : mixinList) {
|
||||
mixinPaths.add(packageName + "/" + mixin.getAsString().replace('.', '/') + ".class");
|
||||
}
|
||||
} catch(IOException | JsonParseException e) {
|
||||
LOGGER.error("Error loading config " + configFile, e);
|
||||
}
|
||||
}
|
||||
Splitter dotSplitter = Splitter.on('.');
|
||||
for(String mixinPath : mixinPaths) {
|
||||
try(InputStream stream = ModernFixEarlyConfig.class.getClassLoader().getResourceAsStream(mixinPath)) {
|
||||
ClassReader reader = new ClassReader(stream);
|
||||
ClassNode node = new ClassNode();
|
||||
reader.accept(node, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
|
||||
if(node.invisibleAnnotations == null)
|
||||
return;
|
||||
boolean isMixin = false, isClientOnly = false, requiredModPresent = true;
|
||||
String requiredModId = "";
|
||||
for(AnnotationNode annotation : node.invisibleAnnotations) {
|
||||
if(Objects.equals(annotation.desc, MIXIN_DESC)) {
|
||||
isMixin = true;
|
||||
} else if(Objects.equals(annotation.desc, MIXIN_CLIENT_ONLY_DESC)) {
|
||||
isClientOnly = true;
|
||||
} else if(Objects.equals(annotation.desc, MIXIN_REQUIRES_MOD_DESC)) {
|
||||
for(int i = 0; i < annotation.values.size(); i += 2) {
|
||||
if(annotation.values.get(i).equals("value")) {
|
||||
String modId = (String)annotation.values.get(i + 1);
|
||||
if(modId != null) {
|
||||
requiredModPresent = modPresent(modId);
|
||||
requiredModId = modId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(isMixin) {
|
||||
String mixinClassName = sanitize(node.name.replace('/', '.')).replace("org.embeddedt.modernfix.mixin.", "");
|
||||
if(!requiredModPresent)
|
||||
mixinsMissingMods.put(mixinClassName, requiredModId);
|
||||
else if(isClientOnly && !ModernFixPlatformHooks.isClient())
|
||||
mixinsMissingMods.put(mixinClassName, "[not client]");
|
||||
List<String> mixinOptionNames = dotSplitter.splitToList(mixinClassName);
|
||||
StringBuilder optionBuilder = new StringBuilder(mixinClassName.length());
|
||||
optionBuilder.append("mixin");
|
||||
for(int i = 0; i < mixinOptionNames.size() - 1; i++) {
|
||||
optionBuilder.append('.');
|
||||
optionBuilder.append(mixinOptionNames.get(i));
|
||||
mixinOptions.add(optionBuilder.toString());
|
||||
}
|
||||
}
|
||||
} catch(IOException e) {
|
||||
ModernFix.LOGGER.error("Error scanning file " + mixinPath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean isDevEnv = ModernFixPlatformHooks.isDevEnv();
|
||||
|
||||
private static class DefaultSettingMapBuilder extends ImmutableMap.Builder<String, Boolean> {
|
||||
public DefaultSettingMapBuilder putConditionally(BooleanSupplier condition, String k, Boolean v) {
|
||||
if(condition.getAsBoolean())
|
||||
put(k, v);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultSettingMapBuilder put(String key, Boolean value) {
|
||||
super.put(key, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ImmutableMap<String, Boolean> DEFAULT_SETTING_OVERRIDES = new DefaultSettingMapBuilder()
|
||||
.put("mixin.perf.dynamic_resources", false)
|
||||
.put("mixin.feature.direct_stack_trace", false)
|
||||
.put("mixin.perf.rewrite_registry", false)
|
||||
.put("mixin.perf.clear_mixin_classinfo", false)
|
||||
.put("mixin.perf.compress_blockstate", false)
|
||||
.put("mixin.bugfix.packet_leak", false)
|
||||
.put("mixin.perf.deduplicate_location", false)
|
||||
.put("mixin.perf.dynamic_entity_renderers", false)
|
||||
.put("mixin.feature.integrated_server_watchdog", true)
|
||||
.put("mixin.perf.faster_item_rendering", false)
|
||||
.put("mixin.feature.spam_thread_dump", false)
|
||||
.put("mixin.feature.snapshot_easter_egg", true)
|
||||
.put("mixin.feature.warn_missing_perf_mods", true)
|
||||
.put("mixin.feature.spark_profile_launch", false)
|
||||
.put("mixin.devenv", isDevEnv)
|
||||
.put("mixin.perf.remove_spawn_chunks", isDevEnv)
|
||||
.putConditionally(() -> !isFabric, "mixin.bugfix.fix_config_crashes", true)
|
||||
.putConditionally(() -> isFabric, "mixin.perf.clear_fabric_mapping_tables", false)
|
||||
.build();
|
||||
|
||||
private ModernFixEarlyConfig(File file) {
|
||||
this.configFile = file;
|
||||
OptionCategories.load();
|
||||
this.scanForAndBuildMixinOptions();
|
||||
mixinOptions.addAll(DEFAULT_SETTING_OVERRIDES.keySet());
|
||||
for(String optionName : mixinOptions) {
|
||||
boolean defaultEnabled = DEFAULT_SETTING_OVERRIDES.getOrDefault(optionName, true);
|
||||
Option option = new Option(optionName, defaultEnabled, false);
|
||||
this.options.putIfAbsent(optionName, option);
|
||||
this.optionsByCategory.put(OptionCategories.getCategoryForOption(optionName), option);
|
||||
}
|
||||
// Defines the default rules which can be configured by the user or other mods.
|
||||
// You must manually add a rule for any new mixins not covered by an existing package rule.
|
||||
this.addMixinRule("launch.class_search_cache", true);
|
||||
/*
|
||||
this.addMixinRule("perf.use_integrated_resources.jepb", modPresent("jepb"));
|
||||
this.addMixinRule("perf.use_integrated_resources.jeresources", modPresent("jeresources"));
|
||||
this.addMixinRule("perf.jeresources_startup", modPresent("jeresources"));
|
||||
this.addMixinRule("perf.state_definition_construct", modPresent("ferritecore"));
|
||||
this.addMixinRule("bugfix.starlight_emptiness", modPresent("starlight"));
|
||||
this.addMixinRule("bugfix.chunk_deadlock.valhesia", modPresent("valhelsia_structures"));
|
||||
this.addMixinRule("bugfix.tf_cme_on_load", modPresent("twilightforest"));
|
||||
this.addMixinRule("bugfix.refinedstorage", modPresent("refinedstorage"));
|
||||
this.addMixinRule("perf.async_jei", modPresent("jei"));
|
||||
this.addMixinRule("perf.patchouli_deduplicate_books", modPresent("patchouli"));
|
||||
this.addMixinRule("perf.kubejs", modPresent("kubejs"));
|
||||
*/
|
||||
|
||||
/* Mod compat */
|
||||
disableIfModPresent("mixin.perf.thread_priorities", "smoothboot");
|
||||
disableIfModPresent("mixin.perf.boost_worker_count", "smoothboot");
|
||||
disableIfModPresent("mixin.perf.async_jei", "modernui");
|
||||
disableIfModPresent("mixin.perf.compress_biome_container", "chocolate", "betterendforge" ,"skyblockbuilder");
|
||||
disableIfModPresent("mixin.bugfix.mc218112", "performant");
|
||||
disableIfModPresent("mixin.bugfix.remove_block_chunkloading", "performant");
|
||||
disableIfModPresent("mixin.bugfix.paper_chunk_patches", "c2me");
|
||||
disableIfModPresent("mixin.perf.cache_strongholds", "littletiles");
|
||||
disableIfModPresent("mixin.perf.nbt_memory_usage", "c2me");
|
||||
// DimThread makes changes to the server chunk manager (understandably), C2ME probably does the same
|
||||
disableIfModPresent("mixin.bugfix.chunk_deadlock", "c2me", "dimthread");
|
||||
disableIfModPresent("mixin.perf.reuse_datapacks", "tac");
|
||||
disableIfModPresent("mixin.launch.class_search_cache", "optifine");
|
||||
disableIfModPresent("mixin.perf.faster_texture_stitching", "optifine");
|
||||
disableIfModPresent("mixin.perf.datapack_reload_exceptions", "cyanide");
|
||||
disableIfModPresent("mixin.perf.faster_texture_loading", "stitch", "optifine", "changed");
|
||||
if(isFabric) {
|
||||
disableIfModPresent("mixin.bugfix.packet_leak", "memoryleakfix");
|
||||
}
|
||||
}
|
||||
|
||||
private void disableIfModPresent(String configName, String... ids) {
|
||||
for(String id : ids) {
|
||||
if(modPresent(id)) {
|
||||
Option option = this.options.get(configName);
|
||||
if(option != null)
|
||||
option.addModOverride(false, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a Mixin rule which can be configured by users and other mods.
|
||||
* @throws IllegalStateException If a rule with that name already exists
|
||||
* @param mixin The name of the mixin package which will be controlled by this rule
|
||||
* @param enabled True if the rule will be enabled by default, otherwise false
|
||||
*/
|
||||
private void addMixinRule(String mixin, boolean enabled) {
|
||||
String name = getMixinRuleName(mixin);
|
||||
|
||||
if (this.options.putIfAbsent(name, new Option(name, enabled, false)) != null) {
|
||||
throw new IllegalStateException("Mixin rule already defined: " + mixin);
|
||||
}
|
||||
}
|
||||
|
||||
private void readProperties(Properties props) {
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
String key = (String) entry.getKey();
|
||||
String value = (String) entry.getValue();
|
||||
|
||||
Option option = this.options.get(key);
|
||||
|
||||
if (option == null) {
|
||||
LOGGER.warn("No configuration key exists with name '{}', ignoring", key);
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean enabled;
|
||||
|
||||
if (value.equalsIgnoreCase("true")) {
|
||||
enabled = true;
|
||||
} else if (value.equalsIgnoreCase("false")) {
|
||||
enabled = false;
|
||||
} else {
|
||||
LOGGER.warn("Invalid value '{}' encountered for configuration key '{}', ignoring", value, key);
|
||||
continue;
|
||||
}
|
||||
|
||||
option.setEnabled(enabled, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective option for the specified class name. This traverses the package path of the given mixin
|
||||
* and checks each root for configuration rules. If a configuration rule disables a package, all mixins located in
|
||||
* that package and its children will be disabled. The effective option is that of the highest-priority rule, either
|
||||
* a enable rule at the end of the chain or a disable rule at the earliest point in the chain.
|
||||
*
|
||||
* @return Null if no options matched the given mixin name, otherwise the effective option for this Mixin
|
||||
*/
|
||||
public Option getEffectiveOptionForMixin(String mixinClassName) {
|
||||
int lastSplit = 0;
|
||||
int nextSplit;
|
||||
|
||||
Option rule = null;
|
||||
|
||||
while ((nextSplit = mixinClassName.indexOf('.', lastSplit)) != -1) {
|
||||
String key = getMixinRuleName(mixinClassName.substring(0, nextSplit));
|
||||
|
||||
Option candidate = this.options.get(key);
|
||||
|
||||
if (candidate != null) {
|
||||
rule = candidate;
|
||||
|
||||
if (!rule.isEnabled()) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
|
||||
lastSplit = nextSplit + 1;
|
||||
}
|
||||
|
||||
return rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the configuration file from the specified location. If it does not exist, a new configuration file will be
|
||||
* created. The file on disk will then be updated to include any new options.
|
||||
*/
|
||||
public static ModernFixEarlyConfig load(File file) {
|
||||
ModernFixEarlyConfig config = new ModernFixEarlyConfig(file);
|
||||
Properties props = new Properties();
|
||||
if(!Boolean.getBoolean("modernfix.ignoreConfigForTesting")) {
|
||||
if(file.exists()) {
|
||||
try (FileInputStream fin = new FileInputStream(file)){
|
||||
props.load(fin);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not load config file", e);
|
||||
}
|
||||
config.readProperties(props);
|
||||
}
|
||||
|
||||
try {
|
||||
config.save();
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn("Could not write configuration file", e);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
File dir = configFile.getParentFile();
|
||||
|
||||
if (!dir.exists()) {
|
||||
if (!dir.mkdirs()) {
|
||||
throw new IOException("Could not create parent directories");
|
||||
}
|
||||
} else if (!dir.isDirectory()) {
|
||||
throw new IOException("The parent file is not a directory");
|
||||
}
|
||||
|
||||
try (Writer writer = new FileWriter(configFile)) {
|
||||
writer.write("# This is the configuration file for ModernFix.\n");
|
||||
writer.write("#\n");
|
||||
writer.write("# The following options can be enabled or disabled if there is a compatibility issue.\n");
|
||||
writer.write("# Add a line mixin.example_name=true/false without the # sign to enable/disable a rule.\n");
|
||||
List<String> keys = this.options.keySet().stream()
|
||||
.filter(key -> !key.equals("mixin.core"))
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
for(String line : keys) {
|
||||
if(!line.equals("mixin.core"))
|
||||
writer.write("# " + line + "\n");
|
||||
}
|
||||
|
||||
for (String key : keys) {
|
||||
Option option = this.options.get(key);
|
||||
if(option.isUserDefined())
|
||||
writer.write(key + "=" + option.isEnabled() + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getMixinRuleName(String name) {
|
||||
return "mixin." + name;
|
||||
}
|
||||
|
||||
public int getOptionCount() {
|
||||
return this.options.size();
|
||||
}
|
||||
|
||||
public int getOptionOverrideCount() {
|
||||
return (int) this.options.values()
|
||||
.stream()
|
||||
.filter(Option::isOverridden)
|
||||
.count();
|
||||
}
|
||||
|
||||
public Map<String, Option> getOptionMap() {
|
||||
return Collections.unmodifiableMap(this.options);
|
||||
}
|
||||
|
||||
public Multimap<String, Option> getOptionCategoryMap() {
|
||||
return Multimaps.unmodifiableMultimap(this.optionsByCategory);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
|
||||
public class ClimateCache {
|
||||
public static final Interner<Climate.Parameter> MFIX_INTERNER = Interners.newStrongInterner();
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
import it.unimi.dsi.fastutil.Hash;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DeduplicationCache<T> {
|
||||
private final ObjectOpenCustomHashSet<T> pool;
|
||||
|
||||
private int attemptedInsertions = 0;
|
||||
private int deduplicated = 0;
|
||||
|
||||
public DeduplicationCache(Hash.Strategy<T> strategy) {
|
||||
this.pool = new ObjectOpenCustomHashSet<>(strategy);
|
||||
}
|
||||
|
||||
public DeduplicationCache() {
|
||||
this.pool = new ObjectOpenCustomHashSet<>(new Hash.Strategy<T>() {
|
||||
@Override
|
||||
public int hashCode(T o) {
|
||||
return Objects.hashCode(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(T a, T b) {
|
||||
return Objects.equals(a, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public synchronized T deduplicate(T item) {
|
||||
this.attemptedInsertions++;
|
||||
|
||||
T result = this.pool.addOrGet(item);
|
||||
|
||||
if (result != item) {
|
||||
this.deduplicated++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void clearCache() {
|
||||
this.attemptedInsertions = 0;
|
||||
this.deduplicated = 0;
|
||||
|
||||
this.pool.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return String.format("DeduplicationCache ( %d/%d de-duplicated, %d pooled )",
|
||||
this.deduplicated, this.attemptedInsertions, this.pool.size());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
public class IdentifierCaches {
|
||||
public static final DeduplicationCache<String> NAMESPACES = new DeduplicationCache<>();
|
||||
public static final DeduplicationCache<String> PATH = new DeduplicationCache<>();
|
||||
public static final DeduplicationCache<String> PROPERTY = new DeduplicationCache<>();
|
||||
|
||||
public static void printDebug() {
|
||||
ModernFix.LOGGER.info("[[[ Identifier de-duplication statistics ]]]");
|
||||
ModernFix.LOGGER.info("Namespace cache: {}", NAMESPACES);
|
||||
ModernFix.LOGGER.info("Path cache: {}", PATH);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package org.embeddedt.modernfix.dfu;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.mojang.datafixers.RewriteResult;
|
||||
import com.mojang.datafixers.TypeRewriteRule;
|
||||
import com.mojang.datafixers.functions.PointFreeRule;
|
||||
import com.mojang.datafixers.types.Type;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class DFUBlaster {
|
||||
private static final Cache<Pair<IntFunction<RewriteResult<?, ?>>, Integer>, RewriteResult<?, ?>> hmapApplyCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
private static final Cache<Triple<Type<?>, TypeRewriteRule, PointFreeRule>, Optional<? extends RewriteResult<?, ?>>> rewriteCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
public static void blastMaps() {
|
||||
try {
|
||||
Class<?> FOLD_CLASS = Class.forName("com.mojang.datafixers.functions.Fold");
|
||||
Field hmapField = FOLD_CLASS.getDeclaredField("HMAP_APPLY_CACHE");
|
||||
hmapField.setAccessible(true);
|
||||
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
theUnsafe.setAccessible(true);
|
||||
Unsafe unsafe = (Unsafe)theUnsafe.get(null);
|
||||
Object base = unsafe.staticFieldBase(hmapField);
|
||||
long offset = unsafe.staticFieldOffset(hmapField);
|
||||
unsafe.putObject(base, offset, hmapApplyCache.asMap());
|
||||
Field rewriteCacheField = Type.class.getDeclaredField("REWRITE_CACHE");
|
||||
rewriteCacheField.setAccessible(true);
|
||||
base = unsafe.staticFieldBase(rewriteCacheField);
|
||||
offset = unsafe.staticFieldOffset(rewriteCacheField);
|
||||
unsafe.putObject(base, offset, rewriteCache.asMap());
|
||||
new CleanerThread().start();
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Could not replace DFU map", e);
|
||||
}
|
||||
}
|
||||
|
||||
static class CleanerThread extends Thread {
|
||||
CleanerThread() {
|
||||
this.setName("DFU cleaning thread");
|
||||
this.setPriority(1);
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(true) {
|
||||
try {
|
||||
Thread.sleep(15000);
|
||||
} catch(InterruptedException e){
|
||||
return;
|
||||
}
|
||||
rewriteCache.cleanUp();
|
||||
hmapApplyCache.cleanUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
public interface ICachedMaterialsModel {
|
||||
public void clearMaterialsCache();
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
||||
public interface IChunkGenerator {
|
||||
void mfix$setAssociatedServerLevel(ServerLevel level);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
public interface ILevelSave {
|
||||
public void runWorldPersistenceHooks(LevelStorageSource format);
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
public interface IPaperChunkHolder {
|
||||
boolean mfix$canAdvanceStatus();
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import org.embeddedt.modernfix.world.StrongholdLocationCache;
|
||||
|
||||
public interface IServerLevel {
|
||||
StrongholdLocationCache mfix$getStrongholdCache();
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck.reuse_datapacks;
|
||||
|
||||
import net.minecraft.server.ReloadableServerResources;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ICachingResourceClient {
|
||||
void setCachedResources(ReloadableServerResources r);
|
||||
void setCachedDataPackConfig(Collection<String> c);
|
||||
}
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import com.mojang.math.Transformation;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.BlockModelRotation;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DynamicBakedModelProvider implements Map<ResourceLocation, BakedModel> {
|
||||
private final ModelBakery bakery;
|
||||
private final Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> bakedCache;
|
||||
private final Map<ResourceLocation, BakedModel> permanentOverrides;
|
||||
private BakedModel missingModel;
|
||||
private static final BakedModel SENTINEL = new BakedModel() {
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesBlockLight() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustomRenderer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleIcon() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemTransforms getTransforms() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrides getOverrides() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
public DynamicBakedModelProvider(ModelBakery bakery, Map<Triple<ResourceLocation, Transformation, Boolean>, BakedModel> cache) {
|
||||
this.bakery = bakery;
|
||||
this.bakedCache = cache;
|
||||
this.permanentOverrides = Collections.synchronizedMap(new Object2ObjectOpenHashMap<>());
|
||||
}
|
||||
|
||||
public void setMissingModel(BakedModel model) {
|
||||
this.missingModel = model;
|
||||
}
|
||||
|
||||
private static Triple<ResourceLocation, Transformation, Boolean> vanillaKey(Object o) {
|
||||
return Triple.of((ResourceLocation)o, BlockModelRotation.X0_Y0.getRotation(), false);
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return bakedCache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return bakedCache.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return permanentOverrides.getOrDefault(o, SENTINEL) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return permanentOverrides.containsValue(o) || bakedCache.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel get(Object o) {
|
||||
BakedModel model = permanentOverrides.getOrDefault(o, SENTINEL);
|
||||
if(model != SENTINEL)
|
||||
return model;
|
||||
else {
|
||||
try {
|
||||
model = bakery.bake((ResourceLocation)o, BlockModelRotation.X0_Y0);
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Exception baking {}: {}", o, e);
|
||||
model = missingModel;
|
||||
}
|
||||
if(model == missingModel) {
|
||||
// to correctly emulate the original map, we return null for missing models
|
||||
permanentOverrides.put((ResourceLocation) o, null);
|
||||
return null;
|
||||
} else
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel put(ResourceLocation resourceLocation, BakedModel bakedModel) {
|
||||
BakedModel m = permanentOverrides.put(resourceLocation, bakedModel);
|
||||
if(m != null)
|
||||
return m;
|
||||
else
|
||||
return bakedCache.get(vanillaKey(resourceLocation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModel remove(Object o) {
|
||||
BakedModel m = permanentOverrides.remove(o);
|
||||
if(m != null)
|
||||
return m;
|
||||
return bakedCache.remove(vanillaKey(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends ResourceLocation, ? extends BakedModel> map) {
|
||||
permanentOverrides.putAll(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<ResourceLocation> keySet() {
|
||||
return bakedCache.keySet().stream().map(Triple::getLeft).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<BakedModel> values() {
|
||||
return bakedCache.values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<ResourceLocation, BakedModel>> entrySet() {
|
||||
return bakedCache.entrySet().stream().map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getKey().getLeft(), entry.getValue())).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel replace(ResourceLocation key, BakedModel value) {
|
||||
BakedModel existingOverride = permanentOverrides.get(key);
|
||||
// as long as no valid override was put in (null can mean unable to load model, so we treat as invalid), replace
|
||||
// the model
|
||||
if(existingOverride == null)
|
||||
return this.put(key, value);
|
||||
else
|
||||
return existingOverride;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(BiFunction<? super ResourceLocation, ? super BakedModel, ? extends BakedModel> function) {
|
||||
Set<ResourceLocation> overridenLocations = permanentOverrides.keySet();
|
||||
permanentOverrides.replaceAll(function);
|
||||
boolean uvLock = BlockModelRotation.X0_Y0.isUvLocked();
|
||||
Transformation rotation = BlockModelRotation.X0_Y0.getRotation();
|
||||
bakedCache.replaceAll((loc, oldModel) -> {
|
||||
if(loc.getMiddle() != rotation || loc.getRight() != uvLock || overridenLocations.contains(loc.getLeft()))
|
||||
return oldModel;
|
||||
else
|
||||
return function.apply(loc.getLeft(), oldModel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,376 +0,0 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.*;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceSet;
|
||||
import net.minecraft.client.renderer.block.model.BlockModel;
|
||||
import net.minecraft.client.resources.model.*;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackResources;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.server.packs.resources.FallbackResourceManager;
|
||||
import net.minecraft.server.packs.resources.MultiPackResourceManager;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.minecraft.client.resources.model.ModelBakery.BLOCK_ENTITY_MARKER;
|
||||
import static net.minecraft.client.resources.model.ModelBakery.GENERATION_MARKER;
|
||||
|
||||
public class ModelBakeryHelpers {
|
||||
/**
|
||||
* The maximum number of baked models kept in memory at once.
|
||||
*/
|
||||
public static final int MAX_BAKED_MODEL_COUNT = 10000;
|
||||
/**
|
||||
* The maximum number of unbaked models kept in memory at once.
|
||||
*/
|
||||
public static final int MAX_UNBAKED_MODEL_COUNT = 10000;
|
||||
/**
|
||||
* The time in seconds after which a model becomes eligible for eviction if not used.
|
||||
*/
|
||||
public static final int MAX_MODEL_LIFETIME_SECS = 300;
|
||||
|
||||
private static void gatherAdditionalViaManualScan(List<PackResources> untrustedPacks, Set<ResourceLocation> knownLocations,
|
||||
Collection<ResourceLocation> uncertainLocations, String filePrefix) {
|
||||
if(untrustedPacks.size() > 0) {
|
||||
/* Now make a fallback resource manager and use it on the remaining packs to see if they actually contain these files */
|
||||
FallbackResourceManager frm = new FallbackResourceManager(PackType.CLIENT_RESOURCES, "dummy");
|
||||
for (int i = untrustedPacks.size() - 1; i >= 0; i--) {
|
||||
frm.add(untrustedPacks.get(i));
|
||||
}
|
||||
for (ResourceLocation blockstate : uncertainLocations) {
|
||||
if (knownLocations.contains(blockstate))
|
||||
continue; // don't check ones we know exist
|
||||
ResourceLocation fileLocation = new ResourceLocation(blockstate.getNamespace(), filePrefix + blockstate.getPath() + ".json");
|
||||
try (Resource resource = frm.getResource(fileLocation)) {
|
||||
knownLocations.add(blockstate);
|
||||
} catch (IOException | RuntimeException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int ERROR_THRESHOLD = 200;
|
||||
|
||||
private static void logOrSuppressError(Object2IntOpenHashMap<String> suppressionMap, String type, ResourceLocation location, Throwable e) {
|
||||
int numErrors;
|
||||
synchronized (suppressionMap) {
|
||||
numErrors = suppressionMap.computeInt(location.getNamespace(), (k, oldVal) -> (oldVal == null ? 1 : oldVal + 1));
|
||||
}
|
||||
if(numErrors <= ERROR_THRESHOLD)
|
||||
ModernFix.LOGGER.error("Error reading {} {}: {}", type, location, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some mods (cough, EBE) inject their custom resource pack into the namespaced resource managers, but not into the
|
||||
* main list contained inside the parent resource manager, so we need to scan each of the namespaced managers as
|
||||
* well.
|
||||
*/
|
||||
private static void checkFallbacks(MultiPackResourceManager manager, List<PackResources> resourcePackList) {
|
||||
ReferenceSet<PackResources> knownPacks = new ReferenceOpenHashSet<>(resourcePackList);
|
||||
Map<String, FallbackResourceManager> namespacedMap = manager.namespacedManagers;
|
||||
namespacedMap.values().stream().flatMap(FallbackResourceManager::listPacks).forEach(pack -> {
|
||||
if(knownPacks.add(pack)) {
|
||||
/* the pack was not previously known, add to our list */
|
||||
ModernFix.LOGGER.debug("Injecting unlisted pack '{}': {}", pack.getName(), pack.getClass().getName());
|
||||
resourcePackList.add(pack);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void gatherModelMaterials(ResourceManager manager, Predicate<PackResources> isTrustedPack,
|
||||
Set<Material> materialSet, Set<ResourceLocation> blockStateFiles,
|
||||
Set<ResourceLocation> modelFiles, UnbakedModel missingModel,
|
||||
Function<JsonElement, BlockModel> modelDeserializer,
|
||||
Function<ResourceLocation, UnbakedModel> bakeryModelGetter) {
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
final Object2IntOpenHashMap<String> blockstateErrors = new Object2IntOpenHashMap<>();
|
||||
/*
|
||||
* First, gather all vanilla packs, and use listResources on them. This will allow us to (hopefully) avoid
|
||||
* scanning most packs a lot.
|
||||
*/
|
||||
List<PackResources> allPackResources = new ArrayList<>(manager.listPacks().collect(Collectors.toList()));
|
||||
if(manager instanceof MultiPackResourceManager) {
|
||||
checkFallbacks((MultiPackResourceManager)manager, allPackResources);
|
||||
}
|
||||
Collections.reverse(allPackResources);
|
||||
ObjectOpenHashSet<ResourceLocation> allAvailableModels = new ObjectOpenHashSet<>(), allAvailableStates = new ObjectOpenHashSet<>();
|
||||
/* try to fix CME in some runtime packs by forcing generation */
|
||||
for(PackResources pack : allPackResources) {
|
||||
try(InputStream stream = pack.getResource(PackType.CLIENT_RESOURCES, new ResourceLocation("modernfix", "dummy.json"))) {
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
}
|
||||
allPackResources.removeIf(pack -> {
|
||||
for(String namespace : pack.getNamespaces(PackType.CLIENT_RESOURCES)) {
|
||||
Collection<ResourceLocation> allBlockstates = pack.getResources(PackType.CLIENT_RESOURCES, namespace, "blockstates", Integer.MAX_VALUE, p -> p.endsWith(".json"));
|
||||
for(ResourceLocation blockstate : allBlockstates) {
|
||||
allAvailableStates.add(new ResourceLocation(blockstate.getNamespace(), blockstate.getPath().replace("blockstates/", "").replace(".json", "")));
|
||||
}
|
||||
Collection<ResourceLocation> allModels = pack.getResources(PackType.CLIENT_RESOURCES, namespace, "models", Integer.MAX_VALUE, p -> p.endsWith(".json"));
|
||||
for(ResourceLocation blockstate : allModels) {
|
||||
allAvailableModels.add(new ResourceLocation(blockstate.getNamespace(), blockstate.getPath().replace("models/", "").replace(".json", "")));
|
||||
}
|
||||
}
|
||||
if(!isTrustedPack.test(pack)) {
|
||||
ModernFix.LOGGER.debug("Pack with class {} needs manual scan", pack.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
gatherAdditionalViaManualScan(allPackResources, allAvailableStates, blockStateFiles, "blockstates/");
|
||||
// We now have a list of all blockstates known to exist. Delete anything that we don't have
|
||||
blockStateFiles.retainAll(allAvailableStates);
|
||||
allAvailableStates.clear();
|
||||
allAvailableStates.trim();
|
||||
|
||||
ConcurrentLinkedQueue<Pair<ResourceLocation, JsonElement>> blockStateLoadedFiles = new ConcurrentLinkedQueue<>();
|
||||
List<CompletableFuture<Void>> blockStateData = new ArrayList<>();
|
||||
for(ResourceLocation blockstate : blockStateFiles) {
|
||||
ResourceLocation fileLocation = new ResourceLocation(blockstate.getNamespace(), "blockstates/" + blockstate.getPath() + ".json");
|
||||
List<Resource> resources;
|
||||
try {
|
||||
resources = manager.getResources(fileLocation);
|
||||
if(resources.isEmpty())
|
||||
continue;
|
||||
} catch(IOException e) {
|
||||
logOrSuppressError(blockstateErrors, "blockstate", blockstate, e);
|
||||
continue;
|
||||
}
|
||||
blockStateData.add(CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
for(Resource resource : resources) {
|
||||
JsonParser parser = new JsonParser();
|
||||
try {
|
||||
blockStateLoadedFiles.add(Pair.of(blockstate, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))));
|
||||
} catch(JsonParseException e) {
|
||||
logOrSuppressError(blockstateErrors, "blockstate", blockstate, e);
|
||||
} finally {
|
||||
resource.close();
|
||||
}
|
||||
}
|
||||
} catch(IOException e) {
|
||||
logOrSuppressError(blockstateErrors, "blockstate", blockstate, e);
|
||||
}
|
||||
}, ModernFix.resourceReloadExecutor()));
|
||||
}
|
||||
blockStateFiles = null;
|
||||
CompletableFuture.allOf(blockStateData.toArray(new CompletableFuture[0])).join();
|
||||
for(Pair<ResourceLocation, JsonElement> pair : blockStateLoadedFiles) {
|
||||
if(pair.getSecond() != null) {
|
||||
try {
|
||||
JsonObject obj = pair.getSecond().getAsJsonObject();
|
||||
if(obj.has("variants")) {
|
||||
JsonObject eachVariant = obj.getAsJsonObject("variants");
|
||||
for(Map.Entry<String, JsonElement> entry : eachVariant.entrySet()) {
|
||||
JsonElement variantData = entry.getValue();
|
||||
List<JsonObject> variantModels;
|
||||
if(variantData.isJsonArray()) {
|
||||
variantModels = new ArrayList<>();
|
||||
for(JsonElement model : variantData.getAsJsonArray()) {
|
||||
variantModels.add(model.getAsJsonObject());
|
||||
}
|
||||
} else
|
||||
variantModels = Collections.singletonList(variantData.getAsJsonObject());
|
||||
for(JsonObject variant : variantModels) {
|
||||
modelFiles.add(new ResourceLocation(variant.get("model").getAsString()));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
JsonArray multipartData = obj.get("multipart").getAsJsonArray();
|
||||
for(JsonElement element : multipartData) {
|
||||
JsonObject self = element.getAsJsonObject();
|
||||
JsonElement apply = self.get("apply");
|
||||
List<JsonObject> applyObjects;
|
||||
if(apply.isJsonArray()) {
|
||||
applyObjects = new ArrayList<>();
|
||||
for(JsonElement e : apply.getAsJsonArray()) {
|
||||
applyObjects.add(e.getAsJsonObject());
|
||||
}
|
||||
} else
|
||||
applyObjects = Collections.singletonList(apply.getAsJsonObject());
|
||||
for(JsonObject applyEntry : applyObjects) {
|
||||
modelFiles.add(new ResourceLocation(applyEntry.get("model").getAsString()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} catch(RuntimeException e) {
|
||||
logOrSuppressError(blockstateErrors, "blockstate", pair.getFirst(), e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
blockstateErrors.object2IntEntrySet().forEach(entry -> {
|
||||
if(entry.getIntValue() > ERROR_THRESHOLD) {
|
||||
ModernFix.LOGGER.error("Suppressed additional {} blockstate errors for domain {}", entry.getIntValue(), entry.getKey());
|
||||
}
|
||||
});
|
||||
blockstateErrors.clear();
|
||||
blockStateData = null;
|
||||
blockStateLoadedFiles.clear();
|
||||
|
||||
modelFiles.addAll(allAvailableModels);
|
||||
/* figure out which models we should actually load */
|
||||
gatherAdditionalViaManualScan(allPackResources, allAvailableModels, modelFiles, "models/");
|
||||
modelFiles.retainAll(allAvailableModels);
|
||||
allAvailableModels.clear();
|
||||
allAvailableModels.trim();
|
||||
|
||||
Map<ResourceLocation, BlockModel> basicModels = new HashMap<>();
|
||||
basicModels.put(ModelBakery.MISSING_MODEL_LOCATION, (BlockModel)missingModel);
|
||||
basicModels.put(new ResourceLocation("builtin/generated"), GENERATION_MARKER);
|
||||
basicModels.put(new ResourceLocation("builtin/entity"), BLOCK_ENTITY_MARKER);
|
||||
Set<Pair<String, String>> errorSet = Sets.newLinkedHashSet();
|
||||
while(modelFiles.size() > 0) {
|
||||
List<CompletableFuture<Pair<ResourceLocation, JsonElement>>> modelBytes = new ArrayList<>();
|
||||
for(ResourceLocation model : modelFiles) {
|
||||
if(basicModels.containsKey(model))
|
||||
continue;
|
||||
ResourceLocation fileLocation = new ResourceLocation(model.getNamespace(), "models/" + model.getPath() + ".json");
|
||||
modelBytes.add(CompletableFuture.supplyAsync(() -> {
|
||||
try(Resource resource = manager.getResource(fileLocation)) {
|
||||
JsonParser parser = new JsonParser();
|
||||
return Pair.of(model, parser.parse(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)));
|
||||
} catch(IOException | JsonParseException e) {
|
||||
logOrSuppressError(blockstateErrors, "model", fileLocation, e);
|
||||
return Pair.of(fileLocation, null);
|
||||
}
|
||||
}, ModernFix.resourceReloadExecutor()));
|
||||
}
|
||||
modelFiles.clear();
|
||||
CompletableFuture.allOf(modelBytes.toArray(new CompletableFuture[0])).join();
|
||||
UVController.useDummyUv.set(Boolean.TRUE);
|
||||
for(CompletableFuture<Pair<ResourceLocation, JsonElement>> future : modelBytes) {
|
||||
Pair<ResourceLocation, JsonElement> pair = future.join();
|
||||
try {
|
||||
if(pair.getSecond() != null) {
|
||||
|
||||
BlockModel model = modelDeserializer.apply(pair.getSecond());
|
||||
model.name = pair.getFirst().toString();
|
||||
modelFiles.addAll(model.getDependencies());
|
||||
basicModels.put(pair.getFirst(), model);
|
||||
continue;
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
logOrSuppressError(blockstateErrors, "model", pair.getFirst(), e);
|
||||
}
|
||||
basicModels.put(pair.getFirst(), (BlockModel)missingModel);
|
||||
}
|
||||
UVController.useDummyUv.set(Boolean.FALSE);
|
||||
}
|
||||
blockstateErrors.object2IntEntrySet().forEach(entry -> {
|
||||
if(entry.getIntValue() > ERROR_THRESHOLD) {
|
||||
ModernFix.LOGGER.error("Suppressed additional {} model errors for domain {}", entry.getIntValue(), entry.getKey());
|
||||
}
|
||||
});
|
||||
blockstateErrors.clear();
|
||||
modelFiles = null;
|
||||
Function<ResourceLocation, UnbakedModel> modelGetter = loc -> {
|
||||
UnbakedModel m = basicModels.get(loc);
|
||||
/* fallback to vanilla loader if missing */
|
||||
return m != null ? m : bakeryModelGetter.apply(loc);
|
||||
};
|
||||
for(BlockModel model : basicModels.values()) {
|
||||
try {
|
||||
materialSet.addAll(model.getMaterials(modelGetter, errorSet));
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Model {} threw error while getting materials", model.name, e);
|
||||
}
|
||||
}
|
||||
//errorSet.stream().filter(pair -> !pair.getSecond().equals(MISSING_MODEL_LOCATION_STRING)).forEach(pair -> LOGGER.warn("Unable to resolve texture reference: {} in {}", pair.getFirst(), pair.getSecond()));
|
||||
stopwatch.stop();
|
||||
ModernFix.LOGGER.info("Resolving model textures took " + stopwatch);
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>, V extends T> BlockState setPropertyGeneric(BlockState state, Property<T> prop, Object o) {
|
||||
return state.setValue(prop, (V)o);
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> T getValueHelper(Property<T> property, String value) {
|
||||
return property.getValue(value).orElse((T) null);
|
||||
}
|
||||
|
||||
private static final Splitter COMMA_SPLITTER = Splitter.on(',');
|
||||
private static final Splitter EQUAL_SPLITTER = Splitter.on('=').limit(2);
|
||||
|
||||
public static ImmutableList<BlockState> getBlockStatesForMRL(StateDefinition<Block, BlockState> stateDefinition, ModelResourceLocation location) {
|
||||
if(Objects.equals(location.getVariant(), "inventory"))
|
||||
return ImmutableList.of();
|
||||
Set<Property<?>> fixedProperties = new HashSet<>();
|
||||
BlockState fixedState = stateDefinition.any();
|
||||
for(String s : COMMA_SPLITTER.split(location.getVariant())) {
|
||||
Iterator<String> iterator = EQUAL_SPLITTER.split(s).iterator();
|
||||
if (iterator.hasNext()) {
|
||||
String s1 = iterator.next();
|
||||
Property<?> property = stateDefinition.getProperty(s1);
|
||||
if (property != null && iterator.hasNext()) {
|
||||
String s2 = iterator.next();
|
||||
Object value = getValueHelper(property, s2);
|
||||
if (value == null) {
|
||||
throw new RuntimeException("Unknown value: '" + s2 + "' for blockstate property: '" + s1 + "' " + property.getPossibleValues());
|
||||
}
|
||||
fixedState = setPropertyGeneric(fixedState, property, value);
|
||||
fixedProperties.add(property);
|
||||
} else if (!s1.isEmpty()) {
|
||||
throw new RuntimeException("Unknown blockstate property: '" + s1 + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if there is only one possible state
|
||||
if(fixedProperties.size() == stateDefinition.getProperties().size()) {
|
||||
return ImmutableList.of(fixedState);
|
||||
}
|
||||
// generate all possible blockstates from the remaining properties
|
||||
ArrayList<Property<?>> anyProperties = new ArrayList<>(stateDefinition.getProperties());
|
||||
anyProperties.removeAll(fixedProperties);
|
||||
ArrayList<BlockState> finalList = new ArrayList<>();
|
||||
finalList.add(fixedState);
|
||||
for(Property<?> property : anyProperties) {
|
||||
ArrayList<BlockState> newPermutations = new ArrayList<>();
|
||||
for(BlockState state : finalList) {
|
||||
for(Comparable<?> value : property.getPossibleValues()) {
|
||||
newPermutations.add(setPropertyGeneric(state, property, value));
|
||||
}
|
||||
}
|
||||
finalList = newPermutations;
|
||||
}
|
||||
return ImmutableList.copyOf(finalList);
|
||||
}
|
||||
|
||||
public static ModernFixClientIntegration bakedModelWrapper(BiFunction<ResourceLocation, Pair<UnbakedModel, BakedModel>, BakedModel> consumer) {
|
||||
return new ModernFixClientIntegration() {
|
||||
@Override
|
||||
public BakedModel onBakedModelLoad(ResourceLocation location, UnbakedModel baseModel, BakedModel originalModel, ModelState state, ModelBakery bakery) {
|
||||
return consumer.apply(location, Pair.of(baseModel, originalModel));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BlockFaceUV;
|
||||
|
||||
public class UVController {
|
||||
public static final ThreadLocal<Boolean> useDummyUv = ThreadLocal.withInitial(() -> Boolean.FALSE);
|
||||
public static final BlockFaceUV dummyUv = new BlockFaceUV(new float[4], 0);
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
package org.embeddedt.modernfix.entity;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class EntityDataIDSyncHandler {
|
||||
private static Map<Class<? extends Entity>, List<Pair<String, Integer>>> fieldsToSyncMap;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void onDatapackSyncEvent(ServerPlayer targetPlayer) {
|
||||
if(targetPlayer != null) {
|
||||
/* Compute the current set of serializer IDs in use and send them */
|
||||
if(fieldsToSyncMap == null) {
|
||||
fieldsToSyncMap = new HashMap<>();
|
||||
Map<Class<? extends Entity>, Integer> entityPoolMap = SynchedEntityData.ENTITY_ID_POOL;
|
||||
List<Field> fieldsToSync = new ArrayList<>();
|
||||
for(Class<? extends Entity> eClass : entityPoolMap.keySet()) {
|
||||
fieldsToSync.clear();
|
||||
try {
|
||||
Field[] classFields = eClass.getDeclaredFields();
|
||||
for(Field field : classFields) {
|
||||
if(!Modifier.isStatic(field.getModifiers()))
|
||||
continue;
|
||||
field.setAccessible(true);
|
||||
Object o = field.get(null);
|
||||
if(o != null && EntityDataAccessor.class.isAssignableFrom(o.getClass())) {
|
||||
fieldsToSync.add(field);
|
||||
}
|
||||
}
|
||||
for(Field field : fieldsToSync) {
|
||||
int id = ((EntityDataAccessor<?>)field.get(null)).id;
|
||||
fieldsToSyncMap.computeIfAbsent(eClass, k -> new ArrayList<>()).add(Pair.of(field.getName(), id));
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Skipping entity ID sync for {}: {}", eClass.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
EntityIDSyncPacket packet = new EntityIDSyncPacket(fieldsToSyncMap);
|
||||
ModernFix.LOGGER.debug("Sending ID correction packet to client with " + fieldsToSyncMap.size() + " classes");
|
||||
ModernFixPlatformHooks.sendPacket(targetPlayer, packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
package org.embeddedt.modernfix.entity;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderers;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class EntityRendererMap implements Map<EntityType<?>, EntityRenderer<?>> {
|
||||
private final Map<EntityType<?>, EntityRendererProvider<?>> rendererProviders;
|
||||
private final LoadingCache<EntityType<?>, EntityRenderer<?>> rendererMap;
|
||||
private final EntityRendererProvider.Context context;
|
||||
|
||||
public EntityRendererMap(Map<EntityType<?>, EntityRendererProvider<?>> rendererProviders, EntityRendererProvider.Context context) {
|
||||
this.rendererProviders = rendererProviders;
|
||||
this.context = context;
|
||||
this.rendererMap = CacheBuilder.newBuilder().build(new RenderConstructor());
|
||||
}
|
||||
|
||||
class RenderConstructor extends CacheLoader<EntityType<?>, EntityRenderer<?>> {
|
||||
@Override
|
||||
public EntityRenderer<?> load(EntityType<?> key) throws Exception {
|
||||
EntityRendererProvider<?> provider = rendererProviders.get(key);
|
||||
synchronized(EntityRenderers.class) {
|
||||
EntityRenderer<?> renderer;
|
||||
try {
|
||||
if(provider == null)
|
||||
throw new RuntimeException("Provider not registered");
|
||||
renderer = provider.create(context);
|
||||
ModernFix.LOGGER.info("Loaded entity {}", Registry.ENTITY_TYPE.getKey(key));
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Failed to create entity model for " + Registry.ENTITY_TYPE.getKey(key) + ":", e);
|
||||
renderer = new ErroredEntityRenderer<>(context);
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return rendererProviders.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rendererProviders.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return rendererProviders.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderer<?> get(Object o) {
|
||||
try {
|
||||
EntityRenderer<?> renderer = rendererMap.get((EntityType<?>)o);
|
||||
if(renderer == null)
|
||||
throw new AssertionError("Returned entity renderer should never be null");
|
||||
return renderer;
|
||||
} catch (IllegalStateException e) {
|
||||
return null; /* emulate value not being present if recursive load occurs */
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public EntityRenderer<?> put(EntityType<?> entityType, EntityRenderer<?> entityRenderer) {
|
||||
EntityRenderer<?> old = rendererMap.getIfPresent(entityType);
|
||||
rendererMap.put(entityType, entityRenderer);
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRenderer<?> remove(Object o) {
|
||||
EntityRenderer<?> r = rendererMap.getIfPresent(o);
|
||||
rendererMap.invalidate(o);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends EntityType<?>, ? extends EntityRenderer<?>> map) {
|
||||
rendererMap.putAll(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
rendererMap.invalidateAll();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<EntityType<?>> keySet() {
|
||||
return rendererProviders.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<EntityRenderer<?>> values() {
|
||||
return rendererMap.asMap().values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Map.Entry<EntityType<?>, EntityRenderer<?>>> entrySet() {
|
||||
return rendererMap.asMap().entrySet();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package org.embeddedt.modernfix.entity;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.client.renderer.entity.EntityRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererProvider;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
public class ErroredEntityRenderer<T extends Entity> extends EntityRenderer<T> {
|
||||
public ErroredEntityRenderer(EntityRendererProvider.Context arg) {
|
||||
super(arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureLocation(T entity) {
|
||||
return TextureAtlas.LOCATION_BLOCKS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender(T livingEntity, Frustum camera, double camX, double camY, double camZ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(T entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
package org.embeddedt.modernfix.packet;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
public class EntityIDSyncPacket {
|
||||
private Map<Class<? extends Entity>, List<Pair<String, Integer>>> map;
|
||||
|
||||
public EntityIDSyncPacket(Map<Class<? extends Entity>, List<Pair<String, Integer>>> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public Map<Class<? extends Entity>, List<Pair<String, Integer>>> getFieldInfo() {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
public EntityIDSyncPacket() {
|
||||
this.map = new HashMap<>();
|
||||
}
|
||||
|
||||
public void serialize(FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(map.keySet().size());
|
||||
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : map.entrySet()) {
|
||||
buf.writeUtf(entry.getKey().getName());
|
||||
buf.writeVarInt(entry.getValue().size());
|
||||
for(Pair<String, Integer> field : entry.getValue()) {
|
||||
buf.writeUtf(field.getFirst());
|
||||
buf.writeVarInt(field.getSecond());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static EntityIDSyncPacket deserialize(FriendlyByteBuf buf) {
|
||||
EntityIDSyncPacket self = new EntityIDSyncPacket();
|
||||
int numEntityClasses = buf.readVarInt();
|
||||
for(int i = 0; i < numEntityClasses; i++) {
|
||||
String clzName = buf.readUtf();
|
||||
try {
|
||||
Class<?> clz;
|
||||
try {
|
||||
clz = Class.forName(clzName);
|
||||
} catch(ClassNotFoundException e) {
|
||||
ModernFix.LOGGER.warn("Entity class not found: {}", clzName);
|
||||
break;
|
||||
}
|
||||
if(!Entity.class.isAssignableFrom(clz)) {
|
||||
ModernFix.LOGGER.error("Not an entity: " + clzName);
|
||||
break;
|
||||
}
|
||||
int numFields = buf.readVarInt();
|
||||
for(int j = 0; j < numFields; j++) {
|
||||
String fieldName = buf.readUtf();
|
||||
int id = buf.readVarInt();
|
||||
Field f = clz.getDeclaredField(fieldName);
|
||||
if(!Modifier.isStatic(f.getModifiers()))
|
||||
continue;
|
||||
f.setAccessible(true);
|
||||
if(!EntityDataAccessor.class.isAssignableFrom(f.get(null).getClass())) {
|
||||
ModernFix.LOGGER.error("Not a data accessor field: " + clz + "." + fieldName);
|
||||
continue;
|
||||
}
|
||||
self.map.computeIfAbsent((Class<? extends Entity>)clz, k -> new ArrayList<>()).add(Pair.of(fieldName, id));
|
||||
}
|
||||
} catch(ReflectiveOperationException e) {
|
||||
ModernFix.LOGGER.error("Error deserializing packet", e);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
package org.embeddedt.modernfix.platform;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import dev.architectury.injectables.annotations.ExpectPlatform;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ModernFixPlatformHooks {
|
||||
@ExpectPlatform
|
||||
public static boolean isClient() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static boolean isDedicatedServer() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static String getVersionString() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static boolean modPresent(String modId) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static boolean isDevEnv() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static void injectPlatformSpecificHacks() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static void applyASMTransformers(String mixinClassName, ClassNode targetClass) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static MinecraftServer getCurrentServer() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static boolean isLoadingNormally() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static TextureAtlasSprite loadTextureAtlasSprite(TextureAtlas atlasTexture,
|
||||
ResourceManager resourceManager, TextureAtlasSprite.Info textureInfo,
|
||||
Resource resource,
|
||||
int atlasWidth, int atlasHeight,
|
||||
int spriteX, int spriteY, int mipmapLevel,
|
||||
NativeImage image) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static Path getGameDirectory() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static void sendPacket(ServerPlayer player, Object packet) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static void onServerCommandRegister(Consumer<CommandDispatcher<CommandSourceStack>> handler) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static Multimap<String, String> getCustomModOptions() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static void onLaunchComplete() {
|
||||
|
||||
}
|
||||
|
||||
@ExpectPlatform
|
||||
public static String getPlatformName() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
package org.embeddedt.modernfix.resources;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import org.embeddedt.modernfix.util.FileUtil;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class CachedResourcePath {
|
||||
private final String[] pathComponents;
|
||||
|
||||
public static final Interner<String> PATH_COMPONENT_INTERNER = Interners.newStrongInterner();
|
||||
private static final Splitter SLASH_SPLITTER = Splitter.on('/');
|
||||
private static final String[] NO_PREFIX = new String[0];
|
||||
|
||||
public CachedResourcePath(String[] prefix, Path path) {
|
||||
this(prefix, path, path.getNameCount(), true);
|
||||
}
|
||||
|
||||
public CachedResourcePath(String s) {
|
||||
// normalize so we can guarantee there are no empty sections
|
||||
this(NO_PREFIX, SLASH_SPLITTER.splitToList(FileUtil.normalize(s)), false);
|
||||
}
|
||||
|
||||
public <T> CachedResourcePath(String[] prefixElements, Collection<T> collection, boolean intern) {
|
||||
this(prefixElements, collection, collection.size(), intern);
|
||||
}
|
||||
|
||||
public <T> CachedResourcePath(String[] prefixElements, Iterable<T> path, int count, boolean intern) {
|
||||
String[] components = new String[prefixElements.length + count];
|
||||
int i = 0;
|
||||
while(i < prefixElements.length) {
|
||||
components[i] = intern ? PATH_COMPONENT_INTERNER.intern(prefixElements[i]) : prefixElements[i];
|
||||
i++;
|
||||
}
|
||||
for(Object component : path) {
|
||||
String s = component.toString();
|
||||
if(s.length() == 0)
|
||||
continue;
|
||||
components[i] = intern ? PATH_COMPONENT_INTERNER.intern(s) : s;
|
||||
i++;
|
||||
}
|
||||
pathComponents = components;
|
||||
}
|
||||
|
||||
public CachedResourcePath(String[] prefixElements, CachedResourcePath other) {
|
||||
String[] components = new String[prefixElements.length + other.pathComponents.length];
|
||||
int i = 0;
|
||||
while(i < prefixElements.length) {
|
||||
components[i] = PATH_COMPONENT_INTERNER.intern(prefixElements[i]);
|
||||
i++;
|
||||
}
|
||||
System.arraycopy(other.pathComponents, 0, components, i, other.pathComponents.length);
|
||||
pathComponents = components;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOES NOT INTERN!
|
||||
*/
|
||||
public CachedResourcePath(String[] pathComponents) {
|
||||
this.pathComponents = pathComponents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(pathComponents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CachedResourcePath that = (CachedResourcePath) o;
|
||||
return Arrays.equals(pathComponents, that.pathComponents);
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return pathComponents[pathComponents.length - 1];
|
||||
}
|
||||
|
||||
public int getNameCount() {
|
||||
return pathComponents.length;
|
||||
}
|
||||
|
||||
public String getNameAt(int i) {
|
||||
return pathComponents[i];
|
||||
}
|
||||
|
||||
public String getFullPath(int startIndex) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int i = startIndex; i < pathComponents.length; i++) {
|
||||
sb.append(pathComponents[i]);
|
||||
if(i != (pathComponents.length - 1))
|
||||
sb.append('/');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
package org.embeddedt.modernfix.resources;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.embeddedt.modernfix.util.PackTypeHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The core of the resource pack cache system.
|
||||
*
|
||||
* Using a dedicated set and also separate lists is important; testing without this showed a huge performance
|
||||
* drop.
|
||||
*/
|
||||
public class PackResourcesCacheEngine {
|
||||
private final Map<PackType, Set<String>> namespacesByType;
|
||||
private final Set<CachedResourcePath> containedPaths;
|
||||
private final EnumMap<PackType, Map<String, List<CachedResourcePath>>> resourceListings;
|
||||
private volatile boolean cacheGenerationFlag = false;
|
||||
private List<Runnable> cacheGenerationTasks = new ArrayList<>();
|
||||
private Path debugPath;
|
||||
|
||||
public PackResourcesCacheEngine(Function<PackType, Set<String>> namespacesRetriever, BiFunction<PackType, String, Path> basePathRetriever) {
|
||||
this.namespacesByType = new EnumMap<>(PackType.class);
|
||||
for(PackType type : PackType.values()) {
|
||||
if(!PackTypeHelper.isVanillaPackType(type))
|
||||
continue;
|
||||
this.namespacesByType.put(type, namespacesRetriever.apply(type));
|
||||
}
|
||||
this.containedPaths = new ObjectOpenHashSet<>();
|
||||
this.resourceListings = new EnumMap<>(PackType.class);
|
||||
// used for log message
|
||||
this.debugPath = basePathRetriever.apply(PackType.CLIENT_RESOURCES, "minecraft").toAbsolutePath();
|
||||
for(PackType type : PackType.values()) {
|
||||
Collection<String> namespaces = PackTypeHelper.isVanillaPackType(type) ? this.namespacesByType.get(type) : namespacesRetriever.apply(type);
|
||||
Collection<Pair<String, Path>> namespacedRoots = namespaces.stream().map(s -> Pair.of(s, basePathRetriever.apply(type, s).toAbsolutePath())).collect(Collectors.toList());
|
||||
cacheGenerationTasks.add(() -> {
|
||||
ImmutableMap.Builder<String, List<CachedResourcePath>> packTypedMap = ImmutableMap.builder();
|
||||
for(Pair<String, Path> pair : namespacedRoots) {
|
||||
try {
|
||||
ImmutableList.Builder<CachedResourcePath> namespacedList = ImmutableList.builder();
|
||||
String namespace = pair.getFirst();
|
||||
Path root = pair.getSecond();
|
||||
String[] prefix = new String[] { type.getDirectory(), namespace };
|
||||
try (Stream<Path> stream = Files.walk(root)) {
|
||||
stream
|
||||
.map(path -> root.relativize(path.toAbsolutePath()))
|
||||
.filter(PackResourcesCacheEngine::isValidCachedResourcePath)
|
||||
.forEach(path -> {
|
||||
CachedResourcePath cachedPath = new CachedResourcePath(prefix, path);
|
||||
synchronized (this.containedPaths) {
|
||||
this.containedPaths.add(cachedPath);
|
||||
}
|
||||
if(!cachedPath.getFileName().endsWith(".mcmeta"))
|
||||
namespacedList.add(cachedPath);
|
||||
});
|
||||
}
|
||||
packTypedMap.put(namespace, namespacedList.build());
|
||||
} catch(IOException ignored) {
|
||||
}
|
||||
}
|
||||
synchronized (this.resourceListings) {
|
||||
this.resourceListings.put(type, packTypedMap.build());
|
||||
}
|
||||
});
|
||||
}
|
||||
cacheGenerationTasks.add(() -> {
|
||||
((ObjectOpenHashSet<CachedResourcePath>)this.containedPaths).trim();
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isValidCachedResourcePath(Path path) {
|
||||
if(path.getFileName() == null || path.getNameCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
String str = path.toString();
|
||||
if(str.length() == 0)
|
||||
return false;
|
||||
for(int i = 0; i < str.length(); i++) {
|
||||
if(!ResourceLocation.validPathChar(str.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<String> getNamespaces(PackType type) {
|
||||
if(PackTypeHelper.isVanillaPackType(type))
|
||||
return this.namespacesByType.get(type);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private void doGenerateCache() {
|
||||
Stopwatch watch = Stopwatch.createStarted();
|
||||
for(Runnable r : this.cacheGenerationTasks) {
|
||||
r.run();
|
||||
}
|
||||
watch.stop();
|
||||
ModernFix.LOGGER.debug("Generated cache for {} in {}", debugPath, watch);
|
||||
debugPath = null;
|
||||
cacheGenerationTasks = ImmutableList.of();
|
||||
}
|
||||
|
||||
private void awaitLoad() {
|
||||
if(!this.cacheGenerationFlag) {
|
||||
synchronized (this) {
|
||||
if(!this.cacheGenerationFlag) {
|
||||
this.doGenerateCache();
|
||||
this.cacheGenerationFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasResource(String path) {
|
||||
awaitLoad();
|
||||
return this.containedPaths.contains(new CachedResourcePath(path));
|
||||
}
|
||||
|
||||
public boolean hasResource(String[] paths) {
|
||||
awaitLoad();
|
||||
return this.containedPaths.contains(new CachedResourcePath(paths));
|
||||
}
|
||||
|
||||
public Collection<ResourceLocation> getResources(PackType type, String resourceNamespace, String pathIn, int maxDepth, Predicate<String> filter) {
|
||||
if(!PackTypeHelper.isVanillaPackType(type))
|
||||
throw new IllegalArgumentException("Only vanilla PackTypes are supported");
|
||||
awaitLoad();
|
||||
List<CachedResourcePath> paths = resourceListings.get(type).getOrDefault(resourceNamespace, Collections.emptyList());
|
||||
if(paths.isEmpty())
|
||||
return Collections.emptyList();
|
||||
String testPath = pathIn.endsWith("/") ? pathIn : (pathIn + "/");
|
||||
ArrayList<ResourceLocation> resources = new ArrayList<>();
|
||||
for(CachedResourcePath cachePath : paths) {
|
||||
if((cachePath.getNameCount() - 2) > maxDepth)
|
||||
continue;
|
||||
String fullPath = cachePath.getFullPath(2);
|
||||
if(!fullPath.startsWith(testPath))
|
||||
continue;
|
||||
if(!filter.test(cachePath.getFileName()))
|
||||
continue;
|
||||
ResourceLocation foundResource = new ResourceLocation(resourceNamespace, fullPath);
|
||||
resources.add(foundResource);
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
|
||||
private static final WeakHashMap<ICachingResourcePack, Boolean> cachingPacks = new WeakHashMap<>();
|
||||
public static void track(ICachingResourcePack pack) {
|
||||
synchronized (cachingPacks) {
|
||||
cachingPacks.put(pack, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidate() {
|
||||
if(!ModernFixPlatformHooks.isDevEnv())
|
||||
return;
|
||||
synchronized (cachingPacks) {
|
||||
cachingPacks.keySet().forEach(pack -> {
|
||||
if(pack != null)
|
||||
pack.invalidateCache();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package org.embeddedt.modernfix.screen;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.CommonComponents;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ModernFixConfigScreen extends Screen {
|
||||
private OptionList optionList;
|
||||
private Screen lastScreen;
|
||||
|
||||
public boolean madeChanges = false;
|
||||
private Button doneButton;
|
||||
public ModernFixConfigScreen(Screen lastScreen) {
|
||||
super(new TranslatableComponent("modernfix.config"));
|
||||
this.lastScreen = lastScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
this.optionList = new OptionList(this, this.minecraft);
|
||||
this.addWidget(this.optionList);
|
||||
this.doneButton = new Button(this.width / 2 - 100, this.height - 29, 200, 20, CommonComponents.GUI_DONE, (arg) -> {
|
||||
this.onClose();
|
||||
});
|
||||
this.addRenderableWidget(this.doneButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
this.minecraft.setScreen(lastScreen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) {
|
||||
this.renderBackground(poseStack);
|
||||
this.optionList.render(poseStack, mouseX, mouseY, partialTicks);
|
||||
drawCenteredString(poseStack, this.font, this.title, this.width / 2, 8, 16777215);
|
||||
this.doneButton.setMessage(madeChanges ? new TranslatableComponent("modernfix.config.done_restart") : CommonComponents.GUI_DONE);
|
||||
super.render(poseStack, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderComponentHoverEffect(PoseStack matrixStack, @Nullable Style style, int mouseX, int mouseY) {
|
||||
super.renderComponentHoverEffect(matrixStack, style, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package org.embeddedt.modernfix.screen;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.CommonComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
|
||||
public class ModernFixOptionInfoScreen extends Screen {
|
||||
private final Screen lastScreen;
|
||||
private final Component description;
|
||||
|
||||
public ModernFixOptionInfoScreen(Screen lastScreen, String optionName) {
|
||||
super(new TextComponent(optionName));
|
||||
|
||||
this.lastScreen = lastScreen;
|
||||
this.description = new TranslatableComponent("modernfix.option." + optionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
this.addRenderableWidget(new Button(this.width / 2 - 100, this.height - 29, 200, 20, CommonComponents.GUI_DONE, (button) -> {
|
||||
this.onClose();
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
this.minecraft.setScreen(lastScreen);
|
||||
}
|
||||
|
||||
private void drawMultilineString(PoseStack mStack, Font fr, Component str, int x, int y) {
|
||||
for(FormattedCharSequence s : fr.split(str, this.width - 50)) {
|
||||
fr.drawShadow(mStack, s, (float)x, (float)y, 16777215);
|
||||
y += fr.lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack poseStack, int mouseX, int mouseY, float partialTicks) {
|
||||
this.renderBackground(poseStack);
|
||||
drawCenteredString(poseStack, this.font, this.title, this.width / 2, 8, 16777215);
|
||||
this.drawMultilineString(poseStack, this.minecraft.font, description, 10, 50);
|
||||
super.render(poseStack, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
package org.embeddedt.modernfix.searchtree;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
|
||||
import me.shedaniel.rei.api.common.entry.EntryStack;
|
||||
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
|
||||
import me.shedaniel.rei.impl.client.search.AsyncSearchManager;
|
||||
import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl;
|
||||
import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper;
|
||||
import net.minecraft.client.searchtree.ReloadableIdSearchTree;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class REIBackedSearchTree extends DummySearchTree<ItemStack> {
|
||||
private final AsyncSearchManager searchManager = createSearchManager();
|
||||
|
||||
private final boolean filteringByTag;
|
||||
private String lastSearchText = "";
|
||||
private final List<ItemStack> listCache = new ArrayList<>();
|
||||
|
||||
public REIBackedSearchTree(boolean filteringByTag) {
|
||||
this.filteringByTag = filteringByTag;
|
||||
}
|
||||
@Override
|
||||
public List<ItemStack> search(String pSearchText) {
|
||||
if(true) {
|
||||
return this.searchREI(pSearchText);
|
||||
} else {
|
||||
/* Use the default, dummy implementation */
|
||||
return super.search(pSearchText);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ItemStack> searchREI(String pSearchText) {
|
||||
if(!pSearchText.equals(lastSearchText)) {
|
||||
listCache.clear();
|
||||
this.searchManager.updateFilter(pSearchText);
|
||||
List stacks;
|
||||
try {
|
||||
stacks = this.searchManager.getNow();
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Couldn't search for '" + pSearchText + "'", e);
|
||||
stacks = Collections.emptyList();
|
||||
}
|
||||
for(Object o : stacks) {
|
||||
EntryStack<?> stack;
|
||||
if(o instanceof EntryStack<?>)
|
||||
stack = (EntryStack<?>)o;
|
||||
else if(o instanceof HashedEntryStackWrapper) {
|
||||
stack = ((HashedEntryStackWrapper)o).unwrap();
|
||||
} else {
|
||||
ModernFix.LOGGER.error("Don't know how to handle {}", o.getClass().getName());
|
||||
continue;
|
||||
}
|
||||
if(stack.getType() == VanillaEntryTypes.ITEM) {
|
||||
listCache.add(stack.cheatsAs().getValue());
|
||||
}
|
||||
}
|
||||
lastSearchText = pSearchText;
|
||||
}
|
||||
return listCache;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static AsyncSearchManager createSearchManager() {
|
||||
Method m, normalizeMethod;
|
||||
try {
|
||||
try {
|
||||
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredComplexList");
|
||||
m.setAccessible(true);
|
||||
normalizeMethod = HashedEntryStackWrapper.class.getDeclaredMethod("normalize");
|
||||
normalizeMethod.setAccessible(true);
|
||||
} catch(NoSuchMethodException e) {
|
||||
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredList");
|
||||
m.setAccessible(true);
|
||||
normalizeMethod = EntryStack.class.getDeclaredMethod("normalize");
|
||||
normalizeMethod.setAccessible(true);
|
||||
}
|
||||
final MethodHandle getListMethod = MethodHandles.publicLookup().unreflect(m);
|
||||
final MethodHandle normalize = MethodHandles.publicLookup().unreflect(normalizeMethod);
|
||||
final EntryRegistryImpl registry = (EntryRegistryImpl)EntryRegistry.getInstance();
|
||||
Supplier stackListSupplier = () -> {
|
||||
try {
|
||||
return (List)getListMethod.invokeExact(registry);
|
||||
} catch(Throwable e) {
|
||||
if(e instanceof RuntimeException)
|
||||
throw (RuntimeException)e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
UnaryOperator normalizeOperator = o -> {
|
||||
try {
|
||||
return normalize.invoke(o);
|
||||
} catch(Throwable e) {
|
||||
if(e instanceof RuntimeException)
|
||||
throw (RuntimeException)e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
return new AsyncSearchManager(stackListSupplier, () -> {
|
||||
return Predicates.alwaysTrue();
|
||||
}, normalizeOperator);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
|
||||
@Override
|
||||
public ReloadableIdSearchTree<ItemStack> getSearchTree(boolean tag) {
|
||||
return new REIBackedSearchTree(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return ModernFixPlatformHooks.modPresent("roughlyenoughitems");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "REI";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LoadableTickableObject<T> implements TickableObject {
|
||||
private volatile int ticksInactive = 0;
|
||||
private final int timeout;
|
||||
private final Supplier<T> loader;
|
||||
private final Consumer<T> finalizer;
|
||||
private volatile T theObject = null;
|
||||
|
||||
public LoadableTickableObject(int timeout, Supplier<T> loader, Consumer<T> finalizer) {
|
||||
this(timeout, loader, finalizer, null);
|
||||
}
|
||||
|
||||
public LoadableTickableObject(int timeout, Supplier<T> loader, Consumer<T> finalizer, @Nullable T initialValue) {
|
||||
this.timeout = timeout;
|
||||
this.loader = loader;
|
||||
this.finalizer = finalizer;
|
||||
this.theObject = initialValue;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
synchronized (this) {
|
||||
ticksInactive++;
|
||||
T obj = theObject;
|
||||
if(obj == null) {
|
||||
obj = loader.get();
|
||||
theObject = obj;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public final void tick() {
|
||||
synchronized (this) {
|
||||
ticksInactive++;
|
||||
if(ticksInactive >= this.timeout) {
|
||||
finalizer.accept(theObject);
|
||||
theObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
public interface TickableObject {
|
||||
void tick();
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class TickableObjectManager {
|
||||
private static final List<TickableObject> TICKABLE_OBJECT_LIST = new CopyOnWriteArrayList<>();
|
||||
|
||||
public static void register(TickableObject object) {
|
||||
TICKABLE_OBJECT_LIST.add(object);
|
||||
}
|
||||
|
||||
public static void runTick() {
|
||||
for(TickableObject o : TICKABLE_OBJECT_LIST) {
|
||||
o.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
public enum BakeReason {
|
||||
FREEZE,
|
||||
REMOTE_SNAPSHOT_INJECT,
|
||||
LOCAL_SNAPSHOT_INJECT,
|
||||
REVERT,
|
||||
UNKNOWN;
|
||||
private static BakeReason currentBakeReason = null;
|
||||
private static boolean bakeReasonWarned = false;
|
||||
|
||||
public static BakeReason getCurrentBakeReason() {
|
||||
if(currentBakeReason == null && !bakeReasonWarned) {
|
||||
ModernFix.LOGGER.warn("No bake reason found, mixin probably not applied correctly", new IllegalStateException());
|
||||
bakeReasonWarned = false;
|
||||
}
|
||||
return currentBakeReason;
|
||||
}
|
||||
|
||||
public static void setCurrentBakeReason(BakeReason reason) {
|
||||
currentBakeReason = reason;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Replacement backing map for CompoundTags that interns keys.
|
||||
*/
|
||||
public class CanonizingStringMap<T> extends HashMap<String, T> {
|
||||
private static final Interner<String> KEY_INTERNER = Interners.newStrongInterner();
|
||||
|
||||
private static String intern(String key) {
|
||||
return key != null ? KEY_INTERNER.intern(key) : null;
|
||||
}
|
||||
|
||||
public CanonizingStringMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T put(String key, T value) {
|
||||
return super.put(intern(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends T> m) {
|
||||
HashMap<String, T> tmp = new HashMap<>();
|
||||
m.forEach((k, v) -> tmp.put(intern(k), v));
|
||||
super.putAll(tmp);
|
||||
}
|
||||
|
||||
private void putAllWithoutInterning(Map<? extends String, ? extends T> m) {
|
||||
super.putAll(m);
|
||||
}
|
||||
|
||||
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> incomingMap, Function<T, T> deepCopier) {
|
||||
CanonizingStringMap<T> newMap = new CanonizingStringMap<>();
|
||||
newMap.putAllWithoutInterning(Maps.transformValues(incomingMap, deepCopier));
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.level.DataPackConfig;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.storage.WorldData;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class DummyServerConfiguration implements WorldData {
|
||||
@Override
|
||||
public DataPackConfig getDataPackConfig() {
|
||||
return DataPackConfig.DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataPackConfig(DataPackConfig codec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasModded() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getKnownServerBrands() {
|
||||
return ImmutableSet.of("forge");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModdedInfo(String name, boolean isModded) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getCustomBossEvents() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomBossEvents(CompoundTag nbt) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLevelData overworldData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelSettings getLevelSettings() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag createTag(RegistryAccess registries, CompoundTag hostPlayerNBT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHardcore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLevelName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameType getGameType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGameType(GameType type) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAllowCommands() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Difficulty getDifficulty() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDifficulty(Difficulty difficulty) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDifficultyLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDifficultyLocked(boolean locked) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameRules getGameRules() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getLoadedPlayerTag() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag endDragonFightData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEndDragonFightData(CompoundTag nbt) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldGenSettings worldGenSettings() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lifecycle worldGenSettingsLifecycle() {
|
||||
return Lifecycle.stable();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Simple forwarding map implementation that allows layering multiple maps together.
|
||||
*/
|
||||
public class LayeredForwardingMap<K, V> implements Map<K, V> {
|
||||
private final Map<K, V>[] layers;
|
||||
|
||||
public LayeredForwardingMap(Map<K, V>[] layers) {
|
||||
if(layers.length < 1)
|
||||
throw new IllegalArgumentException();
|
||||
for(Map<K, V> layer : layers) {
|
||||
if(layer == null)
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.layers = layers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
for(Map<K, V> map : layers) {
|
||||
if(!map.isEmpty())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
for(Map<K, V> map : layers) {
|
||||
if(map.containsKey(key))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
for(Map<K, V> map : layers) {
|
||||
if(map.containsValue(value))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
for(Map<K, V> map : layers) {
|
||||
V value = map.get(key);
|
||||
if(value != null)
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
if(value == null)
|
||||
throw new IllegalArgumentException();
|
||||
V originalValue = null;
|
||||
for(Map<K, V> map : layers) {
|
||||
V oldVal = map.remove(key);
|
||||
if(originalValue == null)
|
||||
originalValue = oldVal;
|
||||
map.put(key, value);
|
||||
}
|
||||
return originalValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
for(Map<K, V> map : layers) {
|
||||
map.remove(key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
|
||||
for(V value : m.values()) {
|
||||
if(value == null)
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
for(Map<K, V> map : layers) {
|
||||
map.putAll(m);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
Set<K> keys = new ObjectOpenHashSet<>();
|
||||
for(Map<K, V> map : layers) {
|
||||
keys.addAll(map.keySet());
|
||||
}
|
||||
return Collections.unmodifiableSet(keys);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
Set<K> keys = keySet();
|
||||
List<V> vals = new ArrayList<>();
|
||||
for(K key : keys) {
|
||||
vals.add(get(key));
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
package org.embeddedt.modernfix.world;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.DefaultUncaughtExceptionHandlerWithName;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class IntegratedWatchdog extends Thread {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private final WeakReference<MinecraftServer> server;
|
||||
|
||||
private static final long MAX_TICK_DELTA = 40*1000;
|
||||
|
||||
public IntegratedWatchdog(MinecraftServer server) {
|
||||
this.server = new WeakReference<>(server);
|
||||
this.setDaemon(true);
|
||||
this.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandlerWithName(LOGGER));
|
||||
this.setName("ModernFix integrated server watchdog");
|
||||
}
|
||||
|
||||
public static String obtainThreadDump() {
|
||||
ThreadMXBean threadmxbean = ManagementFactory.getThreadMXBean();
|
||||
ThreadInfo[] athreadinfo = threadmxbean.dumpAllThreads(true, true);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Thread Dump:\n");
|
||||
for(ThreadInfo threadinfo : athreadinfo) {
|
||||
sb.append(threadinfo);
|
||||
StackTraceElement[] elements = threadinfo.getStackTrace();
|
||||
if(elements.length > 8) {
|
||||
sb.append("extended trace:\n");
|
||||
for(int i = 8; i < elements.length; i++) {
|
||||
sb.append("\tat ");
|
||||
sb.append(elements[i]);
|
||||
sb.append('\n');
|
||||
}
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while(true) {
|
||||
MinecraftServer server = this.server.get();
|
||||
if(server == null || !server.isRunning())
|
||||
return;
|
||||
long nextTick = server.getNextTickTime();
|
||||
long curTime = Util.getMillis();
|
||||
long delta = curTime - nextTick;
|
||||
if(delta > MAX_TICK_DELTA) {
|
||||
LOGGER.error("A single server tick has taken {}, more than {} milliseconds", delta, MAX_TICK_DELTA);
|
||||
LOGGER.error(obtainThreadDump());
|
||||
nextTick = 0;
|
||||
curTime = 0;
|
||||
}
|
||||
server = null; /* allow GC */
|
||||
try {
|
||||
Thread.sleep(nextTick + MAX_TICK_DELTA - curTime);
|
||||
} catch(InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
package org.embeddedt.modernfix.world;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.saveddata.SavedData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StrongholdLocationCache extends SavedData {
|
||||
private List<ChunkPos> chunkPosList;
|
||||
public StrongholdLocationCache() {
|
||||
super();
|
||||
chunkPosList = new ArrayList<>();
|
||||
}
|
||||
|
||||
public List<ChunkPos> getChunkPosList() {
|
||||
return new ArrayList<>(chunkPosList);
|
||||
}
|
||||
|
||||
public void setChunkPosList(List<ChunkPos> positions) {
|
||||
this.chunkPosList = new ArrayList<>(positions);
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
public static StrongholdLocationCache load(CompoundTag arg) {
|
||||
StrongholdLocationCache cache = new StrongholdLocationCache();
|
||||
if(arg.contains("Positions", Tag.TAG_LONG_ARRAY)) {
|
||||
long[] positions = arg.getLongArray("Positions");
|
||||
for(long position : positions) {
|
||||
cache.chunkPosList.add(new ChunkPos(position));
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag save(CompoundTag compoundTag) {
|
||||
long[] serialized = new long[chunkPosList.size()];
|
||||
for(int i = 0; i < chunkPosList.size(); i++) {
|
||||
ChunkPos thePos = chunkPosList.get(i);
|
||||
serialized[i] = thePos.toLong();
|
||||
}
|
||||
compoundTag.putLongArray("Positions", serialized);
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
public static String getFileId(Holder<DimensionType> dimensionType) {
|
||||
return "mfix_strongholds";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
{
|
||||
"key.modernfix": "ModernFix",
|
||||
"key.modernfix.config": "Open config screen",
|
||||
"modernfix.jei_load": "Loading JEI, this may take a while",
|
||||
"modernfix.no_lazydfu": "LazyDFU is not installed. If Minecraft needs to update game data from an older version, there may be noticeable lag.",
|
||||
"modernfix.no_ferritecore": "FerriteCore is not installed. Memory usage will be very high.",
|
||||
"modernfix.perf_mod_warning": "It is recommended to install the mods, but the warning(s) can be disabled in the ModernFix config.",
|
||||
"modernfix.config": "ModernFix mixin config",
|
||||
"modernfix.config.done_restart": "Done (restart required)",
|
||||
"modernfix.option.on": "on",
|
||||
"modernfix.option.off": "off",
|
||||
"modernfix.config.not_default": " (modified)",
|
||||
"asynclocator.map.locating": "Map (Locating...)",
|
||||
"asynclocator.map.none": "Map (No nearby feature found)",
|
||||
"modernfix.option.category.performance": "Performance",
|
||||
"modernfix.option.category.performance.description": "Features that help improve game/launch performance",
|
||||
"modernfix.option.category.bugfixes": "Bugfixes",
|
||||
"modernfix.option.category.bugfixes.description": "Core bugfixes to improve game stability",
|
||||
"modernfix.option.category.troubleshooting": "Troubleshooting/Utilities",
|
||||
"modernfix.option.category.troubleshooting.description": "Features intended to assist in diagnosing problems",
|
||||
"modernfix.option.category.expert_only": "Expert only",
|
||||
"modernfix.option.category.expert_only.description": "Do not change unless you know what you are doing",
|
||||
"modernfix.option.name.mixin.perf.async_jei": "Background JEI loading",
|
||||
"modernfix.option.mixin.perf.async_jei": "1.16 only. **A key optimization.** Patches JEI to perform its reloading on a background thread, completely eliminating the long delay it adds to world loading.",
|
||||
"modernfix.option.mixin.perf.async_locator": "1.16 only. Backports the Async Locator mod's patches to eliminate server freezes associated with `/locate`, loot table generation, etc.",
|
||||
"modernfix.option.mixin.perf.biome_zoomer": "1.16 only. Minor optimization to improve the performance of the biome zoomer using logic from 1.18.",
|
||||
"modernfix.option.mixin.perf.blast_search_trees": "All versions. If REI or JEI are installed, construction of the vanilla search trees for creative searching is disabled, and searching is instead done using these mods' search implementations. This saves several seconds during world loading, and probably also saves some RAM as well (although I have not measured).",
|
||||
"modernfix.option.mixin.perf.boost_worker_count": "1.16 only. Removes the hardcoded cap on worker thread count, similarly to what Mojang did in 1.18.",
|
||||
"modernfix.option.mixin.perf.cache_blockstate_cache_arrays": "All versions. Avoids creating fresh copies of enum arrays each time a blockstate cache is initialized. Minor optimization, but easy to do.",
|
||||
"modernfix.option.mixin.perf.cache_model_materials": "All versions. Memoizes the `RenderMaterial` (texture) collection and dependency list that models return instead of requiring them to be recalculated on each request. Helps accelerate the model load/bake process.",
|
||||
"modernfix.option.mixin.perf.cache_strongholds": "All versions. Saves the generated list of stronghold positions with the world, instead of regenerating it on every world load. Saves a little bit of time on 1.16, and quite a bit more on 1.18 and 1.19.",
|
||||
"modernfix.option.mixin.perf.cache_upgraded_structures": "All versions. Many mods ship outdated structure files, which requires the game to upgrade them using DFU every single time they are loaded. This can be quite slow. This patch adds logic to instead save the upgraded version of the structure, and reuse it on the next load. To handle the case that the mod changes a structure file but not the name, the original file's hash is compared against the cached version, and if they do not match the structure will be upgraded again.",
|
||||
"modernfix.option.mixin.perf.compress_biome_container": "1.16 only. Minor optimization borrowed from Hydrogen, which attempts to save space in the biome container when possible. This gets disabled automatically if conflicting mods like BetterEnd or Chocolate are installed.",
|
||||
"modernfix.option.mixin.perf.datapack_reload_exceptions": "All versions. Reduces log spam and possibly slightly improves loading speed by not printing the stacktraces for some commonly thrown exceptions during datapack reload (e.g. missing items in loot tables/recipes). The message is still printed.",
|
||||
"modernfix.option.mixin.perf.dedicated_reload_executor": "All versions. Moves resource pack and data pack reloading to a dedicated worker pool instead of using the default `Worker-Main` threads. This allows mods to Smooth Boot to still potentially improve singleplayer performance at runtime while not slowing down launch due to a limited thread count.",
|
||||
"modernfix.option.mixin.perf.deduplicate_location": "All versions, but disabled by default due to load time impact. Deduplicates resource location namespaces and paths. This saves RAM but also increases the cost of constructing a new `ResourceLocation` by quite a bit.",
|
||||
"modernfix.option.mixin.perf.dynamic_dfu": "All versions. Modifies DFU initialization to happen the first time something needs to be upgraded. This sounds similar to LazyDFU but is distinctly implemented, as it avoids loading *any* DFU classes/data structures, while LazyDFU only disables rule optimization. Essentially, this option is a safer version of DataFixerSlayer as it will still load DFU when needed.\n\nYou should typically continue to use LazyDFU even with this option enabled, as otherwise DFU rule optimization will cause lag.",
|
||||
"modernfix.option.mixin.perf.dynamic_resources": "All versions. See https://github.com/embeddedt/ModernFix/wiki/Dynamic-Resources-FAQ.",
|
||||
"modernfix.option.mixin.perf.dynamic_structure_manager": "All versions. Allows the game to unload structure files after generation concludes instead of keeping them loaded forever.",
|
||||
"modernfix.option.mixin.perf.fast_registry_validation": "All versions. Forge needlessly looks up a method via reflection every single time a registry is validated. This patch simply caches the returned value since it will be the same every time.",
|
||||
"modernfix.option.mixin.perf.faster_font_loading": "All versions. Optimizes the font renderer to load fonts faster, speeding up resource reload.",
|
||||
"modernfix.option.mixin.perf.faster_item_rendering": "All versions. Avoids rendering the sides of items in GUIs. (Yes, vanilla appears to do that.)\n\nThis can triple FPS with a mod like REI/JEI installed on weaker GPUs, if enough items are visible. Disabled by default since it's new and not tested much, but should be safe. The most likely issue is items being completely invisible in GUIs, or appearing flat in the world.",
|
||||
"modernfix.option.mixin.perf.faster_texture_loading": "All versions pre 1.19.4. Avoids reading textures twice (the first time using a very slow code path) and instead does one faster load (similar to 1.19.3+).",
|
||||
"modernfix.option.mixin.perf.faster_texture_stitching": "All versions. Enables the game to use a faster texture stitching system originally written by SuperCoder79 for lwjgl3ify on 1.7.10, which can save some time during loading. Rarely, it's been reported to cause weird artifacts on blocks or in GUIs, this may be a Sodium bug.",
|
||||
"modernfix.option.mixin.perf.jeresources_startup": "1.16 only. Optimizes Just Enough Resources to not needlessly recreate villager entities many times for the same profession, which saves time during JEI startup.",
|
||||
"modernfix.option.mixin.perf.kubejs": "1.16 only. Optimizations to KubeJS to avoid needless `ItemStack` copying, etc., which reduces the time taken to load datapacks.",
|
||||
"modernfix.option.mixin.perf.model_optimizations": "All versions. Implements optimizations to speed up the model loading process.",
|
||||
"modernfix.option.mixin.perf.nbt_memory_usage": "All versions. Uses a more efficient backing map for compound NBT tags that deduplicates key names and also uses an array map for very small compounds. This reduces the overhead of storing many compound tags in memory.",
|
||||
"modernfix.option.mixin.perf.nuke_empty_chunk_sections": "1.16 only, inspired by Hydrogen. Avoids storing chunk sections that are full of air in memory, instead marking them as empty.",
|
||||
"modernfix.option.mixin.perf.reduce_blockstate_cache_rebuilds": "All versions. **A key optimization.** Newer Minecraft versions (after 1.12) implemented a blockstate cache system that caches frequently used information about a blockstate, such as whether it is solid, its collision shape, etc. Rebuilding this cache is quite fast in vanilla (it takes only a second or two) but is quite slow with many mods installed, as there are many more blockstates present in the game which all must have their caches rebuilt.\n\nThis problem is exacerbated by Forge as the cache is rebuilt at many points when the data would almost certainly be unused before the next rebuild. Examples include right before reaching the main menu (during the \"Freezing data\" stage), as well as multiple (!) times when a world is being loaded.\n\nModernFix solves this performance bottleneck by instead making cache rebuilds lazy. Each blockstate rebuilds its cache the first time the data would be accessed. At any point when vanilla or Forge would attempt to rebuild caches for all blockstates, this is redirected to simply invalidate the cache on each blockstate instead.\n\nThis should not have any impact on TPS after startup concludes.",
|
||||
"modernfix.option.mixin.perf.remove_biome_temperature_cache": "All versions. Removes the biome temperature cache as Lithium does on modern versions.",
|
||||
"modernfix.option.mixin.perf.resourcepacks": "All versions. **A key optimization.** Launches on modern versions are heavily bottlenecked by filesystem access. Many requests are frequently made to resource packs to list resources or check if a given resource exists, and each of these result in a very slow file API call.\n\nModernFix completely eliminates most of the bottleneck here by simply caching a listing of all resources that exist within mod-provided and vanilla resourcepacks. The cache is rebuilt on resource reload (except for the vanilla resources, since they should never change while the game is running).\n\nThere are no known compatibility issues with this patch except for OptiFine (its CTM resources do not load correctly). However I do not recommend using OptiFine in any scenario, as it adds several minutes to launch time by itself and is not tested with ModernFix at all.",
|
||||
"modernfix.option.mixin.perf.reuse_datapacks": "1.16 only. Attempts to speed up switching between singleplayer worlds by skipping the datapack reload when possible. May cause compatibility issues with some mods, but is currently enabled by default.",
|
||||
"modernfix.option.mixin.perf.rewrite_registry": "All versions. **Currently semi-broken.** Aggressively replaces some internals of the Forge registry system with faster versions, however it currently causes freezes when loading some modpacks. Off by default for obvious reasons.",
|
||||
"modernfix.option.mixin.perf.skip_first_datapack_reload": "1.16 & 1.19 only. **A key optimization.**\n\nIn the middle of the 1.16 development cycle, Forge patched the game to reload datapacks twice when loading an existing world, in order to fix an issue with biome ID shifting. Unfortunately, datapack reloads often take upwards of 30 seconds and so this very severely affects world loading times.\n\nModernFix makes the necessary changes to avoid this reload, based on the unfinished Forge PR #8163.\n\nThis change was removed by Forge in 1.18, but then a similar patch was added *again* in 1.19 to fix mod datapacks not being loaded when creating new singleplayer worlds. Fortunately, the issue is localized to the world creation screen in 1.19, and existing worlds only require a single reload. However, this still doubles the length of the lag spike when clicking \"Create New World\" in 1.19, and so ModernFix again makes changes to not perform a redundant reload.",
|
||||
"modernfix.option.mixin.perf.state_definition_construct": "All versions. Enabled only if FerriteCore is installed. Takes advantage of FerriteCore's handling of blockstates to speed up their creation. This can help speed up launch with mods that add lots of blockstates, such as furniture mods.",
|
||||
"modernfix.option.mixin.perf.sync_executor_sleep": "All versions. Avoids having the main thread spin idly consuming one CPU core while waiting for the modloading workers to finish.",
|
||||
"modernfix.option.mixin.perf.thread_priorities": "All versions. Adjusts the worker and server thread priorities to be lower than the client thread. This helps improve FPS stability on machines with few CPU cores, provided that the Java implementation in use respects priorities.",
|
||||
"modernfix.option.mixin.perf.use_integrated_resources": "Mostly for 1.16. Patches JEResources to use the integrated server's loot table data if playing singleplayer, rather than pointlessly reloading loot tables. Saves a couple more seconds during JEI startup.",
|
||||
"modernfix.option.mixin.bugfix.concurrency": "The patches in this group fix concurrency-related issues within Minecraft and/or Forge. Most of them result in rare, hard-to-diagnose crashes during loading.",
|
||||
"modernfix.option.mixin.bugfix.edge_chunk_not_saved": "This option is a port of SuperCoder's Chunk Saving Fix mod (because I didn't realize it was already available for Forge at the time).",
|
||||
"modernfix.option.mixin.bugfix.mc218112": "This option fixes a deadlock that can occur if an exception is thrown while processing entity data. Vanilla does not correctly unlock the data manager when it should. This is tracked as MC-218112 on the bug tracker, and was fixed by Mojang in 1.17.",
|
||||
"modernfix.option.mixin.bugfix.packet_leak": "**Experimental**, not enabled by default. An attempted fix for the memory leak issue that occurs after playing long enough on 1.16.",
|
||||
"modernfix.option.mixin.bugfix.paper_chunk_patches": "1.18 and newer. **A key optimization.** Ports a patch from Paper that fixes issues on 1.17 with chunkloading requiring huge amounts of memory and generating many `CompletableFuture` instances. 1.18+ are now able to load worlds on only 400MB of memory like 1.16 could.",
|
||||
"modernfix.option.mixin.bugfix.tf_cme_on_load": "Patches Twilight Forest to perform non-thread-safe client setup using the main thread as it should, rather than the FML worker thread.",
|
||||
"modernfix.option.mixin.feature.branding": "Adds ModernFix to the branding list on the title screen, and also to the F3 screen.",
|
||||
"modernfix.option.mixin.feature.direct_stack_trace": "Normally off, can be enabled to force the raw stack trace to be dumped to the log when a crash occurs. Occasionally vanilla's crash report system fails to work properly and gives an entirely irrelevant stack trace/report.",
|
||||
"modernfix.option.mixin.feature.measure_time": "Uses a couple injections to measure world load time, datapack reload time, resource reload time, bootstrap time, and adds the necessary hooks to enable vanilla's unused profiler logic for resource reloading if so configured.",
|
||||
"modernfix.option.mixin.feature.spam_thread_dump": "**To be used for debugging purposes only.** Causes a thread dump to be output to the log every 60 seconds. This can help to diagnose unexplained freezes during loading/gameplay."
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
{
|
||||
"key.modernfix": "现代化修复",
|
||||
"key.modernfix.config": "打开配置屏幕",
|
||||
"modernfix.jei_load": "正在加载JEI,这可能会花费一段时间。",
|
||||
"modernfix.no_lazydfu": "未检测到DFU载入优化。如果Minecraft需要从旧版本更新游戏数据,可能会出现极大的延迟。",
|
||||
"modernfix.config": "现代化修复Mixin配置",
|
||||
"modernfix.config.done_restart": "完成(生效需重启)",
|
||||
"modernfix.option.on": "开启",
|
||||
"modernfix.option.off": "关闭",
|
||||
"modernfix.config.not_default": " (已修改)",
|
||||
"asynclocator.map.locating": "地图(定位中……)",
|
||||
"asynclocator.map.none": "地图(未能在附近找到相关地物)",
|
||||
"modernfix.option.category.performance": "性能",
|
||||
"modernfix.option.category.performance.description": "有助于提升游戏内性能或启动速度的功能",
|
||||
"modernfix.option.category.bugfixes": "错误修复",
|
||||
"modernfix.option.category.bugfixes.description": "修复重点错误,从而提升游戏稳定性",
|
||||
"modernfix.option.category.troubleshooting": "错误排查/实用工具",
|
||||
"modernfix.option.category.troubleshooting.description": "用于辅助错误排查的功能",
|
||||
"modernfix.option.category.expert_only": "高级设置",
|
||||
"modernfix.option.category.expert_only.description": "不要轻易修改,除非您清楚地知道自己正在做什么",
|
||||
"modernfix.option.name.mixin.perf.async_jei": "后台 JEI 加载",
|
||||
"modernfix.option.mixin.perf.async_jei": "仅 1.16。一项关键优化。对 JEI 进行了调整,让其重载运行于后台线程,完全消除了它在载入世界时造成的巨大延迟。",
|
||||
"modernfix.option.mixin.perf.biome_zoomer": "仅 1.16。使用 1.18 版本的逻辑进行微优化,提高生物群系过渡生成的性能。",
|
||||
"modernfix.option.mixin.perf.async_locator": "仅 1.16。移植了 Async Locator 模组的补丁,以消除与 `/locate`、战利品表生成等相关事物的服务器冻结现象。",
|
||||
"modernfix.option.mixin.perf.blast_search_trees": "全版本共有。如果安装了 REI 或者 JEI,原版创造模式物品栏的搜索功能构建将被禁用,取而代之地是这两种模组中的搜索实现。这在加载世界时会节省几秒钟,并可能(虽然 embeddedt 没有测试)会降低一些内存。",
|
||||
"modernfix.option.mixin.perf.boost_worker_count": "仅 1.16。移除被硬编码的工作线程数量上限,类似于 Mojang 在 1.18 中所做的。",
|
||||
"modernfix.option.mixin.perf.cache_blockstate_cache_arrays": "全版本共有。避免每次初始化方块状态缓存时对枚举数组进行的一次复制。次要的优化,但很容易做到。",
|
||||
"modernfix.option.mixin.perf.cache_model_materials": "全版本共有。记录`RenderMaterial`(纹理)集合和模型返回的依赖列表,而不是要求它们在每次请求中都重复计算一次,有助于加快模型加载/烘焙进程。",
|
||||
"modernfix.option.mixin.perf.cache_strongholds": "全版本共有。将生成的要塞位置列表与世界一起保存,而非再每次加载世界时都重新生成。在 1.16 版本中可以减少一点世界加载时间,在 1.18 和 1.19 中减少的会更多。",
|
||||
"modernfix.option.mixin.perf.cache_upgraded_structures": "全版本共有。许多模组的结构文件已经过时,这就要求游戏在每次加载它们时都要用 DFU 来升级它们。这个升级过程可能是相当慢的。此补丁添加了一些逻辑,保存结构的升级版本,并在下次加载时直接重新使用它们。为了处理模组改变了结构文件但没有改变名称的情况,原始文件的哈希值将与被缓存的版本进行比较,若不匹配,该结构将被再次升级。",
|
||||
"modernfix.option.mixin.perf.compress_biome_container": "仅 1.16。来自 Hydrogen 的微优化,会在可能时下试图节省生物群系容器的空间。如果安装了 BetterEnd 或 Chocolate Fix 这两个冲突的模组,此功能会被自动禁用。",
|
||||
"modernfix.option.mixin.perf.datapack_reload_exceptions": "全版本共有。通过不打印数据包重载过程中一些常见的异常(如战利品表/配方中的物品丢失)的堆栈跟踪,减少日志刷屏,稍微提高了启动速度。只是不打印堆栈,一条异常消息依然会被打印出来。",
|
||||
"modernfix.option.mixin.perf.dedicated_reload_executor": "全版本共有。将资源包和数据包的重载给转移到一个专门的工作线程,而不是使用默认的工作主线程。这让 Smooth Boot 模组在运行时仍然可能提高单人游戏的性能,同时不会因为线程数量限制减慢启动速度。",
|
||||
"modernfix.option.mixin.perf.deduplicate_location": "全版本共有,但由于会影响加载时间,默认禁用。重复使用资源位置的命名空间和路径。这节省了内存,但也稍稍增加了构建新资源位置的成本。",
|
||||
"modernfix.option.mixin.perf.dynamic_dfu": "全版本共有。修改了 DFU 的初始化,使其仅在第一次需要升级数据时加载。这听起来跟 DFU 载入优化相似,但实际的实现其实是截然不同的,因为它能避免加载*任何*DFU的类/数据结构,而 DFU 载入优化只是禁用其规则优化。本质上说,这是一个 DataFixerSlayer 的安全版本,因为它仍然会在需要时加载 DFU。\n\n即使启用了这个选项,你也应该继续使用 DFU 载入优化,否则 DFU 规则的优化依然会导致延迟。",
|
||||
"modernfix.option.mixin.perf.dynamic_structure_manager": "全版本共有。允许游戏在生成结束后停载结构文件,而非永远保持它们的加载。",
|
||||
"modernfix.option.mixin.perf.fast_registry_validation": "全版本共有。Forge 在每次验证注册时都通过反射来查找一个方法,这个补丁简单地缓存了方法的返回值,因为它每次都是一样的。",
|
||||
"modernfix.option.mixin.perf.faster_font_loading": "全版本共有。优化字体渲染器,以更快地加载字体,加快资源重载速度。",
|
||||
"modernfix.option.mixin.perf.faster_item_rendering": "全版本共有。避免在 GUI 中渲染物品的侧面。(是的,原版就是这样做的。)\n\n如果你的 GPU 不是很好,又安装了 REI/JEI 这样的模组,当在 GUI 中有足够多的物品显示时,这可以使 FPS 增加三倍。由于此优化刚刚诞生,没有经过很多测试,所以默认情况下是禁用的,但应是能安全使用的。最有可能的问题是物品在 GUI 中完全不可见,或者在世界中显示为平面。",
|
||||
"modernfix.option.mixin.perf.faster_texture_loading": "所有 1.19.4 之前的版本。避免读取两次纹理资源(第一次读取使用的是非常慢的文件路径),使用更快的第二次读取逻辑(类似于 1.19.3+)。",
|
||||
"modernfix.option.mixin.perf.faster_texture_stitching": "全版本共有。使游戏能够使用一个效率更高的纹理缝合系统,该系统最初由 SuperCoder79 在 1.7.0 上为 lwjgl3ify 编写,可以减少一点加载时间。有时候,有人报告说这会在方块或 GUI 上引起奇怪的显示假象,但这可能是 Sodium 的 bug。",
|
||||
"modernfix.option.mixin.perf.jeresources_startup": "仅 1.16。优化了 Just Enough Resources,让其不需要为同一职业多次创建村民实体,这在 JEI 启动时可以节省时间。",
|
||||
"modernfix.option.mixin.perf.kubejs": "仅 1.16。对 KubeJS 进行了优化以避免不必要的 ItemStack 复制等,减少了数据包加载时间。",
|
||||
"modernfix.option.mixin.perf.model_optimizations": "全版本共有。通过优化以加快模型加载过程。",
|
||||
"modernfix.option.mixin.perf.nbt_memory_usage": "全版本共有。对 NBT 标签使用一个更加高效的 backing map,重复使用键名,对小型 NBT 也使用数组 Map。这减少了在内存中存储许多 NBT 标签的开销。",
|
||||
"modernfix.option.mixin.perf.nuke_empty_chunk_sections": "仅 1.16。灵感来自 Hydrogen。避免在内存中存储充满空气的区块部分,而是将其标记为空。",
|
||||
"modernfix.option.mixin.perf.reduce_blockstate_cache_rebuilds": "全版本共有。一项关键优化。较新的 MC(1.12 以上)实现了一个方块状态缓存系统,它可以缓存方块状态的常用信息,譬如其是否为实体,它的碰撞箱形状等等。重建此缓存在原版中是相当快速的(只要一两秒钟),但在安装了许多模组后就相当慢了,因为游戏中多了很多其它的方块状态,它们的缓存都需要被其重建。\n\n在 Forge 的影响下,这个问题变得更加严重,因为缓存虽在很多地方被重建,但这些数据在下一次重建前几乎肯定不会被用到。这种情况一般出现在到达主菜单之前(在“Freezing data”阶段),以及在加载世界时(会出现很多次!)。\n\n通过让缓存的重建变得懒惰,现代化修复解决了这个性能瓶颈问题。每个方块状态在第一次访问数据时都会重建其缓存。原版或 Forge 试图重建所有方块状态缓存时的行为会被重定向为简单地使每个方块状态的缓存失效。\n\n这应不会对启动后的 TPS 产生任何影响。",
|
||||
"modernfix.option.mixin.perf.remove_biome_temperature_cache": "全版本共有。移除生物群系温度缓存以减少内存占用,就像新版本的锂所做的那样。",
|
||||
"modernfix.option.mixin.perf.resourcepacks": "全版本共有。一项关键优化。高版本的启动受到了文件系统访问的严重限制。资源包经常接受到很多请求,要列出资源或检查一项给定的资源是否存在,而每次请求都会调用一个效率极低的文件 API。\n\n现代化修复通过简单地缓存模组和原版提供的所有资源的列表,完全消除了此处的大部分限制。缓存在资源重载时会进行重建(除了原版资源,因为它们在游戏运行时不应改变)。\n\n除了高清修复(它的连接纹理资源不能正确加载),此补丁没有已知的兼容问题。但 embeddedt 不建议在任何情况下使用高清修复,因为它本身就会增加几分钟的启动时间,而且根本没有与现代化修复进行过任何测试。",
|
||||
"modernfix.option.mixin.perf.reuse_datapacks": "仅 1.16。试图通过在可能的情况下跳过数据包重载来加快单人世界切换/重进时的速度。可能会导致一些模组的兼容性问题,但目前是默认启用的。",
|
||||
"modernfix.option.mixin.perf.rewrite_registry": "全版本共有。**目前处于半崩溃状态。**非常暴力地使用更快地版本替换了 Forge 注册系统的一些内部结构,但目前在加载一些整合包时导致冻结。显而易见,默认关闭。",
|
||||
"modernfix.option.mixin.perf.skip_first_datapack_reload": "仅 1.16 & 1.19。一项关键优化。\n\n在 1.16 开发周期的中期, Forge 对游戏打了补丁,在加载现有世界时重载两次数据包,来解决生物群系 ID 转移的问题。不幸地是,数据包重载往往需要 30 秒以上的时间,所以这非常严重地影响了世界加载时间。\n\n现代化修复在未完成的 Forge PR #8163 基础上做了必要的修改来避免这种重载。\n\n此改动在 1.18 版本被 Forge 删除,但随后又在 1.19 版本中添加了一个类似补丁,来修复创建新单人世界时无法加载模组数据包的问题。幸运的是,此问题在 1.19 版本中被定为到了世界创建界面,现有的世界只需要重载一次。然而,这依然使得 1.19 版本中点击“创建新世界”时的延迟峰值长度增加了一倍,因此现代化修复再次做出修改,不执行多余的重载。",
|
||||
"modernfix.option.mixin.perf.state_definition_construct": "全版本共有。只在安装 FerriteCore 时启用。利用 FerriteCore 对 BlockState 的处理,加快 BlockState 的创建速度。这有助于在你安装了有大量 BlockState 的模组(例如家具模组)时加快启动速度。",
|
||||
"modernfix.option.mixin.perf.sync_executor_sleep": "全版本共有。避免了主线程在等待模组加载工作完成时空转,消耗一个 CPU 核心。",
|
||||
"modernfix.option.mixin.perf.thread_priorities": "全版本共有。调整工作线程和服务端线程的优先级,使其低于客户端线程。这有助于提供 CPU 核心少的机器游戏时的 FPS 稳定性,只要其使用的 Java 实现尊重优先级。",
|
||||
"modernfix.option.mixin.perf.use_integrated_resources": "主要针对 1.16 版本。为 Just Enough Resources 打补丁,以便于在玩单人游戏时使用集成服务器的战利品表数据,而不是无意义地重载战利品表。可以减少几秒钟的 JEI 启动时间。",
|
||||
"modernfix.option.mixin.bugfix.concurrency": "此补丁修复了 MC 和/或 Forge 中的一些并发问题。其中大部分问题都会导致在加载过程中出现罕见的、难以诊断的崩溃。",
|
||||
"modernfix.option.mixin.bugfix.edge_chunk_not_saved": "此项是 SuperCoder 的 Chunk Saving Fix 模组的移植(因为 embeddedt 当时并没有意识到 Chunk Saving Fix 原来有 Forge 版本)。",
|
||||
"modernfix.option.mixin.bugfix.mc218112": "此项修复了在处理实体数据抛出异常时可能的死锁问题。原版在应该解锁数据管理器时不能正常解锁。问题追踪器链接:MC-218112,由 Mojang 在 1.17 中修复。",
|
||||
"modernfix.option.mixin.bugfix.packet_leak": "实现性的,默认禁用。尝试修复在 1.16 版本上长时间游玩后出现的内存泄漏问题。",
|
||||
"modernfix.option.mixin.bugfix.paper_chunk_patches": "仅 1.18+。**一项关键优化**。移植了 Paper 的一个补丁,修复了 1.17 版本中区块加载需要大量内存和产生许多 `CompletableFuture` 实例的问题。1.18 以上版本现在能够像 1.16 版本那样在只有 400MB 的内存中加载世界。",
|
||||
"modernfix.option.mixin.bugfix.tf_cme_on_load": "对暮色森林进行了调整,使用主线程执行非线程安全的客户端设置,而非 FML 工作线程。",
|
||||
"modernfix.option.mixin.feature.branding": "将现代化修复相关信息添加到标题页和 F3 屏幕中。",
|
||||
"modernfix.option.mixin.feature.direct_stack_trace": "通常是关闭的,当崩溃发生时,可以启用它来强制将原始堆栈跟踪转储到日志里。因为原版的崩溃报告系统偶尔不能正常工作,只能给出一个完全不相关的堆栈跟踪/报告。",
|
||||
"modernfix.option.mixin.feature.measure_time": "若启用,现代化修复会借助几个注入来记录世界加载时间、数据包重载时间、资源重载时间、启动时间,并添加必要的挂钩以启用原版未使用的分析器逻辑来进行资源重载。",
|
||||
"modernfix.option.mixin.feature.spam_thread_dump": "**仅用于调试**。每 60 秒向日志输出一个线程数据导出。这可以帮助诊断加载/游戏过程中无法解释的冻结现象。"
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"key.modernfix": "ModernFix",
|
||||
"key.modernfix.config": "開啟設定介面",
|
||||
"modernfix.jei_load": "正在載入 JEI,這可能需要一點時間",
|
||||
"modernfix.no_lazydfu": "沒有安裝 LazyDFU。如果 Minecraft 需要從舊版本更新遊戲資料,可能會出現明顯的延遲。",
|
||||
"modernfix.config": "ModernFix 注入設定",
|
||||
"modernfix.config.done_restart": "完成(需要重新啟動遊戲)",
|
||||
"modernfix.option.on": "開啟",
|
||||
"modernfix.option.off": "關閉",
|
||||
"modernfix.config.not_default": "(已修改)",
|
||||
"asynclocator.map.locating": "地圖(定位中...)",
|
||||
"asynclocator.map.none": "地圖(未找到附近的特徵)"
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user