Compare commits
1727 Commits
| 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 | ||
|
|
ef47883262 | ||
|
|
dfdbf8544a | ||
|
|
7e25e31d33 | ||
|
|
a1d56e2921 | ||
|
|
a34ac84e00 | ||
|
|
e248e2966a | ||
|
|
49b31c347b | ||
|
|
f040f66bf9 | ||
|
|
6bc2b5b36e | ||
|
|
540b19c141 | ||
|
|
aa493c41d5 | ||
|
|
637f844601 | ||
|
|
1815554070 | ||
|
|
5f255a83dc | ||
|
|
05e66b2492 | ||
|
|
ae115c4908 | ||
|
|
28c456e4c5 | ||
|
|
2d12c6e282 | ||
|
|
d987c1227b | ||
|
|
7d8dd5ab09 | ||
|
|
adfbc7508e | ||
|
|
417eaa544f | ||
|
|
a502a509ed | ||
|
|
bb0c4fdf54 | ||
|
|
1a7909aaa4 | ||
|
|
0fea79a647 | ||
|
|
ed2d01df4d | ||
|
|
2e31a4e2c1 | ||
|
|
352f8bb6d1 | ||
|
|
e56898fff4 | ||
|
|
7671066548 | ||
|
|
87cd49a291 | ||
|
|
8fd31f3190 | ||
|
|
f3a2ca73e8 | ||
|
|
f27d6e154c | ||
|
|
60d06c92f6 | ||
|
|
f1cd8bac20 | ||
|
|
6020ff7ee1 | ||
|
|
b99e253715 | ||
|
|
1c0fca8f06 | ||
|
|
003cad94a0 | ||
|
|
98663fa416 | ||
|
|
cb55bd3612 | ||
|
|
2ec8a2719e | ||
|
|
79219d286f | ||
|
|
e65727937c | ||
|
|
a1f3cb9270 | ||
|
|
3b85b20020 | ||
|
|
36a1b6bb93 | ||
|
|
29f7badbfb | ||
|
|
8f896f065c | ||
|
|
45ada33d7d | ||
|
|
5e7421ab24 | ||
|
|
8325a8ba02 | ||
|
|
9b93a28ddb | ||
|
|
ad798850c6 | ||
|
|
8bc0946977 | ||
|
|
92264ed37e | ||
|
|
9ce029b35d | ||
|
|
127f091728 | ||
|
|
16d317af97 | ||
|
|
1b6880ed9f | ||
|
|
db4f6738ee | ||
|
|
ff39e9022b | ||
|
|
03b2395782 | ||
|
|
e023829310 | ||
|
|
582f17c0e7 | ||
|
|
454256d455 | ||
|
|
6300dd6737 | ||
|
|
5ca9485f0b | ||
|
|
ce8c6e0283 | ||
|
|
50e558644a | ||
|
|
1592e1c0ce | ||
|
|
d155dacadc | ||
|
|
c6cb0acd3c | ||
|
|
ba7bda0b04 | ||
|
|
c7787d1390 | ||
|
|
a7d0d4892f | ||
|
|
463f1c540e | ||
|
|
079c348948 | ||
|
|
c50388a358 | ||
|
|
b637d79c50 | ||
|
|
5a6ab4f781 | ||
|
|
2a16229056 | ||
|
|
7652a1dbc5 | ||
|
|
6d4071f42c | ||
|
|
6cd49334d7 | ||
|
|
095b9c3277 | ||
|
|
5aeea48413 | ||
|
|
7632647e1d | ||
|
|
41a56fba20 | ||
|
|
1600dc4f84 | ||
|
|
46cff62796 | ||
|
|
0a2601257c | ||
|
|
c73cb8115e | ||
|
|
ce6ce1d341 | ||
|
|
18f78b9624 | ||
|
|
c33fe2e928 | ||
|
|
cac4ca96d5 | ||
|
|
cc89986a94 | ||
|
|
5a0c286ff4 | ||
|
|
dd4a72a6f9 | ||
|
|
3237de7e33 | ||
|
|
37d6f477c4 | ||
|
|
0f90f53f53 | ||
|
|
9d5953c9f5 | ||
|
|
3541019ee0 | ||
|
|
ace3975156 | ||
|
|
6f07cbdc70 | ||
|
|
dc8d727bdb | ||
|
|
90adb1c627 | ||
|
|
3be36f826c | ||
|
|
dc268285b0 | ||
|
|
7464cf7420 | ||
|
|
30c9956acd | ||
|
|
7cc579fd61 | ||
|
|
709ba2d4f7 | ||
|
|
25df7fd8c7 | ||
|
|
73a0c4f400 | ||
|
|
a6cce0c043 | ||
|
|
152e795c32 | ||
|
|
89a78bf43b | ||
|
|
1e9e310922 | ||
|
|
8bb5e464bc | ||
|
|
840cad23b8 | ||
|
|
93817c5d3a | ||
|
|
470f8aed5b | ||
|
|
e9ee866c60 | ||
|
|
4f711a4995 | ||
|
|
463bd4ef51 | ||
|
|
c8192ef7e3 | ||
|
|
6ed3c5d365 | ||
|
|
c53ed4650d | ||
|
|
9b31616585 | ||
|
|
ca60a13bb4 | ||
|
|
96a83533dd | ||
|
|
99521e4d76 | ||
|
|
299abeb25d | ||
|
|
454dacea6b | ||
|
|
922d0f26e1 | ||
|
|
54f55ee257 | ||
|
|
84b08ef64a | ||
|
|
bd3a9eb345 | ||
|
|
a937ccb07c | ||
|
|
58eaf44d89 | ||
|
|
7a2d38f2f1 | ||
|
|
370a0fad11 | ||
|
|
ec49868d27 | ||
|
|
25d1956da0 | ||
|
|
e3944d7879 | ||
|
|
af486998d2 | ||
|
|
5229643761 | ||
|
|
4fd4de7d52 | ||
|
|
8e467e8239 | ||
|
|
04c6ea2e66 | ||
|
|
b6182b4913 | ||
|
|
ce92fedf09 | ||
|
|
9eff3e1aa9 | ||
|
|
a01cc836c1 | ||
|
|
012ed17208 | ||
|
|
4943702061 | ||
|
|
33c0cf33d9 | ||
|
|
60525ad594 | ||
|
|
2bad1c0226 | ||
|
|
65506ba7e1 | ||
|
|
c93380dcf2 | ||
|
|
7849ba16f5 | ||
|
|
b611830b7e | ||
|
|
b755d745c5 | ||
|
|
c23a4be440 | ||
|
|
f89b5d0a34 | ||
|
|
2ffb31f00d | ||
|
|
a6e2fc94b4 | ||
|
|
bac13236e7 | ||
|
|
99acb17d90 | ||
|
|
565dfb4b81 | ||
|
|
24f31dd92a | ||
|
|
63bcdb1019 | ||
|
|
5b2e70cda0 | ||
|
|
d97b18e633 | ||
|
|
6678864555 | ||
|
|
20ca15f0c4 | ||
|
|
1f589901d6 | ||
|
|
1afdfccf3b | ||
|
|
5bd745a188 | ||
|
|
1ac69035ed | ||
|
|
19c6687745 | ||
|
|
2fe5c088e3 | ||
|
|
fa76a4e2be | ||
|
|
9bc6b7b0f7 | ||
|
|
49bc941dd7 | ||
|
|
4037124c57 | ||
|
|
fc908bb92c | ||
|
|
c1c0010126 | ||
|
|
9cdeeb4c94 | ||
|
|
a84fa8ac0c | ||
|
|
6a7d62b38d | ||
|
|
40be7c9fc1 | ||
|
|
5d6566512c | ||
|
|
c1acdf1bb4 | ||
|
|
41b71c5e59 | ||
|
|
1bf64f9aa1 | ||
|
|
f77221d6d8 | ||
|
|
6006026deb | ||
|
|
00b6261a1c | ||
|
|
ce9d5f78e2 | ||
|
|
e75aca9236 | ||
|
|
aa3fa93a9f | ||
|
|
683f9fd13b | ||
|
|
5d2310bda4 | ||
|
|
18e1cec611 | ||
|
|
4d47ca26db | ||
|
|
42d41eb0f7 | ||
|
|
fea1d35d08 | ||
|
|
2414f87a86 | ||
|
|
15ed114e87 | ||
|
|
f7037e3ecb | ||
|
|
a72ebc4c26 | ||
|
|
75bd0a5ff7 | ||
|
|
c3a5964f17 | ||
|
|
9bd8870ef9 | ||
|
|
cb6f595cc3 | ||
|
|
f7d66257fb | ||
|
|
9aa96b4d03 | ||
|
|
0765b6770f | ||
|
|
761417833e | ||
|
|
e5ab4df3dc | ||
|
|
12345a67e8 | ||
|
|
440cc21d8c | ||
|
|
d242431e9a | ||
|
|
173d71b04f | ||
|
|
6df837f9ba | ||
|
|
eea53ee9ef | ||
|
|
7fb65f5144 | ||
|
|
916710eb77 | ||
|
|
d352c9e8ca | ||
|
|
e5cc8ef97c | ||
|
|
af63a6ca09 | ||
|
|
a13e8d98e1 | ||
|
|
5136889c3e | ||
|
|
9c76052be0 | ||
|
|
fcd6663835 | ||
|
|
cfd3920c8c | ||
|
|
a7e5147104 | ||
|
|
017d81a8c6 | ||
|
|
77436644aa | ||
|
|
31ba30d101 | ||
|
|
b0ab187da8 | ||
|
|
cc6191cef2 | ||
|
|
9aa28c3e3f | ||
|
|
055d27a9f6 | ||
|
|
140e19dffe | ||
|
|
74a3e4ed2b | ||
|
|
002ff639a0 | ||
|
|
826694793c | ||
|
|
4699b9be7a | ||
|
|
ec9ce77ace | ||
|
|
99e030ce27 | ||
|
|
22c52171d9 | ||
|
|
cff1363050 | ||
|
|
02a68f7689 | ||
|
|
a2d82472a4 | ||
|
|
48924e3364 | ||
|
|
79c4b1cb27 | ||
|
|
b1bfcfb190 | ||
|
|
7ab2dac645 | ||
|
|
702fe0ba24 | ||
|
|
755bc35d09 | ||
|
|
a7e93ad8f9 | ||
|
|
57e1e54441 | ||
|
|
7bbc10acc3 | ||
|
|
690e1b7f8a | ||
|
|
ca4f2a2aeb | ||
|
|
531b5d58ba | ||
|
|
aedbc88e3c | ||
|
|
f9cbd8fa61 | ||
|
|
a614c050e7 | ||
|
|
931bf29c47 | ||
|
|
313981f30e | ||
|
|
b4470eb520 | ||
|
|
5de87576ca | ||
|
|
4d54c624e1 | ||
|
|
489a30d185 | ||
|
|
3996804e71 | ||
|
|
b430b67052 | ||
|
|
54759008e5 | ||
|
|
41c2bb733e | ||
|
|
673e6dec8d | ||
|
|
f8771a260c | ||
|
|
39531aba0e | ||
|
|
941331f90a | ||
|
|
a635671276 | ||
|
|
144bdaa301 | ||
|
|
444f5ad446 | ||
|
|
32aa4794d0 | ||
|
|
db223b62a0 | ||
|
|
618698aab3 | ||
|
|
b610a21b37 | ||
|
|
39e1ac0896 | ||
|
|
945027295a | ||
|
|
c8bce3e015 | ||
|
|
4b73d699e4 | ||
|
|
1a7d5b3436 | ||
|
|
b15537a32f | ||
|
|
830eb19420 | ||
|
|
76485b0cba | ||
|
|
cb22591182 | ||
|
|
8c061ec070 | ||
|
|
4a9cd78d1d | ||
|
|
0f4e835f6c | ||
|
|
39939076e7 | ||
|
|
3dbff477e9 | ||
|
|
d51de3fa55 | ||
|
|
f1811bbbc6 | ||
|
|
a7d894cbc2 | ||
|
|
04f34a00fa | ||
|
|
d89f8da5a2 | ||
|
|
b8850b2aae | ||
|
|
f4b2db89ec | ||
|
|
5a09f27c6a | ||
|
|
c4a50bb6e2 | ||
|
|
b5d62b4bbb | ||
|
|
214b39ce8f | ||
|
|
a327b460bb | ||
|
|
ddf2fc5a74 | ||
|
|
74eb8a0619 | ||
|
|
2bd2fd7ef7 | ||
|
|
baca029fd0 | ||
|
|
0f180ff979 | ||
|
|
86e748dd93 | ||
|
|
be4c8607d4 | ||
|
|
bd94b5dd2e | ||
|
|
31feef398f | ||
|
|
49a8f72151 | ||
|
|
675e3bb08b | ||
|
|
aae4db7ebf | ||
|
|
80617c24f1 | ||
|
|
61eca643d4 | ||
|
|
b6a0767d03 | ||
|
|
91c2d50d97 | ||
|
|
d428f9c4c0 | ||
|
|
09b4021e23 | ||
|
|
cfd542623c | ||
|
|
f48ed1212b | ||
|
|
117febba6d | ||
|
|
fc65975252 | ||
|
|
7517c36b36 | ||
|
|
0a035b6e0d | ||
|
|
a22308fb89 | ||
|
|
98da673cbe | ||
|
|
c2f09fb998 | ||
|
|
bb6d498d58 | ||
|
|
0d901c6281 | ||
|
|
d5bd392cff | ||
|
|
962b843199 | ||
|
|
adbf61a06b | ||
|
|
b44739e33c | ||
|
|
30bf3afc1d | ||
|
|
a7e64f2947 | ||
|
|
b4ffe68adb | ||
|
|
57947441ce | ||
|
|
f1515c5063 |
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
|
||||||
139
.github/workflows/gradle.yml
vendored
139
.github/workflows/gradle.yml
vendored
|
|
@ -1,27 +1,136 @@
|
||||||
# This workflow will build a Java project with Gradle
|
name: Build ModernFix using Gradle
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
|
||||||
|
|
||||||
name: Build mod
|
on:
|
||||||
|
push:
|
||||||
on: [push, pull_request]
|
branches:
|
||||||
|
- '**'
|
||||||
|
tags-ignore:
|
||||||
|
- '**'
|
||||||
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
concurrency:
|
||||||
|
group: release-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-java@v3
|
- name: Set up JDK 21
|
||||||
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '17'
|
java-version: 21
|
||||||
cache: 'gradle'
|
check-latest: true
|
||||||
- name: Grant execute permission for gradlew
|
- name: Check if release branch
|
||||||
run: chmod +x gradlew
|
id: check_branch
|
||||||
- name: Build the mod
|
if: github.event_name == 'push'
|
||||||
run: ./gradlew --no-daemon build
|
run: |
|
||||||
- uses: actions/upload-artifact@v2
|
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:
|
with:
|
||||||
name: Package
|
name: Package
|
||||||
path: bin
|
path: bin
|
||||||
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 }}
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -2,8 +2,12 @@ eclipse
|
||||||
run
|
run
|
||||||
libs
|
libs
|
||||||
media
|
media
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
classes/
|
classes/
|
||||||
.architectury-transformer/
|
.architectury-transformer/
|
||||||
|
fabric/fabricloader.log
|
||||||
|
fabric/test_run
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
CHANGELOG.md
|
CHANGELOG.md
|
||||||
|
|
|
||||||
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.
|
||||||
|
|
@ -6,10 +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.
|
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)
|
## Development builds (generally stable, but may occasionally have bugs)
|
||||||
- 1.16.5, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/main/Package.zip
|
- 1.16.5: 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.18.2: 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.2: 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: 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;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.CLASS)
|
@Retention(RetentionPolicy.CLASS)
|
||||||
@Target(ElementType.TYPE)
|
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
||||||
public @interface RequiresMod {
|
public @interface RequiresMod {
|
||||||
String value() default "";
|
String value() default "";
|
||||||
}
|
}
|
||||||
175
build.gradle
175
build.gradle
|
|
@ -1,175 +0,0 @@
|
||||||
plugins {
|
|
||||||
id "architectury-plugin" version "3.4-SNAPSHOT"
|
|
||||||
id "dev.architectury.loom" version "1.1-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 '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-mc' + minecraft_version
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
apply plugin: "java"
|
|
||||||
apply plugin: "architectury-plugin"
|
|
||||||
apply plugin: "maven-publish"
|
|
||||||
apply plugin: "com.diffplug.spotless"
|
|
||||||
|
|
||||||
spotless {
|
|
||||||
java {
|
|
||||||
removeUnusedImports()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
group = 'org.embeddedt'
|
|
||||||
version = gitVersion()
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exclusiveContent {
|
|
||||||
forRepository {
|
|
||||||
maven {
|
|
||||||
name = "Modrinth"
|
|
||||||
url = "https://api.modrinth.com/maven"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
filter {
|
|
||||||
includeGroup "maven.modrinth"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
subprojects {
|
|
||||||
apply plugin: "dev.architectury.loom"
|
|
||||||
|
|
||||||
loom {
|
|
||||||
silentMojangMappingsLicense()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
|
||||||
mappings loom.officialMojangMappings()
|
|
||||||
}
|
|
||||||
|
|
||||||
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.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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,46 +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
|
|
||||||
}
|
|
||||||
// compile against the JEI API but do not include it at runtime
|
|
||||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-common:${jei_version}")
|
|
||||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-gui:${jei_version}")
|
|
||||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-lib:${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,187 +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.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.world.IntegratedWatchdog;
|
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public ModernFixClient() {
|
|
||||||
// clear reserve as it's not needed
|
|
||||||
MemoryReserve.release();
|
|
||||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
|
|
||||||
brandingString = "ModernFix " + ModernFixPlatformHooks.getVersionString();
|
|
||||||
}
|
|
||||||
SearchTreeProviderRegistry.register(JEIBackedSearchTree.PROVIDER);
|
|
||||||
SearchTreeProviderRegistry.register(REIBackedSearchTree.PROVIDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
ModernFix.LOGGER.warn("Game took " + gameStartTimeSeconds + " seconds to start");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
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.registries.Registries;
|
|
||||||
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(Registries.BIOME).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,33 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.bugfix.edge_chunk_not_saved;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
|
||||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
|
||||||
import net.minecraft.world.level.chunk.ImposterProtoChunk;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ChunkMap;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
/* https://github.com/SuperCoder7979/chunksavingfix-fabric/blob/main/src/main/java/supercoder79/chunksavingfix/mixin/MixinThreadedAnvilChunkStorage.java */
|
|
||||||
@Mixin(ChunkMap.class)
|
|
||||||
public class ChunkManagerMixin {
|
|
||||||
// TODO: hits both at the moment- check and re-evaluate
|
|
||||||
@ModifyArg(method = "saveAllChunks(Z)V", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;", ordinal = 0), require = 0)
|
|
||||||
private Predicate<ChunkHolder> alwaysAccessibleFlush(Predicate<ChunkHolder> chunkHolder) {
|
|
||||||
return c -> true;
|
|
||||||
}
|
|
||||||
@ModifyArg(method = "saveAllChunks(Z)V", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;filter(Ljava/util/function/Predicate;)Ljava/util/stream/Stream;", ordinal = 1), require = 0)
|
|
||||||
private Predicate<ChunkAccess> allowProtoChunkFlush(Predicate<ChunkAccess> chunk) {
|
|
||||||
return c -> c instanceof ProtoChunk || c instanceof ImposterProtoChunk || c instanceof LevelChunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Redirect(method = "saveChunkIfNeeded", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ChunkHolder;wasAccessibleSinceLastSave()Z"), require = 0)
|
|
||||||
private boolean alwaysAccessible(ChunkHolder chunkHolder) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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,113 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.bugfix.paper_chunk_patches;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Either;
|
|
||||||
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.StructureTemplateManager;
|
|
||||||
import org.embeddedt.modernfix.duck.IPaperChunkHolder;
|
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
|
||||||
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 ThreadedLevelLightEngine lightEngine;
|
|
||||||
@Shadow @Final private ChunkProgressListener progressListener;
|
|
||||||
|
|
||||||
@Shadow protected abstract CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> scheduleChunkGeneration(ChunkHolder chunkHolder, ChunkStatus chunkStatus);
|
|
||||||
|
|
||||||
@Shadow @Final private StructureTemplateManager structureTemplateManager;
|
|
||||||
private Executor mainInvokingExecutor;
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("RETURN"))
|
|
||||||
private void setup(CallbackInfo ci) {
|
|
||||||
this.mainInvokingExecutor = (runnable) -> {
|
|
||||||
if(ModernFixPlatformHooks.getCurrentServer().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.structureTemplateManager, 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,17 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.devenv;
|
|
||||||
|
|
||||||
import com.mojang.text2speech.Narrator;
|
|
||||||
import net.minecraft.client.GameNarrator;
|
|
||||||
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(GameNarrator.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 Narrator.EMPTY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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,33 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.feature.measure_time;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
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(Minecraft.class)
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public class MinecraftMixin {
|
|
||||||
/* not supported in 1.19
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
@Inject(method = "doWorldLoad", at = @At("HEAD"))
|
|
||||||
private void recordWorldLoadStart(CallbackInfo ci) {
|
|
||||||
ModernFixClient.worldLoadStartTime = System.nanoTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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,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,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.ChunkGeneratorStructureState;
|
|
||||||
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(ChunkGeneratorStructureState.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> structureSet, 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> structureSet, 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,69 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.cache_strongholds;
|
|
||||||
|
|
||||||
import net.minecraft.core.Holder;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
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.ChunkGeneratorStructureState;
|
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
|
||||||
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.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 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, RegistryAccess arg3, Holder<DimensionType> arg4, Supplier<ProfilerFiller> supplier, boolean bl, boolean bl2, long l, int i) {
|
|
||||||
super(arg, arg2, arg3, arg4, supplier, bl, bl2, l, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Shadow public abstract DimensionDataStorage getDataStorage();
|
|
||||||
|
|
||||||
@Shadow @Final private ServerChunkCache chunkSource;
|
|
||||||
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/ChunkGeneratorStructureState;ensureStructuresGenerated()V"))
|
|
||||||
private void hookStrongholdCache(ChunkGeneratorStructureState 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, LevelStem arg4, ChunkProgressListener arg5, boolean bl, long l, List<CustomSpawner> list, boolean bl2, CallbackInfo ci) {
|
|
||||||
mfix$strongholdCache = this.getDataStorage().computeIfAbsent(StrongholdLocationCache::load,
|
|
||||||
StrongholdLocationCache::new,
|
|
||||||
StrongholdLocationCache.getFileId(this.dimensionTypeRegistration()));
|
|
||||||
this.chunkSource.getGeneratorState().ensureStructuresGenerated();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public StrongholdLocationCache mfix$getStrongholdCache() {
|
|
||||||
return mfix$strongholdCache;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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,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,111 +0,0 @@
|
||||||
package org.embeddedt.modernfix.common.mixin.perf.faster_item_rendering;
|
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
|
||||||
import net.minecraft.client.color.item.ItemColors;
|
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
|
||||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
|
||||||
import net.minecraft.client.renderer.block.model.ItemTransform;
|
|
||||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.client.resources.model.SimpleBakedModel;
|
|
||||||
import net.minecraft.core.Direction;
|
|
||||||
import net.minecraft.util.RandomSource;
|
|
||||||
import net.minecraft.world.item.ItemDisplayContext;
|
|
||||||
import net.minecraft.world.item.BlockItem;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.embeddedt.modernfix.render.FastItemRenderType;
|
|
||||||
import org.embeddedt.modernfix.render.RenderState;
|
|
||||||
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.CallbackInfo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Mixin(ItemRenderer.class)
|
|
||||||
public abstract class ItemRendererMixin {
|
|
||||||
@Shadow @Final private ItemColors itemColors;
|
|
||||||
|
|
||||||
private final RandomSource dummyRandom = RandomSource.createNewThreadLocalInstance();
|
|
||||||
|
|
||||||
private static final float[] COLOR_MULTIPLIER = new float[]{1.0F, 1.0F, 1.0F, 1.0F};
|
|
||||||
|
|
||||||
private ItemDisplayContext transformType;
|
|
||||||
|
|
||||||
@Inject(method = "render", at = @At("HEAD"))
|
|
||||||
private void markRenderingType(ItemStack itemStack, ItemDisplayContext transformType, boolean leftHand, PoseStack matrixStack, MultiBufferSource buffer, int combinedLight, int combinedOverlay, BakedModel model, CallbackInfo ci) {
|
|
||||||
this.transformType = transformType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Direction[] ITEM_DIRECTIONS = new Direction[] { Direction.SOUTH };
|
|
||||||
private static final Direction[] BLOCK_DIRECTIONS = new Direction[] { Direction.UP, Direction.EAST, Direction.NORTH };
|
|
||||||
|
|
||||||
private boolean isCorrectDirectionForType(FastItemRenderType type, Direction direction) {
|
|
||||||
if(type == FastItemRenderType.SIMPLE_ITEM)
|
|
||||||
return direction == Direction.SOUTH;
|
|
||||||
else {
|
|
||||||
return direction == Direction.UP || direction == Direction.EAST || direction == Direction.NORTH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If a model
|
|
||||||
* - is a vanilla item model (SimpleBakedModel),
|
|
||||||
* - has no custom GUI transforms, and
|
|
||||||
* - is being rendered in 2D on a GUI
|
|
||||||
* we do not need to go through the process of rendering every quad. Just render the south ones (the ones facing the
|
|
||||||
* camera).
|
|
||||||
*/
|
|
||||||
@Inject(method = "renderModelLists", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void fasterItemRender(BakedModel model, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack matrixStack, VertexConsumer buffer, CallbackInfo ci) {
|
|
||||||
if(!RenderState.IS_RENDERING_LEVEL && !stack.isEmpty() && model.getClass() == SimpleBakedModel.class && transformType == ItemDisplayContext.GUI) {
|
|
||||||
FastItemRenderType type;
|
|
||||||
ItemTransform transform = model.getTransforms().gui;
|
|
||||||
if(transform == ItemTransform.NO_TRANSFORM)
|
|
||||||
type = FastItemRenderType.SIMPLE_ITEM;
|
|
||||||
else if(stack.getItem() instanceof BlockItem && isBlockTransforms(transform))
|
|
||||||
type = FastItemRenderType.SIMPLE_BLOCK;
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
ci.cancel();
|
|
||||||
PoseStack.Pose pose = matrixStack.last();
|
|
||||||
int[] combinedLights = new int[] {combinedLight, combinedLight, combinedLight, combinedLight};
|
|
||||||
Direction[] directions = type == FastItemRenderType.SIMPLE_ITEM ? ITEM_DIRECTIONS : BLOCK_DIRECTIONS;
|
|
||||||
for(Direction direction : directions) {
|
|
||||||
List<BakedQuad> culledFaces = model.getQuads(null, direction, dummyRandom);
|
|
||||||
/* check size to avoid instantiating iterator when the list is empty */
|
|
||||||
if(culledFaces.size() > 0) {
|
|
||||||
for(BakedQuad quad : culledFaces) {
|
|
||||||
render2dItemFace(quad, stack, buffer, pose, combinedLights, combinedOverlay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
List<BakedQuad> unculledFaces = model.getQuads(null, null, dummyRandom);
|
|
||||||
for(BakedQuad quad : unculledFaces) {
|
|
||||||
if(isCorrectDirectionForType(type, quad.getDirection()))
|
|
||||||
render2dItemFace(quad, stack, buffer, pose, combinedLights, combinedOverlay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBlockTransforms(ItemTransform transform) {
|
|
||||||
return transform.rotation.x() == 30f
|
|
||||||
&& transform.rotation.y() == 225f
|
|
||||||
&& transform.rotation.z() == 0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void render2dItemFace(BakedQuad quad, ItemStack stack, VertexConsumer buffer, PoseStack.Pose pose, int[] combinedLights, int combinedOverlay) {
|
|
||||||
int i = -1;
|
|
||||||
if (quad.isTinted()) {
|
|
||||||
i = this.itemColors.getColor(stack, quad.getTintIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
float f = (float)(i >> 16 & 255) / 255.0F;
|
|
||||||
float f1 = (float)(i >> 8 & 255) / 255.0F;
|
|
||||||
float f2 = (float)(i & 255) / 255.0F;
|
|
||||||
buffer.putBulkData(pose, quad, COLOR_MULTIPLIER, f, f1, f2, combinedLights, combinedOverlay, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +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.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Mixin(CompoundTag.class)
|
|
||||||
public class CompoundTagMixin {
|
|
||||||
@Shadow @Final @Mutable
|
|
||||||
private Map<String, Tag> tags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that the backing map is always a CanonizingStringMap.
|
|
||||||
*/
|
|
||||||
@Redirect(method = "<init>(Ljava/util/Map;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/nbt/CompoundTag;tags:Ljava/util/Map;", ordinal = 0))
|
|
||||||
private void replaceTagMap(CompoundTag tag, Map<String, Tag> incomingMap) {
|
|
||||||
if(incomingMap instanceof CanonizingStringMap)
|
|
||||||
this.tags = incomingMap;
|
|
||||||
else {
|
|
||||||
this.tags = new CanonizingStringMap<>();
|
|
||||||
this.tags.putAll(incomingMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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,77 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,127 +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() {
|
|
||||||
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());
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.isOverridden()) {
|
|
||||||
String source = "[unknown]";
|
|
||||||
|
|
||||||
if (option.isUserDefined()) {
|
|
||||||
source = "user configuration";
|
|
||||||
} else if (option.isModDefined()) {
|
|
||||||
source = "mods [" + String.join(", ", option.getDefiningMods()) + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option.isEnabled()) {
|
|
||||||
this.logger.warn("Force-enabling mixin '{}' as rule '{}' (added by {}) enables it", mixin,
|
|
||||||
option.getName(), source);
|
|
||||||
} else {
|
|
||||||
this.logger.warn("Force-disabling mixin '{}' as rule '{}' (added by {}) disables it and children", mixin,
|
|
||||||
option.getName(), source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,350 +0,0 @@
|
||||||
package org.embeddedt.modernfix.core.config;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
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.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<>();
|
|
||||||
|
|
||||||
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 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 final ImmutableMap<String, Boolean> DEFAULT_SETTING_OVERRIDES = ImmutableMap.<String, Boolean>builder()
|
|
||||||
.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.devenv", isDevEnv)
|
|
||||||
.put("mixin.perf.remove_spawn_chunks", isDevEnv)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private ModernFixEarlyConfig(File file) {
|
|
||||||
this.configFile = file;
|
|
||||||
|
|
||||||
this.scanForAndBuildMixinOptions();
|
|
||||||
mixinOptions.addAll(DEFAULT_SETTING_OVERRIDES.keySet());
|
|
||||||
for(String optionName : mixinOptions) {
|
|
||||||
boolean defaultEnabled = DEFAULT_SETTING_OVERRIDES.getOrDefault(optionName, true);
|
|
||||||
this.options.putIfAbsent(optionName, new Option(optionName, defaultEnabled, false));
|
|
||||||
}
|
|
||||||
// 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");
|
|
||||||
disableIfModPresent("mixin.bugfix.mc218112", "performant");
|
|
||||||
disableIfModPresent("mixin.bugfix.remove_block_chunkloading", "performant");
|
|
||||||
disableIfModPresent("mixin.bugfix.paper_chunk_patches", "c2me");
|
|
||||||
disableIfModPresent("mixin.perf.reuse_datapacks", "tac");
|
|
||||||
disableIfModPresent("mixin.launch.class_search_cache", "optifine");
|
|
||||||
disableIfModPresent("mixin.perf.datapack_reload_exceptions", "cyanide");
|
|
||||||
disableIfModPresent("mixin.perf.faster_texture_loading", "stitch");
|
|
||||||
}
|
|
||||||
|
|
||||||
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(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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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,96 +0,0 @@
|
||||||
package org.embeddedt.modernfix.dfu;
|
|
||||||
|
|
||||||
import com.mojang.datafixers.DSL;
|
|
||||||
import com.mojang.datafixers.DataFixUtils;
|
|
||||||
import com.mojang.datafixers.DataFixer;
|
|
||||||
import com.mojang.datafixers.schemas.Schema;
|
|
||||||
import com.mojang.datafixers.types.Type;
|
|
||||||
import com.mojang.datafixers.types.constant.EmptyPart;
|
|
||||||
import com.mojang.datafixers.types.templates.TypeTemplate;
|
|
||||||
import com.mojang.serialization.Dynamic;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.SharedConstants;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class LazyDataFixer implements DataFixer {
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger("ModernFix");
|
|
||||||
private DataFixer backingDataFixer;
|
|
||||||
private final Supplier<DataFixer> dfuSupplier;
|
|
||||||
private static final Schema FAKE_SCHEMA = new EmptySchema();
|
|
||||||
|
|
||||||
public LazyDataFixer(Supplier<DataFixer> dfuSupplier) {
|
|
||||||
LOGGER.info("Bypassed Mojang DFU");
|
|
||||||
this.backingDataFixer = null;
|
|
||||||
this.dfuSupplier = dfuSupplier;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public <T> Dynamic<T> update(DSL.TypeReference type, Dynamic<T> input, int version, int newVersion) {
|
|
||||||
if(version >= newVersion)
|
|
||||||
return input;
|
|
||||||
synchronized (this) {
|
|
||||||
if(backingDataFixer == null) {
|
|
||||||
LOGGER.info("Instantiating Mojang DFU");
|
|
||||||
DFUBlaster.blastMaps();
|
|
||||||
backingDataFixer = dfuSupplier.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return backingDataFixer.update(type, input, version, newVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* "getSchema is only there for checks that are not important" - fry, 2021
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Schema getSchema(int key) {
|
|
||||||
return FAKE_SCHEMA;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Empty schema that also returns empty Type<?> instances to prevent crashes.
|
|
||||||
*/
|
|
||||||
static class EmptySchema extends Schema {
|
|
||||||
public EmptySchema() {
|
|
||||||
super(DataFixUtils.makeKey(SharedConstants.getCurrentVersion().getDataVersion().getVersion()), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Type<?> EMPTY_TYPE = new EmptyPart();
|
|
||||||
private static final TypeTemplate FAKE_TEMPLATE = EMPTY_TYPE.template();
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Map<String, Type<?>> buildTypes() {
|
|
||||||
Object2ObjectOpenHashMap<String, Type<?>> map = new Object2ObjectOpenHashMap<>();
|
|
||||||
map.defaultReturnValue(new EmptyPart());
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TypeTemplate resolveTemplate(String name) {
|
|
||||||
return FAKE_TEMPLATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type<?> getChoiceType(DSL.TypeReference type, String choiceName) {
|
|
||||||
return EMPTY_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void registerTypes(Schema schema, Map<String, Supplier<TypeTemplate>> entityTypes, Map<String, Supplier<TypeTemplate>> blockEntityTypes) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Supplier<TypeTemplate>> registerEntities(Schema schema) {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Supplier<TypeTemplate>> registerBlockEntities(Schema schema) {
|
|
||||||
return Collections.emptyMap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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,180 +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.util.RandomSource;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import org.apache.commons.lang3.tuple.Triple;
|
|
||||||
import org.embeddedt.modernfix.duck.IExtendedModelBakery;
|
|
||||||
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<ModelBakery.BakedCacheKey, 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 direction, RandomSource random) {
|
|
||||||
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<ModelBakery.BakedCacheKey, BakedModel> cache) {
|
|
||||||
this.bakery = bakery;
|
|
||||||
this.bakedCache = cache;
|
|
||||||
this.permanentOverrides = new Object2ObjectOpenHashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMissingModel(BakedModel model) {
|
|
||||||
this.missingModel = model;
|
|
||||||
this.permanentOverrides.put(ModelBakery.MISSING_MODEL_LOCATION, this.missingModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
model = ((IExtendedModelBakery)bakery).bakeDefault((ResourceLocation)o);
|
|
||||||
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(ModelBakery.BakedCacheKey::id).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().id(), entry.getValue())).collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@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.transformation() != rotation || loc.isUvLocked() != uvLock || overridenLocations.contains(loc.id()))
|
|
||||||
return oldModel;
|
|
||||||
else
|
|
||||||
return function.apply(loc.id(), oldModel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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.registries.BuiltInRegistries;
|
|
||||||
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 {}", BuiltInRegistries.ENTITY_TYPE.getKey(key));
|
|
||||||
} catch(RuntimeException e) {
|
|
||||||
ModernFix.LOGGER.error("Failed to create entity model for " + BuiltInRegistries.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,82 +0,0 @@
|
||||||
package org.embeddedt.modernfix.platform;
|
|
||||||
|
|
||||||
import dev.architectury.injectables.annotations.ExpectPlatform;
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
|
||||||
import net.minecraft.client.searchtree.SearchRegistry;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
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 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 void registerCreativeSearchTrees(SearchRegistry registry, SearchRegistry.TreeBuilderSupplier<ItemStack> nameSupplier, SearchRegistry.TreeBuilderSupplier<ItemStack> tagSupplier, BiConsumer<SearchRegistry.Key<ItemStack>, List<ItemStack>> populator) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,173 +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.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 Collection<ResourceLocation> getResources(PackType type, String resourceNamespace, String pathIn, int maxDepth, Predicate<ResourceLocation> 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;
|
|
||||||
ResourceLocation foundResource = new ResourceLocation(resourceNamespace, fullPath);
|
|
||||||
if(!filter.test(foundResource))
|
|
||||||
continue;
|
|
||||||
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() {
|
|
||||||
synchronized (cachingPacks) {
|
|
||||||
cachingPacks.keySet().forEach(pack -> {
|
|
||||||
if(pack != null)
|
|
||||||
pack.invalidateCache();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +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.Component;
|
|
||||||
|
|
||||||
public class ModernFixConfigScreen extends Screen {
|
|
||||||
private OptionList optionList;
|
|
||||||
private Screen lastScreen;
|
|
||||||
|
|
||||||
public boolean madeChanges = false;
|
|
||||||
private Button doneButton;
|
|
||||||
public ModernFixConfigScreen(Screen lastScreen) {
|
|
||||||
super(Component.translatable("modernfix.config"));
|
|
||||||
this.lastScreen = lastScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void init() {
|
|
||||||
this.optionList = new OptionList(this, this.minecraft);
|
|
||||||
this.addWidget(this.optionList);
|
|
||||||
this.doneButton = new Button.Builder(CommonComponents.GUI_DONE, (arg) -> {
|
|
||||||
this.minecraft.setScreen(lastScreen);
|
|
||||||
}).pos(this.width / 2 - 100, this.height - 29).size(200, 20).build();
|
|
||||||
this.addRenderableWidget(this.doneButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 ? Component.translatable("modernfix.config.done_restart") : CommonComponents.GUI_DONE);
|
|
||||||
super.render(poseStack, mouseX, mouseY, partialTicks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
package org.embeddedt.modernfix.screen;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import net.minecraft.ChatFormatting;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.gui.components.Button;
|
|
||||||
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
|
||||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
|
||||||
import net.minecraft.client.gui.narration.NarratableEntry;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.network.chat.MutableComponent;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
|
||||||
import org.embeddedt.modernfix.core.config.Option;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class OptionList extends ContainerObjectSelectionList<OptionList.Entry> {
|
|
||||||
private final int maxNameWidth;
|
|
||||||
|
|
||||||
private static final Component OPTION_ON = Component.translatable("modernfix.option.on").withStyle(style -> style.withColor(ChatFormatting.GREEN));
|
|
||||||
private static final Component OPTION_OFF = Component.translatable("modernfix.option.off").withStyle(style -> style.withColor(ChatFormatting.RED));
|
|
||||||
|
|
||||||
private ModernFixConfigScreen mainScreen;
|
|
||||||
|
|
||||||
|
|
||||||
public OptionList(ModernFixConfigScreen arg, Minecraft arg2) {
|
|
||||||
super(arg2,arg.width + 45, arg.height, 43, arg.height - 32, 20);
|
|
||||||
|
|
||||||
this.mainScreen = arg;
|
|
||||||
|
|
||||||
int maxW = 0;
|
|
||||||
Map<String, Option> optionMap = ModernFixMixinPlugin.instance.config.getOptionMap();
|
|
||||||
List<String> sortedKeys = optionMap.keySet().stream().filter(key -> !key.equals("mixin.core")).sorted().collect(Collectors.toList());
|
|
||||||
for(String key : sortedKeys) {
|
|
||||||
Option option = optionMap.get(key);
|
|
||||||
int w = this.minecraft.font.width(key);
|
|
||||||
maxW = Math.max(w, maxW);
|
|
||||||
this.addEntry(new OptionEntry(key, option));
|
|
||||||
}
|
|
||||||
this.maxNameWidth = maxW;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getScrollbarPosition() {
|
|
||||||
return super.getScrollbarPosition() + 15 + 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRowWidth() {
|
|
||||||
return super.getRowWidth() + 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
class OptionEntry extends Entry {
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private final Button toggleButton;
|
|
||||||
private final Option option;
|
|
||||||
|
|
||||||
public OptionEntry(String optionName, Option option) {
|
|
||||||
this.name = optionName;
|
|
||||||
this.option = option;
|
|
||||||
this.toggleButton = new Button.Builder(Component.literal(""), (arg) -> {
|
|
||||||
this.option.setEnabled(!this.option.isEnabled(), !this.option.isUserDefined());
|
|
||||||
try {
|
|
||||||
ModernFixMixinPlugin.instance.config.save();
|
|
||||||
if(!OptionList.this.mainScreen.madeChanges) {
|
|
||||||
OptionList.this.mainScreen.madeChanges = true;
|
|
||||||
}
|
|
||||||
} catch(IOException e) {
|
|
||||||
// revert
|
|
||||||
this.option.setEnabled(!this.option.isEnabled(), !this.option.isUserDefined());
|
|
||||||
ModernFix.LOGGER.error("Unable to save config", e);
|
|
||||||
}
|
|
||||||
}).pos(0, 0).size(95, 20).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(PoseStack matrixStack, int index, int top, int left, int width, int height, int mouseX, int mouseY, boolean isMouseOver, float partialTicks) {
|
|
||||||
MutableComponent nameComponent = Component.literal(this.name);
|
|
||||||
if(this.option.isUserDefined())
|
|
||||||
nameComponent = nameComponent.withStyle(ChatFormatting.ITALIC).append(Component.translatable("modernfix.config.not_default"));
|
|
||||||
OptionList.this.minecraft.font.draw(matrixStack, nameComponent, (float)(left + 160 - OptionList.this.maxNameWidth), (float)(top + height / 2 - 4), 16777215);
|
|
||||||
this.toggleButton.setPosition(left + 175, top);
|
|
||||||
this.toggleButton.setMessage(getOptionMessage(this.option));
|
|
||||||
this.toggleButton.render(matrixStack, mouseX, mouseY, partialTicks);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Component getOptionMessage(Option option) {
|
|
||||||
return option.isEnabled() ? OPTION_ON : OPTION_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<? extends GuiEventListener> children() {
|
|
||||||
return ImmutableList.of(this.toggleButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
|
||||||
return this.toggleButton.mouseClicked(mouseX, mouseY, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean mouseReleased(double mouseX, double mouseY, int button) {
|
|
||||||
return this.toggleButton.mouseReleased(mouseX, mouseY, button);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<? extends NarratableEntry> narratables() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class Entry extends ContainerObjectSelectionList.Entry<Entry> {
|
|
||||||
public Entry() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
package org.embeddedt.modernfix.searchtree;
|
|
||||||
|
|
||||||
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 net.minecraft.client.searchtree.RefreshableSearchTree;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class REIBackedSearchTree extends DummySearchTree<ItemStack> {
|
|
||||||
private final AsyncSearchManager searchManager = new AsyncSearchManager(EntryRegistry.getInstance()::getPreFilteredList, () -> {
|
|
||||||
return stack -> true;
|
|
||||||
}, EntryStack::normalize);
|
|
||||||
|
|
||||||
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<EntryStack<?>> stacks;
|
|
||||||
try {
|
|
||||||
stacks = this.searchManager.getNow();
|
|
||||||
} catch(RuntimeException e) {
|
|
||||||
ModernFix.LOGGER.error("Couldn't search for '" + pSearchText + "'", e);
|
|
||||||
stacks = Collections.emptyList();
|
|
||||||
}
|
|
||||||
for(EntryStack<?> stack : stacks) {
|
|
||||||
if(stack.getType() == VanillaEntryTypes.ITEM) {
|
|
||||||
listCache.add(stack.cheatsAs().getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastSearchText = pSearchText;
|
|
||||||
}
|
|
||||||
return listCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
|
|
||||||
@Override
|
|
||||||
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
|
|
||||||
return new REIBackedSearchTree(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canUse() {
|
|
||||||
return ModernFixPlatformHooks.modPresent("roughlyenoughitems");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "REI";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -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,155 +0,0 @@
|
||||||
package org.embeddedt.modernfix.util;
|
|
||||||
|
|
||||||
import com.google.common.collect.Interner;
|
|
||||||
import com.google.common.collect.Interners;
|
|
||||||
import it.unimi.dsi.fastutil.objects.*;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replacement backing map for CompoundTags. Uses an array map for tags with 4 or less entries,
|
|
||||||
* and a hash map for larger tags.
|
|
||||||
*/
|
|
||||||
public class CanonizingStringMap<T> implements Map<String, T> {
|
|
||||||
private Object2ObjectMap<String, T> backingMap;
|
|
||||||
|
|
||||||
private static final int GROWTH_THRESHOLD = 4;
|
|
||||||
private static final Interner<String> KEY_INTERNER = Interners.newStrongInterner();
|
|
||||||
|
|
||||||
public CanonizingStringMap() {
|
|
||||||
this(new Object2ObjectArrayMap<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CanonizingStringMap(Object2ObjectMap<String, T> newMap) {
|
|
||||||
this.backingMap = newMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 T get(Object o) {
|
|
||||||
return backingMap.get(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public T put(String s, T t) {
|
|
||||||
if(backingMap.size() >= GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap) && !backingMap.containsKey(s)) {
|
|
||||||
// map will grow to GROWTH_THRESHOLD + 1 entries, change to hashmap
|
|
||||||
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
|
|
||||||
}
|
|
||||||
s = KEY_INTERNER.intern(s);
|
|
||||||
return backingMap.put(s, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T remove(Object o) {
|
|
||||||
T value = backingMap.remove(o);
|
|
||||||
// need to shrink to be consistent with new maps
|
|
||||||
if(backingMap.size() <= GROWTH_THRESHOLD && backingMap instanceof Object2ObjectOpenHashMap) {
|
|
||||||
backingMap = new Object2ObjectArrayMap<>(backingMap);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putAll(@NotNull Map<? extends String, ? extends T> map) {
|
|
||||||
if(map.size() == 0)
|
|
||||||
return;
|
|
||||||
// grow early if we know there are enough non-overlapping keys
|
|
||||||
if((map.size() - backingMap.size()) > GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap)) {
|
|
||||||
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
|
|
||||||
}
|
|
||||||
map.forEach((String key, T val) -> {
|
|
||||||
key = KEY_INTERNER.intern(key);
|
|
||||||
backingMap.put(key, val);
|
|
||||||
});
|
|
||||||
// if it's still an array, and now too big, grow it
|
|
||||||
if(backingMap.size() > GROWTH_THRESHOLD && !(backingMap instanceof Object2ObjectOpenHashMap)) {
|
|
||||||
backingMap = new Object2ObjectOpenHashMap<>(backingMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
if(!(this.backingMap instanceof Object2ObjectArrayMap))
|
|
||||||
this.backingMap = new Object2ObjectArrayMap<>();
|
|
||||||
else
|
|
||||||
this.backingMap.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Set<String> keySet() {
|
|
||||||
// has to be modifiable because mods (cough, Tinkers) use it to clear the tag
|
|
||||||
return this.backingMap.keySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Collection<T> values() {
|
|
||||||
return Collections.unmodifiableCollection(this.backingMap.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Set<Entry<String, T>> entrySet() {
|
|
||||||
return Collections.unmodifiableSet(this.backingMap.entrySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
CanonizingStringMap<?> that = (CanonizingStringMap<?>)o;
|
|
||||||
if(that.backingMap.size() != backingMap.size())
|
|
||||||
return false;
|
|
||||||
return backingMap.object2ObjectEntrySet().containsAll(that.backingMap.object2ObjectEntrySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We deliberately use a hashcode that will be consistent regardless of underlying map type.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final ObjectIterator<Object2ObjectMap.Entry<String, T>> i = Object2ObjectMaps.fastIterator(backingMap);
|
|
||||||
int h = 0, n = backingMap.size();
|
|
||||||
while (n-- != 0)
|
|
||||||
h += i.next().hashCode();
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> inputMap, Function<T, T> deepCopier) {
|
|
||||||
Object2ObjectMap<String, T> copiedBackingMap;
|
|
||||||
int size = inputMap.backingMap.size();
|
|
||||||
if(size > GROWTH_THRESHOLD) {
|
|
||||||
copiedBackingMap = new Object2ObjectOpenHashMap<>(size);
|
|
||||||
} else
|
|
||||||
copiedBackingMap = new Object2ObjectArrayMap<>(size);
|
|
||||||
inputMap.backingMap.object2ObjectEntrySet().forEach(entry -> {
|
|
||||||
copiedBackingMap.put(entry.getKey(), deepCopier.apply(entry.getValue()));
|
|
||||||
});
|
|
||||||
return new CanonizingStringMap<>(copiedBackingMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,201 +0,0 @@
|
||||||
package org.embeddedt.modernfix.util;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.spongepowered.asm.mixin.transformer.ClassInfo;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.security.CodeSigner;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.jar.Attributes;
|
|
||||||
|
|
||||||
public class ClassInfoManager {
|
|
||||||
private static Map<String, ClassInfo> classInfoCache = null;
|
|
||||||
public static void clear() {
|
|
||||||
if(!ModernFixMixinPlugin.instance.isOptionEnabled("perf.clear_mixin_classinfo.ClassInfoManager"))
|
|
||||||
return;
|
|
||||||
if(classInfoCache == null) {
|
|
||||||
try {
|
|
||||||
Field field = ClassInfo.class.getDeclaredField("cache");
|
|
||||||
field.setAccessible(true);
|
|
||||||
classInfoCache = (Map<String, ClassInfo>)field.get(null);
|
|
||||||
} catch(ReflectiveOperationException | RuntimeException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
classInfoCache.entrySet().removeIf(entry -> !entry.getKey().equals("java/lang/Object") && (entry.getValue() == null || !entry.getValue().isMixin()));
|
|
||||||
} catch(RuntimeException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear manifest entries
|
|
||||||
int numManifestsCleared = 0;
|
|
||||||
// TODO port
|
|
||||||
/*
|
|
||||||
for(IModFileInfo mod : ModList.get().getModFiles()) {
|
|
||||||
Manifest manifest = mod.getFile().getSecureJar().moduleDataProvider().getManifest();
|
|
||||||
if(manifest.getEntries() instanceof HashMap<String, Attributes> entryMap) {
|
|
||||||
for (Map.Entry<String, Attributes> entry : entryMap.entrySet()) {
|
|
||||||
Attributes attributes = entry.getValue();
|
|
||||||
if (attributes.size() == 1 && attributes.getValue("SHA-256-Digest") != null) {
|
|
||||||
try {
|
|
||||||
entry.setValue(EmptyAttributes.INSTANCE);
|
|
||||||
numManifestsCleared++;
|
|
||||||
} catch (RuntimeException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
if(numManifestsCleared > 0)
|
|
||||||
ModernFix.LOGGER.info("Cleared {} manifest attributes", numManifestsCleared);
|
|
||||||
|
|
||||||
try {
|
|
||||||
clearSecureJarStructs();
|
|
||||||
} catch(Throwable e) {
|
|
||||||
ModernFix.LOGGER.error("Couldn't clear Jar structs", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void clearSecureJarStructs() throws Throwable {
|
|
||||||
/*
|
|
||||||
// Clear Jar signing data
|
|
||||||
Unsafe unsafe;
|
|
||||||
Field f = Unsafe.class.getDeclaredField("theUnsafe");
|
|
||||||
f.setAccessible(true);
|
|
||||||
unsafe = (Unsafe)f.get(null);
|
|
||||||
|
|
||||||
Field statusDataField, pendingSignersField, verifiedSignersField;
|
|
||||||
statusDataField = Jar.class.getDeclaredField("statusData");
|
|
||||||
pendingSignersField = Jar.class.getDeclaredField("pendingSigners");
|
|
||||||
verifiedSignersField = Jar.class.getDeclaredField("verifiedSigners");
|
|
||||||
|
|
||||||
long statusDataOffset = unsafe.objectFieldOffset(statusDataField);
|
|
||||||
long pendingSignersOffset = unsafe.objectFieldOffset(pendingSignersField);
|
|
||||||
long verifiedSignersOffset = unsafe.objectFieldOffset(verifiedSignersField);
|
|
||||||
|
|
||||||
for(IModFileInfo mod : ModList.get().getModFiles()) {
|
|
||||||
SecureJar secureJar = mod.getFile().getSecureJar();
|
|
||||||
if(secureJar instanceof Jar) {
|
|
||||||
unsafe.putObject(secureJar, statusDataOffset, LyingStatusDataMap.INSTANCE);
|
|
||||||
unsafe.putObject(secureJar, pendingSignersOffset, EmptyCodeSignerTable.INSTANCE);
|
|
||||||
unsafe.putObject(secureJar, verifiedSignersOffset, EmptyCodeSignerTable.INSTANCE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static class EmptyCodeSignerTable extends Hashtable<String, CodeSigner[]> {
|
|
||||||
public static final EmptyCodeSignerTable INSTANCE = new EmptyCodeSignerTable();
|
|
||||||
private static final CodeSigner[] VAL = new CodeSigner[0];
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized CodeSigner[] put(String key, CodeSigner[] value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized boolean isEmpty() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized boolean containsKey(Object key) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized CodeSigner[] get(Object key) {
|
|
||||||
return VAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This map is used to replace the statusData map.
|
|
||||||
*
|
|
||||||
* The lying in containsKey is intentionally done to force certain code paths to run in Jar.
|
|
||||||
* Otherwise the security information might be recomputed many times.
|
|
||||||
*/
|
|
||||||
static class LyingStatusDataMap implements Map<String, Object> {
|
|
||||||
public static final LyingStatusDataMap INSTANCE = new LyingStatusDataMap();
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsKey(Object o) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean containsValue(Object o) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object get(Object o) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Object put(String s, Object o) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object remove(Object o) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putAll(@NotNull Map<? extends String, ?> map) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Set<String> keySet() {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Collection<Object> values() {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Set<Entry<String, Object>> entrySet() {
|
|
||||||
return Collections.emptySet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class EmptyAttributes extends Attributes {
|
|
||||||
public static final EmptyAttributes INSTANCE = new EmptyAttributes();
|
|
||||||
EmptyAttributes() {
|
|
||||||
super(1);
|
|
||||||
this.map = ImmutableMap.of();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,155 +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.*;
|
|
||||||
import net.minecraft.core.RegistryAccess;
|
|
||||||
import net.minecraft.world.Difficulty;
|
|
||||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
|
||||||
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 WorldDataConfiguration getDataConfiguration() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDataConfiguration(WorldDataConfiguration arg) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 WorldOptions worldGenOptions() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFlatWorld() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDebugWorld() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,66 +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 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);
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
LOGGER.error(sb.toString());
|
|
||||||
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,13 +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.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)"
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +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": "地图(未能在附近找到相关地物)"
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"required": true,
|
|
||||||
"minVersion": "0.8",
|
|
||||||
"package": "org.embeddedt.modernfix.common.mixin",
|
|
||||||
"plugin": "org.embeddedt.modernfix.core.ModernFixMixinPlugin",
|
|
||||||
"compatibilityLevel": "JAVA_17",
|
|
||||||
"mixins": [
|
|
||||||
${mixin_classes}
|
|
||||||
],
|
|
||||||
"injectors": {
|
|
||||||
"defaultRequire": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
accessWidener v2 named
|
|
||||||
|
|
||||||
accessible class net/minecraft/client/renderer/RenderType$CompositeRenderType
|
|
||||||
accessible method net/minecraft/nbt/CompoundTag <init> (Ljava/util/Map;)V
|
|
||||||
|
|
||||||
accessible class net/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache
|
|
||||||
accessible class net/minecraft/server/level/ServerChunkCache$MainThreadExecutor
|
|
||||||
accessible field net/minecraft/world/level/block/state/BlockBehaviour properties Lnet/minecraft/world/level/block/state/BlockBehaviour$Properties;
|
|
||||||
accessible class net/minecraft/client/renderer/block/model/BlockElementFace$Deserializer
|
|
||||||
accessible class net/minecraft/client/renderer/texture/Stitcher$Holder
|
|
||||||
accessible field net/minecraft/client/renderer/texture/Stitcher$Holder width I
|
|
||||||
accessible field net/minecraft/client/renderer/texture/Stitcher$Holder height I
|
|
||||||
accessible field net/minecraft/network/syncher/EntityDataAccessor id I
|
|
||||||
mutable field net/minecraft/network/syncher/EntityDataAccessor id I
|
|
||||||
accessible class net/minecraft/client/resources/model/ModelBakery$BlockStateDefinitionException
|
|
||||||
accessible field net/minecraft/network/syncher/SynchedEntityData itemsById Lit/unimi/dsi/fastutil/ints/Int2ObjectMap;
|
|
||||||
accessible field net/minecraft/network/syncher/SynchedEntityData ENTITY_ID_POOL Lit/unimi/dsi/fastutil/objects/Object2IntMap;
|
|
||||||
accessible field net/minecraft/network/syncher/SynchedEntityData lock Ljava/util/concurrent/locks/ReadWriteLock;
|
|
||||||
accessible method net/minecraft/Util makeExecutor (Ljava/lang/String;)Ljava/util/concurrent/ExecutorService;
|
|
||||||
accessible field net/minecraft/server/level/ChunkMap updatingChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
|
||||||
accessible field net/minecraft/server/level/ChunkMap visibleChunkMap Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
|
||||||
accessible field net/minecraft/server/level/ChunkMap pendingUnloads Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
|
|
||||||
accessible method net/minecraft/resources/ResourceKey <init> (Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)V
|
|
||||||
accessible field net/minecraft/client/renderer/block/model/BlockModel GSON Lcom/google/gson/Gson;
|
|
||||||
accessible class net/minecraft/server/level/ChunkMap$DistanceManager
|
|
||||||
accessible class net/minecraft/client/resources/model/ModelBakery$BakedCacheKey
|
|
||||||
accessible field net/minecraft/client/resources/model/ModelBakery bakedCache Ljava/util/Map;
|
|
||||||
accessible field net/minecraft/client/resources/model/ModelBakery ITEM_MODEL_GENERATOR Lnet/minecraft/client/renderer/block/model/ItemModelGenerator;
|
|
||||||
accessible method net/minecraft/client/resources/model/ModelBakery loadTopLevel (Lnet/minecraft/client/resources/model/ModelResourceLocation;)V
|
|
||||||
accessible field net/minecraft/client/resources/model/ModelBakery topLevelModels Ljava/util/Map;
|
|
||||||
accessible class net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl
|
|
||||||
accessible method net/minecraft/client/resources/model/ModelBakery$ModelBakerImpl <init> (Lnet/minecraft/client/resources/model/ModelBakery;Ljava/util/function/BiFunction;Lnet/minecraft/resources/ResourceLocation;)V
|
|
||||||
accessible method net/minecraft/client/resources/model/ModelBakery$BakedCacheKey <init> (Lnet/minecraft/resources/ResourceLocation;Lcom/mojang/math/Transformation;Z)V
|
|
||||||
accessible class net/minecraft/world/level/chunk/PalettedContainer$Data
|
|
||||||
accessible field net/minecraft/server/MinecraftServer resources Lnet/minecraft/server/MinecraftServer$ReloadableResources;
|
|
||||||
accessible class net/minecraft/server/MinecraftServer$ReloadableResources
|
|
||||||
accessible method net/minecraft/client/gui/screens/Screen addRenderableWidget (Lnet/minecraft/client/gui/components/events/GuiEventListener;)Lnet/minecraft/client/gui/components/events/GuiEventListener;
|
|
||||||
1
doc/generated/.gitignore
vendored
Normal file
1
doc/generated/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
*.md
|
||||||
208
doc/logo.svg
Normal file
208
doc/logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 33 KiB |
209
doc/logo_transparent.svg
Normal file
209
doc/logo_transparent.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 33 KiB |
|
|
@ -1,131 +0,0 @@
|
||||||
plugins {
|
|
||||||
id "com.github.johnrengelman.shadow" version "7.1.2"
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'com.matthewprenger.cursegradle'
|
|
||||||
apply plugin: 'com.modrinth.minotaur'
|
|
||||||
|
|
||||||
architectury {
|
|
||||||
platformSetupLoomIde()
|
|
||||||
fabric()
|
|
||||||
}
|
|
||||||
|
|
||||||
loom {
|
|
||||||
accessWidenerPath = project(":common").loom.accessWidenerPath
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
common
|
|
||||||
shadowCommon // Don't use shadow from the shadow plugin since it *excludes* files.
|
|
||||||
compileClasspath.extendsFrom common
|
|
||||||
runtimeClasspath.extendsFrom common
|
|
||||||
developmentFabric.extendsFrom common
|
|
||||||
|
|
||||||
modIncludeImplementation
|
|
||||||
|
|
||||||
include.extendsFrom modIncludeImplementation
|
|
||||||
modImplementation.extendsFrom modIncludeImplementation
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
|
||||||
modIncludeImplementation(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
|
||||||
modIncludeImplementation(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
|
||||||
modIncludeImplementation(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
|
||||||
modIncludeImplementation(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
|
||||||
modIncludeImplementation(fabricApi.module("fabric-models-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
|
||||||
modImplementation(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
|
||||||
modImplementation(fabricApi.module("fabric-data-generation-api-v1", rootProject.fabric_api_version)) { exclude group: 'net.fabricmc', module: 'fabric-loader' }
|
|
||||||
modCompileOnly("com.terraformersmc:modmenu:${rootProject.modmenu_version}") { transitive false }
|
|
||||||
// Remove the next line if you don't want to depend on the API
|
|
||||||
// modApi "me.shedaniel:architectury-fabric:${rootProject.architectury_version}"
|
|
||||||
|
|
||||||
common(project(path: ":common", configuration: "namedElements")) { transitive false }
|
|
||||||
shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) { transitive false }
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
inputs.property "version", project.version
|
|
||||||
|
|
||||||
filesMatching("fabric.mod.json") {
|
|
||||||
expand "version": project.version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
exclude "architectury.common.json"
|
|
||||||
|
|
||||||
configurations = [project.configurations.shadowCommon]
|
|
||||||
classifier "dev-shadow"
|
|
||||||
}
|
|
||||||
|
|
||||||
remapJar {
|
|
||||||
injectAccessWidener = true
|
|
||||||
input.set shadowJar.archiveFile
|
|
||||||
dependsOn shadowJar
|
|
||||||
classifier null
|
|
||||||
}
|
|
||||||
|
|
||||||
task copyJarToBin(type: Copy) {
|
|
||||||
from remapJar // shortcut for createJar.outputs.files
|
|
||||||
into rootProject.file("bin")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.build.dependsOn(copyJarToBin)
|
|
||||||
|
|
||||||
jar {
|
|
||||||
classifier "dev"
|
|
||||||
}
|
|
||||||
|
|
||||||
components.java {
|
|
||||||
withVariantsFromConfiguration(project.configurations.shadowRuntimeElements) {
|
|
||||||
skip()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenFabric(MavenPublication) {
|
|
||||||
artifactId = rootProject.archives_base_name + "-" + project.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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
curseforge {
|
|
||||||
if (System.getenv("CURSEFORGE_TOKEN") != null) {
|
|
||||||
apiKey = System.getenv("CURSEFORGE_TOKEN")
|
|
||||||
project {
|
|
||||||
id = "790626"
|
|
||||||
changelog = file('../CHANGELOG.md')
|
|
||||||
changelogType = "markdown"
|
|
||||||
releaseType = "beta"
|
|
||||||
addGameVersion "Fabric"
|
|
||||||
addGameVersion minecraft_version
|
|
||||||
mainArtifact remapJar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modrinth {
|
|
||||||
token = System.getenv("MODRINTH_TOKEN")
|
|
||||||
projectId = "modernfix" // This can be the project ID or the slug. Either will work!
|
|
||||||
versionType = "beta" // This is the default -- can also be `beta` or `alpha`
|
|
||||||
uploadFile = remapJar
|
|
||||||
gameVersions = [minecraft_version]
|
|
||||||
loaders = ["fabric"]
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
package org.embeddedt.modernfix;
|
|
||||||
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
|
||||||
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
|
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import org.embeddedt.modernfix.fabric.datagen.RuntimeDatagen;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
public class ModernFixClientFabric implements ClientModInitializer {
|
|
||||||
public static ModernFixClient commonMod;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onInitializeClient() {
|
|
||||||
commonMod = new ModernFixClient();
|
|
||||||
|
|
||||||
ClientTickEvents.END_CLIENT_TICK.register((mc) -> commonMod.onRenderTickEnd());
|
|
||||||
ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
|
|
||||||
AtomicBoolean hasOpened = new AtomicBoolean(false);
|
|
||||||
ScreenEvents.beforeTick(screen).register(screen1 -> {
|
|
||||||
if(Minecraft.getInstance().getOverlay() != null)
|
|
||||||
return;
|
|
||||||
if(!hasOpened.getAndSet(true)) {
|
|
||||||
commonMod.onScreenOpening(screen1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
|
||||||
commonMod.onServerStarted(server);
|
|
||||||
});
|
|
||||||
if(FabricLoader.getInstance().isModLoaded("fabric-data-generation-api-v1")) {
|
|
||||||
RuntimeDatagen.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package org.embeddedt.modernfix;
|
|
||||||
|
|
||||||
import net.fabricmc.api.ModInitializer;
|
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
|
|
||||||
public class ModernFixFabric implements ModInitializer {
|
|
||||||
private ModernFix commonMod;
|
|
||||||
public static MinecraftServer theServer;
|
|
||||||
@Override
|
|
||||||
public void onInitialize() {
|
|
||||||
commonMod = new ModernFix();
|
|
||||||
|
|
||||||
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
|
|
||||||
theServer = server;
|
|
||||||
});
|
|
||||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
|
||||||
commonMod.onServerStarted();
|
|
||||||
});
|
|
||||||
ServerLifecycleEvents.SERVER_STOPPED.register(server -> {
|
|
||||||
commonMod.onServerDead(server);
|
|
||||||
theServer = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: implement entity ID desync
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
package org.embeddedt.modernfix.fabric.datagen;
|
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
|
|
||||||
import net.fabricmc.fabric.impl.datagen.FabricDataGenHelper;
|
|
||||||
import net.minecraft.client.gui.components.Button;
|
|
||||||
import net.minecraft.client.gui.screens.TitleScreen;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import org.embeddedt.modernfix.ModernFix;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public class RuntimeDatagen {
|
|
||||||
private static final boolean SHOULD_RUNTIME_DATAGEN = System.getProperty("fabric-api.datagen.output-dir") != null;
|
|
||||||
|
|
||||||
private static void runRuntimeDatagen() {
|
|
||||||
// call runInternal directly to avoid exiting immediately
|
|
||||||
try {
|
|
||||||
System.setProperty("fabric-api.datagen", "true");
|
|
||||||
Method method = FabricDataGenHelper.class.getDeclaredMethod("runInternal");
|
|
||||||
method.setAccessible(true);
|
|
||||||
method.invoke(null);
|
|
||||||
} catch(Throwable e) {
|
|
||||||
ModernFix.LOGGER.error("Error running datagen", e);
|
|
||||||
} finally {
|
|
||||||
System.clearProperty("fabric-api.datagen");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
if(!SHOULD_RUNTIME_DATAGEN)
|
|
||||||
return;
|
|
||||||
ScreenEvents.AFTER_INIT.register(((client, s, scaledWidth, scaledHeight) -> {
|
|
||||||
if(s instanceof TitleScreen screen) {
|
|
||||||
screen.addRenderableWidget(Button.builder(Component.literal("DG"), (arg) -> {
|
|
||||||
runRuntimeDatagen();
|
|
||||||
}).pos(screen.width / 2 - 100 - 50, screen.height / 4 + 48).size(50, 20).build());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
package org.embeddedt.modernfix.fabric.mixin.core;
|
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientPacketListener;
|
|
||||||
import org.embeddedt.modernfix.ModernFixClientFabric;
|
|
||||||
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(value = ClientPacketListener.class, priority = 1500)
|
|
||||||
public class ClientPlayNetHandlerMixin {
|
|
||||||
@Inject(method = "handleUpdateRecipes", at = @At("RETURN"))
|
|
||||||
private void signalRecipes(CallbackInfo ci) {
|
|
||||||
ModernFixClientFabric.commonMod.onRecipesUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "handleUpdateTags", at = @At("RETURN"))
|
|
||||||
private void signalTags(CallbackInfo ci) {
|
|
||||||
ModernFixClientFabric.commonMod.onTagsUpdated();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package org.embeddedt.modernfix.fabric.mixin.feature.branding;
|
|
||||||
|
|
||||||
import net.minecraft.client.gui.components.DebugScreenOverlay;
|
|
||||||
import org.embeddedt.modernfix.ModernFixClientFabric;
|
|
||||||
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.List;
|
|
||||||
|
|
||||||
@Mixin(DebugScreenOverlay.class)
|
|
||||||
public class GuiMixin {
|
|
||||||
@Inject(method = "getGameInformation", at = @At("RETURN"))
|
|
||||||
private void addModernFix(CallbackInfoReturnable<List<String>> cir) {
|
|
||||||
cir.getReturnValue().add(ModernFixClientFabric.commonMod.brandingString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
package org.embeddedt.modernfix.fabric.mixin.perf.dynamic_resources;
|
|
||||||
|
|
||||||
import net.fabricmc.fabric.impl.client.model.ModelLoadingRegistryImpl;
|
|
||||||
import net.minecraft.client.resources.model.ModelBakery;
|
|
||||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
|
||||||
import org.embeddedt.modernfix.annotation.RequiresMod;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
@Mixin(ModelLoadingRegistryImpl.LoaderInstance.class)
|
|
||||||
@RequiresMod("fabric-models-v0")
|
|
||||||
@ClientOnlyMixin
|
|
||||||
public class LoaderInstanceMixin {
|
|
||||||
@Redirect(method = "finish", at = @At(value = "FIELD", target = "Lnet/fabricmc/fabric/impl/client/model/ModelLoadingRegistryImpl$LoaderInstance;loader:Lnet/minecraft/client/resources/model/ModelBakery;"))
|
|
||||||
private void keepLoader(ModelLoadingRegistryImpl.LoaderInstance instance, ModelBakery value) {
|
|
||||||
/* allow loading models to happen later */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user