Compare commits
912 Commits
eol/1.19.4
...
1.20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
667ac6c6ee | ||
|
|
2d760eecbb | ||
|
|
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 | ||
|
|
dc4850502d | ||
|
|
88d56c451e | ||
|
|
eff07204ea | ||
|
|
e9f6f3d953 | ||
|
|
a0c5c90c8d | ||
|
|
22559131cc | ||
|
|
6198f0c9d3 | ||
|
|
4b560c7d3d | ||
|
|
501d24ddfd | ||
|
|
eadb19d386 | ||
|
|
ce7b7cbaf2 | ||
|
|
73d2a4405d | ||
|
|
8d766a8cc8 | ||
|
|
fe8d0434c5 | ||
|
|
ae59c2496e | ||
|
|
a19d519b4b | ||
|
|
f7097ec394 | ||
|
|
16e1e37fd9 | ||
|
|
26bdb57972 | ||
|
|
29e3d38c55 | ||
|
|
501e498175 | ||
|
|
b171d4bf19 | ||
|
|
9f5ccf28df | ||
|
|
e946cc896a | ||
|
|
bc7fb1e6d4 | ||
|
|
9964693050 | ||
|
|
4aebee53ac | ||
|
|
74c9f684e6 | ||
|
|
6e00010e9a | ||
|
|
00481145b6 | ||
|
|
ef341932a3 | ||
|
|
5203985a25 | ||
|
|
05cc25c761 | ||
|
|
2f92dbe12c | ||
|
|
fab831f143 | ||
|
|
507cc7eb14 | ||
|
|
be4a6abc81 | ||
|
|
770969ec5c | ||
|
|
712409b4e1 | ||
|
|
32e246885b | ||
|
|
180ac925f7 | ||
|
|
8f31048619 | ||
|
|
858551411b | ||
|
|
4ed5e8a434 | ||
|
|
fa17a5f745 | ||
|
|
823fa259e9 | ||
|
|
8a0ff999bd | ||
|
|
5165cf20c8 | ||
|
|
3df41090e4 | ||
|
|
1b73ded110 | ||
|
|
c2695b1180 | ||
|
|
331ad00758 | ||
|
|
d5a9f68575 | ||
|
|
df854f333c | ||
|
|
13aaab4798 | ||
|
|
19593b5574 | ||
|
|
bf5ca2f3cd | ||
|
|
fa6b93fad2 | ||
|
|
5bd5c8289c | ||
|
|
02fd57f9a4 | ||
|
|
a1a441d397 | ||
|
|
11fd512c5e | ||
|
|
156f0a10cb | ||
|
|
858325e40d | ||
|
|
84bd756cdf | ||
|
|
3e3448adb1 | ||
|
|
3a4b8738fc | ||
|
|
4697704763 | ||
|
|
1884996e53 | ||
|
|
51bf5890d3 | ||
|
|
e1fdca5f41 | ||
|
|
4e3dc99479 | ||
|
|
ae8c062631 | ||
|
|
36ae73316a | ||
|
|
7a648f0c5e | ||
|
|
055892cd62 | ||
|
|
76d1c8e8bd | ||
|
|
c41728b97c | ||
|
|
437eb9f793 | ||
|
|
1889ef2d8c | ||
|
|
90f6f6d68d | ||
|
|
d3bd8289ec | ||
|
|
7b670f229e | ||
|
|
821f87b880 | ||
|
|
4031b65099 | ||
|
|
19df78dbe2 | ||
|
|
c6eb703648 | ||
|
|
a5fb8575a6 | ||
|
|
374aac7ff5 | ||
|
|
5ed2faebc4 | ||
|
|
174e609526 | ||
|
|
63111ed938 | ||
|
|
4a0219e9da | ||
|
|
b7a1f16a9d | ||
|
|
d69f5ac18d | ||
|
|
3162a531b7 | ||
|
|
7d7e2f029d | ||
|
|
9eed139cc9 | ||
|
|
30f483245c | ||
|
|
5b716dcdf2 | ||
|
|
ed7bd69116 | ||
|
|
e34beb5775 | ||
|
|
c51b0585a2 | ||
|
|
e86787c663 | ||
|
|
9a5edb9431 | ||
|
|
d9f93ec67e | ||
|
|
ff292cfabc | ||
|
|
faccdb0ed7 | ||
|
|
dc0ea4d828 | ||
|
|
1531762524 | ||
|
|
a799afc19c | ||
|
|
eecf9f0726 | ||
|
|
ad1df59c18 | ||
|
|
804cd04778 | ||
|
|
083664d858 | ||
|
|
c98721056d | ||
|
|
e4cce437d4 | ||
|
|
e5152439ef | ||
|
|
73e8631fcd | ||
|
|
6548d5da80 | ||
|
|
55c9b8e322 | ||
|
|
2bf51c2034 | ||
|
|
944e481f32 | ||
|
|
b409db3e29 | ||
|
|
a5ae6e0631 | ||
|
|
61288a17ac | ||
|
|
9a966cfad9 | ||
|
|
489d3abe65 | ||
|
|
b3f00a0681 | ||
|
|
827d09289d | ||
|
|
7e25e31d33 | ||
|
|
540b19c141 | ||
|
|
05e66b2492 | ||
|
|
7d8dd5ab09 | ||
|
|
1a7909aaa4 | ||
|
|
e56898fff4 | ||
|
|
60d06c92f6 | ||
|
|
e65727937c | ||
|
|
5e7421ab24 | ||
|
|
6300dd6737 | ||
|
|
ce8c6e0283 | ||
|
|
ba7bda0b04 | ||
|
|
a7d0d4892f | ||
|
|
079c348948 | ||
|
|
2a16229056 | ||
|
|
5aeea48413 | ||
|
|
7632647e1d | ||
|
|
c33fe2e928 | ||
|
|
3237de7e33 | ||
|
|
3be36f826c | ||
|
|
7464cf7420 | ||
|
|
a6cce0c043 | ||
|
|
840cad23b8 | ||
|
|
463bd4ef51 | ||
|
|
ca60a13bb4 | ||
|
|
54f55ee257 | ||
|
|
7a2d38f2f1 | ||
|
|
af486998d2 | ||
|
|
4fd4de7d52 | ||
|
|
8e467e8239 | ||
|
|
2bad1c0226 | ||
|
|
b755d745c5 | ||
|
|
99acb17d90 | ||
|
|
565dfb4b81 | ||
|
|
63bcdb1019 | ||
|
|
1f589901d6 | ||
|
|
9bc6b7b0f7 | ||
|
|
c1c0010126 | ||
|
|
6006026deb | ||
|
|
683f9fd13b | ||
|
|
18e1cec611 | ||
|
|
42d41eb0f7 | ||
|
|
75bd0a5ff7 | ||
|
|
9aa96b4d03 | ||
|
|
761417833e | ||
|
|
173d71b04f | ||
|
|
d352c9e8ca | ||
|
|
e5cc8ef97c | ||
|
|
a7e5147104 | ||
|
|
cc6191cef2 | ||
|
|
002ff639a0 | ||
|
|
79c4b1cb27 | ||
|
|
b1bfcfb190 | ||
|
|
7ab2dac645 | ||
|
|
7bbc10acc3 | ||
|
|
ca4f2a2aeb | ||
|
|
aedbc88e3c | ||
|
|
f9cbd8fa61 | ||
|
|
adbf61a06b | ||
|
|
b44739e33c | ||
|
|
30bf3afc1d | ||
|
|
57947441ce | ||
|
|
f1515c5063 |
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
|
||||
134
.github/workflows/gradle.yml
vendored
134
.github/workflows/gradle.yml
vendored
|
|
@ -10,35 +10,127 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
issues: write
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/1.') }}
|
||||
gradle-home-cache-cleanup: true
|
||||
- name: Setup project Loom cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
.gradle/loom-cache
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/gradle.properties', '**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||
restore-keys: ${{ runner.os }}-gradle-
|
||||
- name: Build ModernFix
|
||||
java-version: 21
|
||||
check-latest: true
|
||||
- name: Check if release branch
|
||||
id: check_branch
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew build
|
||||
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@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Package
|
||||
path: bin
|
||||
|
|
|
|||
42
.github/workflows/release.yml
vendored
42
.github/workflows/release.yml
vendored
|
|
@ -1,42 +0,0 @@
|
|||
name: Release ModernFix Artifacts
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.repository_owner == 'embeddedt'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Remove tags for release on other versions
|
||||
run: ./scripts/tagcleaner.sh
|
||||
- name: Build and publish mod to CurseForge & Modrinth
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew forge:publishToModSites fabric:publishToModSites forge:copyJarToBin fabric:copyJarToBin
|
||||
env:
|
||||
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
- name: Upload assets to GitHub
|
||||
uses: AButler/upload-release-assets@v2.0
|
||||
with:
|
||||
files: 'bin/*'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Add changelog to release
|
||||
uses: irongut/EditRelease@v1.2.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: ${{ github.event.release.id }}
|
||||
replacebody: true
|
||||
files: "CHANGELOG.md"
|
||||
6
.github/workflows/wiki_update.yml
vendored
6
.github/workflows/wiki_update.yml
vendored
|
|
@ -8,10 +8,10 @@ on:
|
|||
jobs:
|
||||
wikigen:
|
||||
if: github.repository_owner == 'embeddedt'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Generate Markdown Patch-List
|
||||
|
|
@ -24,4 +24,4 @@ jobs:
|
|||
with:
|
||||
path: "doc/generated"
|
||||
env:
|
||||
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.WIKI_TOKEN }}
|
||||
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.WIKI_TOKEN }}
|
||||
|
|
|
|||
|
|
@ -2,16 +2,15 @@ ModernFix is a standard Minecraft-style Gradle project powered by Architectury L
|
|||
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 17 to develop ModernFix as the toolchain requires it. Nonetheless, the 1.16 mod JARs will work on
|
||||
a Minecraft instance with Java 8.
|
||||
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.
|
||||
For instance, a PR optimizing new worldgen should be made to 1.18 (not 1.19 or 1.20) while a PR optimizing something
|
||||
like recipes should be made to 1.16 (the oldest supported version).
|
||||
* 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
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -6,11 +6,11 @@ Some fixes are based on prior work in various Forge PRs (check commit history an
|
|||
is directly derived from Sodium and used under the terms of the LGPL-3.0 license.
|
||||
|
||||
## Development builds (generally stable, but may occasionally have bugs)
|
||||
- 1.16.5, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.16/Package.zip
|
||||
- 1.18.2, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.18/Package.zip
|
||||
- 1.19.2, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.19.2/Package.zip
|
||||
- 1.19.4, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.19.4/Package.zip
|
||||
- 1.20.1, both modloaders: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.20/Package.zip
|
||||
- 1.16.5: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.16/Package.zip
|
||||
- 1.18.2: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.18/Package.zip
|
||||
- 1.19.2: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.19.2/Package.zip
|
||||
- 1.20.1: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.20/Package.zip
|
||||
- 1.20.2: https://nightly.link/embeddedt/ModernFix/workflows/gradle/1.20.2/Package.zip
|
||||
|
||||
------------
|
||||
|
||||
|
|
|
|||
54
annotation-processor/build.gradle
Normal file
54
annotation-processor/build.gradle
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
plugins {
|
||||
id 'com.gradleup.shadow' version '8.3.9'
|
||||
id 'java-library'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url uri("https://maven.fabricmc.net") }
|
||||
maven { url "https://maven.neoforged.net/releases" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
|
||||
compileOnly 'com.google.auto.service:auto-service:1.1.1'
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
shadow 'com.google.code.gson:gson:2.10.1'
|
||||
implementation 'com.google.auto:auto-common:1.2.1'
|
||||
shadow 'com.google.auto:auto-common:1.2.1'
|
||||
implementation 'com.google.guava:guava:21.0'
|
||||
shadow 'com.google.guava:guava:21.0'
|
||||
|
||||
implementation project(":annotations")
|
||||
shadow project(":annotations")
|
||||
// Shadow annotations
|
||||
implementation 'net.fabricmc:sponge-mixin:0.12.5+'
|
||||
implementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}"
|
||||
implementation 'net.minecraftforge:mergetool:1.1.7'
|
||||
implementation 'net.neoforged:mergetool:2.0.2'
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.release = 17
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
dependencies {
|
||||
include(dependency('net.fabricmc:sponge-mixin:'))
|
||||
include(dependency('net.fabricmc:fabric-loader:'))
|
||||
include(dependency(':mergetool:'))
|
||||
}
|
||||
// shadowJar bug
|
||||
include '*.jar'
|
||||
include 'META-INF/services/javax.annotation.processing.Processor'
|
||||
include 'META-INF/gradle/incremental.annotation.processors'
|
||||
include 'org/spongepowered/asm/mixin/Mixin.class'
|
||||
include 'org/fury_phoenix/**/*'
|
||||
include {it.getName() == 'OnlyIn.class'}
|
||||
include {it.getName() == 'Dist.class'}
|
||||
include {it.getName() == 'Environment.class'}
|
||||
include {it.getName() == 'EnvType.class'}
|
||||
}
|
||||
|
||||
version = '1.1.4'
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
package org.fury_phoenix.mixinAp.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
import org.embeddedt.modernfix.annotation.ClientOnlyMixin;
|
||||
import org.embeddedt.modernfix.annotation.IgnoreMixin;
|
||||
import org.fury_phoenix.mixinAp.util.TypedAccessorMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
|
||||
import static java.util.AbstractMap.SimpleImmutableEntry;
|
||||
|
||||
public class ClientMixinValidator {
|
||||
|
||||
private final Messager messager;
|
||||
|
||||
private final Elements elemUtils;
|
||||
|
||||
private final Types types;
|
||||
|
||||
private final boolean debug;
|
||||
|
||||
private static final TypedAccessorMap<Annotation> markers = new TypedAccessorMap<>();
|
||||
|
||||
private static final Map.Entry<Class<Environment>, Function<? super Environment, ?>>
|
||||
FabricAccessor = new SimpleImmutableEntry<>(Environment.class, Environment::value);
|
||||
|
||||
private static final Map.Entry<
|
||||
Class<net.minecraftforge.api.distmarker.OnlyIn>,
|
||||
Function<? super net.minecraftforge.api.distmarker.OnlyIn, ?>>
|
||||
ForgeAccessor = new SimpleImmutableEntry<>(
|
||||
net.minecraftforge.api.distmarker.OnlyIn.class,
|
||||
net.minecraftforge.api.distmarker.OnlyIn::value
|
||||
);
|
||||
|
||||
private static final Map.Entry<
|
||||
Class<net.neoforged.api.distmarker.OnlyIn>,
|
||||
Function<? super net.neoforged.api.distmarker.OnlyIn, ?>>
|
||||
NeoForgeAccessor = new SimpleImmutableEntry<>(
|
||||
net.neoforged.api.distmarker.OnlyIn.class,
|
||||
net.neoforged.api.distmarker.OnlyIn::value
|
||||
);
|
||||
|
||||
static {
|
||||
markers.put(FabricAccessor);
|
||||
markers.put(ForgeAccessor);
|
||||
markers.put(NeoForgeAccessor);
|
||||
}
|
||||
|
||||
private static final Collection<String> unannotatedClasses = new HashSet<>();
|
||||
|
||||
public ClientMixinValidator(ProcessingEnvironment env) {
|
||||
debug = Boolean.valueOf(env.getOptions().get("org.fury_phoenix.mixinAp.validator.debug"));
|
||||
messager = env.getMessager();
|
||||
elemUtils = env.getElementUtils();
|
||||
types = env.getTypeUtils();
|
||||
}
|
||||
|
||||
public boolean validateMixin(TypeElement annotatedMixinClass) {
|
||||
return targetsClient(annotatedMixinClass) &&
|
||||
(annotatedMixinClass.getAnnotation(ClientOnlyMixin.class) == null);
|
||||
}
|
||||
|
||||
public boolean targetsClient(TypeElement annotatedMixinClass) {
|
||||
return targetsClient(getTargets(annotatedMixinClass)) &&
|
||||
!isIgnored(annotatedMixinClass);
|
||||
}
|
||||
|
||||
private boolean targetsClient(Collection<?> classTargets) {
|
||||
return classTargets.stream().anyMatch(this::targetsClient);
|
||||
}
|
||||
|
||||
private boolean targetsClient(Object classTarget) {
|
||||
if (classTarget instanceof TypeElement te) {
|
||||
return isClientMarked(te);
|
||||
} else if (classTarget instanceof TypeMirror tm) {
|
||||
var el = types.asElement(tm);
|
||||
return el != null ? targetsClient(el) : warn("TypeMirror of " + tm);
|
||||
} else if (classTarget instanceof String s) {
|
||||
var te = elemUtils.getTypeElement(toSourceString(s.split("\\$")[0]));
|
||||
return te != null ? targetsClient(te) : warn(s);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled type: "
|
||||
+ classTarget.getClass() + "\n" + "Stringified contents: "
|
||||
+ classTarget.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isClientMarked(TypeElement te) {
|
||||
for (var entry : markers.entrySet()) {
|
||||
var marker = te.getAnnotation(entry.getKey());
|
||||
if(marker == null) continue;
|
||||
|
||||
return entry.getValue().apply(marker).toString().equals("CLIENT");
|
||||
}
|
||||
if(debug && unannotatedClasses.add(te.toString())) {
|
||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
||||
"No marker annotations present on " + te + "!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isIgnored(TypeElement te) {
|
||||
if(te.getAnnotation(IgnoreMixin.class) != null) {
|
||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
||||
toSourceString(te.toString()) + " is ignored!");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean warn(Object o) {
|
||||
messager.printMessage(Diagnostic.Kind.WARNING,
|
||||
toSourceString(o.toString()) + " can't be loaded, so it is skipped!");
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map.Entry<? extends CharSequence, ? extends CharSequence>
|
||||
getClientMixinEntry(TypeElement annotatedMixinClass) {
|
||||
return new SimpleImmutableEntry<>(
|
||||
annotatedMixinClass.getQualifiedName(),
|
||||
getTargets(annotatedMixinClass)
|
||||
.stream()
|
||||
.filter(this::targetsClient)
|
||||
.map(Object::toString)
|
||||
.map(ClientMixinValidator::toSourceString)
|
||||
.collect(Collectors.joining(", "))
|
||||
);
|
||||
}
|
||||
|
||||
private Collection<Object> getTargets(TypeElement annotatedMixinClass) {
|
||||
Collection<? extends TypeMirror> clzsses = Set.of();
|
||||
Collection<? extends String> imaginaries = Set.of();
|
||||
TypeMirror MixinElement = elemUtils.getTypeElement(Mixin.class.getName()).asType();
|
||||
for (var mirror : annotatedMixinClass.getAnnotationMirrors()) {
|
||||
if(!types.isSameType(mirror.getAnnotationType(), MixinElement))
|
||||
continue;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
var wrappedClzss = (List<? extends AnnotationValue>)
|
||||
getAnnotationValue(mirror, "value").getValue();
|
||||
|
||||
clzsses = wrappedClzss.stream()
|
||||
.map(AnnotationValue::getValue)
|
||||
.map(TypeMirror.class::cast)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
var wrappedStrings = (List<? extends AnnotationValue>)
|
||||
getAnnotationValue(mirror, "targets").getValue();
|
||||
|
||||
imaginaries = wrappedStrings.stream()
|
||||
.map(AnnotationValue::getValue)
|
||||
.map(String.class::cast)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
return Stream.of(clzsses, imaginaries)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static String toSourceString(String bytecodeName) {
|
||||
return bytecodeName.replaceAll("\\/", ".");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
package org.fury_phoenix.mixinAp.annotation;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedOptions;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
import org.fury_phoenix.mixinAp.config.MixinConfig;
|
||||
|
||||
@SupportedAnnotationTypes({"org.spongepowered.asm.mixin.Mixin", "org.embeddedt.modernfix.annotation.ClientOnlyMixin"})
|
||||
@SupportedOptions({"rootProject.name", "project.name", "org.fury_phoenix.mixinAp.validator.debug"})
|
||||
@AutoService(Processor.class)
|
||||
public class MixinProcessor extends AbstractProcessor {
|
||||
|
||||
// Remember to call toString when using aliases
|
||||
private static final Map<String, String> aliases = Map.of(
|
||||
"Mixin", "mixins",
|
||||
"ClientOnlyMixin", "client"
|
||||
);
|
||||
|
||||
private final Map<String, List<String>> mixinConfigList = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public SourceVersion getSupportedSourceVersion() {
|
||||
return SourceVersion.latest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
try {
|
||||
if(roundEnv.processingOver()){
|
||||
filterMixinSets();
|
||||
// create record for serialization, compute package name
|
||||
String packageName = Optional.ofNullable(mixinConfigList.get("mixins"))
|
||||
.orElse(mixinConfigList.get("client"))
|
||||
.get(0).split("(?<=mixin)")[0];
|
||||
finalizeMixinConfig();
|
||||
new MixinConfig(packageName,
|
||||
mixinConfigList.get("mixins"),
|
||||
mixinConfigList.get("client")
|
||||
).generateMixinConfig(processingEnv);
|
||||
} else {
|
||||
processMixins(annotations, roundEnv);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Fatal error:" +
|
||||
Throwables.getStackTraceAsString(e));
|
||||
throw new RuntimeException(e);
|
||||
// Halt the AP to prevent nonsense errors
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processMixins(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
for (TypeElement annotation : annotations) {
|
||||
Set<? extends Element> annotatedMixins = roundEnv.getElementsAnnotatedWith(annotation);
|
||||
|
||||
Stream<TypeElement> mixinStream =
|
||||
annotatedMixins.stream()
|
||||
.map(TypeElement.class::cast);
|
||||
|
||||
validateCommonMixins(annotation, mixinStream);
|
||||
|
||||
List<String> mixins =
|
||||
annotatedMixins.stream()
|
||||
.map(TypeElement.class::cast)
|
||||
.map(e -> processingEnv.getElementUtils().getBinaryName(e).toString())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
mixinConfigList.putIfAbsent(aliases.get(annotation.getSimpleName().toString()), mixins);
|
||||
}
|
||||
}
|
||||
|
||||
private void filterMixinSets() {
|
||||
List<String> commonSet = mixinConfigList.get("mixins");
|
||||
if(commonSet == null) return;
|
||||
commonSet.removeAll(mixinConfigList.get("client"));
|
||||
}
|
||||
|
||||
private void validateCommonMixins(TypeElement annotation, Stream<TypeElement> mixins) {
|
||||
if(!annotation.getSimpleName().toString().equals("Mixin"))
|
||||
return;
|
||||
ClientMixinValidator validator = new ClientMixinValidator(processingEnv);
|
||||
// The implementation may throw a CME
|
||||
mixins.sequential()
|
||||
.filter(validator::validateMixin)
|
||||
.map(validator::getClientMixinEntry)
|
||||
.forEach(this::logClientClassTarget);
|
||||
}
|
||||
|
||||
private void logClientClassTarget(Map.Entry<? extends CharSequence, ? extends CharSequence> mixin) {
|
||||
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
|
||||
"Mixin " + mixin.getKey() + " targets client-side classes: " + mixin.getValue());
|
||||
}
|
||||
|
||||
private void finalizeMixinConfig() {
|
||||
// relativize class names
|
||||
for(var list : mixinConfigList.values()) {
|
||||
list.replaceAll(className -> className.split("(?<=mixin.)")[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package org.fury_phoenix.mixinAp.config;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
public record MixinConfig(
|
||||
boolean required,
|
||||
String minVersion,
|
||||
@SerializedName("package")
|
||||
String packageName,
|
||||
String plugin,
|
||||
String compatibilityLevel,
|
||||
@SerializedName("mixins")
|
||||
List<String> commonMixins,
|
||||
@SerializedName("client")
|
||||
List<String> clientMixins,
|
||||
InjectorOptions injectors, OverwriteOptions overwrites
|
||||
) {
|
||||
public MixinConfig(String packageName, List<String> commonMixins, List<String> clientMixins) {
|
||||
this(true, "0.8", packageName, "org.embeddedt.modernfix.core.ModernFixMixinPlugin", "JAVA_17",
|
||||
commonMixins, clientMixins, InjectorOptions.DEFAULT, OverwriteOptions.DEFAULT);
|
||||
}
|
||||
public record InjectorOptions(int defaultRequire) {
|
||||
public static final InjectorOptions DEFAULT = new InjectorOptions(1);
|
||||
}
|
||||
public record OverwriteOptions(boolean conformVisibility) {
|
||||
public static final OverwriteOptions DEFAULT = new OverwriteOptions(true);
|
||||
}
|
||||
|
||||
public void generateMixinConfig(ProcessingEnvironment env) throws IOException {
|
||||
try (
|
||||
Writer mixinConfigWriter = env.getFiler()
|
||||
.createResource(StandardLocation.SOURCE_OUTPUT, "",
|
||||
MixinConfig.computeMixinConfigPath(
|
||||
Optional.of(env.getOptions().get("rootProject.name")),
|
||||
Optional.ofNullable(env.getOptions().get("project.name"))
|
||||
)
|
||||
).openWriter()
|
||||
) {
|
||||
String mixinConfig = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
.toJson(this);
|
||||
|
||||
mixinConfigWriter.write(mixinConfig);
|
||||
mixinConfigWriter.write("\n");
|
||||
} catch (IOException e) { throw e; }
|
||||
}
|
||||
|
||||
private static String computeMixinConfigPath(Optional<String> rootProjectName, Optional<String> projectName) {
|
||||
return "resources/" +
|
||||
rootProjectName.get() +
|
||||
(projectName.isPresent() ? "-" : "") +
|
||||
projectName.orElse("") +
|
||||
".mixins.json";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package org.fury_phoenix.mixinAp.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* Type-safe heterogenous map of accessors
|
||||
* @author Fury_Phoenix
|
||||
* @reason Type-safety since K, V of Map are non-identical
|
||||
* @param <SuperType> The supertype of desired types.
|
||||
* This is useful in cases such as <A extends Annotation>.
|
||||
*/
|
||||
public class TypedAccessorMap<SuperType> {
|
||||
private final Map<Class<? extends SuperType>, Function<Object, ?>> typedAccessors = new HashMap<>();
|
||||
|
||||
public <T extends SuperType> void put(Class<T> key, Function<? super T, ?> func) {
|
||||
Objects.requireNonNull(func);
|
||||
typedAccessors.put(Objects.requireNonNull(key), o -> func.apply(key.cast(o)));
|
||||
}
|
||||
|
||||
public <T extends SuperType> void put(Entry<Class<T>, Function<? super T, ?>> entry) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
public <T extends SuperType> Function<Object, ?> get(Class<T> key) {
|
||||
return typedAccessors.get(key);
|
||||
}
|
||||
|
||||
public Set<Entry<Class<? extends SuperType>, Function<Object, ?>>> entrySet() {
|
||||
return typedAccessors.entrySet();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.fury_phoenix.mixinAp.annotation.MixinProcessor,aggregating
|
||||
10
annotations/build.gradle.kts
Normal file
10
annotations/build.gradle.kts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
plugins {
|
||||
id("java")
|
||||
}
|
||||
|
||||
version = "1.1.0"
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
|
||||
public enum FeatureLevel {
|
||||
GA, BETA;
|
||||
|
||||
public boolean isAtLeast(FeatureLevel required) {
|
||||
return this.ordinal() >= required.ordinal();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface IgnoreMixin {
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package org.embeddedt.modernfix.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
||||
public @interface RequiresFeatureLevel {
|
||||
FeatureLevel value() default FeatureLevel.GA;
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target(ElementType.TYPE)
|
||||
@Target({ElementType.TYPE, ElementType.PACKAGE})
|
||||
public @interface RequiresMod {
|
||||
String value() default "";
|
||||
}
|
||||
67
build.gradle
67
build.gradle
|
|
@ -1,67 +0,0 @@
|
|||
plugins {
|
||||
id "architectury-plugin" version "3.4-SNAPSHOT"
|
||||
id "dev.architectury.loom" version "1.3-SNAPSHOT" apply false
|
||||
id "maven-publish"
|
||||
id 'com.matthewprenger.cursegradle' version '1.4.0' apply false
|
||||
id 'com.palantir.git-version' version '1.0.0'
|
||||
id 'org.ajoberstar.grgit' version '5.2.0'
|
||||
id 'se.bjurr.gitchangelog.git-changelog-gradle-plugin' version '1.79.0'
|
||||
id "com.modrinth.minotaur" version "2.+" apply false
|
||||
id("com.diffplug.spotless") version "6.18.0" apply false
|
||||
id 'modernfix.common-conventions' apply false
|
||||
}
|
||||
|
||||
architectury {
|
||||
minecraft = rootProject.minecraft_version
|
||||
}
|
||||
|
||||
ext.archives_base_name = 'modernfix'
|
||||
|
||||
apply plugin: 'modernfix.common-conventions'
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
// 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
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
tasks.register('generateChangelog', se.bjurr.gitchangelog.plugin.gradle.GitChangelogTask) {
|
||||
def details = versionDetails();
|
||||
def theVersionRef
|
||||
if (details.commitDistance > 0) {
|
||||
theVersionRef = details.lastTag;
|
||||
} else {
|
||||
def secondLastTagCmd = "git describe --abbrev=0 " + details.lastTag + "^"
|
||||
def secondLastTag = secondLastTagCmd.execute().text.trim()
|
||||
theVersionRef = secondLastTag;
|
||||
}
|
||||
|
||||
fromRef = theVersionRef
|
||||
|
||||
file = new File("${rootDir}/CHANGELOG.md");
|
||||
templateContent = new File("${rootDir}/gradle/changelog.mustache").getText('UTF-8').replace("[[modernFixVersionRef]]", theVersionRef);
|
||||
toCommit = "HEAD";
|
||||
}
|
||||
|
||||
tasks.register('checkCleanTag') {
|
||||
doLast {
|
||||
def details = versionDetails()
|
||||
if (!details.isCleanTag || versionDetails().commitDistance != 0) {
|
||||
throw new GradleException('Not a clean tree.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println "ModernFix: " + version
|
||||
210
build.gradle.kts
Normal file
210
build.gradle.kts
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
plugins {
|
||||
id("net.neoforged.moddev.legacyforge") version("2.0.134")
|
||||
id("me.modmuss50.mod-publish-plugin") version("1.1.0")
|
||||
}
|
||||
|
||||
val minecraft_version = rootProject.properties["minecraft_version"].toString()
|
||||
|
||||
group = "org.embeddedt"
|
||||
|
||||
val gitVersion = providers.of(GitVersionSource::class) {
|
||||
parameters {
|
||||
minecraftVersion.set(minecraft_version)
|
||||
projectDir.set(rootProject.layout.projectDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
version = gitVersion.get()
|
||||
|
||||
base.archivesName = "modernfix-forge"
|
||||
|
||||
legacyForge {
|
||||
enable {
|
||||
forgeVersion = rootProject.properties["forge_version"].toString()
|
||||
isDisableRecompilation = System.getenv("CI") == "true"
|
||||
}
|
||||
|
||||
rootProject.properties["parchment_version"]?.let { parchmentVer ->
|
||||
parchment {
|
||||
minecraftVersion = minecraft_version
|
||||
mappingsVersion = parchmentVer.toString()
|
||||
}
|
||||
}
|
||||
|
||||
runs {
|
||||
create("client") {
|
||||
client()
|
||||
}
|
||||
create("server") {
|
||||
server()
|
||||
}
|
||||
create("auditClient") {
|
||||
client()
|
||||
jvmArguments.addAll("-Dmodernfix.auditAndExit=true", "-Djava.awt.headless=true")
|
||||
}
|
||||
}
|
||||
|
||||
mods {
|
||||
create("modernfix") {
|
||||
sourceSet(sourceSets.main.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mixin {
|
||||
add(sourceSets.main.get(), "modernfix.refmap.json")
|
||||
config("modernfix-modernfix.mixins.json")
|
||||
}
|
||||
|
||||
tasks.named<Jar>("jar") {
|
||||
manifest.attributes(mapOf(
|
||||
"MixinConfigs" to "modernfix-modernfix.mixins.json",
|
||||
"Specification-Version" to "1",
|
||||
"Implementation-Title" to project.name,
|
||||
"Implementation-Version" to version
|
||||
))
|
||||
}
|
||||
|
||||
java {
|
||||
val curSourceCompatLevel = JavaVersion.VERSION_17
|
||||
sourceCompatibility = curSourceCompatLevel
|
||||
targetCompatibility = curSourceCompatLevel
|
||||
}
|
||||
|
||||
repositories {
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
// location of the maven that hosts JEI files
|
||||
name = "Progwml6 maven"
|
||||
url = uri("https://dvs1.progwml6.com/files/maven/")
|
||||
}
|
||||
}
|
||||
forRepository {
|
||||
maven {
|
||||
name = "ModMaven"
|
||||
url = uri("https://modmaven.dev")
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup("mezz.jei")
|
||||
}
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven("https://cursemaven.com")
|
||||
}
|
||||
filter {
|
||||
includeGroup("curse.maven")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val embed by configurations.creating {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
isTransitive = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":annotations"))
|
||||
embed(project(":annotations"))
|
||||
"additionalRuntimeClasspath"(project(":annotations"))
|
||||
annotationProcessor(project(path = ":annotation-processor", configuration = "shadow"))
|
||||
|
||||
val mixinextrasVersion = rootProject.properties["mixinextras_version"].toString()
|
||||
implementation("io.github.llamalad7:mixinextras-common:${mixinextrasVersion}")
|
||||
annotationProcessor("net.fabricmc:sponge-mixin:0.12.5+mixin.0.8.5")
|
||||
annotationProcessor("io.github.llamalad7:mixinextras-common:${mixinextrasVersion}")
|
||||
implementation("io.github.llamalad7:mixinextras-forge:${mixinextrasVersion}")
|
||||
"jarJar"("io.github.llamalad7:mixinextras-forge:${mixinextrasVersion}")
|
||||
|
||||
val jei_version = rootProject.properties["jei_version"].toString()
|
||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-forge:${jei_version}")
|
||||
modCompileOnly("curse.maven:spark-361579:${rootProject.properties["spark_version"].toString()}")
|
||||
modCompileOnly("curse.maven:ctm-267602:${rootProject.properties["ctm_version"].toString()}")
|
||||
modCompileOnly("curse.maven:ldlib-626676:${rootProject.properties["ldlib_version"].toString()}")
|
||||
modCompileOnly("curse.maven:supermartijncore-454372:4455391")
|
||||
modCompileOnly("curse.maven:patchouli-306770:6164575")
|
||||
modCompileOnly("curse.maven:cofhcore-69162:5374122")
|
||||
modCompileOnly("curse.maven:resourcefullib-570073:5659871")
|
||||
modCompileOnly("curse.maven:kubejs-238086:5853326")
|
||||
modCompileOnly("curse.maven:terrablender-563928:6290448")
|
||||
}
|
||||
|
||||
tasks.named<Jar>("jar") {
|
||||
from(embed.map { if (it.isDirectory) it else zipTree(it) })
|
||||
}
|
||||
|
||||
// For the AP
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
if (!name.lowercase().contains("test")) {
|
||||
options.compilerArgs.addAll(
|
||||
listOf(
|
||||
"-ArootProject.name=${rootProject.name}",
|
||||
"-Aproject.name=${project.name}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir(
|
||||
layout.buildDirectory.dir("generated/sources/annotationProcessor/java/main/resources")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named<ProcessResources>("processResources") {
|
||||
dependsOn(tasks.named("compileJava"))
|
||||
|
||||
inputs.property("version", project.version)
|
||||
|
||||
filesMatching("META-INF/mods.toml") {
|
||||
expand("version" to project.version)
|
||||
}
|
||||
}
|
||||
|
||||
val finalJarTask = "reobfJar"
|
||||
|
||||
tasks.register<Copy>("copyJarNameConsistent") {
|
||||
from(tasks.named<Jar>(finalJarTask).get().outputs.files)
|
||||
into(project.file("build/libs"))
|
||||
rename { _ -> "modernfix-" + project.name + "-latest.jar" }
|
||||
}
|
||||
|
||||
tasks.register<Copy>("copyJarToBin") {
|
||||
from(tasks.named<Jar>(finalJarTask).get().outputs.files)
|
||||
into(rootProject.file("bin"))
|
||||
mustRunAfter(tasks.named("copyJarNameConsistent"))
|
||||
}
|
||||
|
||||
tasks.named("build") {
|
||||
dependsOn("copyJarToBin", "copyJarNameConsistent")
|
||||
}
|
||||
|
||||
publishMods {
|
||||
file.set(tasks.named<Jar>(finalJarTask).flatMap { it.archiveFile })
|
||||
displayName.set(tasks.named<Jar>(finalJarTask).flatMap { it.archiveFileName })
|
||||
changelog = "Please check the [GitHub wiki](https://github.com/embeddedt/ModernFix/wiki/Changelog) for major changes."
|
||||
type = STABLE
|
||||
|
||||
modLoaders.add("forge")
|
||||
|
||||
curseforge {
|
||||
projectId = "790626"
|
||||
projectSlug = "modernfix"
|
||||
accessToken = providers.environmentVariable("CURSEFORGE_TOKEN")
|
||||
minecraftVersions.add(minecraft_version)
|
||||
}
|
||||
modrinth {
|
||||
projectId = "nmDcB62a"
|
||||
accessToken = providers.environmentVariable("MODRINTH_TOKEN")
|
||||
minecraftVersions.add(minecraft_version)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("publishMods") {
|
||||
dependsOn(finalJarTask)
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
plugins {
|
||||
id 'groovy-gradle-plugin'
|
||||
}
|
||||
7
buildSrc/build.gradle.kts
Normal file
7
buildSrc/build.gradle.kts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'architectury-plugin'
|
||||
id 'maven-publish'
|
||||
id 'com.diffplug.spotless'
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
removeUnusedImports()
|
||||
}
|
||||
}
|
||||
|
||||
architectury {
|
||||
compileOnly()
|
||||
}
|
||||
|
||||
group = 'org.embeddedt'
|
||||
// extract base version from tag, generate other metadata ourselves
|
||||
def details = versionDetails()
|
||||
def plusIndex = details.lastTag.indexOf("+")
|
||||
if(plusIndex == -1) {
|
||||
plusIndex = details.lastTag.length()
|
||||
}
|
||||
def baseVersion = details.lastTag.substring(0, plusIndex)
|
||||
def dirtyMarker = grgit.status().clean ? "" : ".dirty"
|
||||
def commitHashMarker = details.commitDistance > 0 ? ("." + details.gitHash.substring(0, Math.min(4, details.gitHash.length()))) : ""
|
||||
def preMarker = (details.commitDistance > 0 || !details.isCleanTag) ? ("-beta." + details.commitDistance) : ""
|
||||
if(preMarker.length() > 0) {
|
||||
// bump to next patch release
|
||||
def versionParts = baseVersion.tokenize(".")
|
||||
baseVersion = "${versionParts[0]}.${versionParts[1]}.${versionParts[2].toInteger() + 1}"
|
||||
}
|
||||
def versionString = "${baseVersion}${preMarker}+mc${minecraft_version}${commitHashMarker}${dirtyMarker}"
|
||||
version = versionString
|
||||
archivesBaseName = rootProject.archives_base_name + '-' + project.name
|
||||
|
||||
sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
repositories {
|
||||
maven { url 'https://modmaven.dev/' }
|
||||
maven {
|
||||
url "https://cursemaven.com"
|
||||
content {
|
||||
includeGroup "curse.maven"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
name = 'ParchmentMC'
|
||||
url = 'https://maven.parchmentmc.org'
|
||||
}
|
||||
maven {
|
||||
// Shedaniel's maven (Architectury API)
|
||||
url = "https://maven.architectury.dev"
|
||||
content {
|
||||
includeGroup "me.shedaniel"
|
||||
}
|
||||
}
|
||||
|
||||
maven {
|
||||
// saps.dev Maven (KubeJS and Rhino)
|
||||
url = "https://maven.saps.dev/minecraft"
|
||||
content {
|
||||
includeGroup "dev.latvian.mods"
|
||||
}
|
||||
}
|
||||
maven { // CTM
|
||||
url "https://maven.tterrag.com/"
|
||||
}
|
||||
maven { url 'https://maven.blamejared.com' }
|
||||
repositories {
|
||||
maven {
|
||||
name = "Fuzs Mod Resources"
|
||||
url = "https://raw.githubusercontent.com/Fuzss/modresources/main/maven/"
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url 'https://maven.terraformersmc.com/releases'
|
||||
}
|
||||
maven { url = "https://jitpack.io" }
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
plugins {
|
||||
id 'modernfix.common-conventions'
|
||||
id 'dev.architectury.loom'
|
||||
}
|
||||
|
||||
loom {
|
||||
silentMojangMappingsLicense()
|
||||
accessWidenerPath = file("${rootDir}/common/src/main/resources/modernfix.accesswidener")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
|
||||
mappings loom.layered() {
|
||||
officialMojangMappings()
|
||||
if(rootProject.hasProperty("parchment_version")) {
|
||||
parchment("org.parchmentmc.data:parchment-${minecraft_version}:${parchment_version}@zip")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
plugins {
|
||||
id 'com.matthewprenger.cursegradle'
|
||||
id 'com.modrinth.minotaur'
|
||||
}
|
||||
|
||||
loom {
|
||||
mods {
|
||||
main { // to match the default mod generated for Forge
|
||||
sourceSet project.sourceSets.main
|
||||
sourceSet project(':common').sourceSets.main
|
||||
}
|
||||
}
|
||||
runs {
|
||||
client {
|
||||
vmArgs "-Xmx1G"
|
||||
vmArgs "-Xms1G"
|
||||
property("mixin.debug.export", "true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def copyJarNameConsistent = tasks.register('copyJarNameConsistent', Copy) {
|
||||
from remapJar // shortcut for createJar.outputs.files
|
||||
into project.file("build/libs")
|
||||
rename { name -> "modernfix-" + project.name + "-latest.jar" }
|
||||
}
|
||||
|
||||
def copyJarToBin = tasks.register('copyJarToBin', Copy) {
|
||||
from remapJar // shortcut for createJar.outputs.files
|
||||
into rootProject.file("bin")
|
||||
mustRunAfter "copyJarNameConsistent"
|
||||
}
|
||||
|
||||
tasks.build.dependsOn(copyJarToBin, copyJarNameConsistent)
|
||||
|
||||
def isBeta = project.version.toString().contains("beta")
|
||||
|
||||
curseforge {
|
||||
if (System.getenv("CURSEFORGE_TOKEN") != null) {
|
||||
apiKey = System.getenv("CURSEFORGE_TOKEN")
|
||||
project {
|
||||
id = "790626"
|
||||
changelog = file("${rootDir}/CHANGELOG.md")
|
||||
changelogType = "markdown"
|
||||
releaseType = isBeta ? "beta" : "release"
|
||||
addGameVersion project.name.capitalize()
|
||||
gameVersionStrings.addAll(supported_minecraft_versions.tokenize(","))
|
||||
mainArtifact remapJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modrinth {
|
||||
token = System.getenv("MODRINTH_TOKEN")
|
||||
projectId = "modernfix" // This can be the project ID or the slug. Either will work!
|
||||
versionType = isBeta ? "beta" : "release" // This is the default -- can also be `beta` or `alpha`
|
||||
uploadFile = remapJar
|
||||
gameVersions = supported_minecraft_versions.tokenize(",")
|
||||
loaders = [project.name]
|
||||
changelog.set(provider { file("CHANGELOG.md").getText('UTF-8') })
|
||||
}
|
||||
|
||||
tasks.curseforge.dependsOn(rootProject.generateChangelog)
|
||||
tasks.modrinth.dependsOn(rootProject.generateChangelog)
|
||||
|
||||
tasks.register('publishToModSites') {
|
||||
publishToModSites.dependsOn(tasks.modrinth)
|
||||
publishToModSites.dependsOn(tasks.curseforge)
|
||||
}
|
||||
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,54 +0,0 @@
|
|||
plugins {
|
||||
id "modernfix.mod-common-conventions"
|
||||
}
|
||||
|
||||
architectury {
|
||||
common(rootProject.enabled_platforms.split(","))
|
||||
}
|
||||
|
||||
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}"
|
||||
implementation(annotationProcessor("com.github.llamalad7.mixinextras:mixinextras-common:${rootProject.mixinextras_version}"))
|
||||
|
||||
modCompileOnly("dev.latvian.mods:kubejs:${kubejs_version}") {
|
||||
transitive = false
|
||||
}
|
||||
modApi("dev.latvian.mods:rhino:${rhino_version}") {
|
||||
transitive = false
|
||||
}
|
||||
modApi("me.shedaniel:RoughlyEnoughItems-api:${rei_version}") {
|
||||
transitive = false
|
||||
}
|
||||
modCompileOnly("me.shedaniel:RoughlyEnoughItems-fabric:${rei_version}") {
|
||||
transitive = false
|
||||
}
|
||||
|
||||
modCompileOnly("curse.maven:diagonal-fences-458048:${diagonal_fences_version}")
|
||||
|
||||
modCompileOnly "curse.maven:spark-361579:${rootProject.spark_version}"
|
||||
// compile against the JEI API but do not include it at runtime
|
||||
modCompileOnly("mezz.jei:jei-${minecraft_version}-common:${jei_version}")
|
||||
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}"
|
||||
}
|
||||
|
||||
// don't need remapped common jar
|
||||
tasks.named('remapJar') { enabled = false }
|
||||
|
||||
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,205 +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.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.MemoryReserve;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.embeddedt.modernfix.api.constants.IntegrationConstants;
|
||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
|
||||
import org.embeddedt.modernfix.packet.EntityIDSyncPacket;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree;
|
||||
import org.embeddedt.modernfix.searchtree.REIBackedSearchTree;
|
||||
import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry;
|
||||
import org.embeddedt.modernfix.util.ClassInfoManager;
|
||||
import org.embeddedt.modernfix.world.IntegratedWatchdog;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class ModernFixClient {
|
||||
public static ModernFixClient INSTANCE;
|
||||
public static long worldLoadStartTime = -1;
|
||||
private static int numRenderTicks;
|
||||
|
||||
public static float gameStartTimeSeconds = -1;
|
||||
|
||||
public static boolean recipesUpdated, tagsUpdated = false;
|
||||
|
||||
public String brandingString = null;
|
||||
|
||||
/**
|
||||
* The list of loaded client integrations.
|
||||
*/
|
||||
public static List<ModernFixClientIntegration> CLIENT_INTEGRATIONS = new CopyOnWriteArrayList<>();
|
||||
|
||||
public ModernFixClient() {
|
||||
INSTANCE = this;
|
||||
// clear reserve as it's not needed
|
||||
MemoryReserve.release();
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.branding.F3Screen")) {
|
||||
brandingString = ModernFix.NAME + " " + ModernFixPlatformHooks.INSTANCE.getVersionString();
|
||||
}
|
||||
SearchTreeProviderRegistry.register(JEIBackedSearchTree.PROVIDER);
|
||||
SearchTreeProviderRegistry.register(REIBackedSearchTree.PROVIDER);
|
||||
for(String className : ModernFixPlatformHooks.INSTANCE.getCustomModOptions().get(IntegrationConstants.CLIENT_INTEGRATION_CLASS)) {
|
||||
try {
|
||||
CLIENT_INTEGRATIONS.add((ModernFixClientIntegration)Class.forName(className).getDeclaredConstructor().newInstance());
|
||||
} catch(ReflectiveOperationException | ClassCastException e) {
|
||||
ModernFix.LOGGER.error("Could not instantiate integration {}", className, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void resetWorldLoadStateMachine() {
|
||||
numRenderTicks = 0;
|
||||
worldLoadStartTime = -1;
|
||||
recipesUpdated = false;
|
||||
tagsUpdated = false;
|
||||
}
|
||||
|
||||
public void onGameLaunchFinish() {
|
||||
if(gameStartTimeSeconds >= 0)
|
||||
return;
|
||||
gameStartTimeSeconds = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.measure_time.GameLoad"))
|
||||
ModernFix.LOGGER.warn("Game took " + gameStartTimeSeconds + " seconds to start");
|
||||
ModernFixPlatformHooks.INSTANCE.onLaunchComplete();
|
||||
ClassInfoManager.clear();
|
||||
}
|
||||
|
||||
public void onRecipesUpdated() {
|
||||
recipesUpdated = true;
|
||||
}
|
||||
|
||||
public void onTagsUpdated() {
|
||||
tagsUpdated = true;
|
||||
}
|
||||
|
||||
public void onRenderTickEnd() {
|
||||
if(recipesUpdated
|
||||
&& tagsUpdated
|
||||
&& worldLoadStartTime != -1
|
||||
&& Minecraft.getInstance().player != null
|
||||
&& numRenderTicks++ >= 10) {
|
||||
float timeSpentLoading = ((float)(System.nanoTime() - worldLoadStartTime) / 1000000000f);
|
||||
if(ModernFixMixinPlugin.instance.isOptionEnabled("feature.measure_time.WorldLoad")) {
|
||||
ModernFix.LOGGER.warn("Time from main menu to in-game was " + timeSpentLoading + " seconds");
|
||||
ModernFix.LOGGER.warn("Total time to load game and open world was " + (timeSpentLoading + gameStartTimeSeconds) + " seconds");
|
||||
}
|
||||
resetWorldLoadStateMachine();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the IDs match and remap them if not.
|
||||
* @return true if ID remap was needed
|
||||
*/
|
||||
private static boolean compareAndSwitchIds(Class<? extends Entity> eClass, String fieldName, EntityDataAccessor<?> accessor, int newId) {
|
||||
if(accessor.id != newId) {
|
||||
ModernFix.LOGGER.warn("Corrected ID mismatch on {} field {}. Client had {} but server wants {}.",
|
||||
eClass,
|
||||
fieldName,
|
||||
accessor.id,
|
||||
newId);
|
||||
accessor.id = newId;
|
||||
return true;
|
||||
} else {
|
||||
ModernFix.LOGGER.debug("{} {} ID fine: {}", eClass, fieldName, newId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Horrendous hack to allow tracking every synced entity data manager.
|
||||
*
|
||||
* This is to ensure we can perform ID fixup on already constructed managers.
|
||||
*/
|
||||
public static final Set<SynchedEntityData> allEntityDatas = Collections.newSetFromMap(new WeakHashMap<>());
|
||||
|
||||
private static final Field entriesArrayField;
|
||||
static {
|
||||
Field field;
|
||||
try {
|
||||
field = SynchedEntityData.class.getDeclaredField("entriesArray");
|
||||
field.setAccessible(true);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
field = null;
|
||||
}
|
||||
entriesArrayField = field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extremely hacky method to detect and correct mismatched entity data parameter IDs on the client and server.
|
||||
*
|
||||
* The technique is far from ideal, but it should detect reliably and also not break already constructed entities.
|
||||
*/
|
||||
public static void handleEntityIDSync(EntityIDSyncPacket packet) {
|
||||
Map<Class<? extends Entity>, List<Pair<String, Integer>>> info = packet.getFieldInfo();
|
||||
boolean fixNeeded = false;
|
||||
for(Map.Entry<Class<? extends Entity>, List<Pair<String, Integer>>> entry : info.entrySet()) {
|
||||
Class<? extends Entity> eClass = entry.getKey();
|
||||
for(Pair<String, Integer> field : entry.getValue()) {
|
||||
String fieldName = field.getFirst();
|
||||
int newId = field.getSecond();
|
||||
try {
|
||||
Field f = eClass.getDeclaredField(fieldName);
|
||||
f.setAccessible(true);
|
||||
EntityDataAccessor<?> accessor = (EntityDataAccessor<?>)f.get(null);
|
||||
if(compareAndSwitchIds(eClass, fieldName, accessor, newId))
|
||||
fixNeeded = true;
|
||||
} catch(NoSuchFieldException e) {
|
||||
ModernFix.LOGGER.warn("Couldn't find field on {}: {}", eClass, fieldName);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Unexpected exception", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Now the ID mappings on synced entity data instances are probably all wrong. Fix that. */
|
||||
List<SynchedEntityData> dataEntries;
|
||||
synchronized (allEntityDatas) {
|
||||
if(fixNeeded) {
|
||||
dataEntries = new ArrayList<>(allEntityDatas);
|
||||
for(SynchedEntityData manager : dataEntries) {
|
||||
Int2ObjectOpenHashMap<SynchedEntityData.DataItem<?>> fixedMap = new Int2ObjectOpenHashMap<>();
|
||||
List<SynchedEntityData.DataItem<?>> items = new ArrayList<>(manager.itemsById.values());
|
||||
for(SynchedEntityData.DataItem<?> item : items) {
|
||||
fixedMap.put(item.getAccessor().id, item);
|
||||
}
|
||||
manager.lock.writeLock().lock();
|
||||
try {
|
||||
manager.itemsById.replaceAll((id, parameter) -> fixedMap.get((int)id));
|
||||
if(entriesArrayField != null) {
|
||||
try {
|
||||
SynchedEntityData.DataItem<?>[] dataArray = new SynchedEntityData.DataItem[items.size()];
|
||||
for(int i = 0; i < dataArray.length; i++) {
|
||||
dataArray[i] = fixedMap.get(i);
|
||||
}
|
||||
entriesArrayField.set(manager, dataArray);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
ModernFix.LOGGER.error(e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
manager.lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
allEntityDatas.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void onServerStarted(MinecraftServer server) {
|
||||
if(!ModernFixMixinPlugin.instance.isOptionEnabled("feature.integrated_server_watchdog.IntegratedWatchdog"))
|
||||
return;
|
||||
IntegratedWatchdog watchdog = new IntegratedWatchdog(server);
|
||||
watchdog.start();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
package org.embeddedt.modernfix.blockstate;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.StateHolder;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class FerriteCorePostProcess {
|
||||
private static final boolean willPostProcess;
|
||||
|
||||
private static final MethodHandle theTable, toKeyIndex;
|
||||
|
||||
static {
|
||||
boolean success = true;
|
||||
MethodHandle table = null, keyIndex = null;
|
||||
try {
|
||||
Class<?> fastMap = Class.forName("malte0811.ferritecore.fastmap.FastMap");
|
||||
Field field = fastMap.getDeclaredField("toKeyIndex");
|
||||
field.setAccessible(true);
|
||||
keyIndex = MethodHandles.publicLookup().unreflectSetter(field);
|
||||
field = StateHolder.class.getDeclaredField("ferritecore_globalTable");
|
||||
field.setAccessible(true);
|
||||
table = MethodHandles.publicLookup().unreflectGetter(field);
|
||||
} catch(ReflectiveOperationException | RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
willPostProcess = success;
|
||||
theTable = table;
|
||||
toKeyIndex = keyIndex;
|
||||
}
|
||||
|
||||
private static final Object2IntMap<?> EMPTY_MAP;
|
||||
|
||||
static {
|
||||
Object2IntArrayMap<?> map = new Object2IntArrayMap<>();
|
||||
map.defaultReturnValue(-1);
|
||||
EMPTY_MAP = Object2IntMaps.unmodifiable(map);
|
||||
}
|
||||
|
||||
public static <O, S extends StateHolder<O, S>> void postProcess(StateDefinition<O, S> state) {
|
||||
if(!willPostProcess)
|
||||
return;
|
||||
try {
|
||||
if(state.getProperties().size() == 0) {
|
||||
for(S holder : state.getPossibleStates()) {
|
||||
// deduplicate Object2IntMap objects from FerriteCore
|
||||
// will probably be fixed upstream at some point, but likely not for older versions
|
||||
Object table = theTable.invoke(holder);
|
||||
toKeyIndex.invoke(table, EMPTY_MAP);
|
||||
}
|
||||
}
|
||||
} catch(Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
package org.embeddedt.modernfix.chunk;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SafeBlockGetter implements BlockGetter {
|
||||
private final ServerLevel wrapped;
|
||||
private final Thread mainThread;
|
||||
|
||||
public SafeBlockGetter(ServerLevel wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
this.mainThread = Thread.currentThread();
|
||||
}
|
||||
|
||||
public boolean shouldUse() {
|
||||
return Thread.currentThread() != this.mainThread;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BlockGetter getChunkSafe(BlockPos pos) {
|
||||
// can safely call getChunkForLighting off-thread
|
||||
BlockGetter access = this.wrapped.getChunkSource().getChunkForLighting(pos.getX() >> 4, pos.getZ() >> 4);
|
||||
if(!(access instanceof ChunkAccess))
|
||||
return null;
|
||||
ChunkAccess chunk = (ChunkAccess)access;
|
||||
if(!chunk.getStatus().isOrAfter(ChunkStatus.FULL))
|
||||
return null;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxBuildHeight() {
|
||||
return this.wrapped.getMaxBuildHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLightLevel() {
|
||||
return this.wrapped.getMaxLightLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight() {
|
||||
return this.wrapped.getMinBuildHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return this.wrapped.getHeight();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity getBlockEntity(BlockPos pos) {
|
||||
BlockGetter g = getChunkSafe(pos);
|
||||
return g == null ? null : g.getBlockEntity(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos) {
|
||||
BlockGetter g = getChunkSafe(pos);
|
||||
return g == null ? Blocks.AIR.defaultBlockState() : g.getBlockState(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos pos) {
|
||||
BlockGetter g = getChunkSafe(pos);
|
||||
return g == null ? Fluids.EMPTY.defaultFluidState() : g.getFluidState(pos);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
|
||||
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
@Mixin(value = BlockBehaviour.BlockStateBase.class, priority = 100)
|
||||
public class BlockStateBaseMixin {
|
||||
@ModifyVariable(method = "getOffset", at = @At("HEAD"), argsOnly = true, index = 1)
|
||||
private BlockGetter useSafeGetter(BlockGetter g) {
|
||||
if(g instanceof ISafeBlockGetter) {
|
||||
SafeBlockGetter replacement = ((ISafeBlockGetter) g).mfix$getSafeBlockGetter();
|
||||
if(replacement.shouldUse())
|
||||
return replacement;
|
||||
}
|
||||
return g;
|
||||
}
|
||||
}
|
||||
|
|
@ -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,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.chunk_deadlock;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||
import org.embeddedt.modernfix.duck.ISafeBlockGetter;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
@Mixin(ServerLevel.class)
|
||||
public class ServerLevelMixin implements ISafeBlockGetter {
|
||||
@Unique
|
||||
private final SafeBlockGetter mfix$safeBlockGetter = new SafeBlockGetter((ServerLevel)(Object)this);
|
||||
|
||||
@Override
|
||||
public SafeBlockGetter mfix$getSafeBlockGetter() {
|
||||
return mfix$safeBlockGetter;
|
||||
}
|
||||
}
|
||||
|
|
@ -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,49 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.bugfix.item_cache_flag;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
/**
|
||||
* Remove emptyCacheFlag from ItemStack, as Mojang did in 1.20 due to <a href="https://bugs.mojang.com/browse/MC-258939">MC-258939</a>.
|
||||
*/
|
||||
@Mixin(ItemStack.class)
|
||||
public class ItemStackMixin {
|
||||
@Shadow @Final @Deprecated private Item item;
|
||||
|
||||
/**
|
||||
* @author embeddedt, Mojang
|
||||
* @reason avoid getItem()
|
||||
*/
|
||||
@Redirect(method = "isEmpty", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;getItem()Lnet/minecraft/world/item/Item;"))
|
||||
private Item getItemDirect(ItemStack stack) {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt, Mojang
|
||||
* @reason avoid getItem()
|
||||
*/
|
||||
@Redirect(method = "isEmpty", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;is(Lnet/minecraft/world/item/Item;)Z"))
|
||||
private boolean checkIsDirect(ItemStack stack, Item item) {
|
||||
return this.item == item;
|
||||
}
|
||||
|
||||
@Redirect(method = "*", at = @At(value = "FIELD", opcode = Opcodes.GETFIELD, target = "Lnet/minecraft/world/item/ItemStack;emptyCacheFlag:Z"))
|
||||
private boolean checkEmptyDirect(ItemStack stack) {
|
||||
return stack.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt, Mojang
|
||||
* @reason flag is no longer used
|
||||
*/
|
||||
@Overwrite
|
||||
private void updateEmptyCacheFlag() {}
|
||||
}
|
||||
|
|
@ -1,75 +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.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.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
|
||||
@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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 (requiredStatus == ChunkStatus.LIGHT) {
|
||||
this.distanceManager.addTicket(TicketType.LIGHT, chunkpos, 33 + ChunkStatus.getDistance(ChunkStatus.LIGHT), chunkpos);
|
||||
}
|
||||
|
||||
// from original method
|
||||
if (optional.isPresent() && 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,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,24 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
|
||||
import org.embeddedt.modernfix.registry.DirectStorageRegistryObject;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
@Mixin({ Block.class, Item.class })
|
||||
@IgnoreOutsideDev
|
||||
public class DirectObjectMixin implements DirectStorageRegistryObject {
|
||||
private ResourceLocation mfix$resourceKey;
|
||||
|
||||
@Override
|
||||
public ResourceLocation mfix$getResourceKey() {
|
||||
return mfix$resourceKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mfix$setResourceKey(ResourceLocation key) {
|
||||
mfix$resourceKey = key;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.deduplicate_climate_parameters;
|
||||
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
import org.embeddedt.modernfix.dedup.ClimateCache;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin({ Climate.Parameter.class, Climate.ParameterPoint.class })
|
||||
public class ParameterMixin {
|
||||
@Redirect(method = "*", at = @At(value = "NEW", target = "net/minecraft/world/level/biome/Climate$Parameter"), require = 0)
|
||||
private static Climate.Parameter internParameterStatic(long min, long max) {
|
||||
return ClimateCache.MFIX_INTERNER.intern(new Climate.Parameter(min, max));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.deduplicate_location;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.embeddedt.modernfix.dedup.IdentifierCaches;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ResourceLocation.class)
|
||||
public class MixinResourceLocation {
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
protected String namespace;
|
||||
|
||||
@Mutable
|
||||
@Shadow
|
||||
@Final
|
||||
protected String path;
|
||||
|
||||
@Inject(method = "<init>([Ljava/lang/String;)V", at = @At("RETURN"))
|
||||
private void reinit(String[] id, CallbackInfo ci) {
|
||||
this.namespace = IdentifierCaches.NAMESPACES.deduplicate(this.namespace);
|
||||
this.path = IdentifierCaches.PATH.deduplicate(this.path);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_block_codecs;
|
||||
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.StateHolder;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
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.ModifyVariable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mixin(StateDefinition.class)
|
||||
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
|
||||
@Shadow @Final private O owner;
|
||||
|
||||
@ModifyVariable(method = "<init>", at = @At("HEAD"), ordinal = 0, argsOnly = true)
|
||||
private static <O, S extends StateHolder<O, S>> StateDefinition.Factory<O, S> replaceMapCodec(StateDefinition.Factory<O, S> factory, Function<O, S> function, O object, StateDefinition.Factory<O, S> factory2, Map<String, Property<?>> map) {
|
||||
if(object instanceof Block)
|
||||
return (o, m, c) -> factory.create(o, m, null);
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_block_codecs;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.Decoder;
|
||||
import com.mojang.serialization.Encoder;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.StateHolder;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(StateHolder.class)
|
||||
public class StateHolderMixin {
|
||||
private static final LoadingCache<Block, MapCodec<BlockState>> MODERNFIX_CODEC_CACHE = CacheBuilder.newBuilder()
|
||||
.maximumSize(100000)
|
||||
.build(new CacheLoader<Block, MapCodec<BlockState>>() {
|
||||
@Override
|
||||
public MapCodec<BlockState> load(Block block) throws Exception {
|
||||
Supplier<BlockState> stateSupplier = block::defaultBlockState;
|
||||
MapCodec<BlockState> mapCodec = MapCodec.of(Encoder.empty(), Decoder.unit(stateSupplier));
|
||||
for(Property<?> property : block.getStateDefinition().getProperties()) {
|
||||
mapCodec = StateDefinition.appendPropertyCodec(mapCodec, stateSupplier, property.getName(), property);
|
||||
}
|
||||
return mapCodec;
|
||||
}
|
||||
});
|
||||
|
||||
@Redirect(method = "codec", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Codec;dispatch(Ljava/lang/String;Ljava/util/function/Function;Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;", remap = false))
|
||||
private static <O, S extends StateHolder<O, S>> Codec<S> obtainCodec(Codec<O> codec, String typeKey, Function<S, O> type, Function<O, ? extends Codec<S>> codecFn, Codec<O> codecMethodArg, Function<O, S> stateSupplier) {
|
||||
return codec.dispatch(typeKey, type, block -> {
|
||||
if(block instanceof Block) {
|
||||
S state = stateSupplier.apply(block);
|
||||
if(state.getValues().isEmpty())
|
||||
return Codec.unit(state);
|
||||
MapCodec<S> mapCodec;
|
||||
try {
|
||||
mapCodec = (MapCodec<S>)MODERNFIX_CODEC_CACHE.get((Block)block);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return mapCodec.fieldOf("Properties").codec();
|
||||
} else {
|
||||
return codecFn.apply(block);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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, remap = false))
|
||||
private Object skipUvsForInitialLoad(JsonDeserializationContext context, JsonElement element, Type type) {
|
||||
return UVController.useDummyUv.get() ? UVController.dummyUv : context.deserialize(element, type);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import org.embeddedt.modernfix.dynamicresources.ItemOverrideBakedModel;
|
||||
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(ItemOverrides.class)
|
||||
public class ItemOverridesMixin {
|
||||
@Inject(method = "resolve", at = @At("RETURN"), cancellable = true)
|
||||
private void getRealModel(CallbackInfoReturnable<BakedModel> cir) {
|
||||
BakedModel original = cir.getReturnValue();
|
||||
if(original instanceof ItemOverrideBakedModel) {
|
||||
ItemOverrideBakedModel override = (ItemOverrideBakedModel)original;
|
||||
cir.setReturnValue(override.getRealModel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.dynamic_resources.diagonalfences;
|
||||
|
||||
import fuzs.diagonalfences.api.world.level.block.DiagonalBlock;
|
||||
import fuzs.diagonalfences.client.model.MultipartAppender;
|
||||
import fuzs.diagonalfences.mixin.client.accessor.ModelBakeryAccessor;
|
||||
import net.minecraft.client.renderer.block.model.multipart.MultiPart;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.FenceBlock;
|
||||
import net.minecraft.world.level.block.IronBarsBlock;
|
||||
import org.apache.logging.log4j.util.BiConsumer;
|
||||
import org.embeddedt.modernfix.ModernFixClient;
|
||||
import org.embeddedt.modernfix.annotation.RequiresMod;
|
||||
import org.embeddedt.modernfix.api.entrypoint.ModernFixClientIntegration;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(MultipartAppender.class)
|
||||
@RequiresMod("diagonalfences")
|
||||
public abstract class MultipartAppenderMixin {
|
||||
@Shadow(remap = false)
|
||||
public static void appendDiagonalSelectors(BiConsumer<ResourceLocation, UnbakedModel> modelBakery, MultiPart multiPart, boolean rotateCenter) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
private static boolean handlerInjected = false;
|
||||
|
||||
@Inject(method = "onPrepareModelBaking", at = @At("HEAD"), cancellable = true)
|
||||
private static void setupHelper(CallbackInfo ci) {
|
||||
ci.cancel();
|
||||
if(handlerInjected)
|
||||
return;
|
||||
handlerInjected = true;
|
||||
ModernFixClient.CLIENT_INTEGRATIONS.add(new ModernFixClientIntegration() {
|
||||
@Override
|
||||
public UnbakedModel onUnbakedModelLoad(ResourceLocation location, UnbakedModel originalModel, ModelBakery bakery) {
|
||||
if(originalModel instanceof MultiPart multipart) {
|
||||
Block block = multipart.definition.getOwner();
|
||||
if((block instanceof FenceBlock || block instanceof IronBarsBlock) && block instanceof DiagonalBlock diagonalBlock && diagonalBlock.hasProperties()) {
|
||||
try {
|
||||
appendDiagonalSelectors(((ModelBakeryAccessor)bakery)::diagonalfences$callCacheAndQueueDependencies, multipart, block instanceof IronBarsBlock);
|
||||
} catch(RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return originalModel;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.mojang_registry_size;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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.Map;
|
||||
|
||||
@Mixin(ResourceKey.class)
|
||||
public class ResourceKeyMixin<T> {
|
||||
private static final Map<ResourceLocation, Map<ResourceLocation, ResourceKey<?>>> INTERNING_MAP = new Object2ObjectOpenHashMap<>();
|
||||
@Inject(method = "create(Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/resources/ResourceKey;", at = @At("HEAD"), cancellable = true)
|
||||
private static <T> void createEfficient(ResourceLocation parent, ResourceLocation location, CallbackInfoReturnable<ResourceKey<T>> cir) {
|
||||
synchronized (ResourceKey.class) {
|
||||
Map<ResourceLocation, ResourceKey<?>> keys = INTERNING_MAP.computeIfAbsent(parent, k -> new Object2ObjectOpenHashMap<>());
|
||||
ResourceKey<?> key = keys.get(location);
|
||||
if(key == null) {
|
||||
key = new ResourceKey<>(parent, location);
|
||||
keys.put(location, key);
|
||||
}
|
||||
cir.setReturnValue((ResourceKey<T>)key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.nbt_memory_usage;
|
||||
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.embeddedt.modernfix.util.CanonizingStringMap;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(targets = "net/minecraft/nbt/CompoundTag$1")
|
||||
public class CompoundTag1Mixin {
|
||||
@ModifyVariable(method = "load(Ljava/io/DataInput;ILnet/minecraft/nbt/NbtAccounter;)Lnet/minecraft/nbt/CompoundTag;", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false))
|
||||
private Map<String, Tag> modifyMap(Map<String, Tag> map) {
|
||||
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
|
||||
if(map != null)
|
||||
newMap.putAll(map);
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
package org.embeddedt.modernfix.common.mixin.perf.nbt_memory_usage;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import org.embeddedt.modernfix.util.CanonizingStringMap;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Mixin(CompoundTag.class)
|
||||
public class CompoundTagMixin {
|
||||
@Shadow @Final
|
||||
private Map<String, Tag> tags;
|
||||
|
||||
/**
|
||||
* Ensure that the default backing map is a CanonizingStringMap.
|
||||
*/
|
||||
@ModifyArg(method = "<init>()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;<init>(Ljava/util/Map;)V"), index = 0)
|
||||
private static Map<String, Tag> useCanonizingStringMap(Map<String, Tag> incoming) {
|
||||
CanonizingStringMap<Tag> newMap = new CanonizingStringMap<>();
|
||||
if(incoming != null)
|
||||
newMap.putAll(incoming);
|
||||
return newMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author embeddedt
|
||||
* @reason use more efficient method when copying canonizing string map
|
||||
*/
|
||||
@Inject(method = "copy()Lnet/minecraft/nbt/CompoundTag;", at = @At("HEAD"), cancellable = true)
|
||||
public void copyEfficient(CallbackInfoReturnable<Tag> cir) {
|
||||
if(this.tags instanceof CanonizingStringMap) {
|
||||
cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap<Tag>)this.tags, Tag::copy)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,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,9 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
|
||||
public class ClimateCache {
|
||||
public static final Interner<Climate.Parameter> MFIX_INTERNER = Interners.newStrongInterner();
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
import it.unimi.dsi.fastutil.Hash;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DeduplicationCache<T> {
|
||||
private final ObjectOpenCustomHashSet<T> pool;
|
||||
|
||||
private int attemptedInsertions = 0;
|
||||
private int deduplicated = 0;
|
||||
|
||||
public DeduplicationCache(Hash.Strategy<T> strategy) {
|
||||
this.pool = new ObjectOpenCustomHashSet<>(strategy);
|
||||
}
|
||||
|
||||
public DeduplicationCache() {
|
||||
this.pool = new ObjectOpenCustomHashSet<>(new Hash.Strategy<T>() {
|
||||
@Override
|
||||
public int hashCode(T o) {
|
||||
return Objects.hashCode(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(T a, T b) {
|
||||
return Objects.equals(a, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public synchronized T deduplicate(T item) {
|
||||
this.attemptedInsertions++;
|
||||
|
||||
T result = this.pool.addOrGet(item);
|
||||
|
||||
if (result != item) {
|
||||
this.deduplicated++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void clearCache() {
|
||||
this.attemptedInsertions = 0;
|
||||
this.deduplicated = 0;
|
||||
|
||||
this.pool.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return String.format("DeduplicationCache ( %d/%d de-duplicated, %d pooled )",
|
||||
this.deduplicated, this.attemptedInsertions, this.pool.size());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package org.embeddedt.modernfix.dedup;
|
||||
|
||||
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
public class IdentifierCaches {
|
||||
public static final DeduplicationCache<String> NAMESPACES = new DeduplicationCache<>();
|
||||
public static final DeduplicationCache<String> PATH = new DeduplicationCache<>();
|
||||
public static final DeduplicationCache<String> PROPERTY = new DeduplicationCache<>();
|
||||
|
||||
public static void printDebug() {
|
||||
ModernFix.LOGGER.info("[[[ Identifier de-duplication statistics ]]]");
|
||||
ModernFix.LOGGER.info("Namespace cache: {}", NAMESPACES);
|
||||
ModernFix.LOGGER.info("Path cache: {}", PATH);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package org.embeddedt.modernfix.dfu;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.mojang.datafixers.RewriteResult;
|
||||
import com.mojang.datafixers.TypeRewriteRule;
|
||||
import com.mojang.datafixers.functions.PointFreeRule;
|
||||
import com.mojang.datafixers.types.Type;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
public class DFUBlaster {
|
||||
private static final Cache<Pair<IntFunction<RewriteResult<?, ?>>, Integer>, RewriteResult<?, ?>> hmapApplyCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
private static final Cache<Triple<Type<?>, TypeRewriteRule, PointFreeRule>, Optional<? extends RewriteResult<?, ?>>> rewriteCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
public static void blastMaps() {
|
||||
try {
|
||||
Class<?> FOLD_CLASS = Class.forName("com.mojang.datafixers.functions.Fold");
|
||||
Field hmapField = FOLD_CLASS.getDeclaredField("HMAP_APPLY_CACHE");
|
||||
hmapField.setAccessible(true);
|
||||
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
theUnsafe.setAccessible(true);
|
||||
Unsafe unsafe = (Unsafe)theUnsafe.get(null);
|
||||
Object base = unsafe.staticFieldBase(hmapField);
|
||||
long offset = unsafe.staticFieldOffset(hmapField);
|
||||
unsafe.putObject(base, offset, hmapApplyCache.asMap());
|
||||
Field rewriteCacheField = Type.class.getDeclaredField("REWRITE_CACHE");
|
||||
rewriteCacheField.setAccessible(true);
|
||||
base = unsafe.staticFieldBase(rewriteCacheField);
|
||||
offset = unsafe.staticFieldOffset(rewriteCacheField);
|
||||
unsafe.putObject(base, offset, rewriteCache.asMap());
|
||||
new CleanerThread().start();
|
||||
} catch(Throwable e) {
|
||||
ModernFix.LOGGER.error("Could not replace DFU map", e);
|
||||
}
|
||||
}
|
||||
|
||||
static class CleanerThread extends Thread {
|
||||
CleanerThread() {
|
||||
this.setName("DFU cleaning thread");
|
||||
this.setPriority(1);
|
||||
this.setDaemon(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(true) {
|
||||
try {
|
||||
Thread.sleep(15000);
|
||||
} catch(InterruptedException e){
|
||||
return;
|
||||
}
|
||||
rewriteCache.cleanUp();
|
||||
hmapApplyCache.cleanUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
public interface ICachedMaterialsModel {
|
||||
public void clearMaterialsCache();
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
||||
public interface IChunkGenerator {
|
||||
void mfix$setAssociatedServerLevel(ServerLevel level);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
|
||||
public interface ILevelSave {
|
||||
public void runWorldPersistenceHooks(LevelStorageSource format);
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.embeddedt.modernfix.duck;
|
||||
|
||||
import org.embeddedt.modernfix.chunk.SafeBlockGetter;
|
||||
|
||||
public interface ISafeBlockGetter {
|
||||
SafeBlockGetter mfix$getSafeBlockGetter();
|
||||
}
|
||||
|
|
@ -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,76 +0,0 @@
|
|||
package org.embeddedt.modernfix.dynamicresources;
|
||||
|
||||
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.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Delegate model that stores the location of an actual baked model, for use in ItemOverrides.
|
||||
*/
|
||||
public class ItemOverrideBakedModel implements BakedModel {
|
||||
private static final Map<ResourceLocation, ItemOverrideBakedModel> OVERRIDE_MODELS = new ConcurrentHashMap<>();
|
||||
public final ResourceLocation realLocation;
|
||||
|
||||
private ItemOverrideBakedModel(ResourceLocation realLocation) {
|
||||
this.realLocation = realLocation;
|
||||
}
|
||||
|
||||
public static ItemOverrideBakedModel of(ResourceLocation realLocation) {
|
||||
return OVERRIDE_MODELS.computeIfAbsent(realLocation, ItemOverrideBakedModel::new);
|
||||
}
|
||||
|
||||
public BakedModel getRealModel() {
|
||||
return DynamicBakedModelProvider.currentInstance.get(realLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) {
|
||||
return getRealModel().getQuads(state, direction, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion() {
|
||||
return getRealModel().useAmbientOcclusion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d() {
|
||||
return getRealModel().isGui3d();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesBlockLight() {
|
||||
return getRealModel().usesBlockLight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustomRenderer() {
|
||||
return getRealModel().isCustomRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleIcon() {
|
||||
return getRealModel().getParticleIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemTransforms getTransforms() {
|
||||
return getRealModel().getTransforms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrides getOverrides() {
|
||||
return getRealModel().getOverrides();
|
||||
}
|
||||
}
|
||||
|
|
@ -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.INSTANCE.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,184 +0,0 @@
|
|||
package org.embeddedt.modernfix.registry;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class DirectStorageBiMap<K, V> implements BiMap<K, V> {
|
||||
private final Function<V, K> keyGetter;
|
||||
private final BiConsumer<V, K> keySetter;
|
||||
private final Map<K, V> forwardMap;
|
||||
|
||||
public DirectStorageBiMap(Function<V, K> keyGetter, BiConsumer<V, K> keySetter) {
|
||||
Objects.requireNonNull(keyGetter);
|
||||
Objects.requireNonNull(keySetter);
|
||||
this.keyGetter = keyGetter;
|
||||
this.keySetter = keySetter;
|
||||
this.forwardMap = new Object2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.forwardMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.forwardMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return this.forwardMap.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return o != null && keyGetter.apply((V)o) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object o) {
|
||||
return this.forwardMap.get(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
if(this.forwardMap.containsKey(key) || (value != null && keyGetter.apply(value) != null))
|
||||
throw new IllegalArgumentException("Already have mapping for " + key);
|
||||
return forcePut(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object o) {
|
||||
return put((K)o, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V forcePut(K key, V value) {
|
||||
V previousValue = this.forwardMap.put(key, value);
|
||||
if(previousValue != null)
|
||||
keySetter.accept(previousValue, null);
|
||||
if(value != null)
|
||||
keySetter.accept(value, key);
|
||||
return previousValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
map.forEach(this::put);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
for(V value : this.forwardMap.values()) {
|
||||
if(value != null)
|
||||
keySetter.accept(value, null);
|
||||
}
|
||||
this.forwardMap.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return this.forwardMap.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<V> values() {
|
||||
return new HashSet<>(this.forwardMap.values());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return this.forwardMap.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiMap<V, K> inverse() {
|
||||
return new Reverse();
|
||||
}
|
||||
|
||||
class Reverse implements BiMap<V, K> {
|
||||
@Override
|
||||
public int size() {
|
||||
return DirectStorageBiMap.this.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return DirectStorageBiMap.this.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return DirectStorageBiMap.this.containsValue(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return DirectStorageBiMap.this.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public K get(Object o) {
|
||||
return o == null ? null : keyGetter.apply((V)o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public K put(V key, K value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K remove(Object o) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K forcePut(V key, K value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends V, ? extends K> map) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<V> keySet() {
|
||||
return DirectStorageBiMap.this.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> values() {
|
||||
return DirectStorageBiMap.this.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<V, K>> entrySet() {
|
||||
return DirectStorageBiMap.this.entrySet().stream()
|
||||
.map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getValue(), entry.getKey()))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiMap<K, V> inverse() {
|
||||
return DirectStorageBiMap.this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package org.embeddedt.modernfix.registry;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface DirectStorageRegistryObject {
|
||||
ResourceLocation mfix$getResourceKey();
|
||||
void mfix$setResourceKey(ResourceLocation key);
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package org.embeddedt.modernfix.registry;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class RegistryStorage {
|
||||
public static BiMap<ResourceLocation, DirectStorageRegistryObject> createStorage() {
|
||||
return new DirectStorageBiMap<>(DirectStorageRegistryObject::mfix$getResourceKey, DirectStorageRegistryObject::mfix$setResourceKey);
|
||||
}
|
||||
|
||||
public static <T> BiMap<ResourceKey<T>, DirectStorageRegistryObject> createKeyStorage(ResourceKey<? extends Registry<T>> registryKey, BiMap<ResourceLocation, DirectStorageRegistryObject> storage) {
|
||||
if(storage instanceof DirectStorageBiMap) {
|
||||
DirectStorageBiMap<ResourceLocation, DirectStorageRegistryObject> directStorageBiMap = (DirectStorageBiMap<ResourceLocation, DirectStorageRegistryObject>)storage;
|
||||
// silently ignore put/putAll calls on this map
|
||||
return new TransformingBiMap<ResourceLocation, DirectStorageRegistryObject, ResourceKey<T>, DirectStorageRegistryObject>(directStorageBiMap, loc -> ResourceKey.create(registryKey, loc), ResourceKey::location, Function.identity(), Function.identity()) {
|
||||
@Override
|
||||
public DirectStorageRegistryObject put(ResourceKey<T> key, DirectStorageRegistryObject value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends ResourceKey<T>, ? extends DirectStorageRegistryObject> map) {
|
||||
|
||||
}
|
||||
};
|
||||
} else
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
package org.embeddedt.modernfix.registry;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Iterators;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TransformingBiMap<KFrom, VFrom, KTo, VTo> implements BiMap<KTo, VTo> {
|
||||
private final BiMap<KFrom, VFrom> delegate;
|
||||
private final Function<KFrom, KTo> keyFwd;
|
||||
private final Function<KTo, KFrom> keyBack;
|
||||
private final Function<VFrom, VTo> valueFwd;
|
||||
private final Function<VTo, VFrom> valueBack;
|
||||
|
||||
public TransformingBiMap(BiMap<KFrom, VFrom> map, Function<KFrom, KTo> keyFwd, Function<KTo, KFrom> keyBack, Function<VFrom, VTo> valueFwd, Function<VTo, VFrom> valueBack) {
|
||||
this.delegate = map;
|
||||
this.keyFwd = keyFwd;
|
||||
this.keyBack = keyBack;
|
||||
this.valueFwd = valueFwd;
|
||||
this.valueBack = valueBack;
|
||||
}
|
||||
|
||||
private KFrom keyBack(KTo key) {
|
||||
return key == null ? null : this.keyBack.apply(key);
|
||||
}
|
||||
|
||||
private KTo keyFwd(KFrom key) {
|
||||
return key == null ? null : this.keyFwd.apply(key);
|
||||
}
|
||||
|
||||
private VFrom valueBack(VTo value) {
|
||||
return value == null ? null : this.valueBack.apply(value);
|
||||
}
|
||||
|
||||
private VTo valueFwd(VFrom value) {
|
||||
return value == null ? null : this.valueFwd.apply(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.delegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object o) {
|
||||
return this.delegate.containsKey(keyBack((KTo)o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VTo get(Object o) {
|
||||
return valueFwd(this.delegate.get(keyBack((KTo)o)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VTo put(KTo key, VTo value) {
|
||||
return valueFwd(this.delegate.put(keyBack(key), valueBack(value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VTo remove(Object o) {
|
||||
return valueFwd(this.delegate.remove(keyBack((KTo)o)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VTo forcePut(KTo key, VTo value) {
|
||||
return valueFwd(this.delegate.forcePut(keyBack(key), valueBack(value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends KTo, ? extends VTo> map) {
|
||||
map.forEach((key, value) -> {
|
||||
this.delegate.put(keyBack(key), valueBack(value));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.delegate.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<KTo> keySet() {
|
||||
return new TransformingSet<>(this.delegate.keySet(), this.keyFwd, this.keyBack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<VTo> values() {
|
||||
return new TransformingSet<>(this.delegate.values(), this.valueFwd, this.valueBack);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<Entry<KTo, VTo>> entrySet() {
|
||||
return new TransformingSet<>(this.delegate.entrySet(), entry -> {
|
||||
return new AbstractMap.SimpleImmutableEntry<>(keyFwd(entry.getKey()), valueFwd(entry.getValue()));
|
||||
}, entry -> {
|
||||
return new AbstractMap.SimpleImmutableEntry<>(keyBack(entry.getKey()), valueBack(entry.getValue()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiMap<VTo, KTo> inverse() {
|
||||
return new TransformingBiMap<>(this.delegate.inverse(), this.valueFwd, this.valueBack, this.keyFwd, this.keyBack);
|
||||
}
|
||||
|
||||
static class TransformingSet<TypeFrom, TypeTo> implements Set<TypeTo> {
|
||||
private final Set<TypeFrom> delegate;
|
||||
private final Function<TypeFrom, TypeTo> forward;
|
||||
private final Function<TypeTo, TypeFrom> reverse;
|
||||
|
||||
public TransformingSet(Set<TypeFrom> set, Function<TypeFrom, TypeTo> forward, Function<TypeTo, TypeFrom> reverse) {
|
||||
this.delegate = set;
|
||||
this.forward = forward;
|
||||
this.reverse = reverse;
|
||||
}
|
||||
|
||||
private TypeTo forward(TypeFrom t) {
|
||||
return t == null ? null : this.forward.apply(t);
|
||||
}
|
||||
|
||||
private TypeFrom reverse(TypeTo t) {
|
||||
return t == null ? null : this.reverse.apply(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.delegate.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.delegate.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return this.delegate.contains(reverse((TypeTo)o));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<TypeTo> iterator() {
|
||||
return Iterators.transform(this.delegate.iterator(), this::forward);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
Object[] array = this.delegate.toArray();
|
||||
for(int i = 0; i < array.length; i++) {
|
||||
array[i] = this.forward((TypeFrom)array[i]);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T[] toArray(@NotNull T[] ts) {
|
||||
if(ts.length >= this.delegate.size()) {
|
||||
Object[] setContents = toArray();
|
||||
System.arraycopy(setContents, 0, ts, 0, Math.min(setContents.length, ts.length));
|
||||
if(ts.length > setContents.length)
|
||||
ts[setContents.length] = null;
|
||||
return ts;
|
||||
} else {
|
||||
T[] realArray = Arrays.copyOf(ts, this.delegate.size());
|
||||
Iterator<TypeTo> iterator = this.iterator();
|
||||
int i = 0;
|
||||
while(iterator.hasNext())
|
||||
realArray[i++] = (T)iterator.next();
|
||||
return realArray;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(TypeTo typeFrom) {
|
||||
return this.delegate.add(reverse(typeFrom));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return this.delegate.remove(reverse((TypeTo)o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> collection) {
|
||||
return this.delegate.containsAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends TypeTo> collection) {
|
||||
return this.delegate.addAll(Collections2.transform(collection, this::reverse));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> collection) {
|
||||
return this.delegate.retainAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> collection) {
|
||||
return this.delegate.removeAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.delegate.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
package org.embeddedt.modernfix.resources;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import org.embeddedt.modernfix.util.FileUtil;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class CachedResourcePath {
|
||||
private final String[] pathComponents;
|
||||
|
||||
public static final Interner<String> PATH_COMPONENT_INTERNER = Interners.newStrongInterner();
|
||||
private static final Splitter SLASH_SPLITTER = Splitter.on('/');
|
||||
public static final String[] NO_PREFIX = new String[0];
|
||||
|
||||
public CachedResourcePath(String[] prefix, Path path) {
|
||||
this(prefix, path, path.getNameCount(), true);
|
||||
}
|
||||
|
||||
public CachedResourcePath(String s) {
|
||||
// normalize so we can guarantee there are no empty sections
|
||||
this(NO_PREFIX, SLASH_SPLITTER.splitToList(FileUtil.normalize(s)), false);
|
||||
}
|
||||
|
||||
public <T> CachedResourcePath(String[] prefixElements, Collection<T> collection, boolean intern) {
|
||||
this(prefixElements, collection, collection.size(), intern);
|
||||
}
|
||||
|
||||
public <T> CachedResourcePath(String[] prefixElements, Iterable<T> path, int count, boolean intern) {
|
||||
String[] components = new String[prefixElements.length + count];
|
||||
int i = 0;
|
||||
while(i < prefixElements.length) {
|
||||
components[i] = intern ? PATH_COMPONENT_INTERNER.intern(prefixElements[i]) : prefixElements[i];
|
||||
i++;
|
||||
}
|
||||
for(Object component : path) {
|
||||
String s = component.toString();
|
||||
if(s.length() == 0)
|
||||
continue;
|
||||
components[i] = intern ? PATH_COMPONENT_INTERNER.intern(s) : s;
|
||||
i++;
|
||||
}
|
||||
pathComponents = components;
|
||||
}
|
||||
|
||||
public CachedResourcePath(String[] prefixElements, CachedResourcePath other) {
|
||||
String[] components = new String[prefixElements.length + other.pathComponents.length];
|
||||
int i = 0;
|
||||
while(i < prefixElements.length) {
|
||||
components[i] = PATH_COMPONENT_INTERNER.intern(prefixElements[i]);
|
||||
i++;
|
||||
}
|
||||
System.arraycopy(other.pathComponents, 0, components, i, other.pathComponents.length);
|
||||
pathComponents = components;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOES NOT INTERN!
|
||||
*/
|
||||
public CachedResourcePath(String[] pathComponents) {
|
||||
this.pathComponents = pathComponents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(pathComponents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CachedResourcePath that = (CachedResourcePath) o;
|
||||
return Arrays.equals(pathComponents, that.pathComponents);
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return pathComponents[pathComponents.length - 1];
|
||||
}
|
||||
|
||||
public int getNameCount() {
|
||||
return pathComponents.length;
|
||||
}
|
||||
|
||||
public String getNameAt(int i) {
|
||||
return pathComponents[i];
|
||||
}
|
||||
|
||||
public String getFullPath(int startIndex) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int i = startIndex; i < pathComponents.length; i++) {
|
||||
sb.append(pathComponents[i]);
|
||||
if(i != (pathComponents.length - 1))
|
||||
sb.append('/');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package org.embeddedt.modernfix.resources;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackResources;
|
||||
import net.minecraft.server.packs.resources.IoSupplier;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class NewResourcePackAdapter {
|
||||
public static void sendToOutput(Function<ResourceLocation, IoSupplier<InputStream>> streamCreator, PackResources.ResourceOutput output, Collection<ResourceLocation> locations) {
|
||||
for(ResourceLocation rl : locations) {
|
||||
output.accept(rl, streamCreator.apply(rl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
package org.embeddedt.modernfix.resources;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
import org.embeddedt.modernfix.util.PackTypeHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* The core of the resource pack cache system.
|
||||
*
|
||||
* Using a dedicated set and also separate lists is important; testing without this showed a huge performance
|
||||
* drop.
|
||||
*/
|
||||
public class PackResourcesCacheEngine {
|
||||
private static final Joiner SLASH_JOINER = Joiner.on('/');
|
||||
|
||||
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 = SLASH_JOINER.join(path);
|
||||
if(str.length() == 0)
|
||||
return false;
|
||||
for(int i = 0; i < str.length(); i++) {
|
||||
if(!ResourceLocation.validPathChar(str.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<String> getNamespaces(PackType type) {
|
||||
if(PackTypeHelper.isVanillaPackType(type))
|
||||
return this.namespacesByType.get(type);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private void doGenerateCache() {
|
||||
Stopwatch watch = Stopwatch.createStarted();
|
||||
for(Runnable r : this.cacheGenerationTasks) {
|
||||
r.run();
|
||||
}
|
||||
watch.stop();
|
||||
ModernFix.LOGGER.debug("Generated cache for {} in {}", debugPath, watch);
|
||||
debugPath = null;
|
||||
cacheGenerationTasks = ImmutableList.of();
|
||||
}
|
||||
|
||||
private void awaitLoad() {
|
||||
if(!this.cacheGenerationFlag) {
|
||||
synchronized (this) {
|
||||
if(!this.cacheGenerationFlag) {
|
||||
this.doGenerateCache();
|
||||
this.cacheGenerationFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasResource(String path) {
|
||||
awaitLoad();
|
||||
return this.containedPaths.contains(new CachedResourcePath(path));
|
||||
}
|
||||
|
||||
public boolean hasResource(String[] paths) {
|
||||
awaitLoad();
|
||||
return this.containedPaths.contains(new CachedResourcePath(paths));
|
||||
}
|
||||
|
||||
public Collection<ResourceLocation> getResources(PackType type, String resourceNamespace, String pathIn, int maxDepth, Predicate<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() {
|
||||
if(!ModernFixPlatformHooks.INSTANCE.isDevEnv())
|
||||
return;
|
||||
synchronized (cachingPacks) {
|
||||
cachingPacks.keySet().forEach(pack -> {
|
||||
if(pack != null)
|
||||
pack.invalidateCache();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
package org.embeddedt.modernfix.searchtree;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
|
||||
import me.shedaniel.rei.api.common.entry.EntryStack;
|
||||
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
|
||||
import me.shedaniel.rei.impl.client.search.AsyncSearchManager;
|
||||
import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl;
|
||||
import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper;
|
||||
import net.minecraft.client.searchtree.RefreshableSearchTree;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class REIBackedSearchTree extends DummySearchTree<ItemStack> {
|
||||
private final AsyncSearchManager searchManager = createSearchManager();
|
||||
|
||||
private final boolean filteringByTag;
|
||||
private String lastSearchText = "";
|
||||
private final List<ItemStack> listCache = new ArrayList<>();
|
||||
|
||||
public REIBackedSearchTree(boolean filteringByTag) {
|
||||
this.filteringByTag = filteringByTag;
|
||||
}
|
||||
@Override
|
||||
public List<ItemStack> search(String pSearchText) {
|
||||
if(true) {
|
||||
return this.searchREI(pSearchText);
|
||||
} else {
|
||||
/* Use the default, dummy implementation */
|
||||
return super.search(pSearchText);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ItemStack> searchREI(String pSearchText) {
|
||||
if(!pSearchText.equals(lastSearchText)) {
|
||||
listCache.clear();
|
||||
this.searchManager.updateFilter(pSearchText);
|
||||
List stacks;
|
||||
try {
|
||||
stacks = this.searchManager.getNow();
|
||||
} catch(RuntimeException e) {
|
||||
ModernFix.LOGGER.error("Couldn't search for '" + pSearchText + "'", e);
|
||||
stacks = Collections.emptyList();
|
||||
}
|
||||
for(Object o : stacks) {
|
||||
EntryStack<?> stack;
|
||||
if(o instanceof EntryStack<?>)
|
||||
stack = (EntryStack<?>)o;
|
||||
else if(o instanceof HashedEntryStackWrapper) {
|
||||
stack = ((HashedEntryStackWrapper)o).unwrap();
|
||||
} else {
|
||||
ModernFix.LOGGER.error("Don't know how to handle {}", o.getClass().getName());
|
||||
continue;
|
||||
}
|
||||
if(stack.getType() == VanillaEntryTypes.ITEM) {
|
||||
listCache.add(stack.cheatsAs().getValue());
|
||||
}
|
||||
}
|
||||
lastSearchText = pSearchText;
|
||||
}
|
||||
return listCache;
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private static AsyncSearchManager createSearchManager() {
|
||||
Method m, normalizeMethod;
|
||||
try {
|
||||
try {
|
||||
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredComplexList");
|
||||
m.setAccessible(true);
|
||||
normalizeMethod = HashedEntryStackWrapper.class.getDeclaredMethod("normalize");
|
||||
normalizeMethod.setAccessible(true);
|
||||
} catch(NoSuchMethodException e) {
|
||||
m = EntryRegistryImpl.class.getDeclaredMethod("getPreFilteredList");
|
||||
m.setAccessible(true);
|
||||
normalizeMethod = EntryStack.class.getDeclaredMethod("normalize");
|
||||
normalizeMethod.setAccessible(true);
|
||||
}
|
||||
final MethodHandle getListMethod = MethodHandles.publicLookup().unreflect(m);
|
||||
final MethodHandle normalize = MethodHandles.publicLookup().unreflect(normalizeMethod);
|
||||
final EntryRegistryImpl registry = (EntryRegistryImpl)EntryRegistry.getInstance();
|
||||
Supplier stackListSupplier = () -> {
|
||||
try {
|
||||
return (List)getListMethod.invokeExact(registry);
|
||||
} catch(Throwable e) {
|
||||
if(e instanceof RuntimeException)
|
||||
throw (RuntimeException)e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
UnaryOperator normalizeOperator = o -> {
|
||||
try {
|
||||
return normalize.invoke(o);
|
||||
} catch(Throwable e) {
|
||||
if(e instanceof RuntimeException)
|
||||
throw (RuntimeException)e;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
return new AsyncSearchManager(stackListSupplier, () -> {
|
||||
return Predicates.alwaysTrue();
|
||||
}, normalizeOperator);
|
||||
} catch(ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final SearchTreeProviderRegistry.Provider PROVIDER = new SearchTreeProviderRegistry.Provider() {
|
||||
@Override
|
||||
public RefreshableSearchTree<ItemStack> getSearchTree(boolean tag) {
|
||||
return new REIBackedSearchTree(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
return ModernFixPlatformHooks.INSTANCE.modPresent("roughlyenoughitems");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "REI";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class LoadableTickableObject<T> implements TickableObject {
|
||||
private volatile int ticksInactive = 0;
|
||||
private final int timeout;
|
||||
private final Supplier<T> loader;
|
||||
private final Consumer<T> finalizer;
|
||||
private volatile T theObject = null;
|
||||
|
||||
public LoadableTickableObject(int timeout, Supplier<T> loader, Consumer<T> finalizer) {
|
||||
this(timeout, loader, finalizer, null);
|
||||
}
|
||||
|
||||
public LoadableTickableObject(int timeout, Supplier<T> loader, Consumer<T> finalizer, @Nullable T initialValue) {
|
||||
this.timeout = timeout;
|
||||
this.loader = loader;
|
||||
this.finalizer = finalizer;
|
||||
this.theObject = initialValue;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
synchronized (this) {
|
||||
ticksInactive++;
|
||||
T obj = theObject;
|
||||
if(obj == null) {
|
||||
obj = loader.get();
|
||||
theObject = obj;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public final void tick() {
|
||||
synchronized (this) {
|
||||
ticksInactive++;
|
||||
if(ticksInactive >= this.timeout) {
|
||||
finalizer.accept(theObject);
|
||||
theObject = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
public interface TickableObject {
|
||||
void tick();
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package org.embeddedt.modernfix.tickables;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class TickableObjectManager {
|
||||
private static final List<TickableObject> TICKABLE_OBJECT_LIST = new CopyOnWriteArrayList<>();
|
||||
|
||||
public static void register(TickableObject object) {
|
||||
TICKABLE_OBJECT_LIST.add(object);
|
||||
}
|
||||
|
||||
public static void runTick() {
|
||||
for(TickableObject o : TICKABLE_OBJECT_LIST) {
|
||||
o.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import org.embeddedt.modernfix.ModernFix;
|
||||
|
||||
public enum BakeReason {
|
||||
FREEZE,
|
||||
REMOTE_SNAPSHOT_INJECT,
|
||||
LOCAL_SNAPSHOT_INJECT,
|
||||
REVERT,
|
||||
UNKNOWN;
|
||||
private static BakeReason currentBakeReason = null;
|
||||
private static boolean bakeReasonWarned = false;
|
||||
|
||||
public static BakeReason getCurrentBakeReason() {
|
||||
if(currentBakeReason == null && !bakeReasonWarned) {
|
||||
ModernFix.LOGGER.warn("No bake reason found, mixin probably not applied correctly", new IllegalStateException());
|
||||
bakeReasonWarned = false;
|
||||
}
|
||||
return currentBakeReason;
|
||||
}
|
||||
|
||||
public static void setCurrentBakeReason(BakeReason reason) {
|
||||
currentBakeReason = reason;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package org.embeddedt.modernfix.util;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Replacement backing map for CompoundTags that interns keys.
|
||||
*/
|
||||
public class CanonizingStringMap<T> extends HashMap<String, T> {
|
||||
private static final Interner<String> KEY_INTERNER = Interners.newWeakInterner();
|
||||
|
||||
private static String intern(String key) {
|
||||
return key != null ? KEY_INTERNER.intern(key) : null;
|
||||
}
|
||||
|
||||
public CanonizingStringMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T put(String key, T value) {
|
||||
return super.put(intern(key), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends T> m) {
|
||||
if(m.isEmpty())
|
||||
return;
|
||||
HashMap<String, T> tmp = new HashMap<>();
|
||||
m.forEach((k, v) -> tmp.put(intern(k), v));
|
||||
super.putAll(tmp);
|
||||
}
|
||||
|
||||
private void putAllWithoutInterning(Map<? extends String, ? extends T> m) {
|
||||
super.putAll(m);
|
||||
}
|
||||
|
||||
public static <T> CanonizingStringMap<T> deepCopy(CanonizingStringMap<T> incomingMap, Function<T, T> deepCopier) {
|
||||
CanonizingStringMap<T> newMap = new CanonizingStringMap<>();
|
||||
newMap.putAllWithoutInterning(Maps.transformValues(incomingMap, deepCopier));
|
||||
return newMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,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,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": "開啟設定介面",
|
||||
"modernfix.jei_load": "正在載入 JEI,這可能需要一點時間",
|
||||
"modernfix.no_lazydfu": "沒有安裝 LazyDFU。如果 Minecraft 需要從舊版本更新遊戲資料,可能會出現明顯的延遲。",
|
||||
"modernfix.config": "ModernFix 注入設定",
|
||||
"modernfix.config.done_restart": "完成(需要重新啟動遊戲)",
|
||||
"modernfix.option.on": "開啟",
|
||||
"modernfix.option.off": "關閉",
|
||||
"modernfix.config.not_default": "(已修改)",
|
||||
"asynclocator.map.locating": "地圖(定位中...)",
|
||||
"asynclocator.map.none": "地圖(未找到附近的特徵)"
|
||||
}
|
||||
|
|
@ -1,16 +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
|
||||
},
|
||||
"overwrites": {
|
||||
"conformVisibility": true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
accessWidener v2 named
|
||||
|
||||
accessible field net/minecraft/client/multiplayer/ClientChunkCache storage Lnet/minecraft/client/multiplayer/ClientChunkCache$Storage;
|
||||
accessible field net/minecraft/client/multiplayer/ClientChunkCache lightEngine Lnet/minecraft/world/level/lighting/LevelLightEngine;
|
||||
mutable field net/minecraft/client/multiplayer/ClientChunkCache lightEngine Lnet/minecraft/world/level/lighting/LevelLightEngine;
|
||||
accessible class net/minecraft/client/multiplayer/ClientChunkCache$Storage
|
||||
accessible field net/minecraft/client/multiplayer/ClientChunkCache$Storage chunks Ljava/util/concurrent/atomic/AtomicReferenceArray;
|
||||
|
||||
accessible field net/minecraft/world/level/Level blockEntityTickers Ljava/util/List;
|
||||
|
||||
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;
|
||||
accessible field net/minecraft/client/KeyMapping ALL Ljava/util/Map;
|
||||
accessible field net/minecraft/client/renderer/block/model/multipart/MultiPart definition Lnet/minecraft/world/level/block/state/StateDefinition;
|
||||
accessible field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
||||
mutable field net/minecraft/client/renderer/block/model/ItemOverrides$BakedOverride model Lnet/minecraft/client/resources/model/BakedModel;
|
||||
accessible field net/minecraft/client/renderer/entity/EnderDragonRenderer$DragonModel entity Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;
|
||||
accessible method net/minecraft/world/level/block/state/StateDefinition appendPropertyCodec (Lcom/mojang/serialization/MapCodec;Ljava/util/function/Supplier;Ljava/lang/String;Lnet/minecraft/world/level/block/state/properties/Property;)Lcom/mojang/serialization/MapCodec;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user