aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSpottedleaf <[email protected]>2022-09-26 01:02:51 -0700
committerGitHub <[email protected]>2022-09-26 01:02:51 -0700
commit01a13871deefa50e186a10b63f71c5e0459e7d30 (patch)
treecaf7056fa3ad155645b2dec6046b13841eb5d4a2
parentabe53a7eb477664aba5f32ff22d81f11ed48a44d (diff)
downloadPaper-01a13871deefa50e186a10b63f71c5e0459e7d30.tar.gz
Paper-01a13871deefa50e186a10b63f71c5e0459e7d30.zip
Rewrite chunk system (#8177)
Patch documentation to come Issues with the old system that are fixed now: - World generation does not scale with cpu cores effectively. - Relies on the main thread for scheduling and maintaining chunk state, dropping chunk load/generate rates at lower tps. - Unreliable prioritisation of chunk gen/load calls that block the main thread. - Shutdown logic is utterly unreliable, as it has to wait for all chunks to unload - is it guaranteed that the chunk system is in a state on shutdown that it can reliably do this? Watchdog shutdown also typically failed due to thread checks, which is now resolved. - Saving of data is not unified (i.e can save chunk data without saving entity data, poses problems for desync if shutdown is really abnormal. - Entities are not loaded with chunks. This caused quite a bit of headache for Chunk#getEntities API, but now the new chunk system loads entities with chunks so that they are ready whenever the chunk loads in. Effectively brings the behavior back to 1.16 era, but still storing entities in their own separate regionfiles. The above list is not complete. The patch documentation will complete it. New chunk system hard relies on starlight and dataconverter, and most importantly the new concurrent utilities in ConcurrentUtil. Some of the old async chunk i/o interface (i.e the old file io thread reroutes _some_ calls to the new file io thread) is kept for plugin compat reasons. It will be removed in the next major version of minecraft. The old legacy chunk system patches have been moved to the removed folder in case we need them again.
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0014-ChunkMapDistance-CME.patch (renamed from patches/server/0014-ChunkMapDistance-CME.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0015-Do-not-copy-visible-chunks.patch (renamed from patches/server/0015-Do-not-copy-visible-chunks.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0016-Chunk-debug-command.patch (renamed from patches/server/0016-Chunk-debug-command.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0017-Make-CallbackExecutor-strict-again.patch (renamed from patches/server/0017-Make-CallbackExecutor-strict-again.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0019-Asynchronous-chunk-IO-and-loading.patch (renamed from patches/server/0019-Asynchronous-chunk-IO-and-loading.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0020-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch (renamed from patches/server/0020-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0048-Per-Player-View-Distance-API-placeholders.patch (renamed from patches/server/0048-Per-Player-View-Distance-API-placeholders.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0137-Make-targetSize-more-aggressive-in-the-chunk-unload-.patch (renamed from patches/server/0137-Make-targetSize-more-aggressive-in-the-chunk-unload-.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0324-Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch (renamed from patches/server/0324-Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0355-Fix-Light-Command.patch (renamed from patches/server/0355-Fix-Light-Command.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0393-Optimise-ArraySetSorted-removeIf.patch (renamed from patches/server/0393-Optimise-ArraySetSorted-removeIf.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0397-Fix-Chunk-Post-Processing-deadlock-risk.patch (renamed from patches/server/0397-Fix-Chunk-Post-Processing-deadlock-risk.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0429-Optimize-ServerLevels-chunk-level-checking-methods.patch (renamed from patches/server/0429-Optimize-ServerLevels-chunk-level-checking-methods.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0482-Improve-Chunk-Status-Transition-Speed.patch (renamed from patches/server/0482-Improve-Chunk-Status-Transition-Speed.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0720-Do-not-allow-the-server-to-unload-chunks-at-request-.patch (renamed from patches/server/0720-Do-not-allow-the-server-to-unload-chunks-at-request-.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0722-Correctly-handle-recursion-for-chunkholder-updates.patch (renamed from patches/server/0722-Correctly-handle-recursion-for-chunkholder-updates.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0724-Fix-chunks-refusing-to-unload-at-low-TPS.patch (renamed from patches/server/0724-Fix-chunks-refusing-to-unload-at-low-TPS.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0725-Do-not-allow-ticket-level-changes-while-unloading-pl.patch (renamed from patches/server/0725-Do-not-allow-ticket-level-changes-while-unloading-pl.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0726-Do-not-allow-ticket-level-changes-when-updating-chun.patch (renamed from patches/server/0726-Do-not-allow-ticket-level-changes-when-updating-chun.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0729-Prevent-unload-calls-removing-tickets-for-sync-loads.patch (renamed from patches/server/0729-Prevent-unload-calls-removing-tickets-for-sync-loads.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0734-Rewrite-entity-bounding-box-lookup-calls.patch (renamed from patches/server/0734-Rewrite-entity-bounding-box-lookup-calls.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0754-Allow-removal-addition-of-entities-to-entity-ticklis.patch (renamed from patches/server/0754-Allow-removal-addition-of-entities-to-entity-ticklis.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0763-Do-not-process-entity-loads-in-CraftChunk-getEntitie.patch (renamed from patches/server/0763-Do-not-process-entity-loads-in-CraftChunk-getEntitie.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0801-Actually-unload-POI-data.patch (renamed from patches/server/0801-Actually-unload-POI-data.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0845-Replace-ticket-level-propagator.patch (renamed from patches/server/0845-Replace-ticket-level-propagator.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0853-Replace-player-chunk-loader-system.patch (renamed from patches/server/0853-Replace-player-chunk-loader-system.patch)0
-rw-r--r--patches/removed/1.19.2-legacy-chunksystem/0859-Fix-save-problems-on-shutdown.patch (renamed from patches/server/0859-Fix-save-problems-on-shutdown.patch)0
-rw-r--r--patches/server/0004-Paper-config-files.patch17
-rw-r--r--patches/server/0006-ConcurrentUtil.patch160
-rw-r--r--patches/server/0008-MC-Utils.patch221
-rw-r--r--patches/server/0009-Adventure.patch36
-rw-r--r--patches/server/0012-Timings-v2.patch10
-rw-r--r--patches/server/0013-Rewrite-dataconverter-system.patch (renamed from patches/server/0760-Rewrite-dataconverter-system.patch)20
-rw-r--r--patches/server/0014-Starlight.patch (renamed from patches/server/0788-Rewrite-the-light-engine.patch)443
-rw-r--r--patches/server/0015-Not-implemeneted.patch (renamed from patches/server/0013-Not-implemeneted.patch)0
-rw-r--r--patches/server/0016-Rewrite-chunk-system.patch18056
-rw-r--r--patches/server/0017-Add-command-line-option-to-load-extra-plugin-jars-no.patch (renamed from patches/server/0021-Add-command-line-option-to-load-extra-plugin-jars-no.patch)2
-rw-r--r--patches/server/0018-Configurable-cactus-bamboo-and-reed-growth-heights.patch (renamed from patches/server/0022-Configurable-cactus-bamboo-and-reed-growth-heights.patch)0
-rw-r--r--patches/server/0018-Delay-Chunk-Unloads-based-on-Player-Movement.patch89
-rw-r--r--patches/server/0019-Configurable-baby-zombie-movement-speed.patch (renamed from patches/server/0023-Configurable-baby-zombie-movement-speed.patch)0
-rw-r--r--patches/server/0020-Configurable-fishing-time-ranges.patch (renamed from patches/server/0024-Configurable-fishing-time-ranges.patch)0
-rw-r--r--patches/server/0021-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch (renamed from patches/server/0025-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch)0
-rw-r--r--patches/server/0022-Add-configurable-despawn-distances-for-living-entiti.patch (renamed from patches/server/0026-Add-configurable-despawn-distances-for-living-entiti.patch)0
-rw-r--r--patches/server/0023-Allow-for-toggling-of-spawn-chunks.patch (renamed from patches/server/0027-Allow-for-toggling-of-spawn-chunks.patch)2
-rw-r--r--patches/server/0024-Drop-falling-block-and-tnt-entities-at-the-specified.patch (renamed from patches/server/0028-Drop-falling-block-and-tnt-entities-at-the-specified.patch)0
-rw-r--r--patches/server/0025-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch (renamed from patches/server/0029-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch)16
-rw-r--r--patches/server/0026-Implement-Paper-VersionChecker.patch (renamed from patches/server/0030-Implement-Paper-VersionChecker.patch)0
-rw-r--r--patches/server/0027-Add-version-history-to-version-command.patch (renamed from patches/server/0031-Add-version-history-to-version-command.patch)2
-rw-r--r--patches/server/0028-Player-affects-spawning-API.patch (renamed from patches/server/0032-Player-affects-spawning-API.patch)10
-rw-r--r--patches/server/0029-Further-improve-server-tick-loop.patch (renamed from patches/server/0033-Further-improve-server-tick-loop.patch)25
-rw-r--r--patches/server/0030-Only-refresh-abilities-if-needed.patch (renamed from patches/server/0034-Only-refresh-abilities-if-needed.patch)4
-rw-r--r--patches/server/0031-Entity-Origin-API.patch (renamed from patches/server/0035-Entity-Origin-API.patch)10
-rw-r--r--patches/server/0032-Prevent-tile-entity-and-entity-crashes.patch (renamed from patches/server/0036-Prevent-tile-entity-and-entity-crashes.patch)8
-rw-r--r--patches/server/0033-Configurable-top-of-nether-void-damage.patch (renamed from patches/server/0037-Configurable-top-of-nether-void-damage.patch)4
-rw-r--r--patches/server/0034-Check-online-mode-before-converting-and-renaming-pla.patch (renamed from patches/server/0038-Check-online-mode-before-converting-and-renaming-pla.patch)2
-rw-r--r--patches/server/0035-Always-tick-falling-blocks.patch (renamed from patches/server/0039-Always-tick-falling-blocks.patch)2
-rw-r--r--patches/server/0036-Configurable-end-credits.patch (renamed from patches/server/0040-Configurable-end-credits.patch)4
-rw-r--r--patches/server/0037-Fix-lag-from-explosions-processing-dead-entities.patch (renamed from patches/server/0041-Fix-lag-from-explosions-processing-dead-entities.patch)0
-rw-r--r--patches/server/0038-Optimize-explosions.patch (renamed from patches/server/0042-Optimize-explosions.patch)6
-rw-r--r--patches/server/0039-Disable-explosion-knockback.patch (renamed from patches/server/0043-Disable-explosion-knockback.patch)0
-rw-r--r--patches/server/0040-Disable-thunder.patch (renamed from patches/server/0044-Disable-thunder.patch)4
-rw-r--r--patches/server/0041-Disable-ice-and-snow.patch (renamed from patches/server/0045-Disable-ice-and-snow.patch)4
-rw-r--r--patches/server/0042-Configurable-mob-spawner-tick-rate.patch (renamed from patches/server/0046-Configurable-mob-spawner-tick-rate.patch)0
-rw-r--r--patches/server/0043-Implement-PlayerLocaleChangeEvent.patch (renamed from patches/server/0047-Implement-PlayerLocaleChangeEvent.patch)10
-rw-r--r--patches/server/0044-Add-BeaconEffectEvent.patch (renamed from patches/server/0049-Add-BeaconEffectEvent.patch)0
-rw-r--r--patches/server/0045-Configurable-container-update-tick-rate.patch (renamed from patches/server/0050-Configurable-container-update-tick-rate.patch)6
-rw-r--r--patches/server/0046-Use-UserCache-for-player-heads.patch (renamed from patches/server/0051-Use-UserCache-for-player-heads.patch)0
-rw-r--r--patches/server/0047-Disable-spigot-tick-limiters.patch (renamed from patches/server/0052-Disable-spigot-tick-limiters.patch)4
-rw-r--r--patches/server/0048-Add-PlayerInitialSpawnEvent.patch (renamed from patches/server/0053-Add-PlayerInitialSpawnEvent.patch)2
-rw-r--r--patches/server/0049-Configurable-Disabling-Cat-Chest-Detection.patch (renamed from patches/server/0054-Configurable-Disabling-Cat-Chest-Detection.patch)0
-rw-r--r--patches/server/0050-Ensure-commands-are-not-ran-async.patch (renamed from patches/server/0055-Ensure-commands-are-not-ran-async.patch)8
-rw-r--r--patches/server/0051-All-chunks-are-slime-spawn-chunks-toggle.patch (renamed from patches/server/0056-All-chunks-are-slime-spawn-chunks-toggle.patch)4
-rw-r--r--patches/server/0052-Expose-server-CommandMap.patch (renamed from patches/server/0057-Expose-server-CommandMap.patch)2
-rw-r--r--patches/server/0053-Be-a-bit-more-informative-in-maxHealth-exception.patch (renamed from patches/server/0058-Be-a-bit-more-informative-in-maxHealth-exception.patch)0
-rw-r--r--patches/server/0054-Ensure-inv-drag-is-in-bounds.patch (renamed from patches/server/0059-Ensure-inv-drag-is-in-bounds.patch)0
-rw-r--r--patches/server/0055-Player-Tab-List-and-Title-APIs.patch (renamed from patches/server/0060-Player-Tab-List-and-Title-APIs.patch)4
-rw-r--r--patches/server/0056-Add-configurable-portal-search-radius.patch (renamed from patches/server/0061-Add-configurable-portal-search-radius.patch)4
-rw-r--r--patches/server/0057-Add-velocity-warnings.patch (renamed from patches/server/0062-Add-velocity-warnings.patch)8
-rw-r--r--patches/server/0058-Configurable-inter-world-teleportation-safety.patch (renamed from patches/server/0063-Configurable-inter-world-teleportation-safety.patch)4
-rw-r--r--patches/server/0059-Add-exception-reporting-event.patch (renamed from patches/server/0064-Add-exception-reporting-event.patch)22
-rw-r--r--patches/server/0060-Don-t-nest-if-we-don-t-need-to-when-cerealising-text.patch (renamed from patches/server/0065-Don-t-nest-if-we-don-t-need-to-when-cerealising-text.patch)0
-rw-r--r--patches/server/0061-Disable-Scoreboards-for-non-players-by-default.patch (renamed from patches/server/0066-Disable-Scoreboards-for-non-players-by-default.patch)4
-rw-r--r--patches/server/0062-Add-methods-for-working-with-arrows-stuck-in-living-.patch (renamed from patches/server/0067-Add-methods-for-working-with-arrows-stuck-in-living-.patch)0
-rw-r--r--patches/server/0063-Chunk-Save-Reattempt.patch (renamed from patches/server/0068-Chunk-Save-Reattempt.patch)10
-rw-r--r--patches/server/0064-Complete-resource-pack-API.patch (renamed from patches/server/0069-Complete-resource-pack-API.patch)4
-rw-r--r--patches/server/0065-Default-loading-permissions.yml-before-plugins.patch (renamed from patches/server/0070-Default-loading-permissions.yml-before-plugins.patch)2
-rw-r--r--patches/server/0066-Allow-Reloading-of-Custom-Permissions.patch (renamed from patches/server/0071-Allow-Reloading-of-Custom-Permissions.patch)2
-rw-r--r--patches/server/0067-Remove-Metadata-on-reload.patch (renamed from patches/server/0072-Remove-Metadata-on-reload.patch)2
-rw-r--r--patches/server/0068-Handle-Item-Meta-Inconsistencies.patch (renamed from patches/server/0073-Handle-Item-Meta-Inconsistencies.patch)0
-rw-r--r--patches/server/0069-Configurable-Non-Player-Arrow-Despawn-Rate.patch (renamed from patches/server/0074-Configurable-Non-Player-Arrow-Despawn-Rate.patch)0
-rw-r--r--patches/server/0070-Add-World-Util-Methods.patch (renamed from patches/server/0075-Add-World-Util-Methods.patch)4
-rw-r--r--patches/server/0071-Custom-replacement-for-eaten-items.patch (renamed from patches/server/0076-Custom-replacement-for-eaten-items.patch)0
-rw-r--r--patches/server/0072-handle-NaN-health-absorb-values-and-repair-bad-data.patch (renamed from patches/server/0077-handle-NaN-health-absorb-values-and-repair-bad-data.patch)4
-rw-r--r--patches/server/0073-Use-a-Shared-Random-for-Entities.patch (renamed from patches/server/0078-Use-a-Shared-Random-for-Entities.patch)4
-rw-r--r--patches/server/0074-Configurable-spawn-chances-for-skeleton-horses.patch (renamed from patches/server/0079-Configurable-spawn-chances-for-skeleton-horses.patch)4
-rw-r--r--patches/server/0075-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch (renamed from patches/server/0080-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch)26
-rw-r--r--patches/server/0076-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch (renamed from patches/server/0081-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch)10
-rw-r--r--patches/server/0077-Entity-AddTo-RemoveFrom-World-Events.patch (renamed from patches/server/0082-Entity-AddTo-RemoveFrom-World-Events.patch)6
-rw-r--r--patches/server/0078-Configurable-Chunk-Inhabited-Time.patch (renamed from patches/server/0083-Configurable-Chunk-Inhabited-Time.patch)4
-rw-r--r--patches/server/0079-EntityPathfindEvent.patch (renamed from patches/server/0084-EntityPathfindEvent.patch)0
-rw-r--r--patches/server/0080-Sanitise-RegionFileCache-and-make-configurable.patch (renamed from patches/server/0085-Sanitise-RegionFileCache-and-make-configurable.patch)2
-rw-r--r--patches/server/0081-Do-not-load-chunks-for-Pathfinding.patch (renamed from patches/server/0086-Do-not-load-chunks-for-Pathfinding.patch)0
-rw-r--r--patches/server/0082-Add-PlayerUseUnknownEntityEvent.patch (renamed from patches/server/0087-Add-PlayerUseUnknownEntityEvent.patch)0
-rw-r--r--patches/server/0083-Configurable-Grass-Spread-Tick-Rate.patch (renamed from patches/server/0088-Configurable-Grass-Spread-Tick-Rate.patch)0
-rw-r--r--patches/server/0084-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch (renamed from patches/server/0089-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch)4
-rw-r--r--patches/server/0085-Optimize-DataBits.patch (renamed from patches/server/0090-Optimize-DataBits.patch)0
-rw-r--r--patches/server/0086-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch (renamed from patches/server/0091-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch)0
-rw-r--r--patches/server/0087-Configurable-Player-Collision.patch (renamed from patches/server/0092-Configurable-Player-Collision.patch)4
-rw-r--r--patches/server/0088-Add-handshake-event-to-allow-plugins-to-handle-clien.patch (renamed from patches/server/0093-Add-handshake-event-to-allow-plugins-to-handle-clien.patch)0
-rw-r--r--patches/server/0089-Configurable-RCON-IP-address.patch (renamed from patches/server/0094-Configurable-RCON-IP-address.patch)0
-rw-r--r--patches/server/0090-EntityRegainHealthEvent-isFastRegen-API.patch (renamed from patches/server/0095-EntityRegainHealthEvent-isFastRegen-API.patch)0
-rw-r--r--patches/server/0091-Add-ability-to-configure-frosted_ice-properties.patch (renamed from patches/server/0096-Add-ability-to-configure-frosted_ice-properties.patch)0
-rw-r--r--patches/server/0092-remove-null-possibility-for-getServer-singleton.patch (renamed from patches/server/0097-remove-null-possibility-for-getServer-singleton.patch)4
-rw-r--r--patches/server/0093-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch (renamed from patches/server/0098-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch)4
-rw-r--r--patches/server/0094-LootTable-API-Replenishable-Lootables-Feature.patch (renamed from patches/server/0099-LootTable-API-Replenishable-Lootables-Feature.patch)2
-rw-r--r--patches/server/0095-Don-t-save-empty-scoreboard-teams-to-scoreboard.dat.patch (renamed from patches/server/0100-Don-t-save-empty-scoreboard-teams-to-scoreboard.dat.patch)0
-rw-r--r--patches/server/0096-System-property-for-disabling-watchdoge.patch (renamed from patches/server/0101-System-property-for-disabling-watchdoge.patch)4
-rw-r--r--patches/server/0097-Async-GameProfileCache-saving.patch (renamed from patches/server/0102-Async-GameProfileCache-saving.patch)8
-rw-r--r--patches/server/0098-Optional-TNT-doesn-t-move-in-water.patch (renamed from patches/server/0103-Optional-TNT-doesn-t-move-in-water.patch)0
-rw-r--r--patches/server/0099-Faster-redstone-torch-rapid-clock-removal.patch (renamed from patches/server/0104-Faster-redstone-torch-rapid-clock-removal.patch)2
-rw-r--r--patches/server/0100-Add-server-name-parameter.patch (renamed from patches/server/0105-Add-server-name-parameter.patch)0
-rw-r--r--patches/server/0101-Only-send-Dragon-Wither-Death-sounds-to-same-world.patch (renamed from patches/server/0106-Only-send-Dragon-Wither-Death-sounds-to-same-world.patch)18
-rw-r--r--patches/server/0102-Fix-Old-Sign-Conversion.patch (renamed from patches/server/0107-Fix-Old-Sign-Conversion.patch)0
-rw-r--r--patches/server/0103-Avoid-blocking-on-Network-Manager-creation.patch (renamed from patches/server/0108-Avoid-blocking-on-Network-Manager-creation.patch)0
-rw-r--r--patches/server/0104-Don-t-lookup-game-profiles-that-have-no-UUID-and-no-.patch (renamed from patches/server/0109-Don-t-lookup-game-profiles-that-have-no-UUID-and-no-.patch)0
-rw-r--r--patches/server/0105-Add-setting-for-proxy-online-mode-status.patch (renamed from patches/server/0110-Add-setting-for-proxy-online-mode-status.patch)2
-rw-r--r--patches/server/0106-Optimise-BlockState-s-hashCode-equals.patch (renamed from patches/server/0111-Optimise-BlockState-s-hashCode-equals.patch)0
-rw-r--r--patches/server/0107-Configurable-packet-in-spam-threshold.patch (renamed from patches/server/0112-Configurable-packet-in-spam-threshold.patch)0
-rw-r--r--patches/server/0108-Configurable-flying-kick-messages.patch (renamed from patches/server/0113-Configurable-flying-kick-messages.patch)0
-rw-r--r--patches/server/0109-Add-EntityZapEvent.patch (renamed from patches/server/0114-Add-EntityZapEvent.patch)0
-rw-r--r--patches/server/0110-Filter-bad-data-from-ArmorStand-and-SpawnEgg-items.patch (renamed from patches/server/0115-Filter-bad-data-from-ArmorStand-and-SpawnEgg-items.patch)0
-rw-r--r--patches/server/0111-Cache-user-authenticator-threads.patch (renamed from patches/server/0116-Cache-user-authenticator-threads.patch)0
-rw-r--r--patches/server/0112-Allow-Reloading-of-Command-Aliases.patch (renamed from patches/server/0117-Allow-Reloading-of-Command-Aliases.patch)2
-rw-r--r--patches/server/0113-Add-source-to-PlayerExpChangeEvent.patch (renamed from patches/server/0118-Add-source-to-PlayerExpChangeEvent.patch)0
-rw-r--r--patches/server/0114-Add-ProjectileCollideEvent.patch (renamed from patches/server/0119-Add-ProjectileCollideEvent.patch)0
-rw-r--r--patches/server/0115-Prevent-Pathfinding-out-of-World-Border.patch (renamed from patches/server/0120-Prevent-Pathfinding-out-of-World-Border.patch)0
-rw-r--r--patches/server/0116-Optimize-World.isLoaded-BlockPosition-Z.patch (renamed from patches/server/0121-Optimize-World.isLoaded-BlockPosition-Z.patch)2
-rw-r--r--patches/server/0117-Bound-Treasure-Maps-to-World-Border.patch (renamed from patches/server/0122-Bound-Treasure-Maps-to-World-Border.patch)2
-rw-r--r--patches/server/0118-Configurable-Cartographer-Treasure-Maps.patch (renamed from patches/server/0123-Configurable-Cartographer-Treasure-Maps.patch)0
-rw-r--r--patches/server/0119-Optimize-ItemStack.isEmpty.patch (renamed from patches/server/0124-Optimize-ItemStack.isEmpty.patch)0
-rw-r--r--patches/server/0120-Add-API-methods-to-control-if-armour-stands-can-move.patch (renamed from patches/server/0125-Add-API-methods-to-control-if-armour-stands-can-move.patch)0
-rw-r--r--patches/server/0121-String-based-Action-Bar-API.patch (renamed from patches/server/0126-String-based-Action-Bar-API.patch)4
-rw-r--r--patches/server/0122-Properly-fix-item-duplication-bug.patch (renamed from patches/server/0127-Properly-fix-item-duplication-bug.patch)4
-rw-r--r--patches/server/0123-Firework-API-s.patch (renamed from patches/server/0128-Firework-API-s.patch)0
-rw-r--r--patches/server/0124-PlayerTeleportEndGatewayEvent.patch (renamed from patches/server/0129-PlayerTeleportEndGatewayEvent.patch)0
-rw-r--r--patches/server/0125-Provide-E-TE-Chunk-count-stat-methods.patch (renamed from patches/server/0130-Provide-E-TE-Chunk-count-stat-methods.patch)4
-rw-r--r--patches/server/0126-Enforce-Sync-Player-Saves.patch (renamed from patches/server/0131-Enforce-Sync-Player-Saves.patch)2
-rw-r--r--patches/server/0127-Don-t-allow-entities-to-ride-themselves-572.patch (renamed from patches/server/0132-Don-t-allow-entities-to-ride-themselves-572.patch)4
-rw-r--r--patches/server/0128-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch (renamed from patches/server/0133-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch)4
-rw-r--r--patches/server/0129-Cap-Entity-Collisions.patch (renamed from patches/server/0134-Cap-Entity-Collisions.patch)2
-rw-r--r--patches/server/0130-Remove-CraftScheduler-Async-Task-Debugger.patch (renamed from patches/server/0135-Remove-CraftScheduler-Async-Task-Debugger.patch)0
-rw-r--r--patches/server/0131-Do-not-let-armorstands-drown.patch (renamed from patches/server/0136-Do-not-let-armorstands-drown.patch)0
-rw-r--r--patches/server/0132-Properly-handle-async-calls-to-restart-the-server.patch (renamed from patches/server/0138-Properly-handle-async-calls-to-restart-the-server.patch)8
-rw-r--r--patches/server/0133-Add-option-to-make-parrots-stay-on-shoulders-despite.patch (renamed from patches/server/0139-Add-option-to-make-parrots-stay-on-shoulders-despite.patch)0
-rw-r--r--patches/server/0134-Add-configuration-option-to-prevent-player-names-fro.patch (renamed from patches/server/0140-Add-configuration-option-to-prevent-player-names-fro.patch)2
-rw-r--r--patches/server/0135-Use-TerminalConsoleAppender-for-console-improvements.patch (renamed from patches/server/0141-Use-TerminalConsoleAppender-for-console-improvements.patch)12
-rw-r--r--patches/server/0136-provide-a-configurable-option-to-disable-creeper-lin.patch (renamed from patches/server/0142-provide-a-configurable-option-to-disable-creeper-lin.patch)0
-rw-r--r--patches/server/0137-Item-canEntityPickup.patch (renamed from patches/server/0143-Item-canEntityPickup.patch)0
-rw-r--r--patches/server/0138-PlayerPickupItemEvent-setFlyAtPlayer.patch (renamed from patches/server/0144-PlayerPickupItemEvent-setFlyAtPlayer.patch)0
-rw-r--r--patches/server/0139-PlayerAttemptPickupItemEvent.patch (renamed from patches/server/0145-PlayerAttemptPickupItemEvent.patch)0
-rw-r--r--patches/server/0140-Do-not-submit-profile-lookups-to-worldgen-threads.patch (renamed from patches/server/0146-Do-not-submit-profile-lookups-to-worldgen-threads.patch)0
-rw-r--r--patches/server/0141-Add-UnknownCommandEvent.patch (renamed from patches/server/0147-Add-UnknownCommandEvent.patch)2
-rw-r--r--patches/server/0142-Basic-PlayerProfile-API.patch (renamed from patches/server/0148-Basic-PlayerProfile-API.patch)8
-rw-r--r--patches/server/0143-Shoulder-Entities-Release-API.patch (renamed from patches/server/0149-Shoulder-Entities-Release-API.patch)0
-rw-r--r--patches/server/0144-Profile-Lookup-Events.patch (renamed from patches/server/0150-Profile-Lookup-Events.patch)0
-rw-r--r--patches/server/0145-Block-player-logins-during-server-shutdown.patch (renamed from patches/server/0151-Block-player-logins-during-server-shutdown.patch)0
-rw-r--r--patches/server/0146-Entity-fromMobSpawner.patch (renamed from patches/server/0152-Entity-fromMobSpawner.patch)6
-rw-r--r--patches/server/0147-Improve-the-Saddle-API-for-Horses.patch (renamed from patches/server/0153-Improve-the-Saddle-API-for-Horses.patch)0
-rw-r--r--patches/server/0148-Implement-ensureServerConversions-API.patch (renamed from patches/server/0154-Implement-ensureServerConversions-API.patch)0
-rw-r--r--patches/server/0149-Implement-getI18NDisplayName.patch (renamed from patches/server/0155-Implement-getI18NDisplayName.patch)0
-rw-r--r--patches/server/0150-ProfileWhitelistVerifyEvent.patch (renamed from patches/server/0156-ProfileWhitelistVerifyEvent.patch)2
-rw-r--r--patches/server/0151-Fix-this-stupid-bullshit.patch (renamed from patches/server/0157-Fix-this-stupid-bullshit.patch)0
-rw-r--r--patches/server/0152-LivingEntity-setKiller.patch (renamed from patches/server/0158-LivingEntity-setKiller.patch)0
-rw-r--r--patches/server/0153-Ocelot-despawns-should-honor-nametags-and-leash.patch (renamed from patches/server/0159-Ocelot-despawns-should-honor-nametags-and-leash.patch)0
-rw-r--r--patches/server/0154-Reset-spawner-timer-when-spawner-event-is-cancelled.patch (renamed from patches/server/0160-Reset-spawner-timer-when-spawner-event-is-cancelled.patch)0
-rw-r--r--patches/server/0155-Allow-specifying-a-custom-authentication-servers-dow.patch (renamed from patches/server/0161-Allow-specifying-a-custom-authentication-servers-dow.patch)0
-rw-r--r--patches/server/0156-Handle-plugin-prefixes-using-Log4J-configuration.patch (renamed from patches/server/0162-Handle-plugin-prefixes-using-Log4J-configuration.patch)0
-rw-r--r--patches/server/0157-Improve-Log4J-Configuration-Plugin-Loggers.patch (renamed from patches/server/0163-Improve-Log4J-Configuration-Plugin-Loggers.patch)0
-rw-r--r--patches/server/0158-Add-PlayerJumpEvent.patch (renamed from patches/server/0164-Add-PlayerJumpEvent.patch)0
-rw-r--r--patches/server/0159-handle-ServerboundKeepAlivePacket-async.patch (renamed from patches/server/0165-handle-ServerboundKeepAlivePacket-async.patch)0
-rw-r--r--patches/server/0160-Expose-client-protocol-version-and-virtual-host.patch (renamed from patches/server/0166-Expose-client-protocol-version-and-virtual-host.patch)14
-rw-r--r--patches/server/0161-revert-serverside-behavior-of-keepalives.patch (renamed from patches/server/0167-revert-serverside-behavior-of-keepalives.patch)0
-rw-r--r--patches/server/0162-Send-attack-SoundEffects-only-to-players-who-can-see.patch (renamed from patches/server/0168-Send-attack-SoundEffects-only-to-players-who-can-see.patch)0
-rw-r--r--patches/server/0163-Add-PlayerArmorChangeEvent.patch (renamed from patches/server/0169-Add-PlayerArmorChangeEvent.patch)0
-rw-r--r--patches/server/0164-Prevent-logins-from-being-processed-when-the-player-.patch (renamed from patches/server/0170-Prevent-logins-from-being-processed-when-the-player-.patch)0
-rw-r--r--patches/server/0165-Fix-MC-117075-TE-Unload-Lag-Spike.patch (renamed from patches/server/0171-Fix-MC-117075-TE-Unload-Lag-Spike.patch)8
-rw-r--r--patches/server/0166-use-CB-BlockState-implementations-for-captured-block.patch (renamed from patches/server/0172-use-CB-BlockState-implementations-for-captured-block.patch)4
-rw-r--r--patches/server/0167-API-to-get-a-BlockState-without-a-snapshot.patch (renamed from patches/server/0173-API-to-get-a-BlockState-without-a-snapshot.patch)0
-rw-r--r--patches/server/0168-AsyncTabCompleteEvent.patch (renamed from patches/server/0174-AsyncTabCompleteEvent.patch)2
-rw-r--r--patches/server/0169-PlayerPickupExperienceEvent.patch (renamed from patches/server/0175-PlayerPickupExperienceEvent.patch)0
-rw-r--r--patches/server/0170-Ability-to-apply-mending-to-XP-API.patch (renamed from patches/server/0176-Ability-to-apply-mending-to-XP-API.patch)4
-rw-r--r--patches/server/0171-PlayerNaturallySpawnCreaturesEvent.patch (renamed from patches/server/0177-PlayerNaturallySpawnCreaturesEvent.patch)16
-rw-r--r--patches/server/0172-Add-setPlayerProfile-API-for-Skulls.patch (renamed from patches/server/0178-Add-setPlayerProfile-API-for-Skulls.patch)0
-rw-r--r--patches/server/0173-PreCreatureSpawnEvent.patch (renamed from patches/server/0179-PreCreatureSpawnEvent.patch)0
-rw-r--r--patches/server/0174-Fill-Profile-Property-Events.patch (renamed from patches/server/0180-Fill-Profile-Property-Events.patch)0
-rw-r--r--patches/server/0175-PlayerAdvancementCriterionGrantEvent.patch (renamed from patches/server/0181-PlayerAdvancementCriterionGrantEvent.patch)0
-rw-r--r--patches/server/0176-Add-ArmorStand-Item-Meta.patch (renamed from patches/server/0182-Add-ArmorStand-Item-Meta.patch)0
-rw-r--r--patches/server/0177-Extend-Player-Interact-cancellation.patch (renamed from patches/server/0183-Extend-Player-Interact-cancellation.patch)0
-rw-r--r--patches/server/0178-Tameable-getOwnerUniqueId-API.patch (renamed from patches/server/0184-Tameable-getOwnerUniqueId-API.patch)0
-rw-r--r--patches/server/0179-Toggleable-player-crits-helps-mitigate-hacked-client.patch (renamed from patches/server/0185-Toggleable-player-crits-helps-mitigate-hacked-client.patch)0
-rw-r--r--patches/server/0180-Disable-Explicit-Network-Manager-Flushing.patch (renamed from patches/server/0186-Disable-Explicit-Network-Manager-Flushing.patch)6
-rw-r--r--patches/server/0181-Implement-extended-PaperServerListPingEvent.patch (renamed from patches/server/0187-Implement-extended-PaperServerListPingEvent.patch)4
-rw-r--r--patches/server/0182-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch (renamed from patches/server/0188-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch)0
-rw-r--r--patches/server/0183-Player.setPlayerProfile-API.patch (renamed from patches/server/0189-Player.setPlayerProfile-API.patch)12
-rw-r--r--patches/server/0184-getPlayerUniqueId-API.patch (renamed from patches/server/0190-getPlayerUniqueId-API.patch)2
-rw-r--r--patches/server/0185-Improved-Async-Task-Scheduler.patch (renamed from patches/server/0191-Improved-Async-Task-Scheduler.patch)0
-rw-r--r--patches/server/0186-Make-legacy-ping-handler-more-reliable.patch (renamed from patches/server/0192-Make-legacy-ping-handler-more-reliable.patch)0
-rw-r--r--patches/server/0187-Call-PaperServerListPingEvent-for-legacy-pings.patch (renamed from patches/server/0193-Call-PaperServerListPingEvent-for-legacy-pings.patch)0
-rw-r--r--patches/server/0188-Flag-to-disable-the-channel-limit.patch (renamed from patches/server/0194-Flag-to-disable-the-channel-limit.patch)4
-rw-r--r--patches/server/0189-Add-openSign-method-to-HumanEntity.patch (renamed from patches/server/0195-Add-openSign-method-to-HumanEntity.patch)0
-rw-r--r--patches/server/0190-Configurable-sprint-interruption-on-attack.patch (renamed from patches/server/0196-Configurable-sprint-interruption-on-attack.patch)0
-rw-r--r--patches/server/0191-Fix-exploit-that-allowed-colored-signs-to-be-created.patch (renamed from patches/server/0197-Fix-exploit-that-allowed-colored-signs-to-be-created.patch)0
-rw-r--r--patches/server/0192-EndermanEscapeEvent.patch (renamed from patches/server/0198-EndermanEscapeEvent.patch)0
-rw-r--r--patches/server/0193-Enderman.teleportRandomly.patch (renamed from patches/server/0199-Enderman.teleportRandomly.patch)0
-rw-r--r--patches/server/0194-Block-Enderpearl-Travel-Exploit.patch (renamed from patches/server/0200-Block-Enderpearl-Travel-Exploit.patch)4
-rw-r--r--patches/server/0195-Expand-World.spawnParticle-API-and-add-Builder.patch (renamed from patches/server/0201-Expand-World.spawnParticle-API-and-add-Builder.patch)8
-rw-r--r--patches/server/0196-Prevent-Frosted-Ice-from-loading-holding-chunks.patch (renamed from patches/server/0202-Prevent-Frosted-Ice-from-loading-holding-chunks.patch)0
-rw-r--r--patches/server/0197-EndermanAttackPlayerEvent.patch (renamed from patches/server/0203-EndermanAttackPlayerEvent.patch)0
-rw-r--r--patches/server/0198-WitchConsumePotionEvent.patch (renamed from patches/server/0204-WitchConsumePotionEvent.patch)0
-rw-r--r--patches/server/0199-WitchThrowPotionEvent.patch (renamed from patches/server/0205-WitchThrowPotionEvent.patch)0
-rw-r--r--patches/server/0200-Allow-spawning-Item-entities-with-World.spawnEntity.patch (renamed from patches/server/0206-Allow-spawning-Item-entities-with-World.spawnEntity.patch)0
-rw-r--r--patches/server/0201-WitchReadyPotionEvent.patch (renamed from patches/server/0207-WitchReadyPotionEvent.patch)0
-rw-r--r--patches/server/0202-ItemStack-getMaxItemUseDuration.patch (renamed from patches/server/0208-ItemStack-getMaxItemUseDuration.patch)2
-rw-r--r--patches/server/0203-Implement-EntityTeleportEndGatewayEvent.patch (renamed from patches/server/0209-Implement-EntityTeleportEndGatewayEvent.patch)0
-rw-r--r--patches/server/0204-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch (renamed from patches/server/0210-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch)0
-rw-r--r--patches/server/0205-Fix-CraftEntity-hashCode.patch (renamed from patches/server/0211-Fix-CraftEntity-hashCode.patch)0
-rw-r--r--patches/server/0206-Configurable-Alternative-LootPool-Luck-Formula.patch (renamed from patches/server/0212-Configurable-Alternative-LootPool-Luck-Formula.patch)0
-rw-r--r--patches/server/0207-Print-Error-details-when-failing-to-save-player-data.patch (renamed from patches/server/0213-Print-Error-details-when-failing-to-save-player-data.patch)2
-rw-r--r--patches/server/0208-Make-shield-blocking-delay-configurable.patch (renamed from patches/server/0214-Make-shield-blocking-delay-configurable.patch)0
-rw-r--r--patches/server/0209-Improve-EntityShootBowEvent.patch (renamed from patches/server/0215-Improve-EntityShootBowEvent.patch)0
-rw-r--r--patches/server/0210-PlayerReadyArrowEvent.patch (renamed from patches/server/0216-PlayerReadyArrowEvent.patch)0
-rw-r--r--patches/server/0211-Implement-EntityKnockbackByEntityEvent.patch (renamed from patches/server/0217-Implement-EntityKnockbackByEntityEvent.patch)0
-rw-r--r--patches/server/0212-Expand-Explosions-API.patch (renamed from patches/server/0218-Expand-Explosions-API.patch)4
-rw-r--r--patches/server/0213-LivingEntity-Hand-Raised-Item-Use-API.patch (renamed from patches/server/0219-LivingEntity-Hand-Raised-Item-Use-API.patch)0
-rw-r--r--patches/server/0214-RangedEntity-API.patch (renamed from patches/server/0220-RangedEntity-API.patch)0
-rw-r--r--patches/server/0215-Add-config-to-disable-ender-dragon-legacy-check.patch (renamed from patches/server/0221-Add-config-to-disable-ender-dragon-legacy-check.patch)0
-rw-r--r--patches/server/0216-Implement-World.getEntity-UUID-API.patch (renamed from patches/server/0222-Implement-World.getEntity-UUID-API.patch)4
-rw-r--r--patches/server/0217-InventoryCloseEvent-Reason-API.patch (renamed from patches/server/0223-InventoryCloseEvent-Reason-API.patch)22
-rw-r--r--patches/server/0218-Vex-get-setSummoner-API.patch (renamed from patches/server/0224-Vex-get-setSummoner-API.patch)0
-rw-r--r--patches/server/0219-Refresh-player-inventory-when-cancelling-PlayerInter.patch (renamed from patches/server/0225-Refresh-player-inventory-when-cancelling-PlayerInter.patch)0
-rw-r--r--patches/server/0220-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch (renamed from patches/server/0226-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch)0
-rw-r--r--patches/server/0221-add-more-information-to-Entity.toString.patch (renamed from patches/server/0227-add-more-information-to-Entity.toString.patch)4
-rw-r--r--patches/server/0222-Add-CraftMagicNumbers.isSupportedApiVersion.patch (renamed from patches/server/0228-Add-CraftMagicNumbers.isSupportedApiVersion.patch)0
-rw-r--r--patches/server/0223-EnderDragon-Events.patch (renamed from patches/server/0229-EnderDragon-Events.patch)0
-rw-r--r--patches/server/0224-PlayerElytraBoostEvent.patch (renamed from patches/server/0230-PlayerElytraBoostEvent.patch)0
-rw-r--r--patches/server/0225-PlayerLaunchProjectileEvent.patch (renamed from patches/server/0231-PlayerLaunchProjectileEvent.patch)0
-rw-r--r--patches/server/0226-Improve-BlockPosition-inlining.patch (renamed from patches/server/0232-Improve-BlockPosition-inlining.patch)0
-rw-r--r--patches/server/0227-Option-to-prevent-armor-stands-from-doing-entity-loo.patch (renamed from patches/server/0233-Option-to-prevent-armor-stands-from-doing-entity-loo.patch)4
-rw-r--r--patches/server/0228-Vanished-players-don-t-have-rights.patch (renamed from patches/server/0234-Vanished-players-don-t-have-rights.patch)2
-rw-r--r--patches/server/0229-Allow-disabling-armour-stand-ticking.patch (renamed from patches/server/0235-Allow-disabling-armour-stand-ticking.patch)0
-rw-r--r--patches/server/0230-SkeletonHorse-Additions.patch (renamed from patches/server/0236-SkeletonHorse-Additions.patch)4
-rw-r--r--patches/server/0231-Don-t-call-getItemMeta-on-hasItemMeta.patch (renamed from patches/server/0237-Don-t-call-getItemMeta-on-hasItemMeta.patch)2
-rw-r--r--patches/server/0232-Implement-Expanded-ArmorStand-API.patch (renamed from patches/server/0238-Implement-Expanded-ArmorStand-API.patch)0
-rw-r--r--patches/server/0233-AnvilDamageEvent.patch (renamed from patches/server/0239-AnvilDamageEvent.patch)0
-rw-r--r--patches/server/0234-Add-hand-to-bucket-events.patch (renamed from patches/server/0240-Add-hand-to-bucket-events.patch)0
-rw-r--r--patches/server/0235-Add-TNTPrimeEvent.patch (renamed from patches/server/0241-Add-TNTPrimeEvent.patch)2
-rw-r--r--patches/server/0236-Break-up-and-make-tab-spam-limits-configurable.patch (renamed from patches/server/0242-Break-up-and-make-tab-spam-limits-configurable.patch)0
-rw-r--r--patches/server/0237-MC-135506-Experience-should-save-as-Integers.patch (renamed from patches/server/0243-MC-135506-Experience-should-save-as-Integers.patch)0
-rw-r--r--patches/server/0238-Remove-unnecessary-itemmeta-handling.patch (renamed from patches/server/0244-Remove-unnecessary-itemmeta-handling.patch)0
-rw-r--r--patches/server/0239-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch (renamed from patches/server/0245-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch)14
-rw-r--r--patches/server/0240-Add-Early-Warning-Feature-to-WatchDog.patch (renamed from patches/server/0246-Add-Early-Warning-Feature-to-WatchDog.patch)20
-rw-r--r--patches/server/0241-Use-ConcurrentHashMap-in-JsonList.patch (renamed from patches/server/0247-Use-ConcurrentHashMap-in-JsonList.patch)2
-rw-r--r--patches/server/0242-Use-a-Queue-for-Queueing-Commands.patch (renamed from patches/server/0248-Use-a-Queue-for-Queueing-Commands.patch)12
-rw-r--r--patches/server/0243-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch (renamed from patches/server/0249-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch)6
-rw-r--r--patches/server/0244-Optimize-BlockPosition-helper-methods.patch (renamed from patches/server/0250-Optimize-BlockPosition-helper-methods.patch)0
-rw-r--r--patches/server/0245-Restore-vanilla-default-mob-spawn-range-and-water-an.patch (renamed from patches/server/0251-Restore-vanilla-default-mob-spawn-range-and-water-an.patch)0
-rw-r--r--patches/server/0246-Slime-Pathfinder-Events.patch (renamed from patches/server/0252-Slime-Pathfinder-Events.patch)0
-rw-r--r--patches/server/0247-Configurable-speed-for-water-flowing-over-lava.patch (renamed from patches/server/0253-Configurable-speed-for-water-flowing-over-lava.patch)0
-rw-r--r--patches/server/0248-Optimize-CraftBlockData-Creation.patch (renamed from patches/server/0254-Optimize-CraftBlockData-Creation.patch)6
-rw-r--r--patches/server/0249-Optimize-MappedRegistry.patch (renamed from patches/server/0255-Optimize-MappedRegistry.patch)0
-rw-r--r--patches/server/0250-Add-PhantomPreSpawnEvent.patch (renamed from patches/server/0256-Add-PhantomPreSpawnEvent.patch)0
-rw-r--r--patches/server/0251-Add-More-Creeper-API.patch (renamed from patches/server/0257-Add-More-Creeper-API.patch)0
-rw-r--r--patches/server/0252-Inventory-removeItemAnySlot.patch (renamed from patches/server/0258-Inventory-removeItemAnySlot.patch)0
-rw-r--r--patches/server/0253-Make-CraftWorld-loadChunk-int-int-false-load-unconve.patch (renamed from patches/server/0259-Make-CraftWorld-loadChunk-int-int-false-load-unconve.patch)4
-rw-r--r--patches/server/0254-Add-ray-tracing-methods-to-LivingEntity.patch (renamed from patches/server/0260-Add-ray-tracing-methods-to-LivingEntity.patch)4
-rw-r--r--patches/server/0255-Expose-attack-cooldown-methods-for-Player.patch (renamed from patches/server/0261-Expose-attack-cooldown-methods-for-Player.patch)4
-rw-r--r--patches/server/0256-Improve-death-events.patch (renamed from patches/server/0262-Improve-death-events.patch)12
-rw-r--r--patches/server/0257-Allow-chests-to-be-placed-with-NBT-data.patch (renamed from patches/server/0263-Allow-chests-to-be-placed-with-NBT-data.patch)0
-rw-r--r--patches/server/0258-Mob-Pathfinding-API.patch (renamed from patches/server/0264-Mob-Pathfinding-API.patch)0
-rw-r--r--patches/server/0259-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch (renamed from patches/server/0265-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch)0
-rw-r--r--patches/server/0260-Prevent-chunk-loading-from-Fluid-Flowing.patch (renamed from patches/server/0266-Prevent-chunk-loading-from-Fluid-Flowing.patch)0
-rw-r--r--patches/server/0261-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch (renamed from patches/server/0267-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch)0
-rw-r--r--patches/server/0262-Prevent-mob-spawning-from-loading-generating-chunks.patch (renamed from patches/server/0268-Prevent-mob-spawning-from-loading-generating-chunks.patch)0
-rw-r--r--patches/server/0263-Implement-furnace-cook-speed-multiplier-API.patch (renamed from patches/server/0269-Implement-furnace-cook-speed-multiplier-API.patch)0
-rw-r--r--patches/server/0264-Catch-JsonParseException-in-Entity-and-TE-names.patch (renamed from patches/server/0270-Catch-JsonParseException-in-Entity-and-TE-names.patch)8
-rw-r--r--patches/server/0265-Honor-EntityAgeable.ageLock.patch (renamed from patches/server/0271-Honor-EntityAgeable.ageLock.patch)0
-rw-r--r--patches/server/0266-Configurable-connection-throttle-kick-message.patch (renamed from patches/server/0272-Configurable-connection-throttle-kick-message.patch)0
-rw-r--r--patches/server/0267-Hook-into-CB-plugin-rewrites.patch (renamed from patches/server/0273-Hook-into-CB-plugin-rewrites.patch)0
-rw-r--r--patches/server/0268-PreSpawnerSpawnEvent.patch (renamed from patches/server/0274-PreSpawnerSpawnEvent.patch)0
-rw-r--r--patches/server/0269-Add-LivingEntity-getTargetEntity.patch (renamed from patches/server/0275-Add-LivingEntity-getTargetEntity.patch)0
-rw-r--r--patches/server/0270-Add-sun-related-API.patch (renamed from patches/server/0276-Add-sun-related-API.patch)4
-rw-r--r--patches/server/0271-Turtle-API.patch (renamed from patches/server/0277-Turtle-API.patch)0
-rw-r--r--patches/server/0272-Call-player-spectator-target-events-and-improve-impl.patch (renamed from patches/server/0278-Call-player-spectator-target-events-and-improve-impl.patch)4
-rw-r--r--patches/server/0273-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch (renamed from patches/server/0279-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch)0
-rw-r--r--patches/server/0274-Add-more-Witch-API.patch (renamed from patches/server/0280-Add-more-Witch-API.patch)0
-rw-r--r--patches/server/0275-Check-Drowned-for-Villager-Aggression-Config.patch (renamed from patches/server/0281-Check-Drowned-for-Villager-Aggression-Config.patch)0
-rw-r--r--patches/server/0276-Add-option-to-prevent-players-from-moving-into-unloa.patch (renamed from patches/server/0282-Add-option-to-prevent-players-from-moving-into-unloa.patch)0
-rw-r--r--patches/server/0277-Reset-players-airTicks-on-respawn.patch (renamed from patches/server/0283-Reset-players-airTicks-on-respawn.patch)4
-rw-r--r--patches/server/0278-Don-t-sleep-after-profile-lookups-if-not-needed.patch (renamed from patches/server/0284-Don-t-sleep-after-profile-lookups-if-not-needed.patch)0
-rw-r--r--patches/server/0279-Improve-Server-Thread-Pool-and-Thread-Priorities.patch (renamed from patches/server/0285-Improve-Server-Thread-Pool-and-Thread-Priorities.patch)2
-rw-r--r--patches/server/0280-Optimize-World-Time-Updates.patch (renamed from patches/server/0286-Optimize-World-Time-Updates.patch)4
-rw-r--r--patches/server/0281-Restore-custom-InventoryHolder-support.patch (renamed from patches/server/0287-Restore-custom-InventoryHolder-support.patch)0
-rw-r--r--patches/server/0282-Use-Vanilla-Minecart-Speeds.patch (renamed from patches/server/0288-Use-Vanilla-Minecart-Speeds.patch)0
-rw-r--r--patches/server/0283-Fix-SpongeAbsortEvent-handling.patch (renamed from patches/server/0289-Fix-SpongeAbsortEvent-handling.patch)0
-rw-r--r--patches/server/0284-Don-t-allow-digging-into-unloaded-chunks.patch (renamed from patches/server/0290-Don-t-allow-digging-into-unloaded-chunks.patch)0
-rw-r--r--patches/server/0285-Make-the-default-permission-message-configurable.patch (renamed from patches/server/0291-Make-the-default-permission-message-configurable.patch)6
-rw-r--r--patches/server/0286-Prevent-rayTrace-from-loading-chunks.patch (renamed from patches/server/0292-Prevent-rayTrace-from-loading-chunks.patch)0
-rw-r--r--patches/server/0287-Handle-Large-Packets-disconnecting-client.patch (renamed from patches/server/0293-Handle-Large-Packets-disconnecting-client.patch)4
-rw-r--r--patches/server/0288-force-entity-dismount-during-teleportation.patch (renamed from patches/server/0294-force-entity-dismount-during-teleportation.patch)14
-rw-r--r--patches/server/0289-Add-more-Zombie-API.patch (renamed from patches/server/0295-Add-more-Zombie-API.patch)0
-rw-r--r--patches/server/0290-Book-Size-Limits.patch (renamed from patches/server/0296-Book-Size-Limits.patch)0
-rw-r--r--patches/server/0291-Add-PlayerConnectionCloseEvent.patch (renamed from patches/server/0297-Add-PlayerConnectionCloseEvent.patch)4
-rw-r--r--patches/server/0292-Prevent-Enderman-from-loading-chunks.patch (renamed from patches/server/0298-Prevent-Enderman-from-loading-chunks.patch)0
-rw-r--r--patches/server/0293-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch (renamed from patches/server/0299-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch)16
-rw-r--r--patches/server/0294-Workaround-for-vehicle-tracking-issue-on-disconnect.patch (renamed from patches/server/0300-Workaround-for-vehicle-tracking-issue-on-disconnect.patch)4
-rw-r--r--patches/server/0295-Block-Entity-remove-from-being-called-on-Players.patch (renamed from patches/server/0301-Block-Entity-remove-from-being-called-on-Players.patch)4
-rw-r--r--patches/server/0296-BlockDestroyEvent.patch (renamed from patches/server/0302-BlockDestroyEvent.patch)4
-rw-r--r--patches/server/0297-Async-command-map-building.patch (renamed from patches/server/0303-Async-command-map-building.patch)4
-rw-r--r--patches/server/0298-Implement-Brigadier-Mojang-API.patch (renamed from patches/server/0304-Implement-Brigadier-Mojang-API.patch)0
-rw-r--r--patches/server/0299-Fix-Custom-Shapeless-Custom-Crafting-Recipes.patch (renamed from patches/server/0305-Fix-Custom-Shapeless-Custom-Crafting-Recipes.patch)0
-rw-r--r--patches/server/0300-Limit-Client-Sign-length-more.patch (renamed from patches/server/0306-Limit-Client-Sign-length-more.patch)0
-rw-r--r--patches/server/0301-Don-t-check-ConvertSigns-boolean-every-sign-save.patch (renamed from patches/server/0307-Don-t-check-ConvertSigns-boolean-every-sign-save.patch)0
-rw-r--r--patches/server/0302-Optimize-Network-Manager-and-add-advanced-packet-sup.patch (renamed from patches/server/0308-Optimize-Network-Manager-and-add-advanced-packet-sup.patch)26
-rw-r--r--patches/server/0303-Handle-Oversized-Tile-Entities-in-chunks.patch (renamed from patches/server/0309-Handle-Oversized-Tile-Entities-in-chunks.patch)0
-rw-r--r--patches/server/0304-Set-Zombie-last-tick-at-start-of-drowning-process.patch (renamed from patches/server/0310-Set-Zombie-last-tick-at-start-of-drowning-process.patch)0
-rw-r--r--patches/server/0305-Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch (renamed from patches/server/0311-Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch)2
-rw-r--r--patches/server/0306-Entity-getEntitySpawnReason.patch (renamed from patches/server/0312-Entity-getEntitySpawnReason.patch)12
-rw-r--r--patches/server/0307-Update-entity-Metadata-for-all-tracked-players.patch (renamed from patches/server/0313-Update-entity-Metadata-for-all-tracked-players.patch)0
-rw-r--r--patches/server/0308-Fire-event-on-GS4-query.patch (renamed from patches/server/0314-Fire-event-on-GS4-query.patch)0
-rw-r--r--patches/server/0309-Implement-PlayerPostRespawnEvent.patch (renamed from patches/server/0315-Implement-PlayerPostRespawnEvent.patch)2
-rw-r--r--patches/server/0310-don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch (renamed from patches/server/0316-don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch)0
-rw-r--r--patches/server/0311-Server-Tick-Events.patch (renamed from patches/server/0317-Server-Tick-Events.patch)6
-rw-r--r--patches/server/0312-PlayerDeathEvent-getItemsToKeep.patch (renamed from patches/server/0318-PlayerDeathEvent-getItemsToKeep.patch)6
-rw-r--r--patches/server/0313-Optimize-Captured-TileEntity-Lookup.patch (renamed from patches/server/0319-Optimize-Captured-TileEntity-Lookup.patch)6
-rw-r--r--patches/server/0314-Add-Heightmap-API.patch (renamed from patches/server/0320-Add-Heightmap-API.patch)2
-rw-r--r--patches/server/0315-Mob-Spawner-API-Enhancements.patch (renamed from patches/server/0321-Mob-Spawner-API-Enhancements.patch)0
-rw-r--r--patches/server/0316-Fix-CB-call-to-changed-postToMainThread-method.patch (renamed from patches/server/0322-Fix-CB-call-to-changed-postToMainThread-method.patch)0
-rw-r--r--patches/server/0317-Fix-sounds-when-item-frames-are-modified-MC-123450.patch (renamed from patches/server/0323-Fix-sounds-when-item-frames-are-modified-MC-123450.patch)0
-rw-r--r--patches/server/0318-Implement-CraftBlockSoundGroup.patch (renamed from patches/server/0325-Implement-CraftBlockSoundGroup.patch)0
-rw-r--r--patches/server/0319-Configurable-Keep-Spawn-Loaded-range-per-world.patch (renamed from patches/server/0326-Configurable-Keep-Spawn-Loaded-range-per-world.patch)10
-rw-r--r--patches/server/0320-Allow-Saving-of-Oversized-Chunks.patch (renamed from patches/server/0327-Allow-Saving-of-Oversized-Chunks.patch)4
-rw-r--r--patches/server/0321-Expose-the-internal-current-tick.patch (renamed from patches/server/0328-Expose-the-internal-current-tick.patch)2
-rw-r--r--patches/server/0322-Fix-World-isChunkGenerated-calls.patch (renamed from patches/server/0329-Fix-World-isChunkGenerated-calls.patch)60
-rw-r--r--patches/server/0323-Show-blockstate-location-if-we-failed-to-read-it.patch (renamed from patches/server/0330-Show-blockstate-location-if-we-failed-to-read-it.patch)0
-rw-r--r--patches/server/0324-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch (renamed from patches/server/0331-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch)0
-rw-r--r--patches/server/0325-Configurable-projectile-relative-velocity.patch (renamed from patches/server/0332-Configurable-projectile-relative-velocity.patch)0
-rw-r--r--patches/server/0326-offset-item-frame-ticking.patch (renamed from patches/server/0333-offset-item-frame-ticking.patch)0
-rw-r--r--patches/server/0327-Fix-MC-158900.patch (renamed from patches/server/0334-Fix-MC-158900.patch)2
-rw-r--r--patches/server/0328-Prevent-consuming-the-wrong-itemstack.patch (renamed from patches/server/0335-Prevent-consuming-the-wrong-itemstack.patch)0
-rw-r--r--patches/server/0329-Dont-send-unnecessary-sign-update.patch (renamed from patches/server/0336-Dont-send-unnecessary-sign-update.patch)0
-rw-r--r--patches/server/0330-Add-option-to-disable-pillager-patrols.patch (renamed from patches/server/0337-Add-option-to-disable-pillager-patrols.patch)0
-rw-r--r--patches/server/0331-Flat-bedrock-generator-settings.patch (renamed from patches/server/0338-Flat-bedrock-generator-settings.patch)2
-rw-r--r--patches/server/0332-Prevent-sync-chunk-loads-when-villagers-try-to-find-.patch (renamed from patches/server/0339-Prevent-sync-chunk-loads-when-villagers-try-to-find-.patch)0
-rw-r--r--patches/server/0333-MC-145656-Fix-Follow-Range-Initial-Target.patch (renamed from patches/server/0340-MC-145656-Fix-Follow-Range-Initial-Target.patch)0
-rw-r--r--patches/server/0334-Duplicate-UUID-Resolve-Option.patch (renamed from patches/server/0341-Duplicate-UUID-Resolve-Option.patch)51
-rw-r--r--patches/server/0335-Optimize-Hoppers.patch (renamed from patches/server/0342-Optimize-Hoppers.patch)4
-rw-r--r--patches/server/0336-PlayerDeathEvent-shouldDropExperience.patch (renamed from patches/server/0343-PlayerDeathEvent-shouldDropExperience.patch)4
-rw-r--r--patches/server/0337-Prevent-bees-loading-chunks-checking-hive-position.patch (renamed from patches/server/0344-Prevent-bees-loading-chunks-checking-hive-position.patch)0
-rw-r--r--patches/server/0338-Don-t-load-Chunks-from-Hoppers-and-other-things.patch (renamed from patches/server/0345-Don-t-load-Chunks-from-Hoppers-and-other-things.patch)0
-rw-r--r--patches/server/0339-Guard-against-serializing-mismatching-chunk-coordina.patch (renamed from patches/server/0346-Guard-against-serializing-mismatching-chunk-coordina.patch)8
-rw-r--r--patches/server/0340-Optimise-IEntityAccess-getPlayerByUUID.patch (renamed from patches/server/0347-Optimise-IEntityAccess-getPlayerByUUID.patch)8
-rw-r--r--patches/server/0341-Fix-items-not-falling-correctly.patch (renamed from patches/server/0348-Fix-items-not-falling-correctly.patch)4
-rw-r--r--patches/server/0342-Lag-compensate-eating.patch (renamed from patches/server/0349-Lag-compensate-eating.patch)0
-rw-r--r--patches/server/0343-Optimize-call-to-getFluid-for-explosions.patch (renamed from patches/server/0350-Optimize-call-to-getFluid-for-explosions.patch)0
-rw-r--r--patches/server/0344-Fix-last-firework-in-stack-not-having-effects-when-d.patch (renamed from patches/server/0351-Fix-last-firework-in-stack-not-having-effects-when-d.patch)0
-rw-r--r--patches/server/0345-Add-effect-to-block-break-naturally.patch (renamed from patches/server/0352-Add-effect-to-block-break-naturally.patch)0
-rw-r--r--patches/server/0346-Entity-Activation-Range-2.0.patch (renamed from patches/server/0353-Entity-Activation-Range-2.0.patch)42
-rw-r--r--patches/server/0347-Increase-Light-Queue-Size.patch (renamed from patches/server/0354-Increase-Light-Queue-Size.patch)2
-rw-r--r--patches/server/0348-Anti-Xray.patch (renamed from patches/server/0356-Anti-Xray.patch)70
-rw-r--r--patches/server/0349-Implement-alternative-item-despawn-rate.patch (renamed from patches/server/0357-Implement-alternative-item-despawn-rate.patch)0
-rw-r--r--patches/server/0350-Tracking-Range-Improvements.patch (renamed from patches/server/0358-Tracking-Range-Improvements.patch)4
-rw-r--r--patches/server/0351-Fix-items-vanishing-through-end-portal.patch (renamed from patches/server/0359-Fix-items-vanishing-through-end-portal.patch)4
-rw-r--r--patches/server/0352-implement-optional-per-player-mob-spawns.patch (renamed from patches/server/0360-implement-optional-per-player-mob-spawns.patch)58
-rw-r--r--patches/server/0353-Bees-get-gravity-in-void.-Fixes-MC-167279.patch (renamed from patches/server/0362-Bees-get-gravity-in-void.-Fixes-MC-167279.patch)0
-rw-r--r--patches/server/0354-Optimise-getChunkAt-calls-for-loaded-chunks.patch (renamed from patches/server/0363-Optimise-getChunkAt-calls-for-loaded-chunks.patch)8
-rw-r--r--patches/server/0355-Add-debug-for-sync-chunk-loads.patch (renamed from patches/server/0364-Add-debug-for-sync-chunk-loads.patch)24
-rw-r--r--patches/server/0356-Remove-garbage-Java-version-check.patch (renamed from patches/server/0365-Remove-garbage-Java-version-check.patch)0
-rw-r--r--patches/server/0357-Add-ThrownEggHatchEvent.patch (renamed from patches/server/0366-Add-ThrownEggHatchEvent.patch)0
-rw-r--r--patches/server/0358-Entity-Jump-API.patch (renamed from patches/server/0367-Entity-Jump-API.patch)0
-rw-r--r--patches/server/0359-Add-option-to-nerf-pigmen-from-nether-portals.patch (renamed from patches/server/0368-Add-option-to-nerf-pigmen-from-nether-portals.patch)6
-rw-r--r--patches/server/0360-Make-the-GUI-graph-fancier.patch (renamed from patches/server/0369-Make-the-GUI-graph-fancier.patch)0
-rw-r--r--patches/server/0361-Avoid-hopper-searches-if-there-are-no-items.patch124
-rw-r--r--patches/server/0361-add-hand-to-BlockMultiPlaceEvent.patch (renamed from patches/server/0370-add-hand-to-BlockMultiPlaceEvent.patch)0
-rw-r--r--patches/server/0362-Validate-tripwire-hook-placement-before-update.patch (renamed from patches/server/0371-Validate-tripwire-hook-placement-before-update.patch)0
-rw-r--r--patches/server/0363-Add-option-to-allow-iron-golems-to-spawn-in-air.patch (renamed from patches/server/0372-Add-option-to-allow-iron-golems-to-spawn-in-air.patch)0
-rw-r--r--patches/server/0364-Configurable-chance-of-villager-zombie-infection.patch (renamed from patches/server/0373-Configurable-chance-of-villager-zombie-infection.patch)0
-rw-r--r--patches/server/0365-Optimise-Chunk-getFluid.patch (renamed from patches/server/0374-Optimise-Chunk-getFluid.patch)6
-rw-r--r--patches/server/0366-Set-spigots-verbose-world-setting-to-false-by-def.patch (renamed from patches/server/0375-Set-spigots-verbose-world-setting-to-false-by-def.patch)0
-rw-r--r--patches/server/0367-Add-tick-times-API-and-mspt-command.patch (renamed from patches/server/0376-Add-tick-times-API-and-mspt-command.patch)8
-rw-r--r--patches/server/0368-Expose-MinecraftServer-isRunning.patch (renamed from patches/server/0377-Expose-MinecraftServer-isRunning.patch)2
-rw-r--r--patches/server/0369-Add-Raw-Byte-ItemStack-Serialization.patch (renamed from patches/server/0378-Add-Raw-Byte-ItemStack-Serialization.patch)0
-rw-r--r--patches/server/0370-Pillager-patrol-spawn-settings-and-per-player-option.patch (renamed from patches/server/0379-Pillager-patrol-spawn-settings-and-per-player-option.patch)4
-rw-r--r--patches/server/0371-Remote-Connections-shouldn-t-hold-up-shutdown.patch (renamed from patches/server/0380-Remote-Connections-shouldn-t-hold-up-shutdown.patch)2
-rw-r--r--patches/server/0372-Do-not-allow-bees-to-load-chunks-for-beehives.patch (renamed from patches/server/0381-Do-not-allow-bees-to-load-chunks-for-beehives.patch)0
-rw-r--r--patches/server/0373-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch (renamed from patches/server/0382-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch)10
-rw-r--r--patches/server/0374-Don-t-tick-dead-players.patch (renamed from patches/server/0383-Don-t-tick-dead-players.patch)4
-rw-r--r--patches/server/0375-Dead-Player-s-shouldn-t-be-able-to-move.patch (renamed from patches/server/0384-Dead-Player-s-shouldn-t-be-able-to-move.patch)0
-rw-r--r--patches/server/0376-Optimize-Collision-to-not-load-chunks.patch (renamed from patches/server/0385-Optimize-Collision-to-not-load-chunks.patch)4
-rw-r--r--patches/server/0377-Don-t-move-existing-players-to-world-spawn.patch (renamed from patches/server/0386-Don-t-move-existing-players-to-world-spawn.patch)6
-rw-r--r--patches/server/0378-Optimize-GoalSelector-Goal.Flag-Set-operations.patch (renamed from patches/server/0387-Optimize-GoalSelector-Goal.Flag-Set-operations.patch)2
-rw-r--r--patches/server/0379-Improved-Watchdog-Support.patch (renamed from patches/server/0388-Improved-Watchdog-Support.patch)84
-rw-r--r--patches/server/0380-Optimize-Pathfinding.patch (renamed from patches/server/0389-Optimize-Pathfinding.patch)0
-rw-r--r--patches/server/0381-Reduce-Either-Optional-allocation.patch (renamed from patches/server/0390-Reduce-Either-Optional-allocation.patch)2
-rw-r--r--patches/server/0382-Reduce-memory-footprint-of-NBTTagCompound.patch (renamed from patches/server/0391-Reduce-memory-footprint-of-NBTTagCompound.patch)2
-rw-r--r--patches/server/0383-Prevent-opening-inventories-when-frozen.patch (renamed from patches/server/0392-Prevent-opening-inventories-when-frozen.patch)6
-rw-r--r--patches/server/0384-Don-t-run-entity-collision-code-if-not-needed.patch (renamed from patches/server/0394-Don-t-run-entity-collision-code-if-not-needed.patch)0
-rw-r--r--patches/server/0385-Implement-Player-Client-Options-API.patch (renamed from patches/server/0395-Implement-Player-Client-Options-API.patch)12
-rw-r--r--patches/server/0386-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch (renamed from patches/server/0396-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch)6
-rw-r--r--patches/server/0387-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch (renamed from patches/server/0398-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch)10
-rw-r--r--patches/server/0388-Load-Chunks-for-Login-Asynchronously.patch (renamed from patches/server/0399-Load-Chunks-for-Login-Asynchronously.patch)18
-rw-r--r--patches/server/0389-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch (renamed from patches/server/0400-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch)4
-rw-r--r--patches/server/0390-Add-PlayerAttackEntityCooldownResetEvent.patch (renamed from patches/server/0401-Add-PlayerAttackEntityCooldownResetEvent.patch)0
-rw-r--r--patches/server/0391-Don-t-fire-BlockFade-on-worldgen-threads.patch (renamed from patches/server/0402-Don-t-fire-BlockFade-on-worldgen-threads.patch)0
-rw-r--r--patches/server/0392-Add-phantom-creative-and-insomniac-controls.patch (renamed from patches/server/0403-Add-phantom-creative-and-insomniac-controls.patch)0
-rw-r--r--patches/server/0393-Fix-numerous-item-duplication-issues-and-teleport-is.patch (renamed from patches/server/0404-Fix-numerous-item-duplication-issues-and-teleport-is.patch)12
-rw-r--r--patches/server/0394-Villager-Restocks-API.patch (renamed from patches/server/0405-Villager-Restocks-API.patch)0
-rw-r--r--patches/server/0395-Validate-PickItem-Packet-and-kick-for-invalid.patch (renamed from patches/server/0406-Validate-PickItem-Packet-and-kick-for-invalid.patch)0
-rw-r--r--patches/server/0396-Expose-game-version.patch (renamed from patches/server/0407-Expose-game-version.patch)2
-rw-r--r--patches/server/0397-Optimize-Voxel-Shape-Merging.patch (renamed from patches/server/0408-Optimize-Voxel-Shape-Merging.patch)0
-rw-r--r--patches/server/0398-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch (renamed from patches/server/0409-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch)0
-rw-r--r--patches/server/0399-misc-debugging-dumps.patch (renamed from patches/server/0410-misc-debugging-dumps.patch)10
-rw-r--r--patches/server/0400-Prevent-teleporting-dead-entities.patch (renamed from patches/server/0411-Prevent-teleporting-dead-entities.patch)0
-rw-r--r--patches/server/0401-Deobfuscate-stacktraces-in-log-messages-crash-report.patch (renamed from patches/server/0412-Deobfuscate-stacktraces-in-log-messages-crash-report.patch)10
-rw-r--r--patches/server/0402-Implement-Mob-Goal-API.patch (renamed from patches/server/0413-Implement-Mob-Goal-API.patch)2
-rw-r--r--patches/server/0403-Add-villager-reputation-API.patch (renamed from patches/server/0414-Add-villager-reputation-API.patch)0
-rw-r--r--patches/server/0404-Option-for-maximum-exp-value-when-merging-orbs.patch (renamed from patches/server/0415-Option-for-maximum-exp-value-when-merging-orbs.patch)0
-rw-r--r--patches/server/0405-ExperienceOrbMergeEvent.patch (renamed from patches/server/0416-ExperienceOrbMergeEvent.patch)0
-rw-r--r--patches/server/0406-Fix-PotionEffect-ignores-icon-flag.patch (renamed from patches/server/0417-Fix-PotionEffect-ignores-icon-flag.patch)0
-rw-r--r--patches/server/0407-Optimize-brigadier-child-sorting-performance.patch (renamed from patches/server/0418-Optimize-brigadier-child-sorting-performance.patch)0
-rw-r--r--patches/server/0408-Potential-bed-API.patch (renamed from patches/server/0419-Potential-bed-API.patch)0
-rw-r--r--patches/server/0409-Wait-for-Async-Tasks-during-shutdown.patch (renamed from patches/server/0420-Wait-for-Async-Tasks-during-shutdown.patch)6
-rw-r--r--patches/server/0410-Ensure-EntityRaider-respects-game-and-entity-rules-f.patch (renamed from patches/server/0421-Ensure-EntityRaider-respects-game-and-entity-rules-f.patch)0
-rw-r--r--patches/server/0411-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch (renamed from patches/server/0422-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch)8
-rw-r--r--patches/server/0412-Reduce-MutableInt-allocations-from-light-engine.patch (renamed from patches/server/0423-Reduce-MutableInt-allocations-from-light-engine.patch)2
-rw-r--r--patches/server/0413-Reduce-allocation-of-Vec3D-by-entity-tracker.patch (renamed from patches/server/0424-Reduce-allocation-of-Vec3D-by-entity-tracker.patch)8
-rw-r--r--patches/server/0414-Ensure-safe-gateway-teleport.patch (renamed from patches/server/0425-Ensure-safe-gateway-teleport.patch)0
-rw-r--r--patches/server/0415-Add-option-for-console-having-all-permissions.patch (renamed from patches/server/0426-Add-option-for-console-having-all-permissions.patch)0
-rw-r--r--patches/server/0416-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch (renamed from patches/server/0427-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch)96
-rw-r--r--patches/server/0417-Use-distance-map-to-optimise-entity-tracker.patch (renamed from patches/server/0428-Use-distance-map-to-optimise-entity-tracker.patch)130
-rw-r--r--patches/server/0418-Fix-villager-trading-demand-MC-163962.patch (renamed from patches/server/0430-Fix-villager-trading-demand-MC-163962.patch)0
-rw-r--r--patches/server/0419-Maps-shouldn-t-load-chunks.patch (renamed from patches/server/0431-Maps-shouldn-t-load-chunks.patch)0
-rw-r--r--patches/server/0420-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch (renamed from patches/server/0432-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch)0
-rw-r--r--patches/server/0421-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch (renamed from patches/server/0433-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch)0
-rw-r--r--patches/server/0422-Fix-piston-physics-inconsistency-MC-188840.patch (renamed from patches/server/0434-Fix-piston-physics-inconsistency-MC-188840.patch)0
-rw-r--r--patches/server/0423-Fix-sand-duping.patch (renamed from patches/server/0435-Fix-sand-duping.patch)0
-rw-r--r--patches/server/0424-Fix-missing-chunks-due-to-integer-overflow.patch (renamed from patches/server/0436-Fix-missing-chunks-due-to-integer-overflow.patch)0
-rw-r--r--patches/server/0425-Prevent-position-desync-in-playerconnection-causing-.patch (renamed from patches/server/0437-Prevent-position-desync-in-playerconnection-causing-.patch)0
-rw-r--r--patches/server/0426-Inventory-getHolder-method-without-block-snapshot.patch (renamed from patches/server/0438-Inventory-getHolder-method-without-block-snapshot.patch)0
-rw-r--r--patches/server/0427-Improve-Arrow-API.patch (renamed from patches/server/0439-Improve-Arrow-API.patch)0
-rw-r--r--patches/server/0428-Add-and-implement-PlayerRecipeBookClickEvent.patch (renamed from patches/server/0440-Add-and-implement-PlayerRecipeBookClickEvent.patch)0
-rw-r--r--patches/server/0429-Hide-sync-chunk-writes-behind-flag.patch (renamed from patches/server/0441-Hide-sync-chunk-writes-behind-flag.patch)0
-rw-r--r--patches/server/0430-Add-permission-for-command-blocks.patch (renamed from patches/server/0442-Add-permission-for-command-blocks.patch)0
-rw-r--r--patches/server/0431-Ensure-Entity-AABB-s-are-never-invalid.patch (renamed from patches/server/0443-Ensure-Entity-AABB-s-are-never-invalid.patch)14
-rw-r--r--patches/server/0432-Fix-Per-World-Difficulty-Remembering-Difficulty.patch (renamed from patches/server/0444-Fix-Per-World-Difficulty-Remembering-Difficulty.patch)14
-rw-r--r--patches/server/0433-Paper-dumpitem-command.patch (renamed from patches/server/0445-Paper-dumpitem-command.patch)4
-rw-r--r--patches/server/0434-Don-t-allow-null-UUID-s-for-chat.patch (renamed from patches/server/0446-Don-t-allow-null-UUID-s-for-chat.patch)0
-rw-r--r--patches/server/0435-Improve-Legacy-Component-serialization-size.patch (renamed from patches/server/0447-Improve-Legacy-Component-serialization-size.patch)0
-rw-r--r--patches/server/0436-Optimize-Bit-Operations-by-inlining.patch (renamed from patches/server/0448-Optimize-Bit-Operations-by-inlining.patch)0
-rw-r--r--patches/server/0437-Add-Plugin-Tickets-to-API-Chunk-Methods.patch (renamed from patches/server/0449-Add-Plugin-Tickets-to-API-Chunk-Methods.patch)12
-rw-r--r--patches/server/0438-incremental-chunk-and-player-saving.patch164
-rw-r--r--patches/server/0439-Stop-copy-on-write-operations-for-updating-light-dat.patch (renamed from patches/server/0451-Stop-copy-on-write-operations-for-updating-light-dat.patch)2
-rw-r--r--patches/server/0440-Support-old-UUID-format-for-NBT.patch (renamed from patches/server/0452-Support-old-UUID-format-for-NBT.patch)0
-rw-r--r--patches/server/0441-Clean-up-duplicated-GameProfile-Properties.patch (renamed from patches/server/0453-Clean-up-duplicated-GameProfile-Properties.patch)0
-rw-r--r--patches/server/0442-Convert-legacy-attributes-in-Item-Meta.patch (renamed from patches/server/0454-Convert-legacy-attributes-in-Item-Meta.patch)0
-rw-r--r--patches/server/0443-Remove-some-streams-from-structures.patch (renamed from patches/server/0455-Remove-some-streams-from-structures.patch)0
-rw-r--r--patches/server/0444-Remove-streams-from-classes-related-villager-gossip.patch (renamed from patches/server/0456-Remove-streams-from-classes-related-villager-gossip.patch)0
-rw-r--r--patches/server/0445-Support-components-in-ItemMeta.patch (renamed from patches/server/0457-Support-components-in-ItemMeta.patch)0
-rw-r--r--patches/server/0446-Improve-EntityTargetLivingEntityEvent-for-1.16-mobs.patch (renamed from patches/server/0458-Improve-EntityTargetLivingEntityEvent-for-1.16-mobs.patch)0
-rw-r--r--patches/server/0447-Add-entity-liquid-API.patch (renamed from patches/server/0459-Add-entity-liquid-API.patch)0
-rw-r--r--patches/server/0448-Update-itemstack-legacy-name-and-lore.patch (renamed from patches/server/0460-Update-itemstack-legacy-name-and-lore.patch)0
-rw-r--r--patches/server/0449-Spawn-player-in-correct-world-on-login.patch (renamed from patches/server/0461-Spawn-player-in-correct-world-on-login.patch)2
-rw-r--r--patches/server/0450-Add-PrepareResultEvent.patch (renamed from patches/server/0462-Add-PrepareResultEvent.patch)0
-rw-r--r--patches/server/0450-incremental-chunk-and-player-saving.patch375
-rw-r--r--patches/server/0451-Don-t-check-chunk-for-portal-on-world-gen-entity-add.patch (renamed from patches/server/0463-Don-t-check-chunk-for-portal-on-world-gen-entity-add.patch)0
-rw-r--r--patches/server/0452-Optimize-NetworkManager-Exception-Handling.patch (renamed from patches/server/0464-Optimize-NetworkManager-Exception-Handling.patch)0
-rw-r--r--patches/server/0453-Optimize-the-advancement-data-player-iteration-to-be.patch (renamed from patches/server/0465-Optimize-the-advancement-data-player-iteration-to-be.patch)0
-rw-r--r--patches/server/0454-Fix-arrows-never-despawning-MC-125757.patch (renamed from patches/server/0466-Fix-arrows-never-despawning-MC-125757.patch)0
-rw-r--r--patches/server/0455-Thread-Safe-Vanilla-Command-permission-checking.patch (renamed from patches/server/0467-Thread-Safe-Vanilla-Command-permission-checking.patch)0
-rw-r--r--patches/server/0456-Fix-SPIGOT-5989.patch (renamed from patches/server/0468-Fix-SPIGOT-5989.patch)2
-rw-r--r--patches/server/0457-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch (renamed from patches/server/0469-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch)0
-rw-r--r--patches/server/0458-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch (renamed from patches/server/0470-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch)0
-rw-r--r--patches/server/0459-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch (renamed from patches/server/0471-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch)2
-rw-r--r--patches/server/0460-Add-missing-strikeLighting-call-to-World-spigot-stri.patch (renamed from patches/server/0472-Add-missing-strikeLighting-call-to-World-spigot-stri.patch)4
-rw-r--r--patches/server/0461-Fix-some-rails-connecting-improperly.patch (renamed from patches/server/0473-Fix-some-rails-connecting-improperly.patch)2
-rw-r--r--patches/server/0462-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch (renamed from patches/server/0474-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch)0
-rw-r--r--patches/server/0463-Do-not-let-the-server-load-chunks-from-newer-version.patch (renamed from patches/server/0475-Do-not-let-the-server-load-chunks-from-newer-version.patch)4
-rw-r--r--patches/server/0464-Brand-support.patch (renamed from patches/server/0476-Brand-support.patch)4
-rw-r--r--patches/server/0465-Add-setMaxPlayers-API.patch (renamed from patches/server/0477-Add-setMaxPlayers-API.patch)4
-rw-r--r--patches/server/0466-Add-playPickupItemAnimation-to-LivingEntity.patch (renamed from patches/server/0478-Add-playPickupItemAnimation-to-LivingEntity.patch)0
-rw-r--r--patches/server/0467-Don-t-require-FACING-data.patch (renamed from patches/server/0479-Don-t-require-FACING-data.patch)0
-rw-r--r--patches/server/0468-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch (renamed from patches/server/0480-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch)6
-rw-r--r--patches/server/0469-Add-moon-phase-API.patch (renamed from patches/server/0481-Add-moon-phase-API.patch)0
-rw-r--r--patches/server/0470-Prevent-headless-pistons-from-being-created.patch (renamed from patches/server/0483-Prevent-headless-pistons-from-being-created.patch)0
-rw-r--r--patches/server/0471-Add-BellRingEvent.patch (renamed from patches/server/0484-Add-BellRingEvent.patch)0
-rw-r--r--patches/server/0472-Add-zombie-targets-turtle-egg-config.patch (renamed from patches/server/0485-Add-zombie-targets-turtle-egg-config.patch)0
-rw-r--r--patches/server/0473-Buffer-joins-to-world.patch (renamed from patches/server/0486-Buffer-joins-to-world.patch)4
-rw-r--r--patches/server/0474-Eigencraft-redstone-implementation.patch (renamed from patches/server/0487-Eigencraft-redstone-implementation.patch)0
-rw-r--r--patches/server/0475-Fix-hex-colors-not-working-in-some-kick-messages.patch (renamed from patches/server/0488-Fix-hex-colors-not-working-in-some-kick-messages.patch)0
-rw-r--r--patches/server/0476-PortalCreateEvent-needs-to-know-its-entity.patch (renamed from patches/server/0489-PortalCreateEvent-needs-to-know-its-entity.patch)2
-rw-r--r--patches/server/0477-Fix-CraftTeam-null-check.patch (renamed from patches/server/0490-Fix-CraftTeam-null-check.patch)0
-rw-r--r--patches/server/0478-Add-more-Evoker-API.patch (renamed from patches/server/0491-Add-more-Evoker-API.patch)0
-rw-r--r--patches/server/0479-Add-methods-to-get-translation-keys.patch (renamed from patches/server/0492-Add-methods-to-get-translation-keys.patch)0
-rw-r--r--patches/server/0480-Create-HoverEvent-from-ItemStack-Entity.patch (renamed from patches/server/0493-Create-HoverEvent-from-ItemStack-Entity.patch)0
-rw-r--r--patches/server/0481-Cache-block-data-strings.patch (renamed from patches/server/0494-Cache-block-data-strings.patch)4
-rw-r--r--patches/server/0482-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch (renamed from patches/server/0495-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch)4
-rw-r--r--patches/server/0483-Add-additional-open-container-api-to-HumanEntity.patch (renamed from patches/server/0496-Add-additional-open-container-api-to-HumanEntity.patch)0
-rw-r--r--patches/server/0484-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch (renamed from patches/server/0497-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch)0
-rw-r--r--patches/server/0485-Extend-block-drop-capture-to-capture-all-items-added.patch (renamed from patches/server/0498-Extend-block-drop-capture-to-capture-all-items-added.patch)4
-rw-r--r--patches/server/0486-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch (renamed from patches/server/0500-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch)4
-rw-r--r--patches/server/0487-Lazily-track-plugin-scoreboards-by-default.patch (renamed from patches/server/0501-Lazily-track-plugin-scoreboards-by-default.patch)0
-rw-r--r--patches/server/0488-Entity-isTicking.patch (renamed from patches/server/0502-Entity-isTicking.patch)4
-rw-r--r--patches/server/0489-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch (renamed from patches/server/0503-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch)4
-rw-r--r--patches/server/0490-Fix-Concurrency-issue-in-ShufflingList.patch (renamed from patches/server/0504-Fix-Concurrency-issue-in-ShufflingList.patch)0
-rw-r--r--patches/server/0491-Reset-Ender-Crystals-on-Dragon-Spawn.patch (renamed from patches/server/0505-Reset-Ender-Crystals-on-Dragon-Spawn.patch)0
-rw-r--r--patches/server/0492-Fix-for-large-move-vectors-crashing-server.patch (renamed from patches/server/0506-Fix-for-large-move-vectors-crashing-server.patch)2
-rw-r--r--patches/server/0493-Optimise-getType-calls.patch (renamed from patches/server/0507-Optimise-getType-calls.patch)2
-rw-r--r--patches/server/0494-Villager-resetOffers.patch (renamed from patches/server/0508-Villager-resetOffers.patch)0
-rw-r--r--patches/server/0495-Improve-inlinig-for-some-hot-IBlockData-methods.patch (renamed from patches/server/0509-Improve-inlinig-for-some-hot-IBlockData-methods.patch)16
-rw-r--r--patches/server/0496-Retain-block-place-order-when-capturing-blockstates.patch (renamed from patches/server/0510-Retain-block-place-order-when-capturing-blockstates.patch)4
-rw-r--r--patches/server/0497-Reduce-blockpos-allocation-from-pathfinding.patch (renamed from patches/server/0511-Reduce-blockpos-allocation-from-pathfinding.patch)2
-rw-r--r--patches/server/0498-Fix-item-locations-dropped-from-campfires.patch (renamed from patches/server/0512-Fix-item-locations-dropped-from-campfires.patch)0
-rw-r--r--patches/server/0499-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch18
-rw-r--r--patches/server/0499-Player-elytra-boost-API.patch (renamed from patches/server/0513-Player-elytra-boost-API.patch)4
-rw-r--r--patches/server/0500-Fixed-TileEntityBell-memory-leak.patch (renamed from patches/server/0514-Fixed-TileEntityBell-memory-leak.patch)0
-rw-r--r--patches/server/0501-Avoid-error-bubbling-up-when-item-stack-is-empty-in-.patch (renamed from patches/server/0515-Avoid-error-bubbling-up-when-item-stack-is-empty-in-.patch)0
-rw-r--r--patches/server/0502-Add-getOfflinePlayerIfCached-String.patch (renamed from patches/server/0516-Add-getOfflinePlayerIfCached-String.patch)2
-rw-r--r--patches/server/0503-Add-ignore-discounts-API.patch (renamed from patches/server/0517-Add-ignore-discounts-API.patch)0
-rw-r--r--patches/server/0504-Toggle-for-removing-existing-dragon.patch (renamed from patches/server/0518-Toggle-for-removing-existing-dragon.patch)0
-rw-r--r--patches/server/0505-Fix-client-lag-on-advancement-loading.patch (renamed from patches/server/0519-Fix-client-lag-on-advancement-loading.patch)0
-rw-r--r--patches/server/0506-Item-no-age-no-player-pickup.patch (renamed from patches/server/0520-Item-no-age-no-player-pickup.patch)0
-rw-r--r--patches/server/0507-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch (renamed from patches/server/0521-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch)0
-rw-r--r--patches/server/0508-Beacon-API-custom-effect-ranges.patch (renamed from patches/server/0522-Beacon-API-custom-effect-ranges.patch)0
-rw-r--r--patches/server/0509-Add-API-for-quit-reason.patch (renamed from patches/server/0523-Add-API-for-quit-reason.patch)10
-rw-r--r--patches/server/0510-Add-Wandering-Trader-spawn-rate-config-options.patch (renamed from patches/server/0524-Add-Wandering-Trader-spawn-rate-config-options.patch)0
-rw-r--r--patches/server/0511-Expose-world-spawn-angle.patch (renamed from patches/server/0525-Expose-world-spawn-angle.patch)2
-rw-r--r--patches/server/0512-Add-Destroy-Speed-API.patch (renamed from patches/server/0526-Add-Destroy-Speed-API.patch)0
-rw-r--r--patches/server/0513-Fix-Player-spawnParticle-x-y-z-precision-loss.patch (renamed from patches/server/0527-Fix-Player-spawnParticle-x-y-z-precision-loss.patch)4
-rw-r--r--patches/server/0514-Add-LivingEntity-clearActiveItem.patch (renamed from patches/server/0528-Add-LivingEntity-clearActiveItem.patch)0
-rw-r--r--patches/server/0515-Add-PlayerItemCooldownEvent.patch (renamed from patches/server/0529-Add-PlayerItemCooldownEvent.patch)0
-rw-r--r--patches/server/0516-Significantly-improve-performance-of-the-end-generat.patch (renamed from patches/server/0530-Significantly-improve-performance-of-the-end-generat.patch)0
-rw-r--r--patches/server/0517-More-lightning-API.patch (renamed from patches/server/0531-More-lightning-API.patch)0
-rw-r--r--patches/server/0518-Climbing-should-not-bypass-cramming-gamerule.patch (renamed from patches/server/0532-Climbing-should-not-bypass-cramming-gamerule.patch)4
-rw-r--r--patches/server/0519-Added-missing-default-perms-for-commands.patch (renamed from patches/server/0533-Added-missing-default-perms-for-commands.patch)0
-rw-r--r--patches/server/0520-Add-PlayerShearBlockEvent.patch (renamed from patches/server/0534-Add-PlayerShearBlockEvent.patch)0
-rw-r--r--patches/server/0521-Fix-curing-zombie-villager-discount-exploit.patch (renamed from patches/server/0535-Fix-curing-zombie-villager-discount-exploit.patch)0
-rw-r--r--patches/server/0522-Limit-recipe-packets.patch (renamed from patches/server/0536-Limit-recipe-packets.patch)0
-rw-r--r--patches/server/0523-Fix-CraftSound-backwards-compatibility.patch (renamed from patches/server/0537-Fix-CraftSound-backwards-compatibility.patch)0
-rw-r--r--patches/server/0524-Player-Chunk-Load-Unload-Events.patch (renamed from patches/server/0538-Player-Chunk-Load-Unload-Events.patch)4
-rw-r--r--patches/server/0525-Optimize-Dynamic-get-Missing-Keys.patch (renamed from patches/server/0539-Optimize-Dynamic-get-Missing-Keys.patch)0
-rw-r--r--patches/server/0526-Expose-LivingEntity-hurt-direction.patch (renamed from patches/server/0540-Expose-LivingEntity-hurt-direction.patch)0
-rw-r--r--patches/server/0527-Add-OBSTRUCTED-reason-to-BedEnterResult.patch (renamed from patches/server/0541-Add-OBSTRUCTED-reason-to-BedEnterResult.patch)0
-rw-r--r--patches/server/0528-Do-not-crash-from-invalid-ingredient-lists-in-Villag.patch (renamed from patches/server/0542-Do-not-crash-from-invalid-ingredient-lists-in-Villag.patch)0
-rw-r--r--patches/server/0529-Add-PlayerTradeEvent-and-PlayerPurchaseEvent.patch (renamed from patches/server/0543-Add-PlayerTradeEvent-and-PlayerPurchaseEvent.patch)0
-rw-r--r--patches/server/0530-Implement-TargetHitEvent.patch (renamed from patches/server/0544-Implement-TargetHitEvent.patch)0
-rw-r--r--patches/server/0531-MC-4-Fix-item-position-desync.patch (renamed from patches/server/0545-MC-4-Fix-item-position-desync.patch)10
-rw-r--r--patches/server/0532-Additional-Block-Material-API-s.patch (renamed from patches/server/0546-Additional-Block-Material-API-s.patch)0
-rw-r--r--patches/server/0533-Fix-harming-potion-dupe.patch (renamed from patches/server/0547-Fix-harming-potion-dupe.patch)0
-rw-r--r--patches/server/0534-Implement-API-to-get-Material-from-Boats-and-Minecar.patch (renamed from patches/server/0548-Implement-API-to-get-Material-from-Boats-and-Minecar.patch)0
-rw-r--r--patches/server/0535-Cache-burn-durations.patch (renamed from patches/server/0549-Cache-burn-durations.patch)0
-rw-r--r--patches/server/0536-Allow-disabling-mob-spawner-spawn-egg-transformation.patch (renamed from patches/server/0550-Allow-disabling-mob-spawner-spawn-egg-transformation.patch)0
-rw-r--r--patches/server/0537-Fix-Not-a-string-Map-Conversion-spam.patch (renamed from patches/server/0551-Fix-Not-a-string-Map-Conversion-spam.patch)0
-rw-r--r--patches/server/0538-Implement-PlayerFlowerPotManipulateEvent.patch (renamed from patches/server/0552-Implement-PlayerFlowerPotManipulateEvent.patch)0
-rw-r--r--patches/server/0539-Fix-interact-event-not-being-called-in-adventure.patch (renamed from patches/server/0553-Fix-interact-event-not-being-called-in-adventure.patch)0
-rw-r--r--patches/server/0540-Zombie-API-breaking-doors.patch (renamed from patches/server/0554-Zombie-API-breaking-doors.patch)0
-rw-r--r--patches/server/0541-Fix-nerfed-slime-when-splitting.patch (renamed from patches/server/0555-Fix-nerfed-slime-when-splitting.patch)0
-rw-r--r--patches/server/0542-Add-EntityLoadCrossbowEvent.patch (renamed from patches/server/0556-Add-EntityLoadCrossbowEvent.patch)0
-rw-r--r--patches/server/0543-Guardian-beam-workaround.patch (renamed from patches/server/0557-Guardian-beam-workaround.patch)0
-rw-r--r--patches/server/0544-Added-WorldGameRuleChangeEvent.patch (renamed from patches/server/0558-Added-WorldGameRuleChangeEvent.patch)6
-rw-r--r--patches/server/0545-Added-ServerResourcesReloadedEvent.patch (renamed from patches/server/0559-Added-ServerResourcesReloadedEvent.patch)6
-rw-r--r--patches/server/0546-Added-world-settings-for-mobs-picking-up-loot.patch (renamed from patches/server/0560-Added-world-settings-for-mobs-picking-up-loot.patch)0
-rw-r--r--patches/server/0547-Implemented-BlockFailedDispenseEvent.patch (renamed from patches/server/0561-Implemented-BlockFailedDispenseEvent.patch)0
-rw-r--r--patches/server/0548-Added-PlayerLecternPageChangeEvent.patch (renamed from patches/server/0562-Added-PlayerLecternPageChangeEvent.patch)0
-rw-r--r--patches/server/0549-Added-PlayerLoomPatternSelectEvent.patch (renamed from patches/server/0563-Added-PlayerLoomPatternSelectEvent.patch)0
-rw-r--r--patches/server/0550-Configurable-door-breaking-difficulty.patch (renamed from patches/server/0564-Configurable-door-breaking-difficulty.patch)0
-rw-r--r--patches/server/0551-Empty-commands-shall-not-be-dispatched.patch (renamed from patches/server/0565-Empty-commands-shall-not-be-dispatched.patch)0
-rw-r--r--patches/server/0552-Implement-API-to-expose-exact-interaction-point.patch (renamed from patches/server/0566-Implement-API-to-expose-exact-interaction-point.patch)0
-rw-r--r--patches/server/0553-Remove-stale-POIs.patch (renamed from patches/server/0567-Remove-stale-POIs.patch)4
-rw-r--r--patches/server/0554-Fix-villager-boat-exploit.patch (renamed from patches/server/0568-Fix-villager-boat-exploit.patch)2
-rw-r--r--patches/server/0555-Add-sendOpLevel-API.patch (renamed from patches/server/0569-Add-sendOpLevel-API.patch)6
-rw-r--r--patches/server/0556-Add-PaperRegistry.patch (renamed from patches/server/0570-Add-PaperRegistry.patch)4
-rw-r--r--patches/server/0557-Add-StructuresLocateEvent.patch (renamed from patches/server/0571-Add-StructuresLocateEvent.patch)2
-rw-r--r--patches/server/0558-Collision-option-for-requiring-a-player-participant.patch (renamed from patches/server/0572-Collision-option-for-requiring-a-player-participant.patch)4
-rw-r--r--patches/server/0559-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch (renamed from patches/server/0573-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch)0
-rw-r--r--patches/server/0560-Return-chat-component-with-empty-text-instead-of-thr.patch (renamed from patches/server/0574-Return-chat-component-with-empty-text-instead-of-thr.patch)0
-rw-r--r--patches/server/0561-Make-schedule-command-per-world.patch (renamed from patches/server/0575-Make-schedule-command-per-world.patch)0
-rw-r--r--patches/server/0562-Configurable-max-leash-distance.patch (renamed from patches/server/0576-Configurable-max-leash-distance.patch)0
-rw-r--r--patches/server/0563-Implement-BlockPreDispenseEvent.patch (renamed from patches/server/0577-Implement-BlockPreDispenseEvent.patch)0
-rw-r--r--patches/server/0564-Added-firing-of-PlayerChangeBeaconEffectEvent.patch (renamed from patches/server/0578-Added-firing-of-PlayerChangeBeaconEffectEvent.patch)0
-rw-r--r--patches/server/0565-Add-toggle-for-always-placing-the-dragon-egg.patch (renamed from patches/server/0579-Add-toggle-for-always-placing-the-dragon-egg.patch)0
-rw-r--r--patches/server/0566-Added-PlayerStonecutterRecipeSelectEvent.patch (renamed from patches/server/0580-Added-PlayerStonecutterRecipeSelectEvent.patch)0
-rw-r--r--patches/server/0567-Add-dropLeash-variable-to-EntityUnleashEvent.patch (renamed from patches/server/0581-Add-dropLeash-variable-to-EntityUnleashEvent.patch)0
-rw-r--r--patches/server/0568-Reset-shield-blocking-on-dimension-change.patch (renamed from patches/server/0582-Reset-shield-blocking-on-dimension-change.patch)4
-rw-r--r--patches/server/0569-add-DragonEggFormEvent.patch (renamed from patches/server/0583-add-DragonEggFormEvent.patch)0
-rw-r--r--patches/server/0570-EntityMoveEvent.patch (renamed from patches/server/0584-EntityMoveEvent.patch)6
-rw-r--r--patches/server/0571-added-option-to-disable-pathfinding-updates-on-block.patch (renamed from patches/server/0585-added-option-to-disable-pathfinding-updates-on-block.patch)6
-rw-r--r--patches/server/0572-Inline-shift-direction-fields.patch (renamed from patches/server/0586-Inline-shift-direction-fields.patch)0
-rw-r--r--patches/server/0573-Allow-adding-items-to-BlockDropItemEvent.patch (renamed from patches/server/0587-Allow-adding-items-to-BlockDropItemEvent.patch)0
-rw-r--r--patches/server/0574-Add-getMainThreadExecutor-to-BukkitScheduler.patch (renamed from patches/server/0588-Add-getMainThreadExecutor-to-BukkitScheduler.patch)0
-rw-r--r--patches/server/0575-living-entity-allow-attribute-registration.patch (renamed from patches/server/0589-living-entity-allow-attribute-registration.patch)0
-rw-r--r--patches/server/0576-fix-dead-slime-setSize-invincibility.patch (renamed from patches/server/0590-fix-dead-slime-setSize-invincibility.patch)0
-rw-r--r--patches/server/0577-Merchant-getRecipes-should-return-an-immutable-list.patch (renamed from patches/server/0591-Merchant-getRecipes-should-return-an-immutable-list.patch)0
-rw-r--r--patches/server/0578-Add-support-for-hex-color-codes-in-console.patch (renamed from patches/server/0592-Add-support-for-hex-color-codes-in-console.patch)4
-rw-r--r--patches/server/0579-Expose-Tracked-Players.patch (renamed from patches/server/0593-Expose-Tracked-Players.patch)0
-rw-r--r--patches/server/0580-Remove-streams-from-SensorNearest.patch (renamed from patches/server/0594-Remove-streams-from-SensorNearest.patch)0
-rw-r--r--patches/server/0581-Throw-proper-exception-on-empty-JsonList-file.patch (renamed from patches/server/0595-Throw-proper-exception-on-empty-JsonList-file.patch)0
-rw-r--r--patches/server/0582-Improve-ServerGUI.patch (renamed from patches/server/0596-Improve-ServerGUI.patch)0
-rw-r--r--patches/server/0583-stop-firing-pressure-plate-EntityInteractEvent-for-i.patch (renamed from patches/server/0597-stop-firing-pressure-plate-EntityInteractEvent-for-i.patch)0
-rw-r--r--patches/server/0584-fix-converting-txt-to-json-file.patch (renamed from patches/server/0598-fix-converting-txt-to-json-file.patch)6
-rw-r--r--patches/server/0585-Add-worldborder-events.patch (renamed from patches/server/0599-Add-worldborder-events.patch)0
-rw-r--r--patches/server/0586-added-PlayerNameEntityEvent.patch (renamed from patches/server/0600-added-PlayerNameEntityEvent.patch)0
-rw-r--r--patches/server/0587-Prevent-grindstones-from-overstacking-items.patch (renamed from patches/server/0601-Prevent-grindstones-from-overstacking-items.patch)0
-rw-r--r--patches/server/0588-Add-recipe-to-cook-events.patch (renamed from patches/server/0602-Add-recipe-to-cook-events.patch)0
-rw-r--r--patches/server/0589-Add-Block-isValidTool.patch (renamed from patches/server/0603-Add-Block-isValidTool.patch)0
-rw-r--r--patches/server/0590-Allow-using-signs-inside-spawn-protection.patch (renamed from patches/server/0604-Allow-using-signs-inside-spawn-protection.patch)0
-rw-r--r--patches/server/0591-Expand-world-key-API.patch (renamed from patches/server/0605-Expand-world-key-API.patch)2
-rw-r--r--patches/server/0592-Add-fast-alternative-constructor-for-Rotations.patch (renamed from patches/server/0606-Add-fast-alternative-constructor-for-Rotations.patch)0
-rw-r--r--patches/server/0593-Item-Rarity-API.patch (renamed from patches/server/0607-Item-Rarity-API.patch)0
-rw-r--r--patches/server/0594-Only-set-despawnTimer-for-Wandering-Traders-spawned-.patch (renamed from patches/server/0608-Only-set-despawnTimer-for-Wandering-Traders-spawned-.patch)0
-rw-r--r--patches/server/0595-copy-TESign-isEditable-from-snapshots.patch (renamed from patches/server/0609-copy-TESign-isEditable-from-snapshots.patch)0
-rw-r--r--patches/server/0596-Drop-carried-item-when-player-has-disconnected.patch (renamed from patches/server/0610-Drop-carried-item-when-player-has-disconnected.patch)2
-rw-r--r--patches/server/0597-forced-whitelist-use-configurable-kick-message.patch (renamed from patches/server/0611-forced-whitelist-use-configurable-kick-message.patch)4
-rw-r--r--patches/server/0598-Don-t-ignore-result-of-PlayerEditBookEvent.patch (renamed from patches/server/0612-Don-t-ignore-result-of-PlayerEditBookEvent.patch)0
-rw-r--r--patches/server/0599-Entity-load-save-limit-per-chunk.patch (renamed from patches/server/0613-Entity-load-save-limit-per-chunk.patch)38
-rw-r--r--patches/server/0600-Expose-protocol-version.patch (renamed from patches/server/0614-Expose-protocol-version.patch)0
-rw-r--r--patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch (renamed from patches/server/0615-Enhance-console-tab-completions-for-brigadier-comman.patch)2
-rw-r--r--patches/server/0602-Fix-PlayerItemConsumeEvent-cancelling-properly.patch (renamed from patches/server/0616-Fix-PlayerItemConsumeEvent-cancelling-properly.patch)0
-rw-r--r--patches/server/0603-Add-bypass-host-check.patch (renamed from patches/server/0617-Add-bypass-host-check.patch)0
-rw-r--r--patches/server/0604-Set-area-affect-cloud-rotation.patch (renamed from patches/server/0618-Set-area-affect-cloud-rotation.patch)0
-rw-r--r--patches/server/0605-add-isDeeplySleeping-to-HumanEntity.patch (renamed from patches/server/0619-add-isDeeplySleeping-to-HumanEntity.patch)0
-rw-r--r--patches/server/0606-add-consumeFuel-to-FurnaceBurnEvent.patch (renamed from patches/server/0620-add-consumeFuel-to-FurnaceBurnEvent.patch)0
-rw-r--r--patches/server/0607-add-get-set-drop-chance-to-EntityEquipment.patch (renamed from patches/server/0621-add-get-set-drop-chance-to-EntityEquipment.patch)0
-rw-r--r--patches/server/0608-fix-PigZombieAngerEvent-cancellation.patch (renamed from patches/server/0622-fix-PigZombieAngerEvent-cancellation.patch)0
-rw-r--r--patches/server/0609-Fix-checkReach-check-for-Shulker-boxes.patch (renamed from patches/server/0623-Fix-checkReach-check-for-Shulker-boxes.patch)0
-rw-r--r--patches/server/0610-fix-PlayerItemHeldEvent-firing-twice.patch (renamed from patches/server/0624-fix-PlayerItemHeldEvent-firing-twice.patch)0
-rw-r--r--patches/server/0611-Added-PlayerDeepSleepEvent.patch (renamed from patches/server/0625-Added-PlayerDeepSleepEvent.patch)0
-rw-r--r--patches/server/0612-More-World-API.patch (renamed from patches/server/0626-More-World-API.patch)4
-rw-r--r--patches/server/0613-Added-PlayerBedFailEnterEvent.patch (renamed from patches/server/0627-Added-PlayerBedFailEnterEvent.patch)0
-rw-r--r--patches/server/0614-Implement-methods-to-convert-between-Component-and-B.patch (renamed from patches/server/0628-Implement-methods-to-convert-between-Component-and-B.patch)2
-rw-r--r--patches/server/0615-Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch (renamed from patches/server/0629-Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch)2
-rw-r--r--patches/server/0616-Introduce-beacon-activation-deactivation-events.patch (renamed from patches/server/0630-Introduce-beacon-activation-deactivation-events.patch)0
-rw-r--r--patches/server/0617-add-RespawnFlags-to-PlayerRespawnEvent.patch (renamed from patches/server/0631-add-RespawnFlags-to-PlayerRespawnEvent.patch)2
-rw-r--r--patches/server/0618-Add-Channel-initialization-listeners.patch (renamed from patches/server/0632-Add-Channel-initialization-listeners.patch)6
-rw-r--r--patches/server/0619-Send-empty-commands-if-tab-completion-is-disabled.patch (renamed from patches/server/0633-Send-empty-commands-if-tab-completion-is-disabled.patch)0
-rw-r--r--patches/server/0620-Add-more-WanderingTrader-API.patch (renamed from patches/server/0634-Add-more-WanderingTrader-API.patch)0
-rw-r--r--patches/server/0621-Add-EntityBlockStorage-clearEntities.patch (renamed from patches/server/0635-Add-EntityBlockStorage-clearEntities.patch)0
-rw-r--r--patches/server/0622-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch (renamed from patches/server/0636-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch)0
-rw-r--r--patches/server/0623-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch (renamed from patches/server/0637-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch)0
-rw-r--r--patches/server/0624-Inventory-close.patch (renamed from patches/server/0638-Inventory-close.patch)0
-rw-r--r--patches/server/0625-call-PortalCreateEvent-players-and-end-platform.patch (renamed from patches/server/0639-call-PortalCreateEvent-players-and-end-platform.patch)4
-rw-r--r--patches/server/0626-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch (renamed from patches/server/0640-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch)0
-rw-r--r--patches/server/0627-Fix-CraftPotionBrewer-cache.patch (renamed from patches/server/0641-Fix-CraftPotionBrewer-cache.patch)0
-rw-r--r--patches/server/0628-Add-basic-Datapack-API.patch (renamed from patches/server/0642-Add-basic-Datapack-API.patch)2
-rw-r--r--patches/server/0629-Add-environment-variable-to-disable-server-gui.patch (renamed from patches/server/0643-Add-environment-variable-to-disable-server-gui.patch)0
-rw-r--r--patches/server/0630-additions-to-PlayerGameModeChangeEvent.patch (renamed from patches/server/0644-additions-to-PlayerGameModeChangeEvent.patch)12
-rw-r--r--patches/server/0631-ItemStack-repair-check-API.patch (renamed from patches/server/0645-ItemStack-repair-check-API.patch)0
-rw-r--r--patches/server/0632-More-Enchantment-API.patch (renamed from patches/server/0646-More-Enchantment-API.patch)0
-rw-r--r--patches/server/0633-Move-range-check-for-block-placing-up.patch (renamed from patches/server/0647-Move-range-check-for-block-placing-up.patch)0
-rw-r--r--patches/server/0634-Fix-and-optimise-world-force-upgrading.patch (renamed from patches/server/0648-Fix-and-optimise-world-force-upgrading.patch)8
-rw-r--r--patches/server/0635-Add-Mob-lookAt-API.patch (renamed from patches/server/0649-Add-Mob-lookAt-API.patch)0
-rw-r--r--patches/server/0636-Add-Unix-domain-socket-support.patch (renamed from patches/server/0650-Add-Unix-domain-socket-support.patch)6
-rw-r--r--patches/server/0637-Add-EntityInsideBlockEvent.patch (renamed from patches/server/0651-Add-EntityInsideBlockEvent.patch)0
-rw-r--r--patches/server/0638-Attributes-API-for-item-defaults.patch (renamed from patches/server/0652-Attributes-API-for-item-defaults.patch)0
-rw-r--r--patches/server/0639-Add-cause-to-Weather-ThunderChangeEvents.patch (renamed from patches/server/0653-Add-cause-to-Weather-ThunderChangeEvents.patch)14
-rw-r--r--patches/server/0640-More-Lidded-Block-API.patch (renamed from patches/server/0654-More-Lidded-Block-API.patch)0
-rw-r--r--patches/server/0641-Limit-item-frame-cursors-on-maps.patch (renamed from patches/server/0655-Limit-item-frame-cursors-on-maps.patch)0
-rw-r--r--patches/server/0642-Add-PlayerKickEvent-causes.patch (renamed from patches/server/0656-Add-PlayerKickEvent-causes.patch)12
-rw-r--r--patches/server/0643-Add-PufferFishStateChangeEvent.patch (renamed from patches/server/0657-Add-PufferFishStateChangeEvent.patch)0
-rw-r--r--patches/server/0644-Fix-PlayerBucketEmptyEvent-result-itemstack.patch (renamed from patches/server/0658-Fix-PlayerBucketEmptyEvent-result-itemstack.patch)0
-rw-r--r--patches/server/0645-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch (renamed from patches/server/0659-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch)2
-rw-r--r--patches/server/0646-Add-option-to-fix-items-merging-through-walls.patch (renamed from patches/server/0660-Add-option-to-fix-items-merging-through-walls.patch)0
-rw-r--r--patches/server/0647-Add-BellRevealRaiderEvent.patch (renamed from patches/server/0661-Add-BellRevealRaiderEvent.patch)0
-rw-r--r--patches/server/0648-Fix-invulnerable-end-crystals.patch (renamed from patches/server/0662-Fix-invulnerable-end-crystals.patch)0
-rw-r--r--patches/server/0649-Add-ElderGuardianAppearanceEvent.patch (renamed from patches/server/0663-Add-ElderGuardianAppearanceEvent.patch)0
-rw-r--r--patches/server/0650-Fix-dangerous-end-portal-logic.patch (renamed from patches/server/0664-Fix-dangerous-end-portal-logic.patch)6
-rw-r--r--patches/server/0651-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch (renamed from patches/server/0665-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch)0
-rw-r--r--patches/server/0652-Make-item-validations-configurable.patch (renamed from patches/server/0666-Make-item-validations-configurable.patch)0
-rw-r--r--patches/server/0653-Line-Of-Sight-Changes.patch (renamed from patches/server/0667-Line-Of-Sight-Changes.patch)0
-rw-r--r--patches/server/0654-add-per-world-spawn-limits.patch (renamed from patches/server/0668-add-per-world-spawn-limits.patch)2
-rw-r--r--patches/server/0655-Fix-PotionSplashEvent-for-water-splash-potions.patch (renamed from patches/server/0669-Fix-PotionSplashEvent-for-water-splash-potions.patch)0
-rw-r--r--patches/server/0656-Add-more-LimitedRegion-API.patch (renamed from patches/server/0670-Add-more-LimitedRegion-API.patch)0
-rw-r--r--patches/server/0657-Fix-PlayerDropItemEvent-using-wrong-item.patch (renamed from patches/server/0671-Fix-PlayerDropItemEvent-using-wrong-item.patch)4
-rw-r--r--patches/server/0658-Missing-Entity-Behavior-API.patch (renamed from patches/server/0672-Missing-Entity-Behavior-API.patch)4
-rw-r--r--patches/server/0659-Ensure-disconnect-for-book-edit-is-called-on-main.patch (renamed from patches/server/0673-Ensure-disconnect-for-book-edit-is-called-on-main.patch)0
-rw-r--r--patches/server/0660-Fix-return-value-of-Block-applyBoneMeal-always-being.patch (renamed from patches/server/0674-Fix-return-value-of-Block-applyBoneMeal-always-being.patch)0
-rw-r--r--patches/server/0661-Use-getChunkIfLoadedImmediately-in-places.patch (renamed from patches/server/0675-Use-getChunkIfLoadedImmediately-in-places.patch)6
-rw-r--r--patches/server/0662-Fix-commands-from-signs-not-firing-command-events.patch (renamed from patches/server/0676-Fix-commands-from-signs-not-firing-command-events.patch)0
-rw-r--r--patches/server/0663-Adds-PlayerArmSwingEvent.patch (renamed from patches/server/0677-Adds-PlayerArmSwingEvent.patch)0
-rw-r--r--patches/server/0664-Fixes-kick-event-leave-message-not-being-sent.patch (renamed from patches/server/0678-Fixes-kick-event-leave-message-not-being-sent.patch)6
-rw-r--r--patches/server/0665-Add-config-for-mobs-immune-to-default-effects.patch (renamed from patches/server/0679-Add-config-for-mobs-immune-to-default-effects.patch)4
-rw-r--r--patches/server/0666-Fix-incorrect-message-for-outdated-client.patch (renamed from patches/server/0680-Fix-incorrect-message-for-outdated-client.patch)0
-rw-r--r--patches/server/0667-Don-t-apply-cramming-damage-to-players.patch (renamed from patches/server/0681-Don-t-apply-cramming-damage-to-players.patch)4
-rw-r--r--patches/server/0668-Rate-options-and-timings-for-sensors-and-behaviors.patch (renamed from patches/server/0682-Rate-options-and-timings-for-sensors-and-behaviors.patch)0
-rw-r--r--patches/server/0669-Add-a-bunch-of-missing-forceDrop-toggles.patch (renamed from patches/server/0683-Add-a-bunch-of-missing-forceDrop-toggles.patch)0
-rw-r--r--patches/server/0670-Stinger-API.patch (renamed from patches/server/0684-Stinger-API.patch)0
-rw-r--r--patches/server/0671-Fix-incosistency-issue-with-empty-map-items-in-CB.patch (renamed from patches/server/0685-Fix-incosistency-issue-with-empty-map-items-in-CB.patch)0
-rw-r--r--patches/server/0672-Add-System.out-err-catcher.patch (renamed from patches/server/0686-Add-System.out-err-catcher.patch)2
-rw-r--r--patches/server/0673-Fix-test-not-bootstrapping.patch (renamed from patches/server/0687-Fix-test-not-bootstrapping.patch)0
-rw-r--r--patches/server/0674-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch (renamed from patches/server/0688-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch)0
-rw-r--r--patches/server/0675-Improve-boat-collision-performance.patch (renamed from patches/server/0689-Improve-boat-collision-performance.patch)2
-rw-r--r--patches/server/0676-Prevent-AFK-kick-while-watching-end-credits.patch (renamed from patches/server/0690-Prevent-AFK-kick-while-watching-end-credits.patch)0
-rw-r--r--patches/server/0677-Allow-skipping-writing-of-comments-to-server.propert.patch (renamed from patches/server/0691-Allow-skipping-writing-of-comments-to-server.propert.patch)0
-rw-r--r--patches/server/0678-Add-PlayerSetSpawnEvent.patch (renamed from patches/server/0692-Add-PlayerSetSpawnEvent.patch)14
-rw-r--r--patches/server/0679-Make-hoppers-respect-inventory-max-stack-size.patch (renamed from patches/server/0693-Make-hoppers-respect-inventory-max-stack-size.patch)0
-rw-r--r--patches/server/0680-Optimize-entity-tracker-passenger-checks.patch (renamed from patches/server/0694-Optimize-entity-tracker-passenger-checks.patch)0
-rw-r--r--patches/server/0681-Config-option-for-Piglins-guarding-chests.patch (renamed from patches/server/0695-Config-option-for-Piglins-guarding-chests.patch)0
-rw-r--r--patches/server/0682-Added-EntityDamageItemEvent.patch (renamed from patches/server/0696-Added-EntityDamageItemEvent.patch)0
-rw-r--r--patches/server/0683-Optimize-indirect-passenger-iteration.patch (renamed from patches/server/0697-Optimize-indirect-passenger-iteration.patch)7
-rw-r--r--patches/server/0684-Fix-block-drops-position-losing-precision-millions-o.patch (renamed from patches/server/0698-Fix-block-drops-position-losing-precision-millions-o.patch)0
-rw-r--r--patches/server/0685-Configurable-item-frame-map-cursor-update-interval.patch (renamed from patches/server/0699-Configurable-item-frame-map-cursor-update-interval.patch)0
-rw-r--r--patches/server/0686-Make-EntityUnleashEvent-cancellable.patch (renamed from patches/server/0700-Make-EntityUnleashEvent-cancellable.patch)0
-rw-r--r--patches/server/0687-Clear-bucket-NBT-after-dispense.patch (renamed from patches/server/0701-Clear-bucket-NBT-after-dispense.patch)0
-rw-r--r--patches/server/0688-Change-EnderEye-target-without-changing-other-things.patch (renamed from patches/server/0702-Change-EnderEye-target-without-changing-other-things.patch)0
-rw-r--r--patches/server/0689-Add-BlockBreakBlockEvent.patch (renamed from patches/server/0703-Add-BlockBreakBlockEvent.patch)0
-rw-r--r--patches/server/0690-Option-to-prevent-NBT-copy-in-smithing-recipes.patch (renamed from patches/server/0704-Option-to-prevent-NBT-copy-in-smithing-recipes.patch)0
-rw-r--r--patches/server/0691-More-CommandBlock-API.patch (renamed from patches/server/0705-More-CommandBlock-API.patch)0
-rw-r--r--patches/server/0692-Add-missing-team-sidebar-display-slots.patch (renamed from patches/server/0706-Add-missing-team-sidebar-display-slots.patch)0
-rw-r--r--patches/server/0693-Add-back-EntityPortalExitEvent.patch (renamed from patches/server/0707-Add-back-EntityPortalExitEvent.patch)6
-rw-r--r--patches/server/0694-Add-methods-to-find-targets-for-lightning-strikes.patch (renamed from patches/server/0708-Add-methods-to-find-targets-for-lightning-strikes.patch)10
-rw-r--r--patches/server/0695-Get-entity-default-attributes.patch (renamed from patches/server/0709-Get-entity-default-attributes.patch)0
-rw-r--r--patches/server/0696-Left-handed-API.patch (renamed from patches/server/0710-Left-handed-API.patch)0
-rw-r--r--patches/server/0697-Add-advancement-display-API.patch (renamed from patches/server/0711-Add-advancement-display-API.patch)0
-rw-r--r--patches/server/0698-Add-ItemFactory-getMonsterEgg-API.patch (renamed from patches/server/0712-Add-ItemFactory-getMonsterEgg-API.patch)0
-rw-r--r--patches/server/0699-Add-critical-damage-API.patch (renamed from patches/server/0713-Add-critical-damage-API.patch)0
-rw-r--r--patches/server/0700-Fix-issues-with-mob-conversion.patch (renamed from patches/server/0714-Fix-issues-with-mob-conversion.patch)0
-rw-r--r--patches/server/0701-Add-isCollidable-methods-to-various-places.patch (renamed from patches/server/0715-Add-isCollidable-methods-to-various-places.patch)0
-rw-r--r--patches/server/0702-Goat-ram-API.patch (renamed from patches/server/0716-Goat-ram-API.patch)0
-rw-r--r--patches/server/0703-Add-API-for-resetting-a-single-score.patch (renamed from patches/server/0717-Add-API-for-resetting-a-single-score.patch)0
-rw-r--r--patches/server/0704-Add-Raw-Byte-Entity-Serialization.patch (renamed from patches/server/0718-Add-Raw-Byte-Entity-Serialization.patch)4
-rw-r--r--patches/server/0705-Vanilla-command-permission-fixes.patch (renamed from patches/server/0719-Vanilla-command-permission-fixes.patch)0
-rw-r--r--patches/server/0706-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch (renamed from patches/server/0721-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch)8
-rw-r--r--patches/server/0707-Fix-GameProfileCache-concurrency.patch (renamed from patches/server/0723-Fix-GameProfileCache-concurrency.patch)4
-rw-r--r--patches/server/0708-Log-when-the-async-catcher-is-tripped.patch (renamed from patches/server/0727-Log-when-the-async-catcher-is-tripped.patch)4
-rw-r--r--patches/server/0709-Add-paper-mobcaps-and-paper-playermobcaps.patch (renamed from patches/server/0728-Add-paper-mobcaps-and-paper-playermobcaps.patch)10
-rw-r--r--patches/server/0710-Sanitize-ResourceLocation-error-logging.patch (renamed from patches/server/0730-Sanitize-ResourceLocation-error-logging.patch)0
-rw-r--r--patches/server/0711-Allow-controlled-flushing-for-network-manager.patch (renamed from patches/server/0731-Allow-controlled-flushing-for-network-manager.patch)20
-rw-r--r--patches/server/0712-Optimise-general-POI-access.patch (renamed from patches/server/0732-Optimise-general-POI-access.patch)19
-rw-r--r--patches/server/0713-Optimise-chunk-tick-iteration.patch (renamed from patches/server/0735-Optimise-chunk-tick-iteration.patch)42
-rw-r--r--patches/server/0714-Execute-chunk-tasks-mid-tick.patch (renamed from patches/server/0736-Execute-chunk-tasks-mid-tick.patch)28
-rw-r--r--patches/server/0715-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch (renamed from patches/server/0737-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch)10
-rw-r--r--patches/server/0716-Custom-table-implementation-for-blockstate-state-loo.patch (renamed from patches/server/0738-Custom-table-implementation-for-blockstate-state-loo.patch)0
-rw-r--r--patches/server/0717-Detail-more-information-in-watchdog-dumps.patch (renamed from patches/server/0739-Detail-more-information-in-watchdog-dumps.patch)32
-rw-r--r--patches/server/0718-Manually-inline-methods-in-BlockPosition.patch (renamed from patches/server/0740-Manually-inline-methods-in-BlockPosition.patch)2
-rw-r--r--patches/server/0719-Distance-manager-tick-timings.patch (renamed from patches/server/0741-Distance-manager-tick-timings.patch)28
-rw-r--r--patches/server/0720-Name-craft-scheduler-threads-according-to-the-plugin.patch (renamed from patches/server/0742-Name-craft-scheduler-threads-according-to-the-plugin.patch)2
-rw-r--r--patches/server/0721-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch (renamed from patches/server/0743-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch)6
-rw-r--r--patches/server/0722-Add-packet-limiter-config.patch (renamed from patches/server/0744-Add-packet-limiter-config.patch)8
-rw-r--r--patches/server/0723-Use-correct-LevelStem-registry-when-loading-default-.patch (renamed from patches/server/0745-Use-correct-LevelStem-registry-when-loading-default-.patch)2
-rw-r--r--patches/server/0724-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch (renamed from patches/server/0746-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch)2
-rw-r--r--patches/server/0725-Consolidate-flush-calls-for-entity-tracker-packets.patch (renamed from patches/server/0747-Consolidate-flush-calls-for-entity-tracker-packets.patch)6
-rw-r--r--patches/server/0726-Don-t-lookup-fluid-state-when-raytracing.patch (renamed from patches/server/0748-Don-t-lookup-fluid-state-when-raytracing.patch)2
-rw-r--r--patches/server/0727-Time-scoreboard-search.patch (renamed from patches/server/0749-Time-scoreboard-search.patch)2
-rw-r--r--patches/server/0728-Send-full-pos-packets-for-hard-colliding-entities.patch (renamed from patches/server/0750-Send-full-pos-packets-for-hard-colliding-entities.patch)0
-rw-r--r--patches/server/0729-Do-not-run-raytrace-logic-for-AIR.patch (renamed from patches/server/0751-Do-not-run-raytrace-logic-for-AIR.patch)0
-rw-r--r--patches/server/0730-Oprimise-map-impl-for-tracked-players.patch (renamed from patches/server/0752-Oprimise-map-impl-for-tracked-players.patch)12
-rw-r--r--patches/server/0731-Optimise-BlockSoil-nearby-water-lookup.patch (renamed from patches/server/0753-Optimise-BlockSoil-nearby-water-lookup.patch)0
-rw-r--r--patches/server/0732-Optimise-random-block-ticking.patch (renamed from patches/server/0755-Optimise-random-block-ticking.patch)14
-rw-r--r--patches/server/0733-Add-more-async-catchers.patch44
-rw-r--r--patches/server/0733-Optimise-non-flush-packet-sending.patch (renamed from patches/server/0756-Optimise-non-flush-packet-sending.patch)6
-rw-r--r--patches/server/0734-Optimise-nearby-player-lookups.patch (renamed from patches/server/0757-Optimise-nearby-player-lookups.patch)59
-rw-r--r--patches/server/0735-Remove-streams-for-villager-AI.patch (renamed from patches/server/0759-Remove-streams-for-villager-AI.patch)2
-rw-r--r--patches/server/0736-Use-Velocity-compression-and-cipher-natives.patch (renamed from patches/server/0761-Use-Velocity-compression-and-cipher-natives.patch)6
-rw-r--r--patches/server/0737-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch (renamed from patches/server/0762-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch)0
-rw-r--r--patches/server/0738-Async-catch-modifications-to-critical-entity-state.patch (renamed from patches/server/0764-Async-catch-modifications-to-critical-entity-state.patch)40
-rw-r--r--patches/server/0739-Fix-Bukkit-NamespacedKey-shenanigans.patch (renamed from patches/server/0765-Fix-Bukkit-NamespacedKey-shenanigans.patch)0
-rw-r--r--patches/server/0740-Fix-merchant-inventory-not-closing-on-entity-removal.patch (renamed from patches/server/0766-Fix-merchant-inventory-not-closing-on-entity-removal.patch)4
-rw-r--r--patches/server/0741-Check-requirement-before-suggesting-root-nodes.patch (renamed from patches/server/0767-Check-requirement-before-suggesting-root-nodes.patch)0
-rw-r--r--patches/server/0742-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch (renamed from patches/server/0768-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch)0
-rw-r--r--patches/server/0743-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch (renamed from patches/server/0769-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch)0
-rw-r--r--patches/server/0744-Ensure-valid-vehicle-status.patch (renamed from patches/server/0770-Ensure-valid-vehicle-status.patch)4
-rw-r--r--patches/server/0745-Prevent-softlocked-end-exit-portal-generation.patch (renamed from patches/server/0771-Prevent-softlocked-end-exit-portal-generation.patch)0
-rw-r--r--patches/server/0746-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch (renamed from patches/server/0772-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch)0
-rw-r--r--patches/server/0747-Don-t-log-debug-logging-being-disabled.patch (renamed from patches/server/0773-Don-t-log-debug-logging-being-disabled.patch)0
-rw-r--r--patches/server/0748-fix-various-menus-with-empty-level-accesses.patch (renamed from patches/server/0774-fix-various-menus-with-empty-level-accesses.patch)0
-rw-r--r--patches/server/0749-Preserve-overstacked-loot.patch (renamed from patches/server/0775-Preserve-overstacked-loot.patch)0
-rw-r--r--patches/server/0750-Update-head-rotation-in-missing-places.patch (renamed from patches/server/0776-Update-head-rotation-in-missing-places.patch)6
-rw-r--r--patches/server/0751-prevent-unintended-light-block-manipulation.patch (renamed from patches/server/0777-prevent-unintended-light-block-manipulation.patch)0
-rw-r--r--patches/server/0752-Fix-CraftCriteria-defaults-map.patch (renamed from patches/server/0778-Fix-CraftCriteria-defaults-map.patch)0
-rw-r--r--patches/server/0753-Fix-upstreams-block-state-factories.patch (renamed from patches/server/0779-Fix-upstreams-block-state-factories.patch)0
-rw-r--r--patches/server/0754-Add-config-option-for-logging-player-ip-addresses.patch (renamed from patches/server/0780-Add-config-option-for-logging-player-ip-addresses.patch)2
-rw-r--r--patches/server/0755-Configurable-feature-seeds.patch (renamed from patches/server/0781-Configurable-feature-seeds.patch)6
-rw-r--r--patches/server/0756-VanillaCommandWrapper-didnt-account-for-entity-sende.patch (renamed from patches/server/0782-VanillaCommandWrapper-didnt-account-for-entity-sende.patch)0
-rw-r--r--patches/server/0757-Add-root-admin-user-detection.patch (renamed from patches/server/0783-Add-root-admin-user-detection.patch)2
-rw-r--r--patches/server/0758-Always-allow-item-changing-in-Fireball.patch (renamed from patches/server/0784-Always-allow-item-changing-in-Fireball.patch)0
-rw-r--r--patches/server/0758-Optimise-WorldServer-notify.patch337
-rw-r--r--patches/server/0759-don-t-attempt-to-teleport-dead-entities.patch (renamed from patches/server/0785-don-t-attempt-to-teleport-dead-entities.patch)4
-rw-r--r--patches/server/0760-Prevent-excessive-velocity-through-repeated-crits.patch (renamed from patches/server/0786-Prevent-excessive-velocity-through-repeated-crits.patch)0
-rw-r--r--patches/server/0761-Remove-client-side-code-using-deprecated-for-removal.patch (renamed from patches/server/0787-Remove-client-side-code-using-deprecated-for-removal.patch)0
-rw-r--r--patches/server/0762-Always-parse-protochunk-light-sources-unless-it-is-m.patch (renamed from patches/server/0789-Always-parse-protochunk-light-sources-unless-it-is-m.patch)4
-rw-r--r--patches/server/0763-Fix-removing-recipes-from-RecipeIterator.patch (renamed from patches/server/0790-Fix-removing-recipes-from-RecipeIterator.patch)0
-rw-r--r--patches/server/0764-Prevent-sending-oversized-item-data-in-equipment-and.patch (renamed from patches/server/0791-Prevent-sending-oversized-item-data-in-equipment-and.patch)0
-rw-r--r--patches/server/0765-Hide-unnecessary-itemmeta-from-clients.patch (renamed from patches/server/0792-Hide-unnecessary-itemmeta-from-clients.patch)0
-rw-r--r--patches/server/0766-Fix-kelp-modifier-changing-growth-for-other-crops.patch (renamed from patches/server/0793-Fix-kelp-modifier-changing-growth-for-other-crops.patch)0
-rw-r--r--patches/server/0767-Prevent-ContainerOpenersCounter-openCount-from-going.patch (renamed from patches/server/0794-Prevent-ContainerOpenersCounter-openCount-from-going.patch)0
-rw-r--r--patches/server/0768-Add-PlayerItemFrameChangeEvent.patch (renamed from patches/server/0795-Add-PlayerItemFrameChangeEvent.patch)0
-rw-r--r--patches/server/0769-Add-player-health-update-API.patch (renamed from patches/server/0796-Add-player-health-update-API.patch)6
-rw-r--r--patches/server/0770-Optimize-HashMapPalette.patch (renamed from patches/server/0797-Optimize-HashMapPalette.patch)0
-rw-r--r--patches/server/0771-Allow-delegation-to-vanilla-chunk-gen.patch (renamed from patches/server/0798-Allow-delegation-to-vanilla-chunk-gen.patch)2
-rw-r--r--patches/server/0772-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch (renamed from patches/server/0799-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch)31
-rw-r--r--patches/server/0773-Optimise-collision-checking-in-player-move-packet-ha.patch (renamed from patches/server/0800-Optimise-collision-checking-in-player-move-packet-ha.patch)2
-rw-r--r--patches/server/0774-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch (renamed from patches/server/0802-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch)2
-rw-r--r--patches/server/0775-Update-Log4j.patch (renamed from patches/server/0803-Update-Log4j.patch)0
-rw-r--r--patches/server/0776-Add-more-Campfire-API.patch (renamed from patches/server/0804-Add-more-Campfire-API.patch)0
-rw-r--r--patches/server/0777-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch (renamed from patches/server/0805-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch)8
-rw-r--r--patches/server/0778-Fix-tripwire-state-inconsistency.patch (renamed from patches/server/0806-Fix-tripwire-state-inconsistency.patch)0
-rw-r--r--patches/server/0779-Fix-fluid-logging-on-Block-breakNaturally.patch (renamed from patches/server/0807-Fix-fluid-logging-on-Block-breakNaturally.patch)0
-rw-r--r--patches/server/0780-Forward-CraftEntity-in-teleport-command.patch (renamed from patches/server/0808-Forward-CraftEntity-in-teleport-command.patch)6
-rw-r--r--patches/server/0781-Improve-scoreboard-entries.patch (renamed from patches/server/0809-Improve-scoreboard-entries.patch)0
-rw-r--r--patches/server/0782-Entity-powdered-snow-API.patch (renamed from patches/server/0810-Entity-powdered-snow-API.patch)0
-rw-r--r--patches/server/0783-Add-API-for-item-entity-health.patch (renamed from patches/server/0811-Add-API-for-item-entity-health.patch)0
-rw-r--r--patches/server/0784-Fix-entity-type-tags-suggestions-in-selectors.patch (renamed from patches/server/0812-Fix-entity-type-tags-suggestions-in-selectors.patch)0
-rw-r--r--patches/server/0785-Configurable-max-block-light-for-monster-spawning.patch (renamed from patches/server/0813-Configurable-max-block-light-for-monster-spawning.patch)0
-rw-r--r--patches/server/0786-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch (renamed from patches/server/0814-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch)0
-rw-r--r--patches/server/0787-Load-effect-amplifiers-greater-than-127-correctly.patch (renamed from patches/server/0815-Load-effect-amplifiers-greater-than-127-correctly.patch)0
-rw-r--r--patches/server/0788-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch (renamed from patches/server/0816-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch)0
-rw-r--r--patches/server/0789-Fix-bees-aging-inside-hives.patch (renamed from patches/server/0817-Fix-bees-aging-inside-hives.patch)0
-rw-r--r--patches/server/0790-Bucketable-API.patch (renamed from patches/server/0818-Bucketable-API.patch)0
-rw-r--r--patches/server/0791-Check-player-world-in-endPortalSoundRadius.patch (renamed from patches/server/0819-Check-player-world-in-endPortalSoundRadius.patch)0
-rw-r--r--patches/server/0792-Validate-usernames.patch (renamed from patches/server/0820-Validate-usernames.patch)2
-rw-r--r--patches/server/0793-Fix-saving-configs-with-more-long-comments.patch (renamed from patches/server/0821-Fix-saving-configs-with-more-long-comments.patch)0
-rw-r--r--patches/server/0794-Make-water-animal-spawn-height-configurable.patch (renamed from patches/server/0822-Make-water-animal-spawn-height-configurable.patch)0
-rw-r--r--patches/server/0795-Expose-vanilla-BiomeProvider-from-WorldInfo.patch (renamed from patches/server/0823-Expose-vanilla-BiomeProvider-from-WorldInfo.patch)6
-rw-r--r--patches/server/0796-Add-config-option-for-worlds-affected-by-time-cmd.patch (renamed from patches/server/0824-Add-config-option-for-worlds-affected-by-time-cmd.patch)0
-rw-r--r--patches/server/0797-Add-new-overload-to-PersistentDataContainer-has.patch (renamed from patches/server/0825-Add-new-overload-to-PersistentDataContainer-has.patch)0
-rw-r--r--patches/server/0798-Multiple-Entries-with-Scoreboards.patch (renamed from patches/server/0826-Multiple-Entries-with-Scoreboards.patch)0
-rw-r--r--patches/server/0799-Reset-placed-block-on-exception.patch (renamed from patches/server/0827-Reset-placed-block-on-exception.patch)0
-rw-r--r--patches/server/0800-Add-configurable-height-for-slime-spawn.patch (renamed from patches/server/0828-Add-configurable-height-for-slime-spawn.patch)0
-rw-r--r--patches/server/0801-Added-getHostname-to-AsyncPlayerPreLoginEvent.patch (renamed from patches/server/0829-Added-getHostname-to-AsyncPlayerPreLoginEvent.patch)0
-rw-r--r--patches/server/0802-Fix-xp-reward-for-baby-zombies.patch (renamed from patches/server/0830-Fix-xp-reward-for-baby-zombies.patch)0
-rw-r--r--patches/server/0803-Kick-on-main-for-illegal-chat.patch (renamed from patches/server/0831-Kick-on-main-for-illegal-chat.patch)0
-rw-r--r--patches/server/0804-Multi-Block-Change-API-Implementation.patch (renamed from patches/server/0832-Multi-Block-Change-API-Implementation.patch)4
-rw-r--r--patches/server/0805-Fix-NotePlayEvent.patch (renamed from patches/server/0833-Fix-NotePlayEvent.patch)0
-rw-r--r--patches/server/0806-Freeze-Tick-Lock-API.patch (renamed from patches/server/0834-Freeze-Tick-Lock-API.patch)8
-rw-r--r--patches/server/0807-Dolphin-API.patch (renamed from patches/server/0835-Dolphin-API.patch)0
-rw-r--r--patches/server/0808-More-PotionEffectType-API.patch (renamed from patches/server/0836-More-PotionEffectType-API.patch)0
-rw-r--r--patches/server/0809-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch (renamed from patches/server/0837-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch)0
-rw-r--r--patches/server/0810-API-for-creating-command-sender-which-forwards-feedb.patch (renamed from patches/server/0838-API-for-creating-command-sender-which-forwards-feedb.patch)2
-rw-r--r--patches/server/0811-Add-config-for-stronghold-seed.patch (renamed from patches/server/0839-Add-config-for-stronghold-seed.patch)2
-rw-r--r--patches/server/0812-Implement-regenerateChunk.patch (renamed from patches/server/0840-Implement-regenerateChunk.patch)2
-rw-r--r--patches/server/0813-Fix-cancelled-powdered-snow-bucket-placement.patch (renamed from patches/server/0841-Fix-cancelled-powdered-snow-bucket-placement.patch)0
-rw-r--r--patches/server/0814-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch (renamed from patches/server/0842-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch)2
-rw-r--r--patches/server/0815-Add-GameEvent-tags.patch (renamed from patches/server/0843-Add-GameEvent-tags.patch)2
-rw-r--r--patches/server/0816-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch (renamed from patches/server/0844-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch)6
-rw-r--r--patches/server/0817-Furnace-RecipesUsed-API.patch (renamed from patches/server/0846-Furnace-RecipesUsed-API.patch)0
-rw-r--r--patches/server/0818-Configurable-sculk-sensor-listener-range.patch (renamed from patches/server/0847-Configurable-sculk-sensor-listener-range.patch)0
-rw-r--r--patches/server/0819-Add-missing-block-data-mins-and-maxes.patch (renamed from patches/server/0848-Add-missing-block-data-mins-and-maxes.patch)0
-rw-r--r--patches/server/0820-Option-to-have-default-CustomSpawners-in-custom-worl.patch (renamed from patches/server/0849-Option-to-have-default-CustomSpawners-in-custom-worl.patch)2
-rw-r--r--patches/server/0821-Put-world-into-worldlist-before-initing-the-world.patch (renamed from patches/server/0850-Put-world-into-worldlist-before-initing-the-world.patch)6
-rw-r--r--patches/server/0822-Fix-Entity-Position-Desync.patch (renamed from patches/server/0851-Fix-Entity-Position-Desync.patch)0
-rw-r--r--patches/server/0823-Custom-Potion-Mixes.patch (renamed from patches/server/0852-Custom-Potion-Mixes.patch)6
-rw-r--r--patches/server/0824-Fix-Fluid-tags-isTagged-method.patch (renamed from patches/server/0854-Fix-Fluid-tags-isTagged-method.patch)0
-rw-r--r--patches/server/0825-Force-close-world-loading-screen.patch (renamed from patches/server/0855-Force-close-world-loading-screen.patch)2
-rw-r--r--patches/server/0826-Fix-falling-block-spawn-methods.patch (renamed from patches/server/0856-Fix-falling-block-spawn-methods.patch)6
-rw-r--r--patches/server/0827-Expose-furnace-minecart-push-values.patch (renamed from patches/server/0857-Expose-furnace-minecart-push-values.patch)0
-rw-r--r--patches/server/0828-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch (renamed from patches/server/0858-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch)0
-rw-r--r--patches/server/0829-More-Projectile-API.patch (renamed from patches/server/0860-More-Projectile-API.patch)2
-rw-r--r--patches/server/0830-Fix-swamp-hut-cat-generation-deadlock.patch (renamed from patches/server/0861-Fix-swamp-hut-cat-generation-deadlock.patch)0
-rw-r--r--patches/server/0831-Don-t-allow-vehicle-movement-from-players-while-tele.patch (renamed from patches/server/0862-Don-t-allow-vehicle-movement-from-players-while-tele.patch)0
-rw-r--r--patches/server/0832-Implement-getComputedBiome-API.patch (renamed from patches/server/0863-Implement-getComputedBiome-API.patch)0
-rw-r--r--patches/server/0833-Make-some-itemstacks-nonnull.patch (renamed from patches/server/0864-Make-some-itemstacks-nonnull.patch)0
-rw-r--r--patches/server/0834-Add-debug-for-invalid-GameProfiles-on-skull-blocks-i.patch (renamed from patches/server/0865-Add-debug-for-invalid-GameProfiles-on-skull-blocks-i.patch)0
-rw-r--r--patches/server/0835-Implement-enchantWithLevels-API.patch (renamed from patches/server/0866-Implement-enchantWithLevels-API.patch)0
-rw-r--r--patches/server/0836-Fix-saving-in-unloadWorld.patch (renamed from patches/server/0867-Fix-saving-in-unloadWorld.patch)2
-rw-r--r--patches/server/0837-Buffer-OOB-setBlock-calls.patch (renamed from patches/server/0868-Buffer-OOB-setBlock-calls.patch)0
-rw-r--r--patches/server/0838-Add-TameableDeathMessageEvent.patch (renamed from patches/server/0869-Add-TameableDeathMessageEvent.patch)0
-rw-r--r--patches/server/0839-Fix-new-block-data-for-EntityChangeBlockEvent-when-s.patch (renamed from patches/server/0870-Fix-new-block-data-for-EntityChangeBlockEvent-when-s.patch)0
-rw-r--r--patches/server/0840-fix-player-loottables-running-when-mob-loot-gamerule.patch (renamed from patches/server/0871-fix-player-loottables-running-when-mob-loot-gamerule.patch)4
-rw-r--r--patches/server/0841-Ensure-entity-passenger-world-matches-ridden-entity.patch (renamed from patches/server/0872-Ensure-entity-passenger-world-matches-ridden-entity.patch)4
-rw-r--r--patches/server/0842-Guard-against-invalid-entity-positions.patch (renamed from patches/server/0873-Guard-against-invalid-entity-positions.patch)8
-rw-r--r--patches/server/0843-cache-resource-keys.patch (renamed from patches/server/0874-cache-resource-keys.patch)0
-rw-r--r--patches/server/0844-Allow-to-change-the-podium-for-the-EnderDragon.patch (renamed from patches/server/0875-Allow-to-change-the-podium-for-the-EnderDragon.patch)6
-rw-r--r--patches/server/0845-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch (renamed from patches/server/0876-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch)0
-rw-r--r--patches/server/0846-Fix-StructureGrowEvent-species-for-RED_MUSHROOM.patch (renamed from patches/server/0877-Fix-StructureGrowEvent-species-for-RED_MUSHROOM.patch)0
-rw-r--r--patches/server/0847-Prevent-tile-entity-copies-loading-chunks.patch (renamed from patches/server/0878-Prevent-tile-entity-copies-loading-chunks.patch)0
-rw-r--r--patches/server/0848-Use-username-instead-of-display-name-in-PlayerList-g.patch (renamed from patches/server/0879-Use-username-instead-of-display-name-in-PlayerList-g.patch)2
-rw-r--r--patches/server/0849-Fix-slime-spawners-not-spawning-outside-slime-chunks.patch (renamed from patches/server/0880-Fix-slime-spawners-not-spawning-outside-slime-chunks.patch)0
-rw-r--r--patches/server/0850-Pass-ServerLevel-for-gamerule-callbacks.patch (renamed from patches/server/0881-Pass-ServerLevel-for-gamerule-callbacks.patch)8
-rw-r--r--patches/server/0851-Add-pre-unbreaking-amount-to-PlayerItemDamageEvent.patch (renamed from patches/server/0882-Add-pre-unbreaking-amount-to-PlayerItemDamageEvent.patch)0
-rw-r--r--patches/server/0852-WorldCreator-keepSpawnLoaded.patch (renamed from patches/server/0883-WorldCreator-keepSpawnLoaded.patch)4
-rw-r--r--patches/server/0853-Fix-NPE-for-BlockDataMeta-getBlockData.patch (renamed from patches/server/0884-Fix-NPE-for-BlockDataMeta-getBlockData.patch)0
-rw-r--r--patches/server/0854-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch (renamed from patches/server/0885-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch)0
-rw-r--r--patches/server/0855-Add-EntityDyeEvent-and-CollarColorable-interface.patch (renamed from patches/server/0886-Add-EntityDyeEvent-and-CollarColorable-interface.patch)0
-rw-r--r--patches/server/0856-Fire-CauldronLevelChange-on-initial-fill.patch (renamed from patches/server/0887-Fire-CauldronLevelChange-on-initial-fill.patch)0
-rw-r--r--patches/server/0857-fix-powder-snow-cauldrons-not-turning-to-water.patch (renamed from patches/server/0888-fix-powder-snow-cauldrons-not-turning-to-water.patch)0
-rw-r--r--patches/server/0858-Add-PlayerStopUsingItemEvent.patch (renamed from patches/server/0889-Add-PlayerStopUsingItemEvent.patch)0
-rw-r--r--patches/server/0859-FallingBlock-auto-expire-setting.patch (renamed from patches/server/0890-FallingBlock-auto-expire-setting.patch)0
-rw-r--r--patches/server/0860-Don-t-tick-markers.patch (renamed from patches/server/0891-Don-t-tick-markers.patch)6
-rw-r--r--patches/server/0861-Do-not-accept-invalid-client-settings.patch (renamed from patches/server/0892-Do-not-accept-invalid-client-settings.patch)0
-rw-r--r--patches/server/0862-Add-support-for-Proxy-Protocol.patch (renamed from patches/server/0893-Add-support-for-Proxy-Protocol.patch)0
-rw-r--r--patches/server/0863-Fix-OfflinePlayer-getBedSpawnLocation.patch (renamed from patches/server/0894-Fix-OfflinePlayer-getBedSpawnLocation.patch)0
-rw-r--r--patches/server/0864-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch (renamed from patches/server/0895-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch)0
-rw-r--r--patches/server/0865-Sanitize-Sent-BlockEntity-NBT.patch (renamed from patches/server/0896-Sanitize-Sent-BlockEntity-NBT.patch)0
-rw-r--r--patches/server/0866-Prevent-entity-loading-causing-async-lookups.patch (renamed from patches/server/0897-Prevent-entity-loading-causing-async-lookups.patch)4
-rw-r--r--patches/server/0867-Disable-component-selector-resolving-in-books-by-def.patch (renamed from patches/server/0898-Disable-component-selector-resolving-in-books-by-def.patch)0
-rw-r--r--patches/server/0868-Throw-exception-on-world-create-while-being-ticked.patch (renamed from patches/server/0899-Throw-exception-on-world-create-while-being-ticked.patch)10
-rw-r--r--patches/server/0869-Add-Alternate-Current-redstone-implementation.patch (renamed from patches/server/0900-Add-Alternate-Current-redstone-implementation.patch)10
-rw-r--r--patches/server/0870-Dont-resent-entity-on-art-update.patch (renamed from patches/server/0901-Dont-resent-entity-on-art-update.patch)0
-rw-r--r--patches/server/0871-Add-missing-spawn-eggs.patch (renamed from patches/server/0902-Add-missing-spawn-eggs.patch)2
-rw-r--r--patches/server/0872-Add-WardenAngerChangeEvent.patch (renamed from patches/server/0903-Add-WardenAngerChangeEvent.patch)0
-rw-r--r--patches/server/0873-Add-option-for-strict-advancement-dimension-checks.patch (renamed from patches/server/0904-Add-option-for-strict-advancement-dimension-checks.patch)4
-rw-r--r--patches/server/0874-Add-missing-important-BlockStateListPopulator-method.patch (renamed from patches/server/0905-Add-missing-important-BlockStateListPopulator-method.patch)0
-rw-r--r--patches/server/0875-Nameable-Banner-API.patch (renamed from patches/server/0906-Nameable-Banner-API.patch)0
-rw-r--r--patches/server/0876-Don-t-broadcast-messages-to-command-blocks.patch (renamed from patches/server/0907-Don-t-broadcast-messages-to-command-blocks.patch)2
-rw-r--r--patches/server/0877-Prevent-empty-items-from-being-added-to-world.patch (renamed from patches/server/0908-Prevent-empty-items-from-being-added-to-world.patch)4
-rw-r--r--patches/server/0878-Fix-CCE-for-SplashPotion-and-LingeringPotion-spawnin.patch (renamed from patches/server/0909-Fix-CCE-for-SplashPotion-and-LingeringPotion-spawnin.patch)0
-rw-r--r--patches/server/0879-Don-t-print-component-in-resource-pack-rejection-mes.patch (renamed from patches/server/0910-Don-t-print-component-in-resource-pack-rejection-mes.patch)0
-rw-r--r--patches/server/0880-Add-Player-getFishHook.patch (renamed from patches/server/0911-Add-Player-getFishHook.patch)0
-rw-r--r--patches/server/0881-Do-not-sync-load-chunk-for-dynamic-game-event-listen.patch (renamed from patches/server/0912-Do-not-sync-load-chunk-for-dynamic-game-event-listen.patch)0
-rw-r--r--patches/server/0882-Add-various-missing-EntityDropItemEvent-calls.patch (renamed from patches/server/0913-Add-various-missing-EntityDropItemEvent-calls.patch)4
-rw-r--r--patches/server/0883-Add-some-minimal-debug-information-to-chat-packet-er.patch (renamed from patches/server/0914-Add-some-minimal-debug-information-to-chat-packet-er.patch)0
-rw-r--r--patches/server/0884-Fix-Bee-flower-NPE.patch (renamed from patches/server/0915-Fix-Bee-flower-NPE.patch)0
-rw-r--r--patches/server/0885-Fix-Spigot-Config-not-using-commands.spam-exclusions.patch (renamed from patches/server/0916-Fix-Spigot-Config-not-using-commands.spam-exclusions.patch)0
-rw-r--r--patches/server/0886-Add-SpawnReason-to-Tadpoles-spawned-by-Frogspawn.patch (renamed from patches/server/0917-Add-SpawnReason-to-Tadpoles-spawned-by-Frogspawn.patch)0
-rw-r--r--patches/server/0887-More-Teleport-API.patch (renamed from patches/server/0918-More-Teleport-API.patch)2
-rw-r--r--patches/server/0888-Add-EntityPortalReadyEvent.patch (renamed from patches/server/0919-Add-EntityPortalReadyEvent.patch)6
-rw-r--r--patches/server/0889-Don-t-use-level-random-in-entity-constructors.patch (renamed from patches/server/0920-Don-t-use-level-random-in-entity-constructors.patch)0
-rw-r--r--patches/server/0890-Send-block-entities-after-destroy-prediction.patch (renamed from patches/server/0921-Send-block-entities-after-destroy-prediction.patch)0
-rw-r--r--patches/server/0891-Warn-on-plugins-accessing-faraway-chunks.patch (renamed from patches/server/0922-Warn-on-plugins-accessing-faraway-chunks.patch)12
-rw-r--r--patches/server/0892-Custom-Chat-Completion-Suggestions-API.patch (renamed from patches/server/0923-Custom-Chat-Completion-Suggestions-API.patch)2
-rw-r--r--patches/server/0893-Add-missing-BlockFadeEvents.patch (renamed from patches/server/0924-Add-missing-BlockFadeEvents.patch)0
-rw-r--r--patches/server/0894-Collision-API.patch (renamed from patches/server/0925-Collision-API.patch)0
-rw-r--r--patches/server/0895-Fix-suggest-command-message-for-brigadier-syntax-exc.patch (renamed from patches/server/0926-Fix-suggest-command-message-for-brigadier-syntax-exc.patch)0
-rw-r--r--patches/server/0896-Fix-command-preprocess-cancelling-and-command-changi.patch (renamed from patches/server/0927-Fix-command-preprocess-cancelling-and-command-changi.patch)0
-rw-r--r--patches/server/0897-Remove-invalid-signature-login-stacktrace.patch (renamed from patches/server/0928-Remove-invalid-signature-login-stacktrace.patch)0
-rw-r--r--patches/server/0898-Add-async-catcher-to-PlayerConnection-internalTelepo.patch (renamed from patches/server/0929-Add-async-catcher-to-PlayerConnection-internalTelepo.patch)0
-rw-r--r--patches/server/0899-Block-Ticking-API.patch (renamed from patches/server/0930-Block-Ticking-API.patch)0
-rw-r--r--patches/server/0900-Add-Velocity-IP-Forwarding-Support.patch (renamed from patches/server/0931-Add-Velocity-IP-Forwarding-Support.patch)2
-rw-r--r--patches/server/0901-Use-thread-safe-random-in-ServerLoginPacketListenerI.patch (renamed from patches/server/0932-Use-thread-safe-random-in-ServerLoginPacketListenerI.patch)0
-rw-r--r--patches/server/0902-Add-NamespacedKey-biome-methods.patch (renamed from patches/server/0933-Add-NamespacedKey-biome-methods.patch)0
-rw-r--r--patches/server/0903-Fix-plugin-loggers-on-server-shutdown.patch (renamed from patches/server/0934-Fix-plugin-loggers-on-server-shutdown.patch)4
-rw-r--r--patches/server/0904-Workaround-for-client-lag-spikes-MC-162253.patch (renamed from patches/server/0935-Workaround-for-client-lag-spikes-MC-162253.patch)6
-rw-r--r--patches/server/0905-Stop-large-look-changes-from-crashing-the-server.patch (renamed from patches/server/0936-Stop-large-look-changes-from-crashing-the-server.patch)0
-rw-r--r--patches/server/0906-Add-custom-destroyerIdentity-to-sendBlockDamage.patch (renamed from patches/server/0937-Add-custom-destroyerIdentity-to-sendBlockDamage.patch)2
-rw-r--r--patches/server/0907-Fix-EndDragonFight-killed-statuses-should-be-false-f.patch (renamed from patches/server/0938-Fix-EndDragonFight-killed-statuses-should-be-false-f.patch)0
-rw-r--r--patches/server/0908-Fire-EntityChangeBlockEvent-in-more-places.patch (renamed from patches/server/0939-Fire-EntityChangeBlockEvent-in-more-places.patch)0
-rw-r--r--patches/server/0909-Missing-eating-regain-reason.patch (renamed from patches/server/0940-Missing-eating-regain-reason.patch)0
-rw-r--r--patches/server/0910-Missing-effect-cause.patch (renamed from patches/server/0941-Missing-effect-cause.patch)0
-rw-r--r--patches/server/0911-Added-byte-array-serialization-deserialization-for-P.patch (renamed from patches/server/0942-Added-byte-array-serialization-deserialization-for-P.patch)0
-rw-r--r--patches/server/0912-Add-a-consumer-parameter-to-ProjectileSource-launchP.patch (renamed from patches/server/0943-Add-a-consumer-parameter-to-ProjectileSource-launchP.patch)0
-rw-r--r--patches/server/0913-Call-BlockPhysicsEvent-more-often.patch (renamed from patches/server/0944-Call-BlockPhysicsEvent-more-often.patch)0
-rw-r--r--patches/server/0914-Configurable-chat-thread-limit.patch (renamed from patches/server/0945-Configurable-chat-thread-limit.patch)4
-rw-r--r--patches/server/0915-Mitigate-effects-of-WorldCreator-keepSpawnLoaded-ret.patch (renamed from patches/server/0946-Mitigate-effects-of-WorldCreator-keepSpawnLoaded-ret.patch)0
-rw-r--r--patches/server/0916-Set-position-before-player-sending-on-dimension-chan.patch (renamed from patches/server/0947-Set-position-before-player-sending-on-dimension-chan.patch)4
942 files changed, 20129 insertions, 2695 deletions
diff --git a/patches/server/0014-ChunkMapDistance-CME.patch b/patches/removed/1.19.2-legacy-chunksystem/0014-ChunkMapDistance-CME.patch
index 956878857c..956878857c 100644
--- a/patches/server/0014-ChunkMapDistance-CME.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0014-ChunkMapDistance-CME.patch
diff --git a/patches/server/0015-Do-not-copy-visible-chunks.patch b/patches/removed/1.19.2-legacy-chunksystem/0015-Do-not-copy-visible-chunks.patch
index 0f5350c531..0f5350c531 100644
--- a/patches/server/0015-Do-not-copy-visible-chunks.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0015-Do-not-copy-visible-chunks.patch
diff --git a/patches/server/0016-Chunk-debug-command.patch b/patches/removed/1.19.2-legacy-chunksystem/0016-Chunk-debug-command.patch
index 79581b2b72..79581b2b72 100644
--- a/patches/server/0016-Chunk-debug-command.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0016-Chunk-debug-command.patch
diff --git a/patches/server/0017-Make-CallbackExecutor-strict-again.patch b/patches/removed/1.19.2-legacy-chunksystem/0017-Make-CallbackExecutor-strict-again.patch
index e37cf530bb..e37cf530bb 100644
--- a/patches/server/0017-Make-CallbackExecutor-strict-again.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0017-Make-CallbackExecutor-strict-again.patch
diff --git a/patches/server/0019-Asynchronous-chunk-IO-and-loading.patch b/patches/removed/1.19.2-legacy-chunksystem/0019-Asynchronous-chunk-IO-and-loading.patch
index c3a209b034..c3a209b034 100644
--- a/patches/server/0019-Asynchronous-chunk-IO-and-loading.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0019-Asynchronous-chunk-IO-and-loading.patch
diff --git a/patches/server/0020-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch b/patches/removed/1.19.2-legacy-chunksystem/0020-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch
index ec8ccf7a1d..ec8ccf7a1d 100644
--- a/patches/server/0020-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0020-Implement-Chunk-Priority-Urgency-System-for-Chunks.patch
diff --git a/patches/server/0048-Per-Player-View-Distance-API-placeholders.patch b/patches/removed/1.19.2-legacy-chunksystem/0048-Per-Player-View-Distance-API-placeholders.patch
index 05d14292f2..05d14292f2 100644
--- a/patches/server/0048-Per-Player-View-Distance-API-placeholders.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0048-Per-Player-View-Distance-API-placeholders.patch
diff --git a/patches/server/0137-Make-targetSize-more-aggressive-in-the-chunk-unload-.patch b/patches/removed/1.19.2-legacy-chunksystem/0137-Make-targetSize-more-aggressive-in-the-chunk-unload-.patch
index 6a3c1b57fe..6a3c1b57fe 100644
--- a/patches/server/0137-Make-targetSize-more-aggressive-in-the-chunk-unload-.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0137-Make-targetSize-more-aggressive-in-the-chunk-unload-.patch
diff --git a/patches/server/0324-Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch b/patches/removed/1.19.2-legacy-chunksystem/0324-Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch
index c8325ee866..c8325ee866 100644
--- a/patches/server/0324-Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0324-Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch
diff --git a/patches/server/0355-Fix-Light-Command.patch b/patches/removed/1.19.2-legacy-chunksystem/0355-Fix-Light-Command.patch
index a411a251f1..a411a251f1 100644
--- a/patches/server/0355-Fix-Light-Command.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0355-Fix-Light-Command.patch
diff --git a/patches/server/0393-Optimise-ArraySetSorted-removeIf.patch b/patches/removed/1.19.2-legacy-chunksystem/0393-Optimise-ArraySetSorted-removeIf.patch
index eed4ea8bc2..eed4ea8bc2 100644
--- a/patches/server/0393-Optimise-ArraySetSorted-removeIf.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0393-Optimise-ArraySetSorted-removeIf.patch
diff --git a/patches/server/0397-Fix-Chunk-Post-Processing-deadlock-risk.patch b/patches/removed/1.19.2-legacy-chunksystem/0397-Fix-Chunk-Post-Processing-deadlock-risk.patch
index 13219b5e56..13219b5e56 100644
--- a/patches/server/0397-Fix-Chunk-Post-Processing-deadlock-risk.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0397-Fix-Chunk-Post-Processing-deadlock-risk.patch
diff --git a/patches/server/0429-Optimize-ServerLevels-chunk-level-checking-methods.patch b/patches/removed/1.19.2-legacy-chunksystem/0429-Optimize-ServerLevels-chunk-level-checking-methods.patch
index 2bcf0781f9..2bcf0781f9 100644
--- a/patches/server/0429-Optimize-ServerLevels-chunk-level-checking-methods.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0429-Optimize-ServerLevels-chunk-level-checking-methods.patch
diff --git a/patches/server/0482-Improve-Chunk-Status-Transition-Speed.patch b/patches/removed/1.19.2-legacy-chunksystem/0482-Improve-Chunk-Status-Transition-Speed.patch
index 352e70742e..352e70742e 100644
--- a/patches/server/0482-Improve-Chunk-Status-Transition-Speed.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0482-Improve-Chunk-Status-Transition-Speed.patch
diff --git a/patches/server/0720-Do-not-allow-the-server-to-unload-chunks-at-request-.patch b/patches/removed/1.19.2-legacy-chunksystem/0720-Do-not-allow-the-server-to-unload-chunks-at-request-.patch
index ce98eec585..ce98eec585 100644
--- a/patches/server/0720-Do-not-allow-the-server-to-unload-chunks-at-request-.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0720-Do-not-allow-the-server-to-unload-chunks-at-request-.patch
diff --git a/patches/server/0722-Correctly-handle-recursion-for-chunkholder-updates.patch b/patches/removed/1.19.2-legacy-chunksystem/0722-Correctly-handle-recursion-for-chunkholder-updates.patch
index 17b34c16ac..17b34c16ac 100644
--- a/patches/server/0722-Correctly-handle-recursion-for-chunkholder-updates.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0722-Correctly-handle-recursion-for-chunkholder-updates.patch
diff --git a/patches/server/0724-Fix-chunks-refusing-to-unload-at-low-TPS.patch b/patches/removed/1.19.2-legacy-chunksystem/0724-Fix-chunks-refusing-to-unload-at-low-TPS.patch
index 27155400d9..27155400d9 100644
--- a/patches/server/0724-Fix-chunks-refusing-to-unload-at-low-TPS.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0724-Fix-chunks-refusing-to-unload-at-low-TPS.patch
diff --git a/patches/server/0725-Do-not-allow-ticket-level-changes-while-unloading-pl.patch b/patches/removed/1.19.2-legacy-chunksystem/0725-Do-not-allow-ticket-level-changes-while-unloading-pl.patch
index 302867667a..302867667a 100644
--- a/patches/server/0725-Do-not-allow-ticket-level-changes-while-unloading-pl.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0725-Do-not-allow-ticket-level-changes-while-unloading-pl.patch
diff --git a/patches/server/0726-Do-not-allow-ticket-level-changes-when-updating-chun.patch b/patches/removed/1.19.2-legacy-chunksystem/0726-Do-not-allow-ticket-level-changes-when-updating-chun.patch
index d3e3677642..d3e3677642 100644
--- a/patches/server/0726-Do-not-allow-ticket-level-changes-when-updating-chun.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0726-Do-not-allow-ticket-level-changes-when-updating-chun.patch
diff --git a/patches/server/0729-Prevent-unload-calls-removing-tickets-for-sync-loads.patch b/patches/removed/1.19.2-legacy-chunksystem/0729-Prevent-unload-calls-removing-tickets-for-sync-loads.patch
index 516b5b8e0c..516b5b8e0c 100644
--- a/patches/server/0729-Prevent-unload-calls-removing-tickets-for-sync-loads.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0729-Prevent-unload-calls-removing-tickets-for-sync-loads.patch
diff --git a/patches/server/0734-Rewrite-entity-bounding-box-lookup-calls.patch b/patches/removed/1.19.2-legacy-chunksystem/0734-Rewrite-entity-bounding-box-lookup-calls.patch
index 5179560e78..5179560e78 100644
--- a/patches/server/0734-Rewrite-entity-bounding-box-lookup-calls.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0734-Rewrite-entity-bounding-box-lookup-calls.patch
diff --git a/patches/server/0754-Allow-removal-addition-of-entities-to-entity-ticklis.patch b/patches/removed/1.19.2-legacy-chunksystem/0754-Allow-removal-addition-of-entities-to-entity-ticklis.patch
index bf79d6270e..bf79d6270e 100644
--- a/patches/server/0754-Allow-removal-addition-of-entities-to-entity-ticklis.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0754-Allow-removal-addition-of-entities-to-entity-ticklis.patch
diff --git a/patches/server/0763-Do-not-process-entity-loads-in-CraftChunk-getEntitie.patch b/patches/removed/1.19.2-legacy-chunksystem/0763-Do-not-process-entity-loads-in-CraftChunk-getEntitie.patch
index 33087ceabe..33087ceabe 100644
--- a/patches/server/0763-Do-not-process-entity-loads-in-CraftChunk-getEntitie.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0763-Do-not-process-entity-loads-in-CraftChunk-getEntitie.patch
diff --git a/patches/server/0801-Actually-unload-POI-data.patch b/patches/removed/1.19.2-legacy-chunksystem/0801-Actually-unload-POI-data.patch
index bf5d29c068..bf5d29c068 100644
--- a/patches/server/0801-Actually-unload-POI-data.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0801-Actually-unload-POI-data.patch
diff --git a/patches/server/0845-Replace-ticket-level-propagator.patch b/patches/removed/1.19.2-legacy-chunksystem/0845-Replace-ticket-level-propagator.patch
index 8959b46f3f..8959b46f3f 100644
--- a/patches/server/0845-Replace-ticket-level-propagator.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0845-Replace-ticket-level-propagator.patch
diff --git a/patches/server/0853-Replace-player-chunk-loader-system.patch b/patches/removed/1.19.2-legacy-chunksystem/0853-Replace-player-chunk-loader-system.patch
index a6d7f180d4..a6d7f180d4 100644
--- a/patches/server/0853-Replace-player-chunk-loader-system.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0853-Replace-player-chunk-loader-system.patch
diff --git a/patches/server/0859-Fix-save-problems-on-shutdown.patch b/patches/removed/1.19.2-legacy-chunksystem/0859-Fix-save-problems-on-shutdown.patch
index b07c4721de..b07c4721de 100644
--- a/patches/server/0859-Fix-save-problems-on-shutdown.patch
+++ b/patches/removed/1.19.2-legacy-chunksystem/0859-Fix-save-problems-on-shutdown.patch
diff --git a/patches/server/0004-Paper-config-files.patch b/patches/server/0004-Paper-config-files.patch
index 40e5fd63d4..7034b9995c 100644
--- a/patches/server/0004-Paper-config-files.patch
+++ b/patches/server/0004-Paper-config-files.patch
@@ -431,14 +431,13 @@ index 0000000000000000000000000000000000000000..c2dca89291361d60cbf160cab77749cb
+}
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
new file mode 100644
-index 0000000000000000000000000000000000000000..aaecd691922a28e971b859175574c80a330edb8e
+index 0000000000000000000000000000000000000000..84785fed0d85d78c4caf8fabe35c0e89a59240d5
--- /dev/null
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
-@@ -0,0 +1,274 @@
+@@ -0,0 +1,275 @@
+package io.papermc.paper.configuration;
+
+import co.aikar.timings.MinecraftTimings;
-+import com.destroystokyo.paper.io.chunk.ChunkTaskManager;
+import io.papermc.paper.configuration.constraint.Constraint;
+import io.papermc.paper.configuration.constraint.Constraints;
+import net.kyori.adventure.text.Component;
@@ -604,15 +603,17 @@ index 0000000000000000000000000000000000000000..aaecd691922a28e971b859175574c80a
+ public boolean saveEmptyScoreboardTeams = false;
+ }
+
-+ public AsyncChunks asyncChunks;
++ public ChunkSystem chunkSystem;
+
-+ public class AsyncChunks extends ConfigurationPart.Post {
-+ public int threads = -1;
-+ public transient boolean asyncChunks = false;
++ public class ChunkSystem extends ConfigurationPart.Post {
++
++ public int ioThreads = -1;
++ public int workerThreads = -1;
++ public String genParallelism = "default";
+
+ @Override
+ public void postProcess() {
-+ ChunkTaskManager.processConfiguration(this);
++ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.init(this);
+ }
+ }
+
diff --git a/patches/server/0006-ConcurrentUtil.patch b/patches/server/0006-ConcurrentUtil.patch
index 065818eebe..c2bb4af335 100644
--- a/patches/server/0006-ConcurrentUtil.patch
+++ b/patches/server/0006-ConcurrentUtil.patch
@@ -1412,6 +1412,160 @@ index 0000000000000000000000000000000000000000..f4415f782b32fed25da98e44b172f717
+ }
+ }
+}
+diff --git a/src/main/java/ca/spottedleaf/concurrentutil/collection/SRSWLinkedQueue.java b/src/main/java/ca/spottedleaf/concurrentutil/collection/SRSWLinkedQueue.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..597659f38aa816646dcda4ca39c002b6d9f9a792
+--- /dev/null
++++ b/src/main/java/ca/spottedleaf/concurrentutil/collection/SRSWLinkedQueue.java
+@@ -0,0 +1,148 @@
++package ca.spottedleaf.concurrentutil.collection;
++
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import ca.spottedleaf.concurrentutil.util.Validate;
++import java.lang.invoke.VarHandle;
++import java.util.ConcurrentModificationException;
++
++/**
++ * Single reader thread single writer thread queue. The reader side of the queue is ordered by acquire semantics,
++ * and the writer side of the queue is ordered by release semantics.
++ */
++// TODO test
++public class SRSWLinkedQueue<E> {
++
++ // always non-null
++ protected LinkedNode<E> head;
++
++ // always non-null
++ protected LinkedNode<E> tail;
++
++ /* IMPL NOTE: Leave hashCode and equals to their defaults */
++
++ public SRSWLinkedQueue() {
++ final LinkedNode<E> dummy = new LinkedNode<>(null, null);
++ this.head = this.tail = dummy;
++ }
++
++ /**
++ * Must be the reader thread.
++ *
++ * <p>
++ * Returns, without removing, the first element of this queue.
++ * </p>
++ * @return Returns, without removing, the first element of this queue.
++ */
++ public E peekFirst() {
++ LinkedNode<E> head = this.head;
++ E ret = head.getElementPlain();
++ if (ret == null) {
++ head = head.getNextAcquire();
++ if (head == null) {
++ // empty
++ return null;
++ }
++ // update head reference for next poll() call
++ this.head = head;
++ // guaranteed to be non-null
++ ret = head.getElementPlain();
++ if (ret == null) {
++ throw new ConcurrentModificationException("Multiple reader threads");
++ }
++ }
++
++ return ret;
++ }
++
++ /**
++ * Must be the reader thread.
++ *
++ * <p>
++ * Returns and removes the first element of this queue.
++ * </p>
++ * @return Returns and removes the first element of this queue.
++ */
++ public E poll() {
++ LinkedNode<E> head = this.head;
++ E ret = head.getElementPlain();
++ if (ret == null) {
++ head = head.getNextAcquire();
++ if (head == null) {
++ // empty
++ return null;
++ }
++ // guaranteed to be non-null
++ ret = head.getElementPlain();
++ if (ret == null) {
++ throw new ConcurrentModificationException("Multiple reader threads");
++ }
++ }
++
++ head.setElementPlain(null);
++ LinkedNode<E> next = head.getNextAcquire();
++ this.head = next == null ? head : next;
++
++ return ret;
++ }
++
++ /**
++ * Must be the writer thread.
++ *
++ * <p>
++ * Adds the element to the end of the queue.
++ * </p>
++ *
++ * @throws NullPointerException If the provided element is null
++ */
++ public void addLast(final E element) {
++ Validate.notNull(element, "Provided element cannot be null");
++ final LinkedNode<E> append = new LinkedNode<>(element, null);
++
++ this.tail.setNextRelease(append);
++ this.tail = append;
++ }
++
++ protected static final class LinkedNode<E> {
++
++ protected volatile Object element;
++ protected volatile LinkedNode<E> next;
++
++ protected static final VarHandle ELEMENT_HANDLE = ConcurrentUtil.getVarHandle(LinkedNode.class, "element", Object.class);
++ protected static final VarHandle NEXT_HANDLE = ConcurrentUtil.getVarHandle(LinkedNode.class, "next", LinkedNode.class);
++
++ protected LinkedNode(final Object element, final LinkedNode<E> next) {
++ ELEMENT_HANDLE.set(this, element);
++ NEXT_HANDLE.set(this, next);
++ }
++
++ /* element */
++
++ @SuppressWarnings("unchecked")
++ protected final E getElementPlain() {
++ return (E)ELEMENT_HANDLE.get(this);
++ }
++
++ protected final void setElementPlain(final E update) {
++ ELEMENT_HANDLE.set(this, (Object)update);
++ }
++ /* next */
++
++ @SuppressWarnings("unchecked")
++ protected final LinkedNode<E> getNextPlain() {
++ return (LinkedNode<E>)NEXT_HANDLE.get(this);
++ }
++
++ @SuppressWarnings("unchecked")
++ protected final LinkedNode<E> getNextAcquire() {
++ return (LinkedNode<E>)NEXT_HANDLE.getAcquire(this);
++ }
++
++ protected final void setNextPlain(final LinkedNode<E> next) {
++ NEXT_HANDLE.set(this, next);
++ }
++
++ protected final void setNextRelease(final LinkedNode<E> next) {
++ NEXT_HANDLE.setRelease(this, next);
++ }
++ }
++}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/completable/Completable.java b/src/main/java/ca/spottedleaf/concurrentutil/completable/Completable.java
new file mode 100644
index 0000000000000000000000000000000000000000..a1ad3308f9c3545a604b635896259a1cd3382b2a
@@ -1518,7 +1672,7 @@ index 0000000000000000000000000000000000000000..a1ad3308f9c3545a604b635896259a1c
+}
diff --git a/src/main/java/ca/spottedleaf/concurrentutil/executor/BaseExecutor.java b/src/main/java/ca/spottedleaf/concurrentutil/executor/BaseExecutor.java
new file mode 100644
-index 0000000000000000000000000000000000000000..716a0fd3f558df748e355069746272facb91de22
+index 0000000000000000000000000000000000000000..8c452b0988da4725762d543f6bee09915c328ae6
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/concurrentutil/executor/BaseExecutor.java
@@ -0,0 +1,198 @@
@@ -1575,11 +1729,11 @@ index 0000000000000000000000000000000000000000..716a0fd3f558df748e355069746272fa
+ * @throws IllegalStateException If the current thread is not allowed to wait
+ */
+ public default void waitUntilAllExecuted() throws IllegalStateException {
-+ long failures = 9L; // start out at 1ms
++ long failures = 1L; // start at 0.25ms
+
+ while (!this.haveAllTasksExecuted()) {
+ Thread.yield();
-+ failures = ConcurrentUtil.linearLongBackoff(failures, 500_000L, 5_000_000L); // 500us, 5ms
++ failures = ConcurrentUtil.linearLongBackoff(failures, 250_000L, 5_000_000L); // 500us, 5ms
+ }
+ }
+
diff --git a/patches/server/0008-MC-Utils.patch b/patches/server/0008-MC-Utils.patch
index cc478ca443..41a5d247a4 100644
--- a/patches/server/0008-MC-Utils.patch
+++ b/patches/server/0008-MC-Utils.patch
@@ -856,6 +856,137 @@ index 0000000000000000000000000000000000000000..277cfd9d1e8fff5d9b5e534b75c3c516
+ return this.map.values().iterator();
+ }
+}
+diff --git a/src/main/java/com/destroystokyo/paper/util/maplist/ReferenceList.java b/src/main/java/com/destroystokyo/paper/util/maplist/ReferenceList.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..190c5f0b02a3d99054704ae1afbffb3498ddffe1
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/util/maplist/ReferenceList.java
+@@ -0,0 +1,125 @@
++package com.destroystokyo.paper.util.maplist;
++
++import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
++import java.util.Arrays;
++import java.util.Iterator;
++import java.util.NoSuchElementException;
++
++/**
++ * @author Spottedleaf
++ */
++public final class ReferenceList<E> implements Iterable<E> {
++
++ protected final Reference2IntOpenHashMap<E> referenceToIndex = new Reference2IntOpenHashMap<>(2, 0.8f);
++ {
++ this.referenceToIndex.defaultReturnValue(Integer.MIN_VALUE);
++ }
++
++ protected static final Object[] EMPTY_LIST = new Object[0];
++
++ protected Object[] references = EMPTY_LIST;
++ protected int count;
++
++ public int size() {
++ return this.count;
++ }
++
++ public boolean contains(final E obj) {
++ return this.referenceToIndex.containsKey(obj);
++ }
++
++ public boolean remove(final E obj) {
++ final int index = this.referenceToIndex.removeInt(obj);
++ if (index == Integer.MIN_VALUE) {
++ return false;
++ }
++
++ // move the object at the end to this index
++ final int endIndex = --this.count;
++ final E end = (E)this.references[endIndex];
++ if (index != endIndex) {
++ // not empty after this call
++ this.referenceToIndex.put(end, index); // update index
++ }
++ this.references[index] = end;
++ this.references[endIndex] = null;
++
++ return true;
++ }
++
++ public boolean add(final E obj) {
++ final int count = this.count;
++ final int currIndex = this.referenceToIndex.putIfAbsent(obj, count);
++
++ if (currIndex != Integer.MIN_VALUE) {
++ return false; // already in this list
++ }
++
++ Object[] list = this.references;
++
++ if (list.length == count) {
++ // resize required
++ list = this.references = Arrays.copyOf(list, (int)Math.max(4L, count * 2L)); // overflow results in negative
++ }
++
++ list[count] = obj;
++ this.count = count + 1;
++
++ return true;
++ }
++
++ public E getChecked(final int index) {
++ if (index < 0 || index >= this.count) {
++ throw new IndexOutOfBoundsException("Index: " + index + " is out of bounds, size: " + this.count);
++ }
++ return (E)this.references[index];
++ }
++
++ public E getUnchecked(final int index) {
++ return (E)this.references[index];
++ }
++
++ public Object[] getRawData() {
++ return this.references;
++ }
++
++ public void clear() {
++ this.referenceToIndex.clear();
++ Arrays.fill(this.references, 0, this.count, null);
++ this.count = 0;
++ }
++
++ @Override
++ public Iterator<E> iterator() {
++ return new Iterator<>() {
++ private E lastRet;
++ private int current;
++
++ @Override
++ public boolean hasNext() {
++ return this.current < ReferenceList.this.count;
++ }
++
++ @Override
++ public E next() {
++ if (this.current >= ReferenceList.this.count) {
++ throw new NoSuchElementException();
++ }
++ return this.lastRet = (E)ReferenceList.this.references[this.current++];
++ }
++
++ @Override
++ public void remove() {
++ final E lastRet = this.lastRet;
++
++ if (lastRet == null) {
++ throw new IllegalStateException();
++ }
++ this.lastRet = null;
++
++ ReferenceList.this.remove(lastRet);
++ --this.current;
++ }
++ };
++ }
++}
diff --git a/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java b/src/main/java/com/destroystokyo/paper/util/misc/AreaMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..c89f6986eda5a132a948732ea1b6923370685317
@@ -4530,10 +4661,10 @@ index 207f1c1fc9d4451d27047bb8362bded8cd53e32f..021a26a6b1c258deffc26c035ab52a4e
} else {
diff --git a/src/main/java/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
new file mode 100644
-index 0000000000000000000000000000000000000000..83dc09f6526206690c474b50a7a6e71cefc93ab4
+index 0000000000000000000000000000000000000000..c59fca05484c30b28e883f5b5dde0362f294b517
--- /dev/null
+++ b/src/main/java/net/minecraft/server/ChunkSystem.java
-@@ -0,0 +1,269 @@
+@@ -0,0 +1,294 @@
+package net.minecraft.server;
+
+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
@@ -4544,6 +4675,7 @@ index 0000000000000000000000000000000000000000..83dc09f6526206690c474b50a7a6e71c
+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.world.entity.Entity;
+import net.minecraft.world.level.ChunkPos;
@@ -4775,30 +4907,54 @@ index 0000000000000000000000000000000000000000..83dc09f6526206690c474b50a7a6e71c
+ }
+ }
+
-+ public static void onChunkBorder(LevelChunk chunk, ChunkHolder holder) {
++ public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
+ chunk.playerChunk = holder;
+ }
+
-+ public static void onChunkNotBorder(LevelChunk chunk, ChunkHolder holder) {
++ public static void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+ }
+
-+ public static void onChunkTicking(LevelChunk chunk, ChunkHolder holder) {
++ public static void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
+ chunk.level.getChunkSource().tickingChunks.add(chunk);
+ }
+
-+ public static void onChunkNotTicking(LevelChunk chunk, ChunkHolder holder) {
++ public static void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
+ chunk.level.getChunkSource().tickingChunks.remove(chunk);
+ }
+
-+ public static void onChunkEntityTicking(LevelChunk chunk, ChunkHolder holder) {
++ public static void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
+ chunk.level.getChunkSource().entityTickingChunks.add(chunk);
+ }
+
-+ public static void onChunkNotEntityTicking(LevelChunk chunk, ChunkHolder holder) {
++ public static void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
+ chunk.level.getChunkSource().entityTickingChunks.remove(chunk);
+ }
+
++ public static ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
++ return level.chunkSource.chunkMap.getUnloadingChunkHolder(chunkX, chunkZ);
++ }
++
++ public static int getSendViewDistance(final ServerPlayer player) {
++ return getLoadViewDistance(player);
++ }
++
++ public static int getLoadViewDistance(final ServerPlayer player) {
++ final ServerLevel level = player.getLevel();
++ if (level == null) {
++ return Bukkit.getViewDistance() + 1;
++ }
++ return level.chunkSource.chunkMap.getEffectiveViewDistance() + 1;
++ }
++
++ public static int getTickViewDistance(final ServerPlayer player) {
++ final ServerLevel level = player.getLevel();
++ if (level == null) {
++ return Bukkit.getSimulationDistance();
++ }
++ return level.chunkSource.chunkMap.distanceManager.getSimulationDistance();
++ }
++
+ private ChunkSystem() {
+ throw new RuntimeException();
+ }
@@ -5882,7 +6038,7 @@ index 91a9b9ff0d7821a2261e7137fb1b3989ba096b88..1fbe1b6de925f71763f79fe3d2371b70
@Override
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
-index 6c98676827ceb6999f340fa2b06a0b3e1cb4cae2..f08089b8672454acf8c2309e850466b335248692 100644
+index 6c98676827ceb6999f340fa2b06a0b3e1cb4cae2..fbe62a31ab199d83a1db0a4e0b1a813824e6f2c2 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -60,8 +60,9 @@ public abstract class DistanceManager {
@@ -5904,7 +6060,20 @@ index 6c98676827ceb6999f340fa2b06a0b3e1cb4cae2..f08089b8672454acf8c2309e850466b3
}
protected void purgeStaleTickets() {
-@@ -382,7 +384,7 @@ public abstract class DistanceManager {
+@@ -319,6 +321,12 @@ public abstract class DistanceManager {
+ this.playerTicketManager.updateViewDistance(viewDistance);
+ }
+
++ // Paper start
++ public int getSimulationDistance() {
++ return this.simulationDistance;
++ }
++ // Paper end
++
+ public void updateSimulationDistance(int simulationDistance) {
+ if (simulationDistance != this.simulationDistance) {
+ this.simulationDistance = simulationDistance;
+@@ -382,7 +390,7 @@ public abstract class DistanceManager {
}
public void removeTicketsOnClosing() {
@@ -6332,19 +6501,20 @@ index aa396df025115c7fd866cbc63a44c2c17abfde84..b2f79a0c9caa6783816afc36531c9437
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
// Holder holder = worlddimension.typeHolder(); // CraftBukkit - decompile error
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index e2ed77972fcec43fef5f3af044479849f78901a9..84564ca128d2dfc79c0b5a13b699cf6fc80bdea7 100644
+index e2ed77972fcec43fef5f3af044479849f78901a9..bdad7b404067ab65d85d1628db9009896a43a052 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -243,6 +243,8 @@ public class ServerPlayer extends Player {
+@@ -243,6 +243,9 @@ public class ServerPlayer extends Player {
public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent
// CraftBukkit end
++ public boolean isRealPlayer; // Paper
+ public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
+
public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, @Nullable ProfilePublicKey publicKey) {
super(world, world.getSharedSpawnPos(), world.getSharedSpawnAngle(), profile, publicKey);
this.chatVisibility = ChatVisiblity.FULL;
-@@ -306,6 +308,8 @@ public class ServerPlayer extends Player {
+@@ -306,6 +309,8 @@ public class ServerPlayer extends Player {
this.maxUpStep = 1.0F;
this.fudgeSpawnLocation(world);
@@ -6396,6 +6566,18 @@ index 96ab71f72b43758b86f8990a74a238ad68e10890..32d6e4b194c3c4eca7009059f8d18589
@Override
public BlockState getBlockState(BlockPos pos) {
return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())).getBlockState(pos);
+diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
+index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..474843e57028ade5ef36ac5cda4924dbd95f6fe4 100644
+--- a/src/main/java/net/minecraft/server/players/PlayerList.java
++++ b/src/main/java/net/minecraft/server/players/PlayerList.java
+@@ -175,6 +175,7 @@ public abstract class PlayerList {
+ }
+
+ public void placeNewPlayer(Connection connection, ServerPlayer player) {
++ player.isRealPlayer = true; // Paper
+ GameProfile gameprofile = player.getGameProfile();
+ GameProfileCache usercache = this.server.getProfileCache();
+ Optional<GameProfile> optional = usercache.get(gameprofile.getId());
diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
index 288fdbef407d11ab430d5d7026dfad148c3c1065..6fefa619299d3202158490630d62c16aef71e831 100644
--- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
@@ -6848,7 +7030,7 @@ index d484aaae8614e78fdb984b26304b1de8b649e4bd..fabc7df600c89b01d97a76eb0b1206a3
this.levelHeightAccessor = heightLimitView;
this.sections = new LevelChunkSection[heightLimitView.getSectionsCount()];
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index e518e8e417f2eee43ff0847c24b6858054e7c9a9..ab986a3d1dc2f605b5b84d2b62cd97007e3a2c22 100644
+index e518e8e417f2eee43ff0847c24b6858054e7c9a9..bcd0287d99eeba2b3534b4a298dc4b79b293ec58 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -25,6 +25,7 @@ import net.minecraft.nbt.CompoundTag;
@@ -6859,12 +7041,11 @@ index e518e8e417f2eee43ff0847c24b6858054e7c9a9..ab986a3d1dc2f605b5b84d2b62cd9700
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.entity.Entity;
-@@ -124,6 +125,110 @@ public class LevelChunk extends ChunkAccess {
+@@ -124,6 +125,109 @@ public class LevelChunk extends ChunkAccess {
// CraftBukkit end
+ // Paper start
-+ public final com.destroystokyo.paper.util.maplist.EntityList entities = new com.destroystokyo.paper.util.maplist.EntityList();
+ public @Nullable ChunkHolder playerChunk;
+
+ static final int NEIGHBOUR_CACHE_RADIUS = 3;
@@ -6970,7 +7151,7 @@ index e518e8e417f2eee43ff0847c24b6858054e7c9a9..ab986a3d1dc2f605b5b84d2b62cd9700
public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor entityLoader) {
this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), entityLoader, protoChunk.getBlendingData());
Iterator iterator = protoChunk.getBlockEntities().values().iterator();
-@@ -233,6 +338,18 @@ public class LevelChunk extends ChunkAccess {
+@@ -233,6 +337,18 @@ public class LevelChunk extends ChunkAccess {
}
}
@@ -6989,7 +7170,7 @@ index e518e8e417f2eee43ff0847c24b6858054e7c9a9..ab986a3d1dc2f605b5b84d2b62cd9700
@Override
public FluidState getFluidState(BlockPos pos) {
return this.getFluidState(pos.getX(), pos.getY(), pos.getZ());
-@@ -354,6 +471,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -354,6 +470,7 @@ public class LevelChunk extends ChunkAccess {
return this.getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK);
}
@@ -6997,7 +7178,7 @@ index e518e8e417f2eee43ff0847c24b6858054e7c9a9..ab986a3d1dc2f605b5b84d2b62cd9700
@Nullable
public BlockEntity getBlockEntity(BlockPos pos, LevelChunk.EntityCreationType creationType) {
// CraftBukkit start
-@@ -535,7 +653,25 @@ public class LevelChunk extends ChunkAccess {
+@@ -535,7 +652,25 @@ public class LevelChunk extends ChunkAccess {
// CraftBukkit start
public void loadCallback() {
@@ -7023,7 +7204,7 @@ index e518e8e417f2eee43ff0847c24b6858054e7c9a9..ab986a3d1dc2f605b5b84d2b62cd9700
if (server != null) {
/*
* If it's a new world, the first few chunks are generated inside
-@@ -574,6 +710,22 @@ public class LevelChunk extends ChunkAccess {
+@@ -574,6 +709,22 @@ public class LevelChunk extends ChunkAccess {
server.getPluginManager().callEvent(unloadEvent);
// note: saving can be prevented, but not forced if no saving is actually required
this.mustNotSave = !unloadEvent.isSaveChunk();
diff --git a/patches/server/0009-Adventure.patch b/patches/server/0009-Adventure.patch
index 5e5641e209..35a7393050 100644
--- a/patches/server/0009-Adventure.patch
+++ b/patches/server/0009-Adventure.patch
@@ -1959,7 +1959,7 @@ index 805a1773d55e2551911e5b8e69052e23f630359b..e4220f14a5ebf43dd3491fc8649c2be5
}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 84564ca128d2dfc79c0b5a13b699cf6fc80bdea7..9ab4588e4e512176b881ad4c252e400ff6ea97bd 100644
+index bdad7b404067ab65d85d1628db9009896a43a052..5aad3da061d391d1003bdcca95dd4f7e5c0e5ea8 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -154,6 +154,7 @@ import net.minecraft.world.scores.Score;
@@ -1978,7 +1978,7 @@ index 84564ca128d2dfc79c0b5a13b699cf6fc80bdea7..9ab4588e4e512176b881ad4c252e400f
public Component listName;
public org.bukkit.Location compassTarget;
public int newExp = 0;
-@@ -312,6 +314,7 @@ public class ServerPlayer extends Player {
+@@ -313,6 +315,7 @@ public class ServerPlayer extends Player {
// CraftBukkit start
this.displayName = this.getScoreboardName();
@@ -1986,7 +1986,7 @@ index 84564ca128d2dfc79c0b5a13b699cf6fc80bdea7..9ab4588e4e512176b881ad4c252e400f
this.bukkitPickUpLoot = true;
this.maxHealthCache = this.getMaxHealth();
}
-@@ -788,22 +791,17 @@ public class ServerPlayer extends Player {
+@@ -789,22 +792,17 @@ public class ServerPlayer extends Player {
String deathmessage = defaultMessage.getString();
this.keepLevel = keepInventory; // SPIGOT-2222: pre-set keepLevel
@@ -2013,7 +2013,7 @@ index 84564ca128d2dfc79c0b5a13b699cf6fc80bdea7..9ab4588e4e512176b881ad4c252e400f
this.connection.send(new ClientboundPlayerCombatKillPacket(this.getCombatTracker(), ichatbasecomponent), PacketSendListener.exceptionallySend(() -> {
boolean flag1 = true;
-@@ -1729,8 +1727,13 @@ public class ServerPlayer extends Player {
+@@ -1730,8 +1728,13 @@ public class ServerPlayer extends Player {
}
public void sendChatMessage(OutgoingPlayerChatMessage message, boolean filterMaskEnabled, ChatType.Bound params) {
@@ -2028,7 +2028,7 @@ index 84564ca128d2dfc79c0b5a13b699cf6fc80bdea7..9ab4588e4e512176b881ad4c252e400f
}
}
-@@ -1751,6 +1754,7 @@ public class ServerPlayer extends Player {
+@@ -1752,6 +1755,7 @@ public class ServerPlayer extends Player {
}
public String locale = "en_us"; // CraftBukkit - add, lowercase
@@ -2036,7 +2036,7 @@ index 84564ca128d2dfc79c0b5a13b699cf6fc80bdea7..9ab4588e4e512176b881ad4c252e400f
public void updateOptions(ServerboundClientInformationPacket packet) {
// CraftBukkit start
if (getMainArm() != packet.mainHand()) {
-@@ -1762,6 +1766,10 @@ public class ServerPlayer extends Player {
+@@ -1763,6 +1767,10 @@ public class ServerPlayer extends Player {
this.server.server.getPluginManager().callEvent(event);
}
this.locale = packet.language;
@@ -2278,7 +2278,7 @@ index 3a587073dbe5e8a599d342c5f758d842b7b6cddb..a426adfba3fccf1815177e0b8065684c
@Override
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e311706a10 100644
+index 474843e57028ade5ef36ac5cda4924dbd95f6fe4..3710f544a491a837b973daedc2dfa51357b70b56 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -8,6 +8,7 @@ import com.mojang.logging.LogUtils;
@@ -2289,7 +2289,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
import java.io.File;
import java.net.SocketAddress;
import java.nio.file.Path;
-@@ -264,7 +265,7 @@ public abstract class PlayerList {
+@@ -265,7 +266,7 @@ public abstract class PlayerList {
}
// CraftBukkit start
ichatmutablecomponent.withStyle(ChatFormatting.YELLOW);
@@ -2298,7 +2298,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
playerconnection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
this.players.add(player);
-@@ -278,19 +279,18 @@ public abstract class PlayerList {
+@@ -279,19 +280,18 @@ public abstract class PlayerList {
// Ensure that player inventory is populated with its viewer
player.containerMenu.transferTo(player.containerMenu, bukkitPlayer);
@@ -2323,7 +2323,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
}
// CraftBukkit end
-@@ -487,7 +487,7 @@ public abstract class PlayerList {
+@@ -488,7 +488,7 @@ public abstract class PlayerList {
}
@@ -2332,7 +2332,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
ServerLevel worldserver = entityplayer.getLevel();
entityplayer.awardStat(Stats.LEAVE_GAME);
-@@ -498,7 +498,7 @@ public abstract class PlayerList {
+@@ -499,7 +499,7 @@ public abstract class PlayerList {
entityplayer.closeContainer();
}
@@ -2341,7 +2341,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
this.cserver.getPluginManager().callEvent(playerQuitEvent);
entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
-@@ -551,7 +551,7 @@ public abstract class PlayerList {
+@@ -552,7 +552,7 @@ public abstract class PlayerList {
this.cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity());
// CraftBukkit end
@@ -2350,7 +2350,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
}
// CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer
-@@ -597,10 +597,10 @@ public abstract class PlayerList {
+@@ -598,10 +598,10 @@ public abstract class PlayerList {
}
// return chatmessage;
@@ -2363,7 +2363,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
} else if (this.getIpBans().isBanned(socketaddress) && !this.getIpBans().get(socketaddress).hasExpired()) {
IpBanListEntry ipbanentry = this.ipBans.get(socketaddress);
-@@ -610,17 +610,17 @@ public abstract class PlayerList {
+@@ -611,17 +611,17 @@ public abstract class PlayerList {
}
// return chatmessage;
@@ -2384,7 +2384,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
return null;
}
return entity;
-@@ -1128,7 +1128,7 @@ public abstract class PlayerList {
+@@ -1129,7 +1129,7 @@ public abstract class PlayerList {
public void removeAll() {
// CraftBukkit start - disconnect safely
for (ServerPlayer player : this.players) {
@@ -2393,7 +2393,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
}
// CraftBukkit end
-@@ -1169,14 +1169,25 @@ public abstract class PlayerList {
+@@ -1170,14 +1170,25 @@ public abstract class PlayerList {
}
public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, ChatType.Bound params) {
@@ -2421,7 +2421,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
OutgoingPlayerChatMessage outgoingplayerchatmessage = OutgoingPlayerChatMessage.create(message);
boolean flag1 = message.isFullyFiltered();
boolean flag2 = false;
-@@ -1186,7 +1197,7 @@ public abstract class PlayerList {
+@@ -1187,7 +1198,7 @@ public abstract class PlayerList {
ServerPlayer entityplayer1 = (ServerPlayer) iterator.next();
boolean flag3 = shouldSendFiltered.test(entityplayer1);
@@ -2430,7 +2430,7 @@ index 6987bee4bf2c1f3d47ffdd5329f6c0c63a2962a5..521f485366c65527ac3289dd27d8f2e3
if (sender != entityplayer1) {
flag2 |= flag1 && flag3;
}
-@@ -1213,7 +1224,7 @@ public abstract class PlayerList {
+@@ -1214,7 +1225,7 @@ public abstract class PlayerList {
}
diff --git a/patches/server/0012-Timings-v2.patch b/patches/server/0012-Timings-v2.patch
index 0d50ea9704..7d8b8c763c 100644
--- a/patches/server/0012-Timings-v2.patch
+++ b/patches/server/0012-Timings-v2.patch
@@ -1309,7 +1309,7 @@ index 93d02b5de0721e3c5903e80bbf8b3b56ec3ab45d..4e7db441f68019d6e5d3359605b76bc4
}
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 521f485366c65527ac3289dd27d8f2e311706a10..5833cc3d5014dad82607afc4d643b6bed885be64 100644
+index 3710f544a491a837b973daedc2dfa51357b70b56..e7fcb402e3d4e0707a28505a9fb6642764034e23 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1,5 +1,6 @@
@@ -1319,7 +1319,7 @@ index 521f485366c65527ac3289dd27d8f2e311706a10..5833cc3d5014dad82607afc4d643b6be
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
-@@ -1018,10 +1019,11 @@ public abstract class PlayerList {
+@@ -1019,10 +1020,11 @@ public abstract class PlayerList {
}
public void saveAll() {
@@ -1588,10 +1588,10 @@ index dec38e58e30c84887e9d29436c0f76c70c0a627d..be08224c8107aab3e9a3645a20977dd1
private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry();
public CraftPersistentDataContainer persistentDataContainer;
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index ab986a3d1dc2f605b5b84d2b62cd97007e3a2c22..58a245b2ca6e65d491694142ad04d38236b46434 100644
+index bcd0287d99eeba2b3534b4a298dc4b79b293ec58..b322d9b7bd9e107a9adf995b6c4db4ff0af05fc1 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -681,6 +681,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -680,6 +680,7 @@ public class LevelChunk extends ChunkAccess {
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(this.bukkitChunk, this.needsDecoration));
if (this.needsDecoration) {
@@ -1599,7 +1599,7 @@ index ab986a3d1dc2f605b5b84d2b62cd97007e3a2c22..58a245b2ca6e65d491694142ad04d382
this.needsDecoration = false;
java.util.Random random = new java.util.Random();
random.setSeed(this.level.getSeed());
-@@ -700,6 +701,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -699,6 +700,7 @@ public class LevelChunk extends ChunkAccess {
}
}
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(this.bukkitChunk));
diff --git a/patches/server/0760-Rewrite-dataconverter-system.patch b/patches/server/0013-Rewrite-dataconverter-system.patch
index 12653fcbc3..eb71ed8fe3 100644
--- a/patches/server/0760-Rewrite-dataconverter-system.patch
+++ b/patches/server/0013-Rewrite-dataconverter-system.patch
@@ -22656,10 +22656,10 @@ index 0000000000000000000000000000000000000000..967ad1186cbc81a76a4958ea99d4eff3
+ }
+}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-index fee9a8e74bfcc94942991b56799debf67b551f43..b230a3d475357d2ffd340f9a89934ea7227e69d0 100644
+index c56946f86565ad1ac41bb7b655c113f648d2f539..9730ee10042e02741383c8153eb3b7b7103f80e0 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-@@ -87,7 +87,7 @@ public class ChunkStorage implements AutoCloseable {
+@@ -78,7 +78,7 @@ public class ChunkStorage implements AutoCloseable {
int i = ChunkStorage.getVersion(nbttagcompound);
// CraftBukkit start
@@ -22668,16 +22668,16 @@ index fee9a8e74bfcc94942991b56799debf67b551f43..b230a3d475357d2ffd340f9a89934ea7
CompoundTag level = nbttagcompound.getCompound("Level");
if (level.getBoolean("TerrainPopulated") && !level.getBoolean("LightPopulated")) {
ServerChunkCache cps = (generatoraccess == null) ? null : ((ServerLevel) generatoraccess).getChunkSource();
-@@ -99,7 +99,7 @@ public class ChunkStorage implements AutoCloseable {
+@@ -90,7 +90,7 @@ public class ChunkStorage implements AutoCloseable {
// CraftBukkit end
if (i < 1493) {
- nbttagcompound = NbtUtils.update(this.fixerUpper, DataFixTypes.CHUNK, nbttagcompound, i, 1493);
+ ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, nbttagcompound, i, 1493); // Paper - replace chunk converter
if (nbttagcompound.getCompound("Level").getBoolean("hasLegacyStructureData")) {
- synchronized (this.persistentDataLock) { // Paper - Async chunk loading
LegacyStructureDataHandler persistentstructurelegacy = this.getLegacyStructureHandler(resourcekey, supplier);
-@@ -119,7 +119,7 @@ public class ChunkStorage implements AutoCloseable {
+
+@@ -108,7 +108,7 @@ public class ChunkStorage implements AutoCloseable {
// Spigot end
ChunkStorage.injectDatafixingContext(nbttagcompound, resourcekey, optional);
@@ -22687,10 +22687,10 @@ index fee9a8e74bfcc94942991b56799debf67b551f43..b230a3d475357d2ffd340f9a89934ea7
nbttagcompound.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-index de7afc737b1ab099edc29a4ef94baa76329c2947..2bc0384728f89b7c64a8beec78a1b77dc063d37b 100644
+index dae66dd5dbebc7fd8fc331b1f5f06ec461667830..0ede151943109e81f66875340261d77f67f63c95 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-@@ -128,7 +128,7 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
+@@ -117,7 +117,7 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
private CompoundTag upgradeChunkTag(CompoundTag chunkNbt) {
int i = getVersion(chunkNbt);
@@ -22700,10 +22700,10 @@ index de7afc737b1ab099edc29a4ef94baa76329c2947..2bc0384728f89b7c64a8beec78a1b77d
public static int getVersion(CompoundTag chunkNbt) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
-index bb59cff9ba570923a40c1612d5812a64390454ee..10e8d1e36639cca21aa451e81cdab90ba9e9a496 100644
+index 8a4750dd8f604062c4ea452f7b97b05a0c8d583a..a0b61647e5a7e5989aed52522bc9a43bc487421c 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
-@@ -148,7 +148,14 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
+@@ -142,7 +142,14 @@ public class SectionStorage<R> implements AutoCloseable {
int j = getVersion(dynamic);
int k = SharedConstants.getCurrentVersion().getWorldVersion();
boolean bl = j != k;
@@ -22733,7 +22733,7 @@ index 963ad3ce1ef83888ae1537ff01accdbb5b04ffa1..a7cba5b828a586d7435bda4d512af686
LOGGER.warn("Failed to partially datafix chunk {}", pos, var12);
return StructureCheckResult.CHUNK_LOAD_NEEDED;
diff --git a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
-index d785efd61caa2237e05d9ce3dbf84d86076ff047..601f8099f74e81c17600566b3c9b7a6dd39c9bcb 100644
+index 86fb11e9e197357871d603c4f8ce778660d507cf..bf4c895794c2bc2ad65faa128c6fa92cb0656841 100644
--- a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -93,7 +93,7 @@ public class PlayerDataStorage {
diff --git a/patches/server/0788-Rewrite-the-light-engine.patch b/patches/server/0014-Starlight.patch
index 1be70f40a4..182c80449e 100644
--- a/patches/server/0788-Rewrite-the-light-engine.patch
+++ b/patches/server/0014-Starlight.patch
@@ -1,40 +1,9 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Wed, 28 Oct 2020 16:51:55 -0700
-Subject: [PATCH] Rewrite the light engine
+Subject: [PATCH] Starlight
-The standard vanilla light engine is plagued by
-awful performance. Paper's changes to the light engine
-help a bit, however they appear to cause some lighting
-errors - most easily noticed in coral generation.
-
-The vanilla light engine's is too abstract to be modified -
-so an entirely new implementation is required to fix the
-performance and lighting errors.
-
-The new implementation is designed primarily to optimise
-light level propagations (increase and decrease). Unlike
-the vanilla light engine, this implementation tracks more
-information per queued value when performing a
-breadth first search. Vanilla just tracks coordinate, which
-means every time they handle a queued value, they must
-also determine the coordinate's target light level
-from its neighbours - very wasteful, especially considering
-these checks read neighbour block data.
-The new light engine tracks both position and target level,
-as well as whether the target block needs to be read at all
-(for checking sided propagation). So, the work done per coordinate
-is significantly reduced because no work is done for calculating
-the target level.
-In my testing, the block get calls were reduced by approximately
-an order of magnitude. However, the light read checks were only
-reduced by approximately 2x - but this is fine, light read checks
-are extremely cheap compared to block gets.
-
-Generation testing showed that the new light engine improved
-total generation (not lighting itself, but the whole generation process)
-by 2x. According to cpu time, the light engine itself spent 10x less time
-lighting chunks for generation.
+See https://github.com/PaperMC/Starlight
diff --git a/src/main/java/ca/spottedleaf/starlight/common/light/BlockStarLightEngine.java b/src/main/java/ca/spottedleaf/starlight/common/light/BlockStarLightEngine.java
new file mode 100644
@@ -4357,53 +4326,99 @@ index 0000000000000000000000000000000000000000..dd995e25ae620ae36cd5eecb2fe10ad0
+ }
+
+}
+diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
+index b3a58bf4b654e336826dc04da9e2f80ff8b9a9a7..c9a2ac696f7cefc8b0715f53db3fc541f26b62f6 100644
+--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
++++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
+@@ -1,6 +1,7 @@
+ package io.papermc.paper.command;
+
+ import io.papermc.paper.command.subcommands.EntityCommand;
++import io.papermc.paper.command.subcommands.FixLightCommand;
+ import io.papermc.paper.command.subcommands.HeapDumpCommand;
+ import io.papermc.paper.command.subcommands.ReloadCommand;
+ import io.papermc.paper.command.subcommands.VersionCommand;
+@@ -40,6 +41,7 @@ public final class PaperCommand extends Command {
+ commands.put(Set.of("entity"), new EntityCommand());
+ commands.put(Set.of("reload"), new ReloadCommand());
+ commands.put(Set.of("version"), new VersionCommand());
++ commands.put(Set.of("fixlight"), new FixLightCommand());
+
+ return commands.entrySet().stream()
+ .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
diff --git a/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
-index 190df802cb24aa360f6cf4d291e38b4b3fe4a2ac..68645bbbab9b4225048b647252d8f462028a9c84 100644
---- a/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..450bd95218852174cfbc88d4517e17daee5ffd5f
+--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
-@@ -10,6 +10,7 @@ import net.minecraft.server.level.ServerLevel;
- import net.minecraft.server.level.ServerPlayer;
- import net.minecraft.server.level.ThreadedLevelLightEngine;
- import net.minecraft.world.level.ChunkPos;
+@@ -0,0 +1,115 @@
++package io.papermc.paper.command.subcommands;
++
++import io.papermc.paper.command.PaperSubcommand;
++import java.util.ArrayDeque;
++import java.util.Deque;
++import net.minecraft.server.MCUtil;
++import net.minecraft.server.MinecraftServer;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.server.level.ServerPlayer;
++import net.minecraft.server.level.ThreadedLevelLightEngine;
++import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.chunk.ChunkAccess;
- import net.minecraft.world.level.chunk.LevelChunk;
- import org.bukkit.command.CommandSender;
- import org.bukkit.craftbukkit.entity.CraftPlayer;
-@@ -19,6 +20,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
- import org.checkerframework.framework.qual.DefaultQualifier;
-
- import static net.kyori.adventure.text.Component.text;
++import net.minecraft.world.level.chunk.LevelChunk;
++import org.bukkit.command.CommandSender;
++import org.bukkit.craftbukkit.entity.CraftPlayer;
++import org.bukkit.entity.Player;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.BLUE;
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_AQUA;
- import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
- import static net.kyori.adventure.text.format.NamedTextColor.RED;
-
-@@ -44,7 +47,7 @@ public final class FixLightCommand implements PaperSubcommand {
- sender.sendMessage(text("Radius cannot be negative!", RED));
- return;
- }
-- final int maxRadius = 5;
-+ final int maxRadius = 32; // Paper - MOOOOOORE
- radius = Math.min(maxRadius, parsed);
- if (radius != parsed) {
- post = () -> sender.sendMessage(text("Radius '" + parsed + "' was not in the required range [0, " + maxRadius + "], it was lowered to the maximum (" + maxRadius + " chunks).", RED));
-@@ -59,12 +62,67 @@ public final class FixLightCommand implements PaperSubcommand {
- ServerPlayer handle = player.getHandle();
- ServerLevel world = (ServerLevel) handle.level;
- ThreadedLevelLightEngine lightengine = world.getChunkSource().getLightEngine();
-+ // Paper start - rewrite light engine
-+ if (true) {
-+ this.starlightFixLight(handle, world, lightengine, radius, post);
++import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
++import static net.kyori.adventure.text.format.NamedTextColor.RED;
++
++@DefaultQualifier(NonNull.class)
++public final class FixLightCommand implements PaperSubcommand {
++ @Override
++ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
++ this.doFixLight(sender, args);
++ return true;
++ }
++
++ private void doFixLight(final CommandSender sender, final String[] args) {
++ if (!(sender instanceof Player)) {
++ sender.sendMessage(text("Only players can use this command", RED));
+ return;
+ }
-+ // Paper end - rewrite light engine
-
- net.minecraft.core.BlockPos center = MCUtil.toBlockPosition(player.getLocation());
- Deque<ChunkPos> queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius));
- updateLight(sender, world, lightengine, queue, post);
- }
-
-+ // Paper start - rewrite light engine
++ @Nullable Runnable post = null;
++ int radius = 2;
++ if (args.length > 0) {
++ try {
++ final int parsed = Integer.parseInt(args[0]);
++ if (parsed < 0) {
++ sender.sendMessage(text("Radius cannot be negative!", RED));
++ return;
++ }
++ final int maxRadius = 32;
++ radius = Math.min(maxRadius, parsed);
++ if (radius != parsed) {
++ post = () -> sender.sendMessage(text("Radius '" + parsed + "' was not in the required range [0, " + maxRadius + "], it was lowered to the maximum (" + maxRadius + " chunks).", RED));
++ }
++ } catch (final Exception e) {
++ sender.sendMessage(text("'" + args[0] + "' is not a valid number.", RED));
++ return;
++ }
++ }
++
++ CraftPlayer player = (CraftPlayer) sender;
++ ServerPlayer handle = player.getHandle();
++ ServerLevel world = (ServerLevel) handle.level;
++ ThreadedLevelLightEngine lightengine = world.getChunkSource().getLightEngine();
++ this.starlightFixLight(handle, world, lightengine, radius, post);
++ }
++
+ private void starlightFixLight(
+ final ServerPlayer sender,
+ final ServerLevel world,
@@ -4411,10 +4426,10 @@ index 190df802cb24aa360f6cf4d291e38b4b3fe4a2ac..68645bbbab9b4225048b647252d8f462
+ final int radius,
+ final @Nullable Runnable done
+ ) {
-+ long start = System.nanoTime();
-+ java.util.LinkedHashSet<ChunkPos> chunks = new java.util.LinkedHashSet<>(MCUtil.getSpiralOutChunks(sender.blockPosition(), radius)); // getChunkCoordinates is actually just bad mappings, this function rets position as blockpos
++ final long start = System.nanoTime();
++ final java.util.LinkedHashSet<ChunkPos> chunks = new java.util.LinkedHashSet<>(MCUtil.getSpiralOutChunks(sender.blockPosition(), radius)); // getChunkCoordinates is actually just bad mappings, this function rets position as blockpos
+
-+ int[] pending = new int[1];
++ final int[] pending = new int[1];
+ for (java.util.Iterator<ChunkPos> iterator = chunks.iterator(); iterator.hasNext(); ) {
+ final ChunkPos chunkPos = iterator.next();
+
@@ -4428,16 +4443,16 @@ index 190df802cb24aa360f6cf4d291e38b4b3fe4a2ac..68645bbbab9b4225048b647252d8f462
+ ++pending[0];
+ }
+
-+ int[] relitChunks = new int[1];
++ final int[] relitChunks = new int[1];
+ lightengine.relight(chunks,
-+ (ChunkPos chunkPos) -> {
++ (final ChunkPos chunkPos) -> {
+ ++relitChunks[0];
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
+ text("Relit chunk ", BLUE), text(chunkPos.toString()),
+ text(", progress: ", BLUE), text((int) (Math.round(100.0 * (double) (relitChunks[0]) / (double) pending[0])) + "%")
+ ));
+ },
-+ (int totalRelit) -> {
++ (final int totalRelit) -> {
+ final long end = System.nanoTime();
+ final long diff = Math.round(1.0e-6 * (end - start));
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
@@ -4447,16 +4462,13 @@ index 190df802cb24aa360f6cf4d291e38b4b3fe4a2ac..68645bbbab9b4225048b647252d8f462
+ if (done != null) {
+ done.run();
+ }
-+ });
++ }
++ );
+ sender.getBukkitEntity().sendMessage(text().color(BLUE).append(text("Relighting "), text(pending[0], DARK_AQUA), text(" chunks")));
+ }
-+ // Paper end - rewrite light engine
-+
- private void updateLight(
- final CommandSender sender,
- final ServerLevel world,
++}
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-index 09f262de1b12b09013f8277b25d13ffcf53b96d8..73712d6b9c828427d4c066c6d8672534575f3793 100644
+index 0873134f1f6de0c372ba28b89a20302c9a0115d8..86c33f029ae56fcace51b69763202be9f8bc5f44 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -55,7 +55,7 @@ public class ChunkHolder {
@@ -4469,33 +4481,33 @@ index 09f262de1b12b09013f8277b25d13ffcf53b96d8..73712d6b9c828427d4c066c6d8672534
private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory;
public int oldTicketLevel;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 69b8f5dcae4ea75ea9d63c36b3f5b4383fe232f9..fe10c770b511fa8a38ece2bf9679492a85b28eff 100644
+index 2a9e5fb8164f79b0f9c1cb5497216e51f9df3454..cbd4e749574c55c6e52f42b62dd6da8cfcca97f9 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -133,7 +133,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -128,7 +128,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final LongSet entitiesInLevel;
public final ServerLevel level;
private final ThreadedLevelLightEngine lightEngine;
- private final BlockableEventLoop<Runnable> mainThreadExecutor;
+ public final BlockableEventLoop<Runnable> mainThreadExecutor; // Paper - public
- final java.util.concurrent.Executor mainInvokingExecutor; // Paper
public ChunkGenerator generator;
private RandomState randomState;
+ public final Supplier<DimensionDataStorage> overworldDataStorage;
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
-index 537d34a0325a985948c744929b90144a66a35ee3..06e4d3a02e0d1326b7029157856476db4ef3575e 100644
+index fbe62a31ab199d83a1db0a4e0b1a813824e6f2c2..d38ad1b1eee92a6dbd2b79b4fcdb8959cdb4007d 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
-@@ -545,7 +545,7 @@ public abstract class DistanceManager {
+@@ -390,7 +390,7 @@ public abstract class DistanceManager {
}
public void removeTicketsOnClosing() {
-- ImmutableSet<TicketType<?>> immutableset = ImmutableSet.of(TicketType.UNKNOWN, TicketType.POST_TELEPORT, TicketType.LIGHT, TicketType.FUTURE_AWAIT, TicketType.ASYNC_LOAD, TicketType.REQUIRED_LOAD); // Paper - add additional tickets to preserve
-+ ImmutableSet<TicketType<?>> immutableset = ImmutableSet.of(TicketType.UNKNOWN, TicketType.POST_TELEPORT, TicketType.LIGHT, TicketType.FUTURE_AWAIT, TicketType.ASYNC_LOAD, TicketType.REQUIRED_LOAD, TicketType.CHUNK_RELIGHT, ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET); // Paper - add additional tickets to preserve
+- ImmutableSet<TicketType<?>> immutableset = ImmutableSet.of(TicketType.UNKNOWN, TicketType.POST_TELEPORT, TicketType.LIGHT, TicketType.FUTURE_AWAIT); // Paper - add additional tickets to preserve
++ ImmutableSet<TicketType<?>> immutableset = ImmutableSet.of(TicketType.UNKNOWN, TicketType.POST_TELEPORT, TicketType.LIGHT, TicketType.FUTURE_AWAIT, TicketType.CHUNK_RELIGHT, ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET); // Paper - add additional tickets to preserve
ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
while (objectiterator.hasNext()) {
diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
-index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d0b82456e 100644
+index 5b238e41ffa3e374b52ee955cb39087571c6ffc2..89f3380632b098aaf95d68a386bc7e72c8c27f5c 100644
--- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
+++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
@@ -23,6 +23,17 @@ import net.minecraft.world.level.chunk.LightChunkGetter;
@@ -4516,7 +4528,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable {
private static final Logger LOGGER = LogUtils.getLogger();
private final ProcessorMailbox<Runnable> taskMailbox;
-@@ -157,13 +168,168 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+@@ -32,13 +43,168 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
private volatile int taskPerBatch = 5;
private final AtomicBoolean scheduled = new AtomicBoolean();
@@ -4529,7 +4541,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
public ThreadedLevelLightEngine(LightChunkGetter chunkProvider, ChunkMap chunkStorage, boolean hasBlockLight, ProcessorMailbox<Runnable> processor, ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> executor) {
- super(chunkProvider, true, hasBlockLight);
+ super(chunkProvider, false, false); // Paper - destroy vanilla light engine state
- this.chunkMap = chunkStorage; this.playerChunkMap = chunkMap; // Paper
+ this.chunkMap = chunkStorage;
this.sorterMailbox = executor;
this.taskMailbox = processor;
+ // Paper start - replace light engine impl
@@ -4539,7 +4551,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
+ // Paper end - replace light engine impl
+ }
+
-+// Paper start - replace light engine impl
++ // Paper start - replace light engine impl
+ protected final ChunkAccess getChunk(final int chunkX, final int chunkZ) {
+ return ((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().getChunkAtImmediately(chunkX, chunkZ);
+ }
@@ -4664,7 +4676,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
+ @Override
+ public boolean hasLightWork() {
+ // route to new light engine
-+ return this.theLightEngine.hasUpdates() || !this.queue.isEmpty();
++ return this.theLightEngine.hasUpdates();
}
+ @Override
@@ -4686,7 +4698,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
@Override
public void close() {
}
-@@ -180,15 +346,16 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+@@ -55,15 +221,16 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
@Override
public void checkBlock(BlockPos pos) {
@@ -4709,7 +4721,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
this.addTask(pos.x, pos.z, () -> {
return 0;
}, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> {
-@@ -211,17 +378,16 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+@@ -86,17 +253,16 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
@Override
public void updateSectionStatus(SectionPos pos, boolean notReady) {
@@ -4733,7 +4745,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
this.addTask(pos.x, pos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> {
super.enableLightSources(pos, retainData);
}, () -> {
-@@ -231,6 +397,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+@@ -106,6 +272,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
@Override
public void queueSectionData(LightLayer lightType, SectionPos pos, @Nullable DataLayer nibbles, boolean nonEdge) {
@@ -4741,7 +4753,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
this.addTask(pos.x(), pos.z(), () -> {
return 0;
}, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> {
-@@ -252,6 +419,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+@@ -131,6 +298,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
@Override
public void retainData(ChunkPos pos, boolean retainData) {
@@ -4749,7 +4761,7 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
this.addTask(pos.x, pos.z, () -> {
return 0;
}, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> {
-@@ -274,6 +442,37 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+@@ -153,6 +321,37 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
}
public CompletableFuture<ChunkAccess> lightChunk(ChunkAccess chunk, boolean excludeBlocks) {
@@ -4785,61 +4797,55 @@ index 5539f2a7e069cbe98997b734f3b1cd498148f09b..b57bffce30154b196b879209c1ce559d
+ }
+ // Paper end - replace light engine impl
ChunkPos chunkPos = chunk.getPos();
- // Paper start
- //ichunkaccess.b(false); // Don't need to disable this
-@@ -316,7 +515,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+ chunk.setLightCorrect(false);
+ this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> {
+@@ -187,7 +386,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
}
public void tryScheduleUpdate() {
-- if ((!this.queue.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) { // Paper
+- if ((!this.lightTasks.isEmpty() || super.hasLightWork()) && this.scheduled.compareAndSet(false, true)) {
+ if (this.hasLightWork() && this.scheduled.compareAndSet(false, true)) { // Paper // Paper - rewrite light engine
this.taskMailbox.tell(() -> {
this.runUpdate();
this.scheduled.set(false);
-@@ -333,12 +532,12 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
- if (queue.poll(pre, post)) {
- pre.forEach(Runnable::run);
- pre.clear();
-- super.runUpdates(Integer.MAX_VALUE, true, true);
-+ this.theLightEngine.propagateChanges(); // Paper - rewrite light engine
- post.forEach(Runnable::run);
- post.clear();
- } else {
- // might have level updates to go still
-- super.runUpdates(Integer.MAX_VALUE, true, true);
-+ this.theLightEngine.propagateChanges(); // Paper - rewrite light engine
+@@ -209,7 +408,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
}
- // Paper end
- }
+
+ objectListIterator.back(j);
+- super.runUpdates(Integer.MAX_VALUE, true, true);
++ this.theLightEngine.propagateChanges(); // Paper - rewrite light engine
+
+ for(int var5 = 0; objectListIterator.hasNext() && var5 < i; ++var5) {
+ Pair<ThreadedLevelLightEngine.TaskType, Runnable> pair2 = objectListIterator.next();
diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java
-index 41ddcf6775f99c56cf4b13b284420061e5dd6bdc..ae46429264e6a7e5c88b6b6a41a6df4db7b3e70d 100644
+index 0d536d72ac918fbd403397ff369d10143ee9c204..6051e5f272838ef23276a90e21c2fc821ca155d1 100644
--- a/src/main/java/net/minecraft/server/level/TicketType.java
+++ b/src/main/java/net/minecraft/server/level/TicketType.java
-@@ -32,6 +32,7 @@ public class TicketType<T> {
+@@ -26,6 +26,7 @@ public class TicketType<T> {
+ public static final TicketType<ChunkPos> UNKNOWN = TicketType.create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1);
+ public static final TicketType<Unit> PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit
public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
- public static final TicketType<Long> DELAY_UNLOAD = create("delay_unload", Long::compareTo, 300); // Paper
- public static final TicketType<Long> REQUIRED_LOAD = create("required_load", Long::compareTo); // Paper - make sure getChunkAt does not fail
+ public static final TicketType<Long> CHUNK_RELIGHT = create("light_update", Long::compareTo); // Paper - ensure chunks stay loaded for lighting
public static <T> TicketType<T> create(String name, Comparator<T> argumentComparator) {
return new TicketType<>(name, argumentComparator, 0L);
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-index 178d9ad7525b6743038ed45c6f85686a860ffd26..24b820484497714eb8be87e07ca1d37829d4f2c9 100644
+index f0bd06ab32e99c188510b3c3fa41f1737ab4fe78..51ac731cf49e6d2cd574e48f26c4b151e9014826 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-@@ -701,6 +701,7 @@ public abstract class BlockBehaviour {
+@@ -694,6 +694,7 @@ public abstract class BlockBehaviour {
this.hasPostProcess = blockbase_info.hasPostProcess;
this.emissiveRendering = blockbase_info.emissiveRendering;
this.offsetType = (BlockBehaviour.OffsetType) blockbase_info.offsetType.apply(this.asState());
+ this.conditionallyFullOpaque = this.isOpaque() & this.isTransparentOnSomeFaces(); // Paper
}
- // Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time
- private org.bukkit.craftbukkit.block.data.CraftBlockData cachedCraftBlockData;
-@@ -721,6 +722,18 @@ public abstract class BlockBehaviour {
- protected boolean isTicking;
- protected FluidState fluid;
+
+ // Paper start
+@@ -702,12 +703,25 @@ public abstract class BlockBehaviour {
+ return this.shapeExceedsCube;
+ }
// Paper end
-+ // Paper start
++ // Paper start - starlight
+ protected int opacityIfCached = -1;
+ // ret -1 if opacity is dynamic, or -1 if the block is conditionally full opaque, else return opacity in [0, 15]
+ public final int getOpacityIfCached() {
@@ -4850,20 +4856,19 @@ index 178d9ad7525b6743038ed45c6f85686a860ffd26..24b820484497714eb8be87e07ca1d378
+ public final boolean isConditionallyFullOpaque() {
+ return this.conditionallyFullOpaque;
+ }
-+ // Paper end
++ // Paper end - starlight
public void initCache() {
- this.fluid = this.getBlock().getFluidState(this.asState()); // Paper - moved from getFluid()
-@@ -729,6 +742,7 @@ public abstract class BlockBehaviour {
+ if (!this.getBlock().hasDynamicShape()) {
this.cache = new BlockBehaviour.BlockStateBase.Cache(this.asState());
}
this.shapeExceedsCube = this.cache == null || this.cache.largeCollisionShape; // Paper - moved from actual method to here
-+ this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - cache opacity for light
++ this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - starlight - cache opacity for light
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-index a97909e77b9b28aede8c8716831c3f9a90618f09..b68625ebb32b8d1e5bc232d7cc791edbed923378 100644
+index fabc7df600c89b01d97a76eb0b1206a32407b906..0e787d877901dfcea714b0e14e9fc4358ee30bbe 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -81,6 +81,47 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
@@ -4914,14 +4919,37 @@ index a97909e77b9b28aede8c8716831c3f9a90618f09..b68625ebb32b8d1e5bc232d7cc791edb
public ChunkAccess(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor heightLimitView, Registry<Biome> biome, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable BlendingData blendingData) {
this.locX = pos.x; this.locZ = pos.z; // Paper - reduce need for field lookups
+diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
+index 441d46635caedfae3cb2f46d30b8d9ae95636e7b..e6240f891e396d91e31b02fdf3084be77e9d6697 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
+@@ -292,6 +292,17 @@ public class ChunkStatus {
+ return this.chunkType;
+ }
+
++ // Paper start
++ public static ChunkStatus getStatus(String name) {
++ try {
++ // We need this otherwise we return EMPTY for invalid names
++ ResourceLocation key = new ResourceLocation(name);
++ return Registry.CHUNK_STATUS.getOptional(key).orElse(null);
++ } catch (Exception ex) {
++ return null; // invalid name
++ }
++ }
++ // Paper end
+ public static ChunkStatus byName(String id) {
+ return (ChunkStatus) Registry.CHUNK_STATUS.get(ResourceLocation.tryParse(id));
+ }
diff --git a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
-index a9c65c8d36e5c7080133706df1363b3ce52e3370..d1b175f2bb1bc96e4f044a97b14721feb44d78f5 100644
+index 80e383e9a2d12f9f1b0b0d9ae71a0add9b51c9d4..a78bf00d4559dd99869d93ec78b3525d24331925 100644
--- a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
-@@ -21,6 +21,38 @@ public class EmptyLevelChunk extends LevelChunk {
+@@ -21,6 +21,40 @@ public class EmptyLevelChunk extends LevelChunk {
this.biome = holder;
}
++ // Paper start - starlight
+ @Override
+ public ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] getBlockNibbles() {
+ return ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(this.getLevel());
@@ -4953,12 +4981,13 @@ index a9c65c8d36e5c7080133706df1363b3ce52e3370..d1b175f2bb1bc96e4f044a97b14721fe
+
+ @Override
+ public void setBlockEmptinessMap(final boolean[] emptinessMap) {}
++ // Paper end - starlight
+
- // Paper start
@Override
- public BlockState getBlockState(int x, int y, int z) {
+ public BlockState getBlockState(BlockPos pos) {
+ return Blocks.VOID_AIR.defaultBlockState();
diff --git a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
-index 7b320357973202423c29743d922b72dc4ec11efe..8ffc206a858864d277ff94de7c66ffdb07d8f491 100644
+index 3dff0f7c3ccd04a67b2153e402d801de2341e520..ac5dff35e2df23b8790bbe65c40acc6a3c77e6ac 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
@@ -31,6 +31,48 @@ public class ImposterProtoChunk extends ProtoChunk {
@@ -5011,13 +5040,13 @@ index 7b320357973202423c29743d922b72dc4ec11efe..8ffc206a858864d277ff94de7c66ffdb
super(wrapped.getPos(), UpgradeData.EMPTY, wrapped.levelHeightAccessor, wrapped.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), wrapped.getBlendingData());
this.wrapped = wrapped;
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index c85380c3bf3bf4448a28a91af78f41c235a583e4..d870cefbe5b7485f423817f4f639e3e2a304640c 100644
+index b322d9b7bd9e107a9adf995b6c4db4ff0af05fc1..e75ec8f6aa597b5f3048d6269fba45eef057bc71 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -100,6 +100,10 @@ public class LevelChunk extends ChunkAccess {
+@@ -93,6 +93,10 @@ public class LevelChunk extends ChunkAccess {
public LevelChunk(Level world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks<Block> blockTickScheduler, LevelChunkTicks<Fluid> fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable LevelChunk.PostLoadProcessor entityLoader, @Nullable BlendingData blendingData) {
- super(pos, upgradeData, world, net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), inhabitedTime, sectionArrayInitializer, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry
+ super(pos, upgradeData, world, world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), inhabitedTime, sectionArrayInitializer, blendingData);
+ // Paper start - rewrite light engine
+ this.setBlockNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world));
+ this.setSkyNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world));
@@ -5025,7 +5054,7 @@ index c85380c3bf3bf4448a28a91af78f41c235a583e4..d870cefbe5b7485f423817f4f639e3e2
this.tickersInLevel = Maps.newHashMap();
this.clientLightReady = false;
this.level = (ServerLevel) world; // CraftBukkit - type
-@@ -330,6 +334,12 @@ public class LevelChunk extends ChunkAccess {
+@@ -230,6 +234,12 @@ public class LevelChunk extends ChunkAccess {
public LevelChunk(ServerLevel world, ProtoChunk protoChunk, @Nullable LevelChunk.PostLoadProcessor entityLoader) {
this(world, protoChunk.getPos(), protoChunk.getUpgradeData(), protoChunk.unpackBlockTicks(), protoChunk.unpackFluidTicks(), protoChunk.getInhabitedTime(), protoChunk.getSections(), entityLoader, protoChunk.getBlendingData());
@@ -5039,10 +5068,10 @@ index c85380c3bf3bf4448a28a91af78f41c235a583e4..d870cefbe5b7485f423817f4f639e3e2
while (iterator.hasNext()) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-index 5ebde3a4f99b8d017d9a10a30fefc0b7dd011319..7908360dd47937b2cb702e381802b7b278a5198e 100644
+index 78e20871e4bd8d92c4475f797a55733c68f6aeb4..33eecdac9d844af2f70aad97c4788b138dab8896 100644
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-@@ -203,7 +203,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
+@@ -142,7 +142,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
return this.get(this.strategy.getIndex(x, y, z));
}
@@ -5052,7 +5081,7 @@ index 5ebde3a4f99b8d017d9a10a30fefc0b7dd011319..7908360dd47937b2cb702e381802b7b2
return data.palette.valueFor(data.storage.get(index));
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
-index 9014331e4ceac9f77a911aead87bf452d29e3fb4..13b62e8e6569c154547bc0d5626488c5b0839f20 100644
+index 603111a52346f678aba0fd66b010d8f3026fce40..040c6092ceed4c693a7a056c0d1a49d3d2242b19 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
@@ -55,6 +55,12 @@ public class ProtoChunk extends ChunkAccess {
@@ -5069,10 +5098,10 @@ index 9014331e4ceac9f77a911aead87bf452d29e3fb4..13b62e8e6569c154547bc0d5626488c5
this.fluidTicks = fluidTickScheduler;
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096643a2ab8 100644
+index 864e2e0355a5fb8c1d4a5b0896ba299faf9ea534..2dead743775df9b261bdcdca30df9b672c6acc8b 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -94,6 +94,14 @@ public class ChunkSerializer {
+@@ -82,6 +82,14 @@ public class ChunkSerializer {
public static final String BLOCK_LIGHT_TAG = "BlockLight";
public static final String SKY_LIGHT_TAG = "SkyLight";
@@ -5086,13 +5115,13 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
+
public ChunkSerializer() {}
- // Paper start - guard against serializing mismatching coordinates
-@@ -153,13 +161,20 @@ public class ChunkSerializer {
+ public static ProtoChunk read(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt) {
+@@ -92,13 +100,20 @@ public class ChunkSerializer {
}
UpgradeData chunkconverter = nbt.contains("UpgradeData", 10) ? new UpgradeData(nbt.getCompound("UpgradeData"), world) : UpgradeData.EMPTY;
- boolean flag = nbt.getBoolean("isLightOn");
-+ boolean flag = getStatus(nbt).isOrAfter(ChunkStatus.LIGHT) && nbt.get("isLightOn") != null && nbt.getInt(STARLIGHT_VERSION_TAG) == STARLIGHT_LIGHT_VERSION; // Paper
++ boolean flag = getStatus(nbt) != null && getStatus(nbt).isOrAfter(ChunkStatus.LIGHT) && nbt.get("isLightOn") != null && nbt.getInt(STARLIGHT_VERSION_TAG) == STARLIGHT_LIGHT_VERSION; // Paper
ListTag nbttaglist = nbt.getList("sections", 10);
int i = world.getSectionsCount();
LevelChunkSection[] achunksection = new LevelChunkSection[i];
@@ -5100,8 +5129,8 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
ServerChunkCache chunkproviderserver = world.getChunkSource();
LevelLightEngine lightengine = chunkproviderserver.getLightEngine();
+ // Paper start
-+ ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] blockNibbles = ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world); // Paper - replace light impl
-+ ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] skyNibbles = ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world); // Paper - replace light impl
++ ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] blockNibbles = ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world);
++ ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] skyNibbles = ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world);
+ final int minSection = io.papermc.paper.util.WorldUtil.getMinLightSection(world);
+ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxLightSection(world);
+ boolean canReadSky = world.dimensionType().hasSkyLight();
@@ -5109,7 +5138,7 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
Registry<Biome> iregistry = world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
Codec<PalettedContainer<Holder<Biome>>> codec = ChunkSerializer.makeBiomeCodecRW(iregistry); // CraftBukkit - read/write
boolean flag2 = false;
-@@ -167,7 +182,7 @@ public class ChunkSerializer {
+@@ -106,7 +121,7 @@ public class ChunkSerializer {
DataResult dataresult;
for (int j = 0; j < nbttaglist.size(); ++j) {
@@ -5118,46 +5147,33 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
byte b0 = nbttagcompound1.getByte("Y");
int k = world.getSectionIndexFromSectionY(b0);
-@@ -214,31 +229,45 @@ public class ChunkSerializer {
+@@ -147,19 +162,39 @@ public class ChunkSerializer {
boolean flag3 = nbttagcompound1.contains("BlockLight", 7);
boolean flag4 = flag1 && nbttagcompound1.contains("SkyLight", 7);
- if (flag3 || flag4) {
- if (!flag2) {
+- lightengine.retainData(chunkPos, true);
+- flag2 = true;
+- }
+-
+ // Paper start - rewrite the light engine
+ if (flag) {
+ try {
-+ if ((flag3 || flag4) && !flag2) {
-+ // Paper end - rewrite the light engine
- tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main
- lightengine.retainData(chunkPos, true);
- }); // Paper - delay this task since we're executing off-main
- flag2 = true;
- }
-
+ int y = sectionData.getByte("Y");
++ // Paper end - rewrite the light engine
if (flag3) {
-- // Paper start - delay this task since we're executing off-main
-- DataLayer blockLight = new DataLayer(nbttagcompound1.getByteArray("BlockLight").clone());
-- tasksToExecuteOnMain.add(() -> {
-- lightengine.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkPos, b0), blockLight, true);
-- });
-- // Paper end - delay this task since we're executing off-main
+- lightengine.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkPos, b0), new DataLayer(nbttagcompound1.getByteArray("BlockLight")), true);
+ // Paper start - rewrite the light engine
+ // this is where our diff is
+ blockNibbles[y - minSection] = new ca.spottedleaf.starlight.common.light.SWMRNibbleArray(sectionData.getByteArray("BlockLight").clone(), sectionData.getInt(BLOCKLIGHT_STATE_TAG)); // clone for data safety
+ } else {
+ blockNibbles[y - minSection] = new ca.spottedleaf.starlight.common.light.SWMRNibbleArray(null, sectionData.getInt(BLOCKLIGHT_STATE_TAG));
++ // Paper end - rewrite the light engine
}
-+ // Paper end - rewrite the light engine
if (flag4) {
-- // Paper start - delay this task since we're executing off-main
-- DataLayer skyLight = new DataLayer(nbttagcompound1.getByteArray("SkyLight").clone());
-- tasksToExecuteOnMain.add(() -> {
-- lightengine.queueSectionData(LightLayer.SKY, SectionPos.of(chunkPos, b0), skyLight, true);
-- });
-- // Paper end - delay this task since we're executing off-mai
+- lightengine.queueSectionData(LightLayer.SKY, SectionPos.of(chunkPos, b0), new DataLayer(nbttagcompound1.getByteArray("SkyLight")), true);
+ // Paper start - rewrite the light engine
+ // we store under the same key so mod programs editing nbt
+ // can still read the data, hopefully.
@@ -5167,18 +5183,19 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
+ skyNibbles[y - minSection] = new ca.spottedleaf.starlight.common.light.SWMRNibbleArray(sectionData.getByteArray("SkyLight").clone(), sectionData.getInt(SKYLIGHT_STATE_TAG)); // clone for data safety
+ } else if (flag1) {
+ skyNibbles[y - minSection] = new ca.spottedleaf.starlight.common.light.SWMRNibbleArray(null, sectionData.getInt(SKYLIGHT_STATE_TAG));
- }
-+ // Paper end - rewrite the light engine
++ // Paper end - rewrite the light engine
++ }
++
+ // Paper start - rewrite the light engine
+ } catch (Exception ex) {
+ LOGGER.warn("Failed to load light data for chunk " + chunkPos + " in world '" + world.getWorld().getName() + "', light will be regenerated", ex);
+ flag = false;
-+ }
+ }
+ // Paper end - rewrite light engine
}
}
-@@ -267,6 +296,8 @@ public class ChunkSerializer {
+@@ -188,6 +223,8 @@ public class ChunkSerializer {
}, chunkPos);
object1 = new LevelChunk(world.getLevel(), chunkPos, chunkconverter, levelchunkticks, levelchunkticks1, l, achunksection, ChunkSerializer.postLoadChunk(world, nbt), blendingdata);
@@ -5187,7 +5204,7 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
} else {
ProtoChunkTicks<Block> protochunkticklist = ProtoChunkTicks.load(nbt.getList("block_ticks", 10), (s) -> {
return Registry.BLOCK.getOptional(ResourceLocation.tryParse(s));
-@@ -275,6 +306,8 @@ public class ChunkSerializer {
+@@ -196,6 +233,8 @@ public class ChunkSerializer {
return Registry.FLUID.getOptional(ResourceLocation.tryParse(s));
}, chunkPos);
ProtoChunk protochunk = new ProtoChunk(chunkPos, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, world, iregistry, blendingdata);
@@ -5196,19 +5213,10 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
object1 = protochunk;
protochunk.setInhabitedTime(l);
-@@ -420,7 +453,7 @@ public class ChunkSerializer {
- DataLayer[] blockLight = new DataLayer[lightenginethreaded.getMaxLightSection() - lightenginethreaded.getMinLightSection()];
- DataLayer[] skyLight = new DataLayer[lightenginethreaded.getMaxLightSection() - lightenginethreaded.getMinLightSection()];
-
-- for (int i = lightenginethreaded.getMinLightSection(); i < lightenginethreaded.getMaxLightSection(); ++i) {
-+ for (int i = lightenginethreaded.getMinLightSection(); false && i < lightenginethreaded.getMaxLightSection(); ++i) { // Paper - don't run loop, we don't need to - light data is per chunk now
- DataLayer blockArray = lightenginethreaded.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunkPos, i));
- DataLayer skyArray = lightenginethreaded.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunkPos, i));
+@@ -336,6 +375,12 @@ public class ChunkSerializer {
+ // CraftBukkit end
-@@ -478,6 +511,12 @@ public class ChunkSerializer {
- }
- public static CompoundTag saveChunk(ServerLevel world, ChunkAccess chunk, @org.checkerframework.checker.nullness.qual.Nullable AsyncSaveData asyncsavedata) {
- // Paper end
+ public static CompoundTag write(ServerLevel world, ChunkAccess chunk) {
+ // Paper start - rewrite light impl
+ final int minSection = io.papermc.paper.util.WorldUtil.getMinLightSection(world);
+ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxLightSection(world);
@@ -5218,21 +5226,12 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
ChunkPos chunkcoordintpair = chunk.getPos();
CompoundTag nbttagcompound = new CompoundTag();
-@@ -528,20 +567,14 @@ public class ChunkSerializer {
+@@ -386,11 +431,14 @@ public class ChunkSerializer {
for (int i = lightenginethreaded.getMinLightSection(); i < lightenginethreaded.getMaxLightSection(); ++i) {
int j = chunk.getSectionIndexFromSectionY(i);
boolean flag1 = j >= 0 && j < achunksection.length;
-- // Paper start - async chunk save for unload
-- DataLayer nibblearray; // block light
-- DataLayer nibblearray1; // sky light
-- if (asyncsavedata == null) {
-- nibblearray = lightenginethreaded.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunkcoordintpair, i)); /// Paper - diff on method change (see getAsyncSaveData)
-- nibblearray1 = lightenginethreaded.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunkcoordintpair, i)); // Paper - diff on method change (see getAsyncSaveData)
-- } else {
-- nibblearray = asyncsavedata.blockLight[i - lightenginethreaded.getMinLightSection()];
-- nibblearray1 = asyncsavedata.skyLight[i - lightenginethreaded.getMinLightSection()];
-- }
-- // Paper end
+- DataLayer nibblearray = lightenginethreaded.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(chunkcoordintpair, i));
+- DataLayer nibblearray1 = lightenginethreaded.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(chunkcoordintpair, i));
+ // Paper - replace light engine
- if (flag1 || nibblearray != null || nibblearray1 != null) {
@@ -5246,7 +5245,7 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
if (flag1) {
LevelChunkSection chunksection = achunksection[j];
-@@ -556,13 +589,27 @@ public class ChunkSerializer {
+@@ -405,13 +453,27 @@ public class ChunkSerializer {
nbttagcompound1.put("biomes", (Tag) dataresult1.getOrThrow(false, logger1::error));
}
@@ -5278,7 +5277,7 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
if (!nbttagcompound1.isEmpty()) {
nbttagcompound1.putByte("Y", (byte) i);
-@@ -573,7 +620,8 @@ public class ChunkSerializer {
+@@ -422,7 +484,8 @@ public class ChunkSerializer {
nbttagcompound.put("sections", nbttaglist);
if (flag) {
@@ -5287,4 +5286,22 @@ index be9c15fe141ede1132dbe07ba4bfcf22036ab194..4df5853781a2ac89dd391374d34d9096
+ nbttagcompound.putBoolean("isLightOn", false); // Paper - set to false but still store, this allows us to detect --eraseCache (as eraseCache _removes_)
}
- // Paper start
+ ListTag nbttaglist1 = new ListTag();
+@@ -497,6 +560,17 @@ public class ChunkSerializer {
+ }));
+ }
+
++ // Paper start
++ public static @Nullable ChunkStatus getStatus(@Nullable CompoundTag compound) {
++ if (compound == null) {
++ return null;
++ }
++
++ // Note: Copied from below
++ return ChunkStatus.getStatus(compound.getString("Status"));
++ }
++ // Paper end
++
+ public static ChunkStatus.ChunkType getChunkTypeFromTag(@Nullable CompoundTag nbt) {
+ return nbt != null ? ChunkStatus.byName(nbt.getString("Status")).getChunkType() : ChunkStatus.ChunkType.PROTOCHUNK;
+ }
diff --git a/patches/server/0013-Not-implemeneted.patch b/patches/server/0015-Not-implemeneted.patch
index 3f8aab22dd..3f8aab22dd 100644
--- a/patches/server/0013-Not-implemeneted.patch
+++ b/patches/server/0015-Not-implemeneted.patch
diff --git a/patches/server/0016-Rewrite-chunk-system.patch b/patches/server/0016-Rewrite-chunk-system.patch
new file mode 100644
index 0000000000..3f2585cc65
--- /dev/null
+++ b/patches/server/0016-Rewrite-chunk-system.patch
@@ -0,0 +1,18056 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Spottedleaf <[email protected]>
+Date: Thu, 11 Mar 2021 02:32:30 -0800
+Subject: [PATCH] Rewrite chunk system
+
+
+diff --git a/src/main/java/ca/spottedleaf/starlight/common/light/StarLightInterface.java b/src/main/java/ca/spottedleaf/starlight/common/light/StarLightInterface.java
+index ef8dcbb6bbc0769e9ccfdadb05e6a46c070eda98..f6dfaaa0ccd8caeb4bd4b94254aebe7e96732f12 100644
+--- a/src/main/java/ca/spottedleaf/starlight/common/light/StarLightInterface.java
++++ b/src/main/java/ca/spottedleaf/starlight/common/light/StarLightInterface.java
+@@ -41,14 +41,14 @@ public final class StarLightInterface {
+ protected final ArrayDeque<SkyStarLightEngine> cachedSkyPropagators;
+ protected final ArrayDeque<BlockStarLightEngine> cachedBlockPropagators;
+
+- protected final LightQueue lightQueue = new LightQueue(this);
++ public final io.papermc.paper.chunk.system.light.LightQueue lightQueue; // Paper - replace light queue
+
+ protected final LayerLightEventListener skyReader;
+ protected final LayerLightEventListener blockReader;
+ protected final boolean isClientSide;
+
+- protected final int minSection;
+- protected final int maxSection;
++ public final int minSection; // Paper - public
++ public final int maxSection; // Paper - public
+ protected final int minLightSection;
+ protected final int maxLightSection;
+
+@@ -182,6 +182,7 @@ public final class StarLightInterface {
+ StarLightInterface.this.sectionChange(pos, notReady);
+ }
+ };
++ this.lightQueue = new io.papermc.paper.chunk.system.light.LightQueue(this); // Paper - replace light queue
+ }
+
+ protected int getSkyLightValue(final BlockPos blockPos, final ChunkAccess chunk) {
+@@ -325,7 +326,7 @@ public final class StarLightInterface {
+ return this.lightAccess;
+ }
+
+- protected final SkyStarLightEngine getSkyLightEngine() {
++ public final SkyStarLightEngine getSkyLightEngine() { // Paper - public
+ if (this.cachedSkyPropagators == null) {
+ return null;
+ }
+@@ -340,7 +341,7 @@ public final class StarLightInterface {
+ return ret;
+ }
+
+- protected final void releaseSkyLightEngine(final SkyStarLightEngine engine) {
++ public final void releaseSkyLightEngine(final SkyStarLightEngine engine) { // Paper - public
+ if (this.cachedSkyPropagators == null) {
+ return;
+ }
+@@ -349,7 +350,7 @@ public final class StarLightInterface {
+ }
+ }
+
+- protected final BlockStarLightEngine getBlockLightEngine() {
++ public final BlockStarLightEngine getBlockLightEngine() { // Paper - public
+ if (this.cachedBlockPropagators == null) {
+ return null;
+ }
+@@ -364,7 +365,7 @@ public final class StarLightInterface {
+ return ret;
+ }
+
+- protected final void releaseBlockLightEngine(final BlockStarLightEngine engine) {
++ public final void releaseBlockLightEngine(final BlockStarLightEngine engine) { // Paper - public
+ if (this.cachedBlockPropagators == null) {
+ return;
+ }
+@@ -511,57 +512,15 @@ public final class StarLightInterface {
+ }
+
+ public void scheduleChunkLight(final ChunkPos pos, final Runnable run) {
+- this.lightQueue.queueChunkLighting(pos, run);
++ throw new UnsupportedOperationException("No longer implemented, use the new lightQueue field to queue tasks"); // Paper - replace light queue
+ }
+
+ public void removeChunkTasks(final ChunkPos pos) {
+- this.lightQueue.removeChunk(pos);
++ throw new UnsupportedOperationException("No longer implemented, use the new lightQueue field to queue tasks"); // Paper - replace light queue
+ }
+
+ public void propagateChanges() {
+- if (this.lightQueue.isEmpty()) {
+- return;
+- }
+-
+- final SkyStarLightEngine skyEngine = this.getSkyLightEngine();
+- final BlockStarLightEngine blockEngine = this.getBlockLightEngine();
+-
+- try {
+- LightQueue.ChunkTasks task;
+- while ((task = this.lightQueue.removeFirstTask()) != null) {
+- if (task.lightTasks != null) {
+- for (final Runnable run : task.lightTasks) {
+- run.run();
+- }
+- }
+-
+- final long coordinate = task.chunkCoordinate;
+- final int chunkX = CoordinateUtils.getChunkX(coordinate);
+- final int chunkZ = CoordinateUtils.getChunkZ(coordinate);
+-
+- final Set<BlockPos> positions = task.changedPositions;
+- final Boolean[] sectionChanges = task.changedSectionSet;
+-
+- if (skyEngine != null && (!positions.isEmpty() || sectionChanges != null)) {
+- skyEngine.blocksChangedInChunk(this.lightAccess, chunkX, chunkZ, positions, sectionChanges);
+- }
+- if (blockEngine != null && (!positions.isEmpty() || sectionChanges != null)) {
+- blockEngine.blocksChangedInChunk(this.lightAccess, chunkX, chunkZ, positions, sectionChanges);
+- }
+-
+- if (skyEngine != null && task.queuedEdgeChecksSky != null) {
+- skyEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, task.queuedEdgeChecksSky);
+- }
+- if (blockEngine != null && task.queuedEdgeChecksBlock != null) {
+- blockEngine.checkChunkEdges(this.lightAccess, chunkX, chunkZ, task.queuedEdgeChecksBlock);
+- }
+-
+- task.onComplete.complete(null);
+- }
+- } finally {
+- this.releaseSkyLightEngine(skyEngine);
+- this.releaseBlockLightEngine(blockEngine);
+- }
++ throw new UnsupportedOperationException("No longer implemented, task draining is now performed by the light thread"); // Paper - replace light queue
+ }
+
+ protected static final class LightQueue {
+diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java
+index 46297ac0a19fd2398ab777a381eff4d0a256161e..98171f6c8e23f6ef89b897e4b80e3afb2a1950a0 100644
+--- a/src/main/java/co/aikar/timings/TimingsExport.java
++++ b/src/main/java/co/aikar/timings/TimingsExport.java
+@@ -162,7 +162,11 @@ public class TimingsExport extends Thread {
+ pair("gamerules", toObjectMapper(world.getWorld().getGameRules(), rule -> {
+ return pair(rule, world.getWorld().getGameRuleValue(rule));
+ })),
+- pair("ticking-distance", world.getChunkSource().chunkMap.getEffectiveViewDistance())
++ // Paper start - replace chunk loader system
++ pair("ticking-distance", world.getChunkSource().chunkMap.playerChunkManager.getTargetTickViewDistance()),
++ pair("no-ticking-distance", world.getChunkSource().chunkMap.playerChunkManager.getTargetNoTickViewDistance()),
++ pair("sending-distance", world.getChunkSource().chunkMap.playerChunkManager.getTargetSendDistance())
++ // Paper end - replace chunk loader system
+ ));
+ }));
+
+diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java
+index 0fda52841b5e1643efeda92106124998abc4e0aa..fe79c0add4f7cb18d487c5bb9415c40c5b551ea2 100644
+--- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java
++++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java
+@@ -58,6 +58,16 @@ public class WorldTimingsHandler {
+
+ public final Timing miscMobSpawning;
+
++ public final Timing poiUnload;
++ public final Timing chunkUnload;
++ public final Timing poiSaveDataSerialization;
++ public final Timing chunkSave;
++ public final Timing chunkSaveDataSerialization;
++ public final Timing chunkSaveIOWait;
++ public final Timing chunkUnloadPrepareSave;
++ public final Timing chunkUnloadPOISerialization;
++ public final Timing chunkUnloadDataSave;
++
+ public WorldTimingsHandler(Level server) {
+ String name = ((PrimaryLevelData) server.getLevelData()).getLevelName() + " - ";
+
+@@ -111,6 +121,16 @@ public class WorldTimingsHandler {
+
+
+ miscMobSpawning = Timings.ofSafe(name + "Mob spawning - Misc");
++
++ poiUnload = Timings.ofSafe(name + "Chunk unload - POI");
++ chunkUnload = Timings.ofSafe(name + "Chunk unload - Chunk");
++ poiSaveDataSerialization = Timings.ofSafe(name + "Chunk save - POI Data serialization");
++ chunkSave = Timings.ofSafe(name + "Chunk save - Chunk");
++ chunkSaveDataSerialization = Timings.ofSafe(name + "Chunk save - Chunk Data serialization");
++ chunkSaveIOWait = Timings.ofSafe(name + "Chunk save - Chunk IO Wait");
++ chunkUnloadPrepareSave = Timings.ofSafe(name + "Chunk unload - Async Save Prepare");
++ chunkUnloadPOISerialization = Timings.ofSafe(name + "Chunk unload - POI Data Serialization");
++ chunkUnloadDataSave = Timings.ofSafe(name + "Chunk unload - Data Serialization");
+ }
+
+ public static Timing getTickList(ServerLevel worldserver, String timingsType) {
+diff --git a/src/main/java/com/destroystokyo/paper/io/IOUtil.java b/src/main/java/com/destroystokyo/paper/io/IOUtil.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e064f96c90afd1a4890060baa055cfd0469b6a6f
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/io/IOUtil.java
+@@ -0,0 +1,63 @@
++package com.destroystokyo.paper.io;
++
++import org.bukkit.Bukkit;
++
++@Deprecated(forRemoval = true)
++public final class IOUtil {
++
++ /* Copied from concrete or concurrentutil */
++
++ public static long getCoordinateKey(final int x, final int z) {
++ return ((long)z << 32) | (x & 0xFFFFFFFFL);
++ }
++
++ public static int getCoordinateX(final long key) {
++ return (int)key;
++ }
++
++ public static int getCoordinateZ(final long key) {
++ return (int)(key >>> 32);
++ }
++
++ public static int getRegionCoordinate(final int chunkCoordinate) {
++ return chunkCoordinate >> 5;
++ }
++
++ public static int getChunkInRegion(final int chunkCoordinate) {
++ return chunkCoordinate & 31;
++ }
++
++ public static String genericToString(final Object object) {
++ return object == null ? "null" : object.getClass().getName() + ":" + object.toString();
++ }
++
++ public static <T> T notNull(final T obj) {
++ if (obj == null) {
++ throw new NullPointerException();
++ }
++ return obj;
++ }
++
++ public static <T> T notNull(final T obj, final String msgIfNull) {
++ if (obj == null) {
++ throw new NullPointerException(msgIfNull);
++ }
++ return obj;
++ }
++
++ public static void arrayBounds(final int off, final int len, final int arrayLength, final String msgPrefix) {
++ if (off < 0 || len < 0 || (arrayLength - off) < len) {
++ throw new ArrayIndexOutOfBoundsException(msgPrefix + ": off: " + off + ", len: " + len + ", array length: " + arrayLength);
++ }
++ }
++
++ public static int getPriorityForCurrentThread() {
++ return Bukkit.isPrimaryThread() ? PrioritizedTaskQueue.HIGHEST_PRIORITY : PrioritizedTaskQueue.NORMAL_PRIORITY;
++ }
++
++ @SuppressWarnings("unchecked")
++ public static <T extends Throwable> void rethrow(final Throwable throwable) throws T {
++ throw (T)throwable;
++ }
++
++}
+diff --git a/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f2c27e0ac65be4b75c1d86ef6fd45fdb538d96ac
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java
+@@ -0,0 +1,474 @@
++package com.destroystokyo.paper.io;
++
++import com.mojang.logging.LogUtils;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.storage.RegionFile;
++import org.slf4j.Logger;
++
++import java.io.IOException;
++import java.util.concurrent.CompletableFuture;
++import java.util.concurrent.ConcurrentHashMap;
++import java.util.concurrent.atomic.AtomicLong;
++import java.util.function.Consumer;
++import java.util.function.Function;
++
++/**
++ * Prioritized singleton thread responsible for all chunk IO that occurs in a minecraft server.
++ *
++ * <p>
++ * Singleton access: {@link Holder#INSTANCE}
++ * </p>
++ *
++ * <p>
++ * All functions provided are MT-Safe, however certain ordering constraints are (but not enforced):
++ * <li>
++ * Chunk saves may not occur for unloaded chunks.
++ * </li>
++ * <li>
++ * Tasks must be scheduled on the main thread.
++ * </li>
++ * </p>
++ *
++ * @see Holder#INSTANCE
++ * @see #scheduleSave(ServerLevel, int, int, CompoundTag, CompoundTag, int)
++ * @see #loadChunkDataAsync(ServerLevel, int, int, int, Consumer, boolean, boolean, boolean)
++ * @deprecated
++ */
++@Deprecated(forRemoval = true)
++public final class PaperFileIOThread extends QueueExecutorThread {
++
++ public static final Logger LOGGER = LogUtils.getLogger();
++ public static final CompoundTag FAILURE_VALUE = new CompoundTag();
++
++ public static final class Holder {
++
++ public static final PaperFileIOThread INSTANCE = new PaperFileIOThread();
++
++ static {
++ // Paper - fail hard on usage
++ }
++ }
++
++ private final AtomicLong writeCounter = new AtomicLong();
++
++ private PaperFileIOThread() {
++ super(new PrioritizedTaskQueue<>(), (int)(1.0e6)); // 1.0ms spinwait time
++ this.setName("Paper RegionFile IO Thread");
++ this.setPriority(Thread.NORM_PRIORITY - 1); // we keep priority close to normal because threads can wait on us
++ this.setUncaughtExceptionHandler((final Thread unused, final Throwable thr) -> {
++ LOGGER.error("Uncaught exception thrown from IO thread, report this!", thr);
++ });
++ }
++
++ /* run() is implemented by superclass */
++
++ /*
++ *
++ * IO thread will perform reads before writes
++ *
++ * How reads/writes are scheduled:
++ *
++ * If read in progress while scheduling write, ignore read and schedule write
++ * If read in progress while scheduling read (no write in progress), chain the read task
++ *
++ *
++ * If write in progress while scheduling read, use the pending write data and ret immediately
++ * If write in progress while scheduling write (ignore read in progress), overwrite the write in progress data
++ *
++ * This allows the reads and writes to act as if they occur synchronously to the thread scheduling them, however
++ * it fails to properly propagate write failures. When writes fail the data is kept so future reads will actually
++ * read the failed write data. This should hopefully act as a way to prevent data loss for spurious fails for writing data.
++ *
++ */
++
++ /**
++ * Attempts to bump the priority of all IO tasks for the given chunk coordinates. This has no effect if no tasks are queued.
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param priority Priority level to try to bump to
++ */
++ public void bumpPriority(final ServerLevel world, final int chunkX, final int chunkZ, final int priority) {
++ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage
++ }
++
++ public CompoundTag getPendingWrite(final ServerLevel world, final int chunkX, final int chunkZ, final boolean poiData) {
++ // Paper start - rewrite chunk system
++ return io.papermc.paper.chunk.system.io.RegionFileIOThread.getPendingWrite(
++ world, chunkX, chunkZ, poiData ? io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA :
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA
++ );
++ // Paper end - rewrite chunk system
++ }
++
++ /**
++ * Sets the priority of all IO tasks for the given chunk coordinates. This has no effect if no tasks are queued.
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param priority Priority level to set to
++ */
++ public void setPriority(final ServerLevel world, final int chunkX, final int chunkZ, final int priority) {
++ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage
++ }
++
++ /**
++ * Schedules the chunk data to be written asynchronously.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
++ * saves must be scheduled before a chunk is unloaded.
++ * </li>
++ * <li>
++ * Writes may be called concurrently, although only the "later" write will go through.
++ * </li>
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param poiData Chunk point of interest data. If {@code null}, then no poi data is saved.
++ * @param chunkData Chunk data. If {@code null}, then no chunk data is saved.
++ * @param priority Priority level for this task. See {@link PrioritizedTaskQueue}
++ * @throws IllegalArgumentException If both {@code poiData} and {@code chunkData} are {@code null}.
++ * @throws IllegalStateException If the file io thread has shutdown.
++ */
++ public void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ,
++ final CompoundTag poiData, final CompoundTag chunkData,
++ final int priority) throws IllegalArgumentException {
++ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage
++ }
++
++ private void scheduleWrite(final ChunkDataController dataController, final ServerLevel world,
++ final int chunkX, final int chunkZ, final CompoundTag data, final int priority, final long writeCounter) {
++ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage
++ }
++
++ /**
++ * Same as {@link #loadChunkDataAsync(ServerLevel, int, int, int, Consumer, boolean, boolean, boolean)}, except this function returns
++ * a {@link CompletableFuture} which is potentially completed <b>ASYNCHRONOUSLY ON THE FILE IO THREAD</b> when the load task
++ * has completed.
++ * <p>
++ * Note that if the chunk fails to load the returned future is completed with {@code null}.
++ * </p>
++ */
++ public CompletableFuture<ChunkData> loadChunkDataAsyncFuture(final ServerLevel world, final int chunkX, final int chunkZ,
++ final int priority, final boolean readPoiData, final boolean readChunkData,
++ final boolean intendingToBlock) {
++ final CompletableFuture<ChunkData> future = new CompletableFuture<>();
++ this.loadChunkDataAsync(world, chunkX, chunkZ, priority, future::complete, readPoiData, readChunkData, intendingToBlock);
++ return future;
++ }
++
++ /**
++ * Schedules a load to be executed asynchronously.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * If a chunk fails to load, the {@code onComplete} parameter is completed with {@code null}.
++ * </li>
++ * <li>
++ * It is possible for the {@code onComplete} parameter to be given {@link ChunkData} containing data
++ * this call did not request.
++ * </li>
++ * <li>
++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
++ * data is undefined behaviour, and can cause deadlock.
++ * </li>
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param priority Priority level for this task. See {@link PrioritizedTaskQueue}
++ * @param onComplete Consumer to execute once this task has completed
++ * @param readPoiData Whether to read point of interest data. If {@code false}, the {@code NBTTagCompound} will be {@code null}.
++ * @param readChunkData Whether to read chunk data. If {@code false}, the {@code NBTTagCompound} will be {@code null}.
++ * @return The {@link PrioritizedTaskQueue.PrioritizedTask} associated with this task. Note that this task does not support
++ * cancellation.
++ */
++ public void loadChunkDataAsync(final ServerLevel world, final int chunkX, final int chunkZ,
++ final int priority, final Consumer<ChunkData> onComplete,
++ final boolean readPoiData, final boolean readChunkData,
++ final boolean intendingToBlock) {
++ if (!PrioritizedTaskQueue.validPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority: " + priority);
++ }
++
++ if (!(readPoiData | readChunkData)) {
++ throw new IllegalArgumentException("Must read chunk data or poi data");
++ }
++
++ final ChunkData complete = new ChunkData();
++ // Paper start - rewrite chunk system
++ final java.util.List<io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType> types = new java.util.ArrayList<>();
++ if (readPoiData) {
++ types.add(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA);
++ }
++ if (readChunkData) {
++ types.add(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA);
++ }
++ final ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority newPriority;
++ switch (priority) {
++ case PrioritizedTaskQueue.HIGHEST_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.BLOCKING;
++ case PrioritizedTaskQueue.HIGHER_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHEST;
++ case PrioritizedTaskQueue.HIGH_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGH;
++ case PrioritizedTaskQueue.NORMAL_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL;
++ case PrioritizedTaskQueue.LOW_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.LOW;
++ case PrioritizedTaskQueue.LOWEST_PRIORITY -> newPriority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.IDLE;
++ default -> throw new IllegalStateException("Legacy priority " + priority + " should be valid");
++ }
++ final Consumer<io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileData> transformComplete = (io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileData data) -> {
++ if (readPoiData) {
++ if (data.getThrowable(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA) != null) {
++ complete.poiData = FAILURE_VALUE;
++ } else {
++ complete.poiData = data.getData(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA);
++ }
++ }
++
++ if (readChunkData) {
++ if (data.getThrowable(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA) != null) {
++ complete.chunkData = FAILURE_VALUE;
++ } else {
++ complete.chunkData = data.getData(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA);
++ }
++ }
++
++ onComplete.accept(complete);
++ };
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.loadChunkData(world, chunkX, chunkZ, transformComplete, intendingToBlock, newPriority, types.toArray(new io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType[0]));
++ // Paper end - rewrite chunk system
++
++ }
++
++ // Note: the onComplete may be called asynchronously or synchronously here.
++ private void scheduleRead(final ChunkDataController dataController, final ServerLevel world,
++ final int chunkX, final int chunkZ, final Consumer<CompoundTag> onComplete, final int priority,
++ final boolean intendingToBlock) {
++ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage
++ }
++
++ /**
++ * Same as {@link #loadChunkDataAsync(ServerLevel, int, int, int, Consumer, boolean, boolean, boolean)}, except this function returns
++ * the {@link ChunkData} associated with the specified chunk when the task is complete.
++ * @return The chunk data, or {@code null} if the chunk failed to load.
++ */
++ public ChunkData loadChunkData(final ServerLevel world, final int chunkX, final int chunkZ, final int priority,
++ final boolean readPoiData, final boolean readChunkData) {
++ return this.loadChunkDataAsyncFuture(world, chunkX, chunkZ, priority, readPoiData, readChunkData, true).join();
++ }
++
++ /**
++ * Schedules the given task at the specified priority to be executed on the IO thread.
++ * <p>
++ * Internal api. Do not use.
++ * </p>
++ */
++ public void runTask(final int priority, final Runnable runnable) {
++ throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage
++ }
++
++ static final class GeneralTask extends PrioritizedTaskQueue.PrioritizedTask implements Runnable {
++
++ private final Runnable run;
++
++ public GeneralTask(final int priority, final Runnable run) {
++ super(priority);
++ this.run = IOUtil.notNull(run, "Task may not be null");
++ }
++
++ @Override
++ public void run() {
++ try {
++ this.run.run();
++ } catch (final Throwable throwable) {
++ if (throwable instanceof ThreadDeath) {
++ throw (ThreadDeath)throwable;
++ }
++ LOGGER.error("Failed to execute general task on IO thread " + IOUtil.genericToString(this.run), throwable);
++ }
++ }
++ }
++
++ public static final class ChunkData {
++
++ public CompoundTag poiData;
++ public CompoundTag chunkData;
++
++ public ChunkData() {}
++
++ public ChunkData(final CompoundTag poiData, final CompoundTag chunkData) {
++ this.poiData = poiData;
++ this.chunkData = chunkData;
++ }
++ }
++
++ public static abstract class ChunkDataController {
++
++ // ConcurrentHashMap synchronizes per chain, so reduce the chance of task's hashes colliding.
++ public final ConcurrentHashMap<Long, ChunkDataTask> tasks = new ConcurrentHashMap<>(64, 0.5f);
++
++ public abstract void writeData(final int x, final int z, final CompoundTag compound) throws IOException;
++ public abstract CompoundTag readData(final int x, final int z) throws IOException;
++
++ public abstract <T> T computeForRegionFile(final int chunkX, final int chunkZ, final Function<RegionFile, T> function);
++ public abstract <T> T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function<RegionFile, T> function);
++
++ public static final class InProgressWrite {
++ public long writeCounter;
++ public CompoundTag data;
++ }
++
++ public static final class InProgressRead {
++ public final CompletableFuture<CompoundTag> readFuture = new CompletableFuture<>();
++ }
++ }
++
++ public static final class ChunkDataTask extends PrioritizedTaskQueue.PrioritizedTask implements Runnable {
++
++ public ChunkDataController.InProgressWrite inProgressWrite;
++ public ChunkDataController.InProgressRead inProgressRead;
++
++ private final ServerLevel world;
++ private final int x;
++ private final int z;
++ private final ChunkDataController taskController;
++
++ public ChunkDataTask(final int priority, final ServerLevel world, final int x, final int z, final ChunkDataController taskController) {
++ super(priority);
++ this.world = world;
++ this.x = x;
++ this.z = z;
++ this.taskController = taskController;
++ }
++
++ @Override
++ public String toString() {
++ return "Task for world: '" + this.world.getWorld().getName() + "' at " + this.x + "," + this.z +
++ " poi: " + (this.taskController == null) + ", hash: " + this.hashCode(); // Paper - TODO rewrite chunk system
++ }
++
++ /*
++ *
++ * IO thread will perform reads before writes
++ *
++ * How reads/writes are scheduled:
++ *
++ * If read in progress while scheduling write, ignore read and schedule write
++ * If read in progress while scheduling read (no write in progress), chain the read task
++ *
++ *
++ * If write in progress while scheduling read, use the pending write data and ret immediately
++ * If write in progress while scheduling write (ignore read in progress), overwrite the write in progress data
++ *
++ * This allows the reads and writes to act as if they occur synchronously to the thread scheduling them, however
++ * it fails to properly propagate write failures
++ *
++ */
++
++ void reschedule(final int priority) {
++ // priority is checked before this stage // TODO what
++ this.queue.lazySet(null);
++ this.priority.lazySet(priority);
++ PaperFileIOThread.Holder.INSTANCE.queueTask(this);
++ }
++
++ @Override
++ public void run() {
++ if (true) throw new IllegalStateException("Shouldn't get here, use RegionFileIOThread"); // Paper - rewrite chunk system, fail hard on usage
++ ChunkDataController.InProgressRead read = this.inProgressRead;
++ if (read != null) {
++ CompoundTag compound = PaperFileIOThread.FAILURE_VALUE;
++ try {
++ compound = this.taskController.readData(this.x, this.z);
++ } catch (final Throwable thr) {
++ if (thr instanceof ThreadDeath) {
++ throw (ThreadDeath)thr;
++ }
++ LOGGER.error("Failed to read chunk data for task: " + this.toString(), thr);
++ // fall through to complete with null data
++ }
++ read.readFuture.complete(compound);
++ }
++
++ final Long chunkKey = Long.valueOf(IOUtil.getCoordinateKey(this.x, this.z));
++
++ ChunkDataController.InProgressWrite write = this.inProgressWrite;
++
++ if (write == null) {
++ // IntelliJ warns this is invalid, however it does not consider that writes to the task map & the inProgress field can occur concurrently.
++ ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final Long keyInMap, final ChunkDataTask valueInMap) -> {
++ if (valueInMap == null) {
++ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!");
++ }
++ if (valueInMap != ChunkDataTask.this) {
++ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
++ }
++ return valueInMap.inProgressWrite == null ? null : valueInMap;
++ });
++
++ if (inMap == null) {
++ return; // set the task value to null, indicating we're done
++ }
++
++ // not null, which means there was a concurrent write
++ write = this.inProgressWrite;
++ }
++
++ for (;;) {
++ final long writeCounter;
++ final CompoundTag data;
++
++ //noinspection SynchronizationOnLocalVariableOrMethodParameter
++ synchronized (write) {
++ writeCounter = write.writeCounter;
++ data = write.data;
++ }
++
++ boolean failedWrite = false;
++
++ try {
++ this.taskController.writeData(this.x, this.z, data);
++ } catch (final Throwable thr) {
++ if (thr instanceof ThreadDeath) {
++ throw (ThreadDeath)thr;
++ }
++ LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
++ failedWrite = true;
++ }
++
++ boolean finalFailWrite = failedWrite;
++
++ ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final Long keyInMap, final ChunkDataTask valueInMap) -> {
++ if (valueInMap == null) {
++ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!");
++ }
++ if (valueInMap != ChunkDataTask.this) {
++ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
++ }
++ if (valueInMap.inProgressWrite.writeCounter == writeCounter) {
++ if (finalFailWrite) {
++ valueInMap.inProgressWrite.writeCounter = -1L;
++ }
++
++ return null;
++ }
++ return valueInMap;
++ // Hack end
++ });
++
++ if (inMap == null) {
++ // write counter matched, so we wrote the most up-to-date pending data, we're done here
++ // or we failed to write and successfully set the write counter to -1
++ return; // we're done here
++ }
++
++ // fetch & write new data
++ continue;
++ }
++ }
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java b/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..7844a3515430472bd829ff246396bceb0797de1b
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java
+@@ -0,0 +1,299 @@
++package com.destroystokyo.paper.io;
++
++import java.util.concurrent.ConcurrentLinkedQueue;
++import java.util.concurrent.atomic.AtomicBoolean;
++import java.util.concurrent.atomic.AtomicInteger;
++import java.util.concurrent.atomic.AtomicReference;
++
++@Deprecated(forRemoval = true)
++public class PrioritizedTaskQueue<T extends PrioritizedTaskQueue.PrioritizedTask> {
++
++ // lower numbers are a higher priority (except < 0)
++ // higher priorities are always executed before lower priorities
++
++ /**
++ * Priority value indicating the task has completed or is being completed.
++ */
++ public static final int COMPLETING_PRIORITY = -1;
++
++ /**
++ * Highest priority, should only be used for main thread tasks or tasks that are blocking the main thread.
++ */
++ public static final int HIGHEST_PRIORITY = 0;
++
++ /**
++ * Should be only used in an IO task so that chunk loads do not wait on other IO tasks.
++ * This only exists because IO tasks are scheduled before chunk load tasks to decrease IO waiting times.
++ */
++ public static final int HIGHER_PRIORITY = 1;
++
++ /**
++ * Should be used for scheduling chunk loads/generation that would increase response times to users.
++ */
++ public static final int HIGH_PRIORITY = 2;
++
++ /**
++ * Default priority.
++ */
++ public static final int NORMAL_PRIORITY = 3;
++
++ /**
++ * Use for tasks not at all critical and can potentially be delayed.
++ */
++ public static final int LOW_PRIORITY = 4;
++
++ /**
++ * Use for tasks that should "eventually" execute.
++ */
++ public static final int LOWEST_PRIORITY = 5;
++
++ private static final int TOTAL_PRIORITIES = 6;
++
++ final ConcurrentLinkedQueue<T>[] queues = (ConcurrentLinkedQueue<T>[])new ConcurrentLinkedQueue[TOTAL_PRIORITIES];
++
++ private final AtomicBoolean shutdown = new AtomicBoolean();
++
++ {
++ for (int i = 0; i < TOTAL_PRIORITIES; ++i) {
++ this.queues[i] = new ConcurrentLinkedQueue<>();
++ }
++ }
++
++ /**
++ * Returns whether the specified priority is valid
++ */
++ public static boolean validPriority(final int priority) {
++ return priority >= 0 && priority < TOTAL_PRIORITIES;
++ }
++
++ /**
++ * Queues a task.
++ * @throws IllegalStateException If the task has already been queued. Use {@link PrioritizedTask#raisePriority(int)} to
++ * raise a task's priority.
++ * This can also be thrown if the queue has shutdown.
++ */
++ public void add(final T task) throws IllegalStateException {
++ int priority = task.getPriority();
++ if (priority != COMPLETING_PRIORITY) {
++ task.setQueue(this);
++ this.queues[priority].add(task);
++ }
++ if (this.shutdown.get()) {
++ // note: we're not actually sure at this point if our task will go through
++ throw new IllegalStateException("Queue has shutdown, refusing to execute task " + IOUtil.genericToString(task));
++ }
++ }
++
++ /**
++ * Polls the highest priority task currently available. {@code null} if none.
++ */
++ public T poll() {
++ T task;
++ for (int i = 0; i < TOTAL_PRIORITIES; ++i) {
++ final ConcurrentLinkedQueue<T> queue = this.queues[i];
++
++ while ((task = queue.poll()) != null) {
++ final int prevPriority = task.tryComplete(i);
++ if (prevPriority != COMPLETING_PRIORITY && prevPriority <= i) {
++ // if the prev priority was greater-than or equal to our current priority
++ return task;
++ }
++ }
++ }
++
++ return null;
++ }
++
++ /**
++ * Polls the highest priority task currently available. {@code null} if none.
++ */
++ public T poll(final int lowestPriority) {
++ T task;
++ final int max = Math.min(LOWEST_PRIORITY, lowestPriority);
++ for (int i = 0; i <= max; ++i) {
++ final ConcurrentLinkedQueue<T> queue = this.queues[i];
++
++ while ((task = queue.poll()) != null) {
++ final int prevPriority = task.tryComplete(i);
++ if (prevPriority != COMPLETING_PRIORITY && prevPriority <= i) {
++ // if the prev priority was greater-than or equal to our current priority
++ return task;
++ }
++ }
++ }
++
++ return null;
++ }
++
++ /**
++ * Returns whether this queue may have tasks queued.
++ * <p>
++ * This operation is not atomic, but is MT-Safe.
++ * </p>
++ * @return {@code true} if tasks may be queued, {@code false} otherwise
++ */
++ public boolean hasTasks() {
++ for (int i = 0; i < TOTAL_PRIORITIES; ++i) {
++ final ConcurrentLinkedQueue<T> queue = this.queues[i];
++
++ if (queue.peek() != null) {
++ return true;
++ }
++ }
++ return false;
++ }
++
++ /**
++ * Prevent further additions to this queue. Attempts to add after this call has completed (potentially during) will
++ * result in {@link IllegalStateException} being thrown.
++ * <p>
++ * This operation is atomic with respect to other shutdown calls
++ * </p>
++ * <p>
++ * After this call has completed, regardless of return value, this queue will be shutdown.
++ * </p>
++ * @return {@code true} if the queue was shutdown, {@code false} if it has shut down already
++ */
++ public boolean shutdown() {
++ return this.shutdown.getAndSet(false);
++ }
++
++ public abstract static class PrioritizedTask {
++
++ protected final AtomicReference<PrioritizedTaskQueue> queue = new AtomicReference<>();
++
++ protected final AtomicInteger priority;
++
++ protected PrioritizedTask() {
++ this(PrioritizedTaskQueue.NORMAL_PRIORITY);
++ }
++
++ protected PrioritizedTask(final int priority) {
++ if (!PrioritizedTaskQueue.validPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.priority = new AtomicInteger(priority);
++ }
++
++ /**
++ * Returns the current priority. Note that {@link PrioritizedTaskQueue#COMPLETING_PRIORITY} will be returned
++ * if this task is completing or has completed.
++ */
++ public final int getPriority() {
++ return this.priority.get();
++ }
++
++ /**
++ * Returns whether this task is scheduled to execute, or has been already executed.
++ */
++ public boolean isScheduled() {
++ return this.queue.get() != null;
++ }
++
++ final int tryComplete(final int minPriority) {
++ for (int curr = this.getPriorityVolatile();;) {
++ if (curr == COMPLETING_PRIORITY) {
++ return COMPLETING_PRIORITY;
++ }
++ if (curr > minPriority) {
++ // curr is lower priority
++ return curr;
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, COMPLETING_PRIORITY))) {
++ return curr;
++ }
++ continue;
++ }
++ }
++
++ /**
++ * Forces this task to be completed.
++ * @return {@code true} if the task was cancelled, {@code false} if the task has already completed or is being completed.
++ */
++ public boolean cancel() {
++ return this.exchangePriorityVolatile(PrioritizedTaskQueue.COMPLETING_PRIORITY) != PrioritizedTaskQueue.COMPLETING_PRIORITY;
++ }
++
++ /**
++ * Attempts to raise the priority to the priority level specified.
++ * @param priority Priority specified
++ * @return {@code true} if successful, {@code false} otherwise.
++ */
++ public boolean raisePriority(final int priority) {
++ if (!PrioritizedTaskQueue.validPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority");
++ }
++
++ for (int curr = this.getPriorityVolatile();;) {
++ if (curr == COMPLETING_PRIORITY) {
++ return false;
++ }
++ if (priority >= curr) {
++ return true;
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority))) {
++ PrioritizedTaskQueue queue = this.queue.get();
++ if (queue != null) {
++ //noinspection unchecked
++ queue.queues[priority].add(this); // silently fail on shutdown
++ }
++ return true;
++ }
++ continue;
++ }
++ }
++
++ /**
++ * Attempts to set this task's priority level to the level specified.
++ * @param priority Specified priority level.
++ * @return {@code true} if successful, {@code false} if this task is completing or has completed.
++ */
++ public boolean updatePriority(final int priority) {
++ if (!PrioritizedTaskQueue.validPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority");
++ }
++
++ for (int curr = this.getPriorityVolatile();;) {
++ if (curr == COMPLETING_PRIORITY) {
++ return false;
++ }
++ if (curr == priority) {
++ return true;
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority))) {
++ PrioritizedTaskQueue queue = this.queue.get();
++ if (queue != null) {
++ //noinspection unchecked
++ queue.queues[priority].add(this); // silently fail on shutdown
++ }
++ return true;
++ }
++ continue;
++ }
++ }
++
++ void setQueue(final PrioritizedTaskQueue queue) {
++ this.queue.set(queue);
++ }
++
++ /* priority */
++
++ protected final int getPriorityVolatile() {
++ return this.priority.get();
++ }
++
++ protected final int compareAndExchangePriorityVolatile(final int expect, final int update) {
++ if (this.priority.compareAndSet(expect, update)) {
++ return expect;
++ }
++ return this.priority.get();
++ }
++
++ protected final int exchangePriorityVolatile(final int value) {
++ return this.priority.getAndSet(value);
++ }
++ }
++}
+diff --git a/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java b/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..99f49b5625cf51d6c97640553cf5c420bb6fdd36
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java
+@@ -0,0 +1,255 @@
++package com.destroystokyo.paper.io;
++
++import com.mojang.logging.LogUtils;
++import org.slf4j.Logger;
++
++import java.util.concurrent.ConcurrentLinkedQueue;
++import java.util.concurrent.atomic.AtomicBoolean;
++import java.util.concurrent.locks.LockSupport;
++
++@Deprecated(forRemoval = true)
++public class QueueExecutorThread<T extends PrioritizedTaskQueue.PrioritizedTask & Runnable> extends Thread {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ protected final PrioritizedTaskQueue<T> queue;
++ protected final long spinWaitTime;
++
++ protected volatile boolean closed;
++
++ protected final AtomicBoolean parked = new AtomicBoolean();
++
++ protected volatile ConcurrentLinkedQueue<Thread> flushQueue = new ConcurrentLinkedQueue<>();
++ protected volatile long flushCycles;
++
++ protected int lowestPriorityToPoll = PrioritizedTaskQueue.LOWEST_PRIORITY;
++
++ public int getLowestPriorityToPoll() {
++ return this.lowestPriorityToPoll;
++ }
++
++ public void setLowestPriorityToPoll(final int lowestPriorityToPoll) {
++ if (this.isAlive()) {
++ throw new IllegalStateException("Cannot set after starting");
++ }
++ this.lowestPriorityToPoll = lowestPriorityToPoll;
++ }
++
++ public QueueExecutorThread(final PrioritizedTaskQueue<T> queue) {
++ this(queue, (int)(1.e6)); // 1.0ms
++ }
++
++ public QueueExecutorThread(final PrioritizedTaskQueue<T> queue, final long spinWaitTime) { // in ms
++ this.queue = queue;
++ this.spinWaitTime = spinWaitTime;
++ }
++
++ @Override
++ public void run() {
++ final long spinWaitTime = this.spinWaitTime;
++ main_loop:
++ for (;;) {
++ this.pollTasks(true);
++
++ // spinwait
++
++ final long start = System.nanoTime();
++
++ for (;;) {
++ // If we are interrpted for any reason, park() will always return immediately. Clear so that we don't needlessly use cpu in such an event.
++ Thread.interrupted();
++ LockSupport.parkNanos("Spinwaiting on tasks", 1000L); // 1us
++
++ if (this.pollTasks(true)) {
++ // restart loop, found tasks
++ continue main_loop;
++ }
++
++ if (this.handleClose()) {
++ return; // we're done
++ }
++
++ if ((System.nanoTime() - start) >= spinWaitTime) {
++ break;
++ }
++ }
++
++ if (this.handleClose()) {
++ return;
++ }
++
++ this.parked.set(true);
++
++ // We need to parse here to avoid a race condition where a thread queues a task before we set parked to true
++ // (i.e it will not notify us)
++ if (this.pollTasks(true)) {
++ this.parked.set(false);
++ continue;
++ }
++
++ if (this.handleClose()) {
++ return;
++ }
++
++ // we don't need to check parked before sleeping, but we do need to check parked in a do-while loop
++ // LockSupport.park() can fail for any reason
++ do {
++ Thread.interrupted();
++ LockSupport.park("Waiting on tasks");
++ } while (this.parked.get());
++ }
++ }
++
++ protected boolean handleClose() {
++ if (this.closed) {
++ this.pollTasks(true); // this ensures we've emptied the queue
++ this.handleFlushThreads(true);
++ return true;
++ }
++ return false;
++ }
++
++ protected boolean pollTasks(boolean flushTasks) {
++ Runnable task;
++ boolean ret = false;
++
++ while ((task = this.queue.poll(this.lowestPriorityToPoll)) != null) {
++ ret = true;
++ try {
++ task.run();
++ } catch (final Throwable throwable) {
++ if (throwable instanceof ThreadDeath) {
++ throw (ThreadDeath)throwable;
++ }
++ LOGGER.error("Exception thrown from prioritized runnable task in thread '" + this.getName() + "': " + IOUtil.genericToString(task), throwable);
++ }
++ }
++
++ if (flushTasks) {
++ this.handleFlushThreads(false);
++ }
++
++ return ret;
++ }
++
++ protected void handleFlushThreads(final boolean shutdown) {
++ Thread parking;
++ ConcurrentLinkedQueue<Thread> flushQueue = this.flushQueue;
++ do {
++ ++flushCycles; // may be plain read opaque write
++ while ((parking = flushQueue.poll()) != null) {
++ LockSupport.unpark(parking);
++ }
++ } while (this.pollTasks(false));
++
++ if (shutdown) {
++ this.flushQueue = null;
++
++ // defend against a race condition where a flush thread double-checks right before we set to null
++ while ((parking = flushQueue.poll()) != null) {
++ LockSupport.unpark(parking);
++ }
++ }
++ }
++
++ /**
++ * Notify's this thread that a task has been added to its queue
++ * @return {@code true} if this thread was waiting for tasks, {@code false} if it is executing tasks
++ */
++ public boolean notifyTasks() {
++ if (this.parked.get() && this.parked.getAndSet(false)) {
++ LockSupport.unpark(this);
++ return true;
++ }
++ return false;
++ }
++
++ protected void queueTask(final T task) {
++ this.queue.add(task);
++ this.notifyTasks();
++ }
++
++ /**
++ * Waits until this thread's queue is empty.
++ *
++ * @throws IllegalStateException If the current thread is {@code this} thread.
++ */
++ public void flush() {
++ final Thread currentThread = Thread.currentThread();
++
++ if (currentThread == this) {
++ // avoid deadlock
++ throw new IllegalStateException("Cannot flush the queue executor thread while on the queue executor thread");
++ }
++
++ // order is important
++
++ int successes = 0;
++ long lastCycle = -1L;
++
++ do {
++ final ConcurrentLinkedQueue<Thread> flushQueue = this.flushQueue;
++ if (flushQueue == null) {
++ return;
++ }
++
++ flushQueue.add(currentThread);
++
++ // double check flush queue
++ if (this.flushQueue == null) {
++ return;
++ }
++
++ final long currentCycle = this.flushCycles; // may be opaque read
++
++ if (currentCycle == lastCycle) {
++ Thread.yield();
++ continue;
++ }
++
++ // force response
++ this.parked.set(false);
++ LockSupport.unpark(this);
++
++ LockSupport.park("flushing queue executor thread");
++
++ // returns whether there are tasks queued, does not return whether there are tasks executing
++ // this is why we cycle twice twice through flush (we know a pollTask call is made after a flush cycle)
++ // we really only need to guarantee that the tasks this thread has queued has gone through, and can leave
++ // tasks queued concurrently that are unsychronized with this thread as undefined behavior
++ if (this.queue.hasTasks()) {
++ successes = 0;
++ } else {
++ ++successes;
++ }
++
++ } while (successes != 2);
++
++ }
++
++ /**
++ * Closes this queue executor's queue and optionally waits for it to empty.
++ * <p>
++ * If wait is {@code true}, then the queue will be empty by the time this call completes.
++ * </p>
++ * <p>
++ * This function is MT-Safe.
++ * </p>
++ * @param wait If this call is to wait until the queue is empty
++ * @param killQueue Whether to shutdown this thread's queue
++ * @return whether this thread shut down the queue
++ */
++ public boolean close(final boolean wait, final boolean killQueue) {
++ boolean ret = !killQueue ? false : this.queue.shutdown();
++ this.closed = true;
++
++ // force thread to respond to the shutdown
++ this.parked.set(false);
++ LockSupport.unpark(this);
++
++ if (wait) {
++ this.flush();
++ }
++ return ret;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..dd501e83d991e45598509134fab05bafc1904953
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/PlayerChunkLoader.java
+@@ -0,0 +1,1128 @@
++package io.papermc.paper.chunk;
++
++import com.destroystokyo.paper.util.misc.PlayerAreaMap;
++import com.destroystokyo.paper.util.misc.PooledLinkedHashSets;
++import io.papermc.paper.configuration.GlobalConfiguration;
++import io.papermc.paper.util.CoordinateUtils;
++import io.papermc.paper.util.IntervalledCounter;
++import io.papermc.paper.util.TickThread;
++import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
++import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap;
++import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
++import net.minecraft.network.protocol.game.ClientboundSetChunkCacheCenterPacket;
++import net.minecraft.network.protocol.game.ClientboundSetChunkCacheRadiusPacket;
++import net.minecraft.network.protocol.game.ClientboundSetSimulationDistancePacket;
++import net.minecraft.server.MCUtil;
++import net.minecraft.server.MinecraftServer;
++import net.minecraft.server.level.*;
++import net.minecraft.util.Mth;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.LevelChunk;
++import org.apache.commons.lang3.mutable.MutableObject;
++import org.bukkit.craftbukkit.entity.CraftPlayer;
++import org.bukkit.entity.Player;
++import java.util.ArrayDeque;
++import java.util.ArrayList;
++import java.util.List;
++import java.util.TreeSet;
++import java.util.concurrent.atomic.AtomicInteger;
++
++public final class PlayerChunkLoader {
++
++ public static final int MIN_VIEW_DISTANCE = 2;
++ public static final int MAX_VIEW_DISTANCE = 32;
++
++ public static final int TICK_TICKET_LEVEL = 31;
++ public static final int LOADED_TICKET_LEVEL = 33;
++
++ public static int getTickViewDistance(final Player player) {
++ return getTickViewDistance(((CraftPlayer)player).getHandle());
++ }
++
++ public static int getTickViewDistance(final ServerPlayer player) {
++ final ServerLevel level = (ServerLevel)player.level;
++ final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player);
++ if (data == null) {
++ return level.chunkSource.chunkMap.playerChunkManager.getTargetTickViewDistance();
++ }
++ return data.getTargetTickViewDistance();
++ }
++
++ public static int getLoadViewDistance(final Player player) {
++ return getLoadViewDistance(((CraftPlayer)player).getHandle());
++ }
++
++ public static int getLoadViewDistance(final ServerPlayer player) {
++ final ServerLevel level = (ServerLevel)player.level;
++ final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player);
++ if (data == null) {
++ return level.chunkSource.chunkMap.playerChunkManager.getLoadDistance();
++ }
++ return data.getLoadDistance();
++ }
++
++ public static int getSendViewDistance(final Player player) {
++ return getSendViewDistance(((CraftPlayer)player).getHandle());
++ }
++
++ public static int getSendViewDistance(final ServerPlayer player) {
++ final ServerLevel level = (ServerLevel)player.level;
++ final PlayerLoaderData data = level.chunkSource.chunkMap.playerChunkManager.getData(player);
++ if (data == null) {
++ return level.chunkSource.chunkMap.playerChunkManager.getTargetSendDistance();
++ }
++ return data.getTargetSendViewDistance();
++ }
++
++ protected final ChunkMap chunkMap;
++ protected final Reference2ObjectLinkedOpenHashMap<ServerPlayer, PlayerLoaderData> playerMap = new Reference2ObjectLinkedOpenHashMap<>(512, 0.7f);
++ protected final ReferenceLinkedOpenHashSet<PlayerLoaderData> chunkSendQueue = new ReferenceLinkedOpenHashSet<>(512, 0.7f);
++
++ protected final TreeSet<PlayerLoaderData> chunkLoadQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
++ if (p1 == p2) {
++ return 0;
++ }
++
++ final ChunkPriorityHolder holder1 = p1.loadQueue.peekFirst();
++ final ChunkPriorityHolder holder2 = p2.loadQueue.peekFirst();
++
++ final int priorityCompare = Double.compare(holder1 == null ? Double.MAX_VALUE : holder1.priority, holder2 == null ? Double.MAX_VALUE : holder2.priority);
++
++ final int lastLoadTimeCompare = Long.compare(p1.lastChunkLoad, p2.lastChunkLoad);
++
++ if ((holder1 == null || holder2 == null || lastLoadTimeCompare == 0 || holder1.priority < 0.0 || holder2.priority < 0.0) && priorityCompare != 0) {
++ return priorityCompare;
++ }
++
++ if (lastLoadTimeCompare != 0) {
++ return lastLoadTimeCompare;
++ }
++
++ final int idCompare = Integer.compare(p1.player.getId(), p2.player.getId());
++
++ if (idCompare != 0) {
++ return idCompare;
++ }
++
++ // last resort
++ return Integer.compare(System.identityHashCode(p1), System.identityHashCode(p2));
++ });
++
++ protected final TreeSet<PlayerLoaderData> chunkSendWaitQueue = new TreeSet<>((final PlayerLoaderData p1, final PlayerLoaderData p2) -> {
++ if (p1 == p2) {
++ return 0;
++ }
++
++ final int timeCompare = Long.compare(p1.nextChunkSendTarget, p2.nextChunkSendTarget);
++ if (timeCompare != 0) {
++ return timeCompare;
++ }
++
++ final int idCompare = Integer.compare(p1.player.getId(), p2.player.getId());
++
++ if (idCompare != 0) {
++ return idCompare;
++ }
++
++ // last resort
++ return Integer.compare(System.identityHashCode(p1), System.identityHashCode(p2));
++ });
++
++
++ // no throttling is applied below this VD for loading
++
++ /**
++ * The chunks to be sent to players, provided they're send-ready. Send-ready means the chunk and its 1 radius neighbours are loaded.
++ */
++ public final PlayerAreaMap broadcastMap;
++
++ /**
++ * The chunks to be brought up to send-ready status. Send-ready means the chunk and its 1 radius neighbours are loaded.
++ */
++ public final PlayerAreaMap loadMap;
++
++ /**
++ * Areamap used only to remove tickets for send-ready chunks. View distance is always + 1 of load view distance. Thus,
++ * this map is always representing the chunks we are actually going to load.
++ */
++ public final PlayerAreaMap loadTicketCleanup;
++
++ /**
++ * The chunks to brought to ticking level. Each chunk must have 2 radius neighbours loaded before this can happen.
++ */
++ public final PlayerAreaMap tickMap;
++
++ /**
++ * -1 if defaulting to [load distance], else always in [2, load distance]
++ */
++ protected int rawSendDistance = -1;
++
++ /**
++ * -1 if defaulting to [tick view distance + 1], else always in [tick view distance + 1, 32 + 1]
++ */
++ protected int rawLoadDistance = -1;
++
++ /**
++ * Never -1, always in [2, 32]
++ */
++ protected int rawTickDistance = -1;
++
++ // methods to bridge for API
++
++ public int getTargetTickViewDistance() {
++ return this.getTickDistance();
++ }
++
++ public void setTargetTickViewDistance(final int distance) {
++ this.setTickDistance(distance);
++ }
++
++ public int getTargetNoTickViewDistance() {
++ return this.getLoadDistance() - 1;
++ }
++
++ public void setTargetNoTickViewDistance(final int distance) {
++ this.setLoadDistance(distance == -1 ? -1 : distance + 1);
++ }
++
++ public int getTargetSendDistance() {
++ return this.rawSendDistance == -1 ? this.getLoadDistance() : this.rawSendDistance;
++ }
++
++ public void setTargetSendDistance(final int distance) {
++ this.setSendDistance(distance);
++ }
++
++ // internal methods
++
++ public int getSendDistance() {
++ final int loadDistance = this.getLoadDistance();
++ return this.rawSendDistance == -1 ? loadDistance : Math.min(this.rawSendDistance, loadDistance);
++ }
++
++ public void setSendDistance(final int distance) {
++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE + 1)) {
++ throw new IllegalArgumentException("Send distance must be a number between " + MIN_VIEW_DISTANCE + " and " + (MAX_VIEW_DISTANCE + 1) + ", or -1, got: " + distance);
++ }
++ this.rawSendDistance = distance;
++ }
++
++ public int getLoadDistance() {
++ final int tickDistance = this.getTickDistance();
++ return this.rawLoadDistance == -1 ? tickDistance + 1 : Math.max(tickDistance + 1, this.rawLoadDistance);
++ }
++
++ public void setLoadDistance(final int distance) {
++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE + 1)) {
++ throw new IllegalArgumentException("Load distance must be a number between " + MIN_VIEW_DISTANCE + " and " + (MAX_VIEW_DISTANCE + 1) + ", or -1, got: " + distance);
++ }
++ this.rawLoadDistance = distance;
++ }
++
++ public int getTickDistance() {
++ return this.rawTickDistance;
++ }
++
++ public void setTickDistance(final int distance) {
++ if (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE) {
++ throw new IllegalArgumentException("View distance must be a number between " + MIN_VIEW_DISTANCE + " and " + MAX_VIEW_DISTANCE + ", got: " + distance);
++ }
++ this.rawTickDistance = distance;
++ }
++
++ /*
++ Players have 3 different types of view distance:
++ 1. Sending view distance
++ 2. Loading view distance
++ 3. Ticking view distance
++
++ But for configuration purposes (and API) there are:
++ 1. No-tick view distance
++ 2. Tick view distance
++ 3. Broadcast view distance
++
++ These aren't always the same as the types we represent internally.
++
++ Loading view distance is always max(no-tick + 1, tick + 1)
++ - no-tick has 1 added because clients need an extra radius to render chunks
++ - tick has 1 added because it needs an extra radius of chunks to load before they can be marked ticking
++
++ Loading view distance is defined as the radius of chunks that will be brought to send-ready status, which means
++ it loads chunks in radius load-view-distance + 1.
++
++ The maximum value for send view distance is the load view distance. API can set it lower.
++ */
++
++ public PlayerChunkLoader(final ChunkMap chunkMap, final PooledLinkedHashSets<ServerPlayer> pooledHashSets) {
++ this.chunkMap = chunkMap;
++ this.broadcastMap = new PlayerAreaMap(pooledHashSets,
++ null,
++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
++ PlayerChunkLoader.this.onChunkLeave(player, rangeX, rangeZ);
++ });
++ this.loadMap = new PlayerAreaMap(pooledHashSets,
++ null,
++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
++ if (newState != null) {
++ return;
++ }
++ PlayerChunkLoader.this.isTargetedForPlayerLoad.remove(CoordinateUtils.getChunkKey(rangeX, rangeZ));
++ });
++ this.loadTicketCleanup = new PlayerAreaMap(pooledHashSets,
++ null,
++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
++ if (newState != null) {
++ return;
++ }
++ ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ);
++ PlayerChunkLoader.this.chunkMap.level.getChunkSource().removeTicketAtLevel(TicketType.PLAYER, chunkPos, LOADED_TICKET_LEVEL, chunkPos);
++ if (PlayerChunkLoader.this.chunkTicketTracker.remove(chunkPos.toLong())) {
++ --PlayerChunkLoader.this.concurrentChunkLoads;
++ }
++ });
++ this.tickMap = new PlayerAreaMap(pooledHashSets,
++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
++ if (newState.size() != 1) {
++ return;
++ }
++ LevelChunk chunk = PlayerChunkLoader.this.chunkMap.level.getChunkSource().getChunkAtIfLoadedMainThreadNoCache(rangeX, rangeZ);
++ if (chunk == null || !chunk.areNeighboursLoaded(2)) {
++ return;
++ }
++
++ ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ);
++ PlayerChunkLoader.this.chunkMap.level.getChunkSource().addTicketAtLevel(TicketType.PLAYER, chunkPos, TICK_TICKET_LEVEL, chunkPos);
++ },
++ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
++ if (newState != null) {
++ return;
++ }
++ ChunkPos chunkPos = new ChunkPos(rangeX, rangeZ);
++ PlayerChunkLoader.this.chunkMap.level.getChunkSource().removeTicketAtLevel(TicketType.PLAYER, chunkPos, TICK_TICKET_LEVEL, chunkPos);
++ });
++ }
++
++ protected final LongOpenHashSet isTargetedForPlayerLoad = new LongOpenHashSet();
++ protected final LongOpenHashSet chunkTicketTracker = new LongOpenHashSet();
++
++ public boolean isChunkNearPlayers(final int chunkX, final int chunkZ) {
++ final PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInSendRange = this.broadcastMap.getObjectsInRange(chunkX, chunkZ);
++
++ return playersInSendRange != null;
++ }
++
++ public void onChunkPostProcessing(final int chunkX, final int chunkZ) {
++ this.onChunkSendReady(chunkX, chunkZ);
++ }
++
++ private boolean chunkNeedsPostProcessing(final int chunkX, final int chunkZ) {
++ final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
++ final ChunkHolder chunk = this.chunkMap.getVisibleChunkIfPresent(key);
++
++ if (chunk == null) {
++ return false;
++ }
++
++ final LevelChunk levelChunk = chunk.getSendingChunk();
++
++ return levelChunk != null && !levelChunk.isPostProcessingDone;
++ }
++
++ // rets whether the chunk is at a loaded stage that is ready to be sent to players
++ public boolean isChunkPlayerLoaded(final int chunkX, final int chunkZ) {
++ final long key = CoordinateUtils.getChunkKey(chunkX, chunkZ);
++ final ChunkHolder chunk = this.chunkMap.getVisibleChunkIfPresent(key);
++
++ if (chunk == null) {
++ return false;
++ }
++
++ final LevelChunk levelChunk = chunk.getSendingChunk();
++
++ return levelChunk != null && levelChunk.isPostProcessingDone && this.isTargetedForPlayerLoad.contains(key);
++ }
++
++ public boolean isChunkSent(final ServerPlayer player, final int chunkX, final int chunkZ, final boolean borderOnly) {
++ return borderOnly ? this.isChunkSentBorderOnly(player, chunkX, chunkZ) : this.isChunkSent(player, chunkX, chunkZ);
++ }
++
++ public boolean isChunkSent(final ServerPlayer player, final int chunkX, final int chunkZ) {
++ final PlayerLoaderData data = this.playerMap.get(player);
++ if (data == null) {
++ return false;
++ }
++
++ return data.hasSentChunk(chunkX, chunkZ);
++ }
++
++ public boolean isChunkSentBorderOnly(final ServerPlayer player, final int chunkX, final int chunkZ) {
++ final PlayerLoaderData data = this.playerMap.get(player);
++ if (data == null) {
++ return false;
++ }
++
++ final boolean center = data.hasSentChunk(chunkX, chunkZ);
++ if (!center) {
++ return false;
++ }
++
++ return !(data.hasSentChunk(chunkX - 1, chunkZ) && data.hasSentChunk(chunkX + 1, chunkZ) &&
++ data.hasSentChunk(chunkX, chunkZ - 1) && data.hasSentChunk(chunkX, chunkZ + 1));
++ }
++
++ protected int getMaxConcurrentChunkSends() {
++ return GlobalConfiguration.get().chunkLoading.maxConcurrentSends;
++ }
++
++ protected int getMaxChunkLoads() {
++ double config = GlobalConfiguration.get().chunkLoading.playerMaxConcurrentLoads;
++ double max = GlobalConfiguration.get().chunkLoading.globalMaxConcurrentLoads;
++ return (int)Math.ceil(Math.min(config * MinecraftServer.getServer().getPlayerCount(), max <= 1.0 ? Double.MAX_VALUE : max));
++ }
++
++ protected long getTargetSendPerPlayerAddend() {
++ return GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.targetPlayerChunkSendRate);
++ }
++
++ protected long getMaxSendAddend() {
++ return GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate <= 1.0 ? 0L : (long)Math.round(1.0e9 / GlobalConfiguration.get().chunkLoading.globalMaxChunkSendRate);
++ }
++
++ public void onChunkPlayerTickReady(final int chunkX, final int chunkZ) {
++ final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
++ this.chunkMap.level.getChunkSource().addTicketAtLevel(TicketType.PLAYER, chunkPos, TICK_TICKET_LEVEL, chunkPos);
++ }
++
++ public void onChunkSendReady(final int chunkX, final int chunkZ) {
++ final PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInSendRange = this.broadcastMap.getObjectsInRange(chunkX, chunkZ);
++
++ if (playersInSendRange == null) {
++ return;
++ }
++
++ final Object[] rawData = playersInSendRange.getBackingSet();
++ for (int i = 0, len = rawData.length; i < len; ++i) {
++ final Object raw = rawData[i];
++
++ if (!(raw instanceof ServerPlayer)) {
++ continue;
++ }
++ this.onChunkSendReady((ServerPlayer)raw, chunkX, chunkZ);
++ }
++ }
++
++ public void onChunkSendReady(final ServerPlayer player, final int chunkX, final int chunkZ) {
++ final PlayerLoaderData data = this.playerMap.get(player);
++
++ if (data == null) {
++ return;
++ }
++
++ if (data.hasSentChunk(chunkX, chunkZ) || !this.isChunkPlayerLoaded(chunkX, chunkZ)) {
++ // if we don't have player tickets, then the load logic will pick this up and queue to send
++ return;
++ }
++
++ if (!data.chunksToBeSent.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) {
++ // don't queue to send, we don't want the chunk
++ return;
++ }
++
++ final long playerPos = this.broadcastMap.getLastCoordinate(player);
++ final int playerChunkX = CoordinateUtils.getChunkX(playerPos);
++ final int playerChunkZ = CoordinateUtils.getChunkZ(playerPos);
++ final int manhattanDistance = Math.abs(playerChunkX - chunkX) + Math.abs(playerChunkZ - chunkZ);
++
++ final ChunkPriorityHolder holder = new ChunkPriorityHolder(chunkX, chunkZ, manhattanDistance, 0.0);
++ data.sendQueue.add(holder);
++ }
++
++ public void onChunkLoad(final int chunkX, final int chunkZ) {
++ if (this.chunkTicketTracker.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) {
++ --this.concurrentChunkLoads;
++ }
++ }
++
++ public void onChunkLeave(final ServerPlayer player, final int chunkX, final int chunkZ) {
++ final PlayerLoaderData data = this.playerMap.get(player);
++
++ if (data == null) {
++ return;
++ }
++
++ data.unloadChunk(chunkX, chunkZ);
++ }
++
++ public void addPlayer(final ServerPlayer player) {
++ TickThread.ensureTickThread("Cannot add player async");
++ if (!player.isRealPlayer) {
++ return;
++ }
++ final PlayerLoaderData data = new PlayerLoaderData(player, this);
++ if (this.playerMap.putIfAbsent(player, data) == null) {
++ data.update();
++ }
++ }
++
++ public void removePlayer(final ServerPlayer player) {
++ TickThread.ensureTickThread("Cannot remove player async");
++ if (!player.isRealPlayer) {
++ return;
++ }
++
++ final PlayerLoaderData loaderData = this.playerMap.remove(player);
++ if (loaderData == null) {
++ return;
++ }
++ loaderData.remove();
++ this.chunkLoadQueue.remove(loaderData);
++ this.chunkSendQueue.remove(loaderData);
++ this.chunkSendWaitQueue.remove(loaderData);
++ synchronized (this.sendingChunkCounts) {
++ final int count = this.sendingChunkCounts.removeInt(loaderData);
++ if (count != 0) {
++ concurrentChunkSends.getAndAdd(-count);
++ }
++ }
++ }
++
++ public void updatePlayer(final ServerPlayer player) {
++ TickThread.ensureTickThread("Cannot update player async");
++ if (!player.isRealPlayer) {
++ return;
++ }
++ final PlayerLoaderData loaderData = this.playerMap.get(player);
++ if (loaderData != null) {
++ loaderData.update();
++ }
++ }
++
++ public PlayerLoaderData getData(final ServerPlayer player) {
++ return this.playerMap.get(player);
++ }
++
++ public void tick() {
++ TickThread.ensureTickThread("Cannot tick async");
++ for (final PlayerLoaderData data : this.playerMap.values()) {
++ data.update();
++ }
++ this.tickMidTick();
++ }
++
++ protected static final AtomicInteger concurrentChunkSends = new AtomicInteger();
++ protected final Reference2IntOpenHashMap<PlayerLoaderData> sendingChunkCounts = new Reference2IntOpenHashMap<>();
++ private static long nextChunkSend;
++ private void trySendChunks() {
++ final long time = System.nanoTime();
++ if (time < nextChunkSend) {
++ return;
++ }
++ // drain entries from wait queue
++ while (!this.chunkSendWaitQueue.isEmpty()) {
++ final PlayerLoaderData data = this.chunkSendWaitQueue.first();
++
++ if (data.nextChunkSendTarget > time) {
++ break;
++ }
++
++ this.chunkSendWaitQueue.pollFirst();
++
++ this.chunkSendQueue.add(data);
++ }
++
++ if (this.chunkSendQueue.isEmpty()) {
++ return;
++ }
++
++ final int maxSends = this.getMaxConcurrentChunkSends();
++ final long nextPlayerDeadline = this.getTargetSendPerPlayerAddend() + time;
++ for (;;) {
++ if (this.chunkSendQueue.isEmpty()) {
++ break;
++ }
++ final int currSends = concurrentChunkSends.get();
++ if (currSends >= maxSends) {
++ break;
++ }
++
++ if (!concurrentChunkSends.compareAndSet(currSends, currSends + 1)) {
++ continue;
++ }
++
++ // send chunk
++
++ final PlayerLoaderData data = this.chunkSendQueue.removeFirst();
++
++ final ChunkPriorityHolder queuedSend = data.sendQueue.pollFirst();
++ if (queuedSend == null) {
++ concurrentChunkSends.getAndDecrement(); // we never sent, so decrease
++ // stop iterating over players who have nothing to send
++ if (this.chunkSendQueue.isEmpty()) {
++ // nothing left
++ break;
++ }
++ continue;
++ }
++
++ if (!this.isChunkPlayerLoaded(queuedSend.chunkX, queuedSend.chunkZ)) {
++ throw new IllegalStateException();
++ }
++
++ data.nextChunkSendTarget = nextPlayerDeadline;
++ this.chunkSendWaitQueue.add(data);
++
++ synchronized (this.sendingChunkCounts) {
++ this.sendingChunkCounts.addTo(data, 1);
++ }
++
++ data.sendChunk(queuedSend.chunkX, queuedSend.chunkZ, () -> {
++ synchronized (this.sendingChunkCounts) {
++ final int count = this.sendingChunkCounts.getInt(data);
++ if (count == 0) {
++ // disconnected, so we don't need to decrement: it will be decremented for us
++ return;
++ }
++ if (count == 1) {
++ this.sendingChunkCounts.removeInt(data);
++ } else {
++ this.sendingChunkCounts.put(data, count - 1);
++ }
++ }
++
++ concurrentChunkSends.getAndDecrement();
++ });
++
++ nextChunkSend = this.getMaxSendAddend() + time;
++ if (time < nextChunkSend) {
++ break;
++ }
++ }
++ }
++
++ protected int concurrentChunkLoads;
++ // this interval prevents bursting a lot of chunk loads
++ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_SHORT = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms
++ // this interval ensures the rate is kept between ticks correctly
++ protected static final IntervalledCounter TICKET_ADDITION_COUNTER_LONG = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms
++ private void tryLoadChunks() {
++ if (this.chunkLoadQueue.isEmpty()) {
++ return;
++ }
++
++ final int maxLoads = this.getMaxChunkLoads();
++ final long time = System.nanoTime();
++ boolean updatedCounters = false;
++ for (;;) {
++ final PlayerLoaderData data = this.chunkLoadQueue.pollFirst();
++
++ data.lastChunkLoad = time;
++
++ final ChunkPriorityHolder queuedLoad = data.loadQueue.peekFirst();
++ if (queuedLoad == null) {
++ if (this.chunkLoadQueue.isEmpty()) {
++ break;
++ }
++ continue;
++ }
++
++ if (!updatedCounters) {
++ updatedCounters = true;
++ TICKET_ADDITION_COUNTER_SHORT.updateCurrentTime(time);
++ TICKET_ADDITION_COUNTER_LONG.updateCurrentTime(time);
++ data.ticketAdditionCounterShort.updateCurrentTime(time);
++ data.ticketAdditionCounterLong.updateCurrentTime(time);
++ }
++
++ if (this.isChunkPlayerLoaded(queuedLoad.chunkX, queuedLoad.chunkZ)) {
++ // already loaded!
++ data.loadQueue.pollFirst(); // already loaded so we just skip
++ this.chunkLoadQueue.add(data);
++
++ // ensure the chunk is queued to send
++ this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ);
++ continue;
++ }
++
++ final long chunkKey = CoordinateUtils.getChunkKey(queuedLoad.chunkX, queuedLoad.chunkZ);
++
++ final double priority = queuedLoad.priority;
++ // while we do need to rate limit chunk loads, the logic for sending chunks requires that tickets are present.
++ // when chunks are loaded (i.e spawn) but do not have this player's tickets, they have to wait behind the
++ // load queue. To avoid this problem, we check early here if tickets are required to load the chunk - if they
++ // aren't required, it bypasses the limiter system.
++ boolean unloadedTargetChunk = false;
++ unloaded_check:
++ for (int dz = -1; dz <= 1; ++dz) {
++ for (int dx = -1; dx <= 1; ++dx) {
++ final int offX = queuedLoad.chunkX + dx;
++ final int offZ = queuedLoad.chunkZ + dz;
++ if (this.chunkMap.level.getChunkSource().getChunkAtIfLoadedMainThreadNoCache(offX, offZ) == null) {
++ unloadedTargetChunk = true;
++ break unloaded_check;
++ }
++ }
++ }
++ if (unloadedTargetChunk && priority >= 0.0) {
++ // priority >= 0.0 implies rate limited chunks
++
++ final int currentChunkLoads = this.concurrentChunkLoads;
++ if (currentChunkLoads >= maxLoads || (GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate > 0 && (TICKET_ADDITION_COUNTER_SHORT.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate || TICKET_ADDITION_COUNTER_LONG.getRate() >= GlobalConfiguration.get().chunkLoading.globalMaxChunkLoadRate))
++ || (GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate > 0.0 && (data.ticketAdditionCounterShort.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate || data.ticketAdditionCounterLong.getRate() >= GlobalConfiguration.get().chunkLoading.playerMaxChunkLoadRate))) {
++ // don't poll, we didn't load it
++ this.chunkLoadQueue.add(data);
++ break;
++ }
++ }
++
++ // can only poll after we decide to load
++ data.loadQueue.pollFirst();
++
++ // now that we've polled we can re-add to load queue
++ this.chunkLoadQueue.add(data);
++
++ // add necessary tickets to load chunk up to send-ready
++ for (int dz = -1; dz <= 1; ++dz) {
++ for (int dx = -1; dx <= 1; ++dx) {
++ final int offX = queuedLoad.chunkX + dx;
++ final int offZ = queuedLoad.chunkZ + dz;
++ final ChunkPos chunkPos = new ChunkPos(offX, offZ);
++
++ this.chunkMap.level.getChunkSource().addTicketAtLevel(TicketType.PLAYER, chunkPos, LOADED_TICKET_LEVEL, chunkPos);
++ if (this.chunkMap.level.getChunkSource().getChunkAtIfLoadedMainThreadNoCache(offX, offZ) != null) {
++ continue;
++ }
++
++ if (priority > 0.0 && this.chunkTicketTracker.add(CoordinateUtils.getChunkKey(offX, offZ))) {
++ // won't reach here if unloadedTargetChunk is false
++ ++this.concurrentChunkLoads;
++ TICKET_ADDITION_COUNTER_SHORT.addTime(time);
++ TICKET_ADDITION_COUNTER_LONG.addTime(time);
++ data.ticketAdditionCounterShort.addTime(time);
++ data.ticketAdditionCounterLong.addTime(time);
++ }
++ }
++ }
++
++ // mark that we've added tickets here
++ this.isTargetedForPlayerLoad.add(chunkKey);
++
++ // it's possible all we needed was the player tickets to queue up the send.
++ if (this.isChunkPlayerLoaded(queuedLoad.chunkX, queuedLoad.chunkZ)) {
++ // yup, all we needed.
++ this.onChunkSendReady(queuedLoad.chunkX, queuedLoad.chunkZ);
++ } else if (this.chunkNeedsPostProcessing(queuedLoad.chunkX, queuedLoad.chunkZ)) {
++ // requires post processing
++ this.chunkMap.mainThreadExecutor.execute(() -> {
++ final long key = CoordinateUtils.getChunkKey(queuedLoad.chunkX, queuedLoad.chunkZ);
++ final ChunkHolder holder = PlayerChunkLoader.this.chunkMap.getVisibleChunkIfPresent(key);
++
++ if (holder == null) {
++ return;
++ }
++
++ final LevelChunk chunk = holder.getSendingChunk();
++
++ if (chunk != null && !chunk.isPostProcessingDone) {
++ chunk.postProcessGeneration();
++ }
++ });
++ }
++ }
++ }
++
++ public void tickMidTick() {
++ // try to send more chunks
++ this.trySendChunks();
++
++ // try to queue more chunks to load
++ this.tryLoadChunks();
++ }
++
++ static final class ChunkPriorityHolder {
++ public final int chunkX;
++ public final int chunkZ;
++ public final int manhattanDistanceToPlayer;
++ public final double priority;
++
++ public ChunkPriorityHolder(final int chunkX, final int chunkZ, final int manhattanDistanceToPlayer, final double priority) {
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ this.manhattanDistanceToPlayer = manhattanDistanceToPlayer;
++ this.priority = priority;
++ }
++ }
++
++ public static final class PlayerLoaderData {
++
++ protected static final float FOV = 110.0f;
++ protected static final double PRIORITISED_DISTANCE = 12.0 * 16.0;
++
++ // Player max sprint speed is approximately 8m/s
++ protected static final double LOOK_PRIORITY_SPEED_THRESHOLD = (10.0/20.0) * (10.0/20.0);
++ protected static final double LOOK_PRIORITY_YAW_DELTA_RECALC_THRESHOLD = 3.0f;
++
++ protected double lastLocX = Double.NEGATIVE_INFINITY;
++ protected double lastLocZ = Double.NEGATIVE_INFINITY;
++
++ protected int lastChunkX = Integer.MIN_VALUE;
++ protected int lastChunkZ = Integer.MIN_VALUE;
++
++ // this is corrected so that 0 is along the positive x-axis
++ protected float lastYaw = Float.NEGATIVE_INFINITY;
++
++ protected int lastSendDistance = Integer.MIN_VALUE;
++ protected int lastLoadDistance = Integer.MIN_VALUE;
++ protected int lastTickDistance = Integer.MIN_VALUE;
++ protected boolean usingLookingPriority;
++
++ protected final ServerPlayer player;
++ protected final PlayerChunkLoader loader;
++
++ // warning: modifications of this field must be aware that the loadQueue inside PlayerChunkLoader uses this field
++ // in a comparator!
++ protected final ArrayDeque<ChunkPriorityHolder> loadQueue = new ArrayDeque<>();
++ protected final LongOpenHashSet sentChunks = new LongOpenHashSet();
++ protected final LongOpenHashSet chunksToBeSent = new LongOpenHashSet();
++
++ protected final TreeSet<ChunkPriorityHolder> sendQueue = new TreeSet<>((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> {
++ final int distanceCompare = Integer.compare(p1.manhattanDistanceToPlayer, p2.manhattanDistanceToPlayer);
++ if (distanceCompare != 0) {
++ return distanceCompare;
++ }
++
++ final int coordinateXCompare = Integer.compare(p1.chunkX, p2.chunkX);
++ if (coordinateXCompare != 0) {
++ return coordinateXCompare;
++ }
++
++ return Integer.compare(p1.chunkZ, p2.chunkZ);
++ });
++
++ protected int sendViewDistance = -1;
++ protected int loadViewDistance = -1;
++ protected int tickViewDistance = -1;
++
++ protected long nextChunkSendTarget;
++
++ // this interval prevents bursting a lot of chunk loads
++ protected final IntervalledCounter ticketAdditionCounterShort = new IntervalledCounter((long)(1.0e6 * 50.0)); // 50ms
++ // this ensures the rate is kept between ticks correctly
++ protected final IntervalledCounter ticketAdditionCounterLong = new IntervalledCounter((long)(1.0e6 * 1000.0)); // 1000ms
++
++ public long lastChunkLoad;
++
++ public PlayerLoaderData(final ServerPlayer player, final PlayerChunkLoader loader) {
++ this.player = player;
++ this.loader = loader;
++ }
++
++ // these view distance methods are for api
++ public int getTargetSendViewDistance() {
++ final int tickViewDistance = this.tickViewDistance == -1 ? this.loader.getTickDistance() : this.tickViewDistance;
++ final int loadViewDistance = Math.max(tickViewDistance + 1, this.loadViewDistance == -1 ? this.loader.getLoadDistance() : this.loadViewDistance);
++ final int clientViewDistance = this.getClientViewDistance();
++ final int sendViewDistance = Math.min(loadViewDistance, this.sendViewDistance == -1 ? (!GlobalConfiguration.get().chunkLoading.autoconfigSendDistance || clientViewDistance == -1 ? this.loader.getSendDistance() : clientViewDistance + 1) : this.sendViewDistance);
++ return sendViewDistance;
++ }
++
++ public void setTargetSendViewDistance(final int distance) {
++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE + 1)) {
++ throw new IllegalArgumentException("Send view distance must be a number between " + MIN_VIEW_DISTANCE + " and " + (MAX_VIEW_DISTANCE + 1) + " or -1, got: " + distance);
++ }
++ this.sendViewDistance = distance;
++ }
++
++ public int getTargetNoTickViewDistance() {
++ return (this.loadViewDistance == -1 ? this.getLoadDistance() : this.loadViewDistance) - 1;
++ }
++
++ public void setTargetNoTickViewDistance(final int distance) {
++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE)) {
++ throw new IllegalArgumentException("Simulation distance must be a number between " + MIN_VIEW_DISTANCE + " and " + MAX_VIEW_DISTANCE + " or -1, got: " + distance);
++ }
++ this.loadViewDistance = distance == -1 ? -1 : distance + 1;
++ }
++
++ public int getTargetTickViewDistance() {
++ return this.tickViewDistance == -1 ? this.loader.getTickDistance() : this.tickViewDistance;
++ }
++
++ public void setTargetTickViewDistance(final int distance) {
++ if (distance != -1 && (distance < MIN_VIEW_DISTANCE || distance > MAX_VIEW_DISTANCE)) {
++ throw new IllegalArgumentException("View distance must be a number between " + MIN_VIEW_DISTANCE + " and " + MAX_VIEW_DISTANCE + " or -1, got: " + distance);
++ }
++ this.tickViewDistance = distance;
++ }
++
++ protected int getLoadDistance() {
++ final int tickViewDistance = this.tickViewDistance == -1 ? this.loader.getTickDistance() : this.tickViewDistance;
++
++ return Math.max(tickViewDistance + 1, this.loadViewDistance == -1 ? this.loader.getLoadDistance() : this.loadViewDistance);
++ }
++
++ public boolean hasSentChunk(final int chunkX, final int chunkZ) {
++ return this.sentChunks.contains(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
++
++ public void sendChunk(final int chunkX, final int chunkZ, final Runnable onChunkSend) {
++ if (this.sentChunks.add(CoordinateUtils.getChunkKey(chunkX, chunkZ))) {
++ this.player.getLevel().getChunkSource().chunkMap.updateChunkTracking(this.player,
++ new ChunkPos(chunkX, chunkZ), new MutableObject<>(), false, true); // unloaded, loaded
++ this.player.connection.connection.execute(onChunkSend);
++ } else {
++ throw new IllegalStateException();
++ }
++ }
++
++ public void unloadChunk(final int chunkX, final int chunkZ) {
++ if (this.sentChunks.remove(CoordinateUtils.getChunkKey(chunkX, chunkZ))) {
++ this.player.getLevel().getChunkSource().chunkMap.updateChunkTracking(this.player,
++ new ChunkPos(chunkX, chunkZ), null, true, false); // unloaded, loaded
++ }
++ }
++
++ protected static boolean wantChunkLoaded(final int centerX, final int centerZ, final int chunkX, final int chunkZ,
++ final int sendRadius) {
++ // expect sendRadius to be = 1 + target viewable radius
++ return ChunkMap.isChunkInRange(chunkX, chunkZ, centerX, centerZ, sendRadius);
++ }
++
++ protected static boolean triangleIntersects(final double p1x, final double p1z, // triangle point
++ final double p2x, final double p2z, // triangle point
++ final double p3x, final double p3z, // triangle point
++
++ final double targetX, final double targetZ) { // point
++ // from barycentric coordinates:
++ // targetX = a*p1x + b*p2x + c*p3x
++ // targetZ = a*p1z + b*p2z + c*p3z
++ // 1.0 = a*1.0 + b*1.0 + c*1.0
++ // where a, b, c >= 0.0
++ // so, if any of a, b, c are less-than zero then there is no intersection.
++
++ // d = ((p2z - p3z)(p1x - p3x) + (p3x - p2x)(p1z - p3z))
++ // a = ((p2z - p3z)(targetX - p3x) + (p3x - p2x)(targetZ - p3z)) / d
++ // b = ((p3z - p1z)(targetX - p3x) + (p1x - p3x)(targetZ - p3z)) / d
++ // c = 1.0 - a - b
++
++ final double d = (p2z - p3z)*(p1x - p3x) + (p3x - p2x)*(p1z - p3z);
++ final double a = ((p2z - p3z)*(targetX - p3x) + (p3x - p2x)*(targetZ - p3z)) / d;
++
++ if (a < 0.0 || a > 1.0) {
++ return false;
++ }
++
++ final double b = ((p3z - p1z)*(targetX - p3x) + (p1x - p3x)*(targetZ - p3z)) / d;
++ if (b < 0.0 || b > 1.0) {
++ return false;
++ }
++
++ final double c = 1.0 - a - b;
++
++ return c >= 0.0 && c <= 1.0;
++ }
++
++ public void remove() {
++ this.loader.broadcastMap.remove(this.player);
++ this.loader.loadMap.remove(this.player);
++ this.loader.loadTicketCleanup.remove(this.player);
++ this.loader.tickMap.remove(this.player);
++ }
++
++ protected int getClientViewDistance() {
++ return this.player.clientViewDistance == null ? -1 : Math.max(0, this.player.clientViewDistance.intValue());
++ }
++
++ public void update() {
++ final int tickViewDistance = this.tickViewDistance == -1 ? this.loader.getTickDistance() : this.tickViewDistance;
++ // load view cannot be less-than tick view + 1
++ final int loadViewDistance = Math.max(tickViewDistance + 1, this.loadViewDistance == -1 ? this.loader.getLoadDistance() : this.loadViewDistance);
++ // send view cannot be greater-than load view
++ final int clientViewDistance = this.getClientViewDistance();
++ final int sendViewDistance = Math.min(loadViewDistance, this.sendViewDistance == -1 ? (!GlobalConfiguration.get().chunkLoading.autoconfigSendDistance || clientViewDistance == -1 ? this.loader.getSendDistance() : clientViewDistance + 1) : this.sendViewDistance);
++
++ final double posX = this.player.getX();
++ final double posZ = this.player.getZ();
++ final float yaw = MCUtil.normalizeYaw(this.player.getYRot() + 90.0f); // mc yaw 0 is along the positive z axis, but obviously this is really dumb - offset so we are at positive x-axis
++
++ // in general, we really only want to prioritise chunks in front if we know we're moving pretty fast into them.
++ final boolean useLookPriority = GlobalConfiguration.get().chunkLoading.enableFrustumPriority && (this.player.getDeltaMovement().horizontalDistanceSqr() > LOOK_PRIORITY_SPEED_THRESHOLD ||
++ this.player.getAbilities().flying);
++
++ // make sure we're in the send queue
++ this.loader.chunkSendWaitQueue.add(this);
++
++ if (
++ // has view distance stayed the same?
++ sendViewDistance == this.lastSendDistance
++ && loadViewDistance == this.lastLoadDistance
++ && tickViewDistance == this.lastTickDistance
++
++ && (this.usingLookingPriority ? (
++ // has our block stayed the same (this also accounts for chunk change)?
++ Mth.floor(this.lastLocX) == Mth.floor(posX)
++ && Mth.floor(this.lastLocZ) == Mth.floor(posZ)
++ ) : (
++ // has our chunk stayed the same
++ (Mth.floor(this.lastLocX) >> 4) == (Mth.floor(posX) >> 4)
++ && (Mth.floor(this.lastLocZ) >> 4) == (Mth.floor(posZ) >> 4)
++ ))
++
++ // has our decision about look priority changed?
++ && this.usingLookingPriority == useLookPriority
++
++ // if we are currently using look priority, has our yaw stayed within recalc threshold?
++ && (!this.usingLookingPriority || Math.abs(yaw - this.lastYaw) <= LOOK_PRIORITY_YAW_DELTA_RECALC_THRESHOLD)
++ ) {
++ // nothing we care about changed, so we're not re-calculating
++ return;
++ }
++
++ final int centerChunkX = Mth.floor(posX) >> 4;
++ final int centerChunkZ = Mth.floor(posZ) >> 4;
++
++ final boolean needsChunkCenterUpdate = (centerChunkX != this.lastChunkX) || (centerChunkZ != this.lastChunkZ);
++ this.loader.broadcastMap.addOrUpdate(this.player, centerChunkX, centerChunkZ, sendViewDistance);
++ this.loader.loadMap.addOrUpdate(this.player, centerChunkX, centerChunkZ, loadViewDistance);
++ this.loader.loadTicketCleanup.addOrUpdate(this.player, centerChunkX, centerChunkZ, loadViewDistance + 1);
++ this.loader.tickMap.addOrUpdate(this.player, centerChunkX, centerChunkZ, tickViewDistance);
++
++ if (sendViewDistance != this.lastSendDistance) {
++ // update the view radius for client
++ // note that this should be after the map calls because the client wont expect unload calls not in its VD
++ // and it's possible we decreased VD here
++ this.player.connection.send(new ClientboundSetChunkCacheRadiusPacket(sendViewDistance));
++ }
++ if (tickViewDistance != this.lastTickDistance) {
++ this.player.connection.send(new ClientboundSetSimulationDistancePacket(tickViewDistance));
++ }
++
++ this.lastLocX = posX;
++ this.lastLocZ = posZ;
++ this.lastYaw = yaw;
++ this.lastSendDistance = sendViewDistance;
++ this.lastLoadDistance = loadViewDistance;
++ this.lastTickDistance = tickViewDistance;
++ this.usingLookingPriority = useLookPriority;
++
++ this.lastChunkX = centerChunkX;
++ this.lastChunkZ = centerChunkZ;
++
++ // points for player "view" triangle:
++
++ // obviously, the player pos is a vertex
++ final double p1x = posX;
++ final double p1z = posZ;
++
++ // to the left of the looking direction
++ final double p2x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector
++ + p1x; // offset vector
++ final double p2z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw + (double)(FOV / 2.0))) // calculate rotated vector
++ + p1z; // offset vector
++
++ // to the right of the looking direction
++ final double p3x = PRIORITISED_DISTANCE * Math.cos(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector
++ + p1x; // offset vector
++ final double p3z = PRIORITISED_DISTANCE * Math.sin(Math.toRadians(yaw - (double)(FOV / 2.0))) // calculate rotated vector
++ + p1z; // offset vector
++
++ // now that we have all of our points, we can recalculate the load queue
++
++ final List<ChunkPriorityHolder> loadQueue = new ArrayList<>();
++
++ // clear send queue, we are re-sorting
++ this.sendQueue.clear();
++ // clear chunk want set, vd/position might have changed
++ this.chunksToBeSent.clear();
++
++ final int searchViewDistance = Math.max(loadViewDistance, sendViewDistance);
++
++ for (int dx = -searchViewDistance; dx <= searchViewDistance; ++dx) {
++ for (int dz = -searchViewDistance; dz <= searchViewDistance; ++dz) {
++ final int chunkX = dx + centerChunkX;
++ final int chunkZ = dz + centerChunkZ;
++ final int squareDistance = Math.max(Math.abs(dx), Math.abs(dz));
++ final boolean sendChunk = squareDistance <= sendViewDistance && wantChunkLoaded(centerChunkX, centerChunkZ, chunkX, chunkZ, sendViewDistance);
++
++ if (this.hasSentChunk(chunkX, chunkZ)) {
++ // already sent (which means it is also loaded)
++ if (!sendChunk) {
++ // have sent the chunk, but don't want it anymore
++ // unload it now
++ this.unloadChunk(chunkX, chunkZ);
++ }
++ continue;
++ }
++
++ final boolean loadChunk = squareDistance <= loadViewDistance;
++
++ final boolean prioritised = useLookPriority && triangleIntersects(
++ // prioritisation triangle
++ p1x, p1z, p2x, p2z, p3x, p3z,
++
++ // center of chunk
++ (double)((chunkX << 4) | 8), (double)((chunkZ << 4) | 8)
++ );
++
++ final int manhattanDistance = Math.abs(dx) + Math.abs(dz);
++
++ final double priority;
++
++ if (squareDistance <= GlobalConfiguration.get().chunkLoading.minLoadRadius) {
++ // priority should be negative, and we also want to order it from center outwards
++ // so we want (0,0) to be the smallest, and (minLoadedRadius,minLoadedRadius) to be the greatest
++ priority = -((2 * GlobalConfiguration.get().chunkLoading.minLoadRadius + 1) - manhattanDistance);
++ } else {
++ if (prioritised) {
++ // we don't prioritise these chunks above others because we also want to make sure some chunks
++ // will be loaded if the player changes direction
++ priority = (double)manhattanDistance / 6.0;
++ } else {
++ priority = (double)manhattanDistance;
++ }
++ }
++
++ final ChunkPriorityHolder holder = new ChunkPriorityHolder(chunkX, chunkZ, manhattanDistance, priority);
++
++ if (!this.loader.isChunkPlayerLoaded(chunkX, chunkZ)) {
++ if (loadChunk) {
++ loadQueue.add(holder);
++ if (sendChunk) {
++ this.chunksToBeSent.add(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
++ }
++ } else {
++ // loaded but not sent: so queue it!
++ if (sendChunk) {
++ this.sendQueue.add(holder);
++ }
++ }
++ }
++ }
++
++ loadQueue.sort((final ChunkPriorityHolder p1, final ChunkPriorityHolder p2) -> {
++ return Double.compare(p1.priority, p2.priority);
++ });
++
++ // we're modifying loadQueue, must remove
++ this.loader.chunkLoadQueue.remove(this);
++
++ this.loadQueue.clear();
++ this.loadQueue.addAll(loadQueue);
++
++ // must re-add
++ this.loader.chunkLoadQueue.add(this);
++
++ // update the chunk center
++ // this must be done last so that the client does not ignore any of our unload chunk packets
++ if (needsChunkCenterUpdate) {
++ this.player.connection.send(new ClientboundSetChunkCacheCenterPacket(centerChunkX, centerChunkZ));
++ }
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..969f43ffc2e28aac45d1145d35ab37c8740b0880
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/entity/EntityLookup.java
+@@ -0,0 +1,839 @@
++package io.papermc.paper.chunk.system.entity;
++
++import com.destroystokyo.paper.util.maplist.EntityList;
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.util.CoordinateUtils;
++import io.papermc.paper.util.TickThread;
++import io.papermc.paper.util.WorldUtil;
++import io.papermc.paper.world.ChunkEntitySlices;
++import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
++import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
++import net.minecraft.core.BlockPos;
++import net.minecraft.server.ChunkSystem;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ChunkMap;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.util.Mth;
++import net.minecraft.world.entity.Entity;
++import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.level.entity.EntityInLevelCallback;
++import net.minecraft.world.level.entity.EntityTypeTest;
++import net.minecraft.world.level.entity.LevelCallback;
++import net.minecraft.world.level.entity.LevelEntityGetter;
++import net.minecraft.world.level.entity.Visibility;
++import net.minecraft.world.phys.AABB;
++import org.jetbrains.annotations.NotNull;
++import org.jetbrains.annotations.Nullable;
++import org.slf4j.Logger;
++import java.util.ArrayList;
++import java.util.Iterator;
++import java.util.List;
++import java.util.NoSuchElementException;
++import java.util.UUID;
++import java.util.concurrent.locks.StampedLock;
++import java.util.function.Consumer;
++import java.util.function.Predicate;
++
++public final class EntityLookup implements LevelEntityGetter<Entity> {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ protected static final int REGION_SHIFT = 5;
++ protected static final int REGION_MASK = (1 << REGION_SHIFT) - 1;
++ protected static final int REGION_SIZE = 1 << REGION_SHIFT;
++
++ public final ServerLevel world;
++
++ private final StampedLock stateLock = new StampedLock();
++ protected final Long2ObjectOpenHashMap<ChunkSlicesRegion> regions = new Long2ObjectOpenHashMap<>(128, 0.5f);
++
++ private final int minSection; // inclusive
++ private final int maxSection; // inclusive
++ private final LevelCallback<Entity> worldCallback;
++
++ private final StampedLock entityByLock = new StampedLock();
++ private final Int2ReferenceOpenHashMap<Entity> entityById = new Int2ReferenceOpenHashMap<>();
++ private final Object2ReferenceOpenHashMap<UUID, Entity> entityByUUID = new Object2ReferenceOpenHashMap<>();
++ private final EntityList accessibleEntities = new EntityList();
++
++ public EntityLookup(final ServerLevel world, final LevelCallback<Entity> worldCallback) {
++ this.world = world;
++ this.minSection = WorldUtil.getMinSection(world);
++ this.maxSection = WorldUtil.getMaxSection(world);
++ this.worldCallback = worldCallback;
++ }
++
++ private static Entity maskNonAccessible(final Entity entity) {
++ if (entity == null) {
++ return null;
++ }
++ final Visibility visibility = EntityLookup.getEntityStatus(entity);
++ return visibility.isAccessible() ? entity : null;
++ }
++
++ @Nullable
++ @Override
++ public Entity get(final int id) {
++ final long attempt = this.entityByLock.tryOptimisticRead();
++ if (attempt != 0L) {
++ try {
++ final Entity ret = this.entityById.get(id);
++
++ if (this.entityByLock.validate(attempt)) {
++ return maskNonAccessible(ret);
++ }
++ } catch (final Error error) {
++ throw error;
++ } catch (final Throwable thr) {
++ // ignore
++ }
++ }
++
++ this.entityByLock.readLock();
++ try {
++ return maskNonAccessible(this.entityById.get(id));
++ } finally {
++ this.entityByLock.tryUnlockRead();
++ }
++ }
++
++ @Nullable
++ @Override
++ public Entity get(final UUID id) {
++ final long attempt = this.entityByLock.tryOptimisticRead();
++ if (attempt != 0L) {
++ try {
++ final Entity ret = this.entityByUUID.get(id);
++
++ if (this.entityByLock.validate(attempt)) {
++ return maskNonAccessible(ret);
++ }
++ } catch (final Error error) {
++ throw error;
++ } catch (final Throwable thr) {
++ // ignore
++ }
++ }
++
++ this.entityByLock.readLock();
++ try {
++ return maskNonAccessible(this.entityByUUID.get(id));
++ } finally {
++ this.entityByLock.tryUnlockRead();
++ }
++ }
++
++ public boolean hasEntity(final UUID uuid) {
++ return this.get(uuid) != null;
++ }
++
++ public String getDebugInfo() {
++ return "count_id:" + this.entityById.size() + ",count_uuid:" + this.entityByUUID.size() + ",region_count:" + this.regions.size();
++ }
++
++ static final class ArrayIterable<T> implements Iterable<T> {
++
++ private final T[] array;
++ private final int off;
++ private final int length;
++
++ public ArrayIterable(final T[] array, final int off, final int length) {
++ this.array = array;
++ this.off = off;
++ this.length = length;
++ if (length > array.length) {
++ throw new IllegalArgumentException("Length must be no greater-than the array length");
++ }
++ }
++
++ @NotNull
++ @Override
++ public Iterator<T> iterator() {
++ return new ArrayIterator<>(this.array, this.off, this.length);
++ }
++
++ static final class ArrayIterator<T> implements Iterator<T> {
++
++ private final T[] array;
++ private int off;
++ private final int length;
++
++ public ArrayIterator(final T[] array, final int off, final int length) {
++ this.array = array;
++ this.off = off;
++ this.length = length;
++ }
++
++ @Override
++ public boolean hasNext() {
++ return this.off < this.length;
++ }
++
++ @Override
++ public T next() {
++ if (this.off >= this.length) {
++ throw new NoSuchElementException();
++ }
++ return this.array[this.off++];
++ }
++
++ @Override
++ public void remove() {
++ throw new UnsupportedOperationException();
++ }
++ }
++ }
++
++ @Override
++ public Iterable<Entity> getAll() {
++ return new ArrayIterable<>(this.accessibleEntities.getRawData(), 0, this.accessibleEntities.size());
++ }
++
++ @Override
++ public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final Consumer<U> action) {
++ for (final Entity entity : this.entityById.values()) {
++ final Visibility visibility = EntityLookup.getEntityStatus(entity);
++ if (!visibility.isAccessible()) {
++ continue;
++ }
++ final U casted = filter.tryCast(entity);
++ if (casted != null) {
++ action.accept(casted);
++ }
++ }
++ }
++
++ @Override
++ public void get(final AABB box, final Consumer<Entity> action) {
++ List<Entity> entities = new ArrayList<>();
++ this.getEntitiesWithoutDragonParts(null, box, entities, null);
++ for (int i = 0, len = entities.size(); i < len; ++i) {
++ action.accept(entities.get(i));
++ }
++ }
++
++ @Override
++ public <U extends Entity> void get(final EntityTypeTest<Entity, U> filter, final AABB box, final Consumer<U> action) {
++ List<Entity> entities = new ArrayList<>();
++ this.getEntitiesWithoutDragonParts(null, box, entities, null);
++ for (int i = 0, len = entities.size(); i < len; ++i) {
++ final U casted = filter.tryCast(entities.get(i));
++ if (casted != null) {
++ action.accept(casted);
++ }
++ }
++ }
++
++ public void entityStatusChange(final Entity entity, final ChunkEntitySlices slices, final Visibility oldVisibility, final Visibility newVisibility, final boolean moved,
++ final boolean created, final boolean destroyed) {
++ TickThread.ensureTickThread(entity, "Entity status change must only happen on the main thread");
++
++ if (entity.updatingSectionStatus) {
++ // recursive status update
++ LOGGER.error("Cannot recursively update entity chunk status for entity " + entity, new Throwable());
++ return;
++ }
++
++ final boolean entityStatusUpdateBefore = slices == null ? false : slices.startPreventingStatusUpdates();
++
++ if (entityStatusUpdateBefore) {
++ LOGGER.error("Cannot update chunk status for entity " + entity + " since entity chunk (" + slices.chunkX + "," + slices.chunkZ + ") is receiving update", new Throwable());
++ return;
++ }
++
++ try {
++ final Boolean ticketBlockBefore = this.world.chunkTaskScheduler.chunkHolderManager.blockTicketUpdates();
++ try {
++ entity.updatingSectionStatus = true;
++ try {
++ if (created) {
++ EntityLookup.this.worldCallback.onCreated(entity);
++ }
++
++ if (oldVisibility == newVisibility) {
++ if (moved && newVisibility.isAccessible()) {
++ EntityLookup.this.worldCallback.onSectionChange(entity);
++ }
++ return;
++ }
++
++ if (newVisibility.ordinal() > oldVisibility.ordinal()) {
++ // status upgrade
++ if (!oldVisibility.isAccessible() && newVisibility.isAccessible()) {
++ this.accessibleEntities.add(entity);
++ EntityLookup.this.worldCallback.onTrackingStart(entity);
++ }
++
++ if (!oldVisibility.isTicking() && newVisibility.isTicking()) {
++ EntityLookup.this.worldCallback.onTickingStart(entity);
++ }
++ } else {
++ // status downgrade
++ if (oldVisibility.isTicking() && !newVisibility.isTicking()) {
++ EntityLookup.this.worldCallback.onTickingEnd(entity);
++ }
++
++ if (oldVisibility.isAccessible() && !newVisibility.isAccessible()) {
++ this.accessibleEntities.remove(entity);
++ EntityLookup.this.worldCallback.onTrackingEnd(entity);
++ }
++ }
++
++ if (moved && newVisibility.isAccessible()) {
++ EntityLookup.this.worldCallback.onSectionChange(entity);
++ }
++
++ if (destroyed) {
++ EntityLookup.this.worldCallback.onDestroyed(entity);
++ }
++ } finally {
++ entity.updatingSectionStatus = false;
++ }
++ } finally {
++ this.world.chunkTaskScheduler.chunkHolderManager.unblockTicketUpdates(ticketBlockBefore);
++ }
++ } finally {
++ if (slices != null) {
++ slices.stopPreventingStatusUpdates(false);
++ }
++ }
++ }
++
++ public void chunkStatusChange(final int x, final int z, final ChunkHolder.FullChunkStatus newStatus) {
++ this.getChunk(x, z).updateStatus(newStatus, this);
++ }
++
++ public void addLegacyChunkEntities(final List<Entity> entities) {
++ for (int i = 0, len = entities.size(); i < len; ++i) {
++ this.addEntity(entities.get(i), true);
++ }
++ }
++
++ public void addEntityChunkEntities(final List<Entity> entities) {
++ for (int i = 0, len = entities.size(); i < len; ++i) {
++ this.addEntity(entities.get(i), true);
++ }
++ }
++
++ public void addWorldGenChunkEntities(final List<Entity> entities) {
++ for (int i = 0, len = entities.size(); i < len; ++i) {
++ this.addEntity(entities.get(i), false);
++ }
++ }
++
++ public boolean addNewEntity(final Entity entity) {
++ return this.addEntity(entity, false);
++ }
++
++ public static Visibility getEntityStatus(final Entity entity) {
++ if (entity.isAlwaysTicking()) {
++ return Visibility.TICKING;
++ }
++ final ChunkHolder.FullChunkStatus entityStatus = entity.chunkStatus;
++ return Visibility.fromFullChunkStatus(entityStatus == null ? ChunkHolder.FullChunkStatus.INACCESSIBLE : entityStatus);
++ }
++
++ private boolean addEntity(final Entity entity, final boolean fromDisk) {
++ final BlockPos pos = entity.blockPosition();
++ final int sectionX = pos.getX() >> 4;
++ final int sectionY = Mth.clamp(pos.getY() >> 4, this.minSection, this.maxSection);
++ final int sectionZ = pos.getZ() >> 4;
++ TickThread.ensureTickThread(this.world, sectionX, sectionZ, "Cannot add entity off-main thread");
++
++ if (entity.isRemoved()) {
++ LOGGER.warn("Refusing to add removed entity: " + entity);
++ return false;
++ }
++
++ if (entity.updatingSectionStatus) {
++ LOGGER.warn("Entity " + entity + " is currently prevented from being added/removed to world since it is processing section status updates", new Throwable());
++ return false;
++ }
++
++ if (fromDisk) {
++ ChunkSystem.onEntityPreAdd(this.world, entity);
++ if (entity.isRemoved()) {
++ // removed from checkDupeUUID call
++ return false;
++ }
++ }
++
++ this.entityByLock.writeLock();
++ try {
++ if (this.entityById.containsKey(entity.getId())) {
++ LOGGER.warn("Entity id already exists: " + entity.getId() + ", mapped to " + this.entityById.get(entity.getId()) + ", can't add " + entity, new Throwable());
++ return false;
++ }
++ if (this.entityByUUID.containsKey(entity.getUUID())) {
++ LOGGER.warn("Entity uuid already exists: " + entity.getUUID() + ", mapped to " + this.entityByUUID.get(entity.getUUID()) + ", can't add " + entity, new Throwable());
++ return false;
++ }
++ this.entityById.put(entity.getId(), entity);
++ this.entityByUUID.put(entity.getUUID(), entity);
++ } finally {
++ this.entityByLock.tryUnlockWrite();
++ }
++
++ entity.sectionX = sectionX;
++ entity.sectionY = sectionY;
++ entity.sectionZ = sectionZ;
++ final ChunkEntitySlices slices = this.getOrCreateChunk(sectionX, sectionZ);
++ if (!slices.addEntity(entity, sectionY)) {
++ LOGGER.warn("Entity " + entity + " added to world '" + this.world.getWorld().getName() + "', but was already contained in entity chunk (" + sectionX + "," + sectionZ + ")");
++ }
++
++ entity.setLevelCallback(new EntityCallback(entity));
++
++ this.entityStatusChange(entity, slices, Visibility.HIDDEN, getEntityStatus(entity), false, !fromDisk, false);
++
++ return true;
++ }
++
++ private void removeEntity(final Entity entity) {
++ final int sectionX = entity.sectionX;
++ final int sectionY = entity.sectionY;
++ final int sectionZ = entity.sectionZ;
++ TickThread.ensureTickThread(this.world, sectionX, sectionZ, "Cannot remove entity off-main");
++ if (!entity.isRemoved()) {
++ throw new IllegalStateException("Only call Entity#setRemoved to remove an entity");
++ }
++ final ChunkEntitySlices slices = this.getChunk(sectionX, sectionZ);
++ // all entities should be in a chunk
++ if (slices == null) {
++ LOGGER.warn("Cannot remove entity " + entity + " from null entity slices (" + sectionX + "," + sectionZ + ")");
++ } else {
++ if (!slices.removeEntity(entity, sectionY)) {
++ LOGGER.warn("Failed to remove entity " + entity + " from entity slices (" + sectionX + "," + sectionZ + ")");
++ }
++ }
++ entity.sectionX = entity.sectionY = entity.sectionZ = Integer.MIN_VALUE;
++
++ this.entityByLock.writeLock();
++ try {
++ if (!this.entityById.remove(entity.getId(), entity)) {
++ LOGGER.warn("Failed to remove entity " + entity + " by id, current entity mapped: " + this.entityById.get(entity.getId()));
++ }
++ if (!this.entityByUUID.remove(entity.getUUID(), entity)) {
++ LOGGER.warn("Failed to remove entity " + entity + " by uuid, current entity mapped: " + this.entityByUUID.get(entity.getUUID()));
++ }
++ } finally {
++ this.entityByLock.tryUnlockWrite();
++ }
++ }
++
++ private ChunkEntitySlices moveEntity(final Entity entity) {
++ // ensure we own the entity
++ TickThread.ensureTickThread(entity, "Cannot move entity off-main");
++
++ final BlockPos newPos = entity.blockPosition();
++ final int newSectionX = newPos.getX() >> 4;
++ final int newSectionY = Mth.clamp(newPos.getY() >> 4, this.minSection, this.maxSection);
++ final int newSectionZ = newPos.getZ() >> 4;
++
++ if (newSectionX == entity.sectionX && newSectionY == entity.sectionY && newSectionZ == entity.sectionZ) {
++ return null;
++ }
++
++ // ensure the new section is owned by this tick thread
++ TickThread.ensureTickThread(this.world, newSectionX, newSectionZ, "Cannot move entity off-main");
++
++ // ensure the old section is owned by this tick thread
++ TickThread.ensureTickThread(this.world, entity.sectionX, entity.sectionZ, "Cannot move entity off-main");
++
++ final ChunkEntitySlices old = this.getChunk(entity.sectionX, entity.sectionZ);
++ final ChunkEntitySlices slices = this.getOrCreateChunk(newSectionX, newSectionZ);
++
++ if (!old.removeEntity(entity, entity.sectionY)) {
++ LOGGER.warn("Could not remove entity " + entity + " from its old chunk section (" + entity.sectionX + "," + entity.sectionY + "," + entity.sectionZ + ") since it was not contained in the section");
++ }
++
++ if (!slices.addEntity(entity, newSectionY)) {
++ LOGGER.warn("Could not add entity " + entity + " to its new chunk section (" + newSectionX + "," + newSectionY + "," + newSectionZ + ") as it is already contained in the section");
++ }
++
++ entity.sectionX = newSectionX;
++ entity.sectionY = newSectionY;
++ entity.sectionZ = newSectionZ;
++
++ return slices;
++ }
++
++ public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4;
++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4;
++
++ final int minRegionX = minChunkX >> REGION_SHIFT;
++ final int minRegionZ = minChunkZ >> REGION_SHIFT;
++ final int maxRegionX = maxChunkX >> REGION_SHIFT;
++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
++
++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
++
++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
++
++ if (region == null) {
++ continue;
++ }
++
++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
++
++ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
++ for (int currX = minX; currX <= maxX; ++currX) {
++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ continue;
++ }
++
++ chunk.getEntitiesWithoutDragonParts(except, box, into, predicate);
++ }
++ }
++ }
++ }
++ }
++
++ public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4;
++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4;
++
++ final int minRegionX = minChunkX >> REGION_SHIFT;
++ final int minRegionZ = minChunkZ >> REGION_SHIFT;
++ final int maxRegionX = maxChunkX >> REGION_SHIFT;
++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
++
++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
++
++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
++
++ if (region == null) {
++ continue;
++ }
++
++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
++
++ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
++ for (int currX = minX; currX <= maxX; ++currX) {
++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ continue;
++ }
++
++ chunk.getEntities(except, box, into, predicate);
++ }
++ }
++ }
++ }
++ }
++
++ public void getHardCollidingEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4;
++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4;
++
++ final int minRegionX = minChunkX >> REGION_SHIFT;
++ final int minRegionZ = minChunkZ >> REGION_SHIFT;
++ final int maxRegionX = maxChunkX >> REGION_SHIFT;
++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
++
++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
++
++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
++
++ if (region == null) {
++ continue;
++ }
++
++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
++
++ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
++ for (int currX = minX; currX <= maxX; ++currX) {
++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ continue;
++ }
++
++ chunk.getHardCollidingEntities(except, box, into, predicate);
++ }
++ }
++ }
++ }
++ }
++
++ public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
++ final Predicate<? super T> predicate) {
++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4;
++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4;
++
++ final int minRegionX = minChunkX >> REGION_SHIFT;
++ final int minRegionZ = minChunkZ >> REGION_SHIFT;
++ final int maxRegionX = maxChunkX >> REGION_SHIFT;
++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
++
++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
++
++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
++
++ if (region == null) {
++ continue;
++ }
++
++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
++
++ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
++ for (int currX = minX; currX <= maxX; ++currX) {
++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ continue;
++ }
++
++ chunk.getEntities(type, box, (List)into, (Predicate)predicate);
++ }
++ }
++ }
++ }
++ }
++
++ public <T extends Entity> void getEntities(final Class<? extends T> clazz, final Entity except, final AABB box, final List<? super T> into,
++ final Predicate<? super T> predicate) {
++ final int minChunkX = (Mth.floor(box.minX) - 2) >> 4;
++ final int minChunkZ = (Mth.floor(box.minZ) - 2) >> 4;
++ final int maxChunkX = (Mth.floor(box.maxX) + 2) >> 4;
++ final int maxChunkZ = (Mth.floor(box.maxZ) + 2) >> 4;
++
++ final int minRegionX = minChunkX >> REGION_SHIFT;
++ final int minRegionZ = minChunkZ >> REGION_SHIFT;
++ final int maxRegionX = maxChunkX >> REGION_SHIFT;
++ final int maxRegionZ = maxChunkZ >> REGION_SHIFT;
++
++ for (int currRegionZ = minRegionZ; currRegionZ <= maxRegionZ; ++currRegionZ) {
++ final int minZ = currRegionZ == minRegionZ ? minChunkZ & REGION_MASK : 0;
++ final int maxZ = currRegionZ == maxRegionZ ? maxChunkZ & REGION_MASK : REGION_MASK;
++
++ for (int currRegionX = minRegionX; currRegionX <= maxRegionX; ++currRegionX) {
++ final ChunkSlicesRegion region = this.getRegion(currRegionX, currRegionZ);
++
++ if (region == null) {
++ continue;
++ }
++
++ final int minX = currRegionX == minRegionX ? minChunkX & REGION_MASK : 0;
++ final int maxX = currRegionX == maxRegionX ? maxChunkX & REGION_MASK : REGION_MASK;
++
++ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
++ for (int currX = minX; currX <= maxX; ++currX) {
++ final ChunkEntitySlices chunk = region.get(currX | (currZ << REGION_SHIFT));
++ if (chunk == null || !chunk.status.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ continue;
++ }
++
++ chunk.getEntities(clazz, except, box, into, predicate);
++ }
++ }
++ }
++ }
++ }
++
++ public void entitySectionLoad(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) {
++ TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot load in entity section off-main");
++ synchronized (this) {
++ final ChunkEntitySlices curr = this.getChunk(chunkX, chunkZ);
++ if (curr != null) {
++ this.removeChunk(chunkX, chunkZ);
++
++ curr.mergeInto(slices);
++
++ this.addChunk(chunkX, chunkZ, slices);
++ } else {
++ this.addChunk(chunkX, chunkZ, slices);
++ }
++ }
++ }
++
++ public void entitySectionUnload(final int chunkX, final int chunkZ) {
++ TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot unload entity section off-main");
++ this.removeChunk(chunkX, chunkZ);
++ }
++
++ public ChunkEntitySlices getChunk(final int chunkX, final int chunkZ) {
++ final ChunkSlicesRegion region = this.getRegion(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
++ if (region == null) {
++ return null;
++ }
++
++ return region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT));
++ }
++
++ public ChunkEntitySlices getOrCreateChunk(final int chunkX, final int chunkZ) {
++ final ChunkSlicesRegion region = this.getRegion(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
++ ChunkEntitySlices ret;
++ if (region == null || (ret = region.get((chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT))) == null) {
++ // loadInEntityChunk will call addChunk for us
++ return this.world.chunkTaskScheduler.chunkHolderManager.getOrCreateEntityChunk(chunkX, chunkZ, true);
++ }
++
++ return ret;
++ }
++
++ public ChunkSlicesRegion getRegion(final int regionX, final int regionZ) {
++ final long key = CoordinateUtils.getChunkKey(regionX, regionZ);
++ final long attempt = this.stateLock.tryOptimisticRead();
++ if (attempt != 0L) {
++ try {
++ final ChunkSlicesRegion ret = this.regions.get(key);
++
++ if (this.stateLock.validate(attempt)) {
++ return ret;
++ }
++ } catch (final Error error) {
++ throw error;
++ } catch (final Throwable thr) {
++ // ignore
++ }
++ }
++
++ this.stateLock.readLock();
++ try {
++ return this.regions.get(key);
++ } finally {
++ this.stateLock.tryUnlockRead();
++ }
++ }
++
++ private synchronized void removeChunk(final int chunkX, final int chunkZ) {
++ final long key = CoordinateUtils.getChunkKey(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
++ final int relIndex = (chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT);
++
++ final ChunkSlicesRegion region = this.regions.get(key);
++ final int remaining = region.remove(relIndex);
++
++ if (remaining == 0) {
++ this.stateLock.writeLock();
++ try {
++ this.regions.remove(key);
++ } finally {
++ this.stateLock.tryUnlockWrite();
++ }
++ }
++ }
++
++ public synchronized void addChunk(final int chunkX, final int chunkZ, final ChunkEntitySlices slices) {
++ final long key = CoordinateUtils.getChunkKey(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT);
++ final int relIndex = (chunkX & REGION_MASK) | ((chunkZ & REGION_MASK) << REGION_SHIFT);
++
++ ChunkSlicesRegion region = this.regions.get(key);
++ if (region != null) {
++ region.add(relIndex, slices);
++ } else {
++ region = new ChunkSlicesRegion();
++ region.add(relIndex, slices);
++ this.stateLock.writeLock();
++ try {
++ this.regions.put(key, region);
++ } finally {
++ this.stateLock.tryUnlockWrite();
++ }
++ }
++ }
++
++ public static final class ChunkSlicesRegion {
++
++ protected final ChunkEntitySlices[] slices = new ChunkEntitySlices[REGION_SIZE * REGION_SIZE];
++ protected int sliceCount;
++
++ public ChunkEntitySlices get(final int index) {
++ return this.slices[index];
++ }
++
++ public int remove(final int index) {
++ final ChunkEntitySlices slices = this.slices[index];
++ if (slices == null) {
++ throw new IllegalStateException();
++ }
++
++ this.slices[index] = null;
++
++ return --this.sliceCount;
++ }
++
++ public void add(final int index, final ChunkEntitySlices slices) {
++ final ChunkEntitySlices curr = this.slices[index];
++ if (curr != null) {
++ throw new IllegalStateException();
++ }
++
++ this.slices[index] = slices;
++
++ ++this.sliceCount;
++ }
++ }
++
++ private final class EntityCallback implements EntityInLevelCallback {
++
++ public final Entity entity;
++
++ public EntityCallback(final Entity entity) {
++ this.entity = entity;
++ }
++
++ @Override
++ public void onMove() {
++ final Entity entity = this.entity;
++ final Visibility oldVisibility = getEntityStatus(entity);
++ final ChunkEntitySlices newSlices = EntityLookup.this.moveEntity(this.entity);
++ if (newSlices == null) {
++ // no new section, so didn't change sections
++ return;
++ }
++ final Visibility newVisibility = getEntityStatus(entity);
++
++ EntityLookup.this.entityStatusChange(entity, newSlices, oldVisibility, newVisibility, true, false, false);
++ }
++
++ @Override
++ public void onRemove(final Entity.RemovalReason reason) {
++ final Entity entity = this.entity;
++ TickThread.ensureTickThread(entity, "Cannot remove entity off-main"); // Paper - rewrite chunk system
++ final Visibility tickingState = EntityLookup.getEntityStatus(entity);
++
++ EntityLookup.this.removeEntity(entity);
++
++ EntityLookup.this.entityStatusChange(entity, null, tickingState, Visibility.HIDDEN, false, false, reason.shouldDestroy());
++
++ this.entity.setLevelCallback(NoOpCallback.INSTANCE);
++ }
++ }
++
++ private static final class NoOpCallback implements EntityInLevelCallback {
++
++ public static final NoOpCallback INSTANCE = new NoOpCallback();
++
++ @Override
++ public void onMove() {}
++
++ @Override
++ public void onRemove(final Entity.RemovalReason reason) {}
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..de137486f610e9042853512f630b9dcc74b29280
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java
+@@ -0,0 +1,1328 @@
++package io.papermc.paper.chunk.system.io;
++
++import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
++import ca.spottedleaf.concurrentutil.executor.Cancellable;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedQueueExecutorThread;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadedTaskQueue;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.util.CoordinateUtils;
++import io.papermc.paper.util.TickThread;
++import it.unimi.dsi.fastutil.HashCommon;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.storage.RegionFile;
++import net.minecraft.world.level.chunk.storage.RegionFileStorage;
++import org.slf4j.Logger;
++import java.io.IOException;
++import java.lang.invoke.VarHandle;
++import java.util.concurrent.CompletableFuture;
++import java.util.concurrent.CompletionException;
++import java.util.concurrent.ConcurrentHashMap;
++import java.util.concurrent.atomic.AtomicInteger;
++import java.util.function.BiConsumer;
++import java.util.function.BiFunction;
++import java.util.function.Consumer;
++import java.util.function.Function;
++
++/**
++ * Prioritised RegionFile I/O executor, responsible for all RegionFile access.
++ * <p>
++ * All functions provided are MT-Safe, however certain ordering constraints are recommended:
++ * <li>
++ * Chunk saves may not occur for unloaded chunks.
++ * </li>
++ * <li>
++ * Tasks must be scheduled on the chunk scheduler thread.
++ * </li>
++ * By following these constraints, no chunk data loss should occur with the exception of underlying I/O problems.
++ * </p>
++ */
++public final class RegionFileIOThread extends PrioritisedQueueExecutorThread {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ /**
++ * The kinds of region files controlled by the region file thread. Add more when needed, and ensure
++ * getControllerFor is updated.
++ */
++ public static enum RegionFileType {
++ CHUNK_DATA,
++ POI_DATA,
++ ENTITY_DATA;
++ }
++
++ protected static final RegionFileType[] CACHED_REGIONFILE_TYPES = RegionFileType.values();
++
++ private ChunkDataController getControllerFor(final ServerLevel world, final RegionFileType type) {
++ switch (type) {
++ case CHUNK_DATA:
++ return world.chunkDataControllerNew;
++ case POI_DATA:
++ return world.poiDataControllerNew;
++ case ENTITY_DATA:
++ return world.entityDataControllerNew;
++ default:
++ throw new IllegalStateException("Unknown controller type " + type);
++ }
++ }
++
++ /**
++ * Collects regionfile data for a certain chunk.
++ */
++ public static final class RegionFileData {
++
++ private final boolean[] hasResult = new boolean[CACHED_REGIONFILE_TYPES.length];
++ private final CompoundTag[] data = new CompoundTag[CACHED_REGIONFILE_TYPES.length];
++ private final Throwable[] throwables = new Throwable[CACHED_REGIONFILE_TYPES.length];
++
++ /**
++ * Sets the result associated with the specified regionfile type. Note that
++ * results can only be set once per regionfile type.
++ *
++ * @param type The regionfile type.
++ * @param data The result to set.
++ */
++ public void setData(final RegionFileType type, final CompoundTag data) {
++ final int index = type.ordinal();
++
++ if (this.hasResult[index]) {
++ throw new IllegalArgumentException("Result already exists for type " + type);
++ }
++ this.hasResult[index] = true;
++ this.data[index] = data;
++ }
++
++ /**
++ * Sets the result associated with the specified regionfile type. Note that
++ * results can only be set once per regionfile type.
++ *
++ * @param type The regionfile type.
++ * @param throwable The result to set.
++ */
++ public void setThrowable(final RegionFileType type, final Throwable throwable) {
++ final int index = type.ordinal();
++
++ if (this.hasResult[index]) {
++ throw new IllegalArgumentException("Result already exists for type " + type);
++ }
++ this.hasResult[index] = true;
++ this.throwables[index] = throwable;
++ }
++
++ /**
++ * Returns whether there is a result for the specified regionfile type.
++ *
++ * @param type Specified regionfile type.
++ *
++ * @return Whether a result exists for {@code type}.
++ */
++ public boolean hasResult(final RegionFileType type) {
++ return this.hasResult[type.ordinal()];
++ }
++
++ /**
++ * Returns the data result for the regionfile type.
++ *
++ * @param type Specified regionfile type.
++ *
++ * @throws IllegalArgumentException If the result has not been set for {@code type}.
++ * @return The data result for the specified type. If the result is a {@code Throwable},
++ * then returns {@code null}.
++ */
++ public CompoundTag getData(final RegionFileType type) {
++ final int index = type.ordinal();
++
++ if (!this.hasResult[index]) {
++ throw new IllegalArgumentException("Result does not exist for type " + type);
++ }
++
++ return this.data[index];
++ }
++
++ /**
++ * Returns the throwable result for the regionfile type.
++ *
++ * @param type Specified regionfile type.
++ *
++ * @throws IllegalArgumentException If the result has not been set for {@code type}.
++ * @return The throwable result for the specified type. If the result is an {@code CompoundTag},
++ * then returns {@code null}.
++ */
++ public Throwable getThrowable(final RegionFileType type) {
++ final int index = type.ordinal();
++
++ if (!this.hasResult[index]) {
++ throw new IllegalArgumentException("Result does not exist for type " + type);
++ }
++
++ return this.throwables[index];
++ }
++ }
++
++ private static final Object INIT_LOCK = new Object();
++
++ static RegionFileIOThread[] threads;
++
++ /* needs to be consistent given a set of parameters */
++ static RegionFileIOThread selectThread(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
++ if (threads == null) {
++ throw new IllegalStateException("Threads not initialised");
++ }
++
++ final int regionX = chunkX >> 5;
++ final int regionZ = chunkZ >> 5;
++ final int typeOffset = type.ordinal();
++
++ return threads[(System.identityHashCode(world) + regionX + regionZ + typeOffset) % threads.length];
++ }
++
++ /**
++ * Shuts down the I/O executor(s). Watis for all tasks to complete if specified.
++ * Tasks queued during this call might not be accepted, and tasks queued after will not be accepted.
++ *
++ * @param wait Whether to wait until all tasks have completed.
++ */
++ public static void close(final boolean wait) {
++ for (int i = 0, len = threads.length; i < len; ++i) {
++ threads[i].close(false, true);
++ }
++ if (wait) {
++ RegionFileIOThread.flush();
++ }
++ }
++
++ public static long[] getExecutedTasks() {
++ final long[] ret = new long[threads.length];
++ for (int i = 0, len = threads.length; i < len; ++i) {
++ ret[i] = threads[i].getTotalTasksExecuted();
++ }
++
++ return ret;
++ }
++
++ public static long[] getTasksScheduled() {
++ final long[] ret = new long[threads.length];
++ for (int i = 0, len = threads.length; i < len; ++i) {
++ ret[i] = threads[i].getTotalTasksScheduled();
++ }
++ return ret;
++ }
++
++ public static void flush() {
++ for (int i = 0, len = threads.length; i < len; ++i) {
++ threads[i].waitUntilAllExecuted();
++ }
++ }
++
++ public static void partialFlush(final int totalTasksRemaining) {
++ long failures = 1L; // start out at 0.25ms
++
++ for (;;) {
++ final long[] executed = getExecutedTasks();
++ final long[] scheduled = getTasksScheduled();
++
++ long sum = 0;
++ for (int i = 0; i < executed.length; ++i) {
++ sum += scheduled[i] - executed[i];
++ }
++
++ if (sum <= totalTasksRemaining) {
++ break;
++ }
++
++ failures = ConcurrentUtil.linearLongBackoff(failures, 250_000L, 5_000_000L); // 500us, 5ms
++ }
++ }
++
++ /**
++ * Inits the executor with the specified number of threads.
++ *
++ * @param threads Specified number of threads.
++ */
++ public static void init(final int threads) {
++ synchronized (INIT_LOCK) {
++ if (RegionFileIOThread.threads != null) {
++ throw new IllegalStateException("Already initialised threads");
++ }
++
++ RegionFileIOThread.threads = new RegionFileIOThread[threads];
++
++ for (int i = 0; i < threads; ++i) {
++ RegionFileIOThread.threads[i] = new RegionFileIOThread(i);
++ RegionFileIOThread.threads[i].start();
++ }
++ }
++ }
++
++ private RegionFileIOThread(final int threadNumber) {
++ super(new PrioritisedThreadedTaskQueue(), (int)(1.0e6)); // 1.0ms spinwait time
++ this.setName("RegionFile I/O Thread #" + threadNumber);
++ this.setPriority(Thread.NORM_PRIORITY - 2); // we keep priority close to normal because threads can wait on us
++ this.setUncaughtExceptionHandler((final Thread thread, final Throwable thr) -> {
++ LOGGER.error("Uncaught exception thrown from I/O thread, report this! Thread: " + thread.getName(), thr);
++ });
++ }
++
++ /**
++ * Returns whether the current thread is a regionfile I/O executor.
++ * @return Whether the current thread is a regionfile I/O executor.
++ */
++ public static boolean isRegionFileThread() {
++ return Thread.currentThread() instanceof RegionFileIOThread;
++ }
++
++ /**
++ * Returns the priority associated with blocking I/O based on the current thread. The goal is to avoid
++ * dumb plugins from taking away priority from threads we consider crucial.
++ * @return The priroity to use with blocking I/O on the current thread.
++ */
++ public static PrioritisedExecutor.Priority getIOBlockingPriorityForCurrentThread() {
++ if (TickThread.isTickThread()) {
++ return PrioritisedExecutor.Priority.BLOCKING;
++ }
++ return PrioritisedExecutor.Priority.HIGHEST;
++ }
++
++ /**
++ * Returns the current {@code CompoundTag} pending for write for the specified chunk & regionfile type.
++ * Note that this does not copy the result, so do not modify the result returned.
++ *
++ * @param world Specified world.
++ * @param chunkX Specified chunk x.
++ * @param chunkZ Specified chunk z.
++ * @param type Specified regionfile type.
++ *
++ * @return The compound tag associated for the specified chunk. {@code null} if no write was pending, or if {@code null} is the write pending.
++ */
++ public static CompoundTag getPendingWrite(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
++ final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
++ return thread.getPendingWriteInternal(world, chunkX, chunkZ, type);
++ }
++
++ CompoundTag getPendingWriteInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
++ final ChunkDataController taskController = this.getControllerFor(world, type);
++ final ChunkDataTask task = taskController.tasks.get(Long.valueOf(CoordinateUtils.getChunkKey(chunkX, chunkZ)));
++
++ if (task == null) {
++ return null;
++ }
++
++ final CompoundTag ret = task.inProgressWrite;
++
++ return ret == ChunkDataTask.NOTHING_TO_WRITE ? null : ret;
++ }
++
++ /**
++ * Returns the priority for the specified regionfile type for the specified chunk.
++ * @param world Specified world.
++ * @param chunkX Specified chunk x.
++ * @param chunkZ Specified chunk z.
++ * @param type Specified regionfile type.
++ * @return The priority for the chunk
++ */
++ public static PrioritisedExecutor.Priority getPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
++ final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
++ return thread.getPriorityInternal(world, chunkX, chunkZ, type);
++ }
++
++ PrioritisedExecutor.Priority getPriorityInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type) {
++ final ChunkDataController taskController = this.getControllerFor(world, type);
++ final ChunkDataTask task = taskController.tasks.get(Long.valueOf(CoordinateUtils.getChunkKey(chunkX, chunkZ)));
++
++ if (task == null) {
++ return PrioritisedExecutor.Priority.COMPLETING;
++ }
++
++ return task.prioritisedTask.getPriority();
++ }
++
++ /**
++ * Sets the priority for all regionfile types for the specified chunk. Note that great care should
++ * be taken using this method, as there can be multiple tasks tied to the same chunk that want different
++ * priorities.
++ *
++ * @param world Specified world.
++ * @param chunkX Specified chunk x.
++ * @param chunkZ Specified chunk z.
++ * @param priority New priority.
++ *
++ * @see #raisePriority(ServerLevel, int, int, Priority)
++ * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
++ * @see #lowerPriority(ServerLevel, int, int, Priority)
++ * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
++ */
++ public static void setPriority(final ServerLevel world, final int chunkX, final int chunkZ,
++ final PrioritisedExecutor.Priority priority) {
++ for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
++ RegionFileIOThread.setPriority(world, chunkX, chunkZ, type, priority);
++ }
++ }
++
++ /**
++ * Sets the priority for the specified regionfile type for the specified chunk. Note that great care should
++ * be taken using this method, as there can be multiple tasks tied to the same chunk that want different
++ * priorities.
++ *
++ * @param world Specified world.
++ * @param chunkX Specified chunk x.
++ * @param chunkZ Specified chunk z.
++ * @param type Specified regionfile type.
++ * @param priority New priority.
++ *
++ * @see #raisePriority(ServerLevel, int, int, Priority)
++ * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
++ * @see #lowerPriority(ServerLevel, int, int, Priority)
++ * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
++ */
++ public static void setPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
++ final PrioritisedExecutor.Priority priority) {
++ final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
++ thread.setPriorityInternal(world, chunkX, chunkZ, type, priority);
++ }
++
++ void setPriorityInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
++ final PrioritisedExecutor.Priority priority) {
++ final ChunkDataController taskController = this.getControllerFor(world, type);
++ final ChunkDataTask task = taskController.tasks.get(Long.valueOf(CoordinateUtils.getChunkKey(chunkX, chunkZ)));
++
++ if (task != null) {
++ task.prioritisedTask.setPriority(priority);
++ }
++ }
++
++ /**
++ * Raises the priority for all regionfile types for the specified chunk.
++ *
++ * @param world Specified world.
++ * @param chunkX Specified chunk x.
++ * @param chunkZ Specified chunk z.
++ * @param priority New priority.
++ *
++ * @see #setPriority(ServerLevel, int, int, Priority)
++ * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
++ * @see #lowerPriority(ServerLevel, int, int, Priority)
++ * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
++ */
++ public static void raisePriority(final ServerLevel world, final int chunkX, final int chunkZ,
++ final PrioritisedExecutor.Priority priority) {
++ for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
++ RegionFileIOThread.raisePriority(world, chunkX, chunkZ, type, priority);
++ }
++ }
++
++ /**
++ * Raises the priority for the specified regionfile type for the specified chunk.
++ *
++ * @param world Specified world.
++ * @param chunkX Specified chunk x.
++ * @param chunkZ Specified chunk z.
++ * @param type Specified regionfile type.
++ * @param priority New priority.
++ *
++ * @see #setPriority(ServerLevel, int, int, Priority)
++ * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
++ * @see #lowerPriority(ServerLevel, int, int, Priority)
++ * @see #lowerPriority(ServerLevel, int, int, RegionFileType, Priority)
++ */
++ public static void raisePriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
++ final PrioritisedExecutor.Priority priority) {
++ final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
++ thread.raisePriorityInternal(world, chunkX, chunkZ, type, priority);
++ }
++
++ void raisePriorityInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
++ final PrioritisedExecutor.Priority priority) {
++ final ChunkDataController taskController = this.getControllerFor(world, type);
++ final ChunkDataTask task = taskController.tasks.get(Long.valueOf(CoordinateUtils.getChunkKey(chunkX, chunkZ)));
++
++ if (task != null) {
++ task.prioritisedTask.raisePriority(priority);
++ }
++ }
++
++ /**
++ * Lowers the priority for all regionfile types for the specified chunk.
++ *
++ * @param world Specified world.
++ * @param chunkX Specified chunk x.
++ * @param chunkZ Specified chunk z.
++ * @param priority New priority.
++ *
++ * @see #raisePriority(ServerLevel, int, int, Priority)
++ * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
++ * @see #setPriority(ServerLevel, int, int, Priority)
++ * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
++ */
++ public static void lowerPriority(final ServerLevel world, final int chunkX, final int chunkZ,
++ final PrioritisedExecutor.Priority priority) {
++ for (final RegionFileType type : CACHED_REGIONFILE_TYPES) {
++ RegionFileIOThread.lowerPriority(world, chunkX, chunkZ, type, priority);
++ }
++ }
++
++ /**
++ * Lowers the priority for the specified regionfile type for the specified chunk.
++ *
++ * @param world Specified world.
++ * @param chunkX Specified chunk x.
++ * @param chunkZ Specified chunk z.
++ * @param type Specified regionfile type.
++ * @param priority New priority.
++ *
++ * @see #raisePriority(ServerLevel, int, int, Priority)
++ * @see #raisePriority(ServerLevel, int, int, RegionFileType, Priority)
++ * @see #setPriority(ServerLevel, int, int, Priority)
++ * @see #setPriority(ServerLevel, int, int, RegionFileType, Priority)
++ */
++ public static void lowerPriority(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
++ final PrioritisedExecutor.Priority priority) {
++ final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
++ thread.lowerPriorityInternal(world, chunkX, chunkZ, type, priority);
++ }
++
++ void lowerPriorityInternal(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
++ final PrioritisedExecutor.Priority priority) {
++ final ChunkDataController taskController = this.getControllerFor(world, type);
++ final ChunkDataTask task = taskController.tasks.get(Long.valueOf(CoordinateUtils.getChunkKey(chunkX, chunkZ)));
++
++ if (task != null) {
++ task.prioritisedTask.lowerPriority(priority);
++ }
++ }
++
++ /**
++ * Schedules the chunk data to be written asynchronously.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
++ * saves must be scheduled before a chunk is unloaded.
++ * </li>
++ * <li>
++ * Writes may be called concurrently, although only the "later" write will go through.
++ * </li>
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param data Chunk's data
++ * @param type The regionfile type to write to.
++ *
++ * @throws IllegalStateException If the file io thread has shutdown.
++ */
++ public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
++ final RegionFileType type) {
++ RegionFileIOThread.scheduleSave(world, chunkX, chunkZ, data, type, PrioritisedExecutor.Priority.NORMAL);
++ }
++
++ /**
++ * Schedules the chunk data to be written asynchronously.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means
++ * saves must be scheduled before a chunk is unloaded.
++ * </li>
++ * <li>
++ * Writes may be called concurrently, although only the "later" write will go through.
++ * </li>
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param data Chunk's data
++ * @param type The regionfile type to write to.
++ * @param priority The minimum priority to schedule at.
++ *
++ * @throws IllegalStateException If the file io thread has shutdown.
++ */
++ public static void scheduleSave(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
++ final RegionFileType type, final PrioritisedExecutor.Priority priority) {
++ final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
++ thread.scheduleSaveInternal(world, chunkX, chunkZ, data, type, priority);
++ }
++
++ void scheduleSaveInternal(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data,
++ final RegionFileType type, final PrioritisedExecutor.Priority priority) {
++ final ChunkDataController taskController = this.getControllerFor(world, type);
++
++ final boolean[] created = new boolean[1];
++ final ChunkCoordinate key = new ChunkCoordinate(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ final ChunkDataTask task = taskController.tasks.compute(key, (final ChunkCoordinate keyInMap, final ChunkDataTask taskRunning) -> {
++ if (taskRunning == null || taskRunning.failedWrite) {
++ // no task is scheduled or the previous write failed - meaning we need to overwrite it
++
++ // create task
++ final ChunkDataTask newTask = new ChunkDataTask(world, chunkX, chunkZ, taskController, RegionFileIOThread.this, priority);
++ newTask.inProgressWrite = data;
++ created[0] = true;
++
++ return newTask;
++ }
++
++ taskRunning.inProgressWrite = data;
++
++ return taskRunning;
++ });
++
++ if (created[0]) {
++ task.prioritisedTask.queue();
++ } else {
++ task.prioritisedTask.raisePriority(priority);
++ }
++ }
++
++ /**
++ * Schedules a load to be executed asynchronously. This task will load all regionfile types, and then call
++ * {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)}
++ * for single load.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
++ * data is undefined behaviour, and can cause deadlock.
++ * </li>
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param onComplete Consumer to execute once this task has completed
++ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
++ * of this call.
++ *
++ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
++ *
++ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
++ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
++ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
++ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
++ */
++ public static Cancellable loadAllChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
++ final Consumer<RegionFileData> onComplete, final boolean intendingToBlock) {
++ return RegionFileIOThread.loadAllChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, PrioritisedExecutor.Priority.NORMAL);
++ }
++
++ /**
++ * Schedules a load to be executed asynchronously. This task will load all regionfile types, and then call
++ * {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)}
++ * for single load.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
++ * data is undefined behaviour, and can cause deadlock.
++ * </li>
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param onComplete Consumer to execute once this task has completed
++ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
++ * of this call.
++ * @param priority The minimum priority to load the data at.
++ *
++ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
++ *
++ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
++ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
++ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
++ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
++ */
++ public static Cancellable loadAllChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
++ final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
++ final PrioritisedExecutor.Priority priority) {
++ return RegionFileIOThread.loadChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, priority, CACHED_REGIONFILE_TYPES);
++ }
++
++ /**
++ * Schedules a load to be executed asynchronously. This task will load data for the specified regionfile type(s), and
++ * then call {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)}
++ * for single load.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
++ * data is undefined behaviour, and can cause deadlock.
++ * </li>
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param onComplete Consumer to execute once this task has completed
++ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
++ * of this call.
++ * @param types The regionfile type(s) to load.
++ *
++ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
++ *
++ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
++ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
++ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
++ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
++ */
++ public static Cancellable loadChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
++ final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
++ final RegionFileType... types) {
++ return RegionFileIOThread.loadChunkData(world, chunkX, chunkZ, onComplete, intendingToBlock, PrioritisedExecutor.Priority.NORMAL, types);
++ }
++
++ /**
++ * Schedules a load to be executed asynchronously. This task will load data for the specified regionfile type(s), and
++ * then call {@code onComplete}. This is a bulk load operation, see {@link #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)}
++ * for single load.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
++ * data is undefined behaviour, and can cause deadlock.
++ * </li>
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param onComplete Consumer to execute once this task has completed
++ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
++ * of this call.
++ * @param types The regionfile type(s) to load.
++ * @param priority The minimum priority to load the data at.
++ *
++ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
++ *
++ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean)
++ * @see #loadDataAsync(ServerLevel, int, int, RegionFileType, BiConsumer, boolean, Priority)
++ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
++ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
++ */
++ public static Cancellable loadChunkData(final ServerLevel world, final int chunkX, final int chunkZ,
++ final Consumer<RegionFileData> onComplete, final boolean intendingToBlock,
++ final PrioritisedExecutor.Priority priority, final RegionFileType... types) {
++ if (types == null) {
++ throw new NullPointerException("Types cannot be null");
++ }
++ if (types.length == 0) {
++ throw new IllegalArgumentException("Types cannot be empty");
++ }
++
++ final RegionFileData ret = new RegionFileData();
++
++ final Cancellable[] reads = new CancellableRead[types.length];
++ final AtomicInteger completions = new AtomicInteger();
++ final int expectedCompletions = types.length;
++
++ for (int i = 0; i < expectedCompletions; ++i) {
++ final RegionFileType type = types[i];
++ reads[i] = RegionFileIOThread.loadDataAsync(world, chunkX, chunkZ, type,
++ (final CompoundTag data, final Throwable throwable) -> {
++ if (throwable != null) {
++ ret.setThrowable(type, throwable);
++ } else {
++ ret.setData(type, data);
++ }
++
++ if (completions.incrementAndGet() == expectedCompletions) {
++ onComplete.accept(ret);
++ }
++ }, intendingToBlock, priority);
++ }
++
++ return new CancellableReads(reads);
++ }
++
++ /**
++ * Schedules a load to be executed asynchronously. This task will load the specified regionfile type, and then call
++ * {@code onComplete}.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
++ * data is undefined behaviour, and can cause deadlock.
++ * </li>
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param onComplete Consumer to execute once this task has completed
++ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
++ * of this call.
++ *
++ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
++ *
++ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
++ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
++ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
++ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
++ */
++ public static Cancellable loadDataAsync(final ServerLevel world, final int chunkX, final int chunkZ,
++ final RegionFileType type, final BiConsumer<CompoundTag, Throwable> onComplete,
++ final boolean intendingToBlock) {
++ return RegionFileIOThread.loadDataAsync(world, chunkX, chunkZ, type, onComplete, intendingToBlock, PrioritisedExecutor.Priority.NORMAL);
++ }
++
++ /**
++ * Schedules a load to be executed asynchronously. This task will load the specified regionfile type, and then call
++ * {@code onComplete}.
++ * <p>
++ * Impl notes:
++ * </p>
++ * <li>
++ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may
++ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of
++ * data is undefined behaviour, and can cause deadlock.
++ * </li>
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param onComplete Consumer to execute once this task has completed
++ * @param intendingToBlock Whether the caller is intending to block on completion. This only affects the cost
++ * of this call.
++ * @param priority Minimum priority to load the data at.
++ *
++ * @return The {@link Cancellable} for this chunk load. Cancelling it will not affect other loads for the same chunk data.
++ *
++ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, RegionFileType...)
++ * @see #loadChunkData(ServerLevel, int, int, Consumer, boolean, Priority, RegionFileType...)
++ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean)
++ * @see #loadAllChunkData(ServerLevel, int, int, Consumer, boolean, Priority)
++ */
++ public static Cancellable loadDataAsync(final ServerLevel world, final int chunkX, final int chunkZ,
++ final RegionFileType type, final BiConsumer<CompoundTag, Throwable> onComplete,
++ final boolean intendingToBlock, final PrioritisedExecutor.Priority priority) {
++ final RegionFileIOThread thread = RegionFileIOThread.selectThread(world, chunkX, chunkZ, type);
++ return thread.loadDataAsyncInternal(world, chunkX, chunkZ, type, onComplete, intendingToBlock, priority);
++ }
++
++ private static Boolean doesRegionFileExist(final int chunkX, final int chunkZ, final boolean intendingToBlock,
++ final ChunkDataController taskController) {
++ final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
++ if (intendingToBlock) {
++ return taskController.computeForRegionFile(chunkX, chunkZ, true, (final RegionFile file) -> {
++ if (file == null) { // null if no regionfile exists
++ return Boolean.FALSE;
++ }
++
++ return file.hasChunk(chunkPos) ? Boolean.TRUE : Boolean.FALSE;
++ });
++ } else {
++ return taskController.computeForRegionFileIfLoaded(chunkX, chunkZ, (final RegionFile file) -> {
++ if (file == null) { // null if not loaded
++ return Boolean.TRUE;
++ }
++
++ return file.hasChunk(chunkPos) ? Boolean.TRUE : Boolean.FALSE;
++ });
++ }
++ }
++
++ Cancellable loadDataAsyncInternal(final ServerLevel world, final int chunkX, final int chunkZ,
++ final RegionFileType type, final BiConsumer<CompoundTag, Throwable> onComplete,
++ final boolean intendingToBlock, final PrioritisedExecutor.Priority priority) {
++ final ChunkDataController taskController = this.getControllerFor(world, type);
++
++ final ImmediateCallbackCompletion callbackInfo = new ImmediateCallbackCompletion();
++
++ final ChunkCoordinate key = new ChunkCoordinate(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ final BiFunction<ChunkCoordinate, ChunkDataTask, ChunkDataTask> compute = (final ChunkCoordinate keyInMap, final ChunkDataTask running) -> {
++ if (running == null) {
++ // not scheduled
++
++ if (callbackInfo.regionFileCalculation == null) {
++ // caller will compute this outside of compute(), to avoid holding the bin lock
++ callbackInfo.needsRegionFileTest = true;
++ return null;
++ }
++
++ if (callbackInfo.regionFileCalculation == Boolean.FALSE) {
++ // not on disk
++ callbackInfo.data = null;
++ callbackInfo.throwable = null;
++ callbackInfo.completeNow = true;
++ return null;
++ }
++
++ // set up task
++ final ChunkDataTask newTask = new ChunkDataTask(
++ world, chunkX, chunkZ, taskController, RegionFileIOThread.this, priority
++ );
++ newTask.inProgressRead = new RegionFileIOThread.InProgressRead();
++ newTask.inProgressRead.waiters.add(onComplete);
++
++ callbackInfo.tasksNeedsScheduling = true;
++ return newTask;
++ }
++
++ final CompoundTag pendingWrite = running.inProgressWrite;
++
++ if (pendingWrite == ChunkDataTask.NOTHING_TO_WRITE) {
++ // need to add to waiters here, because the regionfile thread will use compute() to lock and check for cancellations
++ if (!running.inProgressRead.addToWaiters(onComplete)) {
++ callbackInfo.data = running.inProgressRead.value;
++ callbackInfo.throwable = running.inProgressRead.throwable;
++ callbackInfo.completeNow = true;
++ }
++ return running;
++ }
++ // using the result sync here - don't bump priority
++
++ // at this stage we have to use the in progress write's data to avoid an order issue
++ callbackInfo.data = pendingWrite;
++ callbackInfo.throwable = null;
++ callbackInfo.completeNow = true;
++ return running;
++ };
++
++ ChunkDataTask curr = taskController.tasks.get(key);
++ if (curr == null) {
++ callbackInfo.regionFileCalculation = doesRegionFileExist(chunkX, chunkZ, intendingToBlock, taskController);
++ }
++ ChunkDataTask ret = taskController.tasks.compute(key, compute);
++ if (callbackInfo.needsRegionFileTest) {
++ // curr isn't null but when we went into compute() it was
++ callbackInfo.regionFileCalculation = doesRegionFileExist(chunkX, chunkZ, intendingToBlock, taskController);
++ // now it should be fine
++ ret = taskController.tasks.compute(key, compute);
++ }
++
++ // needs to be scheduled
++ if (callbackInfo.tasksNeedsScheduling) {
++ ret.prioritisedTask.queue();
++ } else if (callbackInfo.completeNow) {
++ try {
++ onComplete.accept(callbackInfo.data, callbackInfo.throwable);
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ LOGGER.error("Callback " + ConcurrentUtil.genericToString(onComplete) + " synchronously failed to handle chunk data for task " + ret.toString(), thr);
++ }
++ } else {
++ // we're waiting on a task we didn't schedule, so raise its priority to what we want
++ ret.prioritisedTask.raisePriority(priority);
++ }
++
++ return new CancellableRead(onComplete, ret);
++ }
++
++ /**
++ * Schedules a load task to be executed asynchronously, and blocks on that task.
++ *
++ * @param world Chunk's world
++ * @param chunkX Chunk's x coordinate
++ * @param chunkZ Chunk's z coordinate
++ * @param type Regionfile type
++ * @param priority Minimum priority to load the data at.
++ *
++ * @return The chunk data for the chunk. Note that a {@code null} result means the chunk or regionfile does not exist on disk.
++ *
++ * @throws IOException If the load fails for any reason
++ */
++ public static CompoundTag loadData(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileType type,
++ final PrioritisedExecutor.Priority priority) throws IOException {
++ final CompletableFuture<CompoundTag> ret = new CompletableFuture<>();
++
++ RegionFileIOThread.loadDataAsync(world, chunkX, chunkZ, type, (final CompoundTag compound, final Throwable thr) -> {
++ if (thr != null) {
++ ret.completeExceptionally(thr);
++ } else {
++ ret.complete(compound);
++ }
++ }, true, priority);
++
++ try {
++ return ret.join();
++ } catch (final CompletionException ex) {
++ throw new IOException(ex);
++ }
++ }
++
++ private static final class ImmediateCallbackCompletion {
++
++ public CompoundTag data;
++ public Throwable throwable;
++ public boolean completeNow;
++ public boolean tasksNeedsScheduling;
++ public boolean needsRegionFileTest;
++ public Boolean regionFileCalculation;
++
++ }
++
++ static final class CancellableRead implements Cancellable {
++
++ private BiConsumer<CompoundTag, Throwable> callback;
++ private RegionFileIOThread.ChunkDataTask task;
++
++ CancellableRead(final BiConsumer<CompoundTag, Throwable> callback, final RegionFileIOThread.ChunkDataTask task) {
++ this.callback = callback;
++ this.task = task;
++ }
++
++ @Override
++ public boolean cancel() {
++ final BiConsumer<CompoundTag, Throwable> callback = this.callback;
++ final RegionFileIOThread.ChunkDataTask task = this.task;
++
++ if (callback == null || task == null) {
++ return false;
++ }
++
++ this.callback = null;
++ this.task = null;
++
++ final RegionFileIOThread.InProgressRead read = task.inProgressRead;
++
++ // read can be null if no read was scheduled (i.e no regionfile existed or chunk in regionfile didn't)
++ return (read != null && read.waiters.remove(callback));
++ }
++ }
++
++ static final class CancellableReads implements Cancellable {
++
++ private Cancellable[] reads;
++
++ protected static final VarHandle READS_HANDLE = ConcurrentUtil.getVarHandle(CancellableReads.class, "reads", Cancellable[].class);
++
++ CancellableReads(final Cancellable[] reads) {
++ this.reads = reads;
++ }
++
++ @Override
++ public boolean cancel() {
++ final Cancellable[] reads = (Cancellable[])READS_HANDLE.getAndSet((CancellableReads)this, (Cancellable[])null);
++
++ if (reads == null) {
++ return false;
++ }
++
++ boolean ret = false;
++
++ for (final Cancellable read : reads) {
++ ret |= read.cancel();
++ }
++
++ return ret;
++ }
++ }
++
++ static final class InProgressRead {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ CompoundTag value;
++ Throwable throwable;
++ final MultiThreadedQueue<BiConsumer<CompoundTag, Throwable>> waiters = new MultiThreadedQueue<>();
++
++ // rets false if already completed (callback not invoked), true if callback was added
++ boolean addToWaiters(final BiConsumer<CompoundTag, Throwable> callback) {
++ return this.waiters.add(callback);
++ }
++
++ void complete(final RegionFileIOThread.ChunkDataTask task, final CompoundTag value, final Throwable throwable) {
++ this.value = value;
++ this.throwable = throwable;
++
++ BiConsumer<CompoundTag, Throwable> consumer;
++ while ((consumer = this.waiters.pollOrBlockAdds()) != null) {
++ try {
++ consumer.accept(value, throwable);
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ LOGGER.error("Callback " + ConcurrentUtil.genericToString(consumer) + " failed to handle chunk data for task " + task.toString(), thr);
++ }
++ }
++ }
++ }
++
++ /**
++ * Class exists to replace {@link Long} usages as keys inside non-fastutil hashtables. The hash for some Long {@code x}
++ * is defined as {@code (x >>> 32) ^ x}. Chunk keys as long values are defined as {@code ((chunkX & 0xFFFFFFFFL) | (chunkZ << 32))},
++ * which means the hashcode as a Long value will be {@code chunkX ^ chunkZ}. Given that most chunks are created within a radius arounds players,
++ * this will lead to many hash collisions. So, this class uses a better hashing algorithm so that usage of
++ * non-fastutil collections is not degraded.
++ */
++ public static final class ChunkCoordinate implements Comparable<ChunkCoordinate> {
++
++ public final long key;
++
++ public ChunkCoordinate(final long key) {
++ this.key = key;
++ }
++
++ @Override
++ public int hashCode() {
++ return (int)HashCommon.mix(this.key);
++ }
++
++ @Override
++ public boolean equals(final Object obj) {
++ if (this == obj) {
++ return true;
++ }
++
++ if (!(obj instanceof ChunkCoordinate)) {
++ return false;
++ }
++
++ final ChunkCoordinate other = (ChunkCoordinate)obj;
++
++ return this.key == other.key;
++ }
++
++ // This class is intended for HashMap/ConcurrentHashMap usage, which do treeify bin nodes if the chain
++ // is too large. So we should implement compareTo to help.
++ @Override
++ public int compareTo(final RegionFileIOThread.ChunkCoordinate other) {
++ return Long.compare(this.key, other.key);
++ }
++
++ @Override
++ public String toString() {
++ return new ChunkPos(this.key).toString();
++ }
++ }
++
++ public static abstract class ChunkDataController {
++
++ // ConcurrentHashMap synchronizes per chain, so reduce the chance of task's hashes colliding.
++ protected final ConcurrentHashMap<ChunkCoordinate, ChunkDataTask> tasks = new ConcurrentHashMap<>(8192, 0.10f);
++
++ public final RegionFileType type;
++
++ public ChunkDataController(final RegionFileType type) {
++ this.type = type;
++ }
++
++ public abstract RegionFileStorage getCache();
++
++ public abstract void writeData(final int chunkX, final int chunkZ, final CompoundTag compound) throws IOException;
++
++ public abstract CompoundTag readData(final int chunkX, final int chunkZ) throws IOException;
++
++ public boolean hasTasks() {
++ return !this.tasks.isEmpty();
++ }
++
++ public <T> T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function<RegionFile, T> function) {
++ final RegionFileStorage cache = this.getCache();
++ final RegionFile regionFile;
++ synchronized (cache) {
++ try {
++ regionFile = cache.getRegionFile(new ChunkPos(chunkX, chunkZ), existingOnly, true);
++ } catch (final IOException ex) {
++ throw new RuntimeException(ex);
++ }
++ }
++
++ try {
++ return function.apply(regionFile);
++ } finally {
++ if (regionFile != null) {
++ regionFile.fileLock.unlock();
++ }
++ }
++ }
++
++ public <T> T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function<RegionFile, T> function) {
++ final RegionFileStorage cache = this.getCache();
++ final RegionFile regionFile;
++
++ synchronized (cache) {
++ regionFile = cache.getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ));
++ if (regionFile != null) {
++ regionFile.fileLock.lock();
++ }
++ }
++
++ try {
++ return function.apply(regionFile);
++ } finally {
++ if (regionFile != null) {
++ regionFile.fileLock.unlock();
++ }
++ }
++ }
++ }
++
++ static final class ChunkDataTask implements Runnable {
++
++ protected static final CompoundTag NOTHING_TO_WRITE = new CompoundTag();
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ RegionFileIOThread.InProgressRead inProgressRead;
++ volatile CompoundTag inProgressWrite = NOTHING_TO_WRITE; // only needs to be acquire/release
++
++ boolean failedWrite;
++
++ final ServerLevel world;
++ final int chunkX;
++ final int chunkZ;
++ final RegionFileIOThread.ChunkDataController taskController;
++
++ final PrioritisedExecutor.PrioritisedTask prioritisedTask;
++
++ /*
++ * IO thread will perform reads before writes for a given chunk x and z
++ *
++ * How reads/writes are scheduled:
++ *
++ * If read is scheduled while scheduling write, take no special action and just schedule write
++ * If read is scheduled while scheduling read and no write is scheduled, chain the read task
++ *
++ *
++ * If write is scheduled while scheduling read, use the pending write data and ret immediately (so no read is scheduled)
++ * If write is scheduled while scheduling write (ignore read in progress), overwrite the write in progress data
++ *
++ * This allows the reads and writes to act as if they occur synchronously to the thread scheduling them, however
++ * it fails to properly propagate write failures thanks to writes overwriting each other
++ */
++
++ public ChunkDataTask(final ServerLevel world, final int chunkX, final int chunkZ, final RegionFileIOThread.ChunkDataController taskController,
++ final PrioritisedExecutor executor, final PrioritisedExecutor.Priority priority) {
++ this.world = world;
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ this.taskController = taskController;
++ this.prioritisedTask = executor.createTask(this, priority);
++ }
++
++ @Override
++ public String toString() {
++ return "Task for world: '" + this.world.getWorld().getName() + "' at (" + this.chunkX + "," + this.chunkZ +
++ ") type: " + this.taskController.type.name() + ", hash: " + this.hashCode();
++ }
++
++ @Override
++ public void run() {
++ final RegionFileIOThread.InProgressRead read = this.inProgressRead;
++ final ChunkCoordinate chunkKey = new ChunkCoordinate(CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ));
++
++ if (read != null) {
++ final boolean[] canRead = new boolean[] { true };
++
++ if (read.waiters.isEmpty()) {
++ // cancelled read? go to task controller to confirm
++ final ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final ChunkCoordinate keyInMap, final ChunkDataTask valueInMap) -> {
++ if (valueInMap == null) {
++ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!");
++ }
++ if (valueInMap != ChunkDataTask.this) {
++ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
++ }
++
++ if (!read.waiters.isEmpty()) { // as per usual IntelliJ is unable to figure out that there are concurrent accesses.
++ return valueInMap;
++ } else {
++ canRead[0] = false;
++ }
++
++ return valueInMap.inProgressWrite == NOTHING_TO_WRITE ? null : valueInMap;
++ });
++
++ if (inMap == null) {
++ // read is cancelled - and no write pending, so we're done
++ return;
++ }
++ // if there is a write in progress, we don't actually have to worry about waiters gaining new entries -
++ // the readers will just use the in progress write, so the value in canRead is good to use without
++ // further synchronisation.
++ }
++
++ if (canRead[0]) {
++ CompoundTag compound = null;
++ Throwable throwable = null;
++
++ try {
++ compound = this.taskController.readData(this.chunkX, this.chunkZ);
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ throwable = thr;
++ LOGGER.error("Failed to read chunk data for task: " + this.toString(), thr);
++ }
++ read.complete(this, compound, throwable);
++ }
++ }
++
++ CompoundTag write = this.inProgressWrite;
++
++ if (write == NOTHING_TO_WRITE) {
++ final ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final ChunkCoordinate keyInMap, final ChunkDataTask valueInMap) -> {
++ if (valueInMap == null) {
++ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!");
++ }
++ if (valueInMap != ChunkDataTask.this) {
++ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
++ }
++ return valueInMap.inProgressWrite == NOTHING_TO_WRITE ? null : valueInMap;
++ });
++
++ if (inMap == null) {
++ return; // set the task value to null, indicating we're done
++ } // else: inProgressWrite changed, so now we have something to write
++ }
++
++ for (;;) {
++ write = this.inProgressWrite;
++ final CompoundTag dataWritten = write;
++
++ boolean failedWrite = false;
++
++ try {
++ this.taskController.writeData(this.chunkX, this.chunkZ, write);
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ if (thr instanceof RegionFileStorage.RegionFileSizeException) {
++ final int maxSize = RegionFile.MAX_CHUNK_SIZE / (1024 * 1024);
++ LOGGER.error("Chunk at (" + this.chunkX + "," + this.chunkZ + ") in '" + this.world.getWorld().getName() + "' exceeds max size of " + maxSize + "MiB, it has been deleted from disk.");
++ } else {
++ failedWrite = thr instanceof IOException;
++ LOGGER.error("Failed to write chunk data for task: " + this.toString(), thr);
++ }
++ }
++
++ final boolean finalFailWrite = failedWrite;
++ final boolean[] done = new boolean[] { false };
++
++ this.taskController.tasks.compute(chunkKey, (final ChunkCoordinate keyInMap, final ChunkDataTask valueInMap) -> {
++ if (valueInMap == null) {
++ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!");
++ }
++ if (valueInMap != ChunkDataTask.this) {
++ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!");
++ }
++ if (valueInMap.inProgressWrite == dataWritten) {
++ valueInMap.failedWrite = finalFailWrite;
++ done[0] = true;
++ // keep the data in map if we failed the write so we can try to prevent data loss
++ return finalFailWrite ? valueInMap : null;
++ }
++ // different data than expected, means we need to retry write
++ return valueInMap;
++ });
++
++ if (done[0]) {
++ return;
++ }
++
++ // fetch & write new data
++ continue;
++ }
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/light/LightQueue.java b/src/main/java/io/papermc/paper/chunk/system/light/LightQueue.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..0b7a2b0ead4f3bc07bfd9a38c2b7cf024bd140c6
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/light/LightQueue.java
+@@ -0,0 +1,280 @@
++package io.papermc.paper.chunk.system.light;
++
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.starlight.common.light.BlockStarLightEngine;
++import ca.spottedleaf.starlight.common.light.SkyStarLightEngine;
++import ca.spottedleaf.starlight.common.light.StarLightInterface;
++import io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler;
++import io.papermc.paper.util.CoordinateUtils;
++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
++import it.unimi.dsi.fastutil.shorts.ShortCollection;
++import it.unimi.dsi.fastutil.shorts.ShortOpenHashSet;
++import net.minecraft.core.BlockPos;
++import net.minecraft.core.SectionPos;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.ChunkPos;
++import java.util.ArrayList;
++import java.util.HashSet;
++import java.util.List;
++import java.util.Set;
++import java.util.concurrent.CompletableFuture;
++import java.util.function.BooleanSupplier;
++
++public final class LightQueue {
++
++ protected final Long2ObjectOpenHashMap<ChunkTasks> chunkTasks = new Long2ObjectOpenHashMap<>();
++ protected final StarLightInterface manager;
++ protected final ServerLevel world;
++
++ public LightQueue(final StarLightInterface manager) {
++ this.manager = manager;
++ this.world = ((ServerLevel)manager.getWorld());
++ }
++
++ public void lowerPriority(final int chunkX, final int chunkZ, final PrioritisedExecutor.Priority priority) {
++ final ChunkTasks task;
++ synchronized (this) {
++ task = this.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
++ if (task != null) {
++ task.lowerPriority(priority);
++ }
++ }
++
++ public void setPriority(final int chunkX, final int chunkZ, final PrioritisedExecutor.Priority priority) {
++ final ChunkTasks task;
++ synchronized (this) {
++ task = this.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
++ if (task != null) {
++ task.setPriority(priority);
++ }
++ }
++
++ public void raisePriority(final int chunkX, final int chunkZ, final PrioritisedExecutor.Priority priority) {
++ final ChunkTasks task;
++ synchronized (this) {
++ task = this.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
++ if (task != null) {
++ task.raisePriority(priority);
++ }
++ }
++
++ public PrioritisedExecutor.Priority getPriority(final int chunkX, final int chunkZ) {
++ final ChunkTasks task;
++ synchronized (this) {
++ task = this.chunkTasks.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
++ if (task != null) {
++ return task.getPriority();
++ }
++
++ return PrioritisedExecutor.Priority.COMPLETING;
++ }
++
++ public boolean isEmpty() {
++ synchronized (this) {
++ return this.chunkTasks.isEmpty();
++ }
++ }
++
++ public CompletableFuture<Void> queueBlockChange(final BlockPos pos) {
++ final ChunkTasks tasks;
++ synchronized (this) {
++ tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> {
++ return new ChunkTasks(keyInMap, LightQueue.this.manager, LightQueue.this);
++ });
++ tasks.changedPositions.add(pos.immutable());
++ }
++
++ tasks.schedule();
++
++ return tasks.onComplete;
++ }
++
++ public CompletableFuture<Void> queueSectionChange(final SectionPos pos, final boolean newEmptyValue) {
++ final ChunkTasks tasks;
++ synchronized (this) {
++ tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> {
++ return new ChunkTasks(keyInMap, LightQueue.this.manager, LightQueue.this);
++ });
++
++ if (tasks.changedSectionSet == null) {
++ tasks.changedSectionSet = new Boolean[this.manager.maxSection - this.manager.minSection + 1];
++ }
++ tasks.changedSectionSet[pos.getY() - this.manager.minSection] = Boolean.valueOf(newEmptyValue);
++ }
++
++ tasks.schedule();
++
++ return tasks.onComplete;
++ }
++
++ public CompletableFuture<Void> queueChunkLightTask(final ChunkPos pos, final BooleanSupplier lightTask, final PrioritisedExecutor.Priority priority) {
++ final ChunkTasks tasks;
++ synchronized (this) {
++ tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> {
++ return new ChunkTasks(keyInMap, LightQueue.this.manager, LightQueue.this, priority);
++ });
++ if (tasks.lightTasks == null) {
++ tasks.lightTasks = new ArrayList<>();
++ }
++ tasks.lightTasks.add(lightTask);
++ }
++
++ tasks.schedule();
++
++ return tasks.onComplete;
++ }
++
++ public CompletableFuture<Void> queueChunkSkylightEdgeCheck(final SectionPos pos, final ShortCollection sections) {
++ final ChunkTasks tasks;
++ synchronized (this) {
++ tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> {
++ return new ChunkTasks(keyInMap, LightQueue.this.manager, LightQueue.this);
++ });
++
++ ShortOpenHashSet queuedEdges = tasks.queuedEdgeChecksSky;
++ if (queuedEdges == null) {
++ queuedEdges = tasks.queuedEdgeChecksSky = new ShortOpenHashSet();
++ }
++ queuedEdges.addAll(sections);
++ }
++
++ tasks.schedule();
++
++ return tasks.onComplete;
++ }
++
++ public CompletableFuture<Void> queueChunkBlocklightEdgeCheck(final SectionPos pos, final ShortCollection sections) {
++ final ChunkTasks tasks;
++
++ synchronized (this) {
++ tasks = this.chunkTasks.computeIfAbsent(CoordinateUtils.getChunkKey(pos), (final long keyInMap) -> {
++ return new ChunkTasks(keyInMap, LightQueue.this.manager, LightQueue.this);
++ });
++
++ ShortOpenHashSet queuedEdges = tasks.queuedEdgeChecksBlock;
++ if (queuedEdges == null) {
++ queuedEdges = tasks.queuedEdgeChecksBlock = new ShortOpenHashSet();
++ }
++ queuedEdges.addAll(sections);
++ }
++
++ tasks.schedule();
++
++ return tasks.onComplete;
++ }
++
++ public void removeChunk(final ChunkPos pos) {
++ final ChunkTasks tasks;
++ synchronized (this) {
++ tasks = this.chunkTasks.remove(CoordinateUtils.getChunkKey(pos));
++ }
++ if (tasks != null && tasks.cancel()) {
++ tasks.onComplete.complete(null);
++ }
++ }
++
++ protected static final class ChunkTasks implements Runnable {
++
++ final Set<BlockPos> changedPositions = new HashSet<>();
++ Boolean[] changedSectionSet;
++ ShortOpenHashSet queuedEdgeChecksSky;
++ ShortOpenHashSet queuedEdgeChecksBlock;
++ List<BooleanSupplier> lightTasks;
++
++ final CompletableFuture<Void> onComplete = new CompletableFuture<>();
++
++ public final long chunkCoordinate;
++ private final StarLightInterface lightEngine;
++ private final LightQueue queue;
++ private final PrioritisedExecutor.PrioritisedTask task;
++
++ public ChunkTasks(final long chunkCoordinate, final StarLightInterface lightEngine, final LightQueue queue) {
++ this(chunkCoordinate, lightEngine, queue, PrioritisedExecutor.Priority.NORMAL);
++ }
++
++ public ChunkTasks(final long chunkCoordinate, final StarLightInterface lightEngine, final LightQueue queue,
++ final PrioritisedExecutor.Priority priority) {
++ this.chunkCoordinate = chunkCoordinate;
++ this.lightEngine = lightEngine;
++ this.queue = queue;
++ this.task = queue.world.chunkTaskScheduler.lightExecutor.createTask(this, priority);
++ }
++
++ public void schedule() {
++ this.task.queue();
++ }
++
++ public boolean cancel() {
++ return this.task.cancel();
++ }
++
++ public PrioritisedExecutor.Priority getPriority() {
++ return this.task.getPriority();
++ }
++
++ public void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ this.task.lowerPriority(priority);
++ }
++
++ public void setPriority(final PrioritisedExecutor.Priority priority) {
++ this.task.setPriority(priority);
++ }
++
++ public void raisePriority(final PrioritisedExecutor.Priority priority) {
++ this.task.raisePriority(priority);
++ }
++
++ @Override
++ public void run() {
++ final SkyStarLightEngine skyEngine = this.lightEngine.getSkyLightEngine();
++ final BlockStarLightEngine blockEngine = this.lightEngine.getBlockLightEngine();
++ try {
++ synchronized (this.queue) {
++ this.queue.chunkTasks.remove(this.chunkCoordinate);
++ }
++
++ boolean litChunk = false;
++ if (this.lightTasks != null) {
++ for (final BooleanSupplier run : this.lightTasks) {
++ if (run.getAsBoolean()) {
++ litChunk = true;
++ break;
++ }
++ }
++ }
++
++ final long coordinate = this.chunkCoordinate;
++ final int chunkX = CoordinateUtils.getChunkX(coordinate);
++ final int chunkZ = CoordinateUtils.getChunkZ(coordinate);
++
++ final Set<BlockPos> positions = this.changedPositions;
++ final Boolean[] sectionChanges = this.changedSectionSet;
++
++ if (!litChunk) {
++ if (skyEngine != null && (!positions.isEmpty() || sectionChanges != null)) {
++ skyEngine.blocksChangedInChunk(this.lightEngine.getLightAccess(), chunkX, chunkZ, positions, sectionChanges);
++ }
++ if (blockEngine != null && (!positions.isEmpty() || sectionChanges != null)) {
++ blockEngine.blocksChangedInChunk(this.lightEngine.getLightAccess(), chunkX, chunkZ, positions, sectionChanges);
++ }
++
++ if (skyEngine != null && this.queuedEdgeChecksSky != null) {
++ skyEngine.checkChunkEdges(this.lightEngine.getLightAccess(), chunkX, chunkZ, this.queuedEdgeChecksSky);
++ }
++ if (blockEngine != null && this.queuedEdgeChecksBlock != null) {
++ blockEngine.checkChunkEdges(this.lightEngine.getLightAccess(), chunkX, chunkZ, this.queuedEdgeChecksBlock);
++ }
++ }
++
++ this.onComplete.complete(null);
++ } finally {
++ this.lightEngine.releaseSkyLightEngine(skyEngine);
++ this.lightEngine.releaseBlockLightEngine(blockEngine);
++ }
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/poi/PoiChunk.java b/src/main/java/io/papermc/paper/chunk/system/poi/PoiChunk.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b1bdd79044c00635c836dbed327526136ca4bd4e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/poi/PoiChunk.java
+@@ -0,0 +1,211 @@
++package io.papermc.paper.chunk.system.poi;
++
++import com.mojang.logging.LogUtils;
++import com.mojang.serialization.Codec;
++import com.mojang.serialization.DataResult;
++import io.papermc.paper.util.CoordinateUtils;
++import io.papermc.paper.util.TickThread;
++import io.papermc.paper.util.WorldUtil;
++import net.minecraft.SharedConstants;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.nbt.NbtOps;
++import net.minecraft.nbt.Tag;
++import net.minecraft.resources.RegistryOps;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.entity.ai.village.poi.PoiManager;
++import net.minecraft.world.entity.ai.village.poi.PoiSection;
++import org.slf4j.Logger;
++
++import java.util.Optional;
++
++public final class PoiChunk {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ public final ServerLevel world;
++ public final int chunkX;
++ public final int chunkZ;
++ public final int minSection;
++ public final int maxSection;
++
++ protected final PoiSection[] sections;
++
++ private boolean isDirty;
++ private boolean loaded;
++
++ public PoiChunk(final ServerLevel world, final int chunkX, final int chunkZ, final int minSection, final int maxSection) {
++ this(world, chunkX, chunkZ, minSection, maxSection, new PoiSection[maxSection - minSection + 1]);
++ }
++
++ public PoiChunk(final ServerLevel world, final int chunkX, final int chunkZ, final int minSection, final int maxSection, final PoiSection[] sections) {
++ this.world = world;
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ this.minSection = minSection;
++ this.maxSection = maxSection;
++ this.sections = sections;
++ if (this.sections.length != (maxSection - minSection + 1)) {
++ throw new IllegalStateException("Incorrect length used, expected " + (maxSection - minSection + 1) + ", got " + this.sections.length);
++ }
++ }
++
++ public void load() {
++ TickThread.ensureTickThread(this.world, this.chunkX, this.chunkZ, "Loading in poi chunk off-main");
++ if (this.loaded) {
++ return;
++ }
++ this.loaded = true;
++ this.world.chunkSource.getPoiManager().loadInPoiChunk(this);
++ }
++
++ public boolean isLoaded() {
++ return this.loaded;
++ }
++
++ public boolean isEmpty() {
++ for (final PoiSection section : this.sections) {
++ if (section != null && !section.isEmpty()) {
++ return false;
++ }
++ }
++
++ return true;
++ }
++
++ public PoiSection getOrCreateSection(final int chunkY) {
++ if (chunkY >= this.minSection && chunkY <= this.maxSection) {
++ final int idx = chunkY - this.minSection;
++ final PoiSection ret = this.sections[idx];
++ if (ret != null) {
++ return ret;
++ }
++
++ final PoiManager poiManager = this.world.getPoiManager();
++ final long key = CoordinateUtils.getChunkSectionKey(this.chunkX, chunkY, this.chunkZ);
++
++ return this.sections[idx] = new PoiSection(() -> {
++ poiManager.setDirty(key);
++ });
++ }
++ throw new IllegalArgumentException("chunkY is out of bounds, chunkY: " + chunkY + " outside [" + this.minSection + "," + this.maxSection + "]");
++ }
++
++ public PoiSection getSection(final int chunkY) {
++ if (chunkY >= this.minSection && chunkY <= this.maxSection) {
++ return this.sections[chunkY - this.minSection];
++ }
++ return null;
++ }
++
++ public Optional<PoiSection> getSectionForVanilla(final int chunkY) {
++ if (chunkY >= this.minSection && chunkY <= this.maxSection) {
++ final PoiSection ret = this.sections[chunkY - this.minSection];
++ return ret == null ? Optional.empty() : ret.noAllocateOptional;
++ }
++ return Optional.empty();
++ }
++
++ public boolean isDirty() {
++ return this.isDirty;
++ }
++
++ public void setDirty(final boolean dirty) {
++ this.isDirty = dirty;
++ }
++
++ // returns null if empty
++ public CompoundTag save() {
++ final RegistryOps<Tag> registryOps = RegistryOps.create(NbtOps.INSTANCE, world.getPoiManager().registryAccess);
++
++ final CompoundTag ret = new CompoundTag();
++ final CompoundTag sections = new CompoundTag();
++ ret.put("Sections", sections);
++
++ ret.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
++
++ final ServerLevel world = this.world;
++ final PoiManager poiManager = world.getPoiManager();
++ final int chunkX = this.chunkX;
++ final int chunkZ = this.chunkZ;
++
++ for (int sectionY = this.minSection; sectionY <= this.maxSection; ++sectionY) {
++ final PoiSection chunk = this.sections[sectionY - this.minSection];
++ if (chunk == null || chunk.isEmpty()) {
++ continue;
++ }
++
++ final long key = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
++ // codecs are honestly such a fucking disaster. What the fuck is this trash?
++ final Codec<PoiSection> codec = PoiSection.codec(() -> {
++ poiManager.setDirty(key);
++ });
++
++ final DataResult<Tag> serializedResult = codec.encodeStart(registryOps, chunk);
++ final int finalSectionY = sectionY;
++ final Tag serialized = serializedResult.resultOrPartial((final String description) -> {
++ LOGGER.error("Failed to serialize poi chunk for world: " + world.getWorld().getName() + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
++ }).orElse(null);
++ if (serialized == null) {
++ // failed, should be logged from the resultOrPartial
++ continue;
++ }
++
++ sections.put(Integer.toString(sectionY), serialized);
++ }
++
++ return sections.isEmpty() ? null : ret;
++ }
++
++ public static PoiChunk empty(final ServerLevel world, final int chunkX, final int chunkZ) {
++ final PoiChunk ret = new PoiChunk(world, chunkX, chunkZ, WorldUtil.getMinSection(world), WorldUtil.getMaxSection(world));
++ ret.loaded = true;
++ return ret;
++ }
++
++ public static PoiChunk parse(final ServerLevel world, final int chunkX, final int chunkZ, final CompoundTag data) {
++ final PoiChunk ret = empty(world, chunkX, chunkZ);
++
++ final RegistryOps<Tag> registryOps = RegistryOps.create(NbtOps.INSTANCE, world.getPoiManager().registryAccess);
++
++ final CompoundTag sections = data.getCompound("Sections");
++
++ if (sections.isEmpty()) {
++ // nothing to parse
++ return ret;
++ }
++
++ final PoiManager poiManager = world.getPoiManager();
++
++ boolean readAnything = false;
++
++ for (int sectionY = ret.minSection; sectionY <= ret.maxSection; ++sectionY) {
++ final String key = Integer.toString(sectionY);
++ if (!sections.contains(key)) {
++ continue;
++ }
++
++ final long coordinateKey = CoordinateUtils.getChunkSectionKey(chunkX, sectionY, chunkZ);
++ // codecs are honestly such a fucking disaster. What the fuck is this trash?
++ final Codec<PoiSection> codec = PoiSection.codec(() -> {
++ poiManager.setDirty(coordinateKey);
++ });
++
++ final CompoundTag section = sections.getCompound(key);
++ final DataResult<PoiSection> deserializeResult = codec.parse(registryOps, section);
++ final int finalSectionY = sectionY;
++ final PoiSection deserialized = deserializeResult.resultOrPartial((final String description) -> {
++ LOGGER.error("Failed to deserialize poi chunk for world: " + world.getWorld().getName() + ", chunk: (" + chunkX + "," + finalSectionY + "," + chunkZ + "); description: " + description);
++ }).orElse(null);
++
++ if (deserialized == null || deserialized.isEmpty()) {
++ // completely empty, no point in storing this
++ continue;
++ }
++
++ readAnything = true;
++ ret.sections[sectionY - ret.minSection] = deserialized;
++ }
++
++ return ret;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkFullTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkFullTask.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b02619d7111c52d1b4e3b50267e54da31d6161e3
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkFullTask.java
+@@ -0,0 +1,125 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import com.mojang.logging.LogUtils;
++import net.minecraft.server.level.ChunkMap;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
++import net.minecraft.world.level.chunk.ImposterProtoChunk;
++import net.minecraft.world.level.chunk.LevelChunk;
++import net.minecraft.world.level.chunk.ProtoChunk;
++import org.slf4j.Logger;
++import java.lang.invoke.VarHandle;
++
++public final class ChunkFullTask extends ChunkProgressionTask implements Runnable {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ protected final NewChunkHolder chunkHolder;
++ protected final ChunkAccess fromChunk;
++ protected final PrioritisedExecutor.PrioritisedTask convertToFullTask;
++
++ public ChunkFullTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ,
++ final NewChunkHolder chunkHolder, final ChunkAccess fromChunk, final PrioritisedExecutor.Priority priority) {
++ super(scheduler, world, chunkX, chunkZ);
++ this.chunkHolder = chunkHolder;
++ this.fromChunk = fromChunk;
++ this.convertToFullTask = scheduler.createChunkTask(chunkX, chunkZ, this, priority);
++ }
++
++ @Override
++ public ChunkStatus getTargetStatus() {
++ return ChunkStatus.FULL;
++ }
++
++ @Override
++ public void run() {
++ // See Vanilla protoChunkToFullChunk for what this function should be doing
++ final LevelChunk chunk;
++ try {
++ if (this.fromChunk instanceof ImposterProtoChunk wrappedFull) {
++ chunk = wrappedFull.getWrapped();
++ } else {
++ final ServerLevel world = this.world;
++ final ProtoChunk protoChunk = (ProtoChunk)this.fromChunk;
++ chunk = new LevelChunk(this.world, protoChunk, (final LevelChunk unused) -> {
++ ChunkMap.postLoadProtoChunk(world, protoChunk.getEntities());
++ });
++ }
++
++ chunk.setChunkHolder(this.scheduler.chunkHolderManager.getChunkHolder(this.chunkX, this.chunkZ)); // replaces setFullStatus
++ chunk.runPostLoad();
++ // Unlike Vanilla, we load the entity chunk here, as we load the NBT in empty status (unlike Vanilla)
++ // This brings entity addition back in line with older versions of the game
++ // Since we load the NBT in the empty status, this will never block for I/O
++ this.world.chunkTaskScheduler.chunkHolderManager.getOrCreateEntityChunk(this.chunkX, this.chunkZ, false);
++
++ // we don't need the entitiesInLevel trash, this system doesn't double run callbacks
++ chunk.setLoaded(true);
++ chunk.registerAllBlockEntitiesAfterLevelLoad();
++ chunk.registerTickContainerInLevel(this.world);
++ } catch (final Throwable throwable) {
++ this.complete(null, throwable);
++
++ if (throwable instanceof ThreadDeath) {
++ throw (ThreadDeath)throwable;
++ }
++ return;
++ }
++ this.complete(chunk, null);
++ }
++
++ protected volatile boolean scheduled;
++ protected static final VarHandle SCHEDULED_HANDLE = ConcurrentUtil.getVarHandle(ChunkFullTask.class, "scheduled", boolean.class);
++
++ @Override
++ public boolean isScheduled() {
++ return this.scheduled;
++ }
++
++ @Override
++ public void schedule() {
++ if ((boolean)SCHEDULED_HANDLE.getAndSet((ChunkFullTask)this, true)) {
++ throw new IllegalStateException("Cannot double call schedule()");
++ }
++ this.convertToFullTask.queue();
++ }
++
++ @Override
++ public void cancel() {
++ if (this.convertToFullTask.cancel()) {
++ this.complete(null, null);
++ }
++ }
++
++ @Override
++ public PrioritisedExecutor.Priority getPriority() {
++ return this.convertToFullTask.getPriority();
++ }
++
++ @Override
++ public void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.convertToFullTask.lowerPriority(priority);
++ }
++
++ @Override
++ public void setPriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.convertToFullTask.setPriority(priority);
++ }
++
++ @Override
++ public void raisePriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.convertToFullTask.raisePriority(priority);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..a07d920d453c8687ed86d9f9449537c3eb18041e
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
+@@ -0,0 +1,1190 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.map.SWMRLong2ObjectHashTable;
++import co.aikar.timings.Timing;
++import com.google.common.collect.ImmutableList;
++import com.google.gson.JsonArray;
++import com.google.gson.JsonObject;
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.chunk.system.io.RegionFileIOThread;
++import io.papermc.paper.chunk.system.poi.PoiChunk;
++import io.papermc.paper.util.CoordinateUtils;
++import io.papermc.paper.util.TickThread;
++import io.papermc.paper.util.misc.Delayed8WayDistancePropagator2D;
++import io.papermc.paper.world.ChunkEntitySlices;
++import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap;
++import it.unimi.dsi.fastutil.longs.Long2IntMap;
++import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
++import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
++import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
++import it.unimi.dsi.fastutil.longs.LongArrayList;
++import it.unimi.dsi.fastutil.longs.LongIterator;
++import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
++import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.ChunkSystem;
++import net.minecraft.server.MinecraftServer;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ChunkMap;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.server.level.Ticket;
++import net.minecraft.server.level.TicketType;
++import net.minecraft.util.SortedArraySet;
++import net.minecraft.util.Unit;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
++import org.bukkit.plugin.Plugin;
++import org.slf4j.Logger;
++import java.io.IOException;
++import java.text.DecimalFormat;
++import java.util.ArrayDeque;
++import java.util.ArrayList;
++import java.util.Collection;
++import java.util.Collections;
++import java.util.Iterator;
++import java.util.List;
++import java.util.Objects;
++import java.util.concurrent.TimeUnit;
++import java.util.concurrent.atomic.AtomicBoolean;
++import java.util.concurrent.atomic.AtomicReference;
++import java.util.concurrent.locks.LockSupport;
++import java.util.concurrent.locks.ReentrantLock;
++import java.util.function.Predicate;
++
++public final class ChunkHolderManager {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ public static final int FULL_LOADED_TICKET_LEVEL = 33;
++ public static final int BLOCK_TICKING_TICKET_LEVEL = 32;
++ public static final int ENTITY_TICKING_TICKET_LEVEL = 31;
++ public static final int MAX_TICKET_LEVEL = ChunkMap.MAX_CHUNK_DISTANCE; // inclusive
++
++ private static final long NO_TIMEOUT_MARKER = -1L;
++
++ final ReentrantLock ticketLock = new ReentrantLock();
++
++ private final SWMRLong2ObjectHashTable<NewChunkHolder> chunkHolders = new SWMRLong2ObjectHashTable<>(16384, 0.25f);
++ private final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets = new Long2ObjectOpenHashMap<>(8192, 0.25f);
++ // what a disaster of a name
++ // this is a map of removal tick to a map of chunks and the number of tickets a chunk has that are to expire that tick
++ private final Long2ObjectOpenHashMap<Long2IntOpenHashMap> removeTickToChunkExpireTicketCount = new Long2ObjectOpenHashMap<>();
++ private final ServerLevel world;
++ private final ChunkTaskScheduler taskScheduler;
++ private long currentTick;
++
++ private final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = new ArrayDeque<>();
++ private final ObjectRBTreeSet<NewChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((final NewChunkHolder c1, final NewChunkHolder c2) -> {
++ if (c1 == c2) {
++ return 0;
++ }
++
++ final int saveTickCompare = Long.compare(c1.lastAutoSave, c2.lastAutoSave);
++
++ if (saveTickCompare != 0) {
++ return saveTickCompare;
++ }
++
++ final long coord1 = CoordinateUtils.getChunkKey(c1.chunkX, c1.chunkZ);
++ final long coord2 = CoordinateUtils.getChunkKey(c2.chunkX, c2.chunkZ);
++
++ if (coord1 == coord2) {
++ throw new IllegalStateException("Duplicate chunkholder in auto save queue");
++ }
++
++ return Long.compare(coord1, coord2);
++ });
++
++ public ChunkHolderManager(final ServerLevel world, final ChunkTaskScheduler taskScheduler) {
++ this.world = world;
++ this.taskScheduler = taskScheduler;
++ }
++
++ private long statusUpgradeId;
++
++ long getNextStatusUpgradeId() {
++ return ++this.statusUpgradeId;
++ }
++
++ public List<ChunkHolder> getOldChunkHolders() {
++ final List<NewChunkHolder> holders = this.getChunkHolders();
++ final List<ChunkHolder> ret = new ArrayList<>(holders.size());
++ for (final NewChunkHolder holder : holders) {
++ ret.add(holder.vanillaChunkHolder);
++ }
++ return ret;
++ }
++
++ public List<NewChunkHolder> getChunkHolders() {
++ final List<NewChunkHolder> ret = new ArrayList<>(this.chunkHolders.size());
++ this.chunkHolders.forEachValue(ret::add);
++ return ret;
++ }
++
++ public int size() {
++ return this.chunkHolders.size();
++ }
++
++ public void close(final boolean save, final boolean halt) {
++ TickThread.ensureTickThread("Closing world off-main");
++ if (halt) {
++ LOGGER.info("Waiting 60s for chunk system to halt for world '" + this.world.getWorld().getName() + "'");
++ if (!this.taskScheduler.halt(true, TimeUnit.SECONDS.toNanos(60L))) {
++ LOGGER.warn("Failed to halt world generation/loading tasks for world '" + this.world.getWorld().getName() + "'");
++ } else {
++ LOGGER.info("Halted chunk system for world '" + this.world.getWorld().getName() + "'");
++ }
++ }
++
++ if (save) {
++ this.saveAllChunks(true, true, true);
++ }
++
++ if (this.world.chunkDataControllerNew.hasTasks() || this.world.entityDataControllerNew.hasTasks() || this.world.poiDataControllerNew.hasTasks()) {
++ RegionFileIOThread.flush();
++ }
++
++ // kill regionfile cache
++ try {
++ this.world.chunkDataControllerNew.getCache().close();
++ } catch (final IOException ex) {
++ LOGGER.error("Failed to close chunk regionfile cache for world '" + this.world.getWorld().getName() + "'", ex);
++ }
++ try {
++ this.world.entityDataControllerNew.getCache().close();
++ } catch (final IOException ex) {
++ LOGGER.error("Failed to close entity regionfile cache for world '" + this.world.getWorld().getName() + "'", ex);
++ }
++ try {
++ this.world.poiDataControllerNew.getCache().close();
++ } catch (final IOException ex) {
++ LOGGER.error("Failed to close poi regionfile cache for world '" + this.world.getWorld().getName() + "'", ex);
++ }
++ }
++
++ void ensureInAutosave(final NewChunkHolder holder) {
++ if (!this.autoSaveQueue.contains(holder)) {
++ holder.lastAutoSave = MinecraftServer.currentTick;
++ this.autoSaveQueue.add(holder);
++ }
++ }
++
++ public void autoSave() {
++ final List<NewChunkHolder> reschedule = new ArrayList<>();
++ final long currentTick = MinecraftServer.currentTickLong;
++ final long maxSaveTime = currentTick - this.world.paperConfig().chunks.autoSaveInterval.value();
++ for (int autoSaved = 0; autoSaved < this.world.paperConfig().chunks.maxAutoSaveChunksPerTick && !this.autoSaveQueue.isEmpty();) {
++ final NewChunkHolder holder = this.autoSaveQueue.first();
++
++ if (holder.lastAutoSave > maxSaveTime) {
++ break;
++ }
++
++ this.autoSaveQueue.remove(holder);
++
++ holder.lastAutoSave = currentTick;
++ if (holder.save(false, false)) {
++ ++autoSaved;
++ }
++
++ if (holder.getChunkStatus().isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ reschedule.add(holder);
++ }
++ }
++
++ for (final NewChunkHolder holder : reschedule) {
++ if (holder.getChunkStatus().isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ this.autoSaveQueue.add(holder);
++ }
++ }
++ }
++
++ public void saveAllChunks(final boolean flush, final boolean shutdown, final boolean logProgress) {
++ final List<NewChunkHolder> holders = this.getChunkHolders();
++
++ if (logProgress) {
++ LOGGER.info("Saving all chunkholders for world '" + this.world.getWorld().getName() + "'");
++ }
++
++ final DecimalFormat format = new DecimalFormat("#0.00");
++
++ int saved = 0;
++
++ long start = System.nanoTime();
++ long lastLog = start;
++ boolean needsFlush = false;
++ final int flushInterval = 50;
++
++ for (int i = 0, len = holders.size(); i < len; ++i) {
++ final NewChunkHolder holder = holders.get(i);
++ try {
++ if (holder.save(shutdown, false)) {
++ ++saved;
++ needsFlush = flush;
++ }
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to save chunk (" + holder.chunkX + "," + holder.chunkZ + ") in world '" + this.world.getWorld().getName() + "'", thr);
++ }
++ if (needsFlush && (saved % flushInterval) == 0) {
++ needsFlush = false;
++ RegionFileIOThread.partialFlush(flushInterval / 2);
++ }
++ if (logProgress) {
++ final long currTime = System.nanoTime();
++ if ((currTime - lastLog) > TimeUnit.SECONDS.toNanos(10L)) {
++ lastLog = currTime;
++ LOGGER.info("Saved " + saved + " chunks (" + format.format((double)(i+1)/(double)len * 100.0) + "%) in world '" + this.world.getWorld().getName() + "'");
++ }
++ }
++ }
++ if (flush) {
++ RegionFileIOThread.flush();
++ }
++ if (logProgress) {
++ LOGGER.info("Saved " + saved + " chunks in world '" + this.world.getWorld().getName() + "' in " + TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) + "s");
++ }
++ }
++
++ protected final Long2IntLinkedOpenHashMap ticketLevelUpdates = new Long2IntLinkedOpenHashMap() {
++ @Override
++ protected void rehash(final int newN) {
++ // no downsizing allowed
++ if (newN < this.n) {
++ return;
++ }
++ super.rehash(newN);
++ }
++ };
++
++ protected final Delayed8WayDistancePropagator2D ticketLevelPropagator = new Delayed8WayDistancePropagator2D(
++ (final long coordinate, final byte oldLevel, final byte newLevel) -> {
++ ChunkHolderManager.this.ticketLevelUpdates.putAndMoveToLast(coordinate, convertBetweenTicketLevels(newLevel));
++ }
++ );
++ // function for converting between ticket levels and propagator levels and vice versa
++ // the problem is the ticket level propagator will propagate from a set source down to zero, whereas mojang expects
++ // levels to propagate from a set value up to a maximum value. so we need to convert the levels we put into the propagator
++ // and the levels we get out of the propagator
++
++ public static int convertBetweenTicketLevels(final int level) {
++ return ChunkMap.MAX_CHUNK_DISTANCE - level + 1;
++ }
++
++ public boolean hasTickets() {
++ this.ticketLock.lock();
++ try {
++ return !this.tickets.isEmpty();
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++
++ public String getTicketDebugString(final long coordinate) {
++ this.ticketLock.lock();
++ try {
++ final SortedArraySet<Ticket<?>> tickets = this.tickets.get(coordinate);
++
++ return tickets != null ? tickets.first().toString() : "no_ticket";
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++
++ public Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> getTicketsCopy() {
++ this.ticketLock.lock();
++ try {
++ return this.tickets.clone();
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++
++ public Collection<Plugin> getPluginChunkTickets(int x, int z) {
++ ImmutableList.Builder<Plugin> ret;
++ this.ticketLock.lock();
++ try {
++ SortedArraySet<Ticket<?>> tickets = this.tickets.get(ChunkPos.asLong(x, z));
++
++ if (tickets == null) {
++ return Collections.emptyList();
++ }
++
++ ret = ImmutableList.builder();
++ for (Ticket<?> ticket : tickets) {
++ if (ticket.getType() == TicketType.PLUGIN_TICKET) {
++ ret.add((Plugin)ticket.key);
++ }
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++
++ return ret.build();
++ }
++
++ protected final int getPropagatedTicketLevel(final long coordinate) {
++ return convertBetweenTicketLevels(this.ticketLevelPropagator.getLevel(coordinate));
++ }
++
++ protected final void updateTicketLevel(final long coordinate, final int ticketLevel) {
++ if (ticketLevel > ChunkMap.MAX_CHUNK_DISTANCE) {
++ this.ticketLevelPropagator.removeSource(coordinate);
++ } else {
++ this.ticketLevelPropagator.setSource(coordinate, convertBetweenTicketLevels(ticketLevel));
++ }
++ }
++
++ private static int getTicketLevelAt(SortedArraySet<Ticket<?>> tickets) {
++ return !tickets.isEmpty() ? tickets.first().getTicketLevel() : MAX_TICKET_LEVEL + 1;
++ }
++
++ public <T> boolean addTicketAtLevel(final TicketType<T> type, final ChunkPos chunkPos, final int level,
++ final T identifier) {
++ return this.addTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkPos), level, identifier);
++ }
++
++ public <T> boolean addTicketAtLevel(final TicketType<T> type, final int chunkX, final int chunkZ, final int level,
++ final T identifier) {
++ return this.addTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkX, chunkZ), level, identifier);
++ }
++
++ // supposed to return true if the ticket was added and did not replace another
++ // but, we always return false if the ticket cannot be added
++ public <T> boolean addTicketAtLevel(final TicketType<T> type, final long chunk, final int level, final T identifier) {
++ final long removeDelay = Math.max(0, type.timeout);
++ if (level > MAX_TICKET_LEVEL) {
++ return false;
++ }
++
++ this.ticketLock.lock();
++ try {
++ final long removeTick = removeDelay == 0 ? NO_TIMEOUT_MARKER : this.currentTick + removeDelay;
++ final Ticket<T> ticket = new Ticket<>(type, level, identifier, removeTick);
++
++ final SortedArraySet<Ticket<?>> ticketsAtChunk = this.tickets.computeIfAbsent(chunk, (final long keyInMap) -> {
++ return SortedArraySet.create(4);
++ });
++
++ final int levelBefore = getTicketLevelAt(ticketsAtChunk);
++ final Ticket<T> current = (Ticket<T>)ticketsAtChunk.replace(ticket);
++ final int levelAfter = getTicketLevelAt(ticketsAtChunk);
++
++ if (current != ticket) {
++ final long oldRemovalTick = current.removalTick;
++ if (removeTick != oldRemovalTick) {
++ if (oldRemovalTick != NO_TIMEOUT_MARKER) {
++ final Long2IntOpenHashMap removeCounts = this.removeTickToChunkExpireTicketCount.get(oldRemovalTick);
++ final int prevCount = removeCounts.addTo(chunk, -1);
++
++ if (prevCount == 1) {
++ removeCounts.remove(chunk);
++ if (removeCounts.isEmpty()) {
++ this.removeTickToChunkExpireTicketCount.remove(oldRemovalTick);
++ }
++ }
++ }
++ if (removeTick != NO_TIMEOUT_MARKER) {
++ this.removeTickToChunkExpireTicketCount.computeIfAbsent(removeTick, (final long keyInMap) -> {
++ return new Long2IntOpenHashMap();
++ }).addTo(chunk, 1);
++ }
++ }
++ } else {
++ if (removeTick != NO_TIMEOUT_MARKER) {
++ this.removeTickToChunkExpireTicketCount.computeIfAbsent(removeTick, (final long keyInMap) -> {
++ return new Long2IntOpenHashMap();
++ }).addTo(chunk, 1);
++ }
++ }
++
++ if (levelBefore != levelAfter) {
++ this.updateTicketLevel(chunk, levelAfter);
++ }
++
++ return current == ticket;
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++
++ public <T> boolean removeTicketAtLevel(final TicketType<T> type, final ChunkPos chunkPos, final int level, final T identifier) {
++ return this.removeTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkPos), level, identifier);
++ }
++
++ public <T> boolean removeTicketAtLevel(final TicketType<T> type, final int chunkX, final int chunkZ, final int level, final T identifier) {
++ return this.removeTicketAtLevel(type, CoordinateUtils.getChunkKey(chunkX, chunkZ), level, identifier);
++ }
++
++ public <T> boolean removeTicketAtLevel(final TicketType<T> type, final long chunk, final int level, final T identifier) {
++ if (level > MAX_TICKET_LEVEL) {
++ return false;
++ }
++
++ this.ticketLock.lock();
++ try {
++ final SortedArraySet<Ticket<?>> ticketsAtChunk = this.tickets.get(chunk);
++ if (ticketsAtChunk == null) {
++ return false;
++ }
++
++ final int oldLevel = getTicketLevelAt(ticketsAtChunk);
++ final Ticket<T> ticket = (Ticket<T>)ticketsAtChunk.removeAndGet(new Ticket<>(type, level, identifier, -2L));
++
++ if (ticket == null) {
++ return false;
++ }
++
++ if (ticketsAtChunk.isEmpty()) {
++ this.tickets.remove(chunk);
++ }
++
++ final int newLevel = getTicketLevelAt(ticketsAtChunk);
++
++ final long removeTick = ticket.removalTick;
++ if (removeTick != NO_TIMEOUT_MARKER) {
++ final Long2IntOpenHashMap removeCounts = this.removeTickToChunkExpireTicketCount.get(removeTick);
++ final int currCount = removeCounts.addTo(chunk, -1);
++
++ if (currCount == 1) {
++ removeCounts.remove(chunk);
++ if (removeCounts.isEmpty()) {
++ this.removeTickToChunkExpireTicketCount.remove(removeTick);
++ }
++ }
++ }
++
++ if (oldLevel != newLevel) {
++ this.updateTicketLevel(chunk, newLevel);
++ }
++
++ return true;
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++
++ // atomic with respect to all add/remove/addandremove ticket calls for the given chunk
++ public <T, V> void addAndRemoveTickets(final long chunk, final TicketType<T> addType, final int addLevel, final T addIdentifier,
++ final TicketType<V> removeType, final int removeLevel, final V removeIdentifier) {
++ this.ticketLock.lock();
++ try {
++ this.addTicketAtLevel(addType, chunk, addLevel, addIdentifier);
++ this.removeTicketAtLevel(removeType, chunk, removeLevel, removeIdentifier);
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++
++ public <T> void removeAllTicketsFor(final TicketType<T> ticketType, final int ticketLevel, final T ticketIdentifier) {
++ if (ticketLevel > MAX_TICKET_LEVEL) {
++ return;
++ }
++
++ this.ticketLock.lock();
++ try {
++ for (final LongIterator iterator = new LongArrayList(this.tickets.keySet()).longIterator(); iterator.hasNext();) {
++ final long chunk = iterator.nextLong();
++
++ this.removeTicketAtLevel(ticketType, chunk, ticketLevel, ticketIdentifier);
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++
++ public void tick() {
++ TickThread.ensureTickThread("Cannot tick ticket manager off-main");
++
++ this.ticketLock.lock();
++ try {
++ final long tick = ++this.currentTick;
++
++ final Long2IntOpenHashMap toRemove = this.removeTickToChunkExpireTicketCount.remove(tick);
++
++ if (toRemove == null) {
++ return;
++ }
++
++ final Predicate<Ticket<?>> expireNow = (final Ticket<?> ticket) -> {
++ return ticket.removalTick == tick;
++ };
++
++ for (final LongIterator iterator = toRemove.keySet().longIterator(); iterator.hasNext();) {
++ final long chunk = iterator.nextLong();
++
++ final SortedArraySet<Ticket<?>> tickets = this.tickets.get(chunk);
++ tickets.removeIf(expireNow);
++ if (tickets.isEmpty()) {
++ this.tickets.remove(chunk);
++ this.ticketLevelPropagator.removeSource(chunk);
++ } else {
++ this.ticketLevelPropagator.setSource(chunk, convertBetweenTicketLevels(tickets.first().getTicketLevel()));
++ }
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++
++ this.processTicketUpdates();
++ }
++
++ public NewChunkHolder getChunkHolder(final int chunkX, final int chunkZ) {
++ return this.chunkHolders.get(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
++
++ public NewChunkHolder getChunkHolder(final long position) {
++ return this.chunkHolders.get(position);
++ }
++
++ public void raisePriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
++ final NewChunkHolder chunkHolder = this.getChunkHolder(x, z);
++ if (chunkHolder != null) {
++ chunkHolder.raisePriority(priority);
++ }
++ }
++
++ public void setPriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
++ final NewChunkHolder chunkHolder = this.getChunkHolder(x, z);
++ if (chunkHolder != null) {
++ chunkHolder.setPriority(priority);
++ }
++ }
++
++ public void lowerPriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
++ final NewChunkHolder chunkHolder = this.getChunkHolder(x, z);
++ if (chunkHolder != null) {
++ chunkHolder.lowerPriority(priority);
++ }
++ }
++
++ private NewChunkHolder createChunkHolder(final long position) {
++ final NewChunkHolder ret = new NewChunkHolder(this.world, CoordinateUtils.getChunkX(position), CoordinateUtils.getChunkZ(position), this.taskScheduler);
++
++ ChunkSystem.onChunkHolderCreate(this.world, ret.vanillaChunkHolder);
++ ret.vanillaChunkHolder.onChunkAdd();
++
++ return ret;
++ }
++
++ // because this function creates the chunk holder without a ticket, it is the caller's responsibility to ensure
++ // the chunk holder eventually unloads. this should only be used to avoid using processTicketUpdates to create chunkholders,
++ // as processTicketUpdates may call plugin logic; in every other case a ticket is appropriate
++ private NewChunkHolder getOrCreateChunkHolder(final int chunkX, final int chunkZ) {
++ return this.getOrCreateChunkHolder(CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ }
++
++ private NewChunkHolder getOrCreateChunkHolder(final long position) {
++ if (!this.ticketLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Must hold ticket level update lock!");
++ }
++ if (!this.taskScheduler.schedulingLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Must hold scheduler lock!!");
++ }
++
++ // we could just acquire these locks, but...
++ // must own the locks because the caller needs to ensure that no unload can occur AFTER this function returns
++
++ NewChunkHolder current = this.chunkHolders.get(position);
++ if (current != null) {
++ return current;
++ }
++
++ current = this.createChunkHolder(position);
++ this.chunkHolders.put(position, current);
++
++ return current;
++ }
++
++ private long entityLoadCounter;
++
++ public ChunkEntitySlices getOrCreateEntityChunk(final int chunkX, final int chunkZ, final boolean transientChunk) {
++ TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot create entity chunk off-main");
++ ChunkEntitySlices ret;
++
++ NewChunkHolder current = this.getChunkHolder(chunkX, chunkZ);
++ if (current != null && (ret = current.getEntityChunk()) != null && (transientChunk || !ret.isTransient())) {
++ return ret;
++ }
++
++ final AtomicBoolean isCompleted = new AtomicBoolean();
++ final Thread waiter = Thread.currentThread();
++ final Long entityLoadId;
++ NewChunkHolder.GenericDataLoadTaskCallback loadTask = null;
++ this.ticketLock.lock();
++ try {
++ entityLoadId = Long.valueOf(this.entityLoadCounter++);
++ this.addTicketAtLevel(TicketType.ENTITY_LOAD, chunkX, chunkZ, MAX_TICKET_LEVEL, entityLoadId);
++ this.taskScheduler.schedulingLock.lock();
++ try {
++ current = this.getOrCreateChunkHolder(chunkX, chunkZ);
++ if ((ret = current.getEntityChunk()) != null && (transientChunk || !ret.isTransient())) {
++ this.removeTicketAtLevel(TicketType.ENTITY_LOAD, chunkX, chunkZ, MAX_TICKET_LEVEL, entityLoadId);
++ return ret;
++ }
++
++ if (current.isEntityChunkNBTLoaded()) {
++ isCompleted.setPlain(true);
++ } else {
++ loadTask = current.getOrLoadEntityData((final GenericDataLoadTask.TaskResult<CompoundTag, Throwable> result) -> {
++ if (!transientChunk) {
++ isCompleted.set(true);
++ LockSupport.unpark(waiter);
++ }
++ });
++ final ChunkLoadTask.EntityDataLoadTask entityLoad = current.getEntityDataLoadTask();
++
++ if (entityLoad != null && !transientChunk) {
++ entityLoad.raisePriority(PrioritisedExecutor.Priority.BLOCKING);
++ }
++ }
++ } finally {
++ this.taskScheduler.schedulingLock.unlock();
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++
++ if (loadTask != null) {
++ loadTask.schedule();
++ }
++
++ if (!transientChunk) {
++ // Note: no need to busy wait on the chunk queue, entity load will complete off-main
++ boolean interrupted = false;
++ while (!isCompleted.get()) {
++ interrupted |= Thread.interrupted();
++ LockSupport.park();
++ }
++
++ if (interrupted) {
++ Thread.currentThread().interrupt();
++ }
++ }
++
++ // now that the entity data is loaded, we can load it into the world
++
++ ret = current.loadInEntityChunk(transientChunk);
++
++ final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
++ this.addAndRemoveTickets(chunkKey,
++ TicketType.UNKNOWN, MAX_TICKET_LEVEL, new ChunkPos(chunkX, chunkZ),
++ TicketType.ENTITY_LOAD, MAX_TICKET_LEVEL, entityLoadId
++ );
++
++ return ret;
++ }
++
++ public PoiChunk getPoiChunkIfLoaded(final int chunkX, final int chunkZ, final boolean checkLoadInCallback) {
++ final NewChunkHolder holder = this.getChunkHolder(chunkX, chunkZ);
++ if (holder != null) {
++ final PoiChunk ret = holder.getPoiChunk();
++ return ret == null || (checkLoadInCallback && !ret.isLoaded()) ? null : ret;
++ }
++ return null;
++ }
++
++ private long poiLoadCounter;
++
++ public PoiChunk loadPoiChunk(final int chunkX, final int chunkZ) {
++ TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Cannot create poi chunk off-main");
++ PoiChunk ret;
++
++ NewChunkHolder current = this.getChunkHolder(chunkX, chunkZ);
++ if (current != null && (ret = current.getPoiChunk()) != null) {
++ if (!ret.isLoaded()) {
++ ret.load();
++ }
++ return ret;
++ }
++
++ final AtomicReference<PoiChunk> completed = new AtomicReference<>();
++ final AtomicBoolean isCompleted = new AtomicBoolean();
++ final Thread waiter = Thread.currentThread();
++ final Long poiLoadId;
++ NewChunkHolder.GenericDataLoadTaskCallback loadTask = null;
++ this.ticketLock.lock();
++ try {
++ poiLoadId = Long.valueOf(this.poiLoadCounter++);
++ this.addTicketAtLevel(TicketType.POI_LOAD, chunkX, chunkZ, MAX_TICKET_LEVEL, poiLoadId);
++ this.taskScheduler.schedulingLock.lock();
++ try {
++ current = this.getOrCreateChunkHolder(chunkX, chunkZ);
++ if (current.isPoiChunkLoaded()) {
++ this.removeTicketAtLevel(TicketType.POI_LOAD, chunkX, chunkZ, MAX_TICKET_LEVEL, poiLoadId);
++ return current.getPoiChunk();
++ }
++
++ loadTask = current.getOrLoadPoiData((final GenericDataLoadTask.TaskResult<PoiChunk, Throwable> result) -> {
++ completed.setPlain(result.left());
++ isCompleted.set(true);
++ LockSupport.unpark(waiter);
++ });
++ final ChunkLoadTask.PoiDataLoadTask poiLoad = current.getPoiDataLoadTask();
++
++ if (poiLoad != null) {
++ poiLoad.raisePriority(PrioritisedExecutor.Priority.BLOCKING);
++ }
++ } finally {
++ this.taskScheduler.schedulingLock.unlock();
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++
++ if (loadTask != null) {
++ loadTask.schedule();
++ }
++
++ // Note: no need to busy wait on the chunk queue, poi load will complete off-main
++
++ boolean interrupted = false;
++ while (!isCompleted.get()) {
++ interrupted |= Thread.interrupted();
++ LockSupport.park();
++ }
++
++ if (interrupted) {
++ Thread.currentThread().interrupt();
++ }
++
++ ret = completed.getPlain();
++
++ ret.load();
++
++ final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
++ this.addAndRemoveTickets(chunkKey,
++ TicketType.UNKNOWN, MAX_TICKET_LEVEL, new ChunkPos(chunkX, chunkZ),
++ TicketType.POI_LOAD, MAX_TICKET_LEVEL, poiLoadId
++ );
++
++ return ret;
++ }
++
++ void addChangedStatuses(final List<NewChunkHolder> changedFullStatus) {
++ if (changedFullStatus.isEmpty()) {
++ return;
++ }
++ if (!TickThread.isTickThread()) {
++ this.taskScheduler.scheduleChunkTask(() -> {
++ final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = ChunkHolderManager.this.pendingFullLoadUpdate;
++ for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
++ pendingFullLoadUpdate.add(changedFullStatus.get(i));
++ }
++
++ ChunkHolderManager.this.processPendingFullUpdate();
++ }, PrioritisedExecutor.Priority.HIGHEST);
++ } else {
++ final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
++ for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
++ pendingFullLoadUpdate.add(changedFullStatus.get(i));
++ }
++ }
++ }
++
++ final ReferenceLinkedOpenHashSet<NewChunkHolder> unloadQueue = new ReferenceLinkedOpenHashSet<>();
++
++ private void removeChunkHolder(final NewChunkHolder holder) {
++ holder.killed = true;
++ holder.vanillaChunkHolder.onChunkRemove();
++ this.autoSaveQueue.remove(holder);
++ ChunkSystem.onChunkHolderDelete(this.world, holder.vanillaChunkHolder);
++ this.chunkHolders.remove(CoordinateUtils.getChunkKey(holder.chunkX, holder.chunkZ));
++ }
++
++ // note: never call while inside the chunk system, this will absolutely break everything
++ public void processUnloads() {
++ TickThread.ensureTickThread("Cannot unload chunks off-main");
++
++ if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
++ throw new IllegalStateException("Cannot unload chunks recursively");
++ }
++ if (this.ticketLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Cannot hold ticket update lock while calling processUnloads");
++ }
++ if (this.taskScheduler.schedulingLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Cannot hold scheduling lock while calling processUnloads");
++ }
++
++ final List<NewChunkHolder.UnloadState> unloadQueue;
++ final List<ChunkProgressionTask> scheduleList = new ArrayList<>();
++ this.ticketLock.lock();
++ try {
++ this.taskScheduler.schedulingLock.lock();
++ try {
++ if (this.unloadQueue.isEmpty()) {
++ return;
++ }
++ // in order to ensure all chunks in the unload queue do not have a pending ticket level update,
++ // process them now
++ this.processTicketUpdates(false, false, scheduleList);
++ unloadQueue = new ArrayList<>((int)(this.unloadQueue.size() * 0.05) + 1);
++
++ final int unloadCount = Math.max(50, (int)(this.unloadQueue.size() * 0.05));
++ for (int i = 0; i < unloadCount && !this.unloadQueue.isEmpty(); ++i) {
++ final NewChunkHolder chunkHolder = this.unloadQueue.removeFirst();
++ if (chunkHolder.isSafeToUnload() != null) {
++ LOGGER.error("Chunkholder " + chunkHolder + " is not safe to unload but is inside the unload queue?");
++ continue;
++ }
++ final NewChunkHolder.UnloadState state = chunkHolder.unloadStage1();
++ if (state == null) {
++ // can unload immediately
++ this.removeChunkHolder(chunkHolder);
++ continue;
++ }
++ unloadQueue.add(state);
++ }
++ } finally {
++ this.taskScheduler.schedulingLock.unlock();
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++ // schedule tasks, we can't let processTicketUpdates do this because we call it holding the schedule lock
++ for (int i = 0, len = scheduleList.size(); i < len; ++i) {
++ scheduleList.get(i).schedule();
++ }
++
++ final List<NewChunkHolder> toRemove = new ArrayList<>(unloadQueue.size());
++
++ final Boolean before = this.blockTicketUpdates();
++ try {
++ for (int i = 0, len = unloadQueue.size(); i < len; ++i) {
++ final NewChunkHolder.UnloadState state = unloadQueue.get(i);
++ final NewChunkHolder holder = state.holder();
++
++ holder.unloadStage2(state);
++ toRemove.add(holder);
++ }
++ } finally {
++ this.unblockTicketUpdates(before);
++ }
++
++ this.ticketLock.lock();
++ try {
++ this.taskScheduler.schedulingLock.lock();
++ try {
++ for (int i = 0, len = toRemove.size(); i < len; ++i) {
++ final NewChunkHolder holder = toRemove.get(i);
++
++ if (holder.unloadStage3()) {
++ this.removeChunkHolder(holder);
++ } else {
++ // add cooldown so the next unload check is not immediately next tick
++ this.addTicketAtLevel(TicketType.UNLOAD_COOLDOWN, holder.chunkX, holder.chunkZ, MAX_TICKET_LEVEL, Unit.INSTANCE);
++ }
++ }
++ } finally {
++ this.taskScheduler.schedulingLock.unlock();
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++
++ private final ThreadLocal<Boolean> BLOCK_TICKET_UPDATES = ThreadLocal.withInitial(() -> {
++ return Boolean.FALSE;
++ });
++
++ public Boolean blockTicketUpdates() {
++ final Boolean ret = BLOCK_TICKET_UPDATES.get();
++ BLOCK_TICKET_UPDATES.set(Boolean.TRUE);
++ return ret;
++ }
++
++ public void unblockTicketUpdates(final Boolean before) {
++ BLOCK_TICKET_UPDATES.set(before);
++ }
++
++ public boolean processTicketUpdates() {
++ return this.processTicketUpdates(true, true, null);
++ }
++
++ private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
++
++ static List<ChunkProgressionTask> getCurrentTicketUpdateScheduling() {
++ return CURRENT_TICKET_UPDATE_SCHEDULING.get();
++ }
++
++ private boolean processTicketUpdates(final boolean checkLocks, final boolean processFullUpdates, List<ChunkProgressionTask> scheduledTasks) {
++ TickThread.ensureTickThread("Cannot process ticket levels off-main");
++ if (BLOCK_TICKET_UPDATES.get() == Boolean.TRUE) {
++ throw new IllegalStateException("Cannot update ticket level while unloading chunks or updating entity manager");
++ }
++ if (checkLocks && this.ticketLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Illegal recursive processTicketUpdates!");
++ }
++ if (checkLocks && this.taskScheduler.schedulingLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Cannot update ticket levels from a scheduler context!");
++ }
++
++ List<NewChunkHolder> changedFullStatus = null;
++
++ final boolean isTickThread = TickThread.isTickThread();
++
++ boolean ret = false;
++ final boolean canProcessFullUpdates = processFullUpdates & isTickThread;
++ final boolean canProcessScheduling = scheduledTasks == null;
++
++ this.ticketLock.lock();
++ try {
++ final boolean levelsUpdated = this.ticketLevelPropagator.propagateUpdates();
++ if (levelsUpdated) {
++ // Unlike CB, ticket level updates cannot happen recursively. Thank god.
++ if (!this.ticketLevelUpdates.isEmpty()) {
++ ret = true;
++
++ // first the necessary chunkholders must be created, so just update the ticket levels
++ for (final Iterator<Long2IntMap.Entry> iterator = this.ticketLevelUpdates.long2IntEntrySet().fastIterator(); iterator.hasNext();) {
++ final Long2IntMap.Entry entry = iterator.next();
++ final long key = entry.getLongKey();
++ final int newLevel = entry.getIntValue();
++
++ NewChunkHolder current = this.chunkHolders.get(key);
++ if (current == null && newLevel > MAX_TICKET_LEVEL) {
++ // not loaded and it shouldn't be loaded!
++ iterator.remove();
++ continue;
++ }
++
++ final int currentLevel = current == null ? MAX_TICKET_LEVEL + 1 : current.getCurrentTicketLevel();
++ if (currentLevel == newLevel) {
++ // nothing to do
++ iterator.remove();
++ continue;
++ }
++
++ if (current == null) {
++ // must create
++ current = this.createChunkHolder(key);
++ this.chunkHolders.put(key, current);
++ current.updateTicketLevel(newLevel);
++ } else {
++ current.updateTicketLevel(newLevel);
++ }
++ }
++
++ if (scheduledTasks == null) {
++ scheduledTasks = new ArrayList<>();
++ }
++ changedFullStatus = new ArrayList<>();
++
++ // allow the chunkholders to process ticket level updates without needing to acquire the schedule lock every time
++ final List<ChunkProgressionTask> prev = CURRENT_TICKET_UPDATE_SCHEDULING.get();
++ CURRENT_TICKET_UPDATE_SCHEDULING.set(scheduledTasks);
++ try {
++ this.taskScheduler.schedulingLock.lock();
++ try {
++ for (final Iterator<Long2IntMap.Entry> iterator = this.ticketLevelUpdates.long2IntEntrySet().fastIterator(); iterator.hasNext();) {
++ final Long2IntMap.Entry entry = iterator.next();
++ final long key = entry.getLongKey();
++ final NewChunkHolder current = this.chunkHolders.get(key);
++
++ if (current == null) {
++ throw new IllegalStateException("Expected chunk holder to be created");
++ }
++
++ current.processTicketLevelUpdate(scheduledTasks, changedFullStatus);
++ }
++ } finally {
++ this.taskScheduler.schedulingLock.unlock();
++ }
++ } finally {
++ CURRENT_TICKET_UPDATE_SCHEDULING.set(prev);
++ }
++
++ this.ticketLevelUpdates.clear();
++ }
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++
++ if (changedFullStatus != null) {
++ this.addChangedStatuses(changedFullStatus);
++ }
++
++ if (canProcessScheduling && scheduledTasks != null) {
++ for (int i = 0, len = scheduledTasks.size(); i < len; ++i) {
++ scheduledTasks.get(i).schedule();
++ }
++ }
++
++ if (canProcessFullUpdates) {
++ ret |= this.processPendingFullUpdate();
++ }
++
++ return ret;
++ }
++
++ // only call on tick thread
++ protected final boolean processPendingFullUpdate() {
++ final ArrayDeque<NewChunkHolder> pendingFullLoadUpdate = this.pendingFullLoadUpdate;
++
++ boolean ret = false;
++
++ List<NewChunkHolder> changedFullStatus = new ArrayList<>();
++
++ NewChunkHolder holder;
++ while ((holder = pendingFullLoadUpdate.poll()) != null) {
++ ret |= holder.handleFullStatusChange(changedFullStatus);
++
++ if (!changedFullStatus.isEmpty()) {
++ for (int i = 0, len = changedFullStatus.size(); i < len; ++i) {
++ pendingFullLoadUpdate.add(changedFullStatus.get(i));
++ }
++ changedFullStatus.clear();
++ }
++ }
++
++ return ret;
++ }
++
++ public JsonObject getDebugJsonForWatchdog() {
++ // try and detect any potential deadlock that would require us to read unlocked
++ try {
++ if (this.ticketLock.tryLock(10, TimeUnit.SECONDS)) {
++ try {
++ if (this.taskScheduler.schedulingLock.tryLock(10, TimeUnit.SECONDS)) {
++ try {
++ return this.getDebugJsonNoLock();
++ } finally {
++ this.taskScheduler.schedulingLock.unlock();
++ }
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++ }
++ } catch (final InterruptedException ignore) {}
++
++ LOGGER.error("Failed to acquire ticket and scheduling lock before timeout for world " + this.world.getWorld().getName());
++
++ // because we read without locks, it may throw exceptions for fastutil maps
++ // so just try until it works...
++ Throwable lastException = null;
++ for (int count = 0;count < 1000;++count) {
++ try {
++ return this.getDebugJsonNoLock();
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr) {
++ lastException = thr;
++ Thread.yield();
++ LockSupport.parkNanos(10_000L);
++ }
++ }
++
++ // failed, return
++ LOGGER.error("Failed to retrieve debug json for watchdog thread without locking", lastException);
++ return null;
++ }
++
++ private JsonObject getDebugJsonNoLock() {
++ final JsonObject ret = new JsonObject();
++ ret.addProperty("current_tick", Long.valueOf(this.currentTick));
++
++ final JsonArray unloadQueue = new JsonArray();
++ ret.add("unload_queue", unloadQueue);
++ for (final NewChunkHolder holder : this.unloadQueue) {
++ final JsonObject coordinate = new JsonObject();
++ unloadQueue.add(coordinate);
++
++ coordinate.addProperty("chunkX", Integer.valueOf(holder.chunkX));
++ coordinate.addProperty("chunkZ", Integer.valueOf(holder.chunkZ));
++ }
++
++ final JsonArray holders = new JsonArray();
++ ret.add("chunkholders", holders);
++
++ for (final NewChunkHolder holder : this.getChunkHolders()) {
++ holders.add(holder.getDebugJson());
++ }
++
++ final JsonArray removeTickToChunkExpireTicketCount = new JsonArray();
++ ret.add("remove_tick_to_chunk_expire_ticket_count", removeTickToChunkExpireTicketCount);
++
++ for (final Long2ObjectMap.Entry<Long2IntOpenHashMap> tickEntry : this.removeTickToChunkExpireTicketCount.long2ObjectEntrySet()) {
++ final long tick = tickEntry.getLongKey();
++ final Long2IntOpenHashMap coordinateToCount = tickEntry.getValue();
++
++ final JsonObject tickJson = new JsonObject();
++ removeTickToChunkExpireTicketCount.add(tickJson);
++
++ tickJson.addProperty("tick", Long.valueOf(tick));
++
++ final JsonArray tickEntries = new JsonArray();
++ tickJson.add("entries", tickEntries);
++
++ for (final Long2IntMap.Entry entry : coordinateToCount.long2IntEntrySet()) {
++ final long coordinate = entry.getLongKey();
++ final int count = entry.getIntValue();
++
++ final JsonObject entryJson = new JsonObject();
++ tickEntries.add(entryJson);
++
++ entryJson.addProperty("chunkX", Long.valueOf(CoordinateUtils.getChunkX(coordinate)));
++ entryJson.addProperty("chunkZ", Long.valueOf(CoordinateUtils.getChunkZ(coordinate)));
++ entryJson.addProperty("count", Integer.valueOf(count));
++ }
++ }
++
++ final JsonArray allTicketsJson = new JsonArray();
++ ret.add("tickets", allTicketsJson);
++
++ for (final Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>> coordinateTickets : this.tickets.long2ObjectEntrySet()) {
++ final long coordinate = coordinateTickets.getLongKey();
++ final SortedArraySet<Ticket<?>> tickets = coordinateTickets.getValue();
++
++ final JsonObject coordinateJson = new JsonObject();
++ allTicketsJson.add(coordinateJson);
++
++ coordinateJson.addProperty("chunkX", Long.valueOf(CoordinateUtils.getChunkX(coordinate)));
++ coordinateJson.addProperty("chunkZ", Long.valueOf(CoordinateUtils.getChunkZ(coordinate)));
++
++ final JsonArray ticketsSerialized = new JsonArray();
++ coordinateJson.add("tickets", ticketsSerialized);
++
++ for (final Ticket<?> ticket : tickets) {
++ final JsonObject ticketSerialized = new JsonObject();
++ ticketsSerialized.add(ticketSerialized);
++
++ ticketSerialized.addProperty("type", ticket.getType().toString());
++ ticketSerialized.addProperty("level", Integer.valueOf(ticket.getTicketLevel()));
++ ticketSerialized.addProperty("identifier", Objects.toString(ticket.key));
++ ticketSerialized.addProperty("remove_tick", Long.valueOf(ticket.removalTick));
++ }
++ }
++
++ return ret;
++ }
++
++ public JsonObject getDebugJson() {
++ final List<ChunkProgressionTask> scheduleList = new ArrayList<>();
++ try {
++ final JsonObject ret;
++ this.ticketLock.lock();
++ try {
++ this.taskScheduler.schedulingLock.lock();
++ try {
++ this.processTicketUpdates(false, false, scheduleList);
++ ret = this.getDebugJsonNoLock();
++ } finally {
++ this.taskScheduler.schedulingLock.unlock();
++ }
++ } finally {
++ this.ticketLock.unlock();
++ }
++ return ret;
++ } finally {
++ // schedule tasks, we can't let processTicketUpdates do this because we call it holding the schedule lock
++ for (int i = 0, len = scheduleList.size(); i < len; ++i) {
++ scheduleList.get(i).schedule();
++ }
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLightTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLightTask.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..53ddd7e9ac05e6a9eb809f329796e6d4f6bb2ab1
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLightTask.java
+@@ -0,0 +1,181 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.starlight.common.light.StarLightEngine;
++import ca.spottedleaf.starlight.common.light.StarLightInterface;
++import io.papermc.paper.chunk.system.light.LightQueue;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
++import net.minecraft.world.level.chunk.ProtoChunk;
++import org.apache.logging.log4j.LogManager;
++import org.apache.logging.log4j.Logger;
++import java.util.function.BooleanSupplier;
++
++public final class ChunkLightTask extends ChunkProgressionTask {
++
++ private static final Logger LOGGER = LogManager.getLogger();
++
++ protected final ChunkAccess fromChunk;
++
++ private final LightTaskPriorityHolder priorityHolder;
++
++ public ChunkLightTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ,
++ final ChunkAccess chunk, final PrioritisedExecutor.Priority priority) {
++ super(scheduler, world, chunkX, chunkZ);
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.priorityHolder = new LightTaskPriorityHolder(priority, this);
++ this.fromChunk = chunk;
++ }
++
++ @Override
++ public boolean isScheduled() {
++ return this.priorityHolder.isScheduled();
++ }
++
++ @Override
++ public ChunkStatus getTargetStatus() {
++ return ChunkStatus.LIGHT;
++ }
++
++ @Override
++ public void schedule() {
++ this.priorityHolder.schedule();
++ }
++
++ @Override
++ public void cancel() {
++ this.priorityHolder.cancel();
++ }
++
++ @Override
++ public PrioritisedExecutor.Priority getPriority() {
++ return this.priorityHolder.getPriority();
++ }
++
++ @Override
++ public void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ this.priorityHolder.raisePriority(priority);
++ }
++
++ @Override
++ public void setPriority(final PrioritisedExecutor.Priority priority) {
++ this.priorityHolder.setPriority(priority);
++ }
++
++ @Override
++ public void raisePriority(final PrioritisedExecutor.Priority priority) {
++ this.priorityHolder.raisePriority(priority);
++ }
++
++ private static final class LightTaskPriorityHolder extends PriorityHolder {
++
++ protected final ChunkLightTask task;
++
++ protected LightTaskPriorityHolder(final PrioritisedExecutor.Priority priority, final ChunkLightTask task) {
++ super(priority);
++ this.task = task;
++ }
++
++ @Override
++ protected void cancelScheduled() {
++ final ChunkLightTask task = this.task;
++ task.complete(null, null);
++ }
++
++ @Override
++ protected PrioritisedExecutor.Priority getScheduledPriority() {
++ final ChunkLightTask task = this.task;
++ return task.world.getChunkSource().getLightEngine().theLightEngine.lightQueue.getPriority(task.chunkX, task.chunkZ);
++ }
++
++ @Override
++ protected void scheduleTask(final PrioritisedExecutor.Priority priority) {
++ final ChunkLightTask task = this.task;
++ final StarLightInterface starLightInterface = task.world.getChunkSource().getLightEngine().theLightEngine;
++ final LightQueue lightQueue = starLightInterface.lightQueue;
++ lightQueue.queueChunkLightTask(new ChunkPos(task.chunkX, task.chunkZ), new LightTask(starLightInterface, task), priority);
++ lightQueue.setPriority(task.chunkX, task.chunkZ, priority);
++ }
++
++ @Override
++ protected void lowerPriorityScheduled(final PrioritisedExecutor.Priority priority) {
++ final ChunkLightTask task = this.task;
++ final StarLightInterface starLightInterface = task.world.getChunkSource().getLightEngine().theLightEngine;
++ final LightQueue lightQueue = starLightInterface.lightQueue;
++ lightQueue.lowerPriority(task.chunkX, task.chunkZ, priority);
++ }
++
++ @Override
++ protected void setPriorityScheduled(final PrioritisedExecutor.Priority priority) {
++ final ChunkLightTask task = this.task;
++ final StarLightInterface starLightInterface = task.world.getChunkSource().getLightEngine().theLightEngine;
++ final LightQueue lightQueue = starLightInterface.lightQueue;
++ lightQueue.setPriority(task.chunkX, task.chunkZ, priority);
++ }
++
++ @Override
++ protected void raisePriorityScheduled(final PrioritisedExecutor.Priority priority) {
++ final ChunkLightTask task = this.task;
++ final StarLightInterface starLightInterface = task.world.getChunkSource().getLightEngine().theLightEngine;
++ final LightQueue lightQueue = starLightInterface.lightQueue;
++ lightQueue.raisePriority(task.chunkX, task.chunkZ, priority);
++ }
++ }
++
++ private static final class LightTask implements BooleanSupplier {
++
++ protected final StarLightInterface lightEngine;
++ protected final ChunkLightTask task;
++
++ public LightTask(final StarLightInterface lightEngine, final ChunkLightTask task) {
++ this.lightEngine = lightEngine;
++ this.task = task;
++ }
++
++ @Override
++ public boolean getAsBoolean() {
++ final ChunkLightTask task = this.task;
++ // executed on light thread
++ if (!task.priorityHolder.markExecuting()) {
++ // cancelled
++ return false;
++ }
++
++ try {
++ final Boolean[] emptySections = StarLightEngine.getEmptySectionsForChunk(task.fromChunk);
++
++ if (task.fromChunk.isLightCorrect() && task.fromChunk.getStatus().isOrAfter(ChunkStatus.LIGHT)) {
++ this.lightEngine.forceLoadInChunk(task.fromChunk, emptySections);
++ this.lightEngine.checkChunkEdges(task.chunkX, task.chunkZ);
++ } else {
++ task.fromChunk.setLightCorrect(false);
++ this.lightEngine.lightChunk(task.fromChunk, emptySections);
++ task.fromChunk.setLightCorrect(true);
++ }
++ // we need to advance status
++ if (task.fromChunk instanceof ProtoChunk chunk && chunk.getStatus() == ChunkStatus.LIGHT.getParent()) {
++ chunk.setStatus(ChunkStatus.LIGHT);
++ }
++ } catch (final Throwable thr) {
++ if (!(thr instanceof ThreadDeath)) {
++ LOGGER.fatal("Failed to light chunk " + task.fromChunk.getPos().toString() + " in world '" + this.lightEngine.getWorld().getWorld().getName() + "'", thr);
++ }
++
++ task.complete(null, thr);
++
++ if (thr instanceof ThreadDeath) {
++ throw (ThreadDeath)thr;
++ }
++
++ return true;
++ }
++
++ task.complete(task.fromChunk, null);
++ return true;
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..1e2b8e457aabba2a5d1fabfba22be2faa1d3f45d
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkLoadTask.java
+@@ -0,0 +1,499 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import ca.spottedleaf.dataconverter.minecraft.MCDataConverter;
++import ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry;
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.chunk.system.io.RegionFileIOThread;
++import io.papermc.paper.chunk.system.poi.PoiChunk;
++import net.minecraft.SharedConstants;
++import net.minecraft.core.Registry;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.level.ChunkMap;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
++import net.minecraft.world.level.chunk.ProtoChunk;
++import net.minecraft.world.level.chunk.UpgradeData;
++import net.minecraft.world.level.chunk.storage.ChunkSerializer;
++import net.minecraft.world.level.chunk.storage.EntityStorage;
++import net.minecraft.world.level.levelgen.blending.BlendingData;
++import org.slf4j.Logger;
++import java.lang.invoke.VarHandle;
++import java.util.Map;
++import java.util.concurrent.atomic.AtomicInteger;
++import java.util.function.Consumer;
++
++public final class ChunkLoadTask extends ChunkProgressionTask {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ private final NewChunkHolder chunkHolder;
++ private final ChunkDataLoadTask loadTask;
++
++ private boolean cancelled;
++ private NewChunkHolder.GenericDataLoadTaskCallback entityLoadTask;
++ private NewChunkHolder.GenericDataLoadTaskCallback poiLoadTask;
++
++ protected ChunkLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ,
++ final NewChunkHolder chunkHolder, final PrioritisedExecutor.Priority priority) {
++ super(scheduler, world, chunkX, chunkZ);
++ this.chunkHolder = chunkHolder;
++ this.loadTask = new ChunkDataLoadTask(scheduler, world, chunkX, chunkZ, priority);
++ this.loadTask.addCallback((final GenericDataLoadTask.TaskResult<ChunkAccess, Throwable> result) -> {
++ ChunkLoadTask.this.complete(result == null ? null : result.left(), result == null ? null : result.right());
++ });
++ }
++
++ @Override
++ public ChunkStatus getTargetStatus() {
++ return ChunkStatus.EMPTY;
++ }
++
++ private boolean scheduled;
++
++ @Override
++ public boolean isScheduled() {
++ return this.scheduled;
++ }
++
++ @Override
++ public void schedule() {
++ final NewChunkHolder.GenericDataLoadTaskCallback entityLoadTask;
++ final NewChunkHolder.GenericDataLoadTaskCallback poiLoadTask;
++
++ final AtomicInteger count = new AtomicInteger();
++ final Consumer<GenericDataLoadTask.TaskResult<?, ?>> scheduleLoadTask = (final GenericDataLoadTask.TaskResult<?, ?> result) -> {
++ if (count.decrementAndGet() == 0) {
++ ChunkLoadTask.this.loadTask.schedule(false);
++ }
++ };
++
++ // NOTE: it is IMPOSSIBLE for getOrLoadEntityData/getOrLoadPoiData to complete synchronously, because
++ // they must schedule a task to off main or to on main to complete
++ this.scheduler.schedulingLock.lock();
++ try {
++ if (this.scheduled) {
++ throw new IllegalStateException("schedule() called twice");
++ }
++ this.scheduled = true;
++ if (this.cancelled) {
++ return;
++ }
++ if (!this.chunkHolder.isEntityChunkNBTLoaded()) {
++ entityLoadTask = this.chunkHolder.getOrLoadEntityData((Consumer)scheduleLoadTask);
++ count.setPlain(count.getPlain() + 1);
++ } else {
++ entityLoadTask = null;
++ }
++
++ if (!this.chunkHolder.isPoiChunkLoaded()) {
++ poiLoadTask = this.chunkHolder.getOrLoadPoiData((Consumer)scheduleLoadTask);
++ count.setPlain(count.getPlain() + 1);
++ } else {
++ poiLoadTask = null;
++ }
++
++ this.entityLoadTask = entityLoadTask;
++ this.poiLoadTask = poiLoadTask;
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++
++ if (entityLoadTask != null) {
++ entityLoadTask.schedule();
++ }
++
++ if (poiLoadTask != null) {
++ poiLoadTask.schedule();
++ }
++
++ if (entityLoadTask == null && poiLoadTask == null) {
++ // no need to wait on those, we can schedule now
++ this.loadTask.schedule(false);
++ }
++ }
++
++ @Override
++ public void cancel() {
++ // must be before load task access, so we can synchronise with the writes to the fields
++ this.scheduler.schedulingLock.lock();
++ try {
++ this.cancelled = true;
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++
++ /*
++ Note: The entityLoadTask/poiLoadTask do not complete when cancelled,
++ but this is fine because if they are successfully cancelled then
++ we will successfully cancel the load task, which will complete when cancelled
++ */
++
++ if (this.entityLoadTask != null) {
++ this.entityLoadTask.cancel();
++ }
++ if (this.poiLoadTask != null) {
++ this.poiLoadTask.cancel();
++ }
++ this.loadTask.cancel();
++ }
++
++ @Override
++ public PrioritisedExecutor.Priority getPriority() {
++ return this.loadTask.getPriority();
++ }
++
++ @Override
++ public void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ final EntityDataLoadTask entityLoad = this.chunkHolder.getEntityDataLoadTask();
++ if (entityLoad != null) {
++ entityLoad.lowerPriority(priority);
++ }
++
++ final PoiDataLoadTask poiLoad = this.chunkHolder.getPoiDataLoadTask();
++
++ if (poiLoad != null) {
++ poiLoad.lowerPriority(priority);
++ }
++
++ this.loadTask.lowerPriority(priority);
++ }
++
++ @Override
++ public void setPriority(final PrioritisedExecutor.Priority priority) {
++ final EntityDataLoadTask entityLoad = this.chunkHolder.getEntityDataLoadTask();
++ if (entityLoad != null) {
++ entityLoad.setPriority(priority);
++ }
++
++ final PoiDataLoadTask poiLoad = this.chunkHolder.getPoiDataLoadTask();
++
++ if (poiLoad != null) {
++ poiLoad.setPriority(priority);
++ }
++
++ this.loadTask.setPriority(priority);
++ }
++
++ @Override
++ public void raisePriority(final PrioritisedExecutor.Priority priority) {
++ final EntityDataLoadTask entityLoad = this.chunkHolder.getEntityDataLoadTask();
++ if (entityLoad != null) {
++ entityLoad.raisePriority(priority);
++ }
++
++ final PoiDataLoadTask poiLoad = this.chunkHolder.getPoiDataLoadTask();
++
++ if (poiLoad != null) {
++ poiLoad.raisePriority(priority);
++ }
++
++ this.loadTask.raisePriority(priority);
++ }
++
++ protected static abstract class CallbackDataLoadTask<OnMain,FinalCompletion> extends GenericDataLoadTask<OnMain,FinalCompletion> {
++
++ private TaskResult<FinalCompletion, Throwable> result;
++ private final MultiThreadedQueue<Consumer<TaskResult<FinalCompletion, Throwable>>> waiters = new MultiThreadedQueue<>();
++
++ protected volatile boolean completed;
++ protected static final VarHandle COMPLETED_HANDLE = ConcurrentUtil.getVarHandle(CallbackDataLoadTask.class, "completed", boolean.class);
++
++ protected CallbackDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
++ final int chunkZ, final RegionFileIOThread.RegionFileType type,
++ final PrioritisedExecutor.Priority priority) {
++ super(scheduler, world, chunkX, chunkZ, type, priority);
++ }
++
++ public void addCallback(final Consumer<TaskResult<FinalCompletion, Throwable>> consumer) {
++ if (!this.waiters.add(consumer)) {
++ try {
++ consumer.accept(this.result);
++ } catch (final Throwable throwable) {
++ this.scheduler.unrecoverableChunkSystemFailure(this.chunkX, this.chunkZ, Map.of(
++ "Consumer", ChunkTaskScheduler.stringIfNull(consumer),
++ "Completed throwable", ChunkTaskScheduler.stringIfNull(this.result.right())
++ ), throwable);
++ if (throwable instanceof ThreadDeath) {
++ throw (ThreadDeath)throwable;
++ }
++ }
++ }
++ }
++
++ @Override
++ protected void onComplete(final TaskResult<FinalCompletion, Throwable> result) {
++ if ((boolean)COMPLETED_HANDLE.getAndSet((CallbackDataLoadTask)this, (boolean)true)) {
++ throw new IllegalStateException("Already completed");
++ }
++ this.result = result;
++ Consumer<TaskResult<FinalCompletion, Throwable>> consumer;
++ while ((consumer = this.waiters.pollOrBlockAdds()) != null) {
++ try {
++ consumer.accept(result);
++ } catch (final Throwable throwable) {
++ this.scheduler.unrecoverableChunkSystemFailure(this.chunkX, this.chunkZ, Map.of(
++ "Consumer", ChunkTaskScheduler.stringIfNull(consumer),
++ "Completed throwable", ChunkTaskScheduler.stringIfNull(result.right())
++ ), throwable);
++ if (throwable instanceof ThreadDeath) {
++ throw (ThreadDeath)throwable;
++ }
++ return;
++ }
++ }
++ }
++ }
++
++ public final class ChunkDataLoadTask extends CallbackDataLoadTask<ChunkSerializer.InProgressChunkHolder, ChunkAccess> {
++ protected ChunkDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
++ final int chunkZ, final PrioritisedExecutor.Priority priority) {
++ super(scheduler, world, chunkX, chunkZ, RegionFileIOThread.RegionFileType.CHUNK_DATA, priority);
++ }
++
++ @Override
++ protected boolean hasOffMain() {
++ return true;
++ }
++
++ @Override
++ protected boolean hasOnMain() {
++ return true;
++ }
++
++ @Override
++ protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
++ return this.scheduler.loadExecutor.createTask(run, priority);
++ }
++
++ @Override
++ protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
++ return this.scheduler.createChunkTask(this.chunkX, this.chunkZ, run, priority);
++ }
++
++ @Override
++ protected TaskResult<ChunkAccess, Throwable> completeOnMainOffMain(final ChunkSerializer.InProgressChunkHolder data, final Throwable throwable) {
++ if (data != null) {
++ return null;
++ }
++
++ final PoiChunk poiChunk = ChunkLoadTask.this.chunkHolder.getPoiChunk();
++ if (poiChunk == null) {
++ LOGGER.error("Expected poi chunk to be loaded with chunk for task " + this.toString());
++ } else if (!poiChunk.isLoaded()) {
++ // need to call poiChunk.load() on main
++ return null;
++ }
++
++ return new TaskResult<>(this.getEmptyChunk(), null);
++ }
++
++ @Override
++ protected TaskResult<ChunkSerializer.InProgressChunkHolder, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
++ if (throwable != null) {
++ LOGGER.error("Failed to load chunk data for task: " + this.toString() + ", chunk data will be lost", throwable);
++ return new TaskResult<>(null, null);
++ }
++
++ if (data == null) {
++ return new TaskResult<>(null, null);
++ }
++
++ // need to convert data, and then deserialize it
++
++ try {
++ final ChunkPos chunkPos = new ChunkPos(this.chunkX, this.chunkZ);
++ final ChunkMap chunkMap = this.world.getChunkSource().chunkMap;
++ // run converters
++ // note: upgradeChunkTag copies the data already
++ final CompoundTag converted = chunkMap.upgradeChunkTag(
++ this.world.getTypeKey(), chunkMap.overworldDataStorage, data, chunkMap.generator.getTypeNameForDataFixer(),
++ chunkPos, this.world
++ );
++ // deserialize
++ final ChunkSerializer.InProgressChunkHolder chunkHolder = ChunkSerializer.loadChunk(
++ this.world, chunkMap.getPoiManager(), chunkPos, converted, true
++ );
++
++ return new TaskResult<>(chunkHolder, null);
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr2) {
++ LOGGER.error("Failed to parse chunk data for task: " + this.toString() + ", chunk data will be lost", thr2);
++ return new TaskResult<>(null, thr2);
++ }
++ }
++
++ private ProtoChunk getEmptyChunk() {
++ return new ProtoChunk(
++ new ChunkPos(this.chunkX, this.chunkZ), UpgradeData.EMPTY, this.world,
++ this.world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), (BlendingData)null
++ );
++ }
++
++ @Override
++ protected TaskResult<ChunkAccess, Throwable> runOnMain(final ChunkSerializer.InProgressChunkHolder data, final Throwable throwable) {
++ final PoiChunk poiChunk = ChunkLoadTask.this.chunkHolder.getPoiChunk();
++ if (poiChunk == null) {
++ LOGGER.error("Expected poi chunk to be loaded with chunk for task " + this.toString());
++ } else {
++ poiChunk.load();
++ }
++
++ if (data == null || data.protoChunk == null) {
++ // throwable could be non-null, but the off-main task will print its exceptions - so we don't need to care,
++ // it's handled already
++
++ return new TaskResult<>(this.getEmptyChunk(), null);
++ }
++
++ // have tasks to run (at this point, it's just the POI consistency checking)
++ try {
++ if (data.tasks != null) {
++ for (int i = 0, len = data.tasks.size(); i < len; ++i) {
++ data.tasks.poll().run();
++ }
++ }
++
++ return new TaskResult<>(data.protoChunk, null);
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr2) {
++ LOGGER.error("Failed to parse main tasks for task " + this.toString() + ", chunk data will be lost", thr2);
++ return new TaskResult<>(this.getEmptyChunk(), null);
++ }
++ }
++ }
++
++ public static final class PoiDataLoadTask extends CallbackDataLoadTask<PoiChunk, PoiChunk> {
++ public PoiDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
++ final int chunkZ, final PrioritisedExecutor.Priority priority) {
++ super(scheduler, world, chunkX, chunkZ, RegionFileIOThread.RegionFileType.POI_DATA, priority);
++ }
++
++ @Override
++ protected boolean hasOffMain() {
++ return true;
++ }
++
++ @Override
++ protected boolean hasOnMain() {
++ return false;
++ }
++
++ @Override
++ protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
++ return this.scheduler.loadExecutor.createTask(run, priority);
++ }
++
++ @Override
++ protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
++ throw new UnsupportedOperationException();
++ }
++
++ @Override
++ protected TaskResult<PoiChunk, Throwable> completeOnMainOffMain(final PoiChunk data, final Throwable throwable) {
++ throw new UnsupportedOperationException();
++ }
++
++ @Override
++ protected TaskResult<PoiChunk, Throwable> runOffMain(CompoundTag data, final Throwable throwable) {
++ if (throwable != null) {
++ LOGGER.error("Failed to load poi data for task: " + this.toString() + ", poi data will be lost", throwable);
++ return new TaskResult<>(PoiChunk.empty(this.world, this.chunkX, this.chunkZ), null);
++ }
++
++ if (data == null || data.isEmpty()) {
++ // nothing to do
++ return new TaskResult<>(PoiChunk.empty(this.world, this.chunkX, this.chunkZ), null);
++ }
++
++ try {
++ data = data.copy(); // coming from the I/O thread, so we need to copy
++ // run converters
++ final int dataVersion = !data.contains(SharedConstants.DATA_VERSION_TAG, 99) ? 1945 : data.getInt(SharedConstants.DATA_VERSION_TAG);
++ final CompoundTag converted = MCDataConverter.convertTag(
++ MCTypeRegistry.POI_CHUNK, data, dataVersion, SharedConstants.getCurrentVersion().getWorldVersion()
++ );
++
++ // now we need to parse it
++ return new TaskResult<>(PoiChunk.parse(this.world, this.chunkX, this.chunkZ, converted), null);
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr2) {
++ LOGGER.error("Failed to run parse poi data for task: " + this.toString() + ", poi data will be lost", thr2);
++ return new TaskResult<>(PoiChunk.empty(this.world, this.chunkX, this.chunkZ), null);
++ }
++ }
++
++ @Override
++ protected TaskResult<PoiChunk, Throwable> runOnMain(final PoiChunk data, final Throwable throwable) {
++ throw new UnsupportedOperationException();
++ }
++ }
++
++ public static final class EntityDataLoadTask extends CallbackDataLoadTask<CompoundTag, CompoundTag> {
++
++ public EntityDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
++ final int chunkZ, final PrioritisedExecutor.Priority priority) {
++ super(scheduler, world, chunkX, chunkZ, RegionFileIOThread.RegionFileType.ENTITY_DATA, priority);
++ }
++
++ @Override
++ protected boolean hasOffMain() {
++ return true;
++ }
++
++ @Override
++ protected boolean hasOnMain() {
++ return false;
++ }
++
++ @Override
++ protected PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
++ return this.scheduler.loadExecutor.createTask(run, priority);
++ }
++
++ @Override
++ protected PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority) {
++ throw new UnsupportedOperationException();
++ }
++
++ @Override
++ protected TaskResult<CompoundTag, Throwable> completeOnMainOffMain(final CompoundTag data, final Throwable throwable) {
++ throw new UnsupportedOperationException();
++ }
++
++ @Override
++ protected TaskResult<CompoundTag, Throwable> runOffMain(final CompoundTag data, final Throwable throwable) {
++ if (throwable != null) {
++ LOGGER.error("Failed to load entity data for task: " + this.toString() + ", entity data will be lost", throwable);
++ return new TaskResult<>(null, null);
++ }
++
++ if (data == null || data.isEmpty()) {
++ // nothing to do
++ return new TaskResult<>(null, null);
++ }
++
++ try {
++ // note: data comes from the I/O thread, so we need to copy it
++ return new TaskResult<>(EntityStorage.upgradeChunkTag(data.copy()), null);
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr2) {
++ LOGGER.error("Failed to run converters for entity data for task: " + this.toString() + ", entity data will be lost", thr2);
++ return new TaskResult<>(null, thr2);
++ }
++ }
++
++ @Override
++ protected TaskResult<CompoundTag, Throwable> runOnMain(final CompoundTag data, final Throwable throwable) {
++ throw new UnsupportedOperationException();
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkProgressionTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkProgressionTask.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..322675a470eacbf0e5452f4009c643f2d0b4ce24
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkProgressionTask.java
+@@ -0,0 +1,105 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.collection.MultiThreadedQueue;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
++import java.lang.invoke.VarHandle;
++import java.util.Map;
++import java.util.function.BiConsumer;
++
++public abstract class ChunkProgressionTask {
++
++ private final MultiThreadedQueue<BiConsumer<ChunkAccess, Throwable>> waiters = new MultiThreadedQueue<>();
++ private ChunkAccess completedChunk;
++ private Throwable completedThrowable;
++
++ protected final ChunkTaskScheduler scheduler;
++ protected final ServerLevel world;
++ protected final int chunkX;
++ protected final int chunkZ;
++
++ protected volatile boolean completed;
++ protected static final VarHandle COMPLETED_HANDLE = ConcurrentUtil.getVarHandle(ChunkProgressionTask.class, "completed", boolean.class);
++
++ protected ChunkProgressionTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX, final int chunkZ) {
++ this.scheduler = scheduler;
++ this.world = world;
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ }
++
++ // Used only for debug json
++ public abstract boolean isScheduled();
++
++ // Note: It is the responsibility of the task to set the chunk's status once it has completed
++ public abstract ChunkStatus getTargetStatus();
++
++ /* Only executed once */
++ /* Implementations must be prepared to handle cases where cancel() is called before schedule() */
++ public abstract void schedule();
++
++ /* May be called multiple times */
++ public abstract void cancel();
++
++ public abstract PrioritisedExecutor.Priority getPriority();
++
++ /* Schedule lock is always held for the priority update calls */
++
++ public abstract void lowerPriority(final PrioritisedExecutor.Priority priority);
++
++ public abstract void setPriority(final PrioritisedExecutor.Priority priority);
++
++ public abstract void raisePriority(final PrioritisedExecutor.Priority priority);
++
++ public final void onComplete(final BiConsumer<ChunkAccess, Throwable> onComplete) {
++ if (!this.waiters.add(onComplete)) {
++ try {
++ onComplete.accept(this.completedChunk, this.completedThrowable);
++ } catch (final Throwable throwable) {
++ this.scheduler.unrecoverableChunkSystemFailure(this.chunkX, this.chunkZ, Map.of(
++ "Consumer", ChunkTaskScheduler.stringIfNull(onComplete),
++ "Completed throwable", ChunkTaskScheduler.stringIfNull(this.completedThrowable)
++ ), throwable);
++ if (throwable instanceof ThreadDeath) {
++ throw (ThreadDeath)throwable;
++ }
++ }
++ }
++ }
++
++ protected final void complete(final ChunkAccess chunk, final Throwable throwable) {
++ try {
++ this.complete0(chunk, throwable);
++ } catch (final Throwable thr2) {
++ this.scheduler.unrecoverableChunkSystemFailure(this.chunkX, this.chunkZ, Map.of(
++ "Completed throwable", ChunkTaskScheduler.stringIfNull(throwable)
++ ), thr2);
++ if (thr2 instanceof ThreadDeath) {
++ throw (ThreadDeath)thr2;
++ }
++ }
++ }
++
++ private void complete0(final ChunkAccess chunk, final Throwable throwable) {
++ if ((boolean)COMPLETED_HANDLE.getAndSet((ChunkProgressionTask)this, (boolean)true)) {
++ throw new IllegalStateException("Already completed");
++ }
++ this.completedChunk = chunk;
++ this.completedThrowable = throwable;
++
++ BiConsumer<ChunkAccess, Throwable> consumer;
++ while ((consumer = this.waiters.pollOrBlockAdds()) != null) {
++ consumer.accept(chunk, throwable);
++ }
++ }
++
++ @Override
++ public String toString() {
++ return "ChunkProgressionTask{class: " + this.getClass().getName() + ", for world: " + this.world.getWorld().getName() +
++ ", chunk: (" + this.chunkX + "," + this.chunkZ + "), hashcode: " + System.identityHashCode(this) + ", priority: " + this.getPriority() +
++ ", status: " + this.getTargetStatus().toString() + ", scheduled: " + this.isScheduled() + "}";
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..2b4e3f31d7c31aa5a4a5a18ba9e1d8b3f232fd16
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkTaskScheduler.java
+@@ -0,0 +1,780 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadPool;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadedTaskQueue;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.configuration.GlobalConfiguration;
++import io.papermc.paper.util.CoordinateUtils;
++import io.papermc.paper.util.TickThread;
++import net.minecraft.CrashReport;
++import net.minecraft.CrashReportCategory;
++import net.minecraft.ReportedException;
++import net.minecraft.server.MCUtil;
++import net.minecraft.server.MinecraftServer;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ChunkMap;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.server.level.TicketType;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
++import net.minecraft.world.level.chunk.LevelChunk;
++import org.bukkit.Bukkit;
++import org.slf4j.Logger;
++import java.io.File;
++import java.util.ArrayDeque;
++import java.util.ArrayList;
++import java.util.Arrays;
++import java.util.Collections;
++import java.util.List;
++import java.util.Map;
++import java.util.Objects;
++import java.util.concurrent.atomic.AtomicBoolean;
++import java.util.concurrent.atomic.AtomicLong;
++import java.util.concurrent.locks.ReentrantLock;
++import java.util.function.BooleanSupplier;
++import java.util.function.Consumer;
++
++public final class ChunkTaskScheduler {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ static int newChunkSystemIOThreads;
++ static int newChunkSystemWorkerThreads;
++ static int newChunkSystemGenParallelism;
++ static int newChunkSystemLoadParallelism;
++
++ public static ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadPool workerThreads;
++
++ private static boolean initialised = false;
++
++ public static void init(final GlobalConfiguration.ChunkSystem config) {
++ if (initialised) {
++ return;
++ }
++ initialised = true;
++ newChunkSystemIOThreads = config.ioThreads;
++ newChunkSystemWorkerThreads = config.workerThreads;
++ if (newChunkSystemIOThreads < 0) {
++ newChunkSystemIOThreads = 1;
++ } else {
++ newChunkSystemIOThreads = Math.max(1, newChunkSystemIOThreads);
++ }
++ int defaultWorkerThreads = Runtime.getRuntime().availableProcessors() / 2;
++ if (defaultWorkerThreads <= 4) {
++ defaultWorkerThreads = defaultWorkerThreads <= 3 ? 1 : 2;
++ } else {
++ defaultWorkerThreads = defaultWorkerThreads / 2;
++ }
++ defaultWorkerThreads = Integer.getInteger("Paper.WorkerThreadCount", Integer.valueOf(defaultWorkerThreads));
++
++ if (newChunkSystemWorkerThreads < 0) {
++ newChunkSystemWorkerThreads = defaultWorkerThreads;
++ } else {
++ newChunkSystemWorkerThreads = Math.max(1, newChunkSystemWorkerThreads);
++ }
++
++ String newChunkSystemGenParallelism = config.genParallelism;
++ if (newChunkSystemGenParallelism.equalsIgnoreCase("default")) {
++ newChunkSystemGenParallelism = "true";
++ }
++ boolean useParallelGen;
++ if (newChunkSystemGenParallelism.equalsIgnoreCase("on") || newChunkSystemGenParallelism.equalsIgnoreCase("enabled")
++ || newChunkSystemGenParallelism.equalsIgnoreCase("true")) {
++ useParallelGen = true;
++ } else if (newChunkSystemGenParallelism.equalsIgnoreCase("off") || newChunkSystemGenParallelism.equalsIgnoreCase("disabled")
++ || newChunkSystemGenParallelism.equalsIgnoreCase("false")) {
++ useParallelGen = false;
++ } else {
++ throw new IllegalStateException("Invalid option for gen-parallelism: must be one of [on, off, enabled, disabled, true, false, default]");
++ }
++
++ ChunkTaskScheduler.newChunkSystemGenParallelism = useParallelGen ? newChunkSystemWorkerThreads : 1;
++ ChunkTaskScheduler.newChunkSystemLoadParallelism = newChunkSystemWorkerThreads;
++
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.init(newChunkSystemIOThreads);
++ workerThreads = new ca.spottedleaf.concurrentutil.executor.standard.PrioritisedThreadPool(
++ "Paper Chunk System Worker Pool", newChunkSystemWorkerThreads,
++ (final Thread thread, final Integer id) -> {
++ thread.setPriority(Thread.NORM_PRIORITY - 2);
++ thread.setName("Tuinity Chunk System Worker #" + id.intValue());
++ thread.setUncaughtExceptionHandler(io.papermc.paper.chunk.system.scheduling.NewChunkHolder.CHUNKSYSTEM_UNCAUGHT_EXCEPTION_HANDLER);
++ }, (long)(20.0e6)); // 20ms
++
++ LOGGER.info("Chunk system is using " + newChunkSystemIOThreads + " I/O threads, " + newChunkSystemWorkerThreads + " worker threads, and gen parallelism of " + ChunkTaskScheduler.newChunkSystemGenParallelism + " threads");
++ }
++
++ public final ServerLevel world;
++ public final PrioritisedThreadPool workers;
++ public final PrioritisedThreadPool.PrioritisedPoolExecutor lightExecutor;
++ public final PrioritisedThreadPool.PrioritisedPoolExecutor genExecutor;
++ public final PrioritisedThreadPool.PrioritisedPoolExecutor parallelGenExecutor;
++ public final PrioritisedThreadPool.PrioritisedPoolExecutor loadExecutor;
++
++ private final PrioritisedThreadedTaskQueue mainThreadExecutor = new PrioritisedThreadedTaskQueue();
++
++ final ReentrantLock schedulingLock = new ReentrantLock();
++ public final ChunkHolderManager chunkHolderManager;
++
++ static {
++ ChunkStatus.EMPTY.writeRadius = 0;
++ ChunkStatus.STRUCTURE_STARTS.writeRadius = 0;
++ ChunkStatus.STRUCTURE_REFERENCES.writeRadius = 0;
++ ChunkStatus.BIOMES.writeRadius = 0;
++ ChunkStatus.NOISE.writeRadius = 0;
++ ChunkStatus.SURFACE.writeRadius = 0;
++ ChunkStatus.CARVERS.writeRadius = 0;
++ ChunkStatus.LIQUID_CARVERS.writeRadius = 0;
++ ChunkStatus.FEATURES.writeRadius = 1;
++ ChunkStatus.LIGHT.writeRadius = 1;
++ ChunkStatus.SPAWN.writeRadius = 0;
++ ChunkStatus.HEIGHTMAPS.writeRadius = 0;
++ ChunkStatus.FULL.writeRadius = 0;
++
++ /*
++ It's important that the neighbour read radius is taken into account. If _any_ later status is using some chunk as
++ a neighbour, it must be also safe if that neighbour is being generated. i.e for any status later than FEATURES,
++ for a status to be parallel safe it must not read the block data from its neighbours.
++ */
++ final List<ChunkStatus> parallelCapableStatus = Arrays.asList(
++ // No-op executor.
++ ChunkStatus.EMPTY,
++
++ // This is parallel capable, as CB has fixed the concurrency issue with stronghold generations.
++ // Does not touch neighbour chunks.
++ // TODO On another note, what the fuck is StructureFeatureManager.StructureCheck and why is it used? it's leaking
++ ChunkStatus.STRUCTURE_STARTS,
++
++ // Surprisingly this is parallel capable. It is simply reading the already-created structure starts
++ // into the structure references for the chunk. So while it reads from it neighbours, its neighbours
++ // will not change, even if executed in parallel.
++ ChunkStatus.STRUCTURE_REFERENCES,
++
++ // Safe. Mojang runs it in parallel as well.
++ ChunkStatus.BIOMES,
++
++ // Safe. Mojang runs it in parallel as well.
++ ChunkStatus.NOISE,
++
++ // Parallel safe. Only touches the target chunk. Biome retrieval is now noise based, which is
++ // completely thread-safe.
++ ChunkStatus.SURFACE,
++
++ // No global state is modified in the carvers. It only touches the specified chunk. So it is parallel safe.
++ ChunkStatus.CARVERS,
++
++ // No-op executor. Was replaced in 1.18 with carvers, I think.
++ ChunkStatus.LIQUID_CARVERS,
++
++ // FEATURES is not parallel safe. It writes to neighbours.
++
++ // LIGHT is not parallel safe. It also doesn't run on the generation executor, so no point.
++
++ // Only writes to the specified chunk. State is not read by later statuses. Parallel safe.
++ // Note: it may look unsafe because it writes to a worldgenregion, but the region size is always 0 -
++ // see the task margin.
++ // However, if the neighbouring FEATURES chunk is unloaded, but then fails to load in again (for whatever
++ // reason), then it would write to this chunk - and since this status reads blocks from itself, it's not
++ // safe to execute this in parallel.
++ // SPAWN
++
++ // No-op executor.
++ ChunkStatus.HEIGHTMAPS
++
++ // FULL is executed on main.
++ );
++
++ for (final ChunkStatus status : parallelCapableStatus) {
++ status.isParallelCapable = true;
++ }
++ }
++
++ public ChunkTaskScheduler(final ServerLevel world, final PrioritisedThreadPool workers) {
++ this.world = world;
++ this.workers = workers;
++
++ final String worldName = world.getWorld().getName();
++ this.genExecutor = workers.createExecutor("Chunk single-threaded generation executor for world '" + worldName + "'", 1);
++ // same as genExecutor, as there are race conditions between updating blocks in FEATURE status while lighting chunks
++ this.lightExecutor = this.genExecutor;
++ this.parallelGenExecutor = newChunkSystemGenParallelism <= 1 ? this.genExecutor
++ : workers.createExecutor("Chunk parallel generation executor for world '" + worldName + "'", newChunkSystemGenParallelism);
++ this.loadExecutor = workers.createExecutor("Chunk load executor for world '" + worldName + "'", newChunkSystemLoadParallelism);
++ this.chunkHolderManager = new ChunkHolderManager(world, this);
++ }
++
++ private final AtomicBoolean failedChunkSystem = new AtomicBoolean();
++
++ public static Object stringIfNull(final Object obj) {
++ return obj == null ? "null" : obj;
++ }
++
++ public void unrecoverableChunkSystemFailure(final int chunkX, final int chunkZ, final Map<String, Object> objectsOfInterest, final Throwable thr) {
++ final NewChunkHolder holder = this.chunkHolderManager.getChunkHolder(chunkX, chunkZ);
++ LOGGER.error("Chunk system error at chunk (" + chunkX + "," + chunkZ + "), holder: " + holder + ", exception:", new Throwable(thr));
++
++ if (this.failedChunkSystem.getAndSet(true)) {
++ return;
++ }
++
++ final ReportedException reportedException = thr instanceof ReportedException ? (ReportedException)thr : new ReportedException(new CrashReport("Chunk system error", thr));
++
++ CrashReportCategory crashReportCategory = reportedException.getReport().addCategory("Chunk system details");
++ crashReportCategory.setDetail("Chunk coordinate", new ChunkPos(chunkX, chunkZ).toString());
++ crashReportCategory.setDetail("ChunkHolder", Objects.toString(holder));
++ crashReportCategory.setDetail("unrecoverableChunkSystemFailure caller thread", Thread.currentThread().getName());
++
++ crashReportCategory = reportedException.getReport().addCategory("Chunk System Objects of Interest");
++ for (final Map.Entry<String, Object> entry : objectsOfInterest.entrySet()) {
++ if (entry.getValue() instanceof Throwable thrObject) {
++ crashReportCategory.setDetailError(Objects.toString(entry.getKey()), thrObject);
++ } else {
++ crashReportCategory.setDetail(Objects.toString(entry.getKey()), Objects.toString(entry.getValue()));
++ }
++ }
++
++ final Runnable crash = () -> {
++ throw new RuntimeException("Chunk system crash propagated from unrecoverableChunkSystemFailure", reportedException);
++ };
++
++ // this may not be good enough, specifically thanks to stupid ass plugins swallowing exceptions
++ this.scheduleChunkTask(chunkX, chunkZ, crash, PrioritisedExecutor.Priority.BLOCKING);
++ // so, make the main thread pick it up
++ MinecraftServer.chunkSystemCrash = new RuntimeException("Chunk system crash propagated from unrecoverableChunkSystemFailure", reportedException);
++ }
++
++ public boolean executeMainThreadTask() {
++ TickThread.ensureTickThread("Cannot execute main thread task off-main");
++ return this.mainThreadExecutor.executeTask();
++ }
++
++ public void raisePriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
++ this.chunkHolderManager.raisePriority(x, z, priority);
++ }
++
++ public void setPriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
++ this.chunkHolderManager.setPriority(x, z, priority);
++ }
++
++ public void lowerPriority(final int x, final int z, final PrioritisedExecutor.Priority priority) {
++ this.chunkHolderManager.lowerPriority(x, z, priority);
++ }
++
++ private final AtomicLong chunkLoadCounter = new AtomicLong();
++
++ public void scheduleTickingState(final int chunkX, final int chunkZ, final ChunkHolder.FullChunkStatus toStatus,
++ final boolean addTicket, final PrioritisedExecutor.Priority priority,
++ final Consumer<LevelChunk> onComplete) {
++ if (!TickThread.isTickThread()) {
++ this.scheduleChunkTask(chunkX, chunkZ, () -> {
++ ChunkTaskScheduler.this.scheduleTickingState(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
++ }, priority);
++ return;
++ }
++ if (this.chunkHolderManager.ticketLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Cannot schedule chunk load during ticket level update");
++ }
++ if (this.schedulingLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Cannot schedule chunk loading recursively");
++ }
++
++ if (toStatus == ChunkHolder.FullChunkStatus.INACCESSIBLE) {
++ throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status");
++ }
++
++ final int minLevel = 33 - (toStatus.ordinal() - 1);
++ final Long chunkReference = addTicket ? Long.valueOf(this.chunkLoadCounter.getAndIncrement()) : null;
++ final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
++
++ if (addTicket) {
++ this.chunkHolderManager.addTicketAtLevel(TicketType.CHUNK_LOAD, chunkKey, minLevel, chunkReference);
++ this.chunkHolderManager.processTicketUpdates();
++ }
++
++ final Consumer<LevelChunk> loadCallback = (final LevelChunk chunk) -> {
++ try {
++ if (onComplete != null) {
++ onComplete.accept(chunk);
++ }
++ } finally {
++ if (addTicket) {
++ ChunkTaskScheduler.this.chunkHolderManager.addAndRemoveTickets(chunkKey,
++ TicketType.UNKNOWN, minLevel, new ChunkPos(chunkKey),
++ TicketType.CHUNK_LOAD, minLevel, chunkReference
++ );
++ }
++ }
++ };
++
++ final boolean scheduled;
++ final LevelChunk chunk;
++ this.chunkHolderManager.ticketLock.lock();
++ try {
++ this.schedulingLock.lock();
++ try {
++ final NewChunkHolder chunkHolder = this.chunkHolderManager.getChunkHolder(chunkKey);
++ if (chunkHolder == null || chunkHolder.getTicketLevel() > minLevel) {
++ scheduled = false;
++ chunk = null;
++ } else {
++ final ChunkHolder.FullChunkStatus currStatus = chunkHolder.getChunkStatus();
++ if (currStatus.isOrAfter(toStatus)) {
++ scheduled = false;
++ chunk = (LevelChunk)chunkHolder.getCurrentChunk();
++ } else {
++ scheduled = true;
++ chunk = null;
++
++ final int radius = toStatus.ordinal() - 1; // 0 -> BORDER, 1 -> TICKING, 2 -> ENTITY_TICKING
++ for (int dz = -radius; dz <= radius; ++dz) {
++ for (int dx = -radius; dx <= radius; ++dx) {
++ final NewChunkHolder neighbour =
++ (dx | dz) == 0 ? chunkHolder : this.chunkHolderManager.getChunkHolder(dx + chunkX, dz + chunkZ);
++ if (neighbour != null) {
++ neighbour.raisePriority(priority);
++ }
++ }
++ }
++
++ // ticket level should schedule for us
++ chunkHolder.addFullStatusConsumer(toStatus, loadCallback);
++ }
++ }
++ } finally {
++ this.schedulingLock.unlock();
++ }
++ } finally {
++ this.chunkHolderManager.ticketLock.unlock();
++ }
++
++ if (!scheduled) {
++ // couldn't schedule
++ try {
++ loadCallback.accept(chunk);
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to process chunk full status callback", thr);
++ }
++ }
++ }
++
++ public void scheduleChunkLoad(final int chunkX, final int chunkZ, final boolean gen, final ChunkStatus toStatus, final boolean addTicket,
++ final PrioritisedExecutor.Priority priority, final Consumer<ChunkAccess> onComplete) {
++ if (gen) {
++ this.scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
++ return;
++ }
++ this.scheduleChunkLoad(chunkX, chunkZ, ChunkStatus.EMPTY, addTicket, priority, (final ChunkAccess chunk) -> {
++ if (chunk == null) {
++ onComplete.accept(null);
++ } else {
++ if (chunk.getStatus().isOrAfter(toStatus)) {
++ this.scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
++ } else {
++ onComplete.accept(null);
++ }
++ }
++ });
++ }
++
++ public void scheduleChunkLoad(final int chunkX, final int chunkZ, final ChunkStatus toStatus, final boolean addTicket,
++ final PrioritisedExecutor.Priority priority, final Consumer<ChunkAccess> onComplete) {
++ if (!TickThread.isTickThread()) {
++ this.scheduleChunkTask(chunkX, chunkZ, () -> {
++ ChunkTaskScheduler.this.scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
++ }, priority);
++ return;
++ }
++ if (this.chunkHolderManager.ticketLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Cannot schedule chunk load during ticket level update");
++ }
++ if (this.schedulingLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Cannot schedule chunk loading recursively");
++ }
++
++ if (toStatus == ChunkStatus.FULL) {
++ this.scheduleTickingState(chunkX, chunkZ, ChunkHolder.FullChunkStatus.BORDER, addTicket, priority, (Consumer)onComplete);
++ return;
++ }
++
++ final int minLevel = 33 + ChunkStatus.getDistance(toStatus);
++ final Long chunkReference = addTicket ? Long.valueOf(this.chunkLoadCounter.getAndIncrement()) : null;
++ final long chunkKey = CoordinateUtils.getChunkKey(chunkX, chunkZ);
++
++ if (addTicket) {
++ this.chunkHolderManager.addTicketAtLevel(TicketType.CHUNK_LOAD, chunkKey, minLevel, chunkReference);
++ this.chunkHolderManager.processTicketUpdates();
++ }
++
++ final Consumer<ChunkAccess> loadCallback = (final ChunkAccess chunk) -> {
++ try {
++ if (onComplete != null) {
++ onComplete.accept(chunk);
++ }
++ } finally {
++ if (addTicket) {
++ ChunkTaskScheduler.this.chunkHolderManager.addAndRemoveTickets(chunkKey,
++ TicketType.UNKNOWN, minLevel, new ChunkPos(chunkKey),
++ TicketType.CHUNK_LOAD, minLevel, chunkReference
++ );
++ }
++ }
++ };
++
++ final List<ChunkProgressionTask> tasks = new ArrayList<>();
++
++ final boolean scheduled;
++ final ChunkAccess chunk;
++ this.chunkHolderManager.ticketLock.lock();
++ try {
++ this.schedulingLock.lock();
++ try {
++ final NewChunkHolder chunkHolder = this.chunkHolderManager.getChunkHolder(chunkKey);
++ if (chunkHolder == null || chunkHolder.getTicketLevel() > minLevel) {
++ scheduled = false;
++ chunk = null;
++ } else {
++ final ChunkStatus genStatus = chunkHolder.getCurrentGenStatus();
++ if (genStatus != null && genStatus.isOrAfter(toStatus)) {
++ scheduled = false;
++ chunk = chunkHolder.getCurrentChunk();
++ } else {
++ scheduled = true;
++ chunk = null;
++ chunkHolder.raisePriority(priority);
++
++ if (!chunkHolder.upgradeGenTarget(toStatus)) {
++ this.schedule(chunkX, chunkZ, toStatus, chunkHolder, tasks);
++ }
++ chunkHolder.addStatusConsumer(toStatus, loadCallback);
++ }
++ }
++ } finally {
++ this.schedulingLock.unlock();
++ }
++ } finally {
++ this.chunkHolderManager.ticketLock.unlock();
++ }
++
++ for (int i = 0, len = tasks.size(); i < len; ++i) {
++ tasks.get(i).schedule();
++ }
++
++ if (!scheduled) {
++ // couldn't schedule
++ try {
++ loadCallback.accept(chunk);
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to process chunk status callback", thr);
++ }
++ }
++ }
++
++ private ChunkProgressionTask createTask(final int chunkX, final int chunkZ, final ChunkAccess chunk,
++ final NewChunkHolder chunkHolder, final List<ChunkAccess> neighbours,
++ final ChunkStatus toStatus, final PrioritisedExecutor.Priority initialPriority) {
++ if (toStatus == ChunkStatus.EMPTY) {
++ return new ChunkLoadTask(this, this.world, chunkX, chunkZ, chunkHolder, initialPriority);
++ }
++ if (toStatus == ChunkStatus.LIGHT) {
++ return new ChunkLightTask(this, this.world, chunkX, chunkZ, chunk, initialPriority);
++ }
++ if (toStatus == ChunkStatus.FULL) {
++ return new ChunkFullTask(this, this.world, chunkX, chunkZ, chunkHolder, chunk, initialPriority);
++ }
++
++ return new ChunkUpgradeGenericStatusTask(this, this.world, chunkX, chunkZ, chunk, neighbours, toStatus, initialPriority);
++ }
++
++ ChunkProgressionTask schedule(final int chunkX, final int chunkZ, final ChunkStatus targetStatus, final NewChunkHolder chunkHolder,
++ final List<ChunkProgressionTask> allTasks) {
++ return this.schedule(chunkX, chunkZ, targetStatus, chunkHolder, allTasks, chunkHolder.getEffectivePriority());
++ }
++
++ // rets new task scheduled for the _specified_ chunk
++ // note: this must hold the scheduling lock
++ // minPriority is only used to pass the priority through to neighbours, as priority calculation has not yet been done
++ // schedule will ignore the generation target, so it should be checked by the caller to ensure the target is not regressed!
++ private ChunkProgressionTask schedule(final int chunkX, final int chunkZ, final ChunkStatus targetStatus,
++ final NewChunkHolder chunkHolder, final List<ChunkProgressionTask> allTasks,
++ final PrioritisedExecutor.Priority minPriority) {
++ if (!this.schedulingLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Not holding scheduling lock");
++ }
++
++ if (chunkHolder.hasGenerationTask()) {
++ chunkHolder.upgradeGenTarget(targetStatus);
++ return null;
++ }
++
++ final PrioritisedExecutor.Priority requestedPriority = PrioritisedExecutor.Priority.max(minPriority, chunkHolder.getEffectivePriority());
++ final ChunkStatus currentGenStatus = chunkHolder.getCurrentGenStatus();
++ final ChunkAccess chunk = chunkHolder.getCurrentChunk();
++
++ if (currentGenStatus == null) {
++ // not yet loaded
++ final ChunkProgressionTask task = this.createTask(
++ chunkX, chunkZ, chunk, chunkHolder, Collections.emptyList(), ChunkStatus.EMPTY, requestedPriority
++ );
++
++ allTasks.add(task);
++
++ final List<NewChunkHolder> chunkHolderNeighbours = new ArrayList<>(1);
++ chunkHolderNeighbours.add(chunkHolder);
++
++ chunkHolder.setGenerationTarget(targetStatus);
++ chunkHolder.setGenerationTask(task, ChunkStatus.EMPTY, chunkHolderNeighbours);
++
++ return task;
++ }
++
++ if (currentGenStatus.isOrAfter(targetStatus)) {
++ // nothing to do
++ return null;
++ }
++
++ // we know for sure now that we want to schedule _something_, so set the target
++ chunkHolder.setGenerationTarget(targetStatus);
++
++ final ChunkStatus chunkRealStatus = chunk.getStatus();
++ final ChunkStatus toStatus = currentGenStatus.getNextStatus();
++
++ // if this chunk has already generated up to or past the specified status, then we don't
++ // need the neighbours AT ALL.
++ final int neighbourReadRadius = chunkRealStatus.isOrAfter(toStatus) ? toStatus.loadRange : toStatus.getRange();
++
++ boolean unGeneratedNeighbours = false;
++
++ // copied from MCUtil.getSpiralOutChunks
++ for (int r = 1; r <= neighbourReadRadius; r++) {
++ int x = -r;
++ int z = r;
++
++ // Iterates the edge of half of the box; then negates for other half.
++ while (x <= r && z > -r) {
++ final int radius = Math.max(Math.abs(x), Math.abs(z));
++ final ChunkStatus requiredNeighbourStatus = ChunkMap.getDependencyStatus(toStatus, radius);
++
++ unGeneratedNeighbours |= this.checkNeighbour(
++ chunkX + x, chunkZ + z, requiredNeighbourStatus, chunkHolder, allTasks, requestedPriority
++ );
++ unGeneratedNeighbours |= this.checkNeighbour(
++ chunkX - x, chunkZ - z, requiredNeighbourStatus, chunkHolder, allTasks, requestedPriority
++ );
++
++ if (x < r) {
++ x++;
++ } else {
++ z--;
++ }
++ }
++ }
++
++ if (unGeneratedNeighbours) {
++ // can't schedule, but neighbour completion will schedule for us when they're ALL done
++
++ // propagate our priority to neighbours
++ chunkHolder.recalculateNeighbourPriorities();
++ return null;
++ }
++
++ // need to gather neighbours
++
++ final List<ChunkAccess> neighbours;
++ final List<NewChunkHolder> chunkHolderNeighbours;
++ if (neighbourReadRadius <= 0) {
++ neighbours = new ArrayList<>(1);
++ chunkHolderNeighbours = new ArrayList<>(1);
++ neighbours.add(chunk);
++ chunkHolderNeighbours.add(chunkHolder);
++ } else {
++ // the iteration order is _very_ important, as all generation statuses expect a certain order such that:
++ // chunkAtRelative = neighbours.get(relX + relZ * (2 * radius + 1))
++ neighbours = new ArrayList<>((2 * neighbourReadRadius + 1) * (2 * neighbourReadRadius + 1));
++ chunkHolderNeighbours = new ArrayList<>((2 * neighbourReadRadius + 1) * (2 * neighbourReadRadius + 1));
++ for (int dz = -neighbourReadRadius; dz <= neighbourReadRadius; ++dz) {
++ for (int dx = -neighbourReadRadius; dx <= neighbourReadRadius; ++dx) {
++ final NewChunkHolder holder = (dx | dz) == 0 ? chunkHolder : this.chunkHolderManager.getChunkHolder(dx + chunkX, dz + chunkZ);
++ neighbours.add(holder.getChunkForNeighbourAccess());
++ chunkHolderNeighbours.add(holder);
++ }
++ }
++ }
++
++ final ChunkProgressionTask task = this.createTask(chunkX, chunkZ, chunk, chunkHolder, neighbours, toStatus, chunkHolder.getEffectivePriority());
++ allTasks.add(task);
++
++ chunkHolder.setGenerationTask(task, toStatus, chunkHolderNeighbours);
++
++ return task;
++ }
++
++ // rets true if the neighbour is not at the required status, false otherwise
++ private boolean checkNeighbour(final int chunkX, final int chunkZ, final ChunkStatus requiredStatus, final NewChunkHolder center,
++ final List<ChunkProgressionTask> tasks, final PrioritisedExecutor.Priority minPriority) {
++ final NewChunkHolder chunkHolder = this.chunkHolderManager.getChunkHolder(chunkX, chunkZ);
++
++ if (chunkHolder == null) {
++ throw new IllegalStateException("Missing chunkholder when required");
++ }
++
++ final ChunkStatus holderStatus = chunkHolder.getCurrentGenStatus();
++ if (holderStatus != null && holderStatus.isOrAfter(requiredStatus)) {
++ return false;
++ }
++
++ if (chunkHolder.hasFailedGeneration()) {
++ return true;
++ }
++
++ center.addGenerationBlockingNeighbour(chunkHolder);
++ chunkHolder.addWaitingNeighbour(center, requiredStatus);
++
++ if (chunkHolder.upgradeGenTarget(requiredStatus)) {
++ return true;
++ }
++
++ // not at status required, so we need to schedule its generation
++ this.schedule(
++ chunkX, chunkZ, requiredStatus, chunkHolder, tasks, minPriority
++ );
++
++ return true;
++ }
++
++ /**
++ * @deprecated Chunk tasks must be tied to coordinates in the future
++ */
++ @Deprecated
++ public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final Runnable run) {
++ return this.scheduleChunkTask(run, PrioritisedExecutor.Priority.NORMAL);
++ }
++
++ /**
++ * @deprecated Chunk tasks must be tied to coordinates in the future
++ */
++ @Deprecated
++ public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final Runnable run, final PrioritisedExecutor.Priority priority) {
++ return this.mainThreadExecutor.queueRunnable(run, priority);
++ }
++
++ public PrioritisedExecutor.PrioritisedTask createChunkTask(final int chunkX, final int chunkZ, final Runnable run) {
++ return this.createChunkTask(chunkX, chunkZ, run, PrioritisedExecutor.Priority.NORMAL);
++ }
++
++ public PrioritisedExecutor.PrioritisedTask createChunkTask(final int chunkX, final int chunkZ, final Runnable run,
++ final PrioritisedExecutor.Priority priority) {
++ return this.mainThreadExecutor.createTask(run, priority);
++ }
++
++ public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final int chunkX, final int chunkZ, final Runnable run) {
++ return this.mainThreadExecutor.queueRunnable(run);
++ }
++
++ public PrioritisedExecutor.PrioritisedTask scheduleChunkTask(final int chunkX, final int chunkZ, final Runnable run,
++ final PrioritisedExecutor.Priority priority) {
++ return this.mainThreadExecutor.queueRunnable(run, priority);
++ }
++
++ public void executeTasksUntil(final BooleanSupplier exit) {
++ if (Bukkit.isPrimaryThread()) {
++ this.mainThreadExecutor.executeConditionally(exit);
++ } else {
++ long counter = 1L;
++ while (!exit.getAsBoolean()) {
++ counter = ConcurrentUtil.linearLongBackoff(counter, 100_000L, 5_000_000L); // 100us, 5ms
++ }
++ }
++ }
++
++ public boolean halt(final boolean sync, final long maxWaitNS) {
++ this.lightExecutor.halt();
++ this.genExecutor.halt();
++ this.parallelGenExecutor.halt();
++ this.loadExecutor.halt();
++ final long time = System.nanoTime();
++ if (sync) {
++ for (long failures = 9L;; failures = ConcurrentUtil.linearLongBackoff(failures, 500_000L, 50_000_000L)) {
++ if (
++ !this.lightExecutor.isActive() &&
++ !this.genExecutor.isActive() &&
++ !this.parallelGenExecutor.isActive() &&
++ !this.loadExecutor.isActive()
++ ) {
++ return true;
++ }
++ if ((System.nanoTime() - time) >= maxWaitNS) {
++ return false;
++ }
++ }
++ }
++
++ return true;
++ }
++
++ public static final ArrayDeque<ChunkInfo> WAITING_CHUNKS = new ArrayDeque<>(); // stack
++
++ public static final class ChunkInfo {
++
++ public final int chunkX;
++ public final int chunkZ;
++ public final ServerLevel world;
++
++ public ChunkInfo(final int chunkX, final int chunkZ, final ServerLevel world) {
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ this.world = world;
++ }
++
++ @Override
++ public String toString() {
++ return "[( " + this.chunkX + "," + this.chunkZ + ") in '" + this.world.getWorld().getName() + "']";
++ }
++ }
++
++ public static void pushChunkWait(final ServerLevel world, final int chunkX, final int chunkZ) {
++ synchronized (WAITING_CHUNKS) {
++ WAITING_CHUNKS.push(new ChunkInfo(chunkX, chunkZ, world));
++ }
++ }
++
++ public static void popChunkWait() {
++ synchronized (WAITING_CHUNKS) {
++ WAITING_CHUNKS.pop();
++ }
++ }
++
++ public static ChunkInfo[] getChunkInfos() {
++ synchronized (WAITING_CHUNKS) {
++ return WAITING_CHUNKS.toArray(new ChunkInfo[0]);
++ }
++ }
++
++ public static void dumpAllChunkLoadInfo(final boolean longPrint) {
++ final ChunkInfo[] chunkInfos = getChunkInfos();
++ if (chunkInfos.length > 0) {
++ LOGGER.error("Chunk wait task info below: ");
++ for (final ChunkInfo chunkInfo : chunkInfos) {
++ final NewChunkHolder holder = chunkInfo.world.chunkTaskScheduler.chunkHolderManager.getChunkHolder(chunkInfo.chunkX, chunkInfo.chunkZ);
++ LOGGER.error("Chunk wait: " + chunkInfo);
++ LOGGER.error("Chunk holder: " + holder);
++ }
++
++ if (longPrint) {
++ final File file = new File(new File(new File("."), "debug"), "chunks-watchdog.txt");
++ LOGGER.error("Writing chunk information dump to " + file);
++ try {
++ MCUtil.dumpChunks(file, true);
++ LOGGER.error("Successfully written chunk information!");
++ } catch (final Throwable thr) {
++ MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
++ }
++ }
++ }
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkUpgradeGenericStatusTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkUpgradeGenericStatusTask.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..e96ecf351a1952b4e23e9a352f32d326146380e7
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkUpgradeGenericStatusTask.java
+@@ -0,0 +1,209 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import com.mojang.datafixers.util.Either;
++import com.mojang.logging.LogUtils;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ChunkMap;
++import net.minecraft.server.level.ServerChunkCache;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
++import net.minecraft.world.level.chunk.ProtoChunk;
++import org.slf4j.Logger;
++import java.lang.invoke.VarHandle;
++import java.util.List;
++import java.util.Map;
++import java.util.concurrent.CompletableFuture;
++
++public final class ChunkUpgradeGenericStatusTask extends ChunkProgressionTask implements Runnable {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ protected final ChunkAccess fromChunk;
++ protected final ChunkStatus fromStatus;
++ protected final ChunkStatus toStatus;
++ protected final List<ChunkAccess> neighbours;
++
++ protected final PrioritisedExecutor.PrioritisedTask generateTask;
++
++ public ChunkUpgradeGenericStatusTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
++ final int chunkZ, final ChunkAccess chunk, final List<ChunkAccess> neighbours,
++ final ChunkStatus toStatus, final PrioritisedExecutor.Priority priority) {
++ super(scheduler, world, chunkX, chunkZ);
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.fromChunk = chunk;
++ this.fromStatus = chunk.getStatus();
++ this.toStatus = toStatus;
++ this.neighbours = neighbours;
++ this.generateTask = (this.toStatus.isParallelCapable ? this.scheduler.parallelGenExecutor : this.scheduler.genExecutor)
++ .createTask(this, priority);
++ }
++
++ @Override
++ public ChunkStatus getTargetStatus() {
++ return this.toStatus;
++ }
++
++ private boolean isEmptyTask() {
++ // must use fromStatus here to avoid any race condition with run() overwriting the status
++ final boolean generation = !this.fromStatus.isOrAfter(this.toStatus);
++ return (generation && this.toStatus.isEmptyGenStatus()) || (!generation && this.toStatus.isEmptyLoadStatus());
++ }
++
++ @Override
++ public void run() {
++ final ChunkAccess chunk = this.fromChunk;
++
++ final ServerChunkCache serverChunkCache = this.world.chunkSource;
++ final ChunkMap chunkMap = serverChunkCache.chunkMap;
++
++ final CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completeFuture;
++
++ final boolean generation;
++ boolean completing = false;
++
++ // note: should optimise the case where the chunk does not need to execute the status, because
++ // schedule() calls this synchronously if it will run through that path
++
++ try {
++ generation = !chunk.getStatus().isOrAfter(this.toStatus);
++ if (generation) {
++ if (this.toStatus.isEmptyGenStatus()) {
++ if (chunk instanceof ProtoChunk) {
++ ((ProtoChunk)chunk).setStatus(this.toStatus);
++ }
++ completing = true;
++ this.complete(chunk, null);
++ return;
++ }
++ completeFuture = this.toStatus.generate(Runnable::run, this.world, chunkMap.generator, chunkMap.structureTemplateManager,
++ serverChunkCache.getLightEngine(), null, this.neighbours, false)
++ .whenComplete((final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either, final Throwable throwable) -> {
++ final ChunkAccess newChunk = (either == null) ? null : either.left().orElse(null);
++ if (newChunk instanceof ProtoChunk) {
++ ((ProtoChunk)newChunk).setStatus(ChunkUpgradeGenericStatusTask.this.toStatus);
++ }
++ }
++ );
++ } else {
++ if (this.toStatus.isEmptyLoadStatus()) {
++ completing = true;
++ this.complete(chunk, null);
++ return;
++ }
++ completeFuture = this.toStatus.load(this.world, chunkMap.structureTemplateManager, serverChunkCache.getLightEngine(), null, chunk);
++ }
++ } catch (final Throwable throwable) {
++ if (!completing) {
++ this.complete(null, throwable);
++
++ if (throwable instanceof ThreadDeath) {
++ throw (ThreadDeath)throwable;
++ }
++ return;
++ }
++
++ this.scheduler.unrecoverableChunkSystemFailure(this.chunkX, this.chunkZ, Map.of(
++ "Target status", ChunkTaskScheduler.stringIfNull(this.toStatus),
++ "From status", ChunkTaskScheduler.stringIfNull(this.fromStatus),
++ "Generation task", this
++ ), throwable);
++
++ if (!(throwable instanceof ThreadDeath)) {
++ LOGGER.error("Failed to complete status for chunk: status:" + this.toStatus + ", chunk: (" + this.chunkX + "," + this.chunkZ + "), world: " + this.world.getWorld().getName(), throwable);
++ } else {
++ // ensure the chunk system can respond, then die
++ throw (ThreadDeath)throwable;
++ }
++ return;
++ }
++
++ if (!completeFuture.isDone() && !this.toStatus.warnedAboutNoImmediateComplete.getAndSet(true)) {
++ LOGGER.warn("Future status not complete after scheduling: " + this.toStatus.toString() + ", generate: " + generation);
++ }
++
++ final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either;
++ final ChunkAccess newChunk;
++
++ try {
++ either = completeFuture.join();
++ newChunk = (either == null) ? null : either.left().orElse(null);
++ } catch (final Throwable throwable) {
++ this.complete(null, throwable);
++ // ensure the chunk system can respond, then die
++ if (throwable instanceof ThreadDeath) {
++ throw (ThreadDeath)throwable;
++ }
++ return;
++ }
++
++ if (newChunk == null) {
++ this.complete(null, new IllegalStateException("Chunk for status: " + ChunkUpgradeGenericStatusTask.this.toStatus.toString() + ", generation: " + generation + " should not be null! Either: " + either).fillInStackTrace());
++ return;
++ }
++
++ this.complete(newChunk, null);
++ }
++
++ protected volatile boolean scheduled;
++ protected static final VarHandle SCHEDULED_HANDLE = ConcurrentUtil.getVarHandle(ChunkUpgradeGenericStatusTask.class, "scheduled", boolean.class);
++
++ @Override
++ public boolean isScheduled() {
++ return this.scheduled;
++ }
++
++ @Override
++ public void schedule() {
++ if ((boolean)SCHEDULED_HANDLE.getAndSet((ChunkUpgradeGenericStatusTask)this, true)) {
++ throw new IllegalStateException("Cannot double call schedule()");
++ }
++ if (this.isEmptyTask()) {
++ if (this.generateTask.cancel()) {
++ this.run();
++ }
++ } else {
++ this.generateTask.queue();
++ }
++ }
++
++ @Override
++ public void cancel() {
++ if (this.generateTask.cancel()) {
++ this.complete(null, null);
++ }
++ }
++
++ @Override
++ public PrioritisedExecutor.Priority getPriority() {
++ return this.generateTask.getPriority();
++ }
++
++ @Override
++ public void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.generateTask.lowerPriority(priority);
++ }
++
++ @Override
++ public void setPriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.generateTask.setPriority(priority);
++ }
++
++ @Override
++ public void raisePriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.generateTask.raisePriority(priority);
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/GenericDataLoadTask.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/GenericDataLoadTask.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..ffbfaef2a57f0f26d0143f3a8fcf937bee7e7398
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/GenericDataLoadTask.java
+@@ -0,0 +1,746 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.completable.Completable;
++import ca.spottedleaf.concurrentutil.executor.Cancellable;
++import ca.spottedleaf.concurrentutil.executor.standard.DelayedPrioritisedTask;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.chunk.system.io.RegionFileIOThread;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.level.ServerLevel;
++import org.slf4j.Logger;
++import java.lang.invoke.VarHandle;
++import java.util.Map;
++import java.util.concurrent.atomic.AtomicBoolean;
++import java.util.concurrent.atomic.AtomicLong;
++import java.util.function.BiConsumer;
++
++public abstract class GenericDataLoadTask<OnMain,FinalCompletion> {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ protected static final CompoundTag CANCELLED_DATA = new CompoundTag();
++
++ // reference count is the upper 32 bits
++ protected final AtomicLong stageAndReferenceCount = new AtomicLong(STAGE_NOT_STARTED);
++
++ protected static final long STAGE_MASK = 0xFFFFFFFFL;
++ protected static final long STAGE_CANCELLED = 0xFFFFFFFFL;
++ protected static final long STAGE_NOT_STARTED = 0L;
++ protected static final long STAGE_LOADING = 1L;
++ protected static final long STAGE_PROCESSING = 2L;
++ protected static final long STAGE_COMPLETED = 3L;
++
++ // for loading data off disk
++ protected final LoadDataFromDiskTask loadDataFromDiskTask;
++ // processing off-main
++ protected final PrioritisedExecutor.PrioritisedTask processOffMain;
++ // processing on-main
++ protected final PrioritisedExecutor.PrioritisedTask processOnMain;
++
++ protected final ChunkTaskScheduler scheduler;
++ protected final ServerLevel world;
++ protected final int chunkX;
++ protected final int chunkZ;
++ protected final RegionFileIOThread.RegionFileType type;
++
++ public GenericDataLoadTask(final ChunkTaskScheduler scheduler, final ServerLevel world, final int chunkX,
++ final int chunkZ, final RegionFileIOThread.RegionFileType type,
++ final PrioritisedExecutor.Priority priority) {
++ this.scheduler = scheduler;
++ this.world = world;
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ this.type = type;
++
++ final ProcessOnMainTask mainTask;
++ if (this.hasOnMain()) {
++ mainTask = new ProcessOnMainTask();
++ this.processOnMain = this.createOnMain(mainTask, priority);
++ } else {
++ mainTask = null;
++ this.processOnMain = null;
++ }
++
++ final ProcessOffMainTask offMainTask;
++ if (this.hasOffMain()) {
++ offMainTask = new ProcessOffMainTask(mainTask);
++ this.processOffMain = this.createOffMain(offMainTask, priority);
++ } else {
++ offMainTask = null;
++ this.processOffMain = null;
++ }
++
++ if (this.processOffMain == null && this.processOnMain == null) {
++ throw new IllegalStateException("Illegal class implementation: " + this.getClass().getName() + ", should be able to schedule at least one task!");
++ }
++
++ this.loadDataFromDiskTask = new LoadDataFromDiskTask(world, chunkX, chunkZ, type, new DataLoadCallback(offMainTask, mainTask), priority);
++ }
++
++ public static final record TaskResult<L, R>(L left, R right) {}
++
++ protected abstract boolean hasOffMain();
++
++ protected abstract boolean hasOnMain();
++
++ protected abstract PrioritisedExecutor.PrioritisedTask createOffMain(final Runnable run, final PrioritisedExecutor.Priority priority);
++
++ protected abstract PrioritisedExecutor.PrioritisedTask createOnMain(final Runnable run, final PrioritisedExecutor.Priority priority);
++
++ protected abstract TaskResult<OnMain, Throwable> runOffMain(final CompoundTag data, final Throwable throwable);
++
++ protected abstract TaskResult<FinalCompletion, Throwable> runOnMain(final OnMain data, final Throwable throwable);
++
++ protected abstract void onComplete(final TaskResult<FinalCompletion,Throwable> result);
++
++ protected abstract TaskResult<FinalCompletion, Throwable> completeOnMainOffMain(final OnMain data, final Throwable throwable);
++
++ @Override
++ public String toString() {
++ return "GenericDataLoadTask{class: " + this.getClass().getName() + ", world: " + this.world.getWorld().getName() +
++ ", chunk: (" + this.chunkX + "," + this.chunkZ + "), hashcode: " + System.identityHashCode(this) + ", priority: " + this.getPriority() +
++ ", type: " + this.type.toString() + "}";
++ }
++
++ public PrioritisedExecutor.Priority getPriority() {
++ if (this.processOnMain != null) {
++ return this.processOnMain.getPriority();
++ } else {
++ return this.processOffMain.getPriority();
++ }
++ }
++
++ public void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ // can't lower I/O tasks, we don't know what they affect
++ if (this.processOffMain != null) {
++ this.processOffMain.lowerPriority(priority);
++ }
++ if (this.processOnMain != null) {
++ this.processOnMain.lowerPriority(priority);
++ }
++ }
++
++ public void setPriority(final PrioritisedExecutor.Priority priority) {
++ // can't lower I/O tasks, we don't know what they affect
++ this.loadDataFromDiskTask.raisePriority(priority);
++ if (this.processOffMain != null) {
++ this.processOffMain.setPriority(priority);
++ }
++ if (this.processOnMain != null) {
++ this.processOnMain.setPriority(priority);
++ }
++ }
++
++ public void raisePriority(final PrioritisedExecutor.Priority priority) {
++ // can't lower I/O tasks, we don't know what they affect
++ this.loadDataFromDiskTask.raisePriority(priority);
++ if (this.processOffMain != null) {
++ this.processOffMain.raisePriority(priority);
++ }
++ if (this.processOnMain != null) {
++ this.processOnMain.raisePriority(priority);
++ }
++ }
++
++ // returns whether scheduleNow() needs to be called
++ public boolean schedule(final boolean delay) {
++ if (this.stageAndReferenceCount.get() != STAGE_NOT_STARTED ||
++ !this.stageAndReferenceCount.compareAndSet(STAGE_NOT_STARTED, (1L << 32) | STAGE_LOADING)) {
++ // try and increment reference count
++ int failures = 0;
++ for (long curr = this.stageAndReferenceCount.get();;) {
++ if ((curr & STAGE_MASK) == STAGE_CANCELLED || (curr & STAGE_MASK) == STAGE_COMPLETED) {
++ // cancelled or completed, nothing to do here
++ return false;
++ }
++
++ if (curr == (curr = this.stageAndReferenceCount.compareAndExchange(curr, curr + (1L << 32)))) {
++ // successful
++ return false;
++ }
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ if (!delay) {
++ this.scheduleNow();
++ return false;
++ }
++ return true;
++ }
++
++ public void scheduleNow() {
++ this.loadDataFromDiskTask.schedule(); // will schedule the rest
++ }
++
++ // assumes the current stage cannot be completed
++ // returns false if cancelled, returns true if can proceed
++ private boolean advanceStage(final long expect, final long to) {
++ int failures = 0;
++ for (long curr = this.stageAndReferenceCount.get();;) {
++ if ((curr & STAGE_MASK) != expect) {
++ // must be cancelled
++ return false;
++ }
++
++ final long newVal = (curr & ~STAGE_MASK) | to;
++ if (curr == (curr = this.stageAndReferenceCount.compareAndExchange(curr, newVal))) {
++ return true;
++ }
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ public boolean cancel() {
++ int failures = 0;
++ for (long curr = this.stageAndReferenceCount.get();;) {
++ if ((curr & STAGE_MASK) == STAGE_COMPLETED || (curr & STAGE_MASK) == STAGE_CANCELLED) {
++ return false;
++ }
++
++ if ((curr & STAGE_MASK) == STAGE_NOT_STARTED || (curr & ~STAGE_MASK) == (1L << 32)) {
++ // no other references, so we can cancel
++ final long newVal = STAGE_CANCELLED;
++ if (curr == (curr = this.stageAndReferenceCount.compareAndExchange(curr, newVal))) {
++ this.loadDataFromDiskTask.cancel();
++ if (this.processOffMain != null) {
++ this.processOffMain.cancel();
++ }
++ if (this.processOnMain != null) {
++ this.processOnMain.cancel();
++ }
++ this.onComplete(null);
++ return true;
++ }
++ } else {
++ if ((curr & ~STAGE_MASK) == (0L << 32)) {
++ throw new IllegalStateException("Reference count cannot be zero here");
++ }
++ // just decrease the reference count
++ final long newVal = curr - (1L << 32);
++ if (curr == (curr = this.stageAndReferenceCount.compareAndExchange(curr, newVal))) {
++ return false;
++ }
++ }
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ protected final class DataLoadCallback implements BiConsumer<CompoundTag, Throwable> {
++
++ protected final ProcessOffMainTask offMainTask;
++ protected final ProcessOnMainTask onMainTask;
++
++ public DataLoadCallback(final ProcessOffMainTask offMainTask, final ProcessOnMainTask onMainTask) {
++ this.offMainTask = offMainTask;
++ this.onMainTask = onMainTask;
++ }
++
++ @Override
++ public void accept(final CompoundTag compoundTag, final Throwable throwable) {
++ if (GenericDataLoadTask.this.stageAndReferenceCount.get() == STAGE_CANCELLED) {
++ // don't try to schedule further
++ return;
++ }
++
++ try {
++ if (compoundTag == CANCELLED_DATA) {
++ // cancelled, except this isn't possible
++ LOGGER.error("Data callback says cancelled, but stage does not?");
++ return;
++ }
++
++ // get off of the regionfile callback ASAP, no clue what locks are held right now...
++ if (GenericDataLoadTask.this.processOffMain != null) {
++ this.offMainTask.data = compoundTag;
++ this.offMainTask.throwable = throwable;
++ GenericDataLoadTask.this.processOffMain.queue();
++ return;
++ } else {
++ // no off-main task, so go straight to main
++ this.onMainTask.data = (OnMain)compoundTag;
++ this.onMainTask.throwable = throwable;
++ GenericDataLoadTask.this.processOnMain.queue();
++ }
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr2) {
++ LOGGER.error("Failed I/O callback for task: " + GenericDataLoadTask.this.toString(), thr2);
++ GenericDataLoadTask.this.scheduler.unrecoverableChunkSystemFailure(
++ GenericDataLoadTask.this.chunkX, GenericDataLoadTask.this.chunkZ, Map.of(
++ "Callback throwable", ChunkTaskScheduler.stringIfNull(throwable)
++ ), thr2);
++ }
++ }
++ }
++
++ protected final class ProcessOffMainTask implements Runnable {
++
++ protected CompoundTag data;
++ protected Throwable throwable;
++ protected final ProcessOnMainTask schedule;
++
++ public ProcessOffMainTask(final ProcessOnMainTask schedule) {
++ this.schedule = schedule;
++ }
++
++ @Override
++ public void run() {
++ if (!GenericDataLoadTask.this.advanceStage(STAGE_LOADING, this.schedule == null ? STAGE_COMPLETED : STAGE_PROCESSING)) {
++ // cancelled
++ return;
++ }
++ final TaskResult<OnMain, Throwable> newData = GenericDataLoadTask.this.runOffMain(this.data, this.throwable);
++
++ if (GenericDataLoadTask.this.stageAndReferenceCount.get() == STAGE_CANCELLED) {
++ // don't try to schedule further
++ return;
++ }
++
++ if (this.schedule != null) {
++ final TaskResult<FinalCompletion, Throwable> syncComplete = GenericDataLoadTask.this.completeOnMainOffMain(newData.left, newData.right);
++
++ if (syncComplete != null) {
++ if (GenericDataLoadTask.this.advanceStage(STAGE_PROCESSING, STAGE_COMPLETED)) {
++ GenericDataLoadTask.this.onComplete(syncComplete);
++ } // else: cancelled
++ return;
++ }
++
++ this.schedule.data = newData.left;
++ this.schedule.throwable = newData.right;
++
++ GenericDataLoadTask.this.processOnMain.queue();
++ } else {
++ GenericDataLoadTask.this.onComplete((TaskResult<FinalCompletion, Throwable>)newData);
++ }
++ }
++ }
++
++ protected final class ProcessOnMainTask implements Runnable {
++
++ protected OnMain data;
++ protected Throwable throwable;
++
++ @Override
++ public void run() {
++ if (!GenericDataLoadTask.this.advanceStage(STAGE_PROCESSING, STAGE_COMPLETED)) {
++ // cancelled
++ return;
++ }
++ final TaskResult<FinalCompletion, Throwable> result = GenericDataLoadTask.this.runOnMain(this.data, this.throwable);
++
++ GenericDataLoadTask.this.onComplete(result);
++ }
++ }
++
++ public static final class LoadDataFromDiskTask {
++
++ protected volatile int priority;
++ protected static final VarHandle PRIORITY_HANDLE = ConcurrentUtil.getVarHandle(LoadDataFromDiskTask.class, "priority", int.class);
++
++ protected static final int PRIORITY_EXECUTED = Integer.MIN_VALUE >>> 0;
++ protected static final int PRIORITY_LOAD_SCHEDULED = Integer.MIN_VALUE >>> 1;
++ protected static final int PRIORITY_UNLOAD_SCHEDULED = Integer.MIN_VALUE >>> 2;
++
++ protected static final int PRIORITY_FLAGS = ~Character.MAX_VALUE;
++
++ protected final int getPriorityVolatile() {
++ return (int)PRIORITY_HANDLE.getVolatile((LoadDataFromDiskTask)this);
++ }
++
++ protected final int compareAndExchangePriorityVolatile(final int expect, final int update) {
++ return (int)PRIORITY_HANDLE.compareAndExchange((LoadDataFromDiskTask)this, (int)expect, (int)update);
++ }
++
++ protected final int getAndOrPriorityVolatile(final int val) {
++ return (int)PRIORITY_HANDLE.getAndBitwiseOr((LoadDataFromDiskTask)this, (int)val);
++ }
++
++ protected final void setPriorityPlain(final int val) {
++ PRIORITY_HANDLE.set((LoadDataFromDiskTask)this, (int)val);
++ }
++
++ private final ServerLevel world;
++ private final int chunkX;
++ private final int chunkZ;
++
++ private final RegionFileIOThread.RegionFileType type;
++ private Cancellable dataLoadTask;
++ private Cancellable dataUnloadCancellable;
++ private DelayedPrioritisedTask dataUnloadTask;
++
++ private final BiConsumer<CompoundTag, Throwable> onComplete;
++
++ // onComplete should be caller sensitive, it may complete synchronously with schedule() - which does
++ // hold a priority lock.
++ public LoadDataFromDiskTask(final ServerLevel world, final int chunkX, final int chunkZ,
++ final RegionFileIOThread.RegionFileType type,
++ final BiConsumer<CompoundTag, Throwable> onComplete,
++ final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.world = world;
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ this.type = type;
++ this.onComplete = onComplete;
++ this.setPriorityPlain(priority.priority);
++ }
++
++ private void complete(final CompoundTag data, final Throwable throwable) {
++ try {
++ this.onComplete.accept(data, throwable);
++ } catch (final Throwable thr2) {
++ this.world.chunkTaskScheduler.unrecoverableChunkSystemFailure(this.chunkX, this.chunkZ, Map.of(
++ "Completed throwable", ChunkTaskScheduler.stringIfNull(throwable),
++ "Regionfile type", ChunkTaskScheduler.stringIfNull(this.type)
++ ), thr2);
++ if (thr2 instanceof ThreadDeath) {
++ throw (ThreadDeath)thr2;
++ }
++ }
++ }
++
++ protected boolean markExecuting() {
++ return (this.getAndOrPriorityVolatile(PRIORITY_EXECUTED) & PRIORITY_EXECUTED) == 0;
++ }
++
++ protected boolean isMarkedExecuted() {
++ return (this.getPriorityVolatile() & PRIORITY_EXECUTED) != 0;
++ }
++
++ public void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++
++ int failures = 0;
++ for (int curr = this.getPriorityVolatile();;) {
++ if ((curr & PRIORITY_EXECUTED) != 0) {
++ // cancelled or executed
++ return;
++ }
++
++ if ((curr & PRIORITY_LOAD_SCHEDULED) != 0) {
++ RegionFileIOThread.lowerPriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
++ return;
++ }
++
++ if ((curr & PRIORITY_UNLOAD_SCHEDULED) != 0) {
++ if (this.dataUnloadTask != null) {
++ this.dataUnloadTask.lowerPriority(priority);
++ }
++ // no return - we need to propagate priority
++ }
++
++ if (!priority.isHigherPriority(curr & ~PRIORITY_FLAGS)) {
++ return;
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority | (curr & PRIORITY_FLAGS)))) {
++ return;
++ }
++
++ // failed, retry
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ public void setPriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++
++ int failures = 0;
++ for (int curr = this.getPriorityVolatile();;) {
++ if ((curr & PRIORITY_EXECUTED) != 0) {
++ // cancelled or executed
++ return;
++ }
++
++ if ((curr & PRIORITY_LOAD_SCHEDULED) != 0) {
++ RegionFileIOThread.setPriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
++ return;
++ }
++
++ if ((curr & PRIORITY_UNLOAD_SCHEDULED) != 0) {
++ if (this.dataUnloadTask != null) {
++ this.dataUnloadTask.setPriority(priority);
++ }
++ // no return - we need to propagate priority
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority | (curr & PRIORITY_FLAGS)))) {
++ return;
++ }
++
++ // failed, retry
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ public void raisePriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++
++ int failures = 0;
++ for (int curr = this.getPriorityVolatile();;) {
++ if ((curr & PRIORITY_EXECUTED) != 0) {
++ // cancelled or executed
++ return;
++ }
++
++ if ((curr & PRIORITY_LOAD_SCHEDULED) != 0) {
++ RegionFileIOThread.raisePriority(this.world, this.chunkX, this.chunkZ, this.type, priority);
++ return;
++ }
++
++ if ((curr & PRIORITY_UNLOAD_SCHEDULED) != 0) {
++ if (this.dataUnloadTask != null) {
++ this.dataUnloadTask.raisePriority(priority);
++ }
++ // no return - we need to propagate priority
++ }
++
++ if (!priority.isLowerPriority(curr & ~PRIORITY_FLAGS)) {
++ return;
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority | (curr & PRIORITY_FLAGS)))) {
++ return;
++ }
++
++ // failed, retry
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ public void cancel() {
++ if ((this.getAndOrPriorityVolatile(PRIORITY_EXECUTED) & PRIORITY_EXECUTED) != 0) {
++ // cancelled or executed already
++ return;
++ }
++
++ // OK if we miss the field read, the task cannot complete if the cancelled bit is set and
++ // the write to dataLoadTask will check for the cancelled bit
++ if (this.dataUnloadCancellable != null) {
++ this.dataUnloadCancellable.cancel();
++ }
++
++ if (this.dataLoadTask != null) {
++ this.dataLoadTask.cancel();
++ }
++
++ this.complete(CANCELLED_DATA, null);
++ }
++
++ private final AtomicBoolean scheduled = new AtomicBoolean();
++
++ public void schedule() {
++ if (this.scheduled.getAndSet(true)) {
++ throw new IllegalStateException("schedule() called twice");
++ }
++ int priority = this.getPriorityVolatile();
++
++ if ((priority & PRIORITY_EXECUTED) != 0) {
++ // cancelled
++ return;
++ }
++
++ final BiConsumer<CompoundTag, Throwable> consumer = (final CompoundTag data, final Throwable thr) -> {
++ // because cancelScheduled() cannot actually stop this task from executing in every case, we need
++ // to mark complete here to ensure we do not double complete
++ if (LoadDataFromDiskTask.this.markExecuting()) {
++ LoadDataFromDiskTask.this.complete(data, thr);
++ } // else: cancelled
++ };
++
++ final PrioritisedExecutor.Priority initialPriority = PrioritisedExecutor.Priority.getPriority(priority);
++ boolean scheduledUnload = false;
++
++ final NewChunkHolder holder = this.world.chunkTaskScheduler.chunkHolderManager.getChunkHolder(this.chunkX, this.chunkZ);
++ if (holder != null) {
++ final BiConsumer<CompoundTag, Throwable> unloadConsumer = (final CompoundTag data, final Throwable thr) -> {
++ if (data != null) {
++ consumer.accept(data, null);
++ } else {
++ // need to schedule task
++ LoadDataFromDiskTask.this.schedule(false, consumer, PrioritisedExecutor.Priority.getPriority(LoadDataFromDiskTask.this.getPriorityVolatile() & ~PRIORITY_FLAGS));
++ }
++ };
++ Cancellable unloadCancellable = null;
++ CompoundTag syncComplete = null;
++ final NewChunkHolder.UnloadTask unloadTask = holder.getUnloadTask(this.type); // can be null if no task exists
++ final Completable<CompoundTag> unloadCompletable = unloadTask == null ? null : unloadTask.completable();
++ if (unloadCompletable != null) {
++ unloadCancellable = unloadCompletable.addAsynchronousWaiter(unloadConsumer);
++ if (unloadCancellable == null) {
++ syncComplete = unloadCompletable.getResult();
++ }
++ }
++
++ if (syncComplete != null) {
++ consumer.accept(syncComplete, null);
++ return;
++ }
++
++ if (unloadCancellable != null) {
++ scheduledUnload = true;
++ this.dataUnloadCancellable = unloadCancellable;
++ this.dataUnloadTask = unloadTask.task();
++ }
++ }
++
++ this.schedule(scheduledUnload, consumer, initialPriority);
++ }
++
++ private void schedule(final boolean scheduledUnload, final BiConsumer<CompoundTag, Throwable> consumer, final PrioritisedExecutor.Priority initialPriority) {
++ int priority = this.getPriorityVolatile();
++
++ if ((priority & PRIORITY_EXECUTED) != 0) {
++ // cancelled
++ return;
++ }
++
++ if (!scheduledUnload) {
++ this.dataLoadTask = RegionFileIOThread.loadDataAsync(
++ this.world, this.chunkX, this.chunkZ, this.type, consumer,
++ initialPriority.isHigherPriority(PrioritisedExecutor.Priority.NORMAL), initialPriority
++ );
++ }
++
++ int failures = 0;
++ for (;;) {
++ if (priority == (priority = this.compareAndExchangePriorityVolatile(priority, priority | (scheduledUnload ? PRIORITY_UNLOAD_SCHEDULED : PRIORITY_LOAD_SCHEDULED)))) {
++ return;
++ }
++
++ if ((priority & PRIORITY_EXECUTED) != 0) {
++ // cancelled or executed
++ if (this.dataUnloadCancellable != null) {
++ this.dataUnloadCancellable.cancel();
++ }
++
++ if (this.dataLoadTask != null) {
++ this.dataLoadTask.cancel();
++ }
++ return;
++ }
++
++ if (scheduledUnload) {
++ if (this.dataUnloadTask != null) {
++ this.dataUnloadTask.setPriority(PrioritisedExecutor.Priority.getPriority(priority & ~PRIORITY_FLAGS));
++ }
++ } else {
++ RegionFileIOThread.setPriority(this.world, this.chunkX, this.chunkZ, this.type, PrioritisedExecutor.Priority.getPriority(priority & ~PRIORITY_FLAGS));
++ }
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ /*
++ private static final class LoadDataPriorityHolder extends PriorityHolder {
++
++ protected final LoadDataFromDiskTask task;
++
++ protected LoadDataPriorityHolder(final PrioritisedExecutor.Priority priority, final LoadDataFromDiskTask task) {
++ super(priority);
++ this.task = task;
++ }
++
++ @Override
++ protected void cancelScheduled() {
++ final Cancellable dataLoadTask = this.task.dataLoadTask;
++ if (dataLoadTask != null) {
++ // OK if we miss the field read, the task cannot complete if the cancelled bit is set and
++ // the write to dataLoadTask will check for the cancelled bit
++ this.task.dataLoadTask.cancel();
++ }
++ this.task.complete(CANCELLED_DATA, null);
++ }
++
++ @Override
++ protected PrioritisedExecutor.Priority getScheduledPriority() {
++ final LoadDataFromDiskTask task = this.task;
++ return RegionFileIOThread.getPriority(task.world, task.chunkX, task.chunkZ, task.type);
++ }
++
++ @Override
++ protected void scheduleTask(final PrioritisedExecutor.Priority priority) {
++ final LoadDataFromDiskTask task = this.task;
++ final BiConsumer<CompoundTag, Throwable> consumer = (final CompoundTag data, final Throwable thr) -> {
++ // because cancelScheduled() cannot actually stop this task from executing in every case, we need
++ // to mark complete here to ensure we do not double complete
++ if (LoadDataPriorityHolder.this.markExecuting()) {
++ LoadDataPriorityHolder.this.task.complete(data, thr);
++ } // else: cancelled
++ };
++ task.dataLoadTask = RegionFileIOThread.loadDataAsync(
++ task.world, task.chunkX, task.chunkZ, task.type, consumer,
++ priority.isHigherPriority(PrioritisedExecutor.Priority.NORMAL), priority
++ );
++ if (this.isMarkedExecuted()) {
++ // if we are marked as completed, it could be:
++ // 1. we were cancelled
++ // 2. the consumer was completed
++ // in the 2nd case, cancel() does nothing
++ // in the 1st case, we ensure cancel() is called as it is possible for the cancelling thread
++ // to miss the field write here
++ task.dataLoadTask.cancel();
++ }
++ }
++
++ @Override
++ protected void lowerPriorityScheduled(final PrioritisedExecutor.Priority priority) {
++ final LoadDataFromDiskTask task = this.task;
++ RegionFileIOThread.lowerPriority(task.world, task.chunkX, task.chunkZ, task.type, priority);
++ }
++
++ @Override
++ protected void setPriorityScheduled(final PrioritisedExecutor.Priority priority) {
++ final LoadDataFromDiskTask task = this.task;
++ RegionFileIOThread.setPriority(task.world, task.chunkX, task.chunkZ, task.type, priority);
++ }
++
++ @Override
++ protected void raisePriorityScheduled(final PrioritisedExecutor.Priority priority) {
++ final LoadDataFromDiskTask task = this.task;
++ RegionFileIOThread.raisePriority(task.world, task.chunkX, task.chunkZ, task.type, priority);
++ }
++ }
++ */
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9a4ec0f1fb3bac0e84e6bd3aaeb77f44e248aadb
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/NewChunkHolder.java
+@@ -0,0 +1,2071 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.completable.Completable;
++import ca.spottedleaf.concurrentutil.executor.Cancellable;
++import ca.spottedleaf.concurrentutil.executor.standard.DelayedPrioritisedTask;
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import com.google.gson.JsonArray;
++import com.google.gson.JsonElement;
++import com.google.gson.JsonObject;
++import com.google.gson.JsonPrimitive;
++import com.mojang.logging.LogUtils;
++import io.papermc.paper.chunk.system.io.RegionFileIOThread;
++import io.papermc.paper.chunk.system.poi.PoiChunk;
++import io.papermc.paper.util.CoordinateUtils;
++import io.papermc.paper.util.TickThread;
++import io.papermc.paper.util.WorldUtil;
++import io.papermc.paper.world.ChunkEntitySlices;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
++import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ChunkMap;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.server.level.TicketType;
++import net.minecraft.world.entity.Entity;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
++import net.minecraft.world.level.chunk.ImposterProtoChunk;
++import net.minecraft.world.level.chunk.LevelChunk;
++import net.minecraft.world.level.chunk.storage.ChunkSerializer;
++import net.minecraft.world.level.chunk.storage.EntityStorage;
++import org.slf4j.Logger;
++import java.lang.invoke.VarHandle;
++import java.util.ArrayList;
++import java.util.Iterator;
++import java.util.List;
++import java.util.Map;
++import java.util.Objects;
++import java.util.concurrent.atomic.AtomicBoolean;
++import java.util.function.Consumer;
++
++public final class NewChunkHolder {
++
++ private static final Logger LOGGER = LogUtils.getLogger();
++
++ public static final Thread.UncaughtExceptionHandler CHUNKSYSTEM_UNCAUGHT_EXCEPTION_HANDLER = new Thread.UncaughtExceptionHandler() {
++ @Override
++ public void uncaughtException(final Thread thread, final Throwable throwable) {
++ if (!(throwable instanceof ThreadDeath)) {
++ LOGGER.error("Uncaught exception in thread " + thread.getName(), throwable);
++ }
++ }
++ };
++
++ public final ServerLevel world;
++ public final int chunkX;
++ public final int chunkZ;
++
++ public final ChunkTaskScheduler scheduler;
++
++ // load/unload state
++
++ // chunk data state
++
++ private ChunkEntitySlices entityChunk;
++ // entity chunk that is loaded, but not yet deserialized
++ private CompoundTag pendingEntityChunk;
++
++ ChunkEntitySlices loadInEntityChunk(final boolean transientChunk) {
++ TickThread.ensureTickThread(this.world, this.chunkX, this.chunkZ, "Cannot sync load entity data off-main");
++ final CompoundTag entityChunk;
++ final ChunkEntitySlices ret;
++ this.scheduler.schedulingLock.lock();
++ try {
++ if (this.entityChunk != null && (transientChunk || !this.entityChunk.isTransient())) {
++ return this.entityChunk;
++ }
++ final CompoundTag pendingEntityChunk = this.pendingEntityChunk;
++ if (!transientChunk && pendingEntityChunk == null) {
++ throw new IllegalStateException("Must load entity data from disk before loading in the entity chunk!");
++ }
++
++ if (this.entityChunk == null) {
++ ret = this.entityChunk = new ChunkEntitySlices(
++ this.world, this.chunkX, this.chunkZ, this.getChunkStatus(),
++ WorldUtil.getMinSection(this.world), WorldUtil.getMaxSection(this.world)
++ );
++
++ ret.setTransient(transientChunk);
++
++ this.world.getEntityLookup().entitySectionLoad(this.chunkX, this.chunkZ, ret);
++ } else {
++ // transientChunk = false here
++ ret = this.entityChunk;
++ this.entityChunk.setTransient(false);
++ }
++
++ if (!transientChunk) {
++ this.pendingEntityChunk = null;
++ entityChunk = pendingEntityChunk == EMPTY_ENTITY_CHUNK ? null : pendingEntityChunk;
++ } else {
++ entityChunk = null;
++ }
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++
++ if (!transientChunk) {
++ if (entityChunk != null) {
++ final List<Entity> entities = EntityStorage.readEntities(this.world, entityChunk);
++
++ this.world.getEntityLookup().addEntityChunkEntities(entities);
++ }
++ }
++
++ return ret;
++ }
++
++ // needed to distinguish whether the entity chunk has been read from disk but is empty or whether it has _not_
++ // been read from disk
++ private static final CompoundTag EMPTY_ENTITY_CHUNK = new CompoundTag();
++
++ private ChunkLoadTask.EntityDataLoadTask entityDataLoadTask;
++ // note: if entityDataLoadTask is cancelled, but on its completion entityDataLoadTaskWaiters.size() != 0,
++ // then the task is rescheduled
++ private List<GenericDataLoadTaskCallback> entityDataLoadTaskWaiters;
++
++ public ChunkLoadTask.EntityDataLoadTask getEntityDataLoadTask() {
++ return this.entityDataLoadTask;
++ }
++
++ // must hold schedule lock for the two below functions
++
++ // returns only if the data has been loaded from disk, DOES NOT relate to whether it has been deserialized
++ // or added into the world (or even into entityChunk)
++ public boolean isEntityChunkNBTLoaded() {
++ return (this.entityChunk != null && !this.entityChunk.isTransient()) || this.pendingEntityChunk != null;
++ }
++
++ private void completeEntityLoad(final GenericDataLoadTask.TaskResult<CompoundTag, Throwable> result) {
++ final List<GenericDataLoadTaskCallback> completeWaiters;
++ ChunkLoadTask.EntityDataLoadTask entityDataLoadTask = null;
++ boolean scheduleEntityTask = false;
++ this.scheduler.schedulingLock.lock();
++ try {
++ final List<GenericDataLoadTaskCallback> waiters = this.entityDataLoadTaskWaiters;
++ this.entityDataLoadTask = null;
++ if (result != null) {
++ this.entityDataLoadTaskWaiters = null;
++ this.pendingEntityChunk = result.left() == null ? EMPTY_ENTITY_CHUNK : result.left();
++ if (result.right() != null) {
++ LOGGER.error("Unhandled entity data load exception, data data will be lost: ", result.right());
++ }
++
++ completeWaiters = waiters;
++ } else {
++ // cancelled
++ completeWaiters = null;
++
++ // need to re-schedule?
++ if (waiters.isEmpty()) {
++ this.entityDataLoadTaskWaiters = null;
++ // no tasks to schedule _for_
++ } else {
++ entityDataLoadTask = this.entityDataLoadTask = new ChunkLoadTask.EntityDataLoadTask(
++ this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority()
++ );
++ entityDataLoadTask.addCallback(this::completeEntityLoad);
++ // need one schedule() per waiter
++ for (final GenericDataLoadTaskCallback callback : waiters) {
++ scheduleEntityTask |= entityDataLoadTask.schedule(true);
++ }
++ }
++ }
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++
++ if (scheduleEntityTask) {
++ entityDataLoadTask.scheduleNow();
++ }
++
++ // avoid holding the scheduling lock while completing
++ if (completeWaiters != null) {
++ for (final GenericDataLoadTaskCallback callback : completeWaiters) {
++ callback.accept(result);
++ }
++ }
++
++ this.scheduler.schedulingLock.lock();
++ try {
++ this.checkUnload();
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++ }
++
++ // note: it is guaranteed that the consumer cannot be called for the entirety that the schedule lock is held
++ // however, when the consumer is invoked, it will hold the schedule lock
++ public GenericDataLoadTaskCallback getOrLoadEntityData(final Consumer<GenericDataLoadTask.TaskResult<CompoundTag, Throwable>> consumer) {
++ if (this.isEntityChunkNBTLoaded()) {
++ throw new IllegalStateException("Cannot load entity data, it is already loaded");
++ }
++ // why not just acquire the lock? because the caller NEEDS to call isEntityChunkNBTLoaded before this!
++ if (!this.scheduler.schedulingLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Must hold scheduling lock");
++ }
++
++ final GenericDataLoadTaskCallback ret = new EntityDataLoadTaskCallback((Consumer)consumer, this);
++
++ if (this.entityDataLoadTask == null) {
++ this.entityDataLoadTask = new ChunkLoadTask.EntityDataLoadTask(
++ this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority()
++ );
++ this.entityDataLoadTask.addCallback(this::completeEntityLoad);
++ this.entityDataLoadTaskWaiters = new ArrayList<>();
++ }
++ this.entityDataLoadTaskWaiters.add(ret);
++ if (this.entityDataLoadTask.schedule(true)) {
++ ret.schedule = this.entityDataLoadTask;
++ }
++ this.checkUnload();
++
++ return ret;
++ }
++
++ private static final class EntityDataLoadTaskCallback extends GenericDataLoadTaskCallback {
++
++ public EntityDataLoadTaskCallback(final Consumer<GenericDataLoadTask.TaskResult<?, Throwable>> consumer, final NewChunkHolder chunkHolder) {
++ super(consumer, chunkHolder);
++ }
++
++ @Override
++ void internalCancel() {
++ this.chunkHolder.entityDataLoadTaskWaiters.remove(this);
++ this.chunkHolder.entityDataLoadTask.cancel();
++ }
++ }
++
++ private PoiChunk poiChunk;
++
++ private ChunkLoadTask.PoiDataLoadTask poiDataLoadTask;
++ // note: if entityDataLoadTask is cancelled, but on its completion entityDataLoadTaskWaiters.size() != 0,
++ // then the task is rescheduled
++ private List<GenericDataLoadTaskCallback> poiDataLoadTaskWaiters;
++
++ public ChunkLoadTask.PoiDataLoadTask getPoiDataLoadTask() {
++ return this.poiDataLoadTask;
++ }
++
++ // must hold schedule lock for the two below functions
++
++ public boolean isPoiChunkLoaded() {
++ return this.poiChunk != null;
++ }
++
++ private void completePoiLoad(final GenericDataLoadTask.TaskResult<PoiChunk, Throwable> result) {
++ final List<GenericDataLoadTaskCallback> completeWaiters;
++ ChunkLoadTask.PoiDataLoadTask poiDataLoadTask = null;
++ boolean schedulePoiTask = false;
++ this.scheduler.schedulingLock.lock();
++ try {
++ final List<GenericDataLoadTaskCallback> waiters = this.poiDataLoadTaskWaiters;
++ this.poiDataLoadTask = null;
++ if (result != null) {
++ this.poiDataLoadTaskWaiters = null;
++ this.poiChunk = result.left();
++ if (result.right() != null) {
++ LOGGER.error("Unhandled poi load exception, poi data will be lost: ", result.right());
++ }
++
++ completeWaiters = waiters;
++ } else {
++ // cancelled
++ completeWaiters = null;
++
++ // need to re-schedule?
++ if (waiters.isEmpty()) {
++ this.poiDataLoadTaskWaiters = null;
++ // no tasks to schedule _for_
++ } else {
++ poiDataLoadTask = this.poiDataLoadTask = new ChunkLoadTask.PoiDataLoadTask(
++ this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority()
++ );
++ poiDataLoadTask.addCallback(this::completePoiLoad);
++ // need one schedule() per waiter
++ for (final GenericDataLoadTaskCallback callback : waiters) {
++ schedulePoiTask |= poiDataLoadTask.schedule(true);
++ }
++ }
++ }
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++
++ if (schedulePoiTask) {
++ poiDataLoadTask.scheduleNow();
++ }
++
++ // avoid holding the scheduling lock while completing
++ if (completeWaiters != null) {
++ for (final GenericDataLoadTaskCallback callback : completeWaiters) {
++ callback.accept(result);
++ }
++ }
++ this.scheduler.schedulingLock.lock();
++ try {
++ this.checkUnload();
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++ }
++
++ // note: it is guaranteed that the consumer cannot be called for the entirety that the schedule lock is held
++ // however, when the consumer is invoked, it will hold the schedule lock
++ public GenericDataLoadTaskCallback getOrLoadPoiData(final Consumer<GenericDataLoadTask.TaskResult<PoiChunk, Throwable>> consumer) {
++ if (this.isPoiChunkLoaded()) {
++ throw new IllegalStateException("Cannot load poi data, it is already loaded");
++ }
++ // why not just acquire the lock? because the caller NEEDS to call isPoiChunkLoaded before this!
++ if (!this.scheduler.schedulingLock.isHeldByCurrentThread()) {
++ throw new IllegalStateException("Must hold scheduling lock");
++ }
++
++ final GenericDataLoadTaskCallback ret = new PoiDataLoadTaskCallback((Consumer)consumer, this);
++
++ if (this.poiDataLoadTask == null) {
++ this.poiDataLoadTask = new ChunkLoadTask.PoiDataLoadTask(
++ this.scheduler, this.world, this.chunkX, this.chunkZ, this.getEffectivePriority()
++ );
++ this.poiDataLoadTask.addCallback(this::completePoiLoad);
++ this.poiDataLoadTaskWaiters = new ArrayList<>();
++ }
++ this.poiDataLoadTaskWaiters.add(ret);
++ if (this.poiDataLoadTask.schedule(true)) {
++ ret.schedule = this.poiDataLoadTask;
++ }
++ this.checkUnload();
++
++ return ret;
++ }
++
++ private static final class PoiDataLoadTaskCallback extends GenericDataLoadTaskCallback {
++
++ public PoiDataLoadTaskCallback(final Consumer<GenericDataLoadTask.TaskResult<?, Throwable>> consumer, final NewChunkHolder chunkHolder) {
++ super(consumer, chunkHolder);
++ }
++
++ @Override
++ void internalCancel() {
++ this.chunkHolder.poiDataLoadTaskWaiters.remove(this);
++ this.chunkHolder.poiDataLoadTask.cancel();
++ }
++ }
++
++ public static abstract class GenericDataLoadTaskCallback implements Cancellable, Consumer<GenericDataLoadTask.TaskResult<?, Throwable>> {
++
++ protected final Consumer<GenericDataLoadTask.TaskResult<?, Throwable>> consumer;
++ protected final NewChunkHolder chunkHolder;
++ protected boolean completed;
++ protected GenericDataLoadTask<?, ?> schedule;
++ protected final AtomicBoolean scheduled = new AtomicBoolean();
++
++ public GenericDataLoadTaskCallback(final Consumer<GenericDataLoadTask.TaskResult<?, Throwable>> consumer,
++ final NewChunkHolder chunkHolder) {
++ this.consumer = consumer;
++ this.chunkHolder = chunkHolder;
++ }
++
++ public void schedule() {
++ if (this.scheduled.getAndSet(true)) {
++ throw new IllegalStateException("Double calling schedule()");
++ }
++ if (this.schedule != null) {
++ this.schedule.scheduleNow();
++ this.schedule = null;
++ }
++ }
++
++ boolean isCompleted() {
++ return this.completed;
++ }
++
++ // must hold scheduling lock
++ private boolean setCompleted() {
++ if (this.completed) {
++ return false;
++ }
++ return this.completed = true;
++ }
++
++ @Override
++ public void accept(final GenericDataLoadTask.TaskResult<?, Throwable> result) {
++ if (result != null) {
++ if (this.setCompleted()) {
++ this.consumer.accept(result);
++ } else {
++ throw new IllegalStateException("Cannot be cancelled at this point");
++ }
++ } else {
++ throw new NullPointerException("Result cannot be null (cancelled)");
++ }
++ }
++
++ // holds scheduling lock
++ abstract void internalCancel();
++
++ @Override
++ public boolean cancel() {
++ this.chunkHolder.scheduler.schedulingLock.lock();
++ try {
++ if (!this.completed) {
++ this.completed = true;
++ this.internalCancel();
++ return true;
++ }
++ return false;
++ } finally {
++ this.chunkHolder.scheduler.schedulingLock.unlock();
++ }
++ }
++ }
++
++ private ChunkAccess currentChunk;
++
++ // generation status state
++
++ /**
++ * Current status the chunk has been brought up to by the chunk system. null indicates no work at all
++ */
++ private ChunkStatus currentGenStatus;
++
++ // This allows unsynchronised access to the chunk and last gen status
++ private volatile ChunkCompletion lastChunkCompletion;
++
++ public ChunkCompletion getLastChunkCompletion() {
++ return this.lastChunkCompletion;
++ }
++
++ public static final record ChunkCompletion(ChunkAccess chunk, ChunkStatus genStatus) {};
++
++ /**
++ * The target final chunk status the chunk system will bring the chunk to.
++ */
++ private ChunkStatus requestedGenStatus;
++
++ private ChunkProgressionTask generationTask;
++ private ChunkStatus generationTaskStatus;
++
++ /**
++ * contains the neighbours that this chunk generation is blocking on
++ */
++ protected final ReferenceLinkedOpenHashSet<NewChunkHolder> neighboursBlockingGenTask = new ReferenceLinkedOpenHashSet<>(4);
++
++ /**
++ * map of ChunkHolder -> Required Status for this chunk
++ */
++ protected final Reference2ObjectLinkedOpenHashMap<NewChunkHolder, ChunkStatus> neighboursWaitingForUs = new Reference2ObjectLinkedOpenHashMap<>();
++
++ public void addGenerationBlockingNeighbour(final NewChunkHolder neighbour) {
++ this.neighboursBlockingGenTask.add(neighbour);
++ }
++
++ public void addWaitingNeighbour(final NewChunkHolder neighbour, final ChunkStatus requiredStatus) {
++ final boolean wasEmpty = this.neighboursWaitingForUs.isEmpty();
++ this.neighboursWaitingForUs.put(neighbour, requiredStatus);
++ if (wasEmpty) {
++ this.checkUnload();
++ }
++ }
++
++ // priority state
++
++ // the target priority for this chunk to generate at
++ // TODO this will screw over scheduling at lower priorities to neighbours, fix
++ private PrioritisedExecutor.Priority priority = PrioritisedExecutor.Priority.NORMAL;
++ private boolean priorityLocked;
++
++ // the priority neighbouring chunks have requested this chunk generate at
++ private PrioritisedExecutor.Priority neighbourRequestedPriority = PrioritisedExecutor.Priority.IDLE;
++
++ public PrioritisedExecutor.Priority getEffectivePriority() {
++ return PrioritisedExecutor.Priority.max(this.priority, this.neighbourRequestedPriority);
++ }
++
++ protected void recalculateNeighbourRequestedPriority() {
++ if (this.neighboursWaitingForUs.isEmpty()) {
++ this.neighbourRequestedPriority = PrioritisedExecutor.Priority.IDLE;
++ return;
++ }
++
++ PrioritisedExecutor.Priority max = PrioritisedExecutor.Priority.IDLE;
++
++ for (final NewChunkHolder holder : this.neighboursWaitingForUs.keySet()) {
++ final PrioritisedExecutor.Priority neighbourPriority = holder.getEffectivePriority();
++ if (neighbourPriority.isHigherPriority(max)) {
++ max = neighbourPriority;
++ }
++ }
++
++ final PrioritisedExecutor.Priority current = this.getEffectivePriority();
++ this.neighbourRequestedPriority = max;
++ final PrioritisedExecutor.Priority next = this.getEffectivePriority();
++
++ if (current == next) {
++ return;
++ }
++
++ // our effective priority has changed, so change our task
++ if (this.generationTask != null) {
++ this.generationTask.setPriority(next);
++ }
++
++ // now propagate this to our neighbours
++ this.recalculateNeighbourPriorities();
++ }
++
++ public void recalculateNeighbourPriorities() {
++ for (final NewChunkHolder holder : this.neighboursBlockingGenTask) {
++ holder.recalculateNeighbourRequestedPriority();
++ }
++ }
++
++ // must hold scheduling lock
++ public void raisePriority(final PrioritisedExecutor.Priority priority) {
++ if (this.priority != null && this.priority.isHigherOrEqualPriority(priority)) {
++ return;
++ }
++ this.setPriority(priority);
++ }
++
++ private void lockPriority() {
++ this.priority = PrioritisedExecutor.Priority.NORMAL;
++ this.priorityLocked = true;
++ }
++
++ // must hold scheduling lock
++ public void setPriority(final PrioritisedExecutor.Priority priority) {
++ if (this.priorityLocked) {
++ return;
++ }
++ final PrioritisedExecutor.Priority old = this.getEffectivePriority();
++ this.priority = priority;
++ final PrioritisedExecutor.Priority newPriority = this.getEffectivePriority();
++
++ if (old != newPriority) {
++ if (this.generationTask != null) {
++ this.generationTask.setPriority(newPriority);
++ }
++ }
++
++ this.recalculateNeighbourPriorities();
++ }
++
++ // must hold scheduling lock
++ public void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ if (this.priority != null && this.priority.isLowerOrEqualPriority(priority)) {
++ return;
++ }
++ this.setPriority(priority);
++ }
++
++ // error handling state
++ private ChunkStatus failedGenStatus;
++ private Throwable genTaskException;
++ private Thread genTaskFailedThread;
++
++ private boolean failedLightUpdate;
++
++ public void failedLightUpdate() {
++ this.failedLightUpdate = true;
++ }
++
++ public boolean hasFailedGeneration() {
++ return this.genTaskException != null;
++ }
++
++ // ticket level state
++ private int oldTicketLevel = ChunkMap.MAX_CHUNK_DISTANCE + 1;
++ private int currentTicketLevel = ChunkMap.MAX_CHUNK_DISTANCE + 1;
++
++ public int getTicketLevel() {
++ return this.currentTicketLevel;
++ }
++
++ public final ChunkHolder vanillaChunkHolder;
++
++ public NewChunkHolder(final ServerLevel world, final int chunkX, final int chunkZ, final ChunkTaskScheduler scheduler) {
++ this.world = world;
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ this.scheduler = scheduler;
++ this.vanillaChunkHolder = new ChunkHolder(new ChunkPos(chunkX, chunkZ), world, world.getLightEngine(), world.chunkSource.chunkMap, this);
++ }
++
++ protected ImposterProtoChunk wrappedChunkForNeighbour;
++
++ // holds scheduling lock
++ public ChunkAccess getChunkForNeighbourAccess() {
++ // Vanilla overrides the status futures with an imposter chunk to prevent writes to full chunks
++ // But we don't store per-status futures, so we need this hack
++ if (this.wrappedChunkForNeighbour != null) {
++ return this.wrappedChunkForNeighbour;
++ }
++ final ChunkAccess ret = this.currentChunk;
++ return ret instanceof LevelChunk fullChunk ? this.wrappedChunkForNeighbour = new ImposterProtoChunk(fullChunk, false) : ret;
++ }
++
++ public ChunkAccess getCurrentChunk() {
++ return this.currentChunk;
++ }
++
++ int getCurrentTicketLevel() {
++ return this.currentTicketLevel;
++ }
++
++ void updateTicketLevel(final int toLevel) {
++ this.currentTicketLevel = toLevel;
++ }
++
++ private int totalNeighboursUsingThisChunk = 0;
++
++ // holds schedule lock
++ public void addNeighbourUsingChunk() {
++ final int now = ++this.totalNeighboursUsingThisChunk;
++
++ if (now == 1) {
++ this.checkUnload();
++ }
++ }
++
++ // holds schedule lock
++ public void removeNeighbourUsingChunk() {
++ final int now = --this.totalNeighboursUsingThisChunk;
++
++ if (now == 0) {
++ this.checkUnload();
++ }
++
++ if (now < 0) {
++ throw new IllegalStateException("Neighbours using this chunk cannot be negative");
++ }
++ }
++
++ // must hold scheduling lock
++ // returns string reason for why chunk should remain loaded, null otherwise
++ public final String isSafeToUnload() {
++ // is ticket level below threshold?
++ if (this.oldTicketLevel <= ChunkHolderManager.MAX_TICKET_LEVEL) {
++ return "ticket_level";
++ }
++
++ // are we being used by another chunk for generation?
++ if (this.totalNeighboursUsingThisChunk != 0) {
++ return "neighbours_generating";
++ }
++
++ // are we going to be used by another chunk for generation?
++ if (!this.neighboursWaitingForUs.isEmpty()) {
++ return "neighbours_waiting";
++ }
++
++ // chunk must be marked inaccessible (i.e unloaded to plugins)
++ if (this.getChunkStatus() != ChunkHolder.FullChunkStatus.INACCESSIBLE) {
++ return "fullchunkstatus";
++ }
++
++ // are we currently generating anything, or have requested generation?
++ if (this.generationTask != null) {
++ return "generating";
++ }
++ if (this.requestedGenStatus != null) {
++ return "requested_generation";
++ }
++
++ // entity data requested?
++ if (this.entityDataLoadTask != null) {
++ return "entity_data_requested";
++ }
++
++ // poi data requested?
++ if (this.poiDataLoadTask != null) {
++ return "poi_data_requested";
++ }
++
++ // are we pending serialization?
++ if (this.entityDataUnload != null) {
++ return "entity_serialization";
++ }
++ if (this.poiDataUnload != null) {
++ return "poi_serialization";
++ }
++ if (this.chunkDataUnload != null) {
++ return "chunk_serialization";
++ }
++
++ // Note: light tasks do not need a check, as they add a ticket.
++
++ // nothing is using this chunk, so it should be unloaded
++ return null;
++ }
++
++ /** Unloaded from chunk map */
++ boolean killed;
++
++ // must hold scheduling lock
++ private void checkUnload() {
++ if (this.killed) {
++ return;
++ }
++ if (this.isSafeToUnload() == null) {
++ // ensure in unload queue
++ this.scheduler.chunkHolderManager.unloadQueue.add(this);
++ } else {
++ // ensure not in unload queue
++ this.scheduler.chunkHolderManager.unloadQueue.remove(this);
++ }
++ }
++
++ static final record UnloadState(NewChunkHolder holder, ChunkAccess chunk, ChunkEntitySlices entityChunk, PoiChunk poiChunk) {};
++
++ // note: these are completed with null to indicate that no write occurred
++ // they are also completed with null to indicate a null write occurred
++ private UnloadTask chunkDataUnload;
++ private UnloadTask entityDataUnload;
++ private UnloadTask poiDataUnload;
++
++ public static final record UnloadTask(Completable<CompoundTag> completable, DelayedPrioritisedTask task) {}
++
++ public UnloadTask getUnloadTask(final RegionFileIOThread.RegionFileType type) {
++ switch (type) {
++ case CHUNK_DATA:
++ return this.chunkDataUnload;
++ case ENTITY_DATA:
++ return this.entityDataUnload;
++ case POI_DATA:
++ return this.poiDataUnload;
++ default:
++ throw new IllegalStateException("Unknown regionfile type " + type);
++ }
++ }
++
++ private UnloadState unloadState;
++
++ // holds schedule lock
++ UnloadState unloadStage1() {
++ // because we hold the scheduling lock, we cannot actually unload anything
++ // so we need to null this chunk's state
++ ChunkAccess chunk = this.currentChunk;
++ ChunkEntitySlices entityChunk = this.entityChunk;
++ PoiChunk poiChunk = this.poiChunk;
++ // chunk state
++ this.currentChunk = null;
++ this.currentGenStatus = null;
++ this.wrappedChunkForNeighbour = null;
++ this.lastChunkCompletion = null;
++ // entity chunk state
++ this.entityChunk = null;
++ this.pendingEntityChunk = null;
++
++ // poi chunk state
++ this.poiChunk = null;
++
++ // priority state
++ this.priorityLocked = false;
++
++ if (chunk != null) {
++ this.chunkDataUnload = new UnloadTask(new Completable<>(), new DelayedPrioritisedTask(PrioritisedExecutor.Priority.NORMAL));
++ }
++ if (poiChunk != null) {
++ this.poiDataUnload = new UnloadTask(new Completable<>(), null);
++ }
++ if (entityChunk != null) {
++ this.entityDataUnload = new UnloadTask(new Completable<>(), null);
++ }
++
++ return this.unloadState = (chunk != null || entityChunk != null || poiChunk != null) ? new UnloadState(this, chunk, entityChunk, poiChunk) : null;
++ }
++
++ // data is null if failed or does not need to be saved
++ void completeAsyncChunkDataSave(final CompoundTag data) {
++ if (data != null) {
++ RegionFileIOThread.scheduleSave(this.world, this.chunkX, this.chunkZ, data, RegionFileIOThread.RegionFileType.CHUNK_DATA);
++ }
++ this.chunkDataUnload.completable().complete(data);
++ this.scheduler.schedulingLock.lock();
++ try {
++ // can only write to these fields while holding the schedule lock
++ this.chunkDataUnload = null;
++ this.checkUnload();
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++ }
++
++ void unloadStage2(final UnloadState state) {
++ this.unloadState = null;
++ final ChunkAccess chunk = state.chunk();
++ final ChunkEntitySlices entityChunk = state.entityChunk();
++ final PoiChunk poiChunk = state.poiChunk();
++
++ final boolean shouldLevelChunkNotSave = (chunk instanceof LevelChunk levelChunk && levelChunk.mustNotSave);
++
++ // unload chunk data
++ if (chunk != null) {
++ if (chunk instanceof LevelChunk levelChunk) {
++ levelChunk.setLoaded(false);
++ }
++
++ if (!shouldLevelChunkNotSave) {
++ this.saveChunk(chunk, true);
++ } else {
++ this.completeAsyncChunkDataSave(null);
++ }
++
++ if (chunk instanceof LevelChunk levelChunk) {
++ this.world.unload(levelChunk);
++ }
++ }
++
++ // unload entity data
++ if (entityChunk != null) {
++ this.saveEntities(entityChunk, true);
++ // yes this is a hack to pass the compound tag through...
++ final CompoundTag lastEntityUnload = this.lastEntityUnload;
++ this.lastEntityUnload = null;
++
++ if (entityChunk.unload()) {
++ this.scheduler.schedulingLock.lock();
++ try {
++ entityChunk.setTransient(true);
++ this.entityChunk = entityChunk;
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++ } else {
++ this.world.getEntityLookup().entitySectionUnload(this.chunkX, this.chunkZ);
++ }
++ // we need to delay the callback until after determining transience, otherwise a potential loader could
++ // set entityChunk before we do
++ this.entityDataUnload.completable().complete(lastEntityUnload);
++ }
++
++ // unload poi data
++ if (poiChunk != null) {
++ if (poiChunk.isDirty() && !shouldLevelChunkNotSave) {
++ this.savePOI(poiChunk, true);
++ } else {
++ this.poiDataUnload.completable().complete(null);
++ }
++
++ if (poiChunk.isLoaded()) {
++ this.world.getPoiManager().onUnload(CoordinateUtils.getChunkKey(this.chunkX, this.chunkZ));
++ }
++ }
++ }
++
++ boolean unloadStage3() {
++ // can only write to these while holding the schedule lock, and we instantly complete them in stage2
++ this.poiDataUnload = null;
++ this.entityDataUnload = null;
++
++ // we need to check if anything has been loaded in the meantime (or if we have transient entities)
++ if (this.entityChunk != null || this.poiChunk != null || this.currentChunk != null) {
++ return false;
++ }
++
++ return this.isSafeToUnload() == null;
++ }
++
++ private void cancelGenTask() {
++ if (this.generationTask != null) {
++ this.generationTask.cancel();
++ } else {
++ // otherwise, we are blocking on neighbours, so remove them
++ if (!this.neighboursBlockingGenTask.isEmpty()) {
++ for (final NewChunkHolder neighbour : this.neighboursBlockingGenTask) {
++ if (neighbour.neighboursWaitingForUs.remove(this) == null) {
++ throw new IllegalStateException("Corrupt state");
++ }
++ if (neighbour.neighboursWaitingForUs.isEmpty()) {
++ neighbour.checkUnload();
++ }
++ }
++ this.neighboursBlockingGenTask.clear();
++ this.checkUnload();
++ }
++ }
++ }
++
++ // holds: ticket level update lock
++ // holds: schedule lock
++ public void processTicketLevelUpdate(final List<ChunkProgressionTask> scheduledTasks, final List<NewChunkHolder> changedLoadStatus) {
++ final int oldLevel = this.oldTicketLevel;
++ final int newLevel = this.currentTicketLevel;
++
++ if (oldLevel == newLevel) {
++ return;
++ }
++
++ this.oldTicketLevel = newLevel;
++
++ final ChunkHolder.FullChunkStatus oldState = ChunkHolder.getFullChunkStatus(oldLevel);
++ final ChunkHolder.FullChunkStatus newState = ChunkHolder.getFullChunkStatus(newLevel);
++ final boolean oldUnloaded = oldLevel > ChunkHolderManager.MAX_TICKET_LEVEL;
++ final boolean newUnloaded = newLevel > ChunkHolderManager.MAX_TICKET_LEVEL;
++
++ final ChunkStatus maxGenerationStatusOld = ChunkHolder.getStatus(oldLevel);
++ final ChunkStatus maxGenerationStatusNew = ChunkHolder.getStatus(newLevel);
++
++ // check for cancellations from downgrading ticket level
++ if (this.requestedGenStatus != null && !newState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && newLevel > oldLevel) {
++ // note: cancel() may invoke onChunkGenComplete synchronously here
++ if (newUnloaded) {
++ // need to cancel all tasks
++ // note: requested status must be set to null here before cancellation, to indicate to the
++ // completion logic that we do not want rescheduling to occur
++ this.requestedGenStatus = null;
++ this.cancelGenTask();
++ } else {
++ final ChunkStatus toCancel = maxGenerationStatusNew.getNextStatus();
++ final ChunkStatus currentRequestedStatus = this.requestedGenStatus;
++
++ if (currentRequestedStatus.isOrAfter(toCancel)) {
++ // we do have to cancel something here
++ // clamp requested status to the maximum
++ if (this.currentGenStatus != null && this.currentGenStatus.isOrAfter(maxGenerationStatusNew)) {
++ // already generated to status, so we must cancel
++ this.requestedGenStatus = null;
++ this.cancelGenTask();
++ } else {
++ // not generated to status, so we may have to cancel
++ // note: gen task is always 1 status above current gen status if not null
++ this.requestedGenStatus = maxGenerationStatusNew;
++ if (this.generationTaskStatus != null && this.generationTaskStatus.isOrAfter(toCancel)) {
++ // TOOD is this even possible? i don't think so
++ throw new IllegalStateException("?????");
++ }
++ }
++ }
++ }
++ }
++
++ if (newState != oldState) {
++ if (newState.isOrAfter(oldState)) {
++ // status upgrade
++ if (!oldState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && newState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ // may need to schedule full load
++ if (this.currentGenStatus != ChunkStatus.FULL) {
++ if (this.requestedGenStatus != null) {
++ this.requestedGenStatus = ChunkStatus.FULL;
++ } else {
++ this.scheduler.schedule(
++ this.chunkX, this.chunkZ, ChunkStatus.FULL, this, scheduledTasks
++ );
++ }
++ } else {
++ // now we are fully loaded
++ this.queueBorderFullStatus(true, changedLoadStatus);
++ }
++ }
++ } else {
++ // status downgrade
++ if (!newState.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING) && oldState.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING)) {
++ this.completeFullStatusConsumers(ChunkHolder.FullChunkStatus.ENTITY_TICKING, null);
++ }
++
++ if (!newState.isOrAfter(ChunkHolder.FullChunkStatus.TICKING) && oldState.isOrAfter(ChunkHolder.FullChunkStatus.TICKING)) {
++ this.completeFullStatusConsumers(ChunkHolder.FullChunkStatus.TICKING, null);
++ }
++
++ if (!newState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && oldState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ this.completeFullStatusConsumers(ChunkHolder.FullChunkStatus.BORDER, null);
++ }
++ }
++ }
++
++ if (oldState != newState) {
++ if (this.onTicketUpdate(oldState, newState)) {
++ changedLoadStatus.add(this);
++ }
++ }
++
++ if (oldUnloaded != newUnloaded) {
++ this.checkUnload();
++ }
++ }
++
++ /*
++ For full chunks, vanilla just loads chunks around it up to FEATURES, 1 radius
++
++ For ticking chunks, it updates the persistent entity manager (soon to be completely nuked by EntitySliceManager, which
++ will also need to be updated but with far less implications)
++ It also shoves the scheduled block ticks into the tick scheduler
++
++ For entity ticking chunks, updates the entity manager (see above)
++ */
++
++ static final int NEIGHBOUR_RADIUS = 2;
++ private long fullNeighbourChunksLoadedBitset;
++
++ private static int getFullNeighbourIndex(final int relativeX, final int relativeZ) {
++ // index = (relativeX + NEIGHBOUR_CACHE_RADIUS) + (relativeZ + NEIGHBOUR_CACHE_RADIUS) * (NEIGHBOUR_CACHE_RADIUS * 2 + 1)
++ // optimised variant of the above by moving some of the ops to compile time
++ return relativeX + (relativeZ * (NEIGHBOUR_RADIUS * 2 + 1)) + (NEIGHBOUR_RADIUS + NEIGHBOUR_RADIUS * ((NEIGHBOUR_RADIUS * 2 + 1)));
++ }
++ public final boolean isNeighbourFullLoaded(final int relativeX, final int relativeZ) {
++ return (this.fullNeighbourChunksLoadedBitset & (1L << getFullNeighbourIndex(relativeX, relativeZ))) != 0;
++ }
++
++ // returns true if this chunk changed full status
++ public final boolean setNeighbourFullLoaded(final int relativeX, final int relativeZ) {
++ final long before = this.fullNeighbourChunksLoadedBitset;
++ final int index = getFullNeighbourIndex(relativeX, relativeZ);
++ this.fullNeighbourChunksLoadedBitset |= (1L << index);
++ return this.onNeighbourChange(before, this.fullNeighbourChunksLoadedBitset);
++ }
++
++ // returns true if this chunk changed full status
++ public final boolean setNeighbourFullUnloaded(final int relativeX, final int relativeZ) {
++ final long before = this.fullNeighbourChunksLoadedBitset;
++ final int index = getFullNeighbourIndex(relativeX, relativeZ);
++ this.fullNeighbourChunksLoadedBitset &= ~(1L << index);
++ return this.onNeighbourChange(before, this.fullNeighbourChunksLoadedBitset);
++ }
++
++ public static boolean areNeighboursFullLoaded(final long bitset, final int radius) {
++ // index = relativeX + (relativeZ * (NEIGHBOUR_CACHE_RADIUS * 2 + 1)) + (NEIGHBOUR_CACHE_RADIUS + NEIGHBOUR_CACHE_RADIUS * ((NEIGHBOUR_CACHE_RADIUS * 2 + 1)))
++ switch (radius) {
++ case 0: {
++ return (bitset & (1L << getFullNeighbourIndex(0, 0))) != 0L;
++ }
++ case 1: {
++ long mask = 0L;
++ for (int dx = -1; dx <= 1; ++dx) {
++ for (int dz = -1; dz <= 1; ++dz) {
++ mask |= (1L << getFullNeighbourIndex(dx, dz));
++ }
++ }
++ return (bitset & mask) == mask;
++ }
++ case 2: {
++ long mask = 0L;
++ for (int dx = -2; dx <= 2; ++dx) {
++ for (int dz = -2; dz <= 2; ++dz) {
++ mask |= (1L << getFullNeighbourIndex(dx, dz));
++ }
++ }
++ return (bitset & mask) == mask;
++ }
++
++ default: {
++ throw new IllegalArgumentException("Radius not recognized: " + radius);
++ }
++ }
++ }
++
++ // upper 16 bits are pending status, lower 16 bits are current status
++ private volatile long chunkStatus;
++ private static final long PENDING_STATUS_MASK = Long.MIN_VALUE >> 31;
++ private static final ChunkHolder.FullChunkStatus[] CHUNK_STATUS_BY_ID = ChunkHolder.FullChunkStatus.values();
++ private static final VarHandle CHUNK_STATUS_HANDLE = ConcurrentUtil.getVarHandle(NewChunkHolder.class, "chunkStatus", long.class);
++
++ public static ChunkHolder.FullChunkStatus getCurrentChunkStatus(final long encoded) {
++ return CHUNK_STATUS_BY_ID[(int)encoded];
++ }
++
++ public static ChunkHolder.FullChunkStatus getPendingChunkStatus(final long encoded) {
++ return CHUNK_STATUS_BY_ID[(int)(encoded >>> 32)];
++ }
++
++ public ChunkHolder.FullChunkStatus getChunkStatus() {
++ return getCurrentChunkStatus(((long)CHUNK_STATUS_HANDLE.getVolatile((NewChunkHolder)this)));
++ }
++
++ public boolean isEntityTickingReady() {
++ return this.getChunkStatus().isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING);
++ }
++
++ public boolean isTickingReady() {
++ return this.getChunkStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
++ }
++
++ public boolean isFullChunkReady() {
++ return this.getChunkStatus().isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
++ }
++
++ private static ChunkHolder.FullChunkStatus getStatusForBitset(final long bitset) {
++ if (areNeighboursFullLoaded(bitset, 2)) {
++ return ChunkHolder.FullChunkStatus.ENTITY_TICKING;
++ } else if (areNeighboursFullLoaded(bitset, 1)) {
++ return ChunkHolder.FullChunkStatus.TICKING;
++ } else if (areNeighboursFullLoaded(bitset, 0)) {
++ return ChunkHolder.FullChunkStatus.BORDER;
++ } else {
++ return ChunkHolder.FullChunkStatus.INACCESSIBLE;
++ }
++ }
++
++ // note: only while updating ticket level, so holds ticket update lock + scheduling lock
++ protected final boolean onTicketUpdate(final ChunkHolder.FullChunkStatus oldState, final ChunkHolder.FullChunkStatus newState) {
++ if (oldState == newState) {
++ return false;
++ }
++
++ // preserve border request after full status complete, as it does not set anything in the bitset
++ ChunkHolder.FullChunkStatus byNeighbours = getStatusForBitset(this.fullNeighbourChunksLoadedBitset);
++ if (byNeighbours == ChunkHolder.FullChunkStatus.INACCESSIBLE && newState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && this.currentGenStatus == ChunkStatus.FULL) {
++ byNeighbours = ChunkHolder.FullChunkStatus.BORDER;
++ }
++
++ final ChunkHolder.FullChunkStatus toSet;
++
++ if (newState.isOrAfter(byNeighbours)) {
++ // must clamp to neighbours level, even though we have the ticket level
++ toSet = byNeighbours;
++ } else {
++ // must clamp to ticket level, even though we have the neighbours
++ toSet = newState;
++ }
++
++ long curr = (long)CHUNK_STATUS_HANDLE.getVolatile((NewChunkHolder)this);
++
++ if (curr == ((long)toSet.ordinal() | ((long)toSet.ordinal() << 32))) {
++ // nothing to do
++ return false;
++ }
++
++ int failures = 0;
++ for (;;) {
++ final long update = (curr & ~PENDING_STATUS_MASK) | ((long)toSet.ordinal() << 32);
++ if (curr == (curr = (long)CHUNK_STATUS_HANDLE.compareAndExchange((NewChunkHolder)this, curr, update))) {
++ return true;
++ }
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ protected final boolean onNeighbourChange(final long bitsetBefore, final long bitsetAfter) {
++ ChunkHolder.FullChunkStatus oldState = getStatusForBitset(bitsetBefore);
++ ChunkHolder.FullChunkStatus newState = getStatusForBitset(bitsetAfter);
++ final ChunkHolder.FullChunkStatus currStateTicketLevel = ChunkHolder.getFullChunkStatus(this.oldTicketLevel);
++ if (oldState.isOrAfter(currStateTicketLevel)) {
++ oldState = currStateTicketLevel;
++ }
++ if (newState.isOrAfter(currStateTicketLevel)) {
++ newState = currStateTicketLevel;
++ }
++ // preserve border request after full status complete, as it does not set anything in the bitset
++ if (newState == ChunkHolder.FullChunkStatus.INACCESSIBLE && currStateTicketLevel.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && this.currentGenStatus == ChunkStatus.FULL) {
++ newState = ChunkHolder.FullChunkStatus.BORDER;
++ }
++
++ if (oldState == newState) {
++ return false;
++ }
++
++ int failures = 0;
++ for (long curr = (long)CHUNK_STATUS_HANDLE.getVolatile((NewChunkHolder)this);;) {
++ final long update = (curr & ~PENDING_STATUS_MASK) | ((long)newState.ordinal() << 32);
++ if (curr == (curr = (long)CHUNK_STATUS_HANDLE.compareAndExchange((NewChunkHolder)this, curr, update))) {
++ return true;
++ }
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ private boolean queueBorderFullStatus(final boolean loaded, final List<NewChunkHolder> changedFullStatus) {
++ final ChunkHolder.FullChunkStatus toStatus = loaded ? ChunkHolder.FullChunkStatus.BORDER : ChunkHolder.FullChunkStatus.INACCESSIBLE;
++
++ int failures = 0;
++ for (long curr = (long)CHUNK_STATUS_HANDLE.getVolatile((NewChunkHolder)this);;) {
++ final ChunkHolder.FullChunkStatus currPending = getPendingChunkStatus(curr);
++ if (loaded && currPending != ChunkHolder.FullChunkStatus.INACCESSIBLE) {
++ throw new IllegalStateException("Expected " + ChunkHolder.FullChunkStatus.INACCESSIBLE + " for pending, but got " + currPending);
++ }
++
++ final long update = (curr & ~PENDING_STATUS_MASK) | ((long)toStatus.ordinal() << 32);
++ if (curr == (curr = (long)CHUNK_STATUS_HANDLE.compareAndExchange((NewChunkHolder)this, curr, update))) {
++ if ((int)(update) != (int)(update >>> 32)) {
++ changedFullStatus.add(this);
++ return true;
++ }
++ return false;
++ }
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ // only call on main thread, must hold ticket level and scheduling lock
++ private void onFullChunkLoadChange(final boolean loaded, final List<NewChunkHolder> changedFullStatus) {
++ for (int dz = -NEIGHBOUR_RADIUS; dz <= NEIGHBOUR_RADIUS; ++dz) {
++ for (int dx = -NEIGHBOUR_RADIUS; dx <= NEIGHBOUR_RADIUS; ++dx) {
++ final NewChunkHolder holder = (dx | dz) == 0 ? this : this.scheduler.chunkHolderManager.getChunkHolder(dx + this.chunkX, dz + this.chunkZ);
++ if (loaded) {
++ if (holder.setNeighbourFullLoaded(-dx, -dz)) {
++ changedFullStatus.add(holder);
++ }
++ } else {
++ if (holder != null && holder.setNeighbourFullUnloaded(-dx, -dz)) {
++ changedFullStatus.add(holder);
++ }
++ }
++ }
++ }
++ }
++
++ private ChunkHolder.FullChunkStatus updateCurrentState(final ChunkHolder.FullChunkStatus to) {
++ int failures = 0;
++ for (long curr = (long)CHUNK_STATUS_HANDLE.getVolatile((NewChunkHolder)this);;) {
++ final long update = (curr & PENDING_STATUS_MASK) | (long)to.ordinal();
++ if (curr == (curr = (long)CHUNK_STATUS_HANDLE.compareAndExchange((NewChunkHolder)this, curr, update))) {
++ return getPendingChunkStatus(curr);
++ }
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ private void changeEntityChunkStatus(final ChunkHolder.FullChunkStatus toStatus) {
++ this.world.getEntityLookup().chunkStatusChange(this.chunkX, this.chunkZ, toStatus);
++ }
++
++ private boolean processingFullStatus = false;
++
++ // only to be called on the main thread, no locks need to be held
++ public boolean handleFullStatusChange(final List<NewChunkHolder> changedFullStatus) {
++ TickThread.ensureTickThread(this.world, this.chunkX, this.chunkZ, "Cannot update full status thread off-main");
++
++ boolean ret = false;
++
++ if (this.processingFullStatus) {
++ // we cannot process updates recursively
++ return ret;
++ }
++
++ // note: use opaque reads for chunk status read since we need it to be atomic
++
++ // test if anything changed
++ final long statusCheck = (long)CHUNK_STATUS_HANDLE.getOpaque((NewChunkHolder)this);
++ if ((int)statusCheck == (int)(statusCheck >>> 32)) {
++ // nothing changed
++ return ret;
++ }
++
++ final ChunkTaskScheduler scheduler = this.scheduler;
++ final ChunkHolderManager holderManager = scheduler.chunkHolderManager;
++ final int ticketKeep;
++ final Long ticketId;
++ holderManager.ticketLock.lock();
++ try {
++ ticketKeep = this.currentTicketLevel;
++ ticketId = Long.valueOf(holderManager.getNextStatusUpgradeId());
++ holderManager.addTicketAtLevel(TicketType.STATUS_UPGRADE, this.chunkX, this.chunkZ, ticketKeep, ticketId);
++ } finally {
++ holderManager.ticketLock.unlock();
++ }
++
++ this.processingFullStatus = true;
++ try {
++ for (;;) {
++ final long currStateEncoded = (long)CHUNK_STATUS_HANDLE.getOpaque((NewChunkHolder)this);
++ final ChunkHolder.FullChunkStatus currState = getCurrentChunkStatus(currStateEncoded);
++ ChunkHolder.FullChunkStatus nextState = getPendingChunkStatus(currStateEncoded);
++ if (currState == nextState) {
++ if (nextState == ChunkHolder.FullChunkStatus.INACCESSIBLE) {
++ this.scheduler.schedulingLock.lock();
++ try {
++ this.checkUnload();
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++ }
++ break;
++ }
++
++ // chunks cannot downgrade state while status is pending a change
++ final LevelChunk chunk = (LevelChunk)this.currentChunk;
++
++ // Note: we assume that only load/unload contain plugin logic
++ // plugin logic is anything stupid enough to possibly change the chunk status while it is already
++ // being changed (i.e during load it is possible it will try to set to full ticking)
++ // in order to allow this change, we also need this plugin logic to be contained strictly after all
++ // of the chunk system load callbacks are invoked
++ if (nextState.isOrAfter(currState)) {
++ // state upgrade
++ if (!currState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && nextState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ nextState = this.updateCurrentState(ChunkHolder.FullChunkStatus.BORDER);
++ holderManager.ensureInAutosave(this);
++ this.changeEntityChunkStatus(ChunkHolder.FullChunkStatus.BORDER);
++ chunk.onChunkLoad(this);
++ this.onFullChunkLoadChange(true, changedFullStatus);
++ this.completeFullStatusConsumers(ChunkHolder.FullChunkStatus.BORDER, chunk);
++ }
++
++ if (!currState.isOrAfter(ChunkHolder.FullChunkStatus.TICKING) && nextState.isOrAfter(ChunkHolder.FullChunkStatus.TICKING)) {
++ nextState = this.updateCurrentState(ChunkHolder.FullChunkStatus.TICKING);
++ this.changeEntityChunkStatus(ChunkHolder.FullChunkStatus.TICKING);
++ chunk.onChunkTicking(this);
++ this.completeFullStatusConsumers(ChunkHolder.FullChunkStatus.TICKING, chunk);
++ }
++
++ if (!currState.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING) && nextState.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING)) {
++ nextState = this.updateCurrentState(ChunkHolder.FullChunkStatus.ENTITY_TICKING);
++ this.changeEntityChunkStatus(ChunkHolder.FullChunkStatus.ENTITY_TICKING);
++ chunk.onChunkEntityTicking(this);
++ this.completeFullStatusConsumers(ChunkHolder.FullChunkStatus.ENTITY_TICKING, chunk);
++ }
++ } else {
++ if (currState.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING) && !nextState.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING)) {
++ this.changeEntityChunkStatus(ChunkHolder.FullChunkStatus.TICKING);
++ chunk.onChunkNotEntityTicking(this);
++ nextState = this.updateCurrentState(ChunkHolder.FullChunkStatus.TICKING);
++ }
++
++ if (currState.isOrAfter(ChunkHolder.FullChunkStatus.TICKING) && !nextState.isOrAfter(ChunkHolder.FullChunkStatus.TICKING)) {
++ this.changeEntityChunkStatus(ChunkHolder.FullChunkStatus.BORDER);
++ chunk.onChunkNotTicking(this);
++ nextState = this.updateCurrentState(ChunkHolder.FullChunkStatus.BORDER);
++ }
++
++ if (currState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !nextState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ this.onFullChunkLoadChange(false, changedFullStatus);
++ this.changeEntityChunkStatus(ChunkHolder.FullChunkStatus.INACCESSIBLE);
++ chunk.onChunkUnload(this);
++ nextState = this.updateCurrentState(ChunkHolder.FullChunkStatus.INACCESSIBLE);
++ }
++ }
++
++ ret = true;
++ }
++ } finally {
++ this.processingFullStatus = false;
++ holderManager.removeTicketAtLevel(TicketType.STATUS_UPGRADE, this.chunkX, this.chunkZ, ticketKeep, ticketId);
++ }
++
++ return ret;
++ }
++
++ // note: must hold scheduling lock
++ // rets true if the current requested gen status is not null (effectively, whether further scheduling is not needed)
++ boolean upgradeGenTarget(final ChunkStatus toStatus) {
++ if (toStatus == null) {
++ throw new NullPointerException("toStatus cannot be null");
++ }
++ if (this.requestedGenStatus == null && this.generationTask == null) {
++ return false;
++ }
++ if (this.requestedGenStatus == null || !this.requestedGenStatus.isOrAfter(toStatus)) {
++ this.requestedGenStatus = toStatus;
++ }
++ return true;
++ }
++
++ public void setGenerationTarget(final ChunkStatus toStatus) {
++ this.requestedGenStatus = toStatus;
++ }
++
++ public boolean hasGenerationTask() {
++ return this.generationTask != null;
++ }
++
++ public ChunkStatus getCurrentGenStatus() {
++ return this.currentGenStatus;
++ }
++
++ public ChunkStatus getRequestedGenStatus() {
++ return this.requestedGenStatus;
++ }
++
++ private final Reference2ObjectOpenHashMap<ChunkStatus, List<Consumer<ChunkAccess>>> statusWaiters = new Reference2ObjectOpenHashMap<>();
++
++ void addStatusConsumer(final ChunkStatus status, final Consumer<ChunkAccess> consumer) {
++ this.statusWaiters.computeIfAbsent(status, (final ChunkStatus keyInMap) -> {
++ return new ArrayList<>(4);
++ }).add(consumer);
++ }
++
++ private void completeStatusConsumers(ChunkStatus status, final ChunkAccess chunk) {
++ // need to tell future statuses to complete if cancelled
++ do {
++ this.completeStatusConsumers0(status, chunk);
++ } while (chunk == null && status != (status = status.getNextStatus()));
++ }
++
++ private void completeStatusConsumers0(final ChunkStatus status, final ChunkAccess chunk) {
++ final List<Consumer<ChunkAccess>> consumers;
++ consumers = this.statusWaiters.remove(status);
++
++ if (consumers == null) {
++ return;
++ }
++
++ // must be scheduled to main, we do not trust the callback to not do anything stupid
++ this.scheduler.scheduleChunkTask(this.chunkX, this.chunkZ, () -> {
++ for (final Consumer<ChunkAccess> consumer : consumers) {
++ try {
++ consumer.accept(chunk);
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to process chunk status callback", thr);
++ }
++ }
++ }, PrioritisedExecutor.Priority.HIGHEST);
++ }
++
++ private final Reference2ObjectOpenHashMap<ChunkHolder.FullChunkStatus, List<Consumer<LevelChunk>>> fullStatusWaiters = new Reference2ObjectOpenHashMap<>();
++
++ void addFullStatusConsumer(final ChunkHolder.FullChunkStatus status, final Consumer<LevelChunk> consumer) {
++ this.fullStatusWaiters.computeIfAbsent(status, (final ChunkHolder.FullChunkStatus keyInMap) -> {
++ return new ArrayList<>(4);
++ }).add(consumer);
++ }
++
++ private void completeFullStatusConsumers(ChunkHolder.FullChunkStatus status, final LevelChunk chunk) {
++ // need to tell future statuses to complete if cancelled
++ final ChunkHolder.FullChunkStatus max = CHUNK_STATUS_BY_ID[CHUNK_STATUS_BY_ID.length - 1];
++
++ for (;;) {
++ this.completeFullStatusConsumers0(status, chunk);
++ if (chunk != null || status == max) {
++ break;
++ }
++ status = CHUNK_STATUS_BY_ID[status.ordinal() + 1];
++ }
++ }
++
++ private void completeFullStatusConsumers0(final ChunkHolder.FullChunkStatus status, final LevelChunk chunk) {
++ final List<Consumer<LevelChunk>> consumers;
++ consumers = this.fullStatusWaiters.remove(status);
++
++ if (consumers == null) {
++ return;
++ }
++
++ // must be scheduled to main, we do not trust the callback to not do anything stupid
++ this.scheduler.scheduleChunkTask(this.chunkX, this.chunkZ, () -> {
++ for (final Consumer<LevelChunk> consumer : consumers) {
++ try {
++ consumer.accept(chunk);
++ } catch (final ThreadDeath thr) {
++ throw thr;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to process chunk status callback", thr);
++ }
++ }
++ }, PrioritisedExecutor.Priority.HIGHEST);
++ }
++
++ // note: must hold scheduling lock
++ private void onChunkGenComplete(final ChunkAccess newChunk, final ChunkStatus newStatus,
++ final List<ChunkProgressionTask> scheduleList, final List<NewChunkHolder> changedLoadStatus) {
++ if (!this.neighboursBlockingGenTask.isEmpty()) {
++ throw new IllegalStateException("Cannot have neighbours blocking this gen task");
++ }
++ if (newChunk != null || (this.requestedGenStatus == null || !this.requestedGenStatus.isOrAfter(newStatus))) {
++ this.completeStatusConsumers(newStatus, newChunk);
++ }
++ // done now, clear state (must be done before scheduling new tasks)
++ this.generationTask = null;
++ this.generationTaskStatus = null;
++ if (newChunk == null) {
++ // task was cancelled
++ // should be careful as this could be called while holding the schedule lock and/or inside the
++ // ticket level update
++ // while a task may be cancelled, it is possible for it to be later re-scheduled
++ // however, because generationTask is only set to null on _completion_, the scheduler leaves
++ // the rescheduling logic to us here
++ final ChunkStatus requestedGenStatus = this.requestedGenStatus;
++ this.requestedGenStatus = null;
++ if (requestedGenStatus != null) {
++ // it looks like it has been requested, so we must reschedule
++ if (!this.neighboursWaitingForUs.isEmpty()) {
++ for (final Iterator<Reference2ObjectMap.Entry<NewChunkHolder, ChunkStatus>> iterator = this.neighboursWaitingForUs.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
++ final Reference2ObjectMap.Entry<NewChunkHolder, ChunkStatus> entry = iterator.next();
++
++ final NewChunkHolder chunkHolder = entry.getKey();
++ final ChunkStatus toStatus = entry.getValue();
++
++ if (!requestedGenStatus.isOrAfter(toStatus)) {
++ // if we were cancelled, we are responsible for removing the waiter
++ if (!chunkHolder.neighboursBlockingGenTask.remove(this)) {
++ throw new IllegalStateException("Corrupt state");
++ }
++ if (chunkHolder.neighboursBlockingGenTask.isEmpty()) {
++ chunkHolder.checkUnload();
++ }
++ iterator.remove();
++ continue;
++ }
++ }
++ }
++
++ // note: only after generationTask -> null, generationTaskStatus -> null, and requestedGenStatus -> null
++ this.scheduler.schedule(
++ this.chunkX, this.chunkZ, requestedGenStatus, this, scheduleList
++ );
++
++ // return, can't do anything further
++ return;
++ }
++
++ if (!this.neighboursWaitingForUs.isEmpty()) {
++ for (final NewChunkHolder chunkHolder : this.neighboursWaitingForUs.keySet()) {
++ if (!chunkHolder.neighboursBlockingGenTask.remove(this)) {
++ throw new IllegalStateException("Corrupt state");
++ }
++ if (chunkHolder.neighboursBlockingGenTask.isEmpty()) {
++ chunkHolder.checkUnload();
++ }
++ }
++ this.neighboursWaitingForUs.clear();
++ }
++ // reset priority, we have nothing left to generate to
++ this.setPriority(PrioritisedExecutor.Priority.NORMAL);
++ this.checkUnload();
++ return;
++ }
++
++ this.currentChunk = newChunk;
++ this.currentGenStatus = newStatus;
++ this.lastChunkCompletion = new ChunkCompletion(newChunk, newStatus);
++
++ final ChunkStatus requestedGenStatus = this.requestedGenStatus;
++
++ List<NewChunkHolder> needsScheduling = null;
++ boolean recalculatePriority = false;
++ for (final Iterator<Reference2ObjectMap.Entry<NewChunkHolder, ChunkStatus>> iterator
++ = this.neighboursWaitingForUs.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
++ final Reference2ObjectMap.Entry<NewChunkHolder, ChunkStatus> entry = iterator.next();
++ final NewChunkHolder neighbour = entry.getKey();
++ final ChunkStatus requiredStatus = entry.getValue();
++
++ if (!newStatus.isOrAfter(requiredStatus)) {
++ if (requestedGenStatus == null || !requestedGenStatus.isOrAfter(requiredStatus)) {
++ // if we're cancelled, still need to clear this map
++ if (!neighbour.neighboursBlockingGenTask.remove(this)) {
++ throw new IllegalStateException("Neighbour is not waiting for us?");
++ }
++ if (neighbour.neighboursBlockingGenTask.isEmpty()) {
++ neighbour.checkUnload();
++ }
++
++ iterator.remove();
++ }
++ continue;
++ }
++
++ // doesn't matter what isCancelled is here, we need to schedule if we can
++
++ recalculatePriority = true;
++ if (!neighbour.neighboursBlockingGenTask.remove(this)) {
++ throw new IllegalStateException("Neighbour is not waiting for us?");
++ }
++
++ if (neighbour.neighboursBlockingGenTask.isEmpty()) {
++ if (neighbour.requestedGenStatus != null) {
++ if (needsScheduling == null) {
++ needsScheduling = new ArrayList<>();
++ }
++ needsScheduling.add(neighbour);
++ } else {
++ neighbour.checkUnload();
++ }
++ }
++
++ // remove last; access to entry will throw if removed
++ iterator.remove();
++ }
++
++ if (newStatus == ChunkStatus.FULL) {
++ this.lockPriority();
++ // must use oldTicketLevel, we hold the schedule lock but not the ticket level lock
++ // however, schedule lock needs to be held for ticket level callback, so we're fine here
++ if (ChunkHolder.getFullChunkStatus(this.oldTicketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
++ this.queueBorderFullStatus(true, changedLoadStatus);
++ }
++ }
++
++ if (recalculatePriority) {
++ this.recalculateNeighbourRequestedPriority();
++ }
++
++ if (requestedGenStatus != null && !newStatus.isOrAfter(requestedGenStatus)) {
++ this.scheduleNeighbours(needsScheduling, scheduleList);
++
++ // we need to schedule more tasks now
++ this.scheduler.schedule(
++ this.chunkX, this.chunkZ, requestedGenStatus, this, scheduleList
++ );
++ } else {
++ // we're done now
++ if (requestedGenStatus != null) {
++ this.requestedGenStatus = null;
++ }
++ // reached final stage, so stop scheduling now
++ this.setPriority(PrioritisedExecutor.Priority.NORMAL);
++ this.checkUnload();
++
++ this.scheduleNeighbours(needsScheduling, scheduleList);
++ }
++ }
++
++ private void scheduleNeighbours(final List<NewChunkHolder> needsScheduling, final List<ChunkProgressionTask> scheduleList) {
++ if (needsScheduling != null) {
++ for (int i = 0, len = needsScheduling.size(); i < len; ++i) {
++ final NewChunkHolder neighbour = needsScheduling.get(i);
++
++ this.scheduler.schedule(
++ neighbour.chunkX, neighbour.chunkZ, neighbour.requestedGenStatus, neighbour, scheduleList
++ );
++ }
++ }
++ }
++
++ public void setGenerationTask(final ChunkProgressionTask generationTask, final ChunkStatus taskStatus,
++ final List<NewChunkHolder> neighbours) {
++ if (this.generationTask != null || (this.currentGenStatus != null && this.currentGenStatus.isOrAfter(taskStatus))) {
++ throw new IllegalStateException("Currently generating or provided task is trying to generate to a level we are already at!");
++ }
++ if (this.requestedGenStatus == null || !this.requestedGenStatus.isOrAfter(taskStatus)) {
++ throw new IllegalStateException("Cannot schedule generation task when not requested");
++ }
++ this.generationTask = generationTask;
++ this.generationTaskStatus = taskStatus;
++
++ for (int i = 0, len = neighbours.size(); i < len; ++i) {
++ neighbours.get(i).addNeighbourUsingChunk();
++ }
++
++ this.checkUnload();
++
++ generationTask.onComplete((final ChunkAccess access, final Throwable thr) -> {
++ if (generationTask != this.generationTask) {
++ throw new IllegalStateException(
++ "Cannot complete generation task '" + generationTask + "' because we are waiting on '" + this.generationTask + "' instead!"
++ );
++ }
++ if (thr != null) {
++ if (this.genTaskException != null) {
++ // first one is probably the TRUE problem
++ return;
++ }
++ // don't set generation task to null, so that scheduling will not attempt to create another task and it
++ // will automatically block any further scheduling usage of this chunk as it will wait forever for a failed
++ // task to complete
++ this.genTaskException = thr;
++ this.failedGenStatus = taskStatus;
++ this.genTaskFailedThread = Thread.currentThread();
++
++ this.scheduler.unrecoverableChunkSystemFailure(this.chunkX, this.chunkZ, Map.of(
++ "Generation task", ChunkTaskScheduler.stringIfNull(generationTask),
++ "Task to status", ChunkTaskScheduler.stringIfNull(taskStatus)
++ ), thr);
++ return;
++ }
++
++ final boolean scheduleTasks;
++ List<ChunkProgressionTask> tasks = ChunkHolderManager.getCurrentTicketUpdateScheduling();
++ if (tasks == null) {
++ scheduleTasks = true;
++ tasks = new ArrayList<>();
++ } else {
++ scheduleTasks = false;
++ // we are currently updating ticket levels, so we already hold the schedule lock
++ // this means we have to leave the ticket level update to handle the scheduling
++ }
++ final List<NewChunkHolder> changedLoadStatus = new ArrayList<>();
++ this.scheduler.schedulingLock.lock();
++ try {
++ for (int i = 0, len = neighbours.size(); i < len; ++i) {
++ neighbours.get(i).removeNeighbourUsingChunk();
++ }
++ this.onChunkGenComplete(access, taskStatus, tasks, changedLoadStatus);
++ } finally {
++ this.scheduler.schedulingLock.unlock();
++ }
++ this.scheduler.chunkHolderManager.addChangedStatuses(changedLoadStatus);
++
++ if (scheduleTasks) {
++ // can't hold the lock while scheduling, so we have to build the tasks and then schedule after
++ for (int i = 0, len = tasks.size(); i < len; ++i) {
++ tasks.get(i).schedule();
++ }
++ }
++ });
++ }
++
++ public PoiChunk getPoiChunk() {
++ return this.poiChunk;
++ }
++
++ public ChunkEntitySlices getEntityChunk() {
++ return this.entityChunk;
++ }
++
++ public long lastAutoSave;
++
++ public boolean save(final boolean shutdown, final boolean unloading) {
++ TickThread.ensureTickThread(this.world, this.chunkX, this.chunkZ, "Cannot save data off-main");
++
++ ChunkAccess chunk = this.getCurrentChunk();
++ PoiChunk poi = this.getPoiChunk();
++ ChunkEntitySlices entities = this.getEntityChunk();
++ boolean executedUnloadTask = false;
++
++ if (shutdown) {
++ // make sure that the async unloads complete
++ if (this.unloadState != null) {
++ // must have errored during unload
++ chunk = this.unloadState.chunk();
++ poi = this.unloadState.poiChunk();
++ entities = this.unloadState.entityChunk();
++ }
++ final UnloadTask chunkUnloadTask = this.chunkDataUnload;
++ final DelayedPrioritisedTask chunkDataUnloadTask = chunkUnloadTask == null ? null : chunkUnloadTask.task();
++ if (chunkDataUnloadTask != null) {
++ final PrioritisedExecutor.PrioritisedTask unloadTask = chunkDataUnloadTask.getTask();
++ if (unloadTask != null) {
++ executedUnloadTask = unloadTask.execute();
++ }
++ }
++ }
++
++ boolean canSaveChunk = !(chunk instanceof LevelChunk levelChunk && levelChunk.mustNotSave) &&
++ (chunk != null && ((shutdown || chunk instanceof LevelChunk) && chunk.isUnsaved()));
++ boolean canSavePOI = !(chunk instanceof LevelChunk levelChunk && levelChunk.mustNotSave) && (poi != null && poi.isDirty());
++ boolean canSaveEntities = entities != null;
++
++ try (co.aikar.timings.Timing ignored = this.world.timings.chunkSave.startTiming()) { // Paper
++ if (canSaveChunk) {
++ canSaveChunk = this.saveChunk(chunk, unloading);
++ }
++ if (canSavePOI) {
++ canSavePOI = this.savePOI(poi, unloading);
++ }
++ if (canSaveEntities) {
++ // on shutdown, we need to force transient entity chunks to save
++ canSaveEntities = this.saveEntities(entities, unloading || shutdown);
++ if (unloading || shutdown) {
++ this.lastEntityUnload = null;
++ }
++ }
++ }
++
++ return executedUnloadTask | canSaveChunk | canSaveEntities | canSavePOI;
++ }
++
++ static final class AsyncChunkSerializeTask implements Runnable {
++
++ private final ServerLevel world;
++ private final ChunkAccess chunk;
++ private final ChunkSerializer.AsyncSaveData asyncSaveData;
++ private final NewChunkHolder toComplete;
++
++ public AsyncChunkSerializeTask(final ServerLevel world, final ChunkAccess chunk, final ChunkSerializer.AsyncSaveData asyncSaveData,
++ final NewChunkHolder toComplete) {
++ this.world = world;
++ this.chunk = chunk;
++ this.asyncSaveData = asyncSaveData;
++ this.toComplete = toComplete;
++ }
++
++ @Override
++ public void run() {
++ final CompoundTag toSerialize;
++ try {
++ toSerialize = ChunkSerializer.saveChunk(this.world, this.chunk, this.asyncSaveData);
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable throwable) {
++ LOGGER.error("Failed to asynchronously save chunk " + this.chunk.getPos() + " for world '" + this.world.getWorld().getName() + "', falling back to synchronous save", throwable);
++ this.world.chunkTaskScheduler.scheduleChunkTask(this.chunk.locX, this.chunk.locZ, () -> {
++ final CompoundTag synchronousSave;
++ try {
++ synchronousSave = ChunkSerializer.saveChunk(AsyncChunkSerializeTask.this.world, AsyncChunkSerializeTask.this.chunk, AsyncChunkSerializeTask.this.asyncSaveData);
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable throwable2) {
++ LOGGER.error("Failed to synchronously save chunk " + AsyncChunkSerializeTask.this.chunk.getPos() + " for world '" + AsyncChunkSerializeTask.this.world.getWorld().getName() + "', chunk data will be lost", throwable2);
++ AsyncChunkSerializeTask.this.toComplete.completeAsyncChunkDataSave(null);
++ return;
++ }
++
++ AsyncChunkSerializeTask.this.toComplete.completeAsyncChunkDataSave(synchronousSave);
++ LOGGER.info("Successfully serialized chunk " + AsyncChunkSerializeTask.this.chunk.getPos() + " for world '" + AsyncChunkSerializeTask.this.world.getWorld().getName() + "' synchronously");
++
++ }, PrioritisedExecutor.Priority.HIGHEST);
++ return;
++ }
++ this.toComplete.completeAsyncChunkDataSave(toSerialize);
++ }
++
++ @Override
++ public String toString() {
++ return "AsyncChunkSerializeTask{" +
++ "chunk={pos=" + this.chunk.getPos() + ",world=\"" + this.world.getWorld().getName() + "\"}" +
++ "}";
++ }
++ }
++
++ private boolean saveChunk(final ChunkAccess chunk, final boolean unloading) {
++ if (!chunk.isUnsaved()) {
++ if (unloading) {
++ this.completeAsyncChunkDataSave(null);
++ }
++ return false;
++ }
++ boolean completing = false;
++ try {
++ if (unloading) {
++ try {
++ final ChunkSerializer.AsyncSaveData asyncSaveData = ChunkSerializer.getAsyncSaveData(this.world, chunk);
++
++ final PrioritisedExecutor.PrioritisedTask task = this.scheduler.loadExecutor.createTask(new AsyncChunkSerializeTask(this.world, chunk, asyncSaveData, this));
++
++ this.chunkDataUnload.task().setTask(task);
++
++ task.queue();
++
++ return true;
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to prepare async chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + this.world.getWorld().getName() + "', falling back to synchronous save", thr);
++ // fall through to synchronous save
++ }
++ }
++
++ final CompoundTag save = ChunkSerializer.saveChunk(this.world, chunk, null);
++
++ if (unloading) {
++ completing = true;
++ this.completeAsyncChunkDataSave(save);
++ LOGGER.info("Successfully serialized chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + this.world.getWorld().getName() + "' synchronously");
++ } else {
++ RegionFileIOThread.scheduleSave(this.world, this.chunkX, this.chunkZ, save, RegionFileIOThread.RegionFileType.CHUNK_DATA);
++ }
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to save chunk data (" + this.chunkX + "," + this.chunkZ + ") in world '" + this.world.getWorld().getName() + "'");
++ if (unloading && !completing) {
++ this.completeAsyncChunkDataSave(null);
++ }
++ }
++
++ return true;
++ }
++
++ private boolean lastEntitySaveNull;
++ private CompoundTag lastEntityUnload;
++ private boolean saveEntities(final ChunkEntitySlices entities, final boolean unloading) {
++ try {
++ CompoundTag mergeFrom = null;
++ if (entities.isTransient()) {
++ if (!unloading) {
++ // if we're a transient chunk, we cannot save until unloading because otherwise a double save will
++ // result in double adding the entities
++ return false;
++ }
++ try {
++ mergeFrom = RegionFileIOThread.loadData(this.world, this.chunkX, this.chunkZ, RegionFileIOThread.RegionFileType.ENTITY_DATA, PrioritisedExecutor.Priority.BLOCKING);
++ } catch (final Exception ex) {
++ LOGGER.error("Cannot merge transient entities for chunk (" + this.chunkX + "," + this.chunkZ + ") in world '" + this.world.getWorld().getName() + "', data on disk will be replaced", ex);
++ }
++ }
++
++ final CompoundTag save = entities.save();
++ if (mergeFrom != null) {
++ if (save == null) {
++ // don't override the data on disk with nothing
++ return false;
++ } else {
++ EntityStorage.copyEntities(mergeFrom, save);
++ }
++ }
++ if (save == null && this.lastEntitySaveNull) {
++ return false;
++ }
++
++ RegionFileIOThread.scheduleSave(this.world, this.chunkX, this.chunkZ, save, RegionFileIOThread.RegionFileType.ENTITY_DATA);
++ this.lastEntitySaveNull = save == null;
++ if (unloading) {
++ this.lastEntityUnload = save;
++ }
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to save entity data (" + this.chunkX + "," + this.chunkZ + ") in world '" + this.world.getWorld().getName() + "'");
++ }
++
++ return true;
++ }
++
++ private boolean lastPoiSaveNull;
++ private boolean savePOI(final PoiChunk poi, final boolean unloading) {
++ try {
++ final CompoundTag save = poi.save();
++ poi.setDirty(false);
++ if (save == null && this.lastPoiSaveNull) {
++ if (unloading) {
++ this.poiDataUnload.completable().complete(null);
++ }
++ return false;
++ }
++
++ RegionFileIOThread.scheduleSave(this.world, this.chunkX, this.chunkZ, save, RegionFileIOThread.RegionFileType.POI_DATA);
++ this.lastPoiSaveNull = save == null;
++ if (unloading) {
++ this.poiDataUnload.completable().complete(save);
++ }
++ } catch (final ThreadDeath death) {
++ throw death;
++ } catch (final Throwable thr) {
++ LOGGER.error("Failed to save poi data (" + this.chunkX + "," + this.chunkZ + ") in world '" + this.world.getWorld().getName() + "'");
++ }
++
++ return true;
++ }
++
++ @Override
++ public String toString() {
++ final ChunkCompletion lastCompletion = this.lastChunkCompletion;
++ final ChunkEntitySlices entityChunk = this.entityChunk;
++ final long chunkStatus = this.chunkStatus;
++ final int fullChunkStatus = (int)chunkStatus;
++ final int pendingChunkStatus = (int)(chunkStatus >>> 32);
++ final ChunkHolder.FullChunkStatus currentFullStatus = fullChunkStatus < 0 || fullChunkStatus >= CHUNK_STATUS_BY_ID.length ? null : CHUNK_STATUS_BY_ID[fullChunkStatus];
++ final ChunkHolder.FullChunkStatus pendingFullStatus = pendingChunkStatus < 0 || pendingChunkStatus >= CHUNK_STATUS_BY_ID.length ? null : CHUNK_STATUS_BY_ID[pendingChunkStatus];
++ return "NewChunkHolder{" +
++ "world=" + this.world.getWorld().getName() +
++ ", chunkX=" + this.chunkX +
++ ", chunkZ=" + this.chunkZ +
++ ", entityChunkFromDisk=" + (entityChunk != null && !entityChunk.isTransient()) +
++ ", lastChunkCompletion={chunk_class=" + (lastCompletion == null || lastCompletion.chunk() == null ? "null" : lastCompletion.chunk().getClass().getName()) + ",status=" + (lastCompletion == null ? "null" : lastCompletion.genStatus()) + "}" +
++ ", currentGenStatus=" + this.currentGenStatus +
++ ", requestedGenStatus=" + this.requestedGenStatus +
++ ", generationTask=" + this.generationTask +
++ ", generationTaskStatus=" + this.generationTaskStatus +
++ ", priority=" + this.priority +
++ ", priorityLocked=" + this.priorityLocked +
++ ", neighbourRequestedPriority=" + this.neighbourRequestedPriority +
++ ", effective_priority=" + this.getEffectivePriority() +
++ ", oldTicketLevel=" + this.oldTicketLevel +
++ ", currentTicketLevel=" + this.currentTicketLevel +
++ ", totalNeighboursUsingThisChunk=" + this.totalNeighboursUsingThisChunk +
++ ", fullNeighbourChunksLoadedBitset=" + this.fullNeighbourChunksLoadedBitset +
++ ", chunkStatusRaw=" + chunkStatus +
++ ", currentChunkStatus=" + currentFullStatus +
++ ", pendingChunkStatus=" + pendingFullStatus +
++ ", is_unload_safe=" + this.isSafeToUnload() +
++ ", killed=" + this.killed +
++ '}';
++ }
++
++ private static JsonElement serializeCompletable(final Completable<?> completable) {
++ if (completable == null) {
++ return new JsonPrimitive("null");
++ }
++
++ final JsonObject ret = new JsonObject();
++ final boolean isCompleted = completable.isCompleted();
++ ret.addProperty("completed", Boolean.valueOf(isCompleted));
++
++ if (isCompleted) {
++ ret.addProperty("completed_exceptionally", Boolean.valueOf(completable.getThrowable() != null));
++ }
++
++ return ret;
++ }
++
++ // holds ticket and scheduling lock
++ public JsonObject getDebugJson() {
++ final JsonObject ret = new JsonObject();
++
++ final ChunkCompletion lastCompletion = this.lastChunkCompletion;
++ final ChunkEntitySlices slices = this.entityChunk;
++ final PoiChunk poiChunk = this.poiChunk;
++
++ ret.addProperty("chunkX", Integer.valueOf(this.chunkX));
++ ret.addProperty("chunkZ", Integer.valueOf(this.chunkZ));
++ ret.addProperty("entity_chunk", slices == null ? "null" : "transient=" + slices.isTransient());
++ ret.addProperty("poi_chunk", "null=" + (poiChunk == null));
++ ret.addProperty("completed_chunk_class", lastCompletion == null ? "null" : lastCompletion.chunk().getClass().getName());
++ ret.addProperty("completed_gen_status", lastCompletion == null ? "null" : lastCompletion.genStatus().toString());
++ ret.addProperty("priority", Objects.toString(this.priority));
++ ret.addProperty("neighbour_requested_priority", Objects.toString(this.neighbourRequestedPriority));
++ ret.addProperty("generation_task", Objects.toString(this.generationTask));
++ ret.addProperty("is_safe_unload", Objects.toString(this.isSafeToUnload()));
++ ret.addProperty("old_ticket_level", Integer.valueOf(this.oldTicketLevel));
++ ret.addProperty("current_ticket_level", Integer.valueOf(this.currentTicketLevel));
++ ret.addProperty("neighbours_using_chunk", Integer.valueOf(this.totalNeighboursUsingThisChunk));
++
++ final JsonObject neighbourWaitState = new JsonObject();
++ ret.add("neighbour_state", neighbourWaitState);
++
++ final JsonArray blockingGenNeighbours = new JsonArray();
++ neighbourWaitState.add("blocking_gen_task", blockingGenNeighbours);
++ for (final NewChunkHolder blockingGenNeighbour : this.neighboursBlockingGenTask) {
++ final JsonObject neighbour = new JsonObject();
++ blockingGenNeighbours.add(neighbour);
++
++ neighbour.addProperty("chunkX", Integer.valueOf(blockingGenNeighbour.chunkX));
++ neighbour.addProperty("chunkZ", Integer.valueOf(blockingGenNeighbour.chunkZ));
++ }
++
++ final JsonArray neighboursWaitingForUs = new JsonArray();
++ neighbourWaitState.add("neighbours_waiting_on_us", neighboursWaitingForUs);
++ for (final Reference2ObjectMap.Entry<NewChunkHolder, ChunkStatus> entry : this.neighboursWaitingForUs.reference2ObjectEntrySet()) {
++ final NewChunkHolder holder = entry.getKey();
++ final ChunkStatus status = entry.getValue();
++
++ final JsonObject neighbour = new JsonObject();
++ neighboursWaitingForUs.add(neighbour);
++
++
++ neighbour.addProperty("chunkX", Integer.valueOf(holder.chunkX));
++ neighbour.addProperty("chunkZ", Integer.valueOf(holder.chunkZ));
++ neighbour.addProperty("waiting_for", Objects.toString(status));
++ }
++
++ ret.addProperty("fullchunkstatus", Objects.toString(this.getChunkStatus()));
++ ret.addProperty("fullchunkstatus_raw", Long.valueOf(this.chunkStatus));
++ ret.addProperty("generation_task", Objects.toString(this.generationTask));
++ ret.addProperty("requested_generation", Objects.toString(this.requestedGenStatus));
++ ret.addProperty("has_entity_load_task", Boolean.valueOf(this.entityDataLoadTask != null));
++ ret.addProperty("has_poi_load_task", Boolean.valueOf(this.poiDataLoadTask != null));
++
++ final UnloadTask entityDataUnload = this.entityDataUnload;
++ final UnloadTask poiDataUnload = this.poiDataUnload;
++ final UnloadTask chunkDataUnload = this.chunkDataUnload;
++
++ ret.add("entity_unload_completable", serializeCompletable(entityDataUnload == null ? null : entityDataUnload.completable()));
++ ret.add("poi_unload_completable", serializeCompletable(poiDataUnload == null ? null : poiDataUnload.completable()));
++ ret.add("chunk_unload_completable", serializeCompletable(chunkDataUnload == null ? null : chunkDataUnload.completable()));
++
++ final DelayedPrioritisedTask unloadTask = chunkDataUnload == null ? null : chunkDataUnload.task();
++ if (unloadTask == null) {
++ ret.addProperty("unload_task_priority", "null");
++ ret.addProperty("unload_task_priority_raw", "null");
++ } else {
++ ret.addProperty("unload_task_priority", Objects.toString(unloadTask.getPriority()));
++ ret.addProperty("unload_task_priority_raw", Integer.valueOf(unloadTask.getPriorityInternal()));
++ }
++
++ ret.addProperty("killed", Boolean.valueOf(this.killed));
++
++ return ret;
++ }
++}
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/PriorityHolder.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/PriorityHolder.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..b4c56bf12dc8dd17452210ece4fd67411cc6b2fd
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/PriorityHolder.java
+@@ -0,0 +1,215 @@
++package io.papermc.paper.chunk.system.scheduling;
++
++import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
++import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
++import java.lang.invoke.VarHandle;
++
++public abstract class PriorityHolder {
++
++ protected volatile int priority;
++ protected static final VarHandle PRIORITY_HANDLE = ConcurrentUtil.getVarHandle(PriorityHolder.class, "priority", int.class);
++
++ protected static final int PRIORITY_SCHEDULED = Integer.MIN_VALUE >>> 0;
++ protected static final int PRIORITY_EXECUTED = Integer.MIN_VALUE >>> 1;
++
++ protected final int getPriorityVolatile() {
++ return (int)PRIORITY_HANDLE.getVolatile((PriorityHolder)this);
++ }
++
++ protected final int compareAndExchangePriorityVolatile(final int expect, final int update) {
++ return (int)PRIORITY_HANDLE.compareAndExchange((PriorityHolder)this, (int)expect, (int)update);
++ }
++
++ protected final int getAndOrPriorityVolatile(final int val) {
++ return (int)PRIORITY_HANDLE.getAndBitwiseOr((PriorityHolder)this, (int)val);
++ }
++
++ protected final void setPriorityPlain(final int val) {
++ PRIORITY_HANDLE.set((PriorityHolder)this, (int)val);
++ }
++
++ protected PriorityHolder(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++ this.setPriorityPlain(priority.priority);
++ }
++
++ // used only for debug json
++ public boolean isScheduled() {
++ return (this.getPriorityVolatile() & PRIORITY_SCHEDULED) != 0;
++ }
++
++ // returns false if cancelled
++ protected boolean markExecuting() {
++ return (this.getAndOrPriorityVolatile(PRIORITY_EXECUTED) & PRIORITY_EXECUTED) == 0;
++ }
++
++ protected boolean isMarkedExecuted() {
++ return (this.getPriorityVolatile() & PRIORITY_EXECUTED) != 0;
++ }
++
++ public void cancel() {
++ if ((this.getAndOrPriorityVolatile(PRIORITY_EXECUTED) & PRIORITY_EXECUTED) != 0) {
++ // cancelled already
++ return;
++ }
++ this.cancelScheduled();
++ }
++
++ public void schedule() {
++ int priority = this.getPriorityVolatile();
++
++ if ((priority & PRIORITY_SCHEDULED) != 0) {
++ throw new IllegalStateException("schedule() called twice");
++ }
++
++ if ((priority & PRIORITY_EXECUTED) != 0) {
++ // cancelled
++ return;
++ }
++
++ this.scheduleTask(PrioritisedExecutor.Priority.getPriority(priority));
++
++ int failures = 0;
++ for (;;) {
++ if (priority == (priority = this.compareAndExchangePriorityVolatile(priority, priority | PRIORITY_SCHEDULED))) {
++ return;
++ }
++
++ if ((priority & PRIORITY_SCHEDULED) != 0) {
++ throw new IllegalStateException("schedule() called twice");
++ }
++
++ if ((priority & PRIORITY_EXECUTED) != 0) {
++ // cancelled or executed
++ return;
++ }
++
++ this.setPriorityScheduled(PrioritisedExecutor.Priority.getPriority(priority));
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ public final PrioritisedExecutor.Priority getPriority() {
++ final int ret = this.getPriorityVolatile();
++ if ((ret & PRIORITY_EXECUTED) != 0) {
++ return PrioritisedExecutor.Priority.COMPLETING;
++ }
++ if ((ret & PRIORITY_SCHEDULED) != 0) {
++ return this.getScheduledPriority();
++ }
++ return PrioritisedExecutor.Priority.getPriority(ret);
++ }
++
++ public final void lowerPriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++
++ int failures = 0;
++ for (int curr = this.getPriorityVolatile();;) {
++ if ((curr & PRIORITY_EXECUTED) != 0) {
++ return;
++ }
++
++ if ((curr & PRIORITY_SCHEDULED) != 0) {
++ this.lowerPriorityScheduled(priority);
++ return;
++ }
++
++ if (!priority.isLowerPriority(curr)) {
++ return;
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority))) {
++ return;
++ }
++
++ // failed, retry
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ public final void setPriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++
++ int failures = 0;
++ for (int curr = this.getPriorityVolatile();;) {
++ if ((curr & PRIORITY_EXECUTED) != 0) {
++ return;
++ }
++
++ if ((curr & PRIORITY_SCHEDULED) != 0) {
++ this.setPriorityScheduled(priority);
++ return;
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority))) {
++ return;
++ }
++
++ // failed, retry
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ public final void raisePriority(final PrioritisedExecutor.Priority priority) {
++ if (!PrioritisedExecutor.Priority.isValidPriority(priority)) {
++ throw new IllegalArgumentException("Invalid priority " + priority);
++ }
++
++ int failures = 0;
++ for (int curr = this.getPriorityVolatile();;) {
++ if ((curr & PRIORITY_EXECUTED) != 0) {
++ return;
++ }
++
++ if ((curr & PRIORITY_SCHEDULED) != 0) {
++ this.raisePriorityScheduled(priority);
++ return;
++ }
++
++ if (!priority.isHigherPriority(curr)) {
++ return;
++ }
++
++ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority.priority))) {
++ return;
++ }
++
++ // failed, retry
++
++ ++failures;
++ for (int i = 0; i < failures; ++i) {
++ ConcurrentUtil.backoff();
++ }
++ }
++ }
++
++ protected abstract void cancelScheduled();
++
++ protected abstract PrioritisedExecutor.Priority getScheduledPriority();
++
++ protected abstract void scheduleTask(final PrioritisedExecutor.Priority priority);
++
++ protected abstract void lowerPriorityScheduled(final PrioritisedExecutor.Priority priority);
++
++ protected abstract void setPriorityScheduled(final PrioritisedExecutor.Priority priority);
++
++ protected abstract void raisePriorityScheduled(final PrioritisedExecutor.Priority priority);
++}
+diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
+index c9a2ac696f7cefc8b0715f53db3fc541f26b62f6..1e9105cf5ab2ff0ee847fafd00b41e1bd47f1d9e 100644
+--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
++++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
+@@ -1,5 +1,6 @@
+ package io.papermc.paper.command;
+
++import io.papermc.paper.command.subcommands.ChunkDebugCommand;
+ import io.papermc.paper.command.subcommands.EntityCommand;
+ import io.papermc.paper.command.subcommands.FixLightCommand;
+ import io.papermc.paper.command.subcommands.HeapDumpCommand;
+@@ -42,6 +43,7 @@ public final class PaperCommand extends Command {
+ commands.put(Set.of("reload"), new ReloadCommand());
+ commands.put(Set.of("version"), new VersionCommand());
+ commands.put(Set.of("fixlight"), new FixLightCommand());
++ commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand());
+
+ return commands.entrySet().stream()
+ .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
+diff --git a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..628c549b1436c3de75071ecd6182a9beadd4840b
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
+@@ -0,0 +1,264 @@
++package io.papermc.paper.command.subcommands;
++
++import io.papermc.paper.command.CommandUtil;
++import io.papermc.paper.command.PaperSubcommand;
++import java.io.File;
++import java.time.LocalDateTime;
++import java.time.format.DateTimeFormatter;
++import java.util.ArrayList;
++import java.util.Collections;
++import java.util.List;
++import java.util.Locale;
++import net.minecraft.server.MCUtil;
++import net.minecraft.server.MinecraftServer;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ImposterProtoChunk;
++import net.minecraft.world.level.chunk.LevelChunk;
++import net.minecraft.world.level.chunk.ProtoChunk;
++import org.bukkit.Bukkit;
++import org.bukkit.command.CommandSender;
++import org.bukkit.craftbukkit.CraftWorld;
++import org.checkerframework.checker.nullness.qual.NonNull;
++import org.checkerframework.checker.nullness.qual.Nullable;
++import org.checkerframework.framework.qual.DefaultQualifier;
++
++import static net.kyori.adventure.text.Component.text;
++import static net.kyori.adventure.text.format.NamedTextColor.BLUE;
++import static net.kyori.adventure.text.format.NamedTextColor.DARK_AQUA;
++import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
++import static net.kyori.adventure.text.format.NamedTextColor.RED;
++
++@DefaultQualifier(NonNull.class)
++public final class ChunkDebugCommand implements PaperSubcommand {
++ @Override
++ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
++ switch (subCommand) {
++ case "debug" -> this.doDebug(sender, args);
++ case "chunkinfo" -> this.doChunkInfo(sender, args);
++ case "holderinfo" -> this.doHolderInfo(sender, args);
++ }
++ return true;
++ }
++
++ @Override
++ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
++ switch (subCommand) {
++ case "debug" -> {
++ if (args.length == 1) {
++ return CommandUtil.getListMatchingLast(sender, args, "help", "chunks");
++ }
++ }
++ case "holderinfo" -> {
++ List<String> worldNames = new ArrayList<>();
++ worldNames.add("*");
++ for (org.bukkit.World world : Bukkit.getWorlds()) {
++ worldNames.add(world.getName());
++ }
++ if (args.length == 1) {
++ return CommandUtil.getListMatchingLast(sender, args, worldNames);
++ }
++ }
++ case "chunkinfo" -> {
++ List<String> worldNames = new ArrayList<>();
++ worldNames.add("*");
++ for (org.bukkit.World world : Bukkit.getWorlds()) {
++ worldNames.add(world.getName());
++ }
++ if (args.length == 1) {
++ return CommandUtil.getListMatchingLast(sender, args, worldNames);
++ }
++ }
++ }
++ return Collections.emptyList();
++ }
++
++ private void doChunkInfo(final CommandSender sender, final String[] args) {
++ List<org.bukkit.World> worlds;
++ if (args.length < 1 || args[0].equals("*")) {
++ worlds = Bukkit.getWorlds();
++ } else {
++ worlds = new ArrayList<>(args.length);
++ for (final String arg : args) {
++ org.bukkit.@Nullable World world = Bukkit.getWorld(arg);
++ if (world == null) {
++ sender.sendMessage(text("World '" + arg + "' is invalid", RED));
++ return;
++ }
++ worlds.add(world);
++ }
++ }
++
++ int accumulatedTotal = 0;
++ int accumulatedInactive = 0;
++ int accumulatedBorder = 0;
++ int accumulatedTicking = 0;
++ int accumulatedEntityTicking = 0;
++
++ for (final org.bukkit.World bukkitWorld : worlds) {
++ final ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
++
++ int total = 0;
++ int inactive = 0;
++ int border = 0;
++ int ticking = 0;
++ int entityTicking = 0;
++
++ for (final ChunkHolder chunk : net.minecraft.server.ChunkSystem.getVisibleChunkHolders(world)) {
++ if (chunk.getFullChunkNowUnchecked() == null) {
++ continue;
++ }
++
++ ++total;
++
++ ChunkHolder.FullChunkStatus state = chunk.getFullStatus();
++
++ switch (state) {
++ case INACCESSIBLE -> ++inactive;
++ case BORDER -> ++border;
++ case TICKING -> ++ticking;
++ case ENTITY_TICKING -> ++entityTicking;
++ }
++ }
++
++ accumulatedTotal += total;
++ accumulatedInactive += inactive;
++ accumulatedBorder += border;
++ accumulatedTicking += ticking;
++ accumulatedEntityTicking += entityTicking;
++
++ sender.sendMessage(text().append(text("Chunks in ", BLUE), text(bukkitWorld.getName(), GREEN), text(":")));
++ sender.sendMessage(text().color(DARK_AQUA).append(
++ text("Total: ", BLUE), text(total),
++ text(" Inactive: ", BLUE), text(inactive),
++ text(" Border: ", BLUE), text(border),
++ text(" Ticking: ", BLUE), text(ticking),
++ text(" Entity: ", BLUE), text(entityTicking)
++ ));
++ }
++ if (worlds.size() > 1) {
++ sender.sendMessage(text().append(text("Chunks in ", BLUE), text("all listed worlds", GREEN), text(":", DARK_AQUA)));
++ sender.sendMessage(text().color(DARK_AQUA).append(
++ text("Total: ", BLUE), text(accumulatedTotal),
++ text(" Inactive: ", BLUE), text(accumulatedInactive),
++ text(" Border: ", BLUE), text(accumulatedBorder),
++ text(" Ticking: ", BLUE), text(accumulatedTicking),
++ text(" Entity: ", BLUE), text(accumulatedEntityTicking)
++ ));
++ }
++ }
++
++ private void doHolderInfo(final CommandSender sender, final String[] args) {
++ List<org.bukkit.World> worlds;
++ if (args.length < 1 || args[0].equals("*")) {
++ worlds = Bukkit.getWorlds();
++ } else {
++ worlds = new ArrayList<>(args.length);
++ for (final String arg : args) {
++ org.bukkit.@Nullable World world = Bukkit.getWorld(arg);
++ if (world == null) {
++ sender.sendMessage(text("World '" + arg + "' is invalid", RED));
++ return;
++ }
++ worlds.add(world);
++ }
++ }
++
++ int accumulatedTotal = 0;
++ int accumulatedCanUnload = 0;
++ int accumulatedNull = 0;
++ int accumulatedReadOnly = 0;
++ int accumulatedProtoChunk = 0;
++ int accumulatedFullChunk = 0;
++
++ for (final org.bukkit.World bukkitWorld : worlds) {
++ final ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
++
++ int total = 0;
++ int canUnload = 0;
++ int nullChunks = 0;
++ int readOnly = 0;
++ int protoChunk = 0;
++ int fullChunk = 0;
++
++ for (final ChunkHolder chunk : world.chunkTaskScheduler.chunkHolderManager.getOldChunkHolders()) { // Paper - change updating chunks map
++ final ChunkAccess lastChunk = chunk.getAvailableChunkNow();
++
++ ++total;
++
++ if (lastChunk == null) {
++ ++nullChunks;
++ } else if (lastChunk instanceof ImposterProtoChunk) {
++ ++readOnly;
++ } else if (lastChunk instanceof ProtoChunk) {
++ ++protoChunk;
++ } else if (lastChunk instanceof LevelChunk) {
++ ++fullChunk;
++ }
++
++ if (chunk.newChunkHolder.isSafeToUnload() == null) {
++ ++canUnload;
++ }
++ }
++
++ accumulatedTotal += total;
++ accumulatedCanUnload += canUnload;
++ accumulatedNull += nullChunks;
++ accumulatedReadOnly += readOnly;
++ accumulatedProtoChunk += protoChunk;
++ accumulatedFullChunk += fullChunk;
++
++ sender.sendMessage(text().append(text("Chunks in ", BLUE), text(bukkitWorld.getName(), GREEN), text(":")));
++ sender.sendMessage(text().color(DARK_AQUA).append(
++ text("Total: ", BLUE), text(total),
++ text(" Unloadable: ", BLUE), text(canUnload),
++ text(" Null: ", BLUE), text(nullChunks),
++ text(" ReadOnly: ", BLUE), text(readOnly),
++ text(" Proto: ", BLUE), text(protoChunk),
++ text(" Full: ", BLUE), text(fullChunk)
++ ));
++ }
++ if (worlds.size() > 1) {
++ sender.sendMessage(text().append(text("Chunks in ", BLUE), text("all listed worlds", GREEN), text(":", DARK_AQUA)));
++ sender.sendMessage(text().color(DARK_AQUA).append(
++ text("Total: ", BLUE), text(accumulatedTotal),
++ text(" Unloadable: ", BLUE), text(accumulatedCanUnload),
++ text(" Null: ", BLUE), text(accumulatedNull),
++ text(" ReadOnly: ", BLUE), text(accumulatedReadOnly),
++ text(" Proto: ", BLUE), text(accumulatedProtoChunk),
++ text(" Full: ", BLUE), text(accumulatedFullChunk)
++ ));
++ }
++ }
++
++ private void doDebug(final CommandSender sender, final String[] args) {
++ if (args.length < 1) {
++ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
++ return;
++ }
++
++ final String debugType = args[0].toLowerCase(Locale.ENGLISH);
++ switch (debugType) {
++ case "chunks" -> {
++ if (args.length >= 2 && args[1].toLowerCase(Locale.ENGLISH).equals("help")) {
++ sender.sendMessage(text("Use /paper debug chunks [world] to dump loaded chunk information to a file", RED));
++ break;
++ }
++ File file = new File(new File(new File("."), "debug"),
++ "chunks-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt");
++ sender.sendMessage(text("Writing chunk information dump to " + file, GREEN));
++ try {
++ MCUtil.dumpChunks(file, false);
++ sender.sendMessage(text("Successfully written chunk information!", GREEN));
++ } catch (Throwable thr) {
++ MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
++ sender.sendMessage(text("Failed to dump chunk information, see console", RED));
++ }
++ }
++ // "help" & default
++ default -> sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
++ }
++ }
++
++}
+diff --git a/src/main/java/io/papermc/paper/util/TickThread.java b/src/main/java/io/papermc/paper/util/TickThread.java
+index d59885ee9c8b29d5bac34dce0597e345e5358c77..fc57850b80303fcade89ca95794f63910404a407 100644
+--- a/src/main/java/io/papermc/paper/util/TickThread.java
++++ b/src/main/java/io/papermc/paper/util/TickThread.java
+@@ -6,7 +6,7 @@ import net.minecraft.world.entity.Entity;
+ import org.bukkit.Bukkit;
+ import java.util.concurrent.atomic.AtomicInteger;
+
+-public final class TickThread extends Thread {
++public class TickThread extends Thread {
+
+ public static final boolean STRICT_THREAD_CHECKS = Boolean.getBoolean("paper.strict-thread-checks");
+
+@@ -16,6 +16,10 @@ public final class TickThread extends Thread {
+ }
+ }
+
++ /**
++ * @deprecated
++ */
++ @Deprecated
+ public static void softEnsureTickThread(final String reason) {
+ if (!STRICT_THREAD_CHECKS) {
+ return;
+@@ -23,6 +27,10 @@ public final class TickThread extends Thread {
+ ensureTickThread(reason);
+ }
+
++ /**
++ * @deprecated
++ */
++ @Deprecated
+ public static void ensureTickThread(final String reason) {
+ if (!isTickThread()) {
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable());
+@@ -66,14 +74,14 @@ public final class TickThread extends Thread {
+ }
+
+ public static boolean isTickThread() {
+- return Bukkit.isPrimaryThread();
++ return Thread.currentThread() instanceof TickThread;
+ }
+
+ public static boolean isTickThreadFor(final ServerLevel world, final int chunkX, final int chunkZ) {
+- return Bukkit.isPrimaryThread();
++ return Thread.currentThread() instanceof TickThread;
+ }
+
+ public static boolean isTickThreadFor(final Entity entity) {
+- return Bukkit.isPrimaryThread();
++ return Thread.currentThread() instanceof TickThread;
+ }
+ }
+diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..f597d65d56964297eeeed6c7e77703764178fee0
+--- /dev/null
++++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java
+@@ -0,0 +1,601 @@
++package io.papermc.paper.world;
++
++import com.destroystokyo.paper.util.maplist.EntityList;
++import io.papermc.paper.chunk.system.entity.EntityLookup;
++import io.papermc.paper.util.TickThread;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
++import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
++import net.minecraft.nbt.CompoundTag;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ServerLevel;
++import net.minecraft.util.Mth;
++import net.minecraft.world.entity.Entity;
++import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.boss.EnderDragonPart;
++import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
++import net.minecraft.world.level.ChunkPos;
++import net.minecraft.world.level.chunk.storage.EntityStorage;
++import net.minecraft.world.level.entity.Visibility;
++import net.minecraft.world.phys.AABB;
++import org.bukkit.craftbukkit.event.CraftEventFactory;
++import java.util.ArrayList;
++import java.util.Arrays;
++import java.util.Iterator;
++import java.util.List;
++import java.util.function.Predicate;
++
++public final class ChunkEntitySlices {
++
++ protected final int minSection;
++ protected final int maxSection;
++ public final int chunkX;
++ public final int chunkZ;
++ protected final ServerLevel world;
++
++ protected final EntityCollectionBySection allEntities;
++ protected final EntityCollectionBySection hardCollidingEntities;
++ protected final Reference2ObjectOpenHashMap<Class<? extends Entity>, EntityCollectionBySection> entitiesByClass;
++ protected final EntityList entities = new EntityList();
++
++ public ChunkHolder.FullChunkStatus status;
++
++ protected boolean isTransient;
++
++ public boolean isTransient() {
++ return this.isTransient;
++ }
++
++ public void setTransient(final boolean value) {
++ this.isTransient = value;
++ }
++
++ // TODO implement container search optimisations
++
++ public ChunkEntitySlices(final ServerLevel world, final int chunkX, final int chunkZ, final ChunkHolder.FullChunkStatus status,
++ final int minSection, final int maxSection) { // inclusive, inclusive
++ this.minSection = minSection;
++ this.maxSection = maxSection;
++ this.chunkX = chunkX;
++ this.chunkZ = chunkZ;
++ this.world = world;
++
++ this.allEntities = new EntityCollectionBySection(this);
++ this.hardCollidingEntities = new EntityCollectionBySection(this);
++ this.entitiesByClass = new Reference2ObjectOpenHashMap<>();
++
++ this.status = status;
++ }
++
++ // Paper start - optimise CraftChunk#getEntities
++ public org.bukkit.entity.Entity[] getChunkEntities() {
++ List<org.bukkit.entity.Entity> ret = new java.util.ArrayList<>();
++ final Entity[] entities = this.entities.getRawData();
++ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
++ final Entity entity = entities[i];
++ if (entity == null) {
++ continue;
++ }
++ final org.bukkit.entity.Entity bukkit = entity.getBukkitEntity();
++ if (bukkit != null && bukkit.isValid()) {
++ ret.add(bukkit);
++ }
++ }
++
++ return ret.toArray(new org.bukkit.entity.Entity[0]);
++ }
++
++ public CompoundTag save() {
++ final int len = this.entities.size();
++ if (len == 0) {
++ return null;
++ }
++
++ final Entity[] rawData = this.entities.getRawData();
++ final List<Entity> collectedEntities = new ArrayList<>(len);
++ for (int i = 0; i < len; ++i) {
++ final Entity entity = rawData[i];
++ if (entity.shouldBeSaved()) {
++ collectedEntities.add(entity);
++ }
++ }
++
++ if (collectedEntities.isEmpty()) {
++ return null;
++ }
++
++ return EntityStorage.saveEntityChunk(collectedEntities, new ChunkPos(this.chunkX, this.chunkZ), this.world);
++ }
++
++ // returns true if this chunk has transient entities remaining
++ public boolean unload() {
++ final int len = this.entities.size();
++ final Entity[] collectedEntities = Arrays.copyOf(this.entities.getRawData(), len);
++
++ for (int i = 0; i < len; ++i) {
++ final Entity entity = collectedEntities[i];
++ if (entity.isRemoved()) {
++ // removed by us below
++ continue;
++ }
++ if (entity.shouldBeSaved()) {
++ entity.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
++ if (entity.isVehicle()) {
++ // we cannot assume that these entities are contained within this chunk, because entities can
++ // desync - so we need to remove them all
++ for (final Entity passenger : entity.getIndirectPassengers()) {
++ passenger.setRemoved(Entity.RemovalReason.UNLOADED_TO_CHUNK);
++ }
++ }
++ }
++ }
++
++ return this.entities.size() != 0;
++ }
++
++ private List<Entity> getAllEntities() {
++ final int len = this.entities.size();
++ if (len == 0) {
++ return new ArrayList<>();
++ }
++
++ final Entity[] rawData = this.entities.getRawData();
++ final List<Entity> collectedEntities = new ArrayList<>(len);
++ for (int i = 0; i < len; ++i) {
++ collectedEntities.add(rawData[i]);
++ }
++
++ return collectedEntities;
++ }
++
++ public void callEntitiesLoadEvent() {
++ CraftEventFactory.callEntitiesLoadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
++ }
++
++ public void callEntitiesUnloadEvent() {
++ CraftEventFactory.callEntitiesUnloadEvent(this.world, new ChunkPos(this.chunkX, this.chunkZ), this.getAllEntities());
++ }
++ // Paper end - optimise CraftChunk#getEntities
++
++ public boolean isEmpty() {
++ return this.entities.size() == 0;
++ }
++
++ public void mergeInto(final ChunkEntitySlices slices) {
++ final Entity[] entities = this.entities.getRawData();
++ for (int i = 0, size = Math.min(entities.length, this.entities.size()); i < size; ++i) {
++ final Entity entity = entities[i];
++ slices.addEntity(entity, entity.sectionY);
++ }
++ }
++
++ private boolean preventStatusUpdates;
++ public boolean startPreventingStatusUpdates() {
++ final boolean ret = this.preventStatusUpdates;
++ this.preventStatusUpdates = true;
++ return ret;
++ }
++
++ public void stopPreventingStatusUpdates(final boolean prev) {
++ this.preventStatusUpdates = prev;
++ }
++
++ public void updateStatus(final ChunkHolder.FullChunkStatus status, final EntityLookup lookup) {
++ this.status = status;
++
++ final Entity[] entities = this.entities.getRawData();
++
++ for (int i = 0, size = this.entities.size(); i < size; ++i) {
++ final Entity entity = entities[i];
++
++ final Visibility oldVisibility = EntityLookup.getEntityStatus(entity);
++ entity.chunkStatus = status;
++ final Visibility newVisibility = EntityLookup.getEntityStatus(entity);
++
++ lookup.entityStatusChange(entity, this, oldVisibility, newVisibility, false, false, false);
++ }
++ }
++
++ public boolean addEntity(final Entity entity, final int chunkSection) {
++ if (!this.entities.add(entity)) {
++ return false;
++ }
++ entity.chunkStatus = this.status;
++ final int sectionIndex = chunkSection - this.minSection;
++
++ this.allEntities.addEntity(entity, sectionIndex);
++
++ if (entity.hardCollides()) {
++ this.hardCollidingEntities.addEntity(entity, sectionIndex);
++ }
++
++ for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
++ this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
++ final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
++
++ if (entry.getKey().isInstance(entity)) {
++ entry.getValue().addEntity(entity, sectionIndex);
++ }
++ }
++
++ return true;
++ }
++
++ public boolean removeEntity(final Entity entity, final int chunkSection) {
++ if (!this.entities.remove(entity)) {
++ return false;
++ }
++ entity.chunkStatus = null;
++ final int sectionIndex = chunkSection - this.minSection;
++
++ this.allEntities.removeEntity(entity, sectionIndex);
++
++ if (entity.hardCollides()) {
++ this.hardCollidingEntities.removeEntity(entity, sectionIndex);
++ }
++
++ for (final Iterator<Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection>> iterator =
++ this.entitiesByClass.reference2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
++ final Reference2ObjectMap.Entry<Class<? extends Entity>, EntityCollectionBySection> entry = iterator.next();
++
++ if (entry.getKey().isInstance(entity)) {
++ entry.getValue().removeEntity(entity, sectionIndex);
++ }
++ }
++
++ return true;
++ }
++
++ public void getHardCollidingEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
++ this.hardCollidingEntities.getEntities(except, box, into, predicate);
++ }
++
++ public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
++ this.allEntities.getEntitiesWithEnderDragonParts(except, box, into, predicate);
++ }
++
++ public void getEntitiesWithoutDragonParts(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
++ this.allEntities.getEntities(except, box, into, predicate);
++ }
++
++ public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
++ final Predicate<? super T> predicate) {
++ this.allEntities.getEntities(type, box, (List)into, (Predicate)predicate);
++ }
++
++ protected EntityCollectionBySection initClass(final Class<? extends Entity> clazz) {
++ final EntityCollectionBySection ret = new EntityCollectionBySection(this);
++
++ for (int sectionIndex = 0; sectionIndex < this.allEntities.entitiesBySection.length; ++sectionIndex) {
++ final BasicEntityList<Entity> sectionEntities = this.allEntities.entitiesBySection[sectionIndex];
++ if (sectionEntities == null) {
++ continue;
++ }
++
++ final Entity[] storage = sectionEntities.storage;
++
++ for (int i = 0, len = Math.min(storage.length, sectionEntities.size()); i < len; ++i) {
++ final Entity entity = storage[i];
++
++ if (clazz.isInstance(entity)) {
++ ret.addEntity(entity, sectionIndex);
++ }
++ }
++ }
++
++ return ret;
++ }
++
++ public <T extends Entity> void getEntities(final Class<? extends T> clazz, final Entity except, final AABB box, final List<? super T> into,
++ final Predicate<? super T> predicate) {
++ EntityCollectionBySection collection = this.entitiesByClass.get(clazz);
++ if (collection != null) {
++ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate);
++ } else {
++ this.entitiesByClass.putIfAbsent(clazz, collection = this.initClass(clazz));
++ collection.getEntitiesWithEnderDragonParts(except, clazz, box, (List)into, (Predicate)predicate);
++ }
++ }
++
++ protected static final class BasicEntityList<E extends Entity> {
++
++ protected static final Entity[] EMPTY = new Entity[0];
++ protected static final int DEFAULT_CAPACITY = 4;
++
++ protected E[] storage;
++ protected int size;
++
++ public BasicEntityList() {
++ this(0);
++ }
++
++ public BasicEntityList(final int cap) {
++ this.storage = (E[])(cap <= 0 ? EMPTY : new Entity[cap]);
++ }
++
++ public boolean isEmpty() {
++ return this.size == 0;
++ }
++
++ public int size() {
++ return this.size;
++ }
++
++ private void resize() {
++ if (this.storage == EMPTY) {
++ this.storage = (E[])new Entity[DEFAULT_CAPACITY];
++ } else {
++ this.storage = Arrays.copyOf(this.storage, this.storage.length * 2);
++ }
++ }
++
++ public void add(final E entity) {
++ final int idx = this.size++;
++ if (idx >= this.storage.length) {
++ this.resize();
++ this.storage[idx] = entity;
++ } else {
++ this.storage[idx] = entity;
++ }
++ }
++
++ public int indexOf(final E entity) {
++ final E[] storage = this.storage;
++
++ for (int i = 0, len = Math.min(this.storage.length, this.size); i < len; ++i) {
++ if (storage[i] == entity) {
++ return i;
++ }
++ }
++
++ return -1;
++ }
++
++ public boolean remove(final E entity) {
++ final int idx = this.indexOf(entity);
++ if (idx == -1) {
++ return false;
++ }
++
++ final int size = --this.size;
++ final E[] storage = this.storage;
++ if (idx != size) {
++ System.arraycopy(storage, idx + 1, storage, idx, size - idx);
++ }
++
++ storage[size] = null;
++
++ return true;
++ }
++
++ public boolean has(final E entity) {
++ return this.indexOf(entity) != -1;
++ }
++ }
++
++ protected static final class EntityCollectionBySection {
++
++ protected final ChunkEntitySlices manager;
++ protected final long[] nonEmptyBitset;
++ protected final BasicEntityList<Entity>[] entitiesBySection;
++ protected int count;
++
++ public EntityCollectionBySection(final ChunkEntitySlices manager) {
++ this.manager = manager;
++
++ final int sectionCount = manager.maxSection - manager.minSection + 1;
++
++ this.nonEmptyBitset = new long[(sectionCount + (Long.SIZE - 1)) >>> 6]; // (sectionCount + (Long.SIZE - 1)) / Long.SIZE
++ this.entitiesBySection = new BasicEntityList[sectionCount];
++ }
++
++ public void addEntity(final Entity entity, final int sectionIndex) {
++ BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
++
++ if (list != null && list.has(entity)) {
++ return;
++ }
++
++ if (list == null) {
++ this.entitiesBySection[sectionIndex] = list = new BasicEntityList<>();
++ this.nonEmptyBitset[sectionIndex >>> 6] |= (1L << (sectionIndex & (Long.SIZE - 1)));
++ }
++
++ list.add(entity);
++ ++this.count;
++ }
++
++ public void removeEntity(final Entity entity, final int sectionIndex) {
++ final BasicEntityList<Entity> list = this.entitiesBySection[sectionIndex];
++
++ if (list == null || !list.remove(entity)) {
++ return;
++ }
++
++ --this.count;
++
++ if (list.isEmpty()) {
++ this.entitiesBySection[sectionIndex] = null;
++ this.nonEmptyBitset[sectionIndex >>> 6] ^= (1L << (sectionIndex & (Long.SIZE - 1)));
++ }
++ }
++
++ public void getEntities(final Entity except, final AABB box, final List<Entity> into, final Predicate<? super Entity> predicate) {
++ if (this.count == 0) {
++ return;
++ }
++
++ final int minSection = this.manager.minSection;
++ final int maxSection = this.manager.maxSection;
++
++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
++
++ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
++
++ for (int section = min; section <= max; ++section) {
++ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
++
++ if (list == null) {
++ continue;
++ }
++
++ final Entity[] storage = list.storage;
++
++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
++ final Entity entity = storage[i];
++
++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
++ continue;
++ }
++
++ if (predicate != null && !predicate.test(entity)) {
++ continue;
++ }
++
++ into.add(entity);
++ }
++ }
++ }
++
++ public void getEntitiesWithEnderDragonParts(final Entity except, final AABB box, final List<Entity> into,
++ final Predicate<? super Entity> predicate) {
++ if (this.count == 0) {
++ return;
++ }
++
++ final int minSection = this.manager.minSection;
++ final int maxSection = this.manager.maxSection;
++
++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
++
++ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
++
++ for (int section = min; section <= max; ++section) {
++ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
++
++ if (list == null) {
++ continue;
++ }
++
++ final Entity[] storage = list.storage;
++
++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
++ final Entity entity = storage[i];
++
++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
++ continue;
++ }
++
++ if (predicate == null || predicate.test(entity)) {
++ into.add(entity);
++ } // else: continue to test the ender dragon parts
++
++ if (entity instanceof EnderDragon) {
++ for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
++ if (part == except || !part.getBoundingBox().intersects(box)) {
++ continue;
++ }
++
++ if (predicate != null && !predicate.test(part)) {
++ continue;
++ }
++
++ into.add(part);
++ }
++ }
++ }
++ }
++ }
++
++ public void getEntitiesWithEnderDragonParts(final Entity except, final Class<?> clazz, final AABB box, final List<Entity> into,
++ final Predicate<? super Entity> predicate) {
++ if (this.count == 0) {
++ return;
++ }
++
++ final int minSection = this.manager.minSection;
++ final int maxSection = this.manager.maxSection;
++
++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
++
++ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
++
++ for (int section = min; section <= max; ++section) {
++ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
++
++ if (list == null) {
++ continue;
++ }
++
++ final Entity[] storage = list.storage;
++
++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
++ final Entity entity = storage[i];
++
++ if (entity == null || entity == except || !entity.getBoundingBox().intersects(box)) {
++ continue;
++ }
++
++ if (predicate == null || predicate.test(entity)) {
++ into.add(entity);
++ } // else: continue to test the ender dragon parts
++
++ if (entity instanceof EnderDragon) {
++ for (final EnderDragonPart part : ((EnderDragon)entity).subEntities) {
++ if (part == except || !part.getBoundingBox().intersects(box) || !clazz.isInstance(part)) {
++ continue;
++ }
++
++ if (predicate != null && !predicate.test(part)) {
++ continue;
++ }
++
++ into.add(part);
++ }
++ }
++ }
++ }
++ }
++
++ public <T extends Entity> void getEntities(final EntityType<?> type, final AABB box, final List<? super T> into,
++ final Predicate<? super T> predicate) {
++ if (this.count == 0) {
++ return;
++ }
++
++ final int minSection = this.manager.minSection;
++ final int maxSection = this.manager.maxSection;
++
++ final int min = Mth.clamp(Mth.floor(box.minY - 2.0) >> 4, minSection, maxSection);
++ final int max = Mth.clamp(Mth.floor(box.maxY + 2.0) >> 4, minSection, maxSection);
++
++ final BasicEntityList<Entity>[] entitiesBySection = this.entitiesBySection;
++
++ for (int section = min; section <= max; ++section) {
++ final BasicEntityList<Entity> list = entitiesBySection[section - minSection];
++
++ if (list == null) {
++ continue;
++ }
++
++ final Entity[] storage = list.storage;
++
++ for (int i = 0, len = Math.min(storage.length, list.size()); i < len; ++i) {
++ final Entity entity = storage[i];
++
++ if (entity == null || (type != null && entity.getType() != type) || !entity.getBoundingBox().intersects(box)) {
++ continue;
++ }
++
++ if (predicate != null && !predicate.test((T)entity)) {
++ continue;
++ }
++
++ into.add((T)entity);
++ }
++ }
++ }
++ }
++}
+diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
+index 9b96d05094c3b83f6388d479fdca8800453ccd1d..b5b10c57401e1b27175b1960839a81382d89b73f 100644
+--- a/src/main/java/net/minecraft/network/Connection.java
++++ b/src/main/java/net/minecraft/network/Connection.java
+@@ -89,6 +89,28 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+ private float averageSentPackets;
+ private int tickCount;
+ private boolean handlingFault;
++ // Paper start - add pending task queue
++ private final Queue<Runnable> pendingTasks = new java.util.concurrent.ConcurrentLinkedQueue<>();
++ public void execute(final Runnable run) {
++ if (this.channel == null || !this.channel.isRegistered()) {
++ run.run();
++ return;
++ }
++ final boolean queue = !this.queue.isEmpty();
++ if (!queue) {
++ this.channel.eventLoop().execute(run);
++ } else {
++ this.pendingTasks.add(run);
++ if (this.queue.isEmpty()) {
++ // something flushed async, dump tasks now
++ Runnable r;
++ while ((r = this.pendingTasks.poll()) != null) {
++ this.channel.eventLoop().execute(r);
++ }
++ }
++ }
++ }
++ // Paper end - add pending task queue
+
+ public Connection(PacketFlow side) {
+ this.receiving = side;
+@@ -247,6 +269,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+ }
+
+ private void flushQueue() {
++ try { // Paper - add pending task queue
+ if (this.channel != null && this.channel.isOpen()) {
+ Queue queue = this.queue;
+
+@@ -259,6 +282,12 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+
+ }
+ }
++ } finally { // Paper start - add pending task queue
++ Runnable r;
++ while ((r = this.pendingTasks.poll()) != null) {
++ this.channel.eventLoop().execute(r);
++ }
++ } // Paper end - add pending task queue
+ }
+
+ public void tick() {
+diff --git a/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java b/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java
+index a5e438a834826161c52ca9db57d234d9ff80a591..b8bc1b9b8e8a33df90a963f9f9769292bf595642 100644
+--- a/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java
++++ b/src/main/java/net/minecraft/network/protocol/game/ServerboundCommandSuggestionPacket.java
+@@ -14,7 +14,7 @@ public class ServerboundCommandSuggestionPacket implements Packet<ServerGamePack
+
+ public ServerboundCommandSuggestionPacket(FriendlyByteBuf buf) {
+ this.id = buf.readVarInt();
+- this.command = buf.readUtf(32500);
++ this.command = buf.readUtf(2048);
+ }
+
+ @Override
+diff --git a/src/main/java/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
+index c59fca05484c30b28e883f5b5dde0362f294b517..0c6534e64ef023cf613f2c5407c7598c6ed81bc6 100644
+--- a/src/main/java/net/minecraft/server/ChunkSystem.java
++++ b/src/main/java/net/minecraft/server/ChunkSystem.java
+@@ -31,191 +31,41 @@ public final class ChunkSystem {
+ }
+
+ public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final PrioritisedExecutor.Priority priority) {
+- level.chunkSource.mainThreadProcessor.execute(run);
++ level.chunkTaskScheduler.scheduleChunkTask(chunkX, chunkZ, run, priority); // Paper - rewrite chunk system
+ }
+
+ public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
+ final ChunkStatus toStatus, final boolean addTicket, final PrioritisedExecutor.Priority priority,
+ final Consumer<ChunkAccess> onComplete) {
+- if (gen) {
+- scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+- return;
+- }
+- scheduleChunkLoad(level, chunkX, chunkZ, ChunkStatus.EMPTY, addTicket, priority, (final ChunkAccess chunk) -> {
+- if (chunk == null) {
+- onComplete.accept(null);
+- } else {
+- if (chunk.getStatus().isOrAfter(toStatus)) {
+- scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+- } else {
+- onComplete.accept(null);
+- }
+- }
+- });
++ level.chunkTaskScheduler.scheduleChunkLoad(chunkX, chunkZ, gen, toStatus, addTicket, priority, onComplete); // Paper - rewrite chunk system
+ }
+
+- static final TicketType<Long> CHUNK_LOAD = TicketType.create("chunk_load", Long::compareTo);
+-
+- private static long chunkLoadCounter = 0L;
++ // Paper - rewrite chunk system
+ public static void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
+ final boolean addTicket, final PrioritisedExecutor.Priority priority, final Consumer<ChunkAccess> onComplete) {
+- if (!Bukkit.isPrimaryThread()) {
+- scheduleChunkTask(level, chunkX, chunkZ, () -> {
+- scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+- }, priority);
+- return;
+- }
+-
+- final int minLevel = 33 + ChunkStatus.getDistance(toStatus);
+- final Long chunkReference = addTicket ? Long.valueOf(++chunkLoadCounter) : null;
+- final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+-
+- if (addTicket) {
+- level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+- }
+- level.chunkSource.runDistanceManagerUpdates();
+-
+- final Consumer<ChunkAccess> loadCallback = (final ChunkAccess chunk) -> {
+- try {
+- if (onComplete != null) {
+- onComplete.accept(chunk);
+- }
+- } catch (final ThreadDeath death) {
+- throw death;
+- } catch (final Throwable thr) {
+- LOGGER.error("Exception handling chunk load callback", thr);
+- SneakyThrow.sneaky(thr);
+- } finally {
+- if (addTicket) {
+- level.chunkSource.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
+- level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+- }
+- }
+- };
+-
+- final ChunkHolder holder = level.chunkSource.chunkMap.getUpdatingChunkIfPresent(CoordinateUtils.getChunkKey(chunkX, chunkZ));
+-
+- if (holder == null || holder.getTicketLevel() > minLevel) {
+- loadCallback.accept(null);
+- return;
+- }
+-
+- final CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> loadFuture = holder.getOrScheduleFuture(toStatus, level.chunkSource.chunkMap);
+-
+- if (loadFuture.isDone()) {
+- loadCallback.accept(loadFuture.join().left().orElse(null));
+- return;
+- }
+-
+- loadFuture.whenCompleteAsync((final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either, final Throwable thr) -> {
+- if (thr != null) {
+- loadCallback.accept(null);
+- return;
+- }
+- loadCallback.accept(either.left().orElse(null));
+- }, (final Runnable r) -> {
+- scheduleChunkTask(level, chunkX, chunkZ, r, PrioritisedExecutor.Priority.HIGHEST);
+- });
++ level.chunkTaskScheduler.scheduleChunkLoad(chunkX, chunkZ, toStatus, addTicket, priority, onComplete); // Paper - rewrite chunk system
+ }
+
+ public static void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
+ final ChunkHolder.FullChunkStatus toStatus, final boolean addTicket,
+ final PrioritisedExecutor.Priority priority, final Consumer<LevelChunk> onComplete) {
+- if (toStatus == ChunkHolder.FullChunkStatus.INACCESSIBLE) {
+- throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status");
+- }
+-
+- if (!Bukkit.isPrimaryThread()) {
+- scheduleChunkTask(level, chunkX, chunkZ, () -> {
+- scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+- }, priority);
+- return;
+- }
+-
+- final int minLevel = 33 - (toStatus.ordinal() - 1);
+- final int radius = toStatus.ordinal() - 1;
+- final Long chunkReference = addTicket ? Long.valueOf(++chunkLoadCounter) : null;
+- final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+-
+- if (addTicket) {
+- level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+- }
+- level.chunkSource.runDistanceManagerUpdates();
+-
+- final Consumer<LevelChunk> loadCallback = (final LevelChunk chunk) -> {
+- try {
+- if (onComplete != null) {
+- onComplete.accept(chunk);
+- }
+- } catch (final ThreadDeath death) {
+- throw death;
+- } catch (final Throwable thr) {
+- LOGGER.error("Exception handling chunk load callback", thr);
+- SneakyThrow.sneaky(thr);
+- } finally {
+- if (addTicket) {
+- level.chunkSource.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, minLevel, chunkPos);
+- level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel, chunkReference);
+- }
+- }
+- };
+-
+- final ChunkHolder holder = level.chunkSource.chunkMap.getUpdatingChunkIfPresent(CoordinateUtils.getChunkKey(chunkX, chunkZ));
+-
+- if (holder == null || holder.getTicketLevel() > minLevel) {
+- loadCallback.accept(null);
+- return;
+- }
+-
+- final CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> tickingState;
+- switch (toStatus) {
+- case BORDER: {
+- tickingState = holder.getFullChunkFuture();
+- break;
+- }
+- case TICKING: {
+- tickingState = holder.getTickingChunkFuture();
+- break;
+- }
+- case ENTITY_TICKING: {
+- tickingState = holder.getEntityTickingChunkFuture();
+- break;
+- }
+- default: {
+- throw new IllegalStateException("Cannot reach here");
+- }
+- }
+-
+- if (tickingState.isDone()) {
+- loadCallback.accept(tickingState.join().left().orElse(null));
+- return;
+- }
+-
+- tickingState.whenCompleteAsync((final Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either, final Throwable thr) -> {
+- if (thr != null) {
+- loadCallback.accept(null);
+- return;
+- }
+- loadCallback.accept(either.left().orElse(null));
+- }, (final Runnable r) -> {
+- scheduleChunkTask(level, chunkX, chunkZ, r, PrioritisedExecutor.Priority.HIGHEST);
+- });
++ level.chunkTaskScheduler.scheduleTickingState(chunkX, chunkZ, toStatus, addTicket, priority, onComplete); // Paper - rewrite chunk system
+ }
+
+ public static List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
+- return new ArrayList<>(level.chunkSource.chunkMap.visibleChunkMap.values());
++ return level.chunkTaskScheduler.chunkHolderManager.getOldChunkHolders(); // Paper - rewrite chunk system
+ }
+
+ public static List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
+- return new ArrayList<>(level.chunkSource.chunkMap.updatingChunkMap.values());
++ return level.chunkTaskScheduler.chunkHolderManager.getOldChunkHolders(); // Paper - rewrite chunk system
+ }
+
+ public static int getVisibleChunkHolderCount(final ServerLevel level) {
+- return level.chunkSource.chunkMap.visibleChunkMap.size();
++ return level.chunkTaskScheduler.chunkHolderManager.size(); // Paper - rewrite chunk system
+ }
+
+ public static int getUpdatingChunkHolderCount(final ServerLevel level) {
+- return level.chunkSource.chunkMap.updatingChunkMap.size();
++ return level.chunkTaskScheduler.chunkHolderManager.size(); // Paper - rewrite chunk system
+ }
+
+ public static boolean hasAnyChunkHolders(final ServerLevel level) {
+@@ -269,23 +119,15 @@ public final class ChunkSystem {
+ }
+
+ public static int getSendViewDistance(final ServerPlayer player) {
+- return getLoadViewDistance(player);
++ return io.papermc.paper.chunk.PlayerChunkLoader.getSendViewDistance(player);
+ }
+
+ public static int getLoadViewDistance(final ServerPlayer player) {
+- final ServerLevel level = player.getLevel();
+- if (level == null) {
+- return Bukkit.getViewDistance() + 1;
+- }
+- return level.chunkSource.chunkMap.getEffectiveViewDistance() + 1;
++ return io.papermc.paper.chunk.PlayerChunkLoader.getLoadViewDistance(player);
+ }
+
+ public static int getTickViewDistance(final ServerPlayer player) {
+- final ServerLevel level = player.getLevel();
+- if (level == null) {
+- return Bukkit.getSimulationDistance();
+- }
+- return level.chunkSource.chunkMap.distanceManager.getSimulationDistance();
++ return io.papermc.paper.chunk.PlayerChunkLoader.getTickViewDistance(player);
+ }
+
+ private ChunkSystem() {
+diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
+index b310d51b7fe3e8cef0a450674725969fe1ce78a4..c9e169a6dca4dd8fe6e27a23deb410664a5d4466 100644
+--- a/src/main/java/net/minecraft/server/MCUtil.java
++++ b/src/main/java/net/minecraft/server/MCUtil.java
+@@ -1,15 +1,28 @@
+ package net.minecraft.server;
+
+ import com.google.common.util.concurrent.ThreadFactoryBuilder;
++import com.google.gson.JsonArray;
++import com.google.gson.JsonObject;
++import com.google.gson.internal.Streams;
++import com.google.gson.stream.JsonWriter;
++import com.mojang.datafixers.util.Either;
+ import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
+ import java.lang.ref.Cleaner;
++import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
+ import net.minecraft.core.BlockPos;
+ import net.minecraft.core.Direction;
++import net.minecraft.server.level.ChunkHolder;
++import net.minecraft.server.level.ChunkMap;
++import net.minecraft.server.level.DistanceManager;
+ import net.minecraft.server.level.ServerLevel;
++import net.minecraft.server.level.ServerPlayer;
++import net.minecraft.server.level.Ticket;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.level.ChunkPos;
+ import net.minecraft.world.level.ClipContext;
+ import net.minecraft.world.level.Level;
++import net.minecraft.world.level.chunk.ChunkAccess;
++import net.minecraft.world.level.chunk.ChunkStatus;
+ import org.apache.commons.lang.exception.ExceptionUtils;
+ import org.bukkit.Location;
+ import org.bukkit.block.BlockFace;
+@@ -19,8 +32,11 @@ import org.spigotmc.AsyncCatcher;
+
+ import javax.annotation.Nonnull;
+ import javax.annotation.Nullable;
++import java.io.*;
++import java.nio.charset.StandardCharsets;
+ import java.util.List;
+ import java.util.Queue;
++import java.util.Set;
+ import java.util.concurrent.CompletableFuture;
+ import java.util.concurrent.ExecutionException;
+ import java.util.concurrent.LinkedBlockingQueue;
+@@ -505,6 +521,100 @@ public final class MCUtil {
+ }
+ }
+
++ public static ChunkStatus getChunkStatus(ChunkHolder chunk) {
++ return chunk.getChunkHolderStatus();
++ }
++
++ public static void dumpChunks(File file, boolean watchdog) throws IOException {
++ file.getParentFile().mkdirs();
++ file.createNewFile();
++ ReferenceArrayList<org.bukkit.World> worlds = new ReferenceArrayList<>(org.bukkit.Bukkit.getWorlds());
++ ReferenceArrayList<org.bukkit.World> loadedWorlds = new ReferenceArrayList<>(worlds);
++ JsonObject data = new JsonObject();
++
++ data.addProperty("server-version", org.bukkit.Bukkit.getVersion());
++ data.addProperty("data-version", 1);
++
++ {
++ JsonArray players = new JsonArray();
++ data.add("all-players", players);
++ List<ServerPlayer> playerList = MinecraftServer.getServer().getPlayerList().players;
++ for (ServerPlayer player : playerList) {
++ JsonObject playerData = new JsonObject();
++ players.add(playerData);
++
++ Level playerWorld = player.getLevel();
++ org.bukkit.World craftWorld = playerWorld.getWorld();
++ Entity.RemovalReason removalReason = player.getRemovalReason();
++
++ playerData.addProperty("name", player.getScoreboardName());
++ playerData.addProperty("x", player.getX());
++ playerData.addProperty("y", player.getY());
++ playerData.addProperty("z", player.getZ());
++ playerData.addProperty("world", playerWorld == null ? "null world" : craftWorld.getName());
++ playerData.addProperty("removalReason", removalReason == null ? "null" : removalReason.name());
++
++ if (!worlds.contains(craftWorld)) {
++ worlds.add(craftWorld);
++ }
++ }
++ }
++
++ JsonArray chunkWaitInformation = new JsonArray();
++ data.add("chunk-wait-infos", chunkWaitInformation);
++
++ for (io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.ChunkInfo chunkInfo : io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.getChunkInfos()) {
++ chunkWaitInformation.add(chunkInfo.toString());
++ }
++
++ JsonArray worldsData = new JsonArray();
++
++ for (org.bukkit.World bukkitWorld : worlds) {
++ JsonObject worldData = new JsonObject();
++
++ ServerLevel world = ((org.bukkit.craftbukkit.CraftWorld)bukkitWorld).getHandle();
++ List<ServerPlayer> players = world.players;
++
++ worldData.addProperty("is-loaded", loadedWorlds.contains(bukkitWorld));
++ worldData.addProperty("name", world.getWorld().getName());
++ worldData.addProperty("view-distance", world.getChunkSource().chunkMap.playerChunkManager.getTargetNoTickViewDistance()); // Paper - replace chunk loader system
++ worldData.addProperty("tick-view-distance", world.getChunkSource().chunkMap.playerChunkManager.getTargetTickViewDistance()); // Paper - replace chunk loader system
++ worldData.addProperty("keep-spawn-loaded", world.keepSpawnInMemory);
++ worldData.addProperty("keep-spawn-loaded-range", world.paperConfig().spawn.keepSpawnLoadedRange * 16);
++
++ JsonArray playersData = new JsonArray();
++
++ for (ServerPlayer player : players) {
++ JsonObject playerData = new JsonObject();
++
++ playerData.addProperty("name", player.getScoreboardName());
++ playerData.addProperty("x", player.getX());
++ playerData.addProperty("y", player.getY());
++ playerData.addProperty("z", player.getZ());
++
++ playersData.add(playerData);
++ }
++
++ worldData.add("players", playersData);
++ worldData.add("chunk-data", watchdog ? world.chunkTaskScheduler.chunkHolderManager.getDebugJsonForWatchdog() : world.chunkTaskScheduler.chunkHolderManager.getDebugJson());
++ worldsData.add(worldData);
++ }
++
++ data.add("worlds", worldsData);
++
++ StringWriter stringWriter = new StringWriter();
++ JsonWriter jsonWriter = new JsonWriter(stringWriter);
++ jsonWriter.setIndent(" ");
++ jsonWriter.setLenient(false);
++ Streams.write(data, jsonWriter);
++
++ String fileData = stringWriter.toString();
++
++ try (PrintStream out = new PrintStream(new FileOutputStream(file), false, StandardCharsets.UTF_8)) {
++ out.print(fileData);
++ }
++ }
++
+ public static int getTicketLevelFor(net.minecraft.world.level.chunk.ChunkStatus status) {
+ return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + net.minecraft.world.level.chunk.ChunkStatus.getDistance(status);
+ }
+diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java
+index 4dd3af1416cbdad330365a19ad664079f3598c15..45db9f1b1d19319e7f92bd4e61be9ea9b06dd5e5 100644
+--- a/src/main/java/net/minecraft/server/Main.java
++++ b/src/main/java/net/minecraft/server/Main.java
+@@ -262,6 +262,7 @@ public class Main {
+
+ convertable_conversionsession.saveDataTag(iregistrycustom_dimension, savedata);
+ */
++ Class.forName(net.minecraft.world.entity.npc.VillagerTrades.class.getName());// Paper - load this sync so it won't fail later async
+ final DedicatedServer dedicatedserver = (DedicatedServer) MinecraftServer.spin((thread) -> {
+ DedicatedServer dedicatedserver1 = new DedicatedServer(optionset, config.get(), ops.get(), thread, convertable_conversionsession, resourcepackrepository, worldstem, dedicatedserversettings, DataFixers.getDataFixer(), services, LoggerChunkProgressListener::new);
+
+diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
+index 53be6189d3fa6a65a09996683913fbbf5133dcb7..317cd6f68c2368b2f70dfb809db3e418de040f05 100644
+--- a/src/main/java/net/minecraft/server/MinecraftServer.java
++++ b/src/main/java/net/minecraft/server/MinecraftServer.java
+@@ -287,7 +287,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+
+ public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
+ AtomicReference<S> atomicreference = new AtomicReference();
+- Thread thread = new Thread(() -> {
++ Thread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system
+ ((MinecraftServer) atomicreference.get()).runServer();
+ }, "Server thread");
+
+@@ -576,7 +576,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ this.forceDifficulty();
+ for (ServerLevel worldserver : this.getAllLevels()) {
+ this.prepareLevels(worldserver.getChunkSource().chunkMap.progressListener, worldserver);
+- worldserver.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API
++ //worldserver.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API // Paper - rewrite chunk system, not required to "tick" anything
+ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(worldserver.getWorld()));
+ }
+
+@@ -776,6 +776,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ public abstract boolean shouldRconBroadcast();
+
+ public boolean saveAllChunks(boolean suppressLogs, boolean flush, boolean force) {
++ // Paper start - rewrite chunk system - add close param
++ // This allows us to avoid double saving chunks by closing instead of saving then closing
++ return this.saveAllChunks(suppressLogs, flush, force, false);
++ }
++ public boolean saveAllChunks(boolean suppressLogs, boolean flush, boolean force, boolean close) {
++ // Paper end - rewrite chunk system - add close param
+ boolean flag3 = false;
+
+ for (Iterator iterator = this.getAllLevels().iterator(); iterator.hasNext(); flag3 = true) {
+@@ -784,8 +790,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ if (!suppressLogs) {
+ MinecraftServer.LOGGER.info("Saving chunks for level '{}'/{}", worldserver, worldserver.dimension().location());
+ }
+-
+- worldserver.save((ProgressListener) null, flush, worldserver.noSave && !force);
++ // Paper start - rewrite chunk system
++ worldserver.save((ProgressListener) null, flush, worldserver.noSave && !force, close);
++ if (flush) {
++ MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", worldserver.getChunkSource().chunkMap.getStorageName());
++ }
++ // Paper end - rewrite chunk system
+ }
+
+ // CraftBukkit start - moved to WorldServer.save
+@@ -804,7 +814,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ while (iterator1.hasNext()) {
+ ServerLevel worldserver2 = (ServerLevel) iterator1.next();
+
+- MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", worldserver2.getChunkSource().chunkMap.getStorageName());
++ //MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", worldserver2.getChunkSource().chunkMap.getStorageName()); // Paper - move up
+ }
+
+ MinecraftServer.LOGGER.info("ThreadedAnvilChunkStorage: All dimensions are saved");
+@@ -884,36 +894,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ }
+ }
+
+- while (this.levels.values().stream().anyMatch((worldserver1) -> {
+- return worldserver1.getChunkSource().chunkMap.hasWork();
+- })) {
+- this.nextTickTime = Util.getMillis() + 1L;
+- iterator = this.getAllLevels().iterator();
+-
+- while (iterator.hasNext()) {
+- worldserver = (ServerLevel) iterator.next();
+- worldserver.getChunkSource().removeTicketsOnClosing();
+- worldserver.getChunkSource().tick(() -> {
+- return true;
+- }, false);
+- }
+-
+- this.waitUntilNextTick();
+- }
+-
+- this.saveAllChunks(false, true, false);
+- iterator = this.getAllLevels().iterator();
+-
+- while (iterator.hasNext()) {
+- worldserver = (ServerLevel) iterator.next();
+- if (worldserver != null) {
+- try {
+- worldserver.close();
+- } catch (IOException ioexception) {
+- MinecraftServer.LOGGER.error("Exception closing the level", ioexception);
+- }
+- }
+- }
++ this.saveAllChunks(false, true, false, true); // Paper - rewrite chunk system - move closing into here
+
+ this.isSaving = false;
+ this.resources.close();
+@@ -932,7 +913,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ this.getProfileCache().save();
+ }
+ // Spigot end
+-
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.close(true); // Paper // Paper - rewrite chunk system
+ }
+
+ public String getLocalIp() {
+@@ -966,6 +947,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ }
+ // Spigot End
+
++ public static volatile RuntimeException chunkSystemCrash; // Paper - rewrite chunk system
++
+ protected void runServer() {
+ try {
+ if (!this.initServer()) {
+@@ -983,6 +966,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ Arrays.fill( recentTps, 20 );
+ long curTime, tickSection = Util.getMillis(), tickCount = 1;
+ while (this.running) {
++ // Paper start - rewrite chunk system
++ // guarantee that nothing can stop the server from halting if it can at least still tick
++ if (this.chunkSystemCrash != null) {
++ throw this.chunkSystemCrash;
++ }
++ // Paper end - rewrite chunk system
+ long i = (curTime = Util.getMillis()) - this.nextTickTime;
+
+ if (i > 5000L && this.nextTickTime - this.lastOverloadWarning >= 30000L) { // CraftBukkit
+@@ -1095,6 +1084,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ }
+
+ private boolean haveTime() {
++ // Paper start
++ if (this.forceTicks) {
++ return true;
++ }
++ // Paper end
+ // CraftBukkit start
+ if (isOversleep) return canOversleep();// Paper - because of our changes, this logic is broken
+ return this.forceTicks || this.runningTask() || Util.getMillis() < (this.mayHaveDelayedTasks ? this.delayedTasksMaxNextTickTime : this.nextTickTime);
+@@ -2215,7 +2209,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ // CraftBukkit start
+ @Override
+ public boolean isSameThread() {
+- return super.isSameThread() || this.isStopped(); // CraftBukkit - MC-142590
++ return io.papermc.paper.util.TickThread.isTickThread(); // Paper - rewrite chunk system
+ }
+
+ public boolean isDebugging() {
+diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+index c905602d23cdf3af1de7ab4419f11856b07da2ba..fa1ab9974859c75075f3090e36e0de58dda3e8e6 100644
+--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+@@ -406,7 +406,34 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+ return this.getProperties().allowNether;
+ }
+
++ static final java.util.concurrent.atomic.AtomicInteger ASYNC_DEBUG_CHUNKS_COUNT = new java.util.concurrent.atomic.AtomicInteger(); // Paper - rewrite chunk system
++
+ public void handleConsoleInput(String command, CommandSourceStack commandSource) {
++ // Paper start - rewrite chunk system
++ if (command.equalsIgnoreCase("paper debug chunks --async")) {
++ LOGGER.info("Scheduling async debug chunks");
++ Runnable run = () -> {
++ LOGGER.info("Async debug chunks executing");
++ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(false);
++ CommandSender sender = MinecraftServer.getServer().console;
++ java.io.File file = new java.io.File(new java.io.File(new java.io.File("."), "debug"),
++ "chunks-" + java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(java.time.LocalDateTime.now()) + ".txt");
++ sender.sendMessage(net.kyori.adventure.text.Component.text("Writing chunk information dump to " + file, net.kyori.adventure.text.format.NamedTextColor.GREEN));
++ try {
++ net.minecraft.server.MCUtil.dumpChunks(file, true);
++ sender.sendMessage(net.kyori.adventure.text.Component.text("Successfully written chunk information!", net.kyori.adventure.text.format.NamedTextColor.GREEN));
++ } catch (Throwable thr) {
++ MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
++ sender.sendMessage(net.kyori.adventure.text.Component.text("Failed to dump chunk information, see console", net.kyori.adventure.text.format.NamedTextColor.RED));
++ }
++ };
++ Thread t = new Thread(run);
++ t.setName("Async debug thread #" + ASYNC_DEBUG_CHUNKS_COUNT.getAndIncrement());
++ t.setDaemon(true);
++ t.start();
++ return;
++ }
++ // Paper end - rewrite chunk system
+ this.consoleInput.add(new ConsoleInput(command, commandSource));
+ }
+
+diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
+index 86c33f029ae56fcace51b69763202be9f8bc5f44..b550f302b1bb6ef92987c8c3431b94c3ba3a091d 100644
+--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
++++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
+@@ -50,17 +50,12 @@ public class ChunkHolder {
+ private static final List<ChunkStatus> CHUNK_STATUSES = ChunkStatus.getStatusList();
+ private static final ChunkHolder.FullChunkStatus[] FULL_CHUNK_STATUSES = ChunkHolder.FullChunkStatus.values();
+ private static final int BLOCKS_BEFORE_RESEND_FUDGE = 64;
+- private final AtomicReferenceArray<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> futures;
++ // Paper - rewrite chunk system
+ private final LevelHeightAccessor levelHeightAccessor;
+- private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> fullChunkFuture; private int fullChunkCreateCount; private volatile boolean isFullChunkReady; // Paper - cache chunk ticking stage
+- private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> tickingChunkFuture; private volatile boolean isTickingReady; // Paper - cache chunk ticking stage
+- private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> entityTickingChunkFuture; private volatile boolean isEntityTickingReady; // Paper - cache chunk ticking stage
+- public CompletableFuture<ChunkAccess> chunkToSave; // Paper - public
++ // Paper - rewrite chunk system
+ @Nullable
+ private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory;
+- public int oldTicketLevel;
+- private int ticketLevel;
+- private int queueLevel;
++ // Paper - rewrite chunk system
+ public final ChunkPos pos;
+ private boolean hasChangedSections;
+ private final ShortSet[] changedBlocksPerSection;
+@@ -69,11 +64,22 @@ public class ChunkHolder {
+ private final LevelLightEngine lightEngine;
+ private final ChunkHolder.LevelChangeListener onLevelChange;
+ public final ChunkHolder.PlayerProvider playerProvider;
+- private boolean wasAccessibleSinceLastSave;
++ // Paper - rewrite chunk system
+ private boolean resendLight;
+- private CompletableFuture<Void> pendingFullStateConfirmation;
++ // Paper - rewrite chunk system
+
+ private final ChunkMap chunkMap; // Paper
++ // Paper start - no-tick view distance
++ public final LevelChunk getSendingChunk() {
++ // it's important that we use getChunkAtIfLoadedImmediately to mirror the chunk sending logic used
++ // in Chunk's neighbour callback
++ LevelChunk ret = this.chunkMap.level.getChunkSource().getChunkAtIfLoadedImmediately(this.pos.x, this.pos.z);
++ if (ret != null && ret.areNeighboursLoaded(1)) {
++ return ret;
++ }
++ return null;
++ }
++ // Paper end - no-tick view distance
+
+ // Paper start
+ public void onChunkAdd() {
+@@ -85,148 +91,106 @@ public class ChunkHolder {
+ }
+ // Paper end
+
+- public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
+- this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
+- this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+- this.tickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+- this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+- this.chunkToSave = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
++ public final io.papermc.paper.chunk.system.scheduling.NewChunkHolder newChunkHolder; // Paper - rewrite chunk system
++
++ public ChunkHolder(ChunkPos pos, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.PlayerProvider playersWatchingChunkProvider, io.papermc.paper.chunk.system.scheduling.NewChunkHolder newChunkHolder) { // Paper - rewrite chunk system
++ this.newChunkHolder = newChunkHolder; // Paper - rewrite chunk system
+ this.chunkToSaveHistory = null;
+ this.blockChangedLightSectionFilter = new BitSet();
+ this.skyChangedLightSectionFilter = new BitSet();
+- this.pendingFullStateConfirmation = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
++ // Paper - rewrite chunk system
+ this.pos = pos;
+ this.levelHeightAccessor = world;
+ this.lightEngine = lightingProvider;
+- this.onLevelChange = levelUpdateListener;
++ this.onLevelChange = null; // Paper - rewrite chunk system
+ this.playerProvider = playersWatchingChunkProvider;
+- this.oldTicketLevel = ChunkMap.MAX_CHUNK_DISTANCE + 1;
+- this.ticketLevel = this.oldTicketLevel;
+- this.queueLevel = this.oldTicketLevel;
+- this.setTicketLevel(level);
++ // Paper - rewrite chunk system
+ this.changedBlocksPerSection = new ShortSet[world.getSectionsCount()];
+ this.chunkMap = (ChunkMap)playersWatchingChunkProvider; // Paper
+ }
+
+ // Paper start
+ public @Nullable ChunkAccess getAvailableChunkNow() {
+- // TODO can we just getStatusFuture(EMPTY)?
+- for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr);
+- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null);
+- if (either == null || either.left().isEmpty()) {
+- continue;
+- }
+- return either.left().get();
+- }
+- return null;
++ return this.newChunkHolder.getCurrentChunk(); // Paper - rewrite chunk system
+ }
+ // Paper end
+ // CraftBukkit start
+ public LevelChunk getFullChunkNow() {
+- // Note: We use the oldTicketLevel for isLoaded checks.
+- if (!ChunkHolder.getFullChunkStatus(this.oldTicketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) return null;
+- return this.getFullChunkNowUnchecked();
++ // Paper start - rewrite chunk system
++ ChunkAccess chunk = this.getAvailableChunkNow();
++ if (!this.isFullChunkReady() || !(chunk instanceof LevelChunk)) return null; // instanceof to avoid a race condition on off-main threads
++ return (LevelChunk)chunk;
++ // Paper end - rewrite chunk system
+ }
+
+ public LevelChunk getFullChunkNowUnchecked() {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> statusFuture = this.getFutureIfPresentUnchecked(ChunkStatus.FULL);
+- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = (Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>) statusFuture.getNow(null);
+- return (either == null) ? null : (LevelChunk) either.left().orElse(null);
++ // Paper start - rewrite chunk system
++ ChunkAccess chunk = this.getAvailableChunkNow();
++ return chunk instanceof LevelChunk ? (LevelChunk)chunk : null;
++ // Paper end - rewrite chunk system
+ }
+ // CraftBukkit end
+
+ public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getFutureIfPresentUnchecked(ChunkStatus leastStatus) {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(leastStatus.getIndex());
+-
+- return completablefuture == null ? ChunkHolder.UNLOADED_CHUNK_FUTURE : completablefuture;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getFutureIfPresent(ChunkStatus leastStatus) {
+- return ChunkHolder.getStatus(this.ticketLevel).isOrAfter(leastStatus) ? this.getFutureIfPresentUnchecked(leastStatus) : ChunkHolder.UNLOADED_CHUNK_FUTURE;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public final CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getTickingChunkFuture() { // Paper - final for inline
+- return this.tickingChunkFuture;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public final CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getEntityTickingChunkFuture() { // Paper - final for inline
+- return this.entityTickingChunkFuture;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public final CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getFullChunkFuture() { // Paper - final for inline
+- return this.fullChunkFuture;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ @Nullable
+ public final LevelChunk getTickingChunk() { // Paper - final for inline
+- CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getTickingChunkFuture();
+- Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = (Either) completablefuture.getNow(null); // CraftBukkit - decompile error
+-
+- return either == null ? null : (LevelChunk) either.left().orElse(null); // CraftBukkit - decompile error
++ // Paper start - rewrite chunk system
++ if (!this.isTickingReady()) {
++ return null;
++ }
++ return (LevelChunk)this.getAvailableChunkNow();
++ // Paper end - rewrite chunk system
+ }
+
+ @Nullable
+ public final LevelChunk getFullChunk() { // Paper - final for inline
+- CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getFullChunkFuture();
+- Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = (Either) completablefuture.getNow(null); // CraftBukkit - decompile error
+-
+- return either == null ? null : (LevelChunk) either.left().orElse(null); // CraftBukkit - decompile error
++ // Paper start - rewrite chunk system
++ if (!this.isFullChunkReady()) {
++ return null;
++ }
++ return (LevelChunk)this.getAvailableChunkNow();
++ // Paper end - rewrite chunk system
+ }
+
+ @Nullable
+ public ChunkStatus getLastAvailableStatus() {
+- for (int i = ChunkHolder.CHUNK_STATUSES.size() - 1; i >= 0; --i) {
+- ChunkStatus chunkstatus = (ChunkStatus) ChunkHolder.CHUNK_STATUSES.get(i);
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getFutureIfPresentUnchecked(chunkstatus);
+-
+- if (((Either) completablefuture.getNow(ChunkHolder.UNLOADED_CHUNK)).left().isPresent()) {
+- return chunkstatus;
+- }
+- }
+-
+- return null;
++ return this.newChunkHolder.getCurrentGenStatus(); // Paper - rewrite chunk system
+ }
+
+ // Paper start
+ public ChunkStatus getChunkHolderStatus() {
+- for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getParent(); curr != next; curr = next, next = next.getParent()) {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> future = this.getFutureIfPresentUnchecked(curr);
+- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = future.getNow(null);
+- if (either == null || !either.left().isPresent()) {
+- continue;
+- }
+- return curr;
+- }
+-
+- return null;
++ return this.newChunkHolder.getCurrentGenStatus(); // Paper - rewrite chunk system
+ }
+ // Paper end
+
+ @Nullable
+ public ChunkAccess getLastAvailable() {
+- for (int i = ChunkHolder.CHUNK_STATUSES.size() - 1; i >= 0; --i) {
+- ChunkStatus chunkstatus = (ChunkStatus) ChunkHolder.CHUNK_STATUSES.get(i);
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getFutureIfPresentUnchecked(chunkstatus);
+-
+- if (!completablefuture.isCompletedExceptionally()) {
+- Optional<ChunkAccess> optional = ((Either) completablefuture.getNow(ChunkHolder.UNLOADED_CHUNK)).left();
+-
+- if (optional.isPresent()) {
+- return (ChunkAccess) optional.get();
+- }
+- }
+- }
+-
+- return null;
++ return this.newChunkHolder.getCurrentChunk(); // Paper - rewrite chunk system
+ }
+
+- public final CompletableFuture<ChunkAccess> getChunkToSave() { // Paper - final for inline
+- return this.chunkToSave;
+- }
++ // Paper - rewrite chunk system
+
+ public void blockChanged(BlockPos pos) {
+- LevelChunk chunk = this.getTickingChunk();
++ LevelChunk chunk = this.getSendingChunk(); // Paper - no-tick view distance
+
+ if (chunk != null) {
+ int i = this.levelHeightAccessor.getSectionIndex(pos.getY());
+@@ -242,14 +206,15 @@ public class ChunkHolder {
+ }
+
+ public void sectionLightChanged(LightLayer lightType, int y) {
+- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = (Either) this.getFutureIfPresent(ChunkStatus.FEATURES).getNow(null); // CraftBukkit - decompile error
++ // Paper start - no-tick view distance
+
+- if (either != null) {
+- ChunkAccess ichunkaccess = (ChunkAccess) either.left().orElse(null); // CraftBukkit - decompile error
++ if (true) {
++ ChunkAccess ichunkaccess = this.getAvailableChunkNow();
+
+ if (ichunkaccess != null) {
+ ichunkaccess.setUnsaved(true);
+- LevelChunk chunk = this.getTickingChunk();
++ LevelChunk chunk = this.getSendingChunk();
++ // Paper end - no-tick view distance
+
+ if (chunk != null) {
+ int j = this.lightEngine.getMinLightSection();
+@@ -340,66 +305,34 @@ public class ChunkHolder {
+ }
+
+ public void broadcast(Packet<?> packet, boolean onlyOnWatchDistanceEdge) {
+- this.playerProvider.getPlayers(this.pos, onlyOnWatchDistanceEdge).forEach((entityplayer) -> {
+- entityplayer.connection.send(packet);
+- });
+- }
+-
+- public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getOrScheduleFuture(ChunkStatus targetStatus, ChunkMap chunkStorage) {
+- int i = targetStatus.getIndex();
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(i);
+-
+- if (completablefuture != null) {
+- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = (Either) completablefuture.getNow(ChunkHolder.NOT_DONE_YET);
+-
+- if (either == null) {
+- String s = "value in future for status: " + targetStatus + " was incorrectly set to null at chunk: " + this.pos;
++ // Paper start - per player view distance
++ // there can be potential desync with player's last mapped section and the view distance map, so use the
++ // view distance map here.
++ com.destroystokyo.paper.util.misc.PlayerAreaMap viewDistanceMap = this.chunkMap.playerChunkManager.broadcastMap; // Paper - replace old player chunk manager
++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> players = viewDistanceMap.getObjectsInRange(this.pos);
++ if (players == null) {
++ return;
++ }
+
+- throw chunkStorage.debugFuturesAndCreateReportedException(new IllegalStateException("null value previously set for chunk status"), s);
++ Object[] backingSet = players.getBackingSet();
++ for (int i = 0, len = backingSet.length; i < len; ++i) {
++ Object temp = backingSet[i];
++ if (!(temp instanceof ServerPlayer)) {
++ continue;
+ }
+-
+- if (either == ChunkHolder.NOT_DONE_YET || either.right().isEmpty()) {
+- return completablefuture;
++ ServerPlayer player = (ServerPlayer)temp;
++ if (!this.chunkMap.playerChunkManager.isChunkSent(player, this.pos.x, this.pos.z, onlyOnWatchDistanceEdge)) {
++ continue;
+ }
++ player.connection.send(packet);
+ }
+-
+- if (ChunkHolder.getStatus(this.ticketLevel).isOrAfter(targetStatus)) {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture1 = chunkStorage.schedule(this, targetStatus);
+-
+- this.updateChunkToSave(completablefuture1, "schedule " + targetStatus);
+- this.futures.set(i, completablefuture1);
+- return completablefuture1;
+- } else {
+- return completablefuture == null ? ChunkHolder.UNLOADED_CHUNK_FUTURE : completablefuture;
+- }
++ // Paper end - per player view distance
+ }
+
+- protected void addSaveDependency(String thenDesc, CompletableFuture<?> then) {
+- if (this.chunkToSaveHistory != null) {
+- this.chunkToSaveHistory.push(new ChunkHolder.ChunkSaveDebug(Thread.currentThread(), then, thenDesc));
+- }
+-
+- this.chunkToSave = this.chunkToSave.thenCombine(then, (ichunkaccess, object) -> {
+- return ichunkaccess;
+- });
+- }
+-
+- private void updateChunkToSave(CompletableFuture<? extends Either<? extends ChunkAccess, ChunkHolder.ChunkLoadingFailure>> then, String thenDesc) {
+- if (this.chunkToSaveHistory != null) {
+- this.chunkToSaveHistory.push(new ChunkHolder.ChunkSaveDebug(Thread.currentThread(), then, thenDesc));
+- }
+-
+- this.chunkToSave = this.chunkToSave.thenCombine(then, (ichunkaccess, either) -> {
+- return (ChunkAccess) either.map((ichunkaccess1) -> {
+- return ichunkaccess1;
+- }, (playerchunk_failure) -> {
+- return ichunkaccess;
+- });
+- });
+- }
++ // Paper - rewrite chunk system
+
+ public ChunkHolder.FullChunkStatus getFullStatus() {
+- return ChunkHolder.getFullChunkStatus(this.ticketLevel);
++ return this.newChunkHolder.getChunkStatus(); // Paper - rewrite chunk system
+ }
+
+ public final ChunkPos getPos() { // Paper - final for inline
+@@ -407,207 +340,10 @@ public class ChunkHolder {
+ }
+
+ public final int getTicketLevel() { // Paper - final for inline
+- return this.ticketLevel;
+- }
+-
+- public int getQueueLevel() {
+- return this.queueLevel;
+- }
+-
+- private void setQueueLevel(int level) {
+- this.queueLevel = level;
+- }
+-
+- public void setTicketLevel(int level) {
+- this.ticketLevel = level;
+- }
+-
+- private void scheduleFullChunkPromotion(ChunkMap playerchunkmap, CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completablefuture, Executor executor, ChunkHolder.FullChunkStatus playerchunk_state) {
+- this.pendingFullStateConfirmation.cancel(false);
+- CompletableFuture<Void> completablefuture1 = new CompletableFuture();
+-
+- completablefuture1.thenRunAsync(() -> {
+- playerchunkmap.onFullChunkStatusChange(this.pos, playerchunk_state);
+- }, executor);
+- this.pendingFullStateConfirmation = completablefuture1;
+- completablefuture.thenAccept((either) -> {
+- either.ifLeft((chunk) -> {
+- completablefuture1.complete(null); // CraftBukkit - decompile error
+- });
+- });
++ return this.newChunkHolder.getTicketLevel(); // Paper - rewrite chunk system
+ }
+
+- private void demoteFullChunk(ChunkMap playerchunkmap, ChunkHolder.FullChunkStatus playerchunk_state) {
+- this.pendingFullStateConfirmation.cancel(false);
+- playerchunkmap.onFullChunkStatusChange(this.pos, playerchunk_state);
+- }
+-
+- protected void updateFutures(ChunkMap chunkStorage, Executor executor) {
+- ChunkStatus chunkstatus = ChunkHolder.getStatus(this.oldTicketLevel);
+- ChunkStatus chunkstatus1 = ChunkHolder.getStatus(this.ticketLevel);
+- boolean flag = this.oldTicketLevel <= ChunkMap.MAX_CHUNK_DISTANCE;
+- boolean flag1 = this.ticketLevel <= ChunkMap.MAX_CHUNK_DISTANCE;
+- ChunkHolder.FullChunkStatus playerchunk_state = ChunkHolder.getFullChunkStatus(this.oldTicketLevel);
+- ChunkHolder.FullChunkStatus playerchunk_state1 = ChunkHolder.getFullChunkStatus(this.ticketLevel);
+- // CraftBukkit start
+- // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins.
+- if (playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
+- this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+- LevelChunk chunk = (LevelChunk)either.left().orElse(null);
+- if (chunk != null) {
+- chunkStorage.callbackExecutor.execute(() -> {
+- // Minecraft will apply the chunks tick lists to the world once the chunk got loaded, and then store the tick
+- // lists again inside the chunk once the chunk becomes inaccessible and set the chunk's needsSaving flag.
+- // These actions may however happen deferred, so we manually set the needsSaving flag already here.
+- chunk.setUnsaved(true);
+- chunk.unloadCallback();
+- });
+- }
+- }).exceptionally((throwable) -> {
+- // ensure exceptions are printed, by default this is not the case
+- MinecraftServer.LOGGER.error("Failed to schedule unload callback for chunk " + ChunkHolder.this.pos, throwable);
+- return null;
+- });
+-
+- // Run callback right away if the future was already done
+- chunkStorage.callbackExecutor.run();
+- }
+- // CraftBukkit end
+-
+- if (flag) {
+- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = Either.right(new ChunkHolder.ChunkLoadingFailure() {
+- public String toString() {
+- return "Unloaded ticket level " + ChunkHolder.this.pos;
+- }
+- });
+-
+- for (int i = flag1 ? chunkstatus1.getIndex() + 1 : 0; i <= chunkstatus.getIndex(); ++i) {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(i);
+-
+- if (completablefuture == null) {
+- this.futures.set(i, CompletableFuture.completedFuture(either));
+- }
+- }
+- }
+-
+- boolean flag2 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
+- boolean flag3 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
+-
+- this.wasAccessibleSinceLastSave |= flag3;
+- if (!flag2 && flag3) {
+- int expectCreateCount = ++this.fullChunkCreateCount; // Paper
+- this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this);
+- this.scheduleFullChunkPromotion(chunkStorage, this.fullChunkFuture, executor, ChunkHolder.FullChunkStatus.BORDER);
+- // Paper start - cache ticking ready status
+- this.fullChunkFuture.thenAccept(either -> {
+- final Optional<LevelChunk> left = either.left();
+- if (left.isPresent() && ChunkHolder.this.fullChunkCreateCount == expectCreateCount) {
+- LevelChunk fullChunk = either.left().get();
+- ChunkHolder.this.isFullChunkReady = true;
+- net.minecraft.server.ChunkSystem.onChunkBorder(fullChunk, this);
+- }
+- });
+- this.updateChunkToSave(this.fullChunkFuture, "full");
+- }
+-
+- if (flag2 && !flag3) {
+- // Paper start
+- if (this.isFullChunkReady) {
+- net.minecraft.server.ChunkSystem.onChunkNotBorder(this.fullChunkFuture.join().left().get(), this); // Paper
+- }
+- // Paper end
+- this.fullChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK);
+- this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+- ++this.fullChunkCreateCount; // Paper - cache ticking ready status
+- this.isFullChunkReady = false; // Paper - cache ticking ready status
+- }
+-
+- boolean flag4 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
+- boolean flag5 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.TICKING);
+-
+- if (!flag4 && flag5) {
+- this.tickingChunkFuture = chunkStorage.prepareTickingChunk(this);
+- this.scheduleFullChunkPromotion(chunkStorage, this.tickingChunkFuture, executor, ChunkHolder.FullChunkStatus.TICKING);
+- // Paper start - cache ticking ready status
+- this.tickingChunkFuture.thenAccept(either -> {
+- either.ifLeft(chunk -> {
+- // note: Here is a very good place to add callbacks to logic waiting on this.
+- ChunkHolder.this.isTickingReady = true;
+- net.minecraft.server.ChunkSystem.onChunkTicking(chunk, this);
+- });
+- });
+- // Paper end
+- this.updateChunkToSave(this.tickingChunkFuture, "ticking");
+- }
+-
+- if (flag4 && !flag5) {
+- // Paper start
+- if (this.isTickingReady) {
+- net.minecraft.server.ChunkSystem.onChunkNotTicking(this.tickingChunkFuture.join().left().get(), this); // Paper
+- }
+- // Paper end
+- this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isTickingReady = false; // Paper - cache chunk ticking stage
+- this.tickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+- }
+-
+- boolean flag6 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING);
+- boolean flag7 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.ENTITY_TICKING);
+-
+- if (!flag6 && flag7) {
+- if (this.entityTickingChunkFuture != ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE) {
+- throw (IllegalStateException) Util.pauseInIde(new IllegalStateException());
+- }
+-
+- this.entityTickingChunkFuture = chunkStorage.prepareEntityTickingChunk(this.pos);
+- this.scheduleFullChunkPromotion(chunkStorage, this.entityTickingChunkFuture, executor, ChunkHolder.FullChunkStatus.ENTITY_TICKING);
+- // Paper start - cache ticking ready status
+- this.entityTickingChunkFuture.thenAccept(either -> {
+- either.ifLeft(chunk -> {
+- ChunkHolder.this.isEntityTickingReady = true;
+- net.minecraft.server.ChunkSystem.onChunkEntityTicking(chunk, this);
+- });
+- });
+- // Paper end
+- this.updateChunkToSave(this.entityTickingChunkFuture, "entity ticking");
+- }
+-
+- if (flag6 && !flag7) {
+- // Paper start
+- if (this.isEntityTickingReady) {
+- net.minecraft.server.ChunkSystem.onChunkNotEntityTicking(this.entityTickingChunkFuture.join().left().get(), this);
+- }
+- // Paper end
+- this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); this.isEntityTickingReady = false; // Paper - cache chunk ticking stage
+- this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+- }
+-
+- if (!playerchunk_state1.isOrAfter(playerchunk_state)) {
+- this.demoteFullChunk(chunkStorage, playerchunk_state1);
+- }
+-
+- this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel);
+- this.oldTicketLevel = this.ticketLevel;
+- // CraftBukkit start
+- // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins.
+- if (!playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER)) {
+- this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+- LevelChunk chunk = (LevelChunk)either.left().orElse(null);
+- if (chunk != null) {
+- chunkStorage.callbackExecutor.execute(() -> {
+- chunk.loadCallback();
+- });
+- }
+- }).exceptionally((throwable) -> {
+- // ensure exceptions are printed, by default this is not the case
+- MinecraftServer.LOGGER.error("Failed to schedule load callback for chunk " + ChunkHolder.this.pos, throwable);
+- return null;
+- });
+-
+- // Run callback right away if the future was already done
+- chunkStorage.callbackExecutor.run();
+- }
+- // CraftBukkit end
+- }
++ // Paper - rewrite chunk system
+
+ public static ChunkStatus getStatus(int level) {
+ return level < 33 ? ChunkStatus.FULL : ChunkStatus.getStatusAroundFullChunk(level - 33);
+@@ -617,38 +353,14 @@ public class ChunkHolder {
+ return ChunkHolder.FULL_CHUNK_STATUSES[Mth.clamp(33 - distance + 1, (int) 0, ChunkHolder.FULL_CHUNK_STATUSES.length - 1)];
+ }
+
+- public boolean wasAccessibleSinceLastSave() {
+- return this.wasAccessibleSinceLastSave;
+- }
+-
+- public void refreshAccessibility() {
+- this.wasAccessibleSinceLastSave = ChunkHolder.getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
+- }
++ // Paper - rewrite chunk system
+
+ public void replaceProtoChunk(ImposterProtoChunk chunk) {
+- for (int i = 0; i < this.futures.length(); ++i) {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(i);
+-
+- if (completablefuture != null) {
+- Optional<ChunkAccess> optional = ((Either) completablefuture.getNow(ChunkHolder.UNLOADED_CHUNK)).left();
+-
+- if (!optional.isEmpty() && optional.get() instanceof ProtoChunk) {
+- this.futures.set(i, CompletableFuture.completedFuture(Either.left(chunk)));
+- }
+- }
+- }
+-
+- this.updateChunkToSave(CompletableFuture.completedFuture(Either.left(chunk.getWrapped())), "replaceProto");
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public List<Pair<ChunkStatus, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>>> getAllFutures() {
+- List<Pair<ChunkStatus, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>>> list = new ArrayList();
+-
+- for (int i = 0; i < ChunkHolder.CHUNK_STATUSES.size(); ++i) {
+- list.add(Pair.of((ChunkStatus) ChunkHolder.CHUNK_STATUSES.get(i), (CompletableFuture) this.futures.get(i)));
+- }
+-
+- return list;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ @FunctionalInterface
+@@ -697,15 +409,15 @@ public class ChunkHolder {
+
+ // Paper start
+ public final boolean isEntityTickingReady() {
+- return this.isEntityTickingReady;
++ return this.newChunkHolder.isEntityTickingReady(); // Paper - rewrite chunk system
+ }
+
+ public final boolean isTickingReady() {
+- return this.isTickingReady;
++ return this.newChunkHolder.isTickingReady(); // Paper - rewrite chunk system
+ }
+
+ public final boolean isFullChunkReady() {
+- return this.isFullChunkReady;
++ return this.newChunkHolder.isFullChunkReady(); // Paper - rewrite chunk system
+ }
+ // Paper end
+ }
+diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
+index cbd4e749574c55c6e52f42b62dd6da8cfcca97f9..eda47a2c2a26fcf3434af140e1436395b2506cb0 100644
+--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
+@@ -122,10 +122,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ public static final int MAX_VIEW_DISTANCE = 33;
+ public static final int MAX_CHUNK_DISTANCE = 33 + ChunkStatus.maxDistance();
+ public static final int FORCED_TICKET_LEVEL = 31;
+- public final Long2ObjectLinkedOpenHashMap<ChunkHolder> updatingChunkMap = new Long2ObjectLinkedOpenHashMap();
+- public volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
+- private final Long2ObjectLinkedOpenHashMap<ChunkHolder> pendingUnloads;
+- public final LongSet entitiesInLevel;
++ // Paper - rewrite chunk system
+ public final ServerLevel level;
+ private final ThreadedLevelLightEngine lightEngine;
+ public final BlockableEventLoop<Runnable> mainThreadExecutor; // Paper - public
+@@ -133,16 +130,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ private RandomState randomState;
+ public final Supplier<DimensionDataStorage> overworldDataStorage;
+ private final PoiManager poiManager;
+- public final LongSet toDrop;
++ // Paper - rewrite chunk system
+ private boolean modified;
+- private final ChunkTaskPriorityQueueSorter queueSorter;
+- private final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> worldgenMailbox;
+- public final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mainThreadMailbox;
++ // Paper - rewrite chunk system
+ public final ChunkProgressListener progressListener;
+ private final ChunkStatusUpdateListener chunkStatusListener;
+ public final ChunkMap.ChunkDistanceManager distanceManager;
+ private final AtomicInteger tickingGenerated;
+- private final StructureTemplateManager structureTemplateManager;
++ public final StructureTemplateManager structureTemplateManager; // Paper - rewrite chunk system
+ private final String storageName;
+ private final PlayerMap playerMap;
+ public final Int2ObjectMap<ChunkMap.TrackedEntity> entityMap;
+@@ -151,37 +146,21 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ private final Queue<Runnable> unloadQueue;
+ int viewDistance;
+
+- // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
+- public final CallbackExecutor callbackExecutor = new CallbackExecutor();
+- public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable {
+-
+- private final java.util.Queue<Runnable> queue = new java.util.ArrayDeque<>();
+-
+- @Override
+- public void execute(Runnable runnable) {
+- this.queue.add(runnable);
+- }
+-
+- @Override
+- public void run() {
+- Runnable task;
+- while ((task = this.queue.poll()) != null) {
+- task.run();
+- }
+- }
+- };
+- // CraftBukkit end
++ // Paper - rewrite chunk system
+
+ // Paper start - distance maps
+ private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
++ public final io.papermc.paper.chunk.PlayerChunkLoader playerChunkManager = new io.papermc.paper.chunk.PlayerChunkLoader(this, this.pooledLinkedPlayerHashSets); // Paper - replace chunk loader
+
+ void addPlayerToDistanceMaps(ServerPlayer player) {
++ this.playerChunkManager.addPlayer(player); // Paper - replace chunk loader
+ int chunkX = MCUtil.getChunkCoordinate(player.getX());
+ int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
+ // Note: players need to be explicitly added to distance maps before they can be updated
+ }
+
+ void removePlayerFromDistanceMaps(ServerPlayer player) {
++ this.playerChunkManager.removePlayer(player); // Paper - replace chunk loader
+
+ }
+
+@@ -189,6 +168,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ int chunkX = MCUtil.getChunkCoordinate(player.getX());
+ int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
+ // Note: players need to be explicitly added to distance maps before they can be updated
++ this.playerChunkManager.updatePlayer(player); // Paper - replace chunk loader
+ }
+ // Paper end
+ // Paper start
+@@ -218,16 +198,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+ public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) {
+- return this.pendingUnloads.get(io.papermc.paper.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
++ return null; // Paper - rewrite chunk system
+ }
+ // Paper end
+
+ public ChunkMap(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor executor, BlockableEventLoop<Runnable> mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory, int viewDistance, boolean dsync) {
+ super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
+- this.visibleChunkMap = this.updatingChunkMap.clone();
+- this.pendingUnloads = new Long2ObjectLinkedOpenHashMap();
+- this.entitiesInLevel = new LongOpenHashSet();
+- this.toDrop = new LongOpenHashSet();
++ // Paper - rewrite chunk system
+ this.tickingGenerated = new AtomicInteger();
+ this.playerMap = new PlayerMap();
+ this.entityMap = new Int2ObjectOpenHashMap();
+@@ -254,19 +231,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+ this.mainThreadExecutor = mainThreadExecutor;
+- ProcessorMailbox<Runnable> threadedmailbox = ProcessorMailbox.create(executor, "worldgen");
++ // Paper - rewrite chunk system
+
+ Objects.requireNonNull(mainThreadExecutor);
+- ProcessorHandle<Runnable> mailbox = ProcessorHandle.of("main", mainThreadExecutor::tell);
++ // Paper - rewrite chunk system
+
+ this.progressListener = worldGenerationProgressListener;
+ this.chunkStatusListener = chunkStatusChangeListener;
+- ProcessorMailbox<Runnable> threadedmailbox1 = ProcessorMailbox.create(executor, "light");
++ // Paper - rewrite chunk system
+
+- this.queueSorter = new ChunkTaskPriorityQueueSorter(ImmutableList.of(threadedmailbox, mailbox, threadedmailbox1), executor, Integer.MAX_VALUE);
+- this.worldgenMailbox = this.queueSorter.getProcessor(threadedmailbox, false);
+- this.mainThreadMailbox = this.queueSorter.getProcessor(mailbox, false);
+- this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), threadedmailbox1, this.queueSorter.getProcessor(threadedmailbox1, false));
++ // Paper - rewrite chunk system
++ this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), null, null); // Paper - rewrite chunk system
+ this.distanceManager = new ChunkMap.ChunkDistanceManager(executor, mainThreadExecutor);
+ this.overworldDataStorage = persistentStateManagerFactory;
+ this.poiManager = new PoiManager(path.resolve("poi"), dataFixer, dsync, world.registryAccess(), world);
+@@ -327,20 +302,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+
+ @Nullable
+ public ChunkHolder getUpdatingChunkIfPresent(long pos) {
+- return (ChunkHolder) this.updatingChunkMap.get(pos);
++ // Paper start - rewrite chunk system
++ io.papermc.paper.chunk.system.scheduling.NewChunkHolder holder = this.level.chunkTaskScheduler.chunkHolderManager.getChunkHolder(pos);
++ return holder == null ? null : holder.vanillaChunkHolder;
++ // Paper end - rewrite chunk system
+ }
+
+ @Nullable
+ public ChunkHolder getVisibleChunkIfPresent(long pos) {
+- return (ChunkHolder) this.visibleChunkMap.get(pos);
++ // Paper start - rewrite chunk system
++ io.papermc.paper.chunk.system.scheduling.NewChunkHolder holder = this.level.chunkTaskScheduler.chunkHolderManager.getChunkHolder(pos);
++ return holder == null ? null : holder.vanillaChunkHolder;
++ // Paper end - rewrite chunk system
+ }
+
+ protected IntSupplier getChunkQueueLevel(long pos) {
+- return () -> {
+- ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos);
+-
+- return playerchunk == null ? ChunkTaskPriorityQueue.PRIORITY_LEVEL_COUNT - 1 : Math.min(playerchunk.getQueueLevel(), ChunkTaskPriorityQueue.PRIORITY_LEVEL_COUNT - 1);
+- };
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public String getChunkDebugData(ChunkPos chunkPos) {
+@@ -377,75 +354,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ // Paper end
+
+ private CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> getChunkRangeFuture(ChunkPos centerChunk, int margin, IntFunction<ChunkStatus> distanceToStatus) {
+- List<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> list = new ArrayList();
+- List<ChunkHolder> list1 = new ArrayList();
+- int j = centerChunk.x;
+- int k = centerChunk.z;
+-
+- for (int l = -margin; l <= margin; ++l) {
+- for (int i1 = -margin; i1 <= margin; ++i1) {
+- int j1 = Math.max(Math.abs(i1), Math.abs(l));
+- final ChunkPos chunkcoordintpair1 = new ChunkPos(j + i1, k + l);
+- long k1 = chunkcoordintpair1.toLong();
+- ChunkHolder playerchunk = this.getUpdatingChunkIfPresent(k1);
+-
+- if (playerchunk == null) {
+- return CompletableFuture.completedFuture(Either.right(new ChunkHolder.ChunkLoadingFailure() {
+- public String toString() {
+- return "Unloaded " + chunkcoordintpair1;
+- }
+- }));
+- }
+-
+- ChunkStatus chunkstatus = (ChunkStatus) distanceToStatus.apply(j1);
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = playerchunk.getOrScheduleFuture(chunkstatus, this);
+-
+- list1.add(playerchunk);
+- list.add(completablefuture);
+- }
+- }
+-
+- CompletableFuture<List<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> completablefuture1 = Util.sequence(list);
+- CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> completablefuture2 = completablefuture1.thenApply((list2) -> {
+- List<ChunkAccess> list3 = Lists.newArrayList();
+- // CraftBukkit start - decompile error
+- int cnt = 0;
+-
+- for (Iterator iterator = list2.iterator(); iterator.hasNext(); ++cnt) {
+- final int l1 = cnt;
+- // CraftBukkit end
+- final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = (Either) iterator.next();
+-
+- if (either == null) {
+- throw this.debugFuturesAndCreateReportedException(new IllegalStateException("At least one of the chunk futures were null"), "n/a");
+- }
+-
+- Optional<ChunkAccess> optional = either.left();
+-
+- if (!optional.isPresent()) {
+- return Either.right(new ChunkHolder.ChunkLoadingFailure() {
+- public String toString() {
+- ChunkPos chunkcoordintpair2 = new ChunkPos(j + l1 % (margin * 2 + 1), k + l1 / (margin * 2 + 1));
+-
+- return "Unloaded " + chunkcoordintpair2 + " " + either.right().get();
+- }
+- });
+- }
+-
+- list3.add((ChunkAccess) optional.get());
+- }
+-
+- return Either.left(list3);
+- });
+- Iterator iterator = list1.iterator();
+-
+- while (iterator.hasNext()) {
+- ChunkHolder playerchunk1 = (ChunkHolder) iterator.next();
+-
+- playerchunk1.addSaveDependency("getChunkRangeFuture " + centerChunk + " " + margin, completablefuture2);
+- }
+-
+- return completablefuture2;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public ReportedException debugFuturesAndCreateReportedException(IllegalStateException exception, String details) {
+@@ -475,261 +384,72 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+ public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareEntityTickingChunk(ChunkPos pos) {
+- return this.getChunkRangeFuture(pos, 2, (i) -> {
+- return ChunkStatus.FULL;
+- }).thenApplyAsync((either) -> {
+- return either.mapLeft((list) -> {
+- return (LevelChunk) list.get(list.size() / 2);
+- });
+- }, this.mainThreadExecutor);
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ @Nullable
+ ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k) {
+- if (k > ChunkMap.MAX_CHUNK_DISTANCE && level > ChunkMap.MAX_CHUNK_DISTANCE) {
+- return holder;
+- } else {
+- if (holder != null) {
+- holder.setTicketLevel(level);
+- }
+-
+- if (holder != null) {
+- if (level > ChunkMap.MAX_CHUNK_DISTANCE) {
+- this.toDrop.add(pos);
+- } else {
+- this.toDrop.remove(pos);
+- }
+- }
+-
+- if (level <= ChunkMap.MAX_CHUNK_DISTANCE && holder == null) {
+- holder = (ChunkHolder) this.pendingUnloads.remove(pos);
+- if (holder != null) {
+- holder.setTicketLevel(level);
+- } else {
+- holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
+- // Paper start
+- net.minecraft.server.ChunkSystem.onChunkHolderCreate(this.level, holder);
+- // Paper end
+- }
+-
+- // Paper start
+- holder.onChunkAdd();
+- // Paper end
+- this.updatingChunkMap.put(pos, holder);
+- this.modified = true;
+- }
+-
+- return holder;
+- }
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ @Override
+ public void close() throws IOException {
+- try {
+- this.queueSorter.close();
+- this.poiManager.close();
+- } finally {
+- super.close();
+- }
++ throw new UnsupportedOperationException("Use ServerChunkCache#close"); // Paper - rewrite chunk system
++ }
+
++ // Paper start - rewrite chunk system
++ protected void saveIncrementally() {
++ this.level.chunkTaskScheduler.chunkHolderManager.autoSave(); // Paper - rewrite chunk system
+ }
++ // Paper end - - rewrite chunk system
+
+ protected void saveAllChunks(boolean flush) {
+- if (flush) {
+- List<ChunkHolder> list = (List) net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).stream().filter(ChunkHolder::wasAccessibleSinceLastSave).peek(ChunkHolder::refreshAccessibility).collect(Collectors.toList()); // Paper
+- MutableBoolean mutableboolean = new MutableBoolean();
+-
+- do {
+- mutableboolean.setFalse();
+- list.stream().map((playerchunk) -> {
+- CompletableFuture completablefuture;
+-
+- do {
+- completablefuture = playerchunk.getChunkToSave();
+- BlockableEventLoop iasynctaskhandler = this.mainThreadExecutor;
+-
+- Objects.requireNonNull(completablefuture);
+- iasynctaskhandler.managedBlock(completablefuture::isDone);
+- } while (completablefuture != playerchunk.getChunkToSave());
+-
+- return (ChunkAccess) completablefuture.join();
+- }).filter((ichunkaccess) -> {
+- return ichunkaccess instanceof ImposterProtoChunk || ichunkaccess instanceof LevelChunk;
+- }).filter(this::save).forEach((ichunkaccess) -> {
+- mutableboolean.setTrue();
+- });
+- } while (mutableboolean.isTrue());
+-
+- this.processUnloads(() -> {
+- return true;
+- });
+- this.flushWorker();
+- } else {
+- net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).forEach(this::saveChunkIfNeeded);
+- }
+-
++ this.level.chunkTaskScheduler.chunkHolderManager.saveAllChunks(flush, false, false); // Paper - rewrite chunk system
+ }
+
+ protected void tick(BooleanSupplier shouldKeepTicking) {
+ ProfilerFiller gameprofilerfiller = this.level.getProfiler();
+
++ try (Timing ignored = this.level.timings.poiUnload.startTiming()) { // Paper
+ gameprofilerfiller.push("poi");
+ this.poiManager.tick(shouldKeepTicking);
++ } // Paper
+ gameprofilerfiller.popPush("chunk_unload");
+ if (!this.level.noSave()) {
++ try (Timing ignored = this.level.timings.chunkUnload.startTiming()) { // Paper
+ this.processUnloads(shouldKeepTicking);
++ } // Paper
+ }
+
+ gameprofilerfiller.pop();
+ }
+
+ public boolean hasWork() {
+- return this.lightEngine.hasLightWork() || !this.pendingUnloads.isEmpty() || net.minecraft.server.ChunkSystem.hasAnyChunkHolders(this.level) || this.poiManager.hasWork() || !this.toDrop.isEmpty() || !this.unloadQueue.isEmpty() || this.queueSorter.hasWork() || this.distanceManager.hasTickets(); // Paper
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ private void processUnloads(BooleanSupplier shouldKeepTicking) {
+- LongIterator longiterator = this.toDrop.iterator();
+-
+- for (int i = 0; longiterator.hasNext() && (shouldKeepTicking.getAsBoolean() || i < 200 || this.toDrop.size() > 2000); longiterator.remove()) {
+- long j = longiterator.nextLong();
+- ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j);
+-
+- if (playerchunk != null) {
+- playerchunk.onChunkRemove(); // Paper
+- this.pendingUnloads.put(j, playerchunk);
+- this.modified = true;
+- ++i;
+- this.scheduleUnload(j, playerchunk);
+- }
+- }
+-
+- int k = Math.max(0, this.unloadQueue.size() - 2000);
+-
+- Runnable runnable;
+-
+- while ((shouldKeepTicking.getAsBoolean() || k > 0) && (runnable = (Runnable) this.unloadQueue.poll()) != null) {
+- --k;
+- runnable.run();
+- }
+-
+- int l = 0;
+- Iterator objectiterator = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
+-
+- while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) {
+- if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) {
+- ++l;
+- }
+- }
++ this.level.chunkTaskScheduler.chunkHolderManager.processUnloads(); // Paper - rewrite chunk system
+
+ }
+
+ private void scheduleUnload(long pos, ChunkHolder holder) {
+- CompletableFuture<ChunkAccess> completablefuture = holder.getChunkToSave();
+- Consumer<ChunkAccess> consumer = (ichunkaccess) -> { // CraftBukkit - decompile error
+- CompletableFuture<ChunkAccess> completablefuture1 = holder.getChunkToSave();
+-
+- if (completablefuture1 != completablefuture) {
+- this.scheduleUnload(pos, holder);
+- } else {
+- // Paper start
+- boolean removed;
+- if ((removed = this.pendingUnloads.remove(pos, holder)) && ichunkaccess != null) {
+- net.minecraft.server.ChunkSystem.onChunkHolderDelete(this.level, holder);
+- // Paper end
+- if (ichunkaccess instanceof LevelChunk) {
+- ((LevelChunk) ichunkaccess).setLoaded(false);
+- }
+-
+- this.save(ichunkaccess);
+- if (this.entitiesInLevel.remove(pos) && ichunkaccess instanceof LevelChunk) {
+- LevelChunk chunk = (LevelChunk) ichunkaccess;
+-
+- this.level.unload(chunk);
+- }
+-
+- this.lightEngine.updateChunkStatus(ichunkaccess.getPos());
+- this.lightEngine.tryScheduleUpdate();
+- this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null);
+- this.chunkSaveCooldowns.remove(ichunkaccess.getPos().toLong());
+- } else if (removed) { // Paper start
+- net.minecraft.server.ChunkSystem.onChunkHolderDelete(this.level, holder);
+- } // Paper end
+-
+- }
+- };
+- Queue queue = this.unloadQueue;
+-
+- Objects.requireNonNull(this.unloadQueue);
+- completablefuture.thenAcceptAsync(consumer, queue::add).whenComplete((ovoid, throwable) -> {
+- if (throwable != null) {
+- ChunkMap.LOGGER.error("Failed to save chunk {}", holder.getPos(), throwable);
+- }
+-
+- });
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ protected boolean promoteChunkMap() {
+- if (!this.modified) {
+- return false;
+- } else {
+- this.visibleChunkMap = this.updatingChunkMap.clone();
+- this.modified = false;
+- return true;
+- }
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> schedule(ChunkHolder holder, ChunkStatus requiredStatus) {
+- ChunkPos chunkcoordintpair = holder.getPos();
+-
+- if (requiredStatus == ChunkStatus.EMPTY) {
+- return this.scheduleChunkLoad(chunkcoordintpair);
+- } else {
+- if (requiredStatus == ChunkStatus.LIGHT) {
+- this.distanceManager.addTicket(TicketType.LIGHT, chunkcoordintpair, 33 + ChunkStatus.getDistance(ChunkStatus.LIGHT), chunkcoordintpair);
+- }
+-
+- Optional<ChunkAccess> optional = ((Either) holder.getOrScheduleFuture(requiredStatus.getParent(), this).getNow(ChunkHolder.UNLOADED_CHUNK)).left();
+-
+- if (optional.isPresent() && ((ChunkAccess) optional.get()).getStatus().isOrAfter(requiredStatus)) {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = requiredStatus.load(this.level, this.structureTemplateManager, this.lightEngine, (ichunkaccess) -> {
+- return this.protoChunkToFullChunk(holder);
+- }, (ChunkAccess) optional.get());
+-
+- this.progressListener.onStatusChange(chunkcoordintpair, requiredStatus);
+- return completablefuture;
+- } else {
+- return this.scheduleChunkGeneration(holder, requiredStatus);
+- }
+- }
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> scheduleChunkLoad(ChunkPos pos) {
+- return this.readChunk(pos).thenApply((optional) -> {
+- return optional.filter((nbttagcompound) -> {
+- boolean flag = ChunkMap.isChunkDataValid(nbttagcompound);
+-
+- if (!flag) {
+- ChunkMap.LOGGER.error("Chunk file at {} is missing level data, skipping", pos);
+- }
+-
+- return flag;
+- });
+- }).thenApplyAsync((optional) -> {
+- this.level.getProfiler().incrementCounter("chunkLoad");
+- if (optional.isPresent()) {
+- ProtoChunk protochunk = ChunkSerializer.read(this.level, this.poiManager, pos, (CompoundTag) optional.get());
+-
+- this.markPosition(pos, protochunk.getStatus().getChunkType());
+- return Either.<ChunkAccess, ChunkHolder.ChunkLoadingFailure>left(protochunk); // CraftBukkit - decompile error
+- } else {
+- return Either.<ChunkAccess, ChunkHolder.ChunkLoadingFailure>left(this.createEmptyChunk(pos)); // CraftBukkit - decompile error
+- }
+- }, this.mainThreadExecutor).exceptionallyAsync((throwable) -> {
+- return this.handleChunkLoadFailure(throwable, pos);
+- }, this.mainThreadExecutor);
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+- private static boolean isChunkDataValid(CompoundTag nbt) {
++ public static boolean isChunkDataValid(CompoundTag nbt) { // Paper - async chunk loading
+ return nbt.contains("Status", 8);
+ }
+
+@@ -765,45 +485,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+ private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> scheduleChunkGeneration(ChunkHolder holder, ChunkStatus requiredStatus) {
+- ChunkPos chunkcoordintpair = holder.getPos();
+- CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkRangeFuture(chunkcoordintpair, requiredStatus.getRange(), (i) -> {
+- return this.getDependencyStatus(requiredStatus, i);
+- });
+-
+- this.level.getProfiler().incrementCounter(() -> {
+- return "chunkGenerate " + requiredStatus.getName();
+- });
+- Executor executor = (runnable) -> {
+- this.worldgenMailbox.tell(ChunkTaskPriorityQueueSorter.message(holder, runnable));
+- };
+-
+- return completablefuture.thenComposeAsync((either) -> {
+- return (CompletionStage) either.map((list) -> {
+- try {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture1 = requiredStatus.generate(executor, this.level, this.generator, this.structureTemplateManager, this.lightEngine, (ichunkaccess) -> {
+- return this.protoChunkToFullChunk(holder);
+- }, list, false);
+-
+- this.progressListener.onStatusChange(chunkcoordintpair, requiredStatus);
+- return completablefuture1;
+- } catch (Exception exception) {
+- exception.getStackTrace();
+- CrashReport crashreport = CrashReport.forThrowable(exception, "Exception generating new chunk");
+- CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Chunk to be generated");
+-
+- crashreportsystemdetails.setDetail("Location", (Object) String.format(Locale.ROOT, "%d,%d", chunkcoordintpair.x, chunkcoordintpair.z));
+- crashreportsystemdetails.setDetail("Position hash", (Object) ChunkPos.asLong(chunkcoordintpair.x, chunkcoordintpair.z));
+- crashreportsystemdetails.setDetail("Generator", (Object) this.generator);
+- this.mainThreadExecutor.execute(() -> {
+- throw new ReportedException(crashreport);
+- });
+- throw new ReportedException(crashreport);
+- }
+- }, (playerchunk_failure) -> {
+- this.releaseLightTicket(chunkcoordintpair);
+- return CompletableFuture.completedFuture(Either.right(playerchunk_failure));
+- });
+- }, executor);
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ protected void releaseLightTicket(ChunkPos pos) {
+@@ -814,7 +496,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }));
+ }
+
+- private ChunkStatus getDependencyStatus(ChunkStatus centerChunkTargetStatus, int distance) {
++ public static ChunkStatus getDependencyStatus(ChunkStatus centerChunkTargetStatus, int distance) { // Paper -> public, static
+ ChunkStatus chunkstatus1;
+
+ if (distance == 0) {
+@@ -826,7 +508,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ return chunkstatus1;
+ }
+
+- private static void postLoadProtoChunk(ServerLevel world, List<CompoundTag> nbt) {
++ public static void postLoadProtoChunk(ServerLevel world, List<CompoundTag> nbt) { // Paper - public
+ if (!nbt.isEmpty()) {
+ // CraftBukkit start - these are spawned serialized (DefinedStructure) and we don't call an add event below at the moment due to ordering complexities
+ world.addWorldGenChunkEntities(EntityType.loadEntitiesRecursive(nbt, world).filter((entity) -> {
+@@ -848,91 +530,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+ private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> protoChunkToFullChunk(ChunkHolder chunkHolder) {
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = chunkHolder.getFutureIfPresentUnchecked(ChunkStatus.FULL.getParent());
+-
+- return completablefuture.thenApplyAsync((either) -> {
+- ChunkStatus chunkstatus = ChunkHolder.getStatus(chunkHolder.getTicketLevel());
+-
+- return !chunkstatus.isOrAfter(ChunkStatus.FULL) ? ChunkHolder.UNLOADED_CHUNK : either.mapLeft((ichunkaccess) -> {
+- try (Timing ignored = level.timings.chunkPostLoad.startTimingIfSync()) { // Paper
+- ChunkPos chunkcoordintpair = chunkHolder.getPos();
+- ProtoChunk protochunk = (ProtoChunk) ichunkaccess;
+- LevelChunk chunk;
+-
+- if (protochunk instanceof ImposterProtoChunk) {
+- chunk = ((ImposterProtoChunk) protochunk).getWrapped();
+- } else {
+- chunk = new LevelChunk(this.level, protochunk, (chunk1) -> {
+- ChunkMap.postLoadProtoChunk(this.level, protochunk.getEntities());
+- });
+- chunkHolder.replaceProtoChunk(new ImposterProtoChunk(chunk, false));
+- }
+-
+- chunk.setFullStatus(() -> {
+- return ChunkHolder.getFullChunkStatus(chunkHolder.getTicketLevel());
+- });
+- chunk.runPostLoad();
+- if (this.entitiesInLevel.add(chunkcoordintpair.toLong())) {
+- chunk.setLoaded(true);
+- chunk.registerAllBlockEntitiesAfterLevelLoad();
+- chunk.registerTickContainerInLevel(this.level);
+- }
+-
+- return chunk;
+- } // Paper
+- });
+- }, (runnable) -> {
+- ProcessorHandle mailbox = this.mainThreadMailbox;
+- long i = chunkHolder.getPos().toLong();
+-
+- Objects.requireNonNull(chunkHolder);
+- mailbox.tell(ChunkTaskPriorityQueueSorter.message(runnable, i, chunkHolder::getTicketLevel));
+- });
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareTickingChunk(ChunkHolder holder) {
+- ChunkPos chunkcoordintpair = holder.getPos();
+- CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkRangeFuture(chunkcoordintpair, 1, (i) -> {
+- return ChunkStatus.FULL;
+- });
+- CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completablefuture1 = completablefuture.thenApplyAsync((either) -> {
+- return either.mapLeft((list) -> {
+- return (LevelChunk) list.get(list.size() / 2);
+- });
+- }, (runnable) -> {
+- this.mainThreadMailbox.tell(ChunkTaskPriorityQueueSorter.message(holder, runnable));
+- }).thenApplyAsync((either) -> {
+- return either.ifLeft((chunk) -> {
+- chunk.postProcessGeneration();
+- this.level.startTickingChunk(chunk);
+- });
+- }, this.mainThreadExecutor);
+-
+- completablefuture1.thenAcceptAsync((either) -> {
+- either.ifLeft((chunk) -> {
+- this.tickingGenerated.getAndIncrement();
+- MutableObject<ClientboundLevelChunkWithLightPacket> mutableobject = new MutableObject();
+-
+- this.getPlayers(chunkcoordintpair, false).forEach((entityplayer) -> {
+- this.playerLoadedChunk(entityplayer, mutableobject, chunk);
+- });
+- });
+- }, (runnable) -> {
+- this.mainThreadMailbox.tell(ChunkTaskPriorityQueueSorter.message(holder, runnable));
+- });
+- return completablefuture1;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareAccessibleChunk(ChunkHolder holder) {
+- return this.getChunkRangeFuture(holder.getPos(), 1, ChunkStatus::getStatusAroundFullChunk).thenApplyAsync((either) -> {
+- return either.mapLeft((list) -> {
+- LevelChunk chunk = (LevelChunk) list.get(list.size() / 2);
+-
+- return chunk;
+- });
+- }, (runnable) -> {
+- this.mainThreadMailbox.tell(ChunkTaskPriorityQueueSorter.message(holder, runnable));
+- });
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public int getTickingGenerated() {
+@@ -940,94 +546,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+ private boolean saveChunkIfNeeded(ChunkHolder chunkHolder) {
+- if (!chunkHolder.wasAccessibleSinceLastSave()) {
+- return false;
+- } else {
+- ChunkAccess ichunkaccess = (ChunkAccess) chunkHolder.getChunkToSave().getNow(null); // CraftBukkit - decompile error
+-
+- if (!(ichunkaccess instanceof ImposterProtoChunk) && !(ichunkaccess instanceof LevelChunk)) {
+- return false;
+- } else {
+- long i = ichunkaccess.getPos().toLong();
+- long j = this.chunkSaveCooldowns.getOrDefault(i, -1L);
+- long k = System.currentTimeMillis();
+-
+- if (k < j) {
+- return false;
+- } else {
+- boolean flag = this.save(ichunkaccess);
+-
+- chunkHolder.refreshAccessibility();
+- if (flag) {
+- this.chunkSaveCooldowns.put(i, k + 10000L);
+- }
+-
+- return flag;
+- }
+- }
+- }
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public boolean save(ChunkAccess chunk) {
+- this.poiManager.flush(chunk.getPos());
+- if (!chunk.isUnsaved()) {
+- return false;
+- } else {
+- chunk.setUnsaved(false);
+- ChunkPos chunkcoordintpair = chunk.getPos();
+-
+- try {
+- ChunkStatus chunkstatus = chunk.getStatus();
+-
+- if (chunkstatus.getChunkType() != ChunkStatus.ChunkType.LEVELCHUNK) {
+- if (this.isExistingChunkFull(chunkcoordintpair)) {
+- return false;
+- }
+-
+- if (chunkstatus == ChunkStatus.EMPTY && chunk.getAllStarts().values().stream().noneMatch(StructureStart::isValid)) {
+- return false;
+- }
+- }
+-
+- this.level.getProfiler().incrementCounter("chunkSave");
+- CompoundTag nbttagcompound = ChunkSerializer.write(this.level, chunk);
+-
+- this.write(chunkcoordintpair, nbttagcompound);
+- this.markPosition(chunkcoordintpair, chunkstatus.getChunkType());
+- return true;
+- } catch (Exception exception) {
+- ChunkMap.LOGGER.error("Failed to save chunk {},{}", new Object[]{chunkcoordintpair.x, chunkcoordintpair.z, exception});
+- return false;
+- }
+- }
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ private boolean isExistingChunkFull(ChunkPos pos) {
+- byte b0 = this.chunkTypeCache.get(pos.toLong());
+-
+- if (b0 != 0) {
+- return b0 == 1;
+- } else {
+- CompoundTag nbttagcompound;
+-
+- try {
+- nbttagcompound = (CompoundTag) ((Optional) this.readChunk(pos).join()).orElse((Object) null);
+- if (nbttagcompound == null) {
+- this.markPositionReplaceable(pos);
+- return false;
+- }
+- } catch (Exception exception) {
+- ChunkMap.LOGGER.error("Failed to read chunk {}", pos, exception);
+- this.markPositionReplaceable(pos);
+- return false;
+- }
+-
+- ChunkStatus.ChunkType chunkstatus_type = ChunkSerializer.getChunkTypeFromTag(nbttagcompound);
+-
+- return this.markPosition(pos, chunkstatus_type) == 1;
+- }
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
++ // Paper start - replace player loader system
++ public void setTickViewDistance(int distance) {
++ this.playerChunkManager.setTickDistance(distance);
++ }
++ // Paper end - replace player loader system
+ public void setViewDistance(int watchDistance) {
+ int j = Mth.clamp(watchDistance + 1, (int) 3, (int) 33);
+
+@@ -1035,33 +569,18 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ int k = this.viewDistance;
+
+ this.viewDistance = j;
+- this.distanceManager.updatePlayerTickets(this.viewDistance + 1);
+- Iterator objectiterator = net.minecraft.server.ChunkSystem.getUpdatingChunkHolders(this.level).iterator(); // Paper
+-
+- while (objectiterator.hasNext()) {
+- ChunkHolder playerchunk = (ChunkHolder) objectiterator.next();
+- ChunkPos chunkcoordintpair = playerchunk.getPos();
+- MutableObject<ClientboundLevelChunkWithLightPacket> mutableobject = new MutableObject();
+-
+- this.getPlayers(chunkcoordintpair, false).forEach((entityplayer) -> {
+- SectionPos sectionposition = entityplayer.getLastSectionPos();
+- boolean flag = ChunkMap.isChunkInRange(chunkcoordintpair.x, chunkcoordintpair.z, sectionposition.x(), sectionposition.z(), k);
+- boolean flag1 = ChunkMap.isChunkInRange(chunkcoordintpair.x, chunkcoordintpair.z, sectionposition.x(), sectionposition.z(), this.viewDistance);
+-
+- this.updateChunkTracking(entityplayer, chunkcoordintpair, mutableobject, flag, flag1);
+- });
+- }
++ this.playerChunkManager.setLoadDistance(this.viewDistance); // Paper - replace player loader system
+ }
+
+ }
+
+- protected void updateChunkTracking(ServerPlayer player, ChunkPos pos, MutableObject<ClientboundLevelChunkWithLightPacket> packet, boolean oldWithinViewDistance, boolean newWithinViewDistance) {
++ public void updateChunkTracking(ServerPlayer player, ChunkPos pos, MutableObject<ClientboundLevelChunkWithLightPacket> packet, boolean oldWithinViewDistance, boolean newWithinViewDistance) { // Paper - public
+ if (player.level == this.level) {
+ if (newWithinViewDistance && !oldWithinViewDistance) {
+ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos.toLong());
+
+ if (playerchunk != null) {
+- LevelChunk chunk = playerchunk.getTickingChunk();
++ LevelChunk chunk = playerchunk.getSendingChunk(); // Paper - replace chunk loader system
+
+ if (chunk != null) {
+ this.playerLoadedChunk(player, packet, chunk);
+@@ -1091,30 +610,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+ void dumpChunks(Writer writer) throws IOException {
+- CsvOutput csvwriter = CsvOutput.builder().addColumn("x").addColumn("z").addColumn("level").addColumn("in_memory").addColumn("status").addColumn("full_status").addColumn("accessible_ready").addColumn("ticking_ready").addColumn("entity_ticking_ready").addColumn("ticket").addColumn("spawning").addColumn("block_entity_count").addColumn("ticking_ticket").addColumn("ticking_level").addColumn("block_ticks").addColumn("fluid_ticks").build(writer);
+- TickingTracker tickingtracker = this.distanceManager.tickingTracker();
+- Iterator<ChunkHolder> objectbidirectionaliterator = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
+-
+- while (objectbidirectionaliterator.hasNext()) {
+- ChunkHolder playerchunk = objectbidirectionaliterator.next(); // Paper
+- long i = playerchunk.pos.toLong(); // Paper
+- ChunkPos chunkcoordintpair = new ChunkPos(i);
+- // Paper
+- Optional<ChunkAccess> optional = Optional.ofNullable(playerchunk.getLastAvailable());
+- Optional<LevelChunk> optional1 = optional.flatMap((ichunkaccess) -> {
+- return ichunkaccess instanceof LevelChunk ? Optional.of((LevelChunk) ichunkaccess) : Optional.empty();
+- });
+-
+- // CraftBukkit - decompile error
+- csvwriter.writeRow(chunkcoordintpair.x, chunkcoordintpair.z, playerchunk.getTicketLevel(), optional.isPresent(), optional.map(ChunkAccess::getStatus).orElse(null), optional1.map(LevelChunk::getFullStatus).orElse(null), ChunkMap.printFuture(playerchunk.getFullChunkFuture()), ChunkMap.printFuture(playerchunk.getTickingChunkFuture()), ChunkMap.printFuture(playerchunk.getEntityTickingChunkFuture()), this.distanceManager.getTicketDebugString(i), this.anyPlayerCloseEnoughForSpawning(chunkcoordintpair), optional1.map((chunk) -> {
+- return chunk.getBlockEntities().size();
+- }).orElse(0), tickingtracker.getTicketDebugString(i), tickingtracker.getLevel(i), optional1.map((chunk) -> {
+- return chunk.getBlockTicks().count();
+- }).orElse(0), optional1.map((chunk) -> {
+- return chunk.getFluidTicks().count();
+- }).orElse(0));
+- }
+-
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ private static String printFuture(CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> future) {
+@@ -1133,6 +629,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+ }
+
++ // Paper start - Asynchronous chunk io
++ @Nullable
++ @Override
++ public CompoundTag readSync(ChunkPos chunkcoordintpair) throws IOException {
++ // Paper start - rewrite chunk system
++ if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
++ return io.papermc.paper.chunk.system.io.RegionFileIOThread.loadData(
++ this.level, chunkcoordintpair.x, chunkcoordintpair.z, io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA,
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.getIOBlockingPriorityForCurrentThread()
++ );
++ }
++ // Paper end - rewrite chunk system
++ return super.readSync(chunkcoordintpair);
++ }
++
++ @Override
++ public void write(ChunkPos chunkcoordintpair, CompoundTag nbttagcompound) throws IOException {
++ // Paper start - rewrite chunk system
++ if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.scheduleSave(
++ this.level, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound,
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA);
++ return;
++ }
++ // Paper end - rewrite chunk system
++ super.write(chunkcoordintpair, nbttagcompound);
++ }
++ // Paper end
++
+ private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
+ return this.read(chunkPos).thenApplyAsync((optional) -> {
+ return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit
+@@ -1236,15 +761,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ this.removePlayerFromDistanceMaps(player); // Paper - distance maps
+ }
+
+- for (int k = i - this.viewDistance - 1; k <= i + this.viewDistance + 1; ++k) {
+- for (int l = j - this.viewDistance - 1; l <= j + this.viewDistance + 1; ++l) {
+- if (ChunkMap.isChunkInRange(k, l, i, j, this.viewDistance)) {
+- ChunkPos chunkcoordintpair = new ChunkPos(k, l);
+-
+- this.updateChunkTracking(player, chunkcoordintpair, new MutableObject(), !added, added);
+- }
+- }
+- }
++ // Paper - handled by player chunk loader
+
+ }
+
+@@ -1252,7 +769,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ SectionPos sectionposition = SectionPos.of((EntityAccess) player);
+
+ player.setLastSectionPos(sectionposition);
+- player.connection.send(new ClientboundSetChunkCacheCenterPacket(sectionposition.x(), sectionposition.z()));
++ //player.connection.send(new ClientboundSetChunkCacheCenterPacket(sectionposition.x(), sectionposition.z())); // Paper - handled by player chunk loader
+ return sectionposition;
+ }
+
+@@ -1307,65 +824,40 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ int k1;
+ int l1;
+
+- if (Math.abs(i1 - i) <= this.viewDistance * 2 && Math.abs(j1 - j) <= this.viewDistance * 2) {
+- k1 = Math.min(i, i1) - this.viewDistance - 1;
+- l1 = Math.min(j, j1) - this.viewDistance - 1;
+- int i2 = Math.max(i, i1) + this.viewDistance + 1;
+- int j2 = Math.max(j, j1) + this.viewDistance + 1;
+-
+- for (int k2 = k1; k2 <= i2; ++k2) {
+- for (int l2 = l1; l2 <= j2; ++l2) {
+- boolean flag3 = ChunkMap.isChunkInRange(k2, l2, i1, j1, this.viewDistance);
+- boolean flag4 = ChunkMap.isChunkInRange(k2, l2, i, j, this.viewDistance);
+-
+- this.updateChunkTracking(player, new ChunkPos(k2, l2), new MutableObject(), flag3, flag4);
+- }
+- }
+- } else {
+- boolean flag5;
+- boolean flag6;
+-
+- for (k1 = i1 - this.viewDistance - 1; k1 <= i1 + this.viewDistance + 1; ++k1) {
+- for (l1 = j1 - this.viewDistance - 1; l1 <= j1 + this.viewDistance + 1; ++l1) {
+- if (ChunkMap.isChunkInRange(k1, l1, i1, j1, this.viewDistance)) {
+- flag5 = true;
+- flag6 = false;
+- this.updateChunkTracking(player, new ChunkPos(k1, l1), new MutableObject(), true, false);
+- }
+- }
+- }
+-
+- for (k1 = i - this.viewDistance - 1; k1 <= i + this.viewDistance + 1; ++k1) {
+- for (l1 = j - this.viewDistance - 1; l1 <= j + this.viewDistance + 1; ++l1) {
+- if (ChunkMap.isChunkInRange(k1, l1, i, j, this.viewDistance)) {
+- flag5 = false;
+- flag6 = true;
+- this.updateChunkTracking(player, new ChunkPos(k1, l1), new MutableObject(), false, true);
+- }
+- }
+- }
+- }
++ // Paper - replaced by PlayerChunkLoader
+
+ this.updateMaps(player); // Paper - distance maps
++ this.playerChunkManager.updatePlayer(player); // Paper - respond to movement immediately
+
+ }
+
+ @Override
+ public List<ServerPlayer> getPlayers(ChunkPos chunkPos, boolean onlyOnWatchDistanceEdge) {
+- Set<ServerPlayer> set = this.playerMap.getPlayers(chunkPos.toLong());
+- Builder<ServerPlayer> builder = ImmutableList.builder();
+- Iterator iterator = set.iterator();
+-
+- while (iterator.hasNext()) {
+- ServerPlayer entityplayer = (ServerPlayer) iterator.next();
+- SectionPos sectionposition = entityplayer.getLastSectionPos();
++ // Paper start - per player view distance
++ // there can be potential desync with player's last mapped section and the view distance map, so use the
++ // view distance map here.
++ List<ServerPlayer> ret = new java.util.ArrayList<>(4);
++
++ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> players = this.playerChunkManager.broadcastMap.getObjectsInRange(chunkPos);
++ if (players == null) {
++ return ret;
++ }
+
+- if (onlyOnWatchDistanceEdge && ChunkMap.isChunkOnRangeBorder(chunkPos.x, chunkPos.z, sectionposition.x(), sectionposition.z(), this.viewDistance) || !onlyOnWatchDistanceEdge && ChunkMap.isChunkInRange(chunkPos.x, chunkPos.z, sectionposition.x(), sectionposition.z(), this.viewDistance)) {
+- builder.add(entityplayer);
++ Object[] backingSet = players.getBackingSet();
++ for (int i = 0, len = backingSet.length; i < len; ++i) {
++ Object temp = backingSet[i];
++ if (!(temp instanceof ServerPlayer)) {
++ continue;
++ }
++ ServerPlayer player = (ServerPlayer)temp;
++ if (!this.playerChunkManager.isChunkSent(player, chunkPos.x, chunkPos.z, onlyOnWatchDistanceEdge)) {
++ continue;
+ }
++ ret.add(player);
+ }
+
+- return builder.build();
++ return ret;
++ // Paper end - per player view distance
+ }
+
+ public void addEntity(Entity entity) {
+@@ -1560,7 +1052,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+
+ @Override
+ protected boolean isChunkToRemove(long pos) {
+- return ChunkMap.this.toDrop.contains(pos);
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ @Nullable
+@@ -1641,7 +1133,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
+ if (player != this.entity) {
+ Vec3 vec3d = player.position().subtract(this.entity.position());
+- double d0 = (double) Math.min(this.getEffectiveRange(), (ChunkMap.this.viewDistance - 1) * 16);
++ double d0 = (double) Math.min(this.getEffectiveRange(), io.papermc.paper.chunk.PlayerChunkLoader.getSendViewDistance(player) * 16); // Paper - per player view distance
+ double d1 = vec3d.x * vec3d.x + vec3d.z * vec3d.z;
+ double d2 = d0 * d0;
+ boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player);
+diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
+index d38ad1b1eee92a6dbd2b79b4fcdb8959cdb4007d..ffa1e457decf8502c3283352bf5be94d419ff165 100644
+--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
++++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
+@@ -40,6 +40,12 @@ import org.slf4j.Logger;
+
+ public abstract class DistanceManager {
+
++ // Paper start - rewrite chunk system
++ public io.papermc.paper.chunk.system.scheduling.ChunkHolderManager getChunkHolderManager() {
++ return this.chunkMap.level.chunkTaskScheduler.chunkHolderManager;
++ }
++ // Paper end - rewrite chunk system
++
+ static final Logger LOGGER = LogUtils.getLogger();
+ private static final int ENTITY_TICKING_RANGE = 2;
+ static final int PLAYER_TICKET_LEVEL = 33 + ChunkStatus.getDistance(ChunkStatus.FULL) - 2;
+@@ -47,61 +53,20 @@ public abstract class DistanceManager {
+ private static final int ENTITY_TICKING_LEVEL_THRESHOLD = 32;
+ private static final int BLOCK_TICKING_LEVEL_THRESHOLD = 33;
+ final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
+- public final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets = new Long2ObjectOpenHashMap();
+- private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker();
++ // Paper - rewrite chunk system
+ private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
+- private final TickingTracker tickingTicketsTracker = new TickingTracker();
+- private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33);
+- final Set<ChunkHolder> chunksToUpdateFutures = Sets.newHashSet();
+- final ChunkTaskPriorityQueueSorter ticketThrottler;
+- final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> ticketThrottlerInput;
+- final ProcessorHandle<ChunkTaskPriorityQueueSorter.Release> ticketThrottlerReleaser;
+- final LongSet ticketsToRelease = new LongOpenHashSet();
+- final Executor mainThreadExecutor;
+- private long ticketTickCounter;
+- private int simulationDistance = 10;
++ //private final TickingTracker tickingTicketsTracker = new TickingTracker(); // Paper - no longer used
++ //private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33); // Paper - no longer used
++ // Paper - rewrite chunk system
+ private final ChunkMap chunkMap; // Paper
+
+ protected DistanceManager(Executor workerExecutor, Executor mainThreadExecutor, ChunkMap chunkMap) {
+- Objects.requireNonNull(mainThreadExecutor);
+- ProcessorHandle<Runnable> mailbox = ProcessorHandle.of("player ticket throttler", mainThreadExecutor::execute);
+- ChunkTaskPriorityQueueSorter chunktaskqueuesorter = new ChunkTaskPriorityQueueSorter(ImmutableList.of(mailbox), workerExecutor, 4);
+-
+- this.ticketThrottler = chunktaskqueuesorter;
+- this.ticketThrottlerInput = chunktaskqueuesorter.getProcessor(mailbox, true);
+- this.ticketThrottlerReleaser = chunktaskqueuesorter.getReleaseProcessor(mailbox);
+- this.mainThreadExecutor = mainThreadExecutor;
++ // Paper - rewrite chunk system
+ this.chunkMap = chunkMap; // Paper
+ }
+
+ protected void purgeStaleTickets() {
+- ++this.ticketTickCounter;
+- ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
+-
+- while (objectiterator.hasNext()) {
+- Entry<SortedArraySet<Ticket<?>>> entry = (Entry) objectiterator.next();
+- Iterator<Ticket<?>> iterator = ((SortedArraySet) entry.getValue()).iterator();
+- boolean flag = false;
+-
+- while (iterator.hasNext()) {
+- Ticket<?> ticket = (Ticket) iterator.next();
+-
+- if (ticket.timedOut(this.ticketTickCounter)) {
+- iterator.remove();
+- flag = true;
+- this.tickingTicketsTracker.removeTicket(entry.getLongKey(), ticket);
+- }
+- }
+-
+- if (flag) {
+- this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt((SortedArraySet) entry.getValue()), false);
+- }
+-
+- if (((SortedArraySet) entry.getValue()).isEmpty()) {
+- objectiterator.remove();
+- }
+- }
+-
++ this.getChunkHolderManager().tick(); // Paper - rewrite chunk system
+ }
+
+ private static int getTicketLevelAt(SortedArraySet<Ticket<?>> tickets) {
+@@ -117,108 +82,25 @@ public abstract class DistanceManager {
+ protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k);
+
+ public boolean runAllUpdates(ChunkMap chunkStorage) {
+- this.naturalSpawnChunkCounter.runAllUpdates();
+- this.tickingTicketsTracker.runAllUpdates();
+- this.playerTicketManager.runAllUpdates();
+- int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
+- boolean flag = i != 0;
+-
+- if (flag) {
+- ;
+- }
+-
+- if (!this.chunksToUpdateFutures.isEmpty()) {
+- // CraftBukkit start
+- // Iterate pending chunk updates with protection against concurrent modification exceptions
+- java.util.Iterator<ChunkHolder> iter = this.chunksToUpdateFutures.iterator();
+- int expectedSize = this.chunksToUpdateFutures.size();
+- do {
+- ChunkHolder playerchunk = iter.next();
+- iter.remove();
+- expectedSize--;
+-
+- playerchunk.updateFutures(chunkStorage, this.mainThreadExecutor);
+-
+- // Reset iterator if set was modified using add()
+- if (this.chunksToUpdateFutures.size() != expectedSize) {
+- expectedSize = this.chunksToUpdateFutures.size();
+- iter = this.chunksToUpdateFutures.iterator();
+- }
+- } while (iter.hasNext());
+- // CraftBukkit end
+-
+- return true;
+- } else {
+- if (!this.ticketsToRelease.isEmpty()) {
+- LongIterator longiterator = this.ticketsToRelease.iterator();
+-
+- while (longiterator.hasNext()) {
+- long j = longiterator.nextLong();
+-
+- if (this.getTickets(j).stream().anyMatch((ticket) -> {
+- return ticket.getType() == TicketType.PLAYER;
+- })) {
+- ChunkHolder playerchunk = chunkStorage.getUpdatingChunkIfPresent(j);
+-
+- if (playerchunk == null) {
+- throw new IllegalStateException();
+- }
+-
+- CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completablefuture = playerchunk.getEntityTickingChunkFuture();
+-
+- completablefuture.thenAccept((either) -> {
+- this.mainThreadExecutor.execute(() -> {
+- this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> {
+- }, j, false));
+- });
+- });
+- }
+- }
+-
+- this.ticketsToRelease.clear();
+- }
+-
+- return flag;
+- }
++ return this.getChunkHolderManager().processTicketUpdates(); // Paper - rewrite chunk system
+ }
+
+ boolean addTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
+- SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(i);
+- int j = DistanceManager.getTicketLevelAt(arraysetsorted);
+- Ticket<?> ticket1 = (Ticket) arraysetsorted.addOrGet(ticket);
+-
+- ticket1.setCreatedTick(this.ticketTickCounter);
+- if (ticket.getTicketLevel() < j) {
+- this.ticketTracker.update(i, ticket.getTicketLevel(), true);
+- }
+-
+- return ticket == ticket1; // CraftBukkit
++ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::addTicket"); // Paper
++ return this.getChunkHolderManager().addTicketAtLevel((TicketType)ticket.getType(), i, ticket.getTicketLevel(), ticket.key); // Paper - rewrite chunk system
+ }
+
+ boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
+- SortedArraySet<Ticket<?>> arraysetsorted = this.getTickets(i);
+-
+- boolean removed = false; // CraftBukkit
+- if (arraysetsorted.remove(ticket)) {
+- removed = true; // CraftBukkit
+- }
+-
+- if (arraysetsorted.isEmpty()) {
+- this.tickets.remove(i);
+- }
+-
+- this.ticketTracker.update(i, DistanceManager.getTicketLevelAt(arraysetsorted), false);
+- return removed; // CraftBukkit
++ org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::removeTicket"); // Paper
++ return this.getChunkHolderManager().removeTicketAtLevel((TicketType)ticket.getType(), i, ticket.getTicketLevel(), ticket.key); // Paper - rewrite chunk system
+ }
+
+ public <T> void addTicket(TicketType<T> type, ChunkPos pos, int level, T argument) {
+- this.addTicket(pos.toLong(), new Ticket<>(type, level, argument));
++ this.getChunkHolderManager().addTicketAtLevel(type, pos, level, argument); // Paper - rewrite chunk system
+ }
+
+ public <T> void removeTicket(TicketType<T> type, ChunkPos pos, int level, T argument) {
+- Ticket<T> ticket = new Ticket<>(type, level, argument);
+-
+- this.removeTicket(pos.toLong(), ticket);
++ this.getChunkHolderManager().removeTicketAtLevel(type, pos, level, argument); // Paper - rewrite chunk system
+ }
+
+ public <T> void addRegionTicket(TicketType<T> type, ChunkPos pos, int radius, T argument) {
+@@ -227,13 +109,7 @@ public abstract class DistanceManager {
+ }
+
+ public <T> boolean addRegionTicketAtDistance(TicketType<T> tickettype, ChunkPos chunkcoordintpair, int i, T t0) {
+- // CraftBukkit end
+- Ticket<T> ticket = new Ticket<>(tickettype, 33 - i, t0);
+- long j = chunkcoordintpair.toLong();
+-
+- boolean added = this.addTicket(j, ticket); // CraftBukkit
+- this.tickingTicketsTracker.addTicket(j, ticket);
+- return added; // CraftBukkit
++ return this.getChunkHolderManager().addTicketAtLevel(tickettype, chunkcoordintpair, 33 - i, t0); // Paper - rewrite chunk system
+ }
+
+ public <T> void removeRegionTicket(TicketType<T> type, ChunkPos pos, int radius, T argument) {
+@@ -242,31 +118,21 @@ public abstract class DistanceManager {
+ }
+
+ public <T> boolean removeRegionTicketAtDistance(TicketType<T> tickettype, ChunkPos chunkcoordintpair, int i, T t0) {
+- // CraftBukkit end
+- Ticket<T> ticket = new Ticket<>(tickettype, 33 - i, t0);
+- long j = chunkcoordintpair.toLong();
+-
+- boolean removed = this.removeTicket(j, ticket); // CraftBukkit
+- this.tickingTicketsTracker.removeTicket(j, ticket);
+- return removed; // CraftBukkit
++ return this.getChunkHolderManager().removeTicketAtLevel(tickettype, chunkcoordintpair, 33 - i, t0); // Paper - rewrite chunk system
+ }
+
+- private SortedArraySet<Ticket<?>> getTickets(long position) {
+- return (SortedArraySet) this.tickets.computeIfAbsent(position, (j) -> {
+- return SortedArraySet.create(4);
+- });
+- }
++ // Paper - rewrite chunk system
+
+ protected void updateChunkForced(ChunkPos pos, boolean forced) {
+- Ticket<ChunkPos> ticket = new Ticket<>(TicketType.FORCED, 31, pos);
++ Ticket<ChunkPos> ticket = new Ticket<>(TicketType.FORCED, 31, pos, 0L); // Paper - rewrite chunk system
+ long i = pos.toLong();
+
+ if (forced) {
+ this.addTicket(i, ticket);
+- this.tickingTicketsTracker.addTicket(i, ticket);
++ //this.tickingTicketsTracker.addTicket(i, ticket); // Paper - no longer used
+ } else {
+ this.removeTicket(i, ticket);
+- this.tickingTicketsTracker.removeTicket(i, ticket);
++ //this.tickingTicketsTracker.removeTicket(i, ticket); // Paper - no longer used
+ }
+
+ }
+@@ -275,12 +141,10 @@ public abstract class DistanceManager {
+ ChunkPos chunkcoordintpair = pos.chunk();
+ long i = chunkcoordintpair.toLong();
+
+- ((ObjectSet) this.playersPerChunk.computeIfAbsent(i, (j) -> {
+- return new ObjectOpenHashSet();
+- })).add(player);
++ // Paper - no longer used
+ this.naturalSpawnChunkCounter.update(i, 0, true);
+- this.playerTicketManager.update(i, 0, true);
+- this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair);
++ //this.playerTicketManager.update(i, 0, true); // Paper - no longer used
++ //this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
+ }
+
+ public void removePlayer(SectionPos pos, ServerPlayer player) {
+@@ -293,46 +157,44 @@ public abstract class DistanceManager {
+ if (objectset.isEmpty()) {
+ this.playersPerChunk.remove(i);
+ this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
+- this.playerTicketManager.update(i, Integer.MAX_VALUE, false);
+- this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair);
++ //this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
++ //this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
+ }
+
+ }
+
+- private int getPlayerTicketLevel() {
+- return Math.max(0, 31 - this.simulationDistance);
+- }
++ // Paper - rewrite chunk system
+
+ public boolean inEntityTickingRange(long chunkPos) {
+- return this.tickingTicketsTracker.getLevel(chunkPos) < 32;
++ // Paper start - replace player chunk loader system
++ ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(chunkPos);
++ return holder != null && holder.isEntityTickingReady();
++ // Paper end - replace player chunk loader system
+ }
+
+ public boolean inBlockTickingRange(long chunkPos) {
+- return this.tickingTicketsTracker.getLevel(chunkPos) < 33;
++ // Paper start - replace player chunk loader system
++ ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(chunkPos);
++ return holder != null && holder.isTickingReady();
++ // Paper end - replace player chunk loader system
+ }
+
+ protected String getTicketDebugString(long pos) {
+- SortedArraySet<Ticket<?>> arraysetsorted = (SortedArraySet) this.tickets.get(pos);
+-
+- return arraysetsorted != null && !arraysetsorted.isEmpty() ? ((Ticket) arraysetsorted.first()).toString() : "no_ticket";
++ return this.getChunkHolderManager().getTicketDebugString(pos); // Paper - rewrite chunk system
+ }
+
+ protected void updatePlayerTickets(int viewDistance) {
+- this.playerTicketManager.updateViewDistance(viewDistance);
++ this.chunkMap.playerChunkManager.setTargetNoTickViewDistance(viewDistance); // Paper - route to player chunk manager
+ }
+
+ // Paper start
+ public int getSimulationDistance() {
+- return this.simulationDistance;
++ return this.chunkMap.playerChunkManager.getTargetTickViewDistance(); // Paper - route to player chunk manager
+ }
+ // Paper end
+
+ public void updateSimulationDistance(int simulationDistance) {
+- if (simulationDistance != this.simulationDistance) {
+- this.simulationDistance = simulationDistance;
+- this.tickingTicketsTracker.replacePlayerTicketsLevel(this.getPlayerTicketLevel());
+- }
+-
++ this.chunkMap.playerChunkManager.setTargetTickViewDistance(simulationDistance); // Paper - route to player chunk manager
+ }
+
+ public int getNaturalSpawnChunkCount() {
+@@ -346,103 +208,28 @@ public abstract class DistanceManager {
+ }
+
+ public String getDebugStatus() {
+- return this.ticketThrottler.getDebugStatus();
++ return "No DistanceManager stats available"; // Paper - rewrite chunk system
+ }
+
+- private void dumpTickets(String path) {
+- try {
+- FileOutputStream fileoutputstream = new FileOutputStream(new File(path));
+-
+- try {
+- ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().iterator();
+-
+- while (objectiterator.hasNext()) {
+- Entry<SortedArraySet<Ticket<?>>> entry = (Entry) objectiterator.next();
+- ChunkPos chunkcoordintpair = new ChunkPos(entry.getLongKey());
+- Iterator iterator = ((SortedArraySet) entry.getValue()).iterator();
+-
+- while (iterator.hasNext()) {
+- Ticket<?> ticket = (Ticket) iterator.next();
+-
+- fileoutputstream.write((chunkcoordintpair.x + "\t" + chunkcoordintpair.z + "\t" + ticket.getType() + "\t" + ticket.getTicketLevel() + "\t\n").getBytes(StandardCharsets.UTF_8));
+- }
+- }
+- } catch (Throwable throwable) {
+- try {
+- fileoutputstream.close();
+- } catch (Throwable throwable1) {
+- throwable.addSuppressed(throwable1);
+- }
++ // Paper - rewrite chunk system
+
+- throw throwable;
+- }
+-
+- fileoutputstream.close();
+- } catch (IOException ioexception) {
+- DistanceManager.LOGGER.error("Failed to dump tickets to {}", path, ioexception);
+- }
+-
+- }
+-
+- @VisibleForTesting
+- TickingTracker tickingTracker() {
+- return this.tickingTicketsTracker;
+- }
++ // Paper - replace player chunk loader
+
+ public void removeTicketsOnClosing() {
+- ImmutableSet<TicketType<?>> immutableset = ImmutableSet.of(TicketType.UNKNOWN, TicketType.POST_TELEPORT, TicketType.LIGHT, TicketType.FUTURE_AWAIT, TicketType.CHUNK_RELIGHT, ca.spottedleaf.starlight.common.light.StarLightInterface.CHUNK_WORK_TICKET); // Paper - add additional tickets to preserve
+- ObjectIterator objectiterator = this.tickets.long2ObjectEntrySet().fastIterator();
+-
+- while (objectiterator.hasNext()) {
+- Entry<SortedArraySet<Ticket<?>>> entry = (Entry) objectiterator.next();
+- Iterator<Ticket<?>> iterator = ((SortedArraySet) entry.getValue()).iterator();
+- boolean flag = false;
+-
+- while (iterator.hasNext()) {
+- Ticket<?> ticket = (Ticket) iterator.next();
+-
+- if (!immutableset.contains(ticket.getType())) {
+- iterator.remove();
+- flag = true;
+- this.tickingTicketsTracker.removeTicket(entry.getLongKey(), ticket);
+- }
+- }
+-
+- if (flag) {
+- this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt((SortedArraySet) entry.getValue()), false);
+- }
+-
+- if (((SortedArraySet) entry.getValue()).isEmpty()) {
+- objectiterator.remove();
+- }
+- }
+-
++ // Paper - rewrite chunk system - this stupid hack ain't needed anymore
+ }
+
+ public boolean hasTickets() {
+- return !this.tickets.isEmpty();
++ return this.getChunkHolderManager().hasTickets(); // Paper - rewrite chunk system
+ }
+
+ // CraftBukkit start
+ public <T> void removeAllTicketsFor(TicketType<T> ticketType, int ticketLevel, T ticketIdentifier) {
+- Ticket<T> target = new Ticket<>(ticketType, ticketLevel, ticketIdentifier);
+-
+- for (java.util.Iterator<Entry<SortedArraySet<Ticket<?>>>> iterator = this.tickets.long2ObjectEntrySet().fastIterator(); iterator.hasNext();) {
+- Entry<SortedArraySet<Ticket<?>>> entry = iterator.next();
+- SortedArraySet<Ticket<?>> tickets = entry.getValue();
+- if (tickets.remove(target)) {
+- // copied from removeTicket
+- this.ticketTracker.update(entry.getLongKey(), DistanceManager.getTicketLevelAt(tickets), false);
+-
+- // can't use entry after it's removed
+- if (tickets.isEmpty()) {
+- iterator.remove();
+- }
+- }
+- }
++ this.getChunkHolderManager().removeAllTicketsFor(ticketType, ticketLevel, ticketIdentifier); // Paper - rewrite chunk system
+ }
+ // CraftBukkit end
+
++ /* Paper - rewrite chunk system
+ private class ChunkTicketTracker extends ChunkTracker {
+
+ public ChunkTicketTracker() {
+@@ -487,6 +274,7 @@ public abstract class DistanceManager {
+ return this.runUpdates(distance);
+ }
+ }
++ */ // Paper - rewrite chunk system
+
+ private class FixedPlayerDistanceChunkTracker extends ChunkTracker {
+
+@@ -566,6 +354,7 @@ public abstract class DistanceManager {
+ }
+ }
+
++ /* Paper - rewrite chunk system
+ private class PlayerTicketTracker extends DistanceManager.FixedPlayerDistanceChunkTracker {
+
+ private int viewDistance = 0;
+@@ -661,4 +450,5 @@ public abstract class DistanceManager {
+ return distance <= this.viewDistance - 2;
+ }
+ }
++ */ // Paper - rewrite chunk system
+ }
+diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+index 8c99e9d1cc1abf5a425846eb4edd52bf38aa2f75..b3cff10fece84839fe85feb297fafbcd4a02d838 100644
+--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+@@ -366,7 +366,7 @@ public class ServerChunkCache extends ChunkSource {
+ public LevelChunk getChunkAtIfLoadedImmediately(int x, int z) {
+ long k = ChunkPos.asLong(x, z);
+
+- if (Thread.currentThread() == this.mainThread) {
++ if (io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
+ return this.getChunkAtIfLoadedMainThread(x, z);
+ }
+
+@@ -388,11 +388,34 @@ public class ServerChunkCache extends ChunkSource {
+ return ret;
+ }
+ // Paper end
++ // Paper start - async chunk io
++ public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkAtAsynchronously(int x, int z, boolean gen, boolean isUrgent) {
++ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> ret = new CompletableFuture<>();
++
++ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority priority;
++ if (isUrgent) {
++ priority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHER;
++ } else {
++ priority = ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL;
++ }
++
++ net.minecraft.server.ChunkSystem.scheduleChunkLoad(this.level, x, z, gen, ChunkStatus.FULL, true, priority, (chunk) -> {
++ if (chunk == null) {
++ ret.complete(ChunkHolder.UNLOADED_CHUNK);
++ } else {
++ ret.complete(Either.left(chunk));
++ }
++ });
++
++ return ret;
++ }
++ // Paper end - async chunk io
+
+ @Nullable
+ @Override
+ public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) {
+- if (Thread.currentThread() != this.mainThread) {
++ final int x1 = x; final int z1 = z; // Paper - conflict on variable change
++ if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
+ return (ChunkAccess) CompletableFuture.supplyAsync(() -> {
+ return this.getChunk(x, z, leastStatus, create);
+ }, this.mainThreadProcessor).join();
+@@ -404,23 +427,20 @@ public class ServerChunkCache extends ChunkSource {
+
+ ChunkAccess ichunkaccess;
+
+- for (int l = 0; l < 4; ++l) {
+- if (k == this.lastChunkPos[l] && leastStatus == this.lastChunkStatus[l]) {
+- ichunkaccess = this.lastChunk[l];
+- if (ichunkaccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime
+- return ichunkaccess;
+- }
+- }
+- }
++ // Paper - rewrite chunk system - there are no correct callbacks to remove items from cache in the new chunk system
+
+ gameprofilerfiller.incrementCounter("getChunkCacheMiss");
+- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create);
++ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkFutureMainThread(x, z, leastStatus, create, true); // Paper
+ ServerChunkCache.MainThreadExecutor chunkproviderserver_b = this.mainThreadProcessor;
+
+ Objects.requireNonNull(completablefuture);
+ if (!completablefuture.isDone()) { // Paper
++ // Paper start - async chunk io/loading
++ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
++ // Paper end
+ this.level.timings.syncChunkLoad.startTiming(); // Paper
+ chunkproviderserver_b.managedBlock(completablefuture::isDone);
++ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
+ this.level.timings.syncChunkLoad.stopTiming(); // Paper
+ } // Paper
+ ichunkaccess = (ChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
+@@ -440,7 +460,7 @@ public class ServerChunkCache extends ChunkSource {
+ @Nullable
+ @Override
+ public LevelChunk getChunkNow(int chunkX, int chunkZ) {
+- if (Thread.currentThread() != this.mainThread) {
++ if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
+ return null;
+ } else {
+ this.level.getProfiler().incrementCounter("getChunkNow");
+@@ -486,7 +506,7 @@ public class ServerChunkCache extends ChunkSource {
+ }
+
+ public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFuture(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
+- boolean flag1 = Thread.currentThread() == this.mainThread;
++ boolean flag1 = io.papermc.paper.util.TickThread.isTickThread(); // Paper - rewrite chunk system
+ CompletableFuture completablefuture;
+
+ if (flag1) {
+@@ -507,47 +527,52 @@ public class ServerChunkCache extends ChunkSource {
+ }
+
+ private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
+- ChunkPos chunkcoordintpair = new ChunkPos(chunkX, chunkZ);
+- long k = chunkcoordintpair.toLong();
+- int l = 33 + ChunkStatus.getDistance(leastStatus);
+- ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k);
++ // Paper start - add isUrgent - old sig left in place for dirty nms plugins
++ return getChunkFutureMainThread(chunkX, chunkZ, leastStatus, create, false);
++ }
++ private CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create, boolean isUrgent) {
++ // Paper start - rewrite chunk system
++ io.papermc.paper.util.TickThread.ensureTickThread(this.level, chunkX, chunkZ, "Scheduling chunk load off-main");
++ int minLevel = 33 + ChunkStatus.getDistance(leastStatus);
++ io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder = this.level.chunkTaskScheduler.chunkHolderManager.getChunkHolder(chunkX, chunkZ);
+
+- // CraftBukkit start - don't add new ticket for currently unloading chunk
+- boolean currentlyUnloading = false;
+- if (playerchunk != null) {
+- ChunkHolder.FullChunkStatus oldChunkState = ChunkHolder.getFullChunkStatus(playerchunk.oldTicketLevel);
+- ChunkHolder.FullChunkStatus currentChunkState = ChunkHolder.getFullChunkStatus(playerchunk.getTicketLevel());
+- currentlyUnloading = (oldChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER) && !currentChunkState.isOrAfter(ChunkHolder.FullChunkStatus.BORDER));
+- }
+- if (create && !currentlyUnloading) {
+- // CraftBukkit end
+- this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
+- if (this.chunkAbsent(playerchunk, l)) {
+- ProfilerFiller gameprofilerfiller = this.level.getProfiler();
+-
+- gameprofilerfiller.push("chunkLoad");
+- this.runDistanceManagerUpdates();
+- playerchunk = this.getVisibleChunkIfPresent(k);
+- gameprofilerfiller.pop();
+- if (this.chunkAbsent(playerchunk, l)) {
+- throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added"));
+- }
+- }
++ boolean needsFullScheduling = leastStatus == ChunkStatus.FULL && (chunkHolder == null || !chunkHolder.getChunkStatus().isOrAfter(ChunkHolder.FullChunkStatus.BORDER));
++
++ if ((chunkHolder == null || chunkHolder.getTicketLevel() > minLevel || needsFullScheduling) && !create) {
++ return ChunkHolder.UNLOADED_CHUNK_FUTURE;
+ }
+
+- return this.chunkAbsent(playerchunk, l) ? ChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.getOrScheduleFuture(leastStatus, this.chunkMap);
+- }
++ io.papermc.paper.chunk.system.scheduling.NewChunkHolder.ChunkCompletion chunkCompletion = chunkHolder == null ? null : chunkHolder.getLastChunkCompletion();
++ if (needsFullScheduling || chunkCompletion == null || !chunkCompletion.genStatus().isOrAfter(leastStatus)) {
++ // schedule
++ CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> ret = new CompletableFuture<>();
++ Consumer<ChunkAccess> complete = (ChunkAccess chunk) -> {
++ if (chunk == null) {
++ ret.complete(Either.right(ChunkHolder.ChunkLoadingFailure.UNLOADED));
++ } else {
++ ret.complete(Either.left(chunk));
++ }
++ };
+
+- private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) {
+- return holder == null || holder.oldTicketLevel > maxLevel; // CraftBukkit using oldTicketLevel for isLoaded checks
++ this.level.chunkTaskScheduler.scheduleChunkLoad(
++ chunkX, chunkZ, leastStatus, true,
++ isUrgent ? ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.BLOCKING : ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.NORMAL,
++ complete
++ );
++
++ return ret;
++ } else {
++ // can return now
++ return CompletableFuture.completedFuture(Either.left(chunkCompletion.chunk()));
++ }
++ // Paper end - rewrite chunk system
+ }
+
++ // Paper - rewrite chunk system
++
+ @Override
+ public boolean hasChunk(int x, int z) {
+- ChunkHolder playerchunk = this.getVisibleChunkIfPresent((new ChunkPos(x, z)).toLong());
+- int k = 33 + ChunkStatus.getDistance(ChunkStatus.FULL);
+-
+- return !this.chunkAbsent(playerchunk, k);
++ return this.getChunkAtIfLoadedImmediately(x, z) != null; // Paper - rewrite chunk system
+ }
+
+ @Override
+@@ -558,22 +583,13 @@ public class ServerChunkCache extends ChunkSource {
+ if (playerchunk == null) {
+ return null;
+ } else {
+- int l = ServerChunkCache.CHUNK_STATUSES.size() - 1;
+-
+- while (true) {
+- ChunkStatus chunkstatus = (ChunkStatus) ServerChunkCache.CHUNK_STATUSES.get(l);
+- Optional<ChunkAccess> optional = ((Either) playerchunk.getFutureIfPresentUnchecked(chunkstatus).getNow(ChunkHolder.UNLOADED_CHUNK)).left();
+-
+- if (optional.isPresent()) {
+- return (BlockGetter) optional.get();
+- }
+-
+- if (chunkstatus == ChunkStatus.LIGHT.getParent()) {
+- return null;
+- }
+-
+- --l;
++ // Paper start - rewrite chunk system
++ ChunkStatus status = playerchunk.getChunkHolderStatus();
++ if (status != null && !status.isOrAfter(ChunkStatus.LIGHT.getParent())) {
++ return null;
+ }
++ return playerchunk.getAvailableChunkNow();
++ // Paper end - rewrite chunk system
+ }
+ }
+
+@@ -587,15 +603,7 @@ public class ServerChunkCache extends ChunkSource {
+ }
+
+ public boolean runDistanceManagerUpdates() {
+- boolean flag = this.distanceManager.runAllUpdates(this.chunkMap);
+- boolean flag1 = this.chunkMap.promoteChunkMap();
+-
+- if (!flag && !flag1) {
+- return false;
+- } else {
+- this.clearCache();
+- return true;
+- }
++ return this.level.chunkTaskScheduler.chunkHolderManager.processTicketUpdates(); // Paper - rewrite chunk system
+ }
+
+ // Paper start
+@@ -605,17 +613,10 @@ public class ServerChunkCache extends ChunkSource {
+ // Paper end
+
+ public boolean isPositionTicking(long pos) {
+- ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos);
+-
+- if (playerchunk == null) {
+- return false;
+- } else if (!this.level.shouldTickBlocksAt(pos)) {
+- return false;
+- } else {
+- Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = (Either) playerchunk.getTickingChunkFuture().getNow(null); // CraftBukkit - decompile error
+-
+- return either != null && either.left().isPresent();
+- }
++ // Paper start - replace player chunk loader system
++ ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(pos);
++ return holder != null && holder.isTickingReady();
++ // Paper end - replace player chunk loader system
+ }
+
+ public void save(boolean flush) {
+@@ -631,17 +632,13 @@ public class ServerChunkCache extends ChunkSource {
+ this.close(true);
+ }
+
+- public void close(boolean save) throws IOException {
+- if (save) {
+- this.save(true);
+- }
+- // CraftBukkit end
+- this.lightEngine.close();
+- this.chunkMap.close();
++ public void close(boolean save) { // Paper - rewrite chunk system
++ this.level.chunkTaskScheduler.chunkHolderManager.close(save, true); // Paper - rewrite chunk system
+ }
+
+ // CraftBukkit start - modelled on below
+ public void purgeUnload() {
++ if (true) return; // Paper - tickets will be removed later, this behavior isn't really well accounted for by the chunk system
+ this.level.getProfiler().push("purge");
+ this.distanceManager.purgeStaleTickets();
+ this.runDistanceManagerUpdates();
+@@ -662,6 +659,7 @@ public class ServerChunkCache extends ChunkSource {
+ this.level.getProfiler().popPush("chunks");
+ if (tickChunks) {
+ this.level.timings.chunks.startTiming(); // Paper - timings
++ this.chunkMap.playerChunkManager.tick(); // Paper - this is mostly is to account for view distance changes
+ this.tickChunks();
+ this.level.timings.chunks.stopTiming(); // Paper - timings
+ }
+@@ -758,7 +756,12 @@ public class ServerChunkCache extends ChunkSource {
+ ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos);
+
+ if (playerchunk != null) {
+- ((Either) playerchunk.getFullChunkFuture().getNow(ChunkHolder.UNLOADED_LEVEL_CHUNK)).left().ifPresent(chunkConsumer);
++ // Paper start - rewrite chunk system
++ LevelChunk chunk = playerchunk.getFullChunk();
++ if (chunk != null) {
++ chunkConsumer.accept(chunk);
++ }
++ // Paper end - rewrite chunk system
+ }
+
+ }
+@@ -920,17 +923,11 @@ public class ServerChunkCache extends ChunkSource {
+ @Override
+ // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
+ public boolean pollTask() {
+- try {
++ ServerChunkCache.this.chunkMap.playerChunkManager.tickMidTick();
+ if (ServerChunkCache.this.runDistanceManagerUpdates()) {
+ return true;
+- } else {
+- ServerChunkCache.this.lightEngine.tryScheduleUpdate();
+- return super.pollTask();
+ }
+- } finally {
+- chunkMap.callbackExecutor.run();
+- }
+- // CraftBukkit end
++ return super.pollTask() | ServerChunkCache.this.level.chunkTaskScheduler.executeMainThreadTask(); // Paper - rewrite chunk system
+ }
+ }
+
+diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
+index 99d44faab5b5da244fdc170c73d73723c174c8fd..7c709be5d43bcd45064c79e84d5b2fff0b3d0cfe 100644
+--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
+@@ -190,7 +190,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ private final MinecraftServer server;
+ public final PrimaryLevelData serverLevelData; // CraftBukkit - type
+ final EntityTickList entityTickList;
+- public final PersistentEntitySectionManager<Entity> entityManager;
++ //public final PersistentEntitySectionManager<Entity> entityManager; // Paper - rewrite chunk system
+ public boolean noSave;
+ private final SleepStatus sleepStatus;
+ private int emptyTime;
+@@ -315,7 +315,108 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ }
+ }
+ }
+- // Paper end
++
++ // Paper start - rewrite chunk system
++ public final io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler chunkTaskScheduler;
++ public final io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController chunkDataControllerNew
++ = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.CHUNK_DATA) {
++
++ @Override
++ public net.minecraft.world.level.chunk.storage.RegionFileStorage getCache() {
++ return ServerLevel.this.getChunkSource().chunkMap.regionFileCache;
++ }
++
++ @Override
++ public void writeData(int chunkX, int chunkZ, net.minecraft.nbt.CompoundTag compound) throws IOException {
++ ServerLevel.this.getChunkSource().chunkMap.write(new ChunkPos(chunkX, chunkZ), compound);
++ }
++
++ @Override
++ public net.minecraft.nbt.CompoundTag readData(int chunkX, int chunkZ) throws IOException {
++ return ServerLevel.this.getChunkSource().chunkMap.readSync(new ChunkPos(chunkX, chunkZ));
++ }
++ };
++ public final io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController poiDataControllerNew
++ = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA) {
++
++ @Override
++ public net.minecraft.world.level.chunk.storage.RegionFileStorage getCache() {
++ return ServerLevel.this.getChunkSource().chunkMap.getPoiManager();
++ }
++
++ @Override
++ public void writeData(int chunkX, int chunkZ, net.minecraft.nbt.CompoundTag compound) throws IOException {
++ ServerLevel.this.getChunkSource().chunkMap.getPoiManager().write(new ChunkPos(chunkX, chunkZ), compound);
++ }
++
++ @Override
++ public net.minecraft.nbt.CompoundTag readData(int chunkX, int chunkZ) throws IOException {
++ return ServerLevel.this.getChunkSource().chunkMap.getPoiManager().read(new ChunkPos(chunkX, chunkZ));
++ }
++ };
++ public final io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController entityDataControllerNew
++ = new io.papermc.paper.chunk.system.io.RegionFileIOThread.ChunkDataController(io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA) {
++
++ @Override
++ public net.minecraft.world.level.chunk.storage.RegionFileStorage getCache() {
++ return ServerLevel.this.entityStorage;
++ }
++
++ @Override
++ public void writeData(int chunkX, int chunkZ, net.minecraft.nbt.CompoundTag compound) throws IOException {
++ ServerLevel.this.writeEntityChunk(chunkX, chunkZ, compound);
++ }
++
++ @Override
++ public net.minecraft.nbt.CompoundTag readData(int chunkX, int chunkZ) throws IOException {
++ return ServerLevel.this.readEntityChunk(chunkX, chunkZ);
++ }
++ };
++ private final EntityRegionFileStorage entityStorage;
++
++ private static final class EntityRegionFileStorage extends net.minecraft.world.level.chunk.storage.RegionFileStorage {
++
++ public EntityRegionFileStorage(Path directory, boolean dsync) {
++ super(directory, dsync);
++ }
++
++ protected void write(ChunkPos pos, net.minecraft.nbt.CompoundTag nbt) throws IOException {
++ ChunkPos nbtPos = nbt == null ? null : EntityStorage.readChunkPos(nbt);
++ if (nbtPos != null && !pos.equals(nbtPos)) {
++ throw new IllegalArgumentException(
++ "Entity chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos.toString()
++ + " but compound says coordinate is " + nbtPos + " for world: " + this
++ );
++ }
++ super.write(pos, nbt);
++ }
++ }
++
++ private void writeEntityChunk(int chunkX, int chunkZ, net.minecraft.nbt.CompoundTag compound) throws IOException {
++ if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.scheduleSave(
++ this, chunkX, chunkZ, compound,
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA);
++ return;
++ }
++ this.entityStorage.write(new ChunkPos(chunkX, chunkZ), compound);
++ }
++
++ private net.minecraft.nbt.CompoundTag readEntityChunk(int chunkX, int chunkZ) throws IOException {
++ if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
++ return io.papermc.paper.chunk.system.io.RegionFileIOThread.loadData(
++ this, chunkX, chunkZ, io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.ENTITY_DATA,
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.getIOBlockingPriorityForCurrentThread()
++ );
++ }
++ return this.entityStorage.read(new ChunkPos(chunkX, chunkZ));
++ }
++
++ private final io.papermc.paper.chunk.system.entity.EntityLookup entityLookup;
++ public final io.papermc.paper.chunk.system.entity.EntityLookup getEntityLookup() {
++ return this.entityLookup;
++ }
++ // Paper end - rewrite chunk system
+
+ // Add env and gen to constructor, IWorldDataServer -> WorldDataServer
+ public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
+@@ -359,16 +460,16 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ // CraftBukkit end
+ boolean flag2 = minecraftserver.forceSynchronousWrites();
+ DataFixer datafixer = minecraftserver.getFixerUpper();
+- EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver);
++ this.entityStorage = new EntityRegionFileStorage(convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage<Entity> entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver);
+
+- this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage);
++ // this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage, this.entitySliceManager); // Paper // Paper - rewrite chunk system
+ StructureTemplateManager structuretemplatemanager = minecraftserver.getStructureManager();
+ int j = this.spigotConfig.viewDistance; // Spigot
+ int k = this.spigotConfig.simulationDistance; // Spigot
+- PersistentEntitySectionManager persistententitysectionmanager = this.entityManager;
++ //PersistentEntitySectionManager persistententitysectionmanager = this.entityManager; // Paper - rewrite chunk system
+
+- Objects.requireNonNull(this.entityManager);
+- this.chunkSource = new ServerChunkCache(this, convertable_conversionsession, datafixer, structuretemplatemanager, executor, chunkgenerator, j, k, flag2, worldloadlistener, persistententitysectionmanager::updateChunkStatus, () -> {
++ //Objects.requireNonNull(this.entityManager); // Paper - rewrite chunk system
++ this.chunkSource = new ServerChunkCache(this, convertable_conversionsession, datafixer, structuretemplatemanager, executor, chunkgenerator, j, k, flag2, worldloadlistener, null, () -> { // Paper - rewrite chunk system
+ return minecraftserver.overworld().getDataStorage();
+ });
+ chunkgenerator.ensureStructuresGenerated(this.chunkSource.randomState());
+@@ -397,6 +498,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
+
+ this.sleepStatus = new SleepStatus();
+ this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
++
++ this.chunkTaskScheduler = new io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler(this, io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.workerThreads); // Paper - rewrite chunk system
++ this.entityLookup = new io.papermc.paper.chunk.system.entity.EntityLookup(this, new EntityCallbacks()); // Paper - rewrite chunk system
+ }
+
+ public void setWeatherParameters(int clearDuration, int rainDuration, boolean raining, boolean thundering) {
+@@ -500,7 +604,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ gameprofilerfiller.push("checkDespawn");
+ entity.checkDespawn();
+ gameprofilerfiller.pop();
+- if (this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) {
++ if (true || this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(entity.chunkPosition().toLong())) { // Paper - now always true if in the ticking list
+ Entity entity1 = entity.getVehicle();
+
+ if (entity1 != null) {
+@@ -525,7 +629,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ }
+
+ gameprofilerfiller.push("entityManagement");
+- this.entityManager.tick();
++ //this.entityManager.tick(); // Paper - rewrite chunk system
+ gameprofilerfiller.popPush("gameEvents");
+ this.sendGameEvents();
+ gameprofilerfiller.pop();
+@@ -533,7 +637,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
+
+ @Override
+ public boolean shouldTickBlocksAt(long chunkPos) {
+- return this.chunkSource.chunkMap.getDistanceManager().inBlockTickingRange(chunkPos);
++ // Paper start - replace player chunk loader system
++ ChunkHolder holder = this.chunkSource.chunkMap.getVisibleChunkIfPresent(chunkPos);
++ return holder != null && holder.isTickingReady();
++ // Paper end - replace player chunk loader system
+ }
+
+ protected void tickTime() {
+@@ -975,6 +1082,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ }
+
+ public void save(@Nullable ProgressListener progressListener, boolean flush, boolean savingDisabled) {
++ // Paper start - rewrite chunk system - add close param
++ this.save(progressListener, flush, savingDisabled, false);
++ }
++ public void save(@Nullable ProgressListener progressListener, boolean flush, boolean savingDisabled, boolean close) {
++ // Paper end - rewrite chunk system - add close param
+ ServerChunkCache chunkproviderserver = this.getChunkSource();
+
+ if (!savingDisabled) {
+@@ -990,16 +1102,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ }
+
+ timings.worldSaveChunks.startTiming(); // Paper
+- chunkproviderserver.save(flush);
++ if (!close) chunkproviderserver.save(flush); // Paper - rewrite chunk system
++ if (close) chunkproviderserver.close(true); // Paper - rewrite chunk system
+ timings.worldSaveChunks.stopTiming(); // Paper
+ }// Paper
+- if (flush) {
+- this.entityManager.saveAll();
+- } else {
+- this.entityManager.autoSave();
+- }
++ // Paper - rewrite chunk system - entity saving moved into ChunkHolder
+
+- }
++ } else if (close) { chunkproviderserver.close(false); } // Paper - rewrite chunk system
+
+ // CraftBukkit start - moved from MinecraftServer.saveChunks
+ ServerLevel worldserver1 = this;
+@@ -1116,7 +1225,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ this.removePlayerImmediately((ServerPlayer) entity, Entity.RemovalReason.DISCARDED);
+ }
+
+- this.entityManager.addNewEntity(player);
++ this.entityLookup.addNewEntity(player); // Paper - rewite chunk system
+ }
+
+ // CraftBukkit start
+@@ -1132,7 +1241,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ }
+ // CraftBukkit end
+
+- return this.entityManager.addNewEntity(entity);
++ return this.entityLookup.addNewEntity(entity); // Paper - rewrite chunk system
+ }
+ }
+
+@@ -1144,10 +1253,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ public boolean tryAddFreshEntityWithPassengers(Entity entity, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
+ // CraftBukkit end
+ Stream<UUID> stream = entity.getSelfAndPassengers().map(Entity::getUUID); // CraftBukkit - decompile error
+- PersistentEntitySectionManager persistententitysectionmanager = this.entityManager;
++ //PersistentEntitySectionManager persistententitysectionmanager = this.entityManager; // Paper - rewrite chunk system
+
+- Objects.requireNonNull(this.entityManager);
+- if (stream.anyMatch(persistententitysectionmanager::isLoaded)) {
++ //Objects.requireNonNull(this.entityManager); // Paper - rewrite chunk system
++ if (stream.anyMatch(this.entityLookup::hasEntity)) { // Paper - rewrite chunk system
+ return false;
+ } else {
+ this.addFreshEntityWithPassengers(entity, reason); // CraftBukkit
+@@ -1731,7 +1840,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ }
+ }
+
+- bufferedwriter.write(String.format(Locale.ROOT, "entities: %s\n", this.entityManager.gatherStats()));
++ bufferedwriter.write(String.format(Locale.ROOT, "entities: %s\n", this.entityLookup.getDebugInfo())); // Paper - rewrite chunk system
+ bufferedwriter.write(String.format(Locale.ROOT, "block_entity_tickers: %d\n", this.blockEntityTickers.size()));
+ bufferedwriter.write(String.format(Locale.ROOT, "block_ticks: %d\n", this.getBlockTicks().count()));
+ bufferedwriter.write(String.format(Locale.ROOT, "fluid_ticks: %d\n", this.getFluidTicks().count()));
+@@ -1780,7 +1889,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ BufferedWriter bufferedwriter2 = Files.newBufferedWriter(path1);
+
+ try {
+- playerchunkmap.dumpChunks(bufferedwriter2);
++ //playerchunkmap.dumpChunks(bufferedwriter2); // Paper - rewrite chunk system
+ } catch (Throwable throwable4) {
+ if (bufferedwriter2 != null) {
+ try {
+@@ -1801,7 +1910,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ BufferedWriter bufferedwriter3 = Files.newBufferedWriter(path2);
+
+ try {
+- this.entityManager.dumpSections(bufferedwriter3);
++ //this.entityManager.dumpSections(bufferedwriter3); // Paper - rewrite chunk system
+ } catch (Throwable throwable6) {
+ if (bufferedwriter3 != null) {
+ try {
+@@ -1943,7 +2052,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+
+ @VisibleForTesting
+ public String getWatchdogStats() {
+- return String.format(Locale.ROOT, "players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s", this.players.size(), this.entityManager.gatherStats(), ServerLevel.getTypeCount(this.entityManager.getEntityGetter().getAll(), (entity) -> {
++ return String.format(Locale.ROOT, "players: %s, entities: %s [%s], block_entities: %d [%s], block_ticks: %d, fluid_ticks: %d, chunk_source: %s", this.players.size(), this.entityLookup.getDebugInfo(), ServerLevel.getTypeCount(this.entityLookup.getAll(), (entity) -> { // Paper - rewrite chunk system
+ return Registry.ENTITY_TYPE.getKey(entity.getType()).toString();
+ }), this.blockEntityTickers.size(), ServerLevel.getTypeCount(this.blockEntityTickers, TickingBlockEntity::getType), this.getBlockTicks().count(), this.getFluidTicks().count(), this.gatherChunkSourceStats());
+ }
+@@ -2003,15 +2112,15 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ @Override
+ public LevelEntityGetter<Entity> getEntities() {
+ org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot
+- return this.entityManager.getEntityGetter();
++ return this.entityLookup; // Paper - rewrite chunk system
+ }
+
+ public void addLegacyChunkEntities(Stream<Entity> entities) {
+- this.entityManager.addLegacyChunkEntities(entities);
++ this.entityLookup.addLegacyChunkEntities(entities.toList()); // Paper - rewrite chunk system
+ }
+
+ public void addWorldGenChunkEntities(Stream<Entity> entities) {
+- this.entityManager.addWorldGenChunkEntities(entities);
++ this.entityLookup.addWorldGenChunkEntities(entities.toList()); // Paper - rewrite chunk system
+ }
+
+ public void startTickingChunk(LevelChunk chunk) {
+@@ -2027,34 +2136,49 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ @Override
+ public void close() throws IOException {
+ super.close();
+- this.entityManager.close();
++ //this.entityManager.close(); // Paper - rewrite chunk system
+ }
+
+ @Override
+ public String gatherChunkSourceStats() {
+ String s = this.chunkSource.gatherStats();
+
+- return "Chunks[S] W: " + s + " E: " + this.entityManager.gatherStats();
++ return "Chunks[S] W: " + s + " E: " + this.entityLookup.getDebugInfo(); // Paper - rewrite chunk system
+ }
+
+ public boolean areEntitiesLoaded(long chunkPos) {
+- return this.entityManager.areEntitiesLoaded(chunkPos);
++ // Paper start - rewrite chunk system
++ return this.getChunkIfLoadedImmediately(ChunkPos.getX(chunkPos), ChunkPos.getZ(chunkPos)) != null;
++ // Paper end - rewrite chunk system
+ }
+
+ private boolean isPositionTickingWithEntitiesLoaded(long chunkPos) {
+- return this.areEntitiesLoaded(chunkPos) && this.chunkSource.isPositionTicking(chunkPos);
++ // Paper start - optimize is ticking ready type functions
++ io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder = this.chunkTaskScheduler.chunkHolderManager.getChunkHolder(chunkPos);
++ // isTicking implies the chunk is loaded, and the chunk is loaded now implies the entities are loaded
++ return chunkHolder != null && chunkHolder.isTickingReady();
++ // Paper end
+ }
+
+ public boolean isPositionEntityTicking(BlockPos pos) {
+- return this.entityManager.canPositionTick(pos) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.asLong(pos));
++ // Paper start - rewrite chunk system
++ io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder = this.chunkTaskScheduler.chunkHolderManager.getChunkHolder(io.papermc.paper.util.CoordinateUtils.getChunkKey(pos));
++ return chunkHolder != null && chunkHolder.isEntityTickingReady();
++ // Paper end - rewrite chunk system
+ }
+
+ public boolean isNaturalSpawningAllowed(BlockPos pos) {
+- return this.entityManager.canPositionTick(pos);
++ // Paper start - rewrite chunk system
++ io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder = this.chunkTaskScheduler.chunkHolderManager.getChunkHolder(io.papermc.paper.util.CoordinateUtils.getChunkKey(pos));
++ return chunkHolder != null && chunkHolder.isEntityTickingReady();
++ // Paper end - rewrite chunk system
+ }
+
+ public boolean isNaturalSpawningAllowed(ChunkPos pos) {
+- return this.entityManager.canPositionTick(pos);
++ // Paper start - rewrite chunk system
++ io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder = this.chunkTaskScheduler.chunkHolderManager.getChunkHolder(io.papermc.paper.util.CoordinateUtils.getChunkKey(pos));
++ return chunkHolder != null && chunkHolder.isEntityTickingReady();
++ // Paper end - rewrite chunk system
+ }
+
+ private final class EntityCallbacks implements LevelCallback<Entity> {
+diff --git a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
+index 89f3380632b098aaf95d68a386bc7e72c8c27f5c..3a2e4d42a8509f8f5a0e2c34e1f6568fce82cd80 100644
+--- a/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
++++ b/src/main/java/net/minecraft/server/level/ThreadedLevelLightEngine.java
+@@ -36,15 +36,14 @@ import net.minecraft.world.level.chunk.ChunkStatus;
+
+ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCloseable {
+ private static final Logger LOGGER = LogUtils.getLogger();
+- private final ProcessorMailbox<Runnable> taskMailbox;
+- private final ObjectList<Pair<ThreadedLevelLightEngine.TaskType, Runnable>> lightTasks = new ObjectArrayList<>();
++ // Paper - rewrite chunk system
+ private final ChunkMap chunkMap;
+- private final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> sorterMailbox;
++ // Paper - rewrite chunk system
+ private volatile int taskPerBatch = 5;
+- private final AtomicBoolean scheduled = new AtomicBoolean();
++ // Paper - rewrite chunk system
+
+ // Paper start - replace light engine impl
+- protected final ca.spottedleaf.starlight.common.light.StarLightInterface theLightEngine;
++ public final ca.spottedleaf.starlight.common.light.StarLightInterface theLightEngine;
+ public final boolean hasBlockLight;
+ public final boolean hasSkyLight;
+ // Paper end - replace light engine impl
+@@ -52,8 +51,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+ public ThreadedLevelLightEngine(LightChunkGetter chunkProvider, ChunkMap chunkStorage, boolean hasBlockLight, ProcessorMailbox<Runnable> processor, ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> executor) {
+ super(chunkProvider, false, false); // Paper - destroy vanilla light engine state
+ this.chunkMap = chunkStorage;
+- this.sorterMailbox = executor;
+- this.taskMailbox = processor;
++ // Paper - rewrite chunk system
+ // Paper start - replace light engine impl
+ this.hasBlockLight = true;
+ this.hasSkyLight = hasBlockLight; // Nice variable name.
+@@ -97,7 +95,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+ ++totalChunks;
+ }
+
+- this.taskMailbox.tell(() -> {
++ this.chunkMap.level.chunkTaskScheduler.lightExecutor.queueRunnable(() -> { // Paper - rewrite chunk system
+ this.theLightEngine.relightChunks(chunks, (ChunkPos chunkPos) -> {
+ chunkLightCallback.accept(chunkPos);
+ ((java.util.concurrent.Executor)((ServerLevel)this.theLightEngine.getWorld()).getChunkSource().mainThreadProcessor).execute(() -> {
+@@ -160,9 +158,7 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+ for (int dz = -1; dz <= 1; ++dz) {
+ ChunkHolder neighbour = world.getChunkSource().chunkMap.getUpdatingChunkIfPresent(CoordinateUtils.getChunkKey(dx + chunkX, dz + chunkZ));
+ if (neighbour != null) {
+- neighbour.chunkToSave = neighbour.chunkToSave.thenCombine(updateFuture, (final ChunkAccess curr, final Void ignore) -> {
+- return curr;
+- });
++ // Paper - rewrite chunk system - not needed, light ticket will keep these chunks loaded
+ }
+ }
+ }
+@@ -283,17 +279,11 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+ }
+
+ private void addTask(int x, int z, ThreadedLevelLightEngine.TaskType stage, Runnable task) {
+- this.addTask(x, z, this.chunkMap.getChunkQueueLevel(ChunkPos.asLong(x, z)), stage, task);
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ private void addTask(int x, int z, IntSupplier completedLevelSupplier, ThreadedLevelLightEngine.TaskType stage, Runnable task) {
+- this.sorterMailbox.tell(ChunkTaskPriorityQueueSorter.message(() -> {
+- this.lightTasks.add(Pair.of(stage, task));
+- if (this.lightTasks.size() >= this.taskPerBatch) {
+- this.runUpdate();
+- }
+-
+- }, ChunkPos.asLong(x, z), completedLevelSupplier));
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ @Override
+@@ -351,74 +341,15 @@ public class ThreadedLevelLightEngine extends LevelLightEngine implements AutoCl
+ }
+ });
+ }
+- // Paper end - replace light engine impl
+- ChunkPos chunkPos = chunk.getPos();
+- chunk.setLightCorrect(false);
+- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.PRE_UPDATE, Util.name(() -> {
+- LevelChunkSection[] levelChunkSections = chunk.getSections();
+-
+- for(int i = 0; i < chunk.getSectionsCount(); ++i) {
+- LevelChunkSection levelChunkSection = levelChunkSections[i];
+- if (!levelChunkSection.hasOnlyAir()) {
+- int j = this.levelHeightAccessor.getSectionYFromSectionIndex(i);
+- super.updateSectionStatus(SectionPos.of(chunkPos, j), false);
+- }
+- }
+-
+- super.enableLightSources(chunkPos, true);
+- if (!excludeBlocks) {
+- chunk.getLights().forEach((pos) -> {
+- super.onBlockEmissionIncrease(pos, chunk.getLightEmission(pos));
+- });
+- }
+-
+- }, () -> {
+- return "lightChunk " + chunkPos + " " + excludeBlocks;
+- }));
+- return CompletableFuture.supplyAsync(() -> {
+- chunk.setLightCorrect(true);
+- super.retainData(chunkPos, false);
+- this.chunkMap.releaseLightTicket(chunkPos);
+- return chunk;
+- }, (runnable) -> {
+- this.addTask(chunkPos.x, chunkPos.z, ThreadedLevelLightEngine.TaskType.POST_UPDATE, runnable);
+- });
++ throw new InternalError(); // Paper - rewrite chunk system
+ }
+
+ public void tryScheduleUpdate() {
+- if (this.hasLightWork() && this.scheduled.compareAndSet(false, true)) { // Paper // Paper - rewrite light engine
+- this.taskMailbox.tell(() -> {
+- this.runUpdate();
+- this.scheduled.set(false);
+- });
+- }
+-
++ // Paper - rewrite chunk system
+ }
+
+ private void runUpdate() {
+- int i = Math.min(this.lightTasks.size(), this.taskPerBatch);
+- ObjectListIterator<Pair<ThreadedLevelLightEngine.TaskType, Runnable>> objectListIterator = this.lightTasks.iterator();
+-
+- int j;
+- for(j = 0; objectListIterator.hasNext() && j < i; ++j) {
+- Pair<ThreadedLevelLightEngine.TaskType, Runnable> pair = objectListIterator.next();
+- if (pair.getFirst() == ThreadedLevelLightEngine.TaskType.PRE_UPDATE) {
+- pair.getSecond().run();
+- }
+- }
+-
+- objectListIterator.back(j);
+- this.theLightEngine.propagateChanges(); // Paper - rewrite light engine
+-
+- for(int var5 = 0; objectListIterator.hasNext() && var5 < i; ++var5) {
+- Pair<ThreadedLevelLightEngine.TaskType, Runnable> pair2 = objectListIterator.next();
+- if (pair2.getFirst() == ThreadedLevelLightEngine.TaskType.POST_UPDATE) {
+- pair2.getSecond().run();
+- }
+-
+- objectListIterator.remove();
+- }
+-
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ public void setTaskPerBatch(int taskBatchSize) {
+diff --git a/src/main/java/net/minecraft/server/level/Ticket.java b/src/main/java/net/minecraft/server/level/Ticket.java
+index ffc43e5d3d0563c9e9c171064511b2c65ddf67e1..768a2667f950a635a562fa8a0c75b31a3ae9190e 100644
+--- a/src/main/java/net/minecraft/server/level/Ticket.java
++++ b/src/main/java/net/minecraft/server/level/Ticket.java
+@@ -6,9 +6,12 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
+ private final TicketType<T> type;
+ private final int ticketLevel;
+ public final T key;
+- public long createdTick;
++ // Paper start - rewrite chunk system
++ public final long removalTick;
+
+- protected Ticket(TicketType<T> type, int level, T argument) {
++ public Ticket(TicketType<T> type, int level, T argument, long removalTick) {
++ this.removalTick = removalTick;
++ // Paper end - rewrite chunk system
+ this.type = type;
+ this.ticketLevel = level;
+ this.key = argument;
+@@ -44,7 +47,7 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
+
+ @Override
+ public String toString() {
+- return "Ticket[" + this.type + " " + this.ticketLevel + " (" + this.key + ")] at " + this.createdTick;
++ return "Ticket[" + this.type + " " + this.ticketLevel + " (" + this.key + ")] to die on " + this.removalTick; // Paper - rewrite chunk system
+ }
+
+ public TicketType<T> getType() {
+@@ -56,11 +59,10 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
+ }
+
+ protected void setCreatedTick(long tickCreated) {
+- this.createdTick = tickCreated;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+ protected boolean timedOut(long currentTick) {
+- long l = this.type.timeout();
+- return l != 0L && currentTick - this.createdTick > l;
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+ }
+diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java
+index 6051e5f272838ef23276a90e21c2fc821ca155d1..97d1ff2af23bac14e67bca5896843325aaa5bfc1 100644
+--- a/src/main/java/net/minecraft/server/level/TicketType.java
++++ b/src/main/java/net/minecraft/server/level/TicketType.java
+@@ -8,6 +8,7 @@ import net.minecraft.world.level.ChunkPos;
+
+ public class TicketType<T> {
+ public static final TicketType<Long> FUTURE_AWAIT = create("future_await", Long::compareTo); // Paper
++ public static final TicketType<Long> ASYNC_LOAD = create("async_load", Long::compareTo); // Paper
+
+ private final String name;
+ private final Comparator<T> comparator;
+@@ -27,6 +28,13 @@ public class TicketType<T> {
+ public static final TicketType<Unit> PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit
+ public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
+ public static final TicketType<Long> CHUNK_RELIGHT = create("light_update", Long::compareTo); // Paper - ensure chunks stay loaded for lighting
++ // Paper start - rewrite chunk system
++ public static final TicketType<Long> CHUNK_LOAD = create("chunk_load", Long::compareTo);
++ public static final TicketType<Long> STATUS_UPGRADE = create("status_upgrade", Long::compareTo);
++ public static final TicketType<Long> ENTITY_LOAD = create("entity_load", Long::compareTo);
++ public static final TicketType<Long> POI_LOAD = create("poi_load", Long::compareTo);
++ public static final TicketType<Unit> UNLOAD_COOLDOWN = create("unload_cooldown", (u1, u2) -> 0, 5 * 20);
++ // Paper end - rewrite chunk system
+
+ public static <T> TicketType<T> create(String name, Comparator<T> argumentComparator) {
+ return new TicketType<>(name, argumentComparator, 0L);
+diff --git a/src/main/java/net/minecraft/server/level/WorldGenRegion.java b/src/main/java/net/minecraft/server/level/WorldGenRegion.java
+index 32d6e4b194c3c4eca7009059f8d185896b5ae556..51d3150e732f95be13f5f54d994dab1fa89ed3f2 100644
+--- a/src/main/java/net/minecraft/server/level/WorldGenRegion.java
++++ b/src/main/java/net/minecraft/server/level/WorldGenRegion.java
+@@ -498,4 +498,21 @@ public class WorldGenRegion implements WorldGenLevel {
+ public long nextSubTickCount() {
+ return this.subTickCount.getAndIncrement();
+ }
++
++ // Paper start
++ // No-op, this class doesn't provide entity access
++ @Override
++ public List<Entity> getHardCollidingEntities(Entity except, AABB box, Predicate<? super Entity> predicate) {
++ return Collections.emptyList();
++ }
++
++ @Override
++ public void getEntities(Entity except, AABB box, Predicate<? super Entity> predicate, List<Entity> into) {}
++
++ @Override
++ public void getHardCollidingEntities(Entity except, AABB box, Predicate<? super Entity> predicate, List<Entity> into) {}
++
++ @Override
++ public <T> void getEntitiesByClass(Class<? extends T> clazz, Entity except, AABB box, List<? super T> into, Predicate<? super T> predicate) {}
++ // Paper end
+ }
+diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+index 4e7db441f68019d6e5d3359605b76bc4b258e87e..22c095539425a6667b8e7f5c5f0a8ff2e87adfb5 100644
+--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+@@ -784,6 +784,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
+ this.disconnect(Component.translatable("disconnect.spam"));
+ return;
+ }
++ // Paper start
++ String str = packet.getCommand(); int index = -1;
++ if (str.length() > 64 && ((index = str.indexOf(' ')) == -1 || index >= 64)) {
++ server.scheduleOnMain(() -> this.disconnect(Component.translatable("disconnect.spam", new Object[0]))); // Paper
++ return;
++ }
++ // Paper end
+ // CraftBukkit end
+ StringReader stringreader = new StringReader(packet.getCommand());
+
+diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
+index e7fcb402e3d4e0707a28505a9fb6642764034e23..3235d6f98794709a53208e20ef33f2164725be48 100644
+--- a/src/main/java/net/minecraft/server/players/PlayerList.java
++++ b/src/main/java/net/minecraft/server/players/PlayerList.java
+@@ -245,7 +245,7 @@ public abstract class PlayerList {
+ boolean flag1 = gamerules.getBoolean(GameRules.RULE_REDUCEDDEBUGINFO);
+
+ // Spigot - view distance
+- playerconnection.send(new ClientboundLoginPacket(player.getId(), worlddata.isHardcore(), player.gameMode.getGameModeForPlayer(), player.gameMode.getPreviousGameModeForPlayer(), this.server.levelKeys(), this.registryHolder, worldserver1.dimensionTypeId(), worldserver1.dimension(), BiomeManager.obfuscateSeed(worldserver1.getSeed()), this.getMaxPlayers(), worldserver1.spigotConfig.viewDistance, worldserver1.spigotConfig.simulationDistance, flag1, !flag, worldserver1.isDebug(), worldserver1.isFlat(), player.getLastDeathLocation()));
++ playerconnection.send(new ClientboundLoginPacket(player.getId(), worlddata.isHardcore(), player.gameMode.getGameModeForPlayer(), player.gameMode.getPreviousGameModeForPlayer(), this.server.levelKeys(), this.registryHolder, worldserver1.dimensionTypeId(), worldserver1.dimension(), BiomeManager.obfuscateSeed(worldserver1.getSeed()), this.getMaxPlayers(), worldserver1.getChunkSource().chunkMap.playerChunkManager.getTargetSendDistance(), worldserver1.getChunkSource().chunkMap.playerChunkManager.getTargetTickViewDistance(), flag1, !flag, worldserver1.isDebug(), worldserver1.isFlat(), player.getLastDeathLocation())); // Paper - replace old player chunk management
+ player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit
+ playerconnection.send(new ClientboundCustomPayloadPacket(ClientboundCustomPayloadPacket.BRAND, (new FriendlyByteBuf(Unpooled.buffer())).writeUtf(this.getServer().getServerModName())));
+ playerconnection.send(new ClientboundChangeDifficultyPacket(worlddata.getDifficulty(), worlddata.isDifficultyLocked()));
+@@ -773,8 +773,8 @@ public abstract class PlayerList {
+ // CraftBukkit start
+ LevelData worlddata = worldserver1.getLevelData();
+ entityplayer1.connection.send(new ClientboundRespawnPacket(worldserver1.dimensionTypeId(), worldserver1.dimension(), BiomeManager.obfuscateSeed(worldserver1.getSeed()), entityplayer1.gameMode.getGameModeForPlayer(), entityplayer1.gameMode.getPreviousGameModeForPlayer(), worldserver1.isDebug(), worldserver1.isFlat(), flag, entityplayer1.getLastDeathLocation()));
+- entityplayer1.connection.send(new ClientboundSetChunkCacheRadiusPacket(worldserver1.spigotConfig.viewDistance)); // Spigot
+- entityplayer1.connection.send(new ClientboundSetSimulationDistancePacket(worldserver1.spigotConfig.simulationDistance)); // Spigot
++ entityplayer1.connection.send(new ClientboundSetChunkCacheRadiusPacket(worldserver1.getChunkSource().chunkMap.playerChunkManager.getTargetSendDistance())); // Spigot // Paper - replace old player chunk management
++ entityplayer1.connection.send(new ClientboundSetSimulationDistancePacket(worldserver1.getChunkSource().chunkMap.playerChunkManager.getTargetTickViewDistance())); // Spigot // Paper - replace old player chunk management
+ entityplayer1.spawnIn(worldserver1);
+ entityplayer1.unsetRemoved();
+ entityplayer1.connection.teleport(new Location(worldserver1.getWorld(), entityplayer1.getX(), entityplayer1.getY(), entityplayer1.getZ(), entityplayer1.getYRot(), entityplayer1.getXRot()));
+@@ -1280,7 +1280,7 @@ public abstract class PlayerList {
+
+ public void setViewDistance(int viewDistance) {
+ this.viewDistance = viewDistance;
+- this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance));
++ //this.broadcastAll(new ClientboundSetChunkCacheRadiusPacket(viewDistance)); // Paper - move into setViewDistance
+ Iterator iterator = this.server.getAllLevels().iterator();
+
+ while (iterator.hasNext()) {
+@@ -1295,7 +1295,7 @@ public abstract class PlayerList {
+
+ public void setSimulationDistance(int simulationDistance) {
+ this.simulationDistance = simulationDistance;
+- this.broadcastAll(new ClientboundSetSimulationDistancePacket(simulationDistance));
++ //this.broadcastAll(new ClientboundSetSimulationDistancePacket(simulationDistance)); // Paper - handled by playerchunkloader
+ Iterator iterator = this.server.getAllLevels().iterator();
+
+ while (iterator.hasNext()) {
+diff --git a/src/main/java/net/minecraft/util/SortedArraySet.java b/src/main/java/net/minecraft/util/SortedArraySet.java
+index d1b2ba24ef54e01c6249c3b2ca16e80f03c001a6..5fcbb4fd003603408c48408230a17b692fabd519 100644
+--- a/src/main/java/net/minecraft/util/SortedArraySet.java
++++ b/src/main/java/net/minecraft/util/SortedArraySet.java
+@@ -22,6 +22,41 @@ public class SortedArraySet<T> extends AbstractSet<T> {
+ this.contents = (T[])castRawArray(new Object[initialCapacity]);
+ }
+ }
++ // Paper start - optimise removeIf
++ @Override
++ public boolean removeIf(java.util.function.Predicate<? super T> filter) {
++ // prev. impl used an iterator, which could be n^2 and creates garbage
++ int i = 0, len = this.size;
++ T[] backingArray = this.contents;
++
++ for (;;) {
++ if (i >= len) {
++ return false;
++ }
++ if (!filter.test(backingArray[i])) {
++ ++i;
++ continue;
++ }
++ break;
++ }
++
++ // we only want to write back to backingArray if we really need to
++
++ int lastIndex = i; // this is where new elements are shifted to
++
++ for (; i < len; ++i) {
++ T curr = backingArray[i];
++ if (!filter.test(curr)) { // if test throws we're screwed
++ backingArray[lastIndex++] = curr;
++ }
++ }
++
++ // cleanup end
++ Arrays.fill(backingArray, lastIndex, len, null);
++ this.size = lastIndex;
++ return true;
++ }
++ // Paper end - optimise removeIf
+
+ public static <T extends Comparable<T>> SortedArraySet<T> create() {
+ return create(10);
+@@ -110,6 +145,31 @@ public class SortedArraySet<T> extends AbstractSet<T> {
+ }
+ }
+
++ // Paper start - rewrite chunk system
++ public T replace(T object) {
++ int i = this.findIndex(object);
++ if (i >= 0) {
++ T old = this.contents[i];
++ this.contents[i] = object;
++ return old;
++ } else {
++ this.addInternal(object, getInsertionPosition(i));
++ return object;
++ }
++ }
++
++ public T removeAndGet(T object) {
++ int i = this.findIndex(object);
++ if (i >= 0) {
++ final T ret = this.contents[i];
++ this.removeInternal(i);
++ return ret;
++ } else {
++ return null;
++ }
++ }
++ // Paper end - rewrite chunk system
++
+ @Override
+ public boolean remove(Object object) {
+ int i = this.findIndex((T)object);
+diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
+index 821052e93ee753db6aaf499bbf39dc30598fe72f..2955c1ee153c410ea45fe367bac8597621c9bbd0 100644
+--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
++++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
+@@ -182,7 +182,11 @@ public class WorldUpgrader {
+ }
+
+ WorldUpgrader.LOGGER.error("Error upgrading chunk {}", chunkcoordintpair, throwable);
++ // Paper start
++ } catch (IOException e) {
++ WorldUpgrader.LOGGER.error("Error upgrading chunk {}", chunkcoordintpair, e);
+ }
++ // Paper end
+
+ if (flag1) {
+ ++this.converted;
+diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
+index 1a44c98b69398ba5dcb4115f0e8fdcf3f62fd920..1a2ee5824c6af6b548e7006d583b73f4eba0f64a 100644
+--- a/src/main/java/net/minecraft/world/entity/Entity.java
++++ b/src/main/java/net/minecraft/world/entity/Entity.java
+@@ -319,6 +319,58 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ }
+ // Paper end
+
++ // Paper start
++ /**
++ * Overriding this field will cause memory leaks.
++ */
++ private final boolean hardCollides;
++
++ private static final java.util.Map<Class<? extends Entity>, Boolean> cachedOverrides = java.util.Collections.synchronizedMap(new java.util.WeakHashMap<>());
++ {
++ /* // Goodbye, broken on reobf...
++ Boolean hardCollides = cachedOverrides.get(this.getClass());
++ if (hardCollides == null) {
++ try {
++ java.lang.reflect.Method getHardCollisionBoxEntityMethod = Entity.class.getMethod("canCollideWith", Entity.class);
++ java.lang.reflect.Method hasHardCollisionBoxMethod = Entity.class.getMethod("canBeCollidedWith");
++ if (!this.getClass().getMethod(hasHardCollisionBoxMethod.getName(), hasHardCollisionBoxMethod.getParameterTypes()).equals(hasHardCollisionBoxMethod)
++ || !this.getClass().getMethod(getHardCollisionBoxEntityMethod.getName(), getHardCollisionBoxEntityMethod.getParameterTypes()).equals(getHardCollisionBoxEntityMethod)) {
++ hardCollides = Boolean.TRUE;
++ } else {
++ hardCollides = Boolean.FALSE;
++ }
++ cachedOverrides.put(this.getClass(), hardCollides);
++ }
++ catch (ThreadDeath thr) { throw thr; }
++ catch (Throwable thr) {
++ // shouldn't happen, just explode
++ throw new RuntimeException(thr);
++ }
++ } */
++ this.hardCollides = this instanceof Boat
++ || this instanceof net.minecraft.world.entity.monster.Shulker
++ || this instanceof net.minecraft.world.entity.vehicle.AbstractMinecart
++ || this.shouldHardCollide();
++ }
++
++ // plugins can override
++ protected boolean shouldHardCollide() {
++ return false;
++ }
++
++ public final boolean hardCollides() {
++ return this.hardCollides;
++ }
++
++ public net.minecraft.server.level.ChunkHolder.FullChunkStatus chunkStatus;
++
++ public int sectionX = Integer.MIN_VALUE;
++ public int sectionY = Integer.MIN_VALUE;
++ public int sectionZ = Integer.MIN_VALUE;
++
++ public boolean updatingSectionStatus = false;
++ // Paper end
++
+ public Entity(EntityType<?> type, Level world) {
+ this.id = Entity.ENTITY_COUNTER.incrementAndGet();
+ this.passengers = ImmutableList.of();
+@@ -2106,11 +2158,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ return InteractionResult.PASS;
+ }
+
+- public boolean canCollideWith(Entity other) {
++ public boolean canCollideWith(Entity other) { // Paper - diff on change, hard colliding entities override this - TODO CHECK ON UPDATE - AbstractMinecart/Boat override
+ return other.canBeCollidedWith() && !this.isPassengerOfSameVehicle(other);
+ }
+
+- public boolean canBeCollidedWith() {
++ public boolean canBeCollidedWith() { // Paper - diff on change, hard colliding entities override this TODO CHECK ON UPDATE - Boat/Shulker override
+ return false;
+ }
+
+@@ -3345,6 +3397,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ };
+ }
+
++ // Paper start - rewrite chunk system
++ public boolean hasAnyPlayerPassengers() {
++ // copied from below
++ if (this.passengers.isEmpty()) { return false; }
++ return this.getIndirectPassengersStream().anyMatch((entity) -> {
++ return entity instanceof Player;
++ });
++ }
++ // Paper end - rewrite chunk system
++
+ public boolean hasExactlyOnePlayerPassenger() {
+ return this.getIndirectPassengersStream().filter((entity) -> {
+ return entity instanceof Player;
+@@ -3647,6 +3709,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ }
+
+ public final void setPosRaw(double x, double y, double z) {
++ // Paper start - rewrite chunk system
++ if (this.updatingSectionStatus) {
++ LOGGER.error("Refusing to update position for entity " + this + " to position " + new Vec3(x, y, z) + " since it is processing a section status update", new Throwable());
++ return;
++ }
++ // Paper end - rewrite chunk system
+ if (this.position.x != x || this.position.y != y || this.position.z != z) {
+ this.position = new Vec3(x, y, z);
+ int i = Mth.floor(x);
+@@ -3742,6 +3810,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+
+ @Override
+ public final void setRemoved(Entity.RemovalReason reason) {
++ // Paper start - rewrite chunk system
++ io.papermc.paper.util.TickThread.ensureTickThread(this, "Cannot remove entity off-main");
++ if (this.updatingSectionStatus) {
++ LOGGER.warn("Entity " + this + " is currently prevented from being added/removed to world since it is processing section status updates", new Throwable());
++ return;
++ }
++ // Paper end - rewrite chunk system
+ if (this.removalReason == null) {
+ this.removalReason = reason;
+ }
+@@ -3750,7 +3825,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ this.stopRiding();
+ }
+
+- this.getPassengers().forEach(Entity::stopRiding);
++ if (reason != RemovalReason.UNLOADED_TO_CHUNK) this.getPassengers().forEach(Entity::stopRiding); // Paper - chunk system - don't adjust passenger state when unloading, it's just not safe (and messes with our logic in entity chunk unload)
+ this.levelCallback.onRemove(reason);
+ }
+
+@@ -3765,7 +3840,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+
+ @Override
+ public boolean shouldBeSaved() {
+- return this.removalReason != null && !this.removalReason.shouldSave() ? false : (this.isPassenger() ? false : !this.isVehicle() || !this.hasExactlyOnePlayerPassenger());
++ return this.removalReason != null && !this.removalReason.shouldSave() ? false : (this.isPassenger() ? false : !this.isVehicle() || !this.hasAnyPlayerPassengers()); // Paper - rewrite chunk system - it should check if the entity has ANY player passengers
+ }
+
+ @Override
+diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+index db4fa7355b1f834d0f8a0710c1c583dded184613..8938e04ffab2d31e64b80879d21d9f1eb90c20bb 100644
+--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+@@ -38,12 +38,28 @@ import net.minecraft.world.level.chunk.storage.SectionStorage;
+ public class PoiManager extends SectionStorage<PoiSection> {
+ public static final int MAX_VILLAGE_DISTANCE = 6;
+ public static final int VILLAGE_SECTION_SIZE = 1;
+- private final PoiManager.DistanceTracker distanceTracker;
+- private final LongSet loadedChunks = new LongOpenHashSet();
++ // Paper start - rewrite chunk system
++ // the vanilla tracker needs to be replaced because it does not support level removes
++ public final net.minecraft.server.level.ServerLevel world;
++ private final io.papermc.paper.util.misc.Delayed26WayDistancePropagator3D villageDistanceTracker = new io.papermc.paper.util.misc.Delayed26WayDistancePropagator3D();
++ static final int POI_DATA_SOURCE = 7;
++ public static int convertBetweenLevels(final int level) {
++ return POI_DATA_SOURCE - level;
++ }
++
++ protected void updateDistanceTracking(long section) {
++ if (this.isVillageCenter(section)) {
++ this.villageDistanceTracker.setSource(section, POI_DATA_SOURCE);
++ } else {
++ this.villageDistanceTracker.removeSource(section);
++ }
++ }
++ // Paper end - rewrite chunk system
++
+
+ public PoiManager(Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) {
+ super(path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world);
+- this.distanceTracker = new PoiManager.DistanceTracker();
++ this.world = (net.minecraft.server.level.ServerLevel)world; // Paper - rewrite chunk system
+ }
+
+ public void add(BlockPos pos, Holder<PoiType> type) {
+@@ -180,8 +196,8 @@ public class PoiManager extends SectionStorage<PoiSection> {
+ }
+
+ public int sectionsToVillage(SectionPos pos) {
+- this.distanceTracker.runAllUpdates();
+- return this.distanceTracker.getLevel(pos.asLong());
++ this.villageDistanceTracker.propagateUpdates(); // Paper - replace distance tracking util
++ return convertBetweenLevels(this.villageDistanceTracker.getLevel(io.papermc.paper.util.CoordinateUtils.getChunkSectionKey(pos))); // Paper - replace distance tracking util
+ }
+
+ boolean isVillageCenter(long pos) {
+@@ -195,21 +211,106 @@ public class PoiManager extends SectionStorage<PoiSection> {
+
+ @Override
+ public void tick(BooleanSupplier shouldKeepTicking) {
+- super.tick(shouldKeepTicking);
+- this.distanceTracker.runAllUpdates();
++ this.villageDistanceTracker.propagateUpdates(); // Paper - rewrite chunk system
+ }
+
+ @Override
+- protected void setDirty(long pos) {
+- super.setDirty(pos);
+- this.distanceTracker.update(pos, this.distanceTracker.getLevelFromSource(pos), false);
++ public void setDirty(long pos) {
++ // Paper start - rewrite chunk system
++ int chunkX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(pos);
++ int chunkZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(pos);
++ io.papermc.paper.chunk.system.scheduling.ChunkHolderManager manager = this.world.chunkTaskScheduler.chunkHolderManager;
++ io.papermc.paper.chunk.system.poi.PoiChunk chunk = manager.getPoiChunkIfLoaded(chunkX, chunkZ, false);
++ if (chunk != null) {
++ chunk.setDirty(true);
++ }
++ this.updateDistanceTracking(pos);
++ // Paper end - rewrite chunk system
+ }
+
+ @Override
+ protected void onSectionLoad(long pos) {
+- this.distanceTracker.update(pos, this.distanceTracker.getLevelFromSource(pos), false);
++ this.updateDistanceTracking(pos); // Paper - move to new distance tracking util
+ }
+
++ @Override
++ public Optional<PoiSection> get(long pos) {
++ int chunkX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(pos);
++ int chunkY = io.papermc.paper.util.CoordinateUtils.getChunkSectionY(pos);
++ int chunkZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(pos);
++
++ io.papermc.paper.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
++
++ io.papermc.paper.chunk.system.scheduling.ChunkHolderManager manager = this.world.chunkTaskScheduler.chunkHolderManager;
++ io.papermc.paper.chunk.system.poi.PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
++
++ return ret == null ? Optional.empty() : ret.getSectionForVanilla(chunkY);
++ }
++
++ @Override
++ public Optional<PoiSection> getOrLoad(long pos) {
++ int chunkX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(pos);
++ int chunkY = io.papermc.paper.util.CoordinateUtils.getChunkSectionY(pos);
++ int chunkZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(pos);
++
++ io.papermc.paper.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
++
++ io.papermc.paper.chunk.system.scheduling.ChunkHolderManager manager = this.world.chunkTaskScheduler.chunkHolderManager;
++
++ if (chunkY >= io.papermc.paper.util.WorldUtil.getMinSection(this.world) &&
++ chunkY <= io.papermc.paper.util.WorldUtil.getMaxSection(this.world)) {
++ io.papermc.paper.chunk.system.poi.PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
++ if (ret != null) {
++ return ret.getSectionForVanilla(chunkY);
++ } else {
++ return manager.loadPoiChunk(chunkX, chunkZ).getSectionForVanilla(chunkY);
++ }
++ }
++ // retain vanilla behavior: do not load section if out of bounds!
++ return Optional.empty();
++ }
++
++ @Override
++ protected PoiSection getOrCreate(long pos) {
++ int chunkX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(pos);
++ int chunkY = io.papermc.paper.util.CoordinateUtils.getChunkSectionY(pos);
++ int chunkZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(pos);
++
++ io.papermc.paper.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
++
++ io.papermc.paper.chunk.system.scheduling.ChunkHolderManager manager = this.world.chunkTaskScheduler.chunkHolderManager;
++
++ io.papermc.paper.chunk.system.poi.PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
++ if (ret != null) {
++ return ret.getOrCreateSection(chunkY);
++ } else {
++ return manager.loadPoiChunk(chunkX, chunkZ).getOrCreateSection(chunkY);
++ }
++ }
++
++ public void onUnload(long coordinate) { // Paper - rewrite chunk system
++ int chunkX = net.minecraft.server.MCUtil.getCoordinateX(coordinate);
++ int chunkZ = net.minecraft.server.MCUtil.getCoordinateZ(coordinate);
++ io.papermc.paper.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Unloading poi chunk off-main");
++ for (int section = this.levelHeightAccessor.getMinSection(); section < this.levelHeightAccessor.getMaxSection(); ++section) {
++ long sectionPos = SectionPos.asLong(chunkX, section, chunkZ);
++ this.updateDistanceTracking(sectionPos);
++ }
++ }
++
++ public void loadInPoiChunk(io.papermc.paper.chunk.system.poi.PoiChunk poiChunk) {
++ int chunkX = poiChunk.chunkX;
++ int chunkZ = poiChunk.chunkZ;
++ io.papermc.paper.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Loading poi chunk off-main");
++ for (int sectionY = this.levelHeightAccessor.getMinSection(); sectionY < this.levelHeightAccessor.getMaxSection(); ++sectionY) {
++ PoiSection section = poiChunk.getSection(sectionY);
++ if (section != null && !section.isEmpty()) {
++ this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
++ }
++ }
++ }
++ // Paper end - rewrite chunk system
++
+ public void checkConsistencyWithBlocks(ChunkPos chunkPos, LevelChunkSection chunkSection) {
+ SectionPos sectionPos = SectionPos.of(chunkPos, SectionPos.blockToSectionCoord(chunkSection.bottomBlockY()));
+ Util.ifElse(this.getOrLoad(sectionPos.asLong()), (poiSet) -> {
+@@ -249,7 +350,7 @@ public class PoiManager extends SectionStorage<PoiSection> {
+ }).map((pair) -> {
+ return pair.getFirst().chunk();
+ }).filter((chunkPos) -> {
+- return this.loadedChunks.add(chunkPos.toLong());
++ return true; // Paper - rewrite chunk system
+ }).forEach((chunkPos) -> {
+ world.getChunk(chunkPos.x, chunkPos.z, ChunkStatus.EMPTY);
+ });
+@@ -265,7 +366,7 @@ public class PoiManager extends SectionStorage<PoiSection> {
+
+ @Override
+ protected int getLevelFromSource(long id) {
+- return PoiManager.this.isVillageCenter(id) ? 0 : 7;
++ return PoiManager.this.isVillageCenter(id) ? 0 : 7; // Paper - rewrite chunk system - diff on change, this specifies the source level to use for distance tracking
+ }
+
+ @Override
+@@ -288,6 +389,35 @@ public class PoiManager extends SectionStorage<PoiSection> {
+ }
+ }
+
++ // Paper start - Asynchronous chunk io
++ @javax.annotation.Nullable
++ @Override
++ public net.minecraft.nbt.CompoundTag read(ChunkPos chunkcoordintpair) throws java.io.IOException {
++ // Paper start - rewrite chunk system
++ if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
++ return io.papermc.paper.chunk.system.io.RegionFileIOThread.loadData(
++ this.world, chunkcoordintpair.x, chunkcoordintpair.z, io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA,
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.getIOBlockingPriorityForCurrentThread()
++ );
++ }
++ // Paper end - rewrite chunk system
++ return super.read(chunkcoordintpair);
++ }
++
++ @Override
++ public void write(ChunkPos chunkcoordintpair, net.minecraft.nbt.CompoundTag nbttagcompound) throws java.io.IOException {
++ // Paper start - rewrite chunk system
++ if (!io.papermc.paper.chunk.system.io.RegionFileIOThread.isRegionFileThread()) {
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.scheduleSave(
++ this.world, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound,
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.RegionFileType.POI_DATA);
++ return;
++ }
++ // Paper end - rewrite chunk system
++ super.write(chunkcoordintpair, nbttagcompound);
++ }
++ // Paper end
++
+ public static enum Occupancy {
+ HAS_SPACE(PoiRecord::hasSpace),
+ IS_OCCUPIED(PoiRecord::isOccupied),
+diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+index bb2be6eea7a0cff4cc70bd43738b1ce213e43558..d0ce7b14d29459e276961c38cfc5b5da1cd15634 100644
+--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+@@ -29,6 +29,7 @@ public class PoiSection {
+ private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newHashMap();
+ private final Runnable setDirty;
+ private boolean isValid;
++ public final Optional<PoiSection> noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system
+
+ public static Codec<PoiSection> codec(Runnable updateListener) {
+ return RecordCodecBuilder.<PoiSection>create((instance) -> { // Paper - decompile fix
+@@ -46,6 +47,12 @@ public class PoiSection {
+ this(updateListener, true, ImmutableList.of());
+ }
+
++ // Paper start - isEmpty
++ public boolean isEmpty() {
++ return this.isValid && this.records.isEmpty() && this.byType.isEmpty();
++ }
++ // Paper end
++
+ private PoiSection(Runnable updateListener, boolean valid, List<PoiRecord> pois) {
+ this.setDirty = updateListener;
+ this.isValid = valid;
+diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java
+index 7c5918f84d2b8f9c778258b7e7d745105effb082..f62e4e36dd26bde067e4787fd0da1440b15a57fa 100644
+--- a/src/main/java/net/minecraft/world/level/EntityGetter.java
++++ b/src/main/java/net/minecraft/world/level/EntityGetter.java
+@@ -18,6 +18,18 @@ import net.minecraft.world.phys.shapes.Shapes;
+ import net.minecraft.world.phys.shapes.VoxelShape;
+
+ public interface EntityGetter {
++
++ // Paper start
++ List<Entity> getHardCollidingEntities(Entity except, AABB box, Predicate<? super Entity> predicate);
++
++ void getEntities(Entity except, AABB box, Predicate<? super Entity> predicate, List<Entity> into);
++
++ void getHardCollidingEntities(Entity except, AABB box, Predicate<? super Entity> predicate, List<Entity> into);
++
++ <T> void getEntitiesByClass(Class<? extends T> clazz, Entity except, final AABB box, List<? super T> into,
++ Predicate<? super T> predicate);
++ // Paper end
++
+ List<Entity> getEntities(@Nullable Entity except, AABB box, Predicate<? super Entity> predicate);
+
+ <T extends Entity> List<T> getEntities(EntityTypeTest<Entity, T> filter, AABB box, Predicate<? super T> predicate);
+diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
+index 39d64f3aeb998df5452699e098148d86fdd48c98..c12e2ecaea13597f56254e3ab7fd83bff129ddd3 100644
+--- a/src/main/java/net/minecraft/world/level/Level.java
++++ b/src/main/java/net/minecraft/world/level/Level.java
+@@ -450,6 +450,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+
+ if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || (chunk.getFullStatus() != null && chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING)))) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement
+ this.sendBlockUpdated(blockposition, iblockdata1, iblockdata, i);
++ // Paper start - per player view distance - allow block updates for non-ticking chunks in player view distance
++ // if copied from above
++ } else if ((i & 2) != 0 && (!this.isClientSide || (i & 4) == 0) && (this.isClientSide || chunk == null || ((ServerLevel)this).getChunkSource().chunkMap.playerChunkManager.broadcastMap.getObjectsInRange(MCUtil.getCoordinateKey(blockposition)) != null)) { // Paper - replace old player chunk management
++ ((ServerLevel)this).getChunkSource().blockChanged(blockposition);
++ // Paper end - per player view distance
+ }
+
+ if ((i & 1) != 0) {
+@@ -748,7 +753,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+ return this.capturedTileEntities.get(blockposition);
+ }
+ // CraftBukkit end
+- return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE));
++ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system
+ }
+
+ public void setBlockEntity(BlockEntity blockEntity) {
+@@ -839,26 +844,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+ public List<Entity> getEntities(@Nullable Entity except, AABB box, Predicate<? super Entity> predicate) {
+ this.getProfiler().incrementCounter("getEntities");
+ List<Entity> list = Lists.newArrayList();
+-
+- this.getEntities().get(box, (entity1) -> {
+- if (entity1 != except && predicate.test(entity1)) {
+- list.add(entity1);
+- }
+-
+- if (entity1 instanceof EnderDragon) {
+- EnderDragonPart[] aentitycomplexpart = ((EnderDragon) entity1).getSubEntities();
+- int i = aentitycomplexpart.length;
+-
+- for (int j = 0; j < i; ++j) {
+- EnderDragonPart entitycomplexpart = aentitycomplexpart[j];
+-
+- if (entity1 != except && predicate.test(entitycomplexpart)) {
+- list.add(entitycomplexpart);
+- }
+- }
+- }
+-
+- });
++ ((ServerLevel)this).getEntityLookup().getEntities(except, box, list, predicate); // Paper - optimise this call
+ return list;
+ }
+
+@@ -867,27 +853,22 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+ this.getProfiler().incrementCounter("getEntities");
+ List<T> list = Lists.newArrayList();
+
+- this.getEntities().get(filter, box, (entity) -> {
+- if (predicate.test(entity)) {
+- list.add(entity);
+- }
+-
+- if (entity instanceof EnderDragon) {
+- EnderDragon entityenderdragon = (EnderDragon) entity;
+- EnderDragonPart[] aentitycomplexpart = entityenderdragon.getSubEntities();
+- int i = aentitycomplexpart.length;
+-
+- for (int j = 0; j < i; ++j) {
+- EnderDragonPart entitycomplexpart = aentitycomplexpart[j];
+- T t0 = filter.tryCast(entitycomplexpart); // CraftBukkit - decompile error
+-
+- if (t0 != null && predicate.test(t0)) {
+- list.add(t0);
+- }
+- }
++ // Paper start - optimise this call
++ if (filter instanceof net.minecraft.world.entity.EntityType) {
++ ((ServerLevel)this).getEntityLookup().getEntities((net.minecraft.world.entity.EntityType)filter, box, list, predicate);
++ } else {
++ Predicate<? super T> test = (obj) -> {
++ return filter.tryCast(obj) != null;
++ };
++ predicate = predicate == null ? test : test.and((Predicate)predicate);
++ Class base;
++ if (filter == null || (base = filter.getBaseClass()) == null || base == Entity.class) {
++ ((ServerLevel)this).getEntityLookup().getEntities((Entity) null, box, (List)list, (Predicate)predicate);
++ } else {
++ ((ServerLevel)this).getEntityLookup().getEntities(base, null, box, (List)list, (Predicate)predicate); // Paper - optimise this call
+ }
+-
+- });
++ }
++ // Paper end - optimise this call
+ return list;
+ }
+
+@@ -1214,4 +1195,46 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+ public long nextSubTickCount() {
+ return (long) (this.subTickCount++);
+ }
++
++ // Paper start
++ //protected final io.papermc.paper.world.EntitySliceManager entitySliceManager; // Paper - rewrite chunk system
++
++ public org.bukkit.entity.Entity[] getChunkEntities(int chunkX, int chunkZ) {
++ io.papermc.paper.world.ChunkEntitySlices slices = ((ServerLevel)this).getEntityLookup().getChunk(chunkX, chunkZ);
++ if (slices == null) {
++ return new org.bukkit.entity.Entity[0];
++ }
++ return slices.getChunkEntities();
++ }
++
++ @Override
++ public List<Entity> getHardCollidingEntities(Entity except, AABB box, Predicate<? super Entity> predicate) {
++ List<Entity> ret = new java.util.ArrayList<>();
++ ((ServerLevel)this).getEntityLookup().getHardCollidingEntities(except, box, ret, predicate);
++ return ret;
++ }
++
++ @Override
++ public void getEntities(Entity except, AABB box, Predicate<? super Entity> predicate, List<Entity> into) {
++ ((ServerLevel)this).getEntityLookup().getEntities(except, box, into, predicate);
++ }
++
++ @Override
++ public void getHardCollidingEntities(Entity except, AABB box, Predicate<? super Entity> predicate, List<Entity> into) {
++ ((ServerLevel)this).getEntityLookup().getHardCollidingEntities(except, box, into, predicate);
++ }
++
++ @Override
++ public <T> void getEntitiesByClass(Class<? extends T> clazz, Entity except, final AABB box, List<? super T> into,
++ Predicate<? super T> predicate) {
++ ((ServerLevel)this).getEntityLookup().getEntities((Class)clazz, except, box, (List)into, (Predicate)predicate);
++ }
++
++ @Override
++ public <T extends Entity> List<T> getEntitiesOfClass(Class<T> entityClass, AABB box, Predicate<? super T> predicate) {
++ List<T> ret = new java.util.ArrayList<>();
++ ((ServerLevel)this).getEntityLookup().getEntities(entityClass, null, box, ret, predicate);
++ return ret;
++ }
++ // Paper end
+ }
+diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+index 2b543472302faf4b6298efe9bc44c023051f4997..f284eefe5de6ad8b42f7d4185fc4c3b8b2cd5895 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+@@ -288,7 +288,7 @@ public abstract class ChunkGenerator {
+ return CompletableFuture.supplyAsync(Util.wrapThreadWithTaskName("init_biomes", () -> {
+ chunk.fillBiomesFromNoise(this.biomeSource, noiseConfig.sampler());
+ return chunk;
+- }), Util.backgroundExecutor());
++ }), executor); // Paper - run with supplied executor
+ }
+
+ public abstract void applyCarvers(WorldGenRegion chunkRegion, long seed, RandomState noiseConfig, BiomeManager biomeAccess, StructureManager structureAccessor, ChunkAccess chunk, GenerationStep.Carving carverStep);
+diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
+index e6240f891e396d91e31b02fdf3084be77e9d6697..00cb9dafc711607f28529ea9afbcdb492b1b2595 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
+@@ -29,6 +29,30 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
+
+ public class ChunkStatus {
+
++ // Paper start - rewrite chunk system
++ public boolean isParallelCapable; // Paper
++ public int writeRadius = -1;
++ public int loadRange = 0;
++
++ protected static final java.util.List<ChunkStatus> statuses = new java.util.ArrayList<>();
++
++ private ChunkStatus nextStatus;
++
++ public final ChunkStatus getNextStatus() {
++ return this.nextStatus;
++ }
++
++ public final boolean isEmptyLoadStatus() {
++ return this.loadingTask == PASSTHROUGH_LOAD_TASK;
++ }
++
++ public final boolean isEmptyGenStatus() {
++ return this == ChunkStatus.EMPTY || this == ChunkStatus.HEIGHTMAPS || this == ChunkStatus.LIQUID_CARVERS;
++ }
++
++ public final java.util.concurrent.atomic.AtomicBoolean warnedAboutNoImmediateComplete = new java.util.concurrent.atomic.AtomicBoolean();
++ // Paper end - rewrite chunk system
++
+ public static final int MAX_STRUCTURE_DISTANCE = 8;
+ private static final EnumSet<Heightmap.Types> PRE_FEATURES = EnumSet.of(Heightmap.Types.OCEAN_FLOOR_WG, Heightmap.Types.WORLD_SURFACE_WG);
+ public static final EnumSet<Heightmap.Types> POST_FEATURES = EnumSet.of(Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.WORLD_SURFACE, Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES);
+@@ -150,10 +174,8 @@ public class ChunkStatus {
+ protochunk.setStatus(chunkstatus);
+ }
+
+- return lightenginethreaded.retainData(ichunkaccess).thenApply(Either::left);
+- }, (chunkstatus, worldserver, structuretemplatemanager, lightenginethreaded, function, ichunkaccess) -> {
+- return lightenginethreaded.retainData(ichunkaccess).thenApply(Either::left);
+- });
++ return CompletableFuture.completedFuture(Either.left(ichunkaccess)); // Paper - rewrite chunk system
++ }); // Paper - rewrite chunk system
+ public static final ChunkStatus LIGHT = ChunkStatus.register("light", ChunkStatus.FEATURES, 1, ChunkStatus.POST_FEATURES, ChunkStatus.ChunkType.PROTOCHUNK, (chunkstatus, executor, worldserver, chunkgenerator, structuretemplatemanager, lightenginethreaded, function, list, ichunkaccess, flag) -> {
+ return ChunkStatus.lightChunk(chunkstatus, lightenginethreaded, ichunkaccess);
+ }, (chunkstatus, worldserver, structuretemplatemanager, lightenginethreaded, function, ichunkaccess) -> {
+@@ -255,6 +277,13 @@ public class ChunkStatus {
+ this.chunkType = chunkType;
+ this.heightmapsAfter = heightMapTypes;
+ this.index = previous == null ? 0 : previous.getIndex() + 1;
++ // Paper start
++ this.nextStatus = this;
++ if (statuses.size() > 0) {
++ statuses.get(statuses.size() - 1).nextStatus = this;
++ }
++ statuses.add(this);
++ // Paper end
+ }
+
+ public int getIndex() {
+diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+index e75ec8f6aa597b5f3048d6269fba45eef057bc71..8b65e8361918b5e6fe936fec99ee63ff9eef66f0 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+@@ -183,6 +183,43 @@ public class LevelChunk extends ChunkAccess {
+
+ protected void onNeighbourChange(final long bitsetBefore, final long bitsetAfter) {
+
++ // Paper start - no-tick view distance
++ ServerChunkCache chunkProviderServer = ((ServerLevel)this.level).getChunkSource();
++ net.minecraft.server.level.ChunkMap chunkMap = chunkProviderServer.chunkMap;
++ // this code handles the addition of ticking tickets - the distance map handles the removal
++ if (!areNeighboursLoaded(bitsetBefore, 2) && areNeighboursLoaded(bitsetAfter, 2)) {
++ if (chunkMap.playerChunkManager.tickMap.getObjectsInRange(this.coordinateKey) != null) { // Paper - replace old player chunk loading system
++ // now we're ready for entity ticking
++ chunkProviderServer.mainThreadProcessor.execute(() -> {
++ // double check that this condition still holds.
++ if (LevelChunk.this.areNeighboursLoaded(2) && chunkMap.playerChunkManager.tickMap.getObjectsInRange(LevelChunk.this.coordinateKey) != null) { // Paper - replace old player chunk loading system
++ chunkMap.playerChunkManager.onChunkPlayerTickReady(this.chunkPos.x, this.chunkPos.z); // Paper - replace old player chunk
++ chunkProviderServer.addTicketAtLevel(net.minecraft.server.level.TicketType.PLAYER, LevelChunk.this.chunkPos, 31, LevelChunk.this.chunkPos); // 31 -> entity ticking, TODO check on update
++ }
++ });
++ }
++ }
++
++ // this code handles the chunk sending
++ if (!areNeighboursLoaded(bitsetBefore, 1) && areNeighboursLoaded(bitsetAfter, 1)) {
++ // Paper start - replace old player chunk loading system
++ if (chunkMap.playerChunkManager.isChunkNearPlayers(this.chunkPos.x, this.chunkPos.z)) {
++ // the post processing is expensive, so we don't want to run it unless we're actually near
++ // a player.
++ chunkProviderServer.mainThreadProcessor.execute(() -> {
++ if (!LevelChunk.this.areNeighboursLoaded(1)) {
++ return;
++ }
++ LevelChunk.this.postProcessGeneration();
++ if (!LevelChunk.this.areNeighboursLoaded(1)) {
++ return;
++ }
++ chunkMap.playerChunkManager.onChunkSendReady(this.chunkPos.x, this.chunkPos.z);
++ });
++ }
++ // Paper end - replace old player chunk loading system
++ }
++ // Paper end - no-tick view distance
+ }
+
+ public final boolean isAnyNeighborsLoaded() {
+@@ -660,8 +697,67 @@ public class LevelChunk extends ChunkAccess {
+
+ }
+
++ // Paper start - new load callbacks
++ private io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder;
++ public io.papermc.paper.chunk.system.scheduling.NewChunkHolder getChunkHolder() {
++ return this.chunkHolder;
++ }
++
++ public void setChunkHolder(io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder) {
++ if (chunkHolder == null) {
++ throw new NullPointerException("Chunkholder cannot be null");
++ }
++ if (this.chunkHolder != null) {
++ throw new IllegalStateException("Already have chunkholder: " + this.chunkHolder + ", cannot replace with " + chunkHolder);
++ }
++ this.chunkHolder = chunkHolder;
++ this.playerChunk = chunkHolder.vanillaChunkHolder;
++ }
++
++ /* Note: We skip the light neighbour chunk loading done for the vanilla full chunk */
++ /* Starlight does not need these chunks for lighting purposes because of edge checks */
++
++ public void onChunkLoad(io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder) {
++ // figure out how this should interface with:
++ // the entity chunk load event // -> moved to the FULL status
++ // the chunk load event // -> stays here
++ // any entity add to world events // -> in FULL status
++ this.loadCallback();
++ net.minecraft.server.ChunkSystem.onChunkBorder(this, chunkHolder.vanillaChunkHolder);
++ }
++
++ public void onChunkUnload(io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder) {
++ // figure out how this should interface with:
++ // the entity chunk load event // -> moved to chunk unload to disk (not written yet)
++ // the chunk load event // -> stays here
++ // any entity add to world events // -> goes into the unload logic, it will completely explode
++ // etc later
++ this.unloadCallback();
++ net.minecraft.server.ChunkSystem.onChunkNotBorder(this, chunkHolder.vanillaChunkHolder);
++ }
++
++ public void onChunkTicking(io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder) {
++ this.postProcessGeneration();
++ this.level.startTickingChunk(this);
++ net.minecraft.server.ChunkSystem.onChunkTicking(this, chunkHolder.vanillaChunkHolder);
++ }
++
++ public void onChunkNotTicking(io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder) {
++ net.minecraft.server.ChunkSystem.onChunkNotTicking(this, chunkHolder.vanillaChunkHolder);
++ }
++
++ public void onChunkEntityTicking(io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder) {
++ net.minecraft.server.ChunkSystem.onChunkEntityTicking(this, chunkHolder.vanillaChunkHolder);
++ }
++
++ public void onChunkNotEntityTicking(io.papermc.paper.chunk.system.scheduling.NewChunkHolder chunkHolder) {
++ net.minecraft.server.ChunkSystem.onChunkNotEntityTicking(this, chunkHolder.vanillaChunkHolder);
++ }
++ // Paper end - new load callbacks
++
+ // CraftBukkit start
+ public void loadCallback() {
++ if (this.loadedTicketLevel) { LOGGER.error("Double calling chunk load!", new Throwable()); } // Paper
+ // Paper start - neighbour cache
+ int chunkX = this.chunkPos.x;
+ int chunkZ = this.chunkPos.z;
+@@ -681,6 +777,7 @@ public class LevelChunk extends ChunkAccess {
+ // Paper end - neighbour cache
+ org.bukkit.Server server = this.level.getCraftServer();
+ this.level.getChunkSource().addLoadedChunk(this); // Paper
++ ((ServerLevel)this.level).getChunkSource().chunkMap.playerChunkManager.onChunkLoad(this.chunkPos.x, this.chunkPos.z); // Paper - rewrite player chunk management
+ if (server != null) {
+ /*
+ * If it's a new world, the first few chunks are generated inside
+@@ -688,6 +785,7 @@ public class LevelChunk extends ChunkAccess {
+ * no way of creating a CraftWorld/CraftServer at that point.
+ */
+ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(this.bukkitChunk, this.needsDecoration));
++ this.chunkHolder.getEntityChunk().callEntitiesLoadEvent(); // Paper - rewrite chunk system
+
+ if (this.needsDecoration) {
+ try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper
+@@ -716,7 +814,9 @@ public class LevelChunk extends ChunkAccess {
+ }
+
+ public void unloadCallback() {
++ if (!this.loadedTicketLevel) { LOGGER.error("Double calling chunk unload!", new Throwable()); } // Paper
+ org.bukkit.Server server = this.level.getCraftServer();
++ this.chunkHolder.getEntityChunk().callEntitiesUnloadEvent(); // Paper - rewrite chunk system
+ org.bukkit.event.world.ChunkUnloadEvent unloadEvent = new org.bukkit.event.world.ChunkUnloadEvent(this.bukkitChunk, this.isUnsaved());
+ server.getPluginManager().callEvent(unloadEvent);
+ // note: saving can be prevented, but not forced if no saving is actually required
+@@ -804,7 +904,10 @@ public class LevelChunk extends ChunkAccess {
+ });
+ }
+
++ public boolean isPostProcessingDone; // Paper - replace chunk loader system
++
+ public void postProcessGeneration() {
++ try { // Paper - replace chunk loader system
+ ChunkPos chunkcoordintpair = this.getPos();
+
+ for (int i = 0; i < this.postProcessing.length; ++i) {
+@@ -842,6 +945,11 @@ public class LevelChunk extends ChunkAccess {
+
+ this.pendingBlockEntities.clear();
+ this.upgradeData.upgrade(this);
++ } finally { // Paper start - replace chunk loader system
++ this.isPostProcessingDone = true;
++ this.level.getChunkSource().chunkMap.playerChunkManager.onChunkPostProcessing(this.chunkPos.x, this.chunkPos.z);
++ }
++ // Paper end - replace chunk loader system
+ }
+
+ @Nullable
+@@ -891,7 +999,7 @@ public class LevelChunk extends ChunkAccess {
+ }
+
+ public ChunkHolder.FullChunkStatus getFullStatus() {
+- return this.fullStatus == null ? ChunkHolder.FullChunkStatus.BORDER : (ChunkHolder.FullChunkStatus) this.fullStatus.get();
++ return this.chunkHolder == null ? ChunkHolder.FullChunkStatus.INACCESSIBLE : this.chunkHolder.getChunkStatus(); // Paper - rewrite chunk system
+ }
+
+ public void setFullStatus(Supplier<ChunkHolder.FullChunkStatus> levelTypeProvider) {
+diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+index 2dead743775df9b261bdcdca30df9b672c6acc8b..e59910540458ca912efea64d9f7cd212d63d110a 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
++++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+@@ -92,7 +92,31 @@ public class ChunkSerializer {
+
+ public ChunkSerializer() {}
+
++ // Paper start
++ public static final class InProgressChunkHolder {
++
++ public final ProtoChunk protoChunk;
++ public final java.util.ArrayDeque<Runnable> tasks;
++
++ public CompoundTag poiData;
++
++ public InProgressChunkHolder(final ProtoChunk protoChunk, final java.util.ArrayDeque<Runnable> tasks) {
++ this.protoChunk = protoChunk;
++ this.tasks = tasks;
++ }
++ }
++ // Paper end
++
+ public static ProtoChunk read(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt) {
++ // Paper start - add variant for async calls
++ InProgressChunkHolder holder = loadChunk(world, poiStorage, chunkPos, nbt, true);
++ holder.tasks.forEach(Runnable::run);
++ return holder.protoChunk;
++ }
++
++ public static InProgressChunkHolder loadChunk(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt, boolean distinguish) {
++ java.util.ArrayDeque<Runnable> tasksToExecuteOnMain = new java.util.ArrayDeque<>();
++ // Paper end
+ ChunkPos chunkcoordintpair1 = new ChunkPos(nbt.getInt("xPos"), nbt.getInt("zPos"));
+
+ if (!Objects.equals(chunkPos, chunkcoordintpair1)) {
+@@ -156,7 +180,9 @@ public class ChunkSerializer {
+ LevelChunkSection chunksection = new LevelChunkSection(b0, datapaletteblock, (PalettedContainer) object); // CraftBukkit - read/write
+
+ achunksection[k] = chunksection;
++ tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main
+ poiStorage.checkConsistencyWithBlocks(chunkPos, chunksection);
++ }); // Paper - delay this task since we're executing off-main
+ }
+
+ boolean flag3 = nbttagcompound1.contains("BlockLight", 7);
+@@ -317,7 +343,7 @@ public class ChunkSerializer {
+ }
+
+ if (chunkstatus_type == ChunkStatus.ChunkType.LEVELCHUNK) {
+- return new ImposterProtoChunk((LevelChunk) object1, false);
++ return new InProgressChunkHolder(new ImposterProtoChunk((LevelChunk) object1, false), tasksToExecuteOnMain); // Paper - Async chunk loading
+ } else {
+ ProtoChunk protochunk1 = (ProtoChunk) object1;
+
+@@ -356,9 +382,41 @@ public class ChunkSerializer {
+ protochunk1.setCarvingMask(worldgenstage_features, new CarvingMask(nbttagcompound4.getLongArray(s1), ((ChunkAccess) object1).getMinBuildHeight()));
+ }
+
+- return protochunk1;
++ return new InProgressChunkHolder(protochunk1, tasksToExecuteOnMain); // Paper - Async chunk loading
++ }
++ }
++
++ // Paper start - async chunk save for unload
++ public record AsyncSaveData(
++ Tag blockTickList, // non-null if we had to go to the server's tick list
++ Tag fluidTickList, // non-null if we had to go to the server's tick list
++ ListTag blockEntities,
++ long worldTime
++ ) {}
++
++ // must be called sync
++ public static AsyncSaveData getAsyncSaveData(ServerLevel world, ChunkAccess chunk) {
++ org.spigotmc.AsyncCatcher.catchOp("preparation of chunk data for async save");
++
++ final CompoundTag tickLists = new CompoundTag();
++ ChunkSerializer.saveTicks(world, tickLists, chunk.getTicksForSerialization());
++
++ ListTag blockEntitiesSerialized = new ListTag();
++ for (final BlockPos blockPos : chunk.getBlockEntitiesPos()) {
++ final CompoundTag blockEntityNbt = chunk.getBlockEntityNbtForSaving(blockPos);
++ if (blockEntityNbt != null) {
++ blockEntitiesSerialized.add(blockEntityNbt);
++ }
+ }
++
++ return new AsyncSaveData(
++ tickLists.get(BLOCK_TICKS_TAG),
++ tickLists.get(FLUID_TICKS_TAG),
++ blockEntitiesSerialized,
++ world.getGameTime()
++ );
+ }
++ // Paper end
+
+ private static void logErrors(ChunkPos chunkPos, int y, String message) {
+ ChunkSerializer.LOGGER.error("Recoverable errors when loading section [" + chunkPos.x + ", " + y + ", " + chunkPos.z + "]: " + message);
+@@ -375,6 +433,11 @@ public class ChunkSerializer {
+ // CraftBukkit end
+
+ public static CompoundTag write(ServerLevel world, ChunkAccess chunk) {
++ // Paper start
++ return saveChunk(world, chunk, null);
++ }
++ public static CompoundTag saveChunk(ServerLevel world, ChunkAccess chunk, @org.checkerframework.checker.nullness.qual.Nullable AsyncSaveData asyncsavedata) {
++ // Paper end
+ // Paper start - rewrite light impl
+ final int minSection = io.papermc.paper.util.WorldUtil.getMinLightSection(world);
+ final int maxSection = io.papermc.paper.util.WorldUtil.getMaxLightSection(world);
+@@ -388,7 +451,7 @@ public class ChunkSerializer {
+ nbttagcompound.putInt("xPos", chunkcoordintpair.x);
+ nbttagcompound.putInt("yPos", chunk.getMinSection());
+ nbttagcompound.putInt("zPos", chunkcoordintpair.z);
+- nbttagcompound.putLong("LastUpdate", world.getGameTime());
++ nbttagcompound.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading
+ nbttagcompound.putLong("InhabitedTime", chunk.getInhabitedTime());
+ nbttagcompound.putString("Status", chunk.getStatus().getName());
+ BlendingData blendingdata = chunk.getBlendingData();
+@@ -488,8 +551,17 @@ public class ChunkSerializer {
+ nbttagcompound.putBoolean("isLightOn", false); // Paper - set to false but still store, this allows us to detect --eraseCache (as eraseCache _removes_)
+ }
+
+- ListTag nbttaglist1 = new ListTag();
+- Iterator iterator = chunk.getBlockEntitiesPos().iterator();
++ // Paper start
++ ListTag nbttaglist1;
++ Iterator<BlockPos> iterator;
++ if (asyncsavedata != null) {
++ nbttaglist1 = asyncsavedata.blockEntities;
++ iterator = java.util.Collections.emptyIterator();
++ } else {
++ nbttaglist1 = new ListTag();
++ iterator = chunk.getBlockEntitiesPos().iterator();
++ }
++ // Paper end
+
+ CompoundTag nbttagcompound2;
+
+@@ -526,7 +598,14 @@ public class ChunkSerializer {
+ nbttagcompound.put("CarvingMasks", nbttagcompound2);
+ }
+
++ // Paper start
++ if (asyncsavedata != null) {
++ nbttagcompound.put(BLOCK_TICKS_TAG, asyncsavedata.blockTickList);
++ nbttagcompound.put(FLUID_TICKS_TAG, asyncsavedata.fluidTickList);
++ } else {
+ ChunkSerializer.saveTicks(world, nbttagcompound, chunk.getTicksForSerialization());
++ }
++ // Paper end
+ nbttagcompound.put("PostProcessing", ChunkSerializer.packOffsets(chunk.getPostProcessing()));
+ CompoundTag nbttagcompound3 = new CompoundTag();
+ Iterator iterator1 = chunk.getHeightmaps().iterator();
+diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+index 9730ee10042e02741383c8153eb3b7b7103f80e0..f2539f2aab4086bc6772db9985ca9f75ff6a7c71 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
++++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+@@ -28,26 +28,33 @@ import net.minecraft.world.level.storage.DimensionDataStorage;
+ public class ChunkStorage implements AutoCloseable {
+
+ public static final int LAST_MONOLYTH_STRUCTURE_DATA_VERSION = 1493;
+- private final IOWorker worker;
++ // Paper - nuke IO worker
+ protected final DataFixer fixerUpper;
+ @Nullable
+ private volatile LegacyStructureDataHandler legacyStructureHandler;
++ // Paper start - async chunk loading
++ private final Object persistentDataLock = new Object(); // Paper
++ public final RegionFileStorage regionFileCache;
++ // Paper end - async chunk loading
+
+ public ChunkStorage(Path directory, DataFixer dataFixer, boolean dsync) {
+ this.fixerUpper = dataFixer;
+- this.worker = new IOWorker(directory, dsync, "chunk");
++ // Paper start - async chunk io
++ // remove IO worker
++ this.regionFileCache = new RegionFileStorage(directory, dsync); // Paper - nuke IOWorker
++ // Paper end - async chunk io
+ }
+
+ public boolean isOldChunkAround(ChunkPos chunkPos, int checkRadius) {
+- return this.worker.isOldChunkAround(chunkPos, checkRadius);
++ return true; // Paper - (for now, old unoptimised behavior) TODO implement later? the chunk status that blender uses SHOULD already have this radius loaded, no need to go back for it...
+ }
+
+ // CraftBukkit start
+ private boolean check(ServerChunkCache cps, int x, int z) {
+ ChunkPos pos = new ChunkPos(x, z);
+ if (cps != null) {
+- com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread");
+- if (cps.hasChunk(x, z)) {
++ //com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); // Paper - this function is now MT-Safe
++ if (cps.getChunkAtIfCachedImmediately(x, z) != null) { // Paper - isLoaded is a ticket level check, not a chunk loaded check!
+ return true;
+ }
+ }
+@@ -75,6 +82,7 @@ public class ChunkStorage implements AutoCloseable {
+
+ public CompoundTag upgradeChunkTag(ResourceKey<LevelStem> resourcekey, Supplier<DimensionDataStorage> supplier, CompoundTag nbttagcompound, Optional<ResourceKey<Codec<? extends ChunkGenerator>>> optional, ChunkPos pos, @Nullable LevelAccessor generatoraccess) {
+ // CraftBukkit end
++ nbttagcompound = nbttagcompound.copy(); // Paper - defensive copy, another thread might modify this
+ int i = ChunkStorage.getVersion(nbttagcompound);
+
+ // CraftBukkit start
+@@ -92,9 +100,11 @@ public class ChunkStorage implements AutoCloseable {
+ if (i < 1493) {
+ ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.CHUNK, nbttagcompound, i, 1493); // Paper - replace chunk converter
+ if (nbttagcompound.getCompound("Level").getBoolean("hasLegacyStructureData")) {
++ synchronized (this.persistentDataLock) { // Paper - Async chunk loading
+ LegacyStructureDataHandler persistentstructurelegacy = this.getLegacyStructureHandler(resourcekey, supplier);
+
+ nbttagcompound = persistentstructurelegacy.updateFromLegacy(nbttagcompound);
++ } // Paper - Async chunk loading
+ }
+ }
+
+@@ -127,7 +137,7 @@ public class ChunkStorage implements AutoCloseable {
+ LegacyStructureDataHandler persistentstructurelegacy = this.legacyStructureHandler;
+
+ if (persistentstructurelegacy == null) {
+- synchronized (this) {
++ synchronized (this.persistentDataLock) { // Paper - async chunk loading
+ persistentstructurelegacy = this.legacyStructureHandler;
+ if (persistentstructurelegacy == null) {
+ this.legacyStructureHandler = persistentstructurelegacy = LegacyStructureDataHandler.getLegacyStructureHandler(resourcekey, (DimensionDataStorage) supplier.get());
+@@ -153,26 +163,49 @@ public class ChunkStorage implements AutoCloseable {
+ }
+
+ public CompletableFuture<Optional<CompoundTag>> read(ChunkPos chunkPos) {
+- return this.worker.loadAsync(chunkPos);
++ // Paper start - async chunk io
++ try {
++ return CompletableFuture.completedFuture(Optional.ofNullable(this.readSync(chunkPos)));
++ } catch (Throwable thr) {
++ return CompletableFuture.failedFuture(thr);
++ }
++ }
++ @Nullable
++ public CompoundTag readSync(ChunkPos chunkPos) throws IOException {
++ return this.regionFileCache.read(chunkPos);
+ }
++ // Paper end - async chunk io
+
+- public void write(ChunkPos chunkPos, CompoundTag nbt) {
+- this.worker.store(chunkPos, nbt);
++ // Paper start - async chunk io
++ public void write(ChunkPos chunkPos, CompoundTag nbt) throws IOException {
++ this.regionFileCache.write(chunkPos, nbt);
++ // Paper end - Async chunk loading
+ if (this.legacyStructureHandler != null) {
++ synchronized (this.persistentDataLock) { // Paper - Async chunk loading
+ this.legacyStructureHandler.removeIndex(chunkPos.toLong());
++ } // Paper - Async chunk loading
+ }
+
+ }
+
+ public void flushWorker() {
+- this.worker.synchronize(true).join();
++ io.papermc.paper.chunk.system.io.RegionFileIOThread.flush(); // Paper - rewrite chunk system
+ }
+
+ public void close() throws IOException {
+- this.worker.close();
++ this.regionFileCache.close(); // Paper - nuke IO worker
+ }
+
+ public ChunkScanAccess chunkScanner() {
+- return this.worker;
++ // Paper start - nuke IO worker
++ return ((chunkPos, streamTagVisitor) -> {
++ try {
++ this.regionFileCache.scanChunk(chunkPos, streamTagVisitor);
++ return java.util.concurrent.CompletableFuture.completedFuture(null);
++ } catch (IOException e) {
++ throw new RuntimeException(e);
++ }
++ });
++ // Paper end
+ }
+ }
+diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
+index 0ede151943109e81f66875340261d77f67f63c95..0b92db95416b878f41b83b5c74d1c0a1031ff6af 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
++++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
+@@ -31,43 +31,31 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
+ private static final String ENTITIES_TAG = "Entities";
+ private static final String POSITION_TAG = "Position";
+ public final ServerLevel level;
+- private final IOWorker worker;
++ // Paper - rewrite chunk system
+ private final LongSet emptyChunks = new LongOpenHashSet();
+- public final ProcessorMailbox<Runnable> entityDeserializerQueue;
++ // Paper - rewrite chunk system
+ protected final DataFixer fixerUpper;
+
+ public EntityStorage(ServerLevel world, Path path, DataFixer dataFixer, boolean dsync, Executor executor) {
+ this.level = world;
+ this.fixerUpper = dataFixer;
+- this.entityDeserializerQueue = ProcessorMailbox.create(executor, "entity-deserializer");
+- this.worker = new IOWorker(path, dsync, "entities");
++ // Paper - rewrite chunk system
+ }
+
+ @Override
+ public CompletableFuture<ChunkEntities<Entity>> loadEntities(ChunkPos pos) {
+- return this.emptyChunks.contains(pos.toLong()) ? CompletableFuture.completedFuture(emptyChunk(pos)) : this.worker.loadAsync(pos).thenApplyAsync((nbt) -> {
+- if (nbt.isEmpty()) {
+- this.emptyChunks.add(pos.toLong());
+- return emptyChunk(pos);
+- } else {
+- try {
+- ChunkPos chunkPos2 = readChunkPos(nbt.get());
+- if (!Objects.equals(pos, chunkPos2)) {
+- LOGGER.error("Chunk file at {} is in the wrong location. (Expected {}, got {})", pos, pos, chunkPos2);
+- }
+- } catch (Exception var6) {
+- LOGGER.warn("Failed to parse chunk {} position info", pos, var6);
+- }
+-
+- CompoundTag compoundTag = this.upgradeChunkTag(nbt.get());
+- ListTag listTag = compoundTag.getList("Entities", 10);
+- List<Entity> list = EntityType.loadEntitiesRecursive(listTag, this.level).collect(ImmutableList.toImmutableList());
+- return new ChunkEntities<>(pos, list);
+- }
+- }, this.entityDeserializerQueue::tell);
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system - copy out read logic into readEntities
++ }
++
++ // Paper start - rewrite chunk system
++ public static List<Entity> readEntities(ServerLevel level, CompoundTag compoundTag) {
++ ListTag listTag = compoundTag.getList("Entities", 10);
++ List<Entity> list = EntityType.loadEntitiesRecursive(listTag, level).collect(ImmutableList.toImmutableList());
++ return list;
+ }
++ // Paper end - rewrite chunk system
+
+- private static ChunkPos readChunkPos(CompoundTag chunkNbt) {
++ public static ChunkPos readChunkPos(CompoundTag chunkNbt) { // Paper - public
+ int[] is = chunkNbt.getIntArray("Position");
+ return new ChunkPos(is[0], is[1]);
+ }
+@@ -82,40 +70,68 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
+
+ @Override
+ public void storeEntities(ChunkEntities<Entity> dataList) {
++ // Paper start - rewrite chunk system
++ if (true) {
++ throw new UnsupportedOperationException();
++ }
++ // Paper end - rewrite chunk system
+ ChunkPos chunkPos = dataList.getPos();
+ if (dataList.isEmpty()) {
+ if (this.emptyChunks.add(chunkPos.toLong())) {
+- this.worker.store(chunkPos, (CompoundTag)null);
++ // Paper - rewrite chunk system
+ }
+
+ } else {
+- ListTag listTag = new ListTag();
+- dataList.getEntities().forEach((entity) -> {
+- CompoundTag compoundTag = new CompoundTag();
+- if (entity.save(compoundTag)) {
+- listTag.add(compoundTag);
+- }
+-
+- });
+- CompoundTag compoundTag = new CompoundTag();
+- compoundTag.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
+- compoundTag.put("Entities", listTag);
+- writeChunkPos(compoundTag, chunkPos);
+- this.worker.store(chunkPos, compoundTag).exceptionally((ex) -> {
+- LOGGER.error("Failed to store chunk {}", chunkPos, ex);
+- return null;
+- });
++ // Paper - move into saveEntityChunk0
+ this.emptyChunks.remove(chunkPos.toLong());
+ }
+ }
+
++ // Paper start - rewrite chunk system
++ public static void copyEntities(final CompoundTag from, final CompoundTag into) {
++ if (from == null) {
++ return;
++ }
++ final ListTag entitiesFrom = from.getList("Entities", net.minecraft.nbt.Tag.TAG_COMPOUND);
++ if (entitiesFrom == null || entitiesFrom.isEmpty()) {
++ return;
++ }
++
++ final ListTag entitiesInto = into.getList("Entities", net.minecraft.nbt.Tag.TAG_COMPOUND);
++ into.put("Entities", entitiesInto); // this is in case into doesn't have any entities
++ entitiesInto.addAll(0, entitiesFrom.copy()); // need to copy, this is coming from the save thread
++ }
++
++ public static CompoundTag saveEntityChunk(List<Entity> entities, ChunkPos chunkPos, ServerLevel level) {
++ return saveEntityChunk0(entities, chunkPos, level, false);
++ }
++ private static CompoundTag saveEntityChunk0(List<Entity> entities, ChunkPos chunkPos, ServerLevel level, boolean force) {
++ if (!force && entities.isEmpty()) {
++ return null;
++ }
++ ListTag listTag = new ListTag();
++ entities.forEach((entity) -> { // diff here: use entities parameter
++ CompoundTag compoundTag = new CompoundTag();
++ if (entity.save(compoundTag)) {
++ listTag.add(compoundTag);
++ }
++
++ });
++ CompoundTag compoundTag = new CompoundTag();
++ compoundTag.putInt("DataVersion", SharedConstants.getCurrentVersion().getWorldVersion());
++ compoundTag.put("Entities", listTag);
++ writeChunkPos(compoundTag, chunkPos);
++
++ return !force && listTag.isEmpty() ? null : compoundTag;
++ }
++ // Paper end - rewrite chunk system
++
+ @Override
+ public void flush(boolean sync) {
+- this.worker.synchronize(sync).join();
+- this.entityDeserializerQueue.runAll();
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+
+- private CompoundTag upgradeChunkTag(CompoundTag chunkNbt) {
++ public static CompoundTag upgradeChunkTag(CompoundTag chunkNbt) { // Paper - public and static
+ int i = getVersion(chunkNbt);
+ return ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ENTITY_CHUNK, chunkNbt, i, SharedConstants.getCurrentVersion().getWorldVersion()); // Paper - route to new converter system
+ }
+@@ -126,6 +142,6 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
+
+ @Override
+ public void close() throws IOException {
+- this.worker.close();
++ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
+ }
+diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
+index d51bafd2f5a763b8a49c835ab74a7cf60caa1ab6..7412da51c2eae70f17f4883f7223303d570c8402 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
+@@ -44,6 +44,7 @@ public class RegionFile implements AutoCloseable {
+ private final IntBuffer timestamps;
+ @VisibleForTesting
+ protected final RegionBitmap usedSectors;
++ public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(true); // Paper
+
+ public RegionFile(Path file, Path directory, boolean dsync) throws IOException {
+ this(file, directory, RegionFileVersion.VERSION_DEFLATE, dsync);
+@@ -228,7 +229,7 @@ public class RegionFile implements AutoCloseable {
+ return (byteCount + 4096 - 1) / 4096;
+ }
+
+- public boolean doesChunkExist(ChunkPos pos) {
++ public synchronized boolean doesChunkExist(ChunkPos pos) { // Paper - synchronized
+ int i = this.getOffset(pos);
+
+ if (i == 0) {
+@@ -393,6 +394,11 @@ public class RegionFile implements AutoCloseable {
+ }
+
+ public void close() throws IOException {
++ // Paper start - Prevent regionfiles from being closed during use
++ this.fileLock.lock();
++ synchronized (this) {
++ try {
++ // Paper end
+ try {
+ this.padToFullSector();
+ } finally {
+@@ -402,6 +408,10 @@ public class RegionFile implements AutoCloseable {
+ this.file.close();
+ }
+ }
++ } finally { // Paper start - Prevent regionfiles from being closed during use
++ this.fileLock.unlock();
++ }
++ } // Paper end
+
+ }
+
+diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+index 8ba1c073387fa21a20bd42a873ec3cc314eae64e..45cec71b3b2f9444d7be7e5bf9fab166b28ea34d 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+@@ -24,16 +24,37 @@ public class RegionFileStorage implements AutoCloseable {
+ private final Path folder;
+ private final boolean sync;
+
+- RegionFileStorage(Path directory, boolean dsync) {
++ protected RegionFileStorage(Path directory, boolean dsync) { // Paper - protected constructor
+ this.folder = directory;
+ this.sync = dsync;
+ }
+
+- private RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit
++ // Paper start
++ public synchronized RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) {
++ return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()));
++ }
++
++ public synchronized boolean chunkExists(ChunkPos pos) throws IOException {
++ RegionFile regionfile = getRegionFile(pos, true);
++
++ return regionfile != null ? regionfile.hasChunk(pos) : false;
++ }
++
++ public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit
++ return this.getRegionFile(chunkcoordintpair, existingOnly, false);
++ }
++ public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException {
++ // Paper end
+ long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ());
+ RegionFile regionfile = (RegionFile) this.regionCache.getAndMoveToFirst(i);
+
+ if (regionfile != null) {
++ // Paper start
++ if (lock) {
++ // must be in this synchronized block
++ regionfile.fileLock.lock();
++ }
++ // Paper end
+ return regionfile;
+ } else {
+ if (this.regionCache.size() >= 256) {
+@@ -48,6 +69,12 @@ public class RegionFileStorage implements AutoCloseable {
+ RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync);
+
+ this.regionCache.putAndMoveToFirst(i, regionfile1);
++ // Paper start
++ if (lock) {
++ // must be in this synchronized block
++ regionfile1.fileLock.lock();
++ }
++ // Paper end
+ return regionfile1;
+ }
+ }
+@@ -55,11 +82,12 @@ public class RegionFileStorage implements AutoCloseable {
+ @Nullable
+ public CompoundTag read(ChunkPos pos) throws IOException {
+ // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
+- RegionFile regionfile = this.getRegionFile(pos, true);
++ RegionFile regionfile = this.getRegionFile(pos, true, true); // Paper
+ if (regionfile == null) {
+ return null;
+ }
+ // CraftBukkit end
++ try { // Paper
+ DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos);
+
+ CompoundTag nbttagcompound;
+@@ -96,6 +124,9 @@ public class RegionFileStorage implements AutoCloseable {
+ }
+
+ return nbttagcompound;
++ } finally { // Paper start
++ regionfile.fileLock.unlock();
++ } // Paper end
+ }
+
+ public void scanChunk(ChunkPos chunkcoordintpair, StreamTagVisitor streamtagvisitor) throws IOException {
+@@ -130,7 +161,12 @@ public class RegionFileStorage implements AutoCloseable {
+ }
+
+ protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException {
+- RegionFile regionfile = this.getRegionFile(pos, false); // CraftBukkit
++ RegionFile regionfile = this.getRegionFile(pos, nbt == null, true); // CraftBukkit // Paper // Paper start - rewrite chunk system
++ if (nbt == null && regionfile == null) {
++ return;
++ }
++ // Paper end - rewrite chunk system
++ try { // Paper
+
+ if (nbt == null) {
+ regionfile.clear(pos);
+@@ -156,9 +192,12 @@ public class RegionFileStorage implements AutoCloseable {
+ }
+ }
+
++ } finally { // Paper start
++ regionfile.fileLock.unlock();
++ } // Paper end
+ }
+
+- public void close() throws IOException {
++ public synchronized void close() throws IOException { // Paper -> synchronized
+ ExceptionCollector<IOException> exceptionsuppressor = new ExceptionCollector<>();
+ ObjectIterator objectiterator = this.regionCache.values().iterator();
+
+@@ -175,7 +214,7 @@ public class RegionFileStorage implements AutoCloseable {
+ exceptionsuppressor.throwIfPresent();
+ }
+
+- public void flush() throws IOException {
++ public synchronized void flush() throws IOException { // Paper - synchronize
+ ObjectIterator objectiterator = this.regionCache.values().iterator();
+
+ while (objectiterator.hasNext()) {
+diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+index a0b61647e5a7e5989aed52522bc9a43bc487421c..27f766fc72d779cff1b5a88a79961aa7ef91b11f 100644
+--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
++++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+@@ -34,27 +34,28 @@ import net.minecraft.world.level.ChunkPos;
+ import net.minecraft.world.level.LevelHeightAccessor;
+ import org.slf4j.Logger;
+
+-public class SectionStorage<R> implements AutoCloseable {
++public class SectionStorage<R> extends RegionFileStorage implements AutoCloseable { // Paper - nuke IOWorker
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private static final String SECTIONS_TAG = "Sections";
+- private final IOWorker worker;
++ // Paper - remove mojang I/O thread
+ private final Long2ObjectMap<Optional<R>> storage = new Long2ObjectOpenHashMap<>();
+ public final LongLinkedOpenHashSet dirty = new LongLinkedOpenHashSet();
+ private final Function<Runnable, Codec<R>> codec;
+ private final Function<Runnable, R> factory;
+ private final DataFixer fixerUpper;
+ private final DataFixTypes type;
+- private final RegistryAccess registryAccess;
++ public final RegistryAccess registryAccess; // Paper - rewrite chunk system
+ protected final LevelHeightAccessor levelHeightAccessor;
+
+ public SectionStorage(Path path, Function<Runnable, Codec<R>> codecFactory, Function<Runnable, R> factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, RegistryAccess dynamicRegistryManager, LevelHeightAccessor world) {
++ super(path, dsync); // Paper - remove mojang I/O thread
+ this.codec = codecFactory;
+ this.factory = factory;
+ this.fixerUpper = dataFixer;
+ this.type = dataFixTypes;
+ this.registryAccess = dynamicRegistryManager;
+ this.levelHeightAccessor = world;
+- this.worker = new IOWorker(path, dsync, path.getFileName().toString());
++ // Paper - remove mojang I/O thread
+ }
+
+ protected void tick(BooleanSupplier shouldKeepTicking) {
+@@ -116,23 +117,21 @@ public class SectionStorage<R> implements AutoCloseable {
+ }
+
+ private void readColumn(ChunkPos pos) {
+- Optional<CompoundTag> optional = this.tryRead(pos).join();
+- RegistryOps<Tag> registryOps = RegistryOps.create(NbtOps.INSTANCE, this.registryAccess);
+- this.readColumn(pos, registryOps, optional.orElse((CompoundTag)null));
++ throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName()); // Paper - rewrite chunk system
+ }
+
+ private CompletableFuture<Optional<CompoundTag>> tryRead(ChunkPos pos) {
+- return this.worker.loadAsync(pos).exceptionally((throwable) -> {
+- if (throwable instanceof IOException iOException) {
+- LOGGER.error("Error reading chunk {} data from disk", pos, iOException);
+- return Optional.empty();
+- } else {
+- throw new CompletionException(throwable);
+- }
+- });
++ // Paper start - rewrite chunk system
++ try {
++ return CompletableFuture.completedFuture(Optional.ofNullable(this.read(pos)));
++ } catch (Throwable thr) {
++ return CompletableFuture.failedFuture(thr);
++ }
++ // Paper end - rewrite chunk system
+ }
+
+ private <T> void readColumn(ChunkPos pos, DynamicOps<T> ops, @Nullable T data) {
++ if (true) throw new IllegalStateException("Only chunk system can load in state, offending class:" + this.getClass().getName()); // Paper - rewrite chunk system
+ if (data == null) {
+ for(int i = this.levelHeightAccessor.getMinSection(); i < this.levelHeightAccessor.getMaxSection(); ++i) {
+ this.storage.put(getKey(pos, i), Optional.empty());
+@@ -177,7 +176,7 @@ public class SectionStorage<R> implements AutoCloseable {
+ Dynamic<Tag> dynamic = this.writeColumn(pos, registryOps);
+ Tag tag = dynamic.getValue();
+ if (tag instanceof CompoundTag) {
+- this.worker.store(pos, (CompoundTag)tag);
++ try { this.write(pos, (CompoundTag)tag); } catch (IOException ioexception) { SectionStorage.LOGGER.error("Error writing data to disk", ioexception); } // Paper - nuke IOWorker
+ } else {
+ LOGGER.error("Expected compound tag, got {}", (Object)tag);
+ }
+@@ -222,7 +221,7 @@ public class SectionStorage<R> implements AutoCloseable {
+ }
+
+ private static int getVersion(Dynamic<?> dynamic) {
+- return dynamic.get("DataVersion").asInt(1945);
++ return dynamic.get("DataVersion").asInt(1945); // Paper - diff on change, constant used in ChunkLoadTask
+ }
+
+ public void flush(ChunkPos pos) {
+@@ -240,6 +239,9 @@ public class SectionStorage<R> implements AutoCloseable {
+
+ @Override
+ public void close() throws IOException {
+- this.worker.close();
++ //this.worker.close(); // Paper - nuke I/O worker - don't call the worker
++ super.close(); // Paper - nuke I/O worker - call super.close method which is responsible for closing used files.
+ }
++
++ // Paper - rewrite chunk system
+ }
+diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
+index 2830d32bba3dc85847e3a5d9b4d98f822e34b606..4cdfc433df67afcd455422e9baf56f167dd712ae 100644
+--- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
++++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
+@@ -8,54 +8,42 @@ import javax.annotation.Nullable;
+ import net.minecraft.world.entity.Entity;
+
+ public class EntityTickList {
+- private Int2ObjectMap<Entity> active = new Int2ObjectLinkedOpenHashMap<>();
+- private Int2ObjectMap<Entity> passive = new Int2ObjectLinkedOpenHashMap<>();
+- @Nullable
+- private Int2ObjectMap<Entity> iterated;
++ private final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Entity> entities = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(true); // Paper - rewrite this, always keep this updated - why would we EVER tick an entity that's not ticking?
+
+ private void ensureActiveIsNotIterated() {
+- if (this.iterated == this.active) {
+- this.passive.clear();
+-
+- for(Int2ObjectMap.Entry<Entity> entry : Int2ObjectMaps.fastIterable(this.active)) {
+- this.passive.put(entry.getIntKey(), entry.getValue());
+- }
+-
+- Int2ObjectMap<Entity> int2ObjectMap = this.active;
+- this.active = this.passive;
+- this.passive = int2ObjectMap;
+- }
++ // Paper - replace with better logic, do not delay removals
+
+ }
+
+ public void add(Entity entity) {
++ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper
+ this.ensureActiveIsNotIterated();
+- this.active.put(entity.getId(), entity);
++ this.entities.add(entity); // Paper - replace with better logic, do not delay removals/additions
+ }
+
+ public void remove(Entity entity) {
++ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper
+ this.ensureActiveIsNotIterated();
+- this.active.remove(entity.getId());
++ this.entities.remove(entity); // Paper - replace with better logic, do not delay removals/additions
+ }
+
+ public boolean contains(Entity entity) {
+- return this.active.containsKey(entity.getId());
++ return this.entities.contains(entity); // Paper - replace with better logic, do not delay removals/additions
+ }
+
+ public void forEach(Consumer<Entity> action) {
+- if (this.iterated != null) {
+- throw new UnsupportedOperationException("Only one concurrent iteration supported");
+- } else {
+- this.iterated = this.active;
+-
+- try {
+- for(Entity entity : this.active.values()) {
+- action.accept(entity);
+- }
+- } finally {
+- this.iterated = null;
++ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper
++ // Paper start - replace with better logic, do not delay removals/additions
++ // To ensure nothing weird happens with dimension travelling, do not iterate over new entries...
++ // (by dfl iterator() is configured to not iterate over new entries)
++ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Entity> iterator = this.entities.iterator();
++ try {
++ while (iterator.hasNext()) {
++ action.accept(iterator.next());
+ }
+-
++ } finally {
++ iterator.finishedIterating();
+ }
++ // Paper end - replace with better logic, do not delay removals/additions
+ }
+ }
+diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
+index e66e52da84408eb705d23504e500bd8a98322b0e..298ea7c5776c4476dbb69e68debbd50376d14165 100644
+--- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
++++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
+@@ -90,7 +90,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
+ return CompletableFuture.supplyAsync(Util.wrapThreadWithTaskName("init_biomes", () -> {
+ this.doCreateBiomes(blender, noiseConfig, structureAccessor, chunk);
+ return chunk;
+- }), Util.backgroundExecutor());
++ }), executor); // Paper - run with supplied executor
+ }
+
+ private void doCreateBiomes(Blender blender, RandomState noiseConfig, StructureManager structureAccessor, ChunkAccess chunk) {
+@@ -289,7 +289,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
+
+ return CompletableFuture.supplyAsync(Util.wrapThreadWithTaskName("wgen_fill_noise", () -> {
+ return this.doFill(blender, structureAccessor, noiseConfig, chunk, j, k);
+- }), Util.backgroundExecutor()).whenCompleteAsync((ichunkaccess1, throwable) -> {
++ }), executor).whenCompleteAsync((ichunkaccess1, throwable) -> { // Paper - run with supplied executor
+ Iterator iterator = set.iterator();
+
+ while (iterator.hasNext()) {
+diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
+index 6d44634ae6dcbc392011f248f6ab429b9845af55..29465d24b6c9160fcd6293006dcc26bcfbeb5e10 100644
+--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
+@@ -119,7 +119,7 @@ public class CraftChunk implements Chunk {
+
+ @Override
+ public boolean isEntitiesLoaded() {
+- return this.getCraftWorld().getHandle().entityManager.areEntitiesLoaded(ChunkPos.asLong(x, z));
++ return this.getCraftWorld().getHandle().areEntitiesLoaded(io.papermc.paper.util.CoordinateUtils.getChunkKey(this.x, this.z)); // Paper - rewrite chunk system
+ }
+
+ @Override
+@@ -128,51 +128,7 @@ public class CraftChunk implements Chunk {
+ this.getWorld().getChunkAt(x, z); // Transient load for this tick
+ }
+
+- PersistentEntitySectionManager<net.minecraft.world.entity.Entity> entityManager = this.getCraftWorld().getHandle().entityManager;
+- long pair = ChunkPos.asLong(x, z);
+-
+- if (entityManager.areEntitiesLoaded(pair)) {
+- return entityManager.getEntities(new ChunkPos(this.x, this.z)).stream()
+- .map(net.minecraft.world.entity.Entity::getBukkitEntity)
+- .filter(Objects::nonNull).toArray(Entity[]::new);
+- }
+-
+- entityManager.ensureChunkQueuedForLoad(pair); // Start entity loading
+-
+- // SPIGOT-6772: Use entity mailbox and re-schedule entities if they get unloaded
+- ProcessorMailbox<Runnable> mailbox = ((EntityStorage) entityManager.permanentStorage).entityDeserializerQueue;
+- BooleanSupplier supplier = () -> {
+- // only execute inbox if our entities are not present
+- if (entityManager.areEntitiesLoaded(pair)) {
+- return true;
+- }
+-
+- if (!entityManager.isPending(pair)) {
+- // Our entities got unloaded, this should normally not happen.
+- entityManager.ensureChunkQueuedForLoad(pair); // Re-start entity loading
+- }
+-
+- // tick loading inbox, which loads the created entities to the world
+- // (if present)
+- entityManager.tick();
+- // check if our entities are loaded
+- return entityManager.areEntitiesLoaded(pair);
+- };
+-
+- // now we wait until the entities are loaded,
+- // the converting from NBT to entity object is done on the main Thread which is why we wait
+- while (!supplier.getAsBoolean()) {
+- if (mailbox.size() != 0) {
+- mailbox.run();
+- } else {
+- Thread.yield();
+- LockSupport.parkNanos("waiting for entity loading", 100000L);
+- }
+- }
+-
+- return entityManager.getEntities(new ChunkPos(this.x, this.z)).stream()
+- .map(net.minecraft.world.entity.Entity::getBukkitEntity)
+- .filter(Objects::nonNull).toArray(Entity[]::new);
++ return getCraftWorld().getHandle().getChunkEntities(this.x, this.z); // Paper - rewrite chunk system
+ }
+
+ @Override
+diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+index eb5c7e15366ee5902d8c754a1e9daec50d26fb17..2103081cf6a8db00d78618340eef514082fede6e 100644
+--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+@@ -1115,7 +1115,7 @@ public final class CraftServer implements Server {
+ this.console.addLevel(internal);
+
+ this.getServer().prepareLevels(internal.getChunkSource().chunkMap.progressListener, internal);
+- internal.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API
++ //internal.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API // Paper - rewrite chunk system
+
+ this.pluginManager.callEvent(new WorldLoadEvent(internal.getWorld()));
+ return internal.getWorld();
+@@ -1159,7 +1159,7 @@ public final class CraftServer implements Server {
+ }
+
+ handle.getChunkSource().close(save);
+- handle.entityManager.close(save); // SPIGOT-6722: close entityManager
++ // handle.entityManager.close(save); // SPIGOT-6722: close entityManager // Paper - rewrite chunk system
+ handle.convertable.close();
+ } catch (Exception ex) {
+ this.getLogger().log(Level.SEVERE, null, ex);
+@@ -1974,7 +1974,7 @@ public final class CraftServer implements Server {
+
+ @Override
+ public boolean isPrimaryThread() {
+- return Thread.currentThread().equals(console.serverThread) || this.console.hasStopped() || !org.spigotmc.AsyncCatcher.enabled; // All bets are off if we have shut down (e.g. due to watchdog)
++ return io.papermc.paper.util.TickThread.isTickThread(); // Paper - rewrite chunk system
+ }
+
+ // Paper start
+diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+index 9a6820b10e4164cc38d269853b5c2a49175cb890..e2c051dd0d639ba28da0f62d06259fcf0d3244ce 100644
+--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+@@ -314,10 +314,14 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+ ChunkHolder playerChunk = this.world.getChunkSource().chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z));
+ if (playerChunk == null) return false;
+
+- playerChunk.getTickingChunkFuture().thenAccept(either -> {
+- either.left().ifPresent(chunk -> {
++ // Paper start - rewrite player chunk loader
++ net.minecraft.world.level.chunk.LevelChunk chunk = playerChunk.getSendingChunk();
++ if (chunk == null) {
++ return false;
++ }
++ // Paper end - rewrite player chunk loader
+ List<ServerPlayer> playersInRange = playerChunk.playerProvider.getPlayers(playerChunk.getPos(), false);
+- if (playersInRange.isEmpty()) return;
++ if (playersInRange.isEmpty()) return true; // Paper - rewrite player chunk loader
+
+ ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null, true);
+ for (ServerPlayer player : playersInRange) {
+@@ -325,8 +329,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+
+ player.connection.send(refreshPacket);
+ }
+- });
+- });
++ // Paper - rewrite player chunk loader
+
+ return true;
+ }
+@@ -404,20 +407,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+ @Override
+ public Collection<Plugin> getPluginChunkTickets(int x, int z) {
+ DistanceManager chunkDistanceManager = this.world.getChunkSource().chunkMap.distanceManager;
+- SortedArraySet<Ticket<?>> tickets = chunkDistanceManager.tickets.get(ChunkPos.asLong(x, z));
+-
+- if (tickets == null) {
+- return Collections.emptyList();
+- }
+-
+- ImmutableList.Builder<Plugin> ret = ImmutableList.builder();
+- for (Ticket<?> ticket : tickets) {
+- if (ticket.getType() == TicketType.PLUGIN_TICKET) {
+- ret.add((Plugin) ticket.key);
+- }
+- }
+-
+- return ret.build();
++ return chunkDistanceManager.getChunkHolderManager().getPluginChunkTickets(x, z); // Paper - rewrite chunk system
+ }
+
+ @Override
+@@ -425,7 +415,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+ Map<Plugin, ImmutableList.Builder<Chunk>> ret = new HashMap<>();
+ DistanceManager chunkDistanceManager = this.world.getChunkSource().chunkMap.distanceManager;
+
+- for (Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>> chunkTickets : chunkDistanceManager.tickets.long2ObjectEntrySet()) {
++ for (Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>> chunkTickets : chunkDistanceManager.getChunkHolderManager().getTicketsCopy().long2ObjectEntrySet()) { // Paper - rewrite chunk system
+ long chunkKey = chunkTickets.getLongKey();
+ SortedArraySet<Ticket<?>> tickets = chunkTickets.getValue();
+
+@@ -1905,14 +1895,53 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+ // Spigot start
+ @Override
+ public int getViewDistance() {
+- return world.spigotConfig.viewDistance;
++ return getHandle().getChunkSource().chunkMap.playerChunkManager.getTargetNoTickViewDistance(); // Paper - replace old player chunk management
+ }
+
+ @Override
+ public int getSimulationDistance() {
+- return world.spigotConfig.simulationDistance;
++ return getHandle().getChunkSource().chunkMap.playerChunkManager.getTargetTickViewDistance(); // Paper - replace old player chunk management
+ }
+ // Spigot end
++ // Paper start - view distance api
++ @Override
++ public void setViewDistance(int viewDistance) {
++ if (viewDistance < 2 || viewDistance > 32) {
++ throw new IllegalArgumentException("View distance " + viewDistance + " is out of range of [2, 32]");
++ }
++ net.minecraft.server.level.ChunkMap chunkMap = getHandle().getChunkSource().chunkMap;
++ chunkMap.setViewDistance(viewDistance);
++ }
++
++ @Override
++ public void setSimulationDistance(int simulationDistance) {
++ if (simulationDistance < 2 || simulationDistance > 32) {
++ throw new IllegalArgumentException("Simulation distance " + simulationDistance + " is out of range of [2, 32]");
++ }
++ net.minecraft.server.level.ChunkMap chunkMap = getHandle().getChunkSource().chunkMap;
++ chunkMap.setTickViewDistance(simulationDistance);
++ }
++
++ @Override
++ public int getNoTickViewDistance() {
++ return this.getViewDistance();
++ }
++
++ @Override
++ public void setNoTickViewDistance(int viewDistance) {
++ this.setViewDistance(viewDistance);
++ }
++
++ @Override
++ public int getSendViewDistance() {
++ return getHandle().getChunkSource().chunkMap.playerChunkManager.getTargetSendDistance();
++ }
++
++ @Override
++ public void setSendViewDistance(int viewDistance) {
++ getHandle().getChunkSource().chunkMap.playerChunkManager.setSendDistance(viewDistance);
++ }
++ // Paper end - view distance api
+
+ // Spigot start
+ private final org.bukkit.World.Spigot spigot = new org.bukkit.World.Spigot()
+diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+index d5e6123622f6bbc4c3b8b91be7f9ba8e5c1c22d8..6708c2c88251f4e5f623cd785c14b2d3ed2d3de0 100644
+--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+@@ -174,6 +174,81 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+ this.firstPlayed = System.currentTimeMillis();
+ }
+
++ // Paper start - implement view distances
++ @Override
++ public int getViewDistance() {
++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap;
++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle());
++ if (data == null) {
++ return chunkMap.playerChunkManager.getTargetNoTickViewDistance();
++ }
++ return data.getTargetNoTickViewDistance();
++ }
++
++ @Override
++ public void setViewDistance(int viewDistance) {
++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap;
++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle());
++ if (data == null) {
++ throw new IllegalStateException("Player is not attached to world");
++ }
++
++ data.setTargetNoTickViewDistance(viewDistance);
++ }
++
++ @Override
++ public int getSimulationDistance() {
++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap;
++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle());
++ if (data == null) {
++ return chunkMap.playerChunkManager.getTargetTickViewDistance();
++ }
++ return data.getTargetTickViewDistance();
++ }
++
++ @Override
++ public void setSimulationDistance(int simulationDistance) {
++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap;
++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle());
++ if (data == null) {
++ throw new IllegalStateException("Player is not attached to world");
++ }
++
++ data.setTargetTickViewDistance(simulationDistance);
++ }
++
++ @Override
++ public int getNoTickViewDistance() {
++ return this.getViewDistance();
++ }
++
++ @Override
++ public void setNoTickViewDistance(int viewDistance) {
++ this.setViewDistance(viewDistance);
++ }
++
++ @Override
++ public int getSendViewDistance() {
++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap;
++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle());
++ if (data == null) {
++ return chunkMap.playerChunkManager.getTargetSendDistance();
++ }
++ return data.getTargetSendViewDistance();
++ }
++
++ @Override
++ public void setSendViewDistance(int viewDistance) {
++ net.minecraft.server.level.ChunkMap chunkMap = this.getHandle().getLevel().getChunkSource().chunkMap;
++ io.papermc.paper.chunk.PlayerChunkLoader.PlayerLoaderData data = chunkMap.playerChunkManager.getData(this.getHandle());
++ if (data == null) {
++ throw new IllegalStateException("Player is not attached to world");
++ }
++
++ data.setTargetSendViewDistance(viewDistance);
++ }
++ // Paper end - implement view distances
++
+ public GameProfile getProfile() {
+ return this.getHandle().getGameProfile();
+ }
+diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java
+index 19b938f9b86552034c2a0e4af40e342a17f56504..382879562a808290cc4dd59dcd5022c6c22fb169 100644
+--- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java
++++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java
+@@ -256,7 +256,7 @@ public class CustomChunkGenerator extends InternalChunkGenerator {
+ return ichunkaccess1;
+ };
+
+- return future == null ? CompletableFuture.supplyAsync(() -> function.apply(chunk), net.minecraft.Util.backgroundExecutor()) : future.thenApply(function);
++ return future == null ? CompletableFuture.supplyAsync(() -> function.apply(chunk), executor) : future.thenApply(function); // Paper - run with supplied executor
+ }
+
+ @Override
+diff --git a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java
+index 3bedc22c253c3632b5624c05e78ed3671e5d30ce..fbd82b6be6604bf854e01ed5718e4e072f42b265 100644
+--- a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java
++++ b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java
+@@ -254,4 +254,20 @@ public class DummyGeneratorAccess implements WorldGenLevel {
+ public boolean destroyBlock(BlockPos pos, boolean drop, Entity breakingEntity, int maxUpdateDepth) {
+ return false; // SPIGOT-6515
+ }
++
++ // Paper start
++ @Override
++ public List<Entity> getHardCollidingEntities(Entity except, AABB box, Predicate<? super Entity> predicate) {
++ return java.util.Collections.emptyList();
++ }
++
++ @Override
++ public void getEntities(Entity except, AABB box, Predicate<? super Entity> predicate, List<Entity> into) {}
++
++ @Override
++ public void getHardCollidingEntities(Entity except, AABB box, Predicate<? super Entity> predicate, List<Entity> into) {}
++
++ @Override
++ public <T> void getEntitiesByClass(Class<? extends T> clazz, Entity except, AABB box, List<? super T> into, Predicate<? super T> predicate) {}
++ // Paper end
+ }
+diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
+index 38cf408899cef72bc9d2888109a7ac7ce0aec638..d3639643cda7d8ccf3c1208502605120590a2d30 100644
+--- a/src/main/java/org/spigotmc/ActivationRange.java
++++ b/src/main/java/org/spigotmc/ActivationRange.java
+@@ -132,7 +132,13 @@ public class ActivationRange
+ ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate( animalActivationRange, 256, animalActivationRange );
+ ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate( monsterActivationRange, 256, monsterActivationRange );
+
+- world.getEntities().get(maxBB, ActivationRange::activateEntity);
++ // Paper start
++ java.util.List<Entity> entities = world.getEntities((Entity)null, maxBB, null);
++ for (int i = 0; i < entities.size(); i++) {
++ Entity entity = entities.get(i);
++ ActivationRange.activateEntity(entity);
++ }
++ // Paper end
+ }
+ MinecraftTimings.entityActivationCheckTimer.stopTiming();
+ }
+diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java
+index 78669fa035b7537ff7e533cf32aaf2995625424f..05e94702e42b8f5c35d2a112c486d57948a3acba 100644
+--- a/src/main/java/org/spigotmc/AsyncCatcher.java
++++ b/src/main/java/org/spigotmc/AsyncCatcher.java
+@@ -9,7 +9,7 @@ public class AsyncCatcher
+
+ public static void catchOp(String reason)
+ {
+- if ( (AsyncCatcher.enabled || io.papermc.paper.util.TickThread.STRICT_THREAD_CHECKS) && Thread.currentThread() != MinecraftServer.getServer().serverThread ) // Paper
++ if ( !io.papermc.paper.util.TickThread.isTickThread() ) // Paper // Paper - rewrite chunk system
+ {
+ throw new IllegalStateException( "Asynchronous " + reason + "!" );
+ }
+diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
+index 335120afc88a8fc1543c2e6df516fd728e3ab032..f1194eb6fdfba60959e00080d0562f2820d13b27 100644
+--- a/src/main/java/org/spigotmc/WatchdogThread.java
++++ b/src/main/java/org/spigotmc/WatchdogThread.java
+@@ -8,7 +8,7 @@ import java.util.logging.Logger;
+ import net.minecraft.server.MinecraftServer;
+ import org.bukkit.Bukkit;
+
+-public class WatchdogThread extends Thread
++public final class WatchdogThread extends io.papermc.paper.util.TickThread // Paper - rewrite chunk system
+ {
+
+ private static WatchdogThread instance;
+@@ -83,6 +83,7 @@ public class WatchdogThread extends Thread
+ //
+ log.log( Level.SEVERE, "------------------------------" );
+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" );
++ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system
+ WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
+ log.log( Level.SEVERE, "------------------------------" );
+ //
diff --git a/patches/server/0021-Add-command-line-option-to-load-extra-plugin-jars-no.patch b/patches/server/0017-Add-command-line-option-to-load-extra-plugin-jars-no.patch
index 2858b73a46..06b4a4b177 100644
--- a/patches/server/0021-Add-command-line-option-to-load-extra-plugin-jars-no.patch
+++ b/patches/server/0017-Add-command-line-option-to-load-extra-plugin-jars-no.patch
@@ -7,7 +7,7 @@ Subject: [PATCH] Add command line option to load extra plugin jars not in the
ex: java -jar paperclip.jar nogui -add-plugin=/path/to/plugin.jar -add-plugin=/path/to/another/plugin_jar.jar
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index eb5c7e15366ee5902d8c754a1e9daec50d26fb17..37fefdf0d96cd2b6e23b6e69ee5a8db16f0e51da 100644
+index 2103081cf6a8db00d78618340eef514082fede6e..6723b52fd9306b0f9e3f1d0eadcbfd76d383beee 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -405,10 +405,15 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0022-Configurable-cactus-bamboo-and-reed-growth-heights.patch b/patches/server/0018-Configurable-cactus-bamboo-and-reed-growth-heights.patch
index e794b359ef..e794b359ef 100644
--- a/patches/server/0022-Configurable-cactus-bamboo-and-reed-growth-heights.patch
+++ b/patches/server/0018-Configurable-cactus-bamboo-and-reed-growth-heights.patch
diff --git a/patches/server/0018-Delay-Chunk-Unloads-based-on-Player-Movement.patch b/patches/server/0018-Delay-Chunk-Unloads-based-on-Player-Movement.patch
deleted file mode 100644
index bd5e8ce10c..0000000000
--- a/patches/server/0018-Delay-Chunk-Unloads-based-on-Player-Movement.patch
+++ /dev/null
@@ -1,89 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Aikar <[email protected]>
-Date: Sat, 18 Jun 2016 23:22:12 -0400
-Subject: [PATCH] Delay Chunk Unloads based on Player Movement
-
-When players are moving in the world, doing things such as building or exploring,
-they will commonly go back and forth in a small area. This causes a ton of chunk load
-and unload activity on the edge chunks of their view distance.
-
-A simple back and forth movement in 6 blocks could spam a chunk to thrash a
-loading and unload cycle over and over again.
-
-This is very wasteful. This system introduces a delay of inactivity on a chunk
-before it actually unloads, which will be handled by the ticket expiry process.
-
-This allows servers with smaller worlds who do less long distance exploring to stop
-wasting cpu cycles on saving/unloading/reloading chunks repeatedly.
-
-diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
-index 6181f675d7addde30f7018b4cd46fe061a14da51..aaf6344d3187ceada947ce6ee0fbba91ca0271a3 100644
---- a/src/main/java/net/minecraft/server/level/DistanceManager.java
-+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
-@@ -198,6 +198,27 @@ public abstract class DistanceManager {
- boolean removed = false; // CraftBukkit
- if (arraysetsorted.remove(ticket)) {
- removed = true; // CraftBukkit
-+ // Paper start - delay chunk unloads for player tickets
-+ long delayChunkUnloadsBy = chunkMap.level.paperConfig().chunks.delayChunkUnloadsBy.ticks();
-+ if (ticket.getType() == TicketType.PLAYER && delayChunkUnloadsBy > 0) {
-+ boolean hasPlayer = false;
-+ for (Ticket<?> ticket1 : arraysetsorted) {
-+ if (ticket1.getType() == TicketType.PLAYER) {
-+ hasPlayer = true;
-+ break;
-+ }
-+ }
-+ ChunkHolder playerChunk = chunkMap.getUpdatingChunkIfPresent(i);
-+ if (!hasPlayer && playerChunk != null && playerChunk.isFullChunkReady()) {
-+ Ticket<Long> delayUnload = new Ticket<Long>(TicketType.DELAY_UNLOAD, 33, i);
-+ delayUnload.delayUnloadBy = delayChunkUnloadsBy;
-+ delayUnload.setCreatedTick(this.ticketTickCounter);
-+ arraysetsorted.remove(delayUnload);
-+ // refresh ticket
-+ arraysetsorted.add(delayUnload);
-+ }
-+ }
-+ // Paper end
- }
-
- if (arraysetsorted.isEmpty()) {
-diff --git a/src/main/java/net/minecraft/server/level/Ticket.java b/src/main/java/net/minecraft/server/level/Ticket.java
-index ffc43e5d3d0563c9e9c171064511b2c65ddf67e1..f1128f0d4a9a0241ac6c9bc18dd13b431c616bb1 100644
---- a/src/main/java/net/minecraft/server/level/Ticket.java
-+++ b/src/main/java/net/minecraft/server/level/Ticket.java
-@@ -7,11 +7,13 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
- private final int ticketLevel;
- public final T key;
- public long createdTick;
-+ public long delayUnloadBy; // Paper
-
- protected Ticket(TicketType<T> type, int level, T argument) {
- this.type = type;
- this.ticketLevel = level;
- this.key = argument;
-+ this.delayUnloadBy = type.timeout; // Paper
- }
-
- @Override
-@@ -60,7 +62,7 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
- }
-
- protected boolean timedOut(long currentTick) {
-- long l = this.type.timeout();
-+ long l = delayUnloadBy; // Paper
- return l != 0L && currentTick - this.createdTick > l;
- }
- }
-diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java
-index 0d536d72ac918fbd403397ff369d10143ee9c204..dfa08dbf025ed702a864280a540e0169b9f33cbd 100644
---- a/src/main/java/net/minecraft/server/level/TicketType.java
-+++ b/src/main/java/net/minecraft/server/level/TicketType.java
-@@ -26,6 +26,7 @@ public class TicketType<T> {
- public static final TicketType<ChunkPos> UNKNOWN = TicketType.create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1);
- public static final TicketType<Unit> PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit
- public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
-+ public static final TicketType<Long> DELAY_UNLOAD = create("delay_unload", Long::compareTo, 300); // Paper
-
- public static <T> TicketType<T> create(String name, Comparator<T> argumentComparator) {
- return new TicketType<>(name, argumentComparator, 0L);
diff --git a/patches/server/0023-Configurable-baby-zombie-movement-speed.patch b/patches/server/0019-Configurable-baby-zombie-movement-speed.patch
index e0d10bc34b..e0d10bc34b 100644
--- a/patches/server/0023-Configurable-baby-zombie-movement-speed.patch
+++ b/patches/server/0019-Configurable-baby-zombie-movement-speed.patch
diff --git a/patches/server/0024-Configurable-fishing-time-ranges.patch b/patches/server/0020-Configurable-fishing-time-ranges.patch
index 71d66591ff..71d66591ff 100644
--- a/patches/server/0024-Configurable-fishing-time-ranges.patch
+++ b/patches/server/0020-Configurable-fishing-time-ranges.patch
diff --git a/patches/server/0025-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch b/patches/server/0021-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch
index 621e6370ae..621e6370ae 100644
--- a/patches/server/0025-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch
+++ b/patches/server/0021-Allow-nerfed-mobs-to-jump-and-take-water-damage.patch
diff --git a/patches/server/0026-Add-configurable-despawn-distances-for-living-entiti.patch b/patches/server/0022-Add-configurable-despawn-distances-for-living-entiti.patch
index a88ee8f743..a88ee8f743 100644
--- a/patches/server/0026-Add-configurable-despawn-distances-for-living-entiti.patch
+++ b/patches/server/0022-Add-configurable-despawn-distances-for-living-entiti.patch
diff --git a/patches/server/0027-Allow-for-toggling-of-spawn-chunks.patch b/patches/server/0023-Allow-for-toggling-of-spawn-chunks.patch
index eb27f9892b..69db9e2246 100644
--- a/patches/server/0027-Allow-for-toggling-of-spawn-chunks.patch
+++ b/patches/server/0023-Allow-for-toggling-of-spawn-chunks.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Allow for toggling of spawn chunks
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 39d64f3aeb998df5452699e098148d86fdd48c98..46d4e56ff939034de79ce045e348753d181c7ce3 100644
+index c12e2ecaea13597f56254e3ab7fd83bff129ddd3..e4dfaec6a1efa7bb44c843a283dd3c3d3a4e199c 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -253,6 +253,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0028-Drop-falling-block-and-tnt-entities-at-the-specified.patch b/patches/server/0024-Drop-falling-block-and-tnt-entities-at-the-specified.patch
index ed1b412de9..ed1b412de9 100644
--- a/patches/server/0028-Drop-falling-block-and-tnt-entities-at-the-specified.patch
+++ b/patches/server/0024-Drop-falling-block-and-tnt-entities-at-the-specified.patch
diff --git a/patches/server/0029-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch b/patches/server/0025-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch
index a259b9c63f..268c394466 100644
--- a/patches/server/0029-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch
+++ b/patches/server/0025-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Show 'Paper' in client crashes, server lists, and Mojang
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index d53fb6bba90936c1182b0687d014964cef81694f..645eaf7e6717486b583c49724c5891d5c270c38f 100644
+index 317cd6f68c2368b2f70dfb809db3e418de040f05..4d7ea3b3b8fb00b2b02c79d90a067c2f32b46988 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1432,7 +1432,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1426,7 +1426,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@DontObfuscate
public String getServerModName() {
@@ -19,7 +19,7 @@ index d53fb6bba90936c1182b0687d014964cef81694f..645eaf7e6717486b583c49724c5891d5
public SystemReport fillSystemReport(SystemReport details) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 37fefdf0d96cd2b6e23b6e69ee5a8db16f0e51da..fc22de3e1bb4e01fc2c43ffd9ecd0a8cd6d40ba5 100644
+index 6723b52fd9306b0f9e3f1d0eadcbfd76d383beee..92a4deb9202fd20143b8890e781b4116e3706dd8 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -246,7 +246,7 @@ import org.yaml.snakeyaml.error.MarkedYAMLException;
@@ -63,10 +63,10 @@ index ef0bbec06981afa13384fb75067599eaa1a87e33..dbdb2a30c5dbe4f02bc1d1a8ce7294d2
net.minecraft.server.Main.main(options);
} catch (Throwable t) {
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
-index 581cde7a74e00bee1ce69086132d5f871d206399..f8018902365e9115ae0132885a2546d115b24c36 100644
+index f1194eb6fdfba60959e00080d0562f2820d13b27..11d7ede26b46d0bf9cced65e8c3bcc41c8b66dbf 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
-@@ -19,7 +19,7 @@ public class WatchdogThread extends Thread
+@@ -19,7 +19,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
private WatchdogThread(long timeoutTime, boolean restart)
{
@@ -75,7 +75,7 @@ index 581cde7a74e00bee1ce69086132d5f871d206399..f8018902365e9115ae0132885a2546d1
this.timeoutTime = timeoutTime;
this.restart = restart;
}
-@@ -65,14 +65,14 @@ public class WatchdogThread extends Thread
+@@ -65,14 +65,14 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
{
Logger log = Bukkit.getServer().getLogger();
log.log( Level.SEVERE, "------------------------------" );
@@ -93,12 +93,12 @@ index 581cde7a74e00bee1ce69086132d5f871d206399..f8018902365e9115ae0132885a2546d1
//
if ( net.minecraft.world.level.Level.lastPhysicsProblem != null )
{
-@@ -82,7 +82,7 @@ public class WatchdogThread extends Thread
+@@ -82,7 +82,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
}
//
log.log( Level.SEVERE, "------------------------------" );
- log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" );
+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
- com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
log.log( Level.SEVERE, "------------------------------" );
diff --git a/patches/server/0030-Implement-Paper-VersionChecker.patch b/patches/server/0026-Implement-Paper-VersionChecker.patch
index ed72f43e9d..ed72f43e9d 100644
--- a/patches/server/0030-Implement-Paper-VersionChecker.patch
+++ b/patches/server/0026-Implement-Paper-VersionChecker.patch
diff --git a/patches/server/0031-Add-version-history-to-version-command.patch b/patches/server/0027-Add-version-history-to-version-command.patch
index 3adbac0862..11da12b0bc 100644
--- a/patches/server/0031-Add-version-history-to-version-command.patch
+++ b/patches/server/0027-Add-version-history-to-version-command.patch
@@ -201,7 +201,7 @@ index 0000000000000000000000000000000000000000..aac3f66cb23d260729c2a48d8710a9de
+ }
+}
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index c905602d23cdf3af1de7ab4419f11856b07da2ba..d70d97c65d5bdb47a17a226d65bad8ba1421b11b 100644
+index fa1ab9974859c75075f3090e36e0de58dda3e8e6..4dc5a5888f0180e1490597e43956e8e80981f8b9 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -194,6 +194,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
diff --git a/patches/server/0032-Player-affects-spawning-API.patch b/patches/server/0028-Player-affects-spawning-API.patch
index c3767ea521..db71dd0ac3 100644
--- a/patches/server/0032-Player-affects-spawning-API.patch
+++ b/patches/server/0028-Player-affects-spawning-API.patch
@@ -74,10 +74,10 @@ index ab22310d5ab4ad7014b88080cbd44a2881002b55..c5b9b19763fd944b4f31c6d3c9b71d37
public void clientTick(Level world, BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java
-index 7c5918f84d2b8f9c778258b7e7d745105effb082..cfb286020b8ee87bad7edbda4cd0b999fb607a06 100644
+index f62e4e36dd26bde067e4787fd0da1440b15a57fa..be2ec73b5900354a9c340b0a03affd59dbf55377 100644
--- a/src/main/java/net/minecraft/world/level/EntityGetter.java
+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java
-@@ -70,8 +70,8 @@ public interface EntityGetter {
+@@ -82,8 +82,8 @@ public interface EntityGetter {
}
}
@@ -88,7 +88,7 @@ index 7c5918f84d2b8f9c778258b7e7d745105effb082..cfb286020b8ee87bad7edbda4cd0b999
double d = -1.0D;
Player player = null;
-@@ -99,6 +99,27 @@ public interface EntityGetter {
+@@ -111,6 +111,27 @@ public interface EntityGetter {
return this.getNearestPlayer(x, y, z, maxDistance, predicate);
}
@@ -117,10 +117,10 @@ index 7c5918f84d2b8f9c778258b7e7d745105effb082..cfb286020b8ee87bad7edbda4cd0b999
for(Player player : this.players()) {
if (EntitySelector.NO_SPECTATORS.test(player) && EntitySelector.LIVING_ENTITY_STILL_ALIVE.test(player)) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index d5e6123622f6bbc4c3b8b91be7f9ba8e5c1c22d8..213024c868e7e33cd1ecef4173449b02f5b9b425 100644
+index 6708c2c88251f4e5f623cd785c14b2d3ed2d3de0..182881c867e68e59e89f56f8dc66001b0d2b3add 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -2009,8 +2009,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2084,8 +2084,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public String getLocale() {
return this.getHandle().locale;
diff --git a/patches/server/0033-Further-improve-server-tick-loop.patch b/patches/server/0029-Further-improve-server-tick-loop.patch
index f9f86a3271..e6f977211f 100644
--- a/patches/server/0033-Further-improve-server-tick-loop.patch
+++ b/patches/server/0029-Further-improve-server-tick-loop.patch
@@ -12,7 +12,7 @@ Previous implementation did not calculate TPS correctly.
Switch to a realistic rolling average and factor in std deviation as an extra reporting variable
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 645eaf7e6717486b583c49724c5891d5c270c38f..48a393f66013bf210ddac1e95496e1ec3727f314 100644
+index 1d4b3f02ebf918bf669647635bf236ca6094c0c7..a7e58261bc54f1177045f094678e85d00b054435 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -270,7 +270,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -33,7 +33,7 @@ index 645eaf7e6717486b583c49724c5891d5c270c38f..48a393f66013bf210ddac1e95496e1ec
public final double[] recentTps = new double[ 3 ];
// Spigot end
public final io.papermc.paper.configuration.PaperConfigurations paperConfigurations;
-@@ -964,6 +964,57 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -945,6 +945,57 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
{
return ( avg * exp ) + ( tps * ( 1 - exp ) );
}
@@ -90,8 +90,8 @@ index 645eaf7e6717486b583c49724c5891d5c270c38f..48a393f66013bf210ddac1e95496e1ec
+ // Paper End
// Spigot End
- protected void runServer() {
-@@ -981,26 +1032,33 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ public static volatile RuntimeException chunkSystemCrash; // Paper - rewrite chunk system
+@@ -964,7 +1015,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Spigot start
Arrays.fill( recentTps, 20 );
@@ -99,17 +99,18 @@ index 645eaf7e6717486b583c49724c5891d5c270c38f..48a393f66013bf210ddac1e95496e1ec
+ long start = System.nanoTime(), curTime, tickSection = start; // Paper - Further improve server tick loop
+ lastTick = start - TICK_TIME; // Paper
while (this.running) {
+ // Paper start - rewrite chunk system
+ // guarantee that nothing can stop the server from halting if it can at least still tick
+@@ -972,7 +1024,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ throw this.chunkSystemCrash;
+ }
+ // Paper end - rewrite chunk system
- long i = (curTime = Util.getMillis()) - this.nextTickTime;
+ long i = ((curTime = System.nanoTime()) / (1000L * 1000L)) - this.nextTickTime; // Paper
if (i > 5000L && this.nextTickTime - this.lastOverloadWarning >= 30000L) { // CraftBukkit
long j = i / 50L;
-
- if (this.server.getWarnOnOverload()) // CraftBukkit
-- MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", i, j);
-+ MinecraftServer.LOGGER.warn("Can't keep up! Is the server overloaded? Running {}ms or {} ticks behind", i, j);
- this.nextTickTime += j * 50L;
- this.lastOverloadWarning = this.nextTickTime;
+@@ -984,12 +1036,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
++MinecraftServer.currentTickLong; // Paper
@@ -133,7 +134,7 @@ index 645eaf7e6717486b583c49724c5891d5c270c38f..48a393f66013bf210ddac1e95496e1ec
tickSection = curTime;
}
// Spigot end
-@@ -1010,7 +1068,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -999,7 +1057,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
}
@@ -144,7 +145,7 @@ index 645eaf7e6717486b583c49724c5891d5c270c38f..48a393f66013bf210ddac1e95496e1ec
this.startMetricsRecordingTick();
this.profiler.push("tick");
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index fc22de3e1bb4e01fc2c43ffd9ecd0a8cd6d40ba5..56efe95512c851b965f2295d5eac7bc0c67bdb1f 100644
+index 92a4deb9202fd20143b8890e781b4116e3706dd8..58f3ee6c47313b5c3b093a03b1b759e9fb0207ee 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2409,6 +2409,17 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0034-Only-refresh-abilities-if-needed.patch b/patches/server/0030-Only-refresh-abilities-if-needed.patch
index 899a5ac925..c1fff86ca3 100644
--- a/patches/server/0034-Only-refresh-abilities-if-needed.patch
+++ b/patches/server/0030-Only-refresh-abilities-if-needed.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Only refresh abilities if needed
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 213024c868e7e33cd1ecef4173449b02f5b9b425..00df00676337435e6ec62a86dc03fecde30f9819 100644
+index 182881c867e68e59e89f56f8dc66001b0d2b3add..8e6e7c5960b9273e816d7275b8a3058024190f5a 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -1678,12 +1678,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1753,12 +1753,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void setFlying(boolean value) {
diff --git a/patches/server/0035-Entity-Origin-API.patch b/patches/server/0031-Entity-Origin-API.patch
index f8e6475651..5e1fdf91d4 100644
--- a/patches/server/0035-Entity-Origin-API.patch
+++ b/patches/server/0031-Entity-Origin-API.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Entity Origin API
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 2f7646e2bcc9622d8579eec25b56615da5a84d06..f5ded21e15ca425d23af90f0e339a961c5600504 100644
+index 7c709be5d43bcd45064c79e84d5b2fff0b3d0cfe..e5fcabaaf600eabab2192e12c3c0ecce3fbecf31 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2185,6 +2185,15 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2235,6 +2235,15 @@ public class ServerLevel extends Level implements WorldGenLevel {
entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
entity.valid = true; // CraftBukkit
@@ -25,7 +25,7 @@ index 2f7646e2bcc9622d8579eec25b56615da5a84d06..f5ded21e15ca425d23af90f0e339a961
public void onTrackingEnd(Entity entity) {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 9878aded49d0049b066fa608c7eaf25a55fcf12e..385c81c9e0faf7a51d24b3542713e0d57e5398dd 100644
+index 1a2ee5824c6af6b548e7006d583b73f4eba0f64a..ab2671c21f2f44973fb2507b178ac8e1e03bc8dc 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -304,7 +304,27 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -56,7 +56,7 @@ index 9878aded49d0049b066fa608c7eaf25a55fcf12e..385c81c9e0faf7a51d24b3542713e0d5
public float getBukkitYaw() {
return this.yRot;
}
-@@ -1861,6 +1881,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1913,6 +1933,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.bukkitEntity.storeBukkitValues(nbt);
}
// CraftBukkit end
@@ -72,7 +72,7 @@ index 9878aded49d0049b066fa608c7eaf25a55fcf12e..385c81c9e0faf7a51d24b3542713e0d5
return nbt;
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT");
-@@ -1987,6 +2016,20 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2039,6 +2068,20 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
// CraftBukkit end
diff --git a/patches/server/0036-Prevent-tile-entity-and-entity-crashes.patch b/patches/server/0032-Prevent-tile-entity-and-entity-crashes.patch
index bd67245c87..ce3f080af3 100644
--- a/patches/server/0036-Prevent-tile-entity-and-entity-crashes.patch
+++ b/patches/server/0032-Prevent-tile-entity-and-entity-crashes.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Prevent tile entity and entity crashes
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 46d4e56ff939034de79ce045e348753d181c7ce3..a73684fc706beca7342786e5d92d6cae750a891c 100644
+index e4dfaec6a1efa7bb44c843a283dd3c3d3a4e199c..4c4dff95b88c4f228c9e0eb51613ed1094bec8a5 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -698,11 +698,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -703,11 +703,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
try {
tickConsumer.accept(entity);
} catch (Throwable throwable) {
@@ -44,10 +44,10 @@ index be08224c8107aab3e9a3645a20977dd14bfff782..c518704386f14cd033307dd976455c35
}
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 893051059df51133a127b0870e27ab67461052fa..51b53bbed1dbd344d8646d4827e44ee7215524a9 100644
+index 8b65e8361918b5e6fe936fec99ee63ff9eef66f0..a4594b6b28eab545694491bc547f05a971a6ffad 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -1051,11 +1051,11 @@ public class LevelChunk extends ChunkAccess {
+@@ -1166,11 +1166,11 @@ public class LevelChunk extends ChunkAccess {
gameprofilerfiller.pop();
} catch (Throwable throwable) {
diff --git a/patches/server/0037-Configurable-top-of-nether-void-damage.patch b/patches/server/0033-Configurable-top-of-nether-void-damage.patch
index c7d17a7bc7..a1d481b12e 100644
--- a/patches/server/0037-Configurable-top-of-nether-void-damage.patch
+++ b/patches/server/0033-Configurable-top-of-nether-void-damage.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Configurable top of nether void damage
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 385c81c9e0faf7a51d24b3542713e0d57e5398dd..f9f0cb28811e3a14bf4b5005050920b4992f868b 100644
+index ab2671c21f2f44973fb2507b178ac8e1e03bc8dc..4b5ec8921a86adf919e87ec9d7b2e58705478022 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -653,7 +653,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -705,7 +705,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public void checkOutOfWorld() {
diff --git a/patches/server/0038-Check-online-mode-before-converting-and-renaming-pla.patch b/patches/server/0034-Check-online-mode-before-converting-and-renaming-pla.patch
index 64f713e616..06f27ae774 100644
--- a/patches/server/0038-Check-online-mode-before-converting-and-renaming-pla.patch
+++ b/patches/server/0034-Check-online-mode-before-converting-and-renaming-pla.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Check online mode before converting and renaming player data
diff --git a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
-index 86fb11e9e197357871d603c4f8ce778660d507cf..0132eb685dbcf3d3402096bd34513e91d2b20abd 100644
+index bf4c895794c2bc2ad65faa128c6fa92cb0656841..6909b6be840e8d4feadcc8b0c5a44fc1b9c81a54 100644
--- a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -56,7 +56,7 @@ public class PlayerDataStorage {
diff --git a/patches/server/0039-Always-tick-falling-blocks.patch b/patches/server/0035-Always-tick-falling-blocks.patch
index c52ac50408..39f37a0fd0 100644
--- a/patches/server/0039-Always-tick-falling-blocks.patch
+++ b/patches/server/0035-Always-tick-falling-blocks.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Always tick falling blocks
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
-index 38cf408899cef72bc9d2888109a7ac7ce0aec638..07e5ece37af6b02210920ce6cc31738274d447a9 100644
+index d3639643cda7d8ccf3c1208502605120590a2d30..e2dfc4d9a16a738dd0fe91838603e1d4370afa56 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -89,6 +89,7 @@ public class ActivationRange
diff --git a/patches/server/0040-Configurable-end-credits.patch b/patches/server/0036-Configurable-end-credits.patch
index f36351998f..007435653d 100644
--- a/patches/server/0040-Configurable-end-credits.patch
+++ b/patches/server/0036-Configurable-end-credits.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Configurable end credits
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 4adf2d503015cac85b12fbaae833b33eeeb44403..13bb12a2d1260b1c20ce3c8755906c2227f29f86 100644
+index 5aad3da061d391d1003bdcca95dd4f7e5c0e5ea8..301f78d1f9a0eae05096de071bda7def3a45f648 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1004,6 +1004,7 @@ public class ServerPlayer extends Player {
+@@ -989,6 +989,7 @@ public class ServerPlayer extends Player {
this.unRide();
this.getLevel().removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION);
if (!this.wonGame) {
diff --git a/patches/server/0041-Fix-lag-from-explosions-processing-dead-entities.patch b/patches/server/0037-Fix-lag-from-explosions-processing-dead-entities.patch
index 3a8886ecac..3a8886ecac 100644
--- a/patches/server/0041-Fix-lag-from-explosions-processing-dead-entities.patch
+++ b/patches/server/0037-Fix-lag-from-explosions-processing-dead-entities.patch
diff --git a/patches/server/0042-Optimize-explosions.patch b/patches/server/0038-Optimize-explosions.patch
index f644570779..41f7ebc370 100644
--- a/patches/server/0042-Optimize-explosions.patch
+++ b/patches/server/0038-Optimize-explosions.patch
@@ -10,10 +10,10 @@ This patch adds a per-tick cache that is used for storing and retrieving
an entity's exposure during an explosion.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 48a393f66013bf210ddac1e95496e1ec3727f314..a65d86f7b48bf1fbc2186804a8c05921363c7272 100644
+index f24b2ea3932c660710f5b38e50b45f40b18e98ea..c0b8daca75feec011423ba4b4ef5900558bc4d92 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1395,6 +1395,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1389,6 +1389,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.profiler.pop();
this.profiler.pop();
@@ -120,7 +120,7 @@ index 292571fc9fa999d3b92e0fdd56d07ebfb4ae7402..db7a025cd064c898e33037133b65eecc
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index a73684fc706beca7342786e5d92d6cae750a891c..2c8ea4f494c25c107bb0b0b44d84be237f77c2c8 100644
+index 4c4dff95b88c4f228c9e0eb51613ed1094bec8a5..2439c5e1eae1582196b1d2103b1ebd22140f3fc1 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -161,6 +161,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0043-Disable-explosion-knockback.patch b/patches/server/0039-Disable-explosion-knockback.patch
index 72708a7e53..72708a7e53 100644
--- a/patches/server/0043-Disable-explosion-knockback.patch
+++ b/patches/server/0039-Disable-explosion-knockback.patch
diff --git a/patches/server/0044-Disable-thunder.patch b/patches/server/0040-Disable-thunder.patch
index cdf69c62c6..326c236157 100644
--- a/patches/server/0044-Disable-thunder.patch
+++ b/patches/server/0040-Disable-thunder.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Disable thunder
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index f5ded21e15ca425d23af90f0e339a961c5600504..e44b2662da3af5967801a21aaee0b30ef939e2ac 100644
+index c58a204d9959b0836c3d9e5025b7c63d8cf0549c..fa2b27a3876af959868fab9a1852302a87f43380 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -659,7 +659,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -692,7 +692,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
gameprofilerfiller.push("thunder");
BlockPos blockposition;
diff --git a/patches/server/0045-Disable-ice-and-snow.patch b/patches/server/0041-Disable-ice-and-snow.patch
index 25327d73d7..f80eeaf912 100644
--- a/patches/server/0045-Disable-ice-and-snow.patch
+++ b/patches/server/0041-Disable-ice-and-snow.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Disable ice and snow
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index e44b2662da3af5967801a21aaee0b30ef939e2ac..3a41a346e886918160eccaee57118747e33f6cc1 100644
+index fa2b27a3876af959868fab9a1852302a87f43380..e235531d694e3e904042f2d42641c1ffbecfd5a3 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -683,7 +683,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -716,7 +716,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
gameprofilerfiller.popPush("iceandsnow");
diff --git a/patches/server/0046-Configurable-mob-spawner-tick-rate.patch b/patches/server/0042-Configurable-mob-spawner-tick-rate.patch
index b33478a7a0..b33478a7a0 100644
--- a/patches/server/0046-Configurable-mob-spawner-tick-rate.patch
+++ b/patches/server/0042-Configurable-mob-spawner-tick-rate.patch
diff --git a/patches/server/0047-Implement-PlayerLocaleChangeEvent.patch b/patches/server/0043-Implement-PlayerLocaleChangeEvent.patch
index f8e270f8fa..8fc7a9d370 100644
--- a/patches/server/0047-Implement-PlayerLocaleChangeEvent.patch
+++ b/patches/server/0043-Implement-PlayerLocaleChangeEvent.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Implement PlayerLocaleChangeEvent
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 13bb12a2d1260b1c20ce3c8755906c2227f29f86..db7e2207612b56b0869a947edd03a6d3f9209e22 100644
+index 301f78d1f9a0eae05096de071bda7def3a45f648..54f585f12890c665a097845acfe13108691f12b6 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1770,7 +1770,7 @@ public class ServerPlayer extends Player {
+@@ -1755,7 +1755,7 @@ public class ServerPlayer extends Player {
return s;
}
@@ -17,7 +17,7 @@ index 13bb12a2d1260b1c20ce3c8755906c2227f29f86..db7e2207612b56b0869a947edd03a6d3
public java.util.Locale adventure$locale = java.util.Locale.US; // Paper
public void updateOptions(ServerboundClientInformationPacket packet) {
// CraftBukkit start
-@@ -1778,9 +1778,10 @@ public class ServerPlayer extends Player {
+@@ -1763,9 +1763,10 @@ public class ServerPlayer extends Player {
PlayerChangedMainHandEvent event = new PlayerChangedMainHandEvent(this.getBukkitEntity(), getMainArm() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT);
this.server.server.getPluginManager().callEvent(event);
}
@@ -30,10 +30,10 @@ index 13bb12a2d1260b1c20ce3c8755906c2227f29f86..db7e2207612b56b0869a947edd03a6d3
this.locale = packet.language;
// Paper start
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 00df00676337435e6ec62a86dc03fecde30f9819..9d2506a042b49089094be79b5d0ed54f088b9625 100644
+index 8e6e7c5960b9273e816d7275b8a3058024190f5a..3be63b51511fd12f295e1f07549f281246ee2dc0 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -2009,8 +2009,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2084,8 +2084,10 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public String getLocale() {
diff --git a/patches/server/0049-Add-BeaconEffectEvent.patch b/patches/server/0044-Add-BeaconEffectEvent.patch
index 4c04a07cd0..4c04a07cd0 100644
--- a/patches/server/0049-Add-BeaconEffectEvent.patch
+++ b/patches/server/0044-Add-BeaconEffectEvent.patch
diff --git a/patches/server/0050-Configurable-container-update-tick-rate.patch b/patches/server/0045-Configurable-container-update-tick-rate.patch
index 21abeaf37e..f5d80072e5 100644
--- a/patches/server/0050-Configurable-container-update-tick-rate.patch
+++ b/patches/server/0045-Configurable-container-update-tick-rate.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Configurable container update tick rate
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 981e60c7bf2eee52e84f9894ff689631388a7715..4d8dfe375f5b3b9e5cfc12a6af0b87ae78f9b764 100644
+index 54f585f12890c665a097845acfe13108691f12b6..2ffce1200abe7ab7b50a187069e63392c0b3bfdd 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -229,6 +229,7 @@ public class ServerPlayer extends Player {
+@@ -228,6 +228,7 @@ public class ServerPlayer extends Player {
private int containerCounter;
public int latency;
public boolean wonGame;
@@ -16,7 +16,7 @@ index 981e60c7bf2eee52e84f9894ff689631388a7715..4d8dfe375f5b3b9e5cfc12a6af0b87ae
// CraftBukkit start
public String displayName;
-@@ -616,7 +617,12 @@ public class ServerPlayer extends Player {
+@@ -601,7 +602,12 @@ public class ServerPlayer extends Player {
--this.invulnerableTime;
}
diff --git a/patches/server/0051-Use-UserCache-for-player-heads.patch b/patches/server/0046-Use-UserCache-for-player-heads.patch
index 21457dbd7f..21457dbd7f 100644
--- a/patches/server/0051-Use-UserCache-for-player-heads.patch
+++ b/patches/server/0046-Use-UserCache-for-player-heads.patch
diff --git a/patches/server/0052-Disable-spigot-tick-limiters.patch b/patches/server/0047-Disable-spigot-tick-limiters.patch
index b750d86014..d7fe02ccde 100644
--- a/patches/server/0052-Disable-spigot-tick-limiters.patch
+++ b/patches/server/0047-Disable-spigot-tick-limiters.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Disable spigot tick limiters
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 2c8ea4f494c25c107bb0b0b44d84be237f77c2c8..7b3a81876f04c6aff370ac9cc97b0c9270f34a2e 100644
+index 2439c5e1eae1582196b1d2103b1ebd22140f3fc1..1473664f94f228abd81b8c654d105b8a76cc49e9 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -664,9 +664,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -669,9 +669,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// Spigot start
// Iterator iterator = this.blockEntityTickers.iterator();
int tilesThisCycle = 0;
diff --git a/patches/server/0053-Add-PlayerInitialSpawnEvent.patch b/patches/server/0048-Add-PlayerInitialSpawnEvent.patch
index 4242de963c..40ba8bc5da 100644
--- a/patches/server/0053-Add-PlayerInitialSpawnEvent.patch
+++ b/patches/server/0048-Add-PlayerInitialSpawnEvent.patch
@@ -9,7 +9,7 @@ This is a duplicate API from spigot, so use our duplicate subclass and
improve setPosition to use raw
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 8e0f73dcef189442450b4518437fb3a1c34b9a47..c003a1e8b4c2b2331abc4536352abdec20fef42e 100644
+index 3235d6f98794709a53208e20ef33f2164725be48..dfb53366a0b36b6a4cc43c66837f98fc372c6d2e 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -223,7 +223,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0054-Configurable-Disabling-Cat-Chest-Detection.patch b/patches/server/0049-Configurable-Disabling-Cat-Chest-Detection.patch
index 85180f4352..85180f4352 100644
--- a/patches/server/0054-Configurable-Disabling-Cat-Chest-Detection.patch
+++ b/patches/server/0049-Configurable-Disabling-Cat-Chest-Detection.patch
diff --git a/patches/server/0055-Ensure-commands-are-not-ran-async.patch b/patches/server/0050-Ensure-commands-are-not-ran-async.patch
index a19a940f1f..80e44683ab 100644
--- a/patches/server/0055-Ensure-commands-are-not-ran-async.patch
+++ b/patches/server/0050-Ensure-commands-are-not-ran-async.patch
@@ -74,7 +74,7 @@ index 22c095539425a6667b8e7f5c5f0a8ff2e87adfb5..e21a6961bab606036440f2a6bd90998b
if ( org.spigotmc.SpigotConfig.logCommands ) // Spigot
this.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + s);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 56efe95512c851b965f2295d5eac7bc0c67bdb1f..1c6577bbbc005131661cbb4667cff6494b8fe5e4 100644
+index 58f3ee6c47313b5c3b093a03b1b759e9fb0207ee..524338428265f27aa33774a23b53c24212d98ae2 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -863,6 +863,28 @@ public final class CraftServer implements Server {
@@ -107,10 +107,10 @@ index 56efe95512c851b965f2295d5eac7bc0c67bdb1f..1c6577bbbc005131661cbb4667cff649
return true;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index f2ada37115392466edbed8a2d331084aaaf7b774..2811d631dfd0f4013579734486c3ebbca4cfaad2 100644
+index 3be63b51511fd12f295e1f07549f281246ee2dc0..94ec829c549dac568a94c3262d9de026ff9d9a39 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -467,7 +467,19 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -502,7 +502,19 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void chat(String msg) {
if (this.getHandle().connection == null) return;
@@ -144,7 +144,7 @@ index 19c44daaa407b7c1c7a7ffe56fef8c8814c6d5b2..6a073a9dc44d93eba296a0e18a9c7be8
} finally {
try {
diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java
-index 78669fa035b7537ff7e533cf32aaf2995625424f..7585a30e8f063ac2656b5de519b1e9edaceffbc7 100644
+index 05e94702e42b8f5c35d2a112c486d57948a3acba..5409f230fdd53b70fc03c58177438534731ad4e6 100644
--- a/src/main/java/org/spigotmc/AsyncCatcher.java
+++ b/src/main/java/org/spigotmc/AsyncCatcher.java
@@ -6,6 +6,7 @@ public class AsyncCatcher
diff --git a/patches/server/0056-All-chunks-are-slime-spawn-chunks-toggle.patch b/patches/server/0051-All-chunks-are-slime-spawn-chunks-toggle.patch
index e0111a4cd0..a83c2278d3 100644
--- a/patches/server/0056-All-chunks-are-slime-spawn-chunks-toggle.patch
+++ b/patches/server/0051-All-chunks-are-slime-spawn-chunks-toggle.patch
@@ -18,10 +18,10 @@ index a62f88048d8243f5b98834988c049471114c4199..7b95d18fb5edbd1575f6be92bfcbb3e0
if (random.nextInt(10) == 0 && flag && pos.getY() < 40) {
return checkMobSpawnRules(type, world, spawnReason, pos, random);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-index 6d44634ae6dcbc392011f248f6ab429b9845af55..bda9c6720fe0a91a4c6763795e4d6e01e6dd2747 100644
+index 29465d24b6c9160fcd6293006dcc26bcfbeb5e10..fd50cc4cfc976a9dee82d4f6ad457bba18065614 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-@@ -220,7 +220,7 @@ public class CraftChunk implements Chunk {
+@@ -176,7 +176,7 @@ public class CraftChunk implements Chunk {
@Override
public boolean isSlimeChunk() {
// 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk
diff --git a/patches/server/0057-Expose-server-CommandMap.patch b/patches/server/0052-Expose-server-CommandMap.patch
index 4270ea1e72..cf79a898e2 100644
--- a/patches/server/0057-Expose-server-CommandMap.patch
+++ b/patches/server/0052-Expose-server-CommandMap.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Expose server CommandMap
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 1c6577bbbc005131661cbb4667cff6494b8fe5e4..fd3ccc1726cd73e9f5be3f936115e2431c77183c 100644
+index 524338428265f27aa33774a23b53c24212d98ae2..458c9ce406421f77c9a196f9081f2f36e7e675ea 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1983,6 +1983,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0058-Be-a-bit-more-informative-in-maxHealth-exception.patch b/patches/server/0053-Be-a-bit-more-informative-in-maxHealth-exception.patch
index 72602b87ec..72602b87ec 100644
--- a/patches/server/0058-Be-a-bit-more-informative-in-maxHealth-exception.patch
+++ b/patches/server/0053-Be-a-bit-more-informative-in-maxHealth-exception.patch
diff --git a/patches/server/0059-Ensure-inv-drag-is-in-bounds.patch b/patches/server/0054-Ensure-inv-drag-is-in-bounds.patch
index da2779ebd5..da2779ebd5 100644
--- a/patches/server/0059-Ensure-inv-drag-is-in-bounds.patch
+++ b/patches/server/0054-Ensure-inv-drag-is-in-bounds.patch
diff --git a/patches/server/0060-Player-Tab-List-and-Title-APIs.patch b/patches/server/0055-Player-Tab-List-and-Title-APIs.patch
index 51f179ec48..c530b1515a 100644
--- a/patches/server/0060-Player-Tab-List-and-Title-APIs.patch
+++ b/patches/server/0055-Player-Tab-List-and-Title-APIs.patch
@@ -63,7 +63,7 @@ index bd808eb312ade7122973a47f4b96505829511da5..bf0f9cab7c66c089f35b851e799ba4a4
// Paper end
buf.writeComponent(this.text);
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 2811d631dfd0f4013579734486c3ebbca4cfaad2..59a9168a1158d47eaa6bca223318db8c543023a0 100644
+index 94ec829c549dac568a94c3262d9de026ff9d9a39..6f5248febd3f84f2f5bc83806cbb9953600b71b6 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1,5 +1,6 @@
@@ -73,7 +73,7 @@ index 2811d631dfd0f4013579734486c3ebbca4cfaad2..59a9168a1158d47eaa6bca223318db8c
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.BaseEncoding;
-@@ -273,6 +274,100 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -348,6 +349,100 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
}
diff --git a/patches/server/0061-Add-configurable-portal-search-radius.patch b/patches/server/0056-Add-configurable-portal-search-radius.patch
index 14632c6848..64055f18b0 100644
--- a/patches/server/0061-Add-configurable-portal-search-radius.patch
+++ b/patches/server/0056-Add-configurable-portal-search-radius.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add configurable portal search radius
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index f9f0cb28811e3a14bf4b5005050920b4992f868b..4b5babb9fa755ba2897fc633c8eba30cfd220ea0 100644
+index 4b5ec8921a86adf919e87ec9d7b2e58705478022..71d539341c2ae8c64befc54ef974daa06a859fa5 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2951,7 +2951,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3003,7 +3003,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
double d0 = DimensionType.getTeleportationScale(this.level.dimensionType(), destination.dimensionType());
BlockPos blockposition = worldborder.clampToBounds(this.getX() * d0, this.getY(), this.getZ() * d0);
// CraftBukkit start
diff --git a/patches/server/0062-Add-velocity-warnings.patch b/patches/server/0057-Add-velocity-warnings.patch
index e7ff871665..6d36cf0c55 100644
--- a/patches/server/0062-Add-velocity-warnings.patch
+++ b/patches/server/0057-Add-velocity-warnings.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add velocity warnings
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index fd3ccc1726cd73e9f5be3f936115e2431c77183c..c1c86dc95d610bb391191317fa0c0e4c8e41d198 100644
+index 458c9ce406421f77c9a196f9081f2f36e7e675ea..b8763c6f938319d4712222ecab3ebc132071597b 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -280,6 +280,7 @@ public final class CraftServer implements Server {
@@ -62,10 +62,10 @@ index ddd0fde5c9065cc35b3bcf81defb119f5b0608d6..b9599f4f431098d63be98b5175890371
public double getHeight() {
return this.getHandle().getBbHeight();
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
-index f8018902365e9115ae0132885a2546d115b24c36..600c012b2bf119ddd6760b0401a2c6a22453609e 100644
+index 11d7ede26b46d0bf9cced65e8c3bcc41c8b66dbf..3ad14bf0697e682a2e777baa8faeb323d127fb13 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
-@@ -80,7 +80,19 @@ public class WatchdogThread extends Thread
+@@ -80,7 +80,19 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
log.log( Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed" );
log.log( Level.SEVERE, "near " + net.minecraft.world.level.Level.lastPhysicsProblem );
}
@@ -85,4 +85,4 @@ index f8018902365e9115ae0132885a2546d115b24c36..600c012b2bf119ddd6760b0401a2c6a2
+ // Paper end
log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
- com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system
diff --git a/patches/server/0063-Configurable-inter-world-teleportation-safety.patch b/patches/server/0058-Configurable-inter-world-teleportation-safety.patch
index 5164172bc6..154500b60e 100644
--- a/patches/server/0063-Configurable-inter-world-teleportation-safety.patch
+++ b/patches/server/0058-Configurable-inter-world-teleportation-safety.patch
@@ -16,10 +16,10 @@ The wanted destination was on top of the emerald block however the player ended
This only is the case if the player is teleporting between worlds.
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 59a9168a1158d47eaa6bca223318db8c543023a0..5bc128440e2f12e2c635db70280c46dbb5f3f3c0 100644
+index 6f5248febd3f84f2f5bc83806cbb9953600b71b6..342cd3c6ffdcf46f0329e072fae446c5e9665e33 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -1094,7 +1094,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1129,7 +1129,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
if (fromWorld == toWorld) {
entity.connection.teleport(to);
} else {
diff --git a/patches/server/0064-Add-exception-reporting-event.patch b/patches/server/0059-Add-exception-reporting-event.patch
index fb76a4b5df..528721a007 100644
--- a/patches/server/0064-Add-exception-reporting-event.patch
+++ b/patches/server/0059-Add-exception-reporting-event.patch
@@ -48,18 +48,6 @@ index 0000000000000000000000000000000000000000..f699ce18ca044f813e194ef2786b7ea8
+ return internalTask;
+ }
+}
-diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 46bfaf04867d913c1782d851de101d913376c63a..4af8cee31d20e5dcec510439795e7e90fc668128 100644
---- a/src/main/java/net/minecraft/server/level/ChunkMap.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1187,6 +1187,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- return true;
- } catch (Exception exception) {
- ChunkMap.LOGGER.error("Failed to save chunk {},{}", new Object[]{chunkcoordintpair.x, chunkcoordintpair.z, exception});
-+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper
- return false;
- }
- }
diff --git a/src/main/java/net/minecraft/server/players/OldUsersConverter.java b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
index 439ff01be1521c283d60cacb110fcb4993933057..da98f074ccd5a40c635824112c97fd174c393cb1 100644
--- a/src/main/java/net/minecraft/server/players/OldUsersConverter.java
@@ -100,7 +88,7 @@ index c6fb4c33d7ea52b88d8fc0d90748cbab7387c565..fed09b886f4fa0006d160e5f2abb00df
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 7b3a81876f04c6aff370ac9cc97b0c9270f34a2e..ee71a6cc552bd2bb82beda1bd44905ea4cc14604 100644
+index 1473664f94f228abd81b8c654d105b8a76cc49e9..65fd3a3c1f0a55d034e6f91c4f222e6454e7166c 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -1,5 +1,10 @@
@@ -114,7 +102,7 @@ index 7b3a81876f04c6aff370ac9cc97b0c9270f34a2e..ee71a6cc552bd2bb82beda1bd44905ea
import com.google.common.collect.Lists;
import com.mojang.serialization.Codec;
import java.io.IOException;
-@@ -700,6 +705,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -705,6 +710,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// Paper start - Prevent tile entity and entity crashes
final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
MinecraftServer.LOGGER.error(msg, throwable);
@@ -143,7 +131,7 @@ index b37e04a0c466dacf52e74a4d4fb0885511c2abc1..878fc7f57178bff0e42fd01434f0aaa2
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 51b53bbed1dbd344d8646d4827e44ee7215524a9..a4e121365717036dfc27eec86549d6dbac1ffc1a 100644
+index a4594b6b28eab545694491bc547f05a971a6ffad..20c9eada6f051ecdd5e45e625d7e6289d406a2f8 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -1,6 +1,7 @@
@@ -154,7 +142,7 @@ index 51b53bbed1dbd344d8646d4827e44ee7215524a9..a4e121365717036dfc27eec86549d6db
import com.google.common.collect.Maps;
import com.google.common.collect.UnmodifiableIterator;
import com.mojang.logging.LogUtils;
-@@ -559,10 +560,16 @@ public class LevelChunk extends ChunkAccess {
+@@ -605,10 +606,16 @@ public class LevelChunk extends ChunkAccess {
// CraftBukkit start
} else {
@@ -175,7 +163,7 @@ index 51b53bbed1dbd344d8646d4827e44ee7215524a9..a4e121365717036dfc27eec86549d6db
// CraftBukkit end
}
}
-@@ -1054,6 +1061,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -1169,6 +1176,7 @@ public class LevelChunk extends ChunkAccess {
// Paper start - Prevent tile entity and entity crashes
final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ());
net.minecraft.server.MinecraftServer.LOGGER.error(msg, throwable);
diff --git a/patches/server/0065-Don-t-nest-if-we-don-t-need-to-when-cerealising-text.patch b/patches/server/0060-Don-t-nest-if-we-don-t-need-to-when-cerealising-text.patch
index 3d801ecb90..3d801ecb90 100644
--- a/patches/server/0065-Don-t-nest-if-we-don-t-need-to-when-cerealising-text.patch
+++ b/patches/server/0060-Don-t-nest-if-we-don-t-need-to-when-cerealising-text.patch
diff --git a/patches/server/0066-Disable-Scoreboards-for-non-players-by-default.patch b/patches/server/0061-Disable-Scoreboards-for-non-players-by-default.patch
index 34c1e75d69..a9231a7bf7 100644
--- a/patches/server/0066-Disable-Scoreboards-for-non-players-by-default.patch
+++ b/patches/server/0061-Disable-Scoreboards-for-non-players-by-default.patch
@@ -11,10 +11,10 @@ So avoid looking up scoreboards and short circuit to the "not on a team"
logic which is most likely to be true.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 4b5babb9fa755ba2897fc633c8eba30cfd220ea0..6e8811a6a4b41d38c99ac40a2d4f0bef4713b762 100644
+index 71d539341c2ae8c64befc54ef974daa06a859fa5..e28ccb5651a793775166d51859620882ee54b641 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2589,6 +2589,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2641,6 +2641,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@Nullable
public Team getTeam() {
diff --git a/patches/server/0067-Add-methods-for-working-with-arrows-stuck-in-living-.patch b/patches/server/0062-Add-methods-for-working-with-arrows-stuck-in-living-.patch
index 4b1d9fd2cb..4b1d9fd2cb 100644
--- a/patches/server/0067-Add-methods-for-working-with-arrows-stuck-in-living-.patch
+++ b/patches/server/0062-Add-methods-for-working-with-arrows-stuck-in-living-.patch
diff --git a/patches/server/0068-Chunk-Save-Reattempt.patch b/patches/server/0063-Chunk-Save-Reattempt.patch
index a10ae30efd..66fe0ef951 100644
--- a/patches/server/0068-Chunk-Save-Reattempt.patch
+++ b/patches/server/0063-Chunk-Save-Reattempt.patch
@@ -19,18 +19,18 @@ index 8adebb8408cc22ae7e9e89721645e5dd27a41cd8..038e2177182c94baa4af24f9111cf155
}
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index 6fa0bc18ab05b9fb05521f46c5dadb695f1ec05b..4210c43104de01200b149e13ffab09dea37c5caf 100644
+index 45cec71b3b2f9444d7be7e5bf9fab166b28ea34d..ece0fd4b51d124be232e24d08f115b9267cb21cb 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-@@ -163,6 +163,7 @@ public class RegionFileStorage implements AutoCloseable {
- protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException {
- RegionFile regionfile = this.getRegionFile(pos, false, true); // CraftBukkit // Paper
+@@ -167,6 +167,7 @@ public class RegionFileStorage implements AutoCloseable {
+ }
+ // Paper end - rewrite chunk system
try { // Paper
+ int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper
if (nbt == null) {
regionfile.clear(pos);
-@@ -187,7 +188,18 @@ public class RegionFileStorage implements AutoCloseable {
+@@ -191,7 +192,18 @@ public class RegionFileStorage implements AutoCloseable {
dataoutputstream.close();
}
}
diff --git a/patches/server/0069-Complete-resource-pack-API.patch b/patches/server/0064-Complete-resource-pack-API.patch
index e3a7984fc5..cac7d1f1fb 100644
--- a/patches/server/0069-Complete-resource-pack-API.patch
+++ b/patches/server/0064-Complete-resource-pack-API.patch
@@ -23,7 +23,7 @@ index e21a6961bab606036440f2a6bd90998b4129ae10..9f0a5b950a022aa2a3d3d60837fdb902
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 5bc128440e2f12e2c635db70280c46dbb5f3f3c0..3846905bafd92918349793d346aca5b3a3529e9b 100644
+index 342cd3c6ffdcf46f0329e072fae446c5e9665e33..307c76b2d06b748304d28d6634e86bb91f3ded67 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -150,6 +150,7 @@ import org.bukkit.plugin.Plugin;
@@ -45,7 +45,7 @@ index 5bc128440e2f12e2c635db70280c46dbb5f3f3c0..3846905bafd92918349793d346aca5b3
public CraftPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
-@@ -2171,6 +2176,45 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2206,6 +2211,45 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public boolean getAffectsSpawning() {
return this.getHandle().affectsSpawning;
}
diff --git a/patches/server/0070-Default-loading-permissions.yml-before-plugins.patch b/patches/server/0065-Default-loading-permissions.yml-before-plugins.patch
index 6b823b51be..5e279dbea1 100644
--- a/patches/server/0070-Default-loading-permissions.yml-before-plugins.patch
+++ b/patches/server/0065-Default-loading-permissions.yml-before-plugins.patch
@@ -16,7 +16,7 @@ modify that. Under the previous logic, plugins were unable (cleanly) override pe
A config option has been added for those who depend on the previous behavior, but I don't expect that.
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index c1c86dc95d610bb391191317fa0c0e4c8e41d198..2df58a9f40843abca02125d5f094129a8a75caf5 100644
+index b8763c6f938319d4712222ecab3ebc132071597b..4d9a5297e53b1836e3ba438bae6b869f3822dd28 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -462,6 +462,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0071-Allow-Reloading-of-Custom-Permissions.patch b/patches/server/0066-Allow-Reloading-of-Custom-Permissions.patch
index fa49c037a8..4271b0655d 100644
--- a/patches/server/0071-Allow-Reloading-of-Custom-Permissions.patch
+++ b/patches/server/0066-Allow-Reloading-of-Custom-Permissions.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Allow Reloading of Custom Permissions
https://github.com/PaperMC/Paper/issues/49
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 2df58a9f40843abca02125d5f094129a8a75caf5..d4016a8890fff0805bacab3e5a556e7316c9ab25 100644
+index 4d9a5297e53b1836e3ba438bae6b869f3822dd28..25373b05ab63e71294382c4b2a5b2658f98710a0 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2537,5 +2537,23 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0072-Remove-Metadata-on-reload.patch b/patches/server/0067-Remove-Metadata-on-reload.patch
index 7e8fa9271c..7e465a2091 100644
--- a/patches/server/0072-Remove-Metadata-on-reload.patch
+++ b/patches/server/0067-Remove-Metadata-on-reload.patch
@@ -7,7 +7,7 @@ Metadata is not meant to persist reload as things break badly with non primitive
This will remove metadata on reload so it does not crash everything if a plugin uses it.
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index d4016a8890fff0805bacab3e5a556e7316c9ab25..9a3b5b491256537d54c5fd0ac1646b3eb726187d 100644
+index 25373b05ab63e71294382c4b2a5b2658f98710a0..943971ce16c0c39085f029ab5b57685dda1c6b0f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -950,8 +950,16 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0073-Handle-Item-Meta-Inconsistencies.patch b/patches/server/0068-Handle-Item-Meta-Inconsistencies.patch
index c87023e427..c87023e427 100644
--- a/patches/server/0073-Handle-Item-Meta-Inconsistencies.patch
+++ b/patches/server/0068-Handle-Item-Meta-Inconsistencies.patch
diff --git a/patches/server/0074-Configurable-Non-Player-Arrow-Despawn-Rate.patch b/patches/server/0069-Configurable-Non-Player-Arrow-Despawn-Rate.patch
index 9544e6114a..9544e6114a 100644
--- a/patches/server/0074-Configurable-Non-Player-Arrow-Despawn-Rate.patch
+++ b/patches/server/0069-Configurable-Non-Player-Arrow-Despawn-Rate.patch
diff --git a/patches/server/0075-Add-World-Util-Methods.patch b/patches/server/0070-Add-World-Util-Methods.patch
index 72c0c64816..da13541bad 100644
--- a/patches/server/0075-Add-World-Util-Methods.patch
+++ b/patches/server/0070-Add-World-Util-Methods.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Add World Util Methods
Methods that can be used for other patches to help improve logic.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 3a41a346e886918160eccaee57118747e33f6cc1..7e663cb0a214d59baaa6d3da75a8e2fca05cc56d 100644
+index e235531d694e3e904042f2d42641c1ffbecfd5a3..d935fbdbee8b20f0efa09438b7d4c4a0da93abcc 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -216,7 +216,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
@@ -19,7 +19,7 @@ index 3a41a346e886918160eccaee57118747e33f6cc1..7e663cb0a214d59baaa6d3da75a8e2fc
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index ee71a6cc552bd2bb82beda1bd44905ea4cc14604..e89410b00c8f9ef94cd2baf621802fa847bd6456 100644
+index 65fd3a3c1f0a55d034e6f91c4f222e6454e7166c..62c54a0c789ca460ec29b0c653f5abd113736fb9 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -333,6 +333,22 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0076-Custom-replacement-for-eaten-items.patch b/patches/server/0071-Custom-replacement-for-eaten-items.patch
index f241818165..f241818165 100644
--- a/patches/server/0076-Custom-replacement-for-eaten-items.patch
+++ b/patches/server/0071-Custom-replacement-for-eaten-items.patch
diff --git a/patches/server/0077-handle-NaN-health-absorb-values-and-repair-bad-data.patch b/patches/server/0072-handle-NaN-health-absorb-values-and-repair-bad-data.patch
index 0085f9fe6d..0313a6da1c 100644
--- a/patches/server/0077-handle-NaN-health-absorb-values-and-repair-bad-data.patch
+++ b/patches/server/0072-handle-NaN-health-absorb-values-and-repair-bad-data.patch
@@ -44,10 +44,10 @@ index 5678461976a07f9afecccb1d34ea3eec24fba80e..8dc6fbbdfaecf0eb38a876d87d77f111
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 3846905bafd92918349793d346aca5b3a3529e9b..72b1429c4976b8db13d2185486c240b2fadb4ea9 100644
+index 307c76b2d06b748304d28d6634e86bb91f3ded67..4b7e5996b8b49d96f14cbd0d2167a0a09b47f4ec 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -1976,6 +1976,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2011,6 +2011,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
public void setRealHealth(double health) {
diff --git a/patches/server/0078-Use-a-Shared-Random-for-Entities.patch b/patches/server/0073-Use-a-Shared-Random-for-Entities.patch
index 5064ca7f54..d8ba52e49b 100644
--- a/patches/server/0078-Use-a-Shared-Random-for-Entities.patch
+++ b/patches/server/0073-Use-a-Shared-Random-for-Entities.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Use a Shared Random for Entities
Reduces memory usage and provides ensures more randomness, Especially since a lot of garbage entity objects get created.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 6e8811a6a4b41d38c99ac40a2d4f0bef4713b762..a9fb70e4d37162332b4b5c428ee6907a7d8c21db 100644
+index e28ccb5651a793775166d51859620882ee54b641..233c8950816521be5a9d099c29c99d0a421d30e4 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -159,6 +159,79 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -89,7 +89,7 @@ index 6e8811a6a4b41d38c99ac40a2d4f0bef4713b762..a9fb70e4d37162332b4b5c428ee6907a
private CraftEntity bukkitEntity;
public CraftEntity getBukkitEntity() {
-@@ -346,7 +419,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -398,7 +471,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.bb = Entity.INITIAL_AABB;
this.stuckSpeedMultiplier = Vec3.ZERO;
this.nextStep = 1.0F;
diff --git a/patches/server/0079-Configurable-spawn-chances-for-skeleton-horses.patch b/patches/server/0074-Configurable-spawn-chances-for-skeleton-horses.patch
index 0af7b6e1ad..150405cc49 100644
--- a/patches/server/0079-Configurable-spawn-chances-for-skeleton-horses.patch
+++ b/patches/server/0074-Configurable-spawn-chances-for-skeleton-horses.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Configurable spawn chances for skeleton horses
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 7e663cb0a214d59baaa6d3da75a8e2fca05cc56d..44600f8915f04f0baf3f877dff43d23e1b8ad93a 100644
+index d935fbdbee8b20f0efa09438b7d4c4a0da93abcc..0116fcc8de2b6bd1783db56f1062c9b590a65c34 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -663,7 +663,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -696,7 +696,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15));
if (this.isRainingAt(blockposition)) {
DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition);
diff --git a/patches/server/0080-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch b/patches/server/0075-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
index 4c1319763c..07f2005801 100644
--- a/patches/server/0080-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
+++ b/patches/server/0075-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
@@ -29,7 +29,7 @@ index 4587a3668b6be9222cdd74a38229f89f611d1af6..9f32861d791f7e4cb39d2ad01f48e191
this.x = x;
this.y = y;
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index e89410b00c8f9ef94cd2baf621802fa847bd6456..d3277e8aa7244ab490a6b354d863d4a9f3c60fec 100644
+index 62c54a0c789ca460ec29b0c653f5abd113736fb9..d612b59e04fcd01a8e2c4c7f43d8735fc208b520 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -276,7 +276,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -42,10 +42,10 @@ index e89410b00c8f9ef94cd2baf621802fa847bd6456..d3277e8aa7244ab490a6b354d863d4a9
public static boolean isInSpawnableBounds(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-index fabc7df600c89b01d97a76eb0b1206a32407b906..6cec5cda20531aadf8e2148908a70f8b573d7d82 100644
+index 0e787d877901dfcea714b0e14e9fc4358ee30bbe..41e61e6c128f22224665af3f07cd11d69a43062b 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-@@ -119,6 +119,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
+@@ -160,6 +160,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
return GameEventDispatcher.NOOP;
}
@@ -54,12 +54,12 @@ index fabc7df600c89b01d97a76eb0b1206a32407b906..6cec5cda20531aadf8e2148908a70f8b
public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean moved);
diff --git a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
-index 80e383e9a2d12f9f1b0b0d9ae71a0add9b51c9d4..a9c65c8d36e5c7080133706df1363b3ce52e3370 100644
+index a78bf00d4559dd99869d93ec78b3525d24331925..b7856c420f346ac4923afa66a9f8276490f27e82 100644
--- a/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/EmptyLevelChunk.java
-@@ -21,6 +21,12 @@ public class EmptyLevelChunk extends LevelChunk {
- this.biome = holder;
- }
+@@ -55,6 +55,12 @@ public class EmptyLevelChunk extends LevelChunk {
+ public void setBlockEmptinessMap(final boolean[] emptinessMap) {}
+ // Paper end - starlight
+ // Paper start
+ @Override
@@ -71,10 +71,10 @@ index 80e383e9a2d12f9f1b0b0d9ae71a0add9b51c9d4..a9c65c8d36e5c7080133706df1363b3c
public BlockState getBlockState(BlockPos pos) {
return Blocks.VOID_AIR.defaultBlockState();
diff --git a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
-index 3dff0f7c3ccd04a67b2153e402d801de2341e520..7b320357973202423c29743d922b72dc4ec11efe 100644
+index ac5dff35e2df23b8790bbe65c40acc6a3c77e6ac..8ffc206a858864d277ff94de7c66ffdb07d8f491 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ImposterProtoChunk.java
-@@ -47,6 +47,12 @@ public class ImposterProtoChunk extends ProtoChunk {
+@@ -89,6 +89,12 @@ public class ImposterProtoChunk extends ProtoChunk {
public BlockState getBlockState(BlockPos pos) {
return this.wrapped.getBlockState(pos);
}
@@ -88,10 +88,10 @@ index 3dff0f7c3ccd04a67b2153e402d801de2341e520..7b320357973202423c29743d922b72dc
@Override
public FluidState getFluidState(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index a4e121365717036dfc27eec86549d6dbac1ffc1a..4c4f44457d2066ea37cf48e37a282d8896649bc5 100644
+index 20c9eada6f051ecdd5e45e625d7e6289d406a2f8..5244a0a85d80963493d9106dd2674b1701c1919c 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -296,12 +296,29 @@ public class LevelChunk extends ChunkAccess {
+@@ -342,12 +342,29 @@ public class LevelChunk extends ChunkAccess {
}
}
@@ -138,10 +138,10 @@ index dddae1e226d8f58cdcfc597e25d4228cd3245cb4..ae37e97e52557b48f129cc02eeea3953
private short tickingFluidCount;
public final PalettedContainer<BlockState> states;
diff --git a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
-index 603111a52346f678aba0fd66b010d8f3026fce40..9014331e4ceac9f77a911aead87bf452d29e3fb4 100644
+index 040c6092ceed4c693a7a056c0d1a49d3d2242b19..13b62e8e6569c154547bc0d5626488c5b0839f20 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ProtoChunk.java
-@@ -88,14 +88,18 @@ public class ProtoChunk extends ChunkAccess {
+@@ -94,14 +94,18 @@ public class ProtoChunk extends ChunkAccess {
@Override
public BlockState getBlockState(BlockPos pos) {
diff --git a/patches/server/0081-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch b/patches/server/0076-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch
index ed5e601ed1..8d12218f4b 100644
--- a/patches/server/0081-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch
+++ b/patches/server/0076-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Only process BlockPhysicsEvent if a plugin has a listener
Saves on some object allocation and processing when no plugin listens to this
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index a65d86f7b48bf1fbc2186804a8c05921363c7272..4fc75c6086c7f41414b73b901c7a90f06267a089 100644
+index c0b8daca75feec011423ba4b4ef5900558bc4d92..7dce9f0355c74828f40b07276c34e352132a2549 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1356,6 +1356,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1350,6 +1350,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
@@ -18,7 +18,7 @@ index a65d86f7b48bf1fbc2186804a8c05921363c7272..4fc75c6086c7f41414b73b901c7a90f0
this.profiler.push(() -> {
return worldserver + " " + worldserver.dimension().location();
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 44600f8915f04f0baf3f877dff43d23e1b8ad93a..79c22c0eb5adf6e08f7978272b8482fe53c4a45a 100644
+index 51603dcbe81301557757152404c33fc91e21dc1a..81d24e082ad8be36db40c0ab62db3386401aa821 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -215,6 +215,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
@@ -30,10 +30,10 @@ index 44600f8915f04f0baf3f877dff43d23e1b8ad93a..79c22c0eb5adf6e08f7978272b8482fe
@Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
return this.chunkSource.getChunk(x, z, false);
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index d3277e8aa7244ab490a6b354d863d4a9f3c60fec..8359cc9cde98ffe11f2d9ede4350a404bc5086be 100644
+index d612b59e04fcd01a8e2c4c7f43d8735fc208b520..39b2071a6fc6510929416ccbdf2022cf471ab26c 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -488,7 +488,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -493,7 +493,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// CraftBukkit start
iblockdata1.updateIndirectNeighbourShapes(this, blockposition, k, j - 1); // Don't call an event for the old block to limit event spam
CraftWorld world = ((ServerLevel) this).getWorld();
diff --git a/patches/server/0082-Entity-AddTo-RemoveFrom-World-Events.patch b/patches/server/0077-Entity-AddTo-RemoveFrom-World-Events.patch
index bc343d006f..67b27958eb 100644
--- a/patches/server/0082-Entity-AddTo-RemoveFrom-World-Events.patch
+++ b/patches/server/0077-Entity-AddTo-RemoveFrom-World-Events.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Entity AddTo/RemoveFrom World Events
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 79c22c0eb5adf6e08f7978272b8482fe53c4a45a..14f55837c8614633b9a6edf7d35af465fbf8aa17 100644
+index 81d24e082ad8be36db40c0ab62db3386401aa821..6ec90ab4ff94c103682df9b66917e897cf420f33 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2195,6 +2195,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2245,6 +2245,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
entity.setOrigin(entity.getOriginVector().toLocation(getWorld()));
}
// Paper end
@@ -16,7 +16,7 @@ index 79c22c0eb5adf6e08f7978272b8482fe53c4a45a..14f55837c8614633b9a6edf7d35af465
}
public void onTrackingEnd(Entity entity) {
-@@ -2270,6 +2271,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2320,6 +2321,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
}
// CraftBukkit end
diff --git a/patches/server/0083-Configurable-Chunk-Inhabited-Time.patch b/patches/server/0078-Configurable-Chunk-Inhabited-Time.patch
index 44d87ae0db..913007c7d1 100644
--- a/patches/server/0083-Configurable-Chunk-Inhabited-Time.patch
+++ b/patches/server/0078-Configurable-Chunk-Inhabited-Time.patch
@@ -11,10 +11,10 @@ For people who want all chunks to be treated equally, you can chose a fixed valu
This allows to fine-tune vanilla gameplay.
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 4c4f44457d2066ea37cf48e37a282d8896649bc5..e63086aba2512051fe1321f6e7e72b40276f5dde 100644
+index 5244a0a85d80963493d9106dd2674b1701c1919c..5cd8755dc8db2f1fdb32d2db3a5a137ca7cad3c7 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -281,6 +281,13 @@ public class LevelChunk extends ChunkAccess {
+@@ -327,6 +327,13 @@ public class LevelChunk extends ChunkAccess {
return new ChunkAccess.TicksToSave(this.blockTicks, this.fluidTicks);
}
diff --git a/patches/server/0084-EntityPathfindEvent.patch b/patches/server/0079-EntityPathfindEvent.patch
index 8567351f04..8567351f04 100644
--- a/patches/server/0084-EntityPathfindEvent.patch
+++ b/patches/server/0079-EntityPathfindEvent.patch
diff --git a/patches/server/0085-Sanitise-RegionFileCache-and-make-configurable.patch b/patches/server/0080-Sanitise-RegionFileCache-and-make-configurable.patch
index c1c284728b..cecec7b06e 100644
--- a/patches/server/0085-Sanitise-RegionFileCache-and-make-configurable.patch
+++ b/patches/server/0080-Sanitise-RegionFileCache-and-make-configurable.patch
@@ -11,7 +11,7 @@ The implementation uses a LinkedHashMap as an LRU cache (modified from HashMap).
The maximum size of the RegionFileCache is also made configurable.
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index 4210c43104de01200b149e13ffab09dea37c5caf..b790bb574546fef1969584529e49dbcf1403e198 100644
+index ece0fd4b51d124be232e24d08f115b9267cb21cb..9a4f9251d655b86af5a410cfab8df285098e01cc 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -57,7 +57,7 @@ public class RegionFileStorage implements AutoCloseable {
diff --git a/patches/server/0086-Do-not-load-chunks-for-Pathfinding.patch b/patches/server/0081-Do-not-load-chunks-for-Pathfinding.patch
index f2b3dffd68..f2b3dffd68 100644
--- a/patches/server/0086-Do-not-load-chunks-for-Pathfinding.patch
+++ b/patches/server/0081-Do-not-load-chunks-for-Pathfinding.patch
diff --git a/patches/server/0087-Add-PlayerUseUnknownEntityEvent.patch b/patches/server/0082-Add-PlayerUseUnknownEntityEvent.patch
index fb434cc940..fb434cc940 100644
--- a/patches/server/0087-Add-PlayerUseUnknownEntityEvent.patch
+++ b/patches/server/0082-Add-PlayerUseUnknownEntityEvent.patch
diff --git a/patches/server/0088-Configurable-Grass-Spread-Tick-Rate.patch b/patches/server/0083-Configurable-Grass-Spread-Tick-Rate.patch
index 50a9c78703..50a9c78703 100644
--- a/patches/server/0088-Configurable-Grass-Spread-Tick-Rate.patch
+++ b/patches/server/0083-Configurable-Grass-Spread-Tick-Rate.patch
diff --git a/patches/server/0089-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch b/patches/server/0084-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch
index 02bb35bb40..29f5f1ebcd 100644
--- a/patches/server/0089-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch
+++ b/patches/server/0084-Fix-Cancelling-BlockPlaceEvent-triggering-physics.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix Cancelling BlockPlaceEvent triggering physics
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 14f55837c8614633b9a6edf7d35af465fbf8aa17..af3eab8bb09ca86a38724f38417b03a55b6bb06c 100644
+index b232df5fd870b75dc616dffcf76b15409fb6fdbe..937c4ad3acd69ee77d89c5f32480994d0b1ce5c9 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1441,6 +1441,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1476,6 +1476,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
@Override
public void updateNeighborsAt(BlockPos pos, Block sourceBlock) {
diff --git a/patches/server/0090-Optimize-DataBits.patch b/patches/server/0085-Optimize-DataBits.patch
index 8686b1aff9..8686b1aff9 100644
--- a/patches/server/0090-Optimize-DataBits.patch
+++ b/patches/server/0085-Optimize-DataBits.patch
diff --git a/patches/server/0091-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch b/patches/server/0086-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch
index 7ef4294205..7ef4294205 100644
--- a/patches/server/0091-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch
+++ b/patches/server/0086-Option-to-use-vanilla-per-world-scoreboard-coloring-.patch
diff --git a/patches/server/0092-Configurable-Player-Collision.patch b/patches/server/0087-Configurable-Player-Collision.patch
index e72b7dc458..00970b35e0 100644
--- a/patches/server/0092-Configurable-Player-Collision.patch
+++ b/patches/server/0087-Configurable-Player-Collision.patch
@@ -18,7 +18,7 @@ index 1294b38262505b0d54089e428df9b363219de1f0..ee37ec0de1ca969144824427ae42b0c8
buf.writeComponent(this.playerPrefix);
buf.writeComponent(this.playerSuffix);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 4fc75c6086c7f41414b73b901c7a90f06267a089..23e9a4ba6b108aa7a7fb8e0c4a765986380eefbd 100644
+index 7c389a37b9bac59f0ad3bbe1eae9c9f9b4bf014d..3b364c36f4c572e7f139997ef214bca8a8368e56 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -580,6 +580,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -43,7 +43,7 @@ index 4fc75c6086c7f41414b73b901c7a90f06267a089..23e9a4ba6b108aa7a7fb8e0c4a765986
this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
this.connection.acceptConnections();
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index c003a1e8b4c2b2331abc4536352abdec20fef42e..a474241c66a532791d9734ab9a9f6529f5414169 100644
+index dfb53366a0b36b6a4cc43c66837f98fc372c6d2e..0d0b04775dc36c1749d8f19f5c8d2b9dd9bb5a1e 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -94,6 +94,7 @@ import net.minecraft.world.level.storage.PlayerDataStorage;
diff --git a/patches/server/0093-Add-handshake-event-to-allow-plugins-to-handle-clien.patch b/patches/server/0088-Add-handshake-event-to-allow-plugins-to-handle-clien.patch
index 0f1b5b5544..0f1b5b5544 100644
--- a/patches/server/0093-Add-handshake-event-to-allow-plugins-to-handle-clien.patch
+++ b/patches/server/0088-Add-handshake-event-to-allow-plugins-to-handle-clien.patch
diff --git a/patches/server/0094-Configurable-RCON-IP-address.patch b/patches/server/0089-Configurable-RCON-IP-address.patch
index fa6341c5b8..fa6341c5b8 100644
--- a/patches/server/0094-Configurable-RCON-IP-address.patch
+++ b/patches/server/0089-Configurable-RCON-IP-address.patch
diff --git a/patches/server/0095-EntityRegainHealthEvent-isFastRegen-API.patch b/patches/server/0090-EntityRegainHealthEvent-isFastRegen-API.patch
index dbd8280cac..dbd8280cac 100644
--- a/patches/server/0095-EntityRegainHealthEvent-isFastRegen-API.patch
+++ b/patches/server/0090-EntityRegainHealthEvent-isFastRegen-API.patch
diff --git a/patches/server/0096-Add-ability-to-configure-frosted_ice-properties.patch b/patches/server/0091-Add-ability-to-configure-frosted_ice-properties.patch
index 8e938c31ad..8e938c31ad 100644
--- a/patches/server/0096-Add-ability-to-configure-frosted_ice-properties.patch
+++ b/patches/server/0091-Add-ability-to-configure-frosted_ice-properties.patch
diff --git a/patches/server/0097-remove-null-possibility-for-getServer-singleton.patch b/patches/server/0092-remove-null-possibility-for-getServer-singleton.patch
index 81ef84ab00..0a85e191ad 100644
--- a/patches/server/0097-remove-null-possibility-for-getServer-singleton.patch
+++ b/patches/server/0092-remove-null-possibility-for-getServer-singleton.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] remove null possibility for getServer singleton
to stop IDE complaining about potential NPE
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 23e9a4ba6b108aa7a7fb8e0c4a765986380eefbd..1dc2234b8218699d82a56e23d9390bee6cc7937b 100644
+index 9965bbe95da1dcb17fbb953fb7a7c9a59d593f85..0fbbdad1d4a33c9feb77ae9592db92efb747c12f 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -181,6 +181,7 @@ import co.aikar.timings.MinecraftTimings; // Paper
@@ -25,7 +25,7 @@ index 23e9a4ba6b108aa7a7fb8e0c4a765986380eefbd..1dc2234b8218699d82a56e23d9390bee
this.metricsRecorder = InactiveMetricsRecorder.INSTANCE;
this.profiler = this.metricsRecorder.getProfiler();
this.onMetricsRecordingStopped = (methodprofilerresults) -> {
-@@ -2297,9 +2299,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2291,9 +2293,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return false;
}
diff --git a/patches/server/0098-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch b/patches/server/0093-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch
index 2e33bb81a2..9ffad19e05 100644
--- a/patches/server/0098-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch
+++ b/patches/server/0093-Improve-Maps-in-item-frames-performance-and-bug-fixe.patch
@@ -13,10 +13,10 @@ custom renderers are in use, defaulting to the much simpler Vanilla system.
Additionally, numerous issues to player position tracking on maps has been fixed.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index af3eab8bb09ca86a38724f38417b03a55b6bb06c..4a6c03f421d81b694ca8670994c9322d4425922b 100644
+index 38f91c6f1ca2827122b1ae1fde94bc888638ba0f..4d96a196511621d56507cfb35923bd5270f83716 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2216,6 +2216,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2266,6 +2266,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
{
if ( iter.next().player == entity )
{
diff --git a/patches/server/0099-LootTable-API-Replenishable-Lootables-Feature.patch b/patches/server/0094-LootTable-API-Replenishable-Lootables-Feature.patch
index 69d6037455..1bd3ae7f40 100644
--- a/patches/server/0099-LootTable-API-Replenishable-Lootables-Feature.patch
+++ b/patches/server/0094-LootTable-API-Replenishable-Lootables-Feature.patch
@@ -485,7 +485,7 @@ index 0000000000000000000000000000000000000000..3377b86c337d0234bbb9b0349e4034a7
+ }
+}
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index a9fb70e4d37162332b4b5c428ee6907a7d8c21db..ac2b7e86afc21607564ddea62e39ec484e91bbf2 100644
+index 233c8950816521be5a9d099c29c99d0a421d30e4..77652939ccb3191c29b919170a06ef451b3fd74f 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -232,6 +232,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
diff --git a/patches/server/0100-Don-t-save-empty-scoreboard-teams-to-scoreboard.dat.patch b/patches/server/0095-Don-t-save-empty-scoreboard-teams-to-scoreboard.dat.patch
index c233fb2657..c233fb2657 100644
--- a/patches/server/0100-Don-t-save-empty-scoreboard-teams-to-scoreboard.dat.patch
+++ b/patches/server/0095-Don-t-save-empty-scoreboard-teams-to-scoreboard.dat.patch
diff --git a/patches/server/0101-System-property-for-disabling-watchdoge.patch b/patches/server/0096-System-property-for-disabling-watchdoge.patch
index 62f6387e73..65be04b57b 100644
--- a/patches/server/0101-System-property-for-disabling-watchdoge.patch
+++ b/patches/server/0096-System-property-for-disabling-watchdoge.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] System property for disabling watchdoge
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
-index 600c012b2bf119ddd6760b0401a2c6a22453609e..bd515ddc88f71a31531418c43725e438de100fcd 100644
+index 3ad14bf0697e682a2e777baa8faeb323d127fb13..a9897c494b3dc56d900356d74030359832febbaa 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
-@@ -61,7 +61,7 @@ public class WatchdogThread extends Thread
+@@ -61,7 +61,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
while ( !this.stopping )
{
//
diff --git a/patches/server/0102-Async-GameProfileCache-saving.patch b/patches/server/0097-Async-GameProfileCache-saving.patch
index 12dab86f58..e0d7f93e3f 100644
--- a/patches/server/0102-Async-GameProfileCache-saving.patch
+++ b/patches/server/0097-Async-GameProfileCache-saving.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Async GameProfileCache saving
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 1dc2234b8218699d82a56e23d9390bee6cc7937b..da790861a3689195446040e8e7d2f898eee3068e 100644
+index 7755d79e0dc01e8464f4ffda46db6dbde033aac4..f1d4a7a9e74adc18e18b2df960794ec8c05ce340 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -945,7 +945,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -926,7 +926,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
} catch (java.lang.InterruptedException ignored) {} // Paper
if (org.spigotmc.SpigotConfig.saveUserCacheOnStopOnly) {
MinecraftServer.LOGGER.info("Saving usercache.json");
@@ -16,9 +16,9 @@ index 1dc2234b8218699d82a56e23d9390bee6cc7937b..da790861a3689195446040e8e7d2f898
+ this.getProfileCache().save(false); // Paper
}
// Spigot end
- com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.close(true, true); // Paper
+ io.papermc.paper.chunk.system.io.RegionFileIOThread.close(true); // Paper // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index d70d97c65d5bdb47a17a226d65bad8ba1421b11b..5b2980866ae3cd78f1852b0ad396ff1967ddfc16 100644
+index 4dc5a5888f0180e1490597e43956e8e80981f8b9..6c856dee201d84285283504aad8cf959e88b1c52 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -247,7 +247,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
diff --git a/patches/server/0103-Optional-TNT-doesn-t-move-in-water.patch b/patches/server/0098-Optional-TNT-doesn-t-move-in-water.patch
index 3cb11be6b8..3cb11be6b8 100644
--- a/patches/server/0103-Optional-TNT-doesn-t-move-in-water.patch
+++ b/patches/server/0098-Optional-TNT-doesn-t-move-in-water.patch
diff --git a/patches/server/0104-Faster-redstone-torch-rapid-clock-removal.patch b/patches/server/0099-Faster-redstone-torch-rapid-clock-removal.patch
index bb9244b467..afe95620ee 100644
--- a/patches/server/0104-Faster-redstone-torch-rapid-clock-removal.patch
+++ b/patches/server/0099-Faster-redstone-torch-rapid-clock-removal.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Faster redstone torch rapid clock removal
Only resize the the redstone torch list once, since resizing arrays / lists is costly
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 8359cc9cde98ffe11f2d9ede4350a404bc5086be..aa2098d3af5cc03f3ff3ad663683c5edbe473756 100644
+index 39b2071a6fc6510929416ccbdf2022cf471ab26c..807df663f710311276d69288875bb80e7fa37893 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -167,6 +167,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0105-Add-server-name-parameter.patch b/patches/server/0100-Add-server-name-parameter.patch
index abd91cd6fa..abd91cd6fa 100644
--- a/patches/server/0105-Add-server-name-parameter.patch
+++ b/patches/server/0100-Add-server-name-parameter.patch
diff --git a/patches/server/0106-Only-send-Dragon-Wither-Death-sounds-to-same-world.patch b/patches/server/0101-Only-send-Dragon-Wither-Death-sounds-to-same-world.patch
index 1cfb68f251..9bd57f4d90 100644
--- a/patches/server/0106-Only-send-Dragon-Wither-Death-sounds-to-same-world.patch
+++ b/patches/server/0101-Only-send-Dragon-Wither-Death-sounds-to-same-world.patch
@@ -6,34 +6,28 @@ Subject: [PATCH] Only send Dragon/Wither Death sounds to same world
Also fix view distance lookup
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
-index dd1d606543da93e6068b8fa4239d369a4b648523..0dff7a47ecac1916ad23739fbb06ddd0f0052a65 100644
+index dd1d606543da93e6068b8fa4239d369a4b648523..e6a6b0a298e69ce975eac413723f068aaef72ec0 100644
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
-@@ -653,8 +653,9 @@ public class EnderDragon extends Mob implements Enemy {
- if (this.dragonDeathTime == 1 && !this.isSilent()) {
+@@ -654,7 +654,7 @@ public class EnderDragon extends Mob implements Enemy {
// CraftBukkit start - Use relative location for far away sounds
// this.world.b(1028, this.getChunkCoordinates(), 0);
-- int viewDistance = ((ServerLevel) this.level).getCraftServer().getViewDistance() * 16;
+ int viewDistance = ((ServerLevel) this.level).getCraftServer().getViewDistance() * 16;
- for (net.minecraft.server.level.ServerPlayer player : this.level.getServer().getPlayerList().players) {
-+ //int viewDistance = ((WorldServer) this.world).getServer().getViewDistance() * 16; // Paper - updated to use worlds actual view distance incase we have to uncomment this due to removal of player view distance API
+ for (net.minecraft.server.level.ServerPlayer player : (List<net.minecraft.server.level.ServerPlayer>) ((ServerLevel)level).players()) {
-+ final int viewDistance = player.getViewDistance(); // TODO apply view distance api patch
double deltaX = this.getX() - player.getX();
double deltaZ = this.getZ() - player.getZ();
double distanceSquared = deltaX * deltaX + deltaZ * deltaZ;
diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
-index 78bffaddbc9e478fb53639396e9c4970e9961b21..32b302aad0319ce3ee412912425c1c8db9979f8a 100644
+index 78bffaddbc9e478fb53639396e9c4970e9961b21..1fc862a3b5d40a45cf91703051bcfb06ec1b177a 100644
--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
-@@ -271,8 +271,9 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
- if (!this.isSilent()) {
+@@ -272,7 +272,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
// CraftBukkit start - Use relative location for far away sounds
// this.world.globalLevelEvent(1023, new BlockPosition(this), 0);
-- int viewDistance = ((ServerLevel) this.level).getCraftServer().getViewDistance() * 16;
+ int viewDistance = ((ServerLevel) this.level).getCraftServer().getViewDistance() * 16;
- for (ServerPlayer player : (List<ServerPlayer>) MinecraftServer.getServer().getPlayerList().players) {
-+ //int viewDistance = ((ServerLevel) this.level).getCraftServer().getViewDistance() * 16; // Paper - updated to use worlds actual view distance incase we have to uncomment this due to removal of player view distance API
+ for (ServerPlayer player : (List<ServerPlayer>)this.level.players()) { // Paper
-+ final int viewDistance = player.getViewDistance(); // TODO apply view distance api patch
double deltaX = this.getX() - player.getX();
double deltaZ = this.getZ() - player.getZ();
double distanceSquared = deltaX * deltaX + deltaZ * deltaZ;
diff --git a/patches/server/0107-Fix-Old-Sign-Conversion.patch b/patches/server/0102-Fix-Old-Sign-Conversion.patch
index d223b39ccf..d223b39ccf 100644
--- a/patches/server/0107-Fix-Old-Sign-Conversion.patch
+++ b/patches/server/0102-Fix-Old-Sign-Conversion.patch
diff --git a/patches/server/0108-Avoid-blocking-on-Network-Manager-creation.patch b/patches/server/0103-Avoid-blocking-on-Network-Manager-creation.patch
index 4c0273aeef..4c0273aeef 100644
--- a/patches/server/0108-Avoid-blocking-on-Network-Manager-creation.patch
+++ b/patches/server/0103-Avoid-blocking-on-Network-Manager-creation.patch
diff --git a/patches/server/0109-Don-t-lookup-game-profiles-that-have-no-UUID-and-no-.patch b/patches/server/0104-Don-t-lookup-game-profiles-that-have-no-UUID-and-no-.patch
index e542d272fa..e542d272fa 100644
--- a/patches/server/0109-Don-t-lookup-game-profiles-that-have-no-UUID-and-no-.patch
+++ b/patches/server/0104-Don-t-lookup-game-profiles-that-have-no-UUID-and-no-.patch
diff --git a/patches/server/0110-Add-setting-for-proxy-online-mode-status.patch b/patches/server/0105-Add-setting-for-proxy-online-mode-status.patch
index 3cca2594aa..bd273b8189 100644
--- a/patches/server/0110-Add-setting-for-proxy-online-mode-status.patch
+++ b/patches/server/0105-Add-setting-for-proxy-online-mode-status.patch
@@ -43,7 +43,7 @@ index da98f074ccd5a40c635824112c97fd174c393cb1..6599f874d9f97e9ef4862039ecad7277
} else {
String[] astring1 = astring;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 9a3b5b491256537d54c5fd0ac1646b3eb726187d..6e28fd088ff19bcd2206f2d8decf72184d109baf 100644
+index 943971ce16c0c39085f029ab5b57685dda1c6b0f..0d6dcb670c583f47a299cd96574426932d3b98c8 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1706,7 +1706,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0111-Optimise-BlockState-s-hashCode-equals.patch b/patches/server/0106-Optimise-BlockState-s-hashCode-equals.patch
index 53db895e2e..53db895e2e 100644
--- a/patches/server/0111-Optimise-BlockState-s-hashCode-equals.patch
+++ b/patches/server/0106-Optimise-BlockState-s-hashCode-equals.patch
diff --git a/patches/server/0112-Configurable-packet-in-spam-threshold.patch b/patches/server/0107-Configurable-packet-in-spam-threshold.patch
index 7059a874f8..7059a874f8 100644
--- a/patches/server/0112-Configurable-packet-in-spam-threshold.patch
+++ b/patches/server/0107-Configurable-packet-in-spam-threshold.patch
diff --git a/patches/server/0113-Configurable-flying-kick-messages.patch b/patches/server/0108-Configurable-flying-kick-messages.patch
index b4c5d977eb..b4c5d977eb 100644
--- a/patches/server/0113-Configurable-flying-kick-messages.patch
+++ b/patches/server/0108-Configurable-flying-kick-messages.patch
diff --git a/patches/server/0114-Add-EntityZapEvent.patch b/patches/server/0109-Add-EntityZapEvent.patch
index 545c1bc427..545c1bc427 100644
--- a/patches/server/0114-Add-EntityZapEvent.patch
+++ b/patches/server/0109-Add-EntityZapEvent.patch
diff --git a/patches/server/0115-Filter-bad-data-from-ArmorStand-and-SpawnEgg-items.patch b/patches/server/0110-Filter-bad-data-from-ArmorStand-and-SpawnEgg-items.patch
index 9fc8ffaeaf..9fc8ffaeaf 100644
--- a/patches/server/0115-Filter-bad-data-from-ArmorStand-and-SpawnEgg-items.patch
+++ b/patches/server/0110-Filter-bad-data-from-ArmorStand-and-SpawnEgg-items.patch
diff --git a/patches/server/0116-Cache-user-authenticator-threads.patch b/patches/server/0111-Cache-user-authenticator-threads.patch
index a6fe950dbd..a6fe950dbd 100644
--- a/patches/server/0116-Cache-user-authenticator-threads.patch
+++ b/patches/server/0111-Cache-user-authenticator-threads.patch
diff --git a/patches/server/0117-Allow-Reloading-of-Command-Aliases.patch b/patches/server/0112-Allow-Reloading-of-Command-Aliases.patch
index a203b2084c..54b2ffe5a7 100644
--- a/patches/server/0117-Allow-Reloading-of-Command-Aliases.patch
+++ b/patches/server/0112-Allow-Reloading-of-Command-Aliases.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Allow Reloading of Command Aliases
Reload the aliases stored in commands.yml
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 6e28fd088ff19bcd2206f2d8decf72184d109baf..5c9a4327232c1bdf219a66f66ef8ad699104be41 100644
+index 0d6dcb670c583f47a299cd96574426932d3b98c8..8072e45c656e8de0f9061360d353675554ae0182 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2563,5 +2563,24 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0118-Add-source-to-PlayerExpChangeEvent.patch b/patches/server/0113-Add-source-to-PlayerExpChangeEvent.patch
index e290b2ac51..e290b2ac51 100644
--- a/patches/server/0118-Add-source-to-PlayerExpChangeEvent.patch
+++ b/patches/server/0113-Add-source-to-PlayerExpChangeEvent.patch
diff --git a/patches/server/0119-Add-ProjectileCollideEvent.patch b/patches/server/0114-Add-ProjectileCollideEvent.patch
index 150e864a9e..150e864a9e 100644
--- a/patches/server/0119-Add-ProjectileCollideEvent.patch
+++ b/patches/server/0114-Add-ProjectileCollideEvent.patch
diff --git a/patches/server/0120-Prevent-Pathfinding-out-of-World-Border.patch b/patches/server/0115-Prevent-Pathfinding-out-of-World-Border.patch
index 6feb089d75..6feb089d75 100644
--- a/patches/server/0120-Prevent-Pathfinding-out-of-World-Border.patch
+++ b/patches/server/0115-Prevent-Pathfinding-out-of-World-Border.patch
diff --git a/patches/server/0121-Optimize-World.isLoaded-BlockPosition-Z.patch b/patches/server/0116-Optimize-World.isLoaded-BlockPosition-Z.patch
index 2756e56e5d..da22f581ab 100644
--- a/patches/server/0121-Optimize-World.isLoaded-BlockPosition-Z.patch
+++ b/patches/server/0116-Optimize-World.isLoaded-BlockPosition-Z.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Optimize World.isLoaded(BlockPosition)Z
Reduce method invocations for World.isLoaded(BlockPosition)Z
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index aa2098d3af5cc03f3ff3ad663683c5edbe473756..c8350eae421f5655de490c420dcb284c78f58f62 100644
+index 807df663f710311276d69288875bb80e7fa37893..64b763d6dc695e9cb9dd381deb5609e4902713dd 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -334,6 +334,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0122-Bound-Treasure-Maps-to-World-Border.patch b/patches/server/0117-Bound-Treasure-Maps-to-World-Border.patch
index 01b6cd61ca..154a31efb9 100644
--- a/patches/server/0122-Bound-Treasure-Maps-to-World-Border.patch
+++ b/patches/server/0117-Bound-Treasure-Maps-to-World-Border.patch
@@ -34,7 +34,7 @@ index 6ec5a1525d0b8ced8fe78d3eab29c5eb82996844..2442c287a7f26cfee10a19e9015558cd
return (double) pos.getMaxBlockX() > this.getMinX() && (double) pos.getMinBlockX() < this.getMaxX() && (double) pos.getMaxBlockZ() > this.getMinZ() && (double) pos.getMinBlockZ() < this.getMaxZ();
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
-index 2b543472302faf4b6298efe9bc44c023051f4997..b1eecc184bc9daf0a9c7db8da06e11bfb63d9d88 100644
+index f284eefe5de6ad8b42f7d4185fc4c3b8b2cd5895..6e2185bbaac889d51a54ec97907da3b1faa465c4 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
@@ -390,6 +390,7 @@ public abstract class ChunkGenerator {
diff --git a/patches/server/0123-Configurable-Cartographer-Treasure-Maps.patch b/patches/server/0118-Configurable-Cartographer-Treasure-Maps.patch
index 25ad0e6acc..25ad0e6acc 100644
--- a/patches/server/0123-Configurable-Cartographer-Treasure-Maps.patch
+++ b/patches/server/0118-Configurable-Cartographer-Treasure-Maps.patch
diff --git a/patches/server/0124-Optimize-ItemStack.isEmpty.patch b/patches/server/0119-Optimize-ItemStack.isEmpty.patch
index cb004037bf..cb004037bf 100644
--- a/patches/server/0124-Optimize-ItemStack.isEmpty.patch
+++ b/patches/server/0119-Optimize-ItemStack.isEmpty.patch
diff --git a/patches/server/0125-Add-API-methods-to-control-if-armour-stands-can-move.patch b/patches/server/0120-Add-API-methods-to-control-if-armour-stands-can-move.patch
index 6b19100733..6b19100733 100644
--- a/patches/server/0125-Add-API-methods-to-control-if-armour-stands-can-move.patch
+++ b/patches/server/0120-Add-API-methods-to-control-if-armour-stands-can-move.patch
diff --git a/patches/server/0126-String-based-Action-Bar-API.patch b/patches/server/0121-String-based-Action-Bar-API.patch
index 7d7838984d..916142b45d 100644
--- a/patches/server/0126-String-based-Action-Bar-API.patch
+++ b/patches/server/0121-String-based-Action-Bar-API.patch
@@ -26,10 +26,10 @@ index 32ef3edebe94a2014168b7e438752a80b2687e5f..ab6c58eed6707ab7b0aa3e7549a871ad
// Paper end
buf.writeComponent(this.text);
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 72b1429c4976b8db13d2185486c240b2fadb4ea9..538db39d365252a63d578728b362a8dde3e8838e 100644
+index 4b7e5996b8b49d96f14cbd0d2167a0a09b47f4ec..843764d27c490e94bcf2becdafb15e5f1a68bc92 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -280,6 +280,26 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -355,6 +355,26 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
// Paper start
diff --git a/patches/server/0127-Properly-fix-item-duplication-bug.patch b/patches/server/0122-Properly-fix-item-duplication-bug.patch
index 07234fb009..6d598ed59d 100644
--- a/patches/server/0127-Properly-fix-item-duplication-bug.patch
+++ b/patches/server/0122-Properly-fix-item-duplication-bug.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Properly fix item duplication bug
Credit to prplz for figuring out the real issue
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 4d8dfe375f5b3b9e5cfc12a6af0b87ae78f9b764..5d214b7dd4f6d7feff0a1904ce6573cffd258a1c 100644
+index 2ffce1200abe7ab7b50a187069e63392c0b3bfdd..776d2b38f284e13ec9a7dd67a538626817c79887 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -2210,7 +2210,7 @@ public class ServerPlayer extends Player {
+@@ -2195,7 +2195,7 @@ public class ServerPlayer extends Player {
@Override
public boolean isImmobile() {
diff --git a/patches/server/0128-Firework-API-s.patch b/patches/server/0123-Firework-API-s.patch
index ebac775a19..ebac775a19 100644
--- a/patches/server/0128-Firework-API-s.patch
+++ b/patches/server/0123-Firework-API-s.patch
diff --git a/patches/server/0129-PlayerTeleportEndGatewayEvent.patch b/patches/server/0124-PlayerTeleportEndGatewayEvent.patch
index 65f98958b1..65f98958b1 100644
--- a/patches/server/0129-PlayerTeleportEndGatewayEvent.patch
+++ b/patches/server/0124-PlayerTeleportEndGatewayEvent.patch
diff --git a/patches/server/0130-Provide-E-TE-Chunk-count-stat-methods.patch b/patches/server/0125-Provide-E-TE-Chunk-count-stat-methods.patch
index 2558009b17..a2c225e44e 100644
--- a/patches/server/0130-Provide-E-TE-Chunk-count-stat-methods.patch
+++ b/patches/server/0125-Provide-E-TE-Chunk-count-stat-methods.patch
@@ -7,7 +7,7 @@ Provides counts without the ineffeciency of using .getEntities().size()
which creates copy of the collections.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index c8350eae421f5655de490c420dcb284c78f58f62..4e843d9e28f7775faba3e47ca5a725f7921490e6 100644
+index 64b763d6dc695e9cb9dd381deb5609e4902713dd..1954a93a1f566c1c79cdc2e0eb1b41141d6b5fde 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -112,7 +112,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -20,7 +20,7 @@ index c8350eae421f5655de490c420dcb284c78f58f62..4e843d9e28f7775faba3e47ca5a725f7
private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.newArrayList();
private boolean tickingBlockEntities;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 0ed46cdd443ac42a7d57ee59f6f04fd9e9259c16..b2e693b6a799568c6196c1f805f0153ea69b8bd2 100644
+index e2c051dd0d639ba28da0f62d06259fcf0d3244ce..a75f4a1ecfe2790d727f5dda792c5ab4bb45554e 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -152,6 +152,56 @@ public class CraftWorld extends CraftRegionAccessor implements World {
diff --git a/patches/server/0131-Enforce-Sync-Player-Saves.patch b/patches/server/0126-Enforce-Sync-Player-Saves.patch
index 84ab1f0188..b13a65c6ac 100644
--- a/patches/server/0131-Enforce-Sync-Player-Saves.patch
+++ b/patches/server/0126-Enforce-Sync-Player-Saves.patch
@@ -7,7 +7,7 @@ Saving players async is extremely dangerous. This will force it to main
the same way we handle async chunk loads.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index a474241c66a532791d9734ab9a9f6529f5414169..40d1f5433f3f1277663c65be2f85aaee5652f88a 100644
+index 0d0b04775dc36c1749d8f19f5c8d2b9dd9bb5a1e..acb3d75f0777eab5aa117679d2328c22f46cf823 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1042,11 +1042,13 @@ public abstract class PlayerList {
diff --git a/patches/server/0132-Don-t-allow-entities-to-ride-themselves-572.patch b/patches/server/0127-Don-t-allow-entities-to-ride-themselves-572.patch
index 39dd67be9b..e361748ca1 100644
--- a/patches/server/0132-Don-t-allow-entities-to-ride-themselves-572.patch
+++ b/patches/server/0127-Don-t-allow-entities-to-ride-themselves-572.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Don't allow entities to ride themselves - #572
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index ac2b7e86afc21607564ddea62e39ec484e91bbf2..2d6d393ccdb6ed3aadc1e00a7a1a8dcb78277771 100644
+index 77652939ccb3191c29b919170a06ef451b3fd74f..b4d96dfc68788a4eae2197a0f2615de213d5ca8b 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2333,6 +2333,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2385,6 +2385,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
protected boolean addPassenger(Entity entity) { // CraftBukkit
diff --git a/patches/server/0133-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch b/patches/server/0128-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
index 2a6c64ec6d..40dfa4dd65 100644
--- a/patches/server/0133-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
+++ b/patches/server/0128-ExperienceOrbs-API-for-Reason-Source-Triggering-play.patch
@@ -197,7 +197,7 @@ index fe660bbaa4113fb2ffa1ea2f10e4e1e674fbb86d..bb6063ae7f4438916306ce876057f748
}
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
-index 0dff7a47ecac1916ad23739fbb06ddd0f0052a65..d0ebcc23d863be630b55245aa2604c108ee6c93a 100644
+index e6a6b0a298e69ce975eac413723f068aaef72ec0..1709126f0853edc6bece6f31d7c65a5f8955683a 100644
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -647,7 +647,7 @@ public class EnderDragon extends Mob implements Enemy {
@@ -209,7 +209,7 @@ index 0dff7a47ecac1916ad23739fbb06ddd0f0052a65..d0ebcc23d863be630b55245aa2604c10
}
if (this.dragonDeathTime == 1 && !this.isSilent()) {
-@@ -678,7 +678,7 @@ public class EnderDragon extends Mob implements Enemy {
+@@ -677,7 +677,7 @@ public class EnderDragon extends Mob implements Enemy {
this.yBodyRot = this.getYRot();
if (this.dragonDeathTime == 200 && this.level instanceof ServerLevel) {
if (true) { // CraftBukkit - SPIGOT-2420: Already checked for the game rule when calculating the xp
diff --git a/patches/server/0134-Cap-Entity-Collisions.patch b/patches/server/0129-Cap-Entity-Collisions.patch
index ccec87a847..6aa10f4072 100644
--- a/patches/server/0134-Cap-Entity-Collisions.patch
+++ b/patches/server/0129-Cap-Entity-Collisions.patch
@@ -12,7 +12,7 @@ just as it does in Vanilla, but entity pushing logic will be capped.
You can set this to 0 to disable collisions.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 2d6d393ccdb6ed3aadc1e00a7a1a8dcb78277771..55f1886761c7a528f0f1e371de546aff7707e60b 100644
+index b4d96dfc68788a4eae2197a0f2615de213d5ca8b..6e1b53317c4107f907e551a9baeaf64909f6de87 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -379,6 +379,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
diff --git a/patches/server/0135-Remove-CraftScheduler-Async-Task-Debugger.patch b/patches/server/0130-Remove-CraftScheduler-Async-Task-Debugger.patch
index eacad1fb97..eacad1fb97 100644
--- a/patches/server/0135-Remove-CraftScheduler-Async-Task-Debugger.patch
+++ b/patches/server/0130-Remove-CraftScheduler-Async-Task-Debugger.patch
diff --git a/patches/server/0136-Do-not-let-armorstands-drown.patch b/patches/server/0131-Do-not-let-armorstands-drown.patch
index 948bf3a496..948bf3a496 100644
--- a/patches/server/0136-Do-not-let-armorstands-drown.patch
+++ b/patches/server/0131-Do-not-let-armorstands-drown.patch
diff --git a/patches/server/0138-Properly-handle-async-calls-to-restart-the-server.patch b/patches/server/0132-Properly-handle-async-calls-to-restart-the-server.patch
index dda388b4c6..8b06614a56 100644
--- a/patches/server/0138-Properly-handle-async-calls-to-restart-the-server.patch
+++ b/patches/server/0132-Properly-handle-async-calls-to-restart-the-server.patch
@@ -30,7 +30,7 @@ will have plugins and worlds saving to the disk has a high potential to result
in corruption/dataloss.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index da790861a3689195446040e8e7d2f898eee3068e..7f392cb74778de9854704895337bc78901552b44 100644
+index f1d4a7a9e74adc18e18b2df960794ec8c05ce340..37a3c1bd60dbd0e0069120d4f48a17cfbc82dca1 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -221,6 +221,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -41,7 +41,7 @@ index da790861a3689195446040e8e7d2f898eee3068e..7f392cb74778de9854704895337bc789
private boolean stopped;
private int tickCount;
protected final Proxy proxy;
-@@ -884,7 +885,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -894,7 +895,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (this.playerList != null) {
MinecraftServer.LOGGER.info("Saving players");
this.playerList.saveAll();
@@ -50,7 +50,7 @@ index da790861a3689195446040e8e7d2f898eee3068e..7f392cb74778de9854704895337bc789
try { Thread.sleep(100); } catch (InterruptedException ex) {} // CraftBukkit - SPIGOT-625 - give server at least a chance to send packets
}
-@@ -964,6 +965,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -945,6 +946,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
public void halt(boolean flag) {
@@ -64,7 +64,7 @@ index da790861a3689195446040e8e7d2f898eee3068e..7f392cb74778de9854704895337bc789
if (flag) {
try {
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 40d1f5433f3f1277663c65be2f85aaee5652f88a..0a1684609ae155f99959b4cb4db3a1cf7234c54b 100644
+index acb3d75f0777eab5aa117679d2328c22f46cf823..5820dbb6d2129e2f99207e39c3ed9e661610f491 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1153,8 +1153,15 @@ public abstract class PlayerList {
diff --git a/patches/server/0139-Add-option-to-make-parrots-stay-on-shoulders-despite.patch b/patches/server/0133-Add-option-to-make-parrots-stay-on-shoulders-despite.patch
index 05b0e00728..05b0e00728 100644
--- a/patches/server/0139-Add-option-to-make-parrots-stay-on-shoulders-despite.patch
+++ b/patches/server/0133-Add-option-to-make-parrots-stay-on-shoulders-despite.patch
diff --git a/patches/server/0140-Add-configuration-option-to-prevent-player-names-fro.patch b/patches/server/0134-Add-configuration-option-to-prevent-player-names-fro.patch
index b839f69290..0282c2af43 100644
--- a/patches/server/0140-Add-configuration-option-to-prevent-player-names-fro.patch
+++ b/patches/server/0134-Add-configuration-option-to-prevent-player-names-fro.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Add configuration option to prevent player names from being
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 5c9a4327232c1bdf219a66f66ef8ad699104be41..6109763453327f49a15c677a3af8f2de959b58cc 100644
+index 8072e45c656e8de0f9061360d353675554ae0182..a458cf93af182e2b6b7c7ef89a4a7fe7f64f7be5 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2582,5 +2582,10 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0141-Use-TerminalConsoleAppender-for-console-improvements.patch b/patches/server/0135-Use-TerminalConsoleAppender-for-console-improvements.patch
index 47e89994e3..fd8abc7880 100644
--- a/patches/server/0141-Use-TerminalConsoleAppender-for-console-improvements.patch
+++ b/patches/server/0135-Use-TerminalConsoleAppender-for-console-improvements.patch
@@ -112,7 +112,7 @@ index 0000000000000000000000000000000000000000..685deaa0e5d1ddc13e3a7c0471b1cfcf
+
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 7f392cb74778de9854704895337bc78901552b44..2d474f3bf38545d2703f1f8f80419dc774e15bf6 100644
+index f95534b0e6745b8c52351246693da36764ad47b3..3bd4b16da49a730326f5b9039649eb59e3043bd0 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -156,7 +156,7 @@ import org.slf4j.Logger;
@@ -152,7 +152,7 @@ index 7f392cb74778de9854704895337bc78901552b44..2d474f3bf38545d2703f1f8f80419dc7
Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
this.paperConfigurations = services.paperConfigurations(); // Paper
}
-@@ -1140,7 +1144,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1129,7 +1133,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
org.spigotmc.WatchdogThread.doStop(); // Spigot
// CraftBukkit start - Restore terminal to original settings
try {
@@ -161,7 +161,7 @@ index 7f392cb74778de9854704895337bc78901552b44..2d474f3bf38545d2703f1f8f80419dc7
} catch (Exception ignored) {
}
// CraftBukkit end
-@@ -1570,7 +1574,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1564,7 +1568,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public void sendSystemMessage(Component message) {
@@ -171,7 +171,7 @@ index 7f392cb74778de9854704895337bc78901552b44..2d474f3bf38545d2703f1f8f80419dc7
public KeyPair getKeyPair() {
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 5b2980866ae3cd78f1852b0ad396ff1967ddfc16..9411e5664c0067f976018fe19b1e74032f24bd5f 100644
+index 6c856dee201d84285283504aad8cf959e88b1c52..ed3662f2d237fc6a11cb3f6a2e476dc014ba4586 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -103,6 +103,9 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -222,7 +222,7 @@ index 5b2980866ae3cd78f1852b0ad396ff1967ddfc16..9411e5664c0067f976018fe19b1e7403
System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream());
System.setErr(IoBuilder.forLogger(logger).setLevel(Level.WARN).buildPrintStream());
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 0a1684609ae155f99959b4cb4db3a1cf7234c54b..1b021f6048f6030bb8d7b59b39f5e829f1687365 100644
+index 5820dbb6d2129e2f99207e39c3ed9e661610f491..f86d3dfcc19f6f9383d21044f61aa5246cf1f9e5 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -160,8 +160,7 @@ public abstract class PlayerList {
@@ -236,7 +236,7 @@ index 0a1684609ae155f99959b4cb4db3a1cf7234c54b..1b021f6048f6030bb8d7b59b39f5e829
this.bans = new UserBanList(PlayerList.USERBANLIST_FILE);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 6109763453327f49a15c677a3af8f2de959b58cc..8da0beff6a7937130ecd99dd46880da0d4a16a1a 100644
+index a458cf93af182e2b6b7c7ef89a4a7fe7f64f7be5..eb7dd4143f545ab93e55ffac7f3322ce676dfb92 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -45,7 +45,6 @@ import java.util.logging.Level;
diff --git a/patches/server/0142-provide-a-configurable-option-to-disable-creeper-lin.patch b/patches/server/0136-provide-a-configurable-option-to-disable-creeper-lin.patch
index d5c14819e2..d5c14819e2 100644
--- a/patches/server/0142-provide-a-configurable-option-to-disable-creeper-lin.patch
+++ b/patches/server/0136-provide-a-configurable-option-to-disable-creeper-lin.patch
diff --git a/patches/server/0143-Item-canEntityPickup.patch b/patches/server/0137-Item-canEntityPickup.patch
index 07b208cc78..07b208cc78 100644
--- a/patches/server/0143-Item-canEntityPickup.patch
+++ b/patches/server/0137-Item-canEntityPickup.patch
diff --git a/patches/server/0144-PlayerPickupItemEvent-setFlyAtPlayer.patch b/patches/server/0138-PlayerPickupItemEvent-setFlyAtPlayer.patch
index 6e987d76ed..6e987d76ed 100644
--- a/patches/server/0144-PlayerPickupItemEvent-setFlyAtPlayer.patch
+++ b/patches/server/0138-PlayerPickupItemEvent-setFlyAtPlayer.patch
diff --git a/patches/server/0145-PlayerAttemptPickupItemEvent.patch b/patches/server/0139-PlayerAttemptPickupItemEvent.patch
index deec8f3a7d..deec8f3a7d 100644
--- a/patches/server/0145-PlayerAttemptPickupItemEvent.patch
+++ b/patches/server/0139-PlayerAttemptPickupItemEvent.patch
diff --git a/patches/server/0146-Do-not-submit-profile-lookups-to-worldgen-threads.patch b/patches/server/0140-Do-not-submit-profile-lookups-to-worldgen-threads.patch
index b2385e0d42..b2385e0d42 100644
--- a/patches/server/0146-Do-not-submit-profile-lookups-to-worldgen-threads.patch
+++ b/patches/server/0140-Do-not-submit-profile-lookups-to-worldgen-threads.patch
diff --git a/patches/server/0147-Add-UnknownCommandEvent.patch b/patches/server/0141-Add-UnknownCommandEvent.patch
index d9cabca3ac..6ce95d0001 100644
--- a/patches/server/0147-Add-UnknownCommandEvent.patch
+++ b/patches/server/0141-Add-UnknownCommandEvent.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add UnknownCommandEvent
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 8da0beff6a7937130ecd99dd46880da0d4a16a1a..c4641b210b7d4f09ed84ad1c25d9e7b0b6b97303 100644
+index eb7dd4143f545ab93e55ffac7f3322ce676dfb92..75b9d496860d7f3a9d5f680cbdfe89860286522e 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -892,7 +892,13 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0148-Basic-PlayerProfile-API.patch b/patches/server/0142-Basic-PlayerProfile-API.patch
index 395c8c36ce..cba9be0438 100644
--- a/patches/server/0148-Basic-PlayerProfile-API.patch
+++ b/patches/server/0142-Basic-PlayerProfile-API.patch
@@ -555,7 +555,7 @@ index 0000000000000000000000000000000000000000..7ac27392a8647ef7d0dc78efe78703e9
+ @NotNull GameProfile buildGameProfile();
+}
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
-index 5eb6ce20ee17d87db0f6c2dcee96d6d0891d6c50..3e8867c317f7018780f44b62d0bd40fc9fa9ce9f 100644
+index c9e169a6dca4dd8fe6e27a23deb410664a5d4466..e98276943e1690572b8f7bc54a259aa8340bae41 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -1,5 +1,7 @@
@@ -566,7 +566,7 @@ index 5eb6ce20ee17d87db0f6c2dcee96d6d0891d6c50..3e8867c317f7018780f44b62d0bd40fc
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
-@@ -23,6 +25,7 @@ import net.minecraft.world.level.Level;
+@@ -24,6 +26,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import org.apache.commons.lang.exception.ExceptionUtils;
@@ -574,7 +574,7 @@ index 5eb6ce20ee17d87db0f6c2dcee96d6d0891d6c50..3e8867c317f7018780f44b62d0bd40fc
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.CraftWorld;
-@@ -370,6 +373,10 @@ public final class MCUtil {
+@@ -371,6 +374,10 @@ public final class MCUtil {
return run.get();
}
@@ -621,7 +621,7 @@ index 2a0cf0a8a79c09566c598197fc6f8c447d4bbd72..5e3bc0590e59770490b1c6c818d99be0
String s1 = name.toLowerCase(Locale.ROOT);
GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index c4641b210b7d4f09ed84ad1c25d9e7b0b6b97303..90d88637b5690524d1899541abbb310d330d0e50 100644
+index 75b9d496860d7f3a9d5f680cbdfe89860286522e..7013b11fc0cd5fbb5a7e62be45d84d54d3052237 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -244,6 +244,9 @@ import org.yaml.snakeyaml.error.MarkedYAMLException;
diff --git a/patches/server/0149-Shoulder-Entities-Release-API.patch b/patches/server/0143-Shoulder-Entities-Release-API.patch
index 50587a494b..50587a494b 100644
--- a/patches/server/0149-Shoulder-Entities-Release-API.patch
+++ b/patches/server/0143-Shoulder-Entities-Release-API.patch
diff --git a/patches/server/0150-Profile-Lookup-Events.patch b/patches/server/0144-Profile-Lookup-Events.patch
index a9a4210f19..a9a4210f19 100644
--- a/patches/server/0150-Profile-Lookup-Events.patch
+++ b/patches/server/0144-Profile-Lookup-Events.patch
diff --git a/patches/server/0151-Block-player-logins-during-server-shutdown.patch b/patches/server/0145-Block-player-logins-during-server-shutdown.patch
index 8777198e49..8777198e49 100644
--- a/patches/server/0151-Block-player-logins-during-server-shutdown.patch
+++ b/patches/server/0145-Block-player-logins-during-server-shutdown.patch
diff --git a/patches/server/0152-Entity-fromMobSpawner.patch b/patches/server/0146-Entity-fromMobSpawner.patch
index f29310bb65..566620c80b 100644
--- a/patches/server/0152-Entity-fromMobSpawner.patch
+++ b/patches/server/0146-Entity-fromMobSpawner.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Entity#fromMobSpawner()
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 55f1886761c7a528f0f1e371de546aff7707e60b..be1b7014a745a3f7ce6cb690c494bbb876e40fcc 100644
+index 6e1b53317c4107f907e551a9baeaf64909f6de87..65a22c154807c416a63fbf68dad72175b51a52d4 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -380,6 +380,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -16,7 +16,7 @@ index 55f1886761c7a528f0f1e371de546aff7707e60b..be1b7014a745a3f7ce6cb690c494bbb8
@javax.annotation.Nullable
private org.bukkit.util.Vector origin;
@javax.annotation.Nullable
-@@ -1968,6 +1969,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2020,6 +2021,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
nbt.put("Paper.Origin", this.newDoubleList(origin.getX(), origin.getY(), origin.getZ()));
}
@@ -27,7 +27,7 @@ index 55f1886761c7a528f0f1e371de546aff7707e60b..be1b7014a745a3f7ce6cb690c494bbb8
// Paper end
return nbt;
} catch (Throwable throwable) {
-@@ -2107,6 +2112,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2159,6 +2164,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.originWorld = originWorld;
origin = new org.bukkit.util.Vector(originTag.getDouble(0), originTag.getDouble(1), originTag.getDouble(2));
}
diff --git a/patches/server/0153-Improve-the-Saddle-API-for-Horses.patch b/patches/server/0147-Improve-the-Saddle-API-for-Horses.patch
index ccd3b7baf4..ccd3b7baf4 100644
--- a/patches/server/0153-Improve-the-Saddle-API-for-Horses.patch
+++ b/patches/server/0147-Improve-the-Saddle-API-for-Horses.patch
diff --git a/patches/server/0154-Implement-ensureServerConversions-API.patch b/patches/server/0148-Implement-ensureServerConversions-API.patch
index f98e7f543c..f98e7f543c 100644
--- a/patches/server/0154-Implement-ensureServerConversions-API.patch
+++ b/patches/server/0148-Implement-ensureServerConversions-API.patch
diff --git a/patches/server/0155-Implement-getI18NDisplayName.patch b/patches/server/0149-Implement-getI18NDisplayName.patch
index 7011d8a566..7011d8a566 100644
--- a/patches/server/0155-Implement-getI18NDisplayName.patch
+++ b/patches/server/0149-Implement-getI18NDisplayName.patch
diff --git a/patches/server/0156-ProfileWhitelistVerifyEvent.patch b/patches/server/0150-ProfileWhitelistVerifyEvent.patch
index 126a4ea9c1..70f413bfcc 100644
--- a/patches/server/0156-ProfileWhitelistVerifyEvent.patch
+++ b/patches/server/0150-ProfileWhitelistVerifyEvent.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] ProfileWhitelistVerifyEvent
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 1b021f6048f6030bb8d7b59b39f5e829f1687365..bf5cfdae384e44b3cef5d7edb69a559803f583a4 100644
+index f86d3dfcc19f6f9383d21044f61aa5246cf1f9e5..399735c923cfd52bd7b67beb9b974585ab507ca9 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -621,9 +621,9 @@ public abstract class PlayerList {
diff --git a/patches/server/0157-Fix-this-stupid-bullshit.patch b/patches/server/0151-Fix-this-stupid-bullshit.patch
index d4534629d8..d4534629d8 100644
--- a/patches/server/0157-Fix-this-stupid-bullshit.patch
+++ b/patches/server/0151-Fix-this-stupid-bullshit.patch
diff --git a/patches/server/0158-LivingEntity-setKiller.patch b/patches/server/0152-LivingEntity-setKiller.patch
index f705351202..f705351202 100644
--- a/patches/server/0158-LivingEntity-setKiller.patch
+++ b/patches/server/0152-LivingEntity-setKiller.patch
diff --git a/patches/server/0159-Ocelot-despawns-should-honor-nametags-and-leash.patch b/patches/server/0153-Ocelot-despawns-should-honor-nametags-and-leash.patch
index 884de65442..884de65442 100644
--- a/patches/server/0159-Ocelot-despawns-should-honor-nametags-and-leash.patch
+++ b/patches/server/0153-Ocelot-despawns-should-honor-nametags-and-leash.patch
diff --git a/patches/server/0160-Reset-spawner-timer-when-spawner-event-is-cancelled.patch b/patches/server/0154-Reset-spawner-timer-when-spawner-event-is-cancelled.patch
index c47d4472ba..c47d4472ba 100644
--- a/patches/server/0160-Reset-spawner-timer-when-spawner-event-is-cancelled.patch
+++ b/patches/server/0154-Reset-spawner-timer-when-spawner-event-is-cancelled.patch
diff --git a/patches/server/0161-Allow-specifying-a-custom-authentication-servers-dow.patch b/patches/server/0155-Allow-specifying-a-custom-authentication-servers-dow.patch
index 5396b4371a..5396b4371a 100644
--- a/patches/server/0161-Allow-specifying-a-custom-authentication-servers-dow.patch
+++ b/patches/server/0155-Allow-specifying-a-custom-authentication-servers-dow.patch
diff --git a/patches/server/0162-Handle-plugin-prefixes-using-Log4J-configuration.patch b/patches/server/0156-Handle-plugin-prefixes-using-Log4J-configuration.patch
index 24e5ff4f30..24e5ff4f30 100644
--- a/patches/server/0162-Handle-plugin-prefixes-using-Log4J-configuration.patch
+++ b/patches/server/0156-Handle-plugin-prefixes-using-Log4J-configuration.patch
diff --git a/patches/server/0163-Improve-Log4J-Configuration-Plugin-Loggers.patch b/patches/server/0157-Improve-Log4J-Configuration-Plugin-Loggers.patch
index 5afb9562c0..5afb9562c0 100644
--- a/patches/server/0163-Improve-Log4J-Configuration-Plugin-Loggers.patch
+++ b/patches/server/0157-Improve-Log4J-Configuration-Plugin-Loggers.patch
diff --git a/patches/server/0164-Add-PlayerJumpEvent.patch b/patches/server/0158-Add-PlayerJumpEvent.patch
index c861a5386c..c861a5386c 100644
--- a/patches/server/0164-Add-PlayerJumpEvent.patch
+++ b/patches/server/0158-Add-PlayerJumpEvent.patch
diff --git a/patches/server/0165-handle-ServerboundKeepAlivePacket-async.patch b/patches/server/0159-handle-ServerboundKeepAlivePacket-async.patch
index bb33ae2596..bb33ae2596 100644
--- a/patches/server/0165-handle-ServerboundKeepAlivePacket-async.patch
+++ b/patches/server/0159-handle-ServerboundKeepAlivePacket-async.patch
diff --git a/patches/server/0166-Expose-client-protocol-version-and-virtual-host.patch b/patches/server/0160-Expose-client-protocol-version-and-virtual-host.patch
index 0702ce77ce..2257b63fca 100644
--- a/patches/server/0166-Expose-client-protocol-version-and-virtual-host.patch
+++ b/patches/server/0160-Expose-client-protocol-version-and-virtual-host.patch
@@ -60,13 +60,13 @@ index 0000000000000000000000000000000000000000..a5a7624f1f372a26b982836cd31cff15
+
+}
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 9b96d05094c3b83f6388d479fdca8800453ccd1d..308b720a58320aab1e2616542bbdd2e2fc5869ee 100644
+index b5b10c57401e1b27175b1960839a81382d89b73f..9f4d21b3316c1faebc043fda711b0a9d0411dfff 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -89,6 +89,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- private float averageSentPackets;
- private int tickCount;
- private boolean handlingFault;
+@@ -111,6 +111,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+ }
+ }
+ // Paper end - add pending task queue
+ // Paper start - NetworkClient implementation
+ public int protocolVersion;
+ public java.net.InetSocketAddress virtualHost;
@@ -90,10 +90,10 @@ index 9016aced079108aeae09f030a672467a953ef93f..4170bda451df3db43e7d57d87d1abb81
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 538db39d365252a63d578728b362a8dde3e8838e..9db54a62d46a02fc7d2adcb9cbfa275fdd541a28 100644
+index 843764d27c490e94bcf2becdafb15e5f1a68bc92..dd86f2055af67f35a5d265b78e99b12e7b7926ad 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -224,6 +224,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -299,6 +299,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
}
diff --git a/patches/server/0167-revert-serverside-behavior-of-keepalives.patch b/patches/server/0161-revert-serverside-behavior-of-keepalives.patch
index 8736164bf2..8736164bf2 100644
--- a/patches/server/0167-revert-serverside-behavior-of-keepalives.patch
+++ b/patches/server/0161-revert-serverside-behavior-of-keepalives.patch
diff --git a/patches/server/0168-Send-attack-SoundEffects-only-to-players-who-can-see.patch b/patches/server/0162-Send-attack-SoundEffects-only-to-players-who-can-see.patch
index edadd2cc60..edadd2cc60 100644
--- a/patches/server/0168-Send-attack-SoundEffects-only-to-players-who-can-see.patch
+++ b/patches/server/0162-Send-attack-SoundEffects-only-to-players-who-can-see.patch
diff --git a/patches/server/0169-Add-PlayerArmorChangeEvent.patch b/patches/server/0163-Add-PlayerArmorChangeEvent.patch
index fe028a5f3e..fe028a5f3e 100644
--- a/patches/server/0169-Add-PlayerArmorChangeEvent.patch
+++ b/patches/server/0163-Add-PlayerArmorChangeEvent.patch
diff --git a/patches/server/0170-Prevent-logins-from-being-processed-when-the-player-.patch b/patches/server/0164-Prevent-logins-from-being-processed-when-the-player-.patch
index 797b242873..797b242873 100644
--- a/patches/server/0170-Prevent-logins-from-being-processed-when-the-player-.patch
+++ b/patches/server/0164-Prevent-logins-from-being-processed-when-the-player-.patch
diff --git a/patches/server/0171-Fix-MC-117075-TE-Unload-Lag-Spike.patch b/patches/server/0165-Fix-MC-117075-TE-Unload-Lag-Spike.patch
index d0a52d5728..9eb1924bd9 100644
--- a/patches/server/0171-Fix-MC-117075-TE-Unload-Lag-Spike.patch
+++ b/patches/server/0165-Fix-MC-117075-TE-Unload-Lag-Spike.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix MC-117075: TE Unload Lag Spike
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 4e843d9e28f7775faba3e47ca5a725f7921490e6..28c3aca868c11da6f61b19b9b35072995c8be8bf 100644
+index 1954a93a1f566c1c79cdc2e0eb1b41141d6b5fde..6cc047624746d33ed971ec2dc8f64490bdcaeea8 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -691,6 +691,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -696,6 +696,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// Spigot start
// Iterator iterator = this.blockEntityTickers.iterator();
int tilesThisCycle = 0;
@@ -17,7 +17,7 @@ index 4e843d9e28f7775faba3e47ca5a725f7921490e6..28c3aca868c11da6f61b19b9b3507299
for (tileTickPosition = 0; tileTickPosition < this.blockEntityTickers.size(); tileTickPosition++) { // Paper - Disable tick limiters
this.tileTickPosition = (this.tileTickPosition < this.blockEntityTickers.size()) ? this.tileTickPosition : 0;
TickingBlockEntity tickingblockentity = (TickingBlockEntity) this.blockEntityTickers.get(tileTickPosition);
-@@ -698,7 +700,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -703,7 +705,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
if (tickingblockentity == null) {
this.getCraftServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash");
tilesThisCycle--;
@@ -25,7 +25,7 @@ index 4e843d9e28f7775faba3e47ca5a725f7921490e6..28c3aca868c11da6f61b19b9b3507299
continue;
}
// Spigot end
-@@ -706,12 +707,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -711,12 +712,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
if (tickingblockentity.isRemoved()) {
// Spigot start
tilesThisCycle--;
diff --git a/patches/server/0172-use-CB-BlockState-implementations-for-captured-block.patch b/patches/server/0166-use-CB-BlockState-implementations-for-captured-block.patch
index 8c4fa7bc1d..537d6aebf6 100644
--- a/patches/server/0172-use-CB-BlockState-implementations-for-captured-block.patch
+++ b/patches/server/0166-use-CB-BlockState-implementations-for-captured-block.patch
@@ -18,7 +18,7 @@ the blockstate that will be valid for restoration, as opposed to dropping
information on restoration when the event is cancelled.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 28c3aca868c11da6f61b19b9b35072995c8be8bf..4022c4d3b2ce935f69460e4de21f1fad7c100b26 100644
+index 6cc047624746d33ed971ec2dc8f64490bdcaeea8..e11e3ee5a11ebed7a1550da14c3c5c54962e4fdf 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -148,7 +148,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -49,7 +49,7 @@ index 28c3aca868c11da6f61b19b9b35072995c8be8bf..4022c4d3b2ce935f69460e4de21f1fad
this.capturedBlockStates.put(pos.immutable(), blockstate);
captured = true;
}
-@@ -603,7 +604,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -608,7 +609,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public BlockState getBlockState(BlockPos pos) {
// CraftBukkit start - tree generation
if (this.captureTreeGeneration) {
diff --git a/patches/server/0173-API-to-get-a-BlockState-without-a-snapshot.patch b/patches/server/0167-API-to-get-a-BlockState-without-a-snapshot.patch
index c2d8befb3f..c2d8befb3f 100644
--- a/patches/server/0173-API-to-get-a-BlockState-without-a-snapshot.patch
+++ b/patches/server/0167-API-to-get-a-BlockState-without-a-snapshot.patch
diff --git a/patches/server/0174-AsyncTabCompleteEvent.patch b/patches/server/0168-AsyncTabCompleteEvent.patch
index 82028759cd..ddced42488 100644
--- a/patches/server/0174-AsyncTabCompleteEvent.patch
+++ b/patches/server/0168-AsyncTabCompleteEvent.patch
@@ -89,7 +89,7 @@ index e40eeb5e04d96fb55283ded82cea0a5539a2fad5..90bd5c1a010a3a9d24328e5c71905360
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 90d88637b5690524d1899541abbb310d330d0e50..565ae5e0dc0db9aaf694003cb30e9af3ff6fc78d 100644
+index 7013b11fc0cd5fbb5a7e62be45d84d54d3052237..ca6bb66e8ba1e17f025b82091910ca223185ad3b 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2086,7 +2086,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0175-PlayerPickupExperienceEvent.patch b/patches/server/0169-PlayerPickupExperienceEvent.patch
index f59d0999a5..f59d0999a5 100644
--- a/patches/server/0175-PlayerPickupExperienceEvent.patch
+++ b/patches/server/0169-PlayerPickupExperienceEvent.patch
diff --git a/patches/server/0176-Ability-to-apply-mending-to-XP-API.patch b/patches/server/0170-Ability-to-apply-mending-to-XP-API.patch
index 79dc532697..4adad63f75 100644
--- a/patches/server/0176-Ability-to-apply-mending-to-XP-API.patch
+++ b/patches/server/0170-Ability-to-apply-mending-to-XP-API.patch
@@ -10,10 +10,10 @@ of giving the player experience points.
Both an API To standalone mend, and apply mending logic to .giveExp has been added.
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 9db54a62d46a02fc7d2adcb9cbfa275fdd541a28..afb4f2e730fbc57dd4b5fac24071eb99231d4d19 100644
+index dd86f2055af67f35a5d265b78e99b12e7b7926ad..9f959cbb6e8685dacccec1d8df68d4a8a94ab81e 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -1409,7 +1409,37 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1444,7 +1444,37 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
@Override
diff --git a/patches/server/0177-PlayerNaturallySpawnCreaturesEvent.patch b/patches/server/0171-PlayerNaturallySpawnCreaturesEvent.patch
index 53ca338ef3..8e24b34515 100644
--- a/patches/server/0177-PlayerNaturallySpawnCreaturesEvent.patch
+++ b/patches/server/0171-PlayerNaturallySpawnCreaturesEvent.patch
@@ -9,10 +9,10 @@ from triggering monster spawns on a server.
Also a highly more effecient way to blanket block spawns in a world
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index e086135936e4f6c109cd09a4e4df350702b3510a..09a2680162ed9f1d82830778fea6b05a34ab382b 100644
+index eda47a2c2a26fcf3434af140e1436395b2506cb0..ea9f536efd4c76b421f6e02f93f16fae115840d8 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1375,7 +1375,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -680,7 +680,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
chunkRange = (chunkRange > 8) ? 8 : chunkRange;
@@ -23,7 +23,7 @@ index e086135936e4f6c109cd09a4e4df350702b3510a..09a2680162ed9f1d82830778fea6b05a
// Spigot end
long i = chunkcoordintpair.toLong();
-@@ -1392,6 +1394,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -697,6 +699,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
entityplayer = (ServerPlayer) iterator.next();
@@ -40,10 +40,10 @@ index e086135936e4f6c109cd09a4e4df350702b3510a..09a2680162ed9f1d82830778fea6b05a
return true;
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 5057053bcd3fc205e62edd9519a9545c16ce60c7..d1fa959b6e872565b689ee6b4f1bed9ad9e4077e 100644
+index b3cff10fece84839fe85feb297fafbcd4a02d838..70077d3f359944e2df29198ae156be477ebc278d 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -786,6 +786,15 @@ public class ServerChunkCache extends ChunkSource {
+@@ -714,6 +714,15 @@ public class ServerChunkCache extends ChunkSource {
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
Collections.shuffle(list);
@@ -60,7 +60,7 @@ index 5057053bcd3fc205e62edd9519a9545c16ce60c7..d1fa959b6e872565b689ee6b4f1bed9a
while (iterator1.hasNext()) {
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 5d214b7dd4f6d7feff0a1904ce6573cffd258a1c..a214916ff80885af262165d5936b8bdf2056cbed 100644
+index 776d2b38f284e13ec9a7dd67a538626817c79887..91f258aa1d4b888b1e1c604ce5f1cf0d755adb9d 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -1,5 +1,6 @@
@@ -70,11 +70,11 @@ index 5d214b7dd4f6d7feff0a1904ce6573cffd258a1c..a214916ff80885af262165d5936b8bdf
import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Either;
-@@ -246,6 +247,7 @@ public class ServerPlayer extends Player {
+@@ -245,6 +246,7 @@ public class ServerPlayer extends Player {
public Integer clientViewDistance;
public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent
// CraftBukkit end
+ public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper
+ public boolean isRealPlayer; // Paper
public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
-
diff --git a/patches/server/0178-Add-setPlayerProfile-API-for-Skulls.patch b/patches/server/0172-Add-setPlayerProfile-API-for-Skulls.patch
index dfcc8c3751..dfcc8c3751 100644
--- a/patches/server/0178-Add-setPlayerProfile-API-for-Skulls.patch
+++ b/patches/server/0172-Add-setPlayerProfile-API-for-Skulls.patch
diff --git a/patches/server/0179-PreCreatureSpawnEvent.patch b/patches/server/0173-PreCreatureSpawnEvent.patch
index 82fc009561..82fc009561 100644
--- a/patches/server/0179-PreCreatureSpawnEvent.patch
+++ b/patches/server/0173-PreCreatureSpawnEvent.patch
diff --git a/patches/server/0180-Fill-Profile-Property-Events.patch b/patches/server/0174-Fill-Profile-Property-Events.patch
index 30643d081c..30643d081c 100644
--- a/patches/server/0180-Fill-Profile-Property-Events.patch
+++ b/patches/server/0174-Fill-Profile-Property-Events.patch
diff --git a/patches/server/0181-PlayerAdvancementCriterionGrantEvent.patch b/patches/server/0175-PlayerAdvancementCriterionGrantEvent.patch
index 04f350388d..04f350388d 100644
--- a/patches/server/0181-PlayerAdvancementCriterionGrantEvent.patch
+++ b/patches/server/0175-PlayerAdvancementCriterionGrantEvent.patch
diff --git a/patches/server/0182-Add-ArmorStand-Item-Meta.patch b/patches/server/0176-Add-ArmorStand-Item-Meta.patch
index e8f6a478fb..e8f6a478fb 100644
--- a/patches/server/0182-Add-ArmorStand-Item-Meta.patch
+++ b/patches/server/0176-Add-ArmorStand-Item-Meta.patch
diff --git a/patches/server/0183-Extend-Player-Interact-cancellation.patch b/patches/server/0177-Extend-Player-Interact-cancellation.patch
index ef330b35a9..ef330b35a9 100644
--- a/patches/server/0183-Extend-Player-Interact-cancellation.patch
+++ b/patches/server/0177-Extend-Player-Interact-cancellation.patch
diff --git a/patches/server/0184-Tameable-getOwnerUniqueId-API.patch b/patches/server/0178-Tameable-getOwnerUniqueId-API.patch
index 7fca628e8a..7fca628e8a 100644
--- a/patches/server/0184-Tameable-getOwnerUniqueId-API.patch
+++ b/patches/server/0178-Tameable-getOwnerUniqueId-API.patch
diff --git a/patches/server/0185-Toggleable-player-crits-helps-mitigate-hacked-client.patch b/patches/server/0179-Toggleable-player-crits-helps-mitigate-hacked-client.patch
index dd76c430fe..dd76c430fe 100644
--- a/patches/server/0185-Toggleable-player-crits-helps-mitigate-hacked-client.patch
+++ b/patches/server/0179-Toggleable-player-crits-helps-mitigate-hacked-client.patch
diff --git a/patches/server/0186-Disable-Explicit-Network-Manager-Flushing.patch b/patches/server/0180-Disable-Explicit-Network-Manager-Flushing.patch
index 52b1cd9a42..9ddb02e579 100644
--- a/patches/server/0186-Disable-Explicit-Network-Manager-Flushing.patch
+++ b/patches/server/0180-Disable-Explicit-Network-Manager-Flushing.patch
@@ -12,10 +12,10 @@ flushing on the netty event loop, so it won't do the flush on the main thread.
Renable flushing by passing -Dpaper.explicit-flush=true
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 308b720a58320aab1e2616542bbdd2e2fc5869ee..2643c3d99c11bc6783386502c7e21293b6dfa345 100644
+index 9f4d21b3316c1faebc043fda711b0a9d0411dfff..7d85817a0a041c2fa257396d1508488bfc7da55b 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -92,6 +92,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -114,6 +114,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
// Paper start - NetworkClient implementation
public int protocolVersion;
public java.net.InetSocketAddress virtualHost;
@@ -23,7 +23,7 @@ index 308b720a58320aab1e2616542bbdd2e2fc5869ee..2643c3d99c11bc6783386502c7e21293
// Paper end
public Connection(PacketFlow side) {
-@@ -280,7 +281,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -309,7 +310,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
if (this.channel != null) {
diff --git a/patches/server/0187-Implement-extended-PaperServerListPingEvent.patch b/patches/server/0181-Implement-extended-PaperServerListPingEvent.patch
index c9dccdb403..fa4ddb2052 100644
--- a/patches/server/0187-Implement-extended-PaperServerListPingEvent.patch
+++ b/patches/server/0181-Implement-extended-PaperServerListPingEvent.patch
@@ -190,7 +190,7 @@ index 67455a5ba75c9b816213e44d6872c5ddf8e27e98..23efad80934930beadf15e65781551d4
public ClientboundStatusResponsePacket(ServerStatus metadata) {
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 2d474f3bf38545d2703f1f8f80419dc774e15bf6..401b883109f0ab7b30dcdc5c0ea28fc7aa3a0822 100644
+index 3bd4b16da49a730326f5b9039649eb59e3043bd0..d65423b8d7b69db79de05587efba13008602f743 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -2,6 +2,9 @@ package net.minecraft.server;
@@ -203,7 +203,7 @@ index 2d474f3bf38545d2703f1f8f80419dc774e15bf6..401b883109f0ab7b30dcdc5c0ea28fc7
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
-@@ -1309,7 +1312,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1303,7 +1306,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.lastServerStatus = i;
this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount()));
if (!this.hidesOnlinePlayers()) {
diff --git a/patches/server/0188-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch b/patches/server/0182-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch
index 33928fcf03..33928fcf03 100644
--- a/patches/server/0188-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch
+++ b/patches/server/0182-Ability-to-change-PlayerProfile-in-AsyncPreLoginEven.patch
diff --git a/patches/server/0189-Player.setPlayerProfile-API.patch b/patches/server/0183-Player.setPlayerProfile-API.patch
index 4f588923ed..db12c17a5d 100644
--- a/patches/server/0189-Player.setPlayerProfile-API.patch
+++ b/patches/server/0183-Player.setPlayerProfile-API.patch
@@ -24,7 +24,7 @@ index 9a7fe61d7de80eaf044c202e1ed13d9e4b59622a..ef09f5f42a89f767060f015af27269ad
playerName = gameProfile.getName();
uniqueId = gameProfile.getId();
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index afb4f2e730fbc57dd4b5fac24071eb99231d4d19..27f496417aecf5a62fdbabbc7de52533d5252ebf 100644
+index 9f959cbb6e8685dacccec1d8df68d4a8a94ab81e..aadc01662c07ab99466babc8f5ed5b3bef2c1a8e 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -79,6 +79,7 @@ import net.minecraft.world.entity.ai.attributes.Attributes;
@@ -35,7 +35,7 @@ index afb4f2e730fbc57dd4b5fac24071eb99231d4d19..27f496417aecf5a62fdbabbc7de52533
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.world.level.border.BorderChangeListener;
import net.minecraft.world.level.saveddata.maps.MapDecoration;
-@@ -207,11 +208,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -282,11 +283,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
return server.getPlayer(getUniqueId()) != null;
}
@@ -47,7 +47,7 @@ index afb4f2e730fbc57dd4b5fac24071eb99231d4d19..27f496417aecf5a62fdbabbc7de52533
@Override
public InetSocketAddress getAddress() {
if (this.getHandle().connection == null) return null;
-@@ -1542,8 +1538,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1577,8 +1573,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.hiddenEntities.put(entity.getUniqueId(), hidingPlugins);
// Remove this entity from the hidden player's EntityTrackerEntry
@@ -64,7 +64,7 @@ index afb4f2e730fbc57dd4b5fac24071eb99231d4d19..27f496417aecf5a62fdbabbc7de52533
ChunkMap.TrackedEntity entry = tracker.entityMap.get(other.getId());
if (entry != null) {
entry.removePlayer(this.getHandle());
-@@ -1556,8 +1559,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1591,8 +1594,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.getHandle().connection.send(new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.REMOVE_PLAYER, otherPlayer));
}
}
@@ -73,7 +73,7 @@ index afb4f2e730fbc57dd4b5fac24071eb99231d4d19..27f496417aecf5a62fdbabbc7de52533
}
@Override
-@@ -1594,8 +1595,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1629,8 +1630,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
this.hiddenEntities.remove(entity.getUniqueId());
@@ -90,7 +90,7 @@ index afb4f2e730fbc57dd4b5fac24071eb99231d4d19..27f496417aecf5a62fdbabbc7de52533
if (other instanceof ServerPlayer) {
ServerPlayer otherPlayer = (ServerPlayer) other;
-@@ -1606,9 +1614,51 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1641,9 +1649,51 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
if (entry != null && !entry.seenBy.contains(this.getHandle().connection)) {
entry.updatePlayer(this.getHandle());
}
diff --git a/patches/server/0190-getPlayerUniqueId-API.patch b/patches/server/0184-getPlayerUniqueId-API.patch
index 08d3bcf224..9a49d54845 100644
--- a/patches/server/0190-getPlayerUniqueId-API.patch
+++ b/patches/server/0184-getPlayerUniqueId-API.patch
@@ -9,7 +9,7 @@ In Offline Mode, will return an Offline UUID
This is a more performant way to obtain a UUID for a name than loading an OfflinePlayer
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 565ae5e0dc0db9aaf694003cb30e9af3ff6fc78d..94e0134dcf644ed8f2835ddceb2f1e09c1d8c6a4 100644
+index ca6bb66e8ba1e17f025b82091910ca223185ad3b..a60b93a15d5cf80745eb114b393465ff1c53f444 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1708,6 +1708,25 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0191-Improved-Async-Task-Scheduler.patch b/patches/server/0185-Improved-Async-Task-Scheduler.patch
index b62fddc7b9..b62fddc7b9 100644
--- a/patches/server/0191-Improved-Async-Task-Scheduler.patch
+++ b/patches/server/0185-Improved-Async-Task-Scheduler.patch
diff --git a/patches/server/0192-Make-legacy-ping-handler-more-reliable.patch b/patches/server/0186-Make-legacy-ping-handler-more-reliable.patch
index 438fb37f34..438fb37f34 100644
--- a/patches/server/0192-Make-legacy-ping-handler-more-reliable.patch
+++ b/patches/server/0186-Make-legacy-ping-handler-more-reliable.patch
diff --git a/patches/server/0193-Call-PaperServerListPingEvent-for-legacy-pings.patch b/patches/server/0187-Call-PaperServerListPingEvent-for-legacy-pings.patch
index d65878827c..d65878827c 100644
--- a/patches/server/0193-Call-PaperServerListPingEvent-for-legacy-pings.patch
+++ b/patches/server/0187-Call-PaperServerListPingEvent-for-legacy-pings.patch
diff --git a/patches/server/0194-Flag-to-disable-the-channel-limit.patch b/patches/server/0188-Flag-to-disable-the-channel-limit.patch
index 68fc4ad851..3573578067 100644
--- a/patches/server/0194-Flag-to-disable-the-channel-limit.patch
+++ b/patches/server/0188-Flag-to-disable-the-channel-limit.patch
@@ -9,7 +9,7 @@ e.g. servers which allow and support the usage of mod packs.
provide an optional flag to disable this check, at your own risk.
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 27f496417aecf5a62fdbabbc7de52533d5252ebf..ba05bdd83abde37695bacc30d56108ed85f5adb9 100644
+index aadc01662c07ab99466babc8f5ed5b3bef2c1a8e..cf967ce377aab7fd00bd096d68ef0b66697817bf 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -173,6 +173,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@@ -20,7 +20,7 @@ index 27f496417aecf5a62fdbabbc7de52533d5252ebf..ba05bdd83abde37695bacc30d56108ed
// Paper end
public CraftPlayer(CraftServer server, ServerPlayer entity) {
-@@ -1858,7 +1859,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1893,7 +1894,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
// Paper end
public void addChannel(String channel) {
diff --git a/patches/server/0195-Add-openSign-method-to-HumanEntity.patch b/patches/server/0189-Add-openSign-method-to-HumanEntity.patch
index 184a121d22..184a121d22 100644
--- a/patches/server/0195-Add-openSign-method-to-HumanEntity.patch
+++ b/patches/server/0189-Add-openSign-method-to-HumanEntity.patch
diff --git a/patches/server/0196-Configurable-sprint-interruption-on-attack.patch b/patches/server/0190-Configurable-sprint-interruption-on-attack.patch
index 5f5e0d01bf..5f5e0d01bf 100644
--- a/patches/server/0196-Configurable-sprint-interruption-on-attack.patch
+++ b/patches/server/0190-Configurable-sprint-interruption-on-attack.patch
diff --git a/patches/server/0197-Fix-exploit-that-allowed-colored-signs-to-be-created.patch b/patches/server/0191-Fix-exploit-that-allowed-colored-signs-to-be-created.patch
index 1dffd9f0c4..1dffd9f0c4 100644
--- a/patches/server/0197-Fix-exploit-that-allowed-colored-signs-to-be-created.patch
+++ b/patches/server/0191-Fix-exploit-that-allowed-colored-signs-to-be-created.patch
diff --git a/patches/server/0198-EndermanEscapeEvent.patch b/patches/server/0192-EndermanEscapeEvent.patch
index 3fff4f9bad..3fff4f9bad 100644
--- a/patches/server/0198-EndermanEscapeEvent.patch
+++ b/patches/server/0192-EndermanEscapeEvent.patch
diff --git a/patches/server/0199-Enderman.teleportRandomly.patch b/patches/server/0193-Enderman.teleportRandomly.patch
index aafeea74bb..aafeea74bb 100644
--- a/patches/server/0199-Enderman.teleportRandomly.patch
+++ b/patches/server/0193-Enderman.teleportRandomly.patch
diff --git a/patches/server/0200-Block-Enderpearl-Travel-Exploit.patch b/patches/server/0194-Block-Enderpearl-Travel-Exploit.patch
index 7a04fe5676..0500f9ccee 100644
--- a/patches/server/0200-Block-Enderpearl-Travel-Exploit.patch
+++ b/patches/server/0194-Block-Enderpearl-Travel-Exploit.patch
@@ -12,10 +12,10 @@ This disables that by not saving the thrower when the chunk is unloaded.
This is mainly useful for survival servers that do not allow freeform teleporting.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 4a6c03f421d81b694ca8670994c9322d4425922b..3162951ff50d65d0d2fcf55d0606208dc28b9146 100644
+index 4d96a196511621d56507cfb35923bd5270f83716..cc0ff0df8c41cd379ed7536c3e5051f64c7cd3a8 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2149,6 +2149,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2199,6 +2199,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
public void onTickingEnd(Entity entity) {
ServerLevel.this.entityTickList.remove(entity);
diff --git a/patches/server/0201-Expand-World.spawnParticle-API-and-add-Builder.patch b/patches/server/0195-Expand-World.spawnParticle-API-and-add-Builder.patch
index 8fc08a8246..64e817258b 100644
--- a/patches/server/0201-Expand-World.spawnParticle-API-and-add-Builder.patch
+++ b/patches/server/0195-Expand-World.spawnParticle-API-and-add-Builder.patch
@@ -10,10 +10,10 @@ Adds an option to control the force mode of the particle.
This adds a new Builder API which is much friendlier to use.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 3162951ff50d65d0d2fcf55d0606208dc28b9146..3cd979a88be53fb2f5b785fa98d7b5c0c705e1f9 100644
+index c3d323595a2714c8274fddfbdddec3ea19fc0373..dcf611bf64eaf9156ece3a7d18cb42548c534ed8 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1562,12 +1562,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1597,12 +1597,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
public <T extends ParticleOptions> int sendParticles(ServerPlayer sender, T t0, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, boolean force) {
@@ -34,10 +34,10 @@ index 3162951ff50d65d0d2fcf55d0606208dc28b9146..3cd979a88be53fb2f5b785fa98d7b5c0
if (this.sendParticles(entityplayer, force, d0, d1, d2, packetplayoutworldparticles)) { // CraftBukkit
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index b2e693b6a799568c6196c1f805f0153ea69b8bd2..0654af81e20a75c3b0b8e11e824f680f3eb01626 100644
+index a75f4a1ecfe2790d727f5dda792c5ab4bb45554e..fee71f001933f9320daa865db433f3b5855278fa 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -1819,11 +1819,17 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1809,11 +1809,17 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public <T> void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, T data, boolean force) {
diff --git a/patches/server/0202-Prevent-Frosted-Ice-from-loading-holding-chunks.patch b/patches/server/0196-Prevent-Frosted-Ice-from-loading-holding-chunks.patch
index b9c45b572d..b9c45b572d 100644
--- a/patches/server/0202-Prevent-Frosted-Ice-from-loading-holding-chunks.patch
+++ b/patches/server/0196-Prevent-Frosted-Ice-from-loading-holding-chunks.patch
diff --git a/patches/server/0203-EndermanAttackPlayerEvent.patch b/patches/server/0197-EndermanAttackPlayerEvent.patch
index 6a1eef464f..6a1eef464f 100644
--- a/patches/server/0203-EndermanAttackPlayerEvent.patch
+++ b/patches/server/0197-EndermanAttackPlayerEvent.patch
diff --git a/patches/server/0204-WitchConsumePotionEvent.patch b/patches/server/0198-WitchConsumePotionEvent.patch
index f054a8dd3c..f054a8dd3c 100644
--- a/patches/server/0204-WitchConsumePotionEvent.patch
+++ b/patches/server/0198-WitchConsumePotionEvent.patch
diff --git a/patches/server/0205-WitchThrowPotionEvent.patch b/patches/server/0199-WitchThrowPotionEvent.patch
index 86ba404bc1..86ba404bc1 100644
--- a/patches/server/0205-WitchThrowPotionEvent.patch
+++ b/patches/server/0199-WitchThrowPotionEvent.patch
diff --git a/patches/server/0206-Allow-spawning-Item-entities-with-World.spawnEntity.patch b/patches/server/0200-Allow-spawning-Item-entities-with-World.spawnEntity.patch
index 9bfc81d04d..9bfc81d04d 100644
--- a/patches/server/0206-Allow-spawning-Item-entities-with-World.spawnEntity.patch
+++ b/patches/server/0200-Allow-spawning-Item-entities-with-World.spawnEntity.patch
diff --git a/patches/server/0207-WitchReadyPotionEvent.patch b/patches/server/0201-WitchReadyPotionEvent.patch
index 99ac9dce10..99ac9dce10 100644
--- a/patches/server/0207-WitchReadyPotionEvent.patch
+++ b/patches/server/0201-WitchReadyPotionEvent.patch
diff --git a/patches/server/0208-ItemStack-getMaxItemUseDuration.patch b/patches/server/0202-ItemStack-getMaxItemUseDuration.patch
index 50c78294a9..285c3b0db7 100644
--- a/patches/server/0208-ItemStack-getMaxItemUseDuration.patch
+++ b/patches/server/0202-ItemStack-getMaxItemUseDuration.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] ItemStack#getMaxItemUseDuration
Allows you to determine how long it takes to use a usable/consumable item
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-index 777018ba2cb5530b679b1b0a88e09ea30369f033..553dd35795b5339980aa7b5ae4e9acd9768846e4 100644
+index c9093275d2b78b6e2f59e64efab0b4775ff0b43b..ca75f84d2beeef0636fc7032297bf038d13d97e0 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -173,6 +173,13 @@ public final class CraftItemStack extends ItemStack {
diff --git a/patches/server/0209-Implement-EntityTeleportEndGatewayEvent.patch b/patches/server/0203-Implement-EntityTeleportEndGatewayEvent.patch
index b1a4699bbc..b1a4699bbc 100644
--- a/patches/server/0209-Implement-EntityTeleportEndGatewayEvent.patch
+++ b/patches/server/0203-Implement-EntityTeleportEndGatewayEvent.patch
diff --git a/patches/server/0210-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch b/patches/server/0204-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch
index 6f7b7cd31b..6f7b7cd31b 100644
--- a/patches/server/0210-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch
+++ b/patches/server/0204-Unset-Ignited-flag-on-cancel-of-Explosion-Event.patch
diff --git a/patches/server/0211-Fix-CraftEntity-hashCode.patch b/patches/server/0205-Fix-CraftEntity-hashCode.patch
index c57f31a94e..c57f31a94e 100644
--- a/patches/server/0211-Fix-CraftEntity-hashCode.patch
+++ b/patches/server/0205-Fix-CraftEntity-hashCode.patch
diff --git a/patches/server/0212-Configurable-Alternative-LootPool-Luck-Formula.patch b/patches/server/0206-Configurable-Alternative-LootPool-Luck-Formula.patch
index 40dce864c6..40dce864c6 100644
--- a/patches/server/0212-Configurable-Alternative-LootPool-Luck-Formula.patch
+++ b/patches/server/0206-Configurable-Alternative-LootPool-Luck-Formula.patch
diff --git a/patches/server/0213-Print-Error-details-when-failing-to-save-player-data.patch b/patches/server/0207-Print-Error-details-when-failing-to-save-player-data.patch
index 1fa6aa9aac..fda33feee1 100644
--- a/patches/server/0213-Print-Error-details-when-failing-to-save-player-data.patch
+++ b/patches/server/0207-Print-Error-details-when-failing-to-save-player-data.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Print Error details when failing to save player data
diff --git a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
-index 0132eb685dbcf3d3402096bd34513e91d2b20abd..d785efd61caa2237e05d9ce3dbf84d86076ff047 100644
+index 6909b6be840e8d4feadcc8b0c5a44fc1b9c81a54..601f8099f74e81c17600566b3c9b7a6dd39c9bcb 100644
--- a/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/src/main/java/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -43,7 +43,7 @@ public class PlayerDataStorage {
diff --git a/patches/server/0214-Make-shield-blocking-delay-configurable.patch b/patches/server/0208-Make-shield-blocking-delay-configurable.patch
index a1c2cf0105..a1c2cf0105 100644
--- a/patches/server/0214-Make-shield-blocking-delay-configurable.patch
+++ b/patches/server/0208-Make-shield-blocking-delay-configurable.patch
diff --git a/patches/server/0215-Improve-EntityShootBowEvent.patch b/patches/server/0209-Improve-EntityShootBowEvent.patch
index 80a633b156..80a633b156 100644
--- a/patches/server/0215-Improve-EntityShootBowEvent.patch
+++ b/patches/server/0209-Improve-EntityShootBowEvent.patch
diff --git a/patches/server/0216-PlayerReadyArrowEvent.patch b/patches/server/0210-PlayerReadyArrowEvent.patch
index ccaaa2610b..ccaaa2610b 100644
--- a/patches/server/0216-PlayerReadyArrowEvent.patch
+++ b/patches/server/0210-PlayerReadyArrowEvent.patch
diff --git a/patches/server/0217-Implement-EntityKnockbackByEntityEvent.patch b/patches/server/0211-Implement-EntityKnockbackByEntityEvent.patch
index e1f1a50195..e1f1a50195 100644
--- a/patches/server/0217-Implement-EntityKnockbackByEntityEvent.patch
+++ b/patches/server/0211-Implement-EntityKnockbackByEntityEvent.patch
diff --git a/patches/server/0218-Expand-Explosions-API.patch b/patches/server/0212-Expand-Explosions-API.patch
index 890d08a4f6..f503a30fbc 100644
--- a/patches/server/0218-Expand-Explosions-API.patch
+++ b/patches/server/0212-Expand-Explosions-API.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Expand Explosions API
Add Entity as a Source capability, and add more API choices, and on Location.
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 0654af81e20a75c3b0b8e11e824f680f3eb01626..3347e42bf45be9d640ee267bc2e0df8f241ce45c 100644
+index fee71f001933f9320daa865db433f3b5855278fa..339baff7014e98cbe03806d9a689ec09e0b30a47 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -713,6 +713,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -703,6 +703,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
public boolean createExplosion(double x, double y, double z, float power, boolean setFire, boolean breakBlocks, Entity source) {
return !this.world.explode(source == null ? null : ((CraftEntity) source).getHandle(), x, y, z, power, setFire, breakBlocks ? Explosion.BlockInteraction.BREAK : Explosion.BlockInteraction.NONE).wasCanceled;
}
diff --git a/patches/server/0219-LivingEntity-Hand-Raised-Item-Use-API.patch b/patches/server/0213-LivingEntity-Hand-Raised-Item-Use-API.patch
index d429d4ce3a..d429d4ce3a 100644
--- a/patches/server/0219-LivingEntity-Hand-Raised-Item-Use-API.patch
+++ b/patches/server/0213-LivingEntity-Hand-Raised-Item-Use-API.patch
diff --git a/patches/server/0220-RangedEntity-API.patch b/patches/server/0214-RangedEntity-API.patch
index 94da937638..94da937638 100644
--- a/patches/server/0220-RangedEntity-API.patch
+++ b/patches/server/0214-RangedEntity-API.patch
diff --git a/patches/server/0221-Add-config-to-disable-ender-dragon-legacy-check.patch b/patches/server/0215-Add-config-to-disable-ender-dragon-legacy-check.patch
index e2cb389ce5..e2cb389ce5 100644
--- a/patches/server/0221-Add-config-to-disable-ender-dragon-legacy-check.patch
+++ b/patches/server/0215-Add-config-to-disable-ender-dragon-legacy-check.patch
diff --git a/patches/server/0222-Implement-World.getEntity-UUID-API.patch b/patches/server/0216-Implement-World.getEntity-UUID-API.patch
index 49d3106298..7f510f70db 100644
--- a/patches/server/0222-Implement-World.getEntity-UUID-API.patch
+++ b/patches/server/0216-Implement-World.getEntity-UUID-API.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Implement World.getEntity(UUID) API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 3347e42bf45be9d640ee267bc2e0df8f241ce45c..ff842f952d744a13eb563407edc21d03d43ddd66 100644
+index 339baff7014e98cbe03806d9a689ec09e0b30a47..9849c255ed68309da27ba2c9156c50e89c29d498 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -1047,6 +1047,15 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1037,6 +1037,15 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return list;
}
diff --git a/patches/server/0223-InventoryCloseEvent-Reason-API.patch b/patches/server/0217-InventoryCloseEvent-Reason-API.patch
index b55d8b9e1a..e4bf599387 100644
--- a/patches/server/0223-InventoryCloseEvent-Reason-API.patch
+++ b/patches/server/0217-InventoryCloseEvent-Reason-API.patch
@@ -7,10 +7,10 @@ Allows you to determine why an inventory was closed, enabling plugin developers
to "confirm" things based on if it was player triggered close or not.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 3cd979a88be53fb2f5b785fa98d7b5c0c705e1f9..e13fa5d2ed02b5fe8f9f9d124d15dd5374a7f472 100644
+index a6ec766634e804263d55efe9df7d7fbe14d05946..65cf330708b4667fd60fdb1094df5a808ae5c2f2 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1235,7 +1235,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1270,7 +1270,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
for (net.minecraft.world.level.block.entity.BlockEntity tileentity : chunk.getBlockEntities().values()) {
if (tileentity instanceof net.minecraft.world.Container) {
for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((net.minecraft.world.Container) tileentity).getViewers())) {
@@ -19,7 +19,7 @@ index 3cd979a88be53fb2f5b785fa98d7b5c0c705e1f9..e13fa5d2ed02b5fe8f9f9d124d15dd53
}
}
}
-@@ -2239,7 +2239,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2289,7 +2289,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
// Spigot Start
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((org.bukkit.inventory.InventoryHolder) entity.getBukkitEntity()).getInventory().getViewers())) {
@@ -29,10 +29,10 @@ index 3cd979a88be53fb2f5b785fa98d7b5c0c705e1f9..e13fa5d2ed02b5fe8f9f9d124d15dd53
}
// Spigot End
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index a214916ff80885af262165d5936b8bdf2056cbed..4b9af6ef008a297438bfc583025d235d07d9b780 100644
+index 91f258aa1d4b888b1e1c604ce5f1cf0d755adb9d..1a3f9d09df6b6d8c6a84f7f62e142f341d74c6c1 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -626,7 +626,7 @@ public class ServerPlayer extends Player {
+@@ -611,7 +611,7 @@ public class ServerPlayer extends Player {
}
// Paper end
if (!this.level.isClientSide && !this.containerMenu.stillValid(this)) {
@@ -41,7 +41,7 @@ index a214916ff80885af262165d5936b8bdf2056cbed..4b9af6ef008a297438bfc583025d235d
this.containerMenu = this.inventoryMenu;
}
-@@ -819,7 +819,7 @@ public class ServerPlayer extends Player {
+@@ -804,7 +804,7 @@ public class ServerPlayer extends Player {
// SPIGOT-943 - only call if they have an inventory open
if (this.containerMenu != this.inventoryMenu) {
@@ -50,7 +50,7 @@ index a214916ff80885af262165d5936b8bdf2056cbed..4b9af6ef008a297438bfc583025d235d
}
net.kyori.adventure.text.Component deathMessage = event.deathMessage() != null ? event.deathMessage() : net.kyori.adventure.text.Component.empty(); // Paper - Adventure
-@@ -1457,7 +1457,7 @@ public class ServerPlayer extends Player {
+@@ -1442,7 +1442,7 @@ public class ServerPlayer extends Player {
}
// CraftBukkit end
if (this.containerMenu != this.inventoryMenu) {
@@ -59,7 +59,7 @@ index a214916ff80885af262165d5936b8bdf2056cbed..4b9af6ef008a297438bfc583025d235d
}
// this.nextContainerCounter(); // CraftBukkit - moved up
-@@ -1485,7 +1485,13 @@ public class ServerPlayer extends Player {
+@@ -1470,7 +1470,13 @@ public class ServerPlayer extends Player {
@Override
public void closeContainer() {
@@ -104,7 +104,7 @@ index 116dee1f1f9c489e6f85a8fa3b7f36267109d720..ea2f283634c8794bda3e531a20f39f8a
this.player.doCloseContainer();
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index bf5cfdae384e44b3cef5d7edb69a559803f583a4..0629b2df97119e242d10e41a707ba47894ce6d69 100644
+index 399735c923cfd52bd7b67beb9b974585ab507ca9..a99d6938d912a169ac329ba09f5a6becd072a94e 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -508,7 +508,7 @@ public abstract class PlayerList {
@@ -173,10 +173,10 @@ index 7ea4a2d4e691e0a0a4d9ef3189a29a4a4ca4374b..883b6245f44f3fb82d7678e1092177ca
@Override
public boolean isBlocking() {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index ba05bdd83abde37695bacc30d56108ed85f5adb9..63575545d197c4172b1df04090d904b4358f6b5c 100644
+index cf967ce377aab7fd00bd096d68ef0b66697817bf..eb6ead27924cdad9362d350e6257c5995e17b005 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -1123,7 +1123,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1158,7 +1158,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
// Close any foreign inventory
if (this.getHandle().containerMenu != this.getHandle().inventoryMenu) {
diff --git a/patches/server/0224-Vex-get-setSummoner-API.patch b/patches/server/0218-Vex-get-setSummoner-API.patch
index b39cab89d2..b39cab89d2 100644
--- a/patches/server/0224-Vex-get-setSummoner-API.patch
+++ b/patches/server/0218-Vex-get-setSummoner-API.patch
diff --git a/patches/server/0225-Refresh-player-inventory-when-cancelling-PlayerInter.patch b/patches/server/0219-Refresh-player-inventory-when-cancelling-PlayerInter.patch
index 4879efd0d6..4879efd0d6 100644
--- a/patches/server/0225-Refresh-player-inventory-when-cancelling-PlayerInter.patch
+++ b/patches/server/0219-Refresh-player-inventory-when-cancelling-PlayerInter.patch
diff --git a/patches/server/0226-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch b/patches/server/0220-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch
index 93bf1ca349..93bf1ca349 100644
--- a/patches/server/0226-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch
+++ b/patches/server/0220-Use-AsyncAppender-to-keep-logging-IO-off-main-thread.patch
diff --git a/patches/server/0227-add-more-information-to-Entity.toString.patch b/patches/server/0221-add-more-information-to-Entity.toString.patch
index 6a094a84ad..cd83ffc3c1 100644
--- a/patches/server/0227-add-more-information-to-Entity.toString.patch
+++ b/patches/server/0221-add-more-information-to-Entity.toString.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] add more information to Entity.toString()
UUID, ticks lived, valid, dead
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index be1b7014a745a3f7ce6cb690c494bbb876e40fcc..64261427c5f4abf1a4144eb88a6409560667f70b 100644
+index 65a22c154807c416a63fbf68dad72175b51a52d4..2e988d59404c52cd9c290b96d21f886573cda0fa 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2916,7 +2916,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2968,7 +2968,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
public String toString() {
String s = this.level == null ? "~NULL~" : this.level.toString();
diff --git a/patches/server/0228-Add-CraftMagicNumbers.isSupportedApiVersion.patch b/patches/server/0222-Add-CraftMagicNumbers.isSupportedApiVersion.patch
index c90009e730..c90009e730 100644
--- a/patches/server/0228-Add-CraftMagicNumbers.isSupportedApiVersion.patch
+++ b/patches/server/0222-Add-CraftMagicNumbers.isSupportedApiVersion.patch
diff --git a/patches/server/0229-EnderDragon-Events.patch b/patches/server/0223-EnderDragon-Events.patch
index 6368f8e07f..6368f8e07f 100644
--- a/patches/server/0229-EnderDragon-Events.patch
+++ b/patches/server/0223-EnderDragon-Events.patch
diff --git a/patches/server/0230-PlayerElytraBoostEvent.patch b/patches/server/0224-PlayerElytraBoostEvent.patch
index fde7853fad..fde7853fad 100644
--- a/patches/server/0230-PlayerElytraBoostEvent.patch
+++ b/patches/server/0224-PlayerElytraBoostEvent.patch
diff --git a/patches/server/0231-PlayerLaunchProjectileEvent.patch b/patches/server/0225-PlayerLaunchProjectileEvent.patch
index d732a1386e..d732a1386e 100644
--- a/patches/server/0231-PlayerLaunchProjectileEvent.patch
+++ b/patches/server/0225-PlayerLaunchProjectileEvent.patch
diff --git a/patches/server/0232-Improve-BlockPosition-inlining.patch b/patches/server/0226-Improve-BlockPosition-inlining.patch
index c8ba260207..c8ba260207 100644
--- a/patches/server/0232-Improve-BlockPosition-inlining.patch
+++ b/patches/server/0226-Improve-BlockPosition-inlining.patch
diff --git a/patches/server/0233-Option-to-prevent-armor-stands-from-doing-entity-loo.patch b/patches/server/0227-Option-to-prevent-armor-stands-from-doing-entity-loo.patch
index cdc5540281..24e507e580 100644
--- a/patches/server/0233-Option-to-prevent-armor-stands-from-doing-entity-loo.patch
+++ b/patches/server/0227-Option-to-prevent-armor-stands-from-doing-entity-loo.patch
@@ -17,10 +17,10 @@ index ed698f3e3f9ed6003fe621c5f6f7e3a151a1a559..9897dbb03c343e1e1842f7ca6bc9b99b
for (int i = 0; i < list.size(); ++i) {
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 4022c4d3b2ce935f69460e4de21f1fad7c100b26..056531554bf6e8743111607237d942af13d47848 100644
+index e11e3ee5a11ebed7a1550da14c3c5c54962e4fdf..f6ea7b5929f2affec914730c92d1bde48f218acf 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -735,6 +735,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -740,6 +740,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// Paper end
}
}
diff --git a/patches/server/0234-Vanished-players-don-t-have-rights.patch b/patches/server/0228-Vanished-players-don-t-have-rights.patch
index 07339f4496..51ce5f79dc 100644
--- a/patches/server/0234-Vanished-players-don-t-have-rights.patch
+++ b/patches/server/0228-Vanished-players-don-t-have-rights.patch
@@ -38,7 +38,7 @@ index 0d7c4be0c4abefecab367334408594afa7998b5e..78cac63e5bd7c84f59b8e00ee40899be
BlockCanBuildEvent event = new BlockCanBuildEvent(CraftBlock.at(context.getLevel(), context.getClickedPos()), player, CraftBlockData.fromData(state), defaultReturn);
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 056531554bf6e8743111607237d942af13d47848..13fcd9d885043444d3a2c5a3d9bd1c906b4c4934 100644
+index f6ea7b5929f2affec914730c92d1bde48f218acf..f4249ee3259a95b9f079a75c78ccf96a14b99b0d 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -74,6 +74,10 @@ import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
diff --git a/patches/server/0235-Allow-disabling-armour-stand-ticking.patch b/patches/server/0229-Allow-disabling-armour-stand-ticking.patch
index 8c8a8d1532..8c8a8d1532 100644
--- a/patches/server/0235-Allow-disabling-armour-stand-ticking.patch
+++ b/patches/server/0229-Allow-disabling-armour-stand-ticking.patch
diff --git a/patches/server/0236-SkeletonHorse-Additions.patch b/patches/server/0230-SkeletonHorse-Additions.patch
index 587c1eae7f..3cf3e54089 100644
--- a/patches/server/0236-SkeletonHorse-Additions.patch
+++ b/patches/server/0230-SkeletonHorse-Additions.patch
@@ -32,10 +32,10 @@ index 114352b9e8aeea7c728b6b58047e38e2530401a9..68887c1a475c119d061c8325136c5245
this.horse.setTrap(false);
diff --git a/src/main/java/net/minecraft/world/level/EntityGetter.java b/src/main/java/net/minecraft/world/level/EntityGetter.java
-index cfb286020b8ee87bad7edbda4cd0b999fb607a06..1a3be6f0570c7c746eafa36544debe90d7629432 100644
+index be2ec73b5900354a9c340b0a03affd59dbf55377..c0817ef8927f00e2fd3fbf3289f8041fcb494049 100644
--- a/src/main/java/net/minecraft/world/level/EntityGetter.java
+++ b/src/main/java/net/minecraft/world/level/EntityGetter.java
-@@ -88,6 +88,28 @@ public interface EntityGetter {
+@@ -100,6 +100,28 @@ public interface EntityGetter {
return player;
}
diff --git a/patches/server/0237-Don-t-call-getItemMeta-on-hasItemMeta.patch b/patches/server/0231-Don-t-call-getItemMeta-on-hasItemMeta.patch
index 60a113f059..bb60be7331 100644
--- a/patches/server/0237-Don-t-call-getItemMeta-on-hasItemMeta.patch
+++ b/patches/server/0231-Don-t-call-getItemMeta-on-hasItemMeta.patch
@@ -11,7 +11,7 @@ Returns true if getDamage() == 0 or has damage tag or other tag is set.
Check the `ItemMetaTest#testTaggedButNotMeta` method to see how this method behaves.
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-index 553dd35795b5339980aa7b5ae4e9acd9768846e4..a16d6c171a62299fe4bca28dcbb1243b30268f78 100644
+index ca75f84d2beeef0636fc7032297bf038d13d97e0..f3abcd5949011eaef3d1ba68f4cc0751042d2834 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -588,7 +588,7 @@ public final class CraftItemStack extends ItemStack {
diff --git a/patches/server/0238-Implement-Expanded-ArmorStand-API.patch b/patches/server/0232-Implement-Expanded-ArmorStand-API.patch
index ace889510d..ace889510d 100644
--- a/patches/server/0238-Implement-Expanded-ArmorStand-API.patch
+++ b/patches/server/0232-Implement-Expanded-ArmorStand-API.patch
diff --git a/patches/server/0239-AnvilDamageEvent.patch b/patches/server/0233-AnvilDamageEvent.patch
index c20941bfde..c20941bfde 100644
--- a/patches/server/0239-AnvilDamageEvent.patch
+++ b/patches/server/0233-AnvilDamageEvent.patch
diff --git a/patches/server/0240-Add-hand-to-bucket-events.patch b/patches/server/0234-Add-hand-to-bucket-events.patch
index b514aa8def..b514aa8def 100644
--- a/patches/server/0240-Add-hand-to-bucket-events.patch
+++ b/patches/server/0234-Add-hand-to-bucket-events.patch
diff --git a/patches/server/0241-Add-TNTPrimeEvent.patch b/patches/server/0235-Add-TNTPrimeEvent.patch
index ee73234df5..95c3a89f2f 100644
--- a/patches/server/0241-Add-TNTPrimeEvent.patch
+++ b/patches/server/0235-Add-TNTPrimeEvent.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add TNTPrimeEvent
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
-index d0ebcc23d863be630b55245aa2604c108ee6c93a..3a6e5893181ed681099f2748abca738af45ec9c9 100644
+index 1709126f0853edc6bece6f31d7c65a5f8955683a..6495b0421cab1b067b9483cc448222705c15578c 100644
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -532,6 +532,11 @@ public class EnderDragon extends Mob implements Enemy {
diff --git a/patches/server/0242-Break-up-and-make-tab-spam-limits-configurable.patch b/patches/server/0236-Break-up-and-make-tab-spam-limits-configurable.patch
index c211462524..c211462524 100644
--- a/patches/server/0242-Break-up-and-make-tab-spam-limits-configurable.patch
+++ b/patches/server/0236-Break-up-and-make-tab-spam-limits-configurable.patch
diff --git a/patches/server/0243-MC-135506-Experience-should-save-as-Integers.patch b/patches/server/0237-MC-135506-Experience-should-save-as-Integers.patch
index 8248a48e4a..8248a48e4a 100644
--- a/patches/server/0243-MC-135506-Experience-should-save-as-Integers.patch
+++ b/patches/server/0237-MC-135506-Experience-should-save-as-Integers.patch
diff --git a/patches/server/0244-Remove-unnecessary-itemmeta-handling.patch b/patches/server/0238-Remove-unnecessary-itemmeta-handling.patch
index 88a7a7fd0c..88a7a7fd0c 100644
--- a/patches/server/0244-Remove-unnecessary-itemmeta-handling.patch
+++ b/patches/server/0238-Remove-unnecessary-itemmeta-handling.patch
diff --git a/patches/server/0245-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/patches/server/0239-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
index 07ef9835ab..5183fa7c7a 100644
--- a/patches/server/0245-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
+++ b/patches/server/0239-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
@@ -8,10 +8,10 @@ Add -Ddebug.entities=true to your JVM flags to gain more information
1.17: Needs to be reworked for new entity storage system
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 09a2680162ed9f1d82830778fea6b05a34ab382b..8d0f7fb501aa5caa36b7dd273a8a7c7f959759cb 100644
+index ea9f536efd4c76b421f6e02f93f16fae115840d8..d1af0aca0237ee86acd86fea3255ddeadc3db0d6 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1614,6 +1614,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -886,6 +886,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} else {
ChunkMap.TrackedEntity playerchunkmap_entitytracker = new ChunkMap.TrackedEntity(entity, i, j, entitytypes.trackDeltas());
@@ -19,7 +19,7 @@ index 09a2680162ed9f1d82830778fea6b05a34ab382b..8d0f7fb501aa5caa36b7dd273a8a7c7f
this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
playerchunkmap_entitytracker.updatePlayers(this.level.players());
if (entity instanceof ServerPlayer) {
-@@ -1656,7 +1657,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -928,7 +929,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (playerchunkmap_entitytracker1 != null) {
playerchunkmap_entitytracker1.broadcastRemoved();
}
@@ -29,7 +29,7 @@ index 09a2680162ed9f1d82830778fea6b05a34ab382b..8d0f7fb501aa5caa36b7dd273a8a7c7f
protected void tick() {
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index e13fa5d2ed02b5fe8f9f9d124d15dd5374a7f472..377ebed70c5008d69701bd919f22ad4506dd129c 100644
+index 6ad79090c0c186394f1635d420884a5b34c7c952..f85692e588c022a1f0cc5e3cde001d968eea59c8 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -216,6 +216,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
@@ -42,7 +42,7 @@ index e13fa5d2ed02b5fe8f9f9d124d15dd5374a7f472..377ebed70c5008d69701bd919f22ad45
@Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
return this.chunkSource.getChunk(x, z, false);
-@@ -1197,7 +1200,28 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1232,7 +1235,28 @@ public class ServerLevel extends Level implements WorldGenLevel {
// CraftBukkit start
private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
@@ -72,7 +72,7 @@ index e13fa5d2ed02b5fe8f9f9d124d15dd5374a7f472..377ebed70c5008d69701bd919f22ad45
return false;
} else {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 64261427c5f4abf1a4144eb88a6409560667f70b..0282b575d4ff68a306053f86b47908dd44dc54ed 100644
+index 2e988d59404c52cd9c290b96d21f886573cda0fa..0e632f9eabd0d55a4eaacff54abaf637660528dd 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -235,6 +235,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -85,7 +85,7 @@ index 64261427c5f4abf1a4144eb88a6409560667f70b..0282b575d4ff68a306053f86b47908dd
if (this.bukkitEntity == null) {
this.bukkitEntity = CraftEntity.getEntity(this.level.getCraftServer(), this);
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 13fcd9d885043444d3a2c5a3d9bd1c906b4c4934..2225921e988cdcaa4fb906b281c237972c6e7c1b 100644
+index f4249ee3259a95b9f079a75c78ccf96a14b99b0d..9ed8117afa4eb1ff9e7403d3ab92a9e6cb0fab73 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -148,6 +148,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0246-Add-Early-Warning-Feature-to-WatchDog.patch b/patches/server/0240-Add-Early-Warning-Feature-to-WatchDog.patch
index 7cac64f958..f58b929289 100644
--- a/patches/server/0246-Add-Early-Warning-Feature-to-WatchDog.patch
+++ b/patches/server/0240-Add-Early-Warning-Feature-to-WatchDog.patch
@@ -9,10 +9,10 @@ thread dumps at an interval until the point of crash.
This will help diagnose what was going on in that time before the crash.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 401b883109f0ab7b30dcdc5c0ea28fc7aa3a0822..802b7767dd0878cf6d2e52bea74d5664f7d0664f 100644
+index 65b9dfa62a7785b4889d8edac0000487f3fc5367..d79ac1e7a1301e3e5d4e6caecc95bf320e5203e5 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1061,6 +1061,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1044,6 +1044,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.updateStatusIcon(this.status);
// Spigot start
@@ -21,7 +21,7 @@ index 401b883109f0ab7b30dcdc5c0ea28fc7aa3a0822..802b7767dd0878cf6d2e52bea74d5664
long start = System.nanoTime(), curTime, tickSection = start; // Paper - Further improve server tick loop
lastTick = start - TICK_TIME; // Paper
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 9411e5664c0067f976018fe19b1e74032f24bd5f..1e7bccbf551043d822edb1983fa039a4fdb27602 100644
+index ed3662f2d237fc6a11cb3f6a2e476dc014ba4586..0944d339c76b09f183b3a1e191955300fc24cd97 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -202,6 +202,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -33,7 +33,7 @@ index 9411e5664c0067f976018fe19b1e74032f24bd5f..1e7bccbf551043d822edb1983fa039a4
com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics();
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 94e0134dcf644ed8f2835ddceb2f1e09c1d8c6a4..11d7f2d41da52cd0fdb96310153cd6d5547a6b8f 100644
+index a60b93a15d5cf80745eb114b393465ff1c53f444..cf9f10a61f87130a4a58c09edce7ef14fbc6ae30 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -911,6 +911,7 @@ public final class CraftServer implements Server {
@@ -66,10 +66,10 @@ index 645643d102118ad4916a152abfb9ab0d02751e11..edc5f195cc3de8885b839469656650ba
public static boolean bungee;
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
-index bd515ddc88f71a31531418c43725e438de100fcd..e62f085de1678568d422ef0eda5b9bfbd8b4d556 100644
+index a9897c494b3dc56d900356d74030359832febbaa..b47d043144c499b1499f6b4be5a16a3f75c9fcb8 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
-@@ -14,6 +14,10 @@ public class WatchdogThread extends Thread
+@@ -14,6 +14,10 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
private static WatchdogThread instance;
private long timeoutTime;
private boolean restart;
@@ -80,7 +80,7 @@ index bd515ddc88f71a31531418c43725e438de100fcd..e62f085de1678568d422ef0eda5b9bfb
private volatile long lastTick;
private volatile boolean stopping;
-@@ -22,6 +26,8 @@ public class WatchdogThread extends Thread
+@@ -22,6 +26,8 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
super( "Paper Watchdog Thread" );
this.timeoutTime = timeoutTime;
this.restart = restart;
@@ -89,7 +89,7 @@ index bd515ddc88f71a31531418c43725e438de100fcd..e62f085de1678568d422ef0eda5b9bfb
}
private static long monotonicMillis()
-@@ -61,9 +67,18 @@ public class WatchdogThread extends Thread
+@@ -61,9 +67,18 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
while ( !this.stopping )
{
//
@@ -110,7 +110,7 @@ index bd515ddc88f71a31531418c43725e438de100fcd..e62f085de1678568d422ef0eda5b9bfb
log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug." ); // Paper
log.log( Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author" );
-@@ -93,30 +108,46 @@ public class WatchdogThread extends Thread
+@@ -93,30 +108,46 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
}
}
// Paper end
@@ -122,7 +122,7 @@ index bd515ddc88f71a31531418c43725e438de100fcd..e62f085de1678568d422ef0eda5b9bfb
+ // Paper end - Different message for short timeout
log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
- com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
log.log( Level.SEVERE, "------------------------------" );
//
diff --git a/patches/server/0247-Use-ConcurrentHashMap-in-JsonList.patch b/patches/server/0241-Use-ConcurrentHashMap-in-JsonList.patch
index 792b86d193..af29490ae2 100644
--- a/patches/server/0247-Use-ConcurrentHashMap-in-JsonList.patch
+++ b/patches/server/0241-Use-ConcurrentHashMap-in-JsonList.patch
@@ -23,7 +23,7 @@ Modified isEmpty to use the isEmpty() method instead of the slightly confusing s
The point of this is readability, but does have a side-benefit of a small microptimization
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 0629b2df97119e242d10e41a707ba47894ce6d69..c974120dda3bac8a4a74c4b4d8ddcfb802f792dc 100644
+index a99d6938d912a169ac329ba09f5a6becd072a94e..ffa3f9d147a0113d7b8cbf185ca751ed159576c2 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -624,7 +624,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0248-Use-a-Queue-for-Queueing-Commands.patch b/patches/server/0242-Use-a-Queue-for-Queueing-Commands.patch
index 3bb60868e3..a36e37c417 100644
--- a/patches/server/0248-Use-a-Queue-for-Queueing-Commands.patch
+++ b/patches/server/0242-Use-a-Queue-for-Queueing-Commands.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Use a Queue for Queueing Commands
Lists are bad as Queues mmmkay.
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 1e7bccbf551043d822edb1983fa039a4fdb27602..115685012743e775ca3fa031e8a91b6cd2874236 100644
+index 0944d339c76b09f183b3a1e191955300fc24cd97..b23905e5d2d1e8300c710df2aeb370fd4af8ac76 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -74,7 +74,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -18,12 +18,12 @@ index 1e7bccbf551043d822edb1983fa039a4fdb27602..115685012743e775ca3fa031e8a91b6c
@Nullable
private QueryThreadGs4 queryThreadGs4;
public final RconConsoleSource rconConsoleSource;
-@@ -419,13 +419,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
- }
-
- public void handleConsoleInput(String command, CommandSourceStack commandSource) {
+@@ -446,13 +446,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+ return;
+ }
+ // Paper end - rewrite chunk system
- this.consoleInput.add(new ConsoleInput(command, commandSource));
-+ this.serverCommandQueue.add(new ConsoleInput(command, commandSource));
++ this.serverCommandQueue.add(new ConsoleInput(command, commandSource)); // Paper - use proper queue
}
public void handleConsoleInputs() {
diff --git a/patches/server/0249-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch b/patches/server/0243-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch
index be3987951f..005af7afb2 100644
--- a/patches/server/0249-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch
+++ b/patches/server/0243-Ability-to-get-Tile-Entities-from-a-chunk-without-sn.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Ability to get Tile Entities from a chunk without snapshots
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-index bda9c6720fe0a91a4c6763795e4d6e01e6dd2747..dfdcc01c7cb2864d9b5f572c0cafedf16063edd8 100644
+index fd50cc4cfc976a9dee82d4f6ad457bba18065614..1827fdc551095ab411d6b43b94106273f53386c8 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
@@ -4,8 +4,10 @@ import com.google.common.base.Preconditions;
@@ -19,7 +19,7 @@ index bda9c6720fe0a91a4c6763795e4d6e01e6dd2747..dfdcc01c7cb2864d9b5f572c0cafedf1
import java.util.Objects;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BooleanSupplier;
-@@ -177,6 +179,13 @@ public class CraftChunk implements Chunk {
+@@ -133,6 +135,13 @@ public class CraftChunk implements Chunk {
@Override
public BlockState[] getTileEntities() {
@@ -33,7 +33,7 @@ index bda9c6720fe0a91a4c6763795e4d6e01e6dd2747..dfdcc01c7cb2864d9b5f572c0cafedf1
if (!this.isLoaded()) {
this.getWorld().getChunkAt(x, z); // Transient load for this tick
}
-@@ -191,7 +200,29 @@ public class CraftChunk implements Chunk {
+@@ -147,7 +156,29 @@ public class CraftChunk implements Chunk {
}
BlockPos position = (BlockPos) obj;
diff --git a/patches/server/0250-Optimize-BlockPosition-helper-methods.patch b/patches/server/0244-Optimize-BlockPosition-helper-methods.patch
index 3fe5135a6b..3fe5135a6b 100644
--- a/patches/server/0250-Optimize-BlockPosition-helper-methods.patch
+++ b/patches/server/0244-Optimize-BlockPosition-helper-methods.patch
diff --git a/patches/server/0251-Restore-vanilla-default-mob-spawn-range-and-water-an.patch b/patches/server/0245-Restore-vanilla-default-mob-spawn-range-and-water-an.patch
index e10d949eea..e10d949eea 100644
--- a/patches/server/0251-Restore-vanilla-default-mob-spawn-range-and-water-an.patch
+++ b/patches/server/0245-Restore-vanilla-default-mob-spawn-range-and-water-an.patch
diff --git a/patches/server/0252-Slime-Pathfinder-Events.patch b/patches/server/0246-Slime-Pathfinder-Events.patch
index e6984e42ea..e6984e42ea 100644
--- a/patches/server/0252-Slime-Pathfinder-Events.patch
+++ b/patches/server/0246-Slime-Pathfinder-Events.patch
diff --git a/patches/server/0253-Configurable-speed-for-water-flowing-over-lava.patch b/patches/server/0247-Configurable-speed-for-water-flowing-over-lava.patch
index 05e4b14d22..05e4b14d22 100644
--- a/patches/server/0253-Configurable-speed-for-water-flowing-over-lava.patch
+++ b/patches/server/0247-Configurable-speed-for-water-flowing-over-lava.patch
diff --git a/patches/server/0254-Optimize-CraftBlockData-Creation.patch b/patches/server/0248-Optimize-CraftBlockData-Creation.patch
index 19a0baf0b7..f069170285 100644
--- a/patches/server/0254-Optimize-CraftBlockData-Creation.patch
+++ b/patches/server/0248-Optimize-CraftBlockData-Creation.patch
@@ -7,12 +7,12 @@ Avoids a hashmap lookup by cacheing a reference to the CraftBlockData
and cloning it when one is needed.
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-index f0bd06ab32e99c188510b3c3fa41f1737ab4fe78..3ec96c7f2ad0d6ba8ad32a4aabb0292b5c2ff5b4 100644
+index 51ac731cf49e6d2cd574e48f26c4b151e9014826..61a792c5ccf3688d4f78c6e7461090f63a0cc26a 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-@@ -695,6 +695,14 @@ public abstract class BlockBehaviour {
- this.emissiveRendering = blockbase_info.emissiveRendering;
+@@ -696,6 +696,14 @@ public abstract class BlockBehaviour {
this.offsetType = (BlockBehaviour.OffsetType) blockbase_info.offsetType.apply(this.asState());
+ this.conditionallyFullOpaque = this.isOpaque() & this.isTransparentOnSomeFaces(); // Paper
}
+ // Paper start - impl cached craft block data, lazy load to fix issue with loading at the wrong time
+ private org.bukkit.craftbukkit.block.data.CraftBlockData cachedCraftBlockData;
diff --git a/patches/server/0255-Optimize-MappedRegistry.patch b/patches/server/0249-Optimize-MappedRegistry.patch
index 70684ff905..70684ff905 100644
--- a/patches/server/0255-Optimize-MappedRegistry.patch
+++ b/patches/server/0249-Optimize-MappedRegistry.patch
diff --git a/patches/server/0256-Add-PhantomPreSpawnEvent.patch b/patches/server/0250-Add-PhantomPreSpawnEvent.patch
index f4862ce10d..f4862ce10d 100644
--- a/patches/server/0256-Add-PhantomPreSpawnEvent.patch
+++ b/patches/server/0250-Add-PhantomPreSpawnEvent.patch
diff --git a/patches/server/0257-Add-More-Creeper-API.patch b/patches/server/0251-Add-More-Creeper-API.patch
index 701306ca69..701306ca69 100644
--- a/patches/server/0257-Add-More-Creeper-API.patch
+++ b/patches/server/0251-Add-More-Creeper-API.patch
diff --git a/patches/server/0258-Inventory-removeItemAnySlot.patch b/patches/server/0252-Inventory-removeItemAnySlot.patch
index 533e72fb45..533e72fb45 100644
--- a/patches/server/0258-Inventory-removeItemAnySlot.patch
+++ b/patches/server/0252-Inventory-removeItemAnySlot.patch
diff --git a/patches/server/0259-Make-CraftWorld-loadChunk-int-int-false-load-unconve.patch b/patches/server/0253-Make-CraftWorld-loadChunk-int-int-false-load-unconve.patch
index de9fb155cb..2761673c5a 100644
--- a/patches/server/0259-Make-CraftWorld-loadChunk-int-int-false-load-unconve.patch
+++ b/patches/server/0253-Make-CraftWorld-loadChunk-int-int-false-load-unconve.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Make CraftWorld#loadChunk(int, int, false) load unconverted
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index ff842f952d744a13eb563407edc21d03d43ddd66..ef0bff86d3f5f0c404f66b3e2e0a4976006909ee 100644
+index 9849c255ed68309da27ba2c9156c50e89c29d498..b067ac26973ac487cc3386ecda2bfd8def2d8bbf 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -389,7 +389,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -392,7 +392,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean loadChunk(int x, int z, boolean generate) {
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
diff --git a/patches/server/0260-Add-ray-tracing-methods-to-LivingEntity.patch b/patches/server/0254-Add-ray-tracing-methods-to-LivingEntity.patch
index 7f12ab44a9..08c18ed736 100644
--- a/patches/server/0260-Add-ray-tracing-methods-to-LivingEntity.patch
+++ b/patches/server/0254-Add-ray-tracing-methods-to-LivingEntity.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add ray tracing methods to LivingEntity
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
-index 3e8867c317f7018780f44b62d0bd40fc9fa9ce9f..40d033e8b7c29269a5e194f80c8bccc67836e28d 100644
+index e98276943e1690572b8f7bc54a259aa8340bae41..c9b43d077727c22a9eca738e9a75e7f1a6a5a9ee 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
-@@ -508,6 +508,18 @@ public final class MCUtil {
+@@ -509,6 +509,18 @@ public final class MCUtil {
return getNMSWorld(entity.getWorld());
}
diff --git a/patches/server/0261-Expose-attack-cooldown-methods-for-Player.patch b/patches/server/0255-Expose-attack-cooldown-methods-for-Player.patch
index bca960a131..1e3495ddf8 100644
--- a/patches/server/0261-Expose-attack-cooldown-methods-for-Player.patch
+++ b/patches/server/0255-Expose-attack-cooldown-methods-for-Player.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Expose attack cooldown methods for Player
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 63575545d197c4172b1df04090d904b4358f6b5c..15967a7162ceca57a0549f0b3643a63fe03a91f3 100644
+index eb6ead27924cdad9362d350e6257c5995e17b005..04b329bf2a71f600debfbac193c0d1f49afff692 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -2554,6 +2554,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2589,6 +2589,21 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
return this.adventure$pointers;
}
diff --git a/patches/server/0262-Improve-death-events.patch b/patches/server/0256-Improve-death-events.patch
index 5c2e261604..28accbc187 100644
--- a/patches/server/0262-Improve-death-events.patch
+++ b/patches/server/0256-Improve-death-events.patch
@@ -19,10 +19,10 @@ maybe more (please check patch overrides for drops for more):
- players, armor stands, foxes, chested donkeys/llamas
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 4b9af6ef008a297438bfc583025d235d07d9b780..6d95d572092ad50ffa92c2e1731fd59c6dfd7a44 100644
+index 1a3f9d09df6b6d8c6a84f7f62e142f341d74c6c1..645012ea3590a46773e2bb14e9cafd9e3c695fd4 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -231,6 +231,10 @@ public class ServerPlayer extends Player {
+@@ -230,6 +230,10 @@ public class ServerPlayer extends Player {
public int latency;
public boolean wonGame;
private int containerUpdateDelay; // Paper
@@ -33,7 +33,7 @@ index 4b9af6ef008a297438bfc583025d235d07d9b780..6d95d572092ad50ffa92c2e1731fd59c
// CraftBukkit start
public String displayName;
-@@ -816,6 +820,15 @@ public class ServerPlayer extends Player {
+@@ -801,6 +805,15 @@ public class ServerPlayer extends Player {
String deathmessage = defaultMessage.getString();
this.keepLevel = keepInventory; // SPIGOT-2222: pre-set keepLevel
org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, PaperAdventure.asAdventure(defaultMessage), defaultMessage.getString(), keepInventory); // Paper - Adventure
@@ -49,7 +49,7 @@ index 4b9af6ef008a297438bfc583025d235d07d9b780..6d95d572092ad50ffa92c2e1731fd59c
// SPIGOT-943 - only call if they have an inventory open
if (this.containerMenu != this.inventoryMenu) {
-@@ -961,8 +974,17 @@ public class ServerPlayer extends Player {
+@@ -946,8 +959,17 @@ public class ServerPlayer extends Player {
}
}
}
@@ -310,10 +310,10 @@ index f94a74728bd7c02a7f8245c92e7916f0b669ee0d..cd54fa8f7bbcb6036e90f4ef7cdc01d7
this.gameEvent(GameEvent.ENTITY_DIE);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 15967a7162ceca57a0549f0b3643a63fe03a91f3..e950db8a10668c5d7f4d462eaf91540df2159538 100644
+index 04b329bf2a71f600debfbac193c0d1f49afff692..8676067be5cbb149a6a50a9582a22b69ebcfb61b 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -2118,7 +2118,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2153,7 +2153,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
public void sendHealthUpdate() {
diff --git a/patches/server/0263-Allow-chests-to-be-placed-with-NBT-data.patch b/patches/server/0257-Allow-chests-to-be-placed-with-NBT-data.patch
index e03ccbcd5a..e03ccbcd5a 100644
--- a/patches/server/0263-Allow-chests-to-be-placed-with-NBT-data.patch
+++ b/patches/server/0257-Allow-chests-to-be-placed-with-NBT-data.patch
diff --git a/patches/server/0264-Mob-Pathfinding-API.patch b/patches/server/0258-Mob-Pathfinding-API.patch
index 517909c69b..517909c69b 100644
--- a/patches/server/0264-Mob-Pathfinding-API.patch
+++ b/patches/server/0258-Mob-Pathfinding-API.patch
diff --git a/patches/server/0265-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch b/patches/server/0259-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch
index 4ae690f9d8..4ae690f9d8 100644
--- a/patches/server/0265-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch
+++ b/patches/server/0259-Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch
diff --git a/patches/server/0266-Prevent-chunk-loading-from-Fluid-Flowing.patch b/patches/server/0260-Prevent-chunk-loading-from-Fluid-Flowing.patch
index 8e59eff59d..8e59eff59d 100644
--- a/patches/server/0266-Prevent-chunk-loading-from-Fluid-Flowing.patch
+++ b/patches/server/0260-Prevent-chunk-loading-from-Fluid-Flowing.patch
diff --git a/patches/server/0267-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch b/patches/server/0261-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch
index 12a3938c2d..12a3938c2d 100644
--- a/patches/server/0267-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch
+++ b/patches/server/0261-Prevent-Mob-AI-Rules-from-Loading-Chunks.patch
diff --git a/patches/server/0268-Prevent-mob-spawning-from-loading-generating-chunks.patch b/patches/server/0262-Prevent-mob-spawning-from-loading-generating-chunks.patch
index 7c943a7345..7c943a7345 100644
--- a/patches/server/0268-Prevent-mob-spawning-from-loading-generating-chunks.patch
+++ b/patches/server/0262-Prevent-mob-spawning-from-loading-generating-chunks.patch
diff --git a/patches/server/0269-Implement-furnace-cook-speed-multiplier-API.patch b/patches/server/0263-Implement-furnace-cook-speed-multiplier-API.patch
index ef1f41e68e..ef1f41e68e 100644
--- a/patches/server/0269-Implement-furnace-cook-speed-multiplier-API.patch
+++ b/patches/server/0263-Implement-furnace-cook-speed-multiplier-API.patch
diff --git a/patches/server/0270-Catch-JsonParseException-in-Entity-and-TE-names.patch b/patches/server/0264-Catch-JsonParseException-in-Entity-and-TE-names.patch
index 6b2d2dcdad..d823af9154 100644
--- a/patches/server/0270-Catch-JsonParseException-in-Entity-and-TE-names.patch
+++ b/patches/server/0264-Catch-JsonParseException-in-Entity-and-TE-names.patch
@@ -13,11 +13,11 @@ Shulkers) may need to be changed in order for it to re-save properly
No more crashing though.
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
-index 40d033e8b7c29269a5e194f80c8bccc67836e28d..b575d73ae0ff2e4f09a6a1f6fb061ca3da2cedf1 100644
+index c9b43d077727c22a9eca738e9a75e7f1a6a5a9ee..99f56658c70f99592fb40c9df0ce3e47053d1bd5 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
-@@ -12,6 +12,8 @@ import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet;
- import java.lang.ref.Cleaner;
+@@ -13,6 +13,8 @@ import java.lang.ref.Cleaner;
+ import it.unimi.dsi.fastutil.objects.ReferenceArrayList;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
+import net.minecraft.nbt.CompoundTag;
@@ -25,7 +25,7 @@ index 40d033e8b7c29269a5e194f80c8bccc67836e28d..b575d73ae0ff2e4f09a6a1f6fb061ca3
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.DistanceManager;
-@@ -539,6 +541,21 @@ public final class MCUtil {
+@@ -540,6 +542,21 @@ public final class MCUtil {
}
}
diff --git a/patches/server/0271-Honor-EntityAgeable.ageLock.patch b/patches/server/0265-Honor-EntityAgeable.ageLock.patch
index 3c2db99373..3c2db99373 100644
--- a/patches/server/0271-Honor-EntityAgeable.ageLock.patch
+++ b/patches/server/0265-Honor-EntityAgeable.ageLock.patch
diff --git a/patches/server/0272-Configurable-connection-throttle-kick-message.patch b/patches/server/0266-Configurable-connection-throttle-kick-message.patch
index 388accb3aa..388accb3aa 100644
--- a/patches/server/0272-Configurable-connection-throttle-kick-message.patch
+++ b/patches/server/0266-Configurable-connection-throttle-kick-message.patch
diff --git a/patches/server/0273-Hook-into-CB-plugin-rewrites.patch b/patches/server/0267-Hook-into-CB-plugin-rewrites.patch
index a3aff98067..a3aff98067 100644
--- a/patches/server/0273-Hook-into-CB-plugin-rewrites.patch
+++ b/patches/server/0267-Hook-into-CB-plugin-rewrites.patch
diff --git a/patches/server/0274-PreSpawnerSpawnEvent.patch b/patches/server/0268-PreSpawnerSpawnEvent.patch
index ad93f9fbae..ad93f9fbae 100644
--- a/patches/server/0274-PreSpawnerSpawnEvent.patch
+++ b/patches/server/0268-PreSpawnerSpawnEvent.patch
diff --git a/patches/server/0275-Add-LivingEntity-getTargetEntity.patch b/patches/server/0269-Add-LivingEntity-getTargetEntity.patch
index 938905a7ca..938905a7ca 100644
--- a/patches/server/0275-Add-LivingEntity-getTargetEntity.patch
+++ b/patches/server/0269-Add-LivingEntity-getTargetEntity.patch
diff --git a/patches/server/0276-Add-sun-related-API.patch b/patches/server/0270-Add-sun-related-API.patch
index 8c8e84ecc1..1d2375d79d 100644
--- a/patches/server/0276-Add-sun-related-API.patch
+++ b/patches/server/0270-Add-sun-related-API.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add sun related API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index ef0bff86d3f5f0c404f66b3e2e0a4976006909ee..3aa2e80e7d30d8824fd7f009282adfd8712cbb55 100644
+index b067ac26973ac487cc3386ecda2bfd8def2d8bbf..0ca653a0b0059116eaa943207bf04b5bfbe77e8f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -689,6 +689,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -679,6 +679,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
}
diff --git a/patches/server/0277-Turtle-API.patch b/patches/server/0271-Turtle-API.patch
index 1bc6544713..1bc6544713 100644
--- a/patches/server/0277-Turtle-API.patch
+++ b/patches/server/0271-Turtle-API.patch
diff --git a/patches/server/0278-Call-player-spectator-target-events-and-improve-impl.patch b/patches/server/0272-Call-player-spectator-target-events-and-improve-impl.patch
index 19b55a59dc..f5bd1e340c 100644
--- a/patches/server/0278-Call-player-spectator-target-events-and-improve-impl.patch
+++ b/patches/server/0272-Call-player-spectator-target-events-and-improve-impl.patch
@@ -19,10 +19,10 @@ spectate the target entity.
Co-authored-by: Spottedleaf <[email protected]>
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 6d95d572092ad50ffa92c2e1731fd59c6dfd7a44..f48866aff156eb1fb5e673b93becada744a04bc5 100644
+index 645012ea3590a46773e2bb14e9cafd9e3c695fd4..cb55e38b22330dbc542003841e8140eb6bee01bf 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1891,14 +1891,58 @@ public class ServerPlayer extends Player {
+@@ -1876,14 +1876,58 @@ public class ServerPlayer extends Player {
}
public void setCamera(@Nullable Entity entity) {
diff --git a/patches/server/0279-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch b/patches/server/0273-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch
index b766300549..b766300549 100644
--- a/patches/server/0279-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch
+++ b/patches/server/0273-MC-50319-Check-other-worlds-for-shooter-of-projectil.patch
diff --git a/patches/server/0280-Add-more-Witch-API.patch b/patches/server/0274-Add-more-Witch-API.patch
index 4fdcae61a9..4fdcae61a9 100644
--- a/patches/server/0280-Add-more-Witch-API.patch
+++ b/patches/server/0274-Add-more-Witch-API.patch
diff --git a/patches/server/0281-Check-Drowned-for-Villager-Aggression-Config.patch b/patches/server/0275-Check-Drowned-for-Villager-Aggression-Config.patch
index ec62c27d6f..ec62c27d6f 100644
--- a/patches/server/0281-Check-Drowned-for-Villager-Aggression-Config.patch
+++ b/patches/server/0275-Check-Drowned-for-Villager-Aggression-Config.patch
diff --git a/patches/server/0282-Add-option-to-prevent-players-from-moving-into-unloa.patch b/patches/server/0276-Add-option-to-prevent-players-from-moving-into-unloa.patch
index bbb6841159..bbb6841159 100644
--- a/patches/server/0282-Add-option-to-prevent-players-from-moving-into-unloa.patch
+++ b/patches/server/0276-Add-option-to-prevent-players-from-moving-into-unloa.patch
diff --git a/patches/server/0283-Reset-players-airTicks-on-respawn.patch b/patches/server/0277-Reset-players-airTicks-on-respawn.patch
index 699c6e51a7..9eb908fdd0 100644
--- a/patches/server/0283-Reset-players-airTicks-on-respawn.patch
+++ b/patches/server/0277-Reset-players-airTicks-on-respawn.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Reset players airTicks on respawn
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index f48866aff156eb1fb5e673b93becada744a04bc5..4c35529c7ed67c2432ac67e7d8ffe295892757ff 100644
+index cb55e38b22330dbc542003841e8140eb6bee01bf..32aba4c822742d6a2ebbc25b169ffb665e9413f7 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -2304,6 +2304,7 @@ public class ServerPlayer extends Player {
+@@ -2289,6 +2289,7 @@ public class ServerPlayer extends Player {
this.setHealth(this.getMaxHealth());
this.stopUsingItem(); // CraftBukkit - SPIGOT-6682: Clear active item on reset
diff --git a/patches/server/0284-Don-t-sleep-after-profile-lookups-if-not-needed.patch b/patches/server/0278-Don-t-sleep-after-profile-lookups-if-not-needed.patch
index c4f99ad87b..c4f99ad87b 100644
--- a/patches/server/0284-Don-t-sleep-after-profile-lookups-if-not-needed.patch
+++ b/patches/server/0278-Don-t-sleep-after-profile-lookups-if-not-needed.patch
diff --git a/patches/server/0285-Improve-Server-Thread-Pool-and-Thread-Priorities.patch b/patches/server/0279-Improve-Server-Thread-Pool-and-Thread-Priorities.patch
index 6d5902676e..40ed8205d2 100644
--- a/patches/server/0285-Improve-Server-Thread-Pool-and-Thread-Priorities.patch
+++ b/patches/server/0279-Improve-Server-Thread-Pool-and-Thread-Priorities.patch
@@ -58,7 +58,7 @@ index 336a26733b5bf73455f8ec10347c1e08b8e866f7..4fce18c52c8144460ebf0c1e336dce71
return executorService;
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 802b7767dd0878cf6d2e52bea74d5664f7d0664f..df9a67733c757ab128c11d73d001dcb8624d0283 100644
+index d79ac1e7a1301e3e5d4e6caecc95bf320e5203e5..2860bdf9d155b694fe605886a9f99cd956559f3c 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -306,6 +306,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0286-Optimize-World-Time-Updates.patch b/patches/server/0280-Optimize-World-Time-Updates.patch
index 036816fc70..0f02b1a821 100644
--- a/patches/server/0286-Optimize-World-Time-Updates.patch
+++ b/patches/server/0280-Optimize-World-Time-Updates.patch
@@ -8,10 +8,10 @@ the updates per world, so that we can re-use the same packet
object for every player unless they have per-player time enabled.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index df9a67733c757ab128c11d73d001dcb8624d0283..c5a118b1aab0654024a55ce5eae86aff8346c3d6 100644
+index 5e1a0ab40d9d03844c6e0b962bb15d3b4b40d229..44b70cef867093979e5481bee4d60676bdca6d47 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1378,12 +1378,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1372,12 +1372,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot // Paper
// Send time updates to everyone, it will get the right time from the world the player is in.
diff --git a/patches/server/0287-Restore-custom-InventoryHolder-support.patch b/patches/server/0281-Restore-custom-InventoryHolder-support.patch
index b6b1612284..b6b1612284 100644
--- a/patches/server/0287-Restore-custom-InventoryHolder-support.patch
+++ b/patches/server/0281-Restore-custom-InventoryHolder-support.patch
diff --git a/patches/server/0288-Use-Vanilla-Minecart-Speeds.patch b/patches/server/0282-Use-Vanilla-Minecart-Speeds.patch
index 9fde4a8e53..9fde4a8e53 100644
--- a/patches/server/0288-Use-Vanilla-Minecart-Speeds.patch
+++ b/patches/server/0282-Use-Vanilla-Minecart-Speeds.patch
diff --git a/patches/server/0289-Fix-SpongeAbsortEvent-handling.patch b/patches/server/0283-Fix-SpongeAbsortEvent-handling.patch
index a527364310..a527364310 100644
--- a/patches/server/0289-Fix-SpongeAbsortEvent-handling.patch
+++ b/patches/server/0283-Fix-SpongeAbsortEvent-handling.patch
diff --git a/patches/server/0290-Don-t-allow-digging-into-unloaded-chunks.patch b/patches/server/0284-Don-t-allow-digging-into-unloaded-chunks.patch
index c43ce48361..c43ce48361 100644
--- a/patches/server/0290-Don-t-allow-digging-into-unloaded-chunks.patch
+++ b/patches/server/0284-Don-t-allow-digging-into-unloaded-chunks.patch
diff --git a/patches/server/0291-Make-the-default-permission-message-configurable.patch b/patches/server/0285-Make-the-default-permission-message-configurable.patch
index 7997cc7347..fd099d736c 100644
--- a/patches/server/0291-Make-the-default-permission-message-configurable.patch
+++ b/patches/server/0285-Make-the-default-permission-message-configurable.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Make the default permission message configurable
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
-index 8e773f522521d2dd6349c87b582a3337b76f161f..395c43f6440c1e0e47919eef096ea8a8d552ccec 100644
+index 1e9105cf5ab2ff0ee847fafd00b41e1bd47f1d9e..65ee888280f917ccd11146505b7389513280a863 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
-@@ -76,7 +76,7 @@ public final class PaperCommand extends Command {
+@@ -78,7 +78,7 @@ public final class PaperCommand extends Command {
if (sender.hasPermission(BASE_PERM + permission) || sender.hasPermission("bukkit.command.paper")) {
return true;
}
@@ -18,7 +18,7 @@ index 8e773f522521d2dd6349c87b582a3337b76f161f..395c43f6440c1e0e47919eef096ea8a8
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 11d7f2d41da52cd0fdb96310153cd6d5547a6b8f..fec355dfc7e6353759276f82e6677fd9607e6e7c 100644
+index cf9f10a61f87130a4a58c09edce7ef14fbc6ae30..dd7eebe8397764206e07809293f1b8ca4e8f205c 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2622,6 +2622,16 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0292-Prevent-rayTrace-from-loading-chunks.patch b/patches/server/0286-Prevent-rayTrace-from-loading-chunks.patch
index dd9d69d953..dd9d69d953 100644
--- a/patches/server/0292-Prevent-rayTrace-from-loading-chunks.patch
+++ b/patches/server/0286-Prevent-rayTrace-from-loading-chunks.patch
diff --git a/patches/server/0293-Handle-Large-Packets-disconnecting-client.patch b/patches/server/0287-Handle-Large-Packets-disconnecting-client.patch
index 49ba10fe12..9dfbdc7448 100644
--- a/patches/server/0293-Handle-Large-Packets-disconnecting-client.patch
+++ b/patches/server/0287-Handle-Large-Packets-disconnecting-client.patch
@@ -7,10 +7,10 @@ If a players inventory is too big to send in a single packet,
split the inventory set into multiple packets instead.
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 2643c3d99c11bc6783386502c7e21293b6dfa345..07fd527dd5b72ecc66311c1b81e578158e12a35c 100644
+index 7d85817a0a041c2fa257396d1508488bfc7da55b..870badca869aca1ad293542b345a038ddf715135 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -126,6 +126,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -148,6 +148,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
diff --git a/patches/server/0294-force-entity-dismount-during-teleportation.patch b/patches/server/0288-force-entity-dismount-during-teleportation.patch
index c6c4c9f4f7..60fb79c846 100644
--- a/patches/server/0294-force-entity-dismount-during-teleportation.patch
+++ b/patches/server/0288-force-entity-dismount-during-teleportation.patch
@@ -20,10 +20,10 @@ this is going to be the best soultion all around.
Improvements/suggestions welcome!
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 4c35529c7ed67c2432ac67e7d8ffe295892757ff..f91da5bc234a8f1c120261823a1a4e4216513329 100644
+index 32aba4c822742d6a2ebbc25b169ffb665e9413f7..135e61ffa69a56e7c5edcd9e2066334abf963340 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1351,11 +1351,13 @@ public class ServerPlayer extends Player {
+@@ -1336,11 +1336,13 @@ public class ServerPlayer extends Player {
}
}
@@ -41,10 +41,10 @@ index 4c35529c7ed67c2432ac67e7d8ffe295892757ff..f91da5bc234a8f1c120261823a1a4e42
if (entity1 != entity && this.connection != null) {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 0282b575d4ff68a306053f86b47908dd44dc54ed..424eed2daa3a9574cf1179a7f970f1565ea5fc71 100644
+index 0e632f9eabd0d55a4eaacff54abaf637660528dd..e6fa82b942ec176288aaca080e3d9c8df6a16ac0 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2329,11 +2329,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2381,11 +2381,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public void removeVehicle() {
@@ -62,7 +62,7 @@ index 0282b575d4ff68a306053f86b47908dd44dc54ed..424eed2daa3a9574cf1179a7f970f156
}
}
-@@ -2396,7 +2401,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2448,7 +2453,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
return true; // CraftBukkit
}
@@ -74,7 +74,7 @@ index 0282b575d4ff68a306053f86b47908dd44dc54ed..424eed2daa3a9574cf1179a7f970f156
if (entity.getVehicle() == this) {
throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
} else {
-@@ -2406,7 +2414,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2458,7 +2466,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (this.getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
VehicleExitEvent event = new VehicleExitEvent(
(Vehicle) this.getBukkitEntity(),
@@ -83,7 +83,7 @@ index 0282b575d4ff68a306053f86b47908dd44dc54ed..424eed2daa3a9574cf1179a7f970f156
);
// Suppress during worldgen
if (this.valid) {
-@@ -2420,7 +2428,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2472,7 +2480,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
// CraftBukkit end
// Spigot start
diff --git a/patches/server/0295-Add-more-Zombie-API.patch b/patches/server/0289-Add-more-Zombie-API.patch
index d4683e84c2..d4683e84c2 100644
--- a/patches/server/0295-Add-more-Zombie-API.patch
+++ b/patches/server/0289-Add-more-Zombie-API.patch
diff --git a/patches/server/0296-Book-Size-Limits.patch b/patches/server/0290-Book-Size-Limits.patch
index 406e5f0f43..406e5f0f43 100644
--- a/patches/server/0296-Book-Size-Limits.patch
+++ b/patches/server/0290-Book-Size-Limits.patch
diff --git a/patches/server/0297-Add-PlayerConnectionCloseEvent.patch b/patches/server/0291-Add-PlayerConnectionCloseEvent.patch
index d9509c863c..eade64b652 100644
--- a/patches/server/0297-Add-PlayerConnectionCloseEvent.patch
+++ b/patches/server/0291-Add-PlayerConnectionCloseEvent.patch
@@ -34,10 +34,10 @@ how PlayerPreLoginEvent interacts with PlayerConnectionCloseEvent
is undefined.
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 07fd527dd5b72ecc66311c1b81e578158e12a35c..527acbc15f3fe30541eef555480e158ab83a6130 100644
+index 870badca869aca1ad293542b345a038ddf715135..dd9c03611e410e601ba4a7769474fada8c28c104 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -439,6 +439,26 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -468,6 +468,26 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.getPacketListener().onDisconnect(Component.translatable("multiplayer.disconnect.generic"));
}
this.queue.clear(); // Free up packet queue.
diff --git a/patches/server/0298-Prevent-Enderman-from-loading-chunks.patch b/patches/server/0292-Prevent-Enderman-from-loading-chunks.patch
index 0eadb94123..0eadb94123 100644
--- a/patches/server/0298-Prevent-Enderman-from-loading-chunks.patch
+++ b/patches/server/0292-Prevent-Enderman-from-loading-chunks.patch
diff --git a/patches/server/0299-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch b/patches/server/0293-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch
index 6dd4db4eb5..44b74e5833 100644
--- a/patches/server/0299-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch
+++ b/patches/server/0293-Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch
@@ -16,10 +16,10 @@ intent to remove) and replace it with two new methods, clearly named and
documented as to their purpose.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index f91da5bc234a8f1c120261823a1a4e4216513329..ef5a1bdbb059a81763b1d630846dd69685842c19 100644
+index 135e61ffa69a56e7c5edcd9e2066334abf963340..e8ca0d8612e132fac5533b7672d0fccbf1bc9e24 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -231,6 +231,7 @@ public class ServerPlayer extends Player {
+@@ -230,6 +230,7 @@ public class ServerPlayer extends Player {
public int latency;
public boolean wonGame;
private int containerUpdateDelay; // Paper
@@ -28,13 +28,13 @@ index f91da5bc234a8f1c120261823a1a4e4216513329..ef5a1bdbb059a81763b1d630846dd696
public boolean queueHealthUpdatePacket = false;
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index c974120dda3bac8a4a74c4b4d8ddcfb802f792dc..7db8289fe3447f5ca403c5a66fc598b2055f4569 100644
+index ffa3f9d147a0113d7b8cbf185ca751ed159576c2..97f045ee1af5479b2befe2e03f082933cfe91007 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -179,6 +179,7 @@ public abstract class PlayerList {
public void placeNewPlayer(Connection connection, ServerPlayer player) {
- player.isRealPlayer = true; // Paper - Chunk priority
+ player.isRealPlayer = true; // Paper
+ player.loginTime = System.currentTimeMillis(); // Paper
GameProfile gameprofile = player.getGameProfile();
GameProfileCache usercache = this.server.getProfileCache();
@@ -106,7 +106,7 @@ index e7442952ef1f03969949014492a7ddc6d0796ba5..d7823d7dc88cfba6f6ac9dae220e03de
public Location getLastDeathLocation() {
if (this.getData().contains("LastDeathLocation", 10)) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index e950db8a10668c5d7f4d462eaf91540df2159538..31a34d04fbbd5a122c5f971e11649758bd6a4702 100644
+index 8676067be5cbb149a6a50a9582a22b69ebcfb61b..1b708fc4990e92815f2ad56aba52bf85e5ddea7f 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -174,6 +174,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@@ -117,7 +117,7 @@ index e950db8a10668c5d7f4d462eaf91540df2159538..31a34d04fbbd5a122c5f971e11649758
// Paper end
public CraftPlayer(CraftServer server, ServerPlayer entity) {
-@@ -1730,6 +1731,18 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1765,6 +1766,18 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.firstPlayed = firstPlayed;
}
@@ -136,7 +136,7 @@ index e950db8a10668c5d7f4d462eaf91540df2159538..31a34d04fbbd5a122c5f971e11649758
public void readExtraData(CompoundTag nbttagcompound) {
this.hasPlayedBefore = true;
if (nbttagcompound.contains("bukkit")) {
-@@ -1752,6 +1765,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1787,6 +1800,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
public void setExtraData(CompoundTag nbttagcompound) {
@@ -145,7 +145,7 @@ index e950db8a10668c5d7f4d462eaf91540df2159538..31a34d04fbbd5a122c5f971e11649758
if (!nbttagcompound.contains("bukkit")) {
nbttagcompound.put("bukkit", new CompoundTag());
}
-@@ -1766,6 +1781,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1801,6 +1816,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
data.putLong("firstPlayed", this.getFirstPlayed());
data.putLong("lastPlayed", System.currentTimeMillis());
data.putString("lastKnownName", handle.getScoreboardName());
diff --git a/patches/server/0300-Workaround-for-vehicle-tracking-issue-on-disconnect.patch b/patches/server/0294-Workaround-for-vehicle-tracking-issue-on-disconnect.patch
index 8d53da7d4e..699bb74caa 100644
--- a/patches/server/0300-Workaround-for-vehicle-tracking-issue-on-disconnect.patch
+++ b/patches/server/0294-Workaround-for-vehicle-tracking-issue-on-disconnect.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Workaround for vehicle tracking issue on disconnect
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index ef5a1bdbb059a81763b1d630846dd69685842c19..21b585ee91be0636e9d8f223106b5daa7ffb9ee6 100644
+index e8ca0d8612e132fac5533b7672d0fccbf1bc9e24..f45e8b4a536d0cef9b295083f95a556b87fcd2fc 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1593,6 +1593,13 @@ public class ServerPlayer extends Player {
+@@ -1578,6 +1578,13 @@ public class ServerPlayer extends Player {
public void disconnect() {
this.disconnected = true;
this.ejectPassengers();
diff --git a/patches/server/0301-Block-Entity-remove-from-being-called-on-Players.patch b/patches/server/0295-Block-Entity-remove-from-being-called-on-Players.patch
index 71a5f102ee..93b2e112ed 100644
--- a/patches/server/0301-Block-Entity-remove-from-being-called-on-Players.patch
+++ b/patches/server/0295-Block-Entity-remove-from-being-called-on-Players.patch
@@ -12,10 +12,10 @@ Player we will look at limiting the scope of this change. It appears to
be unintentional in the few cases we've seen so far.
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 31a34d04fbbd5a122c5f971e11649758bd6a4702..fa7b1dd573e61f1a99d58bdb723a40b606604b69 100644
+index 1b708fc4990e92815f2ad56aba52bf85e5ddea7f..aebe500d3d0947536d19286195a0c7f5798e862e 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -2601,6 +2601,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2636,6 +2636,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void resetCooldown() {
getHandle().resetAttackStrengthTicker();
}
diff --git a/patches/server/0302-BlockDestroyEvent.patch b/patches/server/0296-BlockDestroyEvent.patch
index 8034bc1082..507652f6ac 100644
--- a/patches/server/0302-BlockDestroyEvent.patch
+++ b/patches/server/0296-BlockDestroyEvent.patch
@@ -11,7 +11,7 @@ floating in the air.
This can replace many uses of BlockPhysicsEvent
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 2225921e988cdcaa4fb906b281c237972c6e7c1b..d72804cd4491c60043e428c0d06e02f9ca7f9303 100644
+index 9ed8117afa4eb1ff9e7403d3ab92a9e6cb0fab73..1a474fb88dc1447fb754e8ad936ab6add470359c 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -28,6 +28,7 @@ import net.minecraft.nbt.CompoundTag;
@@ -22,7 +22,7 @@ index 2225921e988cdcaa4fb906b281c237972c6e7c1b..d72804cd4491c60043e428c0d06e02f9
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
-@@ -578,8 +579,21 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -583,8 +584,21 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
return false;
} else {
FluidState fluid = this.getFluidState(pos);
diff --git a/patches/server/0303-Async-command-map-building.patch b/patches/server/0297-Async-command-map-building.patch
index cab55a5ab5..44b58ce867 100644
--- a/patches/server/0303-Async-command-map-building.patch
+++ b/patches/server/0297-Async-command-map-building.patch
@@ -53,10 +53,10 @@ index 2bf67468a6c745bc6243c65210477ba129bfcb07..c4315531f93f4ed68b4621157b025728
event.getPlayer().getServer().getPluginManager().callEvent(event);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index c5a118b1aab0654024a55ce5eae86aff8346c3d6..a53e1bc43903c10083b713b2c5ea5459ddfd4e27 100644
+index 6f78117189ed56acb9c49450825855dd03ab81ae..0a6a91aba56554ec813fffacbdd8e30591eb1602 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -879,6 +879,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -889,6 +889,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
MinecraftServer.LOGGER.info("Stopping server");
diff --git a/patches/server/0304-Implement-Brigadier-Mojang-API.patch b/patches/server/0298-Implement-Brigadier-Mojang-API.patch
index 9fb421afef..9fb421afef 100644
--- a/patches/server/0304-Implement-Brigadier-Mojang-API.patch
+++ b/patches/server/0298-Implement-Brigadier-Mojang-API.patch
diff --git a/patches/server/0305-Fix-Custom-Shapeless-Custom-Crafting-Recipes.patch b/patches/server/0299-Fix-Custom-Shapeless-Custom-Crafting-Recipes.patch
index 903ba48692..903ba48692 100644
--- a/patches/server/0305-Fix-Custom-Shapeless-Custom-Crafting-Recipes.patch
+++ b/patches/server/0299-Fix-Custom-Shapeless-Custom-Crafting-Recipes.patch
diff --git a/patches/server/0306-Limit-Client-Sign-length-more.patch b/patches/server/0300-Limit-Client-Sign-length-more.patch
index f357b73ac8..f357b73ac8 100644
--- a/patches/server/0306-Limit-Client-Sign-length-more.patch
+++ b/patches/server/0300-Limit-Client-Sign-length-more.patch
diff --git a/patches/server/0307-Don-t-check-ConvertSigns-boolean-every-sign-save.patch b/patches/server/0301-Don-t-check-ConvertSigns-boolean-every-sign-save.patch
index 0d3af0b6f8..0d3af0b6f8 100644
--- a/patches/server/0307-Don-t-check-ConvertSigns-boolean-every-sign-save.patch
+++ b/patches/server/0301-Don-t-check-ConvertSigns-boolean-every-sign-save.patch
diff --git a/patches/server/0308-Optimize-Network-Manager-and-add-advanced-packet-sup.patch b/patches/server/0302-Optimize-Network-Manager-and-add-advanced-packet-sup.patch
index 77e32e1bf6..0d594b9746 100644
--- a/patches/server/0308-Optimize-Network-Manager-and-add-advanced-packet-sup.patch
+++ b/patches/server/0302-Optimize-Network-Manager-and-add-advanced-packet-sup.patch
@@ -28,10 +28,10 @@ and then catch exceptions and close if they fire.
Part of this commit was authored by: Spottedleaf
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a203e67ac9 100644
+index dd9c03611e410e601ba4a7769474fada8c28c104..a283cce82069bf5dfd64229d102a19dfda158daf 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -93,6 +93,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -115,6 +115,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public int protocolVersion;
public java.net.InetSocketAddress virtualHost;
private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush");
@@ -42,7 +42,7 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
// Paper end
public Connection(PacketFlow side) {
-@@ -116,6 +120,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -138,6 +142,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
public void setProtocol(ConnectionProtocol state) {
@@ -50,7 +50,7 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
this.channel.attr(Connection.ATTRIBUTE_PROTOCOL).set(state);
this.channel.config().setAutoRead(true);
Connection.LOGGER.debug("Enabled auto read");
-@@ -194,19 +199,89 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -216,19 +221,89 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
Validate.notNull(listener, "packetListener", new Object[0]);
this.packetListener = listener;
}
@@ -144,7 +144,7 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
}
private void sendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks) {
-@@ -234,6 +309,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -256,6 +331,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.setProtocol(packetState);
}
@@ -160,7 +160,7 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
ChannelFuture channelfuture = this.channel.writeAndFlush(packet);
if (callbacks != null) {
-@@ -252,28 +336,65 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -274,28 +358,64 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
});
}
@@ -185,6 +185,7 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
}
- private void flushQueue() {
+- try { // Paper - add pending task queue
- if (this.channel != null && this.channel.isOpen()) {
- Queue queue = this.queue;
-
@@ -209,6 +210,7 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
+ return false;
+ }
+ private boolean processQueue() {
++ try { // Paper - add pending task queue
+ if (this.queue.isEmpty()) return true;
+ // If we are on main, we are safe here in that nothing else should be processing queue off main anymore
+ // But if we are not on main due to login/status, the parent is synchronized on packetQueue
@@ -230,12 +232,18 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
}
}
+ return true;
+ } finally { // Paper start - add pending task queue
+ Runnable r;
+ while ((r = this.pendingTasks.poll()) != null) {
+@@ -303,6 +423,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+ }
+ } // Paper end - add pending task queue
}
+ // Paper end
public void tick() {
this.flushQueue();
-@@ -310,9 +431,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -339,9 +460,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
return this.address;
}
@@ -258,7 +266,7 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
// Spigot End
if (this.channel.isOpen()) {
this.channel.close(); // We can't wait as this may be called from an event loop.
-@@ -430,7 +564,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -459,7 +593,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void handleDisconnection() {
if (this.channel != null && !this.channel.isOpen()) {
if (this.disconnectionHandled) {
@@ -267,7 +275,7 @@ index 527acbc15f3fe30541eef555480e158ab83a6130..31d35af5d0efbd0bd8528c3f05e660a2
} else {
this.disconnectionHandled = true;
if (this.getDisconnectedReason() != null) {
-@@ -438,7 +572,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -467,7 +601,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
} else if (this.getPacketListener() != null) {
this.getPacketListener().onDisconnect(Component.translatable("multiplayer.disconnect.generic"));
}
diff --git a/patches/server/0309-Handle-Oversized-Tile-Entities-in-chunks.patch b/patches/server/0303-Handle-Oversized-Tile-Entities-in-chunks.patch
index bcaddcfede..bcaddcfede 100644
--- a/patches/server/0309-Handle-Oversized-Tile-Entities-in-chunks.patch
+++ b/patches/server/0303-Handle-Oversized-Tile-Entities-in-chunks.patch
diff --git a/patches/server/0310-Set-Zombie-last-tick-at-start-of-drowning-process.patch b/patches/server/0304-Set-Zombie-last-tick-at-start-of-drowning-process.patch
index 6c82126b25..6c82126b25 100644
--- a/patches/server/0310-Set-Zombie-last-tick-at-start-of-drowning-process.patch
+++ b/patches/server/0304-Set-Zombie-last-tick-at-start-of-drowning-process.patch
diff --git a/patches/server/0311-Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch b/patches/server/0305-Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch
index a8e13395a2..90510e0728 100644
--- a/patches/server/0311-Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch
+++ b/patches/server/0305-Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Call WhitelistToggleEvent when whitelist is toggled
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 7db8289fe3447f5ca403c5a66fc598b2055f4569..3cb257544d95e82f8de2d693f510c15980aa27c8 100644
+index 97f045ee1af5479b2befe2e03f082933cfe91007..45cd4ac70bf322a3bb256579f795db46b9beb7a0 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1129,6 +1129,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0312-Entity-getEntitySpawnReason.patch b/patches/server/0306-Entity-getEntitySpawnReason.patch
index fa232bad2e..e43c04d70a 100644
--- a/patches/server/0312-Entity-getEntitySpawnReason.patch
+++ b/patches/server/0306-Entity-getEntitySpawnReason.patch
@@ -10,10 +10,10 @@ persistenting Living Entity, SPAWNER for spawners,
or DEFAULT since data was not stored.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 377ebed70c5008d69701bd919f22ad4506dd129c..427dfb5c7fe09e94aae617ecc1440fd1530fb857 100644
+index f85692e588c022a1f0cc5e3cde001d968eea59c8..91b0bc73534bec202ba39d6a8386248db1b4da30 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1215,6 +1215,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1250,6 +1250,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
return true;
}
// Paper end
@@ -22,7 +22,7 @@ index 377ebed70c5008d69701bd919f22ad4506dd129c..427dfb5c7fe09e94aae617ecc1440fd1
// Paper start
if (DEBUG_ENTITIES) {
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 3cb257544d95e82f8de2d693f510c15980aa27c8..895d087fbdde840bd6b96b6c8d231fc9beeb2a0b 100644
+index 45cd4ac70bf322a3bb256579f795db46b9beb7a0..80b6eb1e010559c02c9e7624a5c131d11c80c68e 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -348,7 +348,7 @@ public abstract class PlayerList {
@@ -35,7 +35,7 @@ index 3cb257544d95e82f8de2d693f510c15980aa27c8..895d087fbdde840bd6b96b6c8d231fc9
});
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 424eed2daa3a9574cf1179a7f970f1565ea5fc71..a2a607e6ae867a4cabe1c2280154c99e93d0e7e9 100644
+index e6fa82b942ec176288aaca080e3d9c8df6a16ac0..fa128e7f8089339f932e19edf95a8d5c0cc14046 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -74,6 +74,8 @@ import net.minecraft.world.InteractionHand;
@@ -55,7 +55,7 @@ index 424eed2daa3a9574cf1179a7f970f1565ea5fc71..a2a607e6ae867a4cabe1c2280154c99e
public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
private CraftEntity bukkitEntity;
-@@ -1971,6 +1974,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2023,6 +2026,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
nbt.put("Paper.Origin", this.newDoubleList(origin.getX(), origin.getY(), origin.getZ()));
}
@@ -65,7 +65,7 @@ index 424eed2daa3a9574cf1179a7f970f1565ea5fc71..a2a607e6ae867a4cabe1c2280154c99e
// Save entity's from mob spawner status
if (spawnedViaMobSpawner) {
nbt.putBoolean("Paper.FromMobSpawner", true);
-@@ -2116,6 +2122,26 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2168,6 +2174,26 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
spawnedViaMobSpawner = nbt.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status
diff --git a/patches/server/0313-Update-entity-Metadata-for-all-tracked-players.patch b/patches/server/0307-Update-entity-Metadata-for-all-tracked-players.patch
index 3ddc7c5d06..3ddc7c5d06 100644
--- a/patches/server/0313-Update-entity-Metadata-for-all-tracked-players.patch
+++ b/patches/server/0307-Update-entity-Metadata-for-all-tracked-players.patch
diff --git a/patches/server/0314-Fire-event-on-GS4-query.patch b/patches/server/0308-Fire-event-on-GS4-query.patch
index 445725e8b8..445725e8b8 100644
--- a/patches/server/0314-Fire-event-on-GS4-query.patch
+++ b/patches/server/0308-Fire-event-on-GS4-query.patch
diff --git a/patches/server/0315-Implement-PlayerPostRespawnEvent.patch b/patches/server/0309-Implement-PlayerPostRespawnEvent.patch
index 53f80b7443..d4f88880bf 100644
--- a/patches/server/0315-Implement-PlayerPostRespawnEvent.patch
+++ b/patches/server/0309-Implement-PlayerPostRespawnEvent.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Implement PlayerPostRespawnEvent
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 895d087fbdde840bd6b96b6c8d231fc9beeb2a0b..3f95cfe7d6e85a19c4d6c4ce2b662a4259c45477 100644
+index 80b6eb1e010559c02c9e7624a5c131d11c80c68e..d8c936b6b242d6ad2ca63ab98a4a111fed402241 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -728,9 +728,14 @@ public abstract class PlayerList {
diff --git a/patches/server/0316-don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch b/patches/server/0310-don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch
index 94aa72db11..94aa72db11 100644
--- a/patches/server/0316-don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch
+++ b/patches/server/0310-don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch
diff --git a/patches/server/0317-Server-Tick-Events.patch b/patches/server/0311-Server-Tick-Events.patch
index 60fc617e7c..5feb5afd52 100644
--- a/patches/server/0317-Server-Tick-Events.patch
+++ b/patches/server/0311-Server-Tick-Events.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Server Tick Events
Fires event at start and end of a server tick
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index a53e1bc43903c10083b713b2c5ea5459ddfd4e27..1d554a45097cdf0640788bb796b983f18af31a9f 100644
+index acc9f22a1284cea2e29f3616598f8388f0a0e6f6..f9a96a11764b66709a0d74122f9ecc06d0365a93 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1308,6 +1308,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1302,6 +1302,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
});
isOversleep = false;MinecraftTimings.serverOversleep.stopTiming();
// Paper end
@@ -17,7 +17,7 @@ index a53e1bc43903c10083b713b2c5ea5459ddfd4e27..1d554a45097cdf0640788bb796b983f1
++this.tickCount;
this.tickChildren(shouldKeepTicking);
-@@ -1346,6 +1347,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1340,6 +1341,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.runAllTasks();
}
// Paper end
diff --git a/patches/server/0318-PlayerDeathEvent-getItemsToKeep.patch b/patches/server/0312-PlayerDeathEvent-getItemsToKeep.patch
index b8dea8f8ed..9f5f2c75f1 100644
--- a/patches/server/0318-PlayerDeathEvent-getItemsToKeep.patch
+++ b/patches/server/0312-PlayerDeathEvent-getItemsToKeep.patch
@@ -8,10 +8,10 @@ Exposes a mutable array on items a player should keep on death
Example Usage: https://gist.github.com/aikar/5bb202de6057a051a950ce1f29feb0b4
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 21b585ee91be0636e9d8f223106b5daa7ffb9ee6..d868c7f953cba9f7691c62edd2169ad26fc7d867 100644
+index f45e8b4a536d0cef9b295083f95a556b87fcd2fc..e56a6bdcc62f7d82447440ad20e72678a2d6852e 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -791,6 +791,46 @@ public class ServerPlayer extends Player {
+@@ -776,6 +776,46 @@ public class ServerPlayer extends Player {
});
}
@@ -58,7 +58,7 @@ index 21b585ee91be0636e9d8f223106b5daa7ffb9ee6..d868c7f953cba9f7691c62edd2169ad2
@Override
public void die(DamageSource damageSource) {
this.gameEvent(GameEvent.ENTITY_DIE);
-@@ -874,7 +914,12 @@ public class ServerPlayer extends Player {
+@@ -859,7 +899,12 @@ public class ServerPlayer extends Player {
this.dropExperience();
// we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory.
if (!event.getKeepInventory()) {
diff --git a/patches/server/0319-Optimize-Captured-TileEntity-Lookup.patch b/patches/server/0313-Optimize-Captured-TileEntity-Lookup.patch
index 5fce850db9..2aea08c36b 100644
--- a/patches/server/0319-Optimize-Captured-TileEntity-Lookup.patch
+++ b/patches/server/0313-Optimize-Captured-TileEntity-Lookup.patch
@@ -10,10 +10,10 @@ Optimize to check if the captured list even has values in it, and also to
just do a get call since the value can never be null.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index d72804cd4491c60043e428c0d06e02f9ca7f9303..244035be96c5218a27e70c3f80ee9d7dcb4419d5 100644
+index 1a474fb88dc1447fb754e8ad936ab6add470359c..cb7e92f2694cefeecebc840959a01e1e1945061a 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -840,9 +840,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -845,9 +845,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@Nullable
public BlockEntity getBlockEntity(BlockPos blockposition, boolean validate) {
@@ -26,5 +26,5 @@ index d72804cd4491c60043e428c0d06e02f9ca7f9303..244035be96c5218a27e70c3f80ee9d7d
}
+ // Paper end
// CraftBukkit end
- return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE));
+ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system
}
diff --git a/patches/server/0320-Add-Heightmap-API.patch b/patches/server/0314-Add-Heightmap-API.patch
index 6187c5d5d9..2f3dd5b25e 100644
--- a/patches/server/0320-Add-Heightmap-API.patch
+++ b/patches/server/0314-Add-Heightmap-API.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add Heightmap API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 3aa2e80e7d30d8824fd7f009282adfd8712cbb55..e43c68a086f1fcc6f7b3715bd55e75a64e4825de 100644
+index 0ca653a0b0059116eaa943207bf04b5bfbe77e8f..8a8993ff7ec450416ef16941079ee0ac3078767b 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -222,6 +222,29 @@ public class CraftWorld extends CraftRegionAccessor implements World {
diff --git a/patches/server/0321-Mob-Spawner-API-Enhancements.patch b/patches/server/0315-Mob-Spawner-API-Enhancements.patch
index 0947d35c6f..0947d35c6f 100644
--- a/patches/server/0321-Mob-Spawner-API-Enhancements.patch
+++ b/patches/server/0315-Mob-Spawner-API-Enhancements.patch
diff --git a/patches/server/0322-Fix-CB-call-to-changed-postToMainThread-method.patch b/patches/server/0316-Fix-CB-call-to-changed-postToMainThread-method.patch
index 1b53a26930..1b53a26930 100644
--- a/patches/server/0322-Fix-CB-call-to-changed-postToMainThread-method.patch
+++ b/patches/server/0316-Fix-CB-call-to-changed-postToMainThread-method.patch
diff --git a/patches/server/0323-Fix-sounds-when-item-frames-are-modified-MC-123450.patch b/patches/server/0317-Fix-sounds-when-item-frames-are-modified-MC-123450.patch
index 8f80e7c151..8f80e7c151 100644
--- a/patches/server/0323-Fix-sounds-when-item-frames-are-modified-MC-123450.patch
+++ b/patches/server/0317-Fix-sounds-when-item-frames-are-modified-MC-123450.patch
diff --git a/patches/server/0325-Implement-CraftBlockSoundGroup.patch b/patches/server/0318-Implement-CraftBlockSoundGroup.patch
index a62be7b054..a62be7b054 100644
--- a/patches/server/0325-Implement-CraftBlockSoundGroup.patch
+++ b/patches/server/0318-Implement-CraftBlockSoundGroup.patch
diff --git a/patches/server/0326-Configurable-Keep-Spawn-Loaded-range-per-world.patch b/patches/server/0319-Configurable-Keep-Spawn-Loaded-range-per-world.patch
index 9ead7ee244..15997374b4 100644
--- a/patches/server/0326-Configurable-Keep-Spawn-Loaded-range-per-world.patch
+++ b/patches/server/0319-Configurable-Keep-Spawn-Loaded-range-per-world.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Configurable Keep Spawn Loaded range per world
This lets you disable it for some worlds and lower it for others.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index f7d7e69e29f217c233869951d7d3188816f8216c..7342c8ac1d4bb13c0d8ce6f26b1b43d76a327655 100644
+index cfdaf510f8a1ff5bd31f8bc2c60b7b63041a3125..e05d17f96d70ff909f2b37b8390ddd112115fcf3 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -729,31 +729,34 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -64,7 +64,7 @@ index f7d7e69e29f217c233869951d7d3188816f8216c..7342c8ac1d4bb13c0d8ce6f26b1b43d7
// CraftBukkit start
// this.updateMobSpawningFlags();
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 427dfb5c7fe09e94aae617ecc1440fd1530fb857..67a17a0fad369c412a943bb21b574c9cd5031fb8 100644
+index 91b0bc73534bec202ba39d6a8386248db1b4da30..c998741c3b84829590a555f9ed53f0b379b6e4d7 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -64,6 +64,7 @@ import net.minecraft.network.protocol.game.ClientboundSoundEntityPacket;
@@ -75,7 +75,7 @@ index 427dfb5c7fe09e94aae617ecc1440fd1530fb857..67a17a0fad369c412a943bb21b574c9c
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.server.level.progress.ChunkProgressListener;
-@@ -1716,12 +1717,84 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1751,12 +1752,84 @@ public class ServerLevel extends Level implements WorldGenLevel {
return ((MapIndex) this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex::load, MapIndex::new, "idcounts")).getFreeAuxValueForMap();
}
@@ -200,10 +200,10 @@ index 4d2348df25410a0b5364eec066880326d6667dad..286aad3205ef8a9e21a47ef07893844f
this.maxCount = i * i;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index e43c68a086f1fcc6f7b3715bd55e75a64e4825de..acfc8796a871706827f95c43f76b535e386d8daa 100644
+index 8a8993ff7ec450416ef16941079ee0ac3078767b..a4fc3e4f13b359e3a33b867e4bd200265e020226 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -1356,15 +1356,21 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1346,15 +1346,21 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void setKeepSpawnInMemory(boolean keepLoaded) {
diff --git a/patches/server/0327-Allow-Saving-of-Oversized-Chunks.patch b/patches/server/0320-Allow-Saving-of-Oversized-Chunks.patch
index 04e29c57cc..d1923d7fc8 100644
--- a/patches/server/0327-Allow-Saving-of-Oversized-Chunks.patch
+++ b/patches/server/0320-Allow-Saving-of-Oversized-Chunks.patch
@@ -142,7 +142,7 @@ index 038e2177182c94baa4af24f9111cf155ec342dfe..330fb8e6565a5c0490af0c5ca0e7355d
private final ChunkPos pos;
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index b790bb574546fef1969584529e49dbcf1403e198..ae1cb438cec25f8fef5dea0b2fddec6e72d5f26e 100644
+index 9a4f9251d655b86af5a410cfab8df285098e01cc..5a25664a15643ff148db47caad4f53376b55168e 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -11,8 +11,10 @@ import java.nio.file.Files;
@@ -241,7 +241,7 @@ index b790bb574546fef1969584529e49dbcf1403e198..ae1cb438cec25f8fef5dea0b2fddec6e
CompoundTag nbttagcompound;
label43:
{
-@@ -172,6 +245,7 @@ public class RegionFileStorage implements AutoCloseable {
+@@ -176,6 +249,7 @@ public class RegionFileStorage implements AutoCloseable {
try {
NbtIo.write(nbt, (DataOutput) dataoutputstream);
diff --git a/patches/server/0328-Expose-the-internal-current-tick.patch b/patches/server/0321-Expose-the-internal-current-tick.patch
index 78a0152871..d4a0f4b4f8 100644
--- a/patches/server/0328-Expose-the-internal-current-tick.patch
+++ b/patches/server/0321-Expose-the-internal-current-tick.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Expose the internal current tick
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 2d6f8032cd5a5e7542a4a1cd791852873c93657e..e8c11b59dc50fd9c5bbf073b66d0cd9c504d7c25 100644
+index dd7eebe8397764206e07809293f1b8ca4e8f205c..a7ee6b54e763d5ff2eb4afdbccdf5becb4e1f305 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2663,5 +2663,10 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0329-Fix-World-isChunkGenerated-calls.patch b/patches/server/0322-Fix-World-isChunkGenerated-calls.patch
index c673732afb..973a7c6ae7 100644
--- a/patches/server/0329-Fix-World-isChunkGenerated-calls.patch
+++ b/patches/server/0322-Fix-World-isChunkGenerated-calls.patch
@@ -8,10 +8,10 @@ This patch also adds a chunk status cache on region files (note that
its only purpose is to cache the status on DISK)
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 8d0f7fb501aa5caa36b7dd273a8a7c7f959759cb..245f60eb904e26d6e7f7ca02bfa778d4f6db5d76 100644
+index d1af0aca0237ee86acd86fea3255ddeadc3db0d6..06b5fecb37621f66780a396774a37292675d90ac 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1354,9 +1354,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -659,9 +659,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
@@ -28,7 +28,7 @@ index 8d0f7fb501aa5caa36b7dd273a8a7c7f959759cb..245f60eb904e26d6e7f7ca02bfa778d4
}
// CraftBukkit start
-@@ -1365,6 +1369,63 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -670,6 +674,63 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// CraftBukkit end
}
@@ -84,7 +84,7 @@ index 8d0f7fb501aa5caa36b7dd273a8a7c7f959759cb..245f60eb904e26d6e7f7ca02bfa778d4
+ }
+
+ public ChunkAccess getUnloadingChunk(int chunkX, int chunkZ) {
-+ ChunkHolder chunkHolder = this.pendingUnloads.get(ChunkPos.asLong(chunkX, chunkZ));
++ ChunkHolder chunkHolder = net.minecraft.server.ChunkSystem.getUnloadingChunkHolder(this.level, chunkX, chunkZ);
+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow();
+ }
+ // Paper end
@@ -92,50 +92,6 @@ index 8d0f7fb501aa5caa36b7dd273a8a7c7f959759cb..245f60eb904e26d6e7f7ca02bfa778d4
boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) {
// Spigot start
return this.anyPlayerCloseEnoughForSpawning(pos, false);
-diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
-index 441d46635caedfae3cb2f46d30b8d9ae95636e7b..e6240f891e396d91e31b02fdf3084be77e9d6697 100644
---- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java
-@@ -292,6 +292,17 @@ public class ChunkStatus {
- return this.chunkType;
- }
-
-+ // Paper start
-+ public static ChunkStatus getStatus(String name) {
-+ try {
-+ // We need this otherwise we return EMPTY for invalid names
-+ ResourceLocation key = new ResourceLocation(name);
-+ return Registry.CHUNK_STATUS.getOptional(key).orElse(null);
-+ } catch (Exception ex) {
-+ return null; // invalid name
-+ }
-+ }
-+ // Paper end
- public static ChunkStatus byName(String id) {
- return (ChunkStatus) Registry.CHUNK_STATUS.get(ResourceLocation.tryParse(id));
- }
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 8cc2a2c026eb44461cd94faeb64fb2151d2d3898..8480310bd389ad55b8138a20da59cbcffb973819 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -623,6 +623,17 @@ public class ChunkSerializer {
- }));
- }
-
-+ // Paper start
-+ public static @Nullable ChunkStatus getStatus(@Nullable CompoundTag compound) {
-+ if (compound == null) {
-+ return null;
-+ }
-+
-+ // Note: Copied from below
-+ return ChunkStatus.getStatus(compound.getString("Status"));
-+ }
-+ // Paper end
-+
- public static ChunkStatus.ChunkType getChunkTypeFromTag(@Nullable CompoundTag nbt) {
- return nbt != null ? ChunkStatus.byName(nbt.getString("Status")).getChunkType() : ChunkStatus.ChunkType.PROTOCHUNK;
- }
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
index 330fb8e6565a5c0490af0c5ca0e7355d81a82e58..861a25a15f1aab20e3245b6d5cdad5d23bdfd6d0 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -188,10 +144,10 @@ index 330fb8e6565a5c0490af0c5ca0e7355d81a82e58..861a25a15f1aab20e3245b6d5cdad5d2
this.padToFullSector();
} finally {
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index ae1cb438cec25f8fef5dea0b2fddec6e72d5f26e..2a6a4a62feb1c02bef850b0cda578f6f9d46a5e3 100644
+index 5a25664a15643ff148db47caad4f53376b55168e..7bfb0716964af5ee300150d500c97e8f90c849d4 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-@@ -245,6 +245,7 @@ public class RegionFileStorage implements AutoCloseable {
+@@ -249,6 +249,7 @@ public class RegionFileStorage implements AutoCloseable {
try {
NbtIo.write(nbt, (DataOutput) dataoutputstream);
@@ -200,7 +156,7 @@ index ae1cb438cec25f8fef5dea0b2fddec6e72d5f26e..2a6a4a62feb1c02bef850b0cda578f6f
} catch (Throwable throwable) {
if (dataoutputstream != null) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index acfc8796a871706827f95c43f76b535e386d8daa..868d9d13b03751b24c4b6695f97ad343ff8d0b8e 100644
+index a4fc3e4f13b359e3a33b867e4bd200265e020226..37efb3305b4f740d9092c2a253b93f1925ee75d5 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -299,9 +299,23 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@@ -229,7 +185,7 @@ index acfc8796a871706827f95c43f76b535e386d8daa..868d9d13b03751b24c4b6695f97ad343
throw new RuntimeException(ex);
}
}
-@@ -412,20 +426,48 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -415,20 +429,48 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean loadChunk(int x, int z, boolean generate) {
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
diff --git a/patches/server/0330-Show-blockstate-location-if-we-failed-to-read-it.patch b/patches/server/0323-Show-blockstate-location-if-we-failed-to-read-it.patch
index f593178909..f593178909 100644
--- a/patches/server/0330-Show-blockstate-location-if-we-failed-to-read-it.patch
+++ b/patches/server/0323-Show-blockstate-location-if-we-failed-to-read-it.patch
diff --git a/patches/server/0331-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch b/patches/server/0324-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch
index a181d9b9ad..a181d9b9ad 100644
--- a/patches/server/0331-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch
+++ b/patches/server/0324-Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch
diff --git a/patches/server/0332-Configurable-projectile-relative-velocity.patch b/patches/server/0325-Configurable-projectile-relative-velocity.patch
index 024876f999..024876f999 100644
--- a/patches/server/0332-Configurable-projectile-relative-velocity.patch
+++ b/patches/server/0325-Configurable-projectile-relative-velocity.patch
diff --git a/patches/server/0333-offset-item-frame-ticking.patch b/patches/server/0326-offset-item-frame-ticking.patch
index d20777b4c1..d20777b4c1 100644
--- a/patches/server/0333-offset-item-frame-ticking.patch
+++ b/patches/server/0326-offset-item-frame-ticking.patch
diff --git a/patches/server/0334-Fix-MC-158900.patch b/patches/server/0327-Fix-MC-158900.patch
index a2856aa25e..02f255d75a 100644
--- a/patches/server/0334-Fix-MC-158900.patch
+++ b/patches/server/0327-Fix-MC-158900.patch
@@ -7,7 +7,7 @@ The problem was we were checking isExpired() on the entry, but if it
was expired at that point, then it would be null.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 3f95cfe7d6e85a19c4d6c4ce2b662a4259c45477..d362c9c83a73b5f49b54563b423c76c8ed78dfc6 100644
+index d8c936b6b242d6ad2ca63ab98a4a111fed402241..6e585b79bf2eccc6bfc98d7cdc05efea699d4e2f 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -612,8 +612,10 @@ public abstract class PlayerList {
diff --git a/patches/server/0335-Prevent-consuming-the-wrong-itemstack.patch b/patches/server/0328-Prevent-consuming-the-wrong-itemstack.patch
index 6074727c03..6074727c03 100644
--- a/patches/server/0335-Prevent-consuming-the-wrong-itemstack.patch
+++ b/patches/server/0328-Prevent-consuming-the-wrong-itemstack.patch
diff --git a/patches/server/0336-Dont-send-unnecessary-sign-update.patch b/patches/server/0329-Dont-send-unnecessary-sign-update.patch
index 4d63dfa58a..4d63dfa58a 100644
--- a/patches/server/0336-Dont-send-unnecessary-sign-update.patch
+++ b/patches/server/0329-Dont-send-unnecessary-sign-update.patch
diff --git a/patches/server/0337-Add-option-to-disable-pillager-patrols.patch b/patches/server/0330-Add-option-to-disable-pillager-patrols.patch
index 65d645b929..65d645b929 100644
--- a/patches/server/0337-Add-option-to-disable-pillager-patrols.patch
+++ b/patches/server/0330-Add-option-to-disable-pillager-patrols.patch
diff --git a/patches/server/0338-Flat-bedrock-generator-settings.patch b/patches/server/0331-Flat-bedrock-generator-settings.patch
index 43ca0facd8..a234199fa8 100644
--- a/patches/server/0338-Flat-bedrock-generator-settings.patch
+++ b/patches/server/0331-Flat-bedrock-generator-settings.patch
@@ -112,7 +112,7 @@ index c5822637e48fad4ca4e8cf210431b5eafbf5abb1..0ece775ca7e63184f79fcdd1aa7ed5c1
Bootstrap.wrapStreams();
}
diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
-index e66e52da84408eb705d23504e500bd8a98322b0e..575efe82a7219e256afd8362984eb26795445119 100644
+index 298ea7c5776c4476dbb69e68debbd50376d14165..cf87490a446285132daaf9d90154ac6d477a62fe 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
@@ -210,7 +210,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
diff --git a/patches/server/0339-Prevent-sync-chunk-loads-when-villagers-try-to-find-.patch b/patches/server/0332-Prevent-sync-chunk-loads-when-villagers-try-to-find-.patch
index ea3c47b7b2..ea3c47b7b2 100644
--- a/patches/server/0339-Prevent-sync-chunk-loads-when-villagers-try-to-find-.patch
+++ b/patches/server/0332-Prevent-sync-chunk-loads-when-villagers-try-to-find-.patch
diff --git a/patches/server/0340-MC-145656-Fix-Follow-Range-Initial-Target.patch b/patches/server/0333-MC-145656-Fix-Follow-Range-Initial-Target.patch
index 44844fef0d..44844fef0d 100644
--- a/patches/server/0340-MC-145656-Fix-Follow-Range-Initial-Target.patch
+++ b/patches/server/0333-MC-145656-Fix-Follow-Range-Initial-Target.patch
diff --git a/patches/server/0341-Duplicate-UUID-Resolve-Option.patch b/patches/server/0334-Duplicate-UUID-Resolve-Option.patch
index 9c8f0336e7..1bdfb97dc5 100644
--- a/patches/server/0341-Duplicate-UUID-Resolve-Option.patch
+++ b/patches/server/0334-Duplicate-UUID-Resolve-Option.patch
@@ -33,25 +33,33 @@ But for those who are ok with leaving this inconsistent behavior, you may use WA
It is recommended you regenerate the entities, as these were legit entities, and deserve your love.
diff --git a/src/main/java/net/minecraft/server/ChunkSystem.java b/src/main/java/net/minecraft/server/ChunkSystem.java
-index 1b1bfd5f92f85f46ad9661a0a64a2a1b4c33a80d..2a099fe0d514f181bf2b452d5333bc29b0d29e43 100644
+index 0c6534e64ef023cf613f2c5407c7598c6ed81bc6..d38beb8183470a48c4c927b78fbed243eebcfe71 100644
--- a/src/main/java/net/minecraft/server/ChunkSystem.java
+++ b/src/main/java/net/minecraft/server/ChunkSystem.java
-@@ -260,7 +260,9 @@ public final class ChunkSystem {
+@@ -73,7 +73,17 @@ public final class ChunkSystem {
}
public static void onEntityPreAdd(final ServerLevel level, final Entity entity) {
-
++ // Paper start - duplicate uuid resolving
+ if (net.minecraft.server.level.ChunkMap.checkDupeUUID(level, entity)) {
+ return;
+ }
++ if (net.minecraft.world.level.Level.DEBUG_ENTITIES && ((Entity) entity).level.paperConfig().entities.spawning.duplicateUuid.mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.NOTHING) {
++ if (((Entity) entity).addedToWorldStack != null) {
++ ((Entity) entity).addedToWorldStack.printStackTrace();
++ }
++ net.minecraft.server.level.ServerLevel.getAddToWorldStackTrace((Entity) entity).printStackTrace();
++ }
++ // Paper end - duplicate uuid resolving
}
public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 245f60eb904e26d6e7f7ca02bfa778d4f6db5d76..4b24e4d947e96ea0720f8f6bc33470e07c00310d 100644
+index 06b5fecb37621f66780a396774a37292675d90ac..67823979209f9b8a3f44cd21baca667457ef110a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -982,6 +982,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -522,6 +522,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.discard();
needsRemoval = true;
}
@@ -59,8 +67,8 @@ index 245f60eb904e26d6e7f7ca02bfa778d4f6db5d76..4b24e4d947e96ea0720f8f6bc33470e0
return !needsRemoval;
}));
// CraftBukkit end
-@@ -1032,6 +1033,49 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- });
+@@ -533,6 +534,49 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
}
+ // Paper start
@@ -107,32 +115,5 @@ index 245f60eb904e26d6e7f7ca02bfa778d4f6db5d76..4b24e4d947e96ea0720f8f6bc33470e0
+ }
+ // Paper end
public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareTickingChunk(ChunkHolder holder) {
- ChunkPos chunkcoordintpair = holder.getPos();
- CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkRangeFuture(chunkcoordintpair, 1, (i) -> {
-diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-index ab7eadf2fc4c4598fa89068332eaaf9a8e0a100f..16519a6414f6f6418de40b714555a52631980617 100644
---- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-@@ -78,7 +78,22 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
-
- private boolean addEntityUuid(T entity) {
- if (!this.knownUuids.add(entity.getUUID())) {
-+ // Paper start
-+ T conflict = this.visibleEntityStorage.getEntity(entity.getUUID());
-+ if (conflict != null && ((Entity) conflict).isRemoved()) {
-+ stopTracking(conflict); // remove the existing entity
-+ return true;
-+ }
-+ // Paper end
- PersistentEntitySectionManager.LOGGER.warn("UUID of added entity already exists: {}", entity);
-+ // Paper start
-+ if (net.minecraft.world.level.Level.DEBUG_ENTITIES && ((Entity) entity).level.paperConfig().entities.spawning.duplicateUuid.mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.NOTHING) {
-+ if (((Entity) entity).addedToWorldStack != null) {
-+ ((Entity) entity).addedToWorldStack.printStackTrace();
-+ }
-+ net.minecraft.server.level.ServerLevel.getAddToWorldStackTrace((Entity) entity).printStackTrace();
-+ }
-+ // Paper end
- return false;
- } else {
- return true;
+ throw new UnsupportedOperationException(); // Paper - rewrite chunk system
+ }
diff --git a/patches/server/0342-Optimize-Hoppers.patch b/patches/server/0335-Optimize-Hoppers.patch
index 7e66fc243d..e390a98062 100644
--- a/patches/server/0342-Optimize-Hoppers.patch
+++ b/patches/server/0335-Optimize-Hoppers.patch
@@ -13,10 +13,10 @@ Subject: [PATCH] Optimize Hoppers
* Remove Streams from Item Suck In and restore restore 1.12 AABB checks which is simpler and no voxel allocations (was doing TWO Item Suck ins)
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 7342c8ac1d4bb13c0d8ce6f26b1b43d76a327655..3a085c141a5a6e2df9a85d0e3969363d69824294 100644
+index d6ca6ef7262e25620aceda589d21363193c70310..aa52b644b7af9261fdec06b29b7daa7ad8f89b3a 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1411,6 +1411,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1405,6 +1405,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
diff --git a/patches/server/0343-PlayerDeathEvent-shouldDropExperience.patch b/patches/server/0336-PlayerDeathEvent-shouldDropExperience.patch
index f3bcd088d8..d97c9ef24c 100644
--- a/patches/server/0343-PlayerDeathEvent-shouldDropExperience.patch
+++ b/patches/server/0336-PlayerDeathEvent-shouldDropExperience.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] PlayerDeathEvent#shouldDropExperience
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index d868c7f953cba9f7691c62edd2169ad26fc7d867..b36effc88b516958a7c0a46a7eb3a77a98ad3a20 100644
+index e56a6bdcc62f7d82447440ad20e72678a2d6852e..9cae5379d60c8d20ae6966850f7f13640742f9b7 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -911,7 +911,7 @@ public class ServerPlayer extends Player {
+@@ -896,7 +896,7 @@ public class ServerPlayer extends Player {
this.tellNeutralMobsThatIDied();
}
// SPIGOT-5478 must be called manually now
diff --git a/patches/server/0344-Prevent-bees-loading-chunks-checking-hive-position.patch b/patches/server/0337-Prevent-bees-loading-chunks-checking-hive-position.patch
index 3e8d451777..3e8d451777 100644
--- a/patches/server/0344-Prevent-bees-loading-chunks-checking-hive-position.patch
+++ b/patches/server/0337-Prevent-bees-loading-chunks-checking-hive-position.patch
diff --git a/patches/server/0345-Don-t-load-Chunks-from-Hoppers-and-other-things.patch b/patches/server/0338-Don-t-load-Chunks-from-Hoppers-and-other-things.patch
index 71f28eb22f..71f28eb22f 100644
--- a/patches/server/0345-Don-t-load-Chunks-from-Hoppers-and-other-things.patch
+++ b/patches/server/0338-Don-t-load-Chunks-from-Hoppers-and-other-things.patch
diff --git a/patches/server/0346-Guard-against-serializing-mismatching-chunk-coordina.patch b/patches/server/0339-Guard-against-serializing-mismatching-chunk-coordina.patch
index bc391d7a2a..7018c6fabf 100644
--- a/patches/server/0346-Guard-against-serializing-mismatching-chunk-coordina.patch
+++ b/patches/server/0339-Guard-against-serializing-mismatching-chunk-coordina.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Guard against serializing mismatching chunk coordinate
Should help if something dumb happens
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 8480310bd389ad55b8138a20da59cbcffb973819..864e591b10360b0f12fe5c5a650da372555ebd10 100644
+index e59910540458ca912efea64d9f7cd212d63d110a..f4bf0ecde863f1795d764d8cc8d6525af02356ea 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -84,6 +84,18 @@ public class ChunkSerializer {
+@@ -92,6 +92,18 @@ public class ChunkSerializer {
public ChunkSerializer() {}
@@ -28,7 +28,7 @@ index 8480310bd389ad55b8138a20da59cbcffb973819..864e591b10360b0f12fe5c5a650da372
// Paper start
public static final class InProgressChunkHolder {
-@@ -109,7 +121,7 @@ public class ChunkSerializer {
+@@ -117,7 +129,7 @@ public class ChunkSerializer {
public static InProgressChunkHolder loadChunk(ServerLevel world, PoiManager poiStorage, ChunkPos chunkPos, CompoundTag nbt, boolean distinguish) {
java.util.ArrayDeque<Runnable> tasksToExecuteOnMain = new java.util.ArrayDeque<>();
// Paper end
@@ -38,7 +38,7 @@ index 8480310bd389ad55b8138a20da59cbcffb973819..864e591b10360b0f12fe5c5a650da372
if (!Objects.equals(chunkPos, chunkcoordintpair1)) {
ChunkSerializer.LOGGER.error("Chunk file at {} is in the wrong location; relocating. (Expected {}, got {})", new Object[]{chunkPos, chunkPos, chunkcoordintpair1});
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-index 694778b5c23dbe9c8603c3483476b5252aa079bc..315be30daf0be84efbb4d634dc01e1bf9e6e696e 100644
+index f2539f2aab4086bc6772db9985ca9f75ff6a7c71..aa915195d4aab3e931a92bf844f6dc18a0a59b9e 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -178,6 +178,13 @@ public class ChunkStorage implements AutoCloseable {
diff --git a/patches/server/0347-Optimise-IEntityAccess-getPlayerByUUID.patch b/patches/server/0340-Optimise-IEntityAccess-getPlayerByUUID.patch
index 3a47472ec8..7f70d29289 100644
--- a/patches/server/0347-Optimise-IEntityAccess-getPlayerByUUID.patch
+++ b/patches/server/0340-Optimise-IEntityAccess-getPlayerByUUID.patch
@@ -6,12 +6,12 @@ Subject: [PATCH] Optimise IEntityAccess#getPlayerByUUID
Use the world entity map instead of iterating over all players
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 67a17a0fad369c412a943bb21b574c9cd5031fb8..3cf5ad6a77659073b740a2be3946a39c110dd4b8 100644
+index c998741c3b84829590a555f9ed53f0b379b6e4d7..cdb04f32d61590a2a23903de26bab0a4b334569d 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -394,6 +394,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
- public final com.destroystokyo.paper.io.chunk.ChunkTaskManager asyncChunkTaskManager;
- // Paper end
+@@ -423,6 +423,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ }
+ // Paper end - rewrite chunk system
+ // Paper start - optimise getPlayerByUUID
+ @Nullable
diff --git a/patches/server/0348-Fix-items-not-falling-correctly.patch b/patches/server/0341-Fix-items-not-falling-correctly.patch
index 96b422d772..559c7d740b 100644
--- a/patches/server/0348-Fix-items-not-falling-correctly.patch
+++ b/patches/server/0341-Fix-items-not-falling-correctly.patch
@@ -28,10 +28,10 @@ index 95dc82a0a9bd8a3fa9c704696e7b3dc48bf4d6a0..ebcf58fe51d1fd0cb8a0f5a84cdd349f
float f1 = 0.98F;
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
-index 07e5ece37af6b02210920ce6cc31738274d447a9..7bae24598218dcf0012dd21e619e6f5f984bd6f0 100644
+index e2dfc4d9a16a738dd0fe91838603e1d4370afa56..2861c585710eaa00541ff417a29f1f6a2fb5b46a 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
-@@ -251,7 +251,7 @@ public class ActivationRange
+@@ -257,7 +257,7 @@ public class ActivationRange
isActive = true;
}
// Add a little performance juice to active entities. Skip 1/4 if not immune.
diff --git a/patches/server/0349-Lag-compensate-eating.patch b/patches/server/0342-Lag-compensate-eating.patch
index 2619ab0fc9..2619ab0fc9 100644
--- a/patches/server/0349-Lag-compensate-eating.patch
+++ b/patches/server/0342-Lag-compensate-eating.patch
diff --git a/patches/server/0350-Optimize-call-to-getFluid-for-explosions.patch b/patches/server/0343-Optimize-call-to-getFluid-for-explosions.patch
index 952f1fc45d..952f1fc45d 100644
--- a/patches/server/0350-Optimize-call-to-getFluid-for-explosions.patch
+++ b/patches/server/0343-Optimize-call-to-getFluid-for-explosions.patch
diff --git a/patches/server/0351-Fix-last-firework-in-stack-not-having-effects-when-d.patch b/patches/server/0344-Fix-last-firework-in-stack-not-having-effects-when-d.patch
index 448854642c..448854642c 100644
--- a/patches/server/0351-Fix-last-firework-in-stack-not-having-effects-when-d.patch
+++ b/patches/server/0344-Fix-last-firework-in-stack-not-having-effects-when-d.patch
diff --git a/patches/server/0352-Add-effect-to-block-break-naturally.patch b/patches/server/0345-Add-effect-to-block-break-naturally.patch
index b911824bb1..b911824bb1 100644
--- a/patches/server/0352-Add-effect-to-block-break-naturally.patch
+++ b/patches/server/0345-Add-effect-to-block-break-naturally.patch
diff --git a/patches/server/0353-Entity-Activation-Range-2.0.patch b/patches/server/0346-Entity-Activation-Range-2.0.patch
index 701c64d52c..c223b23f30 100644
--- a/patches/server/0353-Entity-Activation-Range-2.0.patch
+++ b/patches/server/0346-Entity-Activation-Range-2.0.patch
@@ -14,7 +14,7 @@ Adds flying monsters to control ghast and phantoms
Adds villagers as separate config
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 3cf5ad6a77659073b740a2be3946a39c110dd4b8..21fdca3b4bdc49d32deaf3f11d5fecc1ed0d4626 100644
+index cdb04f32d61590a2a23903de26bab0a4b334569d..28922059f1a58ae98282ab59b0a9e9014127b12a 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -2,7 +2,6 @@ package net.minecraft.server.level;
@@ -25,7 +25,7 @@ index 3cf5ad6a77659073b740a2be3946a39c110dd4b8..21fdca3b4bdc49d32deaf3f11d5fecc1
import com.google.common.collect.Lists;
import com.mojang.datafixers.DataFixer;
import com.mojang.datafixers.util.Pair;
-@@ -993,17 +992,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1026,17 +1025,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
++TimingHistory.entityTicks; // Paper - timings
// Spigot start
co.aikar.timings.Timing timer; // Paper
@@ -47,7 +47,7 @@ index 3cf5ad6a77659073b740a2be3946a39c110dd4b8..21fdca3b4bdc49d32deaf3f11d5fecc1
try {
// Paper end - timings
entity.setOldPosAndRot();
-@@ -1014,9 +1013,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1047,9 +1046,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
return Registry.ENTITY_TYPE.getKey(entity.getType()).toString();
});
gameprofilerfiller.incrementCounter("tickNonPassenger");
@@ -61,7 +61,7 @@ index 3cf5ad6a77659073b740a2be3946a39c110dd4b8..21fdca3b4bdc49d32deaf3f11d5fecc1
Iterator iterator = entity.getPassengers().iterator();
while (iterator.hasNext()) {
-@@ -1024,13 +1027,18 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1057,13 +1060,18 @@ public class ServerLevel extends Level implements WorldGenLevel {
this.tickPassenger(entity, entity1);
}
@@ -81,7 +81,7 @@ index 3cf5ad6a77659073b740a2be3946a39c110dd4b8..21fdca3b4bdc49d32deaf3f11d5fecc1
passenger.setOldPosAndRot();
++passenger.tickCount;
ProfilerFiller gameprofilerfiller = this.getProfiler();
-@@ -1039,8 +1047,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1072,8 +1080,17 @@ public class ServerLevel extends Level implements WorldGenLevel {
return Registry.ENTITY_TYPE.getKey(passenger.getType()).toString();
});
gameprofilerfiller.incrementCounter("tickPassenger");
@@ -99,7 +99,7 @@ index 3cf5ad6a77659073b740a2be3946a39c110dd4b8..21fdca3b4bdc49d32deaf3f11d5fecc1
gameprofilerfiller.pop();
Iterator iterator = passenger.getPassengers().iterator();
-@@ -1050,6 +1067,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1083,6 +1100,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
this.tickPassenger(passenger, entity2);
}
@@ -108,7 +108,7 @@ index 3cf5ad6a77659073b740a2be3946a39c110dd4b8..21fdca3b4bdc49d32deaf3f11d5fecc1
} else {
passenger.stopRiding();
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index a2a607e6ae867a4cabe1c2280154c99e93d0e7e9..51fda8b02c6259540db220a55dd8ee1aa651a50c 100644
+index fa128e7f8089339f932e19edf95a8d5c0cc14046..c5b1d2e4b577a3f4ad352dc6a8436c04411efa8b 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -384,6 +384,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -120,7 +120,7 @@ index a2a607e6ae867a4cabe1c2280154c99e93d0e7e9..51fda8b02c6259540db220a55dd8ee1a
protected int numCollisions = 0; // Paper
public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one
@javax.annotation.Nullable
-@@ -857,6 +859,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -909,6 +911,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
} else {
this.wasOnFire = this.isOnFire();
if (movementType == MoverType.PISTON) {
@@ -129,7 +129,7 @@ index a2a607e6ae867a4cabe1c2280154c99e93d0e7e9..51fda8b02c6259540db220a55dd8ee1a
movement = this.limitPistonMovement(movement);
if (movement.equals(Vec3.ZERO)) {
return;
-@@ -869,6 +873,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -921,6 +925,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.stuckSpeedMultiplier = Vec3.ZERO;
this.setDeltaMovement(Vec3.ZERO);
}
@@ -341,7 +341,7 @@ index 540c23f6297c34cf8e7bf8312ceaa5fc868f414c..2866385a64b22b7dc82b6122c62bcea6
+
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 244035be96c5218a27e70c3f80ee9d7dcb4419d5..75e74a6bc0072eb9e77a0570d02d050ade0fa92d 100644
+index cb7e92f2694cefeecebc840959a01e1e1945061a..10a5315c6a2f872d88c080910eeca140ac6fbbc4 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -158,6 +158,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -373,7 +373,7 @@ index 4b55b667eebfe50dfeda89015112e275e71b9777..dda0b32a4989bbead35a2219a969a30b
}
}
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
-index 7bae24598218dcf0012dd21e619e6f5f984bd6f0..0508f43ad396679d3372ae4caf029086a1524109 100644
+index 2861c585710eaa00541ff417a29f1f6a2fb5b46a..b1ed97618d08d7691d24f89e9e9b0ed0f2bddd09 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -1,39 +1,52 @@
@@ -557,9 +557,9 @@ index 7bae24598218dcf0012dd21e619e6f5f984bd6f0..0508f43ad396679d3372ae4caf029086
+ ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate( villagerActivationRange, worldHeight, villagerActivationRange );
+ // Paper end
- world.getEntities().get(maxBB, ActivationRange::activateEntity);
- }
-@@ -166,60 +244,118 @@ public class ActivationRange
+ // Paper start
+ java.util.List<Entity> entities = world.getEntities((Entity)null, maxBB, null);
+@@ -172,60 +250,118 @@ public class ActivationRange
* @param entity
* @return
*/
@@ -621,8 +621,7 @@ index 7bae24598218dcf0012dd21e619e6f5f984bd6f0..0508f43ad396679d3372ae4caf029086
{
- return true;
+ return 20; // Paper
- }
-- if ( entity instanceof Villager && ( (Villager) entity ).canBreed() )
++ }
+ // Paper start
+ if (entity instanceof Bee) {
+ Bee bee = (Bee)entity;
@@ -650,7 +649,8 @@ index 7bae24598218dcf0012dd21e619e6f5f984bd6f0..0508f43ad396679d3372ae4caf029086
+ return config.villagersWorkImmunityFor;
+ }
+ }
-+ }
+ }
+- if ( entity instanceof Villager && ( (Villager) entity ).canBreed() )
+ if ( entity instanceof Llama && ( (Llama) entity ).inCaravan() )
{
- return true;
@@ -678,11 +678,11 @@ index 7bae24598218dcf0012dd21e619e6f5f984bd6f0..0508f43ad396679d3372ae4caf029086
+ // Paper start
+ if (entity instanceof Mob && ((Mob) entity).targetSelector.hasTasks() ) {
+ return 0;
-+ }
+ }
+ if (entity instanceof Pillager) {
+ Pillager pillager = (Pillager) entity;
+ // TODO:?
- }
++ }
+ // Paper end
}
// SPIGOT-6644: Otherwise the target refresh tick will be missed
@@ -695,7 +695,7 @@ index 7bae24598218dcf0012dd21e619e6f5f984bd6f0..0508f43ad396679d3372ae4caf029086
}
/**
-@@ -234,8 +370,19 @@ public class ActivationRange
+@@ -240,8 +376,19 @@ public class ActivationRange
if ( entity instanceof FireworkRocketEntity ) {
return true;
}
@@ -716,7 +716,7 @@ index 7bae24598218dcf0012dd21e619e6f5f984bd6f0..0508f43ad396679d3372ae4caf029086
// Should this entity tick?
if ( !isActive )
-@@ -243,15 +390,19 @@ public class ActivationRange
+@@ -249,15 +396,19 @@ public class ActivationRange
if ( ( MinecraftServer.currentTick - entity.activatedTick - 1 ) % 20 == 0 )
{
// Check immunities every 20 ticks.
diff --git a/patches/server/0354-Increase-Light-Queue-Size.patch b/patches/server/0347-Increase-Light-Queue-Size.patch
index 0c0b38a5fb..d6d29e6daa 100644
--- a/patches/server/0354-Increase-Light-Queue-Size.patch
+++ b/patches/server/0347-Increase-Light-Queue-Size.patch
@@ -14,7 +14,7 @@ light engine on shutdown...
The queue size only puts a cap on max loss, doesn't solve that problem.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 3a085c141a5a6e2df9a85d0e3969363d69824294..27f19abc22e295a5480dbed5df86f5d885ad3b73 100644
+index b81a1e63205011c3f968b29daeb0180ded02b629..836d01ec14f37cc83807a30eda9a1dd7d209dbf6 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -780,7 +780,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0356-Anti-Xray.patch b/patches/server/0348-Anti-Xray.patch
index d73ba252f1..73faf243e0 100644
--- a/patches/server/0356-Anti-Xray.patch
+++ b/patches/server/0348-Anti-Xray.patch
@@ -1044,37 +1044,19 @@ index 7825d6f0fdcfda6212cff8033ec55fb7db236154..000853110c7a89f2d0403a7a2737025a
public ClientboundLevelChunkWithLightPacket(FriendlyByteBuf buf) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index d60173b03baee4a66da1109795bf6a19737b8bd0..55a21a4024debc328d4829477d80c2998e291c22 100644
+index 67823979209f9b8a3f44cd21baca667457ef110a..e3296ca34a47f37cdb3f475fcc66c2412fd39ee0 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1104,7 +1104,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- completablefuture1.thenAcceptAsync((either) -> {
- either.ifLeft((chunk) -> {
- this.tickingGenerated.getAndIncrement();
-- MutableObject<ClientboundLevelChunkWithLightPacket> mutableobject = new MutableObject();
-+ MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> mutableobject = new MutableObject<>(); // Paper - Anti-Xray - Bypass
-
- this.getPlayers(chunkcoordintpair, false).forEach((entityplayer) -> {
- this.playerLoadedChunk(entityplayer, mutableobject, chunk);
-@@ -1283,7 +1283,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- while (objectiterator.hasNext()) {
- ChunkHolder playerchunk = (ChunkHolder) objectiterator.next();
- ChunkPos chunkcoordintpair = playerchunk.getPos();
-- MutableObject<ClientboundLevelChunkWithLightPacket> mutableobject = new MutableObject();
-+ MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> mutableobject = new MutableObject<>(); // Paper - Anti-Xray - Bypass
-
- this.getPlayers(chunkcoordintpair, false).forEach((entityplayer) -> {
- SectionPos sectionposition = entityplayer.getLastSectionPos();
-@@ -1297,7 +1297,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -618,7 +618,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
-- protected void updateChunkTracking(ServerPlayer player, ChunkPos pos, MutableObject<ClientboundLevelChunkWithLightPacket> packet, boolean oldWithinViewDistance, boolean newWithinViewDistance) {
-+ protected void updateChunkTracking(ServerPlayer player, ChunkPos pos, MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> packet, boolean oldWithinViewDistance, boolean newWithinViewDistance) { // Paper - Anti-Xray - Bypass
+- public void updateChunkTracking(ServerPlayer player, ChunkPos pos, MutableObject<ClientboundLevelChunkWithLightPacket> packet, boolean oldWithinViewDistance, boolean newWithinViewDistance) { // Paper - public
++ public void updateChunkTracking(ServerPlayer player, ChunkPos pos, MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> packet, boolean oldWithinViewDistance, boolean newWithinViewDistance) { // Paper - public // Paper - Anti-Xray - Bypass
if (player.level == this.level) {
if (newWithinViewDistance && !oldWithinViewDistance) {
ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos.toLong());
-@@ -1834,12 +1834,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1099,12 +1099,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
@@ -1097,10 +1079,10 @@ index d60173b03baee4a66da1109795bf6a19737b8bd0..55a21a4024debc328d4829477d80c299
List<Entity> list = Lists.newArrayList();
List<Entity> list1 = Lists.newArrayList();
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 21fdca3b4bdc49d32deaf3f11d5fecc1ed0d4626..5622917a2884e87d43abfaf58e722957931b3178 100644
+index 4a06da72c8b769c683e9eb03817bd9381a0cf099..b8ac1df1f2e2114a16d417b31fbbbc734074b2b6 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -405,7 +405,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -434,7 +434,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
// Holder holder = worlddimension.typeHolder(); // CraftBukkit - decompile error
// Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error
@@ -1132,7 +1114,7 @@ index 3fadf6b46cc722ad81cf810c0761cf717e9f9b78..af00442931f9f6cf878bd61137c2f29f
public void destroyAndAck(BlockPos pos, int sequence, String reason) {
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 75e74a6bc0072eb9e77a0570d02d050ade0fa92d..7bd8ffb6bfba7f7188532ae3788701c08e1d624a 100644
+index 10a5315c6a2f872d88c080910eeca140ac6fbbc4..0dd6ccc5b281ea46d2d12eb99c28335bdbe66d7e 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -173,6 +173,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -1169,10 +1151,10 @@ index 75e74a6bc0072eb9e77a0570d02d050ade0fa92d..7bd8ffb6bfba7f7188532ae3788701c0
if (iblockdata1 == null) {
// CraftBukkit start - remove blockstate if failed (or the same)
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-index 6cec5cda20531aadf8e2148908a70f8b573d7d82..dc164608bfb2fb18a1adf83fa10bac4028dcac0a 100644
+index 41e61e6c128f22224665af3f07cd11d69a43062b..54e57791f6220325d05939decae46dc4d91d1906 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-@@ -99,17 +99,19 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
+@@ -140,17 +140,19 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
}
}
@@ -1196,7 +1178,7 @@ index 6cec5cda20531aadf8e2148908a70f8b573d7d82..dc164608bfb2fb18a1adf83fa10bac40
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index e63086aba2512051fe1321f6e7e72b40276f5dde..004c8abeafa53cdc3efa0f45742132e3ad492d70 100644
+index 5cd8755dc8db2f1fdb32d2db3a5a137ca7cad3c7..bc573695ace324c7cd536bc24fbfc66a53ef9fa0 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -93,7 +93,7 @@ public class LevelChunk extends ChunkAccess {
@@ -1205,9 +1187,9 @@ index e63086aba2512051fe1321f6e7e72b40276f5dde..004c8abeafa53cdc3efa0f45742132e3
public LevelChunk(Level world, ChunkPos pos, UpgradeData upgradeData, LevelChunkTicks<Block> blockTickScheduler, LevelChunkTicks<Fluid> fluidTickScheduler, long inhabitedTime, @Nullable LevelChunkSection[] sectionArrayInitializer, @Nullable LevelChunk.PostLoadProcessor entityLoader, @Nullable BlendingData blendingData) {
- super(pos, upgradeData, world, world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), inhabitedTime, sectionArrayInitializer, blendingData);
+ super(pos, upgradeData, world, net.minecraft.server.MinecraftServer.getServer().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), inhabitedTime, sectionArrayInitializer, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry
- this.tickersInLevel = Maps.newHashMap();
- this.clientLightReady = false;
- this.level = (ServerLevel) world; // CraftBukkit - type
+ // Paper start - rewrite light engine
+ this.setBlockNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world));
+ this.setSkyNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(world));
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
index ae37e97e52557b48f129cc02eeea395378a48444..785fbcf9bafcdec1c5be213de3d8512690023415 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -1247,7 +1229,7 @@ index ae37e97e52557b48f129cc02eeea395378a48444..785fbcf9bafcdec1c5be213de3d85126
public int getSerializedSize() {
diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-index 78e20871e4bd8d92c4475f797a55733c68f6aeb4..b688d239ff11b315f60cd980d8f6780b982a865b 100644
+index 33eecdac9d844af2f70aad97c4788b138dab8896..8f5114b6d64461866140b6d2ced3f838a1228851 100644
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -29,6 +29,7 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
@@ -1475,7 +1457,7 @@ index 9a2bf744abd8916d492e901be889223591bac3fd..a27fce0f1af9776a713bf1b5277869ed
int getSerializedSize();
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 864e591b10360b0f12fe5c5a650da372555ebd10..f26a08f81495dde6205b34254d159b042e5a6ea9 100644
+index f4bf0ecde863f1795d764d8cc8d6525af02356ea..3c61be19c65b2da9283b2aba2b4e66f84bac6e1c 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
@@ -69,7 +69,7 @@ import org.slf4j.Logger;
@@ -1487,7 +1469,7 @@ index 864e591b10360b0f12fe5c5a650da372555ebd10..f26a08f81495dde6205b34254d159b04
private static final Logger LOGGER = LogUtils.getLogger();
private static final String TAG_UPGRADE_DATA = "UpgradeData";
private static final String BLOCK_TICKS_TAG = "block_ticks";
-@@ -149,16 +149,20 @@ public class ChunkSerializer {
+@@ -164,16 +164,20 @@ public class ChunkSerializer {
if (k >= 0 && k < achunksection.length) {
Logger logger;
PalettedContainer datapaletteblock;
@@ -1510,7 +1492,7 @@ index 864e591b10360b0f12fe5c5a650da372555ebd10..f26a08f81495dde6205b34254d159b04
}
PalettedContainer object; // CraftBukkit - read/write
-@@ -171,7 +175,7 @@ public class ChunkSerializer {
+@@ -186,7 +190,7 @@ public class ChunkSerializer {
Objects.requireNonNull(logger);
object = ((DataResult<PalettedContainer<Holder<Biome>>>) dataresult).getOrThrow(false, logger::error); // CraftBukkit - decompile error
} else {
@@ -1519,7 +1501,7 @@ index 864e591b10360b0f12fe5c5a650da372555ebd10..f26a08f81495dde6205b34254d159b04
}
LevelChunkSection chunksection = new LevelChunkSection(b0, datapaletteblock, (PalettedContainer) object); // CraftBukkit - read/write
-@@ -439,7 +443,7 @@ public class ChunkSerializer {
+@@ -440,7 +444,7 @@ public class ChunkSerializer {
// CraftBukkit start - read/write
private static Codec<PalettedContainer<Holder<Biome>>> makeBiomeCodecRW(Registry<Biome> iregistry) {
@@ -1529,7 +1511,7 @@ index 864e591b10360b0f12fe5c5a650da372555ebd10..f26a08f81495dde6205b34254d159b04
// CraftBukkit end
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-index dfdcc01c7cb2864d9b5f572c0cafedf16063edd8..d9c2e7e18e1ede37d92cecb8ddb32dae1472bd1c 100644
+index 1827fdc551095ab411d6b43b94106273f53386c8..7f23c69e7d6232190a2c75cbc79e1ea56971e8fb 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
@@ -54,7 +54,7 @@ public class CraftChunk implements Chunk {
@@ -1542,7 +1524,7 @@ index dfdcc01c7cb2864d9b5f572c0cafedf16063edd8..d9c2e7e18e1ede37d92cecb8ddb32dae
public CraftChunk(net.minecraft.world.level.chunk.LevelChunk chunk) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index e8c11b59dc50fd9c5bbf073b66d0cd9c504d7c25..8cc2a35486e8c6433e722ddc5e776c3332e7c7fe 100644
+index a7ee6b54e763d5ff2eb4afdbccdf5becb4e1f305..826bd8ae116a94ddc59815d4dbb37ffee71780e7 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2235,7 +2235,7 @@ public final class CraftServer implements Server {
@@ -1555,12 +1537,12 @@ index e8c11b59dc50fd9c5bbf073b66d0cd9c504d7c25..8cc2a35486e8c6433e722ddc5e776c33
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 868d9d13b03751b24c4b6695f97ad343ff8d0b8e..a8ab324bfbaaf946af5998402588244465dd7286 100644
+index 37efb3305b4f740d9092c2a253b93f1925ee75d5..414522b641cd866822c13a753cc623c463004fd6 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -406,11 +406,16 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -410,11 +410,16 @@ public class CraftWorld extends CraftRegionAccessor implements World {
List<ServerPlayer> playersInRange = playerChunk.playerProvider.getPlayers(playerChunk.getPos(), false);
- if (playersInRange.isEmpty()) return;
+ if (playersInRange.isEmpty()) return true; // Paper - rewrite player chunk loader
- ClientboundLevelChunkWithLightPacket refreshPacket = new ClientboundLevelChunkWithLightPacket(chunk, this.world.getLightEngine(), null, null, true);
+ // Paper start - Anti-Xray - Bypass
@@ -1575,8 +1557,8 @@ index 868d9d13b03751b24c4b6695f97ad343ff8d0b8e..a8ab324bfbaaf946af59984025882444
+ }));
+ // Paper end
}
- });
- });
+ // Paper - rewrite player chunk loader
+
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
index 960405935e395a31c0300773c41413801cf0d290..4a23d03757e1735b9ebb8c003adcc0374a7d672d 100644
--- a/src/main/java/org/bukkit/craftbukkit/generator/OldCraftChunkData.java
diff --git a/patches/server/0357-Implement-alternative-item-despawn-rate.patch b/patches/server/0349-Implement-alternative-item-despawn-rate.patch
index db8d53f699..db8d53f699 100644
--- a/patches/server/0357-Implement-alternative-item-despawn-rate.patch
+++ b/patches/server/0349-Implement-alternative-item-despawn-rate.patch
diff --git a/patches/server/0358-Tracking-Range-Improvements.patch b/patches/server/0350-Tracking-Range-Improvements.patch
index 53db6644cf..8a6f1b8071 100644
--- a/patches/server/0358-Tracking-Range-Improvements.patch
+++ b/patches/server/0350-Tracking-Range-Improvements.patch
@@ -8,10 +8,10 @@ Sets tracking range of watermobs to animals instead of misc and simplifies code
Also ignores Enderdragon, defaulting it to Mojang's setting
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 55a21a4024debc328d4829477d80c2998e291c22..0b68e6c7ef63460d596050ed55039a5ba3cefb24 100644
+index e3296ca34a47f37cdb3f475fcc66c2412fd39ee0..3f1bd5e0efa72e8805fcf74f20c0d46f67cfde6b 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -2022,6 +2022,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1287,6 +1287,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
while (iterator.hasNext()) {
Entity entity = (Entity) iterator.next();
int j = entity.getType().clientTrackingRange() * 16;
diff --git a/patches/server/0359-Fix-items-vanishing-through-end-portal.patch b/patches/server/0351-Fix-items-vanishing-through-end-portal.patch
index 32e4e717c9..d3fd369c6d 100644
--- a/patches/server/0359-Fix-items-vanishing-through-end-portal.patch
+++ b/patches/server/0351-Fix-items-vanishing-through-end-portal.patch
@@ -13,10 +13,10 @@ Quickly loading the exact world spawn chunk before searching the
heightmap resolves the issue without having to load all spawn chunks.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 51fda8b02c6259540db220a55dd8ee1aa651a50c..f94462c3aeec3a71b30de1834fb72934e77c35ac 100644
+index c5b1d2e4b577a3f4ad352dc6a8436c04411efa8b..1b546c979d7ebe5f77fe7ad7fc422d94e8181aac 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -3123,6 +3123,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3175,6 +3175,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (flag1) {
blockposition1 = ServerLevel.END_SPAWN_POINT;
} else {
diff --git a/patches/server/0360-implement-optional-per-player-mob-spawns.patch b/patches/server/0352-implement-optional-per-player-mob-spawns.patch
index 7aa2416ddc..04f628363c 100644
--- a/patches/server/0360-implement-optional-per-player-mob-spawns.patch
+++ b/patches/server/0352-implement-optional-per-player-mob-spawns.patch
@@ -252,29 +252,30 @@ index 0000000000000000000000000000000000000000..11de56afaf059b00fa5bec293516bcdc
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 0b68e6c7ef63460d596050ed55039a5ba3cefb24..9e940920f6ff6a43d7162d965936c171c55ed570 100644
+index 3f1bd5e0efa72e8805fcf74f20c0d46f67cfde6b..638d768393c507ff855c7b517434b61736f680d1 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -159,6 +159,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -145,6 +145,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
private final Long2LongMap chunkSaveCooldowns;
private final Queue<Runnable> unloadQueue;
int viewDistance;
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
- // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
- public final CallbackExecutor callbackExecutor = new CallbackExecutor();
-@@ -199,16 +200,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ // Paper - rewrite chunk system
+
+@@ -157,11 +158,21 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
int chunkX = MCUtil.getChunkCoordinate(player.getX());
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
// Note: players need to be explicitly added to distance maps before they can be updated
+ // Paper start - per player mob spawning
+ if (this.playerMobDistanceMap != null) {
-+ this.playerMobDistanceMap.add(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
++ this.playerMobDistanceMap.add(player, chunkX, chunkZ, net.minecraft.server.ChunkSystem.getTickViewDistance(player));
+ }
+ // Paper end - per player mob spawning
}
void removePlayerFromDistanceMaps(ServerPlayer player) {
+ this.playerChunkManager.removePlayer(player); // Paper - replace chunk loader
+ // Paper start - per player mob spawning
+ if (this.playerMobDistanceMap != null) {
@@ -284,18 +285,19 @@ index 0b68e6c7ef63460d596050ed55039a5ba3cefb24..9e940920f6ff6a43d7162d965936c171
}
void updateMaps(ServerPlayer player) {
- int chunkX = MCUtil.getChunkCoordinate(player.getX());
+@@ -169,6 +180,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
// Note: players need to be explicitly added to distance maps before they can be updated
+ this.playerChunkManager.updatePlayer(player); // Paper - replace chunk loader
+ // Paper start - per player mob spawning
+ if (this.playerMobDistanceMap != null) {
-+ this.playerMobDistanceMap.update(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
++ this.playerMobDistanceMap.update(player, chunkX, chunkZ, net.minecraft.server.ChunkSystem.getTickViewDistance(player));
+ }
+ // Paper end - per player mob spawning
}
// Paper end
// Paper start
-@@ -305,6 +321,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -250,6 +266,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new);
this.regionManagers.add(this.dataRegionManager);
// Paper end
@@ -303,10 +305,10 @@ index 0b68e6c7ef63460d596050ed55039a5ba3cefb24..9e940920f6ff6a43d7162d965936c171
}
protected ChunkGenerator generator() {
-@@ -356,6 +373,30 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- }
+@@ -271,6 +288,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ });
}
- // Paper end
+
+ // Paper start
+ public void updatePlayerMobTypeMap(Entity entity) {
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
@@ -331,31 +333,15 @@ index 0b68e6c7ef63460d596050ed55039a5ba3cefb24..9e940920f6ff6a43d7162d965936c171
+ return entityPlayer.mobCounts[mobCategory.ordinal()];
+ }
+ // Paper end
-
++
private static double euclideanDistanceSquared(ChunkPos pos, Entity entity) {
double d0 = (double) SectionPos.sectionToBlockCoord(pos.x, 8);
-diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
-index b9b56068cdacd984f873cfb2a06a312e9912893d..9309ea89a440606be3e56ef634f5048a72b0009e 100644
---- a/src/main/java/net/minecraft/server/level/DistanceManager.java
-+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
-@@ -466,6 +466,12 @@ public abstract class DistanceManager {
-
- }
-
-+ // Paper start
-+ public int getSimulationDistance() {
-+ return this.simulationDistance;
-+ }
-+ // Paper end
-+
- public int getNaturalSpawnChunkCount() {
- this.naturalSpawnChunkCounter.runAllUpdates();
- return this.naturalSpawnChunkCounter.chunks.size();
+ double d1 = (double) SectionPos.sectionToBlockCoord(pos.z, 8);
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index d1fa959b6e872565b689ee6b4f1bed9ad9e4077e..f982241898c4aeaf50854e67fe188478f2f2b186 100644
+index 70077d3f359944e2df29198ae156be477ebc278d..2d0a8668d1828fe085631079798a36374a0d4844 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -764,7 +764,18 @@ public class ServerChunkCache extends ChunkSource {
+@@ -692,7 +692,18 @@ public class ServerChunkCache extends ChunkSource {
gameprofilerfiller.push("naturalSpawnCount");
this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
int l = this.distanceManager.getNaturalSpawnChunkCount();
@@ -376,10 +362,10 @@ index d1fa959b6e872565b689ee6b4f1bed9ad9e4077e..f982241898c4aeaf50854e67fe188478
this.lastSpawnState = spawnercreature_d;
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index b36effc88b516958a7c0a46a7eb3a77a98ad3a20..582e08eaeee8fb6c777ad451ccc0436b02cac000 100644
+index 9cae5379d60c8d20ae6966850f7f13640742f9b7..f2808d62c15c586dff0313e6d27ef92d45f66dc7 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -236,6 +236,11 @@ public class ServerPlayer extends Player {
+@@ -235,6 +235,11 @@ public class ServerPlayer extends Player {
public boolean queueHealthUpdatePacket = false;
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
// Paper end
@@ -397,8 +383,8 @@ index b36effc88b516958a7c0a46a7eb3a77a98ad3a20..582e08eaeee8fb6c777ad451ccc0436b
this.maxHealthCache = this.getMaxHealth();
+ this.cachedSingleMobDistanceMap = new com.destroystokyo.paper.util.PooledHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
}
- // Paper start - Chunk priority
- public BlockPos getPointInFront(double inFront) {
+
+ // Yes, this doesn't match Vanilla, but it's the best we can do for now.
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index b4098068e674b639e82c07e5d60e4e2120b4305b..fa23e9c476d4edc6176d8b8a6cb13c52d2f66a87 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
diff --git a/patches/server/0362-Bees-get-gravity-in-void.-Fixes-MC-167279.patch b/patches/server/0353-Bees-get-gravity-in-void.-Fixes-MC-167279.patch
index 2b398af935..2b398af935 100644
--- a/patches/server/0362-Bees-get-gravity-in-void.-Fixes-MC-167279.patch
+++ b/patches/server/0353-Bees-get-gravity-in-void.-Fixes-MC-167279.patch
diff --git a/patches/server/0363-Optimise-getChunkAt-calls-for-loaded-chunks.patch b/patches/server/0354-Optimise-getChunkAt-calls-for-loaded-chunks.patch
index 2211c9793e..04dd0e4e33 100644
--- a/patches/server/0363-Optimise-getChunkAt-calls-for-loaded-chunks.patch
+++ b/patches/server/0354-Optimise-getChunkAt-calls-for-loaded-chunks.patch
@@ -7,10 +7,10 @@ bypass the need to get a player chunk, then get the either,
then unwrap it...
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index f982241898c4aeaf50854e67fe188478f2f2b186..38788e0ab7e881102d38bae53ba9d2d4f62b99d4 100644
+index 2d0a8668d1828fe085631079798a36374a0d4844..f3375712ae25ef1503a030b3059477f350d922da 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -444,6 +444,12 @@ public class ServerChunkCache extends ChunkSource {
+@@ -420,6 +420,12 @@ public class ServerChunkCache extends ChunkSource {
return this.getChunk(x, z, leastStatus, create);
}, this.mainThreadProcessor).join();
} else {
@@ -23,8 +23,8 @@ index f982241898c4aeaf50854e67fe188478f2f2b186..38788e0ab7e881102d38bae53ba9d2d4
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
gameprofilerfiller.incrementCounter("getChunk");
-@@ -499,39 +505,7 @@ public class ServerChunkCache extends ChunkSource {
- if (Thread.currentThread() != this.mainThread) {
+@@ -463,39 +469,7 @@ public class ServerChunkCache extends ChunkSource {
+ if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
return null;
} else {
- this.level.getProfiler().incrementCounter("getChunkNow");
diff --git a/patches/server/0364-Add-debug-for-sync-chunk-loads.patch b/patches/server/0355-Add-debug-for-sync-chunk-loads.patch
index 909d9b70a6..c26522f512 100644
--- a/patches/server/0364-Add-debug-for-sync-chunk-loads.patch
+++ b/patches/server/0355-Add-debug-for-sync-chunk-loads.patch
@@ -194,7 +194,7 @@ index 0000000000000000000000000000000000000000..0bb4aaa546939b67a5d22865190f3047
+ }
+}
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
-index f44ab1d71210e84328661c0feb662989a5635b6d..25f6d1c15cdfb4abdf1edd2ad9bbdc0e37b546f3 100644
+index 65ee888280f917ccd11146505b7389513280a863..04bf08cbe45763f1338390c5ab4b0dcb334bd07a 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
@@ -5,6 +5,7 @@ import io.papermc.paper.command.subcommands.EntityCommand;
@@ -207,8 +207,8 @@ index f44ab1d71210e84328661c0feb662989a5635b6d..25f6d1c15cdfb4abdf1edd2ad9bbdc0e
import java.util.ArrayList;
@@ -44,6 +45,7 @@ public final class PaperCommand extends Command {
commands.put(Set.of("version"), new VersionCommand());
- commands.put(Set.of("debug", "chunkinfo"), new ChunkDebugCommand());
commands.put(Set.of("fixlight"), new FixLightCommand());
+ commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand());
+ commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
return commands.entrySet().stream()
@@ -298,25 +298,25 @@ index 0000000000000000000000000000000000000000..1120aef5b0dd983c467167f77245884e
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 38788e0ab7e881102d38bae53ba9d2d4f62b99d4..471cc00c677b6581ba84c8cac25d2246c2a14bc9 100644
+index f3375712ae25ef1503a030b3059477f350d922da..60264feb656861d5a9474fe4285ac69d8d12269e 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -478,6 +478,7 @@ public class ServerChunkCache extends ChunkSource {
- this.level.asyncChunkTaskManager.raisePriority(x1, z1, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY);
- com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.level, x1, z1);
+@@ -444,6 +444,7 @@ public class ServerChunkCache extends ChunkSource {
+ // Paper start - async chunk io/loading
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
// Paper end
+ com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info
this.level.timings.syncChunkLoad.startTiming(); // Paper
chunkproviderserver_b.managedBlock(completablefuture::isDone);
- com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 5622917a2884e87d43abfaf58e722957931b3178..ed8014ed36f8354aa1ae07689e9315c0c0d8867a 100644
+index dadfd5781bf3dea8f68c184e0059d477d2d27036..923526d1a04f4a5999b63ddf26bbf51c9192494e 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -392,6 +392,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
- };
- public final com.destroystokyo.paper.io.chunk.ChunkTaskManager asyncChunkTaskManager;
- // Paper end
+@@ -421,6 +421,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ return this.entityLookup;
+ }
+ // Paper end - rewrite chunk system
+ // Paper start
+ @Override
+ public boolean hasChunk(int chunkX, int chunkZ) {
diff --git a/patches/server/0365-Remove-garbage-Java-version-check.patch b/patches/server/0356-Remove-garbage-Java-version-check.patch
index f2867bfa7d..f2867bfa7d 100644
--- a/patches/server/0365-Remove-garbage-Java-version-check.patch
+++ b/patches/server/0356-Remove-garbage-Java-version-check.patch
diff --git a/patches/server/0366-Add-ThrownEggHatchEvent.patch b/patches/server/0357-Add-ThrownEggHatchEvent.patch
index c026427598..c026427598 100644
--- a/patches/server/0366-Add-ThrownEggHatchEvent.patch
+++ b/patches/server/0357-Add-ThrownEggHatchEvent.patch
diff --git a/patches/server/0367-Entity-Jump-API.patch b/patches/server/0358-Entity-Jump-API.patch
index eb51ab24b2..eb51ab24b2 100644
--- a/patches/server/0367-Entity-Jump-API.patch
+++ b/patches/server/0358-Entity-Jump-API.patch
diff --git a/patches/server/0368-Add-option-to-nerf-pigmen-from-nether-portals.patch b/patches/server/0359-Add-option-to-nerf-pigmen-from-nether-portals.patch
index a2cd3658b8..7e248d9bd6 100644
--- a/patches/server/0368-Add-option-to-nerf-pigmen-from-nether-portals.patch
+++ b/patches/server/0359-Add-option-to-nerf-pigmen-from-nether-portals.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add option to nerf pigmen from nether portals
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index f94462c3aeec3a71b30de1834fb72934e77c35ac..152c2f10a71c231a43a21e2016b040c785417589 100644
+index 1b546c979d7ebe5f77fe7ad7fc422d94e8181aac..904015183bbaa3ac3976e3d81a7968ebb18c0c41 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -386,6 +386,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -16,7 +16,7 @@ index f94462c3aeec3a71b30de1834fb72934e77c35ac..152c2f10a71c231a43a21e2016b040c7
protected int numCollisions = 0; // Paper
public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one
@javax.annotation.Nullable
-@@ -1992,6 +1993,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2044,6 +2045,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (spawnedViaMobSpawner) {
nbt.putBoolean("Paper.FromMobSpawner", true);
}
@@ -26,7 +26,7 @@ index f94462c3aeec3a71b30de1834fb72934e77c35ac..152c2f10a71c231a43a21e2016b040c7
// Paper end
return nbt;
} catch (Throwable throwable) {
-@@ -2133,6 +2137,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2185,6 +2189,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
spawnedViaMobSpawner = nbt.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status
diff --git a/patches/server/0369-Make-the-GUI-graph-fancier.patch b/patches/server/0360-Make-the-GUI-graph-fancier.patch
index b97ce20f84..b97ce20f84 100644
--- a/patches/server/0369-Make-the-GUI-graph-fancier.patch
+++ b/patches/server/0360-Make-the-GUI-graph-fancier.patch
diff --git a/patches/server/0361-Avoid-hopper-searches-if-there-are-no-items.patch b/patches/server/0361-Avoid-hopper-searches-if-there-are-no-items.patch
deleted file mode 100644
index bc2e7f6645..0000000000
--- a/patches/server/0361-Avoid-hopper-searches-if-there-are-no-items.patch
+++ /dev/null
@@ -1,124 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: CullanP <[email protected]>
-Date: Thu, 3 Mar 2016 02:13:38 -0600
-Subject: [PATCH] Avoid hopper searches if there are no items
-
-Hoppers searching for items and minecarts is the most expensive part of hopper ticking.
-We keep track of the number of minecarts and items in a chunk.
-If there are no items in the chunk, we skip searching for items.
-If there are no minecarts in the chunk, we skip searching for them.
-
-Usually hoppers aren't near items, so we can skip most item searches.
-And since minecart hoppers are used _very_ rarely near we can avoid alot of searching there.
-
-Combined, this adds up a lot.
-
-diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 7bd8ffb6bfba7f7188532ae3788701c08e1d624a..ef4e48127168acdd614bbdcac97a98da5240928e 100644
---- a/src/main/java/net/minecraft/world/level/Level.java
-+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -966,7 +966,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
- }
- }
-
-- });
-+ }, predicate == net.minecraft.world.entity.EntitySelector.CONTAINER_ENTITY_SELECTOR); // Paper
- return list;
- }
-
-diff --git a/src/main/java/net/minecraft/world/level/entity/EntitySection.java b/src/main/java/net/minecraft/world/level/entity/EntitySection.java
-index 524f3c42964eb83c9109bcc548a1075f1e295411..e9aee7d11798c3bd990466f101e9e342686de11c 100644
---- a/src/main/java/net/minecraft/world/level/entity/EntitySection.java
-+++ b/src/main/java/net/minecraft/world/level/entity/EntitySection.java
-@@ -13,6 +13,10 @@ public class EntitySection<T extends EntityAccess> {
- private static final Logger LOGGER = LogUtils.getLogger();
- private final ClassInstanceMultiMap<T> storage;
- private Visibility chunkStatus;
-+ // Paper start - track number of items and minecarts
-+ public int itemCount;
-+ public int inventoryEntityCount;
-+ // Paper end
-
- public EntitySection(Class<T> entityClass, Visibility status) {
- this.chunkStatus = status;
-@@ -20,10 +24,24 @@ public class EntitySection<T extends EntityAccess> {
- }
-
- public void add(T entity) {
-+ // Paper start
-+ if (entity instanceof net.minecraft.world.entity.item.ItemEntity) {
-+ this.itemCount++;
-+ } else if (entity instanceof net.minecraft.world.Container) {
-+ this.inventoryEntityCount++;
-+ }
-+ // Paper end
- this.storage.add(entity);
- }
-
- public boolean remove(T entity) {
-+ // Paper start
-+ if (entity instanceof net.minecraft.world.entity.item.ItemEntity) {
-+ this.itemCount--;
-+ } else if (entity instanceof net.minecraft.world.Container) {
-+ this.inventoryEntityCount--;
-+ }
-+ // Paper end
- return this.storage.remove(entity);
- }
-
-diff --git a/src/main/java/net/minecraft/world/level/entity/EntitySectionStorage.java b/src/main/java/net/minecraft/world/level/entity/EntitySectionStorage.java
-index 4c1e7c219e1ca153be4423347bd239ebaec4a31d..f54ca6383298848b2ee7108c41fcea593f924881 100644
---- a/src/main/java/net/minecraft/world/level/entity/EntitySectionStorage.java
-+++ b/src/main/java/net/minecraft/world/level/entity/EntitySectionStorage.java
-@@ -112,13 +112,20 @@ public class EntitySectionStorage<T extends EntityAccess> {
- }
-
- public void getEntities(AABB box, Consumer<T> action) {
-+ // Paper start
-+ this.getEntities(box, action, false);
-+ }
-+ public void getEntities(AABB box, Consumer<T> action, boolean isContainerSearch) {
-+ // Paper end
- this.forEachAccessibleNonEmptySection(box, (section) -> {
-+ if (isContainerSearch && section.inventoryEntityCount <= 0) return; // Paper
- section.getEntities(box, action);
- });
- }
-
- public <U extends T> void getEntities(EntityTypeTest<T, U> filter, AABB box, Consumer<U> action) {
- this.forEachAccessibleNonEmptySection(box, (section) -> {
-+ if (filter.getBaseClass() == net.minecraft.world.entity.item.ItemEntity.class && section.itemCount <= 0) return; // Paper
- section.getEntities(filter, box, action);
- });
- }
-diff --git a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java
-index 9723a0ad61548c8c6c4c5ef20a150d5b17d80afd..da1ad0b2679e392ed81b50c15f012c63cb5c939e 100644
---- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java
-+++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetter.java
-@@ -17,6 +17,7 @@ public interface LevelEntityGetter<T extends EntityAccess> {
- <U extends T> void get(EntityTypeTest<T, U> filter, Consumer<U> action);
-
- void get(AABB box, Consumer<T> action);
-+ void get(AABB box, Consumer<T> action, boolean isContainerSearch); // Paper
-
- <U extends T> void get(EntityTypeTest<T, U> filter, AABB box, Consumer<U> action);
- }
-diff --git a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
-index d5129c12c79eb6fe6b7e5f8eed4d24226423f5fd..3b13f6ea36a3bfecabe09221eb5c48dddab119db 100644
---- a/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
-+++ b/src/main/java/net/minecraft/world/level/entity/LevelEntityGetterAdapter.java
-@@ -38,7 +38,13 @@ public class LevelEntityGetterAdapter<T extends EntityAccess> implements LevelEn
-
- @Override
- public void get(AABB box, Consumer<T> action) {
-- this.sectionStorage.getEntities(box, action);
-+ // Paper start
-+ this.get(box, action, false);
-+ }
-+ @Override
-+ public void get(AABB box, Consumer<T> action, boolean isContainerSearch) {
-+ this.sectionStorage.getEntities(box, action, isContainerSearch);
-+ // Paper end
- }
-
- @Override
diff --git a/patches/server/0370-add-hand-to-BlockMultiPlaceEvent.patch b/patches/server/0361-add-hand-to-BlockMultiPlaceEvent.patch
index 486014f2be..486014f2be 100644
--- a/patches/server/0370-add-hand-to-BlockMultiPlaceEvent.patch
+++ b/patches/server/0361-add-hand-to-BlockMultiPlaceEvent.patch
diff --git a/patches/server/0371-Validate-tripwire-hook-placement-before-update.patch b/patches/server/0362-Validate-tripwire-hook-placement-before-update.patch
index fd8a74807c..fd8a74807c 100644
--- a/patches/server/0371-Validate-tripwire-hook-placement-before-update.patch
+++ b/patches/server/0362-Validate-tripwire-hook-placement-before-update.patch
diff --git a/patches/server/0372-Add-option-to-allow-iron-golems-to-spawn-in-air.patch b/patches/server/0363-Add-option-to-allow-iron-golems-to-spawn-in-air.patch
index f058d157d0..f058d157d0 100644
--- a/patches/server/0372-Add-option-to-allow-iron-golems-to-spawn-in-air.patch
+++ b/patches/server/0363-Add-option-to-allow-iron-golems-to-spawn-in-air.patch
diff --git a/patches/server/0373-Configurable-chance-of-villager-zombie-infection.patch b/patches/server/0364-Configurable-chance-of-villager-zombie-infection.patch
index ce51b7964c..ce51b7964c 100644
--- a/patches/server/0373-Configurable-chance-of-villager-zombie-infection.patch
+++ b/patches/server/0364-Configurable-chance-of-villager-zombie-infection.patch
diff --git a/patches/server/0374-Optimise-Chunk-getFluid.patch b/patches/server/0365-Optimise-Chunk-getFluid.patch
index 670a1cca43..1d78a740b0 100644
--- a/patches/server/0374-Optimise-Chunk-getFluid.patch
+++ b/patches/server/0365-Optimise-Chunk-getFluid.patch
@@ -8,10 +8,10 @@ faster on its own, however removing the try catch makes it
easier to inline due to code size
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 004c8abeafa53cdc3efa0f45742132e3ad492d70..ebe17598d3ef17cc5f0e99b876ed67936d46060d 100644
+index bc573695ace324c7cd536bc24fbfc66a53ef9fa0..bd1c957a9405ccf18f110c7976cf8e0af922cf78 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -381,18 +381,20 @@ public class LevelChunk extends ChunkAccess {
+@@ -427,18 +427,20 @@ public class LevelChunk extends ChunkAccess {
}
public FluidState getFluidState(int x, int y, int z) {
@@ -38,7 +38,7 @@ index 004c8abeafa53cdc3efa0f45742132e3ad492d70..ebe17598d3ef17cc5f0e99b876ed6793
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "Getting fluid state");
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Block being got");
-@@ -402,6 +404,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -448,6 +450,7 @@ public class LevelChunk extends ChunkAccess {
});
throw new ReportedException(crashreport);
}
diff --git a/patches/server/0375-Set-spigots-verbose-world-setting-to-false-by-def.patch b/patches/server/0366-Set-spigots-verbose-world-setting-to-false-by-def.patch
index 45ea278818..45ea278818 100644
--- a/patches/server/0375-Set-spigots-verbose-world-setting-to-false-by-def.patch
+++ b/patches/server/0366-Set-spigots-verbose-world-setting-to-false-by-def.patch
diff --git a/patches/server/0376-Add-tick-times-API-and-mspt-command.patch b/patches/server/0367-Add-tick-times-API-and-mspt-command.patch
index b45a036e40..4fe85cbca3 100644
--- a/patches/server/0376-Add-tick-times-API-and-mspt-command.patch
+++ b/patches/server/0367-Add-tick-times-API-and-mspt-command.patch
@@ -125,7 +125,7 @@ index 6a00f3d38da8107825ab1d405f337fd077b09f72..d31b5ed47cffc61c90c926a0cd2005b7
public static void registerCommands(final MinecraftServer server) {
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 27f19abc22e295a5480dbed5df86f5d885ad3b73..12ca13f5b0556c53fd36d638ee4fa854b89ee8ec 100644
+index 668d7479bb9a56f710ead272166abd615b9463cc..a8cbbe13f7da0fe89191196acfa8617e683a66e7 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -237,6 +237,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -140,7 +140,7 @@ index 27f19abc22e295a5480dbed5df86f5d885ad3b73..12ca13f5b0556c53fd36d638ee4fa854
@Nullable
private KeyPair keyPair;
@Nullable
-@@ -1361,6 +1366,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1355,6 +1360,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.averageTickTime = this.averageTickTime * 0.8F + (float) l / 1000000.0F * 0.19999999F;
long i1 = Util.getNanos();
@@ -153,7 +153,7 @@ index 27f19abc22e295a5480dbed5df86f5d885ad3b73..12ca13f5b0556c53fd36d638ee4fa854
this.frameTimer.logFrameDuration(i1 - i);
this.profiler.pop();
org.spigotmc.WatchdogThread.tick(); // Spigot
-@@ -2539,4 +2550,30 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2533,4 +2544,30 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public static record ServerResourcePackInfo(String url, String hash, boolean isRequired, @Nullable Component prompt) {
}
@@ -185,7 +185,7 @@ index 27f19abc22e295a5480dbed5df86f5d885ad3b73..12ca13f5b0556c53fd36d638ee4fa854
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 8cc2a35486e8c6433e722ddc5e776c3332e7c7fe..a6f40cba559a4ca8c91b4daca0867f3be9cc94e6 100644
+index 826bd8ae116a94ddc59815d4dbb37ffee71780e7..d0858a98a26745747ac50943a7ec1bb4c7491fb6 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2485,6 +2485,16 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0377-Expose-MinecraftServer-isRunning.patch b/patches/server/0368-Expose-MinecraftServer-isRunning.patch
index 4bacea7d49..263db14d03 100644
--- a/patches/server/0377-Expose-MinecraftServer-isRunning.patch
+++ b/patches/server/0368-Expose-MinecraftServer-isRunning.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Expose MinecraftServer#isRunning
This allows for plugins to detect if the server is actually turning off in onDisable rather than just plugins reloading.
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index a6f40cba559a4ca8c91b4daca0867f3be9cc94e6..a35501ea43bf3589b346b1e684c318b44ca57977 100644
+index d0858a98a26745747ac50943a7ec1bb4c7491fb6..b92dff790cfe699c3e6a036f1377c3052373fe05 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2678,5 +2678,10 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0378-Add-Raw-Byte-ItemStack-Serialization.patch b/patches/server/0369-Add-Raw-Byte-ItemStack-Serialization.patch
index fb799bc57d..fb799bc57d 100644
--- a/patches/server/0378-Add-Raw-Byte-ItemStack-Serialization.patch
+++ b/patches/server/0369-Add-Raw-Byte-ItemStack-Serialization.patch
diff --git a/patches/server/0379-Pillager-patrol-spawn-settings-and-per-player-option.patch b/patches/server/0370-Pillager-patrol-spawn-settings-and-per-player-option.patch
index 0b94225be1..3a8c274b7e 100644
--- a/patches/server/0379-Pillager-patrol-spawn-settings-and-per-player-option.patch
+++ b/patches/server/0370-Pillager-patrol-spawn-settings-and-per-player-option.patch
@@ -10,10 +10,10 @@ When not per player it will use the Vanilla mechanic of one delay per
world and the world age for the start day.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 582e08eaeee8fb6c777ad451ccc0436b02cac000..71608048e941f83516631e2a3b6bf9f672d9f127 100644
+index f2808d62c15c586dff0313e6d27ef92d45f66dc7..db351747830079d13cabd0010e8906a5e5aa4e96 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -232,6 +232,7 @@ public class ServerPlayer extends Player {
+@@ -231,6 +231,7 @@ public class ServerPlayer extends Player {
public boolean wonGame;
private int containerUpdateDelay; // Paper
public long loginTime; // Paper
diff --git a/patches/server/0380-Remote-Connections-shouldn-t-hold-up-shutdown.patch b/patches/server/0371-Remote-Connections-shouldn-t-hold-up-shutdown.patch
index 74098cbac5..be6afe3eed 100644
--- a/patches/server/0380-Remote-Connections-shouldn-t-hold-up-shutdown.patch
+++ b/patches/server/0371-Remote-Connections-shouldn-t-hold-up-shutdown.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Remote Connections shouldn't hold up shutdown
Bugs in the connection logic appears to leave stale connections even, preventing shutdown
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 115685012743e775ca3fa031e8a91b6cd2874236..637846c2dda1cf27c194ca4f16da454a62dc3f4b 100644
+index b23905e5d2d1e8300c710df2aeb370fd4af8ac76..2a84d4b07b89672b76d31fa60bb4a0c1ba394831 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -397,11 +397,11 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
diff --git a/patches/server/0381-Do-not-allow-bees-to-load-chunks-for-beehives.patch b/patches/server/0372-Do-not-allow-bees-to-load-chunks-for-beehives.patch
index aa61dfee66..aa61dfee66 100644
--- a/patches/server/0381-Do-not-allow-bees-to-load-chunks-for-beehives.patch
+++ b/patches/server/0372-Do-not-allow-bees-to-load-chunks-for-beehives.patch
diff --git a/patches/server/0382-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch b/patches/server/0373-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch
index 0fcb7a0f21..6c7add7f00 100644
--- a/patches/server/0382-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch
+++ b/patches/server/0373-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch
@@ -7,10 +7,10 @@ Suspected case would be around the technique used in .stopRiding
Stack will identify any causer of this and warn instead of crashing.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 9e940920f6ff6a43d7162d965936c171c55ed570..72af82374f47a1effc3d2f45beee1f611b4bb4b6 100644
+index 638d768393c507ff855c7b517434b61736f680d1..de5caa032685787fb04172ec5c05a022d99827c1 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1754,6 +1754,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1020,6 +1020,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public void addEntity(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
@@ -26,10 +26,10 @@ index 9e940920f6ff6a43d7162d965936c171c55ed570..72af82374f47a1effc3d2f45beee1f61
EntityType<?> entitytypes = entity.getType();
int i = entitytypes.clientTrackingRange() * 16;
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index ed8014ed36f8354aa1ae07689e9315c0c0d8867a..f5a9edf47230a1552a36ee5de4cb7560ea9ba7c4 100644
+index d23c04ce96d12c0ec11c97433c9fa4dca53d3e6e..5a2797104d43d5981fe0d4599c0abbbf8658f153 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2294,7 +2294,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2344,7 +2344,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
public void onTrackingStart(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot
@@ -38,7 +38,7 @@ index ed8014ed36f8354aa1ae07689e9315c0c0d8867a..f5a9edf47230a1552a36ee5de4cb7560
if (entity instanceof ServerPlayer) {
ServerPlayer entityplayer = (ServerPlayer) entity;
-@@ -2328,6 +2328,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2378,6 +2378,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
entity.valid = true; // CraftBukkit
diff --git a/patches/server/0383-Don-t-tick-dead-players.patch b/patches/server/0374-Don-t-tick-dead-players.patch
index 183a646d3b..b5386f7c8d 100644
--- a/patches/server/0383-Don-t-tick-dead-players.patch
+++ b/patches/server/0374-Don-t-tick-dead-players.patch
@@ -7,10 +7,10 @@ Causes sync chunk loads and who knows what all else.
This is safe because Spectators are skipped in unloaded chunks too in vanilla.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 71608048e941f83516631e2a3b6bf9f672d9f127..eae65f79bd1b286f35955bf7a0a5a116ecd12520 100644
+index db351747830079d13cabd0010e8906a5e5aa4e96..8e4dff97dba9f8e1395113bed8f91b0cbb70b354 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -668,7 +668,7 @@ public class ServerPlayer extends Player {
+@@ -653,7 +653,7 @@ public class ServerPlayer extends Player {
public void doTick() {
try {
diff --git a/patches/server/0384-Dead-Player-s-shouldn-t-be-able-to-move.patch b/patches/server/0375-Dead-Player-s-shouldn-t-be-able-to-move.patch
index 83aebd18e6..83aebd18e6 100644
--- a/patches/server/0384-Dead-Player-s-shouldn-t-be-able-to-move.patch
+++ b/patches/server/0375-Dead-Player-s-shouldn-t-be-able-to-move.patch
diff --git a/patches/server/0385-Optimize-Collision-to-not-load-chunks.patch b/patches/server/0376-Optimize-Collision-to-not-load-chunks.patch
index e483706ced..1296dd9549 100644
--- a/patches/server/0385-Optimize-Collision-to-not-load-chunks.patch
+++ b/patches/server/0376-Optimize-Collision-to-not-load-chunks.patch
@@ -14,7 +14,7 @@ movement will load only the chunk the player enters anyways and avoids loading
massive amounts of surrounding chunks due to large AABB lookups.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index d362c9c83a73b5f49b54563b423c76c8ed78dfc6..256bd12178c8dca2889fbcedb9327cc4a1164cf5 100644
+index 6e585b79bf2eccc6bfc98d7cdc05efea699d4e2f..78c2872ed6f696053a03e8c37995f4c2f67a9aa6 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -797,6 +797,7 @@ public abstract class PlayerList {
@@ -26,7 +26,7 @@ index d362c9c83a73b5f49b54563b423c76c8ed78dfc6..256bd12178c8dca2889fbcedb9327cc4
entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ());
}
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 152c2f10a71c231a43a21e2016b040c785417589..a1f176432b366b5f106a32ca571d7ea617b19fd3 100644
+index 904015183bbaa3ac3976e3d81a7968ebb18c0c41..7326d892f40447b716c93efe2f01fbbb67d6b942 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -236,6 +236,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
diff --git a/patches/server/0386-Don-t-move-existing-players-to-world-spawn.patch b/patches/server/0377-Don-t-move-existing-players-to-world-spawn.patch
index 0464df901c..bc2a400726 100644
--- a/patches/server/0386-Don-t-move-existing-players-to-world-spawn.patch
+++ b/patches/server/0377-Don-t-move-existing-players-to-world-spawn.patch
@@ -10,7 +10,7 @@ larger than the keep loaded range.
By skipping this, we avoid potential for a large spike on server start.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index eae65f79bd1b286f35955bf7a0a5a116ecd12520..61f97fab1e0e3d6c9206c397cecfff868cf0d9d2 100644
+index 8e4dff97dba9f8e1395113bed8f91b0cbb70b354..eb0e5c43b995e778d28ecfad813ca3882cabe0fa 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -323,7 +323,7 @@ public class ServerPlayer extends Player {
@@ -22,7 +22,7 @@ index eae65f79bd1b286f35955bf7a0a5a116ecd12520..61f97fab1e0e3d6c9206c397cecfff86
this.cachedSingleHashSet = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
-@@ -556,7 +556,7 @@ public class ServerPlayer extends Player {
+@@ -541,7 +541,7 @@ public class ServerPlayer extends Player {
position = Vec3.atCenterOf(((ServerLevel) world).getSharedSpawnPos());
}
this.level = world;
@@ -32,7 +32,7 @@ index eae65f79bd1b286f35955bf7a0a5a116ecd12520..61f97fab1e0e3d6c9206c397cecfff86
this.gameMode.setLevel((ServerLevel) world);
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 256bd12178c8dca2889fbcedb9327cc4a1164cf5..d9175bcd1060dbaee90e50466da3c6d816fb614e 100644
+index 78c2872ed6f696053a03e8c37995f4c2f67a9aa6..f738012c97024e81488ea6683160ee8236458cb1 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -216,6 +216,8 @@ public abstract class PlayerList {
diff --git a/patches/server/0387-Optimize-GoalSelector-Goal.Flag-Set-operations.patch b/patches/server/0378-Optimize-GoalSelector-Goal.Flag-Set-operations.patch
index cf8d69b485..1e856d6671 100644
--- a/patches/server/0387-Optimize-GoalSelector-Goal.Flag-Set-operations.patch
+++ b/patches/server/0378-Optimize-GoalSelector-Goal.Flag-Set-operations.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 6 Apr 2020 17:53:29 -0700
Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
diff --git a/patches/server/0388-Improved-Watchdog-Support.patch b/patches/server/0379-Improved-Watchdog-Support.patch
index 4c4b9a4420..96f8055ba4 100644
--- a/patches/server/0388-Improved-Watchdog-Support.patch
+++ b/patches/server/0379-Improved-Watchdog-Support.patch
@@ -71,7 +71,7 @@ index 336795dff742b7c6957fbd3476aff31d25a5e659..30a58229aa6dac5039511d0c0df5f291
cause = cause.getCause();
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895fbb8059d 100644
+index a8cbbe13f7da0fe89191196acfa8617e683a66e7..1205d5f7d2bee68f6b3cd8e2ccbcd3d056963d8e 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -284,7 +284,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -92,8 +92,8 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
+
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
AtomicReference<S> atomicreference = new AtomicReference();
- Thread thread = new Thread(() -> {
-@@ -867,6 +870,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ Thread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system
+@@ -877,6 +880,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// CraftBukkit start
private boolean hasStopped = false;
@@ -101,7 +101,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
private final Object stopLock = new Object();
public final boolean hasStopped() {
synchronized (this.stopLock) {
-@@ -881,6 +885,19 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -891,6 +895,19 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (this.hasStopped) return;
this.hasStopped = true;
}
@@ -121,13 +121,13 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
// CraftBukkit end
if (this.metricsRecorder.isRecording()) {
this.cancelRecordingMetrics();
-@@ -966,7 +983,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -947,7 +964,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.getProfileCache().save(false); // Paper
}
// Spigot end
+ // Paper start - move final shutdown items here
+ LOGGER.info("Flushing Chunk IO");
- com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.close(true, true); // Paper
+ io.papermc.paper.chunk.system.io.RegionFileIOThread.close(true); // Paper // Paper - rewrite chunk system
+ LOGGER.info("Closing Thread Pool");
+ Util.shutdownExecutors(); // Paper
+ LOGGER.info("Closing Server");
@@ -140,7 +140,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
}
public String getLocalIp() {
-@@ -1059,6 +1087,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1042,6 +1070,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
protected void runServer() {
try {
@@ -148,7 +148,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
if (!this.initServer()) {
throw new IllegalStateException("Failed to initialize server");
}
-@@ -1071,6 +1100,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1054,6 +1083,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.updateStatusIcon(this.status);
// Spigot start
@@ -167,7 +167,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
org.spigotmc.WatchdogThread.hasStarted = true; // Paper
Arrays.fill( recentTps, 20 );
long start = System.nanoTime(), curTime, tickSection = start; // Paper - Further improve server tick loop
-@@ -1125,6 +1166,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1114,6 +1155,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
JvmProfiler.INSTANCE.onServerTick(this.averageTickTime);
}
} catch (Throwable throwable) {
@@ -180,7 +180,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable);
// Spigot Start
if ( throwable.getCause() != null )
-@@ -1155,14 +1202,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1144,14 +1191,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.services.profileCache().clearExecutor();
}
@@ -198,7 +198,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
}
}
-@@ -1226,6 +1273,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1220,6 +1267,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public TickTask wrapRunnable(Runnable runnable) {
@@ -211,7 +211,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
return new TickTask(this.tickCount, runnable);
}
-@@ -1452,6 +1505,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1446,6 +1499,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
try {
crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
} catch (Throwable t) {
@@ -219,7 +219,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
throw new RuntimeException("Error generating crash report", t);
}
// Spigot End
-@@ -1943,7 +1997,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1937,7 +1991,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.packRepository.setSelected(dataPacks);
this.worldData.setDataPackConfig(MinecraftServer.getSelectedPacks(this.packRepository));
this.resources.managers.updateRegistryTags(this.registryAccess());
@@ -237,7 +237,7 @@ index 12ca13f5b0556c53fd36d638ee4fa854b89ee8ec..3a4222f78a02e10ecccc03df3c580895
this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 637846c2dda1cf27c194ca4f16da454a62dc3f4b..648bc209938364a387c3f81dcd073db398e9f864 100644
+index 2a84d4b07b89672b76d31fa60bb4a0c1ba394831..abfcba1c3db090563191e8adea6d8250ae2d138e 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -274,7 +274,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -259,7 +259,7 @@ index 637846c2dda1cf27c194ca4f16da454a62dc3f4b..648bc209938364a387c3f81dcd073db3
}
@Override
-@@ -745,7 +746,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
+@@ -772,7 +773,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@Override
public void stopServer() {
super.stopServer();
@@ -268,20 +268,8 @@ index 637846c2dda1cf27c194ca4f16da454a62dc3f4b..648bc209938364a387c3f81dcd073db3
SkullBlockEntity.clear();
}
-diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 72af82374f47a1effc3d2f45beee1f611b4bb4b6..fcc9dd6e1c54e4ca16102150aa4c12ecc7de06df 100644
---- a/src/main/java/net/minecraft/server/level/ChunkMap.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -667,6 +667,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- MutableBoolean mutableboolean = new MutableBoolean();
-
- do {
-+ boolean isShuttingDown = level.getServer().hasStopped(); // Paper
- mutableboolean.setFalse();
- list.stream().map((playerchunk) -> {
- CompletableFuture completablefuture;
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index d9175bcd1060dbaee90e50466da3c6d816fb614e..602a30f7dc51ffad9e5f5cc0b339108a2225aafd 100644
+index f738012c97024e81488ea6683160ee8236458cb1..401b1f440b7b1e4f12ba5e8080ca004971c56ae6 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -518,7 +518,7 @@ public abstract class PlayerList {
@@ -306,10 +294,10 @@ index 6fefa619299d3202158490630d62c16aef71e831..7a4ade1a4190bf4fbb048919ae2be230
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index ef4e48127168acdd614bbdcac97a98da5240928e..4c93c2ee69c2728d798a750981275f4fc6840525 100644
+index 0dd6ccc5b281ea46d2d12eb99c28335bdbe66d7e..426fe552d2444a4977a3e261d7e60fbda0893756 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -794,6 +794,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -799,6 +799,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
try {
tickConsumer.accept(entity);
} catch (Throwable throwable) {
@@ -318,10 +306,10 @@ index ef4e48127168acdd614bbdcac97a98da5240928e..4c93c2ee69c2728d798a750981275f4f
final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
MinecraftServer.LOGGER.error(msg, throwable);
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index ebe17598d3ef17cc5f0e99b876ed67936d46060d..c8444f4bfd127b7d8194aaa984505eff249ae094 100644
+index bd1c957a9405ccf18f110c7976cf8e0af922cf78..b0f53c99a89b900ffe49bdd277329829b44775d4 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -1085,6 +1085,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -1200,6 +1200,7 @@ public class LevelChunk extends ChunkAccess {
gameprofilerfiller.pop();
} catch (Throwable throwable) {
@@ -329,19 +317,6 @@ index ebe17598d3ef17cc5f0e99b876ed67936d46060d..c8444f4bfd127b7d8194aaa984505eff
// Paper start - Prevent tile entity and entity crashes
final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ());
net.minecraft.server.MinecraftServer.LOGGER.error(msg, throwable);
-diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index a35501ea43bf3589b346b1e684c318b44ca57977..4016b31bd020e00c0e79328646f9b5411b312e88 100644
---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-@@ -2075,7 +2075,7 @@ public final class CraftServer implements Server {
-
- @Override
- public boolean isPrimaryThread() {
-- return Thread.currentThread().equals(console.serverThread); // Paper - Fix issues with detecting main thread properly
-+ return Thread.currentThread().equals(console.serverThread) || Thread.currentThread().equals(net.minecraft.server.MinecraftServer.getServer().shutdownThread); // Paper - Fix issues with detecting main thread properly, the only time Watchdog will be used is during a crash shutdown which is a "try our best" scenario
- }
-
- // Paper start
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index 08e74f41516a545a2371a7418d995ab288831834..cce6886bb3973eed8f0c7ca7b1189547324fd4e2 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -505,18 +480,18 @@ index a142a56a920e153ed84c08cece993f10d76f7793..92d97a5810a379b427a99b4c63fb9844
String[] split = restartScript.split( " " );
if ( split.length > 0 && new File( split[0] ).isFile() )
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
-index e62f085de1678568d422ef0eda5b9bfbd8b4d556..f3d60841f7b2e29e667291092b97ec909ba03ab6 100644
+index b47d043144c499b1499f6b4be5a16a3f75c9fcb8..383c52c62f49b17db2fbf58009d6ea132d124bea 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -11,6 +11,7 @@ import org.bukkit.Bukkit;
- public class WatchdogThread extends Thread
+ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Paper - rewrite chunk system
{
+ public static final boolean DISABLE_WATCHDOG = Boolean.getBoolean("disable.watchdog"); // Paper
private static WatchdogThread instance;
private long timeoutTime;
private boolean restart;
-@@ -39,6 +40,7 @@ public class WatchdogThread extends Thread
+@@ -39,6 +40,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
{
if ( WatchdogThread.instance == null )
{
@@ -524,7 +499,7 @@ index e62f085de1678568d422ef0eda5b9bfbd8b4d556..f3d60841f7b2e29e667291092b97ec90
WatchdogThread.instance = new WatchdogThread( timeoutTime * 1000L, restart );
WatchdogThread.instance.start();
} else
-@@ -70,12 +72,13 @@ public class WatchdogThread extends Thread
+@@ -70,12 +72,13 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
// Paper start
Logger log = Bukkit.getServer().getLogger();
long currentTime = WatchdogThread.monotonicMillis();
@@ -541,16 +516,7 @@ index e62f085de1678568d422ef0eda5b9bfbd8b4d556..f3d60841f7b2e29e667291092b97ec90
lastEarlyWarning = currentTime;
if (isLongTimeout) {
// Paper end
-@@ -117,7 +120,7 @@ public class WatchdogThread extends Thread
- log.log( Level.SEVERE, "------------------------------" );
- log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
- com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
-- WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
-+ WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( server.serverThread.getId(), Integer.MAX_VALUE ), log );
- log.log( Level.SEVERE, "------------------------------" );
- //
- // Paper start - Only print full dump on long timeouts
-@@ -137,9 +140,25 @@ public class WatchdogThread extends Thread
+@@ -137,9 +140,25 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
if ( isLongTimeout )
{
diff --git a/patches/server/0389-Optimize-Pathfinding.patch b/patches/server/0380-Optimize-Pathfinding.patch
index d39cf83257..d39cf83257 100644
--- a/patches/server/0389-Optimize-Pathfinding.patch
+++ b/patches/server/0380-Optimize-Pathfinding.patch
diff --git a/patches/server/0390-Reduce-Either-Optional-allocation.patch b/patches/server/0381-Reduce-Either-Optional-allocation.patch
index 0f4641263b..eb7237e52a 100644
--- a/patches/server/0390-Reduce-Either-Optional-allocation.patch
+++ b/patches/server/0381-Reduce-Either-Optional-allocation.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 6 Apr 2020 18:35:09 -0700
Subject: [PATCH] Reduce Either Optional allocation
diff --git a/patches/server/0391-Reduce-memory-footprint-of-NBTTagCompound.patch b/patches/server/0382-Reduce-memory-footprint-of-NBTTagCompound.patch
index 740fcbecab..7848f60c2c 100644
--- a/patches/server/0391-Reduce-memory-footprint-of-NBTTagCompound.patch
+++ b/patches/server/0382-Reduce-memory-footprint-of-NBTTagCompound.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 6 Apr 2020 17:39:25 -0700
Subject: [PATCH] Reduce memory footprint of NBTTagCompound
diff --git a/patches/server/0392-Prevent-opening-inventories-when-frozen.patch b/patches/server/0383-Prevent-opening-inventories-when-frozen.patch
index 5e7ef9485c..a16f3603df 100644
--- a/patches/server/0392-Prevent-opening-inventories-when-frozen.patch
+++ b/patches/server/0383-Prevent-opening-inventories-when-frozen.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Prevent opening inventories when frozen
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 61f97fab1e0e3d6c9206c397cecfff868cf0d9d2..227bcd9c1cfcc6bf8dedacdca28731a8ffc7171a 100644
+index eb0e5c43b995e778d28ecfad813ca3882cabe0fa..baff31a9dd003eef6191b59598523e387bc759a1 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -637,7 +637,7 @@ public class ServerPlayer extends Player {
+@@ -622,7 +622,7 @@ public class ServerPlayer extends Player {
containerUpdateDelay = level.paperConfig().tickRates.containerUpdate;
}
// Paper end
@@ -17,7 +17,7 @@ index 61f97fab1e0e3d6c9206c397cecfff868cf0d9d2..227bcd9c1cfcc6bf8dedacdca28731a8
this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper
this.containerMenu = this.inventoryMenu;
}
-@@ -1507,7 +1507,7 @@ public class ServerPlayer extends Player {
+@@ -1492,7 +1492,7 @@ public class ServerPlayer extends Player {
} else {
// CraftBukkit start
this.containerMenu = container;
diff --git a/patches/server/0394-Don-t-run-entity-collision-code-if-not-needed.patch b/patches/server/0384-Don-t-run-entity-collision-code-if-not-needed.patch
index 11403f5b9a..11403f5b9a 100644
--- a/patches/server/0394-Don-t-run-entity-collision-code-if-not-needed.patch
+++ b/patches/server/0384-Don-t-run-entity-collision-code-if-not-needed.patch
diff --git a/patches/server/0395-Implement-Player-Client-Options-API.patch b/patches/server/0385-Implement-Player-Client-Options-API.patch
index 16554bcf51..01c8e8989c 100644
--- a/patches/server/0395-Implement-Player-Client-Options-API.patch
+++ b/patches/server/0385-Implement-Player-Client-Options-API.patch
@@ -85,10 +85,10 @@ index 0000000000000000000000000000000000000000..b6f4400df3d8ec7e06a996de54f8cabb
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 227bcd9c1cfcc6bf8dedacdca28731a8ffc7171a..49cb507ddd378b9a16b82e5c2a28531fce9c6708 100644
+index baff31a9dd003eef6191b59598523e387bc759a1..867deadfc38e069931211a2b0db4350acd96247f 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1871,6 +1871,7 @@ public class ServerPlayer extends Player {
+@@ -1856,6 +1856,7 @@ public class ServerPlayer extends Player {
public String locale = null; // CraftBukkit - add, lowercase // Paper - default to null
public java.util.Locale adventure$locale = java.util.Locale.US; // Paper
public void updateOptions(ServerboundClientInformationPacket packet) {
@@ -97,12 +97,12 @@ index 227bcd9c1cfcc6bf8dedacdca28731a8ffc7171a..49cb507ddd378b9a16b82e5c2a28531f
if (getMainArm() != packet.mainHand()) {
PlayerChangedMainHandEvent event = new PlayerChangedMainHandEvent(this.getBukkitEntity(), getMainArm() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT);
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index fa7b1dd573e61f1a99d58bdb723a40b606604b69..b82d42210a84bc350b80bc061ea1f0ee36519522 100644
+index aebe500d3d0947536d19286195a0c7f5798e862e..c75f919617f60fb353583d6541beaa604efe045a 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -580,6 +580,24 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
- public void setSendViewDistance(int viewDistance) {
- throw new UnsupportedOperationException("Per-Player View Distance APIs need further understanding to properly implement (There are per world view distances though!)"); // TODO
+@@ -615,6 +615,24 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+ connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message);
+ }
}
+
+ @Override
diff --git a/patches/server/0396-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch b/patches/server/0386-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch
index 48eea37bf3..db4ff86838 100644
--- a/patches/server/0396-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch
+++ b/patches/server/0386-Don-t-crash-if-player-is-attempted-to-be-removed-fro.patch
@@ -7,10 +7,10 @@ Subject: [PATCH] Don't crash if player is attempted to be removed from
I suspect it deals with teleporting as it uses players current x/y/z
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
-index 1d1ea158d095bb69260929e8d84f2632a875c136..f456ba4bf699e1f6bd15726a037a0047b6ca7380 100644
+index 773ec6ee0f48ee626d7ec8e4c67212327adfe023..5e4140c8f6a201d47eafa086f1047953ab5c1b0e 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
-@@ -440,8 +440,8 @@ public abstract class DistanceManager {
+@@ -153,8 +153,8 @@ public abstract class DistanceManager {
ObjectSet<ServerPlayer> objectset = (ObjectSet) this.playersPerChunk.get(i);
if (objectset == null) return; // CraftBukkit - SPIGOT-6208
@@ -20,4 +20,4 @@ index 1d1ea158d095bb69260929e8d84f2632a875c136..f456ba4bf699e1f6bd15726a037a0047
+ if (objectset == null || objectset.isEmpty()) { // Paper
this.playersPerChunk.remove(i);
this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
- this.playerTicketManager.update(i, Integer.MAX_VALUE, false);
+ //this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
diff --git a/patches/server/0398-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch b/patches/server/0387-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch
index de10cb2f1f..044449f5f3 100644
--- a/patches/server/0398-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch
+++ b/patches/server/0387-Fix-Longstanding-Broken-behavior-of-PlayerJoinEvent.patch
@@ -28,10 +28,10 @@ receives a deterministic result, and should no longer require 1 tick
delays anymore.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index aed3da6ef2d498d3f1c9c64177bf1ba6b8157493..ea224e68b680c5e9ab38998f7709a4dbe3471b86 100644
+index 35d68b46732471b66f33b568d447b6fac9591dac..f84d7dcfab4a636baada92ac9af03fe5a3dc2e9a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1762,6 +1762,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1027,6 +1027,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
.printStackTrace();
return;
}
@@ -40,10 +40,10 @@ index aed3da6ef2d498d3f1c9c64177bf1ba6b8157493..ea224e68b680c5e9ab38998f7709a4db
if (!(entity instanceof EnderDragonPart)) {
EntityType<?> entitytypes = entity.getType();
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 49cb507ddd378b9a16b82e5c2a28531fce9c6708..53dcb40f5217d560ee42da3d73d1e66e2620067a 100644
+index 867deadfc38e069931211a2b0db4350acd96247f..3008e1cce4df86150dec87cca0433676033d4f73 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -255,6 +255,7 @@ public class ServerPlayer extends Player {
+@@ -254,6 +254,7 @@ public class ServerPlayer extends Player {
public double maxHealthCache;
public boolean joining = true;
public boolean sentListPacket = false;
@@ -52,7 +52,7 @@ index 49cb507ddd378b9a16b82e5c2a28531fce9c6708..53dcb40f5217d560ee42da3d73d1e66e
public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 602a30f7dc51ffad9e5f5cc0b339108a2225aafd..a7bc7e83b9355ef920d94fff8572528965352fb6 100644
+index 401b1f440b7b1e4f12ba5e8080ca004971c56ae6..1096e24835194f20425f75228cafe62adebc2282 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -282,6 +282,12 @@ public abstract class PlayerList {
diff --git a/patches/server/0399-Load-Chunks-for-Login-Asynchronously.patch b/patches/server/0388-Load-Chunks-for-Login-Asynchronously.patch
index a26ec14590..e2569d8445 100644
--- a/patches/server/0399-Load-Chunks-for-Login-Asynchronously.patch
+++ b/patches/server/0388-Load-Chunks-for-Login-Asynchronously.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Load Chunks for Login Asynchronously
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index f5a9edf47230a1552a36ee5de4cb7560ea9ba7c4..a778b4b5b2413c25c2f0f0efc72ba1d362d89acf 100644
+index d3cf5554eb1d4d3d28b95abab43419a5efec8596..99df84ed9cda3be5f521bc7ed7bf82a297aa8bdf 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -170,6 +170,7 @@ import org.bukkit.event.world.GenericGameEvent;
@@ -16,7 +16,7 @@ index f5a9edf47230a1552a36ee5de4cb7560ea9ba7c4..a778b4b5b2413c25c2f0f0efc72ba1d3
public class ServerLevel extends Level implements WorldGenLevel {
-@@ -406,6 +407,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -435,6 +436,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
return this.getServer().getPlayerList().getPlayer(uuid);
}
// Paper end
@@ -25,7 +25,7 @@ index f5a9edf47230a1552a36ee5de4cb7560ea9ba7c4..a778b4b5b2413c25c2f0f0efc72ba1d3
// Add env and gen to constructor, IWorldDataServer -> WorldDataServer
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 53dcb40f5217d560ee42da3d73d1e66e2620067a..971f72c1dd713077c128279a78ed37f15aedeff6 100644
+index 3008e1cce4df86150dec87cca0433676033d4f73..fdc1d1d5840ee7f12e4a72656698924c51fea05c 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -182,6 +182,7 @@ public class ServerPlayer extends Player {
@@ -36,7 +36,7 @@ index 53dcb40f5217d560ee42da3d73d1e66e2620067a..971f72c1dd713077c128279a78ed37f1
public final MinecraftServer server;
public final ServerPlayerGameMode gameMode;
private final PlayerAdvancements advancements;
-@@ -256,6 +257,7 @@ public class ServerPlayer extends Player {
+@@ -255,6 +256,7 @@ public class ServerPlayer extends Player {
public boolean joining = true;
public boolean sentListPacket = false;
public boolean supressTrackerForLogin = false; // Paper
@@ -45,10 +45,10 @@ index 53dcb40f5217d560ee42da3d73d1e66e2620067a..971f72c1dd713077c128279a78ed37f1
public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java
-index 478109526cff7ceb0565cea3b5e97b9a07fbf8d1..3c1698ba0d3bc412ab957777d9b5211dbc555208 100644
+index 97d1ff2af23bac14e67bca5896843325aaa5bfc1..e9bc61590d33dc341074371859ceec54599e6c47 100644
--- a/src/main/java/net/minecraft/server/level/TicketType.java
+++ b/src/main/java/net/minecraft/server/level/TicketType.java
-@@ -25,6 +25,7 @@ public class TicketType<T> {
+@@ -23,6 +23,7 @@ public class TicketType<T> {
public static final TicketType<ChunkPos> FORCED = TicketType.create("forced", Comparator.comparingLong(ChunkPos::toLong));
public static final TicketType<ChunkPos> LIGHT = TicketType.create("light", Comparator.comparingLong(ChunkPos::toLong));
public static final TicketType<BlockPos> PORTAL = TicketType.create("portal", Vec3i::compareTo, 300);
@@ -103,7 +103,7 @@ index ef09f5f42a89f767060f015af27269ad496d08c7..984324767cf8afc31fccc0b7add10645
try {
ServerPlayer entityplayer1 = this.server.getPlayerList().getPlayerForLogin(this.gameProfile, s); // CraftBukkit - add player reference
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index a7bc7e83b9355ef920d94fff8572528965352fb6..32ab0cd6cb42b0ab8a14f790dfcf4b155c945d6d 100644
+index 1096e24835194f20425f75228cafe62adebc2282..f37787b9d018a624b509612729f18ca077b55f55 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -139,6 +139,7 @@ public abstract class PlayerList {
@@ -116,7 +116,7 @@ index a7bc7e83b9355ef920d94fff8572528965352fb6..32ab0cd6cb42b0ab8a14f790dfcf4b15
// private final Map<UUID, AdvancementDataPlayer> advancements;
@@ -180,6 +181,13 @@ public abstract class PlayerList {
public void placeNewPlayer(Connection connection, ServerPlayer player) {
- player.isRealPlayer = true; // Paper - Chunk priority
+ player.isRealPlayer = true; // Paper
player.loginTime = System.currentTimeMillis(); // Paper
+ // Paper start
+ ServerPlayer prev = pendingPlayers.put(player.getUUID(), player);
@@ -266,7 +266,7 @@ index a7bc7e83b9355ef920d94fff8572528965352fb6..32ab0cd6cb42b0ab8a14f790dfcf4b15
Iterator iterator = list.iterator();
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 4016b31bd020e00c0e79328646f9b5411b312e88..c8c11ec3c5c4c4d1ed09163aa6d3a4275e497e11 100644
+index b92dff790cfe699c3e6a036f1377c3052373fe05..237127907c3c8ddcd16bdc9645e84a2d0b922440 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1219,7 +1219,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0400-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch b/patches/server/0389-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch
index bd671c5504..9d590b1ffa 100644
--- a/patches/server/0400-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch
+++ b/patches/server/0389-Move-player-to-spawn-point-if-spawn-in-unloaded-worl.patch
@@ -7,10 +7,10 @@ The code following this has better support for null worlds to move
them back to the world spawn.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index a1f176432b366b5f106a32ca571d7ea617b19fd3..69aef07129201308ae275434b754977c6ece5dd7 100644
+index 7326d892f40447b716c93efe2f01fbbb67d6b942..457471f4e5621b312714dcbad733c23d528131d7 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2110,9 +2110,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2162,9 +2162,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
bworld = server.getWorld(worldName);
}
diff --git a/patches/server/0401-Add-PlayerAttackEntityCooldownResetEvent.patch b/patches/server/0390-Add-PlayerAttackEntityCooldownResetEvent.patch
index 9c571cf10f..9c571cf10f 100644
--- a/patches/server/0401-Add-PlayerAttackEntityCooldownResetEvent.patch
+++ b/patches/server/0390-Add-PlayerAttackEntityCooldownResetEvent.patch
diff --git a/patches/server/0402-Don-t-fire-BlockFade-on-worldgen-threads.patch b/patches/server/0391-Don-t-fire-BlockFade-on-worldgen-threads.patch
index b9aecc5313..b9aecc5313 100644
--- a/patches/server/0402-Don-t-fire-BlockFade-on-worldgen-threads.patch
+++ b/patches/server/0391-Don-t-fire-BlockFade-on-worldgen-threads.patch
diff --git a/patches/server/0403-Add-phantom-creative-and-insomniac-controls.patch b/patches/server/0392-Add-phantom-creative-and-insomniac-controls.patch
index 3b9eaafdbf..3b9eaafdbf 100644
--- a/patches/server/0403-Add-phantom-creative-and-insomniac-controls.patch
+++ b/patches/server/0392-Add-phantom-creative-and-insomniac-controls.patch
diff --git a/patches/server/0404-Fix-numerous-item-duplication-issues-and-teleport-is.patch b/patches/server/0393-Fix-numerous-item-duplication-issues-and-teleport-is.patch
index 2cb660b24c..8bc975b5bc 100644
--- a/patches/server/0404-Fix-numerous-item-duplication-issues-and-teleport-is.patch
+++ b/patches/server/0393-Fix-numerous-item-duplication-issues-and-teleport-is.patch
@@ -16,10 +16,10 @@ So even if something NEW comes up, it would be impossible to drop the
same item twice because the source was destroyed.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 69aef07129201308ae275434b754977c6ece5dd7..8bdcf56a5a1c29f78e10f66de5adf5a3786ac092 100644
+index 457471f4e5621b312714dcbad733c23d528131d7..ae76b3404e4251e7705269b2e57068154eb11fb8 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2240,11 +2240,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2292,11 +2292,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
} else {
// CraftBukkit start - Capture drops for death event
if (this instanceof net.minecraft.world.entity.LivingEntity && !((net.minecraft.world.entity.LivingEntity) this).forceDrops) {
@@ -34,7 +34,7 @@ index 69aef07129201308ae275434b754977c6ece5dd7..8bdcf56a5a1c29f78e10f66de5adf5a3
entityitem.setDefaultPickUpDelay();
// CraftBukkit start
-@@ -3008,6 +3009,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3060,6 +3061,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@Nullable
public Entity teleportTo(ServerLevel worldserver, PositionImpl location) {
// CraftBukkit end
@@ -47,7 +47,7 @@ index 69aef07129201308ae275434b754977c6ece5dd7..8bdcf56a5a1c29f78e10f66de5adf5a3
if (this.level instanceof ServerLevel && !this.isRemoved()) {
this.level.getProfiler().push("changeDimension");
// CraftBukkit start
-@@ -3034,6 +3041,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3086,6 +3093,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
// CraftBukkit end
this.level.getProfiler().popPush("reloading");
@@ -59,7 +59,7 @@ index 69aef07129201308ae275434b754977c6ece5dd7..8bdcf56a5a1c29f78e10f66de5adf5a3
Entity entity = this.getType().create(worldserver);
if (entity != null) {
-@@ -3047,10 +3059,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3099,10 +3111,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
// CraftBukkit start - Forward the CraftEntity to the new entity
this.getBukkitEntity().setHandle(entity);
entity.bukkitEntity = this.getBukkitEntity();
@@ -70,7 +70,7 @@ index 69aef07129201308ae275434b754977c6ece5dd7..8bdcf56a5a1c29f78e10f66de5adf5a3
// CraftBukkit end
}
-@@ -3171,7 +3179,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3223,7 +3231,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public boolean canChangeDimensions() {
diff --git a/patches/server/0405-Villager-Restocks-API.patch b/patches/server/0394-Villager-Restocks-API.patch
index d4af2305a8..d4af2305a8 100644
--- a/patches/server/0405-Villager-Restocks-API.patch
+++ b/patches/server/0394-Villager-Restocks-API.patch
diff --git a/patches/server/0406-Validate-PickItem-Packet-and-kick-for-invalid.patch b/patches/server/0395-Validate-PickItem-Packet-and-kick-for-invalid.patch
index a84ea8c5a4..a84ea8c5a4 100644
--- a/patches/server/0406-Validate-PickItem-Packet-and-kick-for-invalid.patch
+++ b/patches/server/0395-Validate-PickItem-Packet-and-kick-for-invalid.patch
diff --git a/patches/server/0407-Expose-game-version.patch b/patches/server/0396-Expose-game-version.patch
index ba5a794206..87d1ff120a 100644
--- a/patches/server/0407-Expose-game-version.patch
+++ b/patches/server/0396-Expose-game-version.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Expose game version
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index c8c11ec3c5c4c4d1ed09163aa6d3a4275e497e11..aac16630c83034cdb1ce14e04bfc24c39ff65454 100644
+index 237127907c3c8ddcd16bdc9645e84a2d0b922440..30199124d6c1a394fa074a221d6015c09acdd43a 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -579,6 +579,13 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0408-Optimize-Voxel-Shape-Merging.patch b/patches/server/0397-Optimize-Voxel-Shape-Merging.patch
index 7bfe849396..7bfe849396 100644
--- a/patches/server/0408-Optimize-Voxel-Shape-Merging.patch
+++ b/patches/server/0397-Optimize-Voxel-Shape-Merging.patch
diff --git a/patches/server/0409-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch b/patches/server/0398-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch
index 6e0cb176e9..6e0cb176e9 100644
--- a/patches/server/0409-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch
+++ b/patches/server/0398-Set-cap-on-JDK-per-thread-native-byte-buffer-cache.patch
diff --git a/patches/server/0410-misc-debugging-dumps.patch b/patches/server/0399-misc-debugging-dumps.patch
index 466556c637..f440f324b4 100644
--- a/patches/server/0410-misc-debugging-dumps.patch
+++ b/patches/server/0399-misc-debugging-dumps.patch
@@ -29,10 +29,10 @@ index 0000000000000000000000000000000000000000..2d5494d2813b773e60ddba6790b750a9
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 3a4222f78a02e10ecccc03df3c580895fbb8059d..ff94b07246b5e17be53f4e7294557c6744c62248 100644
+index b9083f00c7da96c977bd6efa7971cd896bd0cdd5..2fa1a4e453ac908848053ef80f6401764b0bb7f9 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -871,6 +871,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -881,6 +881,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// CraftBukkit start
private boolean hasStopped = false;
public volatile boolean hasFullyShutdown = false; // Paper
@@ -40,7 +40,7 @@ index 3a4222f78a02e10ecccc03df3c580895fbb8059d..ff94b07246b5e17be53f4e7294557c67
private final Object stopLock = new Object();
public final boolean hasStopped() {
synchronized (this.stopLock) {
-@@ -885,6 +886,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -895,6 +896,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (this.hasStopped) return;
this.hasStopped = true;
}
@@ -48,7 +48,7 @@ index 3a4222f78a02e10ecccc03df3c580895fbb8059d..ff94b07246b5e17be53f4e7294557c67
// Paper start - kill main thread, and kill it hard
shutdownThread = Thread.currentThread();
org.spigotmc.WatchdogThread.doStop(); // Paper
-@@ -1015,6 +1017,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -996,6 +998,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
public void safeShutdown(boolean flag, boolean isRestarting) {
this.isRestarting = isRestarting;
@@ -74,7 +74,7 @@ index 984324767cf8afc31fccc0b7add10645aa0925cf..e62bfb9d0803c16e8b34c56c8a61fcaf
this.connection.send(new ClientboundDisconnectPacket(ichatmutablecomponent));
this.connection.disconnect(ichatmutablecomponent);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index aac16630c83034cdb1ce14e04bfc24c39ff65454..4870a16251def58c8259d6f63b3ef6b91687665d 100644
+index 30199124d6c1a394fa074a221d6015c09acdd43a..9cf007fae9aa21897a06de783357f5e14f6004a0 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1002,6 +1002,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0411-Prevent-teleporting-dead-entities.patch b/patches/server/0400-Prevent-teleporting-dead-entities.patch
index bce03a0bd0..bce03a0bd0 100644
--- a/patches/server/0411-Prevent-teleporting-dead-entities.patch
+++ b/patches/server/0400-Prevent-teleporting-dead-entities.patch
diff --git a/patches/server/0412-Deobfuscate-stacktraces-in-log-messages-crash-report.patch b/patches/server/0401-Deobfuscate-stacktraces-in-log-messages-crash-report.patch
index 3f3a055bc6..5968dd9be8 100644
--- a/patches/server/0412-Deobfuscate-stacktraces-in-log-messages-crash-report.patch
+++ b/patches/server/0401-Deobfuscate-stacktraces-in-log-messages-crash-report.patch
@@ -502,7 +502,7 @@ index f114d5dab86aa2cdd59c78406c9d82f9caededca..99fa9f1952ee7ed79b223ff210a658e4
}
}
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 31d35af5d0efbd0bd8528c3f05e660a203e67ac9..7f0bcb69a1a14a01bd80ffcba1afb25ceeab19b8 100644
+index a283cce82069bf5dfd64229d102a19dfda158daf..b8e35f493458ba9e072953dffe6b2429f1d821ec 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -62,13 +62,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -523,7 +523,7 @@ index 31d35af5d0efbd0bd8528c3f05e660a203e67ac9..7f0bcb69a1a14a01bd80ffcba1afb25c
private final PacketFlow receiving;
private final Queue<Connection.PacketHolder> queue = Queues.newConcurrentLinkedQueue();
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 648bc209938364a387c3f81dcd073db398e9f864..e42df2956e2d852a5a4c8fdeda395a3efd32c44c 100644
+index abfcba1c3db090563191e8adea6d8250ae2d138e..487991163f12c1ded3f5d35a718aa89b1fb9278f 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -200,6 +200,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -568,10 +568,10 @@ index 3c1992e212a6d6f1db4d5b807b38d71913619fc0..9c1aff17aabd062640e3f451a2ef8c50
CraftAsyncScheduler() {
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
-index f3d60841f7b2e29e667291092b97ec909ba03ab6..7823e97add506d42161fd86f830e4d988b2113d8 100644
+index 383c52c62f49b17db2fbf58009d6ea132d124bea..e0a71bfc1498a517456b21747ab6ef3f1067a3f3 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
-@@ -105,7 +105,7 @@ public class WatchdogThread extends Thread
+@@ -105,7 +105,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
log.log( Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity" );
log.log( Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated" );
log.log( Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage());
@@ -580,7 +580,7 @@ index f3d60841f7b2e29e667291092b97ec909ba03ab6..7823e97add506d42161fd86f830e4d98
{
log.log( Level.SEVERE, "\t\t" + stack );
}
-@@ -193,7 +193,7 @@ public class WatchdogThread extends Thread
+@@ -193,7 +193,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
}
log.log( Level.SEVERE, "\tStack:" );
//
diff --git a/patches/server/0413-Implement-Mob-Goal-API.patch b/patches/server/0402-Implement-Mob-Goal-API.patch
index f8748925df..35ff8b0f2a 100644
--- a/patches/server/0413-Implement-Mob-Goal-API.patch
+++ b/patches/server/0402-Implement-Mob-Goal-API.patch
@@ -789,7 +789,7 @@ index 4379b9948f1eecfe6fd7dea98e298ad5f761019a..3f081183521603824430709886a9cc31
LOOK,
JUMP,
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 4870a16251def58c8259d6f63b3ef6b91687665d..6e4d06e0eadf3b97a3fa2b8b8499ce30335a4fff 100644
+index 9cf007fae9aa21897a06de783357f5e14f6004a0..547bb2aab1c31cbd2d571a995c1014726993b39f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2691,5 +2691,11 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0414-Add-villager-reputation-API.patch b/patches/server/0403-Add-villager-reputation-API.patch
index 4c38780334..4c38780334 100644
--- a/patches/server/0414-Add-villager-reputation-API.patch
+++ b/patches/server/0403-Add-villager-reputation-API.patch
diff --git a/patches/server/0415-Option-for-maximum-exp-value-when-merging-orbs.patch b/patches/server/0404-Option-for-maximum-exp-value-when-merging-orbs.patch
index 4239e066f4..4239e066f4 100644
--- a/patches/server/0415-Option-for-maximum-exp-value-when-merging-orbs.patch
+++ b/patches/server/0404-Option-for-maximum-exp-value-when-merging-orbs.patch
diff --git a/patches/server/0416-ExperienceOrbMergeEvent.patch b/patches/server/0405-ExperienceOrbMergeEvent.patch
index 1ff06b6779..1ff06b6779 100644
--- a/patches/server/0416-ExperienceOrbMergeEvent.patch
+++ b/patches/server/0405-ExperienceOrbMergeEvent.patch
diff --git a/patches/server/0417-Fix-PotionEffect-ignores-icon-flag.patch b/patches/server/0406-Fix-PotionEffect-ignores-icon-flag.patch
index 1091a96abf..1091a96abf 100644
--- a/patches/server/0417-Fix-PotionEffect-ignores-icon-flag.patch
+++ b/patches/server/0406-Fix-PotionEffect-ignores-icon-flag.patch
diff --git a/patches/server/0418-Optimize-brigadier-child-sorting-performance.patch b/patches/server/0407-Optimize-brigadier-child-sorting-performance.patch
index 3651f34b15..3651f34b15 100644
--- a/patches/server/0418-Optimize-brigadier-child-sorting-performance.patch
+++ b/patches/server/0407-Optimize-brigadier-child-sorting-performance.patch
diff --git a/patches/server/0419-Potential-bed-API.patch b/patches/server/0408-Potential-bed-API.patch
index 633d9baf8a..633d9baf8a 100644
--- a/patches/server/0419-Potential-bed-API.patch
+++ b/patches/server/0408-Potential-bed-API.patch
diff --git a/patches/server/0420-Wait-for-Async-Tasks-during-shutdown.patch b/patches/server/0409-Wait-for-Async-Tasks-during-shutdown.patch
index 6d308392ef..ba50c6bb9e 100644
--- a/patches/server/0420-Wait-for-Async-Tasks-during-shutdown.patch
+++ b/patches/server/0409-Wait-for-Async-Tasks-during-shutdown.patch
@@ -10,10 +10,10 @@ Adds a 5 second grace period for any async tasks to finish and warns
if any are still running after that delay just as reload does.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index ff94b07246b5e17be53f4e7294557c6744c62248..9c2589c7e0517f771b9ca06760273a0aecefb27d 100644
+index 2fa1a4e453ac908848053ef80f6401764b0bb7f9..3c7154e85c4543a01a4e95b9f574f5d803a59e6c 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -911,6 +911,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -921,6 +921,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// CraftBukkit start
if (this.server != null) {
this.server.disablePlugins();
@@ -22,7 +22,7 @@ index ff94b07246b5e17be53f4e7294557c6744c62248..9c2589c7e0517f771b9ca06760273a0a
// CraftBukkit end
if (this.getConnection() != null) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 6e4d06e0eadf3b97a3fa2b8b8499ce30335a4fff..f0f2a5e94e0a4b143befa422fa99e68316f93d7f 100644
+index 547bb2aab1c31cbd2d571a995c1014726993b39f..ef6ca51679c818989f3ddd83f0bc7f37db6d6196 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1011,6 +1011,35 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0421-Ensure-EntityRaider-respects-game-and-entity-rules-f.patch b/patches/server/0410-Ensure-EntityRaider-respects-game-and-entity-rules-f.patch
index 94dbb79f58..94dbb79f58 100644
--- a/patches/server/0421-Ensure-EntityRaider-respects-game-and-entity-rules-f.patch
+++ b/patches/server/0410-Ensure-EntityRaider-respects-game-and-entity-rules-f.patch
diff --git a/patches/server/0422-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch b/patches/server/0411-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
index de3bd7b059..7c01a4e8b6 100644
--- a/patches/server/0422-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
+++ b/patches/server/0411-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
@@ -34,7 +34,7 @@ index 38bb502e9f1272020a23a3ef8ebb0cb1a5a251ef..b18b0e1b5e059f08fd3117eaa0fb28a1
this.level.getProfiler().push("explosion_blocks");
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 4c93c2ee69c2728d798a750981275f4fc6840525..160a3cb1d70f765d277169bb4928238b6a575f26 100644
+index 426fe552d2444a4977a3e261d7e60fbda0893756..ea3b4c52769ff8112d865b226c8d3dcf48982787 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -429,6 +429,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -115,7 +115,7 @@ index 76720517cd2d82065eb8430cf854b536295341db..29755807fdb6c30e31c0ec2bbf33bed9
world.playSound((Player) null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-index 3ec96c7f2ad0d6ba8ad32a4aabb0292b5c2ff5b4..ad38a7ced7f3dc05fb3d133e9da39f0a5eb0915b 100644
+index 61a792c5ccf3688d4f78c6e7461090f63a0cc26a..b2ab7749e3ddf124d5ef97271a76dc875a650771 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -223,7 +223,7 @@ public abstract class BlockBehaviour {
@@ -127,7 +127,7 @@ index 3ec96c7f2ad0d6ba8ad32a4aabb0292b5c2ff5b4..ad38a7ced7f3dc05fb3d133e9da39f0a
}
/** @deprecated */
-@@ -727,6 +727,12 @@ public abstract class BlockBehaviour {
+@@ -741,6 +741,12 @@ public abstract class BlockBehaviour {
return ((Block) this.owner).builtInRegistryHolder();
}
@@ -140,7 +140,7 @@ index 3ec96c7f2ad0d6ba8ad32a4aabb0292b5c2ff5b4..ad38a7ced7f3dc05fb3d133e9da39f0a
public Material getMaterial() {
return this.material;
}
-@@ -824,7 +830,7 @@ public abstract class BlockBehaviour {
+@@ -838,7 +844,7 @@ public abstract class BlockBehaviour {
}
public PushReaction getPistonPushReaction() {
diff --git a/patches/server/0423-Reduce-MutableInt-allocations-from-light-engine.patch b/patches/server/0412-Reduce-MutableInt-allocations-from-light-engine.patch
index 3adb42bf18..1a92be0a01 100644
--- a/patches/server/0423-Reduce-MutableInt-allocations-from-light-engine.patch
+++ b/patches/server/0412-Reduce-MutableInt-allocations-from-light-engine.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 27 Apr 2020 02:48:06 -0700
Subject: [PATCH] Reduce MutableInt allocations from light engine
diff --git a/patches/server/0424-Reduce-allocation-of-Vec3D-by-entity-tracker.patch b/patches/server/0413-Reduce-allocation-of-Vec3D-by-entity-tracker.patch
index 2b5c27897e..7669a4d452 100644
--- a/patches/server/0424-Reduce-allocation-of-Vec3D-by-entity-tracker.patch
+++ b/patches/server/0413-Reduce-allocation-of-Vec3D-by-entity-tracker.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 27 Apr 2020 00:04:16 -0700
Subject: [PATCH] Reduce allocation of Vec3D by entity tracker
@@ -18,10 +18,10 @@ index 3167f5c6be39757e3cc42cbb17ab0cf13a2fe470..3768a71491ef7836b9739bdaec7a077c
private static long encode(double value) {
return Mth.lfloor(value * 4096.0D);
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index ea224e68b680c5e9ab38998f7709a4dbe3471b86..82eceddc15dcdf592dc5bbe6f1249f537be0a91e 100644
+index f84d7dcfab4a636baada92ac9af03fe5a3dc2e9a..84a702dd49d2bbf53410146c2942d4c1d896d40d 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -2040,9 +2040,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1305,9 +1305,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public void updatePlayer(ServerPlayer player) {
org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
if (player != this.entity) {
@@ -31,7 +31,7 @@ index ea224e68b680c5e9ab38998f7709a4dbe3471b86..82eceddc15dcdf592dc5bbe6f1249f53
+ double vec3d_dx = player.getX() - this.entity.getX();
+ double vec3d_dz = player.getZ() - this.entity.getZ();
+ // Paper end - remove allocation of Vec3D here
- double d0 = (double) Math.min(this.getEffectiveRange(), (ChunkMap.this.viewDistance - 1) * 16);
+ double d0 = (double) Math.min(this.getEffectiveRange(), io.papermc.paper.chunk.PlayerChunkLoader.getSendViewDistance(player) * 16); // Paper - per player view distance
- double d1 = vec3d.x * vec3d.x + vec3d.z * vec3d.z;
+ double d1 = vec3d_dx * vec3d_dx + vec3d_dz * vec3d_dz; // Paper
double d2 = d0 * d0;
diff --git a/patches/server/0425-Ensure-safe-gateway-teleport.patch b/patches/server/0414-Ensure-safe-gateway-teleport.patch
index 6d352f74a8..6d352f74a8 100644
--- a/patches/server/0425-Ensure-safe-gateway-teleport.patch
+++ b/patches/server/0414-Ensure-safe-gateway-teleport.patch
diff --git a/patches/server/0426-Add-option-for-console-having-all-permissions.patch b/patches/server/0415-Add-option-for-console-having-all-permissions.patch
index d1e3317d94..d1e3317d94 100644
--- a/patches/server/0426-Add-option-for-console-having-all-permissions.patch
+++ b/patches/server/0415-Add-option-for-console-having-all-permissions.patch
diff --git a/patches/server/0427-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch b/patches/server/0416-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
index 643380032b..90f095a707 100644
--- a/patches/server/0427-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
+++ b/patches/server/0416-Optimize-anyPlayerCloseEnoughForSpawning-to-use-dist.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Optimize anyPlayerCloseEnoughForSpawning to use distance maps
Use a distance map to find the players in range quickly
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-index a52932d665ca45a5e066d7cef0ec0313d1c3f69f..0f75a109c06eb3113be74cf49ec560f5e2ea9cfc 100644
+index b550f302b1bb6ef92987c8c3431b94c3ba3a091d..2add24517d38708a84e7f8ec25fbe9c62309375e 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-@@ -79,14 +79,27 @@ public class ChunkHolder {
+@@ -83,16 +83,29 @@ public class ChunkHolder {
// Paper start
public void onChunkAdd() {
@@ -30,23 +30,25 @@ index a52932d665ca45a5e066d7cef0ec0313d1c3f69f..0f75a109c06eb3113be74cf49ec560f5
}
// Paper end
+ public final io.papermc.paper.chunk.system.scheduling.NewChunkHolder newChunkHolder; // Paper - rewrite chunk system
+
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
+ // cached here to avoid a map lookup
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange;
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
+
- public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
- this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
- this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
+ public ChunkHolder(ChunkPos pos, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.PlayerProvider playersWatchingChunkProvider, io.papermc.paper.chunk.system.scheduling.NewChunkHolder newChunkHolder) { // Paper - rewrite chunk system
+ this.newChunkHolder = newChunkHolder; // Paper - rewrite chunk system
+ this.chunkToSaveHistory = null;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 82eceddc15dcdf592dc5bbe6f1249f537be0a91e..43ad735f57ab513311d700b42d7d0f2f1e43d0e7 100644
+index 8dce24ae70057115feff193a1307eb2437e16773..50e9d29b5a0ea6d11a44c41c49d6f52bc464e6e2 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -196,11 +196,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- final CallbackExecutor chunkLoadConversionCallbackExecutor = new CallbackExecutor(); // Paper
+@@ -152,12 +152,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper start - distance maps
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
+ public final io.papermc.paper.chunk.PlayerChunkLoader playerChunkManager = new io.papermc.paper.chunk.PlayerChunkLoader(this, this.pooledLinkedPlayerHashSets); // Paper - replace chunk loader
+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
+ // A note about the naming used here:
+ // Previously, mojang used a "spawn range" of 8 for controlling both ticking and
@@ -60,16 +62,17 @@ index 82eceddc15dcdf592dc5bbe6f1249f537be0a91e..43ad735f57ab513311d700b42d7d0f2f
+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
void addPlayerToDistanceMaps(ServerPlayer player) {
+ this.playerChunkManager.addPlayer(player); // Paper - replace chunk loader
int chunkX = MCUtil.getChunkCoordinate(player.getX());
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
// Note: players need to be explicitly added to distance maps before they can be updated
+ this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
// Paper start - per player mob spawning
if (this.playerMobDistanceMap != null) {
- this.playerMobDistanceMap.add(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
-@@ -210,6 +222,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-
+ this.playerMobDistanceMap.add(player, chunkX, chunkZ, net.minecraft.server.ChunkSystem.getTickViewDistance(player));
+@@ -168,6 +180,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
void removePlayerFromDistanceMaps(ServerPlayer player) {
+ this.playerChunkManager.removePlayer(player); // Paper - replace chunk loader
+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
+ this.playerMobSpawnMap.remove(player);
@@ -78,15 +81,15 @@ index 82eceddc15dcdf592dc5bbe6f1249f537be0a91e..43ad735f57ab513311d700b42d7d0f2f
// Paper start - per player mob spawning
if (this.playerMobDistanceMap != null) {
this.playerMobDistanceMap.remove(player);
-@@ -221,6 +237,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- int chunkX = MCUtil.getChunkCoordinate(player.getX());
+@@ -180,6 +196,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
// Note: players need to be explicitly added to distance maps before they can be updated
+ this.playerChunkManager.updatePlayer(player); // Paper - replace chunk loader
+ this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
// Paper start - per player mob spawning
if (this.playerMobDistanceMap != null) {
- this.playerMobDistanceMap.update(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
-@@ -323,6 +340,38 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ this.playerMobDistanceMap.update(player, chunkX, chunkZ, net.minecraft.server.ChunkSystem.getTickViewDistance(player));
+@@ -267,6 +284,38 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.regionManagers.add(this.dataRegionManager);
// Paper end
this.playerMobDistanceMap = this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets) : null; // Paper
@@ -125,7 +128,7 @@ index 82eceddc15dcdf592dc5bbe6f1249f537be0a91e..43ad735f57ab513311d700b42d7d0f2f
}
protected ChunkGenerator generator() {
-@@ -1524,43 +1573,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -822,43 +871,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return this.anyPlayerCloseEnoughForSpawning(pos, false);
}
@@ -206,47 +209,38 @@ index 82eceddc15dcdf592dc5bbe6f1249f537be0a91e..43ad735f57ab513311d700b42d7d0f2f
public List<ServerPlayer> getPlayersCloseForSpawning(ChunkPos pos) {
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
-index f456ba4bf699e1f6bd15726a037a0047b6ca7380..b2df5e18ce5260a9781052db7afb0b9786fb887c 100644
+index d3c3db919e9b0507e8543313d9028394e5163673..52cba8f68d274cce106304aef1249a95474d3238 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
-@@ -49,7 +49,7 @@ public abstract class DistanceManager {
+@@ -54,7 +54,7 @@ public abstract class DistanceManager {
+ private static final int BLOCK_TICKING_LEVEL_THRESHOLD = 33;
final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
- public final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets = new Long2ObjectOpenHashMap();
- private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker();
+ // Paper - rewrite chunk system
- private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
-+ public static final int MOB_SPAWN_RANGE = 8; // private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used
- private final TickingTracker tickingTicketsTracker = new TickingTracker();
- private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33);
- // Paper start use a queue, but still keep unique requirement
-@@ -141,7 +141,7 @@ public abstract class DistanceManager {
- protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k);
++ public static final int MOB_SPAWN_RANGE = 8; // private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - no longer used
+ //private final TickingTracker tickingTicketsTracker = new TickingTracker(); // Paper - no longer used
+ //private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33); // Paper - no longer used
+ // Paper - rewrite chunk system
+@@ -142,7 +142,7 @@ public abstract class DistanceManager {
+ long i = chunkcoordintpair.toLong();
- public boolean runAllUpdates(ChunkMap chunkStorage) {
-- this.naturalSpawnChunkCounter.runAllUpdates();
-+ //this.f.a(); // Paper - no longer used
- this.tickingTicketsTracker.runAllUpdates();
- org.spigotmc.AsyncCatcher.catchOp("DistanceManagerTick"); // Paper
- this.playerTicketManager.runAllUpdates();
-@@ -429,7 +429,7 @@ public abstract class DistanceManager {
- ((ObjectSet) this.playersPerChunk.computeIfAbsent(i, (j) -> {
- return new ObjectOpenHashSet();
- })).add(player);
+ // Paper - no longer used
- this.naturalSpawnChunkCounter.update(i, 0, true);
-+ //this.f.update(i, 0, true); // Paper - no longer used
- this.playerTicketManager.update(i, 0, true);
- this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair);
++ //this.naturalSpawnChunkCounter.update(i, 0, true); // Paper - no longer used
+ //this.playerTicketManager.update(i, 0, true); // Paper - no longer used
+ //this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
}
-@@ -443,7 +443,7 @@ public abstract class DistanceManager {
+@@ -156,7 +156,7 @@ public abstract class DistanceManager {
if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully.
if (objectset == null || objectset.isEmpty()) { // Paper
this.playersPerChunk.remove(i);
- this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
-+ //this.f.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
- this.playerTicketManager.update(i, Integer.MAX_VALUE, false);
- this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair);
++ // this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
+ //this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
+ //this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
}
-@@ -487,13 +487,17 @@ public abstract class DistanceManager {
- // Paper end
+@@ -198,13 +198,17 @@ public abstract class DistanceManager {
+ }
public int getNaturalSpawnChunkCount() {
- this.naturalSpawnChunkCounter.runAllUpdates();
@@ -268,10 +262,10 @@ index f456ba4bf699e1f6bd15726a037a0047b6ca7380..b2df5e18ce5260a9781052db7afb0b97
public String getDebugStatus() {
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 497827822a64eeff2a4901f0e7c62f0f2c359b48..a59782d6f3640262c377a676e2b2ef5ec82563db 100644
+index 60264feb656861d5a9474fe4285ac69d8d12269e..44766ea7e5dd8f8411b52cf259187d7557cc0c23 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -729,6 +729,37 @@ public class ServerChunkCache extends ChunkSource {
+@@ -657,6 +657,37 @@ public class ServerChunkCache extends ChunkSource {
if (flag) {
this.chunkMap.tick();
} else {
@@ -309,7 +303,7 @@ index 497827822a64eeff2a4901f0e7c62f0f2c359b48..a59782d6f3640262c377a676e2b2ef5e
LevelData worlddata = this.level.getLevelData();
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
-@@ -772,15 +803,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -700,15 +731,7 @@ public class ServerChunkCache extends ChunkSource {
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
Collections.shuffle(list);
@@ -326,7 +320,7 @@ index 497827822a64eeff2a4901f0e7c62f0f2c359b48..a59782d6f3640262c377a676e2b2ef5e
Iterator iterator1 = list.iterator();
while (iterator1.hasNext()) {
-@@ -788,9 +811,9 @@ public class ServerChunkCache extends ChunkSource {
+@@ -716,9 +739,9 @@ public class ServerChunkCache extends ChunkSource {
LevelChunk chunk1 = chunkproviderserver_a.chunk;
ChunkPos chunkcoordintpair = chunk1.getPos();
@@ -339,13 +333,13 @@ index 497827822a64eeff2a4901f0e7c62f0f2c359b48..a59782d6f3640262c377a676e2b2ef5e
}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 971f72c1dd713077c128279a78ed37f15aedeff6..29a03760f092a004a47e75120841d80e696b6c3d 100644
+index fdc1d1d5840ee7f12e4a72656698924c51fea05c..0faa0d73907e6ef1285eba4cf7d692a5f8c3a0e3 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -263,6 +263,7 @@ public class ServerPlayer extends Player {
- // CraftBukkit end
public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper
+ public boolean isRealPlayer; // Paper
+ public double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks
public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
diff --git a/patches/server/0428-Use-distance-map-to-optimise-entity-tracker.patch b/patches/server/0417-Use-distance-map-to-optimise-entity-tracker.patch
index 28b5efb452..d9a8bf3d29 100644
--- a/patches/server/0428-Use-distance-map-to-optimise-entity-tracker.patch
+++ b/patches/server/0417-Use-distance-map-to-optimise-entity-tracker.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Use distance map to optimise entity tracker
Use the distance map to find candidate players for tracking.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 43ad735f57ab513311d700b42d7d0f2f1e43d0e7..b0e0f85e04438affb8d8e0f75055ea83d0c03bcd 100644
+index 50e9d29b5a0ea6d11a44c41c49d6f52bc464e6e2..2a25d552612bf94c5f54249ae3cba549ea242488 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -66,6 +66,7 @@ import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket;
@@ -17,7 +17,7 @@ index 43ad735f57ab513311d700b42d7d0f2f1e43d0e7..b0e0f85e04438affb8d8e0f75055ea83
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.server.network.ServerPlayerConnection;
import net.minecraft.util.CsvOutput;
-@@ -207,10 +208,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -163,6 +164,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerChunkTickRangeMap;
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
@@ -40,47 +40,50 @@ index 43ad735f57ab513311d700b42d7d0f2f1e43d0e7..b0e0f85e04438affb8d8e0f75055ea83
+ // Paper end - use distance map to optimise tracker
void addPlayerToDistanceMaps(ServerPlayer player) {
- int chunkX = MCUtil.getChunkCoordinate(player.getX());
- int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
+ this.playerChunkManager.addPlayer(player); // Paper - replace chunk loader
+@@ -175,6 +193,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ this.playerMobDistanceMap.add(player, chunkX, chunkZ, net.minecraft.server.ChunkSystem.getTickViewDistance(player));
+ }
+ // Paper end - per player mob spawning
+ // Paper start - use distance map to optimise entity tracker
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i];
+ int trackRange = this.entityTrackerTrackRanges[i];
+
-+ trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance()));
++ trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, net.minecraft.server.ChunkSystem.getSendViewDistance(player)));
+ }
+ // Paper end - use distance map to optimise entity tracker
- // Note: players need to be explicitly added to distance maps before they can be updated
- this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
- // Paper start - per player mob spawning
-@@ -222,6 +248,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
void removePlayerFromDistanceMaps(ServerPlayer player) {
-
+@@ -189,6 +215,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ this.playerMobDistanceMap.remove(player);
+ }
+ // Paper end - per player mob spawning
+ // Paper start - use distance map to optimise tracker
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
+ this.playerEntityTrackerTrackMaps[i].remove(player);
+ }
+ // Paper end - use distance map to optimise tracker
- // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
- this.playerMobSpawnMap.remove(player);
- this.playerChunkTickRangeMap.remove(player);
-@@ -237,6 +268,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- int chunkX = MCUtil.getChunkCoordinate(player.getX());
- int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
- // Note: players need to be explicitly added to distance maps before they can be updated
+ }
+
+ void updateMaps(ServerPlayer player) {
+@@ -202,6 +233,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ this.playerMobDistanceMap.update(player, chunkX, chunkZ, net.minecraft.server.ChunkSystem.getTickViewDistance(player));
+ }
+ // Paper end - per player mob spawning
+ // Paper start - use distance map to optimise entity tracker
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i];
+ int trackRange = this.entityTrackerTrackRanges[i];
+
-+ trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance()));
++ trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, net.minecraft.server.ChunkSystem.getSendViewDistance(player)));
+ }
+ // Paper end - use distance map to optimise entity tracker
- this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
- // Paper start - per player mob spawning
- if (this.playerMobDistanceMap != null) {
-@@ -340,6 +379,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+ // Paper end
+ // Paper start
+@@ -284,6 +323,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.regionManagers.add(this.dataRegionManager);
// Paper end
this.playerMobDistanceMap = this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets) : null; // Paper
@@ -126,7 +129,7 @@ index 43ad735f57ab513311d700b42d7d0f2f1e43d0e7..b0e0f85e04438affb8d8e0f75055ea83
// Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
this.playerChunkTickRangeMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
(ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
-@@ -1696,17 +1774,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -986,17 +1064,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void move(ServerPlayer player) {
@@ -145,7 +148,7 @@ index 43ad735f57ab513311d700b42d7d0f2f1e43d0e7..b0e0f85e04438affb8d8e0f75055ea83
int i = SectionPos.blockToSectionCoord(player.getBlockX());
int j = SectionPos.blockToSectionCoord(player.getBlockZ());
-@@ -1833,7 +1901,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1098,7 +1166,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker
this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
@@ -154,7 +157,7 @@ index 43ad735f57ab513311d700b42d7d0f2f1e43d0e7..b0e0f85e04438affb8d8e0f75055ea83
if (entity instanceof ServerPlayer) {
ServerPlayer entityplayer = (ServerPlayer) entity;
-@@ -1877,7 +1945,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1142,7 +1210,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = null; // Paper - We're no longer tracked
}
@@ -192,51 +195,55 @@ index 43ad735f57ab513311d700b42d7d0f2f1e43d0e7..b0e0f85e04438affb8d8e0f75055ea83
List<ServerPlayer> list = Lists.newArrayList();
List<ServerPlayer> list1 = this.level.players();
ObjectIterator objectiterator = this.entityMap.values().iterator();
-@@ -1953,23 +2051,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1216,46 +1314,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }));
+ // Paper end
DebugPackets.sendPoiPacketsForChunk(this.level, chunk.getPos());
- List<Entity> list = Lists.newArrayList();
- List<Entity> list1 = Lists.newArrayList();
+- List<Entity> list = Lists.newArrayList();
+- List<Entity> list1 = Lists.newArrayList();
- ObjectIterator objectiterator = this.entityMap.values().iterator();
-+ // Paper start - optimise entity tracker
-+ // use the chunk entity list, not the whole trackedEntities map...
-+ Entity[] entities = chunk.entities.getRawData();
-+ for (int i = 0, size = chunk.entities.size(); i < size; ++i) {
-+ Entity entity = entities[i];
-+ if (entity == player) {
-+ continue;
-+ }
-+ ChunkMap.TrackedEntity tracker = this.entityMap.get(entity.getId());
-+ if (tracker != null) { // dumb plugins... move on...
-+ tracker.updatePlayer(player);
-+ }
-
+-
- while (objectiterator.hasNext()) {
- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
- Entity entity = playerchunkmap_entitytracker.entity;
-+ // keep the vanilla logic here - this is REQUIRED or else passengers and their vehicles disappear!
-+ // (and god knows what the leash thing is)
-
+-
- if (entity != player && entity.chunkPosition().equals(chunk.getPos())) {
- playerchunkmap_entitytracker.updatePlayer(player);
- if (entity instanceof Mob && ((Mob) entity).getLeashHolder() != null) {
- list.add(entity);
- }
-+ if (entity instanceof Mob && ((Mob)entity).getLeashHolder() != null) {
-+ list.add(entity);
-+ }
-
+-
- if (!entity.getPassengers().isEmpty()) {
- list1.add(entity);
- }
-+ if (!entity.getPassengers().isEmpty()) {
-+ list1.add(entity);
- }
- }
-+ // Paper end - optimise entity tracker
+- }
+- }
+-
+- Iterator iterator;
+- Entity entity1;
+-
+- if (!list.isEmpty()) {
+- iterator = list.iterator();
+-
+- while (iterator.hasNext()) {
+- entity1 = (Entity) iterator.next();
+- player.connection.send(new ClientboundSetEntityLinkPacket(entity1, ((Mob) entity1).getLeashHolder()));
+- }
+- }
+-
+- if (!list1.isEmpty()) {
+- iterator = list1.iterator();
+-
+- while (iterator.hasNext()) {
+- entity1 = (Entity) iterator.next();
+- player.connection.send(new ClientboundSetPassengersPacket(entity1));
+- }
+- }
++ // Paper - no longer needed - this was used to account for clients bugging out since they needed a chunk to store entities, but they no longer need a chunk
- Iterator iterator;
- Entity entity1;
-@@ -2045,6 +2151,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+@@ -1310,6 +1369,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lastSectionPos = SectionPos.of((EntityAccess) entity);
}
@@ -280,7 +287,7 @@ index 43ad735f57ab513311d700b42d7d0f2f1e43d0e7..b0e0f85e04438affb8d8e0f75055ea83
return object instanceof ChunkMap.TrackedEntity ? ((ChunkMap.TrackedEntity) object).entity.getId() == this.entity.getId() : false;
}
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 8bdcf56a5a1c29f78e10f66de5adf5a3786ac092..afe081b095bb53c7a1a1d2145e60b8b5426d2ce0 100644
+index ae76b3404e4251e7705269b2e57068154eb11fb8..c8ca2835c4fbad85a9680d0cc1da2fdc9a5324b6 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -57,6 +57,7 @@ import net.minecraft.network.syncher.EntityDataSerializers;
@@ -291,10 +298,10 @@ index 8bdcf56a5a1c29f78e10f66de5adf5a3786ac092..afe081b095bb53c7a1a1d2145e60b8b5
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
-@@ -424,6 +425,39 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
- }
- // Paper end
+@@ -475,6 +476,38 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ public boolean updatingSectionStatus = false;
+ // Paper end
+ // Paper start - optimise entity tracking
+ final org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = org.spigotmc.TrackingRange.getTrackingRangeType(this);
+
@@ -327,10 +334,9 @@ index 8bdcf56a5a1c29f78e10f66de5adf5a3786ac092..afe081b095bb53c7a1a1d2145e60b8b5
+ return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this));
+ }
+ // Paper end - optimise entity tracking
-+
+
public Entity(EntityType<?> type, Level world) {
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
- this.passengers = ImmutableList.of();
diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java
index 55ce69b5fe097841d00ef5c241459dce9bb0d4db..e5bcbfe175a697e04886d04543e1278b7e83a184 100644
--- a/src/main/java/org/spigotmc/TrackingRange.java
diff --git a/patches/server/0430-Fix-villager-trading-demand-MC-163962.patch b/patches/server/0418-Fix-villager-trading-demand-MC-163962.patch
index fb5e330854..fb5e330854 100644
--- a/patches/server/0430-Fix-villager-trading-demand-MC-163962.patch
+++ b/patches/server/0418-Fix-villager-trading-demand-MC-163962.patch
diff --git a/patches/server/0431-Maps-shouldn-t-load-chunks.patch b/patches/server/0419-Maps-shouldn-t-load-chunks.patch
index 3ab19f357a..3ab19f357a 100644
--- a/patches/server/0431-Maps-shouldn-t-load-chunks.patch
+++ b/patches/server/0419-Maps-shouldn-t-load-chunks.patch
diff --git a/patches/server/0432-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch b/patches/server/0420-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
index 9ed5e37d35..9ed5e37d35 100644
--- a/patches/server/0432-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
+++ b/patches/server/0420-Use-seed-based-lookup-for-Treasure-Maps-Fixes-lag-fr.patch
diff --git a/patches/server/0433-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch b/patches/server/0421-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch
index 1cafad18f3..1cafad18f3 100644
--- a/patches/server/0433-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch
+++ b/patches/server/0421-Fix-CraftScheduler-runTaskTimerAsynchronously-Plugin.patch
diff --git a/patches/server/0434-Fix-piston-physics-inconsistency-MC-188840.patch b/patches/server/0422-Fix-piston-physics-inconsistency-MC-188840.patch
index dcc303244a..dcc303244a 100644
--- a/patches/server/0434-Fix-piston-physics-inconsistency-MC-188840.patch
+++ b/patches/server/0422-Fix-piston-physics-inconsistency-MC-188840.patch
diff --git a/patches/server/0435-Fix-sand-duping.patch b/patches/server/0423-Fix-sand-duping.patch
index 5780d77de8..5780d77de8 100644
--- a/patches/server/0435-Fix-sand-duping.patch
+++ b/patches/server/0423-Fix-sand-duping.patch
diff --git a/patches/server/0436-Fix-missing-chunks-due-to-integer-overflow.patch b/patches/server/0424-Fix-missing-chunks-due-to-integer-overflow.patch
index 94b8d07c3a..94b8d07c3a 100644
--- a/patches/server/0436-Fix-missing-chunks-due-to-integer-overflow.patch
+++ b/patches/server/0424-Fix-missing-chunks-due-to-integer-overflow.patch
diff --git a/patches/server/0437-Prevent-position-desync-in-playerconnection-causing-.patch b/patches/server/0425-Prevent-position-desync-in-playerconnection-causing-.patch
index ba844e85b3..ba844e85b3 100644
--- a/patches/server/0437-Prevent-position-desync-in-playerconnection-causing-.patch
+++ b/patches/server/0425-Prevent-position-desync-in-playerconnection-causing-.patch
diff --git a/patches/server/0438-Inventory-getHolder-method-without-block-snapshot.patch b/patches/server/0426-Inventory-getHolder-method-without-block-snapshot.patch
index 9ed9fe0826..9ed9fe0826 100644
--- a/patches/server/0438-Inventory-getHolder-method-without-block-snapshot.patch
+++ b/patches/server/0426-Inventory-getHolder-method-without-block-snapshot.patch
diff --git a/patches/server/0439-Improve-Arrow-API.patch b/patches/server/0427-Improve-Arrow-API.patch
index 47bb897a5a..47bb897a5a 100644
--- a/patches/server/0439-Improve-Arrow-API.patch
+++ b/patches/server/0427-Improve-Arrow-API.patch
diff --git a/patches/server/0440-Add-and-implement-PlayerRecipeBookClickEvent.patch b/patches/server/0428-Add-and-implement-PlayerRecipeBookClickEvent.patch
index c067c44ca1..c067c44ca1 100644
--- a/patches/server/0440-Add-and-implement-PlayerRecipeBookClickEvent.patch
+++ b/patches/server/0428-Add-and-implement-PlayerRecipeBookClickEvent.patch
diff --git a/patches/server/0441-Hide-sync-chunk-writes-behind-flag.patch b/patches/server/0429-Hide-sync-chunk-writes-behind-flag.patch
index 1d9e0f2b56..1d9e0f2b56 100644
--- a/patches/server/0441-Hide-sync-chunk-writes-behind-flag.patch
+++ b/patches/server/0429-Hide-sync-chunk-writes-behind-flag.patch
diff --git a/patches/server/0442-Add-permission-for-command-blocks.patch b/patches/server/0430-Add-permission-for-command-blocks.patch
index a0bcd3db18..a0bcd3db18 100644
--- a/patches/server/0442-Add-permission-for-command-blocks.patch
+++ b/patches/server/0430-Add-permission-for-command-blocks.patch
diff --git a/patches/server/0443-Ensure-Entity-AABB-s-are-never-invalid.patch b/patches/server/0431-Ensure-Entity-AABB-s-are-never-invalid.patch
index 8dc151efda..04d761dfc7 100644
--- a/patches/server/0443-Ensure-Entity-AABB-s-are-never-invalid.patch
+++ b/patches/server/0431-Ensure-Entity-AABB-s-are-never-invalid.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Ensure Entity AABB's are never invalid
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index afe081b095bb53c7a1a1d2145e60b8b5426d2ce0..3ebe5297aa2fde1cb347f738738f5d74f6a61b9a 100644
+index c8ca2835c4fbad85a9680d0cc1da2fdc9a5324b6..544d59be3722cd13e0590a5b03059da0fd5ce364 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -662,8 +662,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -713,8 +713,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public void setPos(double x, double y, double z) {
@@ -19,7 +19,7 @@ index afe081b095bb53c7a1a1d2145e60b8b5426d2ce0..3ebe5297aa2fde1cb347f738738f5d74
}
protected AABB makeBoundingBox() {
-@@ -3884,6 +3884,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3945,6 +3945,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public final void setPosRaw(double x, double y, double z) {
@@ -28,10 +28,10 @@ index afe081b095bb53c7a1a1d2145e60b8b5426d2ce0..3ebe5297aa2fde1cb347f738738f5d74
+ }
+ public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
+ // Paper end
- if (this.position.x != x || this.position.y != y || this.position.z != z) {
- this.position = new Vec3(x, y, z);
- int i = Mth.floor(x);
-@@ -3901,6 +3906,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ // Paper start - rewrite chunk system
+ if (this.updatingSectionStatus) {
+ LOGGER.error("Refusing to update position for entity " + this + " to position " + new Vec3(x, y, z) + " since it is processing a section status update", new Throwable());
+@@ -3968,6 +3973,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.levelCallback.onMove();
}
diff --git a/patches/server/0444-Fix-Per-World-Difficulty-Remembering-Difficulty.patch b/patches/server/0432-Fix-Per-World-Difficulty-Remembering-Difficulty.patch
index f734c3b466..308b3b8c69 100644
--- a/patches/server/0444-Fix-Per-World-Difficulty-Remembering-Difficulty.patch
+++ b/patches/server/0432-Fix-Per-World-Difficulty-Remembering-Difficulty.patch
@@ -8,7 +8,7 @@ makes it so that the server keeps the last difficulty used instead
of restoring the server.properties every single load.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 9c2589c7e0517f771b9ca06760273a0aecefb27d..7d2fee97f4d08eae245475c4b60c1a7ba46c840d 100644
+index 057c335b15123061ed239f9559aad137e2c97982..1c39e141a392d96548f5ac62fb279ed1f105677d 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -791,7 +791,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -20,7 +20,7 @@ index 9c2589c7e0517f771b9ca06760273a0aecefb27d..7d2fee97f4d08eae245475c4b60c1a7b
this.forceTicks = false;
// CraftBukkit end
-@@ -1710,11 +1710,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1704,11 +1704,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@@ -40,7 +40,7 @@ index 9c2589c7e0517f771b9ca06760273a0aecefb27d..7d2fee97f4d08eae245475c4b60c1a7b
}
}
-@@ -1728,7 +1731,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1722,7 +1725,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
@@ -63,7 +63,7 @@ index 4bb29f86538552bb62125cc61210fd77b1ec671d..817193ca5fc15134d2985187bc2226cc
return 0;
}
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index e42df2956e2d852a5a4c8fdeda395a3efd32c44c..da83f111199a6b4c712a9bb8ab6f1d1b5c6ae77c 100644
+index 487991163f12c1ded3f5d35a718aa89b1fb9278f..8984a30f0fa790e7854534076912e619f9487160 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -332,7 +332,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -76,10 +76,10 @@ index e42df2956e2d852a5a4c8fdeda395a3efd32c44c..da83f111199a6b4c712a9bb8ab6f1d1b
@Override
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 29a03760f092a004a47e75120841d80e696b6c3d..be7d2275548936beade4aba02dc5b14fec95117a 100644
+index 0faa0d73907e6ef1285eba4cf7d692a5f8c3a0e3..582b9595aa06dd6787fd84c0ea0058c890f84c33 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1144,7 +1144,7 @@ public class ServerPlayer extends Player {
+@@ -1129,7 +1129,7 @@ public class ServerPlayer extends Player {
this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds
this.connection.send(new ClientboundRespawnPacket(worldserver.dimensionTypeId(), worldserver.dimension(), BiomeManager.obfuscateSeed(worldserver.getSeed()), this.gameMode.getGameModeForPlayer(), this.gameMode.getPreviousGameModeForPlayer(), worldserver.isDebug(), worldserver.isFlat(), true, this.getLastDeathLocation()));
@@ -102,7 +102,7 @@ index 02b6cf65f6abedfd4933e4e64d254f190e061301..c59e90ba0de83eeda3719b6303bee979
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index f0f2a5e94e0a4b143befa422fa99e68316f93d7f..99a9bf12f2e3e6ac5457da53f6f12118efce5d82 100644
+index ef6ca51679c818989f3ddd83f0bc7f37db6d6196..262d550e739928fc5f203432138fdcaf6ccb8ddb 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -951,8 +951,8 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0445-Paper-dumpitem-command.patch b/patches/server/0433-Paper-dumpitem-command.patch
index 930a212a00..77cf770637 100644
--- a/patches/server/0445-Paper-dumpitem-command.patch
+++ b/patches/server/0433-Paper-dumpitem-command.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Paper dumpitem command
Let's you quickly view the item in your hands NBT data
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
-index 25f6d1c15cdfb4abdf1edd2ad9bbdc0e37b546f3..d2536f4ffae721f4df714b5345fa3329c3b8e3f5 100644
+index 04bf08cbe45763f1338390c5ab4b0dcb334bd07a..a7b78508ef78229835805300e62a306a3f1ddf6d 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
@@ -1,6 +1,7 @@
@@ -18,8 +18,8 @@ index 25f6d1c15cdfb4abdf1edd2ad9bbdc0e37b546f3..d2536f4ffae721f4df714b5345fa3329
import io.papermc.paper.command.subcommands.FixLightCommand;
import io.papermc.paper.command.subcommands.HeapDumpCommand;
@@ -46,6 +47,7 @@ public final class PaperCommand extends Command {
- commands.put(Set.of("debug", "chunkinfo"), new ChunkDebugCommand());
commands.put(Set.of("fixlight"), new FixLightCommand());
+ commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand());
commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
+ commands.put(Set.of("dumpitem"), new DumpItemCommand());
diff --git a/patches/server/0446-Don-t-allow-null-UUID-s-for-chat.patch b/patches/server/0434-Don-t-allow-null-UUID-s-for-chat.patch
index 9c92814826..9c92814826 100644
--- a/patches/server/0446-Don-t-allow-null-UUID-s-for-chat.patch
+++ b/patches/server/0434-Don-t-allow-null-UUID-s-for-chat.patch
diff --git a/patches/server/0447-Improve-Legacy-Component-serialization-size.patch b/patches/server/0435-Improve-Legacy-Component-serialization-size.patch
index 866302739f..866302739f 100644
--- a/patches/server/0447-Improve-Legacy-Component-serialization-size.patch
+++ b/patches/server/0435-Improve-Legacy-Component-serialization-size.patch
diff --git a/patches/server/0448-Optimize-Bit-Operations-by-inlining.patch b/patches/server/0436-Optimize-Bit-Operations-by-inlining.patch
index c1cce4278a..c1cce4278a 100644
--- a/patches/server/0448-Optimize-Bit-Operations-by-inlining.patch
+++ b/patches/server/0436-Optimize-Bit-Operations-by-inlining.patch
diff --git a/patches/server/0449-Add-Plugin-Tickets-to-API-Chunk-Methods.patch b/patches/server/0437-Add-Plugin-Tickets-to-API-Chunk-Methods.patch
index 506018715e..a7cfa15425 100644
--- a/patches/server/0449-Add-Plugin-Tickets-to-API-Chunk-Methods.patch
+++ b/patches/server/0437-Add-Plugin-Tickets-to-API-Chunk-Methods.patch
@@ -22,7 +22,7 @@ wants it to collect even faster, they can restore that setting back to 1 instead
Not adding it to .getType() though to keep behavior consistent with vanilla for performance reasons.
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 99a9bf12f2e3e6ac5457da53f6f12118efce5d82..58f361ad664ba45c496ab0817c7ab5e1a017b807 100644
+index 262d550e739928fc5f203432138fdcaf6ccb8ddb..007b85cfda82d245ae336efc26aa38ad2f6d2c28 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -360,7 +360,7 @@ public final class CraftServer implements Server {
@@ -44,7 +44,7 @@ index 99a9bf12f2e3e6ac5457da53f6f12118efce5d82..58f361ad664ba45c496ab0817c7ab5e1
this.printSaveWarning = false;
console.autosavePeriod = this.configuration.getInt("ticks-per.autosave");
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index a8ab324bfbaaf946af5998402588244465dd7286..710270775cd47e8df1146ad0636648568d45ac75 100644
+index 414522b641cd866822c13a753cc623c463004fd6..d83ea6113d3f2f7aaeac6e09473282434ec2fc67 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -282,8 +282,21 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@@ -79,7 +79,7 @@ index a8ab324bfbaaf946af5998402588244465dd7286..710270775cd47e8df1146ad063664856
}
return true;
-@@ -433,9 +446,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -436,9 +449,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
// Paper start - Optimize this method
ChunkPos chunkPos = new ChunkPos(x, z);
@@ -93,7 +93,7 @@ index a8ab324bfbaaf946af5998402588244465dd7286..710270775cd47e8df1146ad063664856
if (immediate == null) {
immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
}
-@@ -443,7 +459,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -446,7 +462,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
if (!(immediate instanceof ImposterProtoChunk) && !(immediate instanceof net.minecraft.world.level.chunk.LevelChunk)) {
return false; // not full status
}
@@ -102,7 +102,7 @@ index a8ab324bfbaaf946af5998402588244465dd7286..710270775cd47e8df1146ad063664856
world.getChunk(x, z); // make sure we're at ticket level 32 or lower
return true;
}
-@@ -469,7 +485,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -472,7 +488,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// we do this so we do not re-read the chunk data on disk
}
@@ -111,7 +111,7 @@ index a8ab324bfbaaf946af5998402588244465dd7286..710270775cd47e8df1146ad063664856
world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
return true;
// Paper end
-@@ -2150,6 +2166,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -2148,6 +2164,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
net.minecraft.server.ChunkSystem.scheduleChunkLoad(this.getHandle(), x, z, gen, ChunkStatus.FULL, true, priority, (c) -> {
net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> {
net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c;
diff --git a/patches/server/0438-incremental-chunk-and-player-saving.patch b/patches/server/0438-incremental-chunk-and-player-saving.patch
new file mode 100644
index 0000000000..b53d082bfa
--- /dev/null
+++ b/patches/server/0438-incremental-chunk-and-player-saving.patch
@@ -0,0 +1,164 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Shane Freeder <[email protected]>
+Date: Sun, 9 Jun 2019 03:53:22 +0100
+Subject: [PATCH] incremental chunk and player saving
+
+
+diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
+index 1c39e141a392d96548f5ac62fb279ed1f105677d..922475997902dcc85ae42f351ace15e82b5aa638 100644
+--- a/src/main/java/net/minecraft/server/MinecraftServer.java
++++ b/src/main/java/net/minecraft/server/MinecraftServer.java
+@@ -864,7 +864,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+
+ try {
+ this.isSaving = true;
+- this.getPlayerList().saveAll();
++ this.getPlayerList().saveAll(); // Diff on change
+ flag3 = this.saveAllChunks(suppressLogs, flush, force);
+ } finally {
+ this.isSaving = false;
+@@ -1394,13 +1394,28 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+ }
+ }
+
+- if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit
+- MinecraftServer.LOGGER.debug("Autosave started");
+- this.profiler.push("save");
+- this.saveEverything(true, false, false);
+- this.profiler.pop();
+- MinecraftServer.LOGGER.debug("Autosave finished");
++ // Paper start - incremental chunk and player saving
++ int playerSaveInterval = io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.rate;
++ if (playerSaveInterval < 0) {
++ playerSaveInterval = autosavePeriod;
+ }
++ this.profiler.push("save");
++ final boolean fullSave = autosavePeriod > 0 && this.tickCount % autosavePeriod == 0;
++ try {
++ this.isSaving = true;
++ if (playerSaveInterval > 0) {
++ this.playerList.saveAll(playerSaveInterval);
++ }
++ for (ServerLevel level : this.getAllLevels()) {
++ if (level.paperConfig().chunks.autoSaveInterval.value() > 0) {
++ level.saveIncrementally(fullSave);
++ }
++ }
++ } finally {
++ this.isSaving = false;
++ }
++ this.profiler.pop();
++ // Paper end
+ io.papermc.paper.util.CachedLists.reset(); // Paper
+ // Paper start - move executeAll() into full server tick timing
+ try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) {
+diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+index 44766ea7e5dd8f8411b52cf259187d7557cc0c23..fa7801158b68eaa12d6322c9c0def9de37f03814 100644
+--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+@@ -601,6 +601,15 @@ public class ServerChunkCache extends ChunkSource {
+ } // Paper - Timings
+ }
+
++ // Paper start - duplicate save, but call incremental
++ public void saveIncrementally() {
++ this.runDistanceManagerUpdates();
++ try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings
++ this.chunkMap.saveIncrementally();
++ } // Paper - Timings
++ }
++ // Paper end
++
+ @Override
+ public void close() throws IOException {
+ // CraftBukkit start
+diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
+index c0e17bbf04723da76ea6952d9558dd4d34b00f6c..fc3e5068473e1586024e87fee3eeeb6cf5124923 100644
+--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
+@@ -1120,6 +1120,37 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos);
+ }
+
++ // Paper start - derived from below
++ public void saveIncrementally(boolean doFull) {
++ ServerChunkCache chunkproviderserver = this.getChunkSource();
++
++ if (doFull) {
++ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld()));
++ }
++
++ try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) {
++ if (doFull) {
++ this.saveLevelData();
++ }
++
++ this.timings.worldSaveChunks.startTiming(); // Paper
++ if (!this.noSave()) chunkproviderserver.saveIncrementally();
++ this.timings.worldSaveChunks.stopTiming(); // Paper
++
++ // Copied from save()
++ // CraftBukkit start - moved from MinecraftServer.saveChunks
++ if (doFull) { // Paper
++ ServerLevel worldserver1 = this;
++
++ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
++ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save());
++ this.convertable.saveDataTag(this.server.registryHolder, this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
++ }
++ // CraftBukkit end
++ }
++ }
++ // Paper end
++
+ public void save(@Nullable ProgressListener progressListener, boolean flush, boolean savingDisabled) {
+ // Paper start - rewrite chunk system - add close param
+ this.save(progressListener, flush, savingDisabled, false);
+diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
+index 582b9595aa06dd6787fd84c0ea0058c890f84c33..02bb4ecdc6797d1c5bccc64cc235fe63bdc7ccd4 100644
+--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
++++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
+@@ -179,6 +179,7 @@ import org.bukkit.inventory.MainHand;
+ public class ServerPlayer extends Player {
+
+ private static final Logger LOGGER = LogUtils.getLogger();
++ public long lastSave = MinecraftServer.currentTick; // Paper
+ private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
+ private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
+ public ServerGamePacketListenerImpl connection;
+diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
+index f37787b9d018a624b509612729f18ca077b55f55..b0120df807a61db21da04324644fbd71a285f461 100644
+--- a/src/main/java/net/minecraft/server/players/PlayerList.java
++++ b/src/main/java/net/minecraft/server/players/PlayerList.java
+@@ -569,6 +569,7 @@ public abstract class PlayerList {
+ protected void save(ServerPlayer player) {
+ if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
+ if (!player.didPlayerJoinEvent) return; // Paper - If we never fired PJE, we disconnected during login. Data has not changed, and additionally, our saved vehicle is not loaded! If we save now, we will lose our vehicle (CraftBukkit bug)
++ player.lastSave = MinecraftServer.currentTick; // Paper
+ this.playerIo.save(player);
+ ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit
+
+@@ -1171,10 +1172,22 @@ public abstract class PlayerList {
+ }
+
+ public void saveAll() {
++ // Paper start - incremental player saving
++ this.saveAll(-1);
++ }
++
++ public void saveAll(int interval) {
+ net.minecraft.server.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main
+ MinecraftTimings.savePlayers.startTiming(); // Paper
++ int numSaved = 0;
++ long now = MinecraftServer.currentTick;
+ for (int i = 0; i < this.players.size(); ++i) {
+- this.save(this.players.get(i));
++ ServerPlayer entityplayer = this.players.get(i);
++ if (interval == -1 || now - entityplayer.lastSave >= interval) {
++ this.save(entityplayer);
++ if (interval != -1 && ++numSaved <= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { break; }
++ }
++ // Paper end
+ }
+ MinecraftTimings.savePlayers.stopTiming(); // Paper
+ return null; }); // Paper - ensure main
diff --git a/patches/server/0451-Stop-copy-on-write-operations-for-updating-light-dat.patch b/patches/server/0439-Stop-copy-on-write-operations-for-updating-light-dat.patch
index 74e5860e6b..c13f70aec1 100644
--- a/patches/server/0451-Stop-copy-on-write-operations-for-updating-light-dat.patch
+++ b/patches/server/0439-Stop-copy-on-write-operations-for-updating-light-dat.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 27 Apr 2020 04:05:38 -0700
Subject: [PATCH] Stop copy-on-write operations for updating light data
diff --git a/patches/server/0452-Support-old-UUID-format-for-NBT.patch b/patches/server/0440-Support-old-UUID-format-for-NBT.patch
index 04f6394d7a..04f6394d7a 100644
--- a/patches/server/0452-Support-old-UUID-format-for-NBT.patch
+++ b/patches/server/0440-Support-old-UUID-format-for-NBT.patch
diff --git a/patches/server/0453-Clean-up-duplicated-GameProfile-Properties.patch b/patches/server/0441-Clean-up-duplicated-GameProfile-Properties.patch
index 25554ddf57..25554ddf57 100644
--- a/patches/server/0453-Clean-up-duplicated-GameProfile-Properties.patch
+++ b/patches/server/0441-Clean-up-duplicated-GameProfile-Properties.patch
diff --git a/patches/server/0454-Convert-legacy-attributes-in-Item-Meta.patch b/patches/server/0442-Convert-legacy-attributes-in-Item-Meta.patch
index b949a9e873..b949a9e873 100644
--- a/patches/server/0454-Convert-legacy-attributes-in-Item-Meta.patch
+++ b/patches/server/0442-Convert-legacy-attributes-in-Item-Meta.patch
diff --git a/patches/server/0455-Remove-some-streams-from-structures.patch b/patches/server/0443-Remove-some-streams-from-structures.patch
index d535ed9e91..d535ed9e91 100644
--- a/patches/server/0455-Remove-some-streams-from-structures.patch
+++ b/patches/server/0443-Remove-some-streams-from-structures.patch
diff --git a/patches/server/0456-Remove-streams-from-classes-related-villager-gossip.patch b/patches/server/0444-Remove-streams-from-classes-related-villager-gossip.patch
index b70ae30d26..b70ae30d26 100644
--- a/patches/server/0456-Remove-streams-from-classes-related-villager-gossip.patch
+++ b/patches/server/0444-Remove-streams-from-classes-related-villager-gossip.patch
diff --git a/patches/server/0457-Support-components-in-ItemMeta.patch b/patches/server/0445-Support-components-in-ItemMeta.patch
index 05ab3c1898..05ab3c1898 100644
--- a/patches/server/0457-Support-components-in-ItemMeta.patch
+++ b/patches/server/0445-Support-components-in-ItemMeta.patch
diff --git a/patches/server/0458-Improve-EntityTargetLivingEntityEvent-for-1.16-mobs.patch b/patches/server/0446-Improve-EntityTargetLivingEntityEvent-for-1.16-mobs.patch
index 68a10afd43..68a10afd43 100644
--- a/patches/server/0458-Improve-EntityTargetLivingEntityEvent-for-1.16-mobs.patch
+++ b/patches/server/0446-Improve-EntityTargetLivingEntityEvent-for-1.16-mobs.patch
diff --git a/patches/server/0459-Add-entity-liquid-API.patch b/patches/server/0447-Add-entity-liquid-API.patch
index 57a97d8edd..57a97d8edd 100644
--- a/patches/server/0459-Add-entity-liquid-API.patch
+++ b/patches/server/0447-Add-entity-liquid-API.patch
diff --git a/patches/server/0460-Update-itemstack-legacy-name-and-lore.patch b/patches/server/0448-Update-itemstack-legacy-name-and-lore.patch
index b354241c07..b354241c07 100644
--- a/patches/server/0460-Update-itemstack-legacy-name-and-lore.patch
+++ b/patches/server/0448-Update-itemstack-legacy-name-and-lore.patch
diff --git a/patches/server/0461-Spawn-player-in-correct-world-on-login.patch b/patches/server/0449-Spawn-player-in-correct-world-on-login.patch
index a59a3e8562..a5a6a1cf19 100644
--- a/patches/server/0461-Spawn-player-in-correct-world-on-login.patch
+++ b/patches/server/0449-Spawn-player-in-correct-world-on-login.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Spawn player in correct world on login
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 1850ce4566e6c5d19140cbf2636b3573f16c4239..1cb6900489ad25ddd40a3b8c39f436c03a72b3dc 100644
+index b0120df807a61db21da04324644fbd71a285f461..354719476074acc75b35f8e4a06f0363bd13a3f3 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -203,7 +203,18 @@ public abstract class PlayerList {
diff --git a/patches/server/0462-Add-PrepareResultEvent.patch b/patches/server/0450-Add-PrepareResultEvent.patch
index 415d9409c8..415d9409c8 100644
--- a/patches/server/0462-Add-PrepareResultEvent.patch
+++ b/patches/server/0450-Add-PrepareResultEvent.patch
diff --git a/patches/server/0450-incremental-chunk-and-player-saving.patch b/patches/server/0450-incremental-chunk-and-player-saving.patch
deleted file mode 100644
index 8c38af4a69..0000000000
--- a/patches/server/0450-incremental-chunk-and-player-saving.patch
+++ /dev/null
@@ -1,375 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Shane Freeder <[email protected]>
-Date: Sun, 9 Jun 2019 03:53:22 +0100
-Subject: [PATCH] incremental chunk and player saving
-
-
-diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 7d2fee97f4d08eae245475c4b60c1a7ba46c840d..48650bc1c09b18f1b57d9828dfe27f51c74c4a75 100644
---- a/src/main/java/net/minecraft/server/MinecraftServer.java
-+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -854,7 +854,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
-
- try {
- this.isSaving = true;
-- this.getPlayerList().saveAll();
-+ this.getPlayerList().saveAll(); // Diff on change
- flag3 = this.saveAllChunks(suppressLogs, flush, force);
- } finally {
- this.isSaving = false;
-@@ -1400,13 +1400,28 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
- }
- }
-
-- if (this.autosavePeriod > 0 && this.tickCount % this.autosavePeriod == 0) { // CraftBukkit
-- MinecraftServer.LOGGER.debug("Autosave started");
-- this.profiler.push("save");
-- this.saveEverything(true, false, false);
-- this.profiler.pop();
-- MinecraftServer.LOGGER.debug("Autosave finished");
-+ // Paper start - incremental chunk and player saving
-+ int playerSaveInterval = io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.rate;
-+ if (playerSaveInterval < 0) {
-+ playerSaveInterval = autosavePeriod;
- }
-+ this.profiler.push("save");
-+ final boolean fullSave = autosavePeriod > 0 && this.tickCount % autosavePeriod == 0;
-+ try {
-+ this.isSaving = true;
-+ if (playerSaveInterval > 0) {
-+ this.playerList.saveAll(playerSaveInterval);
-+ }
-+ for (ServerLevel level : this.getAllLevels()) {
-+ if (level.paperConfig().chunks.autoSaveInterval.value() > 0) {
-+ level.saveIncrementally(fullSave);
-+ }
-+ }
-+ } finally {
-+ this.isSaving = false;
-+ }
-+ this.profiler.pop();
-+ // Paper end
- io.papermc.paper.util.CachedLists.reset(); // Paper
- // Paper start - move executeAll() into full server tick timing
- try (co.aikar.timings.Timing ignored = MinecraftTimings.processTasksTimer.startTiming()) {
-diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-index 0f75a109c06eb3113be74cf49ec560f5e2ea9cfc..ac42029596ae0c824bf33a4058ac1009740e29ea 100644
---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-@@ -99,6 +99,8 @@ public class ChunkHolder {
- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
- com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange;
- // Paper end - optimise anyPlayerCloseEnoughForSpawning
-+ long lastAutoSaveTime; // Paper - incremental autosave
-+ long inactiveTimeStart; // Paper - incremental autosave
-
- public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
- this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
-@@ -527,7 +529,19 @@ public class ChunkHolder {
- boolean flag2 = playerchunk_state.isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
- boolean flag3 = playerchunk_state1.isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
-
-+ boolean prevHasBeenLoaded = this.wasAccessibleSinceLastSave; // Paper
- this.wasAccessibleSinceLastSave |= flag3;
-+ // Paper start - incremental autosave
-+ if (this.wasAccessibleSinceLastSave & !prevHasBeenLoaded) {
-+ long timeSinceAutoSave = this.inactiveTimeStart - this.lastAutoSaveTime;
-+ if (timeSinceAutoSave < 0) {
-+ // safest bet is to assume autosave is needed here
-+ timeSinceAutoSave = this.chunkMap.level.paperConfig().chunks.autoSaveInterval.value();
-+ }
-+ this.lastAutoSaveTime = this.chunkMap.level.getGameTime() - timeSinceAutoSave;
-+ this.chunkMap.autoSaveQueue.add(this);
-+ }
-+ // Paper end
- if (!flag2 && flag3) {
- int expectCreateCount = ++this.fullChunkCreateCount; // Paper
- this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this);
-@@ -689,8 +703,32 @@ public class ChunkHolder {
- }
-
- public void refreshAccessibility() {
-+ boolean prev = this.wasAccessibleSinceLastSave; // Paper
- this.wasAccessibleSinceLastSave = ChunkHolder.getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
-+ // Paper start - incremental autosave
-+ if (prev != this.wasAccessibleSinceLastSave) {
-+ if (this.wasAccessibleSinceLastSave) {
-+ long timeSinceAutoSave = this.inactiveTimeStart - this.lastAutoSaveTime;
-+ if (timeSinceAutoSave < 0) {
-+ // safest bet is to assume autosave is needed here
-+ timeSinceAutoSave = this.chunkMap.level.paperConfig().chunks.autoSaveInterval.value();
-+ }
-+ this.lastAutoSaveTime = this.chunkMap.level.getGameTime() - timeSinceAutoSave;
-+ this.chunkMap.autoSaveQueue.add(this);
-+ } else {
-+ this.inactiveTimeStart = this.chunkMap.level.getGameTime();
-+ this.chunkMap.autoSaveQueue.remove(this);
-+ }
-+ }
-+ // Paper end
-+ }
-+
-+ // Paper start - incremental autosave
-+ public boolean setHasBeenLoaded() {
-+ this.wasAccessibleSinceLastSave = getFullChunkStatus(this.ticketLevel).isOrAfter(ChunkHolder.FullChunkStatus.BORDER);
-+ return this.wasAccessibleSinceLastSave;
- }
-+ // Paper end
-
- public void replaceProtoChunk(ImposterProtoChunk chunk) {
- for (int i = 0; i < this.futures.length(); ++i) {
-diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index b0e0f85e04438affb8d8e0f75055ea83d0c03bcd..7493da0f1c3f8ab0ebc517347ef23fbe2747a306 100644
---- a/src/main/java/net/minecraft/server/level/ChunkMap.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -103,6 +103,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
- import net.minecraft.world.level.storage.DimensionDataStorage;
- import net.minecraft.world.level.storage.LevelStorageSource;
- import net.minecraft.world.phys.Vec3;
-+import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; // Paper
- import org.apache.commons.lang3.mutable.MutableBoolean;
- import org.apache.commons.lang3.mutable.MutableObject;
- import org.slf4j.Logger;
-@@ -779,6 +780,64 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-
- }
-
-+ // Paper start - incremental autosave
-+ final ObjectRBTreeSet<ChunkHolder> autoSaveQueue = new ObjectRBTreeSet<>((playerchunk1, playerchunk2) -> {
-+ int timeCompare = Long.compare(playerchunk1.lastAutoSaveTime, playerchunk2.lastAutoSaveTime);
-+ if (timeCompare != 0) {
-+ return timeCompare;
-+ }
-+
-+ return Long.compare(MCUtil.getCoordinateKey(playerchunk1.pos), MCUtil.getCoordinateKey(playerchunk2.pos));
-+ });
-+
-+ protected void saveIncrementally() {
-+ int savedThisTick = 0;
-+ // optimized since we search far less chunks to hit ones that need to be saved
-+ List<ChunkHolder> reschedule = new java.util.ArrayList<>(this.level.paperConfig().chunks.maxAutoSaveChunksPerTick);
-+ long currentTick = this.level.getGameTime();
-+ long maxSaveTime = currentTick - this.level.paperConfig().chunks.autoSaveInterval.value();
-+
-+ for (Iterator<ChunkHolder> iterator = this.autoSaveQueue.iterator(); iterator.hasNext();) {
-+ ChunkHolder playerchunk = iterator.next();
-+ if (playerchunk.lastAutoSaveTime > maxSaveTime) {
-+ break;
-+ }
-+
-+ iterator.remove();
-+
-+ ChunkAccess ichunkaccess = playerchunk.getChunkToSave().getNow(null);
-+ if (ichunkaccess instanceof LevelChunk) {
-+ boolean shouldSave = ((LevelChunk)ichunkaccess).lastSaveTime <= maxSaveTime;
-+
-+ if (shouldSave && this.save(ichunkaccess) && this.level.entityManager.storeChunkSections(playerchunk.pos.toLong(), entity -> {})) {
-+ ++savedThisTick;
-+
-+ if (!playerchunk.setHasBeenLoaded()) {
-+ // do not fall through to reschedule logic
-+ playerchunk.inactiveTimeStart = currentTick;
-+ if (savedThisTick >= this.level.paperConfig().chunks.maxAutoSaveChunksPerTick) {
-+ break;
-+ }
-+ continue;
-+ }
-+ }
-+ }
-+
-+ reschedule.add(playerchunk);
-+
-+ if (savedThisTick >= this.level.paperConfig().chunks.maxAutoSaveChunksPerTick) {
-+ break;
-+ }
-+ }
-+
-+ for (int i = 0, len = reschedule.size(); i < len; ++i) {
-+ ChunkHolder playerchunk = reschedule.get(i);
-+ playerchunk.lastAutoSaveTime = this.level.getGameTime();
-+ this.autoSaveQueue.add(playerchunk);
-+ }
-+ }
-+ // Paper end
-+
- protected void saveAllChunks(boolean flush) {
- // Paper start - do not overload I/O threads with too much work when saving
- int[] saved = new int[1];
-@@ -874,13 +933,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- }
-
- int l = 0;
-- Iterator objectiterator = net.minecraft.server.ChunkSystem.getVisibleChunkHolders(this.level).iterator(); // Paper
--
-- while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) {
-- if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) {
-- ++l;
-- }
-- }
-+ // Paper - incremental chunk and player saving
-
- }
-
-@@ -916,6 +969,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
-
- this.level.unload(chunk);
- }
-+ this.autoSaveQueue.remove(holder); // Paper
-
- this.lightEngine.updateChunkStatus(ichunkaccess.getPos());
- this.lightEngine.tryScheduleUpdate();
-@@ -1367,6 +1421,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- asyncSaveData, chunk);
-
- chunk.setUnsaved(false);
-+ chunk.setLastSaved(this.level.getGameTime()); // Paper - track last saved time
- }
- // Paper end
-
-@@ -1376,6 +1431,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- if (!chunk.isUnsaved()) {
- return false;
- } else {
-+ chunk.setLastSaved(this.level.getGameTime()); // Paper - track save time
- chunk.setUnsaved(false);
- ChunkPos chunkcoordintpair = chunk.getPos();
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index a59782d6f3640262c377a676e2b2ef5ec82563db..b5f46703e536f8138ff4e6769485c45b35941f9f 100644
---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -670,6 +670,15 @@ public class ServerChunkCache extends ChunkSource {
- } // Paper - Timings
- }
-
-+ // Paper start - duplicate save, but call incremental
-+ public void saveIncrementally() {
-+ this.runDistanceManagerUpdates();
-+ try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings
-+ this.chunkMap.saveIncrementally();
-+ } // Paper - Timings
-+ }
-+ // Paper end
-+
- @Override
- public void close() throws IOException {
- // CraftBukkit start
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index c9ecc7593c299b351308634db44596a76fd0c09b..8b5eac2ad96c0ebb6eae04585998cade578ff74b 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1087,6 +1087,37 @@ public class ServerLevel extends Level implements WorldGenLevel {
- return !this.server.isUnderSpawnProtection(this, pos, player) && this.getWorldBorder().isWithinBounds(pos);
- }
-
-+ // Paper start - derived from below
-+ public void saveIncrementally(boolean doFull) {
-+ ServerChunkCache chunkproviderserver = this.getChunkSource();
-+
-+ if (doFull) {
-+ org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld()));
-+ }
-+
-+ try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) {
-+ if (doFull) {
-+ this.saveLevelData();
-+ }
-+
-+ this.timings.worldSaveChunks.startTiming(); // Paper
-+ if (!this.noSave()) chunkproviderserver.saveIncrementally();
-+ this.timings.worldSaveChunks.stopTiming(); // Paper
-+
-+ // Copied from save()
-+ // CraftBukkit start - moved from MinecraftServer.saveChunks
-+ if (doFull) { // Paper
-+ ServerLevel worldserver1 = this;
-+
-+ this.serverLevelData.setWorldBorder(worldserver1.getWorldBorder().createSettings());
-+ this.serverLevelData.setCustomBossEvents(this.server.getCustomBossEvents().save());
-+ this.convertable.saveDataTag(this.server.registryHolder, this.serverLevelData, this.server.getPlayerList().getSingleplayerData());
-+ }
-+ // CraftBukkit end
-+ }
-+ }
-+ // Paper end
-+
- public void save(@Nullable ProgressListener progressListener, boolean flush, boolean savingDisabled) {
- ServerChunkCache chunkproviderserver = this.getChunkSource();
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index be7d2275548936beade4aba02dc5b14fec95117a..6f2b52165c1935511790a429792d3754251537c8 100644
---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
-+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -179,6 +179,7 @@ import org.bukkit.inventory.MainHand;
- public class ServerPlayer extends Player {
-
- private static final Logger LOGGER = LogUtils.getLogger();
-+ public long lastSave = MinecraftServer.currentTick; // Paper
- private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
- private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
- public ServerGamePacketListenerImpl connection;
-diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 32ab0cd6cb42b0ab8a14f790dfcf4b155c945d6d..1850ce4566e6c5d19140cbf2636b3573f16c4239 100644
---- a/src/main/java/net/minecraft/server/players/PlayerList.java
-+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
-@@ -569,6 +569,7 @@ public abstract class PlayerList {
- protected void save(ServerPlayer player) {
- if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
- if (!player.didPlayerJoinEvent) return; // Paper - If we never fired PJE, we disconnected during login. Data has not changed, and additionally, our saved vehicle is not loaded! If we save now, we will lose our vehicle (CraftBukkit bug)
-+ player.lastSave = MinecraftServer.currentTick; // Paper
- this.playerIo.save(player);
- ServerStatsCounter serverstatisticmanager = (ServerStatsCounter) player.getStats(); // CraftBukkit
-
-@@ -1171,10 +1172,22 @@ public abstract class PlayerList {
- }
-
- public void saveAll() {
-+ // Paper start - incremental player saving
-+ this.saveAll(-1);
-+ }
-+
-+ public void saveAll(int interval) {
- net.minecraft.server.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main
- MinecraftTimings.savePlayers.startTiming(); // Paper
-+ int numSaved = 0;
-+ long now = MinecraftServer.currentTick;
- for (int i = 0; i < this.players.size(); ++i) {
-- this.save(this.players.get(i));
-+ ServerPlayer entityplayer = this.players.get(i);
-+ if (interval == -1 || now - entityplayer.lastSave >= interval) {
-+ this.save(entityplayer);
-+ if (interval != -1 && ++numSaved <= io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick()) { break; }
-+ }
-+ // Paper end
- }
- MinecraftTimings.savePlayers.stopTiming(); // Paper
- return null; }); // Paper - ensure main
-diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-index dc164608bfb2fb18a1adf83fa10bac4028dcac0a..a97909e77b9b28aede8c8716831c3f9a90618f09 100644
---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java
-@@ -457,6 +457,7 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom
- public LevelHeightAccessor getHeightAccessorForGeneration() {
- return this;
- }
-+ public void setLastSaved(long ticks) {} // Paper
-
- public static record TicksToSave(SerializableTickContainer<Block> blocks, SerializableTickContainer<Fluid> fluids) {
-
-diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index c8444f4bfd127b7d8194aaa984505eff249ae094..2981ba61e347b8660082ff946521fc7f219d2c0d 100644
---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -87,6 +87,12 @@ public class LevelChunk extends ChunkAccess {
- private final Int2ObjectMap<GameEventDispatcher> gameEventDispatcherSections;
- private final LevelChunkTicks<Block> blockTicks;
- private final LevelChunkTicks<Fluid> fluidTicks;
-+ // Paper start - track last save time
-+ public long lastSaveTime;
-+ public void setLastSaved(long ticks) {
-+ this.lastSaveTime = ticks;
-+ }
-+ // Paper end
-
- public LevelChunk(Level world, ChunkPos pos) {
- this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null);
diff --git a/patches/server/0463-Don-t-check-chunk-for-portal-on-world-gen-entity-add.patch b/patches/server/0451-Don-t-check-chunk-for-portal-on-world-gen-entity-add.patch
index 0319fe7f46..0319fe7f46 100644
--- a/patches/server/0463-Don-t-check-chunk-for-portal-on-world-gen-entity-add.patch
+++ b/patches/server/0451-Don-t-check-chunk-for-portal-on-world-gen-entity-add.patch
diff --git a/patches/server/0464-Optimize-NetworkManager-Exception-Handling.patch b/patches/server/0452-Optimize-NetworkManager-Exception-Handling.patch
index cf2905d1ed..cf2905d1ed 100644
--- a/patches/server/0464-Optimize-NetworkManager-Exception-Handling.patch
+++ b/patches/server/0452-Optimize-NetworkManager-Exception-Handling.patch
diff --git a/patches/server/0465-Optimize-the-advancement-data-player-iteration-to-be.patch b/patches/server/0453-Optimize-the-advancement-data-player-iteration-to-be.patch
index 0763953dd5..0763953dd5 100644
--- a/patches/server/0465-Optimize-the-advancement-data-player-iteration-to-be.patch
+++ b/patches/server/0453-Optimize-the-advancement-data-player-iteration-to-be.patch
diff --git a/patches/server/0466-Fix-arrows-never-despawning-MC-125757.patch b/patches/server/0454-Fix-arrows-never-despawning-MC-125757.patch
index 8bdc8589fd..8bdc8589fd 100644
--- a/patches/server/0466-Fix-arrows-never-despawning-MC-125757.patch
+++ b/patches/server/0454-Fix-arrows-never-despawning-MC-125757.patch
diff --git a/patches/server/0467-Thread-Safe-Vanilla-Command-permission-checking.patch b/patches/server/0455-Thread-Safe-Vanilla-Command-permission-checking.patch
index 09b482e662..09b482e662 100644
--- a/patches/server/0467-Thread-Safe-Vanilla-Command-permission-checking.patch
+++ b/patches/server/0455-Thread-Safe-Vanilla-Command-permission-checking.patch
diff --git a/patches/server/0468-Fix-SPIGOT-5989.patch b/patches/server/0456-Fix-SPIGOT-5989.patch
index 111dc3b220..5136ee4fc5 100644
--- a/patches/server/0468-Fix-SPIGOT-5989.patch
+++ b/patches/server/0456-Fix-SPIGOT-5989.patch
@@ -10,7 +10,7 @@ This fixes that by checking if the modified spawn location is
still at a respawn anchor.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 1cb6900489ad25ddd40a3b8c39f436c03a72b3dc..8fd4494fcd29e92856938792b922ee3ef0102604 100644
+index 354719476074acc75b35f8e4a06f0363bd13a3f3..db03f510195981e9f860ce2fdc59d0a70283dc14 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -842,6 +842,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0469-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch b/patches/server/0457-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch
index c7303d0b5c..c7303d0b5c 100644
--- a/patches/server/0469-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch
+++ b/patches/server/0457-Fix-SPIGOT-5824-Bukkit-world-container-is-not-used.patch
diff --git a/patches/server/0470-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch b/patches/server/0458-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch
index 5010bc5bf1..5010bc5bf1 100644
--- a/patches/server/0470-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch
+++ b/patches/server/0458-Fix-SPIGOT-5885-Unable-to-disable-advancements.patch
diff --git a/patches/server/0471-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch b/patches/server/0459-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch
index 484c50da0a..da7194a954 100644
--- a/patches/server/0471-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch
+++ b/patches/server/0459-Fix-AdvancementDataPlayer-leak-due-from-quitting-ear.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 13 Jul 2020 06:22:54 -0700
Subject: [PATCH] Fix AdvancementDataPlayer leak due from quitting early in
login
diff --git a/patches/server/0472-Add-missing-strikeLighting-call-to-World-spigot-stri.patch b/patches/server/0460-Add-missing-strikeLighting-call-to-World-spigot-stri.patch
index 4c6955f7fd..15a24cf922 100644
--- a/patches/server/0472-Add-missing-strikeLighting-call-to-World-spigot-stri.patch
+++ b/patches/server/0460-Add-missing-strikeLighting-call-to-World-spigot-stri.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Add missing strikeLighting call to
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 710270775cd47e8df1146ad0636648568d45ac75..04896192121039ef6a3be103d59a9d687d7f6c05 100644
+index d83ea6113d3f2f7aaeac6e09473282434ec2fc67..8a5967b5e4196d1df1aacb28ccefadb7863b75a5 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -2136,6 +2136,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -2134,6 +2134,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
lightning.moveTo( loc.getX(), loc.getY(), loc.getZ() );
lightning.visualOnly = true;
lightning.isSilent = isSilent;
diff --git a/patches/server/0473-Fix-some-rails-connecting-improperly.patch b/patches/server/0461-Fix-some-rails-connecting-improperly.patch
index 4dc21a2695..f612612b1c 100644
--- a/patches/server/0473-Fix-some-rails-connecting-improperly.patch
+++ b/patches/server/0461-Fix-some-rails-connecting-improperly.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Fri, 24 Jul 2020 15:56:05 -0700
Subject: [PATCH] Fix some rails connecting improperly
diff --git a/patches/server/0474-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch b/patches/server/0462-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch
index be48fd5ae2..be48fd5ae2 100644
--- a/patches/server/0474-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch
+++ b/patches/server/0462-Fix-regex-mistake-in-CB-NBT-int-deserialization.patch
diff --git a/patches/server/0475-Do-not-let-the-server-load-chunks-from-newer-version.patch b/patches/server/0463-Do-not-let-the-server-load-chunks-from-newer-version.patch
index 6fb1c85c61..efa8bf5dc9 100644
--- a/patches/server/0475-Do-not-let-the-server-load-chunks-from-newer-version.patch
+++ b/patches/server/0463-Do-not-let-the-server-load-chunks-from-newer-version.patch
@@ -9,10 +9,10 @@ the game, immediately stop the server to prevent data corruption.
You can override this functionality at your own peril.
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index f26a08f81495dde6205b34254d159b042e5a6ea9..12e3831324abdc1112cbe65cab0f0c75ce77a9ef 100644
+index 3c61be19c65b2da9283b2aba2b4e66f84bac6e1c..31e552e1b4c3a6931a61a88a75965a0427d6de8d 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -118,9 +118,22 @@ public class ChunkSerializer {
+@@ -126,9 +126,22 @@ public class ChunkSerializer {
return holder.protoChunk;
}
diff --git a/patches/server/0476-Brand-support.patch b/patches/server/0464-Brand-support.patch
index 427556e71b..7fb99e9a34 100644
--- a/patches/server/0476-Brand-support.patch
+++ b/patches/server/0464-Brand-support.patch
@@ -56,10 +56,10 @@ index c59e90ba0de83eeda3719b6303bee9796b4a1af0..da6a0171bd63ac68635de1c23fc9eafa
return (!this.player.joining && !this.connection.isConnected()) || this.processedDisconnect; // Paper
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index b82d42210a84bc350b80bc061ea1f0ee36519522..e5792a497647e68230746f16a91347ec59711f1d 100644
+index c75f919617f60fb353583d6541beaa604efe045a..9a9c87ff97fe1d1020b41746270b5e39ac2bdc72 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -2725,6 +2725,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2760,6 +2760,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
// Paper end
};
diff --git a/patches/server/0477-Add-setMaxPlayers-API.patch b/patches/server/0465-Add-setMaxPlayers-API.patch
index 0efd813c6f..70a4396a97 100644
--- a/patches/server/0477-Add-setMaxPlayers-API.patch
+++ b/patches/server/0465-Add-setMaxPlayers-API.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add #setMaxPlayers API
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 8fd4494fcd29e92856938792b922ee3ef0102604..ceb5e9540e54275e97f08533d16c820061e76c8f 100644
+index db03f510195981e9f860ce2fdc59d0a70283dc14..4d05d23f5b6967da29d5c9c91931cd79745f5dcb 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -147,7 +147,7 @@ public abstract class PlayerList {
@@ -18,7 +18,7 @@ index 8fd4494fcd29e92856938792b922ee3ef0102604..ceb5e9540e54275e97f08533d16c8200
private int simulationDistance;
private boolean allowCheatsForAllPlayers;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 58f361ad664ba45c496ab0817c7ab5e1a017b807..f156620c14e28513fd0f825d5f34de5c5f831b75 100644
+index 007b85cfda82d245ae336efc26aa38ad2f6d2c28..e0050956b90ce35082017b3f1f697e455ecb9d34 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -674,6 +674,13 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0478-Add-playPickupItemAnimation-to-LivingEntity.patch b/patches/server/0466-Add-playPickupItemAnimation-to-LivingEntity.patch
index 93c4be55ff..93c4be55ff 100644
--- a/patches/server/0478-Add-playPickupItemAnimation-to-LivingEntity.patch
+++ b/patches/server/0466-Add-playPickupItemAnimation-to-LivingEntity.patch
diff --git a/patches/server/0479-Don-t-require-FACING-data.patch b/patches/server/0467-Don-t-require-FACING-data.patch
index fd2ab14906..fd2ab14906 100644
--- a/patches/server/0479-Don-t-require-FACING-data.patch
+++ b/patches/server/0467-Don-t-require-FACING-data.patch
diff --git a/patches/server/0480-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch b/patches/server/0468-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch
index 79f5aab58a..0da67efccf 100644
--- a/patches/server/0480-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch
+++ b/patches/server/0468-Fix-SpawnChangeEvent-not-firing-for-all-use-cases.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix SpawnChangeEvent not firing for all use-cases
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 8b5eac2ad96c0ebb6eae04585998cade578ff74b..2ba432f7be370ce1a5861e90de9541d76acdcfd2 100644
+index 5fdf4d9fdc31174dc141bef3fb478e8c0fe65f1c..bcf74fc7ec97a4e8e0f78eecd64dd508841e080b 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1855,6 +1855,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1890,6 +1890,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
//ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(new BlockPosition(this.worldData.a(), 0, this.worldData.c()));
this.levelData.setSpawn(pos, angle);
@@ -17,7 +17,7 @@ index 8b5eac2ad96c0ebb6eae04585998cade578ff74b..2ba432f7be370ce1a5861e90de9541d7
// if this keepSpawnInMemory is false a plugin has already removed our tickets, do not re-add
this.removeTicketsForSpawn(this.paperConfig().spawn.keepSpawnLoadedRange * 16, prevSpawn);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 04896192121039ef6a3be103d59a9d687d7f6c05..b95aed97b48bf31091ee2f44a21ad88071e97bf3 100644
+index 8a5967b5e4196d1df1aacb28ccefadb7863b75a5..55438fc0c789d4ea00653c6b521080460176e4be 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -263,11 +263,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
diff --git a/patches/server/0481-Add-moon-phase-API.patch b/patches/server/0469-Add-moon-phase-API.patch
index c400ce80c7..c400ce80c7 100644
--- a/patches/server/0481-Add-moon-phase-API.patch
+++ b/patches/server/0469-Add-moon-phase-API.patch
diff --git a/patches/server/0483-Prevent-headless-pistons-from-being-created.patch b/patches/server/0470-Prevent-headless-pistons-from-being-created.patch
index b31a2a9b3a..b31a2a9b3a 100644
--- a/patches/server/0483-Prevent-headless-pistons-from-being-created.patch
+++ b/patches/server/0470-Prevent-headless-pistons-from-being-created.patch
diff --git a/patches/server/0484-Add-BellRingEvent.patch b/patches/server/0471-Add-BellRingEvent.patch
index e6528721cf..e6528721cf 100644
--- a/patches/server/0484-Add-BellRingEvent.patch
+++ b/patches/server/0471-Add-BellRingEvent.patch
diff --git a/patches/server/0485-Add-zombie-targets-turtle-egg-config.patch b/patches/server/0472-Add-zombie-targets-turtle-egg-config.patch
index a92287451d..a92287451d 100644
--- a/patches/server/0485-Add-zombie-targets-turtle-egg-config.patch
+++ b/patches/server/0472-Add-zombie-targets-turtle-egg-config.patch
diff --git a/patches/server/0486-Buffer-joins-to-world.patch b/patches/server/0473-Buffer-joins-to-world.patch
index f21684f9df..d0f7588047 100644
--- a/patches/server/0486-Buffer-joins-to-world.patch
+++ b/patches/server/0473-Buffer-joins-to-world.patch
@@ -8,10 +8,10 @@ the world per tick, this attempts to reduce the impact that join floods
has on the server
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 7f0bcb69a1a14a01bd80ffcba1afb25ceeab19b8..4d2b26fb1ac5663b667ffd16eed379f4a3311677 100644
+index b8e35f493458ba9e072953dffe6b2429f1d821ec..c1a59f54f537395bebf6d78459beef3013ea67d9 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -396,8 +396,23 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -425,8 +425,23 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
// Paper end
diff --git a/patches/server/0487-Eigencraft-redstone-implementation.patch b/patches/server/0474-Eigencraft-redstone-implementation.patch
index e4a4e5398a..e4a4e5398a 100644
--- a/patches/server/0487-Eigencraft-redstone-implementation.patch
+++ b/patches/server/0474-Eigencraft-redstone-implementation.patch
diff --git a/patches/server/0488-Fix-hex-colors-not-working-in-some-kick-messages.patch b/patches/server/0475-Fix-hex-colors-not-working-in-some-kick-messages.patch
index 3c5a150c0e..3c5a150c0e 100644
--- a/patches/server/0488-Fix-hex-colors-not-working-in-some-kick-messages.patch
+++ b/patches/server/0475-Fix-hex-colors-not-working-in-some-kick-messages.patch
diff --git a/patches/server/0489-PortalCreateEvent-needs-to-know-its-entity.patch b/patches/server/0476-PortalCreateEvent-needs-to-know-its-entity.patch
index 3c2cb7d994..9630b4e364 100644
--- a/patches/server/0489-PortalCreateEvent-needs-to-know-its-entity.patch
+++ b/patches/server/0476-PortalCreateEvent-needs-to-know-its-entity.patch
@@ -79,7 +79,7 @@ index 5ce5902b13ebb9438433d189f2c03677e4cb54b3..e34b8cff424ad58eee65a65fa510fa99
private static int getFireTickDelay(RandomSource random) {
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-index ad38a7ced7f3dc05fb3d133e9da39f0a5eb0915b..7117dd220c393163fd5be77e332e56a34c667670 100644
+index b2ab7749e3ddf124d5ef97271a76dc875a650771..97b0e820d353e8ab4ca9d9e2efd0a8819119b2ab 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -35,6 +35,7 @@ import net.minecraft.world.item.DyeColor;
diff --git a/patches/server/0490-Fix-CraftTeam-null-check.patch b/patches/server/0477-Fix-CraftTeam-null-check.patch
index fc8ca37076..fc8ca37076 100644
--- a/patches/server/0490-Fix-CraftTeam-null-check.patch
+++ b/patches/server/0477-Fix-CraftTeam-null-check.patch
diff --git a/patches/server/0491-Add-more-Evoker-API.patch b/patches/server/0478-Add-more-Evoker-API.patch
index 367e900e25..367e900e25 100644
--- a/patches/server/0491-Add-more-Evoker-API.patch
+++ b/patches/server/0478-Add-more-Evoker-API.patch
diff --git a/patches/server/0492-Add-methods-to-get-translation-keys.patch b/patches/server/0479-Add-methods-to-get-translation-keys.patch
index 5a5b0a4cd6..5a5b0a4cd6 100644
--- a/patches/server/0492-Add-methods-to-get-translation-keys.patch
+++ b/patches/server/0479-Add-methods-to-get-translation-keys.patch
diff --git a/patches/server/0493-Create-HoverEvent-from-ItemStack-Entity.patch b/patches/server/0480-Create-HoverEvent-from-ItemStack-Entity.patch
index 238466b430..238466b430 100644
--- a/patches/server/0493-Create-HoverEvent-from-ItemStack-Entity.patch
+++ b/patches/server/0480-Create-HoverEvent-from-ItemStack-Entity.patch
diff --git a/patches/server/0494-Cache-block-data-strings.patch b/patches/server/0481-Cache-block-data-strings.patch
index 6258b9db9a..06987a2b52 100644
--- a/patches/server/0494-Cache-block-data-strings.patch
+++ b/patches/server/0481-Cache-block-data-strings.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Cache block data strings
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 48650bc1c09b18f1b57d9828dfe27f51c74c4a75..dbe70160ac703e74613ac65bd62950a5cc951d4c 100644
+index 922475997902dcc85ae42f351ace15e82b5aa638..a06c3c73744385eaa990b111af301f36737fce4b 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2032,6 +2032,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2026,6 +2026,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.getPlayerList().reloadResources();
this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
diff --git a/patches/server/0495-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch b/patches/server/0482-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch
index ec03ac9e56..898fc4db4e 100644
--- a/patches/server/0495-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch
+++ b/patches/server/0482-Fix-Entity-Teleportation-and-cancel-velocity-if-tele.patch
@@ -31,7 +31,7 @@ index da6a0171bd63ac68635de1c23fc9eafa732503bd..214771e661ca3303af167fda3b623d83
}
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 3ebe5297aa2fde1cb347f738738f5d74f6a61b9a..332bce00d355981c657993b504767814dfdd7f05 100644
+index 544d59be3722cd13e0590a5b03059da0fd5ce364..9325d4b5cbcf3785985567deeb9f13e56a4124d0 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -158,6 +158,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -42,7 +42,7 @@ index 3ebe5297aa2fde1cb347f738738f5d74f6a61b9a..332bce00d355981c657993b504767814
static boolean isLevelAtLeast(CompoundTag tag, int level) {
return tag.contains("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level;
}
-@@ -1665,6 +1666,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1716,6 +1717,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public void moveTo(double x, double y, double z, float yaw, float pitch) {
diff --git a/patches/server/0496-Add-additional-open-container-api-to-HumanEntity.patch b/patches/server/0483-Add-additional-open-container-api-to-HumanEntity.patch
index 1c28c3f9cd..1c28c3f9cd 100644
--- a/patches/server/0496-Add-additional-open-container-api-to-HumanEntity.patch
+++ b/patches/server/0483-Add-additional-open-container-api-to-HumanEntity.patch
diff --git a/patches/server/0497-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch b/patches/server/0484-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch
index 602cc3a878..602cc3a878 100644
--- a/patches/server/0497-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch
+++ b/patches/server/0484-Cache-DataFixerUpper-Rewrite-Rules-on-demand.patch
diff --git a/patches/server/0498-Extend-block-drop-capture-to-capture-all-items-added.patch b/patches/server/0485-Extend-block-drop-capture-to-capture-all-items-added.patch
index 9009d631f8..1517704ce1 100644
--- a/patches/server/0498-Extend-block-drop-capture-to-capture-all-items-added.patch
+++ b/patches/server/0485-Extend-block-drop-capture-to-capture-all-items-added.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Extend block drop capture to capture all items added to the
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 2ba432f7be370ce1a5861e90de9541d76acdcfd2..85c0a80f00f5968b684b3609fbe56197e4aeb202 100644
+index bcf74fc7ec97a4e8e0f78eecd64dd508841e080b..af06eb25172ff4ea3fdf2757ce17622e7f4075ae 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1292,6 +1292,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1327,6 +1327,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
// WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getKey(entity.getType())); // CraftBukkit
return false;
} else {
diff --git a/patches/server/0500-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch b/patches/server/0486-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch
index 7c7dcd77b6..fa83ed6181 100644
--- a/patches/server/0500-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch
+++ b/patches/server/0486-Expose-the-Entity-Counter-to-allow-plugins-to-use-va.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Expose the Entity Counter to allow plugins to use valid and
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 332bce00d355981c657993b504767814dfdd7f05..71c674d7b8dfdd401f163cc707f2c9746da30c65 100644
+index 9325d4b5cbcf3785985567deeb9f13e56a4124d0..71b75dd2127c80b782973fdd34be461357042e7d 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -4088,4 +4088,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -4162,4 +4162,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
void accept(Entity entity, double x, double y, double z);
}
diff --git a/patches/server/0501-Lazily-track-plugin-scoreboards-by-default.patch b/patches/server/0487-Lazily-track-plugin-scoreboards-by-default.patch
index bb0fe1c990..bb0fe1c990 100644
--- a/patches/server/0501-Lazily-track-plugin-scoreboards-by-default.patch
+++ b/patches/server/0487-Lazily-track-plugin-scoreboards-by-default.patch
diff --git a/patches/server/0502-Entity-isTicking.patch b/patches/server/0488-Entity-isTicking.patch
index 12f888c73e..e423ab7c13 100644
--- a/patches/server/0502-Entity-isTicking.patch
+++ b/patches/server/0488-Entity-isTicking.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Entity#isTicking
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 71c674d7b8dfdd401f163cc707f2c9746da30c65..902531525c213bd39aaac138f30ee1fc3e7c24a7 100644
+index 71b75dd2127c80b782973fdd34be461357042e7d..a3f3de0c231619702fff29062b1fb47f335783c6 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -59,6 +59,7 @@ import net.minecraft.resources.ResourceKey;
@@ -16,7 +16,7 @@ index 71c674d7b8dfdd401f163cc707f2c9746da30c65..902531525c213bd39aaac138f30ee1fc
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.TicketType;
-@@ -4093,5 +4094,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -4167,5 +4168,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
public static int nextEntityId() {
return ENTITY_COUNTER.incrementAndGet();
}
diff --git a/patches/server/0503-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch b/patches/server/0489-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch
index 2af120114e..6788d38d97 100644
--- a/patches/server/0503-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch
+++ b/patches/server/0489-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Fix deop kicking non-whitelisted player when white list is
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index dbe70160ac703e74613ac65bd62950a5cc951d4c..f02fc7d6d8d0aba84be5e85c8de0ccc0e5a01c78 100644
+index a06c3c73744385eaa990b111af301f36737fce4b..37df16b8f1dd1f75ed4c922347e648386fa54e6a 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2098,13 +2098,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2092,13 +2092,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (this.isEnforceWhitelist()) {
PlayerList playerlist = source.getServer().getPlayerList();
UserWhiteList whitelist = playerlist.getWhiteList();
diff --git a/patches/server/0504-Fix-Concurrency-issue-in-ShufflingList.patch b/patches/server/0490-Fix-Concurrency-issue-in-ShufflingList.patch
index 5b3dba224c..5b3dba224c 100644
--- a/patches/server/0504-Fix-Concurrency-issue-in-ShufflingList.patch
+++ b/patches/server/0490-Fix-Concurrency-issue-in-ShufflingList.patch
diff --git a/patches/server/0505-Reset-Ender-Crystals-on-Dragon-Spawn.patch b/patches/server/0491-Reset-Ender-Crystals-on-Dragon-Spawn.patch
index 31cde5ee93..31cde5ee93 100644
--- a/patches/server/0505-Reset-Ender-Crystals-on-Dragon-Spawn.patch
+++ b/patches/server/0491-Reset-Ender-Crystals-on-Dragon-Spawn.patch
diff --git a/patches/server/0506-Fix-for-large-move-vectors-crashing-server.patch b/patches/server/0492-Fix-for-large-move-vectors-crashing-server.patch
index 0b19e69619..ab6165f75a 100644
--- a/patches/server/0506-Fix-for-large-move-vectors-crashing-server.patch
+++ b/patches/server/0492-Fix-for-large-move-vectors-crashing-server.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Sun, 17 May 2020 23:47:33 -0700
Subject: [PATCH] Fix for large move vectors crashing server
diff --git a/patches/server/0507-Optimise-getType-calls.patch b/patches/server/0493-Optimise-getType-calls.patch
index 5d53107ff2..9d9946c038 100644
--- a/patches/server/0507-Optimise-getType-calls.patch
+++ b/patches/server/0493-Optimise-getType-calls.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Wed, 3 Jun 2020 11:37:13 -0700
Subject: [PATCH] Optimise getType calls
diff --git a/patches/server/0508-Villager-resetOffers.patch b/patches/server/0494-Villager-resetOffers.patch
index 7448270230..7448270230 100644
--- a/patches/server/0508-Villager-resetOffers.patch
+++ b/patches/server/0494-Villager-resetOffers.patch
diff --git a/patches/server/0509-Improve-inlinig-for-some-hot-IBlockData-methods.patch b/patches/server/0495-Improve-inlinig-for-some-hot-IBlockData-methods.patch
index 3639390300..43999ba919 100644
--- a/patches/server/0509-Improve-inlinig-for-some-hot-IBlockData-methods.patch
+++ b/patches/server/0495-Improve-inlinig-for-some-hot-IBlockData-methods.patch
@@ -1,17 +1,17 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 6 Jul 2020 20:46:50 -0700
Subject: [PATCH] Improve inlinig for some hot IBlockData methods
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-index 7117dd220c393163fd5be77e332e56a34c667670..178d9ad7525b6743038ed45c6f85686a860ffd26 100644
+index 97b0e820d353e8ab4ca9d9e2efd0a8819119b2ab..bfcd61e09a307e99d6f2e7edae33a0a069abcb51 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-@@ -717,8 +717,14 @@ public abstract class BlockBehaviour {
- return this.shapeExceedsCube;
+@@ -730,8 +730,14 @@ public abstract class BlockBehaviour {
+ return this.conditionallyFullOpaque;
}
- // Paper end
+ // Paper end - starlight
+ // Paper start
+ protected boolean isTicking;
+ protected FluidState fluid;
@@ -23,7 +23,7 @@ index 7117dd220c393163fd5be77e332e56a34c667670..178d9ad7525b6743038ed45c6f85686a
if (!this.getBlock().hasDynamicShape()) {
this.cache = new BlockBehaviour.BlockStateBase.Cache(this.asState());
}
-@@ -768,15 +774,15 @@ public abstract class BlockBehaviour {
+@@ -782,15 +788,15 @@ public abstract class BlockBehaviour {
return this.shapeExceedsCube; // Paper - moved into shape cache init
}
@@ -42,7 +42,7 @@ index 7117dd220c393163fd5be77e332e56a34c667670..178d9ad7525b6743038ed45c6f85686a
return this.isAir;
}
-@@ -850,7 +856,7 @@ public abstract class BlockBehaviour {
+@@ -864,7 +870,7 @@ public abstract class BlockBehaviour {
}
}
@@ -51,7 +51,7 @@ index 7117dd220c393163fd5be77e332e56a34c667670..178d9ad7525b6743038ed45c6f85686a
return this.canOcclude;
}
-@@ -1048,12 +1054,12 @@ public abstract class BlockBehaviour {
+@@ -1062,12 +1068,12 @@ public abstract class BlockBehaviour {
return this.getBlock() == block;
}
diff --git a/patches/server/0510-Retain-block-place-order-when-capturing-blockstates.patch b/patches/server/0496-Retain-block-place-order-when-capturing-blockstates.patch
index d600fe08e6..a316317a3e 100644
--- a/patches/server/0510-Retain-block-place-order-when-capturing-blockstates.patch
+++ b/patches/server/0496-Retain-block-place-order-when-capturing-blockstates.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Fri, 7 Aug 2020 04:27:56 -0700
Subject: [PATCH] Retain block place order when capturing blockstates
@@ -10,7 +10,7 @@ In general, look at making this logic more robust (i.e properly handling
cases where a captured entry is overriden) - but for now this will do.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 160a3cb1d70f765d277169bb4928238b6a575f26..525df1e5515fff204f790edcd0a051e851c03d33 100644
+index ea3b4c52769ff8112d865b226c8d3dcf48982787..1fcbf1fe2ed5d46766ae71e556f124e7822829e3 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -155,7 +155,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0511-Reduce-blockpos-allocation-from-pathfinding.patch b/patches/server/0497-Reduce-blockpos-allocation-from-pathfinding.patch
index 3f620bf4e9..a6a02f3817 100644
--- a/patches/server/0511-Reduce-blockpos-allocation-from-pathfinding.patch
+++ b/patches/server/0497-Reduce-blockpos-allocation-from-pathfinding.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Sat, 25 Apr 2020 17:10:55 -0700
Subject: [PATCH] Reduce blockpos allocation from pathfinding
diff --git a/patches/server/0512-Fix-item-locations-dropped-from-campfires.patch b/patches/server/0498-Fix-item-locations-dropped-from-campfires.patch
index df944d4a66..df944d4a66 100644
--- a/patches/server/0512-Fix-item-locations-dropped-from-campfires.patch
+++ b/patches/server/0498-Fix-item-locations-dropped-from-campfires.patch
diff --git a/patches/server/0499-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch b/patches/server/0499-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch
deleted file mode 100644
index 89aadfda9c..0000000000
--- a/patches/server/0499-Don-t-mark-dirty-in-invalid-locations-SPIGOT-6086.patch
+++ /dev/null
@@ -1,18 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Mariell Hoversholm <[email protected]>
-Date: Sun, 27 Sep 2020 16:25:24 +0200
-Subject: [PATCH] Don't mark dirty in invalid locations (SPIGOT-6086)
-
-
-diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-index a699107b1afea1c52e5a7e93af8f39ae9e1955b3..c4046b364d1896b781e23c92b241ec73c239d3a0 100644
---- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-@@ -250,6 +250,7 @@ public class ChunkHolder {
- }
-
- public void blockChanged(BlockPos pos) {
-+ if (!pos.isInsideBuildHeightAndWorldBoundsHorizontal(levelHeightAccessor)) return; // Paper - SPIGOT-6086 for all invalid locations; avoid acquiring locks
- LevelChunk chunk = this.getTickingChunk();
-
- if (chunk != null) {
diff --git a/patches/server/0513-Player-elytra-boost-API.patch b/patches/server/0499-Player-elytra-boost-API.patch
index 7eb787b278..f9baa97df3 100644
--- a/patches/server/0513-Player-elytra-boost-API.patch
+++ b/patches/server/0499-Player-elytra-boost-API.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Player elytra boost API
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index e5792a497647e68230746f16a91347ec59711f1d..2cf07d61de7fa86ba1d824ee21f127de58185bb0 100644
+index 9a9c87ff97fe1d1020b41746270b5e39ac2bdc72..edc8ab5551a13e4a0cf326a2d2bc376b586abbc0 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -598,6 +598,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -633,6 +633,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
throw new RuntimeException("Unknown settings type");
}
diff --git a/patches/server/0514-Fixed-TileEntityBell-memory-leak.patch b/patches/server/0500-Fixed-TileEntityBell-memory-leak.patch
index 09d0b77f4a..09d0b77f4a 100644
--- a/patches/server/0514-Fixed-TileEntityBell-memory-leak.patch
+++ b/patches/server/0500-Fixed-TileEntityBell-memory-leak.patch
diff --git a/patches/server/0515-Avoid-error-bubbling-up-when-item-stack-is-empty-in-.patch b/patches/server/0501-Avoid-error-bubbling-up-when-item-stack-is-empty-in-.patch
index f48528d0b5..f48528d0b5 100644
--- a/patches/server/0515-Avoid-error-bubbling-up-when-item-stack-is-empty-in-.patch
+++ b/patches/server/0501-Avoid-error-bubbling-up-when-item-stack-is-empty-in-.patch
diff --git a/patches/server/0516-Add-getOfflinePlayerIfCached-String.patch b/patches/server/0502-Add-getOfflinePlayerIfCached-String.patch
index b453ab0746..2139494956 100644
--- a/patches/server/0516-Add-getOfflinePlayerIfCached-String.patch
+++ b/patches/server/0502-Add-getOfflinePlayerIfCached-String.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add getOfflinePlayerIfCached(String)
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index f156620c14e28513fd0f825d5f34de5c5f831b75..f5783a0df00c7c88c5d5d1c3b55ba671350cd0c6 100644
+index e0050956b90ce35082017b3f1f697e455ecb9d34..9f989f49d6b26386fd2f634769d16390f84629c9 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1803,6 +1803,28 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0517-Add-ignore-discounts-API.patch b/patches/server/0503-Add-ignore-discounts-API.patch
index e77eb448c9..e77eb448c9 100644
--- a/patches/server/0517-Add-ignore-discounts-API.patch
+++ b/patches/server/0503-Add-ignore-discounts-API.patch
diff --git a/patches/server/0518-Toggle-for-removing-existing-dragon.patch b/patches/server/0504-Toggle-for-removing-existing-dragon.patch
index 157073f779..157073f779 100644
--- a/patches/server/0518-Toggle-for-removing-existing-dragon.patch
+++ b/patches/server/0504-Toggle-for-removing-existing-dragon.patch
diff --git a/patches/server/0519-Fix-client-lag-on-advancement-loading.patch b/patches/server/0505-Fix-client-lag-on-advancement-loading.patch
index 3c62dff076..3c62dff076 100644
--- a/patches/server/0519-Fix-client-lag-on-advancement-loading.patch
+++ b/patches/server/0505-Fix-client-lag-on-advancement-loading.patch
diff --git a/patches/server/0520-Item-no-age-no-player-pickup.patch b/patches/server/0506-Item-no-age-no-player-pickup.patch
index 13b456555a..13b456555a 100644
--- a/patches/server/0520-Item-no-age-no-player-pickup.patch
+++ b/patches/server/0506-Item-no-age-no-player-pickup.patch
diff --git a/patches/server/0521-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch b/patches/server/0507-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
index 91c966567f..91c966567f 100644
--- a/patches/server/0521-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
+++ b/patches/server/0507-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
diff --git a/patches/server/0522-Beacon-API-custom-effect-ranges.patch b/patches/server/0508-Beacon-API-custom-effect-ranges.patch
index 42125ab17d..42125ab17d 100644
--- a/patches/server/0522-Beacon-API-custom-effect-ranges.patch
+++ b/patches/server/0508-Beacon-API-custom-effect-ranges.patch
diff --git a/patches/server/0523-Add-API-for-quit-reason.patch b/patches/server/0509-Add-API-for-quit-reason.patch
index 67527963c4..253d09a0be 100644
--- a/patches/server/0523-Add-API-for-quit-reason.patch
+++ b/patches/server/0509-Add-API-for-quit-reason.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add API for quit reason
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 4d2b26fb1ac5663b667ffd16eed379f4a3311677..dd81751f64695180331b82225ac878913afe4513 100644
+index c1a59f54f537395bebf6d78459beef3013ea67d9..b8167dc1f37153deb237fbb77f89091d6a25094a 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -147,12 +147,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -169,12 +169,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
this.handlingFault = true;
if (this.channel.isOpen()) {
@@ -25,11 +25,11 @@ index 4d2b26fb1ac5663b667ffd16eed379f4a3311677..dd81751f64695180331b82225ac87891
Connection.LOGGER.debug("Failed to sent packet", throwable);
ConnectionProtocol enumprotocol = this.getCurrentProtocol();
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 6f2b52165c1935511790a429792d3754251537c8..9b4436bdb697d8350eac57282f8fad8140029d8f 100644
+index 02bb4ecdc6797d1c5bccc64cc235fe63bdc7ccd4..26caa90f9a7ffd1b8576b9de74476b681e13d429 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -266,6 +266,7 @@ public class ServerPlayer extends Player {
-
+ public boolean isRealPlayer; // Paper
public double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks
public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
+ public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - there are a lot of changes to do if we change all methods leading to the event
@@ -49,7 +49,7 @@ index 3d7d23a02e4aceb95ec36fbca9d02294f08c5780..8e12c4d4b54c2f0a265dc627d7981282
this.connection.disconnect(ichatbasecomponent);
}));
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index ceb5e9540e54275e97f08533d16c820061e76c8f..1d7a7a2d11f0524ae7a81fc0e3bdddd72f5ef53c 100644
+index 4d05d23f5b6967da29d5c9c91931cd79745f5dcb..8c7c0e9c972f81d458124d5b067b3c547ef87f6d 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -607,7 +607,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0524-Add-Wandering-Trader-spawn-rate-config-options.patch b/patches/server/0510-Add-Wandering-Trader-spawn-rate-config-options.patch
index a5adffed4f..a5adffed4f 100644
--- a/patches/server/0524-Add-Wandering-Trader-spawn-rate-config-options.patch
+++ b/patches/server/0510-Add-Wandering-Trader-spawn-rate-config-options.patch
diff --git a/patches/server/0525-Expose-world-spawn-angle.patch b/patches/server/0511-Expose-world-spawn-angle.patch
index 5bf390c16c..c14ae7ba25 100644
--- a/patches/server/0525-Expose-world-spawn-angle.patch
+++ b/patches/server/0511-Expose-world-spawn-angle.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Expose world spawn angle
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 1d7a7a2d11f0524ae7a81fc0e3bdddd72f5ef53c..195fcf93c46967e14a3247768a575c7a73bf9e31 100644
+index 8c7c0e9c972f81d458124d5b067b3c547ef87f6d..74d5c45ac10bffc82868a7c34c3378ab9f83916f 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -885,7 +885,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0526-Add-Destroy-Speed-API.patch b/patches/server/0512-Add-Destroy-Speed-API.patch
index a2b7640fee..a2b7640fee 100644
--- a/patches/server/0526-Add-Destroy-Speed-API.patch
+++ b/patches/server/0512-Add-Destroy-Speed-API.patch
diff --git a/patches/server/0527-Fix-Player-spawnParticle-x-y-z-precision-loss.patch b/patches/server/0513-Fix-Player-spawnParticle-x-y-z-precision-loss.patch
index 71162c5dcb..b8e82c83ac 100644
--- a/patches/server/0527-Fix-Player-spawnParticle-x-y-z-precision-loss.patch
+++ b/patches/server/0513-Fix-Player-spawnParticle-x-y-z-precision-loss.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix Player spawnParticle x/y/z precision loss
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 2cf07d61de7fa86ba1d824ee21f127de58185bb0..6731b20689ad3781d2b800826d059044c2c75c66 100644
+index edc8ab5551a13e4a0cf326a2d2bc376b586abbc0..3719f891dfb1e12d68443aa5050b408de3b65f4a 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -2307,7 +2307,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2342,7 +2342,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
if (data != null && !particle.getDataType().isInstance(data)) {
throw new IllegalArgumentException("data should be " + particle.getDataType() + " got " + data.getClass());
}
diff --git a/patches/server/0528-Add-LivingEntity-clearActiveItem.patch b/patches/server/0514-Add-LivingEntity-clearActiveItem.patch
index 2eb3c37c50..2eb3c37c50 100644
--- a/patches/server/0528-Add-LivingEntity-clearActiveItem.patch
+++ b/patches/server/0514-Add-LivingEntity-clearActiveItem.patch
diff --git a/patches/server/0529-Add-PlayerItemCooldownEvent.patch b/patches/server/0515-Add-PlayerItemCooldownEvent.patch
index 751841d117..751841d117 100644
--- a/patches/server/0529-Add-PlayerItemCooldownEvent.patch
+++ b/patches/server/0515-Add-PlayerItemCooldownEvent.patch
diff --git a/patches/server/0530-Significantly-improve-performance-of-the-end-generat.patch b/patches/server/0516-Significantly-improve-performance-of-the-end-generat.patch
index c223e5c115..c223e5c115 100644
--- a/patches/server/0530-Significantly-improve-performance-of-the-end-generat.patch
+++ b/patches/server/0516-Significantly-improve-performance-of-the-end-generat.patch
diff --git a/patches/server/0531-More-lightning-API.patch b/patches/server/0517-More-lightning-API.patch
index 142479579e..142479579e 100644
--- a/patches/server/0531-More-lightning-API.patch
+++ b/patches/server/0517-More-lightning-API.patch
diff --git a/patches/server/0532-Climbing-should-not-bypass-cramming-gamerule.patch b/patches/server/0518-Climbing-should-not-bypass-cramming-gamerule.patch
index 55702b598c..05a32ec667 100644
--- a/patches/server/0532-Climbing-should-not-bypass-cramming-gamerule.patch
+++ b/patches/server/0518-Climbing-should-not-bypass-cramming-gamerule.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Climbing should not bypass cramming gamerule
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 902531525c213bd39aaac138f30ee1fc3e7c24a7..96bb4a32a04851bd3a83f2b214efd2297ff8b57e 100644
+index a3f3de0c231619702fff29062b1fb47f335783c6..0e8a5c3fbbafa90f0975733a534aace646e73fde 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -1846,6 +1846,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1897,6 +1897,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public boolean isPushable() {
diff --git a/patches/server/0533-Added-missing-default-perms-for-commands.patch b/patches/server/0519-Added-missing-default-perms-for-commands.patch
index ddaf0c6aec..ddaf0c6aec 100644
--- a/patches/server/0533-Added-missing-default-perms-for-commands.patch
+++ b/patches/server/0519-Added-missing-default-perms-for-commands.patch
diff --git a/patches/server/0534-Add-PlayerShearBlockEvent.patch b/patches/server/0520-Add-PlayerShearBlockEvent.patch
index 4004036b95..4004036b95 100644
--- a/patches/server/0534-Add-PlayerShearBlockEvent.patch
+++ b/patches/server/0520-Add-PlayerShearBlockEvent.patch
diff --git a/patches/server/0535-Fix-curing-zombie-villager-discount-exploit.patch b/patches/server/0521-Fix-curing-zombie-villager-discount-exploit.patch
index c6cb27bebe..c6cb27bebe 100644
--- a/patches/server/0535-Fix-curing-zombie-villager-discount-exploit.patch
+++ b/patches/server/0521-Fix-curing-zombie-villager-discount-exploit.patch
diff --git a/patches/server/0536-Limit-recipe-packets.patch b/patches/server/0522-Limit-recipe-packets.patch
index f40301fce3..f40301fce3 100644
--- a/patches/server/0536-Limit-recipe-packets.patch
+++ b/patches/server/0522-Limit-recipe-packets.patch
diff --git a/patches/server/0537-Fix-CraftSound-backwards-compatibility.patch b/patches/server/0523-Fix-CraftSound-backwards-compatibility.patch
index 60e8ef1e6b..60e8ef1e6b 100644
--- a/patches/server/0537-Fix-CraftSound-backwards-compatibility.patch
+++ b/patches/server/0523-Fix-CraftSound-backwards-compatibility.patch
diff --git a/patches/server/0538-Player-Chunk-Load-Unload-Events.patch b/patches/server/0524-Player-Chunk-Load-Unload-Events.patch
index 04505672a2..a18b7e1b50 100644
--- a/patches/server/0538-Player-Chunk-Load-Unload-Events.patch
+++ b/patches/server/0524-Player-Chunk-Load-Unload-Events.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Player Chunk Load/Unload Events
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 9b4436bdb697d8350eac57282f8fad8140029d8f..a802529b6f1adfd358295811c4e329e6fe82009b 100644
+index 26caa90f9a7ffd1b8576b9de74476b681e13d429..e79d93940ca728a6a9867148510d969082ddbfd6 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -2134,11 +2134,21 @@ public class ServerPlayer extends Player {
+@@ -2119,11 +2119,21 @@ public class ServerPlayer extends Player {
public void trackChunk(ChunkPos chunkPos, Packet<?> chunkDataPacket) {
this.connection.send(chunkDataPacket);
diff --git a/patches/server/0539-Optimize-Dynamic-get-Missing-Keys.patch b/patches/server/0525-Optimize-Dynamic-get-Missing-Keys.patch
index 5a4efe035c..5a4efe035c 100644
--- a/patches/server/0539-Optimize-Dynamic-get-Missing-Keys.patch
+++ b/patches/server/0525-Optimize-Dynamic-get-Missing-Keys.patch
diff --git a/patches/server/0540-Expose-LivingEntity-hurt-direction.patch b/patches/server/0526-Expose-LivingEntity-hurt-direction.patch
index ff8defb04b..ff8defb04b 100644
--- a/patches/server/0540-Expose-LivingEntity-hurt-direction.patch
+++ b/patches/server/0526-Expose-LivingEntity-hurt-direction.patch
diff --git a/patches/server/0541-Add-OBSTRUCTED-reason-to-BedEnterResult.patch b/patches/server/0527-Add-OBSTRUCTED-reason-to-BedEnterResult.patch
index 5c4a714385..5c4a714385 100644
--- a/patches/server/0541-Add-OBSTRUCTED-reason-to-BedEnterResult.patch
+++ b/patches/server/0527-Add-OBSTRUCTED-reason-to-BedEnterResult.patch
diff --git a/patches/server/0542-Do-not-crash-from-invalid-ingredient-lists-in-Villag.patch b/patches/server/0528-Do-not-crash-from-invalid-ingredient-lists-in-Villag.patch
index a66e8fc14d..a66e8fc14d 100644
--- a/patches/server/0542-Do-not-crash-from-invalid-ingredient-lists-in-Villag.patch
+++ b/patches/server/0528-Do-not-crash-from-invalid-ingredient-lists-in-Villag.patch
diff --git a/patches/server/0543-Add-PlayerTradeEvent-and-PlayerPurchaseEvent.patch b/patches/server/0529-Add-PlayerTradeEvent-and-PlayerPurchaseEvent.patch
index 339a34391b..339a34391b 100644
--- a/patches/server/0543-Add-PlayerTradeEvent-and-PlayerPurchaseEvent.patch
+++ b/patches/server/0529-Add-PlayerTradeEvent-and-PlayerPurchaseEvent.patch
diff --git a/patches/server/0544-Implement-TargetHitEvent.patch b/patches/server/0530-Implement-TargetHitEvent.patch
index 78722933d9..78722933d9 100644
--- a/patches/server/0544-Implement-TargetHitEvent.patch
+++ b/patches/server/0530-Implement-TargetHitEvent.patch
diff --git a/patches/server/0545-MC-4-Fix-item-position-desync.patch b/patches/server/0531-MC-4-Fix-item-position-desync.patch
index 2630f549d6..92072f73f2 100644
--- a/patches/server/0545-MC-4-Fix-item-position-desync.patch
+++ b/patches/server/0531-MC-4-Fix-item-position-desync.patch
@@ -27,13 +27,13 @@ index 3768a71491ef7836b9739bdaec7a077c523dbacd..a57957ace1a72b3308487f180a366c38
public Vec3 decode(long x, long y, long z) {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 96bb4a32a04851bd3a83f2b214efd2297ff8b57e..9770cc15579d90480993135a5a54ad1ac1133df1 100644
+index 0e8a5c3fbbafa90f0975733a534aace646e73fde..45931b056abab7c38bc7c6713192be4b62a7e6e4 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -3904,6 +3904,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
- }
- public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
- // Paper end
+@@ -3971,6 +3971,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ return;
+ }
+ // Paper end - rewrite chunk system
+ // Paper start - fix MC-4
+ if (this instanceof ItemEntity) {
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixEntityPositionDesync) {
diff --git a/patches/server/0546-Additional-Block-Material-API-s.patch b/patches/server/0532-Additional-Block-Material-API-s.patch
index e93239aae5..e93239aae5 100644
--- a/patches/server/0546-Additional-Block-Material-API-s.patch
+++ b/patches/server/0532-Additional-Block-Material-API-s.patch
diff --git a/patches/server/0547-Fix-harming-potion-dupe.patch b/patches/server/0533-Fix-harming-potion-dupe.patch
index d131d6c979..d131d6c979 100644
--- a/patches/server/0547-Fix-harming-potion-dupe.patch
+++ b/patches/server/0533-Fix-harming-potion-dupe.patch
diff --git a/patches/server/0548-Implement-API-to-get-Material-from-Boats-and-Minecar.patch b/patches/server/0534-Implement-API-to-get-Material-from-Boats-and-Minecar.patch
index b68431ed82..b68431ed82 100644
--- a/patches/server/0548-Implement-API-to-get-Material-from-Boats-and-Minecar.patch
+++ b/patches/server/0534-Implement-API-to-get-Material-from-Boats-and-Minecar.patch
diff --git a/patches/server/0549-Cache-burn-durations.patch b/patches/server/0535-Cache-burn-durations.patch
index 35387cbe57..35387cbe57 100644
--- a/patches/server/0549-Cache-burn-durations.patch
+++ b/patches/server/0535-Cache-burn-durations.patch
diff --git a/patches/server/0550-Allow-disabling-mob-spawner-spawn-egg-transformation.patch b/patches/server/0536-Allow-disabling-mob-spawner-spawn-egg-transformation.patch
index b11735c5d7..b11735c5d7 100644
--- a/patches/server/0550-Allow-disabling-mob-spawner-spawn-egg-transformation.patch
+++ b/patches/server/0536-Allow-disabling-mob-spawner-spawn-egg-transformation.patch
diff --git a/patches/server/0551-Fix-Not-a-string-Map-Conversion-spam.patch b/patches/server/0537-Fix-Not-a-string-Map-Conversion-spam.patch
index 8967f3f735..8967f3f735 100644
--- a/patches/server/0551-Fix-Not-a-string-Map-Conversion-spam.patch
+++ b/patches/server/0537-Fix-Not-a-string-Map-Conversion-spam.patch
diff --git a/patches/server/0552-Implement-PlayerFlowerPotManipulateEvent.patch b/patches/server/0538-Implement-PlayerFlowerPotManipulateEvent.patch
index 8f512f54d6..8f512f54d6 100644
--- a/patches/server/0552-Implement-PlayerFlowerPotManipulateEvent.patch
+++ b/patches/server/0538-Implement-PlayerFlowerPotManipulateEvent.patch
diff --git a/patches/server/0553-Fix-interact-event-not-being-called-in-adventure.patch b/patches/server/0539-Fix-interact-event-not-being-called-in-adventure.patch
index 8cd15192dc..8cd15192dc 100644
--- a/patches/server/0553-Fix-interact-event-not-being-called-in-adventure.patch
+++ b/patches/server/0539-Fix-interact-event-not-being-called-in-adventure.patch
diff --git a/patches/server/0554-Zombie-API-breaking-doors.patch b/patches/server/0540-Zombie-API-breaking-doors.patch
index b89994b417..b89994b417 100644
--- a/patches/server/0554-Zombie-API-breaking-doors.patch
+++ b/patches/server/0540-Zombie-API-breaking-doors.patch
diff --git a/patches/server/0555-Fix-nerfed-slime-when-splitting.patch b/patches/server/0541-Fix-nerfed-slime-when-splitting.patch
index 726d362dff..726d362dff 100644
--- a/patches/server/0555-Fix-nerfed-slime-when-splitting.patch
+++ b/patches/server/0541-Fix-nerfed-slime-when-splitting.patch
diff --git a/patches/server/0556-Add-EntityLoadCrossbowEvent.patch b/patches/server/0542-Add-EntityLoadCrossbowEvent.patch
index 6ffab47e6e..6ffab47e6e 100644
--- a/patches/server/0556-Add-EntityLoadCrossbowEvent.patch
+++ b/patches/server/0542-Add-EntityLoadCrossbowEvent.patch
diff --git a/patches/server/0557-Guardian-beam-workaround.patch b/patches/server/0543-Guardian-beam-workaround.patch
index fa66573d2b..fa66573d2b 100644
--- a/patches/server/0557-Guardian-beam-workaround.patch
+++ b/patches/server/0543-Guardian-beam-workaround.patch
diff --git a/patches/server/0558-Added-WorldGameRuleChangeEvent.patch b/patches/server/0544-Added-WorldGameRuleChangeEvent.patch
index 86de4a1af6..98de18415c 100644
--- a/patches/server/0558-Added-WorldGameRuleChangeEvent.patch
+++ b/patches/server/0544-Added-WorldGameRuleChangeEvent.patch
@@ -64,10 +64,10 @@ index 800325a544bb9f228ccbeb0a52d7f380a8c6083e..3c93bfeb94168f832904a8462ae23b06
public int get() {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index b95aed97b48bf31091ee2f44a21ad88071e97bf3..42423b020f9c2ef2ba025b444be076c38314c721 100644
+index 55438fc0c789d4ea00653c6b521080460176e4be..73a475b7d268e84cc9eb608664c5d753da212bee 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -1803,8 +1803,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1793,8 +1793,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
if (!this.isGameRule(rule)) return false;
@@ -82,7 +82,7 @@ index b95aed97b48bf31091ee2f44a21ad88071e97bf3..42423b020f9c2ef2ba025b444be076c3
handle.onChanged(this.getHandle().getServer());
return true;
}
-@@ -1839,8 +1844,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1829,8 +1834,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
if (!this.isGameRule(rule.getName())) return false;
diff --git a/patches/server/0559-Added-ServerResourcesReloadedEvent.patch b/patches/server/0545-Added-ServerResourcesReloadedEvent.patch
index d030c34c58..08c7e91119 100644
--- a/patches/server/0559-Added-ServerResourcesReloadedEvent.patch
+++ b/patches/server/0545-Added-ServerResourcesReloadedEvent.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Added ServerResourcesReloadedEvent
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index f02fc7d6d8d0aba84be5e85c8de0ccc0e5a01c78..5c3b5cb0ffc3920da82b525bbc7a8963c981c595 100644
+index 37df16b8f1dd1f75ed4c922347e648386fa54e6a..a3f5585db8579350c1390d7ee141e1a8af9225ff 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1994,7 +1994,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1988,7 +1988,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.functionManager;
}
@@ -22,7 +22,7 @@ index f02fc7d6d8d0aba84be5e85c8de0ccc0e5a01c78..5c3b5cb0ffc3920da82b525bbc7a8963
RegistryAccess.Frozen iregistrycustom_dimension = this.registryAccess();
CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> {
Stream<String> stream = dataPacks.stream(); // CraftBukkit - decompile error
-@@ -2020,6 +2026,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2014,6 +2020,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.packRepository.setSelected(dataPacks);
this.worldData.setDataPackConfig(MinecraftServer.getSelectedPacks(this.packRepository));
this.resources.managers.updateRegistryTags(this.registryAccess());
diff --git a/patches/server/0560-Added-world-settings-for-mobs-picking-up-loot.patch b/patches/server/0546-Added-world-settings-for-mobs-picking-up-loot.patch
index 4c1e7b2549..4c1e7b2549 100644
--- a/patches/server/0560-Added-world-settings-for-mobs-picking-up-loot.patch
+++ b/patches/server/0546-Added-world-settings-for-mobs-picking-up-loot.patch
diff --git a/patches/server/0561-Implemented-BlockFailedDispenseEvent.patch b/patches/server/0547-Implemented-BlockFailedDispenseEvent.patch
index 9c14300d5a..9c14300d5a 100644
--- a/patches/server/0561-Implemented-BlockFailedDispenseEvent.patch
+++ b/patches/server/0547-Implemented-BlockFailedDispenseEvent.patch
diff --git a/patches/server/0562-Added-PlayerLecternPageChangeEvent.patch b/patches/server/0548-Added-PlayerLecternPageChangeEvent.patch
index d8fd03b1c7..d8fd03b1c7 100644
--- a/patches/server/0562-Added-PlayerLecternPageChangeEvent.patch
+++ b/patches/server/0548-Added-PlayerLecternPageChangeEvent.patch
diff --git a/patches/server/0563-Added-PlayerLoomPatternSelectEvent.patch b/patches/server/0549-Added-PlayerLoomPatternSelectEvent.patch
index a99d30af29..a99d30af29 100644
--- a/patches/server/0563-Added-PlayerLoomPatternSelectEvent.patch
+++ b/patches/server/0549-Added-PlayerLoomPatternSelectEvent.patch
diff --git a/patches/server/0564-Configurable-door-breaking-difficulty.patch b/patches/server/0550-Configurable-door-breaking-difficulty.patch
index 45e5e3e168..45e5e3e168 100644
--- a/patches/server/0564-Configurable-door-breaking-difficulty.patch
+++ b/patches/server/0550-Configurable-door-breaking-difficulty.patch
diff --git a/patches/server/0565-Empty-commands-shall-not-be-dispatched.patch b/patches/server/0551-Empty-commands-shall-not-be-dispatched.patch
index 1c382dc0f6..1c382dc0f6 100644
--- a/patches/server/0565-Empty-commands-shall-not-be-dispatched.patch
+++ b/patches/server/0551-Empty-commands-shall-not-be-dispatched.patch
diff --git a/patches/server/0566-Implement-API-to-expose-exact-interaction-point.patch b/patches/server/0552-Implement-API-to-expose-exact-interaction-point.patch
index 0ede6bf153..0ede6bf153 100644
--- a/patches/server/0566-Implement-API-to-expose-exact-interaction-point.patch
+++ b/patches/server/0552-Implement-API-to-expose-exact-interaction-point.patch
diff --git a/patches/server/0567-Remove-stale-POIs.patch b/patches/server/0553-Remove-stale-POIs.patch
index 8627a969b7..19074bf113 100644
--- a/patches/server/0567-Remove-stale-POIs.patch
+++ b/patches/server/0553-Remove-stale-POIs.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Remove stale POIs
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 85c0a80f00f5968b684b3609fbe56197e4aeb202..3f4e3e57999245a83263e88e221723e72a11b31e 100644
+index af06eb25172ff4ea3fdf2757ce17622e7f4075ae..13eab90d911eccdd6f0a4f4c3608ca94a942c95e 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1920,6 +1920,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1955,6 +1955,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
});
optional1.ifPresent((holder) -> {
this.getServer().execute(() -> {
diff --git a/patches/server/0568-Fix-villager-boat-exploit.patch b/patches/server/0554-Fix-villager-boat-exploit.patch
index 293797f2f9..59a1fd1acc 100644
--- a/patches/server/0568-Fix-villager-boat-exploit.patch
+++ b/patches/server/0554-Fix-villager-boat-exploit.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Fix villager boat exploit
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 195fcf93c46967e14a3247768a575c7a73bf9e31..54fff5ebefa04f64ed1abfd12ebf48762015fae3 100644
+index 74d5c45ac10bffc82868a7c34c3378ab9f83916f..2732af0971dcac3fab8043b1e1ae2a57925699a2 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -632,6 +632,14 @@ public abstract class PlayerList {
diff --git a/patches/server/0569-Add-sendOpLevel-API.patch b/patches/server/0555-Add-sendOpLevel-API.patch
index 4e5a7cff6f..6aef8b6924 100644
--- a/patches/server/0569-Add-sendOpLevel-API.patch
+++ b/patches/server/0555-Add-sendOpLevel-API.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add sendOpLevel API
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 54fff5ebefa04f64ed1abfd12ebf48762015fae3..c4f5a3bf60302743329b17f9b2dffcee9c1e5ab3 100644
+index 2732af0971dcac3fab8043b1e1ae2a57925699a2..a199dee07c67e4e66bbdccd2c5f77223cbc6fecf 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1130,6 +1130,11 @@ public abstract class PlayerList {
@@ -32,10 +32,10 @@ index 54fff5ebefa04f64ed1abfd12ebf48762015fae3..c4f5a3bf60302743329b17f9b2dffcee
public boolean isWhiteListed(GameProfile profile) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 6731b20689ad3781d2b800826d059044c2c75c66..50a6aaa4216ba39f2b3b480be7b5953acac08847 100644
+index 3719f891dfb1e12d68443aa5050b408de3b65f4a..0f523f688a607cc4ff91f2b18be896f64b8d36bd 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -612,6 +612,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -647,6 +647,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
? (org.bukkit.entity.Firework) entity.getBukkitEntity()
: null;
}
diff --git a/patches/server/0570-Add-PaperRegistry.patch b/patches/server/0556-Add-PaperRegistry.patch
index 613898a9f2..172e03c5a5 100644
--- a/patches/server/0570-Add-PaperRegistry.patch
+++ b/patches/server/0556-Add-PaperRegistry.patch
@@ -193,10 +193,10 @@ index 0000000000000000000000000000000000000000..6f39e343147803e15e7681c993b8797a
+public record RegistryKey<API extends Keyed, MINECRAFT>(Class<API> apiClass, ResourceKey<? extends Registry<MINECRAFT>> resourceKey) {
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 5c3b5cb0ffc3920da82b525bbc7a8963c981c595..d60439d49de781b12af6fbe4ff89b7270f57cbeb 100644
+index a3f5585db8579350c1390d7ee141e1a8af9225ff..be7cfe5ce05b51786790a7eaf3cdac9acf9ff566 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2026,6 +2026,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2020,6 +2020,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.packRepository.setSelected(dataPacks);
this.worldData.setDataPackConfig(MinecraftServer.getSelectedPacks(this.packRepository));
this.resources.managers.updateRegistryTags(this.registryAccess());
diff --git a/patches/server/0571-Add-StructuresLocateEvent.patch b/patches/server/0557-Add-StructuresLocateEvent.patch
index c8d1241689..42d31007ce 100644
--- a/patches/server/0571-Add-StructuresLocateEvent.patch
+++ b/patches/server/0557-Add-StructuresLocateEvent.patch
@@ -72,7 +72,7 @@ index 0000000000000000000000000000000000000000..423bf87ebda7ea266dc7b48cbfadbc85
+ }
+}
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
-index b1eecc184bc9daf0a9c7db8da06e11bfb63d9d88..28f01f29796a8a8e6e6331da5525a4306d78230e 100644
+index 6e2185bbaac889d51a54ec97907da3b1faa465c4..50b9c2fbe3a5c12a43b4711d678ed2398dbdee58 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
@@ -295,6 +295,26 @@ public abstract class ChunkGenerator {
diff --git a/patches/server/0572-Collision-option-for-requiring-a-player-participant.patch b/patches/server/0558-Collision-option-for-requiring-a-player-participant.patch
index e8dceea39c..6c436a789b 100644
--- a/patches/server/0572-Collision-option-for-requiring-a-player-participant.patch
+++ b/patches/server/0558-Collision-option-for-requiring-a-player-participant.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Collision option for requiring a player participant
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 9770cc15579d90480993135a5a54ad1ac1133df1..525e712c5715f1fa323fae94d4158c4e66068fe3 100644
+index 45931b056abab7c38bc7c6713192be4b62a7e6e4..9b1892a4bb385ab888cf0c11f9a7a84921755803 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -1729,6 +1729,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1780,6 +1780,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
public void push(Entity entity) {
if (!this.isPassengerOfSameVehicle(entity)) {
if (!entity.noPhysics && !this.noPhysics) {
diff --git a/patches/server/0573-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch b/patches/server/0559-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch
index 1af8b9fb5e..1af8b9fb5e 100644
--- a/patches/server/0573-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch
+++ b/patches/server/0559-Remove-ProjectileHitEvent-call-when-fireballs-dead.patch
diff --git a/patches/server/0574-Return-chat-component-with-empty-text-instead-of-thr.patch b/patches/server/0560-Return-chat-component-with-empty-text-instead-of-thr.patch
index b83b5915fd..b83b5915fd 100644
--- a/patches/server/0574-Return-chat-component-with-empty-text-instead-of-thr.patch
+++ b/patches/server/0560-Return-chat-component-with-empty-text-instead-of-thr.patch
diff --git a/patches/server/0575-Make-schedule-command-per-world.patch b/patches/server/0561-Make-schedule-command-per-world.patch
index 60f5d1fc66..60f5d1fc66 100644
--- a/patches/server/0575-Make-schedule-command-per-world.patch
+++ b/patches/server/0561-Make-schedule-command-per-world.patch
diff --git a/patches/server/0576-Configurable-max-leash-distance.patch b/patches/server/0562-Configurable-max-leash-distance.patch
index ba76f79b7f..ba76f79b7f 100644
--- a/patches/server/0576-Configurable-max-leash-distance.patch
+++ b/patches/server/0562-Configurable-max-leash-distance.patch
diff --git a/patches/server/0577-Implement-BlockPreDispenseEvent.patch b/patches/server/0563-Implement-BlockPreDispenseEvent.patch
index 34982b77cc..34982b77cc 100644
--- a/patches/server/0577-Implement-BlockPreDispenseEvent.patch
+++ b/patches/server/0563-Implement-BlockPreDispenseEvent.patch
diff --git a/patches/server/0578-Added-firing-of-PlayerChangeBeaconEffectEvent.patch b/patches/server/0564-Added-firing-of-PlayerChangeBeaconEffectEvent.patch
index 2f90dea41c..2f90dea41c 100644
--- a/patches/server/0578-Added-firing-of-PlayerChangeBeaconEffectEvent.patch
+++ b/patches/server/0564-Added-firing-of-PlayerChangeBeaconEffectEvent.patch
diff --git a/patches/server/0579-Add-toggle-for-always-placing-the-dragon-egg.patch b/patches/server/0565-Add-toggle-for-always-placing-the-dragon-egg.patch
index caffaecf90..caffaecf90 100644
--- a/patches/server/0579-Add-toggle-for-always-placing-the-dragon-egg.patch
+++ b/patches/server/0565-Add-toggle-for-always-placing-the-dragon-egg.patch
diff --git a/patches/server/0580-Added-PlayerStonecutterRecipeSelectEvent.patch b/patches/server/0566-Added-PlayerStonecutterRecipeSelectEvent.patch
index 1c6497d193..1c6497d193 100644
--- a/patches/server/0580-Added-PlayerStonecutterRecipeSelectEvent.patch
+++ b/patches/server/0566-Added-PlayerStonecutterRecipeSelectEvent.patch
diff --git a/patches/server/0581-Add-dropLeash-variable-to-EntityUnleashEvent.patch b/patches/server/0567-Add-dropLeash-variable-to-EntityUnleashEvent.patch
index 1ab6c748f8..1ab6c748f8 100644
--- a/patches/server/0581-Add-dropLeash-variable-to-EntityUnleashEvent.patch
+++ b/patches/server/0567-Add-dropLeash-variable-to-EntityUnleashEvent.patch
diff --git a/patches/server/0582-Reset-shield-blocking-on-dimension-change.patch b/patches/server/0568-Reset-shield-blocking-on-dimension-change.patch
index d418f0bca8..d43e9466ff 100644
--- a/patches/server/0582-Reset-shield-blocking-on-dimension-change.patch
+++ b/patches/server/0568-Reset-shield-blocking-on-dimension-change.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Reset shield blocking on dimension change
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index a802529b6f1adfd358295811c4e329e6fe82009b..d8ba103fd52c3c540fe386c9ff8264fcb3dbc136 100644
+index e79d93940ca728a6a9867148510d969082ddbfd6..325796bfb75ec4605c7bb13d69873be2a50ac899 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1181,6 +1181,11 @@ public class ServerPlayer extends Player {
+@@ -1166,6 +1166,11 @@ public class ServerPlayer extends Player {
this.level.getCraftServer().getPluginManager().callEvent(changeEvent);
// CraftBukkit end
}
diff --git a/patches/server/0583-add-DragonEggFormEvent.patch b/patches/server/0569-add-DragonEggFormEvent.patch
index 40273bbb47..40273bbb47 100644
--- a/patches/server/0583-add-DragonEggFormEvent.patch
+++ b/patches/server/0569-add-DragonEggFormEvent.patch
diff --git a/patches/server/0584-EntityMoveEvent.patch b/patches/server/0570-EntityMoveEvent.patch
index 921e7daaf0..1961bd23da 100644
--- a/patches/server/0584-EntityMoveEvent.patch
+++ b/patches/server/0570-EntityMoveEvent.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] EntityMoveEvent
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index d60439d49de781b12af6fbe4ff89b7270f57cbeb..abd1935ebc12f963b563023eb5279ad16ed1d8df 100644
+index be7cfe5ce05b51786790a7eaf3cdac9acf9ff566..2bd90398b85c34efcad4e1779a4fbbf043ca478e 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1495,6 +1495,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1489,6 +1489,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
@@ -17,7 +17,7 @@ index d60439d49de781b12af6fbe4ff89b7270f57cbeb..abd1935ebc12f963b563023eb5279ad1
this.profiler.push(() -> {
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 3f4e3e57999245a83263e88e221723e72a11b31e..a1b0256c8faceae89e1eaf5e26c8b588085fa760 100644
+index 944719180fff8ac34682330ecf456fa90a4e95f2..8ccc21373bb52a80d76c62cf875963da8d25b247 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -217,6 +217,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
diff --git a/patches/server/0585-added-option-to-disable-pathfinding-updates-on-block.patch b/patches/server/0571-added-option-to-disable-pathfinding-updates-on-block.patch
index 24045de2fb..5fc269d9a0 100644
--- a/patches/server/0585-added-option-to-disable-pathfinding-updates-on-block.patch
+++ b/patches/server/0571-added-option-to-disable-pathfinding-updates-on-block.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] added option to disable pathfinding updates on block changes
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index a1b0256c8faceae89e1eaf5e26c8b588085fa760..7f121ce977fd5779032450443c94634bc919009d 100644
+index ef4b5ff8f5163effd9526c67e3d9750e8a3f561b..60e86357327f7bd7141d421b2d6d52d433118f1f 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1494,6 +1494,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1529,6 +1529,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
this.getChunkSource().blockChanged(pos);
@@ -16,7 +16,7 @@ index a1b0256c8faceae89e1eaf5e26c8b588085fa760..7f121ce977fd5779032450443c94634b
VoxelShape voxelshape = oldState.getCollisionShape(this, pos);
VoxelShape voxelshape1 = newState.getCollisionShape(this, pos);
-@@ -1535,6 +1536,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1570,6 +1571,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
}
diff --git a/patches/server/0586-Inline-shift-direction-fields.patch b/patches/server/0572-Inline-shift-direction-fields.patch
index d4353368a2..d4353368a2 100644
--- a/patches/server/0586-Inline-shift-direction-fields.patch
+++ b/patches/server/0572-Inline-shift-direction-fields.patch
diff --git a/patches/server/0587-Allow-adding-items-to-BlockDropItemEvent.patch b/patches/server/0573-Allow-adding-items-to-BlockDropItemEvent.patch
index e71e9cff71..e71e9cff71 100644
--- a/patches/server/0587-Allow-adding-items-to-BlockDropItemEvent.patch
+++ b/patches/server/0573-Allow-adding-items-to-BlockDropItemEvent.patch
diff --git a/patches/server/0588-Add-getMainThreadExecutor-to-BukkitScheduler.patch b/patches/server/0574-Add-getMainThreadExecutor-to-BukkitScheduler.patch
index 8cfe3357dc..8cfe3357dc 100644
--- a/patches/server/0588-Add-getMainThreadExecutor-to-BukkitScheduler.patch
+++ b/patches/server/0574-Add-getMainThreadExecutor-to-BukkitScheduler.patch
diff --git a/patches/server/0589-living-entity-allow-attribute-registration.patch b/patches/server/0575-living-entity-allow-attribute-registration.patch
index 8b29222bec..8b29222bec 100644
--- a/patches/server/0589-living-entity-allow-attribute-registration.patch
+++ b/patches/server/0575-living-entity-allow-attribute-registration.patch
diff --git a/patches/server/0590-fix-dead-slime-setSize-invincibility.patch b/patches/server/0576-fix-dead-slime-setSize-invincibility.patch
index 33ce1b72f2..33ce1b72f2 100644
--- a/patches/server/0590-fix-dead-slime-setSize-invincibility.patch
+++ b/patches/server/0576-fix-dead-slime-setSize-invincibility.patch
diff --git a/patches/server/0591-Merchant-getRecipes-should-return-an-immutable-list.patch b/patches/server/0577-Merchant-getRecipes-should-return-an-immutable-list.patch
index cbb0cdc052..cbb0cdc052 100644
--- a/patches/server/0591-Merchant-getRecipes-should-return-an-immutable-list.patch
+++ b/patches/server/0577-Merchant-getRecipes-should-return-an-immutable-list.patch
diff --git a/patches/server/0592-Add-support-for-hex-color-codes-in-console.patch b/patches/server/0578-Add-support-for-hex-color-codes-in-console.patch
index 64b7398ff4..e17cdae98b 100644
--- a/patches/server/0592-Add-support-for-hex-color-codes-in-console.patch
+++ b/patches/server/0578-Add-support-for-hex-color-codes-in-console.patch
@@ -282,10 +282,10 @@ index 0000000000000000000000000000000000000000..b4d0b7ecd56ab952319946854168c129
+
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index abd1935ebc12f963b563023eb5279ad16ed1d8df..df570a64f1a8378f24977f0aa1e1f9b7191f0955 100644
+index 2bd90398b85c34efcad4e1779a4fbbf043ca478e..66870e0bf19124da595dff87bee99926e98d6043 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1688,7 +1688,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1682,7 +1682,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public void sendSystemMessage(Component message) {
diff --git a/patches/server/0593-Expose-Tracked-Players.patch b/patches/server/0579-Expose-Tracked-Players.patch
index e3f023c819..e3f023c819 100644
--- a/patches/server/0593-Expose-Tracked-Players.patch
+++ b/patches/server/0579-Expose-Tracked-Players.patch
diff --git a/patches/server/0594-Remove-streams-from-SensorNearest.patch b/patches/server/0580-Remove-streams-from-SensorNearest.patch
index a7ae5d1a98..a7ae5d1a98 100644
--- a/patches/server/0594-Remove-streams-from-SensorNearest.patch
+++ b/patches/server/0580-Remove-streams-from-SensorNearest.patch
diff --git a/patches/server/0595-Throw-proper-exception-on-empty-JsonList-file.patch b/patches/server/0581-Throw-proper-exception-on-empty-JsonList-file.patch
index e013e32526..e013e32526 100644
--- a/patches/server/0595-Throw-proper-exception-on-empty-JsonList-file.patch
+++ b/patches/server/0581-Throw-proper-exception-on-empty-JsonList-file.patch
diff --git a/patches/server/0596-Improve-ServerGUI.patch b/patches/server/0582-Improve-ServerGUI.patch
index e573bd2f58..e573bd2f58 100644
--- a/patches/server/0596-Improve-ServerGUI.patch
+++ b/patches/server/0582-Improve-ServerGUI.patch
diff --git a/patches/server/0597-stop-firing-pressure-plate-EntityInteractEvent-for-i.patch b/patches/server/0583-stop-firing-pressure-plate-EntityInteractEvent-for-i.patch
index 1099f41d55..1099f41d55 100644
--- a/patches/server/0597-stop-firing-pressure-plate-EntityInteractEvent-for-i.patch
+++ b/patches/server/0583-stop-firing-pressure-plate-EntityInteractEvent-for-i.patch
diff --git a/patches/server/0598-fix-converting-txt-to-json-file.patch b/patches/server/0584-fix-converting-txt-to-json-file.patch
index a8f2f43419..f43088ff63 100644
--- a/patches/server/0598-fix-converting-txt-to-json-file.patch
+++ b/patches/server/0584-fix-converting-txt-to-json-file.patch
@@ -21,7 +21,7 @@ index d83bed436d2ad51cef83ecbf0c7df227a67ff404..dc96b30c70cd79d7b2a0322f32b9399a
this.saveUserBanList();
this.loadIpBanList();
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index da83f111199a6b4c712a9bb8ab6f1d1b5c6ae77c..ef02ceba53943d34bd45070297c72beedd6e1883 100644
+index 8984a30f0fa790e7854534076912e619f9487160..f4378558557c36cdbbaf43481b679f86583bdf16 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -203,6 +203,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -48,7 +48,7 @@ index da83f111199a6b4c712a9bb8ab6f1d1b5c6ae77c..ef02ceba53943d34bd45070297c72bee
if (!OldUsersConverter.serverReadyAfterUserconversion(this)) {
return false;
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index c4f5a3bf60302743329b17f9b2dffcee9c1e5ab3..a0b752e09f375fb72ff5ad7952b5ef6ebdb6c14b 100644
+index a199dee07c67e4e66bbdccd2c5f77223cbc6fecf..78d0ece91358766aa03b833234b8cb9466371565 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -177,6 +177,7 @@ public abstract class PlayerList {
@@ -58,4 +58,4 @@ index c4f5a3bf60302743329b17f9b2dffcee9c1e5ab3..a0b752e09f375fb72ff5ad7952b5ef6e
+ abstract public void loadAndSaveFiles(); // Paper - moved from DedicatedPlayerList constructor
public void placeNewPlayer(Connection connection, ServerPlayer player) {
- player.isRealPlayer = true; // Paper - Chunk priority
+ player.isRealPlayer = true; // Paper
diff --git a/patches/server/0599-Add-worldborder-events.patch b/patches/server/0585-Add-worldborder-events.patch
index af0cf2d370..af0cf2d370 100644
--- a/patches/server/0599-Add-worldborder-events.patch
+++ b/patches/server/0585-Add-worldborder-events.patch
diff --git a/patches/server/0600-added-PlayerNameEntityEvent.patch b/patches/server/0586-added-PlayerNameEntityEvent.patch
index c1df33b717..c1df33b717 100644
--- a/patches/server/0600-added-PlayerNameEntityEvent.patch
+++ b/patches/server/0586-added-PlayerNameEntityEvent.patch
diff --git a/patches/server/0601-Prevent-grindstones-from-overstacking-items.patch b/patches/server/0587-Prevent-grindstones-from-overstacking-items.patch
index 3e79faf014..3e79faf014 100644
--- a/patches/server/0601-Prevent-grindstones-from-overstacking-items.patch
+++ b/patches/server/0587-Prevent-grindstones-from-overstacking-items.patch
diff --git a/patches/server/0602-Add-recipe-to-cook-events.patch b/patches/server/0588-Add-recipe-to-cook-events.patch
index 61023ab91c..61023ab91c 100644
--- a/patches/server/0602-Add-recipe-to-cook-events.patch
+++ b/patches/server/0588-Add-recipe-to-cook-events.patch
diff --git a/patches/server/0603-Add-Block-isValidTool.patch b/patches/server/0589-Add-Block-isValidTool.patch
index 95bf6c3432..95bf6c3432 100644
--- a/patches/server/0603-Add-Block-isValidTool.patch
+++ b/patches/server/0589-Add-Block-isValidTool.patch
diff --git a/patches/server/0604-Allow-using-signs-inside-spawn-protection.patch b/patches/server/0590-Allow-using-signs-inside-spawn-protection.patch
index e2dfe62f54..e2dfe62f54 100644
--- a/patches/server/0604-Allow-using-signs-inside-spawn-protection.patch
+++ b/patches/server/0590-Allow-using-signs-inside-spawn-protection.patch
diff --git a/patches/server/0605-Expand-world-key-API.patch b/patches/server/0591-Expand-world-key-API.patch
index 9d99a03dc8..8ba278c90f 100644
--- a/patches/server/0605-Expand-world-key-API.patch
+++ b/patches/server/0591-Expand-world-key-API.patch
@@ -20,7 +20,7 @@ index ee5e59c37301d9a806e2f696d52d9d217b232833..bb5d22125b6cd4e60d2b7e2e00af158c
// Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index f5783a0df00c7c88c5d5d1c3b55ba671350cd0c6..cebc3c444681d6f422a8befff74476e7e0d8d22e 100644
+index 9f989f49d6b26386fd2f634769d16390f84629c9..11f9add86f43923ceab9eebc5371c2bfc3990f98 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1143,9 +1143,15 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0606-Add-fast-alternative-constructor-for-Rotations.patch b/patches/server/0592-Add-fast-alternative-constructor-for-Rotations.patch
index bbdcc665bb..bbdcc665bb 100644
--- a/patches/server/0606-Add-fast-alternative-constructor-for-Rotations.patch
+++ b/patches/server/0592-Add-fast-alternative-constructor-for-Rotations.patch
diff --git a/patches/server/0607-Item-Rarity-API.patch b/patches/server/0593-Item-Rarity-API.patch
index e3e4b42355..e3e4b42355 100644
--- a/patches/server/0607-Item-Rarity-API.patch
+++ b/patches/server/0593-Item-Rarity-API.patch
diff --git a/patches/server/0608-Only-set-despawnTimer-for-Wandering-Traders-spawned-.patch b/patches/server/0594-Only-set-despawnTimer-for-Wandering-Traders-spawned-.patch
index a8810d8bca..a8810d8bca 100644
--- a/patches/server/0608-Only-set-despawnTimer-for-Wandering-Traders-spawned-.patch
+++ b/patches/server/0594-Only-set-despawnTimer-for-Wandering-Traders-spawned-.patch
diff --git a/patches/server/0609-copy-TESign-isEditable-from-snapshots.patch b/patches/server/0595-copy-TESign-isEditable-from-snapshots.patch
index 766607080f..766607080f 100644
--- a/patches/server/0609-copy-TESign-isEditable-from-snapshots.patch
+++ b/patches/server/0595-copy-TESign-isEditable-from-snapshots.patch
diff --git a/patches/server/0610-Drop-carried-item-when-player-has-disconnected.patch b/patches/server/0596-Drop-carried-item-when-player-has-disconnected.patch
index 8d562472bc..04925bb087 100644
--- a/patches/server/0610-Drop-carried-item-when-player-has-disconnected.patch
+++ b/patches/server/0596-Drop-carried-item-when-player-has-disconnected.patch
@@ -7,7 +7,7 @@ Fixes disappearance of held items, when a player gets disconnected and PlayerDro
Closes #5036
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index a0b752e09f375fb72ff5ad7952b5ef6ebdb6c14b..8eae2de23226700b7186b5e673f7f776854a49bc 100644
+index 78d0ece91358766aa03b833234b8cb9466371565..e535f8c2f81e62ca29f3ae944b9c35ee8ed45be0 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -625,6 +625,14 @@ public abstract class PlayerList {
diff --git a/patches/server/0611-forced-whitelist-use-configurable-kick-message.patch b/patches/server/0597-forced-whitelist-use-configurable-kick-message.patch
index 3066195a02..cf1f9f3caf 100644
--- a/patches/server/0611-forced-whitelist-use-configurable-kick-message.patch
+++ b/patches/server/0597-forced-whitelist-use-configurable-kick-message.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] forced whitelist: use configurable kick message
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index df570a64f1a8378f24977f0aa1e1f9b7191f0955..3c5b7f4b2db421d56e5832e283bd60702b2d67de 100644
+index 66870e0bf19124da595dff87bee99926e98d6043..ac5119577debc7262fc5d1bdb746a2e31a0d901b 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2115,7 +2115,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2109,7 +2109,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
if (!whitelist.isWhiteListed(entityplayer.getGameProfile()) && !this.getPlayerList().isOp(entityplayer.getGameProfile())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420)
diff --git a/patches/server/0612-Don-t-ignore-result-of-PlayerEditBookEvent.patch b/patches/server/0598-Don-t-ignore-result-of-PlayerEditBookEvent.patch
index 661e62955f..661e62955f 100644
--- a/patches/server/0612-Don-t-ignore-result-of-PlayerEditBookEvent.patch
+++ b/patches/server/0598-Don-t-ignore-result-of-PlayerEditBookEvent.patch
diff --git a/patches/server/0613-Entity-load-save-limit-per-chunk.patch b/patches/server/0599-Entity-load-save-limit-per-chunk.patch
index 64d4f515d8..3aaa2613fc 100644
--- a/patches/server/0613-Entity-load-save-limit-per-chunk.patch
+++ b/patches/server/0599-Entity-load-save-limit-per-chunk.patch
@@ -34,25 +34,25 @@ index e7bfd61be4974dbe6abc2ebb870664ff1fce19cb..ac0f0a4da4282c13f6e1f37710cb615d
return entity;
});
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-index dae66dd5dbebc7fd8fc331b1f5f06ec461667830..de7afc737b1ab099edc29a4ef94baa76329c2947 100644
+index 0b92db95416b878f41b83b5c74d1c0a1031ff6af..1782d6957fa0290368e443e2e8d7b3c77ac6b8ef 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/EntityStorage.java
-@@ -90,7 +90,18 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
-
- } else {
- ListTag listTag = new ListTag();
-+ final java.util.Map<net.minecraft.world.entity.EntityType<?>, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper
- dataList.getEntities().forEach((entity) -> {
-+ // Paper start
-+ final EntityType<?> entityType = entity.getType();
-+ final int saveLimit = this.level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
-+ if (saveLimit > -1) {
-+ if (savedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) {
-+ return;
-+ }
-+ savedEntityCounts.merge(entityType, 1, Integer::sum);
+@@ -110,7 +110,18 @@ public class EntityStorage implements EntityPersistentStorage<Entity> {
+ return null;
+ }
+ ListTag listTag = new ListTag();
++ final java.util.Map<net.minecraft.world.entity.EntityType<?>, Integer> savedEntityCounts = new java.util.HashMap<>(); // Paper
+ entities.forEach((entity) -> { // diff here: use entities parameter
++ // Paper start
++ final EntityType<?> entityType = entity.getType();
++ final int saveLimit = level.paperConfig().chunks.entityPerChunkSaveLimit.getOrDefault(entityType, -1);
++ if (saveLimit > -1) {
++ if (savedEntityCounts.getOrDefault(entityType, 0) >= saveLimit) {
++ return;
+ }
-+ // Paper end
- CompoundTag compoundTag = new CompoundTag();
- if (entity.save(compoundTag)) {
- listTag.add(compoundTag);
++ savedEntityCounts.merge(entityType, 1, Integer::sum);
++ }
++ // Paper end
+ CompoundTag compoundTag = new CompoundTag();
+ if (entity.save(compoundTag)) {
+ listTag.add(compoundTag);
diff --git a/patches/server/0614-Expose-protocol-version.patch b/patches/server/0600-Expose-protocol-version.patch
index d01a192416..d01a192416 100644
--- a/patches/server/0614-Expose-protocol-version.patch
+++ b/patches/server/0600-Expose-protocol-version.patch
diff --git a/patches/server/0615-Enhance-console-tab-completions-for-brigadier-comman.patch b/patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch
index 604c72c03e..f20da4badc 100644
--- a/patches/server/0615-Enhance-console-tab-completions-for-brigadier-comman.patch
+++ b/patches/server/0601-Enhance-console-tab-completions-for-brigadier-comman.patch
@@ -208,7 +208,7 @@ index 0000000000000000000000000000000000000000..dd9d77d7c7f1a5a130a1f4c15e5b1e68
+ public void setErrorIndex(final int errorIndex) {}
+}
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index ef02ceba53943d34bd45070297c72beedd6e1883..f812b0a2d5534a7c443361bd69cfc2fe2110ba9a 100644
+index f4378558557c36cdbbaf43481b679f86583bdf16..2b79f5cd92a442f47324e13b5bf1b803e730197c 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -178,7 +178,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
diff --git a/patches/server/0616-Fix-PlayerItemConsumeEvent-cancelling-properly.patch b/patches/server/0602-Fix-PlayerItemConsumeEvent-cancelling-properly.patch
index 419a5f5e50..419a5f5e50 100644
--- a/patches/server/0616-Fix-PlayerItemConsumeEvent-cancelling-properly.patch
+++ b/patches/server/0602-Fix-PlayerItemConsumeEvent-cancelling-properly.patch
diff --git a/patches/server/0617-Add-bypass-host-check.patch b/patches/server/0603-Add-bypass-host-check.patch
index 85854dc71d..85854dc71d 100644
--- a/patches/server/0617-Add-bypass-host-check.patch
+++ b/patches/server/0603-Add-bypass-host-check.patch
diff --git a/patches/server/0618-Set-area-affect-cloud-rotation.patch b/patches/server/0604-Set-area-affect-cloud-rotation.patch
index 08e992b45c..08e992b45c 100644
--- a/patches/server/0618-Set-area-affect-cloud-rotation.patch
+++ b/patches/server/0604-Set-area-affect-cloud-rotation.patch
diff --git a/patches/server/0619-add-isDeeplySleeping-to-HumanEntity.patch b/patches/server/0605-add-isDeeplySleeping-to-HumanEntity.patch
index 23d24c3698..23d24c3698 100644
--- a/patches/server/0619-add-isDeeplySleeping-to-HumanEntity.patch
+++ b/patches/server/0605-add-isDeeplySleeping-to-HumanEntity.patch
diff --git a/patches/server/0620-add-consumeFuel-to-FurnaceBurnEvent.patch b/patches/server/0606-add-consumeFuel-to-FurnaceBurnEvent.patch
index 0c37516dd7..0c37516dd7 100644
--- a/patches/server/0620-add-consumeFuel-to-FurnaceBurnEvent.patch
+++ b/patches/server/0606-add-consumeFuel-to-FurnaceBurnEvent.patch
diff --git a/patches/server/0621-add-get-set-drop-chance-to-EntityEquipment.patch b/patches/server/0607-add-get-set-drop-chance-to-EntityEquipment.patch
index 9720fcdaf4..9720fcdaf4 100644
--- a/patches/server/0621-add-get-set-drop-chance-to-EntityEquipment.patch
+++ b/patches/server/0607-add-get-set-drop-chance-to-EntityEquipment.patch
diff --git a/patches/server/0622-fix-PigZombieAngerEvent-cancellation.patch b/patches/server/0608-fix-PigZombieAngerEvent-cancellation.patch
index df4e7b0b17..df4e7b0b17 100644
--- a/patches/server/0622-fix-PigZombieAngerEvent-cancellation.patch
+++ b/patches/server/0608-fix-PigZombieAngerEvent-cancellation.patch
diff --git a/patches/server/0623-Fix-checkReach-check-for-Shulker-boxes.patch b/patches/server/0609-Fix-checkReach-check-for-Shulker-boxes.patch
index e63651a68f..e63651a68f 100644
--- a/patches/server/0623-Fix-checkReach-check-for-Shulker-boxes.patch
+++ b/patches/server/0609-Fix-checkReach-check-for-Shulker-boxes.patch
diff --git a/patches/server/0624-fix-PlayerItemHeldEvent-firing-twice.patch b/patches/server/0610-fix-PlayerItemHeldEvent-firing-twice.patch
index 3840306aa1..3840306aa1 100644
--- a/patches/server/0624-fix-PlayerItemHeldEvent-firing-twice.patch
+++ b/patches/server/0610-fix-PlayerItemHeldEvent-firing-twice.patch
diff --git a/patches/server/0625-Added-PlayerDeepSleepEvent.patch b/patches/server/0611-Added-PlayerDeepSleepEvent.patch
index b7989ecad1..b7989ecad1 100644
--- a/patches/server/0625-Added-PlayerDeepSleepEvent.patch
+++ b/patches/server/0611-Added-PlayerDeepSleepEvent.patch
diff --git a/patches/server/0626-More-World-API.patch b/patches/server/0612-More-World-API.patch
index fb9c49f999..23edd9b83c 100644
--- a/patches/server/0626-More-World-API.patch
+++ b/patches/server/0612-More-World-API.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] More World API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 42423b020f9c2ef2ba025b444be076c38314c721..9ca188959484041f53a078963cb79d68fd2a4f48 100644
+index 73a475b7d268e84cc9eb608664c5d753da212bee..3e4b077f2c890b28c88c62b0491f9f2a355446c9 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -2045,6 +2045,65 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -2035,6 +2035,65 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return new CraftStructureSearchResult(CraftStructure.minecraftToBukkit(found.getSecond().value(), this.getHandle().registryAccess()), new Location(this, found.getFirst().getX(), found.getFirst().getY(), found.getFirst().getZ()));
}
diff --git a/patches/server/0627-Added-PlayerBedFailEnterEvent.patch b/patches/server/0613-Added-PlayerBedFailEnterEvent.patch
index ad6eaeb223..ad6eaeb223 100644
--- a/patches/server/0627-Added-PlayerBedFailEnterEvent.patch
+++ b/patches/server/0613-Added-PlayerBedFailEnterEvent.patch
diff --git a/patches/server/0628-Implement-methods-to-convert-between-Component-and-B.patch b/patches/server/0614-Implement-methods-to-convert-between-Component-and-B.patch
index 3e1d661a26..361b5a010c 100644
--- a/patches/server/0628-Implement-methods-to-convert-between-Component-and-B.patch
+++ b/patches/server/0614-Implement-methods-to-convert-between-Component-and-B.patch
@@ -42,7 +42,7 @@ index 0000000000000000000000000000000000000000..dd6012b6a097575b2d1471be5069ecce
+ }
+}
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index f812b0a2d5534a7c443361bd69cfc2fe2110ba9a..2c215e2080f00d6c875fbde92fd2c1c051d0cf98 100644
+index 2b79f5cd92a442f47324e13b5bf1b803e730197c..3e600ee2d5370c2ad9c445af15f350dee21248c1 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -214,6 +214,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
diff --git a/patches/server/0629-Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch b/patches/server/0615-Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch
index e51b27c728..cace3debb8 100644
--- a/patches/server/0629-Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch
+++ b/patches/server/0615-Fix-anchor-respawn-acting-as-a-bed-respawn-from-the-.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Fix anchor respawn acting as a bed respawn from the end
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 8eae2de23226700b7186b5e673f7f776854a49bc..6bdcfe46fd447cfdf2e57bb60250984f02c195da 100644
+index e535f8c2f81e62ca29f3ae944b9c35ee8ed45be0..c5aeaf5f9753933874d2d52a13cf24ff2eb2a98b 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -858,6 +858,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0630-Introduce-beacon-activation-deactivation-events.patch b/patches/server/0616-Introduce-beacon-activation-deactivation-events.patch
index b3f265d709..b3f265d709 100644
--- a/patches/server/0630-Introduce-beacon-activation-deactivation-events.patch
+++ b/patches/server/0616-Introduce-beacon-activation-deactivation-events.patch
diff --git a/patches/server/0631-add-RespawnFlags-to-PlayerRespawnEvent.patch b/patches/server/0617-add-RespawnFlags-to-PlayerRespawnEvent.patch
index d03dd9a291..b58233b00a 100644
--- a/patches/server/0631-add-RespawnFlags-to-PlayerRespawnEvent.patch
+++ b/patches/server/0617-add-RespawnFlags-to-PlayerRespawnEvent.patch
@@ -18,7 +18,7 @@ index 917aa2bd63db9a63c75267564d0c3602b0f01392..161b5d6f0d420ac7b6ed112d1b03d42c
} else {
if (this.player.getHealth() > 0.0F) {
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 6bdcfe46fd447cfdf2e57bb60250984f02c195da..977a23684b061d7390f70f8754a1d879d7d7075a 100644
+index c5aeaf5f9753933874d2d52a13cf24ff2eb2a98b..4c2c495d936cb84d85fb3e9f91e101a0fd796026 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -817,6 +817,12 @@ public abstract class PlayerList {
diff --git a/patches/server/0632-Add-Channel-initialization-listeners.patch b/patches/server/0618-Add-Channel-initialization-listeners.patch
index fbea93e057..bbb1c7c21d 100644
--- a/patches/server/0632-Add-Channel-initialization-listeners.patch
+++ b/patches/server/0618-Add-Channel-initialization-listeners.patch
@@ -122,10 +122,10 @@ index 0000000000000000000000000000000000000000..0d7e7db9e37ef0183c32b217bd944fb4
+ COMPRESSION_DISABLED
+}
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index dd81751f64695180331b82225ac878913afe4513..86cc291b5b14523d57c84f8ebd6ba9b9c3b0d1a6 100644
+index b8167dc1f37153deb237fbb77f89091d6a25094a..13213bbc9ca5ca20c3eb8a6744dc3204c8c1e423 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -567,6 +567,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -596,6 +596,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
} else {
this.channel.pipeline().addBefore("encoder", "compress", new CompressionEncoder(compressionThreshold));
}
@@ -133,7 +133,7 @@ index dd81751f64695180331b82225ac878913afe4513..86cc291b5b14523d57c84f8ebd6ba9b9
} else {
if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
this.channel.pipeline().remove("decompress");
-@@ -575,6 +576,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -604,6 +605,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
this.channel.pipeline().remove("compress");
}
diff --git a/patches/server/0633-Send-empty-commands-if-tab-completion-is-disabled.patch b/patches/server/0619-Send-empty-commands-if-tab-completion-is-disabled.patch
index e31efa98f9..e31efa98f9 100644
--- a/patches/server/0633-Send-empty-commands-if-tab-completion-is-disabled.patch
+++ b/patches/server/0619-Send-empty-commands-if-tab-completion-is-disabled.patch
diff --git a/patches/server/0634-Add-more-WanderingTrader-API.patch b/patches/server/0620-Add-more-WanderingTrader-API.patch
index 65d351d6df..65d351d6df 100644
--- a/patches/server/0634-Add-more-WanderingTrader-API.patch
+++ b/patches/server/0620-Add-more-WanderingTrader-API.patch
diff --git a/patches/server/0635-Add-EntityBlockStorage-clearEntities.patch b/patches/server/0621-Add-EntityBlockStorage-clearEntities.patch
index 2bd0ddfa38..2bd0ddfa38 100644
--- a/patches/server/0635-Add-EntityBlockStorage-clearEntities.patch
+++ b/patches/server/0621-Add-EntityBlockStorage-clearEntities.patch
diff --git a/patches/server/0636-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch b/patches/server/0622-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch
index 4169ad0754..4169ad0754 100644
--- a/patches/server/0636-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch
+++ b/patches/server/0622-Add-Adventure-message-to-PlayerAdvancementDoneEvent.patch
diff --git a/patches/server/0637-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch b/patches/server/0623-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch
index e0d7dfa858..e0d7dfa858 100644
--- a/patches/server/0637-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch
+++ b/patches/server/0623-Add-raw-address-to-AsyncPlayerPreLoginEvent.patch
diff --git a/patches/server/0638-Inventory-close.patch b/patches/server/0624-Inventory-close.patch
index e7f478324a..e7f478324a 100644
--- a/patches/server/0638-Inventory-close.patch
+++ b/patches/server/0624-Inventory-close.patch
diff --git a/patches/server/0639-call-PortalCreateEvent-players-and-end-platform.patch b/patches/server/0625-call-PortalCreateEvent-players-and-end-platform.patch
index 11bc717f06..69d7b501e2 100644
--- a/patches/server/0639-call-PortalCreateEvent-players-and-end-platform.patch
+++ b/patches/server/0625-call-PortalCreateEvent-players-and-end-platform.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] call PortalCreateEvent players and end platform
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index d8ba103fd52c3c540fe386c9ff8264fcb3dbc136..66a3148985f864c2e4238cd3b27469d59ab3f354 100644
+index 325796bfb75ec4605c7bb13d69873be2a50ac899..ee6581758784c5aeab40fb35775b18031fb191ea 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1208,15 +1208,21 @@ public class ServerPlayer extends Player {
+@@ -1193,15 +1193,21 @@ public class ServerPlayer extends Player {
private void createEndPlatform(ServerLevel world, BlockPos centerPos) {
BlockPos.MutableBlockPos blockposition_mutableblockposition = centerPos.mutable();
diff --git a/patches/server/0640-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch b/patches/server/0626-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
index 15c8339c99..15c8339c99 100644
--- a/patches/server/0640-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
+++ b/patches/server/0626-Add-a-should-burn-in-sunlight-API-for-Phantoms-and-S.patch
diff --git a/patches/server/0641-Fix-CraftPotionBrewer-cache.patch b/patches/server/0627-Fix-CraftPotionBrewer-cache.patch
index 2d8295382f..2d8295382f 100644
--- a/patches/server/0641-Fix-CraftPotionBrewer-cache.patch
+++ b/patches/server/0627-Fix-CraftPotionBrewer-cache.patch
diff --git a/patches/server/0642-Add-basic-Datapack-API.patch b/patches/server/0628-Add-basic-Datapack-API.patch
index 1c6af9a618..5bcb6e081e 100644
--- a/patches/server/0642-Add-basic-Datapack-API.patch
+++ b/patches/server/0628-Add-basic-Datapack-API.patch
@@ -92,7 +92,7 @@ index 0000000000000000000000000000000000000000..cf4374493c11057451a62a655514415c
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index cebc3c444681d6f422a8befff74476e7e0d8d22e..9447b7cf08401608b2b79e8c68f58fd977cf0d42 100644
+index 11f9add86f43923ceab9eebc5371c2bfc3990f98..ab8ab0180508948c7b21bafcbf914483673047d3 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -282,6 +282,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0643-Add-environment-variable-to-disable-server-gui.patch b/patches/server/0629-Add-environment-variable-to-disable-server-gui.patch
index 49bb358278..49bb358278 100644
--- a/patches/server/0643-Add-environment-variable-to-disable-server-gui.patch
+++ b/patches/server/0629-Add-environment-variable-to-disable-server-gui.patch
diff --git a/patches/server/0644-additions-to-PlayerGameModeChangeEvent.patch b/patches/server/0630-additions-to-PlayerGameModeChangeEvent.patch
index df1c2a81f0..f5024d9728 100644
--- a/patches/server/0644-additions-to-PlayerGameModeChangeEvent.patch
+++ b/patches/server/0630-additions-to-PlayerGameModeChangeEvent.patch
@@ -45,10 +45,10 @@ index 65089c0e78c9913a92ae9c66d664f48e2112ad92..7882ee2b7813d437d3b7580f046f38e7
}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 66a3148985f864c2e4238cd3b27469d59ab3f354..4a35720430990b358ea5d7f2b6293e27e8d9f7ac 100644
+index ee6581758784c5aeab40fb35775b18031fb191ea..73864c47e7f13a48243478bc24d4887aa70791b3 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1810,8 +1810,15 @@ public class ServerPlayer extends Player {
+@@ -1795,8 +1795,15 @@ public class ServerPlayer extends Player {
}
public boolean setGameMode(GameType gameMode) {
@@ -66,7 +66,7 @@ index 66a3148985f864c2e4238cd3b27469d59ab3f354..4a35720430990b358ea5d7f2b6293e27
} else {
this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, (float) gameMode.getId()));
if (gameMode == GameType.SPECTATOR) {
-@@ -1823,7 +1830,7 @@ public class ServerPlayer extends Player {
+@@ -1808,7 +1815,7 @@ public class ServerPlayer extends Player {
this.onUpdateAbilities();
this.updateEffectVisibility();
@@ -75,7 +75,7 @@ index 66a3148985f864c2e4238cd3b27469d59ab3f354..4a35720430990b358ea5d7f2b6293e27
}
}
-@@ -2225,6 +2232,16 @@ public class ServerPlayer extends Player {
+@@ -2210,6 +2217,16 @@ public class ServerPlayer extends Player {
}
public void loadGameTypes(@Nullable CompoundTag nbt) {
@@ -139,10 +139,10 @@ index 161b5d6f0d420ac7b6ed112d1b03d42c3aaec421..de4c3849cc60151de8f3a873adad2bc3
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 50a6aaa4216ba39f2b3b480be7b5953acac08847..05903c7da2ff1a5960c66ef23a685c3da8de04ae 100644
+index 0f523f688a607cc4ff91f2b18be896f64b8d36bd..64c6a35ab369a19924dba94018c361dc2d24be82 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -1430,7 +1430,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1465,7 +1465,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
throw new IllegalArgumentException("Mode cannot be null");
}
diff --git a/patches/server/0645-ItemStack-repair-check-API.patch b/patches/server/0631-ItemStack-repair-check-API.patch
index 136bbcb302..136bbcb302 100644
--- a/patches/server/0645-ItemStack-repair-check-API.patch
+++ b/patches/server/0631-ItemStack-repair-check-API.patch
diff --git a/patches/server/0646-More-Enchantment-API.patch b/patches/server/0632-More-Enchantment-API.patch
index 538c746f7e..538c746f7e 100644
--- a/patches/server/0646-More-Enchantment-API.patch
+++ b/patches/server/0632-More-Enchantment-API.patch
diff --git a/patches/server/0647-Move-range-check-for-block-placing-up.patch b/patches/server/0633-Move-range-check-for-block-placing-up.patch
index f4a82e33e1..f4a82e33e1 100644
--- a/patches/server/0647-Move-range-check-for-block-placing-up.patch
+++ b/patches/server/0633-Move-range-check-for-block-placing-up.patch
diff --git a/patches/server/0648-Fix-and-optimise-world-force-upgrading.patch b/patches/server/0634-Fix-and-optimise-world-force-upgrading.patch
index 95adf64b12..7466f77f39 100644
--- a/patches/server/0648-Fix-and-optimise-world-force-upgrading.patch
+++ b/patches/server/0634-Fix-and-optimise-world-force-upgrading.patch
@@ -272,7 +272,7 @@ index 2f82002c52af7304ff6b2d6fe8f094314daf0bba..5962f7a2b185d7d54a0f9e341a4fdf6e
Main.LOGGER.info("Forcing world upgrade! {}", session.getLevelId()); // CraftBukkit
WorldUpgrader worldupgrader = new WorldUpgrader(session, dataFixer, generatorOptions, eraseCache);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 3c5b7f4b2db421d56e5832e283bd60702b2d67de..84e76fbe3eca77b112c9dff936e21cba1c83e5aa 100644
+index 6758b78d1d780de3fecd6818f0ef83855fb82eef..a61a0a61ee5c96de68521ae3d01fd9f75526502c 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -545,11 +545,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -303,7 +303,7 @@ index 3c5b7f4b2db421d56e5832e283bd60702b2d67de..84e76fbe3eca77b112c9dff936e21cba
if (dimensionKey == LevelStem.OVERWORLD) {
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 525df1e5515fff204f790edcd0a051e851c03d33..ea2e75b227673f8b0016254f8c52a6721c8adf33 100644
+index 1fcbf1fe2ed5d46766ae71e556f124e7822829e3..15050e9bfbb5c5639a8b225a9beb3834aab4eafb 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -182,6 +182,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -323,7 +323,7 @@ index 525df1e5515fff204f790edcd0a051e851c03d33..ea2e75b227673f8b0016254f8c52a672
return this.world;
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index 2a6a4a62feb1c02bef850b0cda578f6f9d46a5e3..666286f94f09299f95538ef2f19b588eb74d9dda 100644
+index 7bfb0716964af5ee300150d500c97e8f90c849d4..c881d15efa94fc379ed2817a534fef3a4dd3d90d 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -32,6 +32,28 @@ public class RegionFileStorage implements AutoCloseable {
@@ -356,7 +356,7 @@ index 2a6a4a62feb1c02bef850b0cda578f6f9d46a5e3..666286f94f09299f95538ef2f19b588e
return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()));
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 9447b7cf08401608b2b79e8c68f58fd977cf0d42..dd43414dde55a5b447f9f51e4e5eef12fbbcfbde 100644
+index ab8ab0180508948c7b21bafcbf914483673047d3..60224fce02148ac7e8a28524b8769372eff98817 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1204,12 +1204,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0649-Add-Mob-lookAt-API.patch b/patches/server/0635-Add-Mob-lookAt-API.patch
index 2179be5193..2179be5193 100644
--- a/patches/server/0649-Add-Mob-lookAt-API.patch
+++ b/patches/server/0635-Add-Mob-lookAt-API.patch
diff --git a/patches/server/0650-Add-Unix-domain-socket-support.patch b/patches/server/0636-Add-Unix-domain-socket-support.patch
index 7873f07d98..e9f8538eff 100644
--- a/patches/server/0650-Add-Unix-domain-socket-support.patch
+++ b/patches/server/0636-Add-Unix-domain-socket-support.patch
@@ -11,10 +11,10 @@ Tested-by: Mariell Hoversholm <[email protected]>
Reviewed-by: Mariell Hoversholm <[email protected]>
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 86cc291b5b14523d57c84f8ebd6ba9b9c3b0d1a6..b5f884d6671823085a2ab0e8da2d30afd2928f32 100644
+index 13213bbc9ca5ca20c3eb8a6744dc3204c8c1e423..2f38d04b369b345c89f85cee32081df8baf4239f 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -641,6 +641,11 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -670,6 +670,11 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
// Spigot Start
public SocketAddress getRawAddress()
{
@@ -27,7 +27,7 @@ index 86cc291b5b14523d57c84f8ebd6ba9b9c3b0d1a6..b5f884d6671823085a2ab0e8da2d30af
}
// Spigot End
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 2c215e2080f00d6c875fbde92fd2c1c051d0cf98..2d01a1d4b2f7fdd38a6b1022f2476ba68b663171 100644
+index 3e600ee2d5370c2ad9c445af15f350dee21248c1..6b40e5659923d74438efaeb846732cb4efbf3f1b 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -224,6 +224,20 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
diff --git a/patches/server/0651-Add-EntityInsideBlockEvent.patch b/patches/server/0637-Add-EntityInsideBlockEvent.patch
index 0ca02bfa9a..0ca02bfa9a 100644
--- a/patches/server/0651-Add-EntityInsideBlockEvent.patch
+++ b/patches/server/0637-Add-EntityInsideBlockEvent.patch
diff --git a/patches/server/0652-Attributes-API-for-item-defaults.patch b/patches/server/0638-Attributes-API-for-item-defaults.patch
index 882921f7c0..882921f7c0 100644
--- a/patches/server/0652-Attributes-API-for-item-defaults.patch
+++ b/patches/server/0638-Attributes-API-for-item-defaults.patch
diff --git a/patches/server/0653-Add-cause-to-Weather-ThunderChangeEvents.patch b/patches/server/0639-Add-cause-to-Weather-ThunderChangeEvents.patch
index ebde715e98..73bdb8eb2b 100644
--- a/patches/server/0653-Add-cause-to-Weather-ThunderChangeEvents.patch
+++ b/patches/server/0639-Add-cause-to-Weather-ThunderChangeEvents.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add cause to Weather/ThunderChangeEvents
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 7f121ce977fd5779032450443c94634bc919009d..6e9f1f01227a94480043ba3120c77f1ae080ec02 100644
+index 60e86357327f7bd7141d421b2d6d52d433118f1f..cb4e720d25cd71c4094e476732b519a0a6ff2b27 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -498,8 +498,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -528,8 +528,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
this.serverLevelData.setClearWeatherTime(clearDuration);
this.serverLevelData.setRainTime(rainDuration);
this.serverLevelData.setThunderTime(rainDuration);
@@ -19,7 +19,7 @@ index 7f121ce977fd5779032450443c94634bc919009d..6e9f1f01227a94480043ba3120c77f1a
}
@Override
-@@ -891,8 +891,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -924,8 +924,8 @@ public class ServerLevel extends Level implements WorldGenLevel {
this.serverLevelData.setThunderTime(j);
this.serverLevelData.setRainTime(k);
this.serverLevelData.setClearWeatherTime(i);
@@ -30,7 +30,7 @@ index 7f121ce977fd5779032450443c94634bc919009d..6e9f1f01227a94480043ba3120c77f1a
}
this.oThunderLevel = this.thunderLevel;
-@@ -958,14 +958,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -991,14 +991,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
private void resetWeatherCycle() {
// CraftBukkit start
@@ -95,10 +95,10 @@ index 401787a5b55384b9ab7755e822b3b881dc45ac45..e537a8df45c31efa80cb898cbef9c3a0
if (weather.isCancelled()) {
return;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 9ca188959484041f53a078963cb79d68fd2a4f48..5fbc8fabf1c94798c21035049f6585e915da0536 100644
+index 3e4b077f2c890b28c88c62b0491f9f2a355446c9..be311f228b6146cb115f5df24290579792592a50 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -1194,7 +1194,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1184,7 +1184,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void setStorm(boolean hasStorm) {
@@ -107,7 +107,7 @@ index 9ca188959484041f53a078963cb79d68fd2a4f48..5fbc8fabf1c94798c21035049f6585e9
this.setWeatherDuration(0); // Reset weather duration (legacy behaviour)
this.setClearWeatherDuration(0); // Reset clear weather duration (reset "/weather clear" commands)
}
-@@ -1216,7 +1216,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1206,7 +1206,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void setThundering(boolean thundering) {
diff --git a/patches/server/0654-More-Lidded-Block-API.patch b/patches/server/0640-More-Lidded-Block-API.patch
index 135fd68698..135fd68698 100644
--- a/patches/server/0654-More-Lidded-Block-API.patch
+++ b/patches/server/0640-More-Lidded-Block-API.patch
diff --git a/patches/server/0655-Limit-item-frame-cursors-on-maps.patch b/patches/server/0641-Limit-item-frame-cursors-on-maps.patch
index 4e2cd3bfa2..4e2cd3bfa2 100644
--- a/patches/server/0655-Limit-item-frame-cursors-on-maps.patch
+++ b/patches/server/0641-Limit-item-frame-cursors-on-maps.patch
diff --git a/patches/server/0656-Add-PlayerKickEvent-causes.patch b/patches/server/0642-Add-PlayerKickEvent-causes.patch
index 2fc63bdf0d..77f684e693 100644
--- a/patches/server/0656-Add-PlayerKickEvent-causes.patch
+++ b/patches/server/0642-Add-PlayerKickEvent-causes.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add PlayerKickEvent causes
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 84e76fbe3eca77b112c9dff936e21cba1c83e5aa..98fe4165d291b47a39ce741884353c87dd0a4789 100644
+index b108b4ce54570a841086adffac542d8f7f2f2c6d..79c4ee2dd842fcf29eb91a69e536960c814c1a0d 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2118,7 +2118,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2112,7 +2112,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
if (!whitelist.isWhiteListed(entityplayer.getGameProfile()) && !this.getPlayerList().isOp(entityplayer.getGameProfile())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420)
@@ -367,7 +367,7 @@ index 3ef39ebeb76f43d521266402e170bd1af6af2348..55c5348e793fa560d7042cf90076a8f7
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 977a23684b061d7390f70f8754a1d879d7d7075a..cd7fdf629ac1dd17e83445afdf352c33823cbd25 100644
+index 4c2c495d936cb84d85fb3e9f91e101a0fd796026..e316acab310fcf689f7a31f73bb3bc2b5e7eff0e 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -726,7 +726,7 @@ public abstract class PlayerList {
@@ -391,10 +391,10 @@ index 977a23684b061d7390f70f8754a1d879d7d7075a..cd7fdf629ac1dd17e83445afdf352c33
// CraftBukkit end
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 05903c7da2ff1a5960c66ef23a685c3da8de04ae..e0d7a03b57d42f7eacb40e17c684b409cf165338 100644
+index 64c6a35ab369a19924dba94018c361dc2d24be82..a979998b235dcd219f16a3d9c9f56a40cef12cfa 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -522,7 +522,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -597,7 +597,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
org.spigotmc.AsyncCatcher.catchOp("player kick"); // Spigot
if (this.getHandle().connection == null) return;
@@ -403,7 +403,7 @@ index 05903c7da2ff1a5960c66ef23a685c3da8de04ae..e0d7a03b57d42f7eacb40e17c684b409
}
// Paper start
-@@ -534,10 +534,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -609,10 +609,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void kick(final net.kyori.adventure.text.Component message) {
diff --git a/patches/server/0657-Add-PufferFishStateChangeEvent.patch b/patches/server/0643-Add-PufferFishStateChangeEvent.patch
index e15126884a..e15126884a 100644
--- a/patches/server/0657-Add-PufferFishStateChangeEvent.patch
+++ b/patches/server/0643-Add-PufferFishStateChangeEvent.patch
diff --git a/patches/server/0658-Fix-PlayerBucketEmptyEvent-result-itemstack.patch b/patches/server/0644-Fix-PlayerBucketEmptyEvent-result-itemstack.patch
index 0ebe5f6ad9..0ebe5f6ad9 100644
--- a/patches/server/0658-Fix-PlayerBucketEmptyEvent-result-itemstack.patch
+++ b/patches/server/0644-Fix-PlayerBucketEmptyEvent-result-itemstack.patch
diff --git a/patches/server/0659-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch b/patches/server/0645-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch
index af97198c95..4fd0ff61f2 100644
--- a/patches/server/0659-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch
+++ b/patches/server/0645-Synchronize-PalettedContainer-instead-of-ThreadingDe.patch
@@ -14,7 +14,7 @@ contention situations.
And this is extremely a low contention situation.
diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-index b688d239ff11b315f60cd980d8f6780b982a865b..d93118b7a602ceb6ef11ddabbce1d13fb8029a44 100644
+index 8f5114b6d64461866140b6d2ced3f838a1228851..9e652cf68b289696b5aee53ac157fc52cca10d2c 100644
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -32,14 +32,14 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
diff --git a/patches/server/0660-Add-option-to-fix-items-merging-through-walls.patch b/patches/server/0646-Add-option-to-fix-items-merging-through-walls.patch
index 0c6f1b92fc..0c6f1b92fc 100644
--- a/patches/server/0660-Add-option-to-fix-items-merging-through-walls.patch
+++ b/patches/server/0646-Add-option-to-fix-items-merging-through-walls.patch
diff --git a/patches/server/0661-Add-BellRevealRaiderEvent.patch b/patches/server/0647-Add-BellRevealRaiderEvent.patch
index 587e0f0400..587e0f0400 100644
--- a/patches/server/0661-Add-BellRevealRaiderEvent.patch
+++ b/patches/server/0647-Add-BellRevealRaiderEvent.patch
diff --git a/patches/server/0662-Fix-invulnerable-end-crystals.patch b/patches/server/0648-Fix-invulnerable-end-crystals.patch
index e5ce326afa..e5ce326afa 100644
--- a/patches/server/0662-Fix-invulnerable-end-crystals.patch
+++ b/patches/server/0648-Fix-invulnerable-end-crystals.patch
diff --git a/patches/server/0663-Add-ElderGuardianAppearanceEvent.patch b/patches/server/0649-Add-ElderGuardianAppearanceEvent.patch
index 13c35cf8b6..13c35cf8b6 100644
--- a/patches/server/0663-Add-ElderGuardianAppearanceEvent.patch
+++ b/patches/server/0649-Add-ElderGuardianAppearanceEvent.patch
diff --git a/patches/server/0664-Fix-dangerous-end-portal-logic.patch b/patches/server/0650-Fix-dangerous-end-portal-logic.patch
index bb45e6156c..949aa844ce 100644
--- a/patches/server/0664-Fix-dangerous-end-portal-logic.patch
+++ b/patches/server/0650-Fix-dangerous-end-portal-logic.patch
@@ -11,10 +11,10 @@ Move the tick logic into the post tick, where portaling was
designed to happen in the first place.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 525e712c5715f1fa323fae94d4158c4e66068fe3..0b524fb57af63ad3b9e11a30e7b2d0fd219664ad 100644
+index 9b1892a4bb385ab888cf0c11f9a7a84921755803..dafe29f3e2b4e7a51d4232c60fd27645e5f20335 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -459,6 +459,36 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -510,6 +510,36 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this));
}
// Paper end - optimise entity tracking
@@ -51,7 +51,7 @@ index 525e712c5715f1fa323fae94d4158c4e66068fe3..0b524fb57af63ad3b9e11a30e7b2d0fd
public Entity(EntityType<?> type, Level world) {
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
-@@ -2635,6 +2665,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2686,6 +2716,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
this.processPortalCooldown();
diff --git a/patches/server/0665-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch b/patches/server/0651-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch
index c46f8fbb65..c46f8fbb65 100644
--- a/patches/server/0665-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch
+++ b/patches/server/0651-Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch
diff --git a/patches/server/0666-Make-item-validations-configurable.patch b/patches/server/0652-Make-item-validations-configurable.patch
index d079cc7ed2..d079cc7ed2 100644
--- a/patches/server/0666-Make-item-validations-configurable.patch
+++ b/patches/server/0652-Make-item-validations-configurable.patch
diff --git a/patches/server/0667-Line-Of-Sight-Changes.patch b/patches/server/0653-Line-Of-Sight-Changes.patch
index 80cd9d95ac..80cd9d95ac 100644
--- a/patches/server/0667-Line-Of-Sight-Changes.patch
+++ b/patches/server/0653-Line-Of-Sight-Changes.patch
diff --git a/patches/server/0668-add-per-world-spawn-limits.patch b/patches/server/0654-add-per-world-spawn-limits.patch
index b75079eb4c..252b1ab32b 100644
--- a/patches/server/0668-add-per-world-spawn-limits.patch
+++ b/patches/server/0654-add-per-world-spawn-limits.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] add per world spawn limits
Taken from #2982. Credit to Chasewhip8
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 5fbc8fabf1c94798c21035049f6585e915da0536..2bed5b4eaeac4e48df606b755489a3ca5ffc895e 100644
+index be311f228b6146cb115f5df24290579792592a50..b6aefca1ef13ddbe4f79b91cfed4fbc109b89475 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -210,6 +210,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
diff --git a/patches/server/0669-Fix-PotionSplashEvent-for-water-splash-potions.patch b/patches/server/0655-Fix-PotionSplashEvent-for-water-splash-potions.patch
index 56f02dad6e..56f02dad6e 100644
--- a/patches/server/0669-Fix-PotionSplashEvent-for-water-splash-potions.patch
+++ b/patches/server/0655-Fix-PotionSplashEvent-for-water-splash-potions.patch
diff --git a/patches/server/0670-Add-more-LimitedRegion-API.patch b/patches/server/0656-Add-more-LimitedRegion-API.patch
index e9e7611f99..e9e7611f99 100644
--- a/patches/server/0670-Add-more-LimitedRegion-API.patch
+++ b/patches/server/0656-Add-more-LimitedRegion-API.patch
diff --git a/patches/server/0671-Fix-PlayerDropItemEvent-using-wrong-item.patch b/patches/server/0657-Fix-PlayerDropItemEvent-using-wrong-item.patch
index 7d613a826d..77f6b6fccb 100644
--- a/patches/server/0671-Fix-PlayerDropItemEvent-using-wrong-item.patch
+++ b/patches/server/0657-Fix-PlayerDropItemEvent-using-wrong-item.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix PlayerDropItemEvent using wrong item
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 4a35720430990b358ea5d7f2b6293e27e8d9f7ac..9a60cf249e0b9f089b0966c670e6046e7e1ed08a 100644
+index 73864c47e7f13a48243478bc24d4887aa70791b3..f5905fd32f1fbbc62e4578a735aae3c45a65a3f9 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -2201,7 +2201,7 @@ public class ServerPlayer extends Player {
+@@ -2186,7 +2186,7 @@ public class ServerPlayer extends Player {
if (retainOwnership) {
if (!itemstack1.isEmpty()) {
diff --git a/patches/server/0672-Missing-Entity-Behavior-API.patch b/patches/server/0658-Missing-Entity-Behavior-API.patch
index ba264fa322..fecc136bbf 100644
--- a/patches/server/0672-Missing-Entity-Behavior-API.patch
+++ b/patches/server/0658-Missing-Entity-Behavior-API.patch
@@ -56,7 +56,7 @@ index 04a119e6641898454253e2478bc1b4dff181b5ee..a8da601b8342aa6e4902b452eb588c76
if (angry) {
this.setEating(false);
diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
-index 32b302aad0319ce3ee412912425c1c8db9979f8a..92734f767fde60351a179a88350a97b861be0e88 100644
+index 1fc862a3b5d40a45cf91703051bcfb06ec1b177a..02d7cd9cd27ff9254c3e99e3e94309a8cb8b243d 100644
--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
@@ -84,6 +84,11 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
@@ -71,7 +71,7 @@ index 32b302aad0319ce3ee412912425c1c8db9979f8a..92734f767fde60351a179a88350a97b8
public WitherBoss(EntityType<? extends WitherBoss> type, Level world) {
super(type, world);
-@@ -602,7 +607,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
+@@ -601,7 +606,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
@Override
public boolean canChangeDimensions() {
diff --git a/patches/server/0673-Ensure-disconnect-for-book-edit-is-called-on-main.patch b/patches/server/0659-Ensure-disconnect-for-book-edit-is-called-on-main.patch
index a2ab09f58e..a2ab09f58e 100644
--- a/patches/server/0673-Ensure-disconnect-for-book-edit-is-called-on-main.patch
+++ b/patches/server/0659-Ensure-disconnect-for-book-edit-is-called-on-main.patch
diff --git a/patches/server/0674-Fix-return-value-of-Block-applyBoneMeal-always-being.patch b/patches/server/0660-Fix-return-value-of-Block-applyBoneMeal-always-being.patch
index 863796fe03..863796fe03 100644
--- a/patches/server/0674-Fix-return-value-of-Block-applyBoneMeal-always-being.patch
+++ b/patches/server/0660-Fix-return-value-of-Block-applyBoneMeal-always-being.patch
diff --git a/patches/server/0675-Use-getChunkIfLoadedImmediately-in-places.patch b/patches/server/0661-Use-getChunkIfLoadedImmediately-in-places.patch
index 1e0ae5d1ce..aabc626bc4 100644
--- a/patches/server/0675-Use-getChunkIfLoadedImmediately-in-places.patch
+++ b/patches/server/0661-Use-getChunkIfLoadedImmediately-in-places.patch
@@ -8,7 +8,7 @@ ticket level 33 (yes getChunkIfLoaded will actually perform a chunk
load in that case).
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 6e9f1f01227a94480043ba3120c77f1ae080ec02..d0c4c9c172c8caa3eaf6c0bf56a8be9f16d8c4e7 100644
+index cb4e720d25cd71c4094e476732b519a0a6ff2b27..10abc211d4b5688de95066caccc7486acafd2858 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -223,7 +223,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
@@ -20,7 +20,7 @@ index 6e9f1f01227a94480043ba3120c77f1ae080ec02..d0c4c9c172c8caa3eaf6c0bf56a8be9f
}
@Override
-@@ -1441,7 +1441,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1476,7 +1476,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
for (int l1 = j; l1 <= i1; ++l1) {
for (int i2 = l; i2 <= k1; ++i2) {
@@ -30,7 +30,7 @@ index 6e9f1f01227a94480043ba3120c77f1ae080ec02..d0c4c9c172c8caa3eaf6c0bf56a8be9f
if (chunk != null) {
for (int j2 = k; j2 <= j1; ++j2) {
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index ea2e75b227673f8b0016254f8c52a6721c8adf33..4b7ed29a7a38cf7f8fb488c9c34471fafcdf2f25 100644
+index 15050e9bfbb5c5639a8b225a9beb3834aab4eafb..e30414e1a5e7907c59917c5cd182bca65bc65825 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -199,6 +199,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0676-Fix-commands-from-signs-not-firing-command-events.patch b/patches/server/0662-Fix-commands-from-signs-not-firing-command-events.patch
index c66c6d829d..c66c6d829d 100644
--- a/patches/server/0676-Fix-commands-from-signs-not-firing-command-events.patch
+++ b/patches/server/0662-Fix-commands-from-signs-not-firing-command-events.patch
diff --git a/patches/server/0677-Adds-PlayerArmSwingEvent.patch b/patches/server/0663-Adds-PlayerArmSwingEvent.patch
index a21292ebf2..a21292ebf2 100644
--- a/patches/server/0677-Adds-PlayerArmSwingEvent.patch
+++ b/patches/server/0663-Adds-PlayerArmSwingEvent.patch
diff --git a/patches/server/0678-Fixes-kick-event-leave-message-not-being-sent.patch b/patches/server/0664-Fixes-kick-event-leave-message-not-being-sent.patch
index 8ca4f8856f..4bf0b330d1 100644
--- a/patches/server/0678-Fixes-kick-event-leave-message-not-being-sent.patch
+++ b/patches/server/0664-Fixes-kick-event-leave-message-not-being-sent.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Fixes kick event leave message not being sent
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 9a60cf249e0b9f089b0966c670e6046e7e1ed08a..787f2b23352fbfb66a76ca5fce6de94cc10b30d2 100644
+index f5905fd32f1fbbc62e4578a735aae3c45a65a3f9..31d976d0a4fdf3bb155e08627ce4a85bd5d051db 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -260,7 +260,6 @@ public class ServerPlayer extends Player {
+@@ -259,7 +259,6 @@ public class ServerPlayer extends Player {
public boolean supressTrackerForLogin = false; // Paper
public boolean didPlayerJoinEvent = false; // Paper
public Integer clientViewDistance;
@@ -59,7 +59,7 @@ index 7d594e23dc7c393f258b16ec5f04e60fc31f91c8..ebaf6ddcdccf4e2a5836782e58770e2c
this.server.getPlayerList().broadcastSystemMessage(PaperAdventure.asVanilla(quitMessage), false);
// Paper end
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index cd7fdf629ac1dd17e83445afdf352c33823cbd25..a34df8db7ae5a1e2fd304e006db7b4af609efb4d 100644
+index e316acab310fcf689f7a31f73bb3bc2b5e7eff0e..627cf258312e5dc7a129286de6dcb4c028f846ac 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -598,6 +598,11 @@ public abstract class PlayerList {
diff --git a/patches/server/0679-Add-config-for-mobs-immune-to-default-effects.patch b/patches/server/0665-Add-config-for-mobs-immune-to-default-effects.patch
index 66f43a21fd..40f29a68fc 100644
--- a/patches/server/0679-Add-config-for-mobs-immune-to-default-effects.patch
+++ b/patches/server/0665-Add-config-for-mobs-immune-to-default-effects.patch
@@ -18,10 +18,10 @@ index 4dee04c8012245b94191454943d68ee20fae887a..6e2301f58f103b70b491fd59d5a66575
}
}
diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
-index 92734f767fde60351a179a88350a97b861be0e88..b3e2e834f4f151497bf842796dd8e3a8b5143f1b 100644
+index 02d7cd9cd27ff9254c3e99e3e94309a8cb8b243d..a6d30f3213d30ba925926437ed4535319061213d 100644
--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java
-@@ -612,7 +612,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
+@@ -611,7 +611,7 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob
@Override
public boolean canBeAffected(MobEffectInstance effect) {
diff --git a/patches/server/0680-Fix-incorrect-message-for-outdated-client.patch b/patches/server/0666-Fix-incorrect-message-for-outdated-client.patch
index 7f39718ca5..7f39718ca5 100644
--- a/patches/server/0680-Fix-incorrect-message-for-outdated-client.patch
+++ b/patches/server/0666-Fix-incorrect-message-for-outdated-client.patch
diff --git a/patches/server/0681-Don-t-apply-cramming-damage-to-players.patch b/patches/server/0667-Don-t-apply-cramming-damage-to-players.patch
index 28e1719c3c..6487e17c55 100644
--- a/patches/server/0681-Don-t-apply-cramming-damage-to-players.patch
+++ b/patches/server/0667-Don-t-apply-cramming-damage-to-players.patch
@@ -11,10 +11,10 @@ It does not make a lot of sense to damage players if they get crammed,
For those who really want it a config option is provided.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 787f2b23352fbfb66a76ca5fce6de94cc10b30d2..56ac440465813a7dab8d166e882e18143a50729f 100644
+index 31d976d0a4fdf3bb155e08627ce4a85bd5d051db..a02b02c71ac6dddcccb10a6aa7993337ed04b829 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1446,7 +1446,7 @@ public class ServerPlayer extends Player {
+@@ -1431,7 +1431,7 @@ public class ServerPlayer extends Player {
@Override
public boolean isInvulnerableTo(DamageSource damageSource) {
diff --git a/patches/server/0682-Rate-options-and-timings-for-sensors-and-behaviors.patch b/patches/server/0668-Rate-options-and-timings-for-sensors-and-behaviors.patch
index 30867e6cf9..30867e6cf9 100644
--- a/patches/server/0682-Rate-options-and-timings-for-sensors-and-behaviors.patch
+++ b/patches/server/0668-Rate-options-and-timings-for-sensors-and-behaviors.patch
diff --git a/patches/server/0683-Add-a-bunch-of-missing-forceDrop-toggles.patch b/patches/server/0669-Add-a-bunch-of-missing-forceDrop-toggles.patch
index 34628f8969..34628f8969 100644
--- a/patches/server/0683-Add-a-bunch-of-missing-forceDrop-toggles.patch
+++ b/patches/server/0669-Add-a-bunch-of-missing-forceDrop-toggles.patch
diff --git a/patches/server/0684-Stinger-API.patch b/patches/server/0670-Stinger-API.patch
index bfa434970f..bfa434970f 100644
--- a/patches/server/0684-Stinger-API.patch
+++ b/patches/server/0670-Stinger-API.patch
diff --git a/patches/server/0685-Fix-incosistency-issue-with-empty-map-items-in-CB.patch b/patches/server/0671-Fix-incosistency-issue-with-empty-map-items-in-CB.patch
index 3f1afe6bf8..3f1afe6bf8 100644
--- a/patches/server/0685-Fix-incosistency-issue-with-empty-map-items-in-CB.patch
+++ b/patches/server/0671-Fix-incosistency-issue-with-empty-map-items-in-CB.patch
diff --git a/patches/server/0686-Add-System.out-err-catcher.patch b/patches/server/0672-Add-System.out-err-catcher.patch
index e2587514e8..2a683a5cf1 100644
--- a/patches/server/0686-Add-System.out-err-catcher.patch
+++ b/patches/server/0672-Add-System.out-err-catcher.patch
@@ -105,7 +105,7 @@ index 0000000000000000000000000000000000000000..76d0d00cd6742991e3f3ec827a75ee87
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index dd43414dde55a5b447f9f51e4e5eef12fbbcfbde..e7863390c8394a6afcfa25099b4ce49c5e010f13 100644
+index 60224fce02148ac7e8a28524b8769372eff98817..7c5dd4a3bf98599356022539f203ef276c29cbab 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -284,6 +284,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0687-Fix-test-not-bootstrapping.patch b/patches/server/0673-Fix-test-not-bootstrapping.patch
index 5d0466c3b3..5d0466c3b3 100644
--- a/patches/server/0687-Fix-test-not-bootstrapping.patch
+++ b/patches/server/0673-Fix-test-not-bootstrapping.patch
diff --git a/patches/server/0688-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch b/patches/server/0674-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch
index 10cd90749d..10cd90749d 100644
--- a/patches/server/0688-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch
+++ b/patches/server/0674-Rewrite-LogEvents-to-contain-the-source-jars-in-stac.patch
diff --git a/patches/server/0689-Improve-boat-collision-performance.patch b/patches/server/0675-Improve-boat-collision-performance.patch
index f6283ceb9a..655f2366da 100644
--- a/patches/server/0689-Improve-boat-collision-performance.patch
+++ b/patches/server/0675-Improve-boat-collision-performance.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 2 Aug 2021 10:10:40 +0200
Subject: [PATCH] Improve boat collision performance
diff --git a/patches/server/0690-Prevent-AFK-kick-while-watching-end-credits.patch b/patches/server/0676-Prevent-AFK-kick-while-watching-end-credits.patch
index e20f451739..e20f451739 100644
--- a/patches/server/0690-Prevent-AFK-kick-while-watching-end-credits.patch
+++ b/patches/server/0676-Prevent-AFK-kick-while-watching-end-credits.patch
diff --git a/patches/server/0691-Allow-skipping-writing-of-comments-to-server.propert.patch b/patches/server/0677-Allow-skipping-writing-of-comments-to-server.propert.patch
index b14eca2ac6..b14eca2ac6 100644
--- a/patches/server/0691-Allow-skipping-writing-of-comments-to-server.propert.patch
+++ b/patches/server/0677-Allow-skipping-writing-of-comments-to-server.propert.patch
diff --git a/patches/server/0692-Add-PlayerSetSpawnEvent.patch b/patches/server/0678-Add-PlayerSetSpawnEvent.patch
index 4de7c11b8e..e91df03693 100644
--- a/patches/server/0692-Add-PlayerSetSpawnEvent.patch
+++ b/patches/server/0678-Add-PlayerSetSpawnEvent.patch
@@ -32,10 +32,10 @@ index ce1c7512cc368e196ae94ee22c6a228c975b4980..1e41de9523c5fa3b9cfced798a5c35a2
String string = resourceKey.location().toString();
if (targets.size() == 1) {
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 56ac440465813a7dab8d166e882e18143a50729f..80e1970f568a74a43e624188a77cfbd28cfa52dd 100644
+index a02b02c71ac6dddcccb10a6aa7993337ed04b829..18f67094f0ce0902963a118744f9fd48f53273c8 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1287,7 +1287,7 @@ public class ServerPlayer extends Player {
+@@ -1272,7 +1272,7 @@ public class ServerPlayer extends Player {
} else if (this.bedBlocked(blockposition, enumdirection)) {
return Either.left(Player.BedSleepingProblem.OBSTRUCTED);
} else {
@@ -44,7 +44,7 @@ index 56ac440465813a7dab8d166e882e18143a50729f..80e1970f568a74a43e624188a77cfbd2
if (this.level.isDay()) {
return Either.left(Player.BedSleepingProblem.NOT_POSSIBLE_NOW);
} else {
-@@ -2128,12 +2128,33 @@ public class ServerPlayer extends Player {
+@@ -2113,12 +2113,33 @@ public class ServerPlayer extends Player {
return this.respawnForced;
}
@@ -80,7 +80,7 @@ index 56ac440465813a7dab8d166e882e18143a50729f..80e1970f568a74a43e624188a77cfbd2
}
this.respawnPosition = pos;
-@@ -2147,6 +2168,7 @@ public class ServerPlayer extends Player {
+@@ -2132,6 +2153,7 @@ public class ServerPlayer extends Player {
this.respawnForced = false;
}
@@ -89,7 +89,7 @@ index 56ac440465813a7dab8d166e882e18143a50729f..80e1970f568a74a43e624188a77cfbd2
public void trackChunk(ChunkPos chunkPos, Packet<?> chunkDataPacket) {
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index a34df8db7ae5a1e2fd304e006db7b4af609efb4d..5ee0c3bb27ffbadc1e088983e643eed974753b65 100644
+index 627cf258312e5dc7a129286de6dcb4c028f846ac..456031783aa902c8fd40050aa2b8d051b996d71d 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -902,13 +902,13 @@ public abstract class PlayerList {
@@ -129,10 +129,10 @@ index c3e49a781f838e6a46cb89744f3f1846de182275..c2f3d3a09327e7cb7d3167609eb3ce68
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index e0d7a03b57d42f7eacb40e17c684b409cf165338..6bc211c98fc4c783545e5c71248f71b27e0853f8 100644
+index a979998b235dcd219f16a3d9c9f56a40cef12cfa..3ff0c79134c1c474bc1e55b879394939a345562e 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -1250,9 +1250,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -1285,9 +1285,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void setBedSpawnLocation(Location location, boolean override) {
if (location == null) {
diff --git a/patches/server/0693-Make-hoppers-respect-inventory-max-stack-size.patch b/patches/server/0679-Make-hoppers-respect-inventory-max-stack-size.patch
index b4a083a5f7..b4a083a5f7 100644
--- a/patches/server/0693-Make-hoppers-respect-inventory-max-stack-size.patch
+++ b/patches/server/0679-Make-hoppers-respect-inventory-max-stack-size.patch
diff --git a/patches/server/0694-Optimize-entity-tracker-passenger-checks.patch b/patches/server/0680-Optimize-entity-tracker-passenger-checks.patch
index d3fef67200..d3fef67200 100644
--- a/patches/server/0694-Optimize-entity-tracker-passenger-checks.patch
+++ b/patches/server/0680-Optimize-entity-tracker-passenger-checks.patch
diff --git a/patches/server/0695-Config-option-for-Piglins-guarding-chests.patch b/patches/server/0681-Config-option-for-Piglins-guarding-chests.patch
index c66e6ea022..c66e6ea022 100644
--- a/patches/server/0695-Config-option-for-Piglins-guarding-chests.patch
+++ b/patches/server/0681-Config-option-for-Piglins-guarding-chests.patch
diff --git a/patches/server/0696-Added-EntityDamageItemEvent.patch b/patches/server/0682-Added-EntityDamageItemEvent.patch
index ec43bd00c9..ec43bd00c9 100644
--- a/patches/server/0696-Added-EntityDamageItemEvent.patch
+++ b/patches/server/0682-Added-EntityDamageItemEvent.patch
diff --git a/patches/server/0697-Optimize-indirect-passenger-iteration.patch b/patches/server/0683-Optimize-indirect-passenger-iteration.patch
index 9768e1b56a..24b6d99483 100644
--- a/patches/server/0697-Optimize-indirect-passenger-iteration.patch
+++ b/patches/server/0683-Optimize-indirect-passenger-iteration.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Optimize indirect passenger iteration
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 0b524fb57af63ad3b9e11a30e7b2d0fd219664ad..307f3e4fa66bdf4f9b0864a7ba4aaa29f6e3b8d1 100644
+index dafe29f3e2b4e7a51d4232c60fd27645e5f20335..517ee85a7425d89e4197887eb6b385bc64ecedd3 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -3610,26 +3610,41 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3661,20 +3661,34 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
private Stream<Entity> getIndirectPassengersStream() {
@@ -43,7 +43,8 @@ index 0b524fb57af63ad3b9e11a30e7b2d0fd219664ad..307f3e4fa66bdf4f9b0864a7ba4aaa29
return () -> {
return this.getIndirectPassengersStream().iterator();
};
- }
+@@ -3691,6 +3705,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+ // Paper end - rewrite chunk system
public boolean hasExactlyOnePlayerPassenger() {
+ if (this.passengers.isEmpty()) { return false; } // Paper
diff --git a/patches/server/0698-Fix-block-drops-position-losing-precision-millions-o.patch b/patches/server/0684-Fix-block-drops-position-losing-precision-millions-o.patch
index 6b5cd596c4..6b5cd596c4 100644
--- a/patches/server/0698-Fix-block-drops-position-losing-precision-millions-o.patch
+++ b/patches/server/0684-Fix-block-drops-position-losing-precision-millions-o.patch
diff --git a/patches/server/0699-Configurable-item-frame-map-cursor-update-interval.patch b/patches/server/0685-Configurable-item-frame-map-cursor-update-interval.patch
index 5e1d739a7c..5e1d739a7c 100644
--- a/patches/server/0699-Configurable-item-frame-map-cursor-update-interval.patch
+++ b/patches/server/0685-Configurable-item-frame-map-cursor-update-interval.patch
diff --git a/patches/server/0700-Make-EntityUnleashEvent-cancellable.patch b/patches/server/0686-Make-EntityUnleashEvent-cancellable.patch
index b16ad47f2f..b16ad47f2f 100644
--- a/patches/server/0700-Make-EntityUnleashEvent-cancellable.patch
+++ b/patches/server/0686-Make-EntityUnleashEvent-cancellable.patch
diff --git a/patches/server/0701-Clear-bucket-NBT-after-dispense.patch b/patches/server/0687-Clear-bucket-NBT-after-dispense.patch
index 52e26f4674..52e26f4674 100644
--- a/patches/server/0701-Clear-bucket-NBT-after-dispense.patch
+++ b/patches/server/0687-Clear-bucket-NBT-after-dispense.patch
diff --git a/patches/server/0702-Change-EnderEye-target-without-changing-other-things.patch b/patches/server/0688-Change-EnderEye-target-without-changing-other-things.patch
index ce9dc32309..ce9dc32309 100644
--- a/patches/server/0702-Change-EnderEye-target-without-changing-other-things.patch
+++ b/patches/server/0688-Change-EnderEye-target-without-changing-other-things.patch
diff --git a/patches/server/0703-Add-BlockBreakBlockEvent.patch b/patches/server/0689-Add-BlockBreakBlockEvent.patch
index a69de03ab7..a69de03ab7 100644
--- a/patches/server/0703-Add-BlockBreakBlockEvent.patch
+++ b/patches/server/0689-Add-BlockBreakBlockEvent.patch
diff --git a/patches/server/0704-Option-to-prevent-NBT-copy-in-smithing-recipes.patch b/patches/server/0690-Option-to-prevent-NBT-copy-in-smithing-recipes.patch
index 14e666a79e..14e666a79e 100644
--- a/patches/server/0704-Option-to-prevent-NBT-copy-in-smithing-recipes.patch
+++ b/patches/server/0690-Option-to-prevent-NBT-copy-in-smithing-recipes.patch
diff --git a/patches/server/0705-More-CommandBlock-API.patch b/patches/server/0691-More-CommandBlock-API.patch
index 3cbd2436d3..3cbd2436d3 100644
--- a/patches/server/0705-More-CommandBlock-API.patch
+++ b/patches/server/0691-More-CommandBlock-API.patch
diff --git a/patches/server/0706-Add-missing-team-sidebar-display-slots.patch b/patches/server/0692-Add-missing-team-sidebar-display-slots.patch
index ee0f1ced64..ee0f1ced64 100644
--- a/patches/server/0706-Add-missing-team-sidebar-display-slots.patch
+++ b/patches/server/0692-Add-missing-team-sidebar-display-slots.patch
diff --git a/patches/server/0707-Add-back-EntityPortalExitEvent.patch b/patches/server/0693-Add-back-EntityPortalExitEvent.patch
index 3dec60a42f..cbc46bd951 100644
--- a/patches/server/0707-Add-back-EntityPortalExitEvent.patch
+++ b/patches/server/0693-Add-back-EntityPortalExitEvent.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add back EntityPortalExitEvent
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 307f3e4fa66bdf4f9b0864a7ba4aaa29f6e3b8d1..f212451892c73320cc9fa0e08b059948cc1b3162 100644
+index 517ee85a7425d89e4197887eb6b385bc64ecedd3..07ba08de35bac64eeac2657e15dcc60d1dce3cca 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -3112,6 +3112,23 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3163,6 +3163,23 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
} else {
// CraftBukkit start
worldserver = shapedetectorshape.world;
@@ -32,7 +32,7 @@ index 307f3e4fa66bdf4f9b0864a7ba4aaa29f6e3b8d1..f212451892c73320cc9fa0e08b059948
if (worldserver == this.level) {
// SPIGOT-6782: Just move the entity if a plugin changed the world to the one the entity is already in
this.moveTo(shapedetectorshape.pos.x, shapedetectorshape.pos.y, shapedetectorshape.pos.z, shapedetectorshape.yRot, shapedetectorshape.xRot);
-@@ -3131,8 +3148,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3182,8 +3199,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (entity != null) {
entity.restoreFrom(this);
diff --git a/patches/server/0708-Add-methods-to-find-targets-for-lightning-strikes.patch b/patches/server/0694-Add-methods-to-find-targets-for-lightning-strikes.patch
index 6880c6cd68..cf815001a9 100644
--- a/patches/server/0708-Add-methods-to-find-targets-for-lightning-strikes.patch
+++ b/patches/server/0694-Add-methods-to-find-targets-for-lightning-strikes.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add methods to find targets for lightning strikes
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index d0c4c9c172c8caa3eaf6c0bf56a8be9f16d8c4e7..10421fdac40c756c52abc9430f7149a9f58efbfb 100644
+index 10abc211d4b5688de95066caccc7486acafd2858..a2a2042a9578af07d1f9399a97f13df5ea2ecf51 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -780,6 +780,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -813,6 +813,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
protected BlockPos findLightningTargetAround(BlockPos pos) {
@@ -20,7 +20,7 @@ index d0c4c9c172c8caa3eaf6c0bf56a8be9f16d8c4e7..10421fdac40c756c52abc9430f7149a9
BlockPos blockposition1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos);
Optional<BlockPos> optional = this.findLightningRod(blockposition1);
-@@ -794,6 +799,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -827,6 +832,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
if (!list.isEmpty()) {
return ((LivingEntity) list.get(this.random.nextInt(list.size()))).blockPosition();
} else {
@@ -29,10 +29,10 @@ index d0c4c9c172c8caa3eaf6c0bf56a8be9f16d8c4e7..10421fdac40c756c52abc9430f7149a9
blockposition1 = blockposition1.above(2);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 2bed5b4eaeac4e48df606b755489a3ca5ffc895e..38a74d683aec58a419af84111844a205596524b2 100644
+index b6aefca1ef13ddbe4f79b91cfed4fbc109b89475..5a3e498776a35770a19535751f9dfcc1573035d7 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -696,6 +696,23 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -686,6 +686,23 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return (LightningStrike) lightning.getBukkitEntity();
}
diff --git a/patches/server/0709-Get-entity-default-attributes.patch b/patches/server/0695-Get-entity-default-attributes.patch
index c2cbc0160d..c2cbc0160d 100644
--- a/patches/server/0709-Get-entity-default-attributes.patch
+++ b/patches/server/0695-Get-entity-default-attributes.patch
diff --git a/patches/server/0710-Left-handed-API.patch b/patches/server/0696-Left-handed-API.patch
index 3b4077b54a..3b4077b54a 100644
--- a/patches/server/0710-Left-handed-API.patch
+++ b/patches/server/0696-Left-handed-API.patch
diff --git a/patches/server/0711-Add-advancement-display-API.patch b/patches/server/0697-Add-advancement-display-API.patch
index dd860e3b4d..dd860e3b4d 100644
--- a/patches/server/0711-Add-advancement-display-API.patch
+++ b/patches/server/0697-Add-advancement-display-API.patch
diff --git a/patches/server/0712-Add-ItemFactory-getMonsterEgg-API.patch b/patches/server/0698-Add-ItemFactory-getMonsterEgg-API.patch
index 1944914081..1944914081 100644
--- a/patches/server/0712-Add-ItemFactory-getMonsterEgg-API.patch
+++ b/patches/server/0698-Add-ItemFactory-getMonsterEgg-API.patch
diff --git a/patches/server/0713-Add-critical-damage-API.patch b/patches/server/0699-Add-critical-damage-API.patch
index 980b435d42..980b435d42 100644
--- a/patches/server/0713-Add-critical-damage-API.patch
+++ b/patches/server/0699-Add-critical-damage-API.patch
diff --git a/patches/server/0714-Fix-issues-with-mob-conversion.patch b/patches/server/0700-Fix-issues-with-mob-conversion.patch
index 69feb4b3a4..69feb4b3a4 100644
--- a/patches/server/0714-Fix-issues-with-mob-conversion.patch
+++ b/patches/server/0700-Fix-issues-with-mob-conversion.patch
diff --git a/patches/server/0715-Add-isCollidable-methods-to-various-places.patch b/patches/server/0701-Add-isCollidable-methods-to-various-places.patch
index 2b95e405a3..2b95e405a3 100644
--- a/patches/server/0715-Add-isCollidable-methods-to-various-places.patch
+++ b/patches/server/0701-Add-isCollidable-methods-to-various-places.patch
diff --git a/patches/server/0716-Goat-ram-API.patch b/patches/server/0702-Goat-ram-API.patch
index 426746ea8f..426746ea8f 100644
--- a/patches/server/0716-Goat-ram-API.patch
+++ b/patches/server/0702-Goat-ram-API.patch
diff --git a/patches/server/0717-Add-API-for-resetting-a-single-score.patch b/patches/server/0703-Add-API-for-resetting-a-single-score.patch
index c2e1fd93b9..c2e1fd93b9 100644
--- a/patches/server/0717-Add-API-for-resetting-a-single-score.patch
+++ b/patches/server/0703-Add-API-for-resetting-a-single-score.patch
diff --git a/patches/server/0718-Add-Raw-Byte-Entity-Serialization.patch b/patches/server/0704-Add-Raw-Byte-Entity-Serialization.patch
index cf158a3d0e..d7d990702b 100644
--- a/patches/server/0718-Add-Raw-Byte-Entity-Serialization.patch
+++ b/patches/server/0704-Add-Raw-Byte-Entity-Serialization.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add Raw Byte Entity Serialization
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index f212451892c73320cc9fa0e08b059948cc1b3162..5936fb0d27266e0e91eae5cf49e15f67aeb208bf 100644
+index 07ba08de35bac64eeac2657e15dcc60d1dce3cca..bdfa91daf69e71e8c2e1e820f14360c567bdeb87 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -1935,6 +1935,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1986,6 +1986,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
}
diff --git a/patches/server/0719-Vanilla-command-permission-fixes.patch b/patches/server/0705-Vanilla-command-permission-fixes.patch
index 603bc9d5f0..603bc9d5f0 100644
--- a/patches/server/0719-Vanilla-command-permission-fixes.patch
+++ b/patches/server/0705-Vanilla-command-permission-fixes.patch
diff --git a/patches/server/0721-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch b/patches/server/0706-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch
index c4c28b34ad..3857124856 100644
--- a/patches/server/0721-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch
+++ b/patches/server/0706-Do-not-run-close-logic-for-inventories-on-chunk-unlo.patch
@@ -9,10 +9,10 @@ chunk through it. This should also be OK from a leak prevention/
state desync POV because the TE is getting unloaded anyways.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 10421fdac40c756c52abc9430f7149a9f58efbfb..39d96b4e3dce6d67b568b7b00456de164f6a7241 100644
+index a2a2042a9578af07d1f9399a97f13df5ea2ecf51..5a8762bacf13818a98b9b85a618a6fe8ddf537a4 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1338,9 +1338,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1373,9 +1373,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
// Spigot Start
for (net.minecraft.world.level.block.entity.BlockEntity tileentity : chunk.getBlockEntities().values()) {
if (tileentity instanceof net.minecraft.world.Container) {
@@ -28,10 +28,10 @@ index 10421fdac40c756c52abc9430f7149a9f58efbfb..39d96b4e3dce6d67b568b7b00456de16
}
// Spigot End
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 80e1970f568a74a43e624188a77cfbd28cfa52dd..951ccf3526dc2f5e4e2f16952036683ad132fbe0 100644
+index 18f67094f0ce0902963a118744f9fd48f53273c8..9075b0476e775af0e5bc173dc736781dfde6938a 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1588,6 +1588,18 @@ public class ServerPlayer extends Player {
+@@ -1573,6 +1573,18 @@ public class ServerPlayer extends Player {
this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId));
this.doCloseContainer();
}
diff --git a/patches/server/0723-Fix-GameProfileCache-concurrency.patch b/patches/server/0707-Fix-GameProfileCache-concurrency.patch
index 1c8bd06db3..694dd0fbaa 100644
--- a/patches/server/0723-Fix-GameProfileCache-concurrency.patch
+++ b/patches/server/0707-Fix-GameProfileCache-concurrency.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Sat, 11 Jul 2020 05:09:28 -0700
Subject: [PATCH] Fix GameProfileCache concurrency
@@ -7,7 +7,7 @@ Separate lookup and state access locks prevent lookups
from stalling simple state access/write calls
diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
-index 2a4f8aa6697ed6144440970c9abaf9f6e1a2c2ce..8d86645ef264287d01203afd7bba516e78be5743 100644
+index 5e3bc0590e59770490b1c6c818d99be054214a8a..84bdf4a849b09a90da6c22f4080386e85a53f6b3 100644
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
@@ -62,6 +62,11 @@ public class GameProfileCache {
diff --git a/patches/server/0727-Log-when-the-async-catcher-is-tripped.patch b/patches/server/0708-Log-when-the-async-catcher-is-tripped.patch
index bede76faf6..b2c6aee8dc 100644
--- a/patches/server/0727-Log-when-the-async-catcher-is-tripped.patch
+++ b/patches/server/0708-Log-when-the-async-catcher-is-tripped.patch
@@ -7,12 +7,12 @@ The chunk system can swallow the exception given it's all
built with completablefuture, so ensure it is at least printed.
diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java
-index 7585a30e8f063ac2656b5de519b1e9edaceffbc7..0c41413ad32f8f6a094462fcd637dd3229abda45 100644
+index 5409f230fdd53b70fc03c58177438534731ad4e6..c02a04d284734b5f545b64307ed4aea337c1465f 100644
--- a/src/main/java/org/spigotmc/AsyncCatcher.java
+++ b/src/main/java/org/spigotmc/AsyncCatcher.java
@@ -12,6 +12,7 @@ public class AsyncCatcher
{
- if ( (AsyncCatcher.enabled || io.papermc.paper.util.TickThread.STRICT_THREAD_CHECKS) && Thread.currentThread() != MinecraftServer.getServer().serverThread ) // Paper
+ if ( !io.papermc.paper.util.TickThread.isTickThread() ) // Paper // Paper - rewrite chunk system
{
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); // Paper
throw new IllegalStateException( "Asynchronous " + reason + "!" );
diff --git a/patches/server/0728-Add-paper-mobcaps-and-paper-playermobcaps.patch b/patches/server/0709-Add-paper-mobcaps-and-paper-playermobcaps.patch
index 07f2b36a13..fc941b81b0 100644
--- a/patches/server/0728-Add-paper-mobcaps-and-paper-playermobcaps.patch
+++ b/patches/server/0709-Add-paper-mobcaps-and-paper-playermobcaps.patch
@@ -10,7 +10,7 @@ Also has a hover text on each mob category listing what entity types are
in said category
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
-index d2536f4ffae721f4df714b5345fa3329c3b8e3f5..60b0ce4557390ee7030efe4c90933402c57bab59 100644
+index a7b78508ef78229835805300e62a306a3f1ddf6d..724592234e2a178a518f6ab7d09c3180780371a7 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
@@ -5,6 +5,7 @@ import io.papermc.paper.command.subcommands.DumpItemCommand;
@@ -22,7 +22,7 @@ index d2536f4ffae721f4df714b5345fa3329c3b8e3f5..60b0ce4557390ee7030efe4c90933402
import io.papermc.paper.command.subcommands.SyncLoadInfoCommand;
import io.papermc.paper.command.subcommands.VersionCommand;
@@ -48,6 +49,7 @@ public final class PaperCommand extends Command {
- commands.put(Set.of("fixlight"), new FixLightCommand());
+ commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand());
commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
commands.put(Set.of("dumpitem"), new DumpItemCommand());
+ commands.put(Set.of("mobcaps", "playermobcaps"), new MobcapsCommand());
@@ -286,7 +286,7 @@ index fa23e9c476d4edc6176d8b8a6cb13c52d2f66a87..4150e8cd7197eac53042d56f0a53a495
// Paper start - add parameters and int ret type
spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index e7863390c8394a6afcfa25099b4ce49c5e010f13..4adacf6f849fe41918690fb8f195727a9c880b53 100644
+index 7c5dd4a3bf98599356022539f203ef276c29cbab..2f793169ee3b8265059f75c5a3cc13a86acedc83 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2156,6 +2156,11 @@ public final class CraftServer implements Server {
@@ -302,10 +302,10 @@ index e7863390c8394a6afcfa25099b4ce49c5e010f13..4adacf6f849fe41918690fb8f195727a
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 38a74d683aec58a419af84111844a205596524b2..5138182d8004aec69848d10b8cc63453c1e8808f 100644
+index 5a3e498776a35770a19535751f9dfcc1573035d7..65c82c3ec11b29245f7d92e402f2cf2ab5b8dae4 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -1711,9 +1711,14 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1701,9 +1701,14 @@ public class CraftWorld extends CraftRegionAccessor implements World {
Validate.notNull(spawnCategory, "SpawnCategory cannot be null");
Validate.isTrue(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory." + spawnCategory + " are not supported.");
diff --git a/patches/server/0730-Sanitize-ResourceLocation-error-logging.patch b/patches/server/0710-Sanitize-ResourceLocation-error-logging.patch
index c2ff3a02e4..c2ff3a02e4 100644
--- a/patches/server/0730-Sanitize-ResourceLocation-error-logging.patch
+++ b/patches/server/0710-Sanitize-ResourceLocation-error-logging.patch
diff --git a/patches/server/0731-Allow-controlled-flushing-for-network-manager.patch b/patches/server/0711-Allow-controlled-flushing-for-network-manager.patch
index cbd07781ef..a88de7918e 100644
--- a/patches/server/0731-Allow-controlled-flushing-for-network-manager.patch
+++ b/patches/server/0711-Allow-controlled-flushing-for-network-manager.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Sat, 4 Apr 2020 15:27:44 -0700
Subject: [PATCH] Allow controlled flushing for network manager
@@ -9,10 +9,10 @@ This patch will be used to optimise out flush calls in later
patches.
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index b5f884d6671823085a2ab0e8da2d30afd2928f32..057a0be81b12bd8a4ac71106dc8ada91bd4c9bfd 100644
+index 2f38d04b369b345c89f85cee32081df8baf4239f..13ab14b1fb3acfd245fbab35f84f5c30c97ed855 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -99,6 +99,39 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -121,6 +121,39 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public ConnectionProtocol protocol;
// Paper end
@@ -52,7 +52,7 @@ index b5f884d6671823085a2ab0e8da2d30afd2928f32..057a0be81b12bd8a4ac71106dc8ada91
public Connection(PacketFlow side) {
this.receiving = side;
}
-@@ -264,7 +297,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -286,7 +319,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
net.minecraft.server.MCUtil.isMainThread() && packet.isReady() && this.queue.isEmpty() &&
(packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())
))) {
@@ -61,7 +61,7 @@ index b5f884d6671823085a2ab0e8da2d30afd2928f32..057a0be81b12bd8a4ac71106dc8ada91
return;
}
// write the packets to the queue, then flush - antixray hooks there already
-@@ -288,6 +321,14 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -310,6 +343,14 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
private void sendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks) {
@@ -76,7 +76,7 @@ index b5f884d6671823085a2ab0e8da2d30afd2928f32..057a0be81b12bd8a4ac71106dc8ada91
ConnectionProtocol enumprotocol = ConnectionProtocol.getProtocolForPacket(packet);
ConnectionProtocol enumprotocol1 = this.getCurrentProtocol();
-@@ -298,16 +339,21 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -320,16 +361,21 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
if (this.channel.eventLoop().inEventLoop()) {
@@ -100,7 +100,7 @@ index b5f884d6671823085a2ab0e8da2d30afd2928f32..057a0be81b12bd8a4ac71106dc8ada91
if (packetState != currentState) {
this.setProtocol(packetState);
}
-@@ -321,7 +367,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -343,7 +389,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
try {
// Paper end
@@ -109,9 +109,9 @@ index b5f884d6671823085a2ab0e8da2d30afd2928f32..057a0be81b12bd8a4ac71106dc8ada91
if (callbacks != null) {
channelfuture.addListener((future) -> {
-@@ -376,6 +422,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
- }
+@@ -399,6 +445,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
private boolean processQueue() {
+ try { // Paper - add pending task queue
if (this.queue.isEmpty()) return true;
+ // Paper start - make only one flush call per sendPacketQueue() call
+ final boolean needsFlush = this.canFlush;
@@ -120,7 +120,7 @@ index b5f884d6671823085a2ab0e8da2d30afd2928f32..057a0be81b12bd8a4ac71106dc8ada91
// If we are on main, we are safe here in that nothing else should be processing queue off main anymore
// But if we are not on main due to login/status, the parent is synchronized on packetQueue
java.util.Iterator<PacketHolder> iterator = this.queue.iterator();
-@@ -383,16 +433,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -406,16 +456,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
PacketHolder queued = iterator.next(); // poll -> peek
// Fix NPE (Spigot bug caused by handleDisconnection())
diff --git a/patches/server/0732-Optimise-general-POI-access.patch b/patches/server/0712-Optimise-general-POI-access.patch
index 621895ffce..482e6baf50 100644
--- a/patches/server/0732-Optimise-general-POI-access.patch
+++ b/patches/server/0712-Optimise-general-POI-access.patch
@@ -876,19 +876,10 @@ index 33fbf72b440e0d164ecd4fb0fdec72e2394d0a1e..8db20db72cd51046213625fac46c3585
BlockPos blockPos = path.getTarget();
Optional<Holder<PoiType>> optional = poiManager.getType(blockPos);
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
-index ab9bb440c8e91ecb49c1e14a427d35087a87ac80..497a81e49d54380713c18523ae8f09f94c453721 100644
+index 8938e04ffab2d31e64b80879d21d9f1eb90c20bb..fea4dd3fe06e66f72969331fdf9491dbbe44f3c2 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java
-@@ -40,7 +40,7 @@ public class PoiManager extends SectionStorage<PoiSection> {
- public static final int VILLAGE_SECTION_SIZE = 1;
- private final PoiManager.DistanceTracker distanceTracker;
- private final LongSet loadedChunks = new LongOpenHashSet();
-- private final net.minecraft.server.level.ServerLevel world; // Paper
-+ public final net.minecraft.server.level.ServerLevel world; // Paper // Paper public
-
- public PoiManager(Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) {
- super(path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world);
-@@ -113,43 +113,62 @@ public class PoiManager extends SectionStorage<PoiSection> {
+@@ -127,43 +127,62 @@ public class PoiManager extends SectionStorage<PoiSection> {
}
public Optional<BlockPos> find(Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int radius, PoiManager.Occupancy occupationStatus) {
@@ -971,7 +962,7 @@ index ab9bb440c8e91ecb49c1e14a427d35087a87ac80..497a81e49d54380713c18523ae8f09f9
public boolean release(BlockPos pos) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
-index bb2be6eea7a0cff4cc70bd43738b1ce213e43558..b71a4027a0eed467a3707c59315092ddecfd6bf3 100644
+index d0ce7b14d29459e276961c38cfc5b5da1cd15634..2e410b21564a067ed04f4179908fba8389062f4c 100644
--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiSection.java
@@ -26,7 +26,7 @@ import org.slf4j.Logger;
@@ -982,9 +973,9 @@ index bb2be6eea7a0cff4cc70bd43738b1ce213e43558..b71a4027a0eed467a3707c59315092dd
+ private final Map<Holder<PoiType>, Set<PoiRecord>> byType = Maps.newHashMap(); public final Map<Holder<PoiType>, Set<PoiRecord>> getData() { return this.byType; } // Paper - public accessor
private final Runnable setDirty;
private boolean isValid;
-
+ public final Optional<PoiSection> noAllocateOptional = Optional.of(this); // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
-index 4dcfffe2e1c5263c3d1bd096d57d090c1e4b0523..bb59cff9ba570923a40c1612d5812a64390454ee 100644
+index c886d461304ad19483eb92a25f5827a95b8cba98..1a0095b9dee1f708c4c3d32fd95b88120245f1e9 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java
@@ -71,11 +71,11 @@ public class SectionStorage<R> extends RegionFileStorage implements AutoCloseabl
diff --git a/patches/server/0735-Optimise-chunk-tick-iteration.patch b/patches/server/0713-Optimise-chunk-tick-iteration.patch
index dff38c5fda..4d574dc627 100644
--- a/patches/server/0735-Optimise-chunk-tick-iteration.patch
+++ b/patches/server/0713-Optimise-chunk-tick-iteration.patch
@@ -1,15 +1,15 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Thu, 7 May 2020 05:48:54 -0700
Subject: [PATCH] Optimise chunk tick iteration
Use a dedicated list of entity ticking chunks to reduce the cost
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-index a2b5f6457b08e4e02544dc71fbf383b5a67a2d69..538f21e6bc29c0307441fe4899dc7f600d2d1a04 100644
+index 2add24517d38708a84e7f8ec25fbe9c62309375e..1fb298ff60b59a7074fb9d7a79709f05887ce32c 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-@@ -84,6 +84,11 @@ public class ChunkHolder {
+@@ -88,6 +88,11 @@ public class ChunkHolder {
this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
// Paper end - optimise anyPlayerCloseEnoughForSpawning
@@ -21,7 +21,7 @@ index a2b5f6457b08e4e02544dc71fbf383b5a67a2d69..538f21e6bc29c0307441fe4899dc7f60
}
public void onChunkRemove() {
-@@ -91,6 +96,11 @@ public class ChunkHolder {
+@@ -95,6 +100,11 @@ public class ChunkHolder {
this.playersInMobSpawnRange = null;
this.playersInChunkTickRange = null;
// Paper end - optimise anyPlayerCloseEnoughForSpawning
@@ -33,7 +33,7 @@ index a2b5f6457b08e4e02544dc71fbf383b5a67a2d69..538f21e6bc29c0307441fe4899dc7f60
}
// Paper end
-@@ -258,7 +268,7 @@ public class ChunkHolder {
+@@ -210,7 +220,7 @@ public class ChunkHolder {
if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
if (this.changedBlocksPerSection[i] == null) {
@@ -42,7 +42,7 @@ index a2b5f6457b08e4e02544dc71fbf383b5a67a2d69..538f21e6bc29c0307441fe4899dc7f60
this.changedBlocksPerSection[i] = new ShortOpenHashSet();
}
-@@ -281,6 +291,7 @@ public class ChunkHolder {
+@@ -234,6 +244,7 @@ public class ChunkHolder {
int k = this.lightEngine.getMaxLightSection();
if (y >= j && y <= k) {
@@ -50,7 +50,7 @@ index a2b5f6457b08e4e02544dc71fbf383b5a67a2d69..538f21e6bc29c0307441fe4899dc7f60
int l = y - j;
if (lightType == LightLayer.SKY) {
-@@ -295,8 +306,19 @@ public class ChunkHolder {
+@@ -248,8 +259,19 @@ public class ChunkHolder {
}
}
@@ -72,19 +72,28 @@ index a2b5f6457b08e4e02544dc71fbf383b5a67a2d69..538f21e6bc29c0307441fe4899dc7f60
int i = 0;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index b34c90497a5492c289839ba74df9f2f27e29370e..e811c7d617b8c9cc684bc0a58a98d5ecfe11db02 100644
+index 37129601dfb8f2af897fb095786616b07e9acdd2..c5a76a6d589133f0f23a2b3b421399acaa7ceb8c 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -162,6 +162,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -110,6 +110,8 @@ import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
+ import org.bukkit.entity.Player;
+ // CraftBukkit end
+
++import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
++
+ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider {
+
+ private static final byte CHUNK_TYPE_REPLACEABLE = -1;
+@@ -147,6 +149,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
private final Queue<Runnable> unloadQueue;
int viewDistance;
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
+ public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
- // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
- public final CallbackExecutor callbackExecutor = new CallbackExecutor();
+ // Paper - rewrite chunk system
+
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 2390fcbc1b21653b1753a58da33f936cec43d0cb..7b1279256ed7963ba4e225b15592816087ab16b4 100644
+index fa7801158b68eaa12d6322c9c0def9de37f03814..cfcb3ba96569f3eb24d1035d770c50085f60b772 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -47,6 +47,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
@@ -95,7 +104,7 @@ index 2390fcbc1b21653b1753a58da33f936cec43d0cb..7b1279256ed7963ba4e225b155928160
public class ServerChunkCache extends ChunkSource {
-@@ -810,34 +811,42 @@ public class ServerChunkCache extends ChunkSource {
+@@ -723,42 +724,59 @@ public class ServerChunkCache extends ChunkSource {
this.lastSpawnState = spawnercreature_d;
gameprofilerfiller.popPush("filteringLoadedChunks");
@@ -147,14 +156,15 @@ index 2390fcbc1b21653b1753a58da33f936cec43d0cb..7b1279256ed7963ba4e225b155928160
ChunkPos chunkcoordintpair = chunk1.getPos();
- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
-+ if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
++ if ((true || this.level.isNaturalSpawningAllowed(chunkcoordintpair)) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning // Paper - the chunk is known ticking
chunk1.incrementInhabitedTime(j);
- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning
+ if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning & optimise chunk tick iteration
NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
}
-@@ -845,7 +854,16 @@ public class ServerChunkCache extends ChunkSource {
+- if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
++ if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking
this.level.tickChunk(chunk1, k);
}
}
@@ -171,7 +181,7 @@ index 2390fcbc1b21653b1753a58da33f936cec43d0cb..7b1279256ed7963ba4e225b155928160
this.level.timings.chunkTicks.stopTiming(); // Paper
gameprofilerfiller.popPush("customSpawners");
if (flag2) {
-@@ -853,15 +871,24 @@ public class ServerChunkCache extends ChunkSource {
+@@ -766,15 +784,24 @@ public class ServerChunkCache extends ChunkSource {
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
} // Paper - timings
}
diff --git a/patches/server/0736-Execute-chunk-tasks-mid-tick.patch b/patches/server/0714-Execute-chunk-tasks-mid-tick.patch
index c2e8035538..974b755ba3 100644
--- a/patches/server/0736-Execute-chunk-tasks-mid-tick.patch
+++ b/patches/server/0714-Execute-chunk-tasks-mid-tick.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 6 Apr 2020 04:20:44 -0700
Subject: [PATCH] Execute chunk tasks mid-tick
@@ -19,10 +19,10 @@ index 23e564b05ba438924180c91f9b19a60731eedd1b..5ec241d49ff5e3a161a39006f05823a5
private MinecraftTimings() {}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 98fe4165d291b47a39ce741884353c87dd0a4789..99073ea2757cd1c15b098d7cfaf8681702f04a19 100644
+index 79c4ee2dd842fcf29eb91a69e536960c814c1a0d..207c1fb92493757de90f1a583a66ae4ad18b0e3f 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1304,6 +1304,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1298,6 +1298,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private boolean pollTaskInternal() {
if (super.pollTask()) {
@@ -30,7 +30,7 @@ index 98fe4165d291b47a39ce741884353c87dd0a4789..99073ea2757cd1c15b098d7cfaf86817
return true;
} else {
if (this.haveTime()) {
-@@ -2675,4 +2676,74 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2669,4 +2670,74 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
// Paper end
@@ -106,10 +106,10 @@ index 98fe4165d291b47a39ce741884353c87dd0a4789..99073ea2757cd1c15b098d7cfaf86817
+ // Paper end - execute chunk tasks mid tick
}
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 7b1279256ed7963ba4e225b15592816087ab16b4..1510e1fcfbbb2ccbaf57dd274bed05afc85eeeb0 100644
+index cfcb3ba96569f3eb24d1035d770c50085f60b772..89ecb5c6c25246d0c71b14cc089386d4c4b83093 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -835,6 +835,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -748,6 +748,7 @@ public class ServerChunkCache extends ChunkSource {
iterator1 = shuffled.iterator();
}
@@ -117,16 +117,16 @@ index 7b1279256ed7963ba4e225b15592816087ab16b4..1510e1fcfbbb2ccbaf57dd274bed05af
try {
while (iterator1.hasNext()) {
LevelChunk chunk1 = iterator1.next();
-@@ -852,6 +853,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -765,6 +766,7 @@ public class ServerChunkCache extends ChunkSource {
- if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
+ if (true || this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) { // Paper - the chunk is known ticking
this.level.tickChunk(chunk1, k);
+ if ((chunksTicked++ & 1) == 0) net.minecraft.server.MinecraftServer.getServer().executeMidTickTasks(); // Paper
}
}
// Paper start - optimise chunk tick iteration
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 29b80d074600fa7e20b05d1fe70ac12969b954a4..7324f2ad437a15c42f84ba2deeb58861c0552209 100644
+index 4fa19370e05600391e60b9b416f343834362cbac..b4b7aa2f7d602fe996ebc320ab9641866b672abe 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -212,6 +212,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
@@ -137,7 +137,7 @@ index 29b80d074600fa7e20b05d1fe70ac12969b954a4..7324f2ad437a15c42f84ba2deeb58861
// CraftBukkit start
public final LevelStorageSource.LevelStorageAccess convertable;
-@@ -991,6 +992,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1024,6 +1025,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
if (fluid1.is(fluid)) {
fluid1.tick(this, pos);
}
@@ -145,7 +145,7 @@ index 29b80d074600fa7e20b05d1fe70ac12969b954a4..7324f2ad437a15c42f84ba2deeb58861
}
-@@ -1000,6 +1002,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1033,6 +1035,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
if (iblockdata.is(block)) {
iblockdata.tick(this, pos, this.random);
}
@@ -154,10 +154,10 @@ index 29b80d074600fa7e20b05d1fe70ac12969b954a4..7324f2ad437a15c42f84ba2deeb58861
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index a04517137ab9deff215b6f9b9ee405500af0e393..90b3ca30a5ab381acde66393eb1fed5be677e5c8 100644
+index e30414e1a5e7907c59917c5cd182bca65bc65825..4a3cfc66fdbff749f098a384c16a2c7cfc72e294 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -800,6 +800,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -804,6 +804,11 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
// Spigot end
} else if (this.shouldTickBlocksAt(tickingblockentity.getPos())) {
tickingblockentity.tick();
@@ -169,7 +169,7 @@ index a04517137ab9deff215b6f9b9ee405500af0e393..90b3ca30a5ab381acde66393eb1fed5b
}
}
this.blockEntityTickers.removeAll(toRemove);
-@@ -814,6 +819,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -818,6 +823,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public <T extends Entity> void guardEntityTick(Consumer<T> tickConsumer, T entity) {
try {
tickConsumer.accept(entity);
diff --git a/patches/server/0737-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch b/patches/server/0715-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
index c448b0249c..cea19c1410 100644
--- a/patches/server/0737-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
+++ b/patches/server/0715-Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
@@ -10,7 +10,7 @@ hoping that at least then we don't swap chunks, and maybe recover
them all.
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 12e3831324abdc1112cbe65cab0f0c75ce77a9ef..be9c15fe141ede1132dbe07ba4bfcf22036ab194 100644
+index 31e552e1b4c3a6931a61a88a75965a0427d6de8d..0254b71ab9af6d74640aa5dc0533afa386e5f57f 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
@@ -68,6 +68,18 @@ import net.minecraft.world.ticks.ProtoChunkTicks;
@@ -32,7 +32,7 @@ index 12e3831324abdc1112cbe65cab0f0c75ce77a9ef..be9c15fe141ede1132dbe07ba4bfcf22
public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null); // Paper - Anti-Xray - Add preset block states
private static final Logger LOGGER = LogUtils.getLogger();
-@@ -473,7 +485,7 @@ public class ChunkSerializer {
+@@ -480,7 +492,7 @@ public class ChunkSerializer {
nbttagcompound.putInt("xPos", chunkcoordintpair.x);
nbttagcompound.putInt("yPos", chunk.getMinSection());
nbttagcompound.putInt("zPos", chunkcoordintpair.z);
@@ -42,7 +42,7 @@ index 12e3831324abdc1112cbe65cab0f0c75ce77a9ef..be9c15fe141ede1132dbe07ba4bfcf22
nbttagcompound.putString("Status", chunk.getStatus().getName());
BlendingData blendingdata = chunk.getBlendingData();
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-index 315be30daf0be84efbb4d634dc01e1bf9e6e696e..3010e092bd1337e5a6b99636bbd312d90e470acf 100644
+index aa915195d4aab3e931a92bf844f6dc18a0a59b9e..84ad2f6dfe33d747453365656c1a0114ea6d2a54 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -41,7 +41,7 @@ public class ChunkStorage implements AutoCloseable {
@@ -685,7 +685,7 @@ index 861a25a15f1aab20e3245b6d5cdad5d23bdfd6d0..8ff8855c5267379b3a5f5d8baa4a275f
return bytebuffer;
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index 666286f94f09299f95538ef2f19b588eb74d9dda..f38ec8914e1953091ab65aa3aaefc886d3eede8a 100644
+index c881d15efa94fc379ed2817a534fef3a4dd3d90d..e71a451a6b385716cc46db7350b58b4192304547 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -26,7 +26,15 @@ public class RegionFileStorage implements AutoCloseable {
@@ -694,7 +694,7 @@ index 666286f94f09299f95538ef2f19b588eb74d9dda..f38ec8914e1953091ab65aa3aaefc886
+ private final boolean isChunkData; // Paper
+
- RegionFileStorage(Path directory, boolean dsync) {
+ protected RegionFileStorage(Path directory, boolean dsync) { // Paper - protected constructor
+ // Paper start - add isChunkData param
+ this(directory, dsync, false);
+ }
diff --git a/patches/server/0738-Custom-table-implementation-for-blockstate-state-loo.patch b/patches/server/0716-Custom-table-implementation-for-blockstate-state-loo.patch
index 6cf6b743b6..6cf6b743b6 100644
--- a/patches/server/0738-Custom-table-implementation-for-blockstate-state-loo.patch
+++ b/patches/server/0716-Custom-table-implementation-for-blockstate-state-loo.patch
diff --git a/patches/server/0739-Detail-more-information-in-watchdog-dumps.patch b/patches/server/0717-Detail-more-information-in-watchdog-dumps.patch
index 0fc0b9475e..9adf6bbea8 100644
--- a/patches/server/0739-Detail-more-information-in-watchdog-dumps.patch
+++ b/patches/server/0717-Detail-more-information-in-watchdog-dumps.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Thu, 26 Mar 2020 21:59:32 -0700
Subject: [PATCH] Detail more information in watchdog dumps
@@ -7,10 +7,10 @@ Subject: [PATCH] Detail more information in watchdog dumps
- Dump player name, player uuid, position, and world for packet handling
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 057a0be81b12bd8a4ac71106dc8ada91bd4c9bfd..98443474d1881bbeee8f249b89e44a0f84261be7 100644
+index 13ab14b1fb3acfd245fbab35f84f5c30c97ed855..9c0c181013d419d1a74f86b5d8cecf83b28925c6 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -475,9 +475,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -504,9 +504,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
PacketListener packetlistener = this.packetListener;
if (packetlistener instanceof TickablePacketListener) {
@@ -78,10 +78,10 @@ index acfa1907bfc9c29d261cfccc00d65bad9ad1a002..d6f3869f5725c7f081efb7f486f74dbb
});
throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD;
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 7324f2ad437a15c42f84ba2deeb58861c0552209..92aafd475d58d1ef2e2736d9363c4b1010724fd7 100644
+index ade1478a6e85bad5a168e3ab8ea3b2744cf18dea..204d101c318cf60fd2e31b0d1b84f78f87d2f68d 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1006,7 +1006,26 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1039,7 +1039,26 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
@@ -108,7 +108,7 @@ index 7324f2ad437a15c42f84ba2deeb58861c0552209..92aafd475d58d1ef2e2736d9363c4b10
++TimingHistory.entityTicks; // Paper - timings
// Spigot start
co.aikar.timings.Timing timer; // Paper
-@@ -1046,7 +1065,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1079,7 +1098,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
this.tickPassenger(entity, entity1);
}
// } finally { timer.stopTiming(); } // Paper - timings - move up
@@ -124,10 +124,10 @@ index 7324f2ad437a15c42f84ba2deeb58861c0552209..92aafd475d58d1ef2e2736d9363c4b10
private void tickPassenger(Entity vehicle, Entity passenger) {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index f8458173dd717ca5fd9d265dcdaee0f0ef1a833c..51239bc47d6d2cfd8d345fb67fe0d92fabe53209 100644
+index bdfa91daf69e71e8c2e1e820f14360c567bdeb87..5e211f1df6739433437930116d134326abbf558d 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -971,7 +971,42 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -972,7 +972,42 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
return this.onGround;
}
@@ -170,7 +170,7 @@ index f8458173dd717ca5fd9d265dcdaee0f0ef1a833c..51239bc47d6d2cfd8d345fb67fe0d92f
if (this.noPhysics) {
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
} else {
-@@ -1144,6 +1179,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1145,6 +1180,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.level.getProfiler().pop();
}
}
@@ -184,7 +184,7 @@ index f8458173dd717ca5fd9d265dcdaee0f0ef1a833c..51239bc47d6d2cfd8d345fb67fe0d92f
}
protected boolean isHorizontalCollisionMinor(Vec3 adjustedMovement) {
-@@ -3962,7 +4004,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3973,7 +4015,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public void setDeltaMovement(Vec3 velocity) {
@@ -194,7 +194,7 @@ index f8458173dd717ca5fd9d265dcdaee0f0ef1a833c..51239bc47d6d2cfd8d345fb67fe0d92f
}
public void setDeltaMovement(double x, double y, double z) {
-@@ -4038,7 +4082,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -4055,7 +4099,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
// Paper end - fix MC-4
if (this.position.x != x || this.position.y != y || this.position.z != z) {
@@ -205,10 +205,10 @@ index f8458173dd717ca5fd9d265dcdaee0f0ef1a833c..51239bc47d6d2cfd8d345fb67fe0d92f
int j = Mth.floor(y);
int k = Mth.floor(z);
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
-index 7823e97add506d42161fd86f830e4d988b2113d8..d568fc92d03c313a782796cc720a1ebb1a5ad8be 100644
+index e0a71bfc1498a517456b21747ab6ef3f1067a3f3..e9fa7faaa4451e36b3908cbcbbe0baf213abde96 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
-@@ -22,6 +22,78 @@ public class WatchdogThread extends Thread
+@@ -22,6 +22,78 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
private volatile long lastTick;
private volatile boolean stopping;
@@ -287,11 +287,11 @@ index 7823e97add506d42161fd86f830e4d988b2113d8..d568fc92d03c313a782796cc720a1ebb
private WatchdogThread(long timeoutTime, boolean restart)
{
super( "Paper Watchdog Thread" );
-@@ -120,6 +192,7 @@ public class WatchdogThread extends Thread
+@@ -120,6 +192,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
log.log( Level.SEVERE, "------------------------------" );
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
- com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
+ io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(isLongTimeout); // Paper // Paper - rewrite chunk system
+ this.dumpTickingInfo(); // Paper - log detailed tick information
- WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( server.serverThread.getId(), Integer.MAX_VALUE ), log );
+ WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
log.log( Level.SEVERE, "------------------------------" );
//
diff --git a/patches/server/0740-Manually-inline-methods-in-BlockPosition.patch b/patches/server/0718-Manually-inline-methods-in-BlockPosition.patch
index 0cab0f9b56..f8ac951367 100644
--- a/patches/server/0740-Manually-inline-methods-in-BlockPosition.patch
+++ b/patches/server/0718-Manually-inline-methods-in-BlockPosition.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 6 Jul 2020 22:48:48 -0700
Subject: [PATCH] Manually inline methods in BlockPosition
diff --git a/patches/server/0741-Distance-manager-tick-timings.patch b/patches/server/0719-Distance-manager-tick-timings.patch
index dd5f5f23b7..0fd96880f3 100644
--- a/patches/server/0741-Distance-manager-tick-timings.patch
+++ b/patches/server/0719-Distance-manager-tick-timings.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Sat, 18 Jul 2020 16:03:57 -0700
Subject: [PATCH] Distance manager tick timings
@@ -18,23 +18,17 @@ index 5ec241d49ff5e3a161a39006f05823a5de847c5e..435b3b6d05e00803386d123c66f961c9
public static final Timing midTickChunkTasks = Timings.ofSafe("Mid Tick Chunk Tasks");
-diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 1510e1fcfbbb2ccbaf57dd274bed05afc85eeeb0..98e279e5330b45d0587d1afebddaf876e9802e33 100644
---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -647,6 +647,7 @@ public class ServerChunkCache extends ChunkSource {
- public boolean runDistanceManagerUpdates() {
- if (distanceManager.delayDistanceManagerTick) return false; // Paper - Chunk priority
- if (this.chunkMap.unloadingPlayerChunk) { LOGGER.error("Cannot tick distance manager while unloading playerchunks", new Throwable()); throw new IllegalStateException("Cannot tick distance manager while unloading playerchunks"); } // Paper
-+ co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager
- boolean flag = this.distanceManager.runAllUpdates(this.chunkMap);
- boolean flag1 = this.chunkMap.promoteChunkMap();
+diff --git a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
+index e28e083373261ab9fa3b015e294ee9f81b4d2f6e..192b52cb3205ea6bbf45bde9bc50324ad9815463 100644
+--- a/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
++++ b/src/main/java/io/papermc/paper/chunk/system/scheduling/ChunkHolderManager.java
+@@ -901,7 +901,9 @@ public final class ChunkHolderManager {
+ }
-@@ -656,6 +657,7 @@ public class ServerChunkCache extends ChunkSource {
- this.clearCache();
- return true;
- }
+ public boolean processTicketUpdates() {
++ co.aikar.timings.MinecraftTimings.distanceManagerTick.startTiming(); try { // Paper - add timings for distance manager
+ return this.processTicketUpdates(true, true, null);
+ } finally { co.aikar.timings.MinecraftTimings.distanceManagerTick.stopTiming(); } // Paper - add timings for distance manager
}
- // Paper start
+ private static final ThreadLocal<List<ChunkProgressionTask>> CURRENT_TICKET_UPDATE_SCHEDULING = new ThreadLocal<>();
diff --git a/patches/server/0742-Name-craft-scheduler-threads-according-to-the-plugin.patch b/patches/server/0720-Name-craft-scheduler-threads-according-to-the-plugin.patch
index 7d081ee195..e2e0d713f0 100644
--- a/patches/server/0742-Name-craft-scheduler-threads-according-to-the-plugin.patch
+++ b/patches/server/0720-Name-craft-scheduler-threads-according-to-the-plugin.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Sun, 19 Jul 2020 15:17:01 -0700
Subject: [PATCH] Name craft scheduler threads according to the plugin using
them
diff --git a/patches/server/0743-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch b/patches/server/0721-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
index 8f16f744ad..07e018b6ef 100644
--- a/patches/server/0743-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
+++ b/patches/server/0721-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Sun, 20 Sep 2020 16:10:49 -0700
Subject: [PATCH] Make sure inlined getChunkAt has inlined logic for loaded
chunks
@@ -13,10 +13,10 @@ Paper recently reverted this optimisation, so it's been reintroduced
here.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 90b3ca30a5ab381acde66393eb1fed5be677e5c8..8eabdd921cc976d9a1b9d3e8c315eeb22de3a968 100644
+index 4a3cfc66fdbff749f098a384c16a2c7cfc72e294..b94af31814c251a9b4b575c206de9807df696c62 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -368,6 +368,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -367,6 +367,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@Override
public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline
diff --git a/patches/server/0744-Add-packet-limiter-config.patch b/patches/server/0722-Add-packet-limiter-config.patch
index 8aa45e5f93..3deb55309a 100644
--- a/patches/server/0744-Add-packet-limiter-config.patch
+++ b/patches/server/0722-Add-packet-limiter-config.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Fri, 30 Oct 2020 22:37:16 -0700
Subject: [PATCH] Add packet limiter config
@@ -24,10 +24,10 @@ and an action can be defined: DROP or KICK
If interval or rate are less-than 0, the limit is ignored
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 98443474d1881bbeee8f249b89e44a0f84261be7..51217798bfd549483ce456b44d14089f35642c55 100644
+index 9c0c181013d419d1a74f86b5d8cecf83b28925c6..335d9e5f11ad3469f3a0310782e41283973f5a5f 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -131,6 +131,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -153,6 +153,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
}
// Paper end - allow controlled flushing
@@ -50,7 +50,7 @@ index 98443474d1881bbeee8f249b89e44a0f84261be7..51217798bfd549483ce456b44d14089f
public Connection(PacketFlow side) {
this.receiving = side;
-@@ -211,6 +227,45 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -233,6 +249,45 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
if (this.channel.isOpen()) {
diff --git a/patches/server/0745-Use-correct-LevelStem-registry-when-loading-default-.patch b/patches/server/0723-Use-correct-LevelStem-registry-when-loading-default-.patch
index acaf871233..7b971869b8 100644
--- a/patches/server/0745-Use-correct-LevelStem-registry-when-loading-default-.patch
+++ b/patches/server/0723-Use-correct-LevelStem-registry-when-loading-default-.patch
@@ -24,7 +24,7 @@ index 8da1226a6c293abb038d10c7921a77ed71ad06cc..f958f0ae738a6fb26400e17e54c8d69e
} else {
Holder<E> holder = registry.getOrCreateHolderOrThrow(entryKey);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 99073ea2757cd1c15b098d7cfaf8681702f04a19..857af8ed37c10723d6cd81d7f3c2ba6169021050 100644
+index 8e4b1d9d84c41b6f3b599c85e551f9f98025c917..9011dbbe6302deb7318d31b9db3d2419a1871c07 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -553,7 +553,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0746-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch b/patches/server/0724-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch
index 7f656dff77..112bd2a090 100644
--- a/patches/server/0746-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch
+++ b/patches/server/0724-Don-t-read-neighbour-chunk-data-off-disk-when-conver.patch
@@ -8,7 +8,7 @@ Lighting is purged on update anyways, so let's not add more
into the conversion process
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-index 3010e092bd1337e5a6b99636bbd312d90e470acf..fee9a8e74bfcc94942991b56799debf67b551f43 100644
+index 84ad2f6dfe33d747453365656c1a0114ea6d2a54..6e9af1401918995e3c268eea7d4a74af94707242 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -51,6 +51,7 @@ public class ChunkStorage implements AutoCloseable {
diff --git a/patches/server/0747-Consolidate-flush-calls-for-entity-tracker-packets.patch b/patches/server/0725-Consolidate-flush-calls-for-entity-tracker-packets.patch
index 5b23522f42..486fcc284b 100644
--- a/patches/server/0747-Consolidate-flush-calls-for-entity-tracker-packets.patch
+++ b/patches/server/0725-Consolidate-flush-calls-for-entity-tracker-packets.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Sat, 4 Apr 2020 17:00:20 -0700
Subject: [PATCH] Consolidate flush calls for entity tracker packets
@@ -22,10 +22,10 @@ With this change I could get all 200 on at 0ms ping.
So in general this patch should reduce Netty I/O thread load.
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index 98e279e5330b45d0587d1afebddaf876e9802e33..4c82f17313e18c9dfd9b28653715b8a3242b826c 100644
+index 89ecb5c6c25246d0c71b14cc089386d4c4b83093..6d1f929eb717f62f0d7ebb9e9b52c3788061e240 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -893,7 +893,24 @@ public class ServerChunkCache extends ChunkSource {
+@@ -804,7 +804,24 @@ public class ServerChunkCache extends ChunkSource {
this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing
gameprofilerfiller.pop();
// Paper end - use set of chunks requiring updates, rather than iterating every single one loaded
diff --git a/patches/server/0748-Don-t-lookup-fluid-state-when-raytracing.patch b/patches/server/0726-Don-t-lookup-fluid-state-when-raytracing.patch
index eef7de0e32..e16e510297 100644
--- a/patches/server/0748-Don-t-lookup-fluid-state-when-raytracing.patch
+++ b/patches/server/0726-Don-t-lookup-fluid-state-when-raytracing.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Fri, 28 Aug 2020 12:33:47 -0700
Subject: [PATCH] Don't lookup fluid state when raytracing
diff --git a/patches/server/0749-Time-scoreboard-search.patch b/patches/server/0727-Time-scoreboard-search.patch
index d1b1fd45ef..abfffa772e 100644
--- a/patches/server/0749-Time-scoreboard-search.patch
+++ b/patches/server/0727-Time-scoreboard-search.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Tue, 21 Apr 2020 01:53:22 -0700
Subject: [PATCH] Time scoreboard search
diff --git a/patches/server/0750-Send-full-pos-packets-for-hard-colliding-entities.patch b/patches/server/0728-Send-full-pos-packets-for-hard-colliding-entities.patch
index b63f55bd81..b63f55bd81 100644
--- a/patches/server/0750-Send-full-pos-packets-for-hard-colliding-entities.patch
+++ b/patches/server/0728-Send-full-pos-packets-for-hard-colliding-entities.patch
diff --git a/patches/server/0751-Do-not-run-raytrace-logic-for-AIR.patch b/patches/server/0729-Do-not-run-raytrace-logic-for-AIR.patch
index baedf55a9d..baedf55a9d 100644
--- a/patches/server/0751-Do-not-run-raytrace-logic-for-AIR.patch
+++ b/patches/server/0729-Do-not-run-raytrace-logic-for-AIR.patch
diff --git a/patches/server/0752-Oprimise-map-impl-for-tracked-players.patch b/patches/server/0730-Oprimise-map-impl-for-tracked-players.patch
index e1ee820045..0b358c261e 100644
--- a/patches/server/0752-Oprimise-map-impl-for-tracked-players.patch
+++ b/patches/server/0730-Oprimise-map-impl-for-tracked-players.patch
@@ -7,18 +7,10 @@ Reference2BooleanOpenHashMap is going to have
better lookups than HashMap.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index e811c7d617b8c9cc684bc0a58a98d5ecfe11db02..f47e2d4a7343f3e3ee68f36688720cedc58861dc 100644
+index 3540ac6f99d724d91ff4e897f7e84ef03037f1bf..57b45ce5feaa5015b5468a0b44f354e96d7d95fc 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -110,6 +110,7 @@ import org.slf4j.Logger;
- import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
- import org.bukkit.entity.Player;
- // CraftBukkit end
-+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper
-
- public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider {
-
-@@ -2212,7 +2213,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1363,7 +1363,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final Entity entity;
private final int range;
SectionPos lastSectionPos;
diff --git a/patches/server/0753-Optimise-BlockSoil-nearby-water-lookup.patch b/patches/server/0731-Optimise-BlockSoil-nearby-water-lookup.patch
index bb523f58c5..bb523f58c5 100644
--- a/patches/server/0753-Optimise-BlockSoil-nearby-water-lookup.patch
+++ b/patches/server/0731-Optimise-BlockSoil-nearby-water-lookup.patch
diff --git a/patches/server/0755-Optimise-random-block-ticking.patch b/patches/server/0732-Optimise-random-block-ticking.patch
index 6573b51a15..53a23a8916 100644
--- a/patches/server/0755-Optimise-random-block-ticking.patch
+++ b/patches/server/0732-Optimise-random-block-ticking.patch
@@ -90,10 +90,10 @@ index 0000000000000000000000000000000000000000..7d93652c1abbb6aee6eb7c26cf35d4d0
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 92aafd475d58d1ef2e2736d9363c4b1010724fd7..bcd1b2534af33e7a9d184e0ea4c9c0a4b58dacc8 100644
+index 204d101c318cf60fd2e31b0d1b84f78f87d2f68d..6aea2e80aab7d2950c057e8523a954c71a05eb0a 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -670,6 +670,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -703,6 +703,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
entityplayer.stopSleepInBed(false, false);
});
}
@@ -104,7 +104,7 @@ index 92aafd475d58d1ef2e2736d9363c4b1010724fd7..bcd1b2534af33e7a9d184e0ea4c9c0a4
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
ChunkPos chunkcoordintpair = chunk.getPos();
-@@ -679,10 +683,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -712,10 +716,10 @@ public class ServerLevel extends Level implements WorldGenLevel {
ProfilerFiller gameprofilerfiller = this.getProfiler();
gameprofilerfiller.push("thunder");
@@ -117,7 +117,7 @@ index 92aafd475d58d1ef2e2736d9363c4b1010724fd7..bcd1b2534af33e7a9d184e0ea4c9c0a4
if (this.isRainingAt(blockposition)) {
DifficultyInstance difficultydamagescaler = this.getCurrentDifficultyAt(blockposition);
boolean flag1 = this.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && this.random.nextDouble() < (double) difficultydamagescaler.getEffectiveDifficulty() * this.paperConfig().entities.spawning.skeletonHorseThunderSpawnChance.or(0.01D) && !this.getBlockState(blockposition.below()).is(Blocks.LIGHTNING_ROD); // Paper
-@@ -706,64 +710,75 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -739,64 +743,75 @@ public class ServerLevel extends Level implements WorldGenLevel {
gameprofilerfiller.popPush("iceandsnow");
if (!this.paperConfig().environment.disableIceAndSnow && this.random.nextInt(16) == 0) { // Paper - Disable ice and snow
@@ -312,10 +312,10 @@ index 69c98c2cb2fd8f149a39bbddcbfe0c5c5adc3904..5575730aa6f77a91467c394fa8465c33
public BlockPos getHomePos() {
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 8eabdd921cc976d9a1b9d3e8c315eeb22de3a968..76a2d00324a85419548005e0aa3cbd8b891b8257 100644
+index b94af31814c251a9b4b575c206de9807df696c62..aa90454e70e5c25eb331ceb212df3128d64b1567 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -1302,10 +1302,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -1306,10 +1306,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public abstract RecipeManager getRecipeManager();
public BlockPos getBlockRandomPos(int x, int y, int z, int l) {
@@ -429,7 +429,7 @@ index 066874d27495dcaa3dea254b7328257e46920357..c3f1334b2bb97f0633f3ea43b97ee49a
public PalettedContainer<BlockState> getStates() {
diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
-index d93118b7a602ceb6ef11ddabbce1d13fb8029a44..5ebde3a4f99b8d017d9a10a30fefc0b7dd011319 100644
+index 9e652cf68b289696b5aee53ac157fc52cca10d2c..7908360dd47937b2cb702e381802b7b278a5198e 100644
--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java
@@ -383,6 +383,14 @@ public class PalettedContainer<T> implements PaletteResize<T>, PalettedContainer
diff --git a/patches/server/0733-Add-more-async-catchers.patch b/patches/server/0733-Add-more-async-catchers.patch
deleted file mode 100644
index c455647bca..0000000000
--- a/patches/server/0733-Add-more-async-catchers.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Thu, 15 Jul 2021 01:41:53 -0700
-Subject: [PATCH] Add more async catchers
-
-
-diff --git a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
-index 2830d32bba3dc85847e3a5d9b4d98f822e34b606..a176a886235494fdc722030a93658d361bf50f03 100644
---- a/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
-+++ b/src/main/java/net/minecraft/world/level/entity/EntityTickList.java
-@@ -29,11 +29,13 @@ public class EntityTickList {
- }
-
- public void add(Entity entity) {
-+ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist addition"); // Paper
- this.ensureActiveIsNotIterated();
- this.active.put(entity.getId(), entity);
- }
-
- public void remove(Entity entity) {
-+ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist removal"); // Paper
- this.ensureActiveIsNotIterated();
- this.active.remove(entity.getId());
- }
-@@ -43,6 +45,7 @@ public class EntityTickList {
- }
-
- public void forEach(Consumer<Entity> action) {
-+ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous entity ticklist iteration"); // Paper
- if (this.iterated != null) {
- throw new UnsupportedOperationException("Only one concurrent iteration supported");
- } else {
-diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-index a5dc8e715c86c1e70a9cf3d99c9cd457a6666b70..a1a52669c19af22e3b5267d43584cb00d1646453 100644
---- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-@@ -178,6 +178,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
- }
-
- public void updateChunkStatus(ChunkPos chunkPos, ChunkHolder.FullChunkStatus levelType) {
-+ io.papermc.paper.util.TickThread.ensureTickThread("Asynchronous chunk ticking status update"); // Paper
- Visibility visibility = Visibility.fromFullChunkStatus(levelType);
-
- this.updateChunkStatus(chunkPos, visibility);
diff --git a/patches/server/0756-Optimise-non-flush-packet-sending.patch b/patches/server/0733-Optimise-non-flush-packet-sending.patch
index 21c35fd4d5..37e5332584 100644
--- a/patches/server/0756-Optimise-non-flush-packet-sending.patch
+++ b/patches/server/0733-Optimise-non-flush-packet-sending.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Tue, 22 Sep 2020 01:49:19 -0700
Subject: [PATCH] Optimise non-flush packet sending
@@ -20,7 +20,7 @@ up on this optimisation before he came along.
Locally this patch drops the entity tracker tick by a full 1.5x.
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index 51217798bfd549483ce456b44d14089f35642c55..fefda9868fd3c4b3392b2bf4c68c0b4b2f311f31 100644
+index 335d9e5f11ad3469f3a0310782e41283973f5a5f..957fc22a6c79fd480fefc3cce9cc374d25bc8cf9 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -46,6 +46,8 @@ import org.slf4j.Logger;
@@ -32,7 +32,7 @@ index 51217798bfd549483ce456b44d14089f35642c55..fefda9868fd3c4b3392b2bf4c68c0b4b
public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
private static final float AVERAGE_PACKETS_SMOOTHING = 0.75F;
-@@ -396,9 +398,19 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -418,9 +420,19 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
if (this.channel.eventLoop().inEventLoop()) {
this.doSendPacket(packet, callbacks, enumprotocol, enumprotocol1, flush); // Paper
} else {
diff --git a/patches/server/0757-Optimise-nearby-player-lookups.patch b/patches/server/0734-Optimise-nearby-player-lookups.patch
index f491e4b47c..c283aa0ebf 100644
--- a/patches/server/0757-Optimise-nearby-player-lookups.patch
+++ b/patches/server/0734-Optimise-nearby-player-lookups.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Thu, 27 Aug 2020 16:22:52 -0700
Subject: [PATCH] Optimise nearby player lookups
@@ -9,10 +9,10 @@ since the penalty of a map lookup could outweigh the benefits of
searching less players (as it basically did in the outside range patch).
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-index 538f21e6bc29c0307441fe4899dc7f600d2d1a04..09f262de1b12b09013f8277b25d13ffcf53b96d8 100644
+index 1fb298ff60b59a7074fb9d7a79709f05887ce32c..94d52a21d40a31cd6e8251f79ffc885de16e48f4 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
-@@ -89,6 +89,12 @@ public class ChunkHolder {
+@@ -93,6 +93,12 @@ public class ChunkHolder {
this.chunkMap.needsChangeBroadcasting.add(this);
}
// Paper end - optimise chunk tick iteration
@@ -25,7 +25,7 @@ index 538f21e6bc29c0307441fe4899dc7f600d2d1a04..09f262de1b12b09013f8277b25d13ffc
}
public void onChunkRemove() {
-@@ -101,6 +107,12 @@ public class ChunkHolder {
+@@ -105,6 +111,12 @@ public class ChunkHolder {
this.chunkMap.needsChangeBroadcasting.remove(this);
}
// Paper end - optimise chunk tick iteration
@@ -39,32 +39,31 @@ index 538f21e6bc29c0307441fe4899dc7f600d2d1a04..09f262de1b12b09013f8277b25d13ffc
// Paper end
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index f47e2d4a7343f3e3ee68f36688720cedc58861dc..aa1b6b692f9107ef1b5091714f1ecc8b29da9897 100644
+index 57b45ce5feaa5015b5468a0b44f354e96d7d95fc..841df3f621081f6b67711cbd047e8bde498eedbf 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -165,6 +165,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper
+@@ -152,6 +152,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final ReferenceOpenHashSet<ChunkHolder> needsChangeBroadcasting = new ReferenceOpenHashSet<>();
+ // Paper - rewrite chunk system
+ // Paper start - optimise checkDespawn
+ public static final int GENERAL_AREA_MAP_SQUARE_RADIUS = 40;
+ public static final double GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE = 16.0 * (GENERAL_AREA_MAP_SQUARE_RADIUS - 1);
+ public static final double GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE_SQUARED = GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE * GENERAL_AREA_MAP_ACCEPTABLE_SEARCH_RANGE;
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerGeneralAreaMap;
+ // Paper end - optimise checkDespawn
-+
- // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
- public final CallbackExecutor callbackExecutor = new CallbackExecutor();
- public static final class CallbackExecutor implements java.util.concurrent.Executor, Runnable {
-@@ -242,6 +249,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+
+ // Paper start - distance maps
+ private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
+@@ -204,6 +210,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, net.minecraft.server.ChunkSystem.getSendViewDistance(player)));
+ }
// Paper end - use distance map to optimise entity tracker
- // Note: players need to be explicitly added to distance maps before they can be updated
- this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
+ this.playerGeneralAreaMap.add(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn
- // Paper start - per player mob spawning
- if (this.playerMobDistanceMap != null) {
- this.playerMobDistanceMap.add(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
-@@ -260,6 +268,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+
+ void removePlayerFromDistanceMaps(ServerPlayer player) {
+@@ -213,6 +220,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.playerMobSpawnMap.remove(player);
this.playerChunkTickRangeMap.remove(player);
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
@@ -72,15 +71,15 @@ index f47e2d4a7343f3e3ee68f36688720cedc58861dc..aa1b6b692f9107ef1b5091714f1ecc8b
// Paper start - per player mob spawning
if (this.playerMobDistanceMap != null) {
this.playerMobDistanceMap.remove(player);
-@@ -280,6 +289,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -244,6 +252,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, net.minecraft.server.ChunkSystem.getSendViewDistance(player)));
}
// Paper end - use distance map to optimise entity tracker
- this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
+ this.playerGeneralAreaMap.update(player, chunkX, chunkZ, GENERAL_AREA_MAP_SQUARE_RADIUS); // Paper - optimise checkDespawn
- // Paper start - per player mob spawning
- if (this.playerMobDistanceMap != null) {
- this.playerMobDistanceMap.update(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
-@@ -454,6 +464,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ }
+ // Paper end
+ // Paper start
+@@ -397,6 +406,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
});
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
@@ -105,10 +104,10 @@ index f47e2d4a7343f3e3ee68f36688720cedc58861dc..aa1b6b692f9107ef1b5091714f1ecc8b
protected ChunkGenerator generator() {
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index bcd1b2534af33e7a9d184e0ea4c9c0a4b58dacc8..25149dde919738859f6fb6b2d0405e90d1732f2b 100644
+index 4f32dff7ec2fc55b085b13464667707454413dac..3fda64e9d530b85ddcfe4277f64286fc3356512f 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -411,6 +411,84 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -440,6 +440,84 @@ public class ServerLevel extends Level implements WorldGenLevel {
// Paper end
public final ReferenceOpenHashSet<ServerPlayer> pendingLogin = new ReferenceOpenHashSet<>(); // Paper
@@ -193,7 +192,7 @@ index bcd1b2534af33e7a9d184e0ea4c9c0a4b58dacc8..25149dde919738859f6fb6b2d0405e90
// Add env and gen to constructor, IWorldDataServer -> WorldDataServer
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
// Holder holder = worlddimension.typeHolder(); // CraftBukkit - decompile error
-@@ -513,6 +591,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -543,6 +621,14 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
public void tick(BooleanSupplier shouldKeepTicking) {
@@ -227,7 +226,7 @@ index 5fb88a3b7242a2712a568aaccebe601f89bfee3a..557e90e54439ce0430075403392b5052
if (entityhuman != null) {
double d0 = entityhuman.distanceToSqr((Entity) this);
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 76a2d00324a85419548005e0aa3cbd8b891b8257..931de769a3b7c993d151f3ee8e1038d95d3899a3 100644
+index aa90454e70e5c25eb331ceb212df3128d64b1567..c1a3bcc8d9df2bf25a9c73faeac84652756d430a 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -205,6 +205,69 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -323,10 +322,10 @@ index 4150e8cd7197eac53042d56f0a53a4951f8824ce..e31a2eea9a62ab2c0bed1a97dab6bae2
private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { // Paper
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-index 2981ba61e347b8660082ff946521fc7f219d2c0d..c85380c3bf3bf4448a28a91af78f41c235a583e4 100644
+index b0f53c99a89b900ffe49bdd277329829b44775d4..05499ae9fc331471db6e763a2adb46b5da8522d3 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -235,6 +235,98 @@ public class LevelChunk extends ChunkAccess {
+@@ -269,6 +269,98 @@ public class LevelChunk extends ChunkAccess {
}
}
// Paper end
diff --git a/patches/server/0759-Remove-streams-for-villager-AI.patch b/patches/server/0735-Remove-streams-for-villager-AI.patch
index 3af840ec77..7493d3ad6b 100644
--- a/patches/server/0759-Remove-streams-for-villager-AI.patch
+++ b/patches/server/0735-Remove-streams-for-villager-AI.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Thu, 27 Aug 2020 20:51:40 -0700
Subject: [PATCH] Remove streams for villager AI
diff --git a/patches/server/0761-Use-Velocity-compression-and-cipher-natives.patch b/patches/server/0736-Use-Velocity-compression-and-cipher-natives.patch
index 6868989474..a37ddabfc8 100644
--- a/patches/server/0761-Use-Velocity-compression-and-cipher-natives.patch
+++ b/patches/server/0736-Use-Velocity-compression-and-cipher-natives.patch
@@ -268,10 +268,10 @@ index 792883afe53d2b7989c25a81c2f9a639d5e21d20..c04379ca8a4db0f4de46ad2b3b338431
return this.threshold;
}
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
-index fefda9868fd3c4b3392b2bf4c68c0b4b2f311f31..66afd752fd7d327e141d49b477f07e1ff3645d02 100644
+index 957fc22a6c79fd480fefc3cce9cc374d25bc8cf9..6967c90c50ea75fb9dd5da808b2c8c8ea046ecec 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
-@@ -652,11 +652,28 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -681,11 +681,28 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
return networkmanager;
}
@@ -304,7 +304,7 @@ index fefda9868fd3c4b3392b2bf4c68c0b4b2f311f31..66afd752fd7d327e141d49b477f07e1f
public boolean isEncrypted() {
return this.encrypted;
-@@ -685,16 +702,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
+@@ -714,16 +731,17 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
if (compressionThreshold >= 0) {
diff --git a/patches/server/0762-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch b/patches/server/0737-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch
index c8dd06ca73..c8dd06ca73 100644
--- a/patches/server/0762-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch
+++ b/patches/server/0737-Reduce-worldgen-thread-worker-count-for-low-core-cou.patch
diff --git a/patches/server/0764-Async-catch-modifications-to-critical-entity-state.patch b/patches/server/0738-Async-catch-modifications-to-critical-entity-state.patch
index 3e8995df49..8f9a21c9d1 100644
--- a/patches/server/0764-Async-catch-modifications-to-critical-entity-state.patch
+++ b/patches/server/0738-Async-catch-modifications-to-critical-entity-state.patch
@@ -8,18 +8,18 @@ Now in 1.17, this state is _even more_ critical than it was before,
so these must exist to catch stupid plugins.
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6fd96e640 100644
+index ab7eadf2fc4c4598fa89068332eaaf9a8e0a100f..e738f058af577150445ac4ef7a0bcbf98e3ba52e 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-@@ -138,6 +138,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -77,6 +77,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean addEntityUuid(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity add by UUID"); // Paper
if (!this.knownUuids.add(entity.getUUID())) {
- // Paper start
- T conflict = this.visibleEntityStorage.getEntity(entity.getUUID());
-@@ -166,6 +167,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+ PersistentEntitySectionManager.LOGGER.warn("UUID of added entity already exists: {}", entity);
+ return false;
+@@ -90,6 +91,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean addEntity(T entity, boolean existing) {
@@ -27,7 +27,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
// Paper start - chunk system hooks
if (existing) {
// I don't want to know why this is a generic type.
-@@ -222,19 +224,23 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -145,19 +147,23 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
void startTicking(T entity) {
@@ -51,7 +51,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
this.callbacks.onTrackingEnd(entity);
this.visibleEntityStorage.remove(entity);
}
-@@ -248,6 +254,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -169,6 +175,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void updateChunkStatus(ChunkPos chunkPos, Visibility trackingStatus) {
@@ -59,7 +59,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
long i = chunkPos.toLong();
if (trackingStatus == Visibility.HIDDEN) {
-@@ -292,6 +299,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -213,6 +220,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void ensureChunkQueuedForLoad(long chunkPos) {
@@ -67,7 +67,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
PersistentEntitySectionManager.ChunkLoadStatus persistententitysectionmanager_b = (PersistentEntitySectionManager.ChunkLoadStatus) this.chunkLoadStatuses.get(chunkPos);
if (persistententitysectionmanager_b == PersistentEntitySectionManager.ChunkLoadStatus.FRESH) {
-@@ -336,6 +344,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -257,6 +265,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private void requestChunkLoad(long chunkPos) {
@@ -75,7 +75,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
this.chunkLoadStatuses.put(chunkPos, PersistentEntitySectionManager.ChunkLoadStatus.PENDING);
ChunkPos chunkcoordintpair = new ChunkPos(chunkPos);
CompletableFuture completablefuture = this.permanentStorage.loadEntities(chunkcoordintpair);
-@@ -349,6 +358,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -270,6 +279,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean processChunkUnload(long chunkPos) {
@@ -83,7 +83,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
boolean flag = this.storeChunkSections(chunkPos, (entityaccess) -> {
entityaccess.getPassengersAndSelf().forEach(this::unloadEntity);
}, true); // CraftBukkit - add boolean for event call
-@@ -373,6 +383,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -294,6 +304,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private void processPendingLoads() {
@@ -91,7 +91,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
ChunkEntities<T> chunkentities; // CraftBukkit - decompile error
while ((chunkentities = (ChunkEntities) this.loadingInbox.poll()) != null) {
-@@ -389,6 +400,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -310,6 +321,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void tick() {
@@ -99,7 +99,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
this.processPendingLoads();
this.processUnloads();
}
-@@ -409,6 +421,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -330,6 +342,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void autoSave() {
@@ -107,7 +107,7 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
this.getAllChunksToSave().forEach((java.util.function.LongConsumer) (i) -> { // CraftBukkit - decompile error
boolean flag = this.chunkVisibility.get(i) == Visibility.HIDDEN;
-@@ -423,6 +436,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+@@ -344,6 +357,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void saveAll() {
@@ -115,15 +115,15 @@ index 1f0eddb0f3ded42bf312f8933def2f5c9a964651..2d3aacdae95963385ea228e73a2073a6
LongSet longset = this.getAllChunksToSave();
while (!longset.isEmpty()) {
-@@ -530,6 +544,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
- long i = SectionPos.asLong(blockposition); final long newSectionPos = i; // Paper - diff on change, new position section
+@@ -446,6 +460,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+ long i = SectionPos.asLong(blockposition);
if (i != this.currentSectionKey) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity move"); // Paper
- PersistentEntitySectionManager.this.entitySliceManager.moveEntity((Entity)this.entity); // Paper
- Visibility visibility = this.currentSection.getStatus(); final Visibility oldVisibility = visibility; // Paper - diff on change - this should be OLD section visibility
- // Paper start
-@@ -604,6 +619,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
+ Visibility visibility = this.currentSection.getStatus();
+
+ if (!this.currentSection.remove(this.entity)) {
+@@ -500,6 +515,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
@Override
public void onRemove(Entity.RemovalReason reason) {
diff --git a/patches/server/0765-Fix-Bukkit-NamespacedKey-shenanigans.patch b/patches/server/0739-Fix-Bukkit-NamespacedKey-shenanigans.patch
index fabf5a82e9..fabf5a82e9 100644
--- a/patches/server/0765-Fix-Bukkit-NamespacedKey-shenanigans.patch
+++ b/patches/server/0739-Fix-Bukkit-NamespacedKey-shenanigans.patch
diff --git a/patches/server/0766-Fix-merchant-inventory-not-closing-on-entity-removal.patch b/patches/server/0740-Fix-merchant-inventory-not-closing-on-entity-removal.patch
index 85602ad6d7..72253bffc5 100644
--- a/patches/server/0766-Fix-merchant-inventory-not-closing-on-entity-removal.patch
+++ b/patches/server/0740-Fix-merchant-inventory-not-closing-on-entity-removal.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix merchant inventory not closing on entity removal
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index a7a403d34551453a1e0502fe1f7bc139f645d917..3bb6dbdd05ed981f70556c8f905d1eeeeade30b8 100644
+index 3fda64e9d530b85ddcfe4277f64286fc3356512f..6388ed56f71f065ab811acf3fb264083fdb5b09a 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2578,6 +2578,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2606,6 +2606,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
// Spigot end
// Spigot Start
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
diff --git a/patches/server/0767-Check-requirement-before-suggesting-root-nodes.patch b/patches/server/0741-Check-requirement-before-suggesting-root-nodes.patch
index d824c5f2af..d824c5f2af 100644
--- a/patches/server/0767-Check-requirement-before-suggesting-root-nodes.patch
+++ b/patches/server/0741-Check-requirement-before-suggesting-root-nodes.patch
diff --git a/patches/server/0768-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch b/patches/server/0742-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch
index b218b2916f..b218b2916f 100644
--- a/patches/server/0768-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch
+++ b/patches/server/0742-Don-t-respond-to-ServerboundCommandSuggestionPacket-.patch
diff --git a/patches/server/0769-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch b/patches/server/0743-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch
index 8bc6527cd8..8bc6527cd8 100644
--- a/patches/server/0769-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch
+++ b/patches/server/0743-Fix-setPatternColor-on-tropical-fish-bucket-meta.patch
diff --git a/patches/server/0770-Ensure-valid-vehicle-status.patch b/patches/server/0744-Ensure-valid-vehicle-status.patch
index 2d1f854e7d..f3e2c1b98b 100644
--- a/patches/server/0770-Ensure-valid-vehicle-status.patch
+++ b/patches/server/0744-Ensure-valid-vehicle-status.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Ensure valid vehicle status
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 951ccf3526dc2f5e4e2f16952036683ad132fbe0..97de35c614e1e9b0e825f9914173a3e1e0e53221 100644
+index 9075b0476e775af0e5bc173dc736781dfde6938a..c6be77e9fae04f2a6dd332dbde9daf0109111e60 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -513,7 +513,7 @@ public class ServerPlayer extends Player {
+@@ -498,7 +498,7 @@ public class ServerPlayer extends Player {
}
}
diff --git a/patches/server/0771-Prevent-softlocked-end-exit-portal-generation.patch b/patches/server/0745-Prevent-softlocked-end-exit-portal-generation.patch
index 2286afa5f2..2286afa5f2 100644
--- a/patches/server/0771-Prevent-softlocked-end-exit-portal-generation.patch
+++ b/patches/server/0745-Prevent-softlocked-end-exit-portal-generation.patch
diff --git a/patches/server/0772-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch b/patches/server/0746-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch
index b415f668ab..b415f668ab 100644
--- a/patches/server/0772-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch
+++ b/patches/server/0746-Fix-CocaoDecorator-causing-a-crash-when-trying-to-ge.patch
diff --git a/patches/server/0773-Don-t-log-debug-logging-being-disabled.patch b/patches/server/0747-Don-t-log-debug-logging-being-disabled.patch
index 83460a961a..83460a961a 100644
--- a/patches/server/0773-Don-t-log-debug-logging-being-disabled.patch
+++ b/patches/server/0747-Don-t-log-debug-logging-being-disabled.patch
diff --git a/patches/server/0774-fix-various-menus-with-empty-level-accesses.patch b/patches/server/0748-fix-various-menus-with-empty-level-accesses.patch
index 3bda24b9c6..3bda24b9c6 100644
--- a/patches/server/0774-fix-various-menus-with-empty-level-accesses.patch
+++ b/patches/server/0748-fix-various-menus-with-empty-level-accesses.patch
diff --git a/patches/server/0775-Preserve-overstacked-loot.patch b/patches/server/0749-Preserve-overstacked-loot.patch
index e39eb87bbc..e39eb87bbc 100644
--- a/patches/server/0775-Preserve-overstacked-loot.patch
+++ b/patches/server/0749-Preserve-overstacked-loot.patch
diff --git a/patches/server/0776-Update-head-rotation-in-missing-places.patch b/patches/server/0750-Update-head-rotation-in-missing-places.patch
index 9b18797ea0..76b34081eb 100644
--- a/patches/server/0776-Update-head-rotation-in-missing-places.patch
+++ b/patches/server/0750-Update-head-rotation-in-missing-places.patch
@@ -8,10 +8,10 @@ This is because bukkit uses a separate head rotation field for yaw.
This issue only applies to players.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 51239bc47d6d2cfd8d345fb67fe0d92fabe53209..cef2cad3c278c4d2258f68dcbe6936c6637ca90a 100644
+index 5e211f1df6739433437930116d134326abbf558d..c455abc1f41aa9372dec6db7a304bc26ce1b7973 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -1763,6 +1763,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1764,6 +1764,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.setXRot(Mth.clamp(pitch, -90.0F, 90.0F) % 360.0F);
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
@@ -19,7 +19,7 @@ index 51239bc47d6d2cfd8d345fb67fe0d92fabe53209..cef2cad3c278c4d2258f68dcbe6936c6
}
public void absMoveTo(double x, double y, double z) {
-@@ -1801,6 +1802,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1802,6 +1803,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.setXRot(pitch);
this.setOldPosAndRot();
this.reapplyPosition();
diff --git a/patches/server/0777-prevent-unintended-light-block-manipulation.patch b/patches/server/0751-prevent-unintended-light-block-manipulation.patch
index 08bd6164f5..08bd6164f5 100644
--- a/patches/server/0777-prevent-unintended-light-block-manipulation.patch
+++ b/patches/server/0751-prevent-unintended-light-block-manipulation.patch
diff --git a/patches/server/0778-Fix-CraftCriteria-defaults-map.patch b/patches/server/0752-Fix-CraftCriteria-defaults-map.patch
index 65eeffccea..65eeffccea 100644
--- a/patches/server/0778-Fix-CraftCriteria-defaults-map.patch
+++ b/patches/server/0752-Fix-CraftCriteria-defaults-map.patch
diff --git a/patches/server/0779-Fix-upstreams-block-state-factories.patch b/patches/server/0753-Fix-upstreams-block-state-factories.patch
index 221799c0b1..221799c0b1 100644
--- a/patches/server/0779-Fix-upstreams-block-state-factories.patch
+++ b/patches/server/0753-Fix-upstreams-block-state-factories.patch
diff --git a/patches/server/0780-Add-config-option-for-logging-player-ip-addresses.patch b/patches/server/0754-Add-config-option-for-logging-player-ip-addresses.patch
index e2e7753113..695d8483fe 100644
--- a/patches/server/0780-Add-config-option-for-logging-player-ip-addresses.patch
+++ b/patches/server/0754-Add-config-option-for-logging-player-ip-addresses.patch
@@ -65,7 +65,7 @@ index 26781be1a01b0683f4d0f3cf565c3a623c987606..c8e373bccb8c8edd8b7a3a812e04fcf6
@Nullable
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 5ee0c3bb27ffbadc1e088983e643eed974753b65..fc14fc8017d89c27b0aeb10a5f38dafde5c15f53 100644
+index 456031783aa902c8fd40050aa2b8d051b996d71d..3e870218321a701b814a4dac97ff1af12142600e 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -243,7 +243,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0781-Configurable-feature-seeds.patch b/patches/server/0755-Configurable-feature-seeds.patch
index 0b298aa5e7..6e138bc312 100644
--- a/patches/server/0781-Configurable-feature-seeds.patch
+++ b/patches/server/0755-Configurable-feature-seeds.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Configurable feature seeds
Co-authored-by: Thonk <[email protected]>
diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java
-index 46297ac0a19fd2398ab777a381eff4d0a256161e..78280fb3bcd8d792a58ece6d735e0824ea4be536 100644
+index 98171f6c8e23f6ef89b897e4b80e3afb2a1950a0..06bff37e4c1fddd3be6343049a66787c63fb420c 100644
--- a/src/main/java/co/aikar/timings/TimingsExport.java
+++ b/src/main/java/co/aikar/timings/TimingsExport.java
-@@ -283,7 +283,7 @@ public class TimingsExport extends Thread {
+@@ -287,7 +287,7 @@ public class TimingsExport extends Thread {
JSONObject object = new JSONObject();
for (String key : config.getKeys(false)) {
String fullKey = (parentKey != null ? parentKey + "." + key : key);
@@ -19,7 +19,7 @@ index 46297ac0a19fd2398ab777a381eff4d0a256161e..78280fb3bcd8d792a58ece6d735e0824
}
final Object val = config.get(key);
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
-index 28f01f29796a8a8e6e6331da5525a4306d78230e..bdcfa5aac4cd0bd5841922295cc8fbb6ca69bd68 100644
+index 50b9c2fbe3a5c12a43b4711d678ed2398dbdee58..cc79ca7ee770408ec59b1f6a3f4ec58e23bd2619 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
@@ -606,7 +606,14 @@ public abstract class ChunkGenerator {
diff --git a/patches/server/0782-VanillaCommandWrapper-didnt-account-for-entity-sende.patch b/patches/server/0756-VanillaCommandWrapper-didnt-account-for-entity-sende.patch
index 54bf3f7028..54bf3f7028 100644
--- a/patches/server/0782-VanillaCommandWrapper-didnt-account-for-entity-sende.patch
+++ b/patches/server/0756-VanillaCommandWrapper-didnt-account-for-entity-sende.patch
diff --git a/patches/server/0783-Add-root-admin-user-detection.patch b/patches/server/0757-Add-root-admin-user-detection.patch
index 404725a045..a3bdc6b127 100644
--- a/patches/server/0783-Add-root-admin-user-detection.patch
+++ b/patches/server/0757-Add-root-admin-user-detection.patch
@@ -57,7 +57,7 @@ index 0000000000000000000000000000000000000000..6bd0afddbcc461149dfe9a5c7a86fff6
+ }
+}
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 2d01a1d4b2f7fdd38a6b1022f2476ba68b663171..20670bc075c387ee0422eb1014207e26105efccd 100644
+index 6b40e5659923d74438efaeb846732cb4efbf3f1b..11efaeb12340946c7447617500cbc69bc9bdaf01 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -184,6 +184,16 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
diff --git a/patches/server/0784-Always-allow-item-changing-in-Fireball.patch b/patches/server/0758-Always-allow-item-changing-in-Fireball.patch
index fe4cbf2ef1..fe4cbf2ef1 100644
--- a/patches/server/0784-Always-allow-item-changing-in-Fireball.patch
+++ b/patches/server/0758-Always-allow-item-changing-in-Fireball.patch
diff --git a/patches/server/0758-Optimise-WorldServer-notify.patch b/patches/server/0758-Optimise-WorldServer-notify.patch
deleted file mode 100644
index 175c5f3102..0000000000
--- a/patches/server/0758-Optimise-WorldServer-notify.patch
+++ /dev/null
@@ -1,337 +0,0 @@
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
-Date: Thu, 9 Jul 2020 13:34:59 -0700
-Subject: [PATCH] Optimise WorldServer#notify
-
-Iterating over all of the navigators in the world is pretty expensive.
-Instead, only iterate over navigators in the current region that are
-eligible for repathing.
-
-diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index aa1b6b692f9107ef1b5091714f1ecc8b29da9897..69b8f5dcae4ea75ea9d63c36b3f5b4383fe232f9 100644
---- a/src/main/java/net/minecraft/server/level/ChunkMap.java
-+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -302,15 +302,81 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- public final io.papermc.paper.chunk.SingleThreadChunkRegionManager dataRegionManager;
-
- public static final class DataRegionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionData {
-+ // Paper start - optimise notify()
-+ private io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> navigators;
-+
-+ public io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> getNavigators() {
-+ return this.navigators;
-+ }
-+
-+ public boolean addToNavigators(final Mob navigator) {
-+ if (this.navigators == null) {
-+ this.navigators = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>();
-+ }
-+ return this.navigators.add(navigator);
-+ }
-+
-+ public boolean removeFromNavigators(final Mob navigator) {
-+ if (this.navigators == null) {
-+ return false;
-+ }
-+ return this.navigators.remove(navigator);
-+ }
-+ // Paper end - optimise notify()
- }
-
- public static final class DataRegionSectionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSectionData {
-
-+ // Paper start - optimise notify()
-+ private io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> navigators;
-+
-+ public io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> getNavigators() {
-+ return this.navigators;
-+ }
-+
-+ public boolean addToNavigators(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, final Mob navigator) {
-+ if (this.navigators == null) {
-+ this.navigators = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>();
-+ }
-+ final boolean ret = this.navigators.add(navigator);
-+ if (ret) {
-+ final DataRegionData data = (DataRegionData)section.getRegion().regionData;
-+ if (!data.addToNavigators(navigator)) {
-+ throw new IllegalStateException();
-+ }
-+ }
-+ return ret;
-+ }
-+
-+ public boolean removeFromNavigators(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section, final Mob navigator) {
-+ if (this.navigators == null) {
-+ return false;
-+ }
-+ final boolean ret = this.navigators.remove(navigator);
-+ if (ret) {
-+ final DataRegionData data = (DataRegionData)section.getRegion().regionData;
-+ if (!data.removeFromNavigators(navigator)) {
-+ throw new IllegalStateException();
-+ }
-+ }
-+ return ret;
-+ }
-+ // Paper end - optimise notify()
-+
- @Override
- public void removeFromRegion(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section,
- final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region from) {
- final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
- final DataRegionData fromData = (DataRegionData)from.regionData;
-+ // Paper start - optimise notify()
-+ if (sectionData.navigators != null) {
-+ for (final Iterator<Mob> iterator = sectionData.navigators.unsafeIterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
-+ if (!fromData.removeFromNavigators(iterator.next())) {
-+ throw new IllegalStateException();
-+ }
-+ }
-+ }
-+ // Paper end - optimise notify()
- }
-
- @Override
-@@ -320,6 +386,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
- final DataRegionData oldRegionData = oldRegion == null ? null : (DataRegionData)oldRegion.regionData;
- final DataRegionData newRegionData = (DataRegionData)newRegion.regionData;
-+ // Paper start - optimise notify()
-+ if (sectionData.navigators != null) {
-+ for (final Iterator<Mob> iterator = sectionData.navigators.unsafeIterator(io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
-+ if (!newRegionData.addToNavigators(iterator.next())) {
-+ throw new IllegalStateException();
-+ }
-+ }
-+ }
-+ // Paper end - optimise notify()
- }
- }
-
-diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 25149dde919738859f6fb6b2d0405e90d1732f2b..a7a403d34551453a1e0502fe1f7bc139f645d917 100644
---- a/src/main/java/net/minecraft/server/level/ServerLevel.java
-+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1122,6 +1122,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
- public void tickNonPassenger(Entity entity) {
- // Paper start - log detailed entity tick information
- io.papermc.paper.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
-+ if (!entity.isRemoved()) this.entityManager.updateNavigatorsInRegion(entity); // Paper - optimise notify
- try {
- if (currentlyTickingEntity.get() == null) {
- currentlyTickingEntity.lazySet(entity);
-@@ -1639,9 +1640,18 @@ public class ServerLevel extends Level implements WorldGenLevel {
-
- if (Shapes.joinIsNotEmpty(voxelshape, voxelshape1, BooleanOp.NOT_SAME)) {
- List<PathNavigation> list = new ObjectArrayList();
-- Iterator iterator = this.navigatingMobs.iterator();
-+ // Paper start - optimise notify()
-+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region region = this.getChunkSource().chunkMap.dataRegionManager.getRegion(pos.getX() >> 4, pos.getZ() >> 4);
-+ if (region == null) {
-+ return;
-+ }
-+ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<Mob> navigatorsFromRegion = ((ChunkMap.DataRegionData)region.regionData).getNavigators();
-+ if (navigatorsFromRegion == null) {
-+ return;
-+ }
-+ io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Mob> iterator = navigatorsFromRegion.iterator();
-
-- while (iterator.hasNext()) {
-+ try { while (iterator.hasNext()) { // Paper end - optimise notify()
- // CraftBukkit start - fix SPIGOT-6362
- Mob entityinsentient;
- try {
-@@ -1663,16 +1673,23 @@ public class ServerLevel extends Level implements WorldGenLevel {
-
- try {
- this.isUpdatingNavigations = true;
-- iterator = list.iterator();
-+ // Paper start - optimise notify()
-+ Iterator<PathNavigation> navigationIterator = list.iterator();
-
-- while (iterator.hasNext()) {
-- PathNavigation navigationabstract1 = (PathNavigation) iterator.next();
-+ while (navigationIterator.hasNext()) {
-+ PathNavigation navigationabstract1 = navigationIterator.next();
-+ // Paper end - optimise notify()
-
- navigationabstract1.recomputePath();
- }
- } finally {
- this.isUpdatingNavigations = false;
- }
-+ // Paper start - optimise notify()
-+ } finally {
-+ iterator.finishedIterating();
-+ }
-+ // Paper end - optimise notify()
-
- }
- } // Paper
-@@ -2470,10 +2487,12 @@ public class ServerLevel extends Level implements WorldGenLevel {
-
- public void onTickingStart(Entity entity) {
- ServerLevel.this.entityTickList.add(entity);
-+ ServerLevel.this.entityManager.addNavigatorsIfPathingToRegion(entity); // Paper - optimise notify
- }
-
- public void onTickingEnd(Entity entity) {
- ServerLevel.this.entityTickList.remove(entity);
-+ ServerLevel.this.entityManager.removeNavigatorsFromData(entity); // Paper - optimise notify
- // Paper start - Reset pearls when they stop being ticked
- if (paperConfig().fixes.disableUnloadedChunkEnderpearlExploit && entity instanceof net.minecraft.world.entity.projectile.ThrownEnderpearl pearl) {
- pearl.cachedOwner = null;
-diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
-index c1781c92ff59f0c9eb47cbbef01e3252c5e1a1bf..02653adc591d390ca8b4ee13289510d4652c8894 100644
---- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
-+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
-@@ -28,7 +28,7 @@ import net.minecraft.world.phys.Vec3;
-
- public abstract class PathNavigation {
- private static final int MAX_TIME_RECOMPUTE = 20;
-- protected final Mob mob;
-+ protected final Mob mob; public final Mob getEntity() { return this.mob; } // Paper - public accessor
- protected final Level level;
- @Nullable
- protected Path path;
-@@ -41,7 +41,7 @@ public abstract class PathNavigation {
- protected long lastTimeoutCheck;
- protected double timeoutLimit;
- protected float maxDistanceToWaypoint = 0.5F;
-- protected boolean hasDelayedRecomputation;
-+ protected boolean hasDelayedRecomputation; protected final boolean needsPathRecalculation() { return this.hasDelayedRecomputation; } // Paper - public accessor
- protected long timeLastRecompute;
- protected NodeEvaluator nodeEvaluator;
- @Nullable
-@@ -419,7 +419,7 @@ public abstract class PathNavigation {
- public boolean shouldRecomputePath(BlockPos pos) {
- if (this.hasDelayedRecomputation) {
- return false;
-- } else if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) {
-+ } else if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) { // Paper - diff on change - needed for isViableForPathRecalculationChecking()
- Node node = this.path.getEndNode();
- Vec3 vec3 = new Vec3(((double)node.x + this.mob.getX()) / 2.0D, ((double)node.y + this.mob.getY()) / 2.0D, ((double)node.z + this.mob.getZ()) / 2.0D);
- return pos.closerToCenterThan(vec3, (double)(this.path.getNodeCount() - this.path.getNextNodeIndex()));
-@@ -435,4 +435,11 @@ public abstract class PathNavigation {
- public boolean isStuck() {
- return this.isStuck;
- }
-+
-+ // Paper start
-+ public boolean isViableForPathRecalculationChecking() {
-+ return !this.needsPathRecalculation() &&
-+ (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0);
-+ }
-+ // Paper end
- }
-diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-index f635b610e68d129aa0ae60c54b83da6943946436..1f0eddb0f3ded42bf312f8933def2f5c9a964651 100644
---- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
-@@ -71,6 +71,65 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
- }
- // CraftBukkit end
-
-+ // Paper start - optimise notify()
-+ public final void removeNavigatorsFromData(Entity entity, final int chunkX, final int chunkZ) {
-+ if (!(entity instanceof net.minecraft.world.entity.Mob)) {
-+ return;
-+ }
-+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section =
-+ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(chunkX, chunkZ);
-+ if (section != null) {
-+ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData;
-+ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity));
-+ }
-+ }
-+
-+ public final void removeNavigatorsFromData(Entity entity) {
-+ if (!(entity instanceof net.minecraft.world.entity.Mob)) {
-+ return;
-+ }
-+ BlockPos entityPos = entity.blockPosition();
-+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section =
-+ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4);
-+ if (section != null) {
-+ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData;
-+ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity));
-+ }
-+ }
-+
-+ public final void addNavigatorsIfPathingToRegion(Entity entity) {
-+ if (!(entity instanceof net.minecraft.world.entity.Mob)) {
-+ return;
-+ }
-+ BlockPos entityPos = entity.blockPosition();
-+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section =
-+ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4);
-+ if (section != null) {
-+ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData;
-+ if (((net.minecraft.world.entity.Mob)entity).getNavigation().isViableForPathRecalculationChecking()) {
-+ sectionData.addToNavigators(section, ((net.minecraft.world.entity.Mob)entity));
-+ }
-+ }
-+ }
-+
-+ public final void updateNavigatorsInRegion(Entity entity) {
-+ if (!(entity instanceof net.minecraft.world.entity.Mob)) {
-+ return;
-+ }
-+ BlockPos entityPos = entity.blockPosition();
-+ io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section =
-+ this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.getRegionSection(entityPos.getX() >> 4, entityPos.getZ() >> 4);
-+ if (section != null) {
-+ net.minecraft.server.level.ChunkMap.DataRegionSectionData sectionData = (net.minecraft.server.level.ChunkMap.DataRegionSectionData)section.sectionData;
-+ if (((net.minecraft.world.entity.Mob)entity).getNavigation().isViableForPathRecalculationChecking()) {
-+ sectionData.addToNavigators(section, ((net.minecraft.world.entity.Mob)entity));
-+ } else {
-+ sectionData.removeFromNavigators(section, ((net.minecraft.world.entity.Mob)entity));
-+ }
-+ }
-+ }
-+ // Paper end - optimise notify()
-+
- void removeSectionIfEmpty(long sectionPos, EntitySection<T> section) {
- if (section.isEmpty()) {
- this.sectionStorage.remove(sectionPos);
-@@ -468,11 +527,25 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
- @Override
- public void onMove() {
- BlockPos blockposition = this.entity.blockPosition();
-- long i = SectionPos.asLong(blockposition);
-+ long i = SectionPos.asLong(blockposition); final long newSectionPos = i; // Paper - diff on change, new position section
-
- if (i != this.currentSectionKey) {
- PersistentEntitySectionManager.this.entitySliceManager.moveEntity((Entity)this.entity); // Paper
-- Visibility visibility = this.currentSection.getStatus();
-+ Visibility visibility = this.currentSection.getStatus(); final Visibility oldVisibility = visibility; // Paper - diff on change - this should be OLD section visibility
-+ // Paper start
-+ int shift = PersistentEntitySectionManager.this.entitySliceManager.world.getChunkSource().chunkMap.dataRegionManager.regionChunkShift;
-+ int oldChunkX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(this.currentSectionKey);
-+ int oldChunkZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(this.currentSectionKey);
-+ int oldRegionX = oldChunkX >> shift;
-+ int oldRegionZ = oldChunkZ >> shift;
-+
-+ int newRegionX = io.papermc.paper.util.CoordinateUtils.getChunkSectionX(newSectionPos) >> shift;
-+ int newRegionZ = io.papermc.paper.util.CoordinateUtils.getChunkSectionZ(newSectionPos) >> shift;
-+
-+ if (oldRegionX != newRegionX || oldRegionZ != newRegionZ) {
-+ PersistentEntitySectionManager.this.removeNavigatorsFromData((Entity)this.entity, oldChunkX, oldChunkZ);
-+ }
-+ // Paper end
-
- if (!this.currentSection.remove(this.entity)) {
- PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (moving to {})", new Object[]{this.entity, SectionPos.of(this.currentSectionKey), i});
-@@ -484,6 +557,11 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
- entitysection.add(this.entity);
- this.currentSection = entitysection;
- this.currentSectionKey = i;
-+ // Paper start
-+ if ((oldRegionX != newRegionX || oldRegionZ != newRegionZ) && oldVisibility.isTicking() && entitysection.getStatus().isTicking()) {
-+ PersistentEntitySectionManager.this.addNavigatorsIfPathingToRegion((Entity)this.entity);
-+ }
-+ // Paper end
- this.updateStatus(visibility, entitysection.getStatus());
- }
-
diff --git a/patches/server/0785-don-t-attempt-to-teleport-dead-entities.patch b/patches/server/0759-don-t-attempt-to-teleport-dead-entities.patch
index 0d9f0564a4..9d1e544ed0 100644
--- a/patches/server/0785-don-t-attempt-to-teleport-dead-entities.patch
+++ b/patches/server/0759-don-t-attempt-to-teleport-dead-entities.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] don't attempt to teleport dead entities
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index cef2cad3c278c4d2258f68dcbe6936c6637ca90a..036894c89e6dcc98b4e53c859d531163ed155a32 100644
+index c455abc1f41aa9372dec6db7a304bc26ce1b7973..ff74f075db4c401a0a8388c04f234afbe8d20c96 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -779,7 +779,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -780,7 +780,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
// CraftBukkit start
public void postTick() {
// No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle
diff --git a/patches/server/0786-Prevent-excessive-velocity-through-repeated-crits.patch b/patches/server/0760-Prevent-excessive-velocity-through-repeated-crits.patch
index 51d947382e..51d947382e 100644
--- a/patches/server/0786-Prevent-excessive-velocity-through-repeated-crits.patch
+++ b/patches/server/0760-Prevent-excessive-velocity-through-repeated-crits.patch
diff --git a/patches/server/0787-Remove-client-side-code-using-deprecated-for-removal.patch b/patches/server/0761-Remove-client-side-code-using-deprecated-for-removal.patch
index d96ebe539e..d96ebe539e 100644
--- a/patches/server/0787-Remove-client-side-code-using-deprecated-for-removal.patch
+++ b/patches/server/0761-Remove-client-side-code-using-deprecated-for-removal.patch
diff --git a/patches/server/0789-Always-parse-protochunk-light-sources-unless-it-is-m.patch b/patches/server/0762-Always-parse-protochunk-light-sources-unless-it-is-m.patch
index 6723907d85..be1ba0eb32 100644
--- a/patches/server/0789-Always-parse-protochunk-light-sources-unless-it-is-m.patch
+++ b/patches/server/0762-Always-parse-protochunk-light-sources-unless-it-is-m.patch
@@ -8,10 +8,10 @@ Chunks not marked as lit will always go through the light engine,
so they should always have their block sources parsed.
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-index 4df5853781a2ac89dd391374d34d9096643a2ab8..3367c75b1c132b42465d4c355a6b5fd00c3efe23 100644
+index 0254b71ab9af6d74640aa5dc0533afa386e5f57f..34e351e04ac57e47e3cea671c61cc01d17983b77 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java
-@@ -331,16 +331,33 @@ public class ChunkSerializer {
+@@ -325,16 +325,33 @@ public class ChunkSerializer {
BelowZeroRetrogen belowzeroretrogen = protochunk.getBelowZeroRetrogen();
boolean flag5 = chunkstatus.isOrAfter(ChunkStatus.LIGHT) || belowzeroretrogen != null && belowzeroretrogen.targetStatus().isOrAfter(ChunkStatus.LIGHT);
diff --git a/patches/server/0790-Fix-removing-recipes-from-RecipeIterator.patch b/patches/server/0763-Fix-removing-recipes-from-RecipeIterator.patch
index 5784fd4dc3..5784fd4dc3 100644
--- a/patches/server/0790-Fix-removing-recipes-from-RecipeIterator.patch
+++ b/patches/server/0763-Fix-removing-recipes-from-RecipeIterator.patch
diff --git a/patches/server/0791-Prevent-sending-oversized-item-data-in-equipment-and.patch b/patches/server/0764-Prevent-sending-oversized-item-data-in-equipment-and.patch
index 63ed303a2b..63ed303a2b 100644
--- a/patches/server/0791-Prevent-sending-oversized-item-data-in-equipment-and.patch
+++ b/patches/server/0764-Prevent-sending-oversized-item-data-in-equipment-and.patch
diff --git a/patches/server/0792-Hide-unnecessary-itemmeta-from-clients.patch b/patches/server/0765-Hide-unnecessary-itemmeta-from-clients.patch
index 9442632b57..9442632b57 100644
--- a/patches/server/0792-Hide-unnecessary-itemmeta-from-clients.patch
+++ b/patches/server/0765-Hide-unnecessary-itemmeta-from-clients.patch
diff --git a/patches/server/0793-Fix-kelp-modifier-changing-growth-for-other-crops.patch b/patches/server/0766-Fix-kelp-modifier-changing-growth-for-other-crops.patch
index 60f87f5973..60f87f5973 100644
--- a/patches/server/0793-Fix-kelp-modifier-changing-growth-for-other-crops.patch
+++ b/patches/server/0766-Fix-kelp-modifier-changing-growth-for-other-crops.patch
diff --git a/patches/server/0794-Prevent-ContainerOpenersCounter-openCount-from-going.patch b/patches/server/0767-Prevent-ContainerOpenersCounter-openCount-from-going.patch
index 62a65ff130..62a65ff130 100644
--- a/patches/server/0794-Prevent-ContainerOpenersCounter-openCount-from-going.patch
+++ b/patches/server/0767-Prevent-ContainerOpenersCounter-openCount-from-going.patch
diff --git a/patches/server/0795-Add-PlayerItemFrameChangeEvent.patch b/patches/server/0768-Add-PlayerItemFrameChangeEvent.patch
index f9ce7fc78c..f9ce7fc78c 100644
--- a/patches/server/0795-Add-PlayerItemFrameChangeEvent.patch
+++ b/patches/server/0768-Add-PlayerItemFrameChangeEvent.patch
diff --git a/patches/server/0796-Add-player-health-update-API.patch b/patches/server/0769-Add-player-health-update-API.patch
index d83f693e81..717cd2e501 100644
--- a/patches/server/0796-Add-player-health-update-API.patch
+++ b/patches/server/0769-Add-player-health-update-API.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add player health update API
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 6bc211c98fc4c783545e5c71248f71b27e0853f8..f6ec634a22bc446286c8ecb89064cfee7719ec40 100644
+index 3ff0c79134c1c474bc1e55b879394939a345562e..e02720435b6ed5f07a13d08a209caf6df49799ad 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -2186,9 +2186,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2221,9 +2221,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.getHandle().maxHealthCache = getMaxHealth();
}
@@ -22,7 +22,7 @@ index 6bc211c98fc4c783545e5c71248f71b27e0853f8..f6ec634a22bc446286c8ecb89064cfee
if (this.getHandle().queueHealthUpdatePacket) {
this.getHandle().queuedHealthUpdatePacket = packet;
} else {
-@@ -2196,7 +2198,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -2231,7 +2233,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
// Paper end
}
diff --git a/patches/server/0797-Optimize-HashMapPalette.patch b/patches/server/0770-Optimize-HashMapPalette.patch
index ef1880895f..ef1880895f 100644
--- a/patches/server/0797-Optimize-HashMapPalette.patch
+++ b/patches/server/0770-Optimize-HashMapPalette.patch
diff --git a/patches/server/0798-Allow-delegation-to-vanilla-chunk-gen.patch b/patches/server/0771-Allow-delegation-to-vanilla-chunk-gen.patch
index 22aa1ee9b7..fc9c73e933 100644
--- a/patches/server/0798-Allow-delegation-to-vanilla-chunk-gen.patch
+++ b/patches/server/0771-Allow-delegation-to-vanilla-chunk-gen.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Allow delegation to vanilla chunk gen
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 4adacf6f849fe41918690fb8f195727a9c880b53..96ad0b334b2985c295be4f20df06e6eb73fbb22f 100644
+index 2f793169ee3b8265059f75c5a3cc13a86acedc83..bf747c7584733014aac771ba4ca039c588582f25 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2329,6 +2329,90 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0799-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch b/patches/server/0772-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch
index 309e93dd2f..95b76cc7a4 100644
--- a/patches/server/0799-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch
+++ b/patches/server/0772-Highly-optimise-single-and-multi-AABB-VoxelShapes-an.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Mon, 4 May 2020 10:06:24 -0700
Subject: [PATCH] Highly optimise single and multi-AABB VoxelShapes and
collisions
@@ -1180,10 +1180,10 @@ index 0000000000000000000000000000000000000000..d67a40e7be030142443680c89e1763fc
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 97de35c614e1e9b0e825f9914173a3e1e0e53221..b35b36527294dd697d146d2ad817d7911145ae8c 100644
+index c6be77e9fae04f2a6dd332dbde9daf0109111e60..370bd3adb0003115ec5774eedd4dac01a41ae4af 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -423,7 +423,7 @@ public class ServerPlayer extends Player {
+@@ -408,7 +408,7 @@ public class ServerPlayer extends Player {
if (blockposition1 != null) {
this.moveTo(blockposition1, 0.0F, 0.0F);
@@ -1192,7 +1192,7 @@ index 97de35c614e1e9b0e825f9914173a3e1e0e53221..b35b36527294dd697d146d2ad817d791
break;
}
}
-@@ -431,7 +431,7 @@ public class ServerPlayer extends Player {
+@@ -416,7 +416,7 @@ public class ServerPlayer extends Player {
} else {
this.moveTo(blockposition, 0.0F, 0.0F);
@@ -1202,7 +1202,7 @@ index 97de35c614e1e9b0e825f9914173a3e1e0e53221..b35b36527294dd697d146d2ad817d791
}
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index fc14fc8017d89c27b0aeb10a5f38dafde5c15f53..70d648bc5e795355d28579cc2fda43c3c9eb255d 100644
+index 3e870218321a701b814a4dac97ff1af12142600e..4277f7fdd8f27e57708a8dee59bf1b9052a1ebe4 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -943,7 +943,7 @@ public abstract class PlayerList {
@@ -1215,10 +1215,10 @@ index fc14fc8017d89c27b0aeb10a5f38dafde5c15f53..70d648bc5e795355d28579cc2fda43c3
}
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 036894c89e6dcc98b4e53c859d531163ed155a32..a7609eb5e037a38d33ffe092502091fbf629db0f 100644
+index ff74f075db4c401a0a8388c04f234afbe8d20c96..c0dd7405b4e26f7bfcfcced164675b124ca1cc4d 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -1160,9 +1160,44 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1161,9 +1161,44 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
float f2 = this.getBlockSpeedFactor();
this.setDeltaMovement(this.getDeltaMovement().multiply((double) f2, 1.0D, (double) f2));
@@ -1266,7 +1266,7 @@ index 036894c89e6dcc98b4e53c859d531163ed155a32..a7609eb5e037a38d33ffe092502091fb
if (this.remainingFireTicks <= 0) {
this.setRemainingFireTicks(-this.getFireImmuneTicks());
}
-@@ -1306,32 +1341,78 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -1307,32 +1342,78 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
private Vec3 collide(Vec3 movement) {
@@ -1366,7 +1366,7 @@ index 036894c89e6dcc98b4e53c859d531163ed155a32..a7609eb5e037a38d33ffe092502091fb
}
public static Vec3 collideBoundingBox(@Nullable Entity entity, Vec3 movement, AABB entityBoundingBox, Level world, List<VoxelShape> collisions) {
-@@ -2454,11 +2535,30 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2455,11 +2536,30 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
float f = this.dimensions.width * 0.8F;
AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f);
@@ -1493,12 +1493,12 @@ index c0817ef8927f00e2fd3fbf3289f8041fcb494049..3f458ddd4dc04ed28510a212be76bb19
return List.of();
} else {
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
-index 24b820484497714eb8be87e07ca1d37829d4f2c9..fd74cc9c0dab84b176f7da3fbbbdbc8fd3a7e26d 100644
+index bfcd61e09a307e99d6f2e7edae33a0a069abcb51..b86c17b5572f8f74bfefd0f3c6f9d25187574392 100644
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -734,6 +734,13 @@ public abstract class BlockBehaviour {
- return this.conditionallyFullOpaque;
- }
+ protected boolean isTicking;
+ protected FluidState fluid;
// Paper end
+ // Paper start
+ private long blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_SPECIAL_BLOCK;
@@ -1510,11 +1510,10 @@ index 24b820484497714eb8be87e07ca1d37829d4f2c9..fd74cc9c0dab84b176f7da3fbbbdbc8f
public void initCache() {
this.fluid = this.getBlock().getFluidState(this.asState()); // Paper - moved from getFluid()
-@@ -743,7 +750,35 @@ public abstract class BlockBehaviour {
- }
+@@ -744,6 +751,35 @@ public abstract class BlockBehaviour {
this.shapeExceedsCube = this.cache == null || this.cache.largeCollisionShape; // Paper - moved from actual method to here
- this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - cache opacity for light
--
+ this.opacityIfCached = this.cache == null || this.isConditionallyFullOpaque() ? -1 : this.cache.lightBlock; // Paper - starlight - cache opacity for light
+
+ // Paper start
+ if (io.papermc.paper.util.CollisionUtil.isSpecialCollidingBlock(this)) {
+ this.blockCollisionBehavior = io.papermc.paper.util.CollisionUtil.KNOWN_SPECIAL_BLOCK;
diff --git a/patches/server/0800-Optimise-collision-checking-in-player-move-packet-ha.patch b/patches/server/0773-Optimise-collision-checking-in-player-move-packet-ha.patch
index a55596dbed..5296115818 100644
--- a/patches/server/0800-Optimise-collision-checking-in-player-move-packet-ha.patch
+++ b/patches/server/0773-Optimise-collision-checking-in-player-move-packet-ha.patch
@@ -1,5 +1,5 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Spottedleaf <[email protected]>
+From: Spottedleaf <[email protected]>
Date: Thu, 2 Jul 2020 12:02:43 -0700
Subject: [PATCH] Optimise collision checking in player move packet handling
diff --git a/patches/server/0802-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch b/patches/server/0774-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch
index 344c081f95..ffb998758e 100644
--- a/patches/server/0802-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch
+++ b/patches/server/0774-Fix-ChunkSnapshot-isSectionEmpty-int-and-optimize-Pa.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Fix ChunkSnapshot#isSectionEmpty(int) and optimize
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
-index 0b9312dc5ee43d2d450dc6e9f07a9ac0320955ca..4c109bbc4694e9d3d8804cc64650f79abf315e3a 100644
+index 7f23c69e7d6232190a2c75cbc79e1ea56971e8fb..75c6b0aa2cb4ae8637460006ff24d7f6028396be 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java
@@ -283,13 +283,17 @@ public class CraftChunk implements Chunk {
diff --git a/patches/server/0803-Update-Log4j.patch b/patches/server/0775-Update-Log4j.patch
index 41cc02c122..41cc02c122 100644
--- a/patches/server/0803-Update-Log4j.patch
+++ b/patches/server/0775-Update-Log4j.patch
diff --git a/patches/server/0804-Add-more-Campfire-API.patch b/patches/server/0776-Add-more-Campfire-API.patch
index 5381096620..5381096620 100644
--- a/patches/server/0804-Add-more-Campfire-API.patch
+++ b/patches/server/0776-Add-more-Campfire-API.patch
diff --git a/patches/server/0805-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch b/patches/server/0777-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch
index fca5a00257..b7ed484259 100644
--- a/patches/server/0805-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch
+++ b/patches/server/0777-Only-write-chunk-data-to-disk-if-it-serializes-witho.patch
@@ -47,10 +47,10 @@ index 8ff8855c5267379b3a5f5d8baa4a275ffee2c4bf..fc3442b4c7e1f22080fe6bf36d4fade1
ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count);
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-index f38ec8914e1953091ab65aa3aaefc886d3eede8a..c2356ed1a00fd8087cca285be5e7f6a5442e73fb 100644
+index e71a451a6b385716cc46db7350b58b4192304547..8db3bcc63aeb23e5b50864ebea675acc75d184ff 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-@@ -298,10 +298,17 @@ public class RegionFileStorage implements AutoCloseable {
+@@ -302,10 +302,17 @@ public class RegionFileStorage implements AutoCloseable {
NbtIo.write(nbt, (DataOutput) dataoutputstream);
regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
@@ -69,7 +69,7 @@ index f38ec8914e1953091ab65aa3aaefc886d3eede8a..c2356ed1a00fd8087cca285be5e7f6a5
} catch (Throwable throwable1) {
throwable.addSuppressed(throwable1);
}
-@@ -309,10 +316,7 @@ public class RegionFileStorage implements AutoCloseable {
+@@ -313,10 +320,7 @@ public class RegionFileStorage implements AutoCloseable {
throw throwable;
}
@@ -81,7 +81,7 @@ index f38ec8914e1953091ab65aa3aaefc886d3eede8a..c2356ed1a00fd8087cca285be5e7f6a5
}
// Paper start
return;
-@@ -358,4 +362,13 @@ public class RegionFileStorage implements AutoCloseable {
+@@ -362,4 +366,13 @@ public class RegionFileStorage implements AutoCloseable {
}
}
diff --git a/patches/server/0806-Fix-tripwire-state-inconsistency.patch b/patches/server/0778-Fix-tripwire-state-inconsistency.patch
index eba71d4136..eba71d4136 100644
--- a/patches/server/0806-Fix-tripwire-state-inconsistency.patch
+++ b/patches/server/0778-Fix-tripwire-state-inconsistency.patch
diff --git a/patches/server/0807-Fix-fluid-logging-on-Block-breakNaturally.patch b/patches/server/0779-Fix-fluid-logging-on-Block-breakNaturally.patch
index 1f9012a293..1f9012a293 100644
--- a/patches/server/0807-Fix-fluid-logging-on-Block-breakNaturally.patch
+++ b/patches/server/0779-Fix-fluid-logging-on-Block-breakNaturally.patch
diff --git a/patches/server/0808-Forward-CraftEntity-in-teleport-command.patch b/patches/server/0780-Forward-CraftEntity-in-teleport-command.patch
index efe396039c..69c880d99c 100644
--- a/patches/server/0808-Forward-CraftEntity-in-teleport-command.patch
+++ b/patches/server/0780-Forward-CraftEntity-in-teleport-command.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Forward CraftEntity in teleport command
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index a7609eb5e037a38d33ffe092502091fbf629db0f..4f5a42d2dc7eee3745e4defb69baafd4c0ab72ec 100644
+index c0dd7405b4e26f7bfcfcced164675b124ca1cc4d..8958c95105394601ca91b4318b598e45ced9e706 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -3276,6 +3276,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3277,6 +3277,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public void restoreFrom(Entity original) {
@@ -22,7 +22,7 @@ index a7609eb5e037a38d33ffe092502091fbf629db0f..4f5a42d2dc7eee3745e4defb69baafd4
CompoundTag nbttagcompound = original.saveWithoutId(new CompoundTag());
nbttagcompound.remove("Dimension");
-@@ -3357,10 +3364,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -3358,10 +3365,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (worldserver.getTypeKey() == LevelStem.END) { // CraftBukkit
ServerLevel.makeObsidianPlatform(worldserver, this); // CraftBukkit
}
diff --git a/patches/server/0809-Improve-scoreboard-entries.patch b/patches/server/0781-Improve-scoreboard-entries.patch
index 2a65cbf36c..2a65cbf36c 100644
--- a/patches/server/0809-Improve-scoreboard-entries.patch
+++ b/patches/server/0781-Improve-scoreboard-entries.patch
diff --git a/patches/server/0810-Entity-powdered-snow-API.patch b/patches/server/0782-Entity-powdered-snow-API.patch
index e3764b4811..e3764b4811 100644
--- a/patches/server/0810-Entity-powdered-snow-API.patch
+++ b/patches/server/0782-Entity-powdered-snow-API.patch
diff --git a/patches/server/0811-Add-API-for-item-entity-health.patch b/patches/server/0783-Add-API-for-item-entity-health.patch
index d923270a18..d923270a18 100644
--- a/patches/server/0811-Add-API-for-item-entity-health.patch
+++ b/patches/server/0783-Add-API-for-item-entity-health.patch
diff --git a/patches/server/0812-Fix-entity-type-tags-suggestions-in-selectors.patch b/patches/server/0784-Fix-entity-type-tags-suggestions-in-selectors.patch
index ca29c1bbf2..ca29c1bbf2 100644
--- a/patches/server/0812-Fix-entity-type-tags-suggestions-in-selectors.patch
+++ b/patches/server/0784-Fix-entity-type-tags-suggestions-in-selectors.patch
diff --git a/patches/server/0813-Configurable-max-block-light-for-monster-spawning.patch b/patches/server/0785-Configurable-max-block-light-for-monster-spawning.patch
index baf913d352..baf913d352 100644
--- a/patches/server/0813-Configurable-max-block-light-for-monster-spawning.patch
+++ b/patches/server/0785-Configurable-max-block-light-for-monster-spawning.patch
diff --git a/patches/server/0814-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch b/patches/server/0786-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch
index 698fbc2d5e..698fbc2d5e 100644
--- a/patches/server/0814-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch
+++ b/patches/server/0786-Fix-sticky-pistons-and-BlockPistonRetractEvent.patch
diff --git a/patches/server/0815-Load-effect-amplifiers-greater-than-127-correctly.patch b/patches/server/0787-Load-effect-amplifiers-greater-than-127-correctly.patch
index 768bf70b1f..768bf70b1f 100644
--- a/patches/server/0815-Load-effect-amplifiers-greater-than-127-correctly.patch
+++ b/patches/server/0787-Load-effect-amplifiers-greater-than-127-correctly.patch
diff --git a/patches/server/0816-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch b/patches/server/0788-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch
index 9f3ee548ee..9f3ee548ee 100644
--- a/patches/server/0816-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch
+++ b/patches/server/0788-Expose-isFuel-and-canSmelt-methods-to-FurnaceInvento.patch
diff --git a/patches/server/0817-Fix-bees-aging-inside-hives.patch b/patches/server/0789-Fix-bees-aging-inside-hives.patch
index 2be01a557c..2be01a557c 100644
--- a/patches/server/0817-Fix-bees-aging-inside-hives.patch
+++ b/patches/server/0789-Fix-bees-aging-inside-hives.patch
diff --git a/patches/server/0818-Bucketable-API.patch b/patches/server/0790-Bucketable-API.patch
index ff1194970a..ff1194970a 100644
--- a/patches/server/0818-Bucketable-API.patch
+++ b/patches/server/0790-Bucketable-API.patch
diff --git a/patches/server/0819-Check-player-world-in-endPortalSoundRadius.patch b/patches/server/0791-Check-player-world-in-endPortalSoundRadius.patch
index a5edd3fe44..a5edd3fe44 100644
--- a/patches/server/0819-Check-player-world-in-endPortalSoundRadius.patch
+++ b/patches/server/0791-Check-player-world-in-endPortalSoundRadius.patch
diff --git a/patches/server/0820-Validate-usernames.patch b/patches/server/0792-Validate-usernames.patch
index 34dcaf0abc..299e6235a5 100644
--- a/patches/server/0820-Validate-usernames.patch
+++ b/patches/server/0792-Validate-usernames.patch
@@ -56,7 +56,7 @@ index c8e373bccb8c8edd8b7a3a812e04fcf6c836df2d..ef6a55a9f0055e23e8887eb3efd17cc2
GameProfile gameprofile = this.server.getSingleplayerProfile();
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 70d648bc5e795355d28579cc2fda43c3c9eb255d..67f90c75aa4858bf1575bf7b0a62b8113de7c2ea 100644
+index 4277f7fdd8f27e57708a8dee59bf1b9052a1ebe4..133bf27e301aa5815cc7d7e275b2b08cdc6e4392 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -714,7 +714,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0821-Fix-saving-configs-with-more-long-comments.patch b/patches/server/0793-Fix-saving-configs-with-more-long-comments.patch
index 7d52b01085..7d52b01085 100644
--- a/patches/server/0821-Fix-saving-configs-with-more-long-comments.patch
+++ b/patches/server/0793-Fix-saving-configs-with-more-long-comments.patch
diff --git a/patches/server/0822-Make-water-animal-spawn-height-configurable.patch b/patches/server/0794-Make-water-animal-spawn-height-configurable.patch
index dea45f0b70..dea45f0b70 100644
--- a/patches/server/0822-Make-water-animal-spawn-height-configurable.patch
+++ b/patches/server/0794-Make-water-animal-spawn-height-configurable.patch
diff --git a/patches/server/0823-Expose-vanilla-BiomeProvider-from-WorldInfo.patch b/patches/server/0795-Expose-vanilla-BiomeProvider-from-WorldInfo.patch
index 7a4e27a8cd..4e8650b1ea 100644
--- a/patches/server/0823-Expose-vanilla-BiomeProvider-from-WorldInfo.patch
+++ b/patches/server/0795-Expose-vanilla-BiomeProvider-from-WorldInfo.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Expose vanilla BiomeProvider from WorldInfo
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 857af8ed37c10723d6cd81d7f3c2ba6169021050..28e2f22cc2e5b6dd47705cdd5095ff2394979c8f 100644
+index 9011dbbe6302deb7318d31b9db3d2419a1871c07..2310b0314cc8a0f298eb82839dbf601901415dea 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -562,7 +562,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -18,7 +18,7 @@ index 857af8ed37c10723d6cd81d7f3c2ba6169021050..28e2f22cc2e5b6dd47705cdd5095ff23
biomeProvider = gen.getDefaultBiomeProvider(worldInfo);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 96ad0b334b2985c295be4f20df06e6eb73fbb22f..2b57b60a3ecb5ac170f2d70532dc8439c5a3459f 100644
+index bf747c7584733014aac771ba4ca039c588582f25..e69480421f4fbebcf2a2000e8fa91fc9a9d34201 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1212,7 +1212,7 @@ public final class CraftServer implements Server {
@@ -31,7 +31,7 @@ index 96ad0b334b2985c295be4f20df06e6eb73fbb22f..2b57b60a3ecb5ac170f2d70532dc8439
biomeProvider = generator.getDefaultBiomeProvider(worldInfo);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 5138182d8004aec69848d10b8cc63453c1e8808f..7916426a9d7953c2cc15a319adea8d982b63b773 100644
+index 65c82c3ec11b29245f7d92e402f2cf2ab5b8dae4..36a8552f046f8f6bf8138bef4ff0c526425e57a4 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -200,6 +200,30 @@ public class CraftWorld extends CraftRegionAccessor implements World {
diff --git a/patches/server/0824-Add-config-option-for-worlds-affected-by-time-cmd.patch b/patches/server/0796-Add-config-option-for-worlds-affected-by-time-cmd.patch
index de155852d0..de155852d0 100644
--- a/patches/server/0824-Add-config-option-for-worlds-affected-by-time-cmd.patch
+++ b/patches/server/0796-Add-config-option-for-worlds-affected-by-time-cmd.patch
diff --git a/patches/server/0825-Add-new-overload-to-PersistentDataContainer-has.patch b/patches/server/0797-Add-new-overload-to-PersistentDataContainer-has.patch
index d19f7179e1..d19f7179e1 100644
--- a/patches/server/0825-Add-new-overload-to-PersistentDataContainer-has.patch
+++ b/patches/server/0797-Add-new-overload-to-PersistentDataContainer-has.patch
diff --git a/patches/server/0826-Multiple-Entries-with-Scoreboards.patch b/patches/server/0798-Multiple-Entries-with-Scoreboards.patch
index b644176ac9..b644176ac9 100644
--- a/patches/server/0826-Multiple-Entries-with-Scoreboards.patch
+++ b/patches/server/0798-Multiple-Entries-with-Scoreboards.patch
diff --git a/patches/server/0827-Reset-placed-block-on-exception.patch b/patches/server/0799-Reset-placed-block-on-exception.patch
index 3881c0d6de..3881c0d6de 100644
--- a/patches/server/0827-Reset-placed-block-on-exception.patch
+++ b/patches/server/0799-Reset-placed-block-on-exception.patch
diff --git a/patches/server/0828-Add-configurable-height-for-slime-spawn.patch b/patches/server/0800-Add-configurable-height-for-slime-spawn.patch
index a05bae35ee..a05bae35ee 100644
--- a/patches/server/0828-Add-configurable-height-for-slime-spawn.patch
+++ b/patches/server/0800-Add-configurable-height-for-slime-spawn.patch
diff --git a/patches/server/0829-Added-getHostname-to-AsyncPlayerPreLoginEvent.patch b/patches/server/0801-Added-getHostname-to-AsyncPlayerPreLoginEvent.patch
index 4963f056c2..4963f056c2 100644
--- a/patches/server/0829-Added-getHostname-to-AsyncPlayerPreLoginEvent.patch
+++ b/patches/server/0801-Added-getHostname-to-AsyncPlayerPreLoginEvent.patch
diff --git a/patches/server/0830-Fix-xp-reward-for-baby-zombies.patch b/patches/server/0802-Fix-xp-reward-for-baby-zombies.patch
index 42920471c5..42920471c5 100644
--- a/patches/server/0830-Fix-xp-reward-for-baby-zombies.patch
+++ b/patches/server/0802-Fix-xp-reward-for-baby-zombies.patch
diff --git a/patches/server/0831-Kick-on-main-for-illegal-chat.patch b/patches/server/0803-Kick-on-main-for-illegal-chat.patch
index 054da69477..054da69477 100644
--- a/patches/server/0831-Kick-on-main-for-illegal-chat.patch
+++ b/patches/server/0803-Kick-on-main-for-illegal-chat.patch
diff --git a/patches/server/0832-Multi-Block-Change-API-Implementation.patch b/patches/server/0804-Multi-Block-Change-API-Implementation.patch
index a5e8971f51..616320f748 100644
--- a/patches/server/0832-Multi-Block-Change-API-Implementation.patch
+++ b/patches/server/0804-Multi-Block-Change-API-Implementation.patch
@@ -25,10 +25,10 @@ index 285da70a15f6e4c868747af9d40ac30bd4e42ef4..a0aeac9c29300a0cf6bad55133019e8c
public void write(FriendlyByteBuf buf) {
buf.writeLong(this.sectionPos.asLong());
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index f6ec634a22bc446286c8ecb89064cfee7719ec40..b318842214ede551215fd68af033feb1c8ef6604 100644
+index e02720435b6ed5f07a13d08a209caf6df49799ad..674b7ff0c1e372ffb06336841e129d5c2130b6f9 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-@@ -888,6 +888,35 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
+@@ -923,6 +923,35 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
this.getHandle().connection.send(packet);
}
diff --git a/patches/server/0833-Fix-NotePlayEvent.patch b/patches/server/0805-Fix-NotePlayEvent.patch
index 83040e523a..83040e523a 100644
--- a/patches/server/0833-Fix-NotePlayEvent.patch
+++ b/patches/server/0805-Fix-NotePlayEvent.patch
diff --git a/patches/server/0834-Freeze-Tick-Lock-API.patch b/patches/server/0806-Freeze-Tick-Lock-API.patch
index ed8c7e90b7..99aa867eaa 100644
--- a/patches/server/0834-Freeze-Tick-Lock-API.patch
+++ b/patches/server/0806-Freeze-Tick-Lock-API.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Freeze Tick Lock API
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 4f5a42d2dc7eee3745e4defb69baafd4c0ab72ec..8060e80e69a75a00c7d9d9cc6717088e04f45212 100644
+index 8958c95105394601ca91b4318b598e45ced9e706..fd536e670036a365f6d0e8c168ef4b5fdf597615 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -397,6 +397,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@@ -16,7 +16,7 @@ index 4f5a42d2dc7eee3745e4defb69baafd4c0ab72ec..8060e80e69a75a00c7d9d9cc6717088e
public void setOrigin(@javax.annotation.Nonnull Location location) {
this.origin = location.toVector();
-@@ -825,7 +826,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -826,7 +827,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.setRemainingFireTicks(this.remainingFireTicks - 1);
}
@@ -25,7 +25,7 @@ index 4f5a42d2dc7eee3745e4defb69baafd4c0ab72ec..8060e80e69a75a00c7d9d9cc6717088e
this.setTicksFrozen(0);
this.level.levelEvent((Player) null, 1009, this.blockPosition, 1);
}
-@@ -2261,6 +2262,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2262,6 +2263,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (fromNetherPortal) {
nbt.putBoolean("Paper.FromNetherPortal", true);
}
@@ -35,7 +35,7 @@ index 4f5a42d2dc7eee3745e4defb69baafd4c0ab72ec..8060e80e69a75a00c7d9d9cc6717088e
// Paper end
return nbt;
} catch (Throwable throwable) {
-@@ -2425,6 +2429,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2426,6 +2430,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (spawnReason == null) {
spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT;
}
diff --git a/patches/server/0835-Dolphin-API.patch b/patches/server/0807-Dolphin-API.patch
index 4bb12a6785..4bb12a6785 100644
--- a/patches/server/0835-Dolphin-API.patch
+++ b/patches/server/0807-Dolphin-API.patch
diff --git a/patches/server/0836-More-PotionEffectType-API.patch b/patches/server/0808-More-PotionEffectType-API.patch
index 5d8c4415d3..5d8c4415d3 100644
--- a/patches/server/0836-More-PotionEffectType-API.patch
+++ b/patches/server/0808-More-PotionEffectType-API.patch
diff --git a/patches/server/0837-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch b/patches/server/0809-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch
index 02ee15a8e1..02ee15a8e1 100644
--- a/patches/server/0837-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch
+++ b/patches/server/0809-Use-a-CHM-for-StructureTemplate.Pallete-cache.patch
diff --git a/patches/server/0838-API-for-creating-command-sender-which-forwards-feedb.patch b/patches/server/0810-API-for-creating-command-sender-which-forwards-feedb.patch
index 1e348f430f..c06c0406b5 100644
--- a/patches/server/0838-API-for-creating-command-sender-which-forwards-feedb.patch
+++ b/patches/server/0810-API-for-creating-command-sender-which-forwards-feedb.patch
@@ -122,7 +122,7 @@ index 0000000000000000000000000000000000000000..e3a5f1ec376319bdfda87fa27ae217bf
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 2b57b60a3ecb5ac170f2d70532dc8439c5a3459f..169f0162479a4a52a6637a9d16eef13c098c8d1a 100644
+index e69480421f4fbebcf2a2000e8fa91fc9a9d34201..126da8691f515998626492667b6a145ffe1bbd2d 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1990,6 +1990,13 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0839-Add-config-for-stronghold-seed.patch b/patches/server/0811-Add-config-for-stronghold-seed.patch
index 5118cd7c4b..d052da6c16 100644
--- a/patches/server/0839-Add-config-for-stronghold-seed.patch
+++ b/patches/server/0811-Add-config-for-stronghold-seed.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config for stronghold seed
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
-index bdcfa5aac4cd0bd5841922295cc8fbb6ca69bd68..19ffd93b7bc745d9a6822f1e5642d2f640f61df7 100644
+index cc79ca7ee770408ec59b1f6a3f4ec58e23bd2619..cb64b46eb874bb7ce22cdbf9e9629c929a05fb61 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java
@@ -236,7 +236,13 @@ public abstract class ChunkGenerator {
diff --git a/patches/server/0840-Implement-regenerateChunk.patch b/patches/server/0812-Implement-regenerateChunk.patch
index ba57a50388..52ed98a62f 100644
--- a/patches/server/0840-Implement-regenerateChunk.patch
+++ b/patches/server/0812-Implement-regenerateChunk.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Implement regenerateChunk
Co-authored-by: Jason Penilla <[email protected]>
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 7916426a9d7953c2cc15a319adea8d982b63b773..73e7181655b78f5bff90d07edfe6c5408cc08235 100644
+index 36a8552f046f8f6bf8138bef4ff0c526425e57a4..7757e5b1aec0b1f0cba79aa4618a7fe1d210b72c 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -139,6 +139,7 @@ import org.bukkit.util.Vector;
diff --git a/patches/server/0841-Fix-cancelled-powdered-snow-bucket-placement.patch b/patches/server/0813-Fix-cancelled-powdered-snow-bucket-placement.patch
index c6754ee3e6..c6754ee3e6 100644
--- a/patches/server/0841-Fix-cancelled-powdered-snow-bucket-placement.patch
+++ b/patches/server/0813-Fix-cancelled-powdered-snow-bucket-placement.patch
diff --git a/patches/server/0842-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch b/patches/server/0814-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch
index 6a270423c8..4e491d2b35 100644
--- a/patches/server/0842-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch
+++ b/patches/server/0814-Add-missing-Validate-calls-to-CraftServer-getSpawnLi.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Add missing Validate calls to CraftServer#getSpawnLimit
Copies appropriate checks from CraftWorld#getSpawnLimit
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 169f0162479a4a52a6637a9d16eef13c098c8d1a..b1703293e46fcc3abe9b994657288b3dc41f034a 100644
+index 126da8691f515998626492667b6a145ffe1bbd2d..a9a084edf27908b42ee4c2673df877469e65ca27 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2164,6 +2164,8 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0843-Add-GameEvent-tags.patch b/patches/server/0815-Add-GameEvent-tags.patch
index 71d11d1e66..bcde64da83 100644
--- a/patches/server/0843-Add-GameEvent-tags.patch
+++ b/patches/server/0815-Add-GameEvent-tags.patch
@@ -45,7 +45,7 @@ index 0000000000000000000000000000000000000000..cb78a3d4e21376ea24347187478525d5
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index b1703293e46fcc3abe9b994657288b3dc41f034a..989dcf684ae448e6ff678bd2e72601005a770803 100644
+index a9a084edf27908b42ee4c2673df877469e65ca27..d457e7ece66b2f901dc12d459533129396ac134d 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2575,6 +2575,15 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0844-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch b/patches/server/0816-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch
index 89a4c1af23..b39e446490 100644
--- a/patches/server/0844-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch
+++ b/patches/server/0816-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch
@@ -9,10 +9,10 @@ This might result in chunks loading far slower in the nether,
for example.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 28e2f22cc2e5b6dd47705cdd5095ff2394979c8f..98654c77b2925228458fae447510ec1d6b217370 100644
+index efe7a3afaf5ef548bded4dc2957375fdbc605d63..8a51a5da86e3e9b277109b0acf2982197292682e 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1314,6 +1314,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1308,6 +1308,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.executeMidTickTasks(); // Paper - execute chunk tasks mid tick
return true;
} else {
@@ -20,7 +20,7 @@ index 28e2f22cc2e5b6dd47705cdd5095ff2394979c8f..98654c77b2925228458fae447510ec1d
if (this.haveTime()) {
Iterator iterator = this.getAllLevels().iterator();
-@@ -1321,12 +1322,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1315,12 +1316,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
ServerLevel worldserver = (ServerLevel) iterator.next();
if (worldserver.getChunkSource().pollTask()) {
diff --git a/patches/server/0846-Furnace-RecipesUsed-API.patch b/patches/server/0817-Furnace-RecipesUsed-API.patch
index 2e6cf1c2fe..2e6cf1c2fe 100644
--- a/patches/server/0846-Furnace-RecipesUsed-API.patch
+++ b/patches/server/0817-Furnace-RecipesUsed-API.patch
diff --git a/patches/server/0847-Configurable-sculk-sensor-listener-range.patch b/patches/server/0818-Configurable-sculk-sensor-listener-range.patch
index f19796285a..f19796285a 100644
--- a/patches/server/0847-Configurable-sculk-sensor-listener-range.patch
+++ b/patches/server/0818-Configurable-sculk-sensor-listener-range.patch
diff --git a/patches/server/0848-Add-missing-block-data-mins-and-maxes.patch b/patches/server/0819-Add-missing-block-data-mins-and-maxes.patch
index 83ba5f6bc1..83ba5f6bc1 100644
--- a/patches/server/0848-Add-missing-block-data-mins-and-maxes.patch
+++ b/patches/server/0819-Add-missing-block-data-mins-and-maxes.patch
diff --git a/patches/server/0849-Option-to-have-default-CustomSpawners-in-custom-worl.patch b/patches/server/0820-Option-to-have-default-CustomSpawners-in-custom-worl.patch
index b1bb942ed4..9055e5c403 100644
--- a/patches/server/0849-Option-to-have-default-CustomSpawners-in-custom-worl.patch
+++ b/patches/server/0820-Option-to-have-default-CustomSpawners-in-custom-worl.patch
@@ -10,7 +10,7 @@ just looking at the LevelStem key, look at the DimensionType key which
is one level below that. Defaults to off to keep vanilla behavior.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 98654c77b2925228458fae447510ec1d6b217370..d53972b69322e03d7e8054a2dcdbdb963055988c 100644
+index 161560836fd8a53cdf899205905e4e83f420a614..ab87b5c7c24e033f760d8619e7b0f728d7787073 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -589,7 +589,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0850-Put-world-into-worldlist-before-initing-the-world.patch b/patches/server/0821-Put-world-into-worldlist-before-initing-the-world.patch
index 7f81f976a5..555d470b26 100644
--- a/patches/server/0850-Put-world-into-worldlist-before-initing-the-world.patch
+++ b/patches/server/0821-Put-world-into-worldlist-before-initing-the-world.patch
@@ -7,7 +7,7 @@ Some parts of legacy conversion will need the overworld
to get the legacy structure data storage
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index d53972b69322e03d7e8054a2dcdbdb963055988c..5f3592f570fae791f2bd55d05d02c8bb5ecc0f85 100644
+index ab87b5c7c24e033f760d8619e7b0f728d7787073..0c375349e2489e2904ef53e5aa96f5f2c91353ec 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -601,9 +601,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -23,7 +23,7 @@ index d53972b69322e03d7e8054a2dcdbdb963055988c..5f3592f570fae791f2bd55d05d02c8bb
if (worlddata.getCustomBossEvents() != null) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 989dcf684ae448e6ff678bd2e72601005a770803..5b13c0b6e7c582e4858515889a46ac510ed29c74 100644
+index d457e7ece66b2f901dc12d459533129396ac134d..418ec6b1fb062ad5b4642c2a5ccc32775f8c8b48 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1241,10 +1241,11 @@ public final class CraftServer implements Server {
@@ -38,4 +38,4 @@ index 989dcf684ae448e6ff678bd2e72601005a770803..5b13c0b6e7c582e4858515889a46ac51
+ // Paper - move up
this.getServer().prepareLevels(internal.getChunkSource().chunkMap.progressListener, internal);
- internal.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API
+ //internal.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API // Paper - rewrite chunk system
diff --git a/patches/server/0851-Fix-Entity-Position-Desync.patch b/patches/server/0822-Fix-Entity-Position-Desync.patch
index f4b0a0dc54..f4b0a0dc54 100644
--- a/patches/server/0851-Fix-Entity-Position-Desync.patch
+++ b/patches/server/0822-Fix-Entity-Position-Desync.patch
diff --git a/patches/server/0852-Custom-Potion-Mixes.patch b/patches/server/0823-Custom-Potion-Mixes.patch
index 1d550ec4d5..83f97585c5 100644
--- a/patches/server/0852-Custom-Potion-Mixes.patch
+++ b/patches/server/0823-Custom-Potion-Mixes.patch
@@ -24,10 +24,10 @@ index 0000000000000000000000000000000000000000..6b0bed550763f34e18c9e92f9a47ec0c
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 5f3592f570fae791f2bd55d05d02c8bb5ecc0f85..df08b7afcf19ce694a87c25e8589c0c72521c5db 100644
+index 56c49187c63fd124738ed1efe634ddfcd0f846f9..41543b69934c5e5c050636482c7d2b93a6bab796 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2049,6 +2049,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2043,6 +2043,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.worldData.setDataPackConfig(MinecraftServer.getSelectedPacks(this.packRepository));
this.resources.managers.updateRegistryTags(this.registryAccess());
io.papermc.paper.registry.PaperRegistry.clearCaches(); // Paper
@@ -164,7 +164,7 @@ index 3d688e334c7287f41460bd866bfd1155e8bb55d2..55006724ccec9f3de828ec18693728e9
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 5b13c0b6e7c582e4858515889a46ac510ed29c74..669fb0206a09377a8682325bc4bd744d285791f6 100644
+index 418ec6b1fb062ad5b4642c2a5ccc32775f8c8b48..b819ec350d230ea9425b4c1512dba2438408f066 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -285,6 +285,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0854-Fix-Fluid-tags-isTagged-method.patch b/patches/server/0824-Fix-Fluid-tags-isTagged-method.patch
index 0ce876073c..0ce876073c 100644
--- a/patches/server/0854-Fix-Fluid-tags-isTagged-method.patch
+++ b/patches/server/0824-Fix-Fluid-tags-isTagged-method.patch
diff --git a/patches/server/0855-Force-close-world-loading-screen.patch b/patches/server/0825-Force-close-world-loading-screen.patch
index 1f75753d30..9294146475 100644
--- a/patches/server/0855-Force-close-world-loading-screen.patch
+++ b/patches/server/0825-Force-close-world-loading-screen.patch
@@ -10,7 +10,7 @@ so we do not need that. The client only needs the chunk it is currently in to
be loaded to close the loading screen, so we just send an empty one.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index b588e14b2826bda5b03b4fc497efcb96b566541a..5a5ea1f9d6e978916d32b170ecf7f848d2524303 100644
+index 133bf27e301aa5815cc7d7e275b2b08cdc6e4392..f29700d1a7cfebbe4a54b06470ab73d1522c5dd0 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -429,6 +429,16 @@ public abstract class PlayerList {
diff --git a/patches/server/0856-Fix-falling-block-spawn-methods.patch b/patches/server/0826-Fix-falling-block-spawn-methods.patch
index c8f73c8b56..7aaafc2cf1 100644
--- a/patches/server/0856-Fix-falling-block-spawn-methods.patch
+++ b/patches/server/0826-Fix-falling-block-spawn-methods.patch
@@ -21,10 +21,10 @@ index d1fca0e3227b5f37c11367548be362f5a49b6a71..5628940cd3c3566c5db2beda506d4f20
if (Snowball.class.isAssignableFrom(clazz)) {
entity = new net.minecraft.world.entity.projectile.Snowball(world, x, y, z);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index cf6fce4f3bddcbbae59fd128cf661e4506b9d2c5..23c68b6cd26fbd05685ebcfbb5e81db4c8dedb29 100644
+index 7757e5b1aec0b1f0cba79aa4618a7fe1d210b72c..8ab92e7a9ed2008b90f3a816d972512f5dc58876 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -1408,7 +1408,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1395,7 +1395,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
Validate.notNull(material, "Material cannot be null");
Validate.isTrue(material.isBlock(), "Material must be a block");
@@ -38,7 +38,7 @@ index cf6fce4f3bddcbbae59fd128cf661e4506b9d2c5..23c68b6cd26fbd05685ebcfbb5e81db4
return (FallingBlock) entity.getBukkitEntity();
}
-@@ -1417,7 +1422,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1404,7 +1409,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
Validate.notNull(location, "Location cannot be null");
Validate.notNull(data, "BlockData cannot be null");
diff --git a/patches/server/0857-Expose-furnace-minecart-push-values.patch b/patches/server/0827-Expose-furnace-minecart-push-values.patch
index 4086d94249..4086d94249 100644
--- a/patches/server/0857-Expose-furnace-minecart-push-values.patch
+++ b/patches/server/0827-Expose-furnace-minecart-push-values.patch
diff --git a/patches/server/0858-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch b/patches/server/0828-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch
index a9cdda7a75..a9cdda7a75 100644
--- a/patches/server/0858-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch
+++ b/patches/server/0828-Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch
diff --git a/patches/server/0860-More-Projectile-API.patch b/patches/server/0829-More-Projectile-API.patch
index a942a94ead..d15949da57 100644
--- a/patches/server/0860-More-Projectile-API.patch
+++ b/patches/server/0829-More-Projectile-API.patch
@@ -195,7 +195,7 @@ index 0db8aa840ea026d48215ac5dc80ffde5f12725b1..397e0df15a0e64e5bc522f62f3b327a5
public net.minecraft.world.entity.projectile.ThrownPotion getHandle() {
return (net.minecraft.world.entity.projectile.ThrownPotion) entity;
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-index a16d6c171a62299fe4bca28dcbb1243b30268f78..8156f8cfe924a80c9cecc0f1555d6b543bde7117 100644
+index f3abcd5949011eaef3d1ba68f4cc0751042d2834..bfb1ad0b6e20e10fee53f94a3e6c4f8aad7aae7d 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -278,12 +278,20 @@ public final class CraftItemStack extends ItemStack {
diff --git a/patches/server/0861-Fix-swamp-hut-cat-generation-deadlock.patch b/patches/server/0830-Fix-swamp-hut-cat-generation-deadlock.patch
index ae8f7dbdb2..ae8f7dbdb2 100644
--- a/patches/server/0861-Fix-swamp-hut-cat-generation-deadlock.patch
+++ b/patches/server/0830-Fix-swamp-hut-cat-generation-deadlock.patch
diff --git a/patches/server/0862-Don-t-allow-vehicle-movement-from-players-while-tele.patch b/patches/server/0831-Don-t-allow-vehicle-movement-from-players-while-tele.patch
index 77c8b0c110..77c8b0c110 100644
--- a/patches/server/0862-Don-t-allow-vehicle-movement-from-players-while-tele.patch
+++ b/patches/server/0831-Don-t-allow-vehicle-movement-from-players-while-tele.patch
diff --git a/patches/server/0863-Implement-getComputedBiome-API.patch b/patches/server/0832-Implement-getComputedBiome-API.patch
index b6a274bb77..b6a274bb77 100644
--- a/patches/server/0863-Implement-getComputedBiome-API.patch
+++ b/patches/server/0832-Implement-getComputedBiome-API.patch
diff --git a/patches/server/0864-Make-some-itemstacks-nonnull.patch b/patches/server/0833-Make-some-itemstacks-nonnull.patch
index e2cba95fe4..e2cba95fe4 100644
--- a/patches/server/0864-Make-some-itemstacks-nonnull.patch
+++ b/patches/server/0833-Make-some-itemstacks-nonnull.patch
diff --git a/patches/server/0865-Add-debug-for-invalid-GameProfiles-on-skull-blocks-i.patch b/patches/server/0834-Add-debug-for-invalid-GameProfiles-on-skull-blocks-i.patch
index fe6855e900..fe6855e900 100644
--- a/patches/server/0865-Add-debug-for-invalid-GameProfiles-on-skull-blocks-i.patch
+++ b/patches/server/0834-Add-debug-for-invalid-GameProfiles-on-skull-blocks-i.patch
diff --git a/patches/server/0866-Implement-enchantWithLevels-API.patch b/patches/server/0835-Implement-enchantWithLevels-API.patch
index 204d4534fb..204d4534fb 100644
--- a/patches/server/0866-Implement-enchantWithLevels-API.patch
+++ b/patches/server/0835-Implement-enchantWithLevels-API.patch
diff --git a/patches/server/0867-Fix-saving-in-unloadWorld.patch b/patches/server/0836-Fix-saving-in-unloadWorld.patch
index ae3e8954eb..0011be143d 100644
--- a/patches/server/0867-Fix-saving-in-unloadWorld.patch
+++ b/patches/server/0836-Fix-saving-in-unloadWorld.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Fix saving in unloadWorld
Change savingDisabled to false to ensure ServerLevel's saving logic gets called when unloadWorld is called with save = true
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 669fb0206a09377a8682325bc4bd744d285791f6..6eb74dcf4b1ed5c04f6890a07fbac7b9fff0d161 100644
+index b819ec350d230ea9425b4c1512dba2438408f066..9ec45da690ab2bcc36b7353cc689c6050936ab5d 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1289,7 +1289,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0868-Buffer-OOB-setBlock-calls.patch b/patches/server/0837-Buffer-OOB-setBlock-calls.patch
index 62e5606e17..62e5606e17 100644
--- a/patches/server/0868-Buffer-OOB-setBlock-calls.patch
+++ b/patches/server/0837-Buffer-OOB-setBlock-calls.patch
diff --git a/patches/server/0869-Add-TameableDeathMessageEvent.patch b/patches/server/0838-Add-TameableDeathMessageEvent.patch
index 8bbbaffa2c..8bbbaffa2c 100644
--- a/patches/server/0869-Add-TameableDeathMessageEvent.patch
+++ b/patches/server/0838-Add-TameableDeathMessageEvent.patch
diff --git a/patches/server/0870-Fix-new-block-data-for-EntityChangeBlockEvent-when-s.patch b/patches/server/0839-Fix-new-block-data-for-EntityChangeBlockEvent-when-s.patch
index 8ecf55d257..8ecf55d257 100644
--- a/patches/server/0870-Fix-new-block-data-for-EntityChangeBlockEvent-when-s.patch
+++ b/patches/server/0839-Fix-new-block-data-for-EntityChangeBlockEvent-when-s.patch
diff --git a/patches/server/0871-fix-player-loottables-running-when-mob-loot-gamerule.patch b/patches/server/0840-fix-player-loottables-running-when-mob-loot-gamerule.patch
index 5030bb2520..8dd4659462 100644
--- a/patches/server/0871-fix-player-loottables-running-when-mob-loot-gamerule.patch
+++ b/patches/server/0840-fix-player-loottables-running-when-mob-loot-gamerule.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] fix player loottables running when mob loot gamerule is false
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 18c3d4aecf498f78040c27336d2ea56fd911d034..3f3ebe28c669419091fd20c18185c61712e7f1e8 100644
+index 370bd3adb0003115ec5774eedd4dac01a41ae4af..7c1c193123c9ecda1bcedd2d89002a24e5b3fbba 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -861,12 +861,14 @@ public class ServerPlayer extends Player {
+@@ -846,12 +846,14 @@ public class ServerPlayer extends Player {
}
}
}
diff --git a/patches/server/0872-Ensure-entity-passenger-world-matches-ridden-entity.patch b/patches/server/0841-Ensure-entity-passenger-world-matches-ridden-entity.patch
index 1119b22306..e28f96f729 100644
--- a/patches/server/0872-Ensure-entity-passenger-world-matches-ridden-entity.patch
+++ b/patches/server/0841-Ensure-entity-passenger-world-matches-ridden-entity.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Ensure entity passenger world matches ridden entity
Bad plugins doing this would cause some obvious problems...
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 8060e80e69a75a00c7d9d9cc6717088e04f45212..eaa52f4d9c98f3b4a6c6505a1734d6667145c10f 100644
+index fd536e670036a365f6d0e8c168ef4b5fdf597615..4aa46029c42dc8a87a354fe6215ce78c59879bf2 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2684,6 +2684,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2685,6 +2685,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
protected boolean addPassenger(Entity entity) { // CraftBukkit
diff --git a/patches/server/0873-Guard-against-invalid-entity-positions.patch b/patches/server/0842-Guard-against-invalid-entity-positions.patch
index 2253a075c9..0a21359168 100644
--- a/patches/server/0873-Guard-against-invalid-entity-positions.patch
+++ b/patches/server/0842-Guard-against-invalid-entity-positions.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Guard against invalid entity positions
Anything not finite should be blocked and logged
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index eaa52f4d9c98f3b4a6c6505a1734d6667145c10f..1acf20ceccd24816151e74e8c5454ac8fcf7eeb2 100644
+index 4aa46029c42dc8a87a354fe6215ce78c59879bf2..25618b33760a3b1f39e6bbf774c75134afe94160 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -4187,11 +4187,33 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -4198,11 +4198,33 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
return this.getZ((2.0D * this.random.nextDouble() - 1.0D) * widthScale);
}
@@ -41,5 +41,5 @@ index eaa52f4d9c98f3b4a6c6505a1734d6667145c10f..1acf20ceccd24816151e74e8c5454ac8
+ }
+ // Paper end - block invalid positions
// Paper end
- // Paper start - fix MC-4
- if (this instanceof ItemEntity) {
+ // Paper start - rewrite chunk system
+ if (this.updatingSectionStatus) {
diff --git a/patches/server/0874-cache-resource-keys.patch b/patches/server/0843-cache-resource-keys.patch
index 4234b8673f..4234b8673f 100644
--- a/patches/server/0874-cache-resource-keys.patch
+++ b/patches/server/0843-cache-resource-keys.patch
diff --git a/patches/server/0875-Allow-to-change-the-podium-for-the-EnderDragon.patch b/patches/server/0844-Allow-to-change-the-podium-for-the-EnderDragon.patch
index b53e022e0b..ca79a2f05b 100644
--- a/patches/server/0875-Allow-to-change-the-podium-for-the-EnderDragon.patch
+++ b/patches/server/0844-Allow-to-change-the-podium-for-the-EnderDragon.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Allow to change the podium for the EnderDragon
diff --git a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
-index bb51a85b33e1701c2e445305d68d3453772f73df..47d6236daca806878399890a8d08e55233f19fd9 100644
+index 6495b0421cab1b067b9483cc448222705c15578c..ac3d4d90407288526a8c787e365ff41234a58543 100644
--- a/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/src/main/java/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -99,6 +99,10 @@ public class EnderDragon extends Mob implements Enemy {
@@ -39,7 +39,7 @@ index bb51a85b33e1701c2e445305d68d3453772f73df..47d6236daca806878399890a8d08e552
@Override
public boolean isFlapping() {
float f = Mth.cos(this.flapTime * 6.2831855F);
-@@ -970,7 +987,7 @@ public class EnderDragon extends Mob implements Enemy {
+@@ -969,7 +986,7 @@ public class EnderDragon extends Mob implements Enemy {
d0 = segment2[1] - segment1[1];
}
} else {
@@ -48,7 +48,7 @@ index bb51a85b33e1701c2e445305d68d3453772f73df..47d6236daca806878399890a8d08e552
double d1 = Math.max(Math.sqrt(blockposition.distToCenterSqr(this.position())) / 4.0D, 1.0D);
d0 = (double) segmentOffset / d1;
-@@ -997,7 +1014,7 @@ public class EnderDragon extends Mob implements Enemy {
+@@ -996,7 +1013,7 @@ public class EnderDragon extends Mob implements Enemy {
vec3d = this.getViewVector(tickDelta);
}
} else {
diff --git a/patches/server/0876-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch b/patches/server/0845-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch
index 9ef13e101f..9ef13e101f 100644
--- a/patches/server/0876-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch
+++ b/patches/server/0845-Fix-NBT-pieces-overriding-a-block-entity-during-worl.patch
diff --git a/patches/server/0877-Fix-StructureGrowEvent-species-for-RED_MUSHROOM.patch b/patches/server/0846-Fix-StructureGrowEvent-species-for-RED_MUSHROOM.patch
index 4b39ce4dc3..4b39ce4dc3 100644
--- a/patches/server/0877-Fix-StructureGrowEvent-species-for-RED_MUSHROOM.patch
+++ b/patches/server/0846-Fix-StructureGrowEvent-species-for-RED_MUSHROOM.patch
diff --git a/patches/server/0878-Prevent-tile-entity-copies-loading-chunks.patch b/patches/server/0847-Prevent-tile-entity-copies-loading-chunks.patch
index 3c5eabbf49..3c5eabbf49 100644
--- a/patches/server/0878-Prevent-tile-entity-copies-loading-chunks.patch
+++ b/patches/server/0847-Prevent-tile-entity-copies-loading-chunks.patch
diff --git a/patches/server/0879-Use-username-instead-of-display-name-in-PlayerList-g.patch b/patches/server/0848-Use-username-instead-of-display-name-in-PlayerList-g.patch
index 2accf61444..0d9a571f4d 100644
--- a/patches/server/0879-Use-username-instead-of-display-name-in-PlayerList-g.patch
+++ b/patches/server/0848-Use-username-instead-of-display-name-in-PlayerList-g.patch
@@ -6,7 +6,7 @@ Subject: [PATCH] Use username instead of display name in
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
-index 5a5ea1f9d6e978916d32b170ecf7f848d2524303..5999d85e38951503fc83b40cfe39671921ae5088 100644
+index f29700d1a7cfebbe4a54b06470ab73d1522c5dd0..06eda955f96b5fe2d08ed0d39229c7a6ebb88931 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1483,7 +1483,7 @@ public abstract class PlayerList {
diff --git a/patches/server/0880-Fix-slime-spawners-not-spawning-outside-slime-chunks.patch b/patches/server/0849-Fix-slime-spawners-not-spawning-outside-slime-chunks.patch
index 951baf9062..951baf9062 100644
--- a/patches/server/0880-Fix-slime-spawners-not-spawning-outside-slime-chunks.patch
+++ b/patches/server/0849-Fix-slime-spawners-not-spawning-outside-slime-chunks.patch
diff --git a/patches/server/0881-Pass-ServerLevel-for-gamerule-callbacks.patch b/patches/server/0850-Pass-ServerLevel-for-gamerule-callbacks.patch
index f29c5078f2..e309f68ed2 100644
--- a/patches/server/0881-Pass-ServerLevel-for-gamerule-callbacks.patch
+++ b/patches/server/0850-Pass-ServerLevel-for-gamerule-callbacks.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Pass ServerLevel for gamerule callbacks
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
-index 20670bc075c387ee0422eb1014207e26105efccd..bdd6560fe85950b0a857a949cb38c044da44ca6b 100644
+index 11efaeb12340946c7447617500cbc69bc9bdaf01..2932d2bb797a49e904cebec4285d24d69b429cd9 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -309,7 +309,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@@ -158,10 +158,10 @@ index 3c93bfeb94168f832904a8462ae23b06e81e080d..468c635d31cfa8051666bbefce8df4b4
this.onChanged(server);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 23c68b6cd26fbd05685ebcfbb5e81db4c8dedb29..9d27b093922f3dee9b459f8a9cdfa96f12f2e654 100644
+index 8ab92e7a9ed2008b90f3a816d972512f5dc58876..53f780bb47979e728e0212f8159bf9d440ea32b0 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-@@ -1912,7 +1912,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1899,7 +1899,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// Paper end
GameRules.Value<?> handle = this.getHandle().getGameRules().getRule(CraftWorld.getGameRulesNMS().get(rule));
handle.deserialize(event.getValue()); // Paper
@@ -170,7 +170,7 @@ index 23c68b6cd26fbd05685ebcfbb5e81db4c8dedb29..9d27b093922f3dee9b459f8a9cdfa96f
return true;
}
-@@ -1952,7 +1952,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -1939,7 +1939,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// Paper end
GameRules.Value<?> handle = this.getHandle().getGameRules().getRule(CraftWorld.getGameRulesNMS().get(rule.getName()));
handle.deserialize(event.getValue()); // Paper
diff --git a/patches/server/0882-Add-pre-unbreaking-amount-to-PlayerItemDamageEvent.patch b/patches/server/0851-Add-pre-unbreaking-amount-to-PlayerItemDamageEvent.patch
index fff84c5a62..fff84c5a62 100644
--- a/patches/server/0882-Add-pre-unbreaking-amount-to-PlayerItemDamageEvent.patch
+++ b/patches/server/0851-Add-pre-unbreaking-amount-to-PlayerItemDamageEvent.patch
diff --git a/patches/server/0883-WorldCreator-keepSpawnLoaded.patch b/patches/server/0852-WorldCreator-keepSpawnLoaded.patch
index 8d52feaaf9..83b3e1ff72 100644
--- a/patches/server/0883-WorldCreator-keepSpawnLoaded.patch
+++ b/patches/server/0852-WorldCreator-keepSpawnLoaded.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] WorldCreator#keepSpawnLoaded
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 6eb74dcf4b1ed5c04f6890a07fbac7b9fff0d161..c75e3cd197bee7caeef3a55b2746c15de3c0c3e4 100644
+index 9ec45da690ab2bcc36b7353cc689c6050936ab5d..8df7c0c29d8bf37e52994751464cc91e44050792 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1248,6 +1248,7 @@ public final class CraftServer implements Server {
@@ -14,5 +14,5 @@ index 6eb74dcf4b1ed5c04f6890a07fbac7b9fff0d161..c75e3cd197bee7caeef3a55b2746c15d
+ internal.keepSpawnInMemory = creator.keepSpawnLoaded().toBooleanOrElse(internal.getWorld().getKeepSpawnInMemory()); // Paper
this.getServer().prepareLevels(internal.getChunkSource().chunkMap.progressListener, internal);
- internal.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API
+ //internal.entityManager.tick(); // SPIGOT-6526: Load pending entities so they are available to the API // Paper - rewrite chunk system
diff --git a/patches/server/0884-Fix-NPE-for-BlockDataMeta-getBlockData.patch b/patches/server/0853-Fix-NPE-for-BlockDataMeta-getBlockData.patch
index d33dabf6c9..d33dabf6c9 100644
--- a/patches/server/0884-Fix-NPE-for-BlockDataMeta-getBlockData.patch
+++ b/patches/server/0853-Fix-NPE-for-BlockDataMeta-getBlockData.patch
diff --git a/patches/server/0885-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch b/patches/server/0854-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch
index 79c74b0a57..79c74b0a57 100644
--- a/patches/server/0885-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch
+++ b/patches/server/0854-Trigger-bee_nest_destroyed-trigger-in-the-correct-pl.patch
diff --git a/patches/server/0886-Add-EntityDyeEvent-and-CollarColorable-interface.patch b/patches/server/0855-Add-EntityDyeEvent-and-CollarColorable-interface.patch
index 8eacb2217e..8eacb2217e 100644
--- a/patches/server/0886-Add-EntityDyeEvent-and-CollarColorable-interface.patch
+++ b/patches/server/0855-Add-EntityDyeEvent-and-CollarColorable-interface.patch
diff --git a/patches/server/0887-Fire-CauldronLevelChange-on-initial-fill.patch b/patches/server/0856-Fire-CauldronLevelChange-on-initial-fill.patch
index c0fcd53f55..c0fcd53f55 100644
--- a/patches/server/0887-Fire-CauldronLevelChange-on-initial-fill.patch
+++ b/patches/server/0856-Fire-CauldronLevelChange-on-initial-fill.patch
diff --git a/patches/server/0888-fix-powder-snow-cauldrons-not-turning-to-water.patch b/patches/server/0857-fix-powder-snow-cauldrons-not-turning-to-water.patch
index aef5bd0551..aef5bd0551 100644
--- a/patches/server/0888-fix-powder-snow-cauldrons-not-turning-to-water.patch
+++ b/patches/server/0857-fix-powder-snow-cauldrons-not-turning-to-water.patch
diff --git a/patches/server/0889-Add-PlayerStopUsingItemEvent.patch b/patches/server/0858-Add-PlayerStopUsingItemEvent.patch
index 90dcaae6a1..90dcaae6a1 100644
--- a/patches/server/0889-Add-PlayerStopUsingItemEvent.patch
+++ b/patches/server/0858-Add-PlayerStopUsingItemEvent.patch
diff --git a/patches/server/0890-FallingBlock-auto-expire-setting.patch b/patches/server/0859-FallingBlock-auto-expire-setting.patch
index a7b0c20dd6..a7b0c20dd6 100644
--- a/patches/server/0890-FallingBlock-auto-expire-setting.patch
+++ b/patches/server/0859-FallingBlock-auto-expire-setting.patch
diff --git a/patches/server/0891-Don-t-tick-markers.patch b/patches/server/0860-Don-t-tick-markers.patch
index d21ff9fe5a..8fb4313275 100644
--- a/patches/server/0891-Don-t-tick-markers.patch
+++ b/patches/server/0860-Don-t-tick-markers.patch
@@ -22,17 +22,17 @@ index 68f99e93ed3e843b4001a7a27620f88a48b85e67..0dc96c39151ec4dbeec3947cb17606f5
}
});
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index eceaa1f2ede1c068f9090d13bf9d3b3afaa08cc3..e5a64e70020487b15825a865623afa45b0ae59d4 100644
+index 6388ed56f71f065ab811acf3fb264083fdb5b09a..ae204baabbadafe4572d476be085a80c867d5fba 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -2495,6 +2495,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -2516,6 +2516,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
public void onTickingStart(Entity entity) {
+ if (entity instanceof net.minecraft.world.entity.Marker) return; // Paper - Don't tick markers
ServerLevel.this.entityTickList.add(entity);
- ServerLevel.this.entityManager.addNavigatorsIfPathingToRegion(entity); // Paper - optimise notify
}
+
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index b1ed97618d08d7691d24f89e9e9b0ed0f2bddd09..40b382c2e0e33fe5c24a51b211cd2f9557a60c5e 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
diff --git a/patches/server/0892-Do-not-accept-invalid-client-settings.patch b/patches/server/0861-Do-not-accept-invalid-client-settings.patch
index 88c7e8c514..88c7e8c514 100644
--- a/patches/server/0892-Do-not-accept-invalid-client-settings.patch
+++ b/patches/server/0861-Do-not-accept-invalid-client-settings.patch
diff --git a/patches/server/0893-Add-support-for-Proxy-Protocol.patch b/patches/server/0862-Add-support-for-Proxy-Protocol.patch
index 1cf43fbbe8..1cf43fbbe8 100644
--- a/patches/server/0893-Add-support-for-Proxy-Protocol.patch
+++ b/patches/server/0862-Add-support-for-Proxy-Protocol.patch
diff --git a/patches/server/0894-Fix-OfflinePlayer-getBedSpawnLocation.patch b/patches/server/0863-Fix-OfflinePlayer-getBedSpawnLocation.patch
index b11ce011a5..b11ce011a5 100644
--- a/patches/server/0894-Fix-OfflinePlayer-getBedSpawnLocation.patch
+++ b/patches/server/0863-Fix-OfflinePlayer-getBedSpawnLocation.patch
diff --git a/patches/server/0895-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch b/patches/server/0864-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch
index 59fabed7e4..59fabed7e4 100644
--- a/patches/server/0895-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch
+++ b/patches/server/0864-Fix-FurnaceInventory-for-smokers-and-blast-furnaces.patch
diff --git a/patches/server/0896-Sanitize-Sent-BlockEntity-NBT.patch b/patches/server/0865-Sanitize-Sent-BlockEntity-NBT.patch
index fb3c8b1818..fb3c8b1818 100644
--- a/patches/server/0896-Sanitize-Sent-BlockEntity-NBT.patch
+++ b/patches/server/0865-Sanitize-Sent-BlockEntity-NBT.patch
diff --git a/patches/server/0897-Prevent-entity-loading-causing-async-lookups.patch b/patches/server/0866-Prevent-entity-loading-causing-async-lookups.patch
index 5877c75f53..947ae1b307 100644
--- a/patches/server/0897-Prevent-entity-loading-causing-async-lookups.patch
+++ b/patches/server/0866-Prevent-entity-loading-causing-async-lookups.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Prevent entity loading causing async lookups
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 1acf20ceccd24816151e74e8c5454ac8fcf7eeb2..da4147636e12deb2973b47d5cea4c742b1af7423 100644
+index 25618b33760a3b1f39e6bbf774c75134afe94160..8ef94c21096131c345b7505630a487bd200f3464 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -788,6 +788,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -789,6 +789,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
public void baseTick() {
this.level.getProfiler().push("entityBaseTick");
diff --git a/patches/server/0898-Disable-component-selector-resolving-in-books-by-def.patch b/patches/server/0867-Disable-component-selector-resolving-in-books-by-def.patch
index 72862c20c5..72862c20c5 100644
--- a/patches/server/0898-Disable-component-selector-resolving-in-books-by-def.patch
+++ b/patches/server/0867-Disable-component-selector-resolving-in-books-by-def.patch
diff --git a/patches/server/0899-Throw-exception-on-world-create-while-being-ticked.patch b/patches/server/0868-Throw-exception-on-world-create-while-being-ticked.patch
index 6c91627e34..92792493ee 100644
--- a/patches/server/0899-Throw-exception-on-world-create-while-being-ticked.patch
+++ b/patches/server/0868-Throw-exception-on-world-create-while-being-ticked.patch
@@ -7,7 +7,7 @@ There are no plans to support creating worlds while worlds are
being ticked themselvess.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 4d920031300a9801debc2eb39a4d3cb9d8fbb330..f1e14f7850c0a64128839285f09e2aa34a7c8d8d 100644
+index 41543b69934c5e5c050636482c7d2b93a6bab796..60a84de276885009310904afc90516d6106458d1 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -297,6 +297,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -18,7 +18,7 @@ index 4d920031300a9801debc2eb39a4d3cb9d8fbb330..f1e14f7850c0a64128839285f09e2aa3
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
AtomicReference<S> atomicreference = new AtomicReference();
-@@ -1495,7 +1496,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1475,7 +1476,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.getFunctions().tick();
MinecraftTimings.commandFunctionsTimer.stopTiming(); // Spigot // Paper
this.profiler.popPush("levels");
@@ -27,7 +27,7 @@ index 4d920031300a9801debc2eb39a4d3cb9d8fbb330..f1e14f7850c0a64128839285f09e2aa3
// CraftBukkit start
// Run tasks that are waiting on processing
-@@ -1527,6 +1528,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1507,6 +1508,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Paper end
MinecraftTimings.timeUpdateTimer.stopTiming(); // Spigot // Paper
@@ -36,7 +36,7 @@ index 4d920031300a9801debc2eb39a4d3cb9d8fbb330..f1e14f7850c0a64128839285f09e2aa3
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
-@@ -1574,6 +1577,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1554,6 +1557,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.profiler.pop();
worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions
}
@@ -45,7 +45,7 @@ index 4d920031300a9801debc2eb39a4d3cb9d8fbb330..f1e14f7850c0a64128839285f09e2aa3
this.profiler.popPush("connection");
MinecraftTimings.connectionTimer.startTiming(); // Spigot
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index c75e3cd197bee7caeef3a55b2746c15de3c0c3e4..19a98e502b4ba49485fa3e51c48309a06ded5a9d 100644
+index 8df7c0c29d8bf37e52994751464cc91e44050792..75aa5bc28fde5d8b542778caeb46b31c57b86e5e 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -855,6 +855,11 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0900-Add-Alternate-Current-redstone-implementation.patch b/patches/server/0869-Add-Alternate-Current-redstone-implementation.patch
index fd04e9a7fb..95e4e6c122 100644
--- a/patches/server/0900-Add-Alternate-Current-redstone-implementation.patch
+++ b/patches/server/0869-Add-Alternate-Current-redstone-implementation.patch
@@ -2008,7 +2008,7 @@ index 0000000000000000000000000000000000000000..33cd90c30c22200a4e1ae64f40a0bf78
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index e5a64e70020487b15825a865623afa45b0ae59d4..684063b8433f78ef0adf0de1057ea24720b32181 100644
+index ae204baabbadafe4572d476be085a80c867d5fba..09aae0e2c958506d93dc6bb3e655f3036c362c41 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -219,6 +219,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
@@ -2019,8 +2019,8 @@ index e5a64e70020487b15825a865623afa45b0ae59d4..684063b8433f78ef0adf0de1057ea247
public static Throwable getAddToWorldStackTrace(Entity entity) {
return new Throwable(entity + " Added to world at " + new java.util.Date());
}
-@@ -2484,6 +2485,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
- return this.entityManager.canPositionTick(pos.toLong()); // Paper
+@@ -2505,6 +2506,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
+ // Paper end - rewrite chunk system
}
+ // Paper start - optimize redstone (Alternate Current)
@@ -2034,10 +2034,10 @@ index e5a64e70020487b15825a865623afa45b0ae59d4..684063b8433f78ef0adf0de1057ea247
EntityCallbacks() {}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 30140ae5a74a511c9031b8e772e724b25e56de3d..46c45f249e71ca94044f1260a23cd7331098fb2c 100644
+index c1a3bcc8d9df2bf25a9c73faeac84652756d430a..01cbff44c8b53458362cf849910cdff0a6da27e2 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -1453,4 +1453,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -1452,4 +1452,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
return ret;
}
// Paper end
diff --git a/patches/server/0901-Dont-resent-entity-on-art-update.patch b/patches/server/0870-Dont-resent-entity-on-art-update.patch
index ff4ac47ad6..ff4ac47ad6 100644
--- a/patches/server/0901-Dont-resent-entity-on-art-update.patch
+++ b/patches/server/0870-Dont-resent-entity-on-art-update.patch
diff --git a/patches/server/0902-Add-missing-spawn-eggs.patch b/patches/server/0871-Add-missing-spawn-eggs.patch
index ba1f69fe6e..ba88344787 100644
--- a/patches/server/0902-Add-missing-spawn-eggs.patch
+++ b/patches/server/0871-Add-missing-spawn-eggs.patch
@@ -22,7 +22,7 @@ index ce64286ac5b836283318ac1ac0bd4afb29db9bb7..09b6475b77ebc7f43c13861aa2af26e2
case ARMOR_STAND:
return meta instanceof CraftMetaArmorStand ? meta : new CraftMetaArmorStand(meta);
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
-index 8156f8cfe924a80c9cecc0f1555d6b543bde7117..a04768f58d26a55d13a559eaf4712863283d72bb 100644
+index bfb1ad0b6e20e10fee53f94a3e6c4f8aad7aae7d..03b4f18000d455e48044eb7a15cd667acef5f14d 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -428,6 +428,12 @@ public final class CraftItemStack extends ItemStack {
diff --git a/patches/server/0903-Add-WardenAngerChangeEvent.patch b/patches/server/0872-Add-WardenAngerChangeEvent.patch
index cfe1a98c72..cfe1a98c72 100644
--- a/patches/server/0903-Add-WardenAngerChangeEvent.patch
+++ b/patches/server/0872-Add-WardenAngerChangeEvent.patch
diff --git a/patches/server/0904-Add-option-for-strict-advancement-dimension-checks.patch b/patches/server/0873-Add-option-for-strict-advancement-dimension-checks.patch
index 3aaa6591bd..400526a4d4 100644
--- a/patches/server/0904-Add-option-for-strict-advancement-dimension-checks.patch
+++ b/patches/server/0873-Add-option-for-strict-advancement-dimension-checks.patch
@@ -11,10 +11,10 @@ distance trigger. This adds a config option to ignore that
and use the exact dimension key of the worlds involved.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 3f3ebe28c669419091fd20c18185c61712e7f1e8..3615576c24d5d6790a6894a91180de25fa0e5a9c 100644
+index 7c1c193123c9ecda1bcedd2d89002a24e5b3fbba..5fca2fc3733880721ae6ff2db020c9809bfffc16 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1251,6 +1251,12 @@ public class ServerPlayer extends Player {
+@@ -1236,6 +1236,12 @@ public class ServerPlayer extends Player {
// CraftBukkit start
ResourceKey<Level> maindimensionkey = CraftDimensionUtil.getMainDimensionKey(origin);
ResourceKey<Level> maindimensionkey1 = CraftDimensionUtil.getMainDimensionKey(this.level);
diff --git a/patches/server/0905-Add-missing-important-BlockStateListPopulator-method.patch b/patches/server/0874-Add-missing-important-BlockStateListPopulator-method.patch
index 2296cce864..2296cce864 100644
--- a/patches/server/0905-Add-missing-important-BlockStateListPopulator-method.patch
+++ b/patches/server/0874-Add-missing-important-BlockStateListPopulator-method.patch
diff --git a/patches/server/0906-Nameable-Banner-API.patch b/patches/server/0875-Nameable-Banner-API.patch
index 78f176be32..78f176be32 100644
--- a/patches/server/0906-Nameable-Banner-API.patch
+++ b/patches/server/0875-Nameable-Banner-API.patch
diff --git a/patches/server/0907-Don-t-broadcast-messages-to-command-blocks.patch b/patches/server/0876-Don-t-broadcast-messages-to-command-blocks.patch
index 95330c3de3..fd94342154 100644
--- a/patches/server/0907-Don-t-broadcast-messages-to-command-blocks.patch
+++ b/patches/server/0876-Don-t-broadcast-messages-to-command-blocks.patch
@@ -20,7 +20,7 @@ index c0195f73cd2c8721e882c681eaead65471710081..861b348f73867af3199f1cc0dab1ddd4
Date date = new Date();
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 19a98e502b4ba49485fa3e51c48309a06ded5a9d..df0472f275b4fa3a1088db84712a39ea0257f17e 100644
+index 75aa5bc28fde5d8b542778caeb46b31c57b86e5e..10f4e8067c21d4a0aa5f121bffe56c45c00e5d62 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1763,7 +1763,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0908-Prevent-empty-items-from-being-added-to-world.patch b/patches/server/0877-Prevent-empty-items-from-being-added-to-world.patch
index 2e13972f86..6d09d8f5e3 100644
--- a/patches/server/0908-Prevent-empty-items-from-being-added-to-world.patch
+++ b/patches/server/0877-Prevent-empty-items-from-being-added-to-world.patch
@@ -7,10 +7,10 @@ The previous solution caused a bunch of bandaid fixes inorder to resolve edge ca
Just simply prevent them from being added to the world instead.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
-index 684063b8433f78ef0adf0de1057ea24720b32181..96bb0e56f12437037b598cd7baabf369e5994517 100644
+index 87b59687f8b484f08c43a10d42460e827eb9a62a..d3767b4db89b9f067c02b412e75b5944b9c353da 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
-@@ -1439,6 +1439,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
+@@ -1464,6 +1464,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
// WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getKey(entity.getType())); // CraftBukkit
return false;
} else {
diff --git a/patches/server/0909-Fix-CCE-for-SplashPotion-and-LingeringPotion-spawnin.patch b/patches/server/0878-Fix-CCE-for-SplashPotion-and-LingeringPotion-spawnin.patch
index ad894dbcce..ad894dbcce 100644
--- a/patches/server/0909-Fix-CCE-for-SplashPotion-and-LingeringPotion-spawnin.patch
+++ b/patches/server/0878-Fix-CCE-for-SplashPotion-and-LingeringPotion-spawnin.patch
diff --git a/patches/server/0910-Don-t-print-component-in-resource-pack-rejection-mes.patch b/patches/server/0879-Don-t-print-component-in-resource-pack-rejection-mes.patch
index e39a508723..e39a508723 100644
--- a/patches/server/0910-Don-t-print-component-in-resource-pack-rejection-mes.patch
+++ b/patches/server/0879-Don-t-print-component-in-resource-pack-rejection-mes.patch
diff --git a/patches/server/0911-Add-Player-getFishHook.patch b/patches/server/0880-Add-Player-getFishHook.patch
index bf5ccb5986..bf5ccb5986 100644
--- a/patches/server/0911-Add-Player-getFishHook.patch
+++ b/patches/server/0880-Add-Player-getFishHook.patch
diff --git a/patches/server/0912-Do-not-sync-load-chunk-for-dynamic-game-event-listen.patch b/patches/server/0881-Do-not-sync-load-chunk-for-dynamic-game-event-listen.patch
index f9cbdd0899..f9cbdd0899 100644
--- a/patches/server/0912-Do-not-sync-load-chunk-for-dynamic-game-event-listen.patch
+++ b/patches/server/0881-Do-not-sync-load-chunk-for-dynamic-game-event-listen.patch
diff --git a/patches/server/0913-Add-various-missing-EntityDropItemEvent-calls.patch b/patches/server/0882-Add-various-missing-EntityDropItemEvent-calls.patch
index 1b5dcc78f3..3fa83bdca7 100644
--- a/patches/server/0913-Add-various-missing-EntityDropItemEvent-calls.patch
+++ b/patches/server/0882-Add-various-missing-EntityDropItemEvent-calls.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add various missing EntityDropItemEvent calls
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index da4147636e12deb2973b47d5cea4c742b1af7423..592456de5aac579bbd54f0085302feb693ba2431 100644
+index 8ef94c21096131c345b7505630a487bd200f3464..125ba0e3dc05e763a09318a4735d14546df6294f 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2520,6 +2520,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2521,6 +2521,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
stack.setCount(0); // Paper - destroy this item - if this ever leaks due to game bugs, ensure it doesn't dupe
entityitem.setDefaultPickUpDelay();
diff --git a/patches/server/0914-Add-some-minimal-debug-information-to-chat-packet-er.patch b/patches/server/0883-Add-some-minimal-debug-information-to-chat-packet-er.patch
index 2cbecbfbe0..2cbecbfbe0 100644
--- a/patches/server/0914-Add-some-minimal-debug-information-to-chat-packet-er.patch
+++ b/patches/server/0883-Add-some-minimal-debug-information-to-chat-packet-er.patch
diff --git a/patches/server/0915-Fix-Bee-flower-NPE.patch b/patches/server/0884-Fix-Bee-flower-NPE.patch
index d1fca0922f..d1fca0922f 100644
--- a/patches/server/0915-Fix-Bee-flower-NPE.patch
+++ b/patches/server/0884-Fix-Bee-flower-NPE.patch
diff --git a/patches/server/0916-Fix-Spigot-Config-not-using-commands.spam-exclusions.patch b/patches/server/0885-Fix-Spigot-Config-not-using-commands.spam-exclusions.patch
index 90422ebe0b..90422ebe0b 100644
--- a/patches/server/0916-Fix-Spigot-Config-not-using-commands.spam-exclusions.patch
+++ b/patches/server/0885-Fix-Spigot-Config-not-using-commands.spam-exclusions.patch
diff --git a/patches/server/0917-Add-SpawnReason-to-Tadpoles-spawned-by-Frogspawn.patch b/patches/server/0886-Add-SpawnReason-to-Tadpoles-spawned-by-Frogspawn.patch
index dd94178b5a..dd94178b5a 100644
--- a/patches/server/0917-Add-SpawnReason-to-Tadpoles-spawned-by-Frogspawn.patch
+++ b/patches/server/0886-Add-SpawnReason-to-Tadpoles-spawned-by-Frogspawn.patch
diff --git a/patches/server/0918-More-Teleport-API.patch b/patches/server/0887-More-Teleport-API.patch
index f03d3d2794..74b25b2e20 100644
--- a/patches/server/0918-More-Teleport-API.patch
+++ b/patches/server/0887-More-Teleport-API.patch
@@ -69,7 +69,7 @@ index c4ffccddce33cf461d9b04ccbb90026544f16b7d..99b99fae67e53a688b3519d8a8d0cc5f
// Let the server handle cross world teleports
if (location.getWorld() != null && !location.getWorld().equals(this.getWorld())) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index 4f5760d782bdfeee25839c50b614301eeb8ba42f..f6743f383408620a05afa64641a6d5e3519f8076 100644
+index 674b7ff0c1e372ffb06336841e129d5c2130b6f9..374c01f22aa677017ff3d9971e0f1026f53a447e 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1178,13 +1178,92 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
diff --git a/patches/server/0919-Add-EntityPortalReadyEvent.patch b/patches/server/0888-Add-EntityPortalReadyEvent.patch
index 788811f23b..24ff0a2a02 100644
--- a/patches/server/0919-Add-EntityPortalReadyEvent.patch
+++ b/patches/server/0888-Add-EntityPortalReadyEvent.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add EntityPortalReadyEvent
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
-index 592456de5aac579bbd54f0085302feb693ba2431..a3f1aef9d8f254747bd6580e8e3087a5c6829128 100644
+index 125ba0e3dc05e763a09318a4735d14546df6294f..008bd65d838819431a0823c6ac5925774bd155a1 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
-@@ -2867,6 +2867,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2868,6 +2868,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (true && !this.isPassenger() && this.portalTime++ >= i) { // CraftBukkit
this.level.getProfiler().push("portal");
this.portalTime = i;
@@ -22,7 +22,7 @@ index 592456de5aac579bbd54f0085302feb693ba2431..a3f1aef9d8f254747bd6580e8e3087a5
this.setPortalCooldown();
// CraftBukkit start
if (this instanceof ServerPlayer) {
-@@ -2874,6 +2881,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
+@@ -2875,6 +2882,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
} else {
this.changeDimension(worldserver1);
}
diff --git a/patches/server/0920-Don-t-use-level-random-in-entity-constructors.patch b/patches/server/0889-Don-t-use-level-random-in-entity-constructors.patch
index aea2ef2591..aea2ef2591 100644
--- a/patches/server/0920-Don-t-use-level-random-in-entity-constructors.patch
+++ b/patches/server/0889-Don-t-use-level-random-in-entity-constructors.patch
diff --git a/patches/server/0921-Send-block-entities-after-destroy-prediction.patch b/patches/server/0890-Send-block-entities-after-destroy-prediction.patch
index 0d7cf43457..0d7cf43457 100644
--- a/patches/server/0921-Send-block-entities-after-destroy-prediction.patch
+++ b/patches/server/0890-Send-block-entities-after-destroy-prediction.patch
diff --git a/patches/server/0922-Warn-on-plugins-accessing-faraway-chunks.patch b/patches/server/0891-Warn-on-plugins-accessing-faraway-chunks.patch
index 003620357a..fd9a449523 100644
--- a/patches/server/0922-Warn-on-plugins-accessing-faraway-chunks.patch
+++ b/patches/server/0891-Warn-on-plugins-accessing-faraway-chunks.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Warn on plugins accessing faraway chunks
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 46c45f249e71ca94044f1260a23cd7331098fb2c..ceb1f18ec16ddcda792ef0393b5f4649fbef3017 100644
+index 01cbff44c8b53458362cf849910cdff0a6da27e2..d89471935b2b5888cb5d1f6829ae479003451fda 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -418,7 +418,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -417,7 +417,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
}
private static boolean isInWorldBoundsHorizontal(BlockPos pos) {
@@ -18,7 +18,7 @@ index 46c45f249e71ca94044f1260a23cd7331098fb2c..ceb1f18ec16ddcda792ef0393b5f4649
private static boolean isOutsideSpawnableHeight(int y) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
-index 9d27b093922f3dee9b459f8a9cdfa96f12f2e654..1b6ae90acffa06502902e11473b65c8431616b05 100644
+index 53f780bb47979e728e0212f8159bf9d440ea32b0..72ed25022d5ea1074304be3c72b23882b8f0f88a 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -313,9 +313,24 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@@ -70,7 +70,7 @@ index 9d27b093922f3dee9b459f8a9cdfa96f12f2e654..1b6ae90acffa06502902e11473b65c84
Preconditions.checkArgument(plugin != null, "null plugin");
Preconditions.checkArgument(plugin.isEnabled(), "plugin is not enabled");
-@@ -661,6 +679,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -648,6 +666,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void setChunkForceLoaded(int x, int z, boolean forced) {
@@ -78,7 +78,7 @@ index 9d27b093922f3dee9b459f8a9cdfa96f12f2e654..1b6ae90acffa06502902e11473b65c84
this.getHandle().setChunkForced(x, z, forced);
}
-@@ -972,6 +991,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -959,6 +978,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public int getHighestBlockYAt(int x, int z, org.bukkit.HeightMap heightMap) {
@@ -86,7 +86,7 @@ index 9d27b093922f3dee9b459f8a9cdfa96f12f2e654..1b6ae90acffa06502902e11473b65c84
// Transient load for this tick
return this.world.getChunk(x >> 4, z >> 4).getHeight(CraftHeightMap.toNMS(heightMap), x, z);
}
-@@ -2333,6 +2353,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
+@@ -2315,6 +2335,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// Spigot end
// Paper start
public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
diff --git a/patches/server/0923-Custom-Chat-Completion-Suggestions-API.patch b/patches/server/0892-Custom-Chat-Completion-Suggestions-API.patch
index e553ad623c..38f390a0e5 100644
--- a/patches/server/0923-Custom-Chat-Completion-Suggestions-API.patch
+++ b/patches/server/0892-Custom-Chat-Completion-Suggestions-API.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Custom Chat Completion Suggestions API
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index f6743f383408620a05afa64641a6d5e3519f8076..feff87fba11ff46ce1ebd0f5fdf5132b85c8eddc 100644
+index 374c01f22aa677017ff3d9971e0f1026f53a447e..f2e5164b0651963cb26e4d776ec5c6e30647790f 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -659,6 +659,22 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
diff --git a/patches/server/0924-Add-missing-BlockFadeEvents.patch b/patches/server/0893-Add-missing-BlockFadeEvents.patch
index 6b887deccf..6b887deccf 100644
--- a/patches/server/0924-Add-missing-BlockFadeEvents.patch
+++ b/patches/server/0893-Add-missing-BlockFadeEvents.patch
diff --git a/patches/server/0925-Collision-API.patch b/patches/server/0894-Collision-API.patch
index e1980a48f2..e1980a48f2 100644
--- a/patches/server/0925-Collision-API.patch
+++ b/patches/server/0894-Collision-API.patch
diff --git a/patches/server/0926-Fix-suggest-command-message-for-brigadier-syntax-exc.patch b/patches/server/0895-Fix-suggest-command-message-for-brigadier-syntax-exc.patch
index 3a5bf3be59..3a5bf3be59 100644
--- a/patches/server/0926-Fix-suggest-command-message-for-brigadier-syntax-exc.patch
+++ b/patches/server/0895-Fix-suggest-command-message-for-brigadier-syntax-exc.patch
diff --git a/patches/server/0927-Fix-command-preprocess-cancelling-and-command-changi.patch b/patches/server/0896-Fix-command-preprocess-cancelling-and-command-changi.patch
index 501d00e40b..501d00e40b 100644
--- a/patches/server/0927-Fix-command-preprocess-cancelling-and-command-changi.patch
+++ b/patches/server/0896-Fix-command-preprocess-cancelling-and-command-changi.patch
diff --git a/patches/server/0928-Remove-invalid-signature-login-stacktrace.patch b/patches/server/0897-Remove-invalid-signature-login-stacktrace.patch
index 44d9c41c00..44d9c41c00 100644
--- a/patches/server/0928-Remove-invalid-signature-login-stacktrace.patch
+++ b/patches/server/0897-Remove-invalid-signature-login-stacktrace.patch
diff --git a/patches/server/0929-Add-async-catcher-to-PlayerConnection-internalTelepo.patch b/patches/server/0898-Add-async-catcher-to-PlayerConnection-internalTelepo.patch
index 05e9d349b8..05e9d349b8 100644
--- a/patches/server/0929-Add-async-catcher-to-PlayerConnection-internalTelepo.patch
+++ b/patches/server/0898-Add-async-catcher-to-PlayerConnection-internalTelepo.patch
diff --git a/patches/server/0930-Block-Ticking-API.patch b/patches/server/0899-Block-Ticking-API.patch
index 43db29d605..43db29d605 100644
--- a/patches/server/0930-Block-Ticking-API.patch
+++ b/patches/server/0899-Block-Ticking-API.patch
diff --git a/patches/server/0931-Add-Velocity-IP-Forwarding-Support.patch b/patches/server/0900-Add-Velocity-IP-Forwarding-Support.patch
index 0ef73f32b0..8e25ed523f 100644
--- a/patches/server/0931-Add-Velocity-IP-Forwarding-Support.patch
+++ b/patches/server/0900-Add-Velocity-IP-Forwarding-Support.patch
@@ -197,7 +197,7 @@ index 90ad59b8f387be37311dd3874c96568e2fb812e6..79d2bb3170c51e4c83a054eb8d47f894
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
-index 3c9ba17cd0f016a21f56c6b6f8861c512734637a..b443aba38258f501f8f00be6b055f07b709277c4 100644
+index 10f4e8067c21d4a0aa5f121bffe56c45c00e5d62..111f8276f26350a5c62a7b8577b4598978b5355d 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -784,7 +784,7 @@ public final class CraftServer implements Server {
diff --git a/patches/server/0932-Use-thread-safe-random-in-ServerLoginPacketListenerI.patch b/patches/server/0901-Use-thread-safe-random-in-ServerLoginPacketListenerI.patch
index 73c43682a4..73c43682a4 100644
--- a/patches/server/0932-Use-thread-safe-random-in-ServerLoginPacketListenerI.patch
+++ b/patches/server/0901-Use-thread-safe-random-in-ServerLoginPacketListenerI.patch
diff --git a/patches/server/0933-Add-NamespacedKey-biome-methods.patch b/patches/server/0902-Add-NamespacedKey-biome-methods.patch
index 3266ac0fb2..3266ac0fb2 100644
--- a/patches/server/0933-Add-NamespacedKey-biome-methods.patch
+++ b/patches/server/0902-Add-NamespacedKey-biome-methods.patch
diff --git a/patches/server/0934-Fix-plugin-loggers-on-server-shutdown.patch b/patches/server/0903-Fix-plugin-loggers-on-server-shutdown.patch
index e2c9ffe621..c1b345f505 100644
--- a/patches/server/0934-Fix-plugin-loggers-on-server-shutdown.patch
+++ b/patches/server/0903-Fix-plugin-loggers-on-server-shutdown.patch
@@ -37,10 +37,10 @@ index 0000000000000000000000000000000000000000..c1d3bac79bb8b4796c013ff4472f75dc
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index dd9ab51e904be2f2f2a2981d4f0f6638a6895e8d..d2161a3c3b9a2b2d463ac778656c95167c10a49d 100644
+index aad7ebbfce1bd42a3e61e5336f57aec26d82dad8..10327586027c8acab99afa90475de2c1a76c40c3 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1025,6 +1025,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -997,6 +997,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
} catch (Exception e) {
}
diff --git a/patches/server/0935-Workaround-for-client-lag-spikes-MC-162253.patch b/patches/server/0904-Workaround-for-client-lag-spikes-MC-162253.patch
index 7da4def3c8..ca03701582 100644
--- a/patches/server/0935-Workaround-for-client-lag-spikes-MC-162253.patch
+++ b/patches/server/0904-Workaround-for-client-lag-spikes-MC-162253.patch
@@ -16,10 +16,10 @@ Co-authored-by: =?UTF-8?q?Dani=C3=ABl=20Goossens?= <[email protected]>
Co-authored-by: Nassim Jahnke <[email protected]>
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 47657f20652a80f50a2e46207c9c05d1a12111b4..c2c01988bf3b6fbb0a7a4716373c2ff0cffce27d 100644
+index 841df3f621081f6b67711cbd047e8bde498eedbf..5461bac37b6dc0575cccd6656b48b2ef18cfaa04 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -2169,6 +2169,46 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1331,6 +1331,46 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
@@ -66,7 +66,7 @@ index 47657f20652a80f50a2e46207c9c05d1a12111b4..c2c01988bf3b6fbb0a7a4716373c2ff0
// Paper start - Anti-Xray - Bypass
private void playerLoadedChunk(ServerPlayer player, MutableObject<java.util.Map<Object, ClientboundLevelChunkWithLightPacket>> cachedDataPackets, LevelChunk chunk) {
if (cachedDataPackets.getValue() == null) {
-@@ -2177,6 +2217,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1339,6 +1379,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
Boolean shouldModify = chunk.getLevel().chunkPacketBlockController.shouldModify(player, chunk);
player.trackChunk(chunk.getPos(), (Packet) cachedDataPackets.getValue().computeIfAbsent(shouldModify, (s) -> {
diff --git a/patches/server/0936-Stop-large-look-changes-from-crashing-the-server.patch b/patches/server/0905-Stop-large-look-changes-from-crashing-the-server.patch
index 136e71286b..136e71286b 100644
--- a/patches/server/0936-Stop-large-look-changes-from-crashing-the-server.patch
+++ b/patches/server/0905-Stop-large-look-changes-from-crashing-the-server.patch
diff --git a/patches/server/0937-Add-custom-destroyerIdentity-to-sendBlockDamage.patch b/patches/server/0906-Add-custom-destroyerIdentity-to-sendBlockDamage.patch
index 679840a612..6a427b822d 100644
--- a/patches/server/0937-Add-custom-destroyerIdentity-to-sendBlockDamage.patch
+++ b/patches/server/0906-Add-custom-destroyerIdentity-to-sendBlockDamage.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add custom destroyerIdentity to sendBlockDamage
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
-index feff87fba11ff46ce1ebd0f5fdf5132b85c8eddc..099987645142a5a065b5bd377a16d9d6c59dabd9 100644
+index f2e5164b0651963cb26e4d776ec5c6e30647790f..a508619e6855ee0b96a9bf61526b1b88abf0e732 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1008,13 +1008,20 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
diff --git a/patches/server/0938-Fix-EndDragonFight-killed-statuses-should-be-false-f.patch b/patches/server/0907-Fix-EndDragonFight-killed-statuses-should-be-false-f.patch
index 0bdc6667a6..0bdc6667a6 100644
--- a/patches/server/0938-Fix-EndDragonFight-killed-statuses-should-be-false-f.patch
+++ b/patches/server/0907-Fix-EndDragonFight-killed-statuses-should-be-false-f.patch
diff --git a/patches/server/0939-Fire-EntityChangeBlockEvent-in-more-places.patch b/patches/server/0908-Fire-EntityChangeBlockEvent-in-more-places.patch
index 35c92f85b3..35c92f85b3 100644
--- a/patches/server/0939-Fire-EntityChangeBlockEvent-in-more-places.patch
+++ b/patches/server/0908-Fire-EntityChangeBlockEvent-in-more-places.patch
diff --git a/patches/server/0940-Missing-eating-regain-reason.patch b/patches/server/0909-Missing-eating-regain-reason.patch
index 1f8bd2bc7b..1f8bd2bc7b 100644
--- a/patches/server/0940-Missing-eating-regain-reason.patch
+++ b/patches/server/0909-Missing-eating-regain-reason.patch
diff --git a/patches/server/0941-Missing-effect-cause.patch b/patches/server/0910-Missing-effect-cause.patch
index 70a763eeb3..70a763eeb3 100644
--- a/patches/server/0941-Missing-effect-cause.patch
+++ b/patches/server/0910-Missing-effect-cause.patch
diff --git a/patches/server/0942-Added-byte-array-serialization-deserialization-for-P.patch b/patches/server/0911-Added-byte-array-serialization-deserialization-for-P.patch
index 3553cbd00c..3553cbd00c 100644
--- a/patches/server/0942-Added-byte-array-serialization-deserialization-for-P.patch
+++ b/patches/server/0911-Added-byte-array-serialization-deserialization-for-P.patch
diff --git a/patches/server/0943-Add-a-consumer-parameter-to-ProjectileSource-launchP.patch b/patches/server/0912-Add-a-consumer-parameter-to-ProjectileSource-launchP.patch
index 8e7053e1f5..8e7053e1f5 100644
--- a/patches/server/0943-Add-a-consumer-parameter-to-ProjectileSource-launchP.patch
+++ b/patches/server/0912-Add-a-consumer-parameter-to-ProjectileSource-launchP.patch
diff --git a/patches/server/0944-Call-BlockPhysicsEvent-more-often.patch b/patches/server/0913-Call-BlockPhysicsEvent-more-often.patch
index 532418a20f..532418a20f 100644
--- a/patches/server/0944-Call-BlockPhysicsEvent-more-often.patch
+++ b/patches/server/0913-Call-BlockPhysicsEvent-more-often.patch
diff --git a/patches/server/0945-Configurable-chat-thread-limit.patch b/patches/server/0914-Configurable-chat-thread-limit.patch
index 45a442f6cf..a5344da3a6 100644
--- a/patches/server/0945-Configurable-chat-thread-limit.patch
+++ b/patches/server/0914-Configurable-chat-thread-limit.patch
@@ -22,10 +22,10 @@ is actually processed, this is honestly really just exposed for the misnomers or
who just wanna ensure that this won't grow over a specific size if chat gets stupidly active
diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
-index aaecd691922a28e971b859175574c80a330edb8e..96b68840ef5c7c726e4c8c2d9f907196561a94bf 100644
+index 84785fed0d85d78c4caf8fabe35c0e89a59240d5..4a8286c78a9a5e305b19cc5d316bc73a78e49b4d 100644
--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
+++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java
-@@ -251,13 +251,26 @@ public class GlobalConfiguration extends ConfigurationPart {
+@@ -252,13 +252,26 @@ public class GlobalConfiguration extends ConfigurationPart {
public Misc misc;
public class Misc extends ConfigurationPart {
diff --git a/patches/server/0946-Mitigate-effects-of-WorldCreator-keepSpawnLoaded-ret.patch b/patches/server/0915-Mitigate-effects-of-WorldCreator-keepSpawnLoaded-ret.patch
index b571f18f1d..b571f18f1d 100644
--- a/patches/server/0946-Mitigate-effects-of-WorldCreator-keepSpawnLoaded-ret.patch
+++ b/patches/server/0915-Mitigate-effects-of-WorldCreator-keepSpawnLoaded-ret.patch
diff --git a/patches/server/0947-Set-position-before-player-sending-on-dimension-chan.patch b/patches/server/0916-Set-position-before-player-sending-on-dimension-chan.patch
index 56b75835df..ecc49e7279 100644
--- a/patches/server/0947-Set-position-before-player-sending-on-dimension-chan.patch
+++ b/patches/server/0916-Set-position-before-player-sending-on-dimension-chan.patch
@@ -7,10 +7,10 @@ This causes a moment where the player entity is sent with the previous location,
teleport packet which is sent shortly after is meant to correct that.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-index 3615576c24d5d6790a6894a91180de25fa0e5a9c..a6fe628eb56833e79a05ce192a3848fc66db93c9 100644
+index 5fca2fc3733880721ae6ff2db020c9809bfffc16..af7acb628b84539b1ee5ef1934f75f091c4cd91e 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
-@@ -1156,6 +1156,7 @@ public class ServerPlayer extends Player {
+@@ -1141,6 +1141,7 @@ public class ServerPlayer extends Player {
// CraftBukkit end
this.setLevel(worldserver);