Commits vergleichen

..

333 Commits

Autor SHA1 Nachricht Datum
6d6de3ee3f Fix 1.19.3 ChunkSections
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-08-21 16:55:36 +02:00
54fd91c7f7 Fix 1.19
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-08-19 20:02:26 +02:00
2b870902a3 Fix 1.19.3
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-08-19 19:58:16 +02:00
6ab048cd7b Revert "Drop support for minor 1.19 versions (#2316)"
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
This reverts commit 770bb008
2023-08-19 19:50:30 +02:00
5b5fd8a229 Rollback 1.19.4 to 1.19.3
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-08-19 19:43:39 +02:00
f0509896e0 Rollback 1.19.4 to 1.19.3
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-08-19 19:39:12 +02:00
bdad5ca1b6 Fix CI
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-08-19 19:29:00 +02:00
c0f935ef55 Merge remote-tracking branch 'upstream/main'
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-08-19 12:43:25 +02:00
Alexander Brandes
fe33be5795
Update userdev 2023-08-18 13:40:16 +02:00
Maddy Miller
a088e6a96b Remove the Bukkit legacy data folder migration code from 11 years ago (#2383)
(cherry picked from commit EngineHub/WorldEdit@7f81aad028)
2023-08-10 19:10:04 -04:00
Alexander Brandes
acb79469fb
[ci skip] Update towny 2023-08-09 20:59:27 +02:00
Alexander Brandes
7b3c674e1c
[ci skip] Ignore snakeyaml updates 2023-08-09 20:56:15 +02:00
Alexander Brandes
a3e855882c
[ci skip] Ignore snakeyaml updates 2023-08-09 20:54:46 +02:00
Alexander Brandes
e81980651e
Cleanup dependencies 2023-08-09 20:52:18 +02:00
Jeff P
50e45f07f0
fix: Do not tab complete if command not owned by FAWE (#2386) 2023-08-02 14:08:58 +02:00
renovate[bot]
a6f1c6ac82
Update dependency com.palmergames.bukkit.towny:towny to v0.99.5.7 (#2390)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-02 14:07:35 +02:00
renovate[bot]
4ca0a95e18
Update dependency com.sk89q.worldguard:worldguard-bukkit to v7.0.9 (#2391)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-02 14:07:27 +02:00
renovate[bot]
0dbc0db43f
Update plugin com.modrinth.minotaur to v2.8.3 (#2392)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-02 14:07:19 +02:00
Alexander Brandes
9ae04b8d40
Update v7 notification 2023-07-31 11:44:14 +02:00
Jordan
d42854845a
fix: disallowed regions can be length 0 (#2368) 2023-07-27 11:02:41 +02:00
Jordan
8b39e41a1a
fix: don't warn on release build (#2369) 2023-07-27 10:59:11 +02:00
dordsor21
48be6ac94b
fix: processorExtent can be null when setting masks too
- fixes #2379
2023-07-22 17:44:05 +01:00
dordsor21
2f6de996e3
fix: processorExtent can be null in editsession
- fixes #2375
2023-07-22 13:14:01 +01:00
Alexander Brandes
006ccd6887
Back to snapshot for development 2023-07-22 12:44:37 +02:00
Alexander Brandes
37bfe426bc
Release 2.7.0 2023-07-22 12:27:54 +02:00
renovate[bot]
f58f00e97f
Update plugin com.modrinth.minotaur to v2.8.2 (#2374)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-22 12:21:08 +02:00
renovate[bot]
34a98a03c1
Update dependency gradle to v8.2.1 (#2373)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-22 10:02:22 +00:00
Alexander Brandes
c91b477e29
Update PlotSquared integration to v7 (#2075)
Co-authored-by: dordsor21 <dordsor21@gmail.com>
2023-07-22 12:01:33 +02:00
renovate[bot]
4bdcf9a510
Update dependency com.palmergames.bukkit.towny:towny to v0.99.5.4 (#2372)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-22 10:00:15 +00:00
Jordan
9543adc776
Implement async notify queue that submits to a KeyQueuedExecutorService (#2334) 2023-07-20 17:58:01 +02:00
Jordan
18df87a4e8
feat: implement a new "type swap" pattern (#2346) 2023-07-20 17:57:48 +02:00
Jordan
0a8a479214
fix: do not clash ChunkHolder recycling with processors that extend outside the chunk (#2353) 2023-07-20 17:57:29 +02:00
Jordan
fe1859e9d2
fix: do not retain clipboards that completed exceptionally (#2358) 2023-07-20 17:56:30 +02:00
Jordan
f65c4743bd
fix: make IdMask thread-safe (#2361) 2023-07-20 17:56:11 +02:00
Jordan
cdd546ee5e
fix: correct NBT for spawners (#2362) 2023-07-20 17:55:59 +02:00
Paul
5167073326
fix: regions not trimming Y correctly when restricted (#2363) 2023-07-17 21:47:47 +01:00
Chris Lang
e0507e6440
Added cone generation command (#2251)
* Added cone generation command

* Fix formatting problems

---------

Co-authored-by: Madeline Miller <mnmiller1@me.com>
2023-07-15 21:18:09 +01:00
Jordan
b1e0ad4ef7
feat: re-submit chunk load request after 10s after checking world is loaded (#2339)
- #2332 makes it seem like paper forgets to load a chunk sometimes
 - resubmit chunk load request after a second to attempt to counter this
2023-07-15 16:41:04 +01:00
Jordan
e6b1308590
feat: synchronise disk clipboard loading and deletion to clipboardLock (#2342)
- another possible help towards the OverlappingFileLockException
2023-07-15 16:40:52 +01:00
Jordan
f4da4b0287
fix: fix adding/getting masks to/from MaskExtent when it is a processor (#2351)
- fixes #2350
2023-07-15 16:40:29 +01:00
dordsor21
8da530ee80
gracefully handle error in EntityInBlockRemovingProcessor
-  ensures edit continues on chunk correctly
2023-07-15 12:27:33 +01:00
Alexander Brandes
1745c50878
Improve error behaviour of //generate 2023-07-15 09:26:00 +02:00
dordsor21
a680c7ce97
fix: fix #2352 2023-07-13 17:08:18 +01:00
dordsor21
0a19f643b6
fix #2349 2023-07-11 16:39:53 +01:00
Jordan
c0e20a6fe5
feat: provide some information upon OverlappingFileLockException (#2341) 2023-07-10 23:16:05 +02:00
Jordan
1f29ab3b79
fix: fix command brush (#2347) 2023-07-10 22:04:33 +01:00
Jordan
685248d8de
chore: deprecate some methods that ought to be removed (#2340) 2023-07-09 15:16:30 +01:00
Hannes Greule
68eb4e214a
refactor: use HttpClient for update check (#2331) 2023-07-08 18:41:20 +01:00
Alexander Brandes
90587e56fc
Deprecate support for 1.16.5 (#2338) 2023-07-07 16:38:10 +00:00
Jordan
d78092b4ca
fix: ensure chunk is loaded when getting entities (#2323) 2023-07-07 18:36:12 +02:00
Jordan
6fbdef5252
feat: allow bracketless '%' pattern definition (#2322) 2023-07-07 18:36:02 +02:00
Jordan
f38c81aa2a
fix: correctly invalidate mask after a plot is merged (#2333) 2023-07-07 18:27:10 +02:00
Jordan
888683d83b
fix: ignore clipboard origin when pasting to match P2 behaviour (#2336) 2023-07-07 18:23:37 +02:00
dordsor21
a669be2041
fix #2319 2023-07-05 22:03:13 +01:00
Alexander Brandes
67fd31ce4c
Update bom 2023-07-05 16:06:15 +02:00
renovate[bot]
ccdb002ade
Update dependency gradle to v8.2 (#2326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-05 15:50:49 +02:00
renovate[bot]
34ead35e03
Update dependency net.minecrell.plugin-yml.bukkit to v0.6.0 (#2327)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-05 15:50:20 +02:00
renovate[bot]
52a51753dd
Update dependency com.palmergames.bukkit.towny:towny to v0.99.2.7 (#2329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-05 15:50:09 +02:00
Jordan
d7cc65d2f2
refactor: some changes that may help #2289 (#2307) 2023-07-03 16:37:42 +01:00
renovate[bot]
a0acbc7168
Update dependency org.mockito:mockito-core to v5.4.0 (#2328)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-01 18:49:40 +02:00
renovate[bot]
e680e8fe1f
Update dependency com.palmergames.bukkit.towny:towny to v0.99.2.5 (#2325)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-01 08:47:03 +00:00
renovate[bot]
4583acda6b
Update auto.value to v1.10.2 (#2324)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-01 08:46:19 +00:00
Alexander Brandes
c880badaf4
[ci skip] Update renovate.json 2023-07-01 10:32:39 +02:00
dordsor21
83a4987252
chore: queue progress messages are not implemented 2023-06-30 19:36:35 +01:00
Alexander Brandes
903064fd41
Back to snapshot for development 2023-06-29 12:46:35 +02:00
Alexander Brandes
e5cbf33317
Release 2.6.4 2023-06-29 12:29:37 +02:00
Alexander Brandes
770bb0087a
Drop support for minor 1.19 versions (#2316)
* Drop support for minor 1.19 versions

* More work
2023-06-29 12:23:26 +02:00
Jordan
24325d91ba
feat: improve (region) fawe mask handling (#2312)
* feat: improve fawe mask handling
 - Actually cache masks, even if the player has left the region
 - Fix P2 isValid test for single plots
 - Fixes #1946

* Fix incorrect delegated method
2023-06-28 09:24:20 +01:00
Jordan
0554b31f11
feat: implement removal of entities if they would be in a block after the edit (#2311)
- Includes some refactoring to EditSessionBuilder to prevent doubling-up of processors that are also extents
 - Better ordering of the EditSessionBuilder process/extent code to match where extents actually end up in the stack
 - Fixes #1941
2023-06-27 18:37:09 +02:00
Jordan
476ba4ab41
refactor: efficiency improvements to some editsession methods (#2304) 2023-06-27 18:36:54 +02:00
Alexander Brandes
97ee71bbd0
Update settings name in queue.target_size warning 2023-06-27 13:56:30 +02:00
dordsor21
924805af8f
fix: fix obnoxious error message if using p2 v7 with FAWE main branch 2023-06-25 13:52:13 +01:00
dordsor21
05d5ad161a
chore: add obnoxious error message if using p2 v7 with FAWE main branch 2023-06-23 21:57:51 +01:00
Alexander Brandes
c0037aa083
[ci skip] Remove unsupported versions from template 2023-06-23 14:56:47 +02:00
Jordan
9f0a0dbd35
fix: ensure mask is initialised upon visiting a point in BreadthFirstSearch (#2314)
fix: ensure mask is initialised upon visiting a point in BreadFirstSearch
2023-06-22 21:32:54 +02:00
Jordan
d961aa91bc
fix: fix regex block masking (#2242) 2023-06-22 11:24:03 +01:00
Jordan
88533118bc
fix: uncache the chunk SET at a better time in ChunkHolder to prevent "lost" changes (#2300)
- blocks could still be set to the ChunkHolder during a call
 - create a new chunk SET in that case
 - make cached get/set volatile to improve thread safety
2023-06-19 22:46:10 +02:00
Jordan
8ca25fa7d7
fix: towny max bounds are exclusive (#2301)
fix: towny max bounds are exlusive
 - Fixes #2293
2023-06-19 22:46:01 +02:00
Jordan
01be53ed65
fix: add missing BlockType constructor (#2306)
- Deprecate public BlockType constructors
 - People really should not be initialising their own block types. This can and most likely will cause issues.
 - Fixes #2290
2023-06-19 14:36:47 +01:00
Jordan
a8c8a0fbd6
fix: always return the changeset from EditSessionBuilder (#2302)
- Ensure history will actually always be closed if combine-stages is false
- Fixes #2148
2023-06-19 14:34:54 +01:00
Jordan
1ede11b129
fix: use new array when creating palette (#2305)
- Fixes #2291
2023-06-19 14:34:02 +01:00
Jordan
39de48cdb1
chore: Address kotlin String#capitalize() deprecation (#2308)
* chore: Address kotlin String#capitalize() deprecation
 - Fixes #2283
 - Don't bother with the longer version that allows Locales, we don't need that

* Address comments
2023-06-18 16:31:49 +00:00
Jordan
f0aef98870
fix: don't "double-up" a setblocks if a region is being set (#2299)
- Fixes #2294
2023-06-18 18:08:31 +02:00
dordsor21
135c77cfb5
chore: minor changes
- null check get tile in ThreadUnsafeCharBlocks
 - 0 to reserved ID
2023-06-18 15:03:51 +01:00
renovate[bot]
78fb74665f
Update plugin com.modrinth.minotaur to v2.8.1 (#2298)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-17 16:36:57 +02:00
Alexander Brandes
7a7373fd3d
Update paperweight 2023-06-17 16:16:16 +02:00
Jordan
51e238c352
feat: add new tree types to tree tool and brush (#2286)
* feat: add new tree types
 - implements #2197

* Remove nonsensical test case

---------

Co-authored-by: Alexander Brandes <mc.cache@web.de>
2023-06-15 10:18:01 +02:00
Jordan
79a2c14caa
fix #2292 2023-06-13 19:12:24 +02:00
Alexander Brandes
d9463ce8b5
Add support for 1.20.1 2023-06-12 16:36:06 +02:00
Jordan
dae6c69e54
feat: send one of an error message during edits if otherwise squashed by LimitExtent (#2246)
* feat: send one of an error message during edits if otherwise squashed by LimitExtent

* no region is not ingorable
2023-06-12 12:14:24 +02:00
Alexander Brandes
ed128797ec
Back to snapshot for development 2023-06-12 12:13:49 +02:00
Alexander Brandes
956a518238
Release 2.6.3 2023-06-12 11:58:38 +02:00
Jordan
e449910af5
feat: prevent edits outside +/- 30,000,000 blocks (#2285)
* feat: prevent edits outside +/- 30,000,000 blocks

* Remove import
2023-06-12 11:27:07 +02:00
LlmDl
ca4080eea7
Update & Fix TownyFeature support. (#2279) 2023-06-11 22:55:11 +01:00
Alexander Brandes
71c172cced
[ci skip] Update bug_report.yml 2023-06-11 12:09:27 +02:00
Hannes Greule
083f8a4dd8
Set proper field access and use mappings for entities on spigot (#2282)
set proper field access and use mappings
2023-06-10 22:47:22 +02:00
Alexander Brandes
689c7f62b8
Fixed setting sign text on 1.20 2023-06-09 15:27:02 +02:00
Alexander Brandes
bb14d93a8d
Add Console/CommandBlock support to Fabric/Forge/Sponge (2317) 2023-06-09 13:58:45 +02:00
Alexander Brandes
e7876c4eba
Update to 1.20 (#2276)
* Towards 1.20

* Init bukkit

* Update YAML to 2.0

* Fixes for 1.20 adapters

* Update obfuscated field names

* Remove getHandle workaround

* entityManager is obfuscated

* Update paperweight

* [ci-skip] Add 1.20 to modrinth task

---------

Co-authored-by: Phillipp Glanz <p.glanz@madfix.me>
Co-authored-by: SirYwell <hannesgreule@outlook.de>
2023-06-09 12:53:42 +02:00
Hannes Greule
152d870cdb
Improve exception handling on adapter loading (#2277) 2023-06-08 08:24:44 +00:00
Alexander Brandes
9be1a58bf3
Back to snapshot for development 2023-06-07 11:30:06 +02:00
Alexander Brandes
3b7d126718
Release 2.6.2 2023-06-07 11:11:54 +02:00
Alexander Brandes
832f93c0f3
Optimize PR builds (#2273)
* Optimize PR build

* Update codeql.yml
2023-06-07 00:51:56 +02:00
renovate[bot]
c5f66c342b
Update dependency com.palmergames.bukkit.towny:towny to v0.99.1.2 (#2272)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-07 00:35:50 +02:00
Jordan
1356cd5caa
feat: implement a player-specific queue for clipboard IO tasks (#2267)
* feat: implement a player-specific queue for clipboard IO tasks
 - Addresses #2222 (hopefully fixes)

* Address comments

* Add since

---------

Co-authored-by: Alexander Brandes <mc.cache@web.de>
2023-06-07 00:35:37 +02:00
Jordan
84872cf9a2
chore: improve queue documentation and submit history to better queue (#2266) 2023-06-06 19:22:25 +02:00
Jordan
b7719d17bd
fix: correctly processSet with blacklist when required (#2270)
* fix: correctly processSet with blacklist when required

* Fix incorrect region
2023-06-06 19:21:18 +02:00
Jordan
5504811f11
chore: minor improvements to processing-related code (#2271) 2023-06-06 19:21:02 +02:00
Alexander Brandes
bdb170a5ca
Explicit call repository task 2023-06-06 19:18:01 +02:00
dordsor21
5da5b216f3
fix #2268
-.-
2023-06-05 22:26:41 +01:00
Alexander Brandes
4b06796e76
Archive PR artifacts (#2269)
* Archive PR artifacts

* Fix artifact path
2023-06-05 10:51:26 +02:00
dordsor21
e5d27c27bb
chore: replace some hardcoded ordinals 2023-06-05 00:17:13 +01:00
dordsor21
46e8a37cf0
fix: improve test for polygonal region containment of cuboid
- Possibly fixes #2265
2023-06-04 23:57:51 +01:00
Jordan
e9fed5a066
feat: improve error when loading biomes (#2241) 2023-06-04 18:50:08 +02:00
Jordan
0e215e98d5
fix: minor changes to fix loading blocks outside stored range in ThreadUnsafeCharBlocks (#2260)
- Fixes #2258
2023-06-04 17:22:21 +02:00
EpicPlayerA10
97d4244126
Fix BlockEntity#remove spigot mapping (#2264)
Fix spigot mapping
2023-06-04 13:14:50 +00:00
Alexander Brandes
b7dc5f7ae5
[ci skip] Update Java tooling name 2023-06-04 13:29:07 +02:00
Alexander Brandes
c9a4a9c8b4
Update gradle to 8 (#2262)
* Update gradle to 8

* Update codeql.yml
2023-06-04 12:34:03 +02:00
dordsor21
048dcaf327
fix: do not always set random rotate to true
- Fixes #2259
 - How'd that even get through
2023-06-03 16:34:23 +01:00
Hannes Greule
4fa927f996
Extract common code when resizing arrays (#2257) 2023-06-02 19:39:00 +02:00
renovate[bot]
82418155f6
Update dependency com.github.TownyAdvanced:Towny to v0.99.1.0 (#2254)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: dordsor21 <dordsor21@gmail.com>
2023-06-02 12:21:16 +00:00
Hannes Greule
7646a067eb
Avoid many threads blocking on AbstractChangeSet#processSet (#2226) 2023-06-02 10:40:34 +01:00
renovate[bot]
03c4bafc45
Update antlr4 to v4.13.0 (#2256)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-01 23:01:56 +02:00
Jordan
228e84e555
fix: Improve edit processing (#2247) 2023-06-01 15:11:16 +01:00
renovate[bot]
7ce17e5834
Update dependency com.sk89q.worldguard:worldguard-bukkit to v7.0.8 (#2255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-01 09:23:17 +02:00
Jordan
03487718e7
feat: configurable image hosts (#2243) 2023-05-31 16:48:45 +01:00
Jordan
a553961c05
refactor: switch to EnumSet for heightmaps to improve performance (#2248) 2023-05-27 14:25:07 +02:00
Jordan
3041ff3e50
feat: add -r (random rotate) flag to schem load and clipboard brush (#2244)
- Implements #1873
2023-05-25 19:06:20 +02:00
dordsor21
362f6cc6e1
fix #2009 2023-05-22 19:43:21 +01:00
Jordan
5dca925bc3
feat: make more commands unqueued (#2235)
* feat: make more commands unqueued
 - Effectively fixes the very specific "issue" in #2223 I guess
 - Use this opportunity to clean up the command classes a bit

* Address comments
2023-05-22 20:33:59 +02:00
Jordan
435161d566
fix: only send invalid region messages with debug true (#2238)
- Addresses #2188
2023-05-22 20:33:15 +02:00
Jordan
7c01c1e95e
feat: allow captions in SuggestInputParseException (#2239)
- Deprecate for removal methods using string message
 - Fixes #2026
2023-05-22 20:32:56 +02:00
Jordan
74aff920a8
fix: fix universal disallowed blocks logic in limit and EditSession compilation (#2237)
- Fixes #2184
2023-05-22 20:32:36 +02:00
Alexander Brandes
2bdc925b0f
Add regression tests to the round Expression function 2023-05-21 09:51:59 +02:00
Alexander Brandes
4c0b535e6e
Ensure non-Double types are boxed in expression function calls. 2023-05-21 09:50:10 +02:00
Alexander Brandes
86f06b7527
Add -Penginehub.obf.none=true gradle property to build mojmap worldedit-bukkit. 2023-05-20 21:01:34 +02:00
Alexander Brandes
5e3222c55d
Ensure blockmap uniqueness across TE values 2023-05-20 21:00:35 +02:00
Alexander Brandes
7f43e448d5
fix(core): Properly reinit convex CUI selection on primary click 2023-05-20 20:58:47 +02:00
Jordan
2c7b529f67
fix: create single threaded executor for use with non-concurrent generators (#2236)
- Fixes #2228
2023-05-20 20:56:46 +02:00
Alexander Brandes
3cac28ea84
Keep extending cuboid selector after //cli selectworld 2023-05-20 20:56:37 +02:00
Alexander Brandes
85214c944f
Keep extending cuboid selector after //paste -s/n 2023-05-20 20:54:52 +02:00
Alexander Brandes
264185a323
Add method to check initialize state in PlatformManager, deny access to registries when uninitialized 2023-05-20 20:52:03 +02:00
Jordan
85f5006a0b
fix: correctly handle creation of extents in EditSessionBuilder (#2232)
- Add regionExtent process to the queue (fixes #2227)
 - Correctly wrap extents in each other
2023-05-20 20:33:18 +02:00
Jordan
1b0fb9ed48
fix: more intuitive limit permissions (#2233)
- immediately return limit if unlimited (limit combination take the "higher" value)
 - add fawe.limit.unlimited permission and it's an intuitive permission to give
2023-05-20 20:32:38 +02:00
Jordan
3a13c4aaa7
chore: remove firework-chunk-loading prevention and default tick limiter to false (#2234)
- It's very resource-intensive and probably doesn't work anyway
 - Tick limiter should not be enabled by default, it confuses a lot of people with the console logs
2023-05-20 16:25:24 +02:00
Jordan
e9b781d127
fix: correctly in initialise unlimited limit (#2231) 2023-05-20 16:22:02 +02:00
Jordan
2a08ad28a4
fix: correct issues with processor scopes (#2230) 2023-05-20 16:21:11 +02:00
Marco Neuhaus
52723439dd
Fix reading of tile entities with "id" instead of "Id" tag (#2211) 2023-05-20 09:57:13 +00:00
Hannes Greule
a14035d430
Revert "Actually delegate in AbstractDelegateExtent" (#2229) 2023-05-16 10:32:03 +02:00
Alexander Brandes
319cb636e5
Simplify Java location in Jenkinsfile (#2221) 2023-05-12 23:46:38 +02:00
Hannes Greule
71bac009db
Properly scale the random in SimpleRandomCollection (#2220) 2023-05-12 23:13:35 +02:00
Phillipp Glanz
c57fee5b86
Fixes command context for bukkit console command sender (#2193) 2023-05-12 12:12:08 +01:00
dordsor21
dd6197922c
fix: remove invalid file length check
- NBT is stored at the end of the clipboard file
 - Fixes #2209
2023-05-10 17:27:44 +01:00
Hannes Greule
2987550e9b
Actually delegate in AbstractDelegateExtent (#2196)
* actually delegate in AbstractDelegateExtent

* exclude enableHistory method
2023-05-04 18:33:04 +02:00
Phillipp Glanz
e08720b310
Improve git ignore (#2206)
* Exclude run-server for all version

* Exclude run-server for all version

* Exclude run-server for all version

* Exclude run-server for all version

* Exclude run-server for all version

* Remove unsless entry
2023-05-04 18:30:10 +02:00
renovate[bot]
92a6ff5f88
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.5.5 (#2201)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-01 10:22:27 +02:00
renovate[bot]
950a3f9430
Update dependency com.github.TownyAdvanced:Towny to v0.99.0.5 (#2202)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-01 10:22:18 +02:00
renovate[bot]
4779bd4916
Update plugin xyz.jpenilla.run-paper to v2.1.0 (#2205)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-01 10:20:24 +02:00
renovate[bot]
909b7d27b8
Update dependency org.ajoberstar.grgit:grgit-gradle to v5.2.0 (#2203)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-01 10:04:49 +02:00
renovate[bot]
ffe704d890
Update dependency org.mockito:mockito-core to v5.3.1 (#2204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-01 10:03:49 +02:00
Alexander Brandes
9f3c909254
Back to snapshot for development 2023-04-23 23:01:03 +02:00
Alexander Brandes
abe120291f
Release 2.6.1 2023-04-23 22:47:30 +02:00
Hannes Greule
c86dfe45df
Ensure regen step not running on the main thread (#2185) 2023-04-23 22:15:37 +02:00
360shvit
488a2e5de4
Add an explanation to Disallowed-Blocks Comment (#2186)
Improve user experience by adding an explanation to Disallowed-Blocks Configblock (#2183)
2023-04-22 16:48:59 +00:00
Jordan
8cfb8cb322
chore: add chunk tickets to all chunks accessed (#2163)
- This isn't necessarily targeting any fix, but I think we should be ensuring a ticket is being added to chunks "access asynchronously", as done by the getChunkAtAsync method
2023-04-22 09:45:02 +00:00
Hannes Greule
05afaf00a9
Address incompatibilities after CraftChunk changes in spigot (#2179) 2023-04-21 23:21:50 +01:00
Hannes Greule
86acb1c4d4
Simplify command dispatch to ensure command order (#2131) 2023-04-18 16:28:38 +01:00
Jordan
5cd9616507
fix: Ensure BaseBlock is not returned when a BlockState is wanted in BlockTransformExtent (#2161
- NBT will be handled appropriately at another point
 - Fixes #2085
2023-04-13 17:34:16 +01:00
Jordan
e4158ac084
fix: invalidate cached FaweMask when a plot is unlinked (#2157)
* fix: invalidate cached FaweMask when a plot is unlinked
 - Fixes IntellectualSites/PlotSquared#3998

* Use a weakly-referenced copy of the copnncted plots set
2023-04-13 17:34:05 +01:00
6a7f51bba0 Update CI + Rebuild
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-04-11 12:50:25 +02:00
cdd4df37c6 Merge remote-tracking branch 'upstream/main'
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-04-11 11:58:22 +02:00
Phillipp Glanz
da4d966d9e
Fix Misaligned SVG In IJ project view (#2174) 2023-04-10 11:54:46 +02:00
Alexander Brandes
a42864e780
[ci skip] Move renovate config to .github folder 2023-04-07 15:12:55 +02:00
Alexander Brandes
2dc8f7bca9
Update paperweight version for 1.19.4 (#2166)
Update paperweight version
2023-04-07 15:11:12 +02:00
renovate[bot]
bb8c9d216b
Update dependency org.bstats:bstats-base to v3.0.2 (#2170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-07 15:10:46 +02:00
Phillipp Glanz
2b51896a01
Improve testability by adding more versions of runPaper to the build scripts (#2172)
* Improve testability

* Change to import
2023-04-07 15:10:14 +02:00
Alexander Brandes
b42f6a2f5e
Update renovate.json (#2167) 2023-04-01 17:53:36 +02:00
Jordan
a9e1d5009c
fix: add null-check for brush in traceMask (#2160)
* fix: add null-check for brush in traceMask
 - Fixes #2149

* Switch to print from printError

Co-authored-by: Alexander Brandes <mc.cache@web.de>

---------

Co-authored-by: Alexander Brandes <mc.cache@web.de>
2023-03-31 20:45:34 +02:00
Jordan
508b94ddc3
feat: improvements to clipboard on disk (#2162)
* feat: improvements to clipboard on disk
 - close clipboard on instantiation failure
 - set canHaveBiomes when loading clipboard from disk (only possible cause I can find for #2151 ..?)
 - check file length and give more appropriate error if it exceeds maximum. Fixes #2151 I guess?#
 - set byteBuffer to null before invoking cleaning. Addresses #1985
 - don't catch and print all exceptions when getBlock fails - prevents large console output for what is likely to be a failed operation anyway

* Fix comments
2023-03-30 00:13:50 +02:00
dordsor21
d82bf0527e
fix: use correct string for disallowed property use 2023-03-22 17:28:50 +00:00
Alexander Brandes
eb1c9dc4db
Apply stash 2023-03-19 10:34:49 +01:00
Alexander Brandes
9d63cb8102
Release 2.6.0 2023-03-19 00:34:10 +01:00
Alexander Brandes
efc917e6df
Add support for 1.19.4 (#2154)
* Add paper adapter for 1.19.4

* Add paper module for 1.19.4

* Port 1.19.3 to 1.19.4

* Switch to Paperweight release

* Update worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java

Co-authored-by: Antti Koponen <koponen942@outlook.com>

* Update worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java

Co-authored-by: Antti Koponen <koponen942@outlook.com>

* Add 1.19.4 to release template

* Update refractions for regen support

* Bump paperweight

---------

Co-authored-by: TheMeinerLP <p.glanz@madfix.me>
Co-authored-by: Antti Koponen <koponen942@outlook.com>
2023-03-18 15:25:04 +01:00
Phillipp Glanz
b54e3a5112
Add nether tress into floating tree remover (#2146)
* Add nether tress into floating tree remover

* Add nether stems
2023-03-17 21:58:53 +00:00
Alexander Brandes
09b899c7a9
[ci skip] Temp disable dc workflow 2023-03-17 22:25:41 +01:00
Jordan
7ab04317fa
fix: clear above and below plot gen heights if build heights are larger (#2153) 2023-03-17 21:56:48 +01:00
Phillipp Glanz
e32a8c40c6
Bump run paper to 1.19.3 and replace path with better solution (#2150) 2023-03-16 09:45:22 +00:00
renovate[bot]
df99226313
Update dependency com.github.TechFortress:GriefPrevention to v16.18.1 (#2142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-13 17:09:18 +01:00
renovate[bot]
5313f9c588
Update dependency org.mockito:mockito-core to v5.2.0 (#2144)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-13 17:08:23 +01:00
renovate[bot]
0146b952c6
Update plugin io.github.gradle-nexus.publish-plugin to v1.3.0 (#2145)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-13 17:08:11 +01:00
Alexander Brandes
bfd919b3cb
Update paperweight 2023-03-07 15:39:22 +01:00
Alexander Brandes
b05e64a7ec
Fetch userdev plugin from plugin portal (#2136) 2023-03-07 15:35:33 +01:00
Alexander Brandes
ccb503e49d
[ci skip] Update CF workflow 2023-03-07 00:14:47 +01:00
Alexander Brandes
f7026338e9
Update adventure javadoc URL 2023-03-07 00:04:44 +01:00
Alexander Brandes
992eaf2b4c
Misc. dependency update to align with libs.versions.toml (#2134) 2023-03-06 15:44:21 +01:00
Hannes Greule
90baa790c3
Make custom toArray methods fulfill the method contract (#2089)
* Make PropertyKeySet#toArray(T) fulfill the method contract

* Make LocalBlockVectorSet#toArray(T) fulfill the method contract
2023-03-06 13:57:40 +01:00
Alexander Brandes
a63037b92d
Update renovate.json 2023-03-06 13:55:02 +01:00
Hannes Greule
8a3052683e
Use unsynchronized buffered OutputStream for history (#2127)
* Use unsychronized buffered OutputStream

* Add note

* Avoid synchronization on DataOutputStream#write
2023-03-06 13:54:35 +01:00
eztaK-red
211e8034ff
Fix off by one error for negative coordinates when using -r with //deform (#2092)
The problem: Off by one error for negative coordinates. Source: Behaviour of rounding coordinates (doubles) after deform.

The off by error came down to rounding using casts (int) and rounding using Math.floor()

(int)( 1.8) = 1
(int)(-1.8) = -1
(int)Math.floor( 1.8) = 1
(int)Math.floor(-1.8) = -2

Looking at the original WorldEdit implementation a Math.floor call is present too. It was missing FAWE which resulted in the bug.

Co-authored-by: Alexander Brandes <mc.cache@web.de>
2023-03-06 13:53:07 +01:00
Alexander Brandes
94f57799d0
Pin GH actions to SHA to avoid mutable refs (#2126) 2023-03-06 13:51:53 +01:00
renovate[bot]
7152fd2032
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.5.3 (#2129)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-06 13:47:13 +01:00
Alexander Brandes
84e1886672
Update developers field 2023-03-02 20:52:12 +01:00
Alexander Brandes
a1d35e26fa
Update developers field 2023-03-02 20:50:01 +01:00
renovate[bot]
214495f8f5
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.5.2 (#2122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-02 00:47:41 +01:00
Hannes Greule
526e1902ca
Fix FAWE using exclusive build height from PlotSquared (#2123)
Use inclusive build height maximum
2023-03-02 00:47:28 +01:00
renovate[bot]
6a31f4179c
Update dependency gradle to v7.6.1 (#2111)
* Update dependency gradle to v7.6.1

* Update gradle/wrapper/gradle-wrapper.properties

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Alexander Brandes <mc.cache@web.de>
2023-02-27 21:22:51 +01:00
Hannes Greule
1904b1fdd6
Fix reading block states in no redo mode (#2119) 2023-02-27 16:31:15 +01:00
renovate[bot]
c0dc42ed3f
Update dependency org.bstats:bstats-base to v3.0.1 (#2093)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-25 11:11:09 +01:00
renovate[bot]
f95383e837
Update dependency org.ajoberstar.grgit:grgit-gradle to v5 (#2114)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-25 11:10:23 +01:00
Alexander Brandes
c7ddee12d9
Revert "Update dependency gradle to v8 (#2094)"
This reverts commit b293a5eea3.
2023-02-25 10:42:01 +01:00
Alexander Brandes
7f6a27d083
Revert "Update dependency gradle.plugin.com.github.johnrengelman:shadow to v8 (#2113)"
This reverts commit 7a667131ba.
2023-02-25 10:41:49 +01:00
Alexander Brandes
184eb215b6
Move Jenkinsfile from ps-v7 to general branch (#2098)
* Move Jenkinsfile from ps-v7 to general branch

* Update Jenkinsfile
2023-02-25 10:31:33 +01:00
renovate[bot]
7a667131ba
Update dependency gradle.plugin.com.github.johnrengelman:shadow to v8 (#2113)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-25 10:26:24 +01:00
renovate[bot]
b293a5eea3
Update dependency gradle to v8 (#2094)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-25 10:01:24 +01:00
renovate[bot]
f70e1a215a
Update dependency net.minecrell.plugin-yml.bukkit to v0.5.3 (#2112)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-25 01:07:59 +01:00
9d5e96946c Merge pull request 'main' (#6) from Mirrors/FastAsyncWorldEdit:main into main
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #6
2023-02-23 10:18:59 +01:00
renovate[bot]
a464f1271d
Update plugin io.github.gradle-nexus.publish-plugin to v1.2.0 (#2109)
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-22 23:46:11 +01:00
renovate[bot]
b72c6902e2
Update antlr4 to v4.12.0 (#2103)
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-20 10:49:54 +01:00
renovate[bot]
8832820b6c
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.5.1 (#2095)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-19 12:11:04 +01:00
renovate[bot]
783dc353c4
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.5.0 (#2090)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-10 16:48:52 +01:00
Alexander Brandes
1e5b2fe0de
Back to snapshot for development 2023-02-07 23:25:17 +01:00
Alexander Brandes
19ed349740
Release 2.5.2 2023-02-07 23:13:05 +01:00
Hannes Greule
321a39f121
Remove PlayerMoveEvent listener and deprecate MovableTool (#2083)
Remove PlayerMoveEventListener and deprecate MovableTool
2023-02-07 23:07:57 +01:00
Hannes Greule
f9d6b127e6
Use Pattern Matching for instanceof in PropertyKeySet (#2086) 2023-02-07 23:07:31 +01:00
Alexander Brandes
07217d0b81
[ci skip] Document metrics addition 2023-02-07 22:50:21 +01:00
Alexander Brandes
b65f3ce1f8
Add metrics for Residence (#2081) 2023-02-07 10:47:49 +01:00
9f97dc2a3c Merge pull request 'Update upstream (for 1.19.3 support)' (#4) from upstream into main
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #4
2023-02-02 11:32:52 +01:00
1889f94a39 Fix ci
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-02-02 11:32:08 +01:00
b87a861098 Merge remote-tracking branch 'upstream/main' into upstream
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-02-02 11:20:09 +01:00
renovate[bot]
cd00bf9771
Update dependency org.mockito:mockito-core to v5.1.1 (#2078)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-31 11:21:29 +01:00
Hannes Greule
158e51191f
Linear Patterns require flooring division (#2079)
Use floorDiv in linear patterns
2023-01-31 11:21:11 +01:00
Alexander Brandes
214954bb09
Back to snapshot for development 2023-01-23 12:51:38 +01:00
Alexander Brandes
09d405935a
Release 2.5.1 2023-01-23 12:37:45 +01:00
95a06eb639 „steamwarci.yml“ ändern
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-18 21:04:30 +01:00
0c9317bb8c Deploy to maven repo
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-18 20:53:13 +01:00
Bernhard
5e8bc5c2ad
fix regex and remove forbidden windows characters (#2012)
refactor: fix regex and remove forbidden windows characters
2023-01-16 00:34:53 +01:00
TheJonstone✓
0f558425f5
Fix: remove unnecessary bypass permission (#2060)
Fix: remove unnecessary permission
2023-01-16 00:34:28 +01:00
Jordan
ae57ac5d50
fix: don't quash a cancellation FaweException in LimitExtent (#2068)
- This allowed some edits to continue after a player left the server when they shouldn't
 - Possible sort-of fix for #1985
 - Possible fix for #1955
2023-01-16 00:33:44 +01:00
Alexander Brandes
98c2d2124e
Split workflows (#2071) 2023-01-16 00:33:13 +01:00
Alexander Brandes
c0563b6574
Terminate process if .git folder does not exist (#2070)
Terminate process if folder is not a repository
2023-01-15 20:24:51 +01:00
renovate[bot]
606ffad12b
Update dependency org.mockito:mockito-core to v5 (#2069)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-14 10:46:24 +01:00
Hannes Greule
bb0e201c52
Remove usages of JoinedCharSequence (#2062) 2023-01-11 00:07:24 +01:00
renovate[bot]
452f968f07
Update antlr4 to v4.11.1 (#2064)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-09 16:52:48 +01:00
renovate[bot]
3ad70a8ef4
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.4.1 (#2063)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-09 16:33:32 +01:00
renovate[bot]
afe601bf93
Update dependency com.github.TownyAdvanced:Towny to v0.98.4.18 (#2057)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-31 01:02:41 +01:00
renovate[bot]
7db1e9c7d6
Update dependency com.github.TownyAdvanced:Towny to v0.98.4.17 (#2053)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-29 13:44:41 +01:00
renovate[bot]
b98c3efc88
Update dependency org.mockito:mockito-core to v4.11.0 (#2055)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-29 13:42:56 +01:00
Alexander Brandes
a9ed8431cd
Use the connection field on 1.19->1.19.3 rather than networkManager 2022-12-27 14:05:41 +01:00
renovate[bot]
a7c402d8dd
Update dependency com.github.TownyAdvanced:Towny to v0.98.4.15 (#2050)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-24 12:01:29 +01:00
renovate[bot]
f8acaf5ec2
Update dependency org.mockito:mockito-core to v4.10.0 (#2047)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-15 22:38:17 +01:00
Alexander Brandes
b0ac500fa1
Bump paperweight 2022-12-13 22:52:30 +01:00
renovate[bot]
83e0c84616
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.4.0 (#2044)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-13 22:44:47 +01:00
Alexander Brandes
26d5f10c04
Back to snapshot for development 2022-12-11 19:28:44 +01:00
Alexander Brandes
d6599fd332
Release 2.5.0 2022-12-11 19:10:40 +01:00
Bernhard
c3ce162fd5
Feature: add "h" as alias for "hand" and "oh" for "offhand" (#2033) 2022-12-11 18:36:33 +01:00
Alexander Brandes
f98e526fa0
Add support for 1.19.3 (#2042)
* 1.19.3

* More fixes

* fix: compile time issues for 1.19.3

* fix: new mappings and GeneratorStructureState

* Add 1.19.3 to release and issue template

* Update dependencies for 1.19.3

* Update worldedit-bukkit/adapters/adapter-1_19_3/build.gradle.kts

Co-authored-by: Pierre Maurice Schwang <mail@pschwang.eu>
2022-12-11 18:36:13 +01:00
Alexander Brandes
62f561cb56
Update dev bundle versions (#2040) 2022-12-09 12:38:10 +01:00
renovate[bot]
62345bb64e
Update plugin xyz.jpenilla.run-paper to v2.0.1 (#2039)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-09 10:10:09 +01:00
Hannes Greule
79c23e06c3
Clean up starlight usage (#2029)
clean up starlight usage
2022-12-04 18:56:41 +01:00
renovate[bot]
e0e66ce2db
Update dependency com.github.TownyAdvanced:Towny to v0.98.4.4 (#2028)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-30 10:09:39 +01:00
Alexander Brandes
41ea21f91d
Run announcement workflow after release workflow 2022-11-28 11:34:09 +01:00
Alexander Brandes
faa0908ba8
Run announcement workflow after 'build' workflow 2022-11-28 11:30:32 +01:00
renovate[bot]
eda5bced65
Update dependency gradle to v7.6 (#2027)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-25 19:25:01 +01:00
renovate[bot]
c1bfadd328
Update dependency com.github.TownyAdvanced:Towny to v0.98.4.3 (#2025)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-25 19:24:10 +01:00
renovate[bot]
b432faef36
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.3.11 (#2023)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-20 00:49:37 +01:00
renovate[bot]
6cb9f0e485
Update auto-value to v1.10.1 (#2019)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-20 00:44:22 +01:00
renovate[bot]
6f6c0508df
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.3.10 (#2022)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-20 00:39:43 +01:00
Alexander Brandes
15b351930d
Back to snapshot for development 2022-11-16 11:29:57 +01:00
Alexander Brandes
1771a04ef9
Release 2.4.10 2022-11-15 19:35:25 +01:00
Alexander Brandes
34dbaa633a
Add CF upload task (#2018) 2022-11-15 19:25:03 +01:00
renovate[bot]
fc701b9260
Update plugin xyz.jpenilla.run-paper to v2 (#2017)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-15 19:12:08 +01:00
renovate[bot]
77b1c7eb9c
Update dependency org.mockito:mockito-core to v4.9.0 (#2016)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-15 19:04:13 +01:00
Pierre Maurice Schwang
f33d93591a
Fix Chorus-Plant tree type generation (#2015)
fix: chorusplant tree type must generate inside end_stone
2022-11-14 22:58:54 +01:00
renovate[bot]
f9527ce2b7
Update plugin xyz.jpenilla.run-paper to v1.1.0 (#2007)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-13 12:14:14 +01:00
renovate[bot]
7f8b0a7e21
Update dependency com.github.TownyAdvanced:Towny to v0.98.4.0 (#2008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-13 11:59:17 +01:00
Pierre Maurice Schwang
4c2119e404
Fix error on latest paper while copying entities (#2006) 2022-11-10 17:45:35 +00:00
renovate[bot]
1aa213fb4c
Update dependency com.github.TownyAdvanced:Towny to v0.98.3.19 (#2003)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-06 11:15:01 +01:00
Alexander Brandes
983e4407b5
Back to snapshot for development 2022-11-02 09:52:28 +01:00
Alexander Brandes
0ee18597ae
Release 2.4.9 2022-11-02 09:45:48 +01:00
Pierre Maurice Schwang
2fe54a04b5
Adjust platform specific code to recent changes (#1997)
* chore: remove usage of MCUtil in StarlightRelighter

* chore: cleanup of unused imports

* hacky shit-fuckery for papers new chunksystem und refactor

* chore: address review comments

* Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.3.9 (#2001)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: suppress exceptions for field retrieval, cache fields / methods

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-02 09:41:20 +01:00
Hannes Greule
bbc1db9bd2
Read minecraft version from paper if available (#2002)
* read version from paper if available

* fix for versions without release information
2022-11-01 23:39:42 +01:00
renovate[bot]
e305c5d5ab
Update dependency io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin to v1.3.9 (#2001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-01 22:45:58 +01:00
renovate[bot]
8127d57158
Update dependency com.github.TownyAdvanced:Towny to v0.98.3.15 (#1993)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-25 23:58:53 +02:00
Jordan
0d112b2913
fix: move main-thread call to be implementation from WorldWrapper (#1991)
- Fixes #1990
2022-10-23 21:22:53 +02:00
Jordan
bf28ab47b8
fix: bring default min/max y in line with upstream in AbstractRegion (#1992)
- Fixes #1987
2022-10-23 21:22:43 +02:00
renovate[bot]
e36b838227
Update dependency org.mockito:mockito-core to v4.8.1 (#1988)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-22 10:25:54 +02:00
Alexander Brandes
4f74cc671f
Back to snapshot for development 2022-10-18 23:25:52 +02:00
Alexander Brandes
dce86ec046
Release 2.4.8 2022-10-18 23:23:35 +02:00
Jordan
2082df4141
fix: Add blocked blocks to the correct set (#1975)
* fix: Add blocked blocks to the correct set

* Address comment
2022-10-13 19:21:25 +02:00
Jordan
8971d7064c
feat: Reduce any spam caused by exceptions thrown when writing history (#1976)
- Closes #1960
2022-10-13 19:21:02 +02:00
Jordan
ccb31c0ecc
fix: Do not cast y to byte in history database (#1978)
- y is/was already stored as an int
 - Fixes #1931
2022-10-13 19:20:17 +02:00
Hannes Greule
13eb1f815f
Fix mappings issue with biome-based regen on 1.18.2 (#1982)
access correct fields
2022-10-13 19:20:01 +02:00
Jordan
878509406b
fix: Add items to a player's inventory on the main thread (#1977)
- Fixes #1954
2022-10-12 20:48:56 +02:00
Alexander Brandes
33be942176
[ci skip] Rewrite README for modrinth (#1979)
docs: Rewrite README for modrinth
2022-10-09 16:41:22 +02:00
Alexander Brandes
f356f64c3d
[ci skip] chore: Announce releases on discord (#1980) 2022-10-09 16:40:04 +02:00
Pierre Maurice Schwang
598cd6ba19
Don't error when NPC-annotated "entity" leaves the server (#1969)
fix: no need to remove never-added attachment
2022-10-08 13:34:50 +02:00
Alexander Brandes
292d9f2439
chore: Re-enable CodeQL (#1973) 2022-10-08 13:34:25 +02:00
Alexander Brandes
a62b3158a7
Back to snapshot for development 2022-10-04 14:21:55 +02:00
Alexander Brandes
18bf911fdc
Release 2.4.7 2022-10-04 14:05:36 +02:00
Alexander Brandes
ad38d88642
[ci skip] Link to GH releases (#1970)
chore: Link to GH releases
2022-10-04 13:28:10 +02:00
Aurélien
2e386ea391
Fix region restrictions with methods non-overriden (#1948)
Fix region registrictions with methods non-overriden
See https://github.com/IntellectualSites/fastasyncvoxelsniper/issues/195 for context
2022-10-03 11:20:53 +02:00
Alexander Brandes
a32d8dcbdb
Release to Modrinth (#1968)
* feat: Release to Modrinth

* chore: List-ify gameVersions
2022-10-02 21:50:16 +02:00
Alexander Brandes
87f680683d
Update Upstream
0ef38b5 Use SnakeYaml 1.32+, set loader code point limit. (2194)

Defaults to 64MB, can be set via -Dworldedit.yaml.codePointLimit sysprop.
2022-09-26 23:45:06 +02:00
942570c754 Merge pull request 'Move Clipboards to /tmp + Update upstream' (#3) from upstream into main
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #3
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2022-09-24 19:48:09 +02:00
ae66ccfa8f Fix ci
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2022-09-24 19:32:14 +02:00
c0a135acc9 Merge remote-tracking branch 'upstream/main' into upstream 2022-09-24 19:28:53 +02:00
Alexander Brandes
10b852d416
Back to snapshot for development 2022-09-21 16:45:40 +02:00
Alexander Brandes
4f26561cc1
Release 2.4.6 2022-09-21 16:41:07 +02:00
Pierre Maurice Schwang
ab55d07ffd
Fix #saturate-Pattern (#1944)
* fix: block type access + resources in #saturate pattern

* chore: no need to download jar file in loadModTextures
2022-09-21 16:17:41 +02:00
Alexander Brandes
1706d64ddf
Retrieve the latest asset release version automatically (#1947)
* feat: retrieve the latest release version automatically

(cherry picked from commit a8885a349a567849f6db29565cc65b14e3dab155)

* feat/fix: validate hash of downloaded asset file

* chore: address review comments
Usage of nio Path instead of direct File access
Usage of HexFormat instead of custom implementation
No need for usage of channels

* chore: simplified sha-1 calculation logic

Co-authored-by: Pierre Maurice Schwang <mail@pschwang.eu>
2022-09-15 21:19:31 +02:00
renovate[bot]
c819031d1f
[ci skip] Update dependency com.github.TownyAdvanced:Towny to v0.98.3.8 (#1936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-12 11:18:46 +02:00
renovate[bot]
2547fd590d
Update dependency org.mockito:mockito-core to v4.8.0 (#1940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-12 11:16:14 +02:00
dd8ed8d5c6 Move clipboards to temp
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2022-09-06 21:34:13 +02:00
2553f169aa Merge pull request 'main' (#2) from Mirrors/FastAsyncWorldEdit:main into upstream
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Reviewed-on: #2
2022-09-06 21:32:29 +02:00
Alexander Brandes
673b467ad9
Back to snapshot for development
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-05 11:29:51 +02:00
Alexander Brandes
122b32b9f0
Release 2.4.5 2022-09-05 11:26:31 +02:00
Hannes Greule
5da558e24c
feature: support paper chunk system (#1884)
* avoid usage of ticking chunk future on paper

* fix entity handling

* fix entity handling but on spigot

* seems like no one uses spigot

Co-authored-by: Alexander Brandes <mc.cache@web.de>
2022-09-04 23:30:32 +02:00
Alexander Brandes
3b109ba3ff
Upload files to curseforge on release (#1929)
* chore: Upload files to curseforge on release

* chore: Use direct artifact path
2022-09-04 23:30:02 +02:00
renovate[bot]
8233f132d4
Update dependency com.github.TownyAdvanced:Towny to v0.98.3.6 (#1926)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-08-29 21:31:06 +02:00
Alexander Brandes
aa216a990a
Update upstream
7e61ff1 Work around invalid legacy values in schematics (2171)
2022-08-29 17:21:21 +02:00
Hannes Greule
a51863c6f0
fix: use proper level data for regen (#1921) 2022-08-27 15:49:01 +01:00
Hannes Greule
a6a0b5eb66
fix: avoid recursive LegacyMapper initialization (#1922) 2022-08-27 15:48:54 +01:00
d5722fe9a5 Fix ci
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-27 11:44:59 +02:00
c3b43e13e3 Improve initialisation performance
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2022-08-27 11:35:56 +02:00
8348c0bf19 Merge remote-tracking branch 'upstream/main' 2022-08-27 11:34:50 +02:00
Alexander Brandes
dc2db5f07f
Back to snapshot for development 2022-08-24 10:07:22 +02:00
Alexander Brandes
37bbebcc9d
Release 2.4.4 2022-08-24 10:06:31 +02:00
Alexander Brandes
d5acb4ec51
fix: Lazy fix macro stacktrace (#1911) 2022-08-24 10:02:25 +02:00
e5833aff58 Merge remote-tracking branch 'origin/main'
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-21 17:58:20 +02:00
113c02ec3f Fix demon 2022-08-21 17:57:41 +02:00
0f74e2a0bb Merge pull request 'Remove Schematic Command' (#1) from remove_schematic_command into main
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #1
Reviewed-by: Lixfel <lixfel@steamwar.de>
2022-08-21 17:50:28 +02:00
Chaoscaot
d4c8bd084e Remove Schematic Command
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-21 17:40:19 +02:00
d67ec4fd63 Add CI
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-21 17:31:44 +02:00
276 geänderte Dateien mit 31260 neuen und 2292 gelöschten Zeilen

Datei anzeigen

@ -8,7 +8,7 @@ body:
value: | 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. 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://intellectualsites.github.io/fastasyncworldedit-documentation/). 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://forms.gle/btgdRn9yhGtzEiGW8) form! 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 - type: dropdown
attributes: attributes:
@ -27,12 +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. 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 multiple: false
options: options:
- '1.19.2' - '1.20.1'
- '1.19.1' - '1.20'
- '1.19' - '1.19.4'
- '1.18.2' - '1.18.2'
- '1.17.1' - '1.17.1'
- '1.16.5'
validations: validations:
required: true required: true

Datei anzeigen

@ -4,6 +4,7 @@
"config:base", "config:base",
":semanticCommitsDisabled" ":semanticCommitsDisabled"
], ],
"automerge": true,
"ignoreDeps": [ "ignoreDeps": [
"guava", "guava",
"com.google.guava:guava", "com.google.guava:guava",
@ -18,11 +19,17 @@
"net.fabricmc:fabric-loader", "net.fabricmc:fabric-loader",
"net.fabricmc.fabric-api:fabric-api", "net.fabricmc.fabric-api:fabric-api",
"com.github.luben:zstd-jni", "com.github.luben:zstd-jni",
"net.kyori",
"net.kyori:adventure-nbt",
"org.jetbrains.kotlin.jvm", "org.jetbrains.kotlin.jvm",
"log4j" "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"], "labels": ["Renovate"],
"rebaseWhen": "conflicted" "rebaseWhen": "conflicted",
"schedule": ["on the first day of the month"]
} }

Datei anzeigen

@ -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
Datei anzeigen

@ -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

Datei anzeigen

@ -1,16 +1,17 @@
name: build name: Build
on:
on: [pull_request, push] push:
branches:
- main
jobs: jobs:
build: build:
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }} if: github.repository_owner == 'IntellectualSites'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v3 uses: actions/checkout@v3
- name : Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses : gradle/wrapper-validation-action@v1 uses: gradle/wrapper-validation-action@v1
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
@ -70,3 +71,8 @@ jobs:
with: with:
name: FastAsyncWorldEdit-Bukkit-SNAPSHOT name: FastAsyncWorldEdit-Bukkit-SNAPSHOT
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar 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
Datei anzeigen

@ -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

Datei anzeigen

@ -1,14 +1,12 @@
name: draft release name: draft release
on: on:
push: push:
branches: branches:
- main - main
pull_request: pull_request:
types: [ opened, reopened, synchronize ] types: [opened, reopened, synchronize]
pull_request_target: pull_request_target:
types: [ opened, reopened, synchronize ] types: [opened, reopened, synchronize]
jobs: jobs:
update_release_draft: update_release_draft:
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }} if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}

Datei anzeigen

@ -1,17 +1,15 @@
name: Upload release assets name: Upload release assets
on: on:
release: release:
types: [published] types: [published]
jobs: jobs:
upload_asset: upload_asset:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repository - name: Checkout Repository
uses: actions/checkout@v3 uses: actions/checkout@v3
- name : Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses : gradle/wrapper-validation-action@v1 uses: gradle/wrapper-validation-action@v1
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:

3
.gitignore vendored
Datei anzeigen

@ -24,7 +24,6 @@ logs/
worldedit-bukkit/src/main/java/ignore/* worldedit-bukkit/src/main/java/ignore/*
todo.txt todo.txt
mvn/* mvn/*
docs/
*.sh *.sh
# i18n # i18n
worldedit-core/src/main/resources/lang/* worldedit-core/src/main/resources/lang/*
@ -34,3 +33,5 @@ worldedit-core/src/main/resources/lang/*
/worldedit-core/.factorypath /worldedit-core/.factorypath
.DS_Store .DS_Store
### Run server ignore
run-*

Datei anzeigen

@ -1,44 +1,112 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" id="svg2" xml:space="preserve" width="4000" height="3333.3333" viewBox="0 0 4000 3333.3333" sodipodi:docname="FastAsyncWorldEdit.ai"> <svg
<metadata id="metadata8"> 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> <rdf:RDF>
<cc:Work rdf:about=""> <cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<defs id="defs6"> <defs
<clipPath clipPathUnits="userSpaceOnUse" id="clipPath18"> id="defs6">
<path d="M 0,2500 H 3000 V 0 H 0 Z" id="path16" /> <clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath18">
<path
d="M 0,2500 H 3000 V 0 H 0 Z"
id="path16" />
</clipPath> </clipPath>
</defs> <clipPath
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview4" /> clipPathUnits="userSpaceOnUse"
<g id="g10" inkscape:groupmode="layer" inkscape:label="FastAsyncWorldEdit" transform="matrix(1.3333333,0,0,-1.3333333,0,3333.3333)"> id="clipPath18-2"><path
<g id="g12"> d="M 0,2500 H 3000 V 0 H 0 Z"
<g id="g14" clip-path="url(#clipPath18)"> id="path16-1" /></clipPath></defs>
<g id="g20" transform="translate(1643.0798,2025.9724)"> <sodipodi:namedview
<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" /> pagecolor="#ffffff"
</g> bordercolor="#666666"
<g id="g24" transform="translate(1887.3362,1969.7008)"> borderopacity="1"
<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" /> objecttolerance="10"
</g> gridtolerance="10"
<g id="g28" transform="translate(1463.3822,1497.8577)"> guidetolerance="10"
<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" /> inkscape:pageopacity="0"
</g> inkscape:pageshadow="2"
<g id="g32" transform="translate(1827.8735,1911.1907)"> inkscape:window-width="1440"
<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" /> inkscape:window-height="872"
</g> id="namedview4"
<g id="g36" transform="translate(1531.0129,1613.4966)"> inkscape:pagecheckerboard="0"
<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" /> showgrid="false"
</g> width="512px"
<g id="g40" transform="translate(2519.302,1558.6062)"> inkscape:snap-others="false"
<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" /> inkscape:zoom="0.2263301"
</g> inkscape:cx="797.50773"
<g id="g44" transform="translate(1463.3822,1497.8577)"> inkscape:cy="547.87234"
<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" /> inkscape:window-x="0"
</g> inkscape:window-y="28"
</g> inkscape:window-maximized="0"
</g> inkscape:current-layer="g10" />
</g> <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> </svg>

Vorher

Breite:  |  Höhe:  |  Größe: 5.6 KiB

Nachher

Breite:  |  Höhe:  |  Größe: 6.3 KiB

36
Jenkinsfile vendored Normale Datei
Datei anzeigen

@ -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'
}
}
}
}

Datei anzeigen

@ -1,47 +0,0 @@
image::https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.svg[200,200,align=center]
'''
FastAsyncWorldEdit is a fork of WorldEdit that has huge speed and memory improvements and considerably more features
== A Minecraft Map Editor... that runs in-game!
* 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.
Java Edition required. FastAsyncWorldEdit is compatible with Bukkit, Spigot and Paper.
=== Download FastAsyncWorldEdit
* Snapshot builds: https://ci.athion.net/job/FastAsyncWorldEdit/
* Spigot: https://www.spigotmc.org/resources/fast-async-worldedit.13932/
* Older, unsupported, versions: https://intellectualsites.github.io/download/fawe.html
=== Links
* link:https://discord.gg/intellectualsites[Discord]
* link:https://intellectualsites.github.io/fastasyncworldedit-documentation/[Wiki]
* link:https://github.com/IntellectualSites/FastAsyncWorldEdit/issues[Report Issue]
* link:https://intellectualsites.github.io/fastasyncworldedit-javadocs/[Javadocs]
* link:https://intellectualsites.crowdin.com/fastasyncworldedit[Crowdin (Translations)]
=== Edit The Code
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 link:COMPILING.adoc[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.
Please read link:https://github.com/IntellectualSites/.github/blob/main/CONTRIBUTING.md[CONTRIBUTING.md] for important guidelines to follow.
=== Special thanks to:
image::https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg[150,150]
link:https://jb.gg/OpenSourceSupport[JetBrains], creators of the IntelliJ IDEA, supports us with their Open Source Licenses.
image::https://www.yourkit.com/images/yklogo.png[200,200,align=left]
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 link:https://www.yourkit.com/java/profiler/[YourKit Java Profiler], link:https://www.yourkit.com/.net/profiler/[YourKit .NET Profiler], and link:https://www.yourkit.com/youmonitor/[YourKit YouMonitor].

95
README.md Normale Datei
Datei anzeigen

@ -0,0 +1,95 @@
# 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?
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
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.
## Downloads
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)
Snapshots are available on [Jenkins](https://ci.athion.net/job/FastAsyncWorldEdit/).
## Features
* 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
### 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, read the [compiling documentation](https://github.com/IntellectualSites/FastAsyncWorldEdit/blob/main/COMPILING.adoc).
## Special thanks
<a href="https://jb.gg/OpenSourceSupport"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg" width="150">
</a>
[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 [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/).

Datei anzeigen

@ -1,12 +1,23 @@
import org.ajoberstar.grgit.Grgit 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.TestExceptionFormat.FULL
import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED
import java.net.URI import java.net.URI
import java.time.format.DateTimeFormatter
import xyz.jpenilla.runpaper.task.RunServer
plugins { plugins {
id("io.github.gradle-nexus.publish-plugin") version "1.1.0" id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
id("xyz.jpenilla.run-paper") version "1.0.6" 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(""" logger.lifecycle("""
@ -23,7 +34,7 @@ logger.lifecycle("""
******************************************* *******************************************
""") """)
var rootVersion by extra("2.4.4") var rootVersion by extra("2.7.1")
var snapshot by extra("SNAPSHOT") var snapshot by extra("SNAPSHOT")
var revision: String by extra("") var revision: String by extra("")
var buildNumber by extra("") var buildNumber by extra("")
@ -72,17 +83,29 @@ allprojects {
} }
applyCommonConfiguration() 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 { 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 { runServer {
minecraftVersion("1.19") minecraftVersion("1.20.1")
pluginJars(project(":worldedit-bukkit").file("build/libs/FastAsyncWorldEdit-Bukkit-$version.jar")) pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
.toTypedArray())
} }
} }
nexusPublishing { nexusPublishing {
repositories { this.repositories {
sonatype { sonatype {
nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/")) nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/")) snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"))

Datei anzeigen

@ -12,13 +12,6 @@ repositories {
name = "EngineHub" name = "EngineHub"
url = uri("https://maven.enginehub.org/repo/") url = uri("https://maven.enginehub.org/repo/")
} }
maven {
name = "PaperMC"
url = uri("https://repo.papermc.io/repository/maven-public/")
content {
includeGroupByRegex("io\\.papermc\\..*")
}
}
} }
val properties = Properties().also { props -> val properties = Properties().also { props ->
@ -29,9 +22,9 @@ val properties = Properties().also { props ->
dependencies { dependencies {
implementation(gradleApi()) implementation(gradleApi())
implementation("org.ajoberstar.grgit:grgit-gradle:5.0.0") implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0")
implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") implementation("com.github.johnrengelman:shadow:8.1.1")
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.3.8") implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5")
} }
kotlin { kotlin {

Datei anzeigen

@ -15,7 +15,6 @@ fun Project.applyPaperweightAdapterConfiguration() {
dependencies { dependencies {
"implementation"(project(":worldedit-bukkit")) "implementation"(project(":worldedit-bukkit"))
"implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.9"))
} }
tasks.named("assemble") { tasks.named("assemble") {

Datei anzeigen

@ -45,15 +45,15 @@ fun Project.applyCommonConfiguration() {
continue continue
} }
add(conf.name, "com.google.guava:guava") { add(conf.name, "com.google.guava:guava") {
version { require("31.0.1-jre") } version { require("31.1-jre") }
because("Mojang provides Guava") because("Mojang provides Guava")
} }
add(conf.name, "com.google.code.gson:gson") { add(conf.name, "com.google.code.gson:gson") {
version { require("2.8.8") } version { require("2.10") }
because("Mojang provides Gson") because("Mojang provides Gson")
} }
add(conf.name, "it.unimi.dsi:fastutil") { add(conf.name, "it.unimi.dsi:fastutil") {
version { require("8.5.6") } version { require("8.5.9") }
because("Mojang provides FastUtil") because("Mojang provides FastUtil")
} }
} }

Datei anzeigen

@ -40,12 +40,11 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
dependencies { dependencies {
"compileOnly"("com.google.code.findbugs:jsr305:3.0.2") "compileOnly"("com.google.code.findbugs:jsr305:3.0.2")
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.8.1") "testImplementation"("org.junit.jupiter:junit-jupiter-api:5.10.0")
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.8.1") "testImplementation"("org.junit.jupiter:junit-jupiter-params:5.10.0")
"testImplementation"("org.mockito:mockito-core:3.12.4") "testImplementation"("org.mockito:mockito-core:5.4.0")
"testImplementation"("org.mockito:mockito-junit-jupiter:3.12.4") "testImplementation"("org.mockito:mockito-junit-jupiter:5.4.0")
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.8.1") "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.10.0")
"implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.9"))
} }
// Java 8 turns on doclint which we fail // Java 8 turns on doclint which we fail
@ -59,12 +58,12 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
) )
options.encoding = "UTF-8" options.encoding = "UTF-8"
links( links(
"https://jd.adventure.kyori.net/api/latest/", "https://jd.advntr.dev/api/latest/",
"https://logging.apache.org/log4j/2.x/log4j-api/apidocs/", "https://logging.apache.org/log4j/2.x/log4j-api/apidocs/",
"https://www.antlr.org/api/Java/", "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/core/0.5.7/",
"https://docs.enginehub.org/javadoc/org.enginehub.piston/default-impl/0.5.7/", "https://docs.enginehub.org/javadoc/org.enginehub.piston/default-impl/0.5.7/",
"https://jd.papermc.io/paper/1.18/", "https://jd.papermc.io/paper/1.20/",
"https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/" "https://intellectualsites.github.io/fastasyncworldedit-javadocs/worldedit-core/"
) )
docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}" docTitle = "${rootProject.name}-${project.description}" + " " + "${rootProject.version}"

Datei anzeigen

@ -204,7 +204,7 @@ fun Project.applyLibrariesConfiguration() {
from(libsComponent) from(libsComponent)
group = "com.fastasyncworldedit" group = "com.fastasyncworldedit"
artifactId = "FastAsyncWorldEdit-Libs-${project.name.capitalize()}" artifactId = "FastAsyncWorldEdit-Libs-${project.name.replaceFirstChar(Char::titlecase)}"
version = version version = version
pom { pom {
@ -224,7 +224,7 @@ fun Project.applyLibrariesConfiguration() {
developer { developer {
id.set("NotMyFault") id.set("NotMyFault")
name.set("Alexander Brandes") name.set("Alexander Brandes")
email.set("contact@notmyfault.dev") email.set("contact(at)notmyfault.dev")
organization.set("IntellectualSites") organization.set("IntellectualSites")
} }
developer { developer {

Datei anzeigen

@ -85,7 +85,7 @@ fun Project.applyPlatformAndCoreConfiguration() {
developer { developer {
id.set("NotMyFault") id.set("NotMyFault")
name.set("Alexander Brandes") name.set("Alexander Brandes")
email.set("contact@notmyfault.dev") email.set("contact(at)notmyfault.dev")
organization.set("IntellectualSites") organization.set("IntellectualSites")
} }
developer { developer {

Datei anzeigen

@ -1,53 +1,66 @@
[versions] [versions]
# Minecraft expectations # Minecraft expectations
fastutil = "8.5.6" paper = "1.20.1-R0.1-SNAPSHOT"
guava = "31.0.1-jre" fastutil = "8.5.9"
log4j = "2.17.1" guava = "31.1-jre"
log4j = "2.19.0"
gson = "2.10"
snakeyaml = "2.0"
# Plugins # Plugins
dummypermscompat = "1.10" dummypermscompat = "1.10"
worldguard-bukkit = "7.0.7" worldguard-bukkit = "7.0.9"
mapmanager = "1.8.0-SNAPSHOT" mapmanager = "1.8.0-SNAPSHOT"
griefprevention = "16.18" griefprevention = "16.18.1"
griefdefender = "2.1.0-SNAPSHOT" griefdefender = "2.1.0-SNAPSHOT"
mcore = "7.0.1"
residence = "4.5._13.1" residence = "4.5._13.1"
towny = "0.98.3.5" towny = "0.99.5.10"
redprotect = "1.9.6" plotsquared = "7.0.0-rc.4"
# Third party # Third party
bstats = "3.0.0" bstats = "3.0.2"
sparsebitset = "1.2" sparsebitset = "1.2"
parallelgzip = "1.0.5" parallelgzip = "1.0.5"
adventure = "4.9.3" adventure = "4.14.0"
adventure-bukkit = "4.3.0"
checkerqual = "3.37.0"
truezip = "6.8.4" truezip = "6.8.4"
auto-value = "1.9" auto-value = "1.10.2"
findbugs = "3.0.2" findbugs = "3.0.2"
rhino-runtime = "1.7.14" rhino-runtime = "1.7.14"
zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs
antlr4 = "4.10.1" antlr4 = "4.13.0"
json-simple = "1.1.1" json-simple = "1.1.1"
jlibnoise = "1.0.0" jlibnoise = "1.0.0"
jchronic = "0.2.4a" jchronic = "0.2.4a"
lz4-java = "1.8.0" lz4-java = "1.8.0"
lz4-stream = "1.0.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 ## Internal
adventure-text-minimessage = "4.2.0-SNAPSHOT"
text-adapter = "3.0.6" text-adapter = "3.0.6"
text = "3.0.4" text = "3.0.4"
piston = "0.5.7" piston = "0.5.7"
# Tests # Tests
mockito = "4.7.0" mockito = "5.4.0"
# Gradle plugins # Gradle plugins
pluginyml = "0.5.2" pluginyml = "0.6.0"
minotaur = "2.8.3"
[libraries] [libraries]
# Minecraft expectations # Minecraft expectations
paper = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" }
fastutil = { group = "it.unimi.dsi", name = "fastutil", version.ref = "fastutil" } fastutil = { group = "it.unimi.dsi", name = "fastutil", version.ref = "fastutil" }
log4jBom = { group = "org.apache.logging.log4j", name = "log4j-bom", version.ref = "log4j" } 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" } 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 # Plugins
dummypermscompat = { group = "com.sk89q", name = "dummypermscompat", version.ref = "dummypermscompat" } dummypermscompat = { group = "com.sk89q", name = "dummypermscompat", version.ref = "dummypermscompat" }
@ -55,13 +68,14 @@ worldguard = { group = "com.sk89q.worldguard", name = "worldguard-bukkit", versi
mapmanager = { group = "com.github.InventivetalentDev", name = "MapManager", version.ref = "mapmanager" } mapmanager = { group = "com.github.InventivetalentDev", name = "MapManager", version.ref = "mapmanager" }
griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention", version.ref = "griefprevention" } griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention", version.ref = "griefprevention" }
griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" } griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" }
mcore = { group = "com.massivecraft", name = "mcore", version.ref = "mcore" }
residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" } residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" }
towny = { group = "com.github.TownyAdvanced", name = "Towny", version.ref = "towny" } towny = { group = "com.palmergames.bukkit.towny", name = "towny", version.ref = "towny" }
redprotect = { group = "net.fabiozumbi12", name = "redprotect", version.ref = "redprotect" } plotSquaredCore = { group = "com.intellectualsites.plotsquared", name = "plotsquared-core", version.ref = "plotsquared" }
plotSquaredBukkit = { group = "com.intellectualsites.plotsquared", name = "plotsquared-bukkit", version.ref = "plotsquared" }
# Third Party # Third Party
bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" } 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" } sparsebitset = { group = "com.zaxxer", name = "SparseBitSet", version.ref = "sparsebitset" }
parallelgzip = { group = "org.anarres", name = "parallelgzip", version.ref = "parallelgzip" } parallelgzip = { group = "org.anarres", name = "parallelgzip", version.ref = "parallelgzip" }
adventureNbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" } adventureNbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" }
@ -78,6 +92,15 @@ jlibnoise = { group = "com.sk89q.lib", name = "jlibnoise", version.ref = "jlibno
jchronic = { group = "com.sk89q", name = "jchronic", version.ref = "jchronic" } jchronic = { group = "com.sk89q", name = "jchronic", version.ref = "jchronic" }
lz4Java = { group = "org.lz4", name = "lz4-java", version.ref = "lz4-java" } lz4Java = { group = "org.lz4", name = "lz4-java", version.ref = "lz4-java" }
lz4JavaStream = { group = "net.jpountz", name = "lz4-java-stream", version.ref = "lz4-stream" } 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 # Internal
## Text ## Text
@ -99,3 +122,4 @@ log4jCore = { group = "org.apache.logging.log4j", name = "log4j-core", version.r
[plugins] [plugins]
pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" } pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }

Binäre Datei nicht angezeigt.

Datei anzeigen

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

24
gradlew vendored
Datei anzeigen

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -80,13 +80,10 @@ do
esac esac
done done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # This is normally unused
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# 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"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -133,22 +130,29 @@ location of your Java installation."
fi fi
else else
JAVACMD=java 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. 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 Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) 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 ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | 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" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -193,6 +197,10 @@ if "$cygwin" || "$msys" ; then
done done
fi 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; # Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in # shell script including quotes and variable substitutions, so put them in

1
gradlew.bat vendored
Datei anzeigen

@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%

Datei anzeigen

@ -2,7 +2,7 @@ rootProject.name = "FastAsyncWorldEdit"
include("worldedit-libs") include("worldedit-libs")
listOf("legacy", "1_17_1", "1_18_2", "1_19").forEach { 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") include("worldedit-bukkit:adapters:adapter-$it")
} }

9
steamwarci.yml Normale Datei
Datei anzeigen

@ -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/"

Datei anzeigen

@ -1,3 +1,5 @@
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
applyPaperweightAdapterConfiguration() applyPaperweightAdapterConfiguration()
plugins { plugins {
@ -6,10 +8,7 @@ plugins {
repositories { repositories {
mavenCentral() mavenCentral()
maven { gradlePluginPortal()
name = "PaperMC"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
} }
java { java {
@ -22,6 +21,6 @@ configurations.all {
dependencies { dependencies {
paperDevBundle("1.17.1-R0.1-20220414.034903-210") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210")
compileOnly("io.papermc:paperlib") compileOnly(libs.paperlib)
} }

Datei anzeigen

@ -103,6 +103,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -658,10 +659,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
.registryAccess() .registryAccess()
.ownedRegistryOrThrow( .ownedRegistryOrThrow(
Registry.BIOME_REGISTRY); Registry.BIOME_REGISTRY);
return biomeRegistry.stream() List<ResourceLocation> keys = biomeRegistry.stream()
.map(biomeRegistry::getKey) .map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
.map(CraftNamespacedKey::fromMinecraft) List<NamespacedKey> namespacedKeys = new ArrayList<>();
.collect(Collectors.toList()); 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 @Override

Datei anzeigen

@ -415,7 +415,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
int ordinal = set.getBlock(lx, ly, lz).getOrdinal(); int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
if (ordinal != 0) { if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) {
BlockEntity tile = entry.getValue(); BlockEntity tile = entry.getValue();
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) { if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
if (beacons == null) { if (beacons == null) {
@ -445,10 +445,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
bitMask |= 1 << layer; bitMask |= 1 << layer;
// Changes may still be written to chunk SET // setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
// this chunk GET when #updateGet is called. Future dords, please listen this time.
char[] tmp = set.load(layerNo); char[] tmp = set.load(layerNo);
char[] setArr = new char[4096]; char[] setArr = new char[tmp.length];
System.arraycopy(tmp, 0, setArr, 0, 4096); System.arraycopy(tmp, 0, setArr, 0, tmp.length);
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
// submitted to keep loaded internal chunks to queue target size. // submitted to keep loaded internal chunks to queue target size.

Datei anzeigen

@ -12,6 +12,7 @@ import com.fastasyncworldedit.core.util.ReflectionUtils;
import com.fastasyncworldedit.core.util.TaskManager; import com.fastasyncworldedit.core.util.TaskManager;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.bukkit.adapter.Refraction; 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.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
@ -29,7 +30,9 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.BitStorage; import net.minecraft.util.BitStorage;
import net.minecraft.util.Unit;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
@ -46,6 +49,8 @@ import net.minecraft.world.level.chunk.Palette;
import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.gameevent.GameEventDispatcher; import net.minecraft.world.level.gameevent.GameEventDispatcher;
import net.minecraft.world.level.gameevent.GameEventListener; 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 org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
import sun.misc.Unsafe; import sun.misc.Unsafe;
@ -59,6 +64,8 @@ import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -89,6 +96,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final Field fieldRemove; private static final Field fieldRemove;
private static final Logger LOGGER = LogManagerCompat.getLogger();
static { static {
try { try {
fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l")); fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l"));
@ -209,10 +218,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} else { } else {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
if (nmsChunk != null) { if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk; return nmsChunk;
} }
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
if (nmsChunk != null) { if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk; return nmsChunk;
} }
// Avoid "async" methods from the main thread. // Avoid "async" methods from the main thread.
@ -221,7 +232,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
try { try {
CraftChunk chunk = (CraftChunk) future.get(); 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(); return chunk.getHandle();
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@ -230,6 +255,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); 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) { public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
try { try {
@ -473,7 +505,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
static List<Entity> getEntities(LevelChunk chunk) { static List<Entity> getEntities(LevelChunk chunk) {
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ)); return chunk.level.entityManager.getEntities(chunk.getPos());
} }
} }

Datei anzeigen

@ -79,6 +79,7 @@ import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.OptionalLong;
import java.util.Random; import java.util.Random;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -217,10 +218,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
BiomeProvider biomeProvider = getBiomeProvider(); BiomeProvider biomeProvider = getBiomeProvider();
MinecraftServer server = originalServerWorld.getCraftServer().getServer(); MinecraftServer server = originalServerWorld.getCraftServer().getServer();
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData(); WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
WorldGenSettings newOpts = options.getSeed().isPresent()
WorldGenSettings newOpts = levelProperties.worldGenSettings() ? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
.withSeed(originalWorldData.settings.hardcore(), options.getSeed()); : originalOpts;
LevelSettings newWorldSettings = new LevelSettings( LevelSettings newWorldSettings = new LevelSettings(
"faweregentempworld", "faweregentempworld",
originalWorldData.settings.gameType(), originalWorldData.settings.gameType(),

Datei anzeigen

@ -1,3 +1,5 @@
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
plugins { plugins {
java java
} }
@ -5,14 +7,11 @@ plugins {
applyPaperweightAdapterConfiguration() applyPaperweightAdapterConfiguration()
repositories { repositories {
maven { gradlePluginPortal()
name = "PaperMC"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
} }
dependencies { dependencies {
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
paperDevBundle("1.18.2-R0.1-20220703.182221-166") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.18.2-R0.1-20220920.010157-167")
compileOnly("io.papermc:paperlib") compileOnly(libs.paperlib)
} }

Datei anzeigen

@ -55,6 +55,7 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.BlockMaterial; import com.sk89q.worldedit.world.registry.BlockMaterial;
import io.papermc.lib.PaperLib;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.WritableRegistry; import net.minecraft.core.WritableRegistry;
@ -655,25 +656,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
.registryAccess() .registryAccess()
.ownedRegistryOrThrow( .ownedRegistryOrThrow(
Registry.BIOME_REGISTRY); Registry.BIOME_REGISTRY);
return biomeRegistry.stream() List<ResourceLocation> keys = biomeRegistry.stream()
.map(biomeRegistry::getKey).filter(Objects::nonNull) .map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
.map(CraftNamespacedKey::fromMinecraft) List<NamespacedKey> namespacedKeys = new ArrayList<>();
.collect(Collectors.toList()); 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 @Override
public RelighterFactory getRelighterFactory() { public RelighterFactory getRelighterFactory() {
try { if (PaperLib.isPaper()) {
Class.forName("ca.spottedleaf.starlight.common.light.StarLightEngine"); return new PaperweightStarlightRelighterFactory();
if (PaperweightStarlightRelighter.isUsable()) { } else {
return new PaperweightStarlightRelighterFactory(); return new NMSRelighterFactory();
}
} catch (ThreadDeath td) {
throw td;
} catch (Throwable ignored) {
} }
return new NMSRelighterFactory();
} }
@Override @Override

Datei anzeigen

@ -408,7 +408,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
int ordinal = set.getBlock(lx, ly, lz).getOrdinal(); int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
if (ordinal != 0) { if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) {
BlockEntity tile = entry.getValue(); BlockEntity tile = entry.getValue();
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) { if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
if (beacons == null) { if (beacons == null) {
@ -491,9 +491,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
bitMask |= 1 << getSectionIndex; bitMask |= 1 << getSectionIndex;
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
// this chunk GET when #updateGet is called. Future dords, please listen this time.
char[] tmp = set.load(layerNo); char[] tmp = set.load(layerNo);
char[] setArr = new char[4096]; char[] setArr = new char[tmp.length];
System.arraycopy(tmp, 0, setArr, 0, 4096); System.arraycopy(tmp, 0, setArr, 0, tmp.length);
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
// submitted to keep loaded internal chunks to queue target size. // submitted to keep loaded internal chunks to queue target size.

Datei anzeigen

@ -13,25 +13,26 @@ import com.mojang.datafixers.util.Either;
import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.Refraction; 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.BiomeType;
import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.IdMap; import net.minecraft.core.IdMap;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.BitStorage; import net.minecraft.util.BitStorage;
import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.SimpleBitStorage;
import net.minecraft.util.ThreadingDetector; import net.minecraft.util.ThreadingDetector;
import net.minecraft.util.Unit;
import net.minecraft.util.ZeroBitStorage; import net.minecraft.util.ZeroBitStorage;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
@ -39,7 +40,6 @@ import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; 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.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.GlobalPalette; import net.minecraft.world.level.chunk.GlobalPalette;
import net.minecraft.world.level.chunk.HashMapPalette; import net.minecraft.world.level.chunk.HashMapPalette;
@ -49,8 +49,8 @@ import net.minecraft.world.level.chunk.LinearPalette;
import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.Palette;
import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.SingleValuePalette; import net.minecraft.world.level.chunk.SingleValuePalette;
import net.minecraft.world.level.gameevent.GameEventDispatcher; import org.apache.logging.log4j.Logger;
import net.minecraft.world.level.gameevent.GameEventListener; import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk; import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
import sun.misc.Unsafe; import sun.misc.Unsafe;
@ -73,6 +73,8 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function; import java.util.function.Function;
public final class PaperweightPlatformAdapter extends NMSAdapter { public final class PaperweightPlatformAdapter extends NMSAdapter {
@ -104,6 +106,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final Field fieldRemove; private static final Field fieldRemove;
private static final Logger LOGGER = LogManagerCompat.getLogger();
static { static {
try { try {
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d")); fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
@ -237,10 +241,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} else { } else {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
if (nmsChunk != null) { if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk; return nmsChunk;
} }
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
if (nmsChunk != null) { if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk; return nmsChunk;
} }
// Avoid "async" methods from the main thread. // Avoid "async" methods from the main thread.
@ -249,7 +255,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
try { try {
CraftChunk chunk = (CraftChunk) future.get(); 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(); return chunk.getHandle();
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
@ -258,6 +278,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); 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) { public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
try { try {
@ -588,7 +615,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
static List<Entity> getEntities(LevelChunk chunk) { static List<Entity> getEntities(LevelChunk chunk) {
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ)); return chunk.level.entityManager.getEntities(chunk.getPos());
} }
record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> { record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {

Datei anzeigen

@ -14,16 +14,12 @@ import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.server.MCUtil; import net.minecraft.server.MCUtil;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -35,7 +31,6 @@ import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter { public class PaperweightStarlightRelighter implements Relighter {
public static final MethodHandle RELIGHT;
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32 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 int CHUNKS_PER_BATCH_SQRT_LOG2 = 5; // for shifting
@ -43,26 +38,6 @@ public class PaperweightStarlightRelighter implements Relighter {
private static final TicketType<Unit> FAWE_TICKET = TicketType.create("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 static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT);
static {
MethodHandle tmp = null;
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
tmp = lookup.findVirtual(
ThreadedLevelLightEngine.class,
"relight",
MethodType.methodType(
int.class, // return type
// params
Set.class,
Consumer.class,
IntConsumer.class
)
);
} catch (NoSuchMethodException | IllegalAccessException e) {
LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
}
RELIGHT = tmp;
}
private final ServerLevel serverLevel; private final ServerLevel serverLevel;
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
@ -76,10 +51,6 @@ public class PaperweightStarlightRelighter implements Relighter {
this.delegate = new NMSRelighter(queue); this.delegate = new NMSRelighter(queue);
} }
public static boolean isUsable() {
return RELIGHT != null;
}
@Override @Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) { public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
areaLock.lock(); areaLock.lock();
@ -169,14 +140,9 @@ public class PaperweightStarlightRelighter implements Relighter {
IntConsumer processCallback IntConsumer processCallback
) { ) {
try { try {
int unused = (int) RELIGHT.invokeExact( serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
serverLevel.getChunkSource().getLightEngine(), } catch (Exception e) {
coords, LOGGER.error("Error occurred on relighting", e);
chunkCallback, // callback per chunk
processCallback // callback for all chunks
);
} catch (Throwable throwable) {
LOGGER.error("Error occurred on relighting", throwable);
} }
} }

Datei anzeigen

@ -146,10 +146,10 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "K")); chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "K"));
chunkSourceField.setAccessible(true); chunkSourceField.setAccessible(true);
ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "i")); ringPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("ringPositions", "h"));
ringPositionsField.setAccessible(true); ringPositionsField.setAccessible(true);
hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "j")); hasGeneratedPositionsField = ChunkGenerator.class.getDeclaredField(Refraction.pickName("hasGeneratedPositions", "i"));
hasGeneratedPositionsField.setAccessible(true); hasGeneratedPositionsField.setAccessible(true);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -213,10 +213,9 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
BiomeProvider biomeProvider = getBiomeProvider(); BiomeProvider biomeProvider = getBiomeProvider();
MinecraftServer server = originalServerWorld.getCraftServer().getServer(); MinecraftServer server = originalServerWorld.getCraftServer().getServer();
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData(); WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
WorldGenSettings originalOpts = levelProperties.worldGenSettings();
WorldGenSettings newOpts = options.getSeed().isPresent() WorldGenSettings newOpts = options.getSeed().isPresent()
? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed)) ? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
: originalOpts; : originalOpts;
LevelSettings newWorldSettings = new LevelSettings( LevelSettings newWorldSettings = new LevelSettings(
"faweregentempworld", "faweregentempworld",

Datei anzeigen

@ -1,3 +1,5 @@
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
plugins { plugins {
java java
} }
@ -5,13 +7,10 @@ plugins {
applyPaperweightAdapterConfiguration() applyPaperweightAdapterConfiguration()
repositories { repositories {
maven { gradlePluginPortal()
name = "PaperMC"
url = uri("https://repo.papermc.io/repository/maven-public/")
}
} }
dependencies { dependencies {
paperDevBundle("1.19.2-R0.1-20220805.230830-1") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.2-R0.1-20221206.184705-189")
compileOnly("io.papermc:paperlib") compileOnly(libs.paperlib)
} }

Datei anzeigen

@ -511,7 +511,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
@Override @Override
public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) { public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) {
((CraftPlayer) player).getHandle().networkManager.send(ClientboundBlockEntityDataPacket.create( ((CraftPlayer) player).getHandle().connection.send(ClientboundBlockEntityDataPacket.create(
new StructureBlockEntity( new StructureBlockEntity(
new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()), new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()),
Blocks.STRUCTURE_BLOCK.defaultBlockState() Blocks.STRUCTURE_BLOCK.defaultBlockState()
@ -522,7 +522,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
@Override @Override
public void sendFakeOP(Player player) { public void sendFakeOP(Player player) {
((CraftPlayer) player).getHandle().networkManager.send(new ClientboundEntityEventPacket( ((CraftPlayer) player).getHandle().connection.send(new ClientboundEntityEventPacket(
((CraftPlayer) player).getHandle(), (byte) 28 ((CraftPlayer) player).getHandle(), (byte) 28
)); ));
} }

Datei anzeigen

@ -55,6 +55,7 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.registry.BlockMaterial; import com.sk89q.worldedit.world.registry.BlockMaterial;
import io.papermc.lib.PaperLib;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.WritableRegistry; import net.minecraft.core.WritableRegistry;
@ -97,6 +98,8 @@ import org.bukkit.entity.Player;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -114,6 +117,14 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> { IDelegateBukkitImplAdapter<net.minecraft.nbt.Tag> {
private static final Logger LOGGER = LogManagerCompat.getLogger(); 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; private final PaperweightAdapter parent;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -490,7 +501,7 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) { public void sendFakeChunk(org.bukkit.World world, Player player, ChunkPacket chunkPacket) {
ServerLevel nmsWorld = ((CraftWorld) world).getHandle(); ServerLevel nmsWorld = ((CraftWorld) world).getHandle();
ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ()); ChunkHolder map = PaperweightPlatformAdapter.getPlayerChunk(nmsWorld, chunkPacket.getChunkX(), chunkPacket.getChunkZ());
if (map != null && map.wasAccessibleSinceLastSave()) { if (map != null && wasAccessibleSinceLastSave(map)) {
boolean flag = false; boolean flag = false;
// PlayerChunk.d players = map.players; // PlayerChunk.d players = map.players;
Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag) Stream<ServerPlayer> stream = /*players.a(new ChunkCoordIntPair(packet.getChunkX(), packet.getChunkZ()), flag)
@ -585,23 +596,6 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
return true; 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 @Override
public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) {
final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
@ -651,25 +645,26 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
.registryAccess() .registryAccess()
.ownedRegistryOrThrow( .ownedRegistryOrThrow(
Registry.BIOME_REGISTRY); Registry.BIOME_REGISTRY);
return biomeRegistry.stream() List<ResourceLocation> keys = biomeRegistry.stream()
.map(biomeRegistry::getKey).filter(Objects::nonNull) .map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
.map(CraftNamespacedKey::fromMinecraft) List<NamespacedKey> namespacedKeys = new ArrayList<>();
.collect(Collectors.toList()); 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 @Override
public RelighterFactory getRelighterFactory() { public RelighterFactory getRelighterFactory() {
try { if (PaperLib.isPaper()) {
Class.forName("ca.spottedleaf.starlight.common.light.StarLightEngine"); return new PaperweightStarlightRelighterFactory();
if (PaperweightStarlightRelighter.isUsable()) { } else {
return new PaperweightStarlightRelighterFactory(); return new NMSRelighterFactory();
}
} catch (ThreadDeath td) {
throw td;
} catch (Throwable ignored) {
} }
return new NMSRelighterFactory();
} }
@Override @Override
@ -691,4 +686,16 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
return new PaperweightPostProcessor(); 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;
}
} }

Datei anzeigen

@ -14,7 +14,6 @@ import com.fastasyncworldedit.core.queue.implementation.blocks.CharGetBlocks;
import com.fastasyncworldedit.core.util.MathMan; import com.fastasyncworldedit.core.util.MathMan;
import com.fastasyncworldedit.core.util.collection.AdaptedMap; import com.fastasyncworldedit.core.util.collection.AdaptedMap;
import com.google.common.base.Suppliers; import com.google.common.base.Suppliers;
import com.google.common.collect.Iterables;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.ListTag; import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.StringTag; import com.sk89q.jnbt.StringTag;
@ -82,7 +81,6 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks { public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBlocks {
@ -408,7 +406,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
int ordinal = set.getBlock(lx, ly, lz).getOrdinal(); int ordinal = set.getBlock(lx, ly, lz).getOrdinal();
if (ordinal != 0) { if (ordinal != BlockTypesCache.ReservedIDs.__RESERVED__) {
BlockEntity tile = entry.getValue(); BlockEntity tile = entry.getValue();
if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) { if (PaperLib.isPaper() && tile instanceof BeaconBlockEntity) {
if (beacons == null) { if (beacons == null) {
@ -490,9 +488,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
bitMask |= 1 << getSectionIndex; bitMask |= 1 << getSectionIndex;
char[] tmp = set.load(layerNo); char[] setArr = set.load(layerNo);
char[] setArr = new char[4096];
System.arraycopy(tmp, 0, setArr, 0, 4096);
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was // synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
// submitted to keep loaded internal chunks to queue target size. // submitted to keep loaded internal chunks to queue target size.
@ -676,7 +672,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
} }
if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) { if (Settings.settings().EXPERIMENTAL.REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL) {
for (UUID uuid : entityRemoves) { for (UUID uuid : entityRemoves) {
Entity entity = nmsWorld.entityManager.getEntityGetter().get(uuid); Entity entity = nmsWorld.getEntities().get(uuid);
if (entity != null) { if (entity != null) {
removeEntity(entity); removeEntity(entity);
} }

Datei anzeigen

@ -1,5 +1,6 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R1; 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.CachedBukkitAdapter;
import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore; import com.fastasyncworldedit.bukkit.adapter.DelegateSemaphore;
import com.fastasyncworldedit.bukkit.adapter.NMSAdapter; import com.fastasyncworldedit.bukkit.adapter.NMSAdapter;
@ -18,20 +19,22 @@ import com.sk89q.worldedit.world.biome.BiomeTypes;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache; import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import io.papermc.paper.world.ChunkEntitySlices;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.IdMap; import net.minecraft.core.IdMap;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket; import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
import net.minecraft.util.BitStorage; import net.minecraft.util.BitStorage;
import net.minecraft.util.ExceptionCollector;
import net.minecraft.util.SimpleBitStorage; import net.minecraft.util.SimpleBitStorage;
import net.minecraft.util.ThreadingDetector; import net.minecraft.util.ThreadingDetector;
import net.minecraft.util.Unit;
import net.minecraft.util.ZeroBitStorage; import net.minecraft.util.ZeroBitStorage;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
@ -49,6 +52,7 @@ import net.minecraft.world.level.chunk.LinearPalette;
import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.Palette;
import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.chunk.PalettedContainer;
import net.minecraft.world.level.chunk.SingleValuePalette; 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.GameEventDispatcher;
import net.minecraft.world.level.gameevent.GameEventListener; import net.minecraft.world.level.gameevent.GameEventListener;
import org.bukkit.craftbukkit.v1_19_R1.CraftChunk; import org.bukkit.craftbukkit.v1_19_R1.CraftChunk;
@ -106,6 +110,11 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final Field fieldRemove; 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 { static {
try { try {
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d")); fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
@ -176,6 +185,28 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
throw new Error("data type scale not a power of two"); throw new Error("data type scale not a power of two");
} }
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale); 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) { } catch (RuntimeException e) {
throw e; throw e;
} catch (Throwable rethrow) { } catch (Throwable rethrow) {
@ -241,10 +272,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} else { } else {
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ); LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
if (nmsChunk != null) { if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk; return nmsChunk;
} }
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ); nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
if (nmsChunk != null) { if (nmsChunk != null) {
addTicket(serverLevel, chunkX, chunkZ);
return nmsChunk; return nmsChunk;
} }
// Avoid "async" methods from the main thread. // Avoid "async" methods from the main thread.
@ -262,6 +295,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ)); 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) { public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap; ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
try { try {
@ -278,20 +318,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
return; return;
} }
ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ); ChunkPos coordIntPair = new ChunkPos(chunkX, chunkZ);
// UNLOADED_CHUNK LevelChunk levelChunk;
Optional<LevelChunk> optional = ((Either) chunkHolder
.getTickingChunkFuture()
.getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left();
if (PaperLib.isPaper()) { if (PaperLib.isPaper()) {
// getChunkAtIfLoadedImmediately is paper only // getChunkAtIfLoadedImmediately is paper only
optional = optional.or(() -> Optional.ofNullable(nmsWorld levelChunk = nmsWorld
.getChunkSource() .getChunkSource()
.getChunkAtIfLoadedImmediately(chunkX, chunkZ))); .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 (optional.isEmpty()) { if (levelChunk == null) {
return; return;
} }
LevelChunk levelChunk = optional.get();
TaskManager.taskManager().task(() -> { TaskManager.taskManager().task(() -> {
ClientboundLevelChunkWithLightPacket packet; ClientboundLevelChunkWithLightPacket packet;
if (PaperLib.isPaper()) { if (PaperLib.isPaper()) {
@ -589,7 +630,32 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
static List<Entity> getEntities(LevelChunk chunk) { static List<Entity> getEntities(LevelChunk chunk) {
return chunk.level.entityManager.getEntities(new ChunkPos(chunk.locX, chunk.locZ)); 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> { record FakeIdMapBlock(int size) implements IdMap<net.minecraft.world.level.block.state.BlockState> {

Datei anzeigen

@ -12,18 +12,14 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArraySet; import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.server.MCUtil; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.server.level.TicketType; import net.minecraft.server.level.TicketType;
import net.minecraft.util.Unit; import net.minecraft.util.Unit;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -35,34 +31,13 @@ import java.util.function.IntConsumer;
public class PaperweightStarlightRelighter implements Relighter { public class PaperweightStarlightRelighter implements Relighter {
public static final MethodHandle RELIGHT;
private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final Logger LOGGER = LogManagerCompat.getLogger();
private static final int CHUNKS_PER_BATCH = 1024; // 32 * 32 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 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 TicketType<Unit> FAWE_TICKET = TicketType.create("fawe_ticket", (a, b) -> 0);
private static final int LIGHT_LEVEL = MCUtil.getTicketLevelFor(ChunkStatus.LIGHT); private static final int LIGHT_LEVEL = ChunkMap.MAX_VIEW_DISTANCE + ChunkStatus.getDistance(ChunkStatus.LIGHT);
static {
MethodHandle tmp = null;
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
tmp = lookup.findVirtual(
ThreadedLevelLightEngine.class,
"relight",
MethodType.methodType(
int.class, // return type
// params
Set.class,
Consumer.class,
IntConsumer.class
)
);
} catch (NoSuchMethodException | IllegalAccessException e) {
LOGGER.error("Failed to locate 'relight' method in ThreadedLevelLightEngine. Is everything up to date?", e);
}
RELIGHT = tmp;
}
private final ServerLevel serverLevel; private final ServerLevel serverLevel;
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
@ -76,10 +51,6 @@ public class PaperweightStarlightRelighter implements Relighter {
this.delegate = new NMSRelighter(queue); this.delegate = new NMSRelighter(queue);
} }
public static boolean isUsable() {
return RELIGHT != null;
}
@Override @Override
public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) { public boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask) {
areaLock.lock(); areaLock.lock();
@ -169,14 +140,9 @@ public class PaperweightStarlightRelighter implements Relighter {
IntConsumer processCallback IntConsumer processCallback
) { ) {
try { try {
int unused = (int) RELIGHT.invokeExact( serverLevel.getChunkSource().getLightEngine().relight(coords, chunkCallback, processCallback);
serverLevel.getChunkSource().getLightEngine(), } catch (Exception e) {
coords, LOGGER.error("Error occurred on relighting", e);
chunkCallback, // callback per chunk
processCallback // callback for all chunks
);
} catch (Throwable throwable) {
LOGGER.error("Error occurred on relighting", throwable);
} }
} }

Datei anzeigen

@ -214,10 +214,9 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData; PrimaryLevelData originalWorldData = originalServerWorld.serverLevelData;
MinecraftServer server = originalServerWorld.getCraftServer().getServer(); MinecraftServer server = originalServerWorld.getCraftServer().getServer();
PrimaryLevelData levelProperties = (PrimaryLevelData) server.getWorldData(); WorldGenSettings originalOpts = originalWorldData.worldGenSettings();
WorldGenSettings originalOpts = levelProperties.worldGenSettings();
WorldGenSettings newOpts = options.getSeed().isPresent() WorldGenSettings newOpts = options.getSeed().isPresent()
? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed)) ? originalOpts.withSeed(originalWorldData.isHardcore(), OptionalLong.of(seed))
: originalOpts; : originalOpts;
LevelSettings newWorldSettings = new LevelSettings( LevelSettings newWorldSettings = new LevelSettings(
"faweregentempworld", "faweregentempworld",
@ -295,7 +294,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
generatorSettingBaseSupplier generatorSettingBaseSupplier
); );
} else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) { } else if (originalGenerator instanceof CustomChunkGenerator customChunkGenerator) {
chunkGenerator = customChunkGenerator.delegate; chunkGenerator = customChunkGenerator.getDelegate();
} else { } else {
LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName()); LOGGER.error("Unsupported generator type {}", originalGenerator.getClass().getName());
return false; return false;
@ -365,7 +364,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
Fawe.instance().getQueueHandler().sync(() -> { Fawe.instance().getQueueHandler().sync(() -> {
try { try {
freshChunkProvider.close(false); freshChunkProvider.close(false);
} catch (IOException e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
}); });

Datei anzeigen

@ -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 anzeigen

@ -0,0 +1,93 @@
/*
* 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_R2;
import com.mojang.authlib.GameProfile;
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 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) {
}
}

Datei anzeigen

@ -0,0 +1,180 @@
/*
* 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_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_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_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());
}
}
// Not sure why neighborChanged is deprecated
@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() {
}
}

Datei anzeigen

@ -0,0 +1,189 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_19_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_19_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", "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::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;
}
}

Datei anzeigen

@ -0,0 +1,706 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_19_R2.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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.core.registries.Registries;
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.dedicated.DedicatedServer;
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_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_19_R2.CraftServer;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_19_R2.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;
import static net.minecraft.core.registries.Registries.BIOME;
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 DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.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(
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.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()
.registryOrThrow(BIOME);
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()
.registryOrThrow(BIOME);
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;
}
}

Datei anzeigen

@ -0,0 +1,286 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_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 anzeigen

@ -0,0 +1,248 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_19_R2.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;
}
}

Datei anzeigen

@ -0,0 +1,34 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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();
}
}

Datei anzeigen

@ -0,0 +1,700 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R2;
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.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 org.bukkit.craftbukkit.v1_19_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.function.Function;
import static net.minecraft.core.registries.Registries.BIOME;
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().registryOrThrow(BIOME);
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();
}
}
}

Datei anzeigen

@ -0,0 +1,175 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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)
);
}
}

Datei anzeigen

@ -0,0 +1,205 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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.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);
}
}

Datei anzeigen

@ -0,0 +1,28 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_19_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);
}
}

Datei anzeigen

@ -0,0 +1,161 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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();
}
}

Datei anzeigen

@ -0,0 +1,594 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_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_19_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.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ChunkMap;
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.ChunkGeneratorStructureState;
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.WorldOptions;
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_R2.CraftServer;
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R2.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import javax.annotation.Nullable;
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;
import static net.minecraft.core.registries.Registries.BIOME;
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 generatorStructureStateField;
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", "e"));
generatorSettingBaseSupplierField.setAccessible(true);
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
generatorSettingFlatField.setAccessible(true);
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
delegateField.setAccessible(true);
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "L"));
chunkSourceField.setAccessible(true);
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "w"));
generatorStructureStateField.setAccessible(true);
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
ringPositionsField.setAccessible(true);
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
Refraction.pickName("hasGeneratedPositions", "h")
);
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();
WorldOptions originalOpts = originalWorldData.worldGenOptions();
WorldOptions newOpts = options.getSeed().isPresent()
? originalOpts.withSeed(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.getDataConfiguration()
);
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
originalWorldData.isFlatWorld()
? PrimaryLevelData.SpecialWorldProperty.FLAT
: originalWorldData.isDebugWorld()
? PrimaryLevelData.SpecialWorldProperty.DEBUG
: PrimaryLevelData.SpecialWorldProperty.NONE;
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
BiomeProvider biomeProvider = getBiomeProvider();
//init world
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
server,
server.executor,
session,
newWorldData,
originalServerWorld.dimension(),
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
.getOrThrow(levelStemResourceKey),
new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(),
seed,
ImmutableList.of(),
false,
environment,
generator,
biomeProvider
) {
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
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(generatorSettingFlat);
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
originalGenerator);
BiomeSource biomeSource;
if (options.hasBiomeType()) {
biomeSource = new FixedBiomeSource(
DedicatedServer.getServer().registryAccess()
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
)
);
} else {
biomeSource = originalGenerator.getBiomeSource();
}
chunkGenerator = new NoiseBasedChunkGenerator(
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; - Does not exist anymore, may need to be re-addressed
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;
}
};
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
// Optimisation for needless ring position calculation when the seed and biome is the same.
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
if (hasGeneratedPositions) {
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
origPositions);
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
ringPositionsField.set(newState, copy);
hasGeneratedPositionsField.setBoolean(newState, true);
}
}
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(BIOME), 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);
}
}
}

Datei anzeigen

@ -0,0 +1,16 @@
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
plugins {
java
}
applyPaperweightAdapterConfiguration()
repositories {
gradlePluginPortal()
}
dependencies {
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.4-R0.1-20230608.201059-104")
compileOnly(libs.paperlib)
}

Datei anzeigen

@ -0,0 +1,93 @@
/*
* 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_R3;
import com.mojang.authlib.GameProfile;
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 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) {
}
}

Datei anzeigen

@ -0,0 +1,180 @@
/*
* 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_R3;
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_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.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());
}
}
// Not sure why neighborChanged is deprecated
@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() {
}
}

Datei anzeigen

@ -0,0 +1,189 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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_R3.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_R3.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::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;
}
}

Datei anzeigen

@ -0,0 +1,652 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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.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_R3.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.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.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.core.registries.Registries;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
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.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 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_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_19_R3.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;
import static net.minecraft.core.registries.Registries.BIOME;
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 DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.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();
}
@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(
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.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()
.registryOrThrow(BIOME);
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()
.registryOrThrow(BIOME);
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;
}
}

Datei anzeigen

@ -0,0 +1,286 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.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.locX, levelChunk.locZ));
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 anzeigen

@ -0,0 +1,248 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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_R3.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;
}
}

Datei anzeigen

@ -0,0 +1,34 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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();
}
}

Datei anzeigen

@ -0,0 +1,723 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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.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 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.entity.BlockEntity;
import net.minecraft.world.level.chunk.ChunkStatus;
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 org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_19_R3.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;
import static net.minecraft.core.registries.Registries.BIOME;
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 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 {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
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 = 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 = lookup.unreflect(removeGameEventListener);
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
Refraction.pickName(
"removeBlockEntityTicker",
"l"
), BlockPos.class
);
removeBlockEntityTicker.setAccessible(true);
methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker);
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
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", "L"));
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
} catch (NoSuchFieldException ignored) {
}
POST_CHUNK_REWRITE = chunkRewrite;
} catch (RuntimeException | Error e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
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 + "!");
}
}
addTicket(serverLevel, chunkX, chunkZ);
return (LevelChunk) chunk.getHandle(ChunkStatus.FULL);
} 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.UNLOAD_COOLDOWN, 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().registryOrThrow(BIOME);
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();
}
}
}

Datei anzeigen

@ -0,0 +1,175 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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)
);
}
}

Datei anzeigen

@ -0,0 +1,205 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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);
}
}

Datei anzeigen

@ -0,0 +1,28 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3;
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_R3.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);
}
}

Datei anzeigen

@ -0,0 +1,161 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.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();
}
}

Datei anzeigen

@ -0,0 +1,599 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_19_R3.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_R3.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.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ChunkMap;
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.ChunkGeneratorStructureState;
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.WorldOptions;
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.Chunk;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import javax.annotation.Nullable;
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;
import static net.minecraft.core.registries.Registries.BIOME;
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 generatorStructureStateField;
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", "e"));
generatorSettingBaseSupplierField.setAccessible(true);
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
generatorSettingFlatField.setAccessible(true);
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
delegateField.setAccessible(true);
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "M"));
chunkSourceField.setAccessible(true);
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "w"));
generatorStructureStateField.setAccessible(true);
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
ringPositionsField.setAccessible(true);
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
Refraction.pickName("hasGeneratedPositions", "h")
);
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();
WorldOptions originalOpts = originalWorldData.worldGenOptions();
WorldOptions newOpts = options.getSeed().isPresent()
? originalOpts.withSeed(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.getDataConfiguration()
);
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
originalWorldData.isFlatWorld()
? PrimaryLevelData.SpecialWorldProperty.FLAT
: originalWorldData.isDebugWorld()
? PrimaryLevelData.SpecialWorldProperty.DEBUG
: PrimaryLevelData.SpecialWorldProperty.NONE;
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
BiomeProvider biomeProvider = getBiomeProvider();
//init world
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
server,
server.executor,
session,
newWorldData,
originalServerWorld.dimension(),
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
.getOrThrow(levelStemResourceKey),
new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(),
seed,
ImmutableList.of(),
false,
environment,
generator,
biomeProvider
) {
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
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(generatorSettingFlat);
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
originalGenerator);
BiomeSource biomeSource;
if (options.hasBiomeType()) {
biomeSource = new FixedBiomeSource(
DedicatedServer.getServer().registryAccess()
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
)
);
} else {
biomeSource = originalGenerator.getBiomeSource();
}
chunkGenerator = new NoiseBasedChunkGenerator(
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; - Does not exist anymore, may need to be re-addressed
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;
}
};
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
// Optimisation for needless ring position calculation when the seed and biome is the same.
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
if (hasGeneratedPositions) {
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
origPositions);
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
ringPositionsField.set(newState, copy);
hasGeneratedPositionsField.setBoolean(newState, true);
}
}
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(BIOME), 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(() -> {
final CraftWorld world = freshWorld.getWorld();
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
blockPopulator.populate(world, random, chunk);
});
}
@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);
}
}
}

Datei anzeigen

@ -0,0 +1,17 @@
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
plugins {
java
}
applyPaperweightAdapterConfiguration()
repositories {
gradlePluginPortal()
}
dependencies {
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.1-R0.1-20230818.021330-113")
compileOnly(libs.paperlib)
}

Datei anzeigen

@ -0,0 +1,93 @@
/*
* 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_20_R1;
import com.mojang.authlib.GameProfile;
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 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, boolean front) {
}
}

Datei anzeigen

@ -0,0 +1,181 @@
/*
* 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_20_R1;
import com.sk89q.worldedit.util.nbt.CompoundBinaryTag;
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.world.block.BlockState;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.FullChunkStatus;
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_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.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(BlockPos position, 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(FullChunkStatus.BLOCK_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());
}
}
// Not sure why neighborChanged is deprecated
@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() {
}
}

Datei anzeigen

@ -0,0 +1,185 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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_20_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.LiquidBlock;
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.PushReaction;
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
public class PaperweightBlockMaterial implements BlockMaterial {
private final Block block;
private final BlockState blockState;
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.craftBlockData = CraftBlockData.fromData(blockState);
this.craftMaterial = craftBlockData.getMaterial();
BlockBehaviour.Properties blockInfo = ReflectionUtil.getField(BlockBehaviour.class, block,
Refraction.pickName("properties", "aN"));
this.isTranslucent = !(boolean) ReflectionUtil.getField(BlockBehaviour.Properties.class, blockInfo,
Refraction.pickName("canOcclude", "m")
);
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;
}
@Override
public boolean isAir() {
return blockState.isAir();
}
@Override
public boolean isFullCube() {
return craftMaterial.isOccluding();
}
@Override
public boolean isOpaque() {
return blockState.isOpaque();
}
@Override
public boolean isPowerSource() {
return blockState.isSignalSource();
}
@Override
public boolean isLiquid() {
// TODO: Better check ?
return block instanceof LiquidBlock;
}
@Override
public boolean isSolid() {
// TODO: Replace
return blockState.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 blockState.getPistonPushReaction() == PushReaction.DESTROY;
}
@Override
public boolean isUnpushable() {
return blockState.getPistonPushReaction() == PushReaction.BLOCK;
}
@Override
public boolean isTicksRandomly() {
return block.isRandomlyTicking(blockState);
}
@Override
public boolean isMovementBlocker() {
return craftMaterial.isSolid();
}
@Override
public boolean isBurnable() {
return craftMaterial.isBurnable();
}
@Override
public boolean isToolRequired() {
// Removed in 1.16.1, this is not present in higher versions
return false;
}
@Override
public boolean isReplacedDuringPlacement() {
return blockState.canBeReplaced();
}
@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 block.defaultMapColor().col;
}
}

Datei anzeigen

@ -0,0 +1,652 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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.bukkit.BukkitAdapter;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R1.PaperweightAdapter;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_R1.nbt.PaperweightLazyCompoundTag;
import com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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.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.core.registries.Registries;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
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.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 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_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.block.CraftBlockState;
import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_20_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;
import static net.minecraft.core.registries.Registries.BIOME;
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 DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.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();
}
@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(
DedicatedServer.getServer().registryAccess().registryOrThrow(Registries.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()
.registryOrThrow(BIOME);
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()
.registryOrThrow(BIOME);
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;
}
}

Datei anzeigen

@ -0,0 +1,287 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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.server.level.FullChunkStatus;
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_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_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.locX, levelChunk.locZ));
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(FullChunkStatus.BLOCK_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 anzeigen

@ -0,0 +1,248 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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_20_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;
}
}

Datei anzeigen

@ -0,0 +1,34 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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();
}
}

Datei anzeigen

@ -0,0 +1,741 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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.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 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.entity.BlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
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 org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_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.invoke.MethodType;
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;
import static java.lang.invoke.MethodType.methodType;
import static net.minecraft.core.registries.Registries.BIOME;
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;
/*
* This is a workaround for the changes from https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1fddefce1cdce44010927b888432bf70c0e88cde#src/main/java/org/bukkit/craftbukkit/CraftChunk.java
* and is only needed to support 1.19.4 versions before *and* after this change.
*/
private static final MethodHandle CRAFT_CHUNK_GET_HANDLE;
private static final Field fieldRemove;
private static final Logger LOGGER = LogManagerCompat.getLogger();
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 {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
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", "g"));
fieldTickingFluidCount.setAccessible(true);
fieldTickingBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("tickingBlockCount", "f"));
fieldTickingBlockCount.setAccessible(true);
fieldNonEmptyBlockCount = LevelChunkSection.class.getDeclaredField(Refraction.pickName("nonEmptyBlockCount", "e"));
fieldNonEmptyBlockCount.setAccessible(true);
Method getVisibleChunkIfPresent = ChunkMap.class.getDeclaredMethod(Refraction.pickName(
"getVisibleChunkIfPresent",
"b"
), long.class);
getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = 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 = lookup.unreflect(removeGameEventListener);
Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod(
Refraction.pickName(
"removeBlockEntityTicker",
"l"
), BlockPos.class
);
removeBlockEntityTicker.setAccessible(true);
methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker);
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
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", "M"));
SERVER_LEVEL_ENTITY_MANAGER.setAccessible(true);
} catch (NoSuchFieldException ignored) {
}
POST_CHUNK_REWRITE = chunkRewrite;
} catch (RuntimeException | Error e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
MethodHandle craftChunkGetHandle;
final MethodType type = methodType(LevelChunk.class);
try {
craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", type);
} catch (NoSuchMethodException | IllegalAccessException e) {
try {
final MethodType newType = methodType(ChunkAccess.class, ChunkStatus.class);
craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", newType);
craftChunkGetHandle = MethodHandles.insertArguments(craftChunkGetHandle, 1, ChunkStatus.FULL);
} catch (NoSuchMethodException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
CRAFT_CHUNK_GET_HANDLE = craftChunkGetHandle;
}
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 + "!");
}
}
addTicket(serverLevel, chunkX, chunkZ);
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
} 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.UNLOAD_COOLDOWN, 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
// last false is to not bother with x-ray
);
} else {
// deprecated on paper - deprecation suppressed
packet = new ClientboundLevelChunkWithLightPacket(
levelChunk,
nmsWorld.getChunkSource().getLightEngine(),
null,
null
);
}
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
);
}
return new LevelChunkSection(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(biomeRegistry);
}
PalettedContainer<net.minecraft.world.level.block.state.BlockState> dataPaletteBlocks = new PalettedContainer<>(
Block.BLOCK_STATE_REGISTRY,
Blocks.AIR.defaultBlockState(),
PalettedContainer.Strategy.SECTION_STATES
);
return new LevelChunkSection(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
);
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().registryOrThrow(BIOME);
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();
}
}
}

Datei anzeigen

@ -0,0 +1,175 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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)
);
}
}

Datei anzeigen

@ -0,0 +1,205 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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);
}
}

Datei anzeigen

@ -0,0 +1,28 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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_20_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);
}
}

Datei anzeigen

@ -0,0 +1,161 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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();
}
}

Datei anzeigen

@ -0,0 +1,594 @@
package com.sk89q.worldedit.bukkit.adapter.impl.fawe.v1_20_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_20_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.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.ChunkMap;
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.ChunkGeneratorStructureState;
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.WorldOptions;
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.Chunk;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.generator.CustomChunkGenerator;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import javax.annotation.Nullable;
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;
import static net.minecraft.core.registries.Registries.BIOME;
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 generatorStructureStateField;
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", "e"));
generatorSettingBaseSupplierField.setAccessible(true);
generatorSettingFlatField = FlatLevelSource.class.getDeclaredField(Refraction.pickName("settings", "d"));
generatorSettingFlatField.setAccessible(true);
delegateField = CustomChunkGenerator.class.getDeclaredField("delegate");
delegateField.setAccessible(true);
chunkSourceField = ServerLevel.class.getDeclaredField(Refraction.pickName("chunkSource", "I"));
chunkSourceField.setAccessible(true);
generatorStructureStateField = ChunkMap.class.getDeclaredField(Refraction.pickName("chunkGeneratorState", "v"));
generatorStructureStateField.setAccessible(true);
ringPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(Refraction.pickName("ringPositions", "g"));
ringPositionsField.setAccessible(true);
hasGeneratedPositionsField = ChunkGeneratorStructureState.class.getDeclaredField(
Refraction.pickName("hasGeneratedPositions", "h")
);
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();
WorldOptions originalOpts = originalWorldData.worldGenOptions();
WorldOptions newOpts = options.getSeed().isPresent()
? originalOpts.withSeed(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.getDataConfiguration()
);
PrimaryLevelData.SpecialWorldProperty specialWorldProperty =
originalWorldData.isFlatWorld()
? PrimaryLevelData.SpecialWorldProperty.FLAT
: originalWorldData.isDebugWorld()
? PrimaryLevelData.SpecialWorldProperty.DEBUG
: PrimaryLevelData.SpecialWorldProperty.NONE;
PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, specialWorldProperty, Lifecycle.stable());
BiomeProvider biomeProvider = getBiomeProvider();
//init world
freshWorld = Fawe.instance().getQueueHandler().sync((Supplier<ServerLevel>) () -> new ServerLevel(
server,
server.executor,
session,
newWorldData,
originalServerWorld.dimension(),
DedicatedServer.getServer().registryAccess().registry(Registries.LEVEL_STEM).orElseThrow()
.getOrThrow(levelStemResourceKey),
new RegenNoOpWorldLoadListener(),
originalServerWorld.isDebug(),
seed,
ImmutableList.of(),
false,
originalServerWorld.getRandomSequences(),
environment,
generator,
biomeProvider
) {
private final Holder<Biome> singleBiome = options.hasBiomeType() ? DedicatedServer.getServer().registryAccess()
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
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(generatorSettingFlat);
} else if (originalGenerator instanceof NoiseBasedChunkGenerator noiseBasedChunkGenerator) {
Holder<NoiseGeneratorSettings> generatorSettingBaseSupplier = (Holder<NoiseGeneratorSettings>) generatorSettingBaseSupplierField.get(
originalGenerator);
BiomeSource biomeSource;
if (options.hasBiomeType()) {
biomeSource = new FixedBiomeSource(
DedicatedServer.getServer().registryAccess()
.registryOrThrow(BIOME).asHolderIdMap().byIdOrThrow(
WorldEditPlugin.getInstance().getBukkitImplAdapter().getInternalBiomeId(options.getBiomeType())
)
);
} else {
biomeSource = originalGenerator.getBiomeSource();
}
chunkGenerator = new NoiseBasedChunkGenerator(
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; - Does not exist anymore, may need to be re-addressed
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;
}
};
if (seed == originalOpts.seed() && !options.hasBiomeType()) {
// Optimisation for needless ring position calculation when the seed and biome is the same.
ChunkGeneratorStructureState state = (ChunkGeneratorStructureState) generatorStructureStateField.get(originalChunkProvider.chunkMap);
boolean hasGeneratedPositions = hasGeneratedPositionsField.getBoolean(state);
if (hasGeneratedPositions) {
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> origPositions =
(Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>>) ringPositionsField.get(state);
Map<ConcentricRingsStructurePlacement, CompletableFuture<List<ChunkPos>>> copy = new Object2ObjectArrayMap<>(
origPositions);
ChunkGeneratorStructureState newState = (ChunkGeneratorStructureState) generatorStructureStateField.get(freshChunkProvider.chunkMap);
ringPositionsField.set(newState, copy);
hasGeneratedPositionsField.setBoolean(newState, true);
}
}
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(BIOME), 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(() -> {
final CraftWorld world = freshWorld.getWorld();
final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ);
blockPopulator.populate(world, random, chunk);
});
}
@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.toString();
}
@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
);
}
}
/**
* 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> lightChunk(final ChunkAccess chunk, final boolean excludeBlocks) {
return CompletableFuture.completedFuture(chunk);
}
}
}

Datei anzeigen

@ -3,6 +3,7 @@ import io.papermc.paperweight.userdev.attribute.Obfuscation
plugins { plugins {
`java-library` `java-library`
alias(libs.plugins.minotaur)
} }
project.description = "Bukkit" project.description = "Bukkit"
@ -31,6 +32,10 @@ repositories {
name = "OSS Sonatype Snapshots" name = "OSS Sonatype Snapshots"
url = uri("https://oss.sonatype.org/content/repositories/snapshots/") url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
} }
maven {
name = "Glaremasters"
url = uri("https://repo.glaremasters.me/repository/towny/")
}
flatDir { dir(File("src/main/resources")) } flatDir { dir(File("src/main/resources")) }
} }
@ -46,7 +51,13 @@ val adapters = configurations.create("adapters") {
isCanBeResolved = true isCanBeResolved = true
shouldResolveConsistentlyWith(configurations["runtimeClasspath"]) shouldResolveConsistentlyWith(configurations["runtimeClasspath"])
attributes { attributes {
attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, objects.named(Obfuscation.OBFUSCATED)) attribute(Obfuscation.OBFUSCATION_ATTRIBUTE,
if ((project.findProperty("enginehub.obf.none") as String?).toBoolean()) {
objects.named(Obfuscation.NONE)
} else {
objects.named(Obfuscation.OBFUSCATED)
}
)
} }
} }
@ -63,19 +74,19 @@ dependencies {
implementation(libs.fastutil) implementation(libs.fastutil)
// Platform expectations // Platform expectations
compileOnly("io.papermc.paper:paper-api") { compileOnly(libs.paper) {
exclude("junit", "junit") exclude("junit", "junit")
exclude(group = "org.slf4j", module = "slf4j-api") exclude(group = "org.slf4j", module = "slf4j-api")
} }
// Logging // Logging
localImplementation("org.apache.logging.log4j:log4j-api") localImplementation(libs.log4jApi)
localImplementation(libs.log4jBom) { localImplementation(libs.log4jBom) {
because("Spigot provides Log4J (sort of, not in API, implicitly part of server)") because("Spigot provides Log4J (sort of, not in API, implicitly part of server)")
} }
// Plugins // Plugins
compileOnly("com.github.MilkBowl:VaultAPI") { isTransitive = false } compileOnly(libs.vault) { isTransitive = false }
compileOnly(libs.dummypermscompat) { compileOnly(libs.dummypermscompat) {
exclude("com.github.MilkBowl", "VaultAPI") exclude("com.github.MilkBowl", "VaultAPI")
} }
@ -88,29 +99,28 @@ dependencies {
compileOnly(libs.mapmanager) { isTransitive = false } compileOnly(libs.mapmanager) { isTransitive = false }
compileOnly(libs.griefprevention) { isTransitive = false } compileOnly(libs.griefprevention) { isTransitive = false }
compileOnly(libs.griefdefender) { isTransitive = false } compileOnly(libs.griefdefender) { isTransitive = false }
compileOnly(libs.mcore) { isTransitive = false }
compileOnly(libs.residence) { isTransitive = false } compileOnly(libs.residence) { isTransitive = false }
compileOnly(libs.towny) { isTransitive = false } compileOnly(libs.towny) { isTransitive = false }
compileOnly("com.plotsquared:PlotSquared-Bukkit") { isTransitive = false } compileOnly(libs.plotSquaredBukkit) { isTransitive = false }
compileOnly("com.plotsquared:PlotSquared-Core") { isTransitive = false } compileOnly(libs.plotSquaredCore) { isTransitive = false }
// Third party // Third party
implementation("io.papermc:paperlib") implementation(libs.paperlib)
implementation("org.bstats:bstats-bukkit") { isTransitive = false } implementation(libs.bstatsBukkit) { isTransitive = false }
implementation(libs.bstatsBase) { isTransitive = false } implementation(libs.bstatsBase) { isTransitive = false }
implementation("dev.notmyfault.serverlib:ServerLib") implementation(libs.serverlib)
implementation("com.intellectualsites.paster:Paster") { isTransitive = false } implementation(libs.paster) { isTransitive = false }
api(libs.lz4Java) { isTransitive = false } api(libs.lz4Java) { isTransitive = false }
api(libs.sparsebitset) { isTransitive = false } api(libs.sparsebitset) { isTransitive = false }
api(libs.parallelgzip) { isTransitive = false } api(libs.parallelgzip) { isTransitive = false }
compileOnly("net.kyori:adventure-api") compileOnly(libs.adventureApi)
compileOnlyApi("org.checkerframework:checker-qual") compileOnlyApi(libs.checkerqual)
// Tests // Tests
testImplementation(libs.mockito) testImplementation(libs.mockito)
testImplementation("net.kyori:adventure-api") testImplementation(libs.adventureApi)
testImplementation("org.checkerframework:checker-qual") testImplementation(libs.checkerqual)
testImplementation("io.papermc.paper:paper-api") { isTransitive = true } testImplementation(libs.paper) { isTransitive = true }
} }
tasks.named<Copy>("processResources") { tasks.named<Copy>("processResources") {
@ -173,7 +183,7 @@ tasks.named<ShadowJar>("shadowJar") {
include(dependency("org.lz4:lz4-java:1.8.0")) include(dependency("org.lz4:lz4-java:1.8.0"))
} }
relocate("net.kyori", "com.fastasyncworldedit.core.adventure") { relocate("net.kyori", "com.fastasyncworldedit.core.adventure") {
include(dependency("net.kyori:adventure-nbt:4.9.3")) include(dependency("net.kyori:adventure-nbt:4.14.0"))
} }
relocate("com.zaxxer", "com.fastasyncworldedit.core.math") { relocate("com.zaxxer", "com.fastasyncworldedit.core.math") {
include(dependency("com.zaxxer:SparseBitSet:1.2")) include(dependency("com.zaxxer:SparseBitSet:1.2"))
@ -187,3 +197,20 @@ tasks.named<ShadowJar>("shadowJar") {
tasks.named("assemble").configure { tasks.named("assemble").configure {
dependsOn("shadowJar") dependsOn("shadowJar")
} }
tasks {
modrinth {
token.set(System.getenv("MODRINTH_TOKEN"))
projectId.set("fastasyncworldedit")
versionName.set("${project.version}")
versionNumber.set("${project.version}")
versionType.set("release")
uploadFile.set(file("build/libs/${rootProject.name}-Bukkit-${project.version}.jar"))
gameVersions.addAll(listOf("1.20.1", "1.20", "1.19.4", "1.19.3", "1.19.2", "1.19.1", "1.19", "1.18.2", "1.17.1", "1.16" +
".5"))
loaders.addAll(listOf("paper", "spigot"))
changelog.set("The changelog is available on GitHub: https://github.com/IntellectualSites/" +
"FastAsyncWorldEdit/releases/tag/${project.version}")
syncBodyFrom.set(rootProject.file("README.md").readText())
}
}

Datei anzeigen

@ -36,10 +36,6 @@ public class BukkitPermissionAttachmentManager {
if (p == null) { if (p == null) {
return; return;
} }
if (p.hasMetadata("NPC") && noopAttachment != null) {
p.removeAttachment(noopAttachment);
return;
}
PermissionAttachment attach = attachments.remove(p); PermissionAttachment attach = attachments.remove(p);
if (attach != null) { if (attach != null) {
p.removeAttachment(attach); p.removeAttachment(attach);

Datei anzeigen

@ -108,6 +108,14 @@ public class FaweBukkit implements IFawe, Listener {
if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) { if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) {
LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?"); LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?");
} }
if (version.isEqualOrLowerThan(MinecraftVersion.ONE_DOT_SIXTEEN_EOL)) {
LOGGER.warn("You are running Minecraft 1.16.5. This version has been released over two years ago (January 2021).");
LOGGER.warn("FastAsyncWorldEdit will stop operating on this version in the near future.");
LOGGER.warn("Neither Mojang, nor Spigot or other software vendors support this version anymore." +
"Please update your server to a newer version of Minecraft (1.20+) to continue receiving updates and " +
"support.");
}
} }
@Override @Override
@ -225,7 +233,7 @@ public class FaweBukkit implements IFawe, Listener {
final Plugin residencePlugin = Bukkit.getServer().getPluginManager().getPlugin("Residence"); final Plugin residencePlugin = Bukkit.getServer().getPluginManager().getPlugin("Residence");
if (residencePlugin != null && residencePlugin.isEnabled()) { if (residencePlugin != null && residencePlugin.isEnabled()) {
try { try {
managers.add(new ResidenceFeature(residencePlugin, this)); managers.add(new ResidenceFeature(residencePlugin));
LOGGER.info("Attempting to use plugin 'Residence'"); LOGGER.info("Attempting to use plugin 'Residence'");
} catch (Throwable ignored) { } catch (Throwable ignored) {
} }
@ -317,11 +325,11 @@ public class FaweBukkit implements IFawe, Listener {
if (plotSquared == null) { if (plotSquared == null) {
return; return;
} }
if (PlotSquared.get().getVersion().version[0] == 6) { if (PlotSquared.get().getVersion().version[0] == 7) {
WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature()); WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature());
LOGGER.info("Plugin 'PlotSquared' v6 found. Using it now."); LOGGER.info("Plugin 'PlotSquared' v7 found. Using it now.");
} else { } else {
LOGGER.error("Incompatible version of PlotSquared found. Please use PlotSquared v6."); LOGGER.error("Incompatible version of PlotSquared found. Please use PlotSquared v7.");
LOGGER.info("https://www.spigotmc.org/resources/77506/"); LOGGER.info("https://www.spigotmc.org/resources/77506/");
} }
} }

Datei anzeigen

@ -3,8 +3,6 @@ package com.fastasyncworldedit.bukkit.adapter;
import co.aikar.timings.Timings; import co.aikar.timings.Timings;
import com.fastasyncworldedit.bukkit.listener.ChunkListener; import com.fastasyncworldedit.bukkit.listener.ChunkListener;
import com.fastasyncworldedit.core.queue.implementation.QueueHandler; import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -31,7 +29,7 @@ public class BukkitQueueHandler extends QueueHandler {
} }
@Override @Override
public void startSet(boolean parallel) { public void startUnsafe(boolean parallel) {
ChunkListener.physicsFreeze = true; ChunkListener.physicsFreeze = true;
if (parallel) { if (parallel) {
try { try {
@ -51,7 +49,7 @@ public class BukkitQueueHandler extends QueueHandler {
} }
@Override @Override
public void endSet(boolean parallel) { public void endUnsafe(boolean parallel) {
ChunkListener.physicsFreeze = false; ChunkListener.physicsFreeze = false;
if (parallel) { if (parallel) {
try { try {

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen