geforkt von Mirrors/FastAsyncWorldEdit
Commits vergleichen
1185 Commits
feature/cl
...
main
Autor | SHA1 | Datum | |
---|---|---|---|
6d6de3ee3f | |||
54fd91c7f7 | |||
2b870902a3 | |||
6ab048cd7b | |||
5b5fd8a229 | |||
f0509896e0 | |||
bdad5ca1b6 | |||
c0f935ef55 | |||
|
fe33be5795 | ||
|
a088e6a96b | ||
|
acb79469fb | ||
|
7b3c674e1c | ||
|
a3e855882c | ||
|
e81980651e | ||
|
50e45f07f0 | ||
|
a6f1c6ac82 | ||
|
4ca0a95e18 | ||
|
0dbc0db43f | ||
|
9ae04b8d40 | ||
|
d42854845a | ||
|
8b39e41a1a | ||
|
48be6ac94b | ||
|
2f6de996e3 | ||
|
006ccd6887 | ||
|
37bfe426bc | ||
|
f58f00e97f | ||
|
34a98a03c1 | ||
|
c91b477e29 | ||
|
4bdcf9a510 | ||
|
9543adc776 | ||
|
18df87a4e8 | ||
|
0a8a479214 | ||
|
fe1859e9d2 | ||
|
f65c4743bd | ||
|
cdd546ee5e | ||
|
5167073326 | ||
|
e0507e6440 | ||
|
b1e0ad4ef7 | ||
|
e6b1308590 | ||
|
f4da4b0287 | ||
|
8da530ee80 | ||
|
1745c50878 | ||
|
a680c7ce97 | ||
|
0a19f643b6 | ||
|
c0e20a6fe5 | ||
|
1f29ab3b79 | ||
|
685248d8de | ||
|
68eb4e214a | ||
|
90587e56fc | ||
|
d78092b4ca | ||
|
6fbdef5252 | ||
|
f38c81aa2a | ||
|
888683d83b | ||
|
a669be2041 | ||
|
67fd31ce4c | ||
|
ccdb002ade | ||
|
34ead35e03 | ||
|
52a51753dd | ||
|
d7cc65d2f2 | ||
|
a0acbc7168 | ||
|
e680e8fe1f | ||
|
4583acda6b | ||
|
c880badaf4 | ||
|
83a4987252 | ||
|
903064fd41 | ||
|
e5cbf33317 | ||
|
770bb0087a | ||
|
24325d91ba | ||
|
0554b31f11 | ||
|
476ba4ab41 | ||
|
97ee71bbd0 | ||
|
924805af8f | ||
|
05d5ad161a | ||
|
c0037aa083 | ||
|
9f0a0dbd35 | ||
|
d961aa91bc | ||
|
88533118bc | ||
|
8ca25fa7d7 | ||
|
01be53ed65 | ||
|
a8c8a0fbd6 | ||
|
1ede11b129 | ||
|
39de48cdb1 | ||
|
f0aef98870 | ||
|
135c77cfb5 | ||
|
78fb74665f | ||
|
7a7373fd3d | ||
|
51e238c352 | ||
|
79a2c14caa | ||
|
d9463ce8b5 | ||
|
dae6c69e54 | ||
|
ed128797ec | ||
|
956a518238 | ||
|
e449910af5 | ||
|
ca4080eea7 | ||
|
71c172cced | ||
|
083f8a4dd8 | ||
|
689c7f62b8 | ||
|
bb14d93a8d | ||
|
e7876c4eba | ||
|
152d870cdb | ||
|
9be1a58bf3 | ||
|
3b7d126718 | ||
|
832f93c0f3 | ||
|
c5f66c342b | ||
|
1356cd5caa | ||
|
84872cf9a2 | ||
|
b7719d17bd | ||
|
5504811f11 | ||
|
bdb170a5ca | ||
|
5da5b216f3 | ||
|
4b06796e76 | ||
|
e5d27c27bb | ||
|
46e8a37cf0 | ||
|
e9fed5a066 | ||
|
0e215e98d5 | ||
|
97d4244126 | ||
|
b7dc5f7ae5 | ||
|
c9a4a9c8b4 | ||
|
048dcaf327 | ||
|
4fa927f996 | ||
|
82418155f6 | ||
|
7646a067eb | ||
|
03c4bafc45 | ||
|
228e84e555 | ||
|
7ce17e5834 | ||
|
03487718e7 | ||
|
a553961c05 | ||
|
3041ff3e50 | ||
|
362f6cc6e1 | ||
|
5dca925bc3 | ||
|
435161d566 | ||
|
7c01c1e95e | ||
|
74aff920a8 | ||
|
2bdc925b0f | ||
|
4c0b535e6e | ||
|
86f06b7527 | ||
|
5e3222c55d | ||
|
7f43e448d5 | ||
|
2c7b529f67 | ||
|
3cac28ea84 | ||
|
85214c944f | ||
|
264185a323 | ||
|
85f5006a0b | ||
|
1b0fb9ed48 | ||
|
3a13c4aaa7 | ||
|
e9b781d127 | ||
|
2a08ad28a4 | ||
|
52723439dd | ||
|
a14035d430 | ||
|
319cb636e5 | ||
|
71bac009db | ||
|
c57fee5b86 | ||
|
dd6197922c | ||
|
2987550e9b | ||
|
e08720b310 | ||
|
92a6ff5f88 | ||
|
950a3f9430 | ||
|
4779bd4916 | ||
|
909b7d27b8 | ||
|
ffe704d890 | ||
|
9f3c909254 | ||
|
abe120291f | ||
|
c86dfe45df | ||
|
488a2e5de4 | ||
|
8cfb8cb322 | ||
|
05afaf00a9 | ||
|
86acb1c4d4 | ||
|
5cd9616507 | ||
|
e4158ac084 | ||
6a7f51bba0 | |||
cdd4df37c6 | |||
|
da4d966d9e | ||
|
a42864e780 | ||
|
2dc8f7bca9 | ||
|
bb8c9d216b | ||
|
2b51896a01 | ||
|
b42f6a2f5e | ||
|
a9e1d5009c | ||
|
508b94ddc3 | ||
|
d82bf0527e | ||
|
eb1c9dc4db | ||
|
9d63cb8102 | ||
|
efc917e6df | ||
|
b54e3a5112 | ||
|
09b899c7a9 | ||
|
7ab04317fa | ||
|
e32a8c40c6 | ||
|
df99226313 | ||
|
5313f9c588 | ||
|
0146b952c6 | ||
|
bfd919b3cb | ||
|
b05e64a7ec | ||
|
ccb503e49d | ||
|
f7026338e9 | ||
|
992eaf2b4c | ||
|
90baa790c3 | ||
|
a63037b92d | ||
|
8a3052683e | ||
|
211e8034ff | ||
|
94f57799d0 | ||
|
7152fd2032 | ||
|
84e1886672 | ||
|
a1d35e26fa | ||
|
214495f8f5 | ||
|
526e1902ca | ||
|
6a31f4179c | ||
|
1904b1fdd6 | ||
|
c0dc42ed3f | ||
|
f95383e837 | ||
|
c7ddee12d9 | ||
|
7f6a27d083 | ||
|
184eb215b6 | ||
|
7a667131ba | ||
|
b293a5eea3 | ||
|
f70e1a215a | ||
9d5e96946c | |||
|
a464f1271d | ||
|
b72c6902e2 | ||
|
8832820b6c | ||
|
783dc353c4 | ||
|
1e5b2fe0de | ||
|
19ed349740 | ||
|
321a39f121 | ||
|
f9d6b127e6 | ||
|
07217d0b81 | ||
|
b65f3ce1f8 | ||
9f97dc2a3c | |||
1889f94a39 | |||
b87a861098 | |||
|
cd00bf9771 | ||
|
158e51191f | ||
|
214954bb09 | ||
|
09d405935a | ||
95a06eb639 | |||
0c9317bb8c | |||
|
5e8bc5c2ad | ||
|
0f558425f5 | ||
|
ae57ac5d50 | ||
|
98c2d2124e | ||
|
c0563b6574 | ||
|
606ffad12b | ||
|
bb0e201c52 | ||
|
452f968f07 | ||
|
3ad70a8ef4 | ||
|
afe601bf93 | ||
|
7db1e9c7d6 | ||
|
b98c3efc88 | ||
|
a9ed8431cd | ||
|
a7c402d8dd | ||
|
f8acaf5ec2 | ||
|
b0ac500fa1 | ||
|
83e0c84616 | ||
|
26d5f10c04 | ||
|
d6599fd332 | ||
|
c3ce162fd5 | ||
|
f98e526fa0 | ||
|
62f561cb56 | ||
|
62345bb64e | ||
|
79c23e06c3 | ||
|
e0e66ce2db | ||
|
41ea21f91d | ||
|
faa0908ba8 | ||
|
eda5bced65 | ||
|
c1bfadd328 | ||
|
b432faef36 | ||
|
6cb9f0e485 | ||
|
6f6c0508df | ||
|
15b351930d | ||
|
1771a04ef9 | ||
|
34dbaa633a | ||
|
fc701b9260 | ||
|
77b1c7eb9c | ||
|
f33d93591a | ||
|
f9527ce2b7 | ||
|
7f8b0a7e21 | ||
|
4c2119e404 | ||
|
1aa213fb4c | ||
|
983e4407b5 | ||
|
0ee18597ae | ||
|
2fe54a04b5 | ||
|
bbc1db9bd2 | ||
|
e305c5d5ab | ||
|
8127d57158 | ||
|
0d112b2913 | ||
|
bf28ab47b8 | ||
|
e36b838227 | ||
|
4f74cc671f | ||
|
dce86ec046 | ||
|
2082df4141 | ||
|
8971d7064c | ||
|
ccb31c0ecc | ||
|
13eb1f815f | ||
|
878509406b | ||
|
33be942176 | ||
|
f356f64c3d | ||
|
598cd6ba19 | ||
|
292d9f2439 | ||
|
a62b3158a7 | ||
|
18bf911fdc | ||
|
ad38d88642 | ||
|
2e386ea391 | ||
|
a32d8dcbdb | ||
|
87f680683d | ||
942570c754 | |||
ae66ccfa8f | |||
c0a135acc9 | |||
|
10b852d416 | ||
|
4f26561cc1 | ||
|
ab55d07ffd | ||
|
1706d64ddf | ||
|
c819031d1f | ||
|
2547fd590d | ||
dd8ed8d5c6 | |||
2553f169aa | |||
|
673b467ad9 | ||
|
122b32b9f0 | ||
|
5da558e24c | ||
|
3b109ba3ff | ||
|
8233f132d4 | ||
|
aa216a990a | ||
|
a51863c6f0 | ||
|
a6a0b5eb66 | ||
d5722fe9a5 | |||
c3b43e13e3 | |||
8348c0bf19 | |||
|
dc2db5f07f | ||
|
37bbebcc9d | ||
|
d5acb4ec51 | ||
e5833aff58 | |||
113c02ec3f | |||
0f74e2a0bb | |||
|
d4c8bd084e | ||
d67ec4fd63 | |||
|
60ad2b51e8 | ||
|
ab659eadde | ||
|
cc1b466a04 | ||
|
26bd9ec5ee | ||
|
5c2679f95d | ||
|
129f4f37a3 | ||
|
c520e1a686 | ||
|
e78277e158 | ||
|
86a9866455 | ||
|
2a55bb5cfc | ||
|
8c8d5d5064 | ||
|
a91519e09d | ||
|
a0b35c9cda | ||
|
a414fd308e | ||
|
e942175559 | ||
|
7a498497c5 | ||
|
8b95c25194 | ||
|
8d57c0b888 | ||
|
8414e64bab | ||
|
50ee45fedd | ||
|
e10774598e | ||
|
77a929f6a1 | ||
|
a1babd5ec9 | ||
|
72f9b34f15 | ||
|
4fc2a25a9a | ||
|
7a1a33aff0 | ||
|
282e47dd19 | ||
|
4aa84776ba | ||
|
4013eceb8e | ||
|
42956839ee | ||
|
92518f71be | ||
|
902f153929 | ||
|
def84a80c6 | ||
|
ffd98ad6d9 | ||
|
db37c28a2f | ||
|
1400ce9958 | ||
|
dbfb521795 | ||
|
3212dddb2d | ||
|
041f4505a3 | ||
|
f812fc14ab | ||
|
edfc5a7a01 | ||
|
34ea713d29 | ||
|
38d1a64bf5 | ||
|
9762e4e220 | ||
|
8094b68967 | ||
|
800988aae7 | ||
|
010611c4ac | ||
|
704f87da23 | ||
|
100288ada5 | ||
|
d498996cbd | ||
|
e3f2d5f737 | ||
|
968799503f | ||
|
d62c88a2ca | ||
|
8b05738929 | ||
|
c5073d79e7 | ||
|
3f103eea9b | ||
|
fe90142f1f | ||
|
2ecf32b37c | ||
|
dacb89a480 | ||
|
396faf6732 | ||
|
dac3610bcf | ||
|
692a010c39 | ||
|
84f6039f41 | ||
|
aadd35c0c4 | ||
|
8e5204c311 | ||
|
003cec30be | ||
|
907ad8528e | ||
|
d2b4154cc0 | ||
|
5b72f396bb | ||
|
fd00635533 | ||
|
f2bab901f4 | ||
|
1da987d594 | ||
|
ac11da855a | ||
|
9395b3c575 | ||
|
462bba4f87 | ||
|
cb6e200ca6 | ||
|
ea434163c4 | ||
|
5b2f8502fb | ||
|
1a3b0dc133 | ||
|
111d604075 | ||
|
aae696686d | ||
|
8a85225523 | ||
|
39e1a811f7 | ||
|
b797655d0c | ||
|
97ab47c90b | ||
|
f8583fb7cb | ||
|
a6b1b411d5 | ||
|
99a57f31b2 | ||
|
63f031b01a | ||
|
02a6bb9b27 | ||
|
8377b0987c | ||
|
ab4f779445 | ||
|
ccc936534b | ||
|
c3cb2c5ed7 | ||
|
c62f0c197f | ||
|
e54cae33f1 | ||
|
aa94612b70 | ||
|
da3fc2e6ea | ||
|
7f8ce69563 | ||
|
3ec17f3507 | ||
|
c5a50179f2 | ||
|
d7d43869e6 | ||
|
9b3608aada | ||
|
0b33fa8757 | ||
|
af234b284b | ||
|
a16074cbe4 | ||
|
d5a84d6e44 | ||
|
32231b48fe | ||
|
198c6b7800 | ||
|
8228b798e5 | ||
|
02eea35c3f | ||
|
9804158865 | ||
|
69eae2e4bd | ||
|
f9c56d94c5 | ||
|
a43fa912d6 | ||
|
8f01a5e4be | ||
|
6bb58dc113 | ||
|
d1f3ac8f76 | ||
|
14811169e3 | ||
|
ace325a90e | ||
|
3a65c64977 | ||
|
3c4695bb73 | ||
|
0d2ed796d8 | ||
|
84f975559c | ||
|
39d77091d9 | ||
|
2c1ffd4ef4 | ||
|
deaf5ad2d6 | ||
|
1c7643bb58 | ||
|
059c5f046d | ||
|
b237ddf076 | ||
|
d7543884a5 | ||
|
ec97fca440 | ||
|
ffeb940aff | ||
|
776dc3b69d | ||
|
267dc153f0 | ||
|
b85888806a | ||
|
210ee9f2ef | ||
|
c1b5f8c84a | ||
|
a51bc6eab3 | ||
|
091d64f723 | ||
|
546e42aab5 | ||
|
617b0ac95c | ||
|
a227e2618f | ||
|
3a22389d35 | ||
|
e294245ec4 | ||
|
82ba96bf71 | ||
|
f2df511263 | ||
|
3f970cdaef | ||
|
65349392a6 | ||
|
f5ef0cadb6 | ||
|
c94cbf25a6 | ||
|
9fdce09a4c | ||
|
3610af15e8 | ||
|
27ee46047e | ||
|
ebf84c523c | ||
|
bb71a18c8c | ||
|
4fd676b931 | ||
|
6e26741fdc | ||
|
8f1ca48bfc | ||
|
a9e43a6d78 | ||
|
2483eacff5 | ||
|
ed9317b4d6 | ||
|
9d56275247 | ||
|
ffea228b52 | ||
|
569f3dc504 | ||
|
d26e15b6da | ||
|
90e77e5074 | ||
|
93846fabaf | ||
|
f477120661 | ||
|
a240b002de | ||
|
4970db0405 | ||
|
2276d82d2c | ||
|
c45b14a52d | ||
|
d1588f9207 | ||
|
465c81d193 | ||
|
05f80f23b9 | ||
|
fbb25f289b | ||
|
07bc33b52b | ||
|
c672d7a0b3 | ||
|
6435fd3bea | ||
|
8afde807b6 | ||
|
ebd7c136fd | ||
|
1708584e8d | ||
|
d400a470a1 | ||
|
5a97b23347 | ||
|
d8c0f8e3b1 | ||
|
7ba185c4ac | ||
|
95512a4c0b | ||
|
c3cdde9d2e | ||
|
e9db749e2f | ||
|
5d18e15128 | ||
|
f59353006a | ||
|
3ee9797408 | ||
|
49bc675f51 | ||
|
fb66ba6adf | ||
|
5e4143c1dc | ||
|
345785a25e | ||
|
668227ee6c | ||
|
44078d1820 | ||
|
4ffea67514 | ||
|
dafcd16a14 | ||
|
a23785632a | ||
|
7db06061f0 | ||
|
340f8dded3 | ||
|
c5d70f0da1 | ||
|
be83c6ec70 | ||
|
dec0373081 | ||
|
081d8ddb50 | ||
|
b8ab2a5204 | ||
|
fc2662e51e | ||
|
49d5183685 | ||
|
28a0239437 | ||
|
0a04b0b4cd | ||
|
77b41e052a | ||
|
23b5f0cb4a | ||
|
f8e6feb1f2 | ||
|
39081e62c9 | ||
|
f7a0c14a1b | ||
|
3f28a5759d | ||
|
a40d1ccf51 | ||
|
fbe0c08c26 | ||
|
05bd84b504 | ||
|
3e85369765 | ||
|
cf0a21d312 | ||
|
b1a93766d3 | ||
|
f657a80dc6 | ||
|
de4f73997e | ||
|
646a683f22 | ||
|
7da993d3f1 | ||
|
7e17a5cbd1 | ||
|
cf1e61c153 | ||
|
15b9b8332c | ||
|
d80bfc7495 | ||
|
2bb72ebfea | ||
|
faa2477305 | ||
|
186386ce4a | ||
|
d3f8a3beb3 | ||
|
f0136460ba | ||
|
38b0dcf7e4 | ||
|
178604bbb7 | ||
|
4610688fcb | ||
|
15076f3d93 | ||
|
272920fb25 | ||
|
a0b61512fb | ||
|
e309cf8f3c | ||
|
0f524e665b | ||
|
e8f72d6521 | ||
|
c461674402 | ||
|
806ca62485 | ||
|
016b4ba81d | ||
|
d17e3a6555 | ||
|
4d2a34fd21 | ||
|
96805b85b5 | ||
|
d3696f91d4 | ||
|
0ccae19ded | ||
|
f077ec9163 | ||
|
2c4453b68f | ||
|
6a972e7b99 | ||
|
9e40b972b1 | ||
|
6b857b0f58 | ||
|
5fc0de0b82 | ||
|
4e2a1c393d | ||
|
f0f201002a | ||
|
9ba90d8c83 | ||
|
c2f3c13a09 | ||
|
4754f660c7 | ||
|
64442a8051 | ||
|
f27959e49a | ||
|
59d4247ddb | ||
|
867d4c05cb | ||
|
d6695d23f0 | ||
|
fe1a77a593 | ||
|
722c411219 | ||
|
0408cd0575 | ||
|
318b1ef690 | ||
|
3a78abe273 | ||
|
8b973da7ee | ||
|
c27a34ce40 | ||
|
f4658cc668 | ||
|
ef686af8dd | ||
|
631fd3c7f5 | ||
|
30c2597aad | ||
|
b8399abfe1 | ||
|
42dacfc7ef | ||
|
7e96853b89 | ||
|
42d6466a4c | ||
|
0994ea1e67 | ||
|
f30ea96268 | ||
|
9faacb38ef | ||
|
aaad86cf1d | ||
|
122cba677a | ||
|
235d0360da | ||
|
52fa44516e | ||
|
9e8f0e7325 | ||
|
f505828af1 | ||
|
8d565b93f7 | ||
|
867b28d439 | ||
|
fd3619f7b5 | ||
|
3a3143065d | ||
|
ca5640e89e | ||
|
e5cbdec67e | ||
|
19de815ab4 | ||
|
e2a1721a5c | ||
|
0d79d084a5 | ||
|
3c2394afa4 | ||
|
f38859237a | ||
|
48e2953910 | ||
|
1a281badc2 | ||
|
177d731957 | ||
|
bca3a1b04d | ||
|
5d4a2a4a0d | ||
|
c28a5ee66e | ||
|
d19369401f | ||
|
3ec1912046 | ||
|
36857a5064 | ||
|
f6319d36a1 | ||
|
f236934a39 | ||
|
46fdceaea6 | ||
|
f9ecb449cf | ||
|
596356a936 | ||
|
07695786ca | ||
|
040a011ff1 | ||
|
a717df3c5f | ||
|
4ab140f6a1 | ||
|
018042769d | ||
|
0c8db1e1fe | ||
|
42035bdb87 | ||
|
3b24a8ae8a | ||
|
96ca83704a | ||
|
28079e8129 | ||
|
ca5ad58f01 | ||
|
7da921e075 | ||
|
34f971c729 | ||
|
1715f35341 | ||
|
77802758a9 | ||
|
d98f626ba6 | ||
|
6b6c94556a | ||
|
a4cec49e89 | ||
|
94b392dd39 | ||
|
28f3bc61ea | ||
|
9ec829eddf | ||
|
aff3d8ba49 | ||
|
c9dc1ed655 | ||
|
8ab1d50f01 | ||
|
7453f12e66 | ||
|
78aff4c81f | ||
|
d6110013b8 | ||
|
01ce0f94af | ||
|
6350fe5411 | ||
|
e9d97fc7b1 | ||
|
d6e3c331d4 | ||
|
c697eb8d41 | ||
|
37cc817cbe | ||
|
c04cc7bec5 | ||
|
0e0883cfb8 | ||
|
fc56304af2 | ||
|
99da51270a | ||
|
511036be23 | ||
|
4a6e2a87aa | ||
|
a87323616d | ||
|
a7b45e7f54 | ||
|
a360026490 | ||
|
1f0f079193 | ||
|
9fdff97e29 | ||
|
5367921496 | ||
|
781bfc542f | ||
|
e0e3688361 | ||
|
ec690f888b | ||
|
0fe847c69a | ||
|
e371bb16e1 | ||
|
6afde13a01 | ||
|
1f975ac044 | ||
|
d3aaf7acff | ||
|
e296a329c2 | ||
|
930025afac | ||
|
38130f701c | ||
|
0b45248b8c | ||
|
2a1bef28d0 | ||
|
05c22a6802 | ||
|
11e25911d5 | ||
|
bd9e237376 | ||
|
0af0d84cf7 | ||
|
22f52fc4e1 | ||
|
121bd58029 | ||
|
64f393201f | ||
|
3a189f65f2 | ||
|
52293f54e8 | ||
|
7d5659aeac | ||
|
705f580290 | ||
|
507b8d5e35 | ||
|
4b57a34f59 | ||
|
fc4517fe4c | ||
|
1e5e9e04b4 | ||
|
4c6e707fcf | ||
|
ef61ecccaa | ||
|
5604000ae2 | ||
|
9bf9885ffb | ||
|
5692ec54dd | ||
|
66357b8adb | ||
|
575b0035df | ||
|
cf6f54bd28 | ||
|
527b7141a3 | ||
|
7f61483609 | ||
|
ae53990136 | ||
|
a2df590fae | ||
|
6f33c5223d | ||
|
3ad2f8b5fa | ||
|
6df16cfe96 | ||
|
806ea14ad2 | ||
|
f04e891e0c | ||
|
4272f96ade | ||
|
5f055d56cb | ||
|
7a9549b75a | ||
|
95ecb5609d | ||
|
971ae04020 | ||
|
21b6f582e1 | ||
|
c4d0a4e921 | ||
|
d91a971e85 | ||
|
a716dd3778 | ||
|
2c56e480c3 | ||
|
0674f39600 | ||
|
d8735da871 | ||
|
bd8a2a5f2a | ||
|
8a03b89434 | ||
|
67874bc9f8 | ||
|
ee0d1b5a5b | ||
|
14cb97d0ad | ||
|
1b1f3bbcbe | ||
|
47f25c4f31 | ||
|
f7e94fd450 | ||
|
4e6aee6bfb | ||
|
75be38925f | ||
|
ad640532dc | ||
|
c55bc8c05f | ||
|
3d70d9e496 | ||
|
ae949d607b | ||
|
7b775ca57d | ||
|
857090bdde | ||
|
d4eda78818 | ||
|
c07ba4e88e | ||
|
7294e2346e | ||
|
8ed4736066 | ||
|
e7a6c3e58c | ||
|
13be5a22ad | ||
|
4ee61a93c8 | ||
|
252abf862b | ||
|
37eb4a1008 | ||
|
b5479e480d | ||
|
bd9476a175 | ||
|
9a33789e41 | ||
|
f7c428a894 | ||
|
6827f17d4d | ||
|
f1e8a1a29a | ||
|
c7a490fa03 | ||
|
74486fc8c9 | ||
|
8fe610311d | ||
|
c7c00021b2 | ||
|
7d894228d0 | ||
|
f0880a27a0 | ||
|
9c1c8bfdf2 | ||
|
27865dc785 | ||
|
3a952e1938 | ||
|
69a85fb068 | ||
|
5db9a601b1 | ||
|
e5fbc4c971 | ||
|
af94f9b1f9 | ||
|
c58f9daf0d | ||
|
4ce16a0343 | ||
|
99b45c13fb | ||
|
6839fa5567 | ||
|
d641e21dfc | ||
|
2eb6451810 | ||
|
e2924f4cba | ||
|
546ad86841 | ||
|
4ffddd8c93 | ||
|
fad1f5c2f0 | ||
|
632c9845c1 | ||
|
1166db60d1 | ||
|
f78730e337 | ||
|
6895234815 | ||
|
d44f297068 | ||
|
3a45a31e65 | ||
|
f886320c44 | ||
|
7a6a0f68fe | ||
|
c10e48320b | ||
|
2e10936b3d | ||
|
eafab87540 | ||
|
0ed98729f8 | ||
|
fa2f50dea8 | ||
|
a3365a12bc | ||
|
e94b85a0fc | ||
|
848eac8623 | ||
|
56999fd1e0 | ||
|
1cf5899586 | ||
|
b73db47e01 | ||
|
58f231ad12 | ||
|
5155055446 | ||
|
e6675f1656 | ||
|
5940ebbccc | ||
|
ca15e93102 | ||
|
268244119f | ||
|
6b5f21d67b | ||
|
4e944052cd | ||
|
e97c945b2f | ||
|
bfd9e5b347 | ||
|
8593c2df9f | ||
|
07c02b5825 | ||
|
6c56fa29aa | ||
|
a5795461f2 | ||
|
40b024fbba | ||
|
663d0139b7 | ||
|
a3d50585c2 | ||
|
3376baa3c5 | ||
|
d25a85e0d4 | ||
|
4400b0f83e | ||
|
a241e594c1 | ||
|
64036a38cf | ||
|
268d8cff49 | ||
|
cf585c48ae | ||
|
e750e014ab | ||
|
b8ec4be95c | ||
|
c805102819 | ||
|
3617a29ba8 | ||
|
1de3a6b54a | ||
|
70f7f00abf | ||
|
c26977f578 | ||
|
57b47d2451 | ||
|
23ca345a2e | ||
|
971977a66a | ||
|
84b896151f | ||
|
d0056870be | ||
|
69ae4a7121 | ||
|
a23abc1bb6 | ||
|
0fcf996c78 | ||
|
e9adf0f30d | ||
|
9d26f030dc | ||
|
040edc8fd0 | ||
|
f8ed4e0c38 | ||
|
bb06492091 | ||
|
017a28b3dd | ||
|
35e0a47beb | ||
|
d5be6940c7 | ||
|
17a97f2f19 | ||
|
7a9cbe5d77 | ||
|
9d68cd8380 | ||
|
aba147f787 | ||
|
8a4f28a7cc | ||
|
b11a67e435 | ||
|
4f68fb0e26 | ||
|
f405994346 | ||
|
abaa98d2a9 | ||
|
5f7411114e | ||
|
8cc93a2255 | ||
|
0a48765c98 | ||
|
7d032ba69f | ||
|
34301b446a | ||
|
51aa500857 | ||
|
7a0c0d5d41 | ||
|
fad1fcd8ee | ||
|
54398ebe1c | ||
|
dbbb450172 | ||
|
bd95d5a86d | ||
|
fbbb4ed8fa | ||
|
1cc6ad1481 | ||
|
90aeb48040 | ||
|
07be1c6a44 | ||
|
ffd74e59ef | ||
|
5093569ce0 | ||
|
74697ee312 | ||
|
9fb6ebe7ac | ||
|
1dd0dac462 | ||
|
4182d7473c | ||
|
e4cbd85197 | ||
|
f10dbe7387 | ||
|
e989a4ebb0 | ||
|
45b745a580 | ||
|
635ec0e7a8 | ||
|
8120163f5e | ||
|
dad991e7aa | ||
|
09d2996451 | ||
|
daa418a287 | ||
|
ce7a97368f | ||
|
06d716248e | ||
|
3088b1245c | ||
|
f641fc1716 | ||
|
5207981fb2 | ||
|
fcbbc72a19 | ||
|
adf83bdd0a | ||
|
40a4010041 | ||
|
af890cf21d | ||
|
b6b6ba7265 | ||
|
f5d6d4079a | ||
|
7876ab825e | ||
|
66b79b4ea2 | ||
|
d60d178513 | ||
|
d9a8c047a2 | ||
|
fc47fd586c | ||
|
da2a846d3d | ||
|
cdbbedb662 | ||
|
5d31b7034d | ||
|
8e214b1232 | ||
|
8c8419a1c5 | ||
|
6e586da83e | ||
|
e85586db80 | ||
|
a923c112ee | ||
|
2c0c57ec4f | ||
|
82220ef87e | ||
|
333b9c184e | ||
|
f84c98e721 | ||
|
6cbd9631a0 | ||
|
37998ec598 | ||
|
5fb4434afe | ||
|
f29d68b16e | ||
|
db24b429c1 | ||
|
470ba64fe4 | ||
|
829ddc393f | ||
|
c16212e456 | ||
|
88ae6d9e1d | ||
|
f0a6fa13da | ||
|
0e72fa5058 | ||
|
91919d31b0 | ||
|
3f182c63bc | ||
|
effb94623a | ||
|
ef62f1e0d9 | ||
|
195a13a23c | ||
|
dc11b74020 | ||
|
8c328abdd2 | ||
|
91ad0a20ef | ||
|
17a1c7e0bf | ||
|
7c3c165fd6 | ||
|
8aadeb9291 | ||
|
e3ab25b3c9 | ||
|
e08a14b3a7 | ||
|
3b4bd5384c | ||
|
c342de28f2 | ||
|
489b0d050c | ||
|
d236470df8 | ||
|
cdc3abbc43 | ||
|
4bf2781cd0 | ||
|
79f4be3941 | ||
|
09c4655be7 | ||
|
f8aaf0cae3 | ||
|
93dff36ee2 | ||
|
15d888a40c | ||
|
b9e4b31c68 | ||
|
fb7e95c440 | ||
|
0c9270dbc1 | ||
|
0e62760abf | ||
|
ebc38a62ad | ||
|
cacfcb8c5c | ||
|
81534b3476 | ||
|
ee14f7ebb1 | ||
|
da8289f8ba | ||
|
4050cf238f | ||
|
29bebcd034 | ||
|
4e463af1df | ||
|
a66080d803 | ||
|
46fb0c9418 | ||
|
ea6b29f145 | ||
|
80d99073ec | ||
|
df18fcef92 | ||
|
46c96b45fc | ||
|
012e55e780 | ||
|
7d9abc9273 | ||
|
6301a7adb9 | ||
|
c0893a404d | ||
|
0aadef97d0 | ||
|
ddc59809d8 | ||
|
869a8058cc | ||
|
1a0f7df3f4 | ||
|
2e0de7b197 | ||
|
cf671ad7ff | ||
|
df8b2802b2 | ||
|
cd576a2a87 | ||
|
8e899b8524 | ||
|
3dd943f961 | ||
|
cf68caff99 | ||
|
f412796f28 | ||
|
563ad7761a | ||
|
188e4b6215 | ||
|
f258a3d52c | ||
|
cc97510523 | ||
|
613b973309 | ||
|
6a932f2eda | ||
|
73a237d75e | ||
|
1d9b1a3d5e | ||
|
5b2bd45d86 | ||
|
f7161ea890 | ||
|
664cd4e33d | ||
|
51eee01eef | ||
|
347bd04065 | ||
|
4cae37fbbf | ||
|
569fdc3861 | ||
|
46f6395279 | ||
|
f6676cf6a3 | ||
|
0ffab8ee69 | ||
|
1ee4de0edf | ||
|
8c11ba7fea | ||
|
da7aca8ef8 | ||
|
d4d98708f9 | ||
|
50137b31c4 | ||
|
c287739be7 | ||
|
98673b5743 | ||
|
3c75ed7d46 | ||
|
c65c72b249 | ||
|
3e4f1d5cd8 | ||
|
00a6a2d917 | ||
|
3ba42df321 | ||
|
6f5430a940 | ||
|
8928556c1d | ||
|
7d3a9ff36d | ||
|
31b41235ac | ||
|
9ea5a94941 | ||
|
a1e6839cae | ||
|
e2b2feb7c5 | ||
|
57f7c93033 | ||
|
f06ba41f91 | ||
|
6562f8adbd | ||
|
08ebef2905 | ||
|
c58b143552 | ||
|
d10b038e84 | ||
|
2376ce8d9d | ||
|
75b888a9f0 | ||
|
9af77c06b8 | ||
|
346223977d | ||
|
e90b261196 | ||
|
0d84967853 | ||
|
abaa347ad4 | ||
|
14b3fd2085 | ||
|
14fc2dbf9b | ||
|
c468d22120 | ||
|
3ccb5e0aed | ||
|
2bcf3eedf1 | ||
|
45a8030aeb | ||
|
a5e84dcfea | ||
|
9f3a451ef6 | ||
|
678fb0a8b3 | ||
|
d62a1b5ad3 | ||
|
84b9dce6be | ||
|
17019ac723 | ||
|
69067fe8c8 | ||
|
77e44c80c2 | ||
|
e1d18dc896 | ||
|
b4f9ade5bd | ||
|
39f6a2e17f | ||
|
92b4524dd6 | ||
|
aae5d037db | ||
|
844691fa2c | ||
|
f479e32b5f | ||
|
8ba2eb3330 | ||
|
8c0195970b | ||
|
3b4beba7d6 | ||
|
f2ee2248e0 | ||
|
5e9535db41 | ||
|
46f2a202e6 | ||
|
b599d86a1c | ||
|
8f55d3f9a4 | ||
|
41f4aadf40 | ||
|
d46af0136b | ||
|
39defaea5e | ||
|
c37ce767d7 | ||
|
4341001a1c | ||
|
0be8746c11 | ||
|
0d26dad397 | ||
|
ae9c5f8490 | ||
|
50ab8ad5c7 | ||
|
ad102ab7a9 | ||
|
8e16acde1d | ||
|
39a3c83801 | ||
|
6acd8498b2 | ||
|
1e49f59001 | ||
|
1ec927f30c | ||
|
5f8addbb5b | ||
|
ca1744aa97 | ||
|
6ab927fa4d | ||
|
e6b18cf230 | ||
|
275ba9bd84 | ||
|
9fd8984804 | ||
|
861fb45e5c | ||
|
420c45a29a | ||
|
4d4db7dcd0 | ||
|
c98f6e4f37 | ||
|
2485f5eccc | ||
|
d9418ec8ae | ||
|
bcceadee6b | ||
|
d7763c8542 | ||
|
3cf283a65b | ||
|
6d360db753 | ||
|
41073bb1a0 | ||
|
338be0ff31 | ||
|
0434b86d8e | ||
|
dd082d4f39 | ||
|
457861b879 | ||
|
a183425642 | ||
|
22f11abce8 | ||
|
35b1202951 | ||
|
4f4f3ea0aa | ||
|
c19b13adb4 | ||
|
af1a3c3ddb | ||
|
876d84aec0 | ||
|
aa3ae63682 | ||
|
b0c0689887 | ||
|
78acb857da | ||
|
a0e84d7a44 | ||
|
7af06a533b | ||
|
391869cf3a | ||
|
96ed367420 | ||
|
17c19766f7 | ||
|
79e8755d93 | ||
|
0af60e5680 | ||
|
256d7c4e5d | ||
|
d015e6b270 | ||
|
8836d1d75c | ||
|
b796106dd7 | ||
|
da264b52e1 | ||
|
f6c87b6726 | ||
|
717a1b5db4 | ||
|
e11a2d7f6d | ||
|
1fa5f4d725 | ||
|
7ef8b2f95e | ||
|
71130025e8 | ||
|
dd217fcb70 | ||
|
09b2cfef56 | ||
|
bae592bf91 | ||
|
75935f91e9 | ||
|
f6af9925e8 | ||
|
1cd2d8d121 | ||
|
ef5211d5b5 | ||
|
e40a657faf | ||
|
798e18ecc2 | ||
|
2bdf6ae18a | ||
|
75fbe654ee | ||
|
d1af6c38e7 | ||
|
f139088b6e | ||
|
6bfc3ceb95 | ||
|
c87a19da5a | ||
|
7f51d8374b | ||
|
930b9b07d5 | ||
|
46ed9a2d67 | ||
|
b599769f8c | ||
|
53681ccc59 | ||
|
04610822a2 | ||
|
cbe5cd4eb3 | ||
|
d2dee87ab9 | ||
|
b7f595cc50 | ||
|
8d35963171 | ||
|
106f9aed79 | ||
|
d48808f111 | ||
|
a7cbf1dd0a | ||
|
71f7103edd | ||
|
706ac35063 | ||
|
aa0ad16a1b |
1013
.editorconfig
Normale Datei
1013
.editorconfig
Normale Datei
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@ -1 +1 @@
|
||||
* @IntellectualSites/fawe-team
|
||||
* @IntellectualSites/fastasyncworldedit-team
|
||||
|
12
.github/FUNDING.yml
vendored
12
.github/FUNDING.yml
vendored
@ -1,12 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [NotMyFault, dordsor21, SirYwell]
|
||||
patreon: IntellectualSites # Replace with a single Patreon username
|
||||
open_collective: IntellectualSites
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://www.paypal.me/AlexanderBrandes # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
16
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
16
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -7,7 +7,8 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report for FastAsyncWorldEdit! Fill out the following form to your best ability to help us fix the problem.
|
||||
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/intellectualsites) or [the wiki](https://github.com/IntellectualSites/FastAsyncWorldEdit/wiki).
|
||||
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/intellectualsites) or [the wiki](https://intellectualsites.github.io/fastasyncworldedit-documentation/).
|
||||
Do NOT use the public issue tracker to report security vulnerabilities! They are disclosed using [this](https://github.com/IntellectualSites/FastAsyncWorldEdit/security/policy) GitHub form!
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
@ -17,8 +18,6 @@ body:
|
||||
options:
|
||||
- Paper
|
||||
- Spigot
|
||||
- Tuinity
|
||||
- Purpur
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@ -28,8 +27,11 @@ body:
|
||||
description: Which server version version you using? If your server version is not listed, it is not supported. Update to a supported version first.
|
||||
multiple: false
|
||||
options:
|
||||
- '1.16.5'
|
||||
- '1.15.2'
|
||||
- '1.20.1'
|
||||
- '1.20'
|
||||
- '1.19.4'
|
||||
- '1.18.2'
|
||||
- '1.17.1'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@ -80,7 +82,7 @@ body:
|
||||
attributes:
|
||||
label: Fawe Version
|
||||
description: What version of Fawe are you running? (`/version FastAsyncWorldEdit`)
|
||||
placeholder: "For example: FastAsyncWorldEdit version 1.16-606;4a6af71"
|
||||
placeholder: "For example: FastAsyncWorldEdit version 2.0.0-SNAPSHOT-1"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@ -91,7 +93,7 @@ body:
|
||||
options:
|
||||
- label: I have included a Fawe debugpaste.
|
||||
required: true
|
||||
- label: I am using the newest build from https://ci.athion.net/job/FastAsyncWorldEdit-1.16/ and the issue still persists.
|
||||
- label: I am using the newest build from https://ci.athion.net/job/FastAsyncWorldEdit/ and the issue still persists.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -4,5 +4,5 @@ contact_links:
|
||||
url: https://discord.gg/intellectualsites
|
||||
about: Our support Discord, please ask questions and seek support here.
|
||||
- name: FastAsyncWorldEdit Wiki
|
||||
url: https://github.com/IntellectualSites/FastAsyncWorldEdit/wiki
|
||||
url: https://intellectualsites.github.io/fastasyncworldedit-documentation/
|
||||
about: Look at the wiki page for answers for setup instructions command syntax and more.
|
||||
|
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -7,7 +7,7 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request for FastAsyncWorldEdit! Fill out the following form to your best ability to help us understand your feature request and greately improve the change of it getting added.
|
||||
For anything else than a feature request, use: [our Discord server](https://discord.gg/intellectualsites) or [the wiki](https://github.com/IntellectualSites/FastAsyncWorldEdit/wiki).
|
||||
For anything else than a feature request, use: [our Discord server](https://discord.gg/intellectualsites) or [the wiki](https://intellectualsites.github.io/fastasyncworldedit-documentation/).
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,19 +0,0 @@
|
||||
## Overview
|
||||
<!-- Please describe which issue this Pull Request targets
|
||||
|
||||
If there is no issue, please create one so we can look into it before approving your PR.
|
||||
You can do so here: https://github.com/IntellectualSites/FastAsyncWorldEdit/issues
|
||||
-->
|
||||
|
||||
<!-- Remove the brackets around the issue to connect your pull request with the issue it resolves -->
|
||||
**Fixes #{issue number}**
|
||||
|
||||
## Description
|
||||
<!-- Please describe what you have changed -->
|
||||
|
||||
## Checklist
|
||||
<!-- Make sure you have completed the following steps (put an "X" between of brackets): -->
|
||||
- [] I included all information required in the sections above
|
||||
- [] I tested my changes and approved their functionality
|
||||
- [] I ensured my changes do not break other parts of the code
|
||||
- [] I read and followed the [contribution guidelines](https://github.com/IntellectualSites/FastAsyncWorldEdit/blob/1.16/CONTRIBUTING.md)
|
1
.github/release-drafter.yml
vendored
Normale Datei
1
.github/release-drafter.yml
vendored
Normale Datei
@ -0,0 +1 @@
|
||||
_extends: .github
|
35
.github/renovate.json
vendored
Normale Datei
35
.github/renovate.json
vendored
Normale Datei
@ -0,0 +1,35 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base",
|
||||
":semanticCommitsDisabled"
|
||||
],
|
||||
"automerge": true,
|
||||
"ignoreDeps": [
|
||||
"guava",
|
||||
"com.google.guava:guava",
|
||||
"rhino-runtime",
|
||||
"org.antlr",
|
||||
"antlr4-runtime",
|
||||
"fastutil",
|
||||
"it.unimi.dsi:fastutil",
|
||||
"auto-value-annotations",
|
||||
"auto-value",
|
||||
"com.google.code.gson:gson",
|
||||
"net.fabricmc:fabric-loader",
|
||||
"net.fabricmc.fabric-api:fabric-api",
|
||||
"com.github.luben:zstd-jni",
|
||||
"org.jetbrains.kotlin.jvm",
|
||||
"log4j",
|
||||
"org.apache.logging.log4j:log4j-api",
|
||||
"org.apache.logging.log4j:log4j-bom",
|
||||
"org.apache.logging.log4j:log4j-slf4j-impl",
|
||||
"org.apache.logging.log4j:log4j-core",
|
||||
"org.bstats:bstats-sponge",
|
||||
"org.spongepowered:spongeapi",
|
||||
"org.yaml:snakeyaml"
|
||||
],
|
||||
"labels": ["Renovate"],
|
||||
"rebaseWhen": "conflicted",
|
||||
"schedule": ["on the first day of the month"]
|
||||
}
|
2
.github/stale.yml
vendored
2
.github/stale.yml
vendored
@ -1,4 +1,4 @@
|
||||
daysUntilStale: 60
|
||||
daysUntilStale: 30
|
||||
daysUntilClose: 7
|
||||
only: issues
|
||||
exemptLabels:
|
||||
|
28
.github/workflows/announce-release-on-discord.yml
vendored
Normale Datei
28
.github/workflows/announce-release-on-discord.yml
vendored
Normale Datei
@ -0,0 +1,28 @@
|
||||
name: Announce release on discord
|
||||
#on:
|
||||
# workflow_run:
|
||||
# workflows: ["Upload release assets"]
|
||||
# types:
|
||||
# - completed
|
||||
jobs:
|
||||
send_announcement:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: send custom message with args
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
DISCORD_USERNAME: FastAsyncWorldEdit Release
|
||||
DISCORD_AVATAR: https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.png
|
||||
uses: Ilshidur/action-discord@0.3.2
|
||||
with:
|
||||
args: |
|
||||
"<@&525015715300900875> <@&706463154804097105> <@&671372968462516240>"
|
||||
""
|
||||
"<:fawe:730750370984493106> **FastAsyncWorldEdit ${{ github.event.release.tag_name }} has been released!**"
|
||||
""
|
||||
"Click here to view changelog: https://github.com/IntellectualSites/FastAsyncWorldEdit/releases/tag/${{ github.event.release.tag_name }}"
|
||||
""
|
||||
"The download is available at:"
|
||||
"- Spigot: <https://www.spigotmc.org/resources/13932/>"
|
||||
"- Modrinth: <https://modrinth.com/plugin/fastasyncworldedit/version/${{ github.event.release.tag_name }}>"
|
||||
"- CurseForge: <https://www.curseforge.com/minecraft/bukkit-plugins/fawe>"
|
27
.github/workflows/build-pr.yml
vendored
Normale Datei
27
.github/workflows/build-pr.yml
vendored
Normale Datei
@ -0,0 +1,27 @@
|
||||
name: Build PR
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
build_pr:
|
||||
if: github.repository_owner == 'IntellectualSites'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
java-version: 17
|
||||
- name: Build on ${{ matrix.os }}
|
||||
run: ./gradlew build -s
|
||||
- name: Archive artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: FastAsyncWorldEdit-SNAPSHOT
|
||||
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar
|
130
.github/workflows/build.yml
vendored
130
.github/workflows/build.yml
vendored
@ -1,58 +1,78 @@
|
||||
name: "build"
|
||||
|
||||
on: ["pull_request", "push"]
|
||||
|
||||
name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
java: ["8", "11"]
|
||||
os: ["ubuntu-latest"]
|
||||
runs-on: "${{ matrix.os }}"
|
||||
if: github.repository_owner == 'IntellectualSites'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: "actions/checkout@v2.3.4"
|
||||
- name: "Setup JDK ${{ matrix.java }}"
|
||||
uses: "actions/setup-java@v2"
|
||||
with:
|
||||
distribution: "adopt"
|
||||
java-version: "${{ matrix.java }}"
|
||||
- name: "Cache Gradle"
|
||||
uses: "actions/cache@v2.1.4"
|
||||
with:
|
||||
path: |
|
||||
"~/.gradle/caches"
|
||||
"~/.gradle/wrapper"
|
||||
key: "${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}"
|
||||
restore-keys: |
|
||||
"${{ runner.os }}-gradle-"
|
||||
- name: "Cache Local Maven Repository"
|
||||
uses: "actions/cache@v2.1.4"
|
||||
with:
|
||||
path: "~/.m2/repository"
|
||||
key: "${{ runner.os }}-${{ matrix.java }}-maven-${{ hashFiles('**/pom.xml') }}"
|
||||
restore-keys: |
|
||||
"${{ runner.os }}-${{ matrix.java }}-maven-"
|
||||
- name: "Cache BuildTools Decompiled Code"
|
||||
uses: "actions/cache@v2.1.4"
|
||||
with:
|
||||
path: "$GITHUB_WORKSPACE/work"
|
||||
key: "${{ runner.os }}-buildtools"
|
||||
restore-keys: |
|
||||
"${{ runner.os }}-buildtools"
|
||||
- name: "Test Enviornment"
|
||||
run: "echo $GITHUB_WORKSPACE"
|
||||
- name: "Download BuildTools"
|
||||
run: "wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar"
|
||||
- name: "Run BuildTools"
|
||||
run: "java -jar BuildTools.jar --rev 1.16.5"
|
||||
- name: "Clean Build"
|
||||
run: "./gradlew clean build sourcesJar javadocJar"
|
||||
- name: Cleanup Gradle Cache
|
||||
# Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
|
||||
# Restoring these files from a GitHub Actions cache might cause problems for future builds.
|
||||
run: |
|
||||
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||
rm -f ~/.gradle/caches/modules-2/gc.properties
|
||||
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
java-version: 17
|
||||
- name: Clean Build
|
||||
run: ./gradlew clean build --no-daemon
|
||||
- name: Determine release status
|
||||
if: ${{ runner.os == 'Linux' }}
|
||||
run: |
|
||||
if [ "$(./gradlew properties | awk '/^version:/ { print $2; }' | grep '\-SNAPSHOT')" ]; then
|
||||
echo "STATUS=snapshot" >> $GITHUB_ENV
|
||||
else
|
||||
echo "STATUS=release" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Publish Release
|
||||
if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
|
||||
run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
|
||||
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
|
||||
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
|
||||
- name: Publish Snapshot
|
||||
if: ${{ runner.os == 'Linux' && env.STATUS != 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
run: ./gradlew publishToSonatype
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_sonatypeUsername: ${{ secrets.SONATYPE_USERNAME }}
|
||||
ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }}
|
||||
- name: Publish core javadoc
|
||||
if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
|
||||
uses: cpina/github-action-push-to-another-repository@main
|
||||
env:
|
||||
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
|
||||
with:
|
||||
source-directory: 'worldedit-core/build/docs/javadoc'
|
||||
destination-github-username: 'IntellectualSites'
|
||||
destination-repository-name: 'fastasyncworldedit-javadocs'
|
||||
user-email: ${{ secrets.USER_EMAIL }}
|
||||
target-branch: main
|
||||
target-directory: worldedit-core
|
||||
- name: Publish bukkit javadoc
|
||||
if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
|
||||
uses: cpina/github-action-push-to-another-repository@main
|
||||
env:
|
||||
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
|
||||
with:
|
||||
source-directory: 'worldedit-bukkit/build/docs/javadoc'
|
||||
destination-github-username: 'IntellectualSites'
|
||||
destination-repository-name: 'fastasyncworldedit-javadocs'
|
||||
user-email: ${{ secrets.USER_EMAIL }}
|
||||
target-branch: main
|
||||
target-directory: worldedit-bukkit
|
||||
- name: Archive Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: FastAsyncWorldEdit-Bukkit-SNAPSHOT
|
||||
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar
|
||||
- name: Publish to Modrinth
|
||||
if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}}
|
||||
run: ./gradlew modrinth
|
||||
env:
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
|
36
.github/workflows/codeql.yml
vendored
Normale Datei
36
.github/workflows/codeql.yml
vendored
Normale Datei
@ -0,0 +1,36 @@
|
||||
name: "CodeQL"
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [main]
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['java']
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
java-version: 17
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
17
.github/workflows/release-drafter.yml
vendored
Normale Datei
17
.github/workflows/release-drafter.yml
vendored
Normale Datei
@ -0,0 +1,17 @@
|
||||
name: draft release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
jobs:
|
||||
update_release_draft:
|
||||
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
26
.github/workflows/upload-release-assets.yml
vendored
Normale Datei
26
.github/workflows/upload-release-assets.yml
vendored
Normale Datei
@ -0,0 +1,26 @@
|
||||
name: Upload release assets
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
jobs:
|
||||
upload_asset:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Validate Gradle Wrapper
|
||||
uses: gradle/wrapper-validation-action@v1
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
cache: gradle
|
||||
java-version: 17
|
||||
- name: Clean Build
|
||||
run: ./gradlew clean build --no-daemon
|
||||
- name: Upload Release Assets
|
||||
uses: AButler/upload-release-assets@v2.0
|
||||
with:
|
||||
files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release-tag: ${{ github.event.release.tag_name }}
|
12
.github/workflows/validate-gradle-wrapper.yml
vendored
12
.github/workflows/validate-gradle-wrapper.yml
vendored
@ -1,12 +0,0 @@
|
||||
name: "validate gradle wrapper"
|
||||
|
||||
on: ["pull_request", "push"]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-18.04"
|
||||
steps:
|
||||
- name: "Checkout Repository"
|
||||
uses: "actions/checkout@v2.3.4"
|
||||
- name: "Validate Gradle Wrapper"
|
||||
uses: "gradle/wrapper-validation-action@v1.0.3"
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -2,7 +2,8 @@
|
||||
.project
|
||||
.settings
|
||||
eclipse
|
||||
.idea
|
||||
/.idea/*
|
||||
!/.idea/icon.svg
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
@ -23,7 +24,6 @@ logs/
|
||||
worldedit-bukkit/src/main/java/ignore/*
|
||||
todo.txt
|
||||
mvn/*
|
||||
docs/
|
||||
*.sh
|
||||
# i18n
|
||||
worldedit-core/src/main/resources/lang/*
|
||||
@ -31,3 +31,7 @@ worldedit-core/src/main/resources/lang/*
|
||||
**/*.eml
|
||||
/worldedit-core/FastAsyncWorldEdit.worldedit-core.eml
|
||||
/worldedit-core/.factorypath
|
||||
|
||||
.DS_Store
|
||||
### Run server ignore
|
||||
run-*
|
||||
|
112
.idea/icon.svg
Normale Datei
112
.idea/icon.svg
Normale Datei
@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
xml:space="preserve"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512.00001"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:version="1.1.2 (b8e25be8, 2022-02-05)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath18">
|
||||
<path
|
||||
d="M 0,2500 H 3000 V 0 H 0 Z"
|
||||
id="path16" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath18-2"><path
|
||||
d="M 0,2500 H 3000 V 0 H 0 Z"
|
||||
id="path16-1" /></clipPath></defs>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="872"
|
||||
id="namedview4"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
width="512px"
|
||||
inkscape:snap-others="false"
|
||||
inkscape:zoom="0.2263301"
|
||||
inkscape:cx="797.50773"
|
||||
inkscape:cy="547.87234"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="28"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="g10" />
|
||||
<g
|
||||
id="g10"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="FastAsyncWorldEdit"
|
||||
transform="matrix(1.3333333,0,0,-1.3333333,0,3333.3333)">
|
||||
|
||||
<g
|
||||
id="g12"
|
||||
transform="matrix(0.17875858,0,0,0.17875858,-78.817913,2093.6589)"><g
|
||||
id="g14"
|
||||
clip-path="url(#clipPath18-2)"><g
|
||||
id="g20"
|
||||
transform="translate(1643.0798,2025.9724)"><path
|
||||
d="m 0,0 c 34.839,11.026 70.869,18.313 107.28,21.408 18.861,-48.96 46.604,-93.992 77.514,-136.19 49.794,-66.487 110.495,-124.616 178.34,-172.506 78.156,-55.366 165.481,-97.254 256.496,-126.783 83.229,-27.124 169.601,-44.008 256.592,-53.295 -1.047,-153.146 -64.344,-303.076 -167.124,-415.762 -47.77,-52.914 -103.208,-99.136 -164.838,-135.047 -91.873,-53.843 -197.557,-83.896 -304.076,-85.705 -10.764,101.16 -31.91,201.534 -67.273,297.051 -28.243,76.537 -65.678,149.835 -112.853,216.465 -47.865,67.583 -105.685,128.332 -172.125,177.888 -42.102,31.22 -87.348,58.939 -136.404,77.823 3.834,48.628 15.479,96.636 33.625,141.906 19.169,46.769 46.96,90.968 85.752,123.926 C -91.706,-36.816 -46.722,-14.717 0,0"
|
||||
style="fill:#4c8fcc;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path22" /></g><g
|
||||
id="g24"
|
||||
transform="translate(1887.3362,1969.7008)"><path
|
||||
d="m 0,0 c 20.837,19.313 56.319,19.718 77.228,0.333 32.243,-32.053 64.272,-64.296 96.492,-96.373 17.313,-17.384 20.766,-46.27 8.93,-67.631 -9.073,-15.431 -23.813,-26.314 -35.744,-39.388 -37.339,25.29 -72.917,53.105 -106.637,83.015 -17.122,17.074 -36.316,32.076 -51.771,50.794 -8.525,10.74 -20.432,18.813 -26.457,31.458 C -25.242,-25.242 -12.788,-12.431 0,0"
|
||||
style="fill:#062f4c;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path26" /></g><g
|
||||
id="g28"
|
||||
transform="translate(1463.3822,1497.8577)"><path
|
||||
d="m 0,0 c -4.644,-0.786 -9.097,-2.786 -12.383,-6.168 -292.384,-292.645 -585.053,-585.029 -877.484,-877.627 -6.74,-6.382 -14.741,-13.145 -14.717,-23.337 -1.262,-12.383 8.668,-21.242 17.384,-28.291 9.882,-6.882 24.552,-4.215 32.481,4.525 293.718,293.693 587.411,587.387 881.104,881.104 10.74,9.049 12.241,27.219 1.929,37.101 C 21.48,-4.406 11.264,2.715 0,0 M -994.266,-946.234 C -647.088,-599.055 -299.885,-251.901 47.27,95.326 84.134,65.44 116.592,30.505 147.502,-5.382 c 23.266,-26.814 43.579,-55.986 64.749,-84.419 -346.679,-346.584 -693.215,-693.286 -1039.941,-1039.822 -20.623,-21.98 -58.439,-23.599 -80.514,-2.953 -32.172,32.149 -64.321,64.345 -96.493,96.493 -16.503,16.741 -19.932,44.007 -9.882,64.963 4.572,9.907 12.692,17.384 20.313,24.886"
|
||||
style="fill:#062f4c;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path30" /></g><g
|
||||
id="g32"
|
||||
transform="translate(1827.8735,1911.1907)"><path
|
||||
d="M 0,0 C 7.859,6.168 14.288,13.883 21.504,20.718 27.529,8.073 39.435,0 47.961,-10.74 c 15.455,-18.717 34.648,-33.72 51.77,-50.794 33.721,-29.91 69.298,-57.725 106.638,-83.015 -9.383,-9.263 -18.67,-18.67 -28.029,-27.957 C 110.495,-124.617 49.794,-66.488 0,0"
|
||||
style="fill:#081d2d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path34" /></g><g
|
||||
id="g36"
|
||||
transform="translate(1531.0129,1613.4966)"><path
|
||||
d="m 0,0 c 66.44,-49.556 124.26,-110.305 172.125,-177.888 -9.145,-9.192 -18.36,-18.336 -27.505,-27.552 -21.17,28.433 -41.483,57.605 -64.749,84.419 C 48.961,-85.134 16.503,-50.199 -20.361,-20.313 -13.574,-13.55 -6.811,-6.763 0,0"
|
||||
style="fill:#081d2d;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path38" /></g><g
|
||||
id="g40"
|
||||
transform="translate(2519.302,1558.6062)"><path
|
||||
d="M 0,0 C 21.408,-2.334 42.865,-3.763 64.321,-5.596 73.179,-84.086 63.178,-164.123 40.126,-239.47 3.072,-360.657 -72.393,-468.319 -168.696,-549.976 c -101.446,-87.038 -230.349,-142.429 -363.943,-152.121 -32.72,-2.548 -65.726,-2.453 -98.279,1.786 -0.857,21.313 -3.072,42.555 -5.12,63.797 106.518,1.81 212.203,31.862 304.076,85.705 61.63,35.911 117.068,82.133 164.838,135.047 C -64.344,-303.076 -1.048,-153.145 0,0"
|
||||
style="fill:#6fc4ee;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path42" /></g><g
|
||||
id="g44"
|
||||
transform="translate(1463.3822,1497.8577)"><path
|
||||
d="m 0,0 c 11.264,2.715 21.48,-4.406 28.314,-12.693 10.312,-9.882 8.811,-28.052 -1.929,-37.101 -293.693,-293.717 -587.386,-587.411 -881.104,-881.104 -7.929,-8.74 -22.599,-11.407 -32.481,-4.525 -8.716,7.049 -18.646,15.908 -17.384,28.291 -0.024,10.192 7.977,16.955 14.717,23.337 292.431,292.598 585.1,584.982 877.484,877.627 C -9.097,-2.786 -4.644,-0.786 0,0"
|
||||
style="fill:#105677;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path46" /></g></g></g></g>
|
||||
</svg>
|
Nachher Breite: | Höhe: | Größe: 6.3 KiB |
54
Annotation-Explanation.adoc
Normale Datei
54
Annotation-Explanation.adoc
Normale Datei
@ -0,0 +1,54 @@
|
||||
:toc:
|
||||
:toclevels: 2
|
||||
|
||||
= Fawe annotations explained
|
||||
|
||||
If we have modified parts of the WorldEdit codebase, we considered annotating it with different styles of comments, which
|
||||
are explained in this document.
|
||||
|
||||
== In-line annotations
|
||||
|
||||
[source,java]
|
||||
-----------------
|
||||
public static Player adapt(com.sk89q.worldedit.entity.Player player) {
|
||||
//FAWE start - Get player from PlayerProxy instead of BukkitPlayer if null
|
||||
player = PlayerProxy.unwrap(player);
|
||||
return player == null ? null : ((BukkitPlayer) player).getPlayer();
|
||||
//FAWE end
|
||||
}
|
||||
-----------------
|
||||
The `-sources` jar retains comments, if you add the FAWE API to your maven or gradle project, you can view differences between the projects with ease.
|
||||
Behind the `//FAWE start - ` you can find a comment what has been changed and why it has been changed.
|
||||
|
||||
== Block annotations
|
||||
|
||||
[source,java]
|
||||
-----------------
|
||||
//FAWE start
|
||||
@Override
|
||||
public void setPermission(String permission, boolean value) {
|
||||
}
|
||||
//FAWE end
|
||||
-----------------
|
||||
Annotations can cover whole methods or go beyond the method and wrap around several added methods.
|
||||
|
||||
== Package annotations
|
||||
Class additions are added under the `com.fastasyncworldedit` namespace, but sometimes classes need to be added in package private.
|
||||
If that is done, you can find a `package-info.java` file within the package affected that outlines FAWE added classes:
|
||||
[source,java]
|
||||
-----------------
|
||||
/**
|
||||
* The following classes are FAWE additions:
|
||||
*
|
||||
* @see com.sk89q.worldedit.world.block.BlockTypesCache
|
||||
*/
|
||||
package com.sk89q.worldedit.world.block;
|
||||
-----------------
|
||||
|
||||
== Undocumented annotations
|
||||
Specific changes are not annotated:
|
||||
|
||||
* `com.fastasyncworldedit.core.configuration.Caption` in `com.sk89q.worldedit` packages have been changed from
|
||||
`com.sk89q.worldedit.util.formatting.text.Text` to allow the usage of color codes for messages.
|
||||
|
||||
* Certain Log4J loggers have been adjusted to use the proper format of placeholders.
|
@ -1,12 +1,14 @@
|
||||
Compiling
|
||||
=========
|
||||
:toc:
|
||||
:toclevels: 2
|
||||
|
||||
You can compile FastAsyncWorldEdit as long as you have some version of Java greater than or equal to 8 installed. Gradle will download JDK 8 specifically if needed,
|
||||
= Compiling
|
||||
|
||||
You can compile FastAsyncWorldEdit as long as you have some version of Java greater than or equal to 17 installed. Gradle will download JDK 17 specifically if needed,
|
||||
but it needs some version of Java to bootstrap from.
|
||||
|
||||
Note that if you have JRE 8 installed, Gradle will currently attempt to use that to compile, which will not work. It is easiest to uninstall JRE 8 and replace it with JDK 8.
|
||||
Note that if you have JRE 8 installed, Gradle will currently attempt to use that to compile, which will not work. It is easiest to uninstall JRE 8 and replace it with JDK 17.
|
||||
|
||||
You can get the JDK 8 [here](https://adoptopenjdk.net/?variant=openjdk8&jvmVariant=hotspot).
|
||||
You can get the JDK 17 link:https://adoptium.net/[here] from Adoptium.
|
||||
|
||||
The build process uses Gradle, which you do *not* need to download. FastAsyncWorldEdit is a multi-module project with three active modules:
|
||||
|
||||
@ -14,23 +16,19 @@ The build process uses Gradle, which you do *not* need to download. FastAsyncWor
|
||||
* `worldedit-bukkit` is the Bukkit plugin
|
||||
* `worldedit-cli` is the command line interface
|
||||
|
||||
## To compile...
|
||||
== To compile...
|
||||
|
||||
### NMS
|
||||
FastAsyncWorldEdit uses NMS (net.minecraft.server) code in a variety of spots. NMS is not distributed via maven and therefore FastAsyncWorldEdit may not build without errors if you didn't install it into your local repository beforehand.
|
||||
You can do that by either running Spigot's [BuildTools](https://www.spigotmc.org/wiki/buildtools/) targeting the versions needed or using Paper's paperclip with `java -Dpaperclip.install=true -jar paperclip.jar`.
|
||||
|
||||
### On Windows
|
||||
=== On Windows
|
||||
|
||||
1. Shift + right-click the folder with FastAsyncWorldEdit's files and click "Open command prompt".
|
||||
2. `gradlew clean build`
|
||||
|
||||
### On Linux, BSD, or Mac OS X
|
||||
=== On Linux, BSD, or Mac OS X
|
||||
|
||||
1. In your terminal, navigate to the folder with FastAsyncWorldEdit's files (`cd /folder/of/fawe/files`)
|
||||
2. `./gradlew clean build`
|
||||
|
||||
## Then you will find...
|
||||
== Then you will find...
|
||||
|
||||
You will find:
|
||||
|
||||
@ -38,14 +36,14 @@ You will find:
|
||||
* FastAsyncWorldEdit for Bukkit in **worldedit-bukkit/build/libs**
|
||||
* the CLI version in **worldedit-cli/build/libs**
|
||||
|
||||
If you want to use FastAsyncWorldEdit, use the `FastAsyncWorldEdit-1.16-<commitHash>` version obtained in **worldedit-bukkit/build/libs**.
|
||||
If you want to use FastAsyncWorldEdit, use the `FastAsyncWorldEdit-<identifier>` version obtained in **worldedit-bukkit/build/libs**.
|
||||
|
||||
(The `-#` version includes FastAsyncWorldEdit + necessary libraries.)
|
||||
|
||||
## Other commands
|
||||
== Other commands
|
||||
|
||||
* `gradlew idea` will generate an [IntelliJ IDEA](http://www.jetbrains.com/idea/) module for each folder.
|
||||
* `gradlew idea` will generate an link:https://www.jetbrains.com/idea/[IntelliJ IDEA] module for each folder.
|
||||
|
||||
_Possibly broken_:
|
||||
* `gradlew eclipse` will generate an [Eclipse](https://www.eclipse.org/downloads/) project for each folder.
|
||||
* `gradlew eclipse` will generate an link:https://www.eclipse.org/downloads/[Eclipse] project for each folder.
|
||||
|
@ -1,74 +0,0 @@
|
||||
Contributing
|
||||
============
|
||||
|
||||
Thank you for your interest in contributing to FastAsyncWorldEdit! We appreciate your
|
||||
effort, but to make sure that the inclusion of your patch is a smooth process, we
|
||||
ask that you make note of the following guidelines.
|
||||
|
||||
* **Follow the [Oracle coding conventions](http://www.oracle.com/technetwork/java/codeconv-138413.html).**
|
||||
We can't stress this enough; if your code has notable issues, it may delay
|
||||
the process significantly.
|
||||
* **Target Java 8 for source and compilation.** Make sure to mark methods with
|
||||
` @Override` that override methods of parent classes, or that implement
|
||||
methods of interfaces.
|
||||
* **Use only spaces for indentation.** Our indents are 4-spaces long, and tabs
|
||||
are unacceptable.
|
||||
* **Wrap code to a 120 column limit.** We do this to make side by side diffs
|
||||
and other such tasks easier. Ignore this guideline if it makes the code
|
||||
too unreadable.
|
||||
* **Write complete Javadocs.** Do so only for public methods, and make sure
|
||||
that your `@param` and `@return` fields are not just blank.
|
||||
* **Don't tag classes with @author.** Some legacy classes may have this tag,
|
||||
but we are phasing it out.
|
||||
* **Make sure the code is efficient.** One way you can achieve this is to spend
|
||||
around ten minutes to think about what the code is doing and whether it
|
||||
seems awfully roundabout. If you had to copy the same large piece of
|
||||
code in several places, that's bad.
|
||||
* **Keep commit summaries under 70 characters.** For more details, place two
|
||||
new lines after the summary line and write away!
|
||||
* **Test your code.** We're not interested in broken code, for the obvious reasons.
|
||||
* **Write unit tests.** While this is strictly optional, we recommend it for
|
||||
complicated algorithms.
|
||||
|
||||
|
||||
Additional Notes
|
||||
----------------
|
||||
If you're submitting a feature, base your code on `main` and PR against the
|
||||
`main` branch.
|
||||
|
||||
Checklist
|
||||
---------
|
||||
|
||||
Ready to submit? Perform the checklist below:
|
||||
|
||||
1. Have all tabs been replaced into four spaces? Are indentations 4-space wide?
|
||||
2. Have I written proper Javadocs for my public methods? Are the @param and
|
||||
@return fields actually filled out?
|
||||
3. Have I `git rebase`d my pull request to the latest commit of the target
|
||||
branch?
|
||||
4. Have I combined my commits into a reasonably small number (if not one)
|
||||
commit using `git rebase`?
|
||||
5. Have I made my pull request too large? Pull requests should introduce
|
||||
small sets of changes at a time. Major changes should be discussed with
|
||||
the team prior to starting work.
|
||||
6. Are my commit messages descriptive?
|
||||
|
||||
You should be aware of [`git rebase`](http://learn.github.com/p/rebasing.html).
|
||||
It allows you to modify existing commit messages, and combine, break apart, or
|
||||
adjust past changes.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
This is **GOOD:**
|
||||
|
||||
if (var.func(param1, param2)) {
|
||||
// do things
|
||||
}
|
||||
|
||||
This is **VERY BAD:**
|
||||
|
||||
if(var.func( param1, param2 ))
|
||||
{
|
||||
// do things
|
||||
}
|
36
Jenkinsfile
vendored
Normale Datei
36
Jenkinsfile
vendored
Normale Datei
@ -0,0 +1,36 @@
|
||||
pipeline {
|
||||
agent any
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
withEnv([
|
||||
"PATH+JAVA=${tool 'Temurin-17.0.7_7'}/bin"
|
||||
]) {
|
||||
sh './gradlew clean build'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Archive artifacts') {
|
||||
steps {
|
||||
sh 'rm -rf artifacts'
|
||||
sh 'mkdir artifacts'
|
||||
sh 'cp worldedit-bukkit/build/libs/FastAsyncWorldEdit*.jar artifacts/'
|
||||
sh 'cp worldedit-cli/build/libs/FastAsyncWorldEdit*.jar artifacts/'
|
||||
archiveArtifacts artifacts: 'artifacts/*.jar', followSymlinks: false
|
||||
}
|
||||
}
|
||||
stage('Fingerprint artifacts') {
|
||||
steps {
|
||||
fingerprint 'worldedit-bukkit/build/libs/FastAsyncWorldEdit*.jar'
|
||||
}
|
||||
}
|
||||
stage('Publish JUnit test results') {
|
||||
steps {
|
||||
junit 'worldedit-core/build/test-results/test/*.xml,worldedit-bukkit/build/test-results/test/*.xml'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
README.md
108
README.md
@ -1,47 +1,95 @@
|
||||
<p align="center">
|
||||
<img src="fawe-logo.png" width="150">
|
||||
</p>
|
||||
# FastAsyncWorldEdit
|
||||
[![Join us on Discord](https://img.shields.io/discord/268444645527126017.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/intellectualsites)
|
||||
[![bStats Servers](https://img.shields.io/bstats/servers/1403)](https://bstats.org/plugin/bukkit/FastAsyncWorldEdit/1403)
|
||||
[![Crowdin](https://badges.crowdin.net/e/4a5819fae3fd88234a8ea13bfbb072bb/localized.svg)](https://intellectualsites.crowdin.com/fastasyncworldedit)
|
||||
|
||||
---
|
||||
## What is FAWE and why should I use it?
|
||||
|
||||
FastAsyncWorldEdit is a fork of WorldEdit that has huge speed and memory improvements and considerably more features
|
||||
FAWE is designed for efficient world editing.
|
||||
* Simple to set up and use
|
||||
* Extremely configurable
|
||||
* Uses minimal CPU/Memory
|
||||
* Safe for many players to use
|
||||
* Insanely fast, when using the slowest mode
|
||||
|
||||
**A Minecraft Map Editor... that runs in-game!**
|
||||
FastAsyncWorldEdit is a fork of WorldEdit that has huge speed and memory improvements and considerably more features.
|
||||
If you use other plugins which depend on WorldEdit, simply having FAWE installed will boost their performance.
|
||||
|
||||
* With selections, schematics, copy and paste, brushes, and scripting!
|
||||
* Use it in creative, survival in single player or on your server.
|
||||
* Use it on your Minecraft server to fix grieving and mistakes.
|
||||
## Downloads
|
||||
|
||||
Java Edition required. FastAsyncWorldEdit is compatible with Bukkit, Spigot, Paper, and Tuinity.
|
||||
Downloads are available either on SpigotMC, Modrinth or on CurseForge.
|
||||
- [SpigotMC](https://www.spigotmc.org/resources/13932/)
|
||||
- [Modrinth](https://modrinth.com/plugin/fastasyncworldedit/)
|
||||
- [CurseForge](https://dev.bukkit.org/projects/fawe)
|
||||
|
||||
## Download FastAsyncWorldEdit
|
||||
* Spigot: https://www.spigotmc.org/resources/fast-async-worldedit.13932/
|
||||
* Older, unsupported, versions: https://intellectualsites.github.io/download/fawe.html
|
||||
Snapshots are available on [Jenkins](https://ci.athion.net/job/FastAsyncWorldEdit/).
|
||||
|
||||
## Links
|
||||
## Features
|
||||
|
||||
* [Discord](https://discord.gg/intellectualsites)
|
||||
* [Wiki](https://github.com/IntellectualSites/FastAsyncWorldEdit/wiki)
|
||||
* [Report Issue](https://github.com/IntellectualSites/FastAsyncWorldEdit/issues)
|
||||
* [Crowdin (Translations)](https://intellectualsites.crowdin.com/fastasyncworldedit)
|
||||
* [JavaDocs](https://ci.athion.net/job/FastAsyncWorldEdit-1.16/javadoc/)
|
||||
* Over 200 Commands
|
||||
* Style and translate messages and commands
|
||||
* (No setup required) Clipboard web integration (Clipboard)
|
||||
* Unlimited //undo, per world history, instant lookups/rollback and cross server clipboards
|
||||
* Advanced per player limits (entity, tiles, memory, changes, iterations, regions, inventory)
|
||||
* Visualization, targeting modes/masks and scroll actions
|
||||
* Adds lots of powerful new //brushes and //tools.
|
||||
* Adds a lot more mask functionality. (new mask syntax, patterns, expressions, source masks)
|
||||
* Adds a lot more pattern functionality. (a lot of new pattern syntax and patterns)
|
||||
* Adds edit transforms (apply transforms to a source, e.g. on //paste)
|
||||
* Adds support for new formats (e.g. Structure Blocks)
|
||||
* Instant copying of arbitrary size with `//lazycopy`
|
||||
* Auto repair partially corrupt schematic files
|
||||
* Biome mixing, in-game world painting, dynamic view distance, vanilla cui, off axis rotation, image importing, cave generation,
|
||||
multi-clipboards, interactive messages, schematic visualization, lag prevention, persistent brushes + A LOT MORE
|
||||
|
||||
## Edit The Code
|
||||
### Performance
|
||||
|
||||
There are several placement modes, each supporting higher throughput than the previous. All editing is processed
|
||||
asynchronously, with
|
||||
certain tasks being broken up on the main thread. The default mode is chunk placement.
|
||||
* Blocks (Bukkit-API) - Only used if chunk placement isn't supported. Still faster than any other plugin on spigot.
|
||||
* Chunks (NMS) - Places entire chunk sections
|
||||
* World (CFI) - Used to generate new worlds / regions
|
||||
|
||||
### Protection Plugins
|
||||
|
||||
The following plugins are supported with Bukkit:
|
||||
* [WorldGuard](https://dev.bukkit.org/projects/worldguard)
|
||||
* [PlotSquared](https://www.spigotmc.org/resources/77506/)
|
||||
|
||||
### Logging and Rollback
|
||||
|
||||
By default you can use `//inspect` and `//history rollback` to search and restore changes. To reduce disk usage, increase the
|
||||
compression level and buffer size. To bypass logging use `//fast`.
|
||||
|
||||
### Developer API
|
||||
|
||||
FAWE maintains API compatibility with WorldEdit, so you can use the normal WorldEdit API asynchronously.
|
||||
FAWE also has some asynchronously wrappers for the Bukkit API.
|
||||
The wiki has examples for various things like reading NBT, modifying world files, pasting schematics, splitting up tasks, lighting etc.
|
||||
If you need help with anything, hop on discord (link on the left bar).
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Wiki](https://intellectualsites.github.io/fastasyncworldedit-documentation/)
|
||||
* [Javadocs](https://intellectualsites.github.io/fastasyncworldedit-javadocs/)
|
||||
|
||||
## Contributing
|
||||
|
||||
Want to add new features to FastAsyncWorldEdit or fix bugs yourself? You can get the game running, with FastAsyncWorldEdit, from the code here:
|
||||
|
||||
For additional information about compiling FastAsyncWorldEdit, see [COMPILING.md](COMPILING.md).
|
||||
For additional information about compiling FastAsyncWorldEdit, read the [compiling documentation](https://github.com/IntellectualSites/FastAsyncWorldEdit/blob/main/COMPILING.adoc).
|
||||
|
||||
## Submitting Your Changes
|
||||
FastAsyncWorldEdit is open source (specifically licensed under GPL v3), so note that your contributions will also be open source. The best way to submit a change is to create a fork on GitHub, put your changes there, and then create a "pull request" on our FastAsyncWorldEdit repository.
|
||||
## Special thanks
|
||||
|
||||
Please read [CONTRIBUTING.md](CONTRIBUTING.md) for important guidelines to follow.
|
||||
|
||||
## YourKit
|
||||
<a href="https://www.yourkit.com">
|
||||
<img src="https://www.yourkit.com/images/yklogo.png">
|
||||
<a href="https://jb.gg/OpenSourceSupport"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg" width="150">
|
||||
</a>
|
||||
</br>
|
||||
|
||||
[JetBrains](https://jb.gg/OpenSourceSupport), creators of the IntelliJ IDEA, supports us with their Open Source Licenses.
|
||||
|
||||
<a href="https://yourkit.com/"><img src="https://www.yourkit.com/images/yklogo.png" width="200">
|
||||
</a>
|
||||
|
||||
Thank you to YourKit for supporting our product by providing us with their innovative and intelligent tools
|
||||
for monitoring and profiling Java and .NET applications.
|
||||
YourKit is the creator of <a href="https://www.yourkit.com/java/profiler/">YourKit Java Profiler</a>, <a href="https://www.yourkit.com/.net/profiler/">YourKit .NET Profiler</a>, and <a href="https://www.yourkit.com/youmonitor/">YourKit YouMonitor</a>
|
||||
YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/), and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/).
|
||||
|
@ -1,14 +1,31 @@
|
||||
import org.ajoberstar.grgit.Grgit
|
||||
import java.time.format.DateTimeFormatter
|
||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
|
||||
import java.net.URI
|
||||
import java.time.format.DateTimeFormatter
|
||||
import xyz.jpenilla.runpaper.task.RunServer
|
||||
|
||||
plugins {
|
||||
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
|
||||
id("xyz.jpenilla.run-paper") version "2.1.0"
|
||||
}
|
||||
|
||||
if (!File("$rootDir/.git").exists()) {
|
||||
logger.lifecycle("""
|
||||
**************************************************************************************
|
||||
You need to fork and clone this repository! Don't download a .zip file.
|
||||
If you need assistance, consult the GitHub docs: https://docs.github.com/get-started/quickstart/fork-a-repo
|
||||
**************************************************************************************
|
||||
""".trimIndent()
|
||||
).also { kotlin.system.exitProcess(1) }
|
||||
}
|
||||
|
||||
logger.lifecycle("""
|
||||
*******************************************
|
||||
You are building FastAsyncWorldEdit!
|
||||
|
||||
If you encounter trouble:
|
||||
1) Read COMPILING.md if you haven't yet
|
||||
1) Read COMPILING.adoc if you haven't yet
|
||||
2) Try running 'build' in a separate Gradle run
|
||||
3) Use gradlew and not gradle
|
||||
4) If you still need help, ask on Discord! https://discord.gg/intellectualsites
|
||||
@ -17,7 +34,8 @@ logger.lifecycle("""
|
||||
*******************************************
|
||||
""")
|
||||
|
||||
var rootVersion by extra("1.16")
|
||||
var rootVersion by extra("2.7.1")
|
||||
var snapshot by extra("SNAPSHOT")
|
||||
var revision: String by extra("")
|
||||
var buildNumber by extra("")
|
||||
var date: String by extra("")
|
||||
@ -27,11 +45,10 @@ ext {
|
||||
}
|
||||
date = git.head().dateTime.format(DateTimeFormatter.ofPattern("yy.MM.dd"))
|
||||
revision = "-${git.head().abbreviatedId}"
|
||||
val commit: String? = git.head().abbreviatedId
|
||||
buildNumber = if (project.hasProperty("buildnumber")) {
|
||||
project.properties["buildnumber"] as String
|
||||
snapshot + "-" + project.properties["buildnumber"] as String
|
||||
} else {
|
||||
commit.toString()
|
||||
project.properties["snapshot"] as String
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,3 +83,32 @@ allprojects {
|
||||
}
|
||||
|
||||
applyCommonConfiguration()
|
||||
val supportedVersions = listOf("1.16.5", "1.17", "1.17.1", "1.18.2", "1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4", "1.20", "1.20.1")
|
||||
|
||||
tasks {
|
||||
supportedVersions.forEach {
|
||||
register<RunServer>("runServer-$it") {
|
||||
minecraftVersion(it)
|
||||
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
|
||||
.toTypedArray())
|
||||
jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true")
|
||||
group = "run paper"
|
||||
runDirectory.set(file("run-$it"))
|
||||
}
|
||||
}
|
||||
runServer {
|
||||
minecraftVersion("1.20.1")
|
||||
pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
|
||||
.toTypedArray())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
nexusPublishing {
|
||||
this.repositories {
|
||||
sonatype {
|
||||
nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/"))
|
||||
snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven {
|
||||
name = "EngineHub"
|
||||
@ -22,6 +22,13 @@ val properties = Properties().also { props ->
|
||||
|
||||
dependencies {
|
||||
implementation(gradleApi())
|
||||
implementation("org.ajoberstar.grgit:grgit-gradle:4.1.0")
|
||||
implementation("com.github.jengelman.gradle.plugins:shadow:6.1.0")
|
||||
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0")
|
||||
implementation("com.github.johnrengelman:shadow:8.1.1")
|
||||
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
|
23
buildSrc/src/main/kotlin/AdapterConfig.kt
Normale Datei
23
buildSrc/src/main/kotlin/AdapterConfig.kt
Normale Datei
@ -0,0 +1,23 @@
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
|
||||
// For specific version pinning, see
|
||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||
fun Project.applyPaperweightAdapterConfiguration() {
|
||||
applyCommonConfiguration()
|
||||
apply(plugin = "java-library")
|
||||
applyCommonJavaConfiguration(
|
||||
sourcesJar = true,
|
||||
banSlf4j = false,
|
||||
)
|
||||
apply(plugin = "io.papermc.paperweight.userdev")
|
||||
|
||||
dependencies {
|
||||
"implementation"(project(":worldedit-bukkit"))
|
||||
}
|
||||
|
||||
tasks.named("assemble") {
|
||||
dependsOn("reobfJar")
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.repositories
|
||||
import org.gradle.kotlin.dsl.the
|
||||
|
||||
@ -11,11 +10,7 @@ fun Project.applyCommonConfiguration() {
|
||||
version = rootProject.version
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
name = "IntellectualSites"
|
||||
url = uri("https://mvn.intellectualsites.com/content/groups/public/")
|
||||
}
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "EngineHub"
|
||||
url = uri("https://maven.enginehub.org/repo/")
|
||||
@ -24,10 +19,6 @@ fun Project.applyCommonConfiguration() {
|
||||
name = "OSS Sonatype Snapshots"
|
||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
maven {
|
||||
name = "OSS Sonatype Releases"
|
||||
url = uri("https://oss.sonatype.org/content/repositories/releases/")
|
||||
}
|
||||
maven {
|
||||
name = "Athion"
|
||||
url = uri("https://ci.athion.net/plugin/repository/tools/")
|
||||
@ -40,17 +31,9 @@ fun Project.applyCommonConfiguration() {
|
||||
}
|
||||
}
|
||||
|
||||
configurations.findByName("compileClasspath")?.apply {
|
||||
resolutionStrategy.componentSelection {
|
||||
withModule("org.slf4j:slf4j-api") {
|
||||
reject("No SLF4J allowed on compile classpath")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins.withId("java") {
|
||||
the<JavaPluginExtension>().toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(8))
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,15 +45,15 @@ fun Project.applyCommonConfiguration() {
|
||||
continue
|
||||
}
|
||||
add(conf.name, "com.google.guava:guava") {
|
||||
version { strictly(Versions.GUAVA) }
|
||||
version { require("31.1-jre") }
|
||||
because("Mojang provides Guava")
|
||||
}
|
||||
add(conf.name, "com.google.code.gson:gson") {
|
||||
version { strictly(Versions.GSON) }
|
||||
version { require("2.10") }
|
||||
because("Mojang provides Gson")
|
||||
}
|
||||
add(conf.name, "it.unimi.dsi:fastutil") {
|
||||
version { strictly(Versions.FAST_UTIL) }
|
||||
version { require("8.5.9") }
|
||||
because("Mojang provides FastUtil")
|
||||
}
|
||||
}
|
||||
|
90
buildSrc/src/main/kotlin/CommonJavaConfig.kt
Normale Datei
90
buildSrc/src/main/kotlin/CommonJavaConfig.kt
Normale Datei
@ -0,0 +1,90 @@
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.attributes.java.TargetJvmVersion
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.external.javadoc.StandardJavadocDocletOptions
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean = true) {
|
||||
applyCommonConfiguration()
|
||||
apply(plugin = "eclipse")
|
||||
apply(plugin = "idea")
|
||||
|
||||
tasks
|
||||
.withType<JavaCompile>()
|
||||
.matching { it.name == "compileJava" || it.name == "compileTestJava" }
|
||||
.configureEach {
|
||||
val disabledLint = listOf(
|
||||
"processing", "path", "fallthrough", "serial"
|
||||
)
|
||||
options.release.set(17)
|
||||
options.compilerArgs.addAll(listOf("-Xlint:all") + disabledLint.map { "-Xlint:-$it" })
|
||||
options.isDeprecation = true
|
||||
options.encoding = "UTF-8"
|
||||
options.compilerArgs.add("-parameters")
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"compileOnly"("com.google.code.findbugs:jsr305:3.0.2")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.10.0")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.10.0")
|
||||
"testImplementation"("org.mockito:mockito-core:5.4.0")
|
||||
"testImplementation"("org.mockito:mockito-junit-jupiter:5.4.0")
|
||||
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.10.0")
|
||||
}
|
||||
|
||||
// Java 8 turns on doclint which we fail
|
||||
tasks.withType<Javadoc>().configureEach {
|
||||
(options as StandardJavadocDocletOptions).apply {
|
||||
addStringOption("Xdoclint:none", "-quiet")
|
||||
tags(
|
||||
"apiNote:a:API Note:",
|
||||
"implSpec:a:Implementation Requirements:",
|
||||
"implNote:a:Implementation Note:"
|
||||
)
|
||||
options.encoding = "UTF-8"
|
||||
links(
|
||||
"https://jd.advntr.dev/api/latest/",
|
||||
"https://logging.apache.org/log4j/2.x/log4j-api/apidocs/",
|
||||
"https://www.antlr.org/api/Java/",
|
||||
"https://docs.enginehub.org/javadoc/org.enginehub.piston/core/0.5.7/",
|
||||
"https://docs.enginehub.org/javadoc/org.enginehub.piston/default-impl/0.5.7/",
|
||||
"https://jd.papermc.io/paper/1.20/",
|
||||
"https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/"
|
||||
)
|
||||
docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}"
|
||||
}
|
||||
}
|
||||
|
||||
configure<JavaPluginExtension> {
|
||||
disableAutoTargetJvm()
|
||||
withJavadocJar()
|
||||
if (sourcesJar) {
|
||||
withSourcesJar()
|
||||
}
|
||||
}
|
||||
|
||||
if (banSlf4j) {
|
||||
configurations["compileClasspath"].apply {
|
||||
resolutionStrategy.componentSelection {
|
||||
withModule("org.slf4j:slf4j-api") {
|
||||
reject("No SLF4J allowed on compile classpath")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.ExtraPropertiesExtension
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.tasks.SourceSetContainer
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.the
|
||||
@ -9,4 +9,4 @@ val Project.ext: ExtraPropertiesExtension
|
||||
get() = extensions.getByType()
|
||||
|
||||
val Project.sourceSets: SourceSetContainer
|
||||
get() = the<JavaPluginConvention>().sourceSets
|
||||
get() = the<JavaPluginExtension>().sourceSets
|
||||
|
@ -1,33 +1,48 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.api.artifacts.ModuleDependency
|
||||
import org.gradle.api.internal.HasConvention
|
||||
import org.gradle.api.plugins.MavenRepositoryHandlerConvention
|
||||
import org.gradle.api.tasks.Upload
|
||||
import org.gradle.api.attributes.Bundling
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.DocsType
|
||||
import org.gradle.api.attributes.LibraryElements
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.attributes.java.TargetJvmVersion
|
||||
import org.gradle.api.component.AdhocComponentWithVariants
|
||||
import org.gradle.api.component.SoftwareComponentFactory
|
||||
import org.gradle.api.publish.PublishingExtension
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.getPlugin
|
||||
import org.gradle.kotlin.dsl.invoke
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import org.gradle.plugins.signing.SigningExtension
|
||||
import javax.inject.Inject
|
||||
|
||||
fun Project.applyLibrariesConfiguration() {
|
||||
applyCommonConfiguration()
|
||||
apply(plugin = "java-base")
|
||||
apply(plugin = "maven-publish")
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
apply(plugin = "signing")
|
||||
|
||||
configurations {
|
||||
create("shade")
|
||||
getByName("archives").extendsFrom(getByName("default"))
|
||||
}
|
||||
|
||||
group = "${rootProject.group}.worldedit-libs"
|
||||
|
||||
val relocations = mapOf(
|
||||
"net.kyori.text" to "com.sk89q.worldedit.util.formatting.text",
|
||||
"net.kyori.minecraft" to "com.sk89q.worldedit.util.kyori"
|
||||
"net.kyori.text" to "com.sk89q.worldedit.util.formatting.text",
|
||||
"net.kyori.minecraft" to "com.sk89q.worldedit.util.kyori",
|
||||
"net.kyori.adventure.nbt" to "com.sk89q.worldedit.util.nbt"
|
||||
|
||||
)
|
||||
|
||||
tasks.register<ShadowJar>("jar") {
|
||||
@ -37,8 +52,10 @@ fun Project.applyLibrariesConfiguration() {
|
||||
dependencies {
|
||||
exclude(dependency("com.google.guava:guava"))
|
||||
exclude(dependency("com.google.code.gson:gson"))
|
||||
exclude(dependency("com.google.errorprone:error_prone_annotations"))
|
||||
exclude(dependency("org.checkerframework:checker-qual"))
|
||||
exclude(dependency("org.apache.logging.log4j:log4j-api"))
|
||||
exclude(dependency("com.google.code.findbugs:jsr305"))
|
||||
}
|
||||
|
||||
relocations.forEach { (from, to) ->
|
||||
@ -47,22 +64,22 @@ fun Project.applyLibrariesConfiguration() {
|
||||
}
|
||||
val altConfigFiles = { artifactType: String ->
|
||||
val deps = configurations["shade"].incoming.dependencies
|
||||
.filterIsInstance<ModuleDependency>()
|
||||
.map { it.copy() }
|
||||
.map { dependency ->
|
||||
dependency.artifact {
|
||||
name = dependency.name
|
||||
type = artifactType
|
||||
extension = "jar"
|
||||
classifier = artifactType
|
||||
}
|
||||
dependency
|
||||
.filterIsInstance<ModuleDependency>()
|
||||
.map { it.copy() }
|
||||
.map { dependency ->
|
||||
dependency.artifact {
|
||||
name = dependency.name
|
||||
type = artifactType
|
||||
extension = "jar"
|
||||
classifier = artifactType
|
||||
}
|
||||
dependency
|
||||
}
|
||||
|
||||
files(configurations.detachedConfiguration(*deps.toTypedArray())
|
||||
.resolvedConfiguration.lenientConfiguration.artifacts
|
||||
.filter { it.classifier == artifactType }
|
||||
.map { zipTree(it.file) })
|
||||
.resolvedConfiguration.lenientConfiguration.artifacts
|
||||
.filter { it.classifier == artifactType }
|
||||
.map { zipTree(it.file) })
|
||||
}
|
||||
tasks.register<Jar>("sourcesJar") {
|
||||
from({
|
||||
@ -81,31 +98,175 @@ fun Project.applyLibrariesConfiguration() {
|
||||
archiveClassifier.set("sources")
|
||||
}
|
||||
|
||||
// This a dummy jar to comply with the requirements of the OSSRH,
|
||||
// libs are not API and therefore no "proper" javadoc jar is necessary
|
||||
tasks.register<Jar>("javadocJar") {
|
||||
archiveClassifier.set("javadoc")
|
||||
}
|
||||
|
||||
tasks.named("assemble").configure {
|
||||
dependsOn("jar", "sourcesJar")
|
||||
dependsOn("jar", "sourcesJar", "javadocJar")
|
||||
}
|
||||
|
||||
artifacts {
|
||||
val jar = tasks.named("jar")
|
||||
add("default", jar) {
|
||||
builtBy(jar)
|
||||
project.apply<LibsConfigPluginHack>()
|
||||
|
||||
val libsComponent = project.components["libs"] as AdhocComponentWithVariants
|
||||
|
||||
val apiElements = project.configurations.register("apiElements") {
|
||||
isVisible = false
|
||||
description = "API elements for libs"
|
||||
isCanBeResolved = false
|
||||
isCanBeConsumed = true
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_API))
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR))
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||
}
|
||||
val sourcesJar = tasks.named("sourcesJar")
|
||||
add("archives", sourcesJar) {
|
||||
builtBy(sourcesJar)
|
||||
outgoing.artifact(tasks.named("jar"))
|
||||
}
|
||||
|
||||
val runtimeElements = project.configurations.register("runtimeElements") {
|
||||
isVisible = false
|
||||
description = "Runtime elements for libs"
|
||||
isCanBeResolved = false
|
||||
isCanBeConsumed = true
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME))
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR))
|
||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||
}
|
||||
outgoing.artifact(tasks.named("jar"))
|
||||
}
|
||||
|
||||
val sourcesElements = project.configurations.register("sourcesElements") {
|
||||
isVisible = false
|
||||
description = "Source elements for libs"
|
||||
isCanBeResolved = false
|
||||
isCanBeConsumed = true
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME))
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.DOCUMENTATION))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED))
|
||||
attribute(DocsType.DOCS_TYPE_ATTRIBUTE, project.objects.named(DocsType.SOURCES))
|
||||
}
|
||||
outgoing.artifact(tasks.named("sourcesJar"))
|
||||
}
|
||||
|
||||
val javadocElements = project.configurations.register("javadocElements") {
|
||||
isVisible = false
|
||||
description = "Javadoc elements for libs"
|
||||
isCanBeResolved = false
|
||||
isCanBeConsumed = true
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME))
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.DOCUMENTATION))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED))
|
||||
attribute(DocsType.DOCS_TYPE_ATTRIBUTE, project.objects.named(DocsType.JAVADOC))
|
||||
}
|
||||
outgoing.artifact(tasks.named("javadocJar"))
|
||||
}
|
||||
|
||||
libsComponent.addVariantsFromConfiguration(apiElements.get()) {
|
||||
mapToMavenScope("compile")
|
||||
}
|
||||
|
||||
libsComponent.addVariantsFromConfiguration(runtimeElements.get()) {
|
||||
mapToMavenScope("runtime")
|
||||
}
|
||||
|
||||
libsComponent.addVariantsFromConfiguration(sourcesElements.get()) {
|
||||
mapToMavenScope("runtime")
|
||||
}
|
||||
|
||||
libsComponent.addVariantsFromConfiguration(javadocElements.get()) {
|
||||
mapToMavenScope("runtime")
|
||||
}
|
||||
|
||||
val publishingExtension = the<PublishingExtension>()
|
||||
|
||||
configure<SigningExtension> {
|
||||
if (!version.toString().endsWith("-SNAPSHOT")) {
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
useInMemoryPgpKeys(signingKey, signingPassword)
|
||||
isRequired
|
||||
sign(publishingExtension.publications)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<Upload>("install") {
|
||||
configuration = configurations["archives"]
|
||||
(repositories as HasConvention).convention.getPlugin<MavenRepositoryHandlerConvention>().mavenInstaller {
|
||||
pom.version = project.version.toString()
|
||||
pom.artifactId = project.name
|
||||
configure<PublishingExtension> {
|
||||
publications {
|
||||
register<MavenPublication>("maven") {
|
||||
from(libsComponent)
|
||||
|
||||
group = "com.fastasyncworldedit"
|
||||
artifactId = "FastAsyncWorldEdit-Libs-${project.name.replaceFirstChar(Char::titlecase)}"
|
||||
version = version
|
||||
|
||||
pom {
|
||||
name.set("${rootProject.name}-Libs" + " " + project.version)
|
||||
description.set("Blazingly fast Minecraft world manipulation for artists, builders and everyone else.")
|
||||
url.set("https://github.com/IntellectualSites/FastAsyncWorldEdit")
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name.set("GNU General Public License, Version 3.0")
|
||||
url.set("https://www.gnu.org/licenses/gpl-3.0.html")
|
||||
distribution.set("repo")
|
||||
}
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id.set("NotMyFault")
|
||||
name.set("Alexander Brandes")
|
||||
email.set("contact(at)notmyfault.dev")
|
||||
organization.set("IntellectualSites")
|
||||
}
|
||||
developer {
|
||||
id.set("SirYwell")
|
||||
name.set("Hannes Greule")
|
||||
organization.set("IntellectualSites")
|
||||
}
|
||||
developer {
|
||||
id.set("dordsor21")
|
||||
name.set("dordsor21")
|
||||
organization.set("IntellectualSites")
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
url.set("https://github.com/IntellectualSites/FastAsyncWorldEdit")
|
||||
connection.set("scm:https://IntellectualSites@github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
developerConnection.set("scm:git://github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system.set("GitHub")
|
||||
url.set("https://github.com/IntellectualSites/FastAsyncWorldEdit/issues")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// A horrible hack because `softwareComponentFactory` has to be gotten via plugin
|
||||
// gradle why
|
||||
internal open class LibsConfigPluginHack @Inject constructor(
|
||||
private val softwareComponentFactory: SoftwareComponentFactory
|
||||
) : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
val libsComponents = softwareComponentFactory.adhoc("libs")
|
||||
project.components.add(libsComponents)
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.constrainDependenciesToLibsCore() {
|
||||
evaluationDependsOn(":worldedit-libs:core")
|
||||
val coreDeps = project(":worldedit-libs:core").configurations["shade"].dependencies
|
||||
|
@ -1,20 +1,18 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginConvention
|
||||
import org.gradle.api.component.AdhocComponentWithVariants
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.publish.PublishingExtension
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.external.javadoc.StandardJavadocDocletOptions
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.getByName
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import org.gradle.plugins.signing.SigningExtension
|
||||
|
||||
fun Project.applyPlatformAndCoreConfiguration() {
|
||||
applyCommonConfiguration()
|
||||
@ -22,92 +20,110 @@ fun Project.applyPlatformAndCoreConfiguration() {
|
||||
apply(plugin = "eclipse")
|
||||
apply(plugin = "idea")
|
||||
apply(plugin = "maven-publish")
|
||||
// apply(plugin = "checkstyle")
|
||||
apply(plugin = "com.github.johnrengelman.shadow")
|
||||
apply(plugin = "signing")
|
||||
|
||||
applyCommonJavaConfiguration(
|
||||
sourcesJar = name in setOf("worldedit-core", "worldedit-bukkit"),
|
||||
)
|
||||
|
||||
if (project.hasProperty("buildnumber")) {
|
||||
ext["internalVersion"] = "$version;${rootProject.ext["gitCommitHash"]}"
|
||||
} else {
|
||||
ext["internalVersion"] = "$version"
|
||||
}
|
||||
|
||||
tasks
|
||||
.withType<JavaCompile>()
|
||||
.matching { it.name == "compileJava" || it.name == "compileTestJava" }
|
||||
.configureEach {
|
||||
val disabledLint = listOf(
|
||||
"processing", "path", "fallthrough", "serial"
|
||||
)
|
||||
//options.compilerArgs.addAll(listOf("-Xlint:all") + disabledLint.map { "-Xlint:-$it" })
|
||||
options.isDeprecation = false
|
||||
options.encoding = "UTF-8"
|
||||
options.compilerArgs.add("-parameters")
|
||||
}
|
||||
|
||||
// configure<CheckstyleExtension> {
|
||||
// configFile = rootProject.file("config/checkstyle/checkstyle.xml")
|
||||
// toolVersion = "8.34"
|
||||
// }
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
configure<JavaPluginExtension> {
|
||||
disableAutoTargetJvm()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
"compileOnly"("org.jetbrains:annotations:20.1.0")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT}")
|
||||
"testImplementation"("org.junit.jupiter:junit-jupiter-params:${Versions.JUNIT}")
|
||||
"testImplementation"("org.mockito:mockito-core:${Versions.MOCKITO}")
|
||||
"testImplementation"("org.mockito:mockito-junit-jupiter:${Versions.MOCKITO}")
|
||||
"testImplementation"("net.bytebuddy:byte-buddy:1.11.0")
|
||||
"testRuntime"("org.junit.jupiter:junit-jupiter-engine:${Versions.JUNIT}")
|
||||
if (name in setOf("worldedit-core", "worldedit-bukkit", "worldedit-cli")) {
|
||||
the<JavaPluginExtension>().withSourcesJar()
|
||||
}
|
||||
|
||||
// Java 8 turns on doclint which we fail
|
||||
tasks.withType<Javadoc>().configureEach {
|
||||
(options as StandardJavadocDocletOptions).apply {
|
||||
addStringOption("Xdoclint:none", "-quiet")
|
||||
tags(
|
||||
"apiNote:a:API Note:",
|
||||
"implSpec:a:Implementation Requirements:",
|
||||
"implNote:a:Implementation Note:"
|
||||
)
|
||||
val javaComponent = components["java"] as AdhocComponentWithVariants
|
||||
javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) {
|
||||
skip()
|
||||
}
|
||||
|
||||
val publishingExtension = the<PublishingExtension>()
|
||||
|
||||
configure<SigningExtension> {
|
||||
if (!version.toString().endsWith("-SNAPSHOT")) {
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
useInMemoryPgpKeys(signingKey, signingPassword)
|
||||
isRequired
|
||||
sign(publishingExtension.publications)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<Jar>("javadocJar") {
|
||||
dependsOn("javadoc")
|
||||
archiveClassifier.set("javadoc")
|
||||
from(tasks.getByName<Javadoc>("javadoc").destinationDir)
|
||||
}
|
||||
configure<PublishingExtension> {
|
||||
publications {
|
||||
register<MavenPublication>("maven") {
|
||||
from(javaComponent)
|
||||
|
||||
tasks.named("assemble").configure {
|
||||
dependsOn("javadocJar")
|
||||
}
|
||||
group = "com.fastasyncworldedit"
|
||||
artifactId = "${rootProject.name}-${project.description}"
|
||||
version = version
|
||||
|
||||
artifacts {
|
||||
add("archives", tasks.named("jar"))
|
||||
add("archives", tasks.named("javadocJar"))
|
||||
}
|
||||
pom {
|
||||
name.set("${rootProject.name}-${project.description}" + " " + project.version)
|
||||
description.set("Blazingly fast Minecraft world manipulation for artists, builders and everyone else.")
|
||||
url.set("https://github.com/IntellectualSites/FastAsyncWorldEdit")
|
||||
|
||||
if (name == "worldedit-core" || name == "worldedit-bukkit") {
|
||||
tasks.register<Jar>("sourcesJar") {
|
||||
dependsOn("classes")
|
||||
archiveClassifier.set("sources")
|
||||
from(sourceSets["main"].allSource)
|
||||
}
|
||||
licenses {
|
||||
license {
|
||||
name.set("GNU General Public License, Version 3.0")
|
||||
url.set("https://www.gnu.org/licenses/gpl-3.0.html")
|
||||
distribution.set("repo")
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
add("archives", tasks.named("sourcesJar"))
|
||||
}
|
||||
tasks.named("assemble").configure {
|
||||
dependsOn("sourcesJar")
|
||||
developers {
|
||||
developer {
|
||||
id.set("NotMyFault")
|
||||
name.set("Alexander Brandes")
|
||||
email.set("contact(at)notmyfault.dev")
|
||||
organization.set("IntellectualSites")
|
||||
}
|
||||
developer {
|
||||
id.set("SirYwell")
|
||||
name.set("Hannes Greule")
|
||||
organization.set("IntellectualSites")
|
||||
}
|
||||
developer {
|
||||
id.set("dordsor21")
|
||||
name.set("dordsor21")
|
||||
organization.set("IntellectualSites")
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
url.set("https://github.com/IntellectualSites/FastAsyncWorldEdit")
|
||||
connection.set("scm:https://IntellectualSites@github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
developerConnection.set("scm:git://github.com/IntellectualSites/FastAsyncWorldEdit.git")
|
||||
}
|
||||
|
||||
issueManagement{
|
||||
system.set("GitHub")
|
||||
url.set("https://github.com/IntellectualSites/FastAsyncWorldEdit/issues")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tasks.named("check").configure {
|
||||
// dependsOn("checkstyleMain", "checkstyleTest")
|
||||
// }
|
||||
if (name != "worldedit-fabric") {
|
||||
configurations["compileClasspath"].apply {
|
||||
resolutionStrategy.componentSelection {
|
||||
withModule("org.slf4j:slf4j-api") {
|
||||
reject("No SLF4J allowed on compile classpath")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -133,27 +149,28 @@ val CLASSPATH = listOf("truezip", "truevfs", "js")
|
||||
.joinToString(separator = " ")
|
||||
|
||||
sealed class WorldEditKind(
|
||||
val name: String,
|
||||
val mainClass: String = "com.sk89q.worldedit.internal.util.InfoEntryPoint"
|
||||
val name: String,
|
||||
val mainClass: String = "com.sk89q.worldedit.internal.util.InfoEntryPoint"
|
||||
) {
|
||||
class Standalone(mainClass: String) : WorldEditKind("STANDALONE", mainClass)
|
||||
object Mod : WorldEditKind("MOD")
|
||||
object Plugin : WorldEditKind("PLUGIN")
|
||||
}
|
||||
|
||||
fun Project.addJarManifest(kind: WorldEditKind, includeClasspath: Boolean = false) {
|
||||
fun Project.addJarManifest(kind: WorldEditKind, includeClasspath: Boolean = false, extraAttributes: Map<String, String> = mapOf()) {
|
||||
tasks.named<Jar>("jar") {
|
||||
val version = project(":worldedit-core").version
|
||||
inputs.property("version", version)
|
||||
val attributes = mutableMapOf(
|
||||
"Implementation-Version" to version,
|
||||
"WorldEdit-Version" to version,
|
||||
"WorldEdit-Kind" to kind.name,
|
||||
"Main-Class" to kind.mainClass
|
||||
"Implementation-Version" to version,
|
||||
"WorldEdit-Version" to version,
|
||||
"WorldEdit-Kind" to kind.name,
|
||||
"Main-Class" to kind.mainClass
|
||||
)
|
||||
if (includeClasspath) {
|
||||
attributes["Class-Path"] = CLASSPATH
|
||||
}
|
||||
attributes.putAll(extraAttributes)
|
||||
manifest.attributes(attributes)
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
import org.gradle.api.Project
|
||||
|
||||
object Versions {
|
||||
const val TEXT = "3.0.4"
|
||||
const val TEXT_EXTRAS = "3.0.6"
|
||||
const val PISTON = "0.5.7"
|
||||
const val AUTO_VALUE = "1.7.4"
|
||||
const val JUNIT = "5.7.0"
|
||||
const val MOCKITO = "3.9.0"
|
||||
const val FAST_UTIL = "8.2.1"
|
||||
const val GUAVA = "21.0"
|
||||
const val GSON = "2.8.0"
|
||||
}
|
||||
|
||||
// Properties that need a project reference to resolve:
|
||||
class ProjectVersions(project: Project) {
|
||||
val loom = project.rootProject.property("loom.version")
|
||||
val mixin = project.rootProject.property("mixin.version")
|
||||
}
|
||||
|
||||
val Project.versions
|
||||
get() = ProjectVersions(this)
|
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE suppressions PUBLIC "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
|
||||
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
|
||||
|
||||
<suppressions>
|
||||
<suppress files=".*DataFixer\.java" checks="MethodName|FallThrough"/>
|
||||
<suppress files=".*[\\/]MathUtils.java" checks="MethodName"/>
|
||||
<suppress files=".*[\\/]bPermissionsResolver.java" checks="TypeName"/>
|
||||
<!-- This thing only works via the use of a finalizer. -->
|
||||
<suppress files=".*[\\/]TracedEditSession.java" checks="NoFinalizer"/>
|
||||
<!-- None of the old command stuff really matters -->
|
||||
<suppress files=".*[\\/]minecraft[\\/]util[\\/]commands[\\/].*\.java" checks=".*"/>
|
||||
<!-- FAWE ADDITIONS -->
|
||||
<suppress files=".*[\\/]MathMan.java" checks="MethodName"/>
|
||||
<suppress files=".*[\\/]net[\\/]jpountz[\\/]lz4[\\/].*\.java" checks=".*"/>
|
||||
<suppress files=".*[\\/]BufferedRandomAccessFile.java" checks=".*"/>
|
||||
<suppress files=".*[\\/]ByteBufferUtils.java" checks="NoWhitespaceBefore"/>
|
||||
<suppress files=".*[\\/]Settings.java" checks="MemberName"/>
|
||||
<suppress files=".*[\\/]FaweLimit.java" checks="MemberName"/>
|
||||
<suppress files=".*[\\/]SimplexNoise.java" checks="MemberName"/>
|
||||
<suppress files=".*[\\/]SparseBitSet.java" checks=".*"/>
|
||||
</suppressions>
|
@ -1,214 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
Checks based on Google Checks, modified for EngineHub.
|
||||
-->
|
||||
|
||||
<module name="Checker">
|
||||
<property name="charset" value="UTF-8"/>
|
||||
|
||||
<property name="severity" value="error"/>
|
||||
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||
<module name="FileTabCharacter">
|
||||
<property name="eachLine" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="OuterTypeFilename"/>
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
<property name="format"
|
||||
value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||
<property name="message" value="Avoid using corresponding octal or Unicode escape."/>
|
||||
</module>
|
||||
<module name="AvoidEscapedUnicodeCharacters">
|
||||
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||
<property name="allowByTailComment" value="true"/>
|
||||
<property name="allowNonPrintableEscapes" value="true"/>
|
||||
</module>
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
<module name="NoLineWrap"/>
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="TEXT"/>
|
||||
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="NeedBraces"/>
|
||||
<module name="RightCurly"/>
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT, INSTANCE_INIT"/>
|
||||
</module>
|
||||
<module name="WhitespaceAround">
|
||||
<property name="allowEmptyConstructors" value="true"/>
|
||||
<property name="allowEmptyMethods" value="true"/>
|
||||
<property name="allowEmptyTypes" value="true"/>
|
||||
<property name="allowEmptyLoops" value="true"/>
|
||||
<message key="ws.notFollowed"
|
||||
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||
<message key="ws.notPreceded"
|
||||
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="WhitespaceAfter">
|
||||
<message key="ws.notFollowed"
|
||||
value="WhitespaceAround: ''{0}'' is not followed by whitespace. (4.1.3)"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore">
|
||||
<property name="tokens" value="COMMA, POST_INC, POST_DEC, ELLIPSIS, LABELED_STAT"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore">
|
||||
<property name="tokens" value="SEMI"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<module name="FallThrough">
|
||||
<property name="reliefPattern" value="FALL-THROUGH|falls?[ -]?thr(u|ough)"/>
|
||||
</module>
|
||||
<module name="UpperEll"/>
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="tokens" value="DOT"/>
|
||||
<property name="option" value="nl"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<property name="tokens" value="COMMA"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName">
|
||||
<message key="name.invalidPattern"
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MemberName">
|
||||
<property name="format" value="^([a-z][_a-zA-Z0-9]+|[xyz])$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ParameterName">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="format" value="^([a-z][a-z0-9][_a-zA-Z0-9]*|[a-z])$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|(^[A-Z][a-zA-Z0-9]*$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|(^[A-Z][a-zA-Z0-9]*$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="NoFinalizer"/>
|
||||
<module name="GenericWhitespace">
|
||||
<message key="ws.followed"
|
||||
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||
<message key="ws.preceded"
|
||||
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||
<message key="ws.illegalFollow"
|
||||
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||
<message key="ws.notPreceded"
|
||||
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="Regexp">
|
||||
<property name="format" value="[ \t]+$"/>
|
||||
<property name="illegalPattern" value="true"/>
|
||||
<property name="message" value="Trailing whitespace"/>
|
||||
</module>
|
||||
<module name="Indentation">
|
||||
<property name="basicOffset" value="4"/>
|
||||
<property name="braceAdjustment" value="0"/>
|
||||
<property name="caseIndent" value="4"/>
|
||||
<property name="throwsIndent" value="4"/>
|
||||
<property name="lineWrappingIndentation" value="4"/>
|
||||
<property name="arrayInitIndent" value="4"/>
|
||||
</module>
|
||||
<module name="CustomImportOrder">
|
||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||
<property name="customImportOrderRules" value="THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE###STATIC"/>
|
||||
</module>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="OperatorWrap">
|
||||
<property name="option" value="NL"/>
|
||||
<property name="tokens"
|
||||
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR "/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</module>
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="JavadocTagContinuationIndentation"/>
|
||||
<module name="SummaryJavadocCheck">
|
||||
<property name="forbiddenSummaryFragments"
|
||||
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||
</module>
|
||||
<module name="JavadocParagraph"/>
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
</module>
|
||||
<!-- No javadoc for now -->
|
||||
<!--<module name="JavadocMethod">
|
||||
<property name="scope" value="public"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingThrowsTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="minLineCount" value="2"/>
|
||||
<property name="allowedAnnotations" value="Override, Test"/>
|
||||
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||
</module>-->
|
||||
<module name="MethodName">
|
||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="SingleLineJavadoc">
|
||||
<property name="ignoreInlineTags" value="false"/>
|
||||
</module>
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="exceptionVariableName" value="expected|ignored"/>
|
||||
</module>
|
||||
<module name="CommentsIndentation"/>
|
||||
<!-- Validate String.to(Lower|Upper)Case() calls include Locale argument -->
|
||||
<module name="Regexp">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="message" value="Case-conversion calls must include an explicit Locale"/>
|
||||
<property name="format" value="(?!Character)\.to(Lower|Upper)Case\(\)"/>
|
||||
<property name="illegalPattern" value="true"/>
|
||||
</module>
|
||||
</module>
|
||||
<!-- Validate that command annotations are formatted correctly -->
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="^( +)@(Arg|Switch|Command)\(.*?\r?\n\1 {5,}"/>
|
||||
</module>
|
||||
<module name="RegexpMultiline">
|
||||
<property name="format" value="(?s:(\r\n|\r).*)"/>
|
||||
<property name="message" value="CRLF and CR line endings are prohibited, but this file uses them."/>
|
||||
</module>
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/checkstyle-suppression.xml"/>
|
||||
</module>
|
||||
</module>
|
@ -1,5 +1,5 @@
|
||||
Write a cool script? You can submit a pull request to [our GitHub Repository](https://github.com/IntellectualSites/FastAsyncWorldEdit).
|
||||
We will consider your script for inclusion in the FastAsyncWorldEdit repository. CraftScripts in the FastAsyncWorldEdit repository are
|
||||
licensed under GPLv3, like the rest of FastAsybcWorldEdit.
|
||||
licensed under GPLv3, like the rest of FastAsyncWorldEdit.
|
||||
|
||||
You can also post your scripts on [our Discord](https://discord.gg/intellectualsites) in the `#sharing-is-caring` channel.
|
||||
|
BIN
fawe-logo.png
BIN
fawe-logo.png
Binäre Datei nicht angezeigt.
Vorher Breite: | Höhe: | Größe: 17 KiB |
@ -1,10 +1,9 @@
|
||||
group=com.sk89q.worldedit
|
||||
group=com.fastasyncworldedit
|
||||
|
||||
org.gradle.jvmargs=-Xmx1512M
|
||||
org.gradle.daemon=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
org.gradle.vfs.watch=true
|
||||
|
||||
loom.version=0.5.43
|
||||
|
125
gradle/libs.versions.toml
Normale Datei
125
gradle/libs.versions.toml
Normale Datei
@ -0,0 +1,125 @@
|
||||
[versions]
|
||||
# Minecraft expectations
|
||||
paper = "1.20.1-R0.1-SNAPSHOT"
|
||||
fastutil = "8.5.9"
|
||||
guava = "31.1-jre"
|
||||
log4j = "2.19.0"
|
||||
gson = "2.10"
|
||||
snakeyaml = "2.0"
|
||||
|
||||
# Plugins
|
||||
dummypermscompat = "1.10"
|
||||
worldguard-bukkit = "7.0.9"
|
||||
mapmanager = "1.8.0-SNAPSHOT"
|
||||
griefprevention = "16.18.1"
|
||||
griefdefender = "2.1.0-SNAPSHOT"
|
||||
residence = "4.5._13.1"
|
||||
towny = "0.99.5.10"
|
||||
plotsquared = "7.0.0-rc.4"
|
||||
|
||||
# Third party
|
||||
bstats = "3.0.2"
|
||||
sparsebitset = "1.2"
|
||||
parallelgzip = "1.0.5"
|
||||
adventure = "4.14.0"
|
||||
adventure-bukkit = "4.3.0"
|
||||
checkerqual = "3.37.0"
|
||||
truezip = "6.8.4"
|
||||
auto-value = "1.10.2"
|
||||
findbugs = "3.0.2"
|
||||
rhino-runtime = "1.7.14"
|
||||
zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs
|
||||
antlr4 = "4.13.0"
|
||||
json-simple = "1.1.1"
|
||||
jlibnoise = "1.0.0"
|
||||
jchronic = "0.2.4a"
|
||||
lz4-java = "1.8.0"
|
||||
lz4-stream = "1.0.0"
|
||||
commons-cli = "1.5.0"
|
||||
paperlib = "1.0.8"
|
||||
paster = "1.1.5"
|
||||
vault = "1.7.1"
|
||||
serverlib = "2.3.1"
|
||||
## Internal
|
||||
text-adapter = "3.0.6"
|
||||
text = "3.0.4"
|
||||
piston = "0.5.7"
|
||||
|
||||
# Tests
|
||||
mockito = "5.4.0"
|
||||
|
||||
# Gradle plugins
|
||||
pluginyml = "0.6.0"
|
||||
minotaur = "2.8.3"
|
||||
|
||||
[libraries]
|
||||
# Minecraft expectations
|
||||
paper = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" }
|
||||
fastutil = { group = "it.unimi.dsi", name = "fastutil", version.ref = "fastutil" }
|
||||
log4jBom = { group = "org.apache.logging.log4j", name = "log4j-bom", version.ref = "log4j" }
|
||||
log4jApi = { group = "org.apache.logging.log4j", name = "log4j-api", version.ref = "log4j" }
|
||||
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
|
||||
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
|
||||
snakeyaml = { group = "org.yaml", name = "snakeyaml", version.ref = "snakeyaml" }
|
||||
|
||||
# Plugins
|
||||
dummypermscompat = { group = "com.sk89q", name = "dummypermscompat", version.ref = "dummypermscompat" }
|
||||
worldguard = { group = "com.sk89q.worldguard", name = "worldguard-bukkit", version.ref = "worldguard-bukkit" }
|
||||
mapmanager = { group = "com.github.InventivetalentDev", name = "MapManager", version.ref = "mapmanager" }
|
||||
griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention", version.ref = "griefprevention" }
|
||||
griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" }
|
||||
residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" }
|
||||
towny = { group = "com.palmergames.bukkit.towny", name = "towny", version.ref = "towny" }
|
||||
plotSquaredCore = { group = "com.intellectualsites.plotsquared", name = "plotsquared-core", version.ref = "plotsquared" }
|
||||
plotSquaredBukkit = { group = "com.intellectualsites.plotsquared", name = "plotsquared-bukkit", version.ref = "plotsquared" }
|
||||
|
||||
# Third Party
|
||||
bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" }
|
||||
bstatsBukkit = { group = "org.bstats", name = "bstats-bukkit", version.ref = "bstats" }
|
||||
sparsebitset = { group = "com.zaxxer", name = "SparseBitSet", version.ref = "sparsebitset" }
|
||||
parallelgzip = { group = "org.anarres", name = "parallelgzip", version.ref = "parallelgzip" }
|
||||
adventureNbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" }
|
||||
truezip = { group = "de.schlichtherle", name = "truezip", version.ref = "truezip" }
|
||||
autoValueAnnotations = { group = "com.google.auto.value", name = "auto-value-annotations", version.ref = "auto-value" }
|
||||
autoValue = { group = "com.google.auto.value", name = "auto-value", version.ref = "auto-value" }
|
||||
findbugs = { group = "com.google.code.findbugs", name = "jsr305", version.ref = "findbugs" }
|
||||
rhino = { group = "org.mozilla", name = "rhino-runtime", version.ref = "rhino-runtime" }
|
||||
zstd = { group = "com.github.luben", name = "zstd-jni", version.ref = "zstd-jni" }
|
||||
antlr4 = { group = "org.antlr", name = "antlr4", version.ref = "antlr4" }
|
||||
antlr4Runtime = { group = "org.antlr", name = "antlr4-runtime", version.ref = "antlr4" }
|
||||
jsonSimple = { group = "com.googlecode.json-simple", name = "json-simple", version.ref = "json-simple" }
|
||||
jlibnoise = { group = "com.sk89q.lib", name = "jlibnoise", version.ref = "jlibnoise" }
|
||||
jchronic = { group = "com.sk89q", name = "jchronic", version.ref = "jchronic" }
|
||||
lz4Java = { group = "org.lz4", name = "lz4-java", version.ref = "lz4-java" }
|
||||
lz4JavaStream = { group = "net.jpountz", name = "lz4-java-stream", version.ref = "lz4-stream" }
|
||||
commonsCli = { group = "commons-cli", name = "commons-cli", version.ref = "commons-cli" }
|
||||
paperlib = { group = "io.papermc", name = "paperlib", version.ref = "paperlib" }
|
||||
adventureApi = { group = "net.kyori", name = "adventure-api", version.ref = "adventure" }
|
||||
adventureMiniMessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" }
|
||||
adventureBukkit = { group = "net.kyori", name = "adventure-platform-bukkit", version.ref = "adventure-bukkit" }
|
||||
paster = { group = "com.intellectualsites.paster", name = "Paster", version.ref = "paster" }
|
||||
vault = { group = "com.github.MilkBowl", name = "VaultAPI", version.ref = "vault" }
|
||||
serverlib = { group = "dev.notmyfault.serverlib", name = "ServerLib", version.ref = "serverlib" }
|
||||
checkerqual = { group = "org.checkerframework", name = "checker-qual", version.ref = "checkerqual" }
|
||||
|
||||
# Internal
|
||||
## Text
|
||||
adventureTextAdapterBukkit = { group = "net.kyori", name = "text-adapter-bukkit", version.ref = "text-adapter" }
|
||||
adventureTextApi = { group = "net.kyori", name = "text-api", version.ref = "text" }
|
||||
adventureTextSerializerGson = { group = "net.kyori", name = "text-serializer-gson", version.ref = "text" }
|
||||
adventureTextSerializerLegacy = { group = "net.kyori", name = "text-serializer-legacy", version.ref = "text" }
|
||||
adventureTextSerializerPlain = { group = "net.kyori", name = "text-serializer-plain", version.ref = "text" }
|
||||
## Piston
|
||||
piston = { group = "org.enginehub.piston", name = "core", version.ref = "piston" }
|
||||
pistonImpl = { group = "org.enginehub.piston", name = "default-impl", version.ref = "piston" }
|
||||
pistonAnnotations = { group = "org.enginehub.piston.core-ap", name = "annotations", version.ref = "piston" }
|
||||
pistonProcessor = { group = "org.enginehub.piston.core-ap", name = "processor", version.ref = "piston" }
|
||||
pistonRuntime = { group = "org.enginehub.piston.core-ap", name = "runtime", version.ref = "piston" }
|
||||
|
||||
# Tests
|
||||
mockito = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" }
|
||||
log4jCore = { group = "org.apache.logging.log4j", name = "log4j-core", version.ref = "log4j" }
|
||||
|
||||
[plugins]
|
||||
pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }
|
||||
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binäre Datei nicht angezeigt.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
293
gradlew
vendored
293
gradlew
vendored
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -17,67 +17,98 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@ -98,88 +129,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
15
gradlew.bat
vendored
15
gradlew.bat
vendored
@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"ignoreDeps": ["guava", "rhino-runtime", "mockito-core", "antlr4", "antlr4-runtime", "paranamer", "fastutil", "auto-value-annotations", "auto-value"]
|
||||
}
|
@ -2,11 +2,14 @@ rootProject.name = "FastAsyncWorldEdit"
|
||||
|
||||
include("worldedit-libs")
|
||||
|
||||
listOf("legacy", "1_17_1", "1_18_2", "1_19", "1_19_3","1_19_4", "1_20").forEach {
|
||||
include("worldedit-bukkit:adapters:adapter-$it")
|
||||
}
|
||||
|
||||
listOf("bukkit", "core", "cli").forEach {
|
||||
include("worldedit-libs:$it")
|
||||
include("worldedit-$it")
|
||||
}
|
||||
// include("worldedit-mod")
|
||||
include("worldedit-libs:core:ap")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
@ -18,3 +21,5 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
9
steamwarci.yml
Normale Datei
9
steamwarci.yml
Normale Datei
@ -0,0 +1,9 @@
|
||||
build:
|
||||
- "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew build"
|
||||
- "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./gradlew --stop"
|
||||
|
||||
artifacts:
|
||||
"/binarys/FastAsyncWorldEdit-1.18.jar": "worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-2.7.1-SNAPSHOT.jar"
|
||||
|
||||
release:
|
||||
- "mvn deploy:deploy-file -DgroupId=de.steamwar -DartifactId=fastasyncworldedit -Dversion=1.18 -Dpackaging=jar -Dfile=/binarys/FastAsyncWorldEdit-1.18.jar -Durl=file:///var/www/html/maven/"
|
26
worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts
Normale Datei
26
worldedit-bukkit/adapters/adapter-1_17_1/build.gradle.kts
Normale Datei
@ -0,0 +1,26 @@
|
||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||
|
||||
applyPaperweightAdapterConfiguration()
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.stats.Stat;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
|
||||
class PaperweightFakePlayer extends ServerPlayer {
|
||||
|
||||
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(
|
||||
UUID.nameUUIDFromBytes("worldedit".getBytes()),
|
||||
"[WorldEdit]"
|
||||
);
|
||||
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
|
||||
|
||||
PaperweightFakePlayer(ServerLevel world) {
|
||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 position() {
|
||||
return ORIGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(DamageSource damagesource) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt openMenu(MenuProvider factory) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayClientMessage(Component message, boolean actionBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message, ChatType type, UUID sender) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awardStat(Stat<?> stat, int amount) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awardStat(Stat<?> stat) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvulnerableTo(DamageSource damageSource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openTextEdit(SignBlockEntity sign) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PaperweightWorldNativeAccess implements
|
||||
WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
|
||||
private final PaperweightAdapter adapter;
|
||||
private final WeakReference<ServerLevel> world;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> world) {
|
||||
this.adapter = adapter;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
private ServerLevel getWorld() {
|
||||
return Objects.requireNonNull(world.get(), "The reference to the world was lost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
||||
this.sideEffectSet = sideEffectSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelChunk getChunk(int x, int z) {
|
||||
return getWorld().getChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) {
|
||||
int stateId = BlockStateIdAccess.getBlockStateId(state);
|
||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
||||
? Block.stateById(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) {
|
||||
return chunk.getBlockState(position);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState setBlockState(
|
||||
LevelChunk chunk,
|
||||
BlockPos position,
|
||||
net.minecraft.world.level.block.state.BlockState state
|
||||
) {
|
||||
return chunk.setType(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
|
||||
net.minecraft.world.level.block.state.BlockState block,
|
||||
BlockPos position
|
||||
) {
|
||||
return Block.updateFromNeighbourShapes(block, getWorld(), position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getPosition(int x, int y, int z) {
|
||||
return new BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLightingForBlock(BlockPos position) {
|
||||
getWorld().getChunkSource().getLightEngine().checkBlock(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyBlockUpdate(
|
||||
LevelChunk chunk,
|
||||
BlockPos position,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
||||
getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(LevelChunk chunk) {
|
||||
return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockChanged(LevelChunk chunk, BlockPos position) {
|
||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
||||
getWorld().getChunkSource().blockChanged(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNeighbors(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
ServerLevel world = getWorld();
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
world.updateNeighborsAt(pos, oldState.getBlock());
|
||||
} else {
|
||||
// When we don't want events, manually run the physics without them.
|
||||
Block block = oldState.getBlock();
|
||||
fireNeighborChanged(pos, world, block, pos.west());
|
||||
fireNeighborChanged(pos, world, block, pos.east());
|
||||
fireNeighborChanged(pos, world, block, pos.below());
|
||||
fireNeighborChanged(pos, world, block, pos.above());
|
||||
fireNeighborChanged(pos, world, block, pos.north());
|
||||
fireNeighborChanged(pos, world, block, pos.south());
|
||||
}
|
||||
if (newState.hasAnalogOutputSignal()) {
|
||||
world.updateNeighbourForOutputSignal(pos, newState.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
||||
world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNeighbors(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
int recursionLimit
|
||||
) {
|
||||
ServerLevel world = getWorld();
|
||||
// a == updateNeighbors
|
||||
// b == updateDiagonalNeighbors
|
||||
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
CraftWorld craftWorld = world.getWorld();
|
||||
BlockPhysicsEvent event = new BlockPhysicsEvent(
|
||||
craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()),
|
||||
CraftBlockData.fromData(newState)
|
||||
);
|
||||
world.getCraftServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
getWorld().onBlockStateChange(pos, oldState, newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.util.ReflectionUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
||||
|
||||
public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
|
||||
private final Block block;
|
||||
private final BlockState blockState;
|
||||
private final Material material;
|
||||
private final boolean isTranslucent;
|
||||
private final CraftBlockData craftBlockData;
|
||||
private final org.bukkit.Material craftMaterial;
|
||||
private final int opacity;
|
||||
private final CompoundTag tile;
|
||||
|
||||
public PaperweightBlockMaterial(Block block) {
|
||||
this(block, block.defaultBlockState());
|
||||
}
|
||||
|
||||
public PaperweightBlockMaterial(Block block, BlockState blockState) {
|
||||
this.block = block;
|
||||
this.blockState = blockState;
|
||||
this.material = blockState.getMaterial();
|
||||
this.craftBlockData = CraftBlockData.fromData(blockState);
|
||||
this.craftMaterial = craftBlockData.getMaterial();
|
||||
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block, Refraction.pickName(
|
||||
"properties", "aP"));
|
||||
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
|
||||
Refraction.pickName("canOcclude", "n")
|
||||
);
|
||||
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
|
||||
BlockPos.ZERO,
|
||||
blockState
|
||||
);
|
||||
tile = tileEntity == null
|
||||
? null
|
||||
: new PaperweightLazyCompoundTag(Suppliers.memoize(() -> tileEntity.save(new net.minecraft.nbt.CompoundTag())));
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public BlockState getState() {
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public CraftBlockData getCraftBlockData() {
|
||||
return craftBlockData;
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAir() {
|
||||
return blockState.isAir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube() {
|
||||
return craftMaterial.isOccluding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return material.isSolidBlocking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSource() {
|
||||
return blockState.isSignalSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLiquid() {
|
||||
return material.isLiquid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid() {
|
||||
return material.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHardness() {
|
||||
return craftBlockData.getState().destroySpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getResistance() {
|
||||
return block.getExplosionResistance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSlipperiness() {
|
||||
return block.getFriction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightValue() {
|
||||
return blockState.getLightEmission();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightOpacity() {
|
||||
return opacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFragileWhenPushed() {
|
||||
return material.getPushReaction() == PushReaction.DESTROY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnpushable() {
|
||||
return material.getPushReaction() == PushReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTicksRandomly() {
|
||||
return block.isRandomlyTicking(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMovementBlocker() {
|
||||
return material.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBurnable() {
|
||||
return material.isFlammable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToolRequired() {
|
||||
// Removed in 1.16.1, this is not present in higher versions
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplacedDuringPlacement() {
|
||||
return material.isReplaceable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTranslucent() {
|
||||
return isTranslucent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContainer() {
|
||||
return block instanceof EntityBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTile() {
|
||||
return block instanceof EntityBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getDefaultTile() {
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMapColor() {
|
||||
// rgb field
|
||||
return material.getColor().col;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,709 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.PaperweightAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.regen.PaperweightRegen;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.registry.state.BooleanProperty;
|
||||
import com.sk89q.worldedit.registry.state.DirectionalProperty;
|
||||
import com.sk89q.worldedit.registry.state.EnumProperty;
|
||||
import com.sk89q.worldedit.registry.state.IntegerProperty;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.TreeGenerator;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.util.nbt.StringBinaryTag;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.WritableRegistry;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.TreeType;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private final PaperweightAdapter parent;
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that may break between versions of Minecraft
|
||||
// ------------------------------------------------------------------------
|
||||
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
|
||||
private char[] ibdToStateOrdinal = null;
|
||||
private int[] ordinalToIbdID = null;
|
||||
private boolean initialised = false;
|
||||
private Map<String, List<Property<?>>> allBlockProperties = null;
|
||||
|
||||
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
|
||||
this.parent = new PaperweightAdapter();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getEntityId(Entity entity) {
|
||||
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
|
||||
return resourceLocation == null ? null : resourceLocation.toString();
|
||||
}
|
||||
|
||||
private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
entity.save(compoundTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitImplAdapter<net.minecraft.nbt.Tag> getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized boolean init() {
|
||||
if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) {
|
||||
return false;
|
||||
}
|
||||
ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size
|
||||
ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size
|
||||
for (int i = 0; i < ibdToStateOrdinal.length; i++) {
|
||||
BlockState blockState = BlockTypesCache.states[i];
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial();
|
||||
int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState());
|
||||
char ordinal = blockState.getOrdinalChar();
|
||||
ibdToStateOrdinal[id] = ordinal;
|
||||
ordinalToIbdID[ordinal] = id;
|
||||
}
|
||||
Map<String, List<Property<?>>> properties = new HashMap<>();
|
||||
try {
|
||||
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
|
||||
Object obj = field.get(null);
|
||||
if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property<?> state)) {
|
||||
continue;
|
||||
}
|
||||
Property<?> property;
|
||||
if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) {
|
||||
property = new BooleanProperty(
|
||||
state.getName(),
|
||||
(List<Boolean>) ImmutableList.copyOf(state.getPossibleValues())
|
||||
);
|
||||
} else if (state instanceof DirectionProperty) {
|
||||
property = new DirectionalProperty(
|
||||
state.getName(),
|
||||
state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase()))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
|
||||
property = new EnumProperty(
|
||||
state.getName(),
|
||||
state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> ((StringRepresentable) e).getSerializedName())
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) {
|
||||
property = new IntegerProperty(
|
||||
state.getName(),
|
||||
(List<Integer>) ImmutableList.copyOf(state.getPossibleValues())
|
||||
);
|
||||
} else {
|
||||
throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state
|
||||
.getClass()
|
||||
.getSimpleName());
|
||||
}
|
||||
properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> {
|
||||
if (v == null) {
|
||||
v = new ArrayList<>(Collections.singletonList(property));
|
||||
} else {
|
||||
v.add(property);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
allBlockProperties = ImmutableMap.copyOf(properties);
|
||||
}
|
||||
initialised = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BlockType blockType) {
|
||||
Block block = getBlock(blockType);
|
||||
return new PaperweightBlockMaterial(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized BlockMaterial getMaterial(BlockState state) {
|
||||
net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
||||
return new PaperweightBlockMaterial(blockState.getBlock(), blockState);
|
||||
}
|
||||
|
||||
public Block getBlock(BlockType blockType) {
|
||||
return Registry.BLOCK.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public BlockState getBlock(Location location) {
|
||||
Preconditions.checkNotNull(location);
|
||||
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
final ServerLevel handle = craftWorld.getHandle();
|
||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||
BlockState state = adapt(blockData);
|
||||
if (state == null) {
|
||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(final Location location) {
|
||||
Preconditions.checkNotNull(location);
|
||||
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
|
||||
final ServerLevel handle = craftWorld.getHandle();
|
||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||
BlockState state = adapt(blockData);
|
||||
if (state == null) {
|
||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
||||
}
|
||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
||||
|
||||
// Read the NBT data
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
|
||||
if (blockEntity != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = blockEntity.save(new net.minecraft.nbt.CompoundTag());
|
||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SideEffect> getSupportedSideEffects() {
|
||||
return SideEffectSet.defaults().getSideEffectsToApply();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) {
|
||||
CraftChunk craftChunk = (CraftChunk) chunk;
|
||||
LevelChunk levelChunk = craftChunk.getHandle();
|
||||
Level level = levelChunk.getLevel();
|
||||
|
||||
BlockPos blockPos = new BlockPos(x, y, z);
|
||||
net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState();
|
||||
LevelChunkSection[] levelChunkSections = levelChunk.getSections();
|
||||
int y4 = y >> 4;
|
||||
LevelChunkSection section = levelChunkSections[y4];
|
||||
|
||||
net.minecraft.world.level.block.state.BlockState existing;
|
||||
if (section == null) {
|
||||
existing = ((PaperweightBlockMaterial) BlockTypes.AIR.getDefaultState().getMaterial()).getState();
|
||||
} else {
|
||||
existing = section.getBlockState(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
levelChunk.removeBlockEntity(blockPos); // Force delete the old tile entity
|
||||
|
||||
CompoundBinaryTag compoundTag = state instanceof BaseBlock ? state.getNbt() : null;
|
||||
if (compoundTag != null || existing instanceof TileEntityBlock) {
|
||||
level.setBlock(blockPos, blockState, 0);
|
||||
// remove tile
|
||||
if (compoundTag != null) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the Forge version
|
||||
BlockEntity blockEntity = level.getBlockEntity(blockPos);
|
||||
if (blockEntity != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(compoundTag);
|
||||
tag.put("x", IntTag.valueOf(x));
|
||||
tag.put("y", IntTag.valueOf(y));
|
||||
tag.put("z", IntTag.valueOf(z));
|
||||
blockEntity.load(tag); // readTagIntoTileEntity - load data
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (existing == blockState) {
|
||||
return true;
|
||||
}
|
||||
if (section == null) {
|
||||
if (blockState.isAir()) {
|
||||
return true;
|
||||
}
|
||||
levelChunkSections[y4] = section = new LevelChunkSection(y4 << 4);
|
||||
}
|
||||
levelChunk.setBlockState(blockPos, blockState, false);
|
||||
}
|
||||
if (update) {
|
||||
level.getMinecraftWorld().sendBlockUpdated(blockPos, existing, blockState, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||
return new PaperweightFaweWorldNativeAccess(
|
||||
this,
|
||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
||||
Preconditions.checkNotNull(entity);
|
||||
|
||||
CraftEntity craftEntity = ((CraftEntity) entity);
|
||||
Entity mcEntity = craftEntity.getHandle();
|
||||
|
||||
String id = getEntityId(mcEntity);
|
||||
|
||||
if (id != null) {
|
||||
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
|
||||
Supplier<CompoundBinaryTag> saveTag = () -> {
|
||||
final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag();
|
||||
readEntityIntoTag(mcEntity, minecraftTag);
|
||||
//add Id for AbstractChangeSet to work
|
||||
final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag);
|
||||
final Map<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
|
||||
tags.put("Id", StringBinaryTag.of(id));
|
||||
return CompoundBinaryTag.from(tags);
|
||||
};
|
||||
return new LazyBaseEntity(type, saveTag);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichBlockName(BlockType blockType) {
|
||||
return parent.getRichBlockName(blockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichItemName(ItemType itemType) {
|
||||
return parent.getRichItemName(itemType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichItemName(BaseItemStack itemStack) {
|
||||
return parent.getRichItemName(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt getInternalBlockStateId(BlockState state) {
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
|
||||
net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState();
|
||||
return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState adapt(BlockData blockData) {
|
||||
CraftBlockData cbd = ((CraftBlockData) blockData);
|
||||
net.minecraft.world.level.block.state.BlockState ibd = cbd.getState();
|
||||
return adapt(ibd);
|
||||
}
|
||||
|
||||
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
|
||||
return BlockTypesCache.states[adaptToChar(blockState)];
|
||||
}
|
||||
|
||||
public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) {
|
||||
int id = Block.BLOCK_STATE_REGISTRY.getId(blockState);
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
try {
|
||||
init();
|
||||
return ibdToStateOrdinal[id];
|
||||
} catch (ArrayIndexOutOfBoundsException e1) {
|
||||
LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!",
|
||||
blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1
|
||||
);
|
||||
return BlockTypesCache.ReservedIDs.AIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public char ibdIDToOrdinal(int id) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
init();
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getIbdToStateOrdinal() {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
init();
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
}
|
||||
|
||||
public int ordinalToIbdID(char ordinal) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
init();
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getOrdinalToIbdID() {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
init();
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> BlockData adapt(B state) {
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
|
||||
return material.getCraftBlockData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||
if (map != null && map.wasAccessibleSinceLastSave()) {
|
||||
boolean flag = false;
|
||||
// PlayerChunk.d players = map.players;
|
||||
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
|
||||
*/ Stream.empty();
|
||||
|
||||
ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
|
||||
stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
|
||||
.forEach(entityPlayer -> {
|
||||
synchronized (chunkPacket) {
|
||||
ClientboundLevelChunkPacket nmsPacket = (ClientboundLevelChunkPacket) chunkPacket.getNativePacket();
|
||||
if (nmsPacket == null) {
|
||||
nmsPacket = mapUtil.create(this, chunkPacket);
|
||||
chunkPacket.setNativePacket(nmsPacket);
|
||||
}
|
||||
try {
|
||||
FaweCache.INSTANCE.CHUNK_FLAG.get().set(true);
|
||||
entityPlayer.connection.send(nmsPacket);
|
||||
} finally {
|
||||
FaweCache.INSTANCE.CHUNK_FLAG.get().set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Property<?>> getProperties(BlockType blockType) {
|
||||
return getParent().getProperties(blockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) {
|
||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
||||
return blockState1.hasPostProcess(
|
||||
((CraftWorld) world).getHandle(),
|
||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
|
||||
ItemStack stack = new ItemStack(
|
||||
Registry.ITEM.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
||||
baseItemStack.getAmount()
|
||||
);
|
||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNativeBinary(baseItemStack.getNbt())));
|
||||
return CraftItemStack.asCraftMirror(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateTree(
|
||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
||||
org.bukkit.World bukkitWorld
|
||||
) {
|
||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
||||
blockVector3 = blockVector3.add(
|
||||
0,
|
||||
1,
|
||||
0
|
||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
||||
}
|
||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
||||
final BlockVector3 finalBlockVector = blockVector3;
|
||||
// Sync to main thread to ensure no clashes occur
|
||||
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
|
||||
serverLevel.captureTreeGeneration = true;
|
||||
serverLevel.captureBlockStates = true;
|
||||
try {
|
||||
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
|
||||
return null;
|
||||
}
|
||||
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
|
||||
} finally {
|
||||
serverLevel.captureBlockStates = false;
|
||||
serverLevel.captureTreeGeneration = false;
|
||||
serverLevel.capturedBlockStates.clear();
|
||||
}
|
||||
});
|
||||
if (placed == null || placed.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (CraftBlockState craftBlockState : placed.values()) {
|
||||
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
|
||||
continue;
|
||||
}
|
||||
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
|
||||
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
||||
// Quickly add each entity to a list copy.
|
||||
List<Entity> mcEntities = new ArrayList<>();
|
||||
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
||||
|
||||
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
||||
mcEntities.forEach((mcEnt) -> {
|
||||
org.bukkit.entity.Entity bukkitEntity = mcEnt.getBukkitEntity();
|
||||
if (bukkitEntity.isValid()) {
|
||||
list.add(bukkitEntity);
|
||||
}
|
||||
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
||||
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
||||
final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount());
|
||||
weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag())));
|
||||
return weStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toNative(net.minecraft.nbt.Tag foreign) {
|
||||
return parent.toNative(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.nbt.Tag fromNative(Tag foreign) {
|
||||
if (foreign instanceof PaperweightLazyCompoundTag) {
|
||||
return ((PaperweightLazyCompoundTag) foreign).get();
|
||||
}
|
||||
return parent.fromNative(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
||||
return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) {
|
||||
return new PaperweightGetBlocks(world, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInternalBiomeId(BiomeType biomeType) {
|
||||
final Registry<Biome> registry = MinecraftServer
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
|
||||
Biome biome = registry.get(resourceLocation);
|
||||
return registry.getId(biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<NamespacedKey> getRegisteredBiomes() {
|
||||
WritableRegistry<Biome> biomeRegistry = ((CraftServer) Bukkit.getServer())
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(
|
||||
Registry.BIOME_REGISTRY);
|
||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||
for (ResourceLocation key : keys) {
|
||||
try {
|
||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||
}
|
||||
}
|
||||
return namespacedKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelighterFactory getRelighterFactory() {
|
||||
try {
|
||||
Class.forName("ca.spottedleaf.starlight.light.StarLightEngine");
|
||||
if (PaperweightStarlightRelighter.isUsable()) {
|
||||
return new PaperweightStarlightRelighterFactory();
|
||||
}
|
||||
} catch (ThreadDeath td) {
|
||||
throw td;
|
||||
} catch (Throwable ignored) {
|
||||
|
||||
}
|
||||
return new NMSRelighterFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Property<?>>> getAllProperties() {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
init();
|
||||
return allBlockProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getTickingPostProcessor() {
|
||||
return new PaperweightPostProcessor();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,286 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<LevelChunk,
|
||||
net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
private static final Direction[] NEIGHBOUR_ORDER = {
|
||||
Direction.EAST,
|
||||
Direction.WEST,
|
||||
Direction.DOWN,
|
||||
Direction.UP,
|
||||
Direction.NORTH,
|
||||
Direction.SOUTH
|
||||
};
|
||||
private final PaperweightFaweAdapter paperweightFaweAdapter;
|
||||
private final WeakReference<Level> level;
|
||||
private final AtomicInteger lastTick;
|
||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
|
||||
this.paperweightFaweAdapter = paperweightFaweAdapter;
|
||||
this.level = level;
|
||||
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
|
||||
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
|
||||
this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
|
||||
}
|
||||
|
||||
private Level getLevel() {
|
||||
return Objects.requireNonNull(level.get(), "The reference to the world was lost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
||||
this.sideEffectSet = sideEffectSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelChunk getChunk(int x, int z) {
|
||||
return getLevel().getChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) {
|
||||
int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar());
|
||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
||||
? Block.stateById(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) {
|
||||
return levelChunk.getBlockState(blockPos);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public synchronized net.minecraft.world.level.block.state.BlockState setBlockState(
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (Fawe.isMainThread()) {
|
||||
return levelChunk.setBlockState(blockPos, blockState,
|
||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
);
|
||||
}
|
||||
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
|
||||
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
|
||||
cachedChunksToSend.add(new IntPair(levelChunk.bukkitChunk.getX(), levelChunk.bukkitChunk.getZ()));
|
||||
boolean nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick || cachedChanges.size() >= 1024) {
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
flushAsync(nextTick);
|
||||
}
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
|
||||
net.minecraft.world.level.block.state.BlockState blockState,
|
||||
BlockPos blockPos
|
||||
) {
|
||||
return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getPosition(int x, int y, int z) {
|
||||
return new BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLightingForBlock(BlockPos blockPos) {
|
||||
getLevel().getChunkSource().getLightEngine().checkBlock(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the other versions
|
||||
BlockEntity blockEntity = getLevel().getBlockEntity(blockPos);
|
||||
if (blockEntity == null) {
|
||||
return false;
|
||||
}
|
||||
net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeBinary(tag);
|
||||
blockEntity.load((CompoundTag) nativeTag);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyBlockUpdate(
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
|
||||
getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(LevelChunk levelChunk) {
|
||||
return levelChunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) {
|
||||
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
|
||||
((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNeighbors(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
Level level = getLevel();
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
level.blockUpdated(blockPos, oldState.getBlock());
|
||||
} else {
|
||||
// When we don't want events, manually run the physics without them.
|
||||
// Un-nest neighbour updating
|
||||
for (Direction direction : NEIGHBOUR_ORDER) {
|
||||
BlockPos shifted = blockPos.relative(direction);
|
||||
level.getBlockState(shifted).neighborChanged(level, shifted, oldState.getBlock(), blockPos, false);
|
||||
}
|
||||
}
|
||||
if (newState.hasAnalogOutputSignal()) {
|
||||
level.updateNeighbourForOutputSignal(blockPos, newState.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNeighbors(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
int recursionLimit
|
||||
) {
|
||||
Level level = getLevel();
|
||||
// a == updateNeighbors
|
||||
// b == updateDiagonalNeighbors
|
||||
oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
CraftWorld craftWorld = level.getWorld();
|
||||
if (craftWorld != null) {
|
||||
BlockPhysicsEvent event = new BlockPhysicsEvent(
|
||||
craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
|
||||
CraftBlockData.fromData(newState)
|
||||
);
|
||||
level.getCraftServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit);
|
||||
newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
getLevel().onBlockStateChange(blockPos, oldState, newState);
|
||||
}
|
||||
|
||||
private synchronized void flushAsync(final boolean sendChunks) {
|
||||
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
|
||||
cachedChanges.clear();
|
||||
final Set<IntPair> toSend;
|
||||
if (sendChunks) {
|
||||
toSend = Set.copyOf(cachedChunksToSend);
|
||||
cachedChunksToSend.clear();
|
||||
} else {
|
||||
toSend = Collections.emptySet();
|
||||
}
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
if (!sendChunks) {
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
TaskManager.taskManager().sync(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
}
|
||||
|
||||
private record CachedChange(
|
||||
LevelChunk levelChunk,
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -0,0 +1,245 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.queue.IBlocks;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private ChunkBiomeContainer chunkBiomeContainer;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
this.serverLevel = levelChunk.level;
|
||||
this.minHeight = serverLevel.getMinBuildHeight();
|
||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
this.blocks = new char[getSectionCount()][];
|
||||
}
|
||||
|
||||
protected void storeTile(BlockEntity blockEntity) {
|
||||
tiles.put(
|
||||
BlockVector3.at(
|
||||
blockEntity.getBlockPos().getX(),
|
||||
blockEntity.getBlockPos().getY(),
|
||||
blockEntity.getBlockPos().getZ()
|
||||
),
|
||||
new PaperweightLazyCompoundTag(Suppliers.memoize(() -> blockEntity.save(new net.minecraft.nbt.CompoundTag())))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
entities.add((CompoundTag) adapter.toNative(compoundTag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
for (CompoundTag tag : entities) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCreateCopy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeightmapToGet(HeightMapType type, int[] data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxY() {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return minHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSectionPosition() {
|
||||
return maxHeight >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinSectionPosition() {
|
||||
return minHeight >> 4;
|
||||
}
|
||||
|
||||
protected void storeBiomes(ChunkBiomeContainer chunkBiomeContainer) {
|
||||
// The to do one line below is pre-paperweight and needs to be revised
|
||||
// TODO revisit last parameter, BiomeStorage[] *would* be more efficient
|
||||
this.chunkBiomeContainer = new ChunkBiomeContainer(chunkBiomeContainer.biomeRegistry, serverLevel,
|
||||
chunkBiomeContainer.writeBiomes()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
Biome biome = null;
|
||||
if (y == -1) {
|
||||
for (y = serverLevel.getMinBuildHeight(); y <= serverLevel.getMaxBuildHeight(); y += 4) {
|
||||
biome = this.chunkBiomeContainer.getNoiseBiome(x >> 2, y >> 2, z >> 2);
|
||||
if (biome != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
biome = this.chunkBiomeContainer.getNoiseBiome(x >> 2, y >> 2, z >> 2);
|
||||
}
|
||||
return biome != null ? PaperweightPlatformAdapter.adapt(biome, serverLevel) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSectionLighting(int layer, boolean sky) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive, int layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlocks reset() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionCount() {
|
||||
return serverLevel.getSectionsCount();
|
||||
}
|
||||
|
||||
protected void storeSection(int layer, char[] data) {
|
||||
blocks[layer] = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
BlockState state = BlockTypesCache.states[get(x, y, z)];
|
||||
return state.toBaseBlock(this, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer] != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] loadIfPresent(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmittedLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getHeightMap(HeightMapType type) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public char get(int x, int y, int z) {
|
||||
final int layer = (y >> 4) - getMinSectionPosition();
|
||||
final int index = (y & 15) << 8 | z << 4 | x;
|
||||
return blocks[layer][index];
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
||||
|
||||
public class PaperweightMapChunkUtil extends MapChunkUtil<ClientboundLevelChunkPacket> {
|
||||
|
||||
public PaperweightMapChunkUtil() throws NoSuchFieldException {
|
||||
fieldX = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a"));
|
||||
fieldZ = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("x", "b"));
|
||||
fieldBitMask = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("z", "c"));
|
||||
fieldHeightMap = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("availableSections", "d"));
|
||||
fieldChunkData = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("biomes", "f"));
|
||||
fieldBlockEntities = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("buffer", "g"));
|
||||
fieldFull = ClientboundLevelChunkPacket.class.getDeclaredField(Refraction.pickName("blockEntitiesTags", "h"));
|
||||
fieldX.setAccessible(true);
|
||||
fieldZ.setAccessible(true);
|
||||
fieldBitMask.setAccessible(true);
|
||||
fieldHeightMap.setAccessible(true);
|
||||
fieldChunkData.setAccessible(true);
|
||||
fieldBlockEntities.setAccessible(true);
|
||||
fieldFull.setAccessible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientboundLevelChunkPacket createPacket() {
|
||||
// TODO ??? return new ClientboundLevelChunkPacket();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,511 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.ChunkBiomeContainer;
|
||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.LinearPalette;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
||||
import net.minecraft.world.level.gameevent.GameEventListener;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
public static final Field fieldStorage;
|
||||
public static final Field fieldPalette;
|
||||
public static final Field fieldBits;
|
||||
|
||||
public static final Field fieldBitsPerEntry;
|
||||
|
||||
private static final Field fieldTickingFluidContent;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final Field fieldBiomes;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final Field fieldGameEventDispatcherSections;
|
||||
private static final MethodHandle methodremoveBlockEntityTicker;
|
||||
|
||||
private static final Field fieldRemove;
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l"));
|
||||
fieldBits.setAccessible(true);
|
||||
fieldStorage = PalettedContainer.class.getDeclaredField(Refraction.pickName("storage", "c"));
|
||||
fieldStorage.setAccessible(true);
|
||||
fieldPalette = PalettedContainer.class.getDeclaredField(Refraction.pickName("palette", "k"));
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldBitsPerEntry = BitStorage.class.getDeclaredField(Refraction.pickName("bits", "c"));
|
||||
fieldBitsPerEntry.setAccessible(true);
|
||||
|
||||
fieldTickingFluidContent = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "h"));
|
||||
fieldTickingFluidContent.setAccessible(true);
|
||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g"));
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f"));
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
fieldBiomes = ChunkBiomeContainer.class.getDeclaredField(Refraction.pickName("biomes", "f"));
|
||||
fieldBiomes.setAccessible(true);
|
||||
|
||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
||||
"getVisibleChunkIfPresent",
|
||||
"getVisibleChunk"
|
||||
), long.class);
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
|
||||
fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
fieldGameEventDispatcherSections = LevelChunk.class.getDeclaredField(Refraction.pickName(
|
||||
"gameEventDispatcherSections", "x"));
|
||||
fieldGameEventDispatcherSections.setAccessible(true);
|
||||
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
|
||||
Refraction.pickName(
|
||||
"removeBlockEntityTicker",
|
||||
"l"
|
||||
), BlockPos.class
|
||||
);
|
||||
removeBlockEntityTicker.setAccessible(true);
|
||||
methodremoveBlockEntityTicker = MethodHandles.lookup().unreflect(removeBlockEntityTicker);
|
||||
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable rethrow) {
|
||||
rethrow.printStackTrace();
|
||||
throw new RuntimeException(rethrow);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean setSectionAtomic(
|
||||
LevelChunkSection[] sections,
|
||||
LevelChunkSection expected,
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// There is no point in having a functional semaphore for paper servers.
|
||||
private static final ThreadLocal<DelegateSemaphore> SEMAPHORE_THREAD_LOCAL =
|
||||
ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null));
|
||||
|
||||
static DelegateSemaphore applyLock(LevelChunkSection section) {
|
||||
if (PaperLib.isPaper()) {
|
||||
return SEMAPHORE_THREAD_LOCAL.get();
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(blocks, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
||||
if (nmsChunk != null) {
|
||||
return nmsChunk;
|
||||
}
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
} else {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
// Avoid "async" methods from the main thread.
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||
try {
|
||||
CraftChunk chunk;
|
||||
try {
|
||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
String world = serverLevel.getWorld().getName();
|
||||
// We've already taken 10 seconds we can afford to wait a little here.
|
||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||
if (loaded) {
|
||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||
// Retry chunk load
|
||||
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
||||
}
|
||||
}
|
||||
return chunk.getHandle();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
// Ensure chunk is definitely loaded before applying a ticket
|
||||
net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||
.getChunkSource()
|
||||
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||
}
|
||||
|
||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||
try {
|
||||
return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ));
|
||||
} catch (Throwable thr) {
|
||||
throw new RuntimeException(thr);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
||||
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||
if (chunkHolder == null) {
|
||||
return;
|
||||
}
|
||||
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
|
||||
// UNLOADED_CHUNK
|
||||
Optional<LevelChunk> optional = ((Either) chunkHolder
|
||||
.getTickingChunkFuture()
|
||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
|
||||
if (PaperLib.isPaper()) {
|
||||
// getChunkAtIfLoadedImmediately is paper only
|
||||
optional = optional.or(() -> Optional.ofNullable(nmsWorld
|
||||
.getChunkSource()
|
||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ)));
|
||||
}
|
||||
if (optional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LevelChunk levelChunk = optional.get();
|
||||
TaskManager.taskManager().task(() -> {
|
||||
ClientboundLevelChunkPacket chunkPacket = new ClientboundLevelChunkPacket(levelChunk);
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(chunkPacket));
|
||||
if (lighting) {
|
||||
//This needs to be true otherwise Minecraft will update lighting from/at the chunk edges (bad)
|
||||
boolean trustEdges = true;
|
||||
ClientboundLightUpdatePacket packet =
|
||||
new ClientboundLightUpdatePacket(coordIntPair, nmsWorld.getChunkSource().getLightEngine(), null, null,
|
||||
trustEdges
|
||||
);
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static Stream<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
|
||||
return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false);
|
||||
}
|
||||
|
||||
/*
|
||||
NMS conversion
|
||||
*/
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final char[] blocks,
|
||||
boolean fastMode,
|
||||
CachedBukkitAdapter adapter
|
||||
) {
|
||||
return newChunkSection(layer, null, blocks, fastMode, adapter);
|
||||
}
|
||||
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
boolean fastMode,
|
||||
CachedBukkitAdapter adapter
|
||||
) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
|
||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int num_palette;
|
||||
final short[] nonEmptyBlockCount = fastMode ? new short[1] : null;
|
||||
if (get == null) {
|
||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, nonEmptyBlockCount);
|
||||
} else {
|
||||
num_palette = createPalette(
|
||||
layer,
|
||||
blockToPalette,
|
||||
paletteToBlock,
|
||||
blocksCopy,
|
||||
get,
|
||||
set,
|
||||
adapter,
|
||||
nonEmptyBlockCount
|
||||
);
|
||||
}
|
||||
// BlockStates
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (Settings.settings().PROTOCOL_SUPPORT_FIX || num_palette != 1) {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
|
||||
} else {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
|
||||
}
|
||||
if (bitsPerEntry > 8) {
|
||||
bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1);
|
||||
}
|
||||
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntry);
|
||||
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
|
||||
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) {
|
||||
blockStates[i] = 0;
|
||||
}
|
||||
} else {
|
||||
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntry, 4096, blockStates);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
LevelChunkSection levelChunkSection = newChunkSection(layer);
|
||||
// set palette & data bits
|
||||
final PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks =
|
||||
levelChunkSection.getStates();
|
||||
// private DataPalette<T> h;
|
||||
// protected DataBits a;
|
||||
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
|
||||
final BitStorage nmsBits = new BitStorage(bitsPerEntry, 4096, bits);
|
||||
final Palette<net.minecraft.world.level.block.state.BlockState> blockStatePalettedContainer;
|
||||
if (bitsPerEntry <= 4) {
|
||||
blockStatePalettedContainer = new LinearPalette<>(Block.BLOCK_STATE_REGISTRY, bitsPerEntry, dataPaletteBlocks,
|
||||
NbtUtils::readBlockState
|
||||
);
|
||||
} else if (bitsPerEntry < 9) {
|
||||
blockStatePalettedContainer = new HashMapPalette<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
bitsPerEntry,
|
||||
dataPaletteBlocks,
|
||||
NbtUtils::readBlockState,
|
||||
NbtUtils::writeBlockState
|
||||
);
|
||||
} else {
|
||||
blockStatePalettedContainer = LevelChunkSection.GLOBAL_BLOCKSTATE_PALETTE;
|
||||
}
|
||||
|
||||
// set palette if required
|
||||
if (bitsPerEntry < 9) {
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
final int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
final BlockState state = BlockTypesCache.states[ordinal];
|
||||
final net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState();
|
||||
blockStatePalettedContainer.idFor(blockState);
|
||||
}
|
||||
}
|
||||
try {
|
||||
fieldStorage.set(dataPaletteBlocks, nmsBits);
|
||||
fieldPalette.set(dataPaletteBlocks, blockStatePalettedContainer);
|
||||
fieldBits.set(dataPaletteBlocks, bitsPerEntry);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (!fastMode) {
|
||||
levelChunkSection.recalcBlockCounts();
|
||||
} else {
|
||||
try {
|
||||
fieldNonEmptyBlockCount.set(levelChunkSection, nonEmptyBlockCount[0]);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return levelChunkSection;
|
||||
} catch (final Throwable e) {
|
||||
throw e;
|
||||
} finally {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
Arrays.fill(paletteToBlock, Integer.MAX_VALUE);
|
||||
Arrays.fill(blockStates, 0);
|
||||
Arrays.fill(blocksCopy, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static LevelChunkSection newChunkSection(int layer) {
|
||||
return new LevelChunkSection(layer);
|
||||
}
|
||||
|
||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
||||
fieldTickingFluidContent.setShort(section, (short) 0);
|
||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
||||
}
|
||||
|
||||
public static Biome[] getBiomeArray(ChunkBiomeContainer chunkBiomeContainer) {
|
||||
try {
|
||||
return (Biome[]) fieldBiomes.get(chunkBiomeContainer);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static BiomeType adapt(Biome biome, LevelAccessor levelAccessor) {
|
||||
ResourceLocation resourceLocation = levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).getKey(
|
||||
biome);
|
||||
if (resourceLocation == null) {
|
||||
return levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY).getId(biome) == -1
|
||||
? BiomeTypes.OCEAN
|
||||
: null;
|
||||
}
|
||||
return BiomeTypes.get(resourceLocation.toString().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
||||
try {
|
||||
// Do the method ourselves to avoid trying to reflect generic method parameters
|
||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
||||
if (blockEntity != null) {
|
||||
if (!levelChunk.level.isClientSide) {
|
||||
Block block = beacon.getBlockState().getBlock();
|
||||
if (block instanceof EntityBlock) {
|
||||
GameEventListener gameEventListener = ((EntityBlock) block).getListener(levelChunk.level, beacon);
|
||||
if (gameEventListener != null) {
|
||||
int i = SectionPos.blockToSectionCoord(beacon.getBlockPos().getY());
|
||||
GameEventDispatcher gameEventDispatcher = levelChunk.getEventDispatcher(i);
|
||||
gameEventDispatcher.unregister(gameEventListener);
|
||||
if (gameEventDispatcher.isEmpty()) {
|
||||
try {
|
||||
((Int2ObjectMap<GameEventDispatcher>) fieldGameEventDispatcherSections.get(levelChunk))
|
||||
.remove(i);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldRemove.set(beacon, true);
|
||||
}
|
||||
}
|
||||
methodremoveBlockEntityTicker.invoke(levelChunk, beacon.getBlockPos());
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static List<Entity> getEntities(LevelChunk chunk) {
|
||||
return chunk.level.entityManager.getEntities(chunk.getPos());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
// The PostProcessor shouldn't be added, but just in case
|
||||
if (!tickFluid) {
|
||||
return;
|
||||
}
|
||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
||||
layer:
|
||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
||||
char[] set = iChunkSet.loadIfPresent(layer);
|
||||
if (set == null) {
|
||||
// No edit means no need to process
|
||||
continue;
|
||||
}
|
||||
char[] get = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean fromGet = false; // Used for liquids
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
||||
// actually being set
|
||||
if (get == null) {
|
||||
continue layer;
|
||||
}
|
||||
fromGet = true;
|
||||
ordinal = replacedOrdinal = get[i];
|
||||
}
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
continue;
|
||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
replacedOrdinal = get[i];
|
||||
}
|
||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
||||
boolean replacedWasLiquid = false;
|
||||
BlockState replacedState = null;
|
||||
if (!ticking) {
|
||||
// If the block being replaced was not ticking, it cannot be a liquid
|
||||
if (!replacedWasTicking) {
|
||||
continue;
|
||||
}
|
||||
// If the block being replaced is not fluid, we do not need to worry
|
||||
if (!(replacedWasLiquid =
|
||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
boolean liquid = state.getMaterial().isLiquid();
|
||||
int x = i & 15;
|
||||
int y = (i >> 8) & 15;
|
||||
int z = (i >> 4) & 15;
|
||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
||||
if (liquid || replacedWasLiquid) {
|
||||
if (liquid) {
|
||||
addFluid(getBlocks.serverLevel, state, position);
|
||||
continue;
|
||||
}
|
||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
||||
// being ticked anyway. We only need it to be "hit" once.
|
||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
||||
if (set == null || get == null) {
|
||||
return false;
|
||||
}
|
||||
char ordinal;
|
||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
if (x > 0 && set[i - 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (x < 15 && set[i + 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z > 0 && set[i - 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z < 15 && set[i + 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y > 0 && set[i - 256] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y < 15 && set[i + 256] != reserved) {
|
||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isFluid(char ordinal) {
|
||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
||||
Fluid type;
|
||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
||||
} else {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
||||
}
|
||||
serverLevel.getLiquidTicks().scheduleTick(
|
||||
position,
|
||||
type,
|
||||
type.getTickDelay(serverLevel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +1,24 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_16_5;
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.boydti.fawe.beta.IQueueChunk;
|
||||
import com.boydti.fawe.beta.IQueueExtent;
|
||||
import com.boydti.fawe.beta.implementation.lighting.NMSRelighter;
|
||||
import com.boydti.fawe.beta.implementation.lighting.Relighter;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.server.v1_16_R3.ChunkCoordIntPair;
|
||||
import net.minecraft.server.v1_16_R3.ChunkStatus;
|
||||
import net.minecraft.server.v1_16_R3.LightEngineThreaded;
|
||||
import net.minecraft.server.v1_16_R3.MCUtil;
|
||||
import net.minecraft.server.v1_16_R3.TicketType;
|
||||
import net.minecraft.server.v1_16_R3.Unit;
|
||||
import net.minecraft.server.v1_16_R3.WorldServer;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -33,31 +33,22 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class TuinityRelighter_1_16_5 implements Relighter {
|
||||
public class PaperweightStarlightRelighter implements Relighter {
|
||||
|
||||
public static final MethodHandle RELIGHT;
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final MethodHandle RELIGHT;
|
||||
|
||||
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
|
||||
private static final int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
|
||||
|
||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.a("fawe_ticket", (a, b) -> 0);
|
||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
|
||||
|
||||
private final WorldServer world;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
static {
|
||||
MethodHandle tmp = null;
|
||||
try {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
tmp = lookup.findVirtual(LightEngineThreaded.class,
|
||||
tmp = lookup.findVirtual(
|
||||
ThreadedLevelLightEngine.class,
|
||||
"relight",
|
||||
MethodType.methodType(
|
||||
int.class, // return type
|
||||
@ -68,15 +59,25 @@ public class TuinityRelighter_1_16_5 implements Relighter {
|
||||
)
|
||||
);
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
LOGGER.error("Failed to locate relight method in LightEngineThreaded on Tuinity. " +
|
||||
"Is everything up to date?", e);
|
||||
LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
|
||||
}
|
||||
RELIGHT = tmp;
|
||||
}
|
||||
|
||||
public TuinityRelighter_1_16_5(WorldServer world, IQueueExtent<IQueueChunk> queue) {
|
||||
this.world = world;
|
||||
this.delegate = new NMSRelighter(queue, false);
|
||||
private final ServerLevel serverLevel;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
||||
this.serverLevel = serverLevel;
|
||||
this.delegate = new NMSRelighter(queue);
|
||||
}
|
||||
|
||||
public static boolean isUsable() {
|
||||
return RELIGHT != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,7 +87,7 @@ public class TuinityRelighter_1_16_5 implements Relighter {
|
||||
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
|
||||
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
|
||||
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
|
||||
chunks.add(ChunkCoordIntPair.pair(cx, cz));
|
||||
chunks.add(ChunkPos.asLong(cx, cz));
|
||||
} finally {
|
||||
areaLock.unlock();
|
||||
}
|
||||
@ -107,7 +108,9 @@ public class TuinityRelighter_1_16_5 implements Relighter {
|
||||
public void fixLightingSafe(boolean sky) {
|
||||
this.areaLock.lock();
|
||||
try {
|
||||
if (regions.isEmpty()) return;
|
||||
if (regions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LongSet first = regions.removeFirst();
|
||||
fixLighting(first, () -> fixLightingSafe(true));
|
||||
} finally {
|
||||
@ -121,46 +124,53 @@ public class TuinityRelighter_1_16_5 implements Relighter {
|
||||
* (as required by the server).
|
||||
*/
|
||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
// convert from long keys to ChunkCoordIntPairs
|
||||
Set<ChunkCoordIntPair> coords = new HashSet<>();
|
||||
// convert from long keys to ChunkPos
|
||||
Set<ChunkPos> coords = new HashSet<>();
|
||||
LongIterator iterator = chunks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
coords.add(new ChunkCoordIntPair(iterator.nextLong()));
|
||||
coords.add(new ChunkPos(iterator.nextLong()));
|
||||
}
|
||||
TaskManager.IMP.task(() -> {
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (ChunkCoordIntPair pos : coords) {
|
||||
futures.add(world.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> world.getChunkProvider().addTicketAtLevel(
|
||||
for (ChunkPos pos : coords) {
|
||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
pos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE))
|
||||
Unit.INSTANCE
|
||||
))
|
||||
);
|
||||
}
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
|
||||
invokeRelight(coords,
|
||||
c -> { }, // no callback for single chunks required
|
||||
invokeRelight(
|
||||
coords,
|
||||
c -> {
|
||||
}, // no callback for single chunks required
|
||||
i -> {
|
||||
if (i != coords.size()) {
|
||||
LOGGER.warn("Processed " + i + " chunks instead of " + coords.size());
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.IMP.task(() -> postProcessChunks(coords));
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.IMP.async(andThen);
|
||||
})
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeRelight(Set<ChunkCoordIntPair> coords,
|
||||
Consumer<ChunkCoordIntPair> chunkCallback,
|
||||
IntConsumer processCallback) {
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
int unused = (int) RELIGHT.invokeExact(world.getChunkProvider().getLightEngine(),
|
||||
int unused = (int) RELIGHT.invokeExact(
|
||||
serverLevel.getChunkSource().getLightEngine(),
|
||||
coords,
|
||||
chunkCallback, // callback per chunk
|
||||
processCallback // callback for all chunks
|
||||
@ -174,15 +184,15 @@ public class TuinityRelighter_1_16_5 implements Relighter {
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
private void postProcessChunks(Set<ChunkCoordIntPair> coords) {
|
||||
boolean delay = Settings.IMP.LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkCoordIntPair pos : coords) {
|
||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
BukkitAdapter_1_16_5.sendChunk(world, x, z, false);
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
||||
}
|
||||
world.getChunkProvider().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,4 +235,5 @@ public class TuinityRelighter_1_16_5 implements Relighter {
|
||||
public void close() throws Exception {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||
|
||||
@Override
|
||||
public @Nonnull
|
||||
@SuppressWarnings("rawtypes")
|
||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||
if (w == null) {
|
||||
return NullRelighter.INSTANCE;
|
||||
}
|
||||
return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,161 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.nbt;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.LazyCompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
|
||||
private final Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier;
|
||||
private CompoundTag compoundTag;
|
||||
|
||||
public PaperweightLazyCompoundTag(Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier) {
|
||||
super(new HashMap<>());
|
||||
this.compoundTagSupplier = compoundTagSupplier;
|
||||
}
|
||||
|
||||
public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
this(() -> compoundTag);
|
||||
}
|
||||
|
||||
public net.minecraft.nbt.CompoundTag get() {
|
||||
return compoundTagSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Tag> getValue() {
|
||||
if (compoundTag == null) {
|
||||
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
|
||||
}
|
||||
return compoundTag.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundBinaryTag asBinaryTag() {
|
||||
getValue();
|
||||
return compoundTag.asBinaryTag();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return compoundTagSupplier.get().contains(key);
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
return compoundTagSupplier.get().getByteArray(key);
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
return compoundTagSupplier.get().getByte(key);
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
return compoundTagSupplier.get().getDouble(key);
|
||||
}
|
||||
|
||||
public double asDouble(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsDouble();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
return compoundTagSupplier.get().getFloat(key);
|
||||
}
|
||||
|
||||
public int[] getIntArray(String key) {
|
||||
return compoundTagSupplier.get().getIntArray(key);
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
return compoundTagSupplier.get().getInt(key);
|
||||
}
|
||||
|
||||
public int asInt(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsInt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> getList(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
|
||||
ArrayList<Tag> list = new ArrayList<>();
|
||||
for (net.minecraft.nbt.Tag elem : nbtList) {
|
||||
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
list.add(new PaperweightLazyCompoundTag(compoundTag));
|
||||
} else {
|
||||
list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ListTag getListTag(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag) {
|
||||
return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag);
|
||||
}
|
||||
return new ListTag(StringTag.class, Collections.emptyList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
|
||||
ListTag listTag = getListTag(key);
|
||||
if (listTag.getType().equals(listType)) {
|
||||
return (List<T>) listTag.getValue();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public long[] getLongArray(String key) {
|
||||
return compoundTagSupplier.get().getLongArray(key);
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
return compoundTagSupplier.get().getLong(key);
|
||||
}
|
||||
|
||||
public long asLong(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsLong();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
return compoundTagSupplier.get().getShort(key);
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return compoundTagSupplier.get().getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return compoundTagSupplier.get().toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,697 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_17_R1_2.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.minecraft.core.MappedRegistry;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.data.worldgen.biome.Biomes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.LinearCongruentialGenerator;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.biome.OverworldBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.SimpleRandomSource;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
import net.minecraft.world.level.levelgen.synth.ImprovedNoise;
|
||||
import net.minecraft.world.level.newbiome.area.Area;
|
||||
import net.minecraft.world.level.newbiome.area.AreaFactory;
|
||||
import net.minecraft.world.level.newbiome.context.BigContext;
|
||||
import net.minecraft.world.level.newbiome.layer.Layer;
|
||||
import net.minecraft.world.level.newbiome.layer.Layers;
|
||||
import net.minecraft.world.level.newbiome.layer.traits.PixelTransformer;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_17_R1.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.LongFunction;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field worldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field generateFlatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
worldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
worldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
generateFlatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "g"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "e"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "C"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureManager structureManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = generateFlatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataPackConfig()
|
||||
);
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable());
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
originalServerWorld.dimensionType(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
// placeholder. Required for new ChunkProviderServer, but we create and then set it later
|
||||
newOpts.dimensions().get(levelStemResourceKey).generator(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
private final Biome singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(
|
||||
options
|
||||
.getBiomeType()
|
||||
.getId())) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig);
|
||||
}
|
||||
|
||||
//generator
|
||||
if (originalChunkProvider.getGenerator() instanceof FlatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = (FlatLevelGeneratorSettings) generatorSettingFlatField.get(
|
||||
originalChunkProvider.getGenerator());
|
||||
chunkGenerator = new FlatLevelSource(generatorSettingFlat);
|
||||
} else if (originalChunkProvider.getGenerator() instanceof NoiseBasedChunkGenerator) {
|
||||
Supplier<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Supplier<NoiseGeneratorSettings>) generatorSettingBaseSupplierField
|
||||
.get(originalChunkProvider.getGenerator());
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options
|
||||
.getBiomeType()
|
||||
.getId())));
|
||||
} else {
|
||||
biomeSource = originalChunkProvider.getGenerator().getBiomeSource();
|
||||
if (biomeSource instanceof OverworldBiomeSource) {
|
||||
biomeSource = fastOverworldBiomeSource(biomeSource);
|
||||
}
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(biomeSource, seed, generatorSettingBaseSupplier);
|
||||
} else if (originalChunkProvider.getGenerator() instanceof CustomChunkGenerator) {
|
||||
chunkGenerator = (ChunkGenerator) delegateField.get(originalChunkProvider.getGenerator());
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalChunkProvider.getGenerator().getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = freshChunkProvider.getLightEngine();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return PaperLib.isPaper()
|
||||
? new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld, freshWorld) // paper
|
||||
: new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld); // spigot
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) worldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private BiomeSource fastOverworldBiomeSource(BiomeSource biomeSource) throws Exception {
|
||||
Field legacyBiomeInitLayerField = OverworldBiomeSource.class.getDeclaredField(
|
||||
Refraction.pickName("legacyBiomeInitLayer", "i"));
|
||||
legacyBiomeInitLayerField.setAccessible(true);
|
||||
Field largeBiomesField = OverworldBiomeSource.class.getDeclaredField(Refraction.pickName("largeBiomes", "j"));
|
||||
largeBiomesField.setAccessible(true);
|
||||
Field biomeRegistryField = OverworldBiomeSource.class.getDeclaredField(Refraction.pickName("biomes", "k"));
|
||||
biomeRegistryField.setAccessible(true);
|
||||
Field areaLazyField = Layer.class.getDeclaredField(Refraction.pickName("area", "b"));
|
||||
areaLazyField.setAccessible(true);
|
||||
Method initAreaFactoryMethod = Layers.class.getDeclaredMethod(
|
||||
Refraction.pickName("getDefaultLayer", "a"),
|
||||
boolean.class,
|
||||
int.class,
|
||||
int.class,
|
||||
LongFunction.class
|
||||
);
|
||||
initAreaFactoryMethod.setAccessible(true);
|
||||
|
||||
//init new WorldChunkManagerOverworld
|
||||
boolean legacyBiomeInitLayer = legacyBiomeInitLayerField.getBoolean(biomeSource);
|
||||
boolean largebiomes = largeBiomesField.getBoolean(biomeSource);
|
||||
Registry<Biome> biomeRegistryMojang = (Registry<Biome>) biomeRegistryField.get(biomeSource);
|
||||
Registry<Biome> biomeRegistry;
|
||||
if (options.hasBiomeType()) {
|
||||
Biome biome = BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(options.getBiomeType().getId()));
|
||||
biomeRegistry = new MappedRegistry<>(
|
||||
ResourceKey.createRegistryKey(new ResourceLocation("fawe_biomes")),
|
||||
Lifecycle.experimental()
|
||||
);
|
||||
((MappedRegistry) biomeRegistry).registerMapping(0, BuiltinRegistries.BIOME.getResourceKey(biome).get(), biome,
|
||||
Lifecycle.experimental()
|
||||
);
|
||||
} else {
|
||||
biomeRegistry = biomeRegistryMojang;
|
||||
}
|
||||
|
||||
//replace genLayer
|
||||
AreaFactory<FastAreaLazy> factory = (AreaFactory<FastAreaLazy>) initAreaFactoryMethod.invoke(
|
||||
null,
|
||||
legacyBiomeInitLayer,
|
||||
largebiomes ? 6 : 4,
|
||||
4,
|
||||
(LongFunction) (salt -> new FastWorldGenContextArea(seed, salt))
|
||||
);
|
||||
biomeSource = new FastOverworldBiomeSource(biomeRegistry, new FastGenLayer(factory));
|
||||
|
||||
return biomeSource;
|
||||
}
|
||||
|
||||
private static class FastOverworldBiomeSource extends BiomeSource {
|
||||
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private final boolean isSingleRegistry;
|
||||
private final FastGenLayer fastGenLayer;
|
||||
|
||||
public FastOverworldBiomeSource(
|
||||
Registry<Biome> biomeRegistry,
|
||||
FastGenLayer genLayer
|
||||
) {
|
||||
super(biomeRegistry.stream().collect(Collectors.toList()));
|
||||
this.biomeRegistry = biomeRegistry;
|
||||
this.isSingleRegistry = biomeRegistry.entrySet().size() == 1;
|
||||
this.fastGenLayer = genLayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Codec<? extends BiomeSource> codec() {
|
||||
return OverworldBiomeSource.CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeSource withSeed(final long seed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (this.isSingleRegistry) {
|
||||
return this.biomeRegistry.byId(0);
|
||||
}
|
||||
return this.fastGenLayer.get(this.biomeRegistry, biomeX, biomeZ);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FastWorldGenContextArea implements BigContext<FastAreaLazy> {
|
||||
|
||||
private final ConcurrentHashMap<Long, Integer> sharedAreaMap = new ConcurrentHashMap<>();
|
||||
private final ImprovedNoise improvedNoise;
|
||||
private final long magicrandom;
|
||||
private final ConcurrentHashMap<Long, Long> map = new ConcurrentHashMap<>(); //needed for multithreaded generation
|
||||
|
||||
public FastWorldGenContextArea(long seed, long lconst) {
|
||||
this.magicrandom = mix(seed, lconst);
|
||||
this.improvedNoise = new ImprovedNoise(new SimpleRandomSource(seed));
|
||||
}
|
||||
|
||||
private static long mix(long seed, long salt) {
|
||||
long l = LinearCongruentialGenerator.next(salt, salt);
|
||||
l = LinearCongruentialGenerator.next(l, salt);
|
||||
l = LinearCongruentialGenerator.next(l, salt);
|
||||
long m = LinearCongruentialGenerator.next(seed, l);
|
||||
m = LinearCongruentialGenerator.next(m, l);
|
||||
m = LinearCongruentialGenerator.next(m, l);
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastAreaLazy createResult(PixelTransformer pixelTransformer) {
|
||||
return new FastAreaLazy(sharedAreaMap, pixelTransformer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initRandom(long x, long z) {
|
||||
long l = this.magicrandom;
|
||||
l = LinearCongruentialGenerator.next(l, x);
|
||||
l = LinearCongruentialGenerator.next(l, z);
|
||||
l = LinearCongruentialGenerator.next(l, x);
|
||||
l = LinearCongruentialGenerator.next(l, z);
|
||||
this.map.put(Thread.currentThread().getId(), l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextRandom(int y) {
|
||||
long tid = Thread.currentThread().getId();
|
||||
long e = this.map.computeIfAbsent(tid, i -> 0L);
|
||||
int mod = (int) Math.floorMod(e >> 24L, (long) y);
|
||||
this.map.put(tid, LinearCongruentialGenerator.next(e, this.magicrandom));
|
||||
return mod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImprovedNoise getBiomeNoise() {
|
||||
return this.improvedNoise;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class FastGenLayer extends Layer {
|
||||
|
||||
private final FastAreaLazy fastAreaLazy;
|
||||
|
||||
public FastGenLayer(AreaFactory<FastAreaLazy> factory) {
|
||||
super(() -> null);
|
||||
this.fastAreaLazy = factory.make();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome get(Registry<Biome> registry, int x, int z) {
|
||||
ResourceKey<Biome> key = Biomes.byId(this.fastAreaLazy.get(x, z));
|
||||
if (key == null) {
|
||||
return registry.get(Biomes.byId(0));
|
||||
}
|
||||
Biome biome = registry.get(key);
|
||||
if (biome == null) {
|
||||
return registry.get(Biomes.byId(0));
|
||||
}
|
||||
return biome;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private record FastAreaLazy(ConcurrentHashMap<Long, Integer> sharedMap, PixelTransformer transformer) implements Area {
|
||||
|
||||
@Override
|
||||
public int get(int x, int z) {
|
||||
long zx = ChunkPos.asLong(x, z);
|
||||
return this.sharedMap.computeIfAbsent(zx, i -> this.transformer.apply(x, z));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
// avoid warning on paper
|
||||
public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor world, ServerLevel serverLevel) {
|
||||
super(pos, upgradeData, world, serverLevel);
|
||||
}
|
||||
|
||||
// compatibility with spigot
|
||||
public FastProtoChunk(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor) {
|
||||
super(pos, upgradeData, levelHeightAccessor);
|
||||
}
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
17
worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts
Normale Datei
17
worldedit-bukkit/adapters/adapter-1_18_2/build.gradle.kts
Normale Datei
@ -0,0 +1,17 @@
|
||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
applyPaperweightAdapterConfiguration()
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.18.2-R0.1-20220920.010157-167")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.stats.Stat;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
|
||||
class PaperweightFakePlayer extends ServerPlayer {
|
||||
|
||||
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(
|
||||
UUID.nameUUIDFromBytes("worldedit".getBytes()),
|
||||
"[WorldEdit]"
|
||||
);
|
||||
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
|
||||
|
||||
PaperweightFakePlayer(ServerLevel world) {
|
||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 position() {
|
||||
return ORIGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(DamageSource damagesource) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt openMenu(MenuProvider factory) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayClientMessage(Component message, boolean actionBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message, ChatType type, UUID sender) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awardStat(Stat<?> stat, int amount) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awardStat(Stat<?> stat) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvulnerableTo(DamageSource damageSource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openTextEdit(SignBlockEntity sign) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightWorldNativeAccess implements
|
||||
WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
|
||||
private final PaperweightAdapter adapter;
|
||||
private final WeakReference<ServerLevel> world;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> world) {
|
||||
this.adapter = adapter;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
private ServerLevel getWorld() {
|
||||
return Objects.requireNonNull(world.get(), "The reference to the world was lost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
||||
this.sideEffectSet = sideEffectSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelChunk getChunk(int x, int z) {
|
||||
return getWorld().getChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) {
|
||||
int stateId = BlockStateIdAccess.getBlockStateId(state);
|
||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
||||
? Block.stateById(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) {
|
||||
return chunk.getBlockState(position);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState setBlockState(
|
||||
LevelChunk chunk,
|
||||
BlockPos position,
|
||||
net.minecraft.world.level.block.state.BlockState state
|
||||
) {
|
||||
return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
|
||||
net.minecraft.world.level.block.state.BlockState block,
|
||||
BlockPos position
|
||||
) {
|
||||
return Block.updateFromNeighbourShapes(block, getWorld(), position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getPosition(int x, int y, int z) {
|
||||
return new BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLightingForBlock(BlockPos position) {
|
||||
getWorld().getChunkSource().getLightEngine().checkBlock(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyBlockUpdate(
|
||||
LevelChunk chunk,
|
||||
BlockPos position,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
||||
getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(LevelChunk chunk) {
|
||||
return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockChanged(LevelChunk chunk, BlockPos position) {
|
||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
||||
getWorld().getChunkSource().blockChanged(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNeighbors(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
ServerLevel world = getWorld();
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
world.updateNeighborsAt(pos, oldState.getBlock());
|
||||
} else {
|
||||
// When we don't want events, manually run the physics without them.
|
||||
Block block = oldState.getBlock();
|
||||
fireNeighborChanged(pos, world, block, pos.west());
|
||||
fireNeighborChanged(pos, world, block, pos.east());
|
||||
fireNeighborChanged(pos, world, block, pos.below());
|
||||
fireNeighborChanged(pos, world, block, pos.above());
|
||||
fireNeighborChanged(pos, world, block, pos.north());
|
||||
fireNeighborChanged(pos, world, block, pos.south());
|
||||
}
|
||||
if (newState.hasAnalogOutputSignal()) {
|
||||
world.updateNeighbourForOutputSignal(pos, newState.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
||||
world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNeighbors(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
int recursionLimit
|
||||
) {
|
||||
ServerLevel world = getWorld();
|
||||
// a == updateNeighbors
|
||||
// b == updateDiagonalNeighbors
|
||||
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
CraftWorld craftWorld = world.getWorld();
|
||||
BlockPhysicsEvent event = new BlockPhysicsEvent(
|
||||
craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()),
|
||||
CraftBlockData.fromData(newState)
|
||||
);
|
||||
world.getCraftServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
getWorld().onBlockStateChange(pos, oldState, newState);
|
||||
}
|
||||
|
||||
//FAWE start
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
//FAWE end
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.util.ReflectionUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
|
||||
|
||||
public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
|
||||
private final Block block;
|
||||
private final BlockState blockState;
|
||||
private final Material material;
|
||||
private final boolean isTranslucent;
|
||||
private final CraftBlockData craftBlockData;
|
||||
private final org.bukkit.Material craftMaterial;
|
||||
private final int opacity;
|
||||
private final CompoundTag tile;
|
||||
|
||||
public PaperweightBlockMaterial(Block block) {
|
||||
this(block, block.defaultBlockState());
|
||||
}
|
||||
|
||||
public PaperweightBlockMaterial(Block block, BlockState blockState) {
|
||||
this.block = block;
|
||||
this.blockState = blockState;
|
||||
this.material = blockState.getMaterial();
|
||||
this.craftBlockData = CraftBlockData.fromData(blockState);
|
||||
this.craftMaterial = craftBlockData.getMaterial();
|
||||
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block, Refraction.pickName(
|
||||
"properties", "aO"));
|
||||
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
|
||||
Refraction.pickName("canOcclude", "n")
|
||||
);
|
||||
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
|
||||
BlockPos.ZERO,
|
||||
blockState
|
||||
);
|
||||
tile = tileEntity == null
|
||||
? null
|
||||
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public BlockState getState() {
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public CraftBlockData getCraftBlockData() {
|
||||
return craftBlockData;
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAir() {
|
||||
return blockState.isAir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube() {
|
||||
return craftMaterial.isOccluding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return material.isSolidBlocking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSource() {
|
||||
return blockState.isSignalSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLiquid() {
|
||||
return material.isLiquid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid() {
|
||||
return material.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHardness() {
|
||||
return craftBlockData.getState().destroySpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getResistance() {
|
||||
return block.getExplosionResistance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSlipperiness() {
|
||||
return block.getFriction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightValue() {
|
||||
return blockState.getLightEmission();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightOpacity() {
|
||||
return opacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFragileWhenPushed() {
|
||||
return material.getPushReaction() == PushReaction.DESTROY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnpushable() {
|
||||
return material.getPushReaction() == PushReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTicksRandomly() {
|
||||
return block.isRandomlyTicking(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMovementBlocker() {
|
||||
return material.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBurnable() {
|
||||
return material.isFlammable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToolRequired() {
|
||||
// Removed in 1.16.1, this is not present in higher versions
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplacedDuringPlacement() {
|
||||
return material.isReplaceable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTranslucent() {
|
||||
return isTranslucent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContainer() {
|
||||
return block instanceof EntityBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTile() {
|
||||
return block instanceof EntityBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getDefaultTile() {
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMapColor() {
|
||||
// rgb field
|
||||
return material.getColor().col;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,700 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_18_R2.PaperweightAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.regen.PaperweightRegen;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.registry.state.BooleanProperty;
|
||||
import com.sk89q.worldedit.registry.state.DirectionalProperty;
|
||||
import com.sk89q.worldedit.registry.state.EnumProperty;
|
||||
import com.sk89q.worldedit.registry.state.IntegerProperty;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.TreeGenerator;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.util.nbt.StringBinaryTag;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.WritableRegistry;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.TreeType;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private final PaperweightAdapter parent;
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that may break between versions of Minecraft
|
||||
// ------------------------------------------------------------------------
|
||||
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
|
||||
private char[] ibdToStateOrdinal = null;
|
||||
private int[] ordinalToIbdID = null;
|
||||
private boolean initialised = false;
|
||||
private Map<String, List<Property<?>>> allBlockProperties = null;
|
||||
|
||||
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
|
||||
this.parent = new PaperweightAdapter();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getEntityId(Entity entity) {
|
||||
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
|
||||
return resourceLocation == null ? null : resourceLocation.toString();
|
||||
}
|
||||
|
||||
private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
entity.save(compoundTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitImplAdapter<net.minecraft.nbt.Tag> getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
private synchronized boolean init() {
|
||||
if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) {
|
||||
return false;
|
||||
}
|
||||
ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size
|
||||
ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size
|
||||
for (int i = 0; i < ibdToStateOrdinal.length; i++) {
|
||||
BlockState blockState = BlockTypesCache.states[i];
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial();
|
||||
int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState());
|
||||
char ordinal = blockState.getOrdinalChar();
|
||||
ibdToStateOrdinal[id] = ordinal;
|
||||
ordinalToIbdID[ordinal] = id;
|
||||
}
|
||||
Map<String, List<Property<?>>> properties = new HashMap<>();
|
||||
try {
|
||||
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
|
||||
Object obj = field.get(null);
|
||||
if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property<?> state)) {
|
||||
continue;
|
||||
}
|
||||
Property<?> property;
|
||||
if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) {
|
||||
property = new BooleanProperty(
|
||||
state.getName(),
|
||||
(List<Boolean>) ImmutableList.copyOf(state.getPossibleValues())
|
||||
);
|
||||
} else if (state instanceof DirectionProperty) {
|
||||
property = new DirectionalProperty(
|
||||
state.getName(),
|
||||
state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase()))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
|
||||
property = new EnumProperty(
|
||||
state.getName(),
|
||||
state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> ((StringRepresentable) e).getSerializedName())
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) {
|
||||
property = new IntegerProperty(
|
||||
state.getName(),
|
||||
(List<Integer>) ImmutableList.copyOf(state.getPossibleValues())
|
||||
);
|
||||
} else {
|
||||
throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state
|
||||
.getClass()
|
||||
.getSimpleName());
|
||||
}
|
||||
properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> {
|
||||
if (v == null) {
|
||||
v = new ArrayList<>(Collections.singletonList(property));
|
||||
} else {
|
||||
v.add(property);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
allBlockProperties = ImmutableMap.copyOf(properties);
|
||||
}
|
||||
initialised = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BlockType blockType) {
|
||||
Block block = getBlock(blockType);
|
||||
return new PaperweightBlockMaterial(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized BlockMaterial getMaterial(BlockState state) {
|
||||
net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
||||
return new PaperweightBlockMaterial(blockState.getBlock(), blockState);
|
||||
}
|
||||
|
||||
public Block getBlock(BlockType blockType) {
|
||||
return Registry.BLOCK.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public BlockState getBlock(Location location) {
|
||||
Preconditions.checkNotNull(location);
|
||||
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
final ServerLevel handle = craftWorld.getHandle();
|
||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||
BlockState state = adapt(blockData);
|
||||
if (state == null) {
|
||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(final Location location) {
|
||||
Preconditions.checkNotNull(location);
|
||||
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
|
||||
final ServerLevel handle = craftWorld.getHandle();
|
||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||
BlockState state = adapt(blockData);
|
||||
if (state == null) {
|
||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
||||
}
|
||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
||||
|
||||
// Read the NBT data
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
|
||||
if (blockEntity != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
|
||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SideEffect> getSupportedSideEffects() {
|
||||
return SideEffectSet.defaults().getSideEffectsToApply();
|
||||
}
|
||||
|
||||
public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) {
|
||||
CraftChunk craftChunk = (CraftChunk) chunk;
|
||||
LevelChunk levelChunk = craftChunk.getHandle();
|
||||
Level level = levelChunk.getLevel();
|
||||
|
||||
BlockPos blockPos = new BlockPos(x, y, z);
|
||||
net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState();
|
||||
LevelChunkSection[] levelChunkSections = levelChunk.getSections();
|
||||
int y4 = y >> 4;
|
||||
LevelChunkSection section = levelChunkSections[y4];
|
||||
|
||||
net.minecraft.world.level.block.state.BlockState existing;
|
||||
if (section == null) {
|
||||
existing = ((PaperweightBlockMaterial) BlockTypes.AIR.getDefaultState().getMaterial()).getState();
|
||||
} else {
|
||||
existing = section.getBlockState(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
levelChunk.removeBlockEntity(blockPos); // Force delete the old tile entity
|
||||
|
||||
CompoundBinaryTag compoundTag = state instanceof BaseBlock ? state.getNbt() : null;
|
||||
if (compoundTag != null || existing instanceof TileEntityBlock) {
|
||||
level.setBlock(blockPos, blockState, 0);
|
||||
// remove tile
|
||||
if (compoundTag != null) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the Forge version
|
||||
BlockEntity blockEntity = level.getBlockEntity(blockPos);
|
||||
if (blockEntity != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(compoundTag);
|
||||
tag.put("x", IntTag.valueOf(x));
|
||||
tag.put("y", IntTag.valueOf(y));
|
||||
tag.put("z", IntTag.valueOf(z));
|
||||
blockEntity.load(tag); // readTagIntoTileEntity - load data
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (existing == blockState) {
|
||||
return true;
|
||||
}
|
||||
levelChunk.setBlockState(blockPos, blockState, false);
|
||||
}
|
||||
if (update) {
|
||||
level.getMinecraftWorld().sendBlockUpdated(blockPos, existing, blockState, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||
return new PaperweightFaweWorldNativeAccess(
|
||||
this,
|
||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
||||
Preconditions.checkNotNull(entity);
|
||||
|
||||
CraftEntity craftEntity = ((CraftEntity) entity);
|
||||
Entity mcEntity = craftEntity.getHandle();
|
||||
|
||||
String id = getEntityId(mcEntity);
|
||||
|
||||
if (id != null) {
|
||||
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
|
||||
Supplier<CompoundBinaryTag> saveTag = () -> {
|
||||
final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag();
|
||||
readEntityIntoTag(mcEntity, minecraftTag);
|
||||
//add Id for AbstractChangeSet to work
|
||||
final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag);
|
||||
final Map<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
|
||||
tags.put("Id", StringBinaryTag.of(id));
|
||||
return CompoundBinaryTag.from(tags);
|
||||
};
|
||||
return new LazyBaseEntity(type, saveTag);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichBlockName(BlockType blockType) {
|
||||
return parent.getRichBlockName(blockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichItemName(ItemType itemType) {
|
||||
return parent.getRichItemName(itemType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichItemName(BaseItemStack itemStack) {
|
||||
return parent.getRichItemName(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt getInternalBlockStateId(BlockState state) {
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
|
||||
net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState();
|
||||
return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState adapt(BlockData blockData) {
|
||||
CraftBlockData cbd = ((CraftBlockData) blockData);
|
||||
net.minecraft.world.level.block.state.BlockState ibd = cbd.getState();
|
||||
return adapt(ibd);
|
||||
}
|
||||
|
||||
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
|
||||
return BlockTypesCache.states[adaptToChar(blockState)];
|
||||
}
|
||||
|
||||
public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) {
|
||||
int id = Block.BLOCK_STATE_REGISTRY.getId(blockState);
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
try {
|
||||
init();
|
||||
return ibdToStateOrdinal[id];
|
||||
} catch (ArrayIndexOutOfBoundsException e1) {
|
||||
LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!",
|
||||
blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1
|
||||
);
|
||||
return BlockTypesCache.ReservedIDs.AIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public char ibdIDToOrdinal(int id) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
init();
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getIbdToStateOrdinal() {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
init();
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
}
|
||||
|
||||
public int ordinalToIbdID(char ordinal) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
init();
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getOrdinalToIbdID() {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
init();
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> BlockData adapt(B state) {
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
|
||||
return material.getCraftBlockData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||
if (map != null && map.wasAccessibleSinceLastSave()) {
|
||||
// PlayerChunk.d players = map.players;
|
||||
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
|
||||
*/ Stream.empty();
|
||||
|
||||
ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
|
||||
stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
|
||||
.forEach(entityPlayer -> {
|
||||
synchronized (chunkPacket) {
|
||||
ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket();
|
||||
if (nmsPacket == null) {
|
||||
nmsPacket = mapUtil.create(this, chunkPacket);
|
||||
chunkPacket.setNativePacket(nmsPacket);
|
||||
}
|
||||
try {
|
||||
FaweCache.INSTANCE.CHUNK_FLAG.get().set(true);
|
||||
entityPlayer.connection.send(nmsPacket);
|
||||
} finally {
|
||||
FaweCache.INSTANCE.CHUNK_FLAG.get().set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Property<?>> getProperties(BlockType blockType) {
|
||||
return getParent().getProperties(blockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) {
|
||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
||||
return blockState1.hasPostProcess(
|
||||
((CraftWorld) world).getHandle(),
|
||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
|
||||
ItemStack stack = new ItemStack(
|
||||
Registry.ITEM.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
||||
baseItemStack.getAmount()
|
||||
);
|
||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
|
||||
return CraftItemStack.asCraftMirror(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateTree(
|
||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
||||
org.bukkit.World bukkitWorld
|
||||
) {
|
||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
||||
blockVector3 = blockVector3.add(
|
||||
0,
|
||||
1,
|
||||
0
|
||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
||||
}
|
||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
||||
final BlockVector3 finalBlockVector = blockVector3;
|
||||
// Sync to main thread to ensure no clashes occur
|
||||
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
|
||||
serverLevel.captureTreeGeneration = true;
|
||||
serverLevel.captureBlockStates = true;
|
||||
try {
|
||||
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
|
||||
return null;
|
||||
}
|
||||
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
|
||||
} finally {
|
||||
serverLevel.captureBlockStates = false;
|
||||
serverLevel.captureTreeGeneration = false;
|
||||
serverLevel.capturedBlockStates.clear();
|
||||
}
|
||||
});
|
||||
if (placed == null || placed.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (CraftBlockState craftBlockState : placed.values()) {
|
||||
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
|
||||
continue;
|
||||
}
|
||||
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
|
||||
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<org.bukkit.entity.Entity> getEntities(org.bukkit.World world) {
|
||||
// Quickly add each entity to a list copy.
|
||||
List<Entity> mcEntities = new ArrayList<>();
|
||||
((CraftWorld) world).getHandle().entityManager.getEntityGetter().getAll().forEach(mcEntities::add);
|
||||
|
||||
List<org.bukkit.entity.Entity> list = new ArrayList<>();
|
||||
mcEntities.forEach((mcEnt) -> {
|
||||
org.bukkit.entity.Entity bukkitEntity = mcEnt.getBukkitEntity();
|
||||
if (bukkitEntity.isValid()) {
|
||||
list.add(bukkitEntity);
|
||||
}
|
||||
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
||||
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
||||
final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount());
|
||||
weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag())));
|
||||
return weStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toNative(net.minecraft.nbt.Tag foreign) {
|
||||
return parent.toNative(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BinaryTag toNativeBinary(final net.minecraft.nbt.Tag foreign) {
|
||||
return parent.toNativeBinary(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.nbt.Tag fromNative(Tag foreign) {
|
||||
if (foreign instanceof PaperweightLazyCompoundTag) {
|
||||
return ((PaperweightLazyCompoundTag) foreign).get();
|
||||
}
|
||||
return (net.minecraft.nbt.Tag) parent.fromNative(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
||||
return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) {
|
||||
return new PaperweightGetBlocks(world, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInternalBiomeId(BiomeType biomeType) {
|
||||
final Registry<Biome> registry = MinecraftServer
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
|
||||
Biome biome = registry.get(resourceLocation);
|
||||
return registry.getId(biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<NamespacedKey> getRegisteredBiomes() {
|
||||
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) ((CraftServer) Bukkit.getServer())
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(
|
||||
Registry.BIOME_REGISTRY);
|
||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||
for (ResourceLocation key : keys) {
|
||||
try {
|
||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||
}
|
||||
}
|
||||
return namespacedKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelighterFactory getRelighterFactory() {
|
||||
if (PaperLib.isPaper()) {
|
||||
return new PaperweightStarlightRelighterFactory();
|
||||
} else {
|
||||
return new NMSRelighterFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Property<?>>> getAllProperties() {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
init();
|
||||
return allBlockProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getTickingPostProcessor() {
|
||||
return new PaperweightPostProcessor();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,286 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<LevelChunk,
|
||||
net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
private static final Direction[] NEIGHBOUR_ORDER = {
|
||||
Direction.EAST,
|
||||
Direction.WEST,
|
||||
Direction.DOWN,
|
||||
Direction.UP,
|
||||
Direction.NORTH,
|
||||
Direction.SOUTH
|
||||
};
|
||||
private final PaperweightFaweAdapter paperweightFaweAdapter;
|
||||
private final WeakReference<Level> level;
|
||||
private final AtomicInteger lastTick;
|
||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
|
||||
this.paperweightFaweAdapter = paperweightFaweAdapter;
|
||||
this.level = level;
|
||||
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
|
||||
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
|
||||
this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
|
||||
}
|
||||
|
||||
private Level getLevel() {
|
||||
return Objects.requireNonNull(level.get(), "The reference to the world was lost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
||||
this.sideEffectSet = sideEffectSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelChunk getChunk(int x, int z) {
|
||||
return getLevel().getChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) {
|
||||
int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar());
|
||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
||||
? Block.stateById(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) {
|
||||
return levelChunk.getBlockState(blockPos);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public synchronized net.minecraft.world.level.block.state.BlockState setBlockState(
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (Fawe.isMainThread()) {
|
||||
return levelChunk.setBlockState(blockPos, blockState,
|
||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
);
|
||||
}
|
||||
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
|
||||
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
|
||||
cachedChunksToSend.add(new IntPair(levelChunk.bukkitChunk.getX(), levelChunk.bukkitChunk.getZ()));
|
||||
boolean nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick || cachedChanges.size() >= 1024) {
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
flushAsync(nextTick);
|
||||
}
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
|
||||
net.minecraft.world.level.block.state.BlockState blockState,
|
||||
BlockPos blockPos
|
||||
) {
|
||||
return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getPosition(int x, int y, int z) {
|
||||
return new BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLightingForBlock(BlockPos blockPos) {
|
||||
getLevel().getChunkSource().getLightEngine().checkBlock(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the other versions
|
||||
BlockEntity blockEntity = getLevel().getBlockEntity(blockPos);
|
||||
if (blockEntity == null) {
|
||||
return false;
|
||||
}
|
||||
net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeBinary(tag);
|
||||
blockEntity.load((CompoundTag) nativeTag);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyBlockUpdate(
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
|
||||
getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(LevelChunk levelChunk) {
|
||||
return levelChunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) {
|
||||
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
|
||||
((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNeighbors(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
Level level = getLevel();
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
level.blockUpdated(blockPos, oldState.getBlock());
|
||||
} else {
|
||||
// When we don't want events, manually run the physics without them.
|
||||
// Un-nest neighbour updating
|
||||
for (Direction direction : NEIGHBOUR_ORDER) {
|
||||
BlockPos shifted = blockPos.relative(direction);
|
||||
level.getBlockState(shifted).neighborChanged(level, shifted, oldState.getBlock(), blockPos, false);
|
||||
}
|
||||
}
|
||||
if (newState.hasAnalogOutputSignal()) {
|
||||
level.updateNeighbourForOutputSignal(blockPos, newState.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNeighbors(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
int recursionLimit
|
||||
) {
|
||||
Level level = getLevel();
|
||||
// a == updateNeighbors
|
||||
// b == updateDiagonalNeighbors
|
||||
oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
CraftWorld craftWorld = level.getWorld();
|
||||
if (craftWorld != null) {
|
||||
BlockPhysicsEvent event = new BlockPhysicsEvent(
|
||||
craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
|
||||
CraftBlockData.fromData(newState)
|
||||
);
|
||||
level.getCraftServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit);
|
||||
newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
getLevel().onBlockStateChange(blockPos, oldState, newState);
|
||||
}
|
||||
|
||||
private synchronized void flushAsync(final boolean sendChunks) {
|
||||
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
|
||||
cachedChanges.clear();
|
||||
final Set<IntPair> toSend;
|
||||
if (sendChunks) {
|
||||
toSend = Set.copyOf(cachedChunksToSend);
|
||||
cachedChunksToSend.clear();
|
||||
} else {
|
||||
toSend = Collections.emptySet();
|
||||
}
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
if (!sendChunks) {
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
TaskManager.taskManager().sync(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
}
|
||||
|
||||
private record CachedChange(
|
||||
LevelChunk levelChunk,
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -0,0 +1,236 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.queue.IBlocks;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private PalettedContainer<Holder<Biome>>[] biomes = null;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
this.serverLevel = levelChunk.level;
|
||||
this.minHeight = serverLevel.getMinBuildHeight();
|
||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
this.blocks = new char[getSectionCount()][];
|
||||
}
|
||||
|
||||
protected void storeTile(BlockEntity blockEntity) {
|
||||
tiles.put(
|
||||
BlockVector3.at(
|
||||
blockEntity.getBlockPos().getX(),
|
||||
blockEntity.getBlockPos().getY(),
|
||||
blockEntity.getBlockPos().getZ()
|
||||
),
|
||||
new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
entities.add((CompoundTag) adapter.toNative(compoundTag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
for (CompoundTag tag : entities) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCreateCopy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeightmapToGet(HeightMapType type, int[] data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxY() {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return minHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSectionPosition() {
|
||||
return maxHeight >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinSectionPosition() {
|
||||
return minHeight >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()].get(x >> 2, (y & 15) >> 2, z >> 2);
|
||||
return PaperweightPlatformAdapter.adapt(biome, serverLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSectionLighting(int layer, boolean sky) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive, int layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlocks reset() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionCount() {
|
||||
return serverLevel.getSectionsCount();
|
||||
}
|
||||
|
||||
protected void storeSection(int layer, char[] data) {
|
||||
blocks[layer] = data;
|
||||
}
|
||||
|
||||
protected void storeBiomes(int layer, PalettedContainer<Holder<Biome>> biomeData) {
|
||||
if (biomes == null) {
|
||||
biomes = new PalettedContainer[getSectionCount()];
|
||||
}
|
||||
biomes[layer] = biomeData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
BlockState state = BlockTypesCache.states[get(x, y, z)];
|
||||
return state.toBaseBlock(this, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer] != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] loadIfPresent(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmittedLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getHeightMap(HeightMapType type) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public char get(int x, int y, int z) {
|
||||
final int layer = (y >> 4) - getMinSectionPosition();
|
||||
final int index = (y & 15) << 8 | z << 4 | x;
|
||||
return blocks[layer][index];
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
|
||||
//TODO un-very-break-this
|
||||
public class PaperweightMapChunkUtil extends MapChunkUtil<ClientboundLevelChunkWithLightPacket> {
|
||||
|
||||
public PaperweightMapChunkUtil() throws NoSuchFieldException {
|
||||
fieldX = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a"));
|
||||
fieldZ = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("x", "a"));
|
||||
fieldBitMask = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("z", "b"));
|
||||
fieldHeightMap = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("heightmaps", "b"));
|
||||
fieldChunkData = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("chunkData", "c"));
|
||||
fieldBlockEntities = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("buffer", "c"));
|
||||
fieldFull = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("blockEntitiesData", "d"));
|
||||
fieldX.setAccessible(true);
|
||||
fieldZ.setAccessible(true);
|
||||
fieldBitMask.setAccessible(true);
|
||||
fieldHeightMap.setAccessible(true);
|
||||
fieldChunkData.setAccessible(true);
|
||||
fieldBlockEntities.setAccessible(true);
|
||||
fieldFull.setAccessible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientboundLevelChunkWithLightPacket createPacket() {
|
||||
// TODO ??? return new ClientboundLevelChunkPacket();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,663 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.SimpleBitStorage;
|
||||
import net.minecraft.util.ThreadingDetector;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.util.ZeroBitStorage;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.GlobalPalette;
|
||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.LinearPalette;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
public static final Field fieldData;
|
||||
|
||||
public static final Constructor<?> dataConstructor;
|
||||
|
||||
public static final Field fieldStorage;
|
||||
public static final Field fieldPalette;
|
||||
|
||||
private static final Field fieldTickingFluidCount;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldThreadingDetector;
|
||||
private static final long fieldThreadingDetectorOffset;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final MethodHandle methodRemoveGameEventListener;
|
||||
private static final MethodHandle methodremoveTickingBlockEntity;
|
||||
|
||||
private static final Field fieldRemove;
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
||||
fieldData.setAccessible(true);
|
||||
|
||||
Class<?> dataClazz = fieldData.getType();
|
||||
dataConstructor = dataClazz.getDeclaredConstructors()[0];
|
||||
dataConstructor.setAccessible(true);
|
||||
|
||||
fieldStorage = dataClazz.getDeclaredField(Refraction.pickName("storage", "b"));
|
||||
fieldStorage.setAccessible(true);
|
||||
fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c"));
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "h"));
|
||||
fieldTickingFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g"));
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f"));
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
||||
"getVisibleChunkIfPresent",
|
||||
"b"
|
||||
), long.class);
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
|
||||
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector);
|
||||
|
||||
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldThreadingDetector = null;
|
||||
fieldThreadingDetectorOffset = -1;
|
||||
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
|
||||
Refraction.pickName("removeGameEventListener", "d"),
|
||||
BlockEntity.class
|
||||
);
|
||||
removeGameEventListener.setAccessible(true);
|
||||
methodRemoveGameEventListener = MethodHandles.lookup().unreflect(removeGameEventListener);
|
||||
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
|
||||
Refraction.pickName(
|
||||
"removeBlockEntityTicker",
|
||||
"l"
|
||||
), BlockPos.class
|
||||
);
|
||||
removeBlockEntityTicker.setAccessible(true);
|
||||
methodremoveTickingBlockEntity = MethodHandles.lookup().unreflect(removeBlockEntityTicker);
|
||||
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable rethrow) {
|
||||
rethrow.printStackTrace();
|
||||
throw new RuntimeException(rethrow);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean setSectionAtomic(
|
||||
LevelChunkSection[] sections,
|
||||
LevelChunkSection expected,
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// There is no point in having a functional semaphore for paper servers.
|
||||
private static final ThreadLocal<DelegateSemaphore> SEMAPHORE_THREAD_LOCAL =
|
||||
ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null));
|
||||
|
||||
static DelegateSemaphore applyLock(LevelChunkSection section) {
|
||||
if (PaperLib.isPaper()) {
|
||||
return SEMAPHORE_THREAD_LOCAL.get();
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject(
|
||||
blocks,
|
||||
fieldThreadingDetectorOffset
|
||||
);
|
||||
synchronized (currentThreadingDetector) {
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
||||
if (nmsChunk != null) {
|
||||
return nmsChunk;
|
||||
}
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
} else {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
// Avoid "async" methods from the main thread.
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||
try {
|
||||
CraftChunk chunk;
|
||||
try {
|
||||
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
String world = serverLevel.getWorld().getName();
|
||||
// We've already taken 10 seconds we can afford to wait a little here.
|
||||
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||
if (loaded) {
|
||||
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||
// Retry chunk load
|
||||
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
||||
}
|
||||
}
|
||||
return chunk.getHandle();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
// Ensure chunk is definitely loaded before applying a ticket
|
||||
net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||
.getChunkSource()
|
||||
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||
}
|
||||
|
||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||
try {
|
||||
return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ));
|
||||
} catch (Throwable thr) {
|
||||
throw new RuntimeException(thr);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
||||
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||
if (chunkHolder == null) {
|
||||
return;
|
||||
}
|
||||
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
|
||||
// UNLOADED_CHUNK
|
||||
Optional<LevelChunk> optional = ((Either) chunkHolder
|
||||
.getTickingChunkFuture()
|
||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
|
||||
if (PaperLib.isPaper()) {
|
||||
// getChunkAtIfLoadedImmediately is paper only
|
||||
optional = optional.or(() -> Optional.ofNullable(nmsWorld
|
||||
.getChunkSource()
|
||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ)));
|
||||
}
|
||||
if (optional.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LevelChunk levelChunk = optional.get();
|
||||
TaskManager.taskManager().task(() -> {
|
||||
ClientboundLevelChunkWithLightPacket packet;
|
||||
if (PaperLib.isPaper()) {
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
false // last false is to not bother with x-ray
|
||||
);
|
||||
} else {
|
||||
// deprecated on paper - deprecation suppressed
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
});
|
||||
}
|
||||
|
||||
private static List<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
|
||||
return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false);
|
||||
}
|
||||
|
||||
/*
|
||||
NMS conversion
|
||||
*/
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final char[] blocks,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes);
|
||||
}
|
||||
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer, biomeRegistry, biomes);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
|
||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int num_palette;
|
||||
if (get == null) {
|
||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, null);
|
||||
} else {
|
||||
num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter, null);
|
||||
}
|
||||
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (bitsPerEntry > 0 && bitsPerEntry < 5) {
|
||||
bitsPerEntry = 4;
|
||||
} else if (bitsPerEntry > 8) {
|
||||
bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1);
|
||||
}
|
||||
|
||||
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
|
||||
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
|
||||
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) {
|
||||
blockStates[i] = 0;
|
||||
}
|
||||
} else {
|
||||
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntryNonZero, 4096, blockStates);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
|
||||
final BitStorage nmsBits;
|
||||
if (bitsPerEntry == 0) {
|
||||
nmsBits = new ZeroBitStorage(4096);
|
||||
} else {
|
||||
nmsBits = new SimpleBitStorage(bitsPerEntry, 4096, bits);
|
||||
}
|
||||
List<net.minecraft.world.level.block.state.BlockState> palette;
|
||||
if (bitsPerEntry < 9) {
|
||||
palette = new ArrayList<>();
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
final BlockState state = BlockTypesCache.states[ordinal];
|
||||
palette.add(((PaperweightBlockMaterial) state.getMaterial()).getState());
|
||||
}
|
||||
} else {
|
||||
palette = List.of();
|
||||
}
|
||||
|
||||
// Create palette with data
|
||||
@SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot
|
||||
final PalettedContainer<net.minecraft.world.level.block.state.BlockState> blockStatePalettedContainer =
|
||||
new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry),
|
||||
nmsBits,
|
||||
palette
|
||||
);
|
||||
if (biomes == null) {
|
||||
IdMap<Holder<Biome>> biomeHolderIdMap = biomeRegistry.asHolderIdMap();
|
||||
biomes = new PalettedContainer<>(
|
||||
biomeHolderIdMap,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(
|
||||
BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
return new LevelChunkSection(layer, blockStatePalettedContainer, biomes);
|
||||
} catch (final Throwable e) {
|
||||
throw e;
|
||||
} finally {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
Arrays.fill(paletteToBlock, Integer.MAX_VALUE);
|
||||
Arrays.fill(blockStates, 0);
|
||||
Arrays.fill(blocksCopy, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Only deprecated in paper
|
||||
private static LevelChunkSection newChunkSection(
|
||||
int layer,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return new LevelChunkSection(layer, biomeRegistry);
|
||||
}
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
Blocks.AIR.defaultBlockState(),
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
null
|
||||
);
|
||||
return new LevelChunkSection(layer, dataPaletteBlocks, biomes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PalettedContainer<Biome>}. Should only be used if no biome container existed beforehand.
|
||||
*/
|
||||
public static PalettedContainer<Holder<Biome>> getBiomePalettedContainer(
|
||||
BiomeType[] biomes,
|
||||
IdMap<Holder<Biome>> biomeRegistry
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return null;
|
||||
}
|
||||
BukkitImplAdapter<?> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
// Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length
|
||||
Map<BiomeType, Holder<Biome>> palette = new HashMap<>();
|
||||
for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) {
|
||||
Holder<Biome> biome;
|
||||
if (biomeType == null) {
|
||||
biome = biomeRegistry.byId(adapter.getInternalBiomeId(BiomeTypes.PLAINS));
|
||||
} else {
|
||||
biome = biomeRegistry.byId(adapter.getInternalBiomeId(biomeType));
|
||||
}
|
||||
palette.put(biomeType, biome);
|
||||
}
|
||||
int biomeCount = palette.size();
|
||||
int bitsPerEntry = MathMan.log2nlz(biomeCount - 1);
|
||||
Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration(
|
||||
new FakeIdMapBiome(biomeCount),
|
||||
bitsPerEntry
|
||||
);
|
||||
if (bitsPerEntry > 3) {
|
||||
bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1);
|
||||
}
|
||||
PalettedContainer<Holder<Biome>> biomePalettedContainer = new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
|
||||
final Palette<Holder<Biome>> biomePalette;
|
||||
if (bitsPerEntry == 0) {
|
||||
biomePalette = new SingleValuePalette<>(
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else if (bitsPerEntry == 4) {
|
||||
biomePalette = LinearPalette.create(
|
||||
4,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else if (bitsPerEntry < 9) {
|
||||
biomePalette = HashMapPalette.create(
|
||||
bitsPerEntry,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else {
|
||||
biomePalette = GlobalPalette.create(
|
||||
bitsPerEntry,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
null // unused
|
||||
);
|
||||
}
|
||||
|
||||
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
|
||||
final int arrayLength = MathMan.ceilZero(64f / blocksPerLong);
|
||||
|
||||
|
||||
BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage(
|
||||
bitsPerEntry,
|
||||
64,
|
||||
new long[arrayLength]
|
||||
);
|
||||
|
||||
try {
|
||||
Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette);
|
||||
fieldData.set(biomePalettedContainer, data);
|
||||
int index = 0;
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int z = 0; z < 4; z++) {
|
||||
for (int x = 0; x < 4; x++, index++) {
|
||||
BiomeType biomeType = biomes[index];
|
||||
if (biomeType == null) {
|
||||
continue;
|
||||
}
|
||||
Holder<Biome> biome = biomeRegistry.byId(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(biomeType));
|
||||
if (biome == null) {
|
||||
continue;
|
||||
}
|
||||
biomePalettedContainer.set(x, y, z, biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return biomePalettedContainer;
|
||||
}
|
||||
|
||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
||||
fieldTickingFluidCount.setShort(section, (short) 0);
|
||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
||||
}
|
||||
|
||||
public static BiomeType adapt(Holder<Biome> biome, LevelAccessor levelAccessor) {
|
||||
final Registry<Biome> biomeRegistry = levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
||||
if (biomeRegistry.getKey(biome.value()) == null) {
|
||||
return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN
|
||||
: null;
|
||||
}
|
||||
return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
||||
try {
|
||||
// Do the method ourselves to avoid trying to reflect generic method parameters
|
||||
// similar to removeGameEventListener
|
||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
||||
if (blockEntity != null) {
|
||||
if (!levelChunk.level.isClientSide) {
|
||||
methodRemoveGameEventListener.invoke(levelChunk, beacon);
|
||||
}
|
||||
fieldRemove.set(beacon, true);
|
||||
}
|
||||
}
|
||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static List<Entity> getEntities(LevelChunk chunk) {
|
||||
return chunk.level.entityManager.getEntities(chunk.getPos());
|
||||
}
|
||||
|
||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||
|
||||
@Override
|
||||
public int getId(final net.minecraft.world.level.block.state.BlockState entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState byId(final int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<net.minecraft.world.level.block.state.BlockState> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record FakeIdMapBiome(int size) implements IdMap<Biome> {
|
||||
|
||||
@Override
|
||||
public int getId(final Biome entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Biome byId(final int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<Biome> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
// The PostProcessor shouldn't be added, but just in case
|
||||
if (!tickFluid) {
|
||||
return;
|
||||
}
|
||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
||||
layer:
|
||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
||||
char[] set = iChunkSet.loadIfPresent(layer);
|
||||
if (set == null) {
|
||||
// No edit means no need to process
|
||||
continue;
|
||||
}
|
||||
char[] get = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean fromGet = false; // Used for liquids
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
||||
// actually being set
|
||||
if (get == null) {
|
||||
continue layer;
|
||||
}
|
||||
fromGet = true;
|
||||
ordinal = replacedOrdinal = get[i];
|
||||
}
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
continue;
|
||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
replacedOrdinal = get[i];
|
||||
}
|
||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
||||
boolean replacedWasLiquid = false;
|
||||
BlockState replacedState = null;
|
||||
if (!ticking) {
|
||||
// If the block being replaced was not ticking, it cannot be a liquid
|
||||
if (!replacedWasTicking) {
|
||||
continue;
|
||||
}
|
||||
// If the block being replaced is not fluid, we do not need to worry
|
||||
if (!(replacedWasLiquid =
|
||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
boolean liquid = state.getMaterial().isLiquid();
|
||||
int x = i & 15;
|
||||
int y = (i >> 8) & 15;
|
||||
int z = (i >> 4) & 15;
|
||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
||||
if (liquid || replacedWasLiquid) {
|
||||
if (liquid) {
|
||||
addFluid(getBlocks.serverLevel, state, position);
|
||||
continue;
|
||||
}
|
||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
||||
// being ticked anyway. We only need it to be "hit" once.
|
||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
||||
if (set == null || get == null) {
|
||||
return false;
|
||||
}
|
||||
char ordinal;
|
||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
if (x > 0 && set[i - 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (x < 15 && set[i + 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z > 0 && set[i - 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z < 15 && set[i + 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y > 0 && set[i - 256] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y < 15 && set[i + 256] != reserved) {
|
||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isFluid(char ordinal) {
|
||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
||||
Fluid type;
|
||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
||||
} else {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
||||
}
|
||||
serverLevel.scheduleTick(
|
||||
position,
|
||||
type,
|
||||
type.getTickDelay(serverLevel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class PaperweightStarlightRelighter implements Relighter {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
|
||||
private static final int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
|
||||
|
||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
|
||||
|
||||
|
||||
private final ServerLevel serverLevel;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
||||
this.serverLevel = serverLevel;
|
||||
this.delegate = new NMSRelighter(queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
||||
areaLock.lock();
|
||||
try {
|
||||
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
|
||||
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
|
||||
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
|
||||
chunks.add(ChunkPos.asLong(cx, cz));
|
||||
} finally {
|
||||
areaLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLightUpdate(int x, int y, int z) {
|
||||
delegate.addLightUpdate(x, y, z);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called "recursively", iterating and removing elements
|
||||
* from the regions linked map. This way, chunks are loaded in batches to avoid
|
||||
* OOMEs.
|
||||
*/
|
||||
@Override
|
||||
public void fixLightingSafe(boolean sky) {
|
||||
this.areaLock.lock();
|
||||
try {
|
||||
if (regions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LongSet first = regions.removeFirst();
|
||||
fixLighting(first, () -> fixLightingSafe(true));
|
||||
} finally {
|
||||
this.areaLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes a set of chunks and runs an action afterwards.
|
||||
* The action is run async, the chunks are partly processed on the main thread
|
||||
* (as required by the server).
|
||||
*/
|
||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
// convert from long keys to ChunkPos
|
||||
Set<ChunkPos> coords = new HashSet<>();
|
||||
LongIterator iterator = chunks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
coords.add(new ChunkPos(iterator.nextLong()));
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (ChunkPos pos : coords) {
|
||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
pos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
))
|
||||
);
|
||||
}
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
|
||||
invokeRelight(
|
||||
coords,
|
||||
c -> {
|
||||
}, // no callback for single chunks required
|
||||
i -> {
|
||||
if (i != coords.size()) {
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred on relighting", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLighting() {
|
||||
this.delegate.removeLighting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixBlockLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixSkyLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReentrantLock getLock() {
|
||||
return this.lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||
|
||||
@Override
|
||||
public @Nonnull
|
||||
@SuppressWarnings("rawtypes")
|
||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||
if (w == null) {
|
||||
return NullRelighter.INSTANCE;
|
||||
}
|
||||
return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.nbt;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.LazyCompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
|
||||
private final Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier;
|
||||
private CompoundTag compoundTag;
|
||||
|
||||
public PaperweightLazyCompoundTag(Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier) {
|
||||
super(new HashMap<>());
|
||||
this.compoundTagSupplier = compoundTagSupplier;
|
||||
}
|
||||
|
||||
public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
this(() -> compoundTag);
|
||||
}
|
||||
|
||||
public net.minecraft.nbt.CompoundTag get() {
|
||||
return compoundTagSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Tag> getValue() {
|
||||
if (compoundTag == null) {
|
||||
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
|
||||
}
|
||||
return compoundTag.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundBinaryTag asBinaryTag() {
|
||||
getValue();
|
||||
return compoundTag.asBinaryTag();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return compoundTagSupplier.get().contains(key);
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
return compoundTagSupplier.get().getByteArray(key);
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
return compoundTagSupplier.get().getByte(key);
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
return compoundTagSupplier.get().getDouble(key);
|
||||
}
|
||||
|
||||
public double asDouble(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsDouble();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
return compoundTagSupplier.get().getFloat(key);
|
||||
}
|
||||
|
||||
public int[] getIntArray(String key) {
|
||||
return compoundTagSupplier.get().getIntArray(key);
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
return compoundTagSupplier.get().getInt(key);
|
||||
}
|
||||
|
||||
public int asInt(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsInt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> getList(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
|
||||
ArrayList<Tag> list = new ArrayList<>();
|
||||
for (net.minecraft.nbt.Tag elem : nbtList) {
|
||||
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
list.add(new PaperweightLazyCompoundTag(compoundTag));
|
||||
} else {
|
||||
list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ListTag getListTag(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag) {
|
||||
return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag);
|
||||
}
|
||||
return new ListTag(StringTag.class, Collections.emptyList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
|
||||
ListTag listTag = getListTag(key);
|
||||
if (listTag.getType().equals(listType)) {
|
||||
return (List<T>) listTag.getValue();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public long[] getLongArray(String key) {
|
||||
return compoundTagSupplier.get().getLongArray(key);
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
return compoundTagSupplier.get().getLong(key);
|
||||
}
|
||||
|
||||
public long asLong(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsLong();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
return compoundTagSupplier.get().getShort(key);
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return compoundTagSupplier.get().getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return compoundTagSupplier.get().toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,541 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_18_R2.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
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.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_18_R2.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field flatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
private static final Field ringPositionsField;
|
||||
private static final Field hasGeneratedPositionsField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "h"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "g"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "K"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "h"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "i"));
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureManager structureManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataPackConfig()
|
||||
);
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable());
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
originalServerWorld.dimensionTypeRegistration(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
// placeholder. Required for new ChunkProviderServer, but we create and then set it later
|
||||
newOpts.dimensions().getOrThrow(levelStemResourceKey).generator(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
private final Holder<Biome> singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.asHolderIdMap().byId(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(biomeX, biomeY, biomeZ,
|
||||
PaperweightRegen.this.chunkGenerator.climateSampler()
|
||||
);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig);
|
||||
}
|
||||
|
||||
//generator
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(originalGenerator.structureSets, generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier =
|
||||
(Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField
|
||||
.get(originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME
|
||||
.asHolderIdMap()
|
||||
.byId(WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())));
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(originalGenerator.structureSets, noiseBasedChunkGenerator.noises,
|
||||
biomeSource, seed,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.delegate;
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(originalGenerator);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(
|
||||
originalGenerator);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(ringPositions);
|
||||
ringPositionsField.set(chunkGenerator, copy);
|
||||
hasGeneratedPositionsField.setBoolean(chunkGenerator, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chunkGenerator.conf = originalServerWorld.spigotConfig;
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = freshChunkProvider.getLightEngine();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
16
worldedit-bukkit/adapters/adapter-1_19/build.gradle.kts
Normale Datei
16
worldedit-bukkit/adapters/adapter-1_19/build.gradle.kts
Normale Datei
@ -0,0 +1,16 @@
|
||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
applyPaperweightAdapterConfiguration()
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.2-R0.1-20221206.184705-189")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R1;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.chat.ChatType;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.stats.Stat;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
import java.util.UUID;
|
||||
|
||||
class PaperweightFakePlayer extends ServerPlayer {
|
||||
|
||||
private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(
|
||||
UUID.nameUUIDFromBytes("worldedit".getBytes()),
|
||||
"[WorldEdit]"
|
||||
);
|
||||
private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D);
|
||||
|
||||
PaperweightFakePlayer(ServerLevel world) {
|
||||
super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 position() {
|
||||
return ORIGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die(DamageSource damagesource) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt openMenu(MenuProvider factory) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayClientMessage(Component message, boolean actionBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awardStat(Stat<?> stat, int amount) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void awardStat(Stat<?> stat) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvulnerableTo(DamageSource damageSource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openTextEdit(SignBlockEntity sign) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R1;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PaperweightWorldNativeAccess implements
|
||||
WorldNativeAccess<LevelChunk, net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
|
||||
private final PaperweightAdapter adapter;
|
||||
private final WeakReference<ServerLevel> world;
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference<ServerLevel> world) {
|
||||
this.adapter = adapter;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
private ServerLevel getWorld() {
|
||||
return Objects.requireNonNull(world.get(), "The reference to the world was lost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
||||
this.sideEffectSet = sideEffectSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelChunk getChunk(int x, int z) {
|
||||
return getWorld().getChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) {
|
||||
int stateId = BlockStateIdAccess.getBlockStateId(state);
|
||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
||||
? Block.stateById(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(state)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) {
|
||||
return chunk.getBlockState(position);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState setBlockState(
|
||||
LevelChunk chunk,
|
||||
BlockPos position,
|
||||
net.minecraft.world.level.block.state.BlockState state
|
||||
) {
|
||||
return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
|
||||
net.minecraft.world.level.block.state.BlockState block,
|
||||
BlockPos position
|
||||
) {
|
||||
return Block.updateFromNeighbourShapes(block, getWorld(), position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getPosition(int x, int y, int z) {
|
||||
return new BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLightingForBlock(BlockPos position) {
|
||||
getWorld().getChunkSource().getLightEngine().checkBlock(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(final BlockPos position, final CompoundBinaryTag tag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyBlockUpdate(
|
||||
LevelChunk chunk,
|
||||
BlockPos position,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
||||
getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(LevelChunk chunk) {
|
||||
return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockChanged(LevelChunk chunk, BlockPos position) {
|
||||
if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) {
|
||||
getWorld().getChunkSource().blockChanged(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNeighbors(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
ServerLevel world = getWorld();
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
world.updateNeighborsAt(pos, oldState.getBlock());
|
||||
} else {
|
||||
// When we don't want events, manually run the physics without them.
|
||||
Block block = oldState.getBlock();
|
||||
fireNeighborChanged(pos, world, block, pos.west());
|
||||
fireNeighborChanged(pos, world, block, pos.east());
|
||||
fireNeighborChanged(pos, world, block, pos.below());
|
||||
fireNeighborChanged(pos, world, block, pos.above());
|
||||
fireNeighborChanged(pos, world, block, pos.north());
|
||||
fireNeighborChanged(pos, world, block, pos.south());
|
||||
}
|
||||
if (newState.hasAnalogOutputSignal()) {
|
||||
world.updateNeighbourForOutputSignal(pos, newState.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) {
|
||||
world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNeighbors(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
int recursionLimit
|
||||
) {
|
||||
ServerLevel world = getWorld();
|
||||
// a == updateNeighbors
|
||||
// b == updateDiagonalNeighbors
|
||||
oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
CraftWorld craftWorld = world.getWorld();
|
||||
BlockPhysicsEvent event = new BlockPhysicsEvent(
|
||||
craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()),
|
||||
CraftBlockData.fromData(newState)
|
||||
);
|
||||
world.getCraftServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(
|
||||
BlockPos pos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
getWorld().onBlockStateChange(pos, oldState, newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.util.ReflectionUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.EmptyBlockGetter;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
|
||||
|
||||
public class PaperweightBlockMaterial implements BlockMaterial {
|
||||
|
||||
private final Block block;
|
||||
private final BlockState blockState;
|
||||
private final Material material;
|
||||
private final boolean isTranslucent;
|
||||
private final CraftBlockData craftBlockData;
|
||||
private final org.bukkit.Material craftMaterial;
|
||||
private final int opacity;
|
||||
private final CompoundTag tile;
|
||||
|
||||
public PaperweightBlockMaterial(Block block) {
|
||||
this(block, block.defaultBlockState());
|
||||
}
|
||||
|
||||
public PaperweightBlockMaterial(Block block, BlockState blockState) {
|
||||
this.block = block;
|
||||
this.blockState = blockState;
|
||||
this.material = blockState.getMaterial();
|
||||
this.craftBlockData = CraftBlockData.fromData(blockState);
|
||||
this.craftMaterial = craftBlockData.getMaterial();
|
||||
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block, Refraction.pickName(
|
||||
"properties", "aO"));
|
||||
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
|
||||
Refraction.pickName("canOcclude", "n")
|
||||
);
|
||||
opacity = blockState.getLightBlock(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||
BlockEntity tileEntity = !(block instanceof EntityBlock) ? null : ((EntityBlock) block).newBlockEntity(
|
||||
BlockPos.ZERO,
|
||||
blockState
|
||||
);
|
||||
tile = tileEntity == null
|
||||
? null
|
||||
: new PaperweightLazyCompoundTag(Suppliers.memoize(tileEntity::saveWithId));
|
||||
}
|
||||
|
||||
public Block getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
public BlockState getState() {
|
||||
return blockState;
|
||||
}
|
||||
|
||||
public CraftBlockData getCraftBlockData() {
|
||||
return craftBlockData;
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAir() {
|
||||
return blockState.isAir();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube() {
|
||||
return craftMaterial.isOccluding();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return material.isSolidBlocking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSource() {
|
||||
return blockState.isSignalSource();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLiquid() {
|
||||
return material.isLiquid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid() {
|
||||
return material.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHardness() {
|
||||
return craftBlockData.getState().destroySpeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getResistance() {
|
||||
return block.getExplosionResistance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSlipperiness() {
|
||||
return block.getFriction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightValue() {
|
||||
return blockState.getLightEmission();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightOpacity() {
|
||||
return opacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFragileWhenPushed() {
|
||||
return material.getPushReaction() == PushReaction.DESTROY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnpushable() {
|
||||
return material.getPushReaction() == PushReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTicksRandomly() {
|
||||
return block.isRandomlyTicking(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMovementBlocker() {
|
||||
return material.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBurnable() {
|
||||
return material.isFlammable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToolRequired() {
|
||||
// Removed in 1.16.1, this is not present in higher versions
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplacedDuringPlacement() {
|
||||
return material.isReplaceable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTranslucent() {
|
||||
return isTranslucent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasContainer() {
|
||||
return block instanceof EntityBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTile() {
|
||||
return block instanceof EntityBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getDefaultTile() {
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMapColor() {
|
||||
// rgb field
|
||||
return material.getColor().col;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,701 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.IDelegateBukkitImplAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSRelighterFactory;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.entity.LazyBaseEntity;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||
import com.fastasyncworldedit.core.util.NbtUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_19_R1.PaperweightAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.regen.PaperweightRegen;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.registry.state.BooleanProperty;
|
||||
import com.sk89q.worldedit.registry.state.DirectionalProperty;
|
||||
import com.sk89q.worldedit.registry.state.EnumProperty;
|
||||
import com.sk89q.worldedit.registry.state.IntegerProperty;
|
||||
import com.sk89q.worldedit.registry.state.Property;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.TreeGenerator;
|
||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||
import com.sk89q.worldedit.util.nbt.BinaryTag;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.util.nbt.StringBinaryTag;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||
import com.sk89q.worldedit.world.block.BlockType;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import com.sk89q.worldedit.world.entity.EntityType;
|
||||
import com.sk89q.worldedit.world.item.ItemType;
|
||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.WritableRegistry;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.TreeType;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.block.CraftBlockState;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftEntity;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.util.CraftNamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
||||
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
private static Method CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE;
|
||||
|
||||
static {
|
||||
try {
|
||||
CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE = ChunkHolder.class.getDeclaredMethod("wasAccessibleSinceLastSave");
|
||||
} catch (NoSuchMethodException ignored) { // may not be present in newer paper versions
|
||||
}
|
||||
}
|
||||
|
||||
private final PaperweightAdapter parent;
|
||||
// ------------------------------------------------------------------------
|
||||
// Code that may break between versions of Minecraft
|
||||
// ------------------------------------------------------------------------
|
||||
private final PaperweightMapChunkUtil mapUtil = new PaperweightMapChunkUtil();
|
||||
private char[] ibdToStateOrdinal = null;
|
||||
private int[] ordinalToIbdID = null;
|
||||
private boolean initialised = false;
|
||||
private Map<String, List<Property<?>>> allBlockProperties = null;
|
||||
|
||||
public PaperweightFaweAdapter() throws NoSuchFieldException, NoSuchMethodException {
|
||||
this.parent = new PaperweightAdapter();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getEntityId(Entity entity) {
|
||||
ResourceLocation resourceLocation = net.minecraft.world.entity.EntityType.getKey(entity.getType());
|
||||
return resourceLocation == null ? null : resourceLocation.toString();
|
||||
}
|
||||
|
||||
private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
entity.save(compoundTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitImplAdapter<net.minecraft.nbt.Tag> getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
private synchronized boolean init() {
|
||||
if (ibdToStateOrdinal != null && ibdToStateOrdinal[1] != 0) {
|
||||
return false;
|
||||
}
|
||||
ibdToStateOrdinal = new char[BlockTypesCache.states.length]; // size
|
||||
ordinalToIbdID = new int[ibdToStateOrdinal.length]; // size
|
||||
for (int i = 0; i < ibdToStateOrdinal.length; i++) {
|
||||
BlockState blockState = BlockTypesCache.states[i];
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) blockState.getMaterial();
|
||||
int id = Block.BLOCK_STATE_REGISTRY.getId(material.getState());
|
||||
char ordinal = blockState.getOrdinalChar();
|
||||
ibdToStateOrdinal[id] = ordinal;
|
||||
ordinalToIbdID[ordinal] = id;
|
||||
}
|
||||
Map<String, List<Property<?>>> properties = new HashMap<>();
|
||||
try {
|
||||
for (Field field : BlockStateProperties.class.getDeclaredFields()) {
|
||||
Object obj = field.get(null);
|
||||
if (!(obj instanceof net.minecraft.world.level.block.state.properties.Property<?> state)) {
|
||||
continue;
|
||||
}
|
||||
Property<?> property;
|
||||
if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) {
|
||||
property = new BooleanProperty(
|
||||
state.getName(),
|
||||
(List<Boolean>) ImmutableList.copyOf(state.getPossibleValues())
|
||||
);
|
||||
} else if (state instanceof DirectionProperty) {
|
||||
property = new DirectionalProperty(
|
||||
state.getName(),
|
||||
state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase()))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) {
|
||||
property = new EnumProperty(
|
||||
state.getName(),
|
||||
state
|
||||
.getPossibleValues()
|
||||
.stream()
|
||||
.map(e -> ((StringRepresentable) e).getSerializedName())
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
} else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) {
|
||||
property = new IntegerProperty(
|
||||
state.getName(),
|
||||
(List<Integer>) ImmutableList.copyOf(state.getPossibleValues())
|
||||
);
|
||||
} else {
|
||||
throw new IllegalArgumentException("FastAsyncWorldEdit needs an update to support " + state
|
||||
.getClass()
|
||||
.getSimpleName());
|
||||
}
|
||||
properties.compute(property.getName().toLowerCase(Locale.ROOT), (k, v) -> {
|
||||
if (v == null) {
|
||||
v = new ArrayList<>(Collections.singletonList(property));
|
||||
} else {
|
||||
v.add(property);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
allBlockProperties = ImmutableMap.copyOf(properties);
|
||||
}
|
||||
initialised = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BlockType blockType) {
|
||||
Block block = getBlock(blockType);
|
||||
return new PaperweightBlockMaterial(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized BlockMaterial getMaterial(BlockState state) {
|
||||
net.minecraft.world.level.block.state.BlockState blockState = ((CraftBlockData) Bukkit.createBlockData(state.getAsString())).getState();
|
||||
return new PaperweightBlockMaterial(blockState.getBlock(), blockState);
|
||||
}
|
||||
|
||||
public Block getBlock(BlockType blockType) {
|
||||
return Registry.BLOCK.get(new ResourceLocation(blockType.getNamespace(), blockType.getResource()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public BlockState getBlock(Location location) {
|
||||
Preconditions.checkNotNull(location);
|
||||
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
final ServerLevel handle = craftWorld.getHandle();
|
||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||
BlockState state = adapt(blockData);
|
||||
if (state == null) {
|
||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(final Location location) {
|
||||
Preconditions.checkNotNull(location);
|
||||
|
||||
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
||||
int x = location.getBlockX();
|
||||
int y = location.getBlockY();
|
||||
int z = location.getBlockZ();
|
||||
|
||||
final ServerLevel handle = craftWorld.getHandle();
|
||||
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
|
||||
final BlockPos blockPos = new BlockPos(x, y, z);
|
||||
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
|
||||
BlockState state = adapt(blockData);
|
||||
if (state == null) {
|
||||
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
||||
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
||||
}
|
||||
if (state.getBlockType().getMaterial().hasContainer()) {
|
||||
|
||||
// Read the NBT data
|
||||
BlockEntity blockEntity = chunk.getBlockEntity(blockPos, LevelChunk.EntityCreationType.CHECK);
|
||||
if (blockEntity != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId();
|
||||
return state.toBaseBlock((CompoundBinaryTag) toNativeBinary(tag));
|
||||
}
|
||||
}
|
||||
|
||||
return state.toBaseBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<SideEffect> getSupportedSideEffects() {
|
||||
return SideEffectSet.defaults().getSideEffectsToApply();
|
||||
}
|
||||
|
||||
public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) {
|
||||
CraftChunk craftChunk = (CraftChunk) chunk;
|
||||
LevelChunk levelChunk = craftChunk.getHandle();
|
||||
Level level = levelChunk.getLevel();
|
||||
|
||||
BlockPos blockPos = new BlockPos(x, y, z);
|
||||
net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState();
|
||||
LevelChunkSection[] levelChunkSections = levelChunk.getSections();
|
||||
int y4 = y >> 4;
|
||||
LevelChunkSection section = levelChunkSections[y4];
|
||||
|
||||
net.minecraft.world.level.block.state.BlockState existing;
|
||||
if (section == null) {
|
||||
existing = ((PaperweightBlockMaterial) BlockTypes.AIR.getDefaultState().getMaterial()).getState();
|
||||
} else {
|
||||
existing = section.getBlockState(x & 15, y & 15, z & 15);
|
||||
}
|
||||
|
||||
levelChunk.removeBlockEntity(blockPos); // Force delete the old tile entity
|
||||
|
||||
CompoundBinaryTag compoundTag = state instanceof BaseBlock ? state.getNbt() : null;
|
||||
if (compoundTag != null || existing instanceof TileEntityBlock) {
|
||||
level.setBlock(blockPos, blockState, 0);
|
||||
// remove tile
|
||||
if (compoundTag != null) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the Forge version
|
||||
BlockEntity blockEntity = level.getBlockEntity(blockPos);
|
||||
if (blockEntity != null) {
|
||||
net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(compoundTag);
|
||||
tag.put("x", IntTag.valueOf(x));
|
||||
tag.put("y", IntTag.valueOf(y));
|
||||
tag.put("z", IntTag.valueOf(z));
|
||||
blockEntity.load(tag); // readTagIntoTileEntity - load data
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (existing == blockState) {
|
||||
return true;
|
||||
}
|
||||
levelChunk.setBlockState(blockPos, blockState, false);
|
||||
}
|
||||
if (update) {
|
||||
level.getMinecraftWorld().sendBlockUpdated(blockPos, existing, blockState, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldNativeAccess<?, ?, ?> createWorldNativeAccess(org.bukkit.World world) {
|
||||
return new PaperweightFaweWorldNativeAccess(
|
||||
this,
|
||||
new WeakReference<>(((CraftWorld) world).getHandle())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
||||
Preconditions.checkNotNull(entity);
|
||||
|
||||
CraftEntity craftEntity = ((CraftEntity) entity);
|
||||
Entity mcEntity = craftEntity.getHandle();
|
||||
|
||||
String id = getEntityId(mcEntity);
|
||||
|
||||
if (id != null) {
|
||||
EntityType type = com.sk89q.worldedit.world.entity.EntityTypes.get(id);
|
||||
Supplier<CompoundBinaryTag> saveTag = () -> {
|
||||
final net.minecraft.nbt.CompoundTag minecraftTag = new net.minecraft.nbt.CompoundTag();
|
||||
readEntityIntoTag(mcEntity, minecraftTag);
|
||||
//add Id for AbstractChangeSet to work
|
||||
final CompoundBinaryTag tag = (CompoundBinaryTag) toNativeBinary(minecraftTag);
|
||||
final Map<String, BinaryTag> tags = NbtUtils.getCompoundBinaryTagValues(tag);
|
||||
tags.put("Id", StringBinaryTag.of(id));
|
||||
return CompoundBinaryTag.from(tags);
|
||||
};
|
||||
return new LazyBaseEntity(type, saveTag);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichBlockName(BlockType blockType) {
|
||||
return parent.getRichBlockName(blockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichItemName(ItemType itemType) {
|
||||
return parent.getRichItemName(itemType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getRichItemName(BaseItemStack itemStack) {
|
||||
return parent.getRichItemName(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalInt getInternalBlockStateId(BlockState state) {
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
|
||||
net.minecraft.world.level.block.state.BlockState mcState = material.getCraftBlockData().getState();
|
||||
return OptionalInt.of(Block.BLOCK_STATE_REGISTRY.getId(mcState));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState adapt(BlockData blockData) {
|
||||
CraftBlockData cbd = ((CraftBlockData) blockData);
|
||||
net.minecraft.world.level.block.state.BlockState ibd = cbd.getState();
|
||||
return adapt(ibd);
|
||||
}
|
||||
|
||||
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
|
||||
return BlockTypesCache.states[adaptToChar(blockState)];
|
||||
}
|
||||
|
||||
public char adaptToChar(net.minecraft.world.level.block.state.BlockState blockState) {
|
||||
int id = Block.BLOCK_STATE_REGISTRY.getId(blockState);
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
try {
|
||||
init();
|
||||
return ibdToStateOrdinal[id];
|
||||
} catch (ArrayIndexOutOfBoundsException e1) {
|
||||
LOGGER.error("Attempted to convert {} with ID {} to char. ibdToStateOrdinal length: {}. Defaulting to air!",
|
||||
blockState.getBlock(), Block.BLOCK_STATE_REGISTRY.getId(blockState), ibdToStateOrdinal.length, e1
|
||||
);
|
||||
return BlockTypesCache.ReservedIDs.AIR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public char ibdIDToOrdinal(int id) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
init();
|
||||
return ibdToStateOrdinal[id];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] getIbdToStateOrdinal() {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
init();
|
||||
return ibdToStateOrdinal;
|
||||
}
|
||||
}
|
||||
|
||||
public int ordinalToIbdID(char ordinal) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
init();
|
||||
return ordinalToIbdID[ordinal];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getOrdinalToIbdID() {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
init();
|
||||
return ordinalToIbdID;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B extends BlockStateHolder<B>> BlockData adapt(B state) {
|
||||
PaperweightBlockMaterial material = (PaperweightBlockMaterial) state.getMaterial();
|
||||
return material.getCraftBlockData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
|
||||
ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
|
||||
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
|
||||
if (map != null && wasAccessibleSinceLastSave(map)) {
|
||||
boolean flag = false;
|
||||
// PlayerChunk.d players = map.players;
|
||||
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
|
||||
*/ Stream.empty();
|
||||
|
||||
ServerPlayer checkPlayer = player == null ? null : ((CraftPlayer) player).getHandle();
|
||||
stream.filter(entityPlayer -> checkPlayer == null || entityPlayer == checkPlayer)
|
||||
.forEach(entityPlayer -> {
|
||||
synchronized (chunkPacket) {
|
||||
ClientboundLevelChunkWithLightPacket nmsPacket = (ClientboundLevelChunkWithLightPacket) chunkPacket.getNativePacket();
|
||||
if (nmsPacket == null) {
|
||||
nmsPacket = mapUtil.create(this, chunkPacket);
|
||||
chunkPacket.setNativePacket(nmsPacket);
|
||||
}
|
||||
try {
|
||||
FaweCache.INSTANCE.CHUNK_FLAG.get().set(true);
|
||||
entityPlayer.connection.send(nmsPacket);
|
||||
} finally {
|
||||
FaweCache.INSTANCE.CHUNK_FLAG.get().set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ? extends Property<?>> getProperties(BlockType blockType) {
|
||||
return getParent().getProperties(blockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceAt(org.bukkit.World world, BlockVector3 blockVector3, BlockState blockState) {
|
||||
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
|
||||
net.minecraft.world.level.block.state.BlockState blockState1 = Block.stateById(internalId);
|
||||
return blockState1.hasPostProcess(
|
||||
((CraftWorld) world).getHandle(),
|
||||
new BlockPos(blockVector3.getX(), blockVector3.getY(), blockVector3.getZ())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack adapt(BaseItemStack baseItemStack) {
|
||||
ItemStack stack = new ItemStack(
|
||||
Registry.ITEM.get(ResourceLocation.tryParse(baseItemStack.getType().getId())),
|
||||
baseItemStack.getAmount()
|
||||
);
|
||||
stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(baseItemStack.getNbtData())));
|
||||
return CraftItemStack.asCraftMirror(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateTree(
|
||||
TreeGenerator.TreeType treeType, EditSession editSession, BlockVector3 blockVector3,
|
||||
org.bukkit.World bukkitWorld
|
||||
) {
|
||||
TreeType bukkitType = BukkitWorld.toBukkitTreeType(treeType);
|
||||
if (bukkitType == TreeType.CHORUS_PLANT) {
|
||||
blockVector3 = blockVector3.add(
|
||||
0,
|
||||
1,
|
||||
0
|
||||
); // bukkit skips the feature gen which does this offset normally, so we have to add it back
|
||||
}
|
||||
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
|
||||
final BlockVector3 finalBlockVector = blockVector3;
|
||||
// Sync to main thread to ensure no clashes occur
|
||||
Map<BlockPos, CraftBlockState> placed = TaskManager.taskManager().sync(() -> {
|
||||
serverLevel.captureTreeGeneration = true;
|
||||
serverLevel.captureBlockStates = true;
|
||||
try {
|
||||
if (!bukkitWorld.generateTree(BukkitAdapter.adapt(bukkitWorld, finalBlockVector), bukkitType)) {
|
||||
return null;
|
||||
}
|
||||
return ImmutableMap.copyOf(serverLevel.capturedBlockStates);
|
||||
} finally {
|
||||
serverLevel.captureBlockStates = false;
|
||||
serverLevel.captureTreeGeneration = false;
|
||||
serverLevel.capturedBlockStates.clear();
|
||||
}
|
||||
});
|
||||
if (placed == null || placed.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (CraftBlockState craftBlockState : placed.values()) {
|
||||
if (craftBlockState == null || craftBlockState.getType() == Material.AIR) {
|
||||
continue;
|
||||
}
|
||||
editSession.setBlock(craftBlockState.getX(), craftBlockState.getY(), craftBlockState.getZ(),
|
||||
BukkitAdapter.adapt(((org.bukkit.block.BlockState) craftBlockState).getBlockData())
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
|
||||
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
|
||||
final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount());
|
||||
weStack.setNbt(((CompoundBinaryTag) toNativeBinary(nmsStack.getTag())));
|
||||
return weStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toNative(net.minecraft.nbt.Tag foreign) {
|
||||
return parent.toNative(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.nbt.Tag fromNative(Tag foreign) {
|
||||
if (foreign instanceof PaperweightLazyCompoundTag) {
|
||||
return ((PaperweightLazyCompoundTag) foreign).get();
|
||||
}
|
||||
return parent.fromNative(foreign);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent target, RegenOptions options) throws Exception {
|
||||
return new PaperweightRegen(bukkitWorld, region, target, options).regenerate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IChunkGet get(org.bukkit.World world, int chunkX, int chunkZ) {
|
||||
return new PaperweightGetBlocks(world, chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInternalBiomeId(BiomeType biomeType) {
|
||||
final Registry<Biome> registry = MinecraftServer
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
||||
ResourceLocation resourceLocation = ResourceLocation.tryParse(biomeType.getId());
|
||||
Biome biome = registry.get(resourceLocation);
|
||||
return registry.getId(biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<NamespacedKey> getRegisteredBiomes() {
|
||||
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) ((CraftServer) Bukkit.getServer())
|
||||
.getServer()
|
||||
.registryAccess()
|
||||
.ownedRegistryOrThrow(
|
||||
Registry.BIOME_REGISTRY);
|
||||
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||
for (ResourceLocation key : keys) {
|
||||
try {
|
||||
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||
}
|
||||
}
|
||||
return namespacedKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelighterFactory getRelighterFactory() {
|
||||
if (PaperLib.isPaper()) {
|
||||
return new PaperweightStarlightRelighterFactory();
|
||||
} else {
|
||||
return new NMSRelighterFactory();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Property<?>>> getAllProperties() {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (initialised) {
|
||||
return allBlockProperties;
|
||||
}
|
||||
init();
|
||||
return allBlockProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBatchProcessor getTickingPostProcessor() {
|
||||
return new PaperweightPostProcessor();
|
||||
}
|
||||
|
||||
private boolean wasAccessibleSinceLastSave(ChunkHolder holder) {
|
||||
if (!PaperLib.isPaper() || !PaperweightPlatformAdapter.POST_CHUNK_REWRITE) {
|
||||
try {
|
||||
return (boolean) CHUNK_HOLDER_WAS_ACCESSIBLE_SINCE_LAST_SAVE.invoke(holder);
|
||||
} catch (IllegalAccessException | InvocationTargetException ignored) {
|
||||
// fall-through
|
||||
}
|
||||
}
|
||||
// Papers new chunk system has no related replacement - therefor we assume true.
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,286 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.math.IntPair;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
|
||||
import com.sk89q.worldedit.internal.wna.WorldNativeAccess;
|
||||
import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.SideEffectSet;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess<LevelChunk,
|
||||
net.minecraft.world.level.block.state.BlockState, BlockPos> {
|
||||
|
||||
private static final int UPDATE = 1;
|
||||
private static final int NOTIFY = 2;
|
||||
private static final Direction[] NEIGHBOUR_ORDER = {
|
||||
Direction.EAST,
|
||||
Direction.WEST,
|
||||
Direction.DOWN,
|
||||
Direction.UP,
|
||||
Direction.NORTH,
|
||||
Direction.SOUTH
|
||||
};
|
||||
private final PaperweightFaweAdapter paperweightFaweAdapter;
|
||||
private final WeakReference<Level> level;
|
||||
private final AtomicInteger lastTick;
|
||||
private final Set<CachedChange> cachedChanges = new HashSet<>();
|
||||
private final Set<IntPair> cachedChunksToSend = new HashSet<>();
|
||||
private SideEffectSet sideEffectSet;
|
||||
|
||||
public PaperweightFaweWorldNativeAccess(PaperweightFaweAdapter paperweightFaweAdapter, WeakReference<Level> level) {
|
||||
this.paperweightFaweAdapter = paperweightFaweAdapter;
|
||||
this.level = level;
|
||||
// Use the actual tick as minecraft-defined so we don't try to force blocks into the world when the server's already lagging.
|
||||
// - With the caveat that we don't want to have too many cached changed (1024) so we'd flush those at 1024 anyway.
|
||||
this.lastTick = new AtomicInteger(MinecraftServer.currentTick);
|
||||
}
|
||||
|
||||
private Level getLevel() {
|
||||
return Objects.requireNonNull(level.get(), "The reference to the world was lost");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) {
|
||||
this.sideEffectSet = sideEffectSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelChunk getChunk(int x, int z) {
|
||||
return getLevel().getChunk(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState toNative(BlockState blockState) {
|
||||
int stateId = paperweightFaweAdapter.ordinalToIbdID(blockState.getOrdinalChar());
|
||||
return BlockStateIdAccess.isValidInternalId(stateId)
|
||||
? Block.stateById(stateId)
|
||||
: ((CraftBlockData) BukkitAdapter.adapt(blockState)).getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk levelChunk, BlockPos blockPos) {
|
||||
return levelChunk.getBlockState(blockPos);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public synchronized net.minecraft.world.level.block.state.BlockState setBlockState(
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (Fawe.isMainThread()) {
|
||||
return levelChunk.setBlockState(blockPos, blockState,
|
||||
this.sideEffectSet != null && this.sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
);
|
||||
}
|
||||
// Since FAWE is.. Async we need to do it on the main thread (wooooo.. :( )
|
||||
cachedChanges.add(new CachedChange(levelChunk, blockPos, blockState));
|
||||
cachedChunksToSend.add(new IntPair(levelChunk.bukkitChunk.getX(), levelChunk.bukkitChunk.getZ()));
|
||||
boolean nextTick = lastTick.get() > currentTick;
|
||||
if (nextTick || cachedChanges.size() >= 1024) {
|
||||
if (nextTick) {
|
||||
lastTick.set(currentTick);
|
||||
}
|
||||
flushAsync(nextTick);
|
||||
}
|
||||
return blockState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(
|
||||
net.minecraft.world.level.block.state.BlockState blockState,
|
||||
BlockPos blockPos
|
||||
) {
|
||||
return Block.updateFromNeighbourShapes(blockState, getLevel(), blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getPosition(int x, int y, int z) {
|
||||
return new BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLightingForBlock(BlockPos blockPos) {
|
||||
getLevel().getChunkSource().getLightEngine().checkBlock(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateTileEntity(BlockPos blockPos, CompoundBinaryTag tag) {
|
||||
// We will assume that the tile entity was created for us,
|
||||
// though we do not do this on the other versions
|
||||
BlockEntity blockEntity = getLevel().getBlockEntity(blockPos);
|
||||
if (blockEntity == null) {
|
||||
return false;
|
||||
}
|
||||
net.minecraft.nbt.Tag nativeTag = paperweightFaweAdapter.fromNativeBinary(tag);
|
||||
blockEntity.load((CompoundTag) nativeTag);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyBlockUpdate(
|
||||
LevelChunk levelChunk, BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
|
||||
getLevel().sendBlockUpdated(blockPos, oldState, newState, UPDATE | NOTIFY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChunkTicking(LevelChunk levelChunk) {
|
||||
return levelChunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markBlockChanged(LevelChunk levelChunk, BlockPos blockPos) {
|
||||
if (levelChunk.getSections()[level.get().getSectionIndex(blockPos.getY())] != null) {
|
||||
((ServerChunkCache) getLevel().getChunkSource()).blockChanged(blockPos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyNeighbors(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
Level level = getLevel();
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
level.blockUpdated(blockPos, oldState.getBlock());
|
||||
} else {
|
||||
// When we don't want events, manually run the physics without them.
|
||||
// Un-nest neighbour updating
|
||||
for (Direction direction : NEIGHBOUR_ORDER) {
|
||||
BlockPos shifted = blockPos.relative(direction);
|
||||
level.getBlockState(shifted).neighborChanged(level, shifted, oldState.getBlock(), blockPos, false);
|
||||
}
|
||||
}
|
||||
if (newState.hasAnalogOutputSignal()) {
|
||||
level.updateNeighbourForOutputSignal(blockPos, newState.getBlock());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNeighbors(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState,
|
||||
int recursionLimit
|
||||
) {
|
||||
Level level = getLevel();
|
||||
// a == updateNeighbors
|
||||
// b == updateDiagonalNeighbors
|
||||
oldState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
||||
if (sideEffectSet.shouldApply(SideEffect.EVENTS)) {
|
||||
CraftWorld craftWorld = level.getWorld();
|
||||
if (craftWorld != null) {
|
||||
BlockPhysicsEvent event = new BlockPhysicsEvent(
|
||||
craftWorld.getBlockAt(blockPos.getX(), blockPos.getY(), blockPos.getZ()),
|
||||
CraftBlockData.fromData(newState)
|
||||
);
|
||||
level.getCraftServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
newState.triggerEvent(level, blockPos, NOTIFY, recursionLimit);
|
||||
newState.updateIndirectNeighbourShapes(level, blockPos, NOTIFY, recursionLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockStateChange(
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState oldState,
|
||||
net.minecraft.world.level.block.state.BlockState newState
|
||||
) {
|
||||
getLevel().onBlockStateChange(blockPos, oldState, newState);
|
||||
}
|
||||
|
||||
private synchronized void flushAsync(final boolean sendChunks) {
|
||||
final Set<CachedChange> changes = Set.copyOf(cachedChanges);
|
||||
cachedChanges.clear();
|
||||
final Set<IntPair> toSend;
|
||||
if (sendChunks) {
|
||||
toSend = Set.copyOf(cachedChunksToSend);
|
||||
cachedChunksToSend.clear();
|
||||
} else {
|
||||
toSend = Collections.emptySet();
|
||||
}
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
changes.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
if (!sendChunks) {
|
||||
return;
|
||||
}
|
||||
for (IntPair chunk : toSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
TaskManager.taskManager().async(() -> TaskManager.taskManager().sync(runnableVal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void flush() {
|
||||
RunnableVal<Object> runnableVal = new RunnableVal<>() {
|
||||
@Override
|
||||
public void run(Object value) {
|
||||
cachedChanges.forEach(cc -> cc.levelChunk.setBlockState(cc.blockPos, cc.blockState,
|
||||
sideEffectSet != null && sideEffectSet.shouldApply(SideEffect.UPDATE)
|
||||
));
|
||||
for (IntPair chunk : cachedChunksToSend) {
|
||||
PaperweightPlatformAdapter.sendChunk(getLevel().getWorld().getHandle(), chunk.x(), chunk.z(), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Fawe.isMainThread()) {
|
||||
runnableVal.run();
|
||||
} else {
|
||||
TaskManager.taskManager().sync(runnableVal);
|
||||
}
|
||||
cachedChanges.clear();
|
||||
cachedChunksToSend.clear();
|
||||
}
|
||||
|
||||
private record CachedChange(
|
||||
LevelChunk levelChunk,
|
||||
BlockPos blockPos,
|
||||
net.minecraft.world.level.block.state.BlockState blockState
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
@ -0,0 +1,248 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||
import com.fastasyncworldedit.core.queue.IBlocks;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.nbt.PaperweightLazyCompoundTag;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.PalettedContainerRO;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class PaperweightGetBlocks_Copy implements IChunkGet {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private final Map<BlockVector3, CompoundTag> tiles = new HashMap<>();
|
||||
private final Set<CompoundTag> entities = new HashSet<>();
|
||||
private final char[][] blocks;
|
||||
private final int minHeight;
|
||||
private final int maxHeight;
|
||||
final ServerLevel serverLevel;
|
||||
final LevelChunk levelChunk;
|
||||
private PalettedContainer<Holder<Biome>>[] biomes = null;
|
||||
|
||||
protected PaperweightGetBlocks_Copy(LevelChunk levelChunk) {
|
||||
this.levelChunk = levelChunk;
|
||||
this.serverLevel = levelChunk.level;
|
||||
this.minHeight = serverLevel.getMinBuildHeight();
|
||||
this.maxHeight = serverLevel.getMaxBuildHeight() - 1; // Minecraft max limit is exclusive.
|
||||
this.blocks = new char[getSectionCount()][];
|
||||
}
|
||||
|
||||
protected void storeTile(BlockEntity blockEntity) {
|
||||
tiles.put(
|
||||
BlockVector3.at(
|
||||
blockEntity.getBlockPos().getX(),
|
||||
blockEntity.getBlockPos().getY(),
|
||||
blockEntity.getBlockPos().getZ()
|
||||
),
|
||||
new PaperweightLazyCompoundTag(Suppliers.memoize(blockEntity::saveWithId))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||
return tiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public CompoundTag getTile(int x, int y, int z) {
|
||||
return tiles.get(BlockVector3.at(x, y, z));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected void storeEntity(Entity entity) {
|
||||
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
net.minecraft.nbt.CompoundTag compoundTag = new net.minecraft.nbt.CompoundTag();
|
||||
entity.save(compoundTag);
|
||||
entities.add((CompoundTag) adapter.toNative(compoundTag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<CompoundTag> getEntities() {
|
||||
return this.entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getEntity(UUID uuid) {
|
||||
for (CompoundTag tag : entities) {
|
||||
if (uuid.equals(tag.getUUID())) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCreateCopy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreateCopy(boolean createCopy) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSkyLightingToGet(char[][] lighting, int minSectionPosition, int maxSectionPosition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeightmapToGet(HeightMapType type, int[] data) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxY() {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinY() {
|
||||
return minHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSectionPosition() {
|
||||
return maxHeight >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinSectionPosition() {
|
||||
return minHeight >> 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeType getBiomeType(int x, int y, int z) {
|
||||
Holder<Biome> biome = biomes[(y >> 4) - getMinSectionPosition()].get(x >> 2, (y & 15) >> 2, z >> 2);
|
||||
return PaperweightPlatformAdapter.adapt(biome, serverLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSectionLighting(int layer, boolean sky) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive, int layer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlocks reset() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionCount() {
|
||||
return serverLevel.getSectionsCount();
|
||||
}
|
||||
|
||||
protected void storeSection(int layer, char[] data) {
|
||||
blocks[layer] = data;
|
||||
}
|
||||
|
||||
protected void storeBiomes(int layer, PalettedContainerRO<Holder<Biome>> biomeData) {
|
||||
if (biomes == null) {
|
||||
biomes = new PalettedContainer[getSectionCount()];
|
||||
}
|
||||
if (biomeData instanceof PalettedContainer<Holder<Biome>> palettedContainer) {
|
||||
biomes[layer] = palettedContainer.copy();
|
||||
} else {
|
||||
LOGGER.error(
|
||||
"Cannot correctly save biomes to history. Expected class type {} but got {}",
|
||||
PalettedContainer.class.getSimpleName(),
|
||||
biomeData.getClass().getSimpleName()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getFullBlock(int x, int y, int z) {
|
||||
BlockState state = BlockTypesCache.states[get(x, y, z)];
|
||||
return state.toBaseBlock(this, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSection(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer] != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] load(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public char[] loadIfPresent(int layer) {
|
||||
layer -= getMinSectionPosition();
|
||||
return blocks[layer];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlock(int x, int y, int z) {
|
||||
return BlockTypesCache.states[get(x, y, z)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSkyLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEmittedLight(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getHeightMap(HeightMapType type) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public char get(int x, int y, int z) {
|
||||
final int layer = (y >> 4) - getMinSectionPosition();
|
||||
final int index = (y & 15) << 8 | z << 4 | x;
|
||||
return blocks[layer][index];
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean trim(boolean aggressive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.MapChunkUtil;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
|
||||
//TODO un-very-break-this
|
||||
public class PaperweightMapChunkUtil extends MapChunkUtil<ClientboundLevelChunkWithLightPacket> {
|
||||
|
||||
public PaperweightMapChunkUtil() throws NoSuchFieldException {
|
||||
fieldX = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("TWO_MEGABYTES", "a"));
|
||||
fieldZ = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("x", "a"));
|
||||
fieldBitMask = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("z", "b"));
|
||||
fieldHeightMap = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("heightmaps", "b"));
|
||||
fieldChunkData = ClientboundLevelChunkWithLightPacket.class.getDeclaredField(Refraction.pickName("chunkData", "c"));
|
||||
fieldBlockEntities = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("buffer", "c"));
|
||||
fieldFull = ClientboundLevelChunkPacketData.class.getDeclaredField(Refraction.pickName("blockEntitiesData", "d"));
|
||||
fieldX.setAccessible(true);
|
||||
fieldZ.setAccessible(true);
|
||||
fieldBitMask.setAccessible(true);
|
||||
fieldHeightMap.setAccessible(true);
|
||||
fieldChunkData.setAccessible(true);
|
||||
fieldBlockEntities.setAccessible(true);
|
||||
fieldFull.setAccessible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientboundLevelChunkWithLightPacket createPacket() {
|
||||
// TODO ??? return new ClientboundLevelChunkPacket();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,703 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.destroystokyo.paper.util.maplist.EntityList;
|
||||
import com.fastasyncworldedit.bukkit.adapter.CachedBukkitAdapter;
|
||||
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
|
||||
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.FaweCache;
|
||||
import com.fastasyncworldedit.core.math.BitArrayUnstretched;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import io.papermc.paper.world.ChunkEntitySlices;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.IdMap;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.util.BitStorage;
|
||||
import net.minecraft.util.ExceptionCollector;
|
||||
import net.minecraft.util.SimpleBitStorage;
|
||||
import net.minecraft.util.ThreadingDetector;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.util.ZeroBitStorage;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.chunk.GlobalPalette;
|
||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||
import net.minecraft.world.level.chunk.LinearPalette;
|
||||
import net.minecraft.world.level.chunk.Palette;
|
||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
||||
import net.minecraft.world.level.gameevent.GameEventListener;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
public static final Field fieldData;
|
||||
|
||||
public static final Constructor<?> dataConstructor;
|
||||
|
||||
public static final Field fieldStorage;
|
||||
public static final Field fieldPalette;
|
||||
|
||||
private static final Field fieldTickingFluidCount;
|
||||
private static final Field fieldTickingBlockCount;
|
||||
private static final Field fieldNonEmptyBlockCount;
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldThreadingDetector;
|
||||
private static final long fieldThreadingDetectorOffset;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final MethodHandle methodRemoveGameEventListener;
|
||||
private static final MethodHandle methodremoveTickingBlockEntity;
|
||||
|
||||
private static final Field fieldRemove;
|
||||
|
||||
static final boolean POST_CHUNK_REWRITE;
|
||||
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
||||
private static Field LEVEL_CHUNK_ENTITIES;
|
||||
private static Field SERVER_LEVEL_ENTITY_MANAGER;
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
||||
fieldData.setAccessible(true);
|
||||
|
||||
Class<?> dataClazz = fieldData.getType();
|
||||
dataConstructor = dataClazz.getDeclaredConstructors()[0];
|
||||
dataConstructor.setAccessible(true);
|
||||
|
||||
fieldStorage = dataClazz.getDeclaredField(Refraction.pickName("storage", "b"));
|
||||
fieldStorage.setAccessible(true);
|
||||
fieldPalette = dataClazz.getDeclaredField(Refraction.pickName("palette", "c"));
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldTickingFluidCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingFluidCount", "h"));
|
||||
fieldTickingFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "g"));
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "f"));
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
|
||||
"getVisibleChunkIfPresent",
|
||||
"b"
|
||||
), long.class);
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
|
||||
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector);
|
||||
|
||||
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldThreadingDetector = null;
|
||||
fieldThreadingDetectorOffset = -1;
|
||||
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
|
||||
Refraction.pickName("removeGameEventListener", "a"),
|
||||
BlockEntity.class,
|
||||
ServerLevel.class
|
||||
);
|
||||
removeGameEventListener.setAccessible(true);
|
||||
methodRemoveGameEventListener = MethodHandles.lookup().unreflect(removeGameEventListener);
|
||||
|
||||
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
|
||||
Refraction.pickName(
|
||||
"removeBlockEntityTicker",
|
||||
"l"
|
||||
), BlockPos.class
|
||||
);
|
||||
removeBlockEntityTicker.setAccessible(true);
|
||||
methodremoveTickingBlockEntity = MethodHandles.lookup().unreflect(removeBlockEntityTicker);
|
||||
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
boolean chunkRewrite;
|
||||
try {
|
||||
ServerLevel.class.getDeclaredMethod("getEntityLookup");
|
||||
chunkRewrite = true;
|
||||
PAPER_CHUNK_GEN_ALL_ENTITIES = ChunkEntitySlices.class.getDeclaredMethod("getAllEntities");
|
||||
PAPER_CHUNK_GEN_ALL_ENTITIES.setAccessible(true);
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
chunkRewrite = false;
|
||||
}
|
||||
try {
|
||||
// Paper - Pre-Chunk-Update
|
||||
LEVEL_CHUNK_ENTITIES = LevelChunk.class.getDeclaredField("entities");
|
||||
LEVEL_CHUNK_ENTITIES.setAccessible(true);
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
try {
|
||||
// Non-Paper
|
||||
SERVER_LEVEL_ENTITY_MANAGER = ServerLevel.class.getDeclaredField(Refraction.pickName("entityManager", "P"));
|
||||
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
}
|
||||
POST_CHUNK_REWRITE = chunkRewrite;
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable rethrow) {
|
||||
rethrow.printStackTrace();
|
||||
throw new RuntimeException(rethrow);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean setSectionAtomic(
|
||||
LevelChunkSection[] sections,
|
||||
LevelChunkSection expected,
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// There is no point in having a functional semaphore for paper servers.
|
||||
private static final ThreadLocal<DelegateSemaphore> SEMAPHORE_THREAD_LOCAL =
|
||||
ThreadLocal.withInitial(() -> new DelegateSemaphore(1, null));
|
||||
|
||||
static DelegateSemaphore applyLock(LevelChunkSection section) {
|
||||
if (PaperLib.isPaper()) {
|
||||
return SEMAPHORE_THREAD_LOCAL.get();
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject(
|
||||
blocks,
|
||||
fieldThreadingDetectorOffset
|
||||
);
|
||||
synchronized (currentThreadingDetector) {
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock);
|
||||
return newLock;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelChunk ensureLoaded(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunk(chunkX, chunkZ, false);
|
||||
if (nmsChunk != null) {
|
||||
return nmsChunk;
|
||||
}
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
} else {
|
||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
if (nmsChunk != null) {
|
||||
addTicket(serverLevel, chunkX, chunkZ);
|
||||
return nmsChunk;
|
||||
}
|
||||
// Avoid "async" methods from the main thread.
|
||||
if (Fawe.isMainThread()) {
|
||||
return serverLevel.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||
try {
|
||||
CraftChunk chunk = (CraftChunk) future.get();
|
||||
return chunk.getHandle();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||
// Ensure chunk is definitely loaded before applying a ticket
|
||||
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||
.getChunkSource()
|
||||
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||
}
|
||||
|
||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||
try {
|
||||
return (ChunkHolder) methodGetVisibleChunk.invoke(chunkMap, ChunkPos.asLong(chunkX, chunkZ));
|
||||
} catch (Throwable thr) {
|
||||
throw new RuntimeException(thr);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void sendChunk(ServerLevel nmsWorld, int chunkX, int chunkZ, boolean lighting) {
|
||||
ChunkHolder chunkHolder = getPlayerChunk(nmsWorld, chunkX, chunkZ);
|
||||
if (chunkHolder == null) {
|
||||
return;
|
||||
}
|
||||
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
|
||||
LevelChunk levelChunk;
|
||||
if (PaperLib.isPaper()) {
|
||||
// getChunkAtIfLoadedImmediately is paper only
|
||||
levelChunk = nmsWorld
|
||||
.getChunkSource()
|
||||
.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||
} else {
|
||||
levelChunk = ((Optional<LevelChunk>) ((Either) chunkHolder
|
||||
.getTickingChunkFuture() // method is not present with new paper chunk system
|
||||
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left())
|
||||
.orElse(null);
|
||||
}
|
||||
if (levelChunk == null) {
|
||||
return;
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
ClientboundLevelChunkWithLightPacket packet;
|
||||
if (PaperLib.isPaper()) {
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
false // last false is to not bother with x-ray
|
||||
);
|
||||
} else {
|
||||
// deprecated on paper - deprecation suppressed
|
||||
packet = new ClientboundLevelChunkWithLightPacket(
|
||||
levelChunk,
|
||||
nmsWorld.getChunkSource().getLightEngine(),
|
||||
null,
|
||||
null,
|
||||
true
|
||||
);
|
||||
}
|
||||
nearbyPlayers(nmsWorld, coordIntPair).forEach(p -> p.connection.send(packet));
|
||||
});
|
||||
}
|
||||
|
||||
private static List<ServerPlayer> nearbyPlayers(ServerLevel serverLevel, ChunkPos coordIntPair) {
|
||||
return serverLevel.getChunkSource().chunkMap.getPlayers(coordIntPair, false);
|
||||
}
|
||||
|
||||
/*
|
||||
NMS conversion
|
||||
*/
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final char[] blocks,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
return newChunkSection(layer, null, blocks, adapter, biomeRegistry, biomes);
|
||||
}
|
||||
|
||||
public static LevelChunkSection newChunkSection(
|
||||
final int layer,
|
||||
final Function<Integer, char[]> get,
|
||||
char[] set,
|
||||
CachedBukkitAdapter adapter,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer, biomeRegistry, biomes);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.INSTANCE.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.INSTANCE.PALETTE_TO_BLOCK.get();
|
||||
final long[] blockStates = FaweCache.INSTANCE.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.INSTANCE.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int num_palette;
|
||||
if (get == null) {
|
||||
num_palette = createPalette(blockToPalette, paletteToBlock, blocksCopy, set, adapter, null);
|
||||
} else {
|
||||
num_palette = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, get, set, adapter, null);
|
||||
}
|
||||
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (bitsPerEntry > 0 && bitsPerEntry < 5) {
|
||||
bitsPerEntry = 4;
|
||||
} else if (bitsPerEntry > 8) {
|
||||
bitsPerEntry = MathMan.log2nlz(Block.BLOCK_STATE_REGISTRY.size() - 1);
|
||||
}
|
||||
|
||||
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
|
||||
final int blockBitArrayEnd = MathMan.ceilZero((float) 4096 / blocksPerLong);
|
||||
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) {
|
||||
blockStates[i] = 0;
|
||||
}
|
||||
} else {
|
||||
final BitArrayUnstretched bitArray = new BitArrayUnstretched(bitsPerEntryNonZero, 4096, blockStates);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
|
||||
final BitStorage nmsBits;
|
||||
if (bitsPerEntry == 0) {
|
||||
nmsBits = new ZeroBitStorage(4096);
|
||||
} else {
|
||||
nmsBits = new SimpleBitStorage(bitsPerEntry, 4096, bits);
|
||||
}
|
||||
List<net.minecraft.world.level.block.state.BlockState> palette;
|
||||
if (bitsPerEntry < 9) {
|
||||
palette = new ArrayList<>();
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
final BlockState state = BlockTypesCache.states[ordinal];
|
||||
palette.add(((PaperweightBlockMaterial) state.getMaterial()).getState());
|
||||
}
|
||||
} else {
|
||||
palette = List.of();
|
||||
}
|
||||
|
||||
// Create palette with data
|
||||
@SuppressWarnings("deprecation") // constructor is deprecated on paper, but needed to keep compatibility with spigot
|
||||
final PalettedContainer<net.minecraft.world.level.block.state.BlockState> blockStatePalettedContainer =
|
||||
new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
PalettedContainer.Strategy.SECTION_STATES.getConfiguration(Block.BLOCK_STATE_REGISTRY, bitsPerEntry),
|
||||
nmsBits,
|
||||
palette
|
||||
);
|
||||
if (biomes == null) {
|
||||
IdMap<Holder<Biome>> biomeHolderIdMap = biomeRegistry.asHolderIdMap();
|
||||
biomes = new PalettedContainer<>(
|
||||
biomeHolderIdMap,
|
||||
biomeHolderIdMap.byIdOrThrow(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(
|
||||
BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
return new LevelChunkSection(layer, blockStatePalettedContainer, biomes);
|
||||
} catch (final Throwable e) {
|
||||
throw e;
|
||||
} finally {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
Arrays.fill(paletteToBlock, Integer.MAX_VALUE);
|
||||
Arrays.fill(blockStates, 0);
|
||||
Arrays.fill(blocksCopy, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Only deprecated in paper
|
||||
private static LevelChunkSection newChunkSection(
|
||||
int layer,
|
||||
Registry<Biome> biomeRegistry,
|
||||
@Nullable PalettedContainer<Holder<Biome>> biomes
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return new LevelChunkSection(layer, biomeRegistry);
|
||||
}
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
|
||||
Block.BLOCK_STATE_REGISTRY,
|
||||
Blocks.AIR.defaultBlockState(),
|
||||
PalettedContainer.Strategy.SECTION_STATES,
|
||||
null
|
||||
);
|
||||
return new LevelChunkSection(layer, dataPaletteBlocks, biomes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PalettedContainer<Biome>}. Should only be used if no biome container existed beforehand.
|
||||
*/
|
||||
public static PalettedContainer<Holder<Biome>> getBiomePalettedContainer(
|
||||
BiomeType[] biomes,
|
||||
IdMap<Holder<Biome>> biomeRegistry
|
||||
) {
|
||||
if (biomes == null) {
|
||||
return null;
|
||||
}
|
||||
BukkitImplAdapter<?> adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||
// Don't stream this as typically will see 1-4 biomes; stream overhead is large for the small length
|
||||
Map<BiomeType, Holder<Biome>> palette = new HashMap<>();
|
||||
for (BiomeType biomeType : new LinkedList<>(Arrays.asList(biomes))) {
|
||||
Holder<Biome> biome;
|
||||
if (biomeType == null) {
|
||||
biome = biomeRegistry.byId(adapter.getInternalBiomeId(BiomeTypes.PLAINS));
|
||||
} else {
|
||||
biome = biomeRegistry.byId(adapter.getInternalBiomeId(biomeType));
|
||||
}
|
||||
palette.put(biomeType, biome);
|
||||
}
|
||||
int biomeCount = palette.size();
|
||||
int bitsPerEntry = MathMan.log2nlz(biomeCount - 1);
|
||||
Object configuration = PalettedContainer.Strategy.SECTION_STATES.getConfiguration(
|
||||
new FakeIdMapBiome(biomeCount),
|
||||
bitsPerEntry
|
||||
);
|
||||
if (bitsPerEntry > 3) {
|
||||
bitsPerEntry = MathMan.log2nlz(biomeRegistry.size() - 1);
|
||||
}
|
||||
PalettedContainer<Holder<Biome>> biomePalettedContainer = new PalettedContainer<>(
|
||||
biomeRegistry,
|
||||
biomeRegistry.byIdOrThrow(adapter.getInternalBiomeId(BiomeTypes.PLAINS)),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES,
|
||||
null
|
||||
);
|
||||
|
||||
final Palette<Holder<Biome>> biomePalette;
|
||||
if (bitsPerEntry == 0) {
|
||||
biomePalette = new SingleValuePalette<>(
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else if (bitsPerEntry == 4) {
|
||||
biomePalette = LinearPalette.create(
|
||||
4,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else if (bitsPerEntry < 9) {
|
||||
biomePalette = HashMapPalette.create(
|
||||
bitsPerEntry,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
new ArrayList<>(palette.values()) // Must be modifiable
|
||||
);
|
||||
} else {
|
||||
biomePalette = GlobalPalette.create(
|
||||
bitsPerEntry,
|
||||
biomePalettedContainer.registry,
|
||||
biomePalettedContainer,
|
||||
null // unused
|
||||
);
|
||||
}
|
||||
|
||||
int bitsPerEntryNonZero = Math.max(bitsPerEntry, 1); // We do want to use zero sometimes
|
||||
final int blocksPerLong = MathMan.floorZero((double) 64 / bitsPerEntryNonZero);
|
||||
final int arrayLength = MathMan.ceilZero(64f / blocksPerLong);
|
||||
|
||||
|
||||
BitStorage bitStorage = bitsPerEntry == 0 ? new ZeroBitStorage(64) : new SimpleBitStorage(
|
||||
bitsPerEntry,
|
||||
64,
|
||||
new long[arrayLength]
|
||||
);
|
||||
|
||||
try {
|
||||
Object data = dataConstructor.newInstance(configuration, bitStorage, biomePalette);
|
||||
fieldData.set(biomePalettedContainer, data);
|
||||
int index = 0;
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int z = 0; z < 4; z++) {
|
||||
for (int x = 0; x < 4; x++, index++) {
|
||||
BiomeType biomeType = biomes[index];
|
||||
if (biomeType == null) {
|
||||
continue;
|
||||
}
|
||||
Holder<Biome> biome = biomeRegistry.byId(WorldEditPlugin
|
||||
.getInstance()
|
||||
.getBukkitImplAdapter()
|
||||
.getInternalBiomeId(biomeType));
|
||||
if (biome == null) {
|
||||
continue;
|
||||
}
|
||||
biomePalettedContainer.set(x, y, z, biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return biomePalettedContainer;
|
||||
}
|
||||
|
||||
public static void clearCounts(final LevelChunkSection section) throws IllegalAccessException {
|
||||
fieldTickingFluidCount.setShort(section, (short) 0);
|
||||
fieldTickingBlockCount.setShort(section, (short) 0);
|
||||
}
|
||||
|
||||
public static BiomeType adapt(Holder<Biome> biome, LevelAccessor levelAccessor) {
|
||||
final Registry<Biome> biomeRegistry = levelAccessor.registryAccess().ownedRegistryOrThrow(Registry.BIOME_REGISTRY);
|
||||
if (biomeRegistry.getKey(biome.value()) == null) {
|
||||
return biomeRegistry.asHolderIdMap().getId(biome) == -1 ? BiomeTypes.OCEAN
|
||||
: null;
|
||||
}
|
||||
return BiomeTypes.get(biome.unwrapKey().orElseThrow().location().toString());
|
||||
}
|
||||
|
||||
static void removeBeacon(BlockEntity beacon, LevelChunk levelChunk) {
|
||||
try {
|
||||
if (levelChunk.loaded || levelChunk.level.isClientSide()) {
|
||||
BlockEntity blockEntity = levelChunk.blockEntities.remove(beacon.getBlockPos());
|
||||
if (blockEntity != null) {
|
||||
if (!levelChunk.level.isClientSide) {
|
||||
methodRemoveGameEventListener.invoke(levelChunk, beacon, levelChunk.level);
|
||||
}
|
||||
fieldRemove.set(beacon, true);
|
||||
}
|
||||
}
|
||||
methodremoveTickingBlockEntity.invoke(levelChunk, beacon.getBlockPos());
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static List<Entity> getEntities(LevelChunk chunk) {
|
||||
ExceptionCollector<RuntimeException> collector = new ExceptionCollector<>();
|
||||
if (PaperLib.isPaper()) {
|
||||
if (POST_CHUNK_REWRITE) {
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (List<Entity>) PAPER_CHUNK_GEN_ALL_ENTITIES.invoke(chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ));
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=true]", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
EntityList entityList = (EntityList) LEVEL_CHUNK_ENTITIES.get(chunk);
|
||||
return List.of(entityList.getRawData());
|
||||
} catch (IllegalAccessException e) {
|
||||
collector.add(new RuntimeException("Failed to lookup entities [POST_CHUNK_REWRITE=false]", e));
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return ((PersistentEntitySectionManager<Entity>) (SERVER_LEVEL_ENTITY_MANAGER.get(chunk.level))).getEntities(chunk.getPos());
|
||||
} catch (IllegalAccessException e) {
|
||||
collector.add(new RuntimeException("Failed to lookup entities [PAPER=false]", e));
|
||||
}
|
||||
collector.throwIfPresent();
|
||||
return List.of();
|
||||
}
|
||||
|
||||
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {
|
||||
|
||||
@Override
|
||||
public int getId(final net.minecraft.world.level.block.state.BlockState entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public net.minecraft.world.level.block.state.BlockState byId(final int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<net.minecraft.world.level.block.state.BlockState> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record FakeIdMapBiome(int size) implements IdMap<Biome> {
|
||||
|
||||
@Override
|
||||
public int getId(final Biome entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Biome byId(final int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Iterator<Biome> iterator() {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
||||
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||
import com.fastasyncworldedit.core.queue.IChunk;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||
import com.fastasyncworldedit.core.registry.state.PropertyKey;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PaperweightPostProcessor implements IBatchProcessor {
|
||||
|
||||
@Override
|
||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||
return set;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void postProcess(final IChunk chunk, final IChunkGet iChunkGet, final IChunkSet iChunkSet) {
|
||||
boolean tickFluid = Settings.settings().EXPERIMENTAL.ALLOW_TICK_FLUIDS;
|
||||
// The PostProcessor shouldn't be added, but just in case
|
||||
if (!tickFluid) {
|
||||
return;
|
||||
}
|
||||
PaperweightGetBlocks_Copy getBlocks = (PaperweightGetBlocks_Copy) iChunkGet;
|
||||
layer:
|
||||
for (int layer = iChunkSet.getMinSectionPosition(); layer <= iChunkSet.getMaxSectionPosition(); layer++) {
|
||||
char[] set = iChunkSet.loadIfPresent(layer);
|
||||
if (set == null) {
|
||||
// No edit means no need to process
|
||||
continue;
|
||||
}
|
||||
char[] get = null;
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
char ordinal = set[i];
|
||||
char replacedOrdinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
boolean fromGet = false; // Used for liquids
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
// If this is null, then it's because we're loading a layer in the range of 0->15, but blocks aren't
|
||||
// actually being set
|
||||
if (get == null) {
|
||||
continue layer;
|
||||
}
|
||||
fromGet = true;
|
||||
ordinal = replacedOrdinal = get[i];
|
||||
}
|
||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||
continue;
|
||||
} else if (!fromGet) { // if fromGet, don't do the same again
|
||||
if (get == null) {
|
||||
get = getBlocks.load(layer);
|
||||
}
|
||||
replacedOrdinal = get[i];
|
||||
}
|
||||
boolean ticking = BlockTypesCache.ticking[ordinal];
|
||||
boolean replacedWasTicking = BlockTypesCache.ticking[replacedOrdinal];
|
||||
boolean replacedWasLiquid = false;
|
||||
BlockState replacedState = null;
|
||||
if (!ticking) {
|
||||
// If the block being replaced was not ticking, it cannot be a liquid
|
||||
if (!replacedWasTicking) {
|
||||
continue;
|
||||
}
|
||||
// If the block being replaced is not fluid, we do not need to worry
|
||||
if (!(replacedWasLiquid =
|
||||
(replacedState = BlockState.getFromOrdinal(replacedOrdinal)).getMaterial().isLiquid())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
BlockState state = BlockState.getFromOrdinal(ordinal);
|
||||
boolean liquid = state.getMaterial().isLiquid();
|
||||
int x = i & 15;
|
||||
int y = (i >> 8) & 15;
|
||||
int z = (i >> 4) & 15;
|
||||
BlockPos position = new BlockPos((chunk.getX() << 4) + x, (layer << 4) + y, (chunk.getZ() << 4) + z);
|
||||
if (liquid || replacedWasLiquid) {
|
||||
if (liquid) {
|
||||
addFluid(getBlocks.serverLevel, state, position);
|
||||
continue;
|
||||
}
|
||||
// If the replaced fluid (is?) adjacent to water. Do not bother to check adjacent chunks(sections) as this
|
||||
// may be time consuming. Chances are any fluid blocks in adjacent chunks are being replaced or will end up
|
||||
// being ticked anyway. We only need it to be "hit" once.
|
||||
if (!wasAdjacentToWater(get, set, i, x, y, z)) {
|
||||
continue;
|
||||
}
|
||||
addFluid(getBlocks.serverLevel, replacedState, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Extent construct(final Extent child) {
|
||||
throw new UnsupportedOperationException("Processing only");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessorScope getScope() {
|
||||
return ProcessorScope.READING_SET_BLOCKS;
|
||||
}
|
||||
|
||||
private boolean wasAdjacentToWater(char[] get, char[] set, int i, int x, int y, int z) {
|
||||
if (set == null || get == null) {
|
||||
return false;
|
||||
}
|
||||
char ordinal;
|
||||
char reserved = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||
if (x > 0 && set[i - 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (x < 15 && set[i + 1] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 1])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z > 0 && set[i - 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (z < 15 && set[i + 16] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i + 16])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y > 0 && set[i - 256] != reserved) {
|
||||
if (BlockTypesCache.ticking[(ordinal = get[i - 256])] && isFluid(ordinal)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (y < 15 && set[i + 256] != reserved) {
|
||||
return BlockTypesCache.ticking[(ordinal = get[i + 256])] && isFluid(ordinal);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private boolean isFluid(char ordinal) {
|
||||
return BlockState.getFromOrdinal(ordinal).getMaterial().isLiquid();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void addFluid(final ServerLevel serverLevel, final BlockState replacedState, final BlockPos position) {
|
||||
Fluid type;
|
||||
if (replacedState.getBlockType() == BlockTypes.LAVA) {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.LAVA : Fluids.FLOWING_LAVA;
|
||||
} else {
|
||||
type = (int) replacedState.getState(PropertyKey.LEVEL) == 0 ? Fluids.WATER : Fluids.FLOWING_WATER;
|
||||
}
|
||||
serverLevel.scheduleTick(
|
||||
position,
|
||||
type,
|
||||
type.getTickDelay(serverLevel)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.configuration.Settings;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NMSRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.fastasyncworldedit.core.util.MathMan;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import net.minecraft.server.level.ChunkMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class PaperweightStarlightRelighter implements Relighter {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32
|
||||
private static final int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
|
||||
|
||||
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
|
||||
private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
|
||||
|
||||
|
||||
private final ServerLevel serverLevel;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final Long2ObjectLinkedOpenHashMap<LongSet> regions = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final ReentrantLock areaLock = new ReentrantLock();
|
||||
private final NMSRelighter delegate;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public PaperweightStarlightRelighter(ServerLevel serverLevel, IQueueExtent<IQueueChunk> queue) {
|
||||
this.serverLevel = serverLevel;
|
||||
this.delegate = new NMSRelighter(queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
|
||||
areaLock.lock();
|
||||
try {
|
||||
long key = MathMan.pairInt(cx >> CHUNKS_PER_BATCH_SQRT_LOG2, cz >> CHUNKS_PER_BATCH_SQRT_LOG2);
|
||||
// TODO probably submit here already if chunks.size == CHUNKS_PER_BATCH?
|
||||
LongSet chunks = this.regions.computeIfAbsent(key, k -> new LongArraySet(CHUNKS_PER_BATCH >> 2));
|
||||
chunks.add(ChunkPos.asLong(cx, cz));
|
||||
} finally {
|
||||
areaLock.unlock();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLightUpdate(int x, int y, int z) {
|
||||
delegate.addLightUpdate(x, y, z);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is called "recursively", iterating and removing elements
|
||||
* from the regions linked map. This way, chunks are loaded in batches to avoid
|
||||
* OOMEs.
|
||||
*/
|
||||
@Override
|
||||
public void fixLightingSafe(boolean sky) {
|
||||
this.areaLock.lock();
|
||||
try {
|
||||
if (regions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LongSet first = regions.removeFirst();
|
||||
fixLighting(first, () -> fixLightingSafe(true));
|
||||
} finally {
|
||||
this.areaLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Processes a set of chunks and runs an action afterwards.
|
||||
* The action is run async, the chunks are partly processed on the main thread
|
||||
* (as required by the server).
|
||||
*/
|
||||
private void fixLighting(LongSet chunks, Runnable andThen) {
|
||||
// convert from long keys to ChunkPos
|
||||
Set<ChunkPos> coords = new HashSet<>();
|
||||
LongIterator iterator = chunks.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
coords.add(new ChunkPos(iterator.nextLong()));
|
||||
}
|
||||
TaskManager.taskManager().task(() -> {
|
||||
// trigger chunk load and apply ticket on main thread
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (ChunkPos pos : coords) {
|
||||
futures.add(serverLevel.getWorld().getChunkAtAsync(pos.x, pos.z)
|
||||
.thenAccept(c -> serverLevel.getChunkSource().addTicketAtLevel(
|
||||
FAWE_TICKET,
|
||||
pos,
|
||||
LIGHT_LEVEL,
|
||||
Unit.INSTANCE
|
||||
))
|
||||
);
|
||||
}
|
||||
// collect futures and trigger relight once all chunks are loaded
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenAccept(v ->
|
||||
invokeRelight(
|
||||
coords,
|
||||
c -> {
|
||||
}, // no callback for single chunks required
|
||||
i -> {
|
||||
if (i != coords.size()) {
|
||||
LOGGER.warn("Processed {} chunks instead of {}", i, coords.size());
|
||||
}
|
||||
// post process chunks on main thread
|
||||
TaskManager.taskManager().task(() -> postProcessChunks(coords));
|
||||
// call callback on our own threads
|
||||
TaskManager.taskManager().async(andThen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void invokeRelight(
|
||||
Set<ChunkPos> coords,
|
||||
Consumer<ChunkPos> chunkCallback,
|
||||
IntConsumer processCallback
|
||||
) {
|
||||
try {
|
||||
serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Error occurred on relighting", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the server to unload the chunks again.
|
||||
* Also, if chunk packets are sent delayed, we need to do that here
|
||||
*/
|
||||
private void postProcessChunks(Set<ChunkPos> coords) {
|
||||
boolean delay = Settings.settings().LIGHTING.DELAY_PACKET_SENDING;
|
||||
for (ChunkPos pos : coords) {
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
if (delay) { // we still need to send the block changes of that chunk
|
||||
PaperweightPlatformAdapter.sendChunk(serverLevel, x, z, false);
|
||||
}
|
||||
serverLevel.getChunkSource().removeTicketAtLevel(FAWE_TICKET, pos, LIGHT_LEVEL, Unit.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeLighting() {
|
||||
this.delegate.removeLighting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixBlockLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixSkyLighting() {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReentrantLock getLock() {
|
||||
return this.lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
fixLightingSafe(true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1;
|
||||
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelightMode;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||
import com.fastasyncworldedit.core.extent.processor.lighting.RelighterFactory;
|
||||
import com.fastasyncworldedit.core.queue.IQueueChunk;
|
||||
import com.fastasyncworldedit.core.queue.IQueueExtent;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PaperweightStarlightRelighterFactory implements RelighterFactory {
|
||||
|
||||
@Override
|
||||
public @Nonnull
|
||||
@SuppressWarnings("rawtypes")
|
||||
Relighter createRelighter(RelightMode relightMode, World world, IQueueExtent<IQueueChunk> queue) {
|
||||
org.bukkit.World w = Bukkit.getWorld(world.getName());
|
||||
if (w == null) {
|
||||
return NullRelighter.INSTANCE;
|
||||
}
|
||||
return new PaperweightStarlightRelighter(((CraftWorld) w).getHandle(), queue);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.nbt;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.LazyCompoundTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.StringTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightLazyCompoundTag extends LazyCompoundTag {
|
||||
|
||||
private final Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier;
|
||||
private CompoundTag compoundTag;
|
||||
|
||||
public PaperweightLazyCompoundTag(Supplier<net.minecraft.nbt.CompoundTag> compoundTagSupplier) {
|
||||
super(new HashMap<>());
|
||||
this.compoundTagSupplier = compoundTagSupplier;
|
||||
}
|
||||
|
||||
public PaperweightLazyCompoundTag(net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
this(() -> compoundTag);
|
||||
}
|
||||
|
||||
public net.minecraft.nbt.CompoundTag get() {
|
||||
return compoundTagSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Tag> getValue() {
|
||||
if (compoundTag == null) {
|
||||
compoundTag = (CompoundTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(compoundTagSupplier.get());
|
||||
}
|
||||
return compoundTag.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundBinaryTag asBinaryTag() {
|
||||
getValue();
|
||||
return compoundTag.asBinaryTag();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return compoundTagSupplier.get().contains(key);
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
return compoundTagSupplier.get().getByteArray(key);
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
return compoundTagSupplier.get().getByte(key);
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
return compoundTagSupplier.get().getDouble(key);
|
||||
}
|
||||
|
||||
public double asDouble(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsDouble();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
return compoundTagSupplier.get().getFloat(key);
|
||||
}
|
||||
|
||||
public int[] getIntArray(String key) {
|
||||
return compoundTagSupplier.get().getIntArray(key);
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
return compoundTagSupplier.get().getInt(key);
|
||||
}
|
||||
|
||||
public int asInt(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsInt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Tag> getList(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag nbtList) {
|
||||
ArrayList<Tag> list = new ArrayList<>();
|
||||
for (net.minecraft.nbt.Tag elem : nbtList) {
|
||||
if (elem instanceof net.minecraft.nbt.CompoundTag compoundTag) {
|
||||
list.add(new PaperweightLazyCompoundTag(compoundTag));
|
||||
} else {
|
||||
list.add(WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(elem));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ListTag getListTag(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof net.minecraft.nbt.ListTag) {
|
||||
return (ListTag) WorldEditPlugin.getInstance().getBukkitImplAdapter().toNative(tag);
|
||||
}
|
||||
return new ListTag(StringTag.class, Collections.emptyList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Tag> List<T> getList(String key, Class<T> listType) {
|
||||
ListTag listTag = getListTag(key);
|
||||
if (listTag.getType().equals(listType)) {
|
||||
return (List<T>) listTag.getValue();
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public long[] getLongArray(String key) {
|
||||
return compoundTagSupplier.get().getLongArray(key);
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
return compoundTagSupplier.get().getLong(key);
|
||||
}
|
||||
|
||||
public long asLong(String key) {
|
||||
net.minecraft.nbt.Tag tag = compoundTagSupplier.get().get(key);
|
||||
if (tag instanceof NumericTag numTag) {
|
||||
return numTag.getAsLong();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
return compoundTagSupplier.get().getShort(key);
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
return compoundTagSupplier.get().getString(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return compoundTagSupplier.get().toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,564 @@
|
||||
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.regen;
|
||||
|
||||
import com.fastasyncworldedit.bukkit.adapter.Regenerator;
|
||||
import com.fastasyncworldedit.core.Fawe;
|
||||
import com.fastasyncworldedit.core.queue.IChunkCache;
|
||||
import com.fastasyncworldedit.core.queue.IChunkGet;
|
||||
import com.fastasyncworldedit.core.util.ReflectionUtils;
|
||||
import com.fastasyncworldedit.core.util.TaskManager;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1.PaperweightGetBlocks;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.io.file.SafeFiles;
|
||||
import com.sk89q.worldedit.world.RegenOptions;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkTaskPriorityQueueSorter.Message;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.util.thread.ProcessorHandle;
|
||||
import net.minecraft.util.thread.ProcessorMailbox;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelHeightAccessor;
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.dimension.LevelStem;
|
||||
import net.minecraft.world.level.levelgen.FlatLevelSource;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.levelgen.blending.BlendingData;
|
||||
import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_19_R1.generator.CustomChunkGenerator;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, LevelChunk, PaperweightRegen.ChunkStatusWrap> {
|
||||
|
||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||
|
||||
private static final Field serverWorldsField;
|
||||
private static final Field paperConfigField;
|
||||
private static final Field flatBedrockField;
|
||||
private static final Field generatorSettingFlatField;
|
||||
private static final Field generatorSettingBaseSupplierField;
|
||||
private static final Field delegateField;
|
||||
private static final Field chunkSourceField;
|
||||
private static final Field ringPositionsField;
|
||||
private static final Field hasGeneratedPositionsField;
|
||||
|
||||
//list of chunk stati in correct order without FULL
|
||||
private static final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
chunkStati.put(ChunkStatus.EMPTY, Concurrency.FULL); // empty: radius -1, does nothing
|
||||
chunkStati.put(ChunkStatus.STRUCTURE_STARTS, Concurrency.NONE); // structure starts: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.STRUCTURE_REFERENCES,
|
||||
Concurrency.FULL
|
||||
); // structure refs: radius 8, but only writes to current chunk
|
||||
chunkStati.put(ChunkStatus.BIOMES, Concurrency.FULL); // biomes: radius 0
|
||||
chunkStati.put(ChunkStatus.NOISE, Concurrency.RADIUS); // noise: radius 8
|
||||
chunkStati.put(ChunkStatus.SURFACE, Concurrency.NONE); // surface: radius 0, requires NONE
|
||||
chunkStati.put(ChunkStatus.CARVERS, Concurrency.NONE); // carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIQUID_CARVERS,
|
||||
Concurrency.NONE
|
||||
); // liquid carvers: radius 0, but RADIUS and FULL change results
|
||||
chunkStati.put(ChunkStatus.FEATURES, Concurrency.NONE); // features: uses unsynchronized maps
|
||||
chunkStati.put(
|
||||
ChunkStatus.LIGHT,
|
||||
Concurrency.FULL
|
||||
); // light: radius 1, but no writes to other chunks, only current chunk
|
||||
chunkStati.put(ChunkStatus.SPAWN, Concurrency.FULL); // spawn: radius 0
|
||||
chunkStati.put(ChunkStatus.HEIGHTMAPS, Concurrency.FULL); // heightmaps: radius 0
|
||||
|
||||
try {
|
||||
serverWorldsField = CraftServer.class.getDeclaredField("worlds");
|
||||
serverWorldsField.setAccessible(true);
|
||||
|
||||
Field tmpPaperConfigField;
|
||||
Field tmpFlatBedrockField;
|
||||
try { //only present on paper
|
||||
tmpPaperConfigField = Level.class.getDeclaredField("paperConfig");
|
||||
tmpPaperConfigField.setAccessible(true);
|
||||
|
||||
tmpFlatBedrockField = tmpPaperConfigField.getType().getDeclaredField("generateFlatBedrock");
|
||||
tmpFlatBedrockField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
tmpPaperConfigField = null;
|
||||
tmpFlatBedrockField = null;
|
||||
}
|
||||
paperConfigField = tmpPaperConfigField;
|
||||
flatBedrockField = tmpFlatBedrockField;
|
||||
|
||||
generatorSettingBaseSupplierField = NoiseBasedChunkGenerator.class.getDeclaredField(Refraction.pickName(
|
||||
"settings", "g"));
|
||||
generatorSettingBaseSupplierField.setAccessible(true);
|
||||
|
||||
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "f"));
|
||||
generatorSettingFlatField.setAccessible(true);
|
||||
|
||||
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
|
||||
delegateField.setAccessible(true);
|
||||
|
||||
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "L"));
|
||||
chunkSourceField.setAccessible(true);
|
||||
|
||||
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "i"));
|
||||
ringPositionsField.setAccessible(true);
|
||||
|
||||
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "j"));
|
||||
hasGeneratedPositionsField.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
//runtime
|
||||
private ServerLevel originalServerWorld;
|
||||
private ServerChunkCache originalChunkProvider;
|
||||
private ServerLevel freshWorld;
|
||||
private ServerChunkCache freshChunkProvider;
|
||||
private LevelStorageSource.LevelStorageAccess session;
|
||||
private StructureTemplateManager structureTemplateManager;
|
||||
private ThreadedLevelLightEngine threadedLevelLightEngine;
|
||||
private ChunkGenerator chunkGenerator;
|
||||
|
||||
private Path tempDir;
|
||||
|
||||
private boolean generateFlatBedrock = false;
|
||||
|
||||
public PaperweightRegen(org.bukkit.World originalBukkitWorld, Region region, Extent target, RegenOptions options) {
|
||||
super(originalBukkitWorld, region, target, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
try {
|
||||
generateFlatBedrock = flatBedrockField.getBoolean(paperConfigField.get(originalServerWorld));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected boolean initNewWorld() throws Exception {
|
||||
//world folder
|
||||
tempDir = java.nio.file.Files.createTempDirectory("FastAsyncWorldEditWorldGen");
|
||||
|
||||
//prepare for world init (see upstream implementation for reference)
|
||||
org.bukkit.World.Environment environment = originalBukkitWorld.getEnvironment();
|
||||
org.bukkit.generator.ChunkGenerator generator = originalBukkitWorld.getGenerator();
|
||||
LevelStorageSource levelStorageSource = LevelStorageSource.createDefault(tempDir);
|
||||
ResourceKey<LevelStem> levelStemResourceKey = getWorldDimKey(environment);
|
||||
session = levelStorageSource.createAccess("faweregentempworld", levelStemResourceKey);
|
||||
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
|
||||
|
||||
MinecraftServer server = originalServerWorld.getCraftServer().getServer();
|
||||
WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
|
||||
WorldGenSettings newOpts = options.getSeed().isPresent()
|
||||
? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
|
||||
: originalOpts;
|
||||
LevelSettings newWorldSettings = new LevelSettings(
|
||||
"faweregentempworld",
|
||||
originalWorldData.settings.gameType(),
|
||||
originalWorldData.settings.hardcore(),
|
||||
originalWorldData.settings.difficulty(),
|
||||
originalWorldData.settings.allowCommands(),
|
||||
originalWorldData.settings.gameRules(),
|
||||
originalWorldData.settings.getDataPackConfig()
|
||||
);
|
||||
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable());
|
||||
|
||||
BiomeProvider biomeProvider = getBiomeProvider();
|
||||
|
||||
//init world
|
||||
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
|
||||
server,
|
||||
server.executor,
|
||||
session,
|
||||
newWorldData,
|
||||
originalServerWorld.dimension(),
|
||||
newOpts.dimensions().getOrThrow(levelStemResourceKey),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
originalServerWorld.isDebug(),
|
||||
seed,
|
||||
ImmutableList.of(),
|
||||
false,
|
||||
environment,
|
||||
generator,
|
||||
biomeProvider
|
||||
) {
|
||||
private final Holder<Biome> singleBiome = options.hasBiomeType() ? BuiltinRegistries.BIOME.asHolderIdMap().byId(
|
||||
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
|
||||
) : null;
|
||||
|
||||
@Override
|
||||
public void tick(BooleanSupplier shouldKeepTicking) { //no ticking
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getUncachedNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
||||
if (options.hasBiomeType()) {
|
||||
return singleBiome;
|
||||
}
|
||||
return PaperweightRegen.this.chunkGenerator.getBiomeSource().getNoiseBiome(
|
||||
biomeX, biomeY, biomeZ, getChunkSource().randomState().sampler()
|
||||
);
|
||||
}
|
||||
}).get();
|
||||
freshWorld.noSave = true;
|
||||
removeWorldFromWorldsMap();
|
||||
newWorldData.checkName(originalServerWorld.serverLevelData.getLevelName()); //rename to original world name
|
||||
if (paperConfigField != null) {
|
||||
paperConfigField.set(freshWorld, originalServerWorld.paperConfig());
|
||||
}
|
||||
|
||||
ChunkGenerator originalGenerator = originalChunkProvider.getGenerator();
|
||||
if (originalGenerator instanceof FlatLevelSource flatLevelSource) {
|
||||
FlatLevelGeneratorSettings generatorSettingFlat = flatLevelSource.settings();
|
||||
chunkGenerator = new FlatLevelSource(originalGenerator.structureSets, generatorSettingFlat);
|
||||
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
|
||||
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
|
||||
originalGenerator);
|
||||
BiomeSource biomeSource;
|
||||
if (options.hasBiomeType()) {
|
||||
biomeSource = new FixedBiomeSource(BuiltinRegistries.BIOME
|
||||
.asHolderIdMap()
|
||||
.byId(WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())));
|
||||
} else {
|
||||
biomeSource = originalGenerator.getBiomeSource();
|
||||
}
|
||||
chunkGenerator = new NoiseBasedChunkGenerator(originalGenerator.structureSets,
|
||||
noiseBasedChunkGenerator.noises,
|
||||
biomeSource,
|
||||
generatorSettingBaseSupplier
|
||||
);
|
||||
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
|
||||
chunkGenerator = customChunkGenerator.getDelegate();
|
||||
} else {
|
||||
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
if (generator != null) {
|
||||
chunkGenerator = new CustomChunkGenerator(freshWorld, chunkGenerator, generator);
|
||||
generateConcurrent = generator.isParallelCapable();
|
||||
}
|
||||
chunkGenerator.conf = freshWorld.spigotConfig;
|
||||
|
||||
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
|
||||
// Optimisation for needless ring position calculation when the seed and biome is the same.
|
||||
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(originalGenerator);
|
||||
if (hasGeneratedPositions) {
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> ringPositions =
|
||||
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(
|
||||
originalGenerator);
|
||||
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(ringPositions);
|
||||
ringPositionsField.set(chunkGenerator, copy);
|
||||
hasGeneratedPositionsField.setBoolean(chunkGenerator, true);
|
||||
}
|
||||
}
|
||||
|
||||
freshChunkProvider = new ServerChunkCache(
|
||||
freshWorld,
|
||||
session,
|
||||
server.getFixerUpper(),
|
||||
server.getStructureManager(),
|
||||
server.executor,
|
||||
chunkGenerator,
|
||||
freshWorld.spigotConfig.viewDistance,
|
||||
freshWorld.spigotConfig.simulationDistance,
|
||||
server.forceSynchronousWrites(),
|
||||
new RegenNoOpWorldLoadListener(),
|
||||
(chunkCoordIntPair, state) -> {
|
||||
},
|
||||
() -> server.overworld().getDataStorage()
|
||||
) {
|
||||
// redirect to LevelChunks created in #createChunks
|
||||
@Override
|
||||
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkstatus, boolean create) {
|
||||
ChunkAccess chunkAccess = getChunkAt(x, z);
|
||||
if (chunkAccess == null && create) {
|
||||
chunkAccess = createChunk(getProtoChunkAt(x, z));
|
||||
}
|
||||
return chunkAccess;
|
||||
}
|
||||
};
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
try {
|
||||
session.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//shutdown chunk provider
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
freshChunkProvider.close(false);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//remove world from server
|
||||
try {
|
||||
Fawe.instance().getQueueHandler().sync(this::removeWorldFromWorldsMap);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
//delete directory
|
||||
try {
|
||||
SafeFiles.tryHardToDeleteDir(tempDir);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtoChunk createProtoChunk(int x, int z) {
|
||||
return new FastProtoChunk(new ChunkPos(x, z), UpgradeData.EMPTY, freshWorld,
|
||||
this.freshWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LevelChunk createChunk(ProtoChunk protoChunk) {
|
||||
return new LevelChunk(
|
||||
freshWorld,
|
||||
protoChunk,
|
||||
null // we don't want to add entities
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ChunkStatusWrap getFullChunkStatus() {
|
||||
return new ChunkStatusWrap(ChunkStatus.FULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<BlockPopulator> getBlockPopulators() {
|
||||
return originalServerWorld.getWorld().getPopulators();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populate(LevelChunk levelChunk, Random random, BlockPopulator blockPopulator) {
|
||||
// BlockPopulator#populate has to be called synchronously for TileEntity access
|
||||
TaskManager.taskManager().task(() -> blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IChunkCache<IChunkGet> initSourceQueueCache() {
|
||||
return (chunkX, chunkZ) -> new PaperweightGetBlocks(freshWorld, chunkX, chunkZ) {
|
||||
@Override
|
||||
public LevelChunk ensureLoaded(ServerLevel nmsWorld, int x, int z) {
|
||||
return getChunkAt(x, z);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//util
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeWorldFromWorldsMap() {
|
||||
Fawe.instance().getQueueHandler().sync(() -> {
|
||||
try {
|
||||
Map<String, org.bukkit.World> map = (Map<String, org.bukkit.World>) serverWorldsField.get(Bukkit.getServer());
|
||||
map.remove("faweregentempworld");
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ResourceKey<LevelStem> getWorldDimKey(org.bukkit.World.Environment env) {
|
||||
return switch (env) {
|
||||
case NETHER -> LevelStem.NETHER;
|
||||
case THE_END -> LevelStem.END;
|
||||
default -> LevelStem.OVERWORLD;
|
||||
};
|
||||
}
|
||||
|
||||
private static class RegenNoOpWorldLoadListener implements ChunkProgressListener {
|
||||
|
||||
private RegenNoOpWorldLoadListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSpawnPos(ChunkPos spawnPos) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(ChunkPos pos, @Nullable ChunkStatus status) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
}
|
||||
|
||||
// TODO Paper only(?) @Override
|
||||
public void setChunkRadius(int radius) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FastProtoChunk extends ProtoChunk {
|
||||
|
||||
public FastProtoChunk(
|
||||
final ChunkPos pos,
|
||||
final UpgradeData upgradeData,
|
||||
final LevelHeightAccessor world,
|
||||
final Registry<Biome> biomeRegistry,
|
||||
@Nullable final BlendingData blendingData
|
||||
) {
|
||||
super(pos, upgradeData, world, biomeRegistry, blendingData);
|
||||
}
|
||||
|
||||
// avoid warning on paper
|
||||
|
||||
// compatibility with spigot
|
||||
|
||||
public boolean generateFlatBedrock() {
|
||||
return generateFlatBedrock;
|
||||
}
|
||||
|
||||
// no one will ever see the entities!
|
||||
@Override
|
||||
public List<CompoundTag> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected class ChunkStatusWrap extends ChunkStatusWrapper<ChunkAccess> {
|
||||
|
||||
private final ChunkStatus chunkStatus;
|
||||
|
||||
public ChunkStatusWrap(ChunkStatus chunkStatus) {
|
||||
this.chunkStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int requiredNeighborChunkRadius() {
|
||||
return chunkStatus.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return chunkStatus.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
chunkGenerator,
|
||||
structureTemplateManager,
|
||||
threadedLevelLightEngine,
|
||||
c -> CompletableFuture.completedFuture(Either.left(c)),
|
||||
accessibleChunks,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A light engine that does nothing. As light is calculated after pasting anyway, we can avoid
|
||||
* work this way.
|
||||
*/
|
||||
static class NoOpLightEngine extends ThreadedLevelLightEngine {
|
||||
private static final ProcessorMailbox<Runnable> MAILBOX = ProcessorMailbox.create(task -> {}, "fawe-no-op");
|
||||
private static final ProcessorHandle<Message<Runnable>> HANDLE = ProcessorHandle.of("fawe-no-op", m -> {});
|
||||
|
||||
public NoOpLightEngine(final ServerChunkCache chunkProvider) {
|
||||
super(chunkProvider, chunkProvider.chunkMap, false, MAILBOX, HANDLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> retainData(final ChunkAccess chunk) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ChunkAccess> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
|
||||
return CompletableFuture.completedFuture(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
17
worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts
Normale Datei
17
worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts
Normale Datei
@ -0,0 +1,17 @@
|
||||
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
|
||||
applyPaperweightAdapterConfiguration()
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.3-R0.1-20230312.180621-141")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren