aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSpottedleaf <[email protected]>2024-06-19 10:29:03 -0700
committerSpottedleaf <[email protected]>2024-06-19 10:29:03 -0700
commit38428c0d6cf9aeea31fffe8f63b8a92c982c8a4f (patch)
tree2164311ca8ff8f4587c7ef1d34421305ac47f023
parentc0268ca86eeb5ca767e5e2860aa2d936e5e7fc4f (diff)
downloadPaper-38428c0d6cf9aeea31fffe8f63b8a92c982c8a4f.tar.gz
Paper-38428c0d6cf9aeea31fffe8f63b8a92c982c8a4f.zip
Cleanup MCUtils patch for chunk system
Remove utilities that are unused, as well as replacing the full chunk map with a concurrentutil implementation. Additionally, fix the addition/removal of chunks to/from the full chunk map so that getChunkIfLoaded correctly returns a non-null chunk when calling the load or unload events.
-rw-r--r--leaf_notes.txt9
-rw-r--r--patches/server/0009-MC-Utils.patch2347
-rw-r--r--patches/server/0010-Adventure.patch6
-rw-r--r--patches/server/0011-Use-TerminalConsoleAppender-for-console-improvements.patch4
-rw-r--r--patches/server/0020-Plugin-remapping.patch2
-rw-r--r--patches/server/0023-Timings-v2.patch30
-rw-r--r--patches/server/0025-Further-improve-server-tick-loop.patch2
-rw-r--r--patches/server/0034-Expose-server-build-information.patch4
-rw-r--r--patches/server/0038-Prevent-block-entity-and-entity-crashes.patch4
-rw-r--r--patches/server/0044-Optimize-explosions.patch4
-rw-r--r--patches/server/0063-Add-exception-reporting-event.patch6
-rw-r--r--patches/server/0078-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch2
-rw-r--r--patches/server/0080-Configurable-Chunk-Inhabited-Time.patch4
-rw-r--r--patches/server/0089-Configurable-Player-Collision.patch2
-rw-r--r--patches/server/0094-remove-null-possibility-for-getServer-singleton.patch4
-rw-r--r--patches/server/0098-Async-GameProfileCache-saving.patch2
-rw-r--r--patches/server/0129-Properly-handle-async-calls-to-restart-the-server.patch2
-rw-r--r--patches/server/0137-Basic-PlayerProfile-API.patch2
-rw-r--r--patches/server/0165-PlayerNaturallySpawnCreaturesEvent.patch10
-rw-r--r--patches/server/0175-Implement-extended-PaperServerListPingEvent.patch2
-rw-r--r--patches/server/0227-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch6
-rw-r--r--patches/server/0228-Add-Early-Warning-Feature-to-WatchDog.patch2
-rw-r--r--patches/server/0263-Improve-Server-Thread-Pool-and-Thread-Priorities.patch2
-rw-r--r--patches/server/0264-Optimize-World-Time-Updates.patch2
-rw-r--r--patches/server/0277-Async-command-map-building.patch2
-rw-r--r--patches/server/0286-Server-Tick-Events.patch2
-rw-r--r--patches/server/0312-Tracking-Range-Improvements.patch4
-rw-r--r--patches/server/0315-Optimise-getChunkAt-calls-for-loaded-chunks.patch6
-rw-r--r--patches/server/0316-Add-debug-for-sync-chunk-loads.patch4
-rw-r--r--patches/server/0326-Optimise-Chunk-getFluid.patch6
-rw-r--r--patches/server/0328-Add-tick-times-API-and-mspt-command.patch4
-rw-r--r--patches/server/0334-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch4
-rw-r--r--patches/server/0345-Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch4
-rw-r--r--patches/server/0354-misc-debugging-dumps.patch2
-rw-r--r--patches/server/0361-Wait-for-Async-Tasks-during-shutdown.patch2
-rw-r--r--patches/server/0376-Fix-Per-World-Difficulty-Remembering-Difficulty.patch6
-rw-r--r--patches/server/0410-Cache-block-data-strings.patch4
-rw-r--r--patches/server/0418-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch4
-rw-r--r--patches/server/0465-Add-ServerResourcesReloadedEvent.patch6
-rw-r--r--patches/server/0489-Add-EntityMoveEvent.patch2
-rw-r--r--patches/server/0508-forced-whitelist-use-configurable-kick-message.patch4
-rw-r--r--patches/server/0548-Add-PlayerKickEvent-causes.patch4
-rw-r--r--patches/server/0607-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch16
-rw-r--r--patches/server/0611-Oprimise-map-impl-for-tracked-players.patch4
-rw-r--r--patches/server/0651-Expose-vanilla-BiomeProvider-from-WorldInfo.patch2
-rw-r--r--patches/server/0668-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch2
-rw-r--r--patches/server/0672-Option-to-have-default-CustomSpawners-in-custom-worl.patch2
-rw-r--r--patches/server/0673-Put-world-into-worldlist-before-initing-the-world.patch2
-rw-r--r--patches/server/0674-Custom-Potion-Mixes.patch4
-rw-r--r--patches/server/0712-Throw-exception-on-world-create-while-being-ticked.patch4
-rw-r--r--patches/server/0730-Warn-on-plugins-accessing-faraway-chunks.patch2
-rw-r--r--patches/server/0738-Fix-plugin-loggers-on-server-shutdown.patch2
-rw-r--r--patches/server/0751-Fix-a-bunch-of-vanilla-bugs.patch4
-rw-r--r--patches/server/0778-Fix-premature-player-kicks-on-shutdown.patch4
-rw-r--r--patches/server/0780-Player-Entity-Tracking-Events.patch4
-rw-r--r--patches/server/0826-Fix-block-place-logic.patch4
-rw-r--r--patches/server/0851-Folia-scheduler-and-owned-region-API.patch2
-rw-r--r--patches/server/0855-Only-capture-actual-tree-growth.patch4
-rw-r--r--patches/server/0869-Configurable-entity-tracking-range-by-Y-coordinate.patch4
-rw-r--r--patches/server/0901-Don-t-check-if-we-can-see-non-visible-entities.patch4
-rw-r--r--patches/server/0915-Don-t-fire-sync-events-during-worldgen.patch2
-rw-r--r--patches/server/0927-Properly-handle-experience-dropping-on-block-break.patch4
-rw-r--r--patches/server/0930-Reduce-allocation-of-Vec3D-by-entity-tracker.patch4
-rw-r--r--patches/server/0945-Add-onboarding-message-for-initial-server-start.patch2
-rw-r--r--patches/server/0951-Fix-creation-of-invalid-block-entity-during-world-ge.patch4
-rw-r--r--patches/server/0955-Per-world-ticks-per-spawn-settings.patch2
-rw-r--r--patches/server/0957-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch4
-rw-r--r--patches/server/0975-Brigadier-based-command-API.patch6
-rw-r--r--patches/server/0981-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch6
-rw-r--r--patches/server/0991-Chunk-System-Starlight-from-Moonrise.patch237
-rw-r--r--patches/server/0994-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch2
-rw-r--r--patches/server/0996-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch2
-rw-r--r--patches/server/1000-Entity-Activation-Range-2.0.patch2
-rw-r--r--patches/server/1001-Optional-per-player-mob-spawns.patch8
-rw-r--r--patches/server/1002-Anti-Xray.patch6
-rw-r--r--patches/server/1004-Add-Alternate-Current-redstone-implementation.patch4
-rw-r--r--patches/server/1005-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch8
-rw-r--r--patches/server/1008-Optimize-Hoppers.patch2
-rw-r--r--patches/server/1023-Improved-Watchdog-Support.patch12
79 files changed, 365 insertions, 2552 deletions
diff --git a/leaf_notes.txt b/leaf_notes.txt
index 7c8d0c37cc..4bd9fdf957 100644
--- a/leaf_notes.txt
+++ b/leaf_notes.txt
@@ -1,13 +1,4 @@
- note: for paper, the chunk debug command
-- rebase IntervalledCounter into util patch
- mcutil diff
- paper debug chunks --async in DedicatedServer
- TODO keep around region file lock?
-- mcutil#getTicketLevelFor is wrong, just delete it later
-
-on another note, clean up mcutils...
-
-apply todo in levelmixin
-
-to fix later:
-- Change loadedChunkMap in ServerChunkCache to use concurrent long map
diff --git a/patches/server/0009-MC-Utils.patch b/patches/server/0009-MC-Utils.patch
index 1d50fab8f5..c12a8e7c5c 100644
--- a/patches/server/0009-MC-Utils.patch
+++ b/patches/server/0009-MC-Utils.patch
@@ -12,456 +12,6 @@ public net.minecraft.server.level.ServerChunkCache mainThreadProcessor
public net.minecraft.server.level.ServerChunkCache$MainThreadExecutor
public net.minecraft.world.level.chunk.LevelChunkSection states
-diff --git a/src/main/java/com/destroystokyo/paper/util/concurrent/WeakSeqLock.java b/src/main/java/com/destroystokyo/paper/util/concurrent/WeakSeqLock.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..4029dc68cf35d63aa70c4a76c35bf65a7fc6358f
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/util/concurrent/WeakSeqLock.java
-@@ -0,0 +1,68 @@
-+package com.destroystokyo.paper.util.concurrent;
-+
-+import java.util.concurrent.atomic.AtomicLong;
-+
-+/**
-+ * copied from https://github.com/Spottedleaf/ConcurrentUtil/blob/master/src/main/java/ca/spottedleaf/concurrentutil/lock/WeakSeqLock.java
-+ * @author Spottedleaf
-+ */
-+public final class WeakSeqLock {
-+ // TODO when the switch to J11 is made, nuke this class from orbit
-+
-+ protected final AtomicLong lock = new AtomicLong();
-+
-+ public WeakSeqLock() {
-+ //VarHandle.storeStoreFence(); // warn: usages must be checked to ensure this behaviour isn't needed
-+ }
-+
-+ public void acquireWrite() {
-+ // must be release-type write
-+ this.lock.lazySet(this.lock.get() + 1);
-+ }
-+
-+ public boolean canRead(final long read) {
-+ return (read & 1) == 0;
-+ }
-+
-+ public boolean tryAcquireWrite() {
-+ this.acquireWrite();
-+ return true;
-+ }
-+
-+ public void releaseWrite() {
-+ // must be acquire-type write
-+ final long lock = this.lock.get(); // volatile here acts as store-store
-+ this.lock.lazySet(lock + 1);
-+ }
-+
-+ public void abortWrite() {
-+ // must be acquire-type write
-+ final long lock = this.lock.get(); // volatile here acts as store-store
-+ this.lock.lazySet(lock ^ 1);
-+ }
-+
-+ public long acquireRead() {
-+ int failures = 0;
-+ long curr;
-+
-+ for (curr = this.lock.get(); !this.canRead(curr); curr = this.lock.get()) {
-+ // without j11, our only backoff is the yield() call...
-+
-+ if (++failures > 5_000) { /* TODO determine a threshold */
-+ Thread.yield();
-+ }
-+ /* Better waiting is beyond the scope of this lock; if it is needed the lock is being misused */
-+ }
-+
-+ //VarHandle.loadLoadFence(); // volatile acts as the load-load barrier
-+ return curr;
-+ }
-+
-+ public boolean tryReleaseRead(final long read) {
-+ return this.lock.get() == read; // volatile acts as the load-load barrier
-+ }
-+
-+ public long getSequentialCounter() {
-+ return this.lock.get();
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/util/map/QueuedChangesMapLong2Int.java b/src/main/java/com/destroystokyo/paper/util/map/QueuedChangesMapLong2Int.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..59868f37d14bbc0ece0836095cdad148778995e6
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/util/map/QueuedChangesMapLong2Int.java
-@@ -0,0 +1,162 @@
-+package com.destroystokyo.paper.util.map;
-+
-+import com.destroystokyo.paper.util.concurrent.WeakSeqLock;
-+import it.unimi.dsi.fastutil.longs.Long2IntMap;
-+import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
-+import it.unimi.dsi.fastutil.longs.LongIterator;
-+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-+import it.unimi.dsi.fastutil.objects.ObjectIterator;
-+
-+/**
-+ * @author Spottedleaf
-+ */
-+public class QueuedChangesMapLong2Int {
-+
-+ protected final Long2IntOpenHashMap updatingMap;
-+ protected final Long2IntOpenHashMap visibleMap;
-+ protected final Long2IntOpenHashMap queuedPuts;
-+ protected final LongOpenHashSet queuedRemove;
-+
-+ protected int queuedDefaultReturnValue;
-+
-+ // we use a seqlock as writes are not common.
-+ protected final WeakSeqLock updatingMapSeqLock = new WeakSeqLock();
-+
-+ public QueuedChangesMapLong2Int() {
-+ this(16, 0.75f);
-+ }
-+
-+ public QueuedChangesMapLong2Int(final int capacity, final float loadFactor) {
-+ this.updatingMap = new Long2IntOpenHashMap(capacity, loadFactor);
-+ this.visibleMap = new Long2IntOpenHashMap(capacity, loadFactor);
-+ this.queuedPuts = new Long2IntOpenHashMap();
-+ this.queuedRemove = new LongOpenHashSet();
-+ }
-+
-+ public void queueDefaultReturnValue(final int dfl) {
-+ this.queuedDefaultReturnValue = dfl;
-+ this.updatingMap.defaultReturnValue(dfl);
-+ }
-+
-+ public int queueUpdate(final long k, final int v) {
-+ this.queuedRemove.remove(k);
-+ this.queuedPuts.put(k, v);
-+
-+ return this.updatingMap.put(k, v);
-+ }
-+
-+ public int queueRemove(final long k) {
-+ this.queuedPuts.remove(k);
-+ this.queuedRemove.add(k);
-+
-+ return this.updatingMap.remove(k);
-+ }
-+
-+ public int getUpdating(final long k) {
-+ return this.updatingMap.get(k);
-+ }
-+
-+ public int getVisible(final long k) {
-+ return this.visibleMap.get(k);
-+ }
-+
-+ public int getVisibleAsync(final long k) {
-+ long readlock;
-+ int ret = 0;
-+
-+ do {
-+ readlock = this.updatingMapSeqLock.acquireRead();
-+ try {
-+ ret = this.visibleMap.get(k);
-+ } catch (final Throwable thr) {
-+ if (thr instanceof ThreadDeath) {
-+ throw (ThreadDeath)thr;
-+ }
-+ // ignore...
-+ continue;
-+ }
-+
-+ } while (!this.updatingMapSeqLock.tryReleaseRead(readlock));
-+
-+ return ret;
-+ }
-+
-+ public boolean performUpdates() {
-+ this.updatingMapSeqLock.acquireWrite();
-+ this.visibleMap.defaultReturnValue(this.queuedDefaultReturnValue);
-+ this.updatingMapSeqLock.releaseWrite();
-+
-+ if (this.queuedPuts.isEmpty() && this.queuedRemove.isEmpty()) {
-+ return false;
-+ }
-+
-+ // update puts
-+ final ObjectIterator<Long2IntMap.Entry> iterator0 = this.queuedPuts.long2IntEntrySet().fastIterator();
-+ while (iterator0.hasNext()) {
-+ final Long2IntMap.Entry entry = iterator0.next();
-+ final long key = entry.getLongKey();
-+ final int val = entry.getIntValue();
-+
-+ this.updatingMapSeqLock.acquireWrite();
-+ try {
-+ this.visibleMap.put(key, val);
-+ } finally {
-+ this.updatingMapSeqLock.releaseWrite();
-+ }
-+ }
-+
-+ this.queuedPuts.clear();
-+
-+ final LongIterator iterator1 = this.queuedRemove.iterator();
-+ while (iterator1.hasNext()) {
-+ final long key = iterator1.nextLong();
-+
-+ this.updatingMapSeqLock.acquireWrite();
-+ try {
-+ this.visibleMap.remove(key);
-+ } finally {
-+ this.updatingMapSeqLock.releaseWrite();
-+ }
-+ }
-+
-+ this.queuedRemove.clear();
-+
-+ return true;
-+ }
-+
-+ public boolean performUpdatesLockMap() {
-+ this.updatingMapSeqLock.acquireWrite();
-+ try {
-+ this.visibleMap.defaultReturnValue(this.queuedDefaultReturnValue);
-+
-+ if (this.queuedPuts.isEmpty() && this.queuedRemove.isEmpty()) {
-+ return false;
-+ }
-+
-+ // update puts
-+ final ObjectIterator<Long2IntMap.Entry> iterator0 = this.queuedPuts.long2IntEntrySet().fastIterator();
-+ while (iterator0.hasNext()) {
-+ final Long2IntMap.Entry entry = iterator0.next();
-+ final long key = entry.getLongKey();
-+ final int val = entry.getIntValue();
-+
-+ this.visibleMap.put(key, val);
-+ }
-+
-+ this.queuedPuts.clear();
-+
-+ final LongIterator iterator1 = this.queuedRemove.iterator();
-+ while (iterator1.hasNext()) {
-+ final long key = iterator1.nextLong();
-+
-+ this.visibleMap.remove(key);
-+ }
-+
-+ this.queuedRemove.clear();
-+
-+ return true;
-+ } finally {
-+ this.updatingMapSeqLock.releaseWrite();
-+ }
-+ }
-+}
-diff --git a/src/main/java/com/destroystokyo/paper/util/map/QueuedChangesMapLong2Object.java b/src/main/java/com/destroystokyo/paper/util/map/QueuedChangesMapLong2Object.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..7bab31a312463cc963d9621cdc543a281459bd32
---- /dev/null
-+++ b/src/main/java/com/destroystokyo/paper/util/map/QueuedChangesMapLong2Object.java
-@@ -0,0 +1,202 @@
-+package com.destroystokyo.paper.util.map;
-+
-+import com.destroystokyo.paper.util.concurrent.WeakSeqLock;
-+import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
-+import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
-+import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
-+import java.util.ArrayList;
-+import java.util.Collection;
-+import java.util.List;
-+
-+/**
-+ * @author Spottedleaf
-+ */
-+public class QueuedChangesMapLong2Object<V> {
-+
-+ protected static final Object REMOVED = new Object();
-+
-+ protected final Long2ObjectLinkedOpenHashMap<V> updatingMap;
-+ protected final Long2ObjectLinkedOpenHashMap<V> visibleMap;
-+ protected final Long2ObjectLinkedOpenHashMap<Object> queuedChanges;
-+
-+ // we use a seqlock as writes are not common.
-+ protected final WeakSeqLock updatingMapSeqLock = new WeakSeqLock();
-+
-+ public QueuedChangesMapLong2Object() {
-+ this(16, 0.75f); // dfl for fastutil
-+ }
-+
-+ public QueuedChangesMapLong2Object(final int capacity, final float loadFactor) {
-+ this.updatingMap = new Long2ObjectLinkedOpenHashMap<>(capacity, loadFactor);
-+ this.visibleMap = new Long2ObjectLinkedOpenHashMap<>(capacity, loadFactor);
-+ this.queuedChanges = new Long2ObjectLinkedOpenHashMap<>();
-+ }
-+
-+ public V queueUpdate(final long k, final V value) {
-+ this.queuedChanges.put(k, value);
-+ return this.updatingMap.put(k, value);
-+ }
-+
-+ public V queueRemove(final long k) {
-+ this.queuedChanges.put(k, REMOVED);
-+ return this.updatingMap.remove(k);
-+ }
-+
-+ public V getUpdating(final long k) {
-+ return this.updatingMap.get(k);
-+ }
-+
-+ public boolean updatingContainsKey(final long k) {
-+ return this.updatingMap.containsKey(k);
-+ }
-+
-+ public V getVisible(final long k) {
-+ return this.visibleMap.get(k);
-+ }
-+
-+ public boolean visibleContainsKey(final long k) {
-+ return this.visibleMap.containsKey(k);
-+ }
-+
-+ public V getVisibleAsync(final long k) {
-+ long readlock;
-+ V ret = null;
-+
-+ do {
-+ readlock = this.updatingMapSeqLock.acquireRead();
-+
-+ try {
-+ ret = this.visibleMap.get(k);
-+ } catch (final Throwable thr) {
-+ if (thr instanceof ThreadDeath) {
-+ throw (ThreadDeath)thr;
-+ }
-+ // ignore...
-+ continue;
-+ }
-+
-+ } while (!this.updatingMapSeqLock.tryReleaseRead(readlock));
-+
-+ return ret;
-+ }
-+
-+ public boolean visibleContainsKeyAsync(final long k) {
-+ long readlock;
-+ boolean ret = false;
-+
-+ do {
-+ readlock = this.updatingMapSeqLock.acquireRead();
-+
-+ try {
-+ ret = this.visibleMap.containsKey(k);
-+ } catch (final Throwable thr) {
-+ if (thr instanceof ThreadDeath) {
-+ throw (ThreadDeath)thr;
-+ }
-+ // ignore...
-+ continue;
-+ }
-+
-+ } while (!this.updatingMapSeqLock.tryReleaseRead(readlock));
-+
-+ return ret;
-+ }
-+
-+ public Long2ObjectLinkedOpenHashMap<V> getVisibleMap() {
-+ return this.visibleMap;
-+ }
-+
-+ public Long2ObjectLinkedOpenHashMap<V> getUpdatingMap() {
-+ return this.updatingMap;
-+ }
-+
-+ public int getVisibleSize() {
-+ return this.visibleMap.size();
-+ }
-+
-+ public int getVisibleSizeAsync() {
-+ long readlock;
-+ int ret;
-+
-+ do {
-+ readlock = this.updatingMapSeqLock.acquireRead();
-+ ret = this.visibleMap.size();
-+ } while (!this.updatingMapSeqLock.tryReleaseRead(readlock));
-+
-+ return ret;
-+ }
-+
-+ // unlike mojang's impl this cannot be used async since it's not a view of an immutable map
-+ public Collection<V> getUpdatingValues() {
-+ return this.updatingMap.values();
-+ }
-+
-+ public List<V> getUpdatingValuesCopy() {
-+ return new ArrayList<>(this.updatingMap.values());
-+ }
-+
-+ // unlike mojang's impl this cannot be used async since it's not a view of an immutable map
-+ public Collection<V> getVisibleValues() {
-+ return this.visibleMap.values();
-+ }
-+
-+ public List<V> getVisibleValuesCopy() {
-+ return new ArrayList<>(this.visibleMap.values());
-+ }
-+
-+ public boolean performUpdates() {
-+ if (this.queuedChanges.isEmpty()) {
-+ return false;
-+ }
-+
-+ final ObjectBidirectionalIterator<Long2ObjectMap.Entry<Object>> iterator = this.queuedChanges.long2ObjectEntrySet().fastIterator();
-+ while (iterator.hasNext()) {
-+ final Long2ObjectMap.Entry<Object> entry = iterator.next();
-+ final long key = entry.getLongKey();
-+ final Object val = entry.getValue();
-+
-+ this.updatingMapSeqLock.acquireWrite();
-+ try {
-+ if (val == REMOVED) {
-+ this.visibleMap.remove(key);
-+ } else {
-+ this.visibleMap.put(key, (V)val);
-+ }
-+ } finally {
-+ this.updatingMapSeqLock.releaseWrite();
-+ }
-+ }
-+
-+ this.queuedChanges.clear();
-+ return true;
-+ }
-+
-+ public boolean performUpdatesLockMap() {
-+ if (this.queuedChanges.isEmpty()) {
-+ return false;
-+ }
-+
-+ final ObjectBidirectionalIterator<Long2ObjectMap.Entry<Object>> iterator = this.queuedChanges.long2ObjectEntrySet().fastIterator();
-+
-+ try {
-+ this.updatingMapSeqLock.acquireWrite();
-+
-+ while (iterator.hasNext()) {
-+ final Long2ObjectMap.Entry<Object> entry = iterator.next();
-+ final long key = entry.getLongKey();
-+ final Object val = entry.getValue();
-+
-+ if (val == REMOVED) {
-+ this.visibleMap.remove(key);
-+ } else {
-+ this.visibleMap.put(key, (V)val);
-+ }
-+ }
-+ } finally {
-+ this.updatingMapSeqLock.releaseWrite();
-+ }
-+
-+ this.queuedChanges.clear();
-+ return true;
-+ }
-+}
diff --git a/src/main/java/com/destroystokyo/paper/util/maplist/ChunkList.java b/src/main/java/com/destroystokyo/paper/util/maplist/ChunkList.java
new file mode 100644
index 0000000000000000000000000000000000000000..554f4d4e63c1431721989e6f502a32ccc53a8807
@@ -2178,495 +1728,12 @@ index 46cab7a8c7b87ab01b26074b04f5a02b3907cfc4..49019b4a9bc4e634d54a9b0acaf9229a
+ }
+ // Paper end
}
-diff --git a/src/main/java/io/papermc/paper/chunk/SingleThreadChunkRegionManager.java b/src/main/java/io/papermc/paper/chunk/SingleThreadChunkRegionManager.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..a5f706d6f716b2a463ae58adcde69d9e665c7733
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/chunk/SingleThreadChunkRegionManager.java
-@@ -0,0 +1,477 @@
-+package io.papermc.paper.chunk;
-+
-+import io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet;
-+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
-+import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
-+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
-+import io.papermc.paper.util.MCUtil;
-+import net.minecraft.server.level.ServerLevel;
-+import net.minecraft.world.level.ChunkPos;
-+import java.util.ArrayList;
-+import java.util.Arrays;
-+import java.util.Iterator;
-+import java.util.List;
-+import java.util.function.Supplier;
-+
-+public final class SingleThreadChunkRegionManager {
-+
-+ protected final int regionSectionMergeRadius;
-+ protected final int regionSectionChunkSize;
-+ public final int regionChunkShift; // log2(REGION_CHUNK_SIZE)
-+
-+ public final ServerLevel world;
-+ public final String name;
-+
-+ protected final Long2ObjectOpenHashMap<RegionSection> regionsBySection = new Long2ObjectOpenHashMap<>();
-+ protected final ReferenceLinkedOpenHashSet<Region> needsRecalculation = new ReferenceLinkedOpenHashSet<>();
-+ protected final int minSectionRecalcCount;
-+ protected final double maxDeadRegionPercent;
-+ protected final Supplier<RegionData> regionDataSupplier;
-+ protected final Supplier<RegionSectionData> regionSectionDataSupplier;
-+
-+ public SingleThreadChunkRegionManager(final ServerLevel world, final int minSectionRecalcCount,
-+ final double maxDeadRegionPercent, final int sectionMergeRadius,
-+ final int regionSectionChunkShift,
-+ final String name, final Supplier<RegionData> regionDataSupplier,
-+ final Supplier<RegionSectionData> regionSectionDataSupplier) {
-+ this.regionSectionMergeRadius = sectionMergeRadius;
-+ this.regionSectionChunkSize = 1 << regionSectionChunkShift;
-+ this.regionChunkShift = regionSectionChunkShift;
-+ this.world = world;
-+ this.name = name;
-+ this.minSectionRecalcCount = Math.max(2, minSectionRecalcCount);
-+ this.maxDeadRegionPercent = maxDeadRegionPercent;
-+ this.regionDataSupplier = regionDataSupplier;
-+ this.regionSectionDataSupplier = regionSectionDataSupplier;
-+ }
-+
-+ // tested via https://gist.github.com/Spottedleaf/aa7ade3451c37b4cac061fc77074db2f
-+
-+ /*
-+ protected void check() {
-+ ReferenceOpenHashSet<Region<T>> checked = new ReferenceOpenHashSet<>();
-+
-+ for (RegionSection<T> section : this.regionsBySection.values()) {
-+ if (!checked.add(section.region)) {
-+ section.region.check();
-+ }
-+ }
-+ for (Region<T> region : this.needsRecalculation) {
-+ region.check();
-+ }
-+ }
-+ */
-+
-+ protected void addToRecalcQueue(final Region region) {
-+ this.needsRecalculation.add(region);
-+ }
-+
-+ protected void removeFromRecalcQueue(final Region region) {
-+ this.needsRecalculation.remove(region);
-+ }
-+
-+ public RegionSection getRegionSection(final int chunkX, final int chunkZ) {
-+ return this.regionsBySection.get(MCUtil.getCoordinateKey(chunkX >> this.regionChunkShift, chunkZ >> this.regionChunkShift));
-+ }
-+
-+ public Region getRegion(final int chunkX, final int chunkZ) {
-+ final RegionSection section = this.regionsBySection.get(MCUtil.getCoordinateKey(chunkX >> regionChunkShift, chunkZ >> regionChunkShift));
-+ return section != null ? section.region : null;
-+ }
-+
-+ private final List<Region> toMerge = new ArrayList<>();
-+
-+ protected RegionSection getOrCreateAndMergeSection(final int sectionX, final int sectionZ, final RegionSection force) {
-+ final long sectionKey = MCUtil.getCoordinateKey(sectionX, sectionZ);
-+
-+ if (force == null) {
-+ RegionSection region = this.regionsBySection.get(sectionKey);
-+ if (region != null) {
-+ return region;
-+ }
-+ }
-+
-+ int mergeCandidateSectionSize = -1;
-+ Region mergeIntoCandidate = null;
-+
-+ // find optimal candidate to merge into
-+
-+ final int minX = sectionX - this.regionSectionMergeRadius;
-+ final int maxX = sectionX + this.regionSectionMergeRadius;
-+ final int minZ = sectionZ - this.regionSectionMergeRadius;
-+ final int maxZ = sectionZ + this.regionSectionMergeRadius;
-+ for (int currX = minX; currX <= maxX; ++currX) {
-+ for (int currZ = minZ; currZ <= maxZ; ++currZ) {
-+ final RegionSection section = this.regionsBySection.get(MCUtil.getCoordinateKey(currX, currZ));
-+ if (section == null) {
-+ continue;
-+ }
-+ final Region region = section.region;
-+ if (region.dead) {
-+ throw new IllegalStateException("Dead region should not be in live region manager state: " + region);
-+ }
-+ final int sections = region.sections.size();
-+
-+ if (sections > mergeCandidateSectionSize) {
-+ mergeCandidateSectionSize = sections;
-+ mergeIntoCandidate = region;
-+ }
-+ this.toMerge.add(region);
-+ }
-+ }
-+
-+ // merge
-+ if (mergeIntoCandidate != null) {
-+ for (int i = 0; i < this.toMerge.size(); ++i) {
-+ final Region region = this.toMerge.get(i);
-+ if (region.dead || mergeIntoCandidate == region) {
-+ continue;
-+ }
-+ region.mergeInto(mergeIntoCandidate);
-+ }
-+ this.toMerge.clear();
-+ } else {
-+ mergeIntoCandidate = new Region(this);
-+ }
-+
-+ final RegionSection section;
-+ if (force == null) {
-+ this.regionsBySection.put(sectionKey, section = new RegionSection(sectionKey, this));
-+ } else {
-+ final RegionSection existing = this.regionsBySection.putIfAbsent(sectionKey, force);
-+ if (existing != null) {
-+ throw new IllegalStateException("Attempting to override section '" + existing.toStringWithRegion() +
-+ ", with " + force.toStringWithRegion());
-+ }
-+
-+ section = force;
-+ }
-+
-+ mergeIntoCandidate.addRegionSection(section);
-+ //mergeIntoCandidate.check();
-+ //this.check();
-+
-+ return section;
-+ }
-+
-+ public void addChunk(final int chunkX, final int chunkZ) {
-+ this.getOrCreateAndMergeSection(chunkX >> this.regionChunkShift, chunkZ >> this.regionChunkShift, null).addChunk(chunkX, chunkZ);
-+ }
-+
-+ public void removeChunk(final int chunkX, final int chunkZ) {
-+ final RegionSection section = this.regionsBySection.get(
-+ MCUtil.getCoordinateKey(chunkX >> this.regionChunkShift, chunkZ >> this.regionChunkShift)
-+ );
-+ if (section != null) {
-+ section.removeChunk(chunkX, chunkZ);
-+ } else {
-+ throw new IllegalStateException("Cannot remove chunk at (" + chunkX + "," + chunkZ + ") from region state, section does not exist");
-+ }
-+ }
-+
-+ public void recalculateRegions() {
-+ for (int i = 0, len = this.needsRecalculation.size(); i < len; ++i) {
-+ final Region region = this.needsRecalculation.removeFirst();
-+
-+ this.recalculateRegion(region);
-+ //this.check();
-+ }
-+ }
-+
-+ protected void recalculateRegion(final Region region) {
-+ region.markedForRecalc = false;
-+ //region.check();
-+ // clear unused regions
-+ for (final Iterator<RegionSection> iterator = region.deadSections.iterator(); iterator.hasNext();) {
-+ final RegionSection deadSection = iterator.next();
-+
-+ if (deadSection.hasChunks()) {
-+ throw new IllegalStateException("Dead section '" + deadSection.toStringWithRegion() + "' is marked dead but has chunks!");
-+ }
-+ if (!region.removeRegionSection(deadSection)) {
-+ throw new IllegalStateException("Region " + region + " has inconsistent state, it should contain section " + deadSection);
-+ }
-+ if (!this.regionsBySection.remove(deadSection.regionCoordinate, deadSection)) {
-+ throw new IllegalStateException("Cannot remove dead section '" +
-+ deadSection.toStringWithRegion() + "' from section state! State at section coordinate: " +
-+ this.regionsBySection.get(deadSection.regionCoordinate));
-+ }
-+ }
-+ region.deadSections.clear();
-+
-+ // implicitly cover cases where size == 0
-+ if (region.sections.size() < this.minSectionRecalcCount) {
-+ //region.check();
-+ return;
-+ }
-+
-+ // run a test to see if we actually need to recalculate
-+ // TODO
-+
-+ // destroy and rebuild the region
-+ region.dead = true;
-+
-+ // destroy region state
-+ for (final Iterator<RegionSection> iterator = region.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
-+ final RegionSection aliveSection = iterator.next();
-+ if (!aliveSection.hasChunks()) {
-+ throw new IllegalStateException("Alive section '" + aliveSection.toStringWithRegion() + "' has no chunks!");
-+ }
-+ if (!this.regionsBySection.remove(aliveSection.regionCoordinate, aliveSection)) {
-+ throw new IllegalStateException("Cannot remove alive section '" +
-+ aliveSection.toStringWithRegion() + "' from section state! State at section coordinate: " +
-+ this.regionsBySection.get(aliveSection.regionCoordinate));
-+ }
-+ }
-+
-+ // rebuild regions
-+ for (final Iterator<RegionSection> iterator = region.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
-+ final RegionSection aliveSection = iterator.next();
-+ this.getOrCreateAndMergeSection(aliveSection.getSectionX(), aliveSection.getSectionZ(), aliveSection);
-+ }
-+ }
-+
-+ public static final class Region {
-+ protected final IteratorSafeOrderedReferenceSet<RegionSection> sections = new IteratorSafeOrderedReferenceSet<>();
-+ protected final ReferenceOpenHashSet<RegionSection> deadSections = new ReferenceOpenHashSet<>(16, 0.7f);
-+ protected boolean dead;
-+ protected boolean markedForRecalc;
-+
-+ public final SingleThreadChunkRegionManager regionManager;
-+ public final RegionData regionData;
-+
-+ protected Region(final SingleThreadChunkRegionManager regionManager) {
-+ this.regionManager = regionManager;
-+ this.regionData = regionManager.regionDataSupplier.get();
-+ }
-+
-+ public IteratorSafeOrderedReferenceSet.Iterator<RegionSection> getSections() {
-+ return this.sections.iterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS);
-+ }
-+
-+ protected final double getDeadSectionPercent() {
-+ return (double)this.deadSections.size() / (double)this.sections.size();
-+ }
-+
-+ /*
-+ protected void check() {
-+ if (this.dead) {
-+ throw new IllegalStateException("Dead region!");
-+ }
-+ for (final Iterator<RegionSection<T>> iterator = this.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
-+ final RegionSection<T> section = iterator.next();
-+ if (section.region != this) {
-+ throw new IllegalStateException("Region section must point to us!");
-+ }
-+ if (this.regionManager.regionsBySection.get(section.regionCoordinate) != section) {
-+ throw new IllegalStateException("Region section must match the regionmanager state!");
-+ }
-+ }
-+ }
-+ */
-+
-+ // note: it is not true that the region at this point is not in any region. use the region field on the section
-+ // to see if it is currently in another region.
-+ protected final boolean addRegionSection(final RegionSection section) {
-+ if (!this.sections.add(section)) {
-+ return false;
-+ }
-+
-+ section.sectionData.addToRegion(section, section.region, this);
-+
-+ section.region = this;
-+ return true;
-+ }
-+
-+ protected final boolean removeRegionSection(final RegionSection section) {
-+ if (!this.sections.remove(section)) {
-+ return false;
-+ }
-+
-+ section.sectionData.removeFromRegion(section, this);
-+
-+ return true;
-+ }
-+
-+ protected void mergeInto(final Region mergeTarget) {
-+ if (this == mergeTarget) {
-+ throw new IllegalStateException("Cannot merge a region onto itself");
-+ }
-+ if (this.dead) {
-+ throw new IllegalStateException("Source region is dead! Source " + this + ", target " + mergeTarget);
-+ } else if (mergeTarget.dead) {
-+ throw new IllegalStateException("Target region is dead! Source " + this + ", target " + mergeTarget);
-+ }
-+ this.dead = true;
-+ if (this.markedForRecalc) {
-+ this.regionManager.removeFromRecalcQueue(this);
-+ }
-+
-+ for (final Iterator<RegionSection> iterator = this.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
-+ final RegionSection section = iterator.next();
-+
-+ if (!mergeTarget.addRegionSection(section)) {
-+ throw new IllegalStateException("Target cannot contain source's sections! Source " + this + ", target " + mergeTarget);
-+ }
-+ }
-+
-+ for (final RegionSection deadSection : this.deadSections) {
-+ if (!this.sections.contains(deadSection)) {
-+ throw new IllegalStateException("Source region does not even contain its own dead sections! Missing " + deadSection + " from region " + this);
-+ }
-+ mergeTarget.deadSections.add(deadSection);
-+ }
-+ //mergeTarget.check();
-+ }
-+
-+ protected void markSectionAlive(final RegionSection section) {
-+ this.deadSections.remove(section);
-+ if (this.markedForRecalc && (this.sections.size() < this.regionManager.minSectionRecalcCount || this.getDeadSectionPercent() < this.regionManager.maxDeadRegionPercent)) {
-+ this.regionManager.removeFromRecalcQueue(this);
-+ this.markedForRecalc = false;
-+ }
-+ }
-+
-+ protected void markSectionDead(final RegionSection section) {
-+ this.deadSections.add(section);
-+ if (!this.markedForRecalc && (this.sections.size() >= this.regionManager.minSectionRecalcCount || this.sections.size() == this.deadSections.size()) && this.getDeadSectionPercent() >= this.regionManager.maxDeadRegionPercent) {
-+ this.regionManager.addToRecalcQueue(this);
-+ this.markedForRecalc = true;
-+ }
-+ }
-+
-+ @Override
-+ public String toString() {
-+ final StringBuilder ret = new StringBuilder(128);
-+
-+ ret.append("Region{");
-+ ret.append("dead=").append(this.dead).append(',');
-+ ret.append("markedForRecalc=").append(this.markedForRecalc).append(',');
-+
-+ ret.append("sectionCount=").append(this.sections.size()).append(',');
-+ ret.append("sections=[");
-+ for (final Iterator<RegionSection> iterator = this.sections.unsafeIterator(IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS); iterator.hasNext();) {
-+ final RegionSection section = iterator.next();
-+ ret.append(section);
-+ if (iterator.hasNext()) {
-+ ret.append(',');
-+ }
-+ }
-+ ret.append(']');
-+
-+ ret.append('}');
-+ return ret.toString();
-+ }
-+ }
-+
-+ public static final class RegionSection {
-+ protected final long regionCoordinate;
-+ protected final long[] chunksBitset;
-+ protected int chunkCount;
-+ protected Region region;
-+
-+ public final SingleThreadChunkRegionManager regionManager;
-+ public final RegionSectionData sectionData;
-+
-+ protected RegionSection(final long regionCoordinate, final SingleThreadChunkRegionManager regionManager) {
-+ this.regionCoordinate = regionCoordinate;
-+ this.regionManager = regionManager;
-+ this.chunksBitset = new long[Math.max(1, regionManager.regionSectionChunkSize * regionManager.regionSectionChunkSize / Long.SIZE)];
-+ this.sectionData = regionManager.regionSectionDataSupplier.get();
-+ }
-+
-+ public int getSectionX() {
-+ return MCUtil.getCoordinateX(this.regionCoordinate);
-+ }
-+
-+ public int getSectionZ() {
-+ return MCUtil.getCoordinateZ(this.regionCoordinate);
-+ }
-+
-+ public Region getRegion() {
-+ return this.region;
-+ }
-+
-+ private int getChunkIndex(final int chunkX, final int chunkZ) {
-+ return (chunkX & (this.regionManager.regionSectionChunkSize - 1)) | ((chunkZ & (this.regionManager.regionSectionChunkSize - 1)) << this.regionManager.regionChunkShift);
-+ }
-+
-+ protected boolean hasChunks() {
-+ return this.chunkCount != 0;
-+ }
-+
-+ protected void addChunk(final int chunkX, final int chunkZ) {
-+ final int index = this.getChunkIndex(chunkX, chunkZ);
-+ final long bitset = this.chunksBitset[index >>> 6]; // index / Long.SIZE
-+ final long after = this.chunksBitset[index >>> 6] = bitset | (1L << (index & (Long.SIZE - 1)));
-+ if (after == bitset) {
-+ throw new IllegalStateException("Cannot add a chunk to a section which already has the chunk! RegionSection: " + this + ", global chunk: " + new ChunkPos(chunkX, chunkZ).toString());
-+ }
-+ if (++this.chunkCount != 1) {
-+ return;
-+ }
-+ this.region.markSectionAlive(this);
-+ }
-+
-+ protected void removeChunk(final int chunkX, final int chunkZ) {
-+ final int index = this.getChunkIndex(chunkX, chunkZ);
-+ final long before = this.chunksBitset[index >>> 6]; // index / Long.SIZE
-+ final long bitset = this.chunksBitset[index >>> 6] = before & ~(1L << (index & (Long.SIZE - 1)));
-+ if (before == bitset) {
-+ throw new IllegalStateException("Cannot remove a chunk from a section which does not have that chunk! RegionSection: " + this + ", global chunk: " + new ChunkPos(chunkX, chunkZ).toString());
-+ }
-+ if (--this.chunkCount != 0) {
-+ return;
-+ }
-+ this.region.markSectionDead(this);
-+ }
-+
-+ @Override
-+ public String toString() {
-+ return "RegionSection{" +
-+ "regionCoordinate=" + new ChunkPos(this.regionCoordinate).toString() + "," +
-+ "chunkCount=" + this.chunkCount + "," +
-+ "chunksBitset=" + toString(this.chunksBitset) + "," +
-+ "hash=" + this.hashCode() +
-+ "}";
-+ }
-+
-+ public String toStringWithRegion() {
-+ return "RegionSection{" +
-+ "regionCoordinate=" + new ChunkPos(this.regionCoordinate).toString() + "," +
-+ "chunkCount=" + this.chunkCount + "," +
-+ "chunksBitset=" + toString(this.chunksBitset) + "," +
-+ "hash=" + this.hashCode() + "," +
-+ "region=" + this.region +
-+ "}";
-+ }
-+
-+ private static String toString(final long[] array) {
-+ final StringBuilder ret = new StringBuilder();
-+ for (final long value : array) {
-+ // zero pad the hex string
-+ final char[] zeros = new char[Long.SIZE / 4];
-+ Arrays.fill(zeros, '0');
-+ final String string = Long.toHexString(value);
-+ System.arraycopy(string.toCharArray(), 0, zeros, zeros.length - string.length(), string.length());
-+
-+ ret.append(zeros);
-+ }
-+
-+ return ret.toString();
-+ }
-+ }
-+
-+ public static interface RegionData {
-+
-+ }
-+
-+ public static interface RegionSectionData {
-+
-+ public void removeFromRegion(final RegionSection section, final Region from);
-+
-+ // removal from the old region is handled via removeFromRegion
-+ public void addToRegion(final RegionSection section, final Region oldRegion, final Region newRegion);
-+
-+ }
-+}
diff --git a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
new file mode 100644
-index 0000000000000000000000000000000000000000..0d0cb3e63acd5156b6f9d6d78cc949b0af36a77b
+index 0000000000000000000000000000000000000000..a79abe9b26f68d573812e91554124783075ae17a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
-@@ -0,0 +1,303 @@
+@@ -0,0 +1,297 @@
+package io.papermc.paper.chunk.system;
+
+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
@@ -2720,12 +1787,16 @@ index 0000000000000000000000000000000000000000..0d0cb3e63acd5156b6f9d6d78cc949b0
+ }
+ scheduleChunkLoad(level, chunkX, chunkZ, ChunkStatus.EMPTY, addTicket, priority, (final ChunkAccess chunk) -> {
+ if (chunk == null) {
-+ onComplete.accept(null);
++ if (onComplete != null) {
++ onComplete.accept(null);
++ }
+ } else {
+ if (chunk.getPersistedStatus().isOrAfter(toStatus)) {
+ scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+ } else {
-+ onComplete.accept(null);
++ if (onComplete != null) {
++ onComplete.accept(null);
++ }
+ }
+ }
+ });
@@ -2757,8 +1828,6 @@ index 0000000000000000000000000000000000000000..0d0cb3e63acd5156b6f9d6d78cc949b0
+ 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);
@@ -2825,8 +1894,6 @@ index 0000000000000000000000000000000000000000..0d0cb3e63acd5156b6f9d6d78cc949b0
+ 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);
@@ -2905,21 +1972,15 @@ index 0000000000000000000000000000000000000000..0d0cb3e63acd5156b6f9d6d78cc949b0
+ }
+
+ public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
-+ final ChunkMap chunkMap = level.chunkSource.chunkMap;
-+ for (int index = 0, len = chunkMap.regionManagers.size(); index < len; ++index) {
-+ chunkMap.regionManagers.get(index).addChunk(holder.getPos().x, holder.getPos().z);
-+ }
++
+ }
+
+ public static void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
-+ final ChunkMap chunkMap = level.chunkSource.chunkMap;
-+ for (int index = 0, len = chunkMap.regionManagers.size(); index < len; ++index) {
-+ chunkMap.regionManagers.get(index).removeChunk(holder.getPos().x, holder.getPos().z);
-+ }
++
+ }
+
+ public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
-+ chunk.playerChunk = holder;
++
+ }
+
+ public static void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
@@ -3368,14 +2429,16 @@ index 0000000000000000000000000000000000000000..16785bd5c0524f6bad0691ca7ecd4514
+}
diff --git a/src/main/java/io/papermc/paper/util/IntervalledCounter.java b/src/main/java/io/papermc/paper/util/IntervalledCounter.java
new file mode 100644
-index 0000000000000000000000000000000000000000..cea9c098ade00ee87b8efc8164ab72f5279758f0
+index 0000000000000000000000000000000000000000..c90acc3bde887b9c8f8d49fcc3195657c721bc14
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/IntervalledCounter.java
-@@ -0,0 +1,115 @@
+@@ -0,0 +1,128 @@
+package io.papermc.paper.util;
+
+public final class IntervalledCounter {
+
++ private static final int INITIAL_SIZE = 8;
++
+ protected long[] times;
+ protected long[] counts;
+ protected final long interval;
@@ -3385,8 +2448,8 @@ index 0000000000000000000000000000000000000000..cea9c098ade00ee87b8efc8164ab72f5
+ protected int tail; // exclusive
+
+ public IntervalledCounter(final long interval) {
-+ this.times = new long[8];
-+ this.counts = new long[8];
++ this.times = new long[INITIAL_SIZE];
++ this.counts = new long[INITIAL_SIZE];
+ this.interval = interval;
+ }
+
@@ -3441,13 +2504,13 @@ index 0000000000000000000000000000000000000000..cea9c098ade00ee87b8efc8164ab72f5
+ this.tail = nextTail;
+ }
+
-+ public void updateAndAdd(final int count) {
++ public void updateAndAdd(final long count) {
+ final long currTime = System.nanoTime();
+ this.updateCurrentTime(currTime);
+ this.addTime(currTime, count);
+ }
+
-+ public void updateAndAdd(final int count, final long currTime) {
++ public void updateAndAdd(final long count, final long currTime) {
+ this.updateCurrentTime(currTime);
+ this.addTime(currTime, count);
+ }
@@ -3467,9 +2530,12 @@ index 0000000000000000000000000000000000000000..cea9c098ade00ee87b8efc8164ab72f5
+ this.tail = size;
+
+ if (tail >= head) {
++ // sequentially ordered from [head, tail)
+ System.arraycopy(oldElements, head, newElements, 0, size);
+ System.arraycopy(oldCounts, head, newCounts, 0, size);
+ } else {
++ // ordered from [head, length)
++ // then followed by [0, tail)
+ System.arraycopy(oldElements, head, newElements, 0, oldElements.length - head);
+ System.arraycopy(oldElements, 0, newElements, oldElements.length - head, tail);
+
@@ -3480,19 +2546,27 @@ index 0000000000000000000000000000000000000000..cea9c098ade00ee87b8efc8164ab72f5
+
+ // returns in units per second
+ public double getRate() {
-+ return this.size() / (this.interval * 1.0e-9);
++ return (double)this.sum / ((double)this.interval * 1.0E-9);
+ }
+
-+ public long size() {
++ public long getInterval() {
++ return this.interval;
++ }
++
++ public long getSum() {
+ return this.sum;
+ }
++
++ public int totalDataPoints() {
++ return this.tail >= this.head ? (this.tail - this.head) : (this.tail + (this.counts.length - this.head));
++ }
+}
diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java
new file mode 100644
-index 0000000000000000000000000000000000000000..eb36bef19e6729c1cc44aefa927317963aba929e
+index 0000000000000000000000000000000000000000..c6c723d9378c593c8608d5940f63c98dff097cd0
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/MCUtil.java
-@@ -0,0 +1,554 @@
+@@ -0,0 +1,550 @@
+package io.papermc.paper.util;
+
+import com.google.common.collect.ImmutableList;
@@ -4020,10 +3094,6 @@ index 0000000000000000000000000000000000000000..eb36bef19e6729c1cc44aefa92731796
+ }
+ }
+
-+ public static int getTicketLevelFor(net.minecraft.world.level.chunk.status.ChunkStatus status) {
-+ return net.minecraft.server.level.ChunkMap.MAX_VIEW_DISTANCE + io.papermc.paper.chunk.system.ChunkSystem.getDistance(status);
-+ }
-+
+ @NotNull
+ public static <T> List<T> copyListAndAdd(@NotNull final List<T> original,
+ @NotNull final T newElement) {
@@ -4469,1033 +3539,6 @@ index 0000000000000000000000000000000000000000..0fd814f1d65c111266a2b20f86561839
+ }
+ }
+}
-diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..470402573bc31106d5a63e415b958fb7f9c36aa9
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/util/misc/Delayed26WayDistancePropagator3D.java
-@@ -0,0 +1,297 @@
-+package io.papermc.paper.util.misc;
-+
-+import io.papermc.paper.util.CoordinateUtils;
-+import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
-+import it.unimi.dsi.fastutil.longs.LongIterator;
-+import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
-+
-+public final class Delayed26WayDistancePropagator3D {
-+
-+ // this map is considered "stale" unless updates are propagated.
-+ protected final Delayed8WayDistancePropagator2D.LevelMap levels = new Delayed8WayDistancePropagator2D.LevelMap(8192*2, 0.6f);
-+
-+ // this map is never stale
-+ protected final Long2ByteOpenHashMap sources = new Long2ByteOpenHashMap(4096, 0.6f);
-+
-+ // Generally updates to positions are made close to other updates, so we link to decrease cache misses when
-+ // propagating updates
-+ protected final LongLinkedOpenHashSet updatedSources = new LongLinkedOpenHashSet();
-+
-+ @FunctionalInterface
-+ public static interface LevelChangeCallback {
-+
-+ /**
-+ * This can be called for intermediate updates. So do not rely on newLevel being close to or
-+ * the exact level that is expected after a full propagation has occured.
-+ */
-+ public void onLevelUpdate(final long coordinate, final byte oldLevel, final byte newLevel);
-+
-+ }
-+
-+ protected final LevelChangeCallback changeCallback;
-+
-+ public Delayed26WayDistancePropagator3D() {
-+ this(null);
-+ }
-+
-+ public Delayed26WayDistancePropagator3D(final LevelChangeCallback changeCallback) {
-+ this.changeCallback = changeCallback;
-+ }
-+
-+ public int getLevel(final long pos) {
-+ return this.levels.get(pos);
-+ }
-+
-+ public int getLevel(final int x, final int y, final int z) {
-+ return this.levels.get(CoordinateUtils.getChunkSectionKey(x, y, z));
-+ }
-+
-+ public void setSource(final int x, final int y, final int z, final int level) {
-+ this.setSource(CoordinateUtils.getChunkSectionKey(x, y, z), level);
-+ }
-+
-+ public void setSource(final long coordinate, final int level) {
-+ if ((level & 63) != level || level == 0) {
-+ throw new IllegalArgumentException("Level must be in (0, 63], not " + level);
-+ }
-+
-+ final byte byteLevel = (byte)level;
-+ final byte oldLevel = this.sources.put(coordinate, byteLevel);
-+
-+ if (oldLevel == byteLevel) {
-+ return; // nothing to do
-+ }
-+
-+ // queue to update later
-+ this.updatedSources.add(coordinate);
-+ }
-+
-+ public void removeSource(final int x, final int y, final int z) {
-+ this.removeSource(CoordinateUtils.getChunkSectionKey(x, y, z));
-+ }
-+
-+ public void removeSource(final long coordinate) {
-+ if (this.sources.remove(coordinate) != 0) {
-+ this.updatedSources.add(coordinate);
-+ }
-+ }
-+
-+ // queues used for BFS propagating levels
-+ protected final Delayed8WayDistancePropagator2D.WorkQueue[] levelIncreaseWorkQueues = new Delayed8WayDistancePropagator2D.WorkQueue[64];
-+ {
-+ for (int i = 0; i < this.levelIncreaseWorkQueues.length; ++i) {
-+ this.levelIncreaseWorkQueues[i] = new Delayed8WayDistancePropagator2D.WorkQueue();
-+ }
-+ }
-+ protected final Delayed8WayDistancePropagator2D.WorkQueue[] levelRemoveWorkQueues = new Delayed8WayDistancePropagator2D.WorkQueue[64];
-+ {
-+ for (int i = 0; i < this.levelRemoveWorkQueues.length; ++i) {
-+ this.levelRemoveWorkQueues[i] = new Delayed8WayDistancePropagator2D.WorkQueue();
-+ }
-+ }
-+ protected long levelIncreaseWorkQueueBitset;
-+ protected long levelRemoveWorkQueueBitset;
-+
-+ protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) {
-+ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[level];
-+ queue.queuedCoordinates.enqueue(coordinate);
-+ queue.queuedLevels.enqueue(level);
-+
-+ this.levelIncreaseWorkQueueBitset |= (1L << level);
-+ }
-+
-+ protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) {
-+ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[index];
-+ queue.queuedCoordinates.enqueue(coordinate);
-+ queue.queuedLevels.enqueue(level);
-+
-+ this.levelIncreaseWorkQueueBitset |= (1L << index);
-+ }
-+
-+ protected final void addToRemoveWorkQueue(final long coordinate, final byte level) {
-+ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[level];
-+ queue.queuedCoordinates.enqueue(coordinate);
-+ queue.queuedLevels.enqueue(level);
-+
-+ this.levelRemoveWorkQueueBitset |= (1L << level);
-+ }
-+
-+ public boolean propagateUpdates() {
-+ if (this.updatedSources.isEmpty()) {
-+ return false;
-+ }
-+
-+ boolean ret = false;
-+
-+ for (final LongIterator iterator = this.updatedSources.iterator(); iterator.hasNext();) {
-+ final long coordinate = iterator.nextLong();
-+
-+ final byte currentLevel = this.levels.get(coordinate);
-+ final byte updatedSource = this.sources.get(coordinate);
-+
-+ if (currentLevel == updatedSource) {
-+ continue;
-+ }
-+ ret = true;
-+
-+ if (updatedSource > currentLevel) {
-+ // level increase
-+ this.addToIncreaseWorkQueue(coordinate, updatedSource);
-+ } else {
-+ // level decrease
-+ this.addToRemoveWorkQueue(coordinate, currentLevel);
-+ // if the current coordinate is a source, then the decrease propagation will detect that and queue
-+ // the source propagation
-+ }
-+ }
-+
-+ this.updatedSources.clear();
-+
-+ // propagate source level increases first for performance reasons (in crowded areas hopefully the additions
-+ // make the removes remove less)
-+ this.propagateIncreases();
-+
-+ // now we propagate the decreases (which will then re-propagate clobbered sources)
-+ this.propagateDecreases();
-+
-+ return ret;
-+ }
-+
-+ protected void propagateIncreases() {
-+ for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset);
-+ this.levelIncreaseWorkQueueBitset != 0L;
-+ this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) {
-+
-+ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex];
-+ while (!queue.queuedLevels.isEmpty()) {
-+ final long coordinate = queue.queuedCoordinates.removeFirstLong();
-+ byte level = queue.queuedLevels.removeFirstByte();
-+
-+ final boolean neighbourCheck = level < 0;
-+
-+ final byte currentLevel;
-+ if (neighbourCheck) {
-+ level = (byte)-level;
-+ currentLevel = this.levels.get(coordinate);
-+ } else {
-+ currentLevel = this.levels.putIfGreater(coordinate, level);
-+ }
-+
-+ if (neighbourCheck) {
-+ // used when propagating from decrease to indicate that this level needs to check its neighbours
-+ // this means the level at coordinate could be equal, but would still need neighbours checked
-+
-+ if (currentLevel != level) {
-+ // something caused the level to change, which means something propagated to it (which means
-+ // us propagating here is redundant), or something removed the level (which means we
-+ // cannot propagate further)
-+ continue;
-+ }
-+ } else if (currentLevel >= level) {
-+ // something higher/equal propagated
-+ continue;
-+ }
-+ if (this.changeCallback != null) {
-+ this.changeCallback.onLevelUpdate(coordinate, currentLevel, level);
-+ }
-+
-+ if (level == 1) {
-+ // can't propagate 0 to neighbours
-+ continue;
-+ }
-+
-+ // propagate to neighbours
-+ final byte neighbourLevel = (byte)(level - 1);
-+ final int x = CoordinateUtils.getChunkSectionX(coordinate);
-+ final int y = CoordinateUtils.getChunkSectionY(coordinate);
-+ final int z = CoordinateUtils.getChunkSectionZ(coordinate);
-+
-+ for (int dy = -1; dy <= 1; ++dy) {
-+ for (int dz = -1; dz <= 1; ++dz) {
-+ for (int dx = -1; dx <= 1; ++dx) {
-+ if ((dy | dz | dx) == 0) {
-+ // already propagated to coordinate
-+ continue;
-+ }
-+
-+ // sure we can check the neighbour level in the map right now and avoid a propagation,
-+ // but then we would still have to recheck it when popping the value off of the queue!
-+ // so just avoid the double lookup
-+ final long neighbourCoordinate = CoordinateUtils.getChunkSectionKey(dx + x, dy + y, dz + z);
-+ this.addToIncreaseWorkQueue(neighbourCoordinate, neighbourLevel);
-+ }
-+ }
-+ }
-+ }
-+ }
-+ }
-+
-+ protected void propagateDecreases() {
-+ for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset);
-+ this.levelRemoveWorkQueueBitset != 0L;
-+ this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) {
-+
-+ final Delayed8WayDistancePropagator2D.WorkQueue queue = this.levelRemoveWorkQueues[queueIndex];
-+ while (!queue.queuedLevels.isEmpty()) {
-+ final long coordinate = queue.queuedCoordinates.removeFirstLong();
-+ final byte level = queue.queuedLevels.removeFirstByte();
-+
-+ final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level);
-+ if (currentLevel == 0) {
-+ // something else removed
-+ continue;
-+ }
-+
-+ if (currentLevel > level) {
-+ // something higher propagated here or we hit the propagation of another source
-+ // in the second case we need to re-propagate because we could have just clobbered another source's
-+ // propagation
-+ this.addToIncreaseWorkQueue(coordinate, currentLevel, (byte)-currentLevel); // indicate to the increase code that the level's neighbours need checking
-+ continue;
-+ }
-+
-+ if (this.changeCallback != null) {
-+ this.changeCallback.onLevelUpdate(coordinate, currentLevel, (byte)0);
-+ }
-+
-+ final byte source = this.sources.get(coordinate);
-+ if (source != 0) {
-+ // must re-propagate source later
-+ this.addToIncreaseWorkQueue(coordinate, source);
-+ }
-+
-+ if (level == 0) {
-+ // can't propagate -1 to neighbours
-+ // we have to check neighbours for removing 1 just in case the neighbour is 2
-+ continue;
-+ }
-+
-+ // propagate to neighbours
-+ final byte neighbourLevel = (byte)(level - 1);
-+ final int x = CoordinateUtils.getChunkSectionX(coordinate);
-+ final int y = CoordinateUtils.getChunkSectionY(coordinate);
-+ final int z = CoordinateUtils.getChunkSectionZ(coordinate);
-+
-+ for (int dy = -1; dy <= 1; ++dy) {
-+ for (int dz = -1; dz <= 1; ++dz) {
-+ for (int dx = -1; dx <= 1; ++dx) {
-+ if ((dy | dz | dx) == 0) {
-+ // already propagated to coordinate
-+ continue;
-+ }
-+
-+ // sure we can check the neighbour level in the map right now and avoid a propagation,
-+ // but then we would still have to recheck it when popping the value off of the queue!
-+ // so just avoid the double lookup
-+ final long neighbourCoordinate = CoordinateUtils.getChunkSectionKey(dx + x, dy + y, dz + z);
-+ this.addToRemoveWorkQueue(neighbourCoordinate, neighbourLevel);
-+ }
-+ }
-+ }
-+ }
-+ }
-+
-+ // propagate sources we clobbered in the process
-+ this.propagateIncreases();
-+ }
-+}
-diff --git a/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java
-new file mode 100644
-index 0000000000000000000000000000000000000000..808d1449ac44ae86a650932365081fbaf178d141
---- /dev/null
-+++ b/src/main/java/io/papermc/paper/util/misc/Delayed8WayDistancePropagator2D.java
-@@ -0,0 +1,718 @@
-+package io.papermc.paper.util.misc;
-+
-+import it.unimi.dsi.fastutil.HashCommon;
-+import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue;
-+import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
-+import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
-+import it.unimi.dsi.fastutil.longs.LongIterator;
-+import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet;
-+import io.papermc.paper.util.MCUtil;
-+
-+public final class Delayed8WayDistancePropagator2D {
-+
-+ // Test
-+ /*
-+ protected static void test(int x, int z, com.destroystokyo.paper.util.misc.DistanceTrackingAreaMap<Ticket> reference, Delayed8WayDistancePropagator2D test) {
-+ int got = test.getLevel(x, z);
-+
-+ int expect = 0;
-+ Object[] nearest = reference.getObjectsInRange(x, z) == null ? null : reference.getObjectsInRange(x, z).getBackingSet();
-+ if (nearest != null) {
-+ for (Object _obj : nearest) {
-+ if (_obj instanceof Ticket) {
-+ Ticket ticket = (Ticket)_obj;
-+ long ticketCoord = reference.getLastCoordinate(ticket);
-+ int viewDistance = reference.getLastViewDistance(ticket);
-+ int distance = Math.max(com.destroystokyo.paper.util.math.IntegerUtil.branchlessAbs(MCUtil.getCoordinateX(ticketCoord) - x),
-+ com.destroystokyo.paper.util.math.IntegerUtil.branchlessAbs(MCUtil.getCoordinateZ(ticketCoord) - z));
-+ int level = viewDistance - distance;
-+ if (level > expect) {
-+ expect = level;
-+ }
-+ }
-+ }
-+ }
-+
-+ if (expect != got) {
-+ throw new IllegalStateException("Expected " + expect + " at pos (" + x + "," + z + ") but got " + got);
-+ }
-+ }
-+
-+ static class Ticket {
-+
-+ int x;
-+ int z;
-+
-+ final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<Ticket> empty
-+ = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<>(this);
-+
-+ }
-+
-+ public static void main(final String[] args) {
-+ com.destroystokyo.paper.util.misc.DistanceTrackingAreaMap<Ticket> reference = new com.destroystokyo.paper.util.misc.DistanceTrackingAreaMap<Ticket>() {
-+ @Override
-+ protected com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<Ticket> getEmptySetFor(Ticket object) {
-+ return object.empty;
-+ }
-+ };
-+ Delayed8WayDistancePropagator2D test = new Delayed8WayDistancePropagator2D();
-+
-+ final int maxDistance = 64;
-+ // test origin
-+ {
-+ Ticket originTicket = new Ticket();
-+ int originDistance = 31;
-+ // test single source
-+ reference.add(originTicket, 0, 0, originDistance);
-+ test.setSource(0, 0, originDistance); test.propagateUpdates(); // set and propagate
-+ for (int dx = -originDistance; dx <= originDistance; ++dx) {
-+ for (int dz = -originDistance; dz <= originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+ // test single source decrease
-+ reference.update(originTicket, 0, 0, originDistance/2);
-+ test.setSource(0, 0, originDistance/2); test.propagateUpdates(); // set and propagate
-+ for (int dx = -originDistance; dx <= originDistance; ++dx) {
-+ for (int dz = -originDistance; dz <= originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+ // test source increase
-+ originDistance = 2*originDistance;
-+ reference.update(originTicket, 0, 0, originDistance);
-+ test.setSource(0, 0, originDistance); test.propagateUpdates(); // set and propagate
-+ for (int dx = -4*originDistance; dx <= 4*originDistance; ++dx) {
-+ for (int dz = -4*originDistance; dz <= 4*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+
-+ reference.remove(originTicket);
-+ test.removeSource(0, 0); test.propagateUpdates();
-+ }
-+
-+ // test multiple sources at origin
-+ {
-+ int originDistance = 31;
-+ java.util.List<Ticket> list = new java.util.ArrayList<>();
-+ for (int i = 0; i < 10; ++i) {
-+ Ticket a = new Ticket();
-+ list.add(a);
-+ a.x = (i & 1) == 1 ? -i : i;
-+ a.z = (i & 1) == 1 ? -i : i;
-+ }
-+ for (Ticket ticket : list) {
-+ reference.add(ticket, ticket.x, ticket.z, originDistance);
-+ test.setSource(ticket.x, ticket.z, originDistance);
-+ }
-+ test.propagateUpdates();
-+
-+ for (int dx = -8*originDistance; dx <= 8*originDistance; ++dx) {
-+ for (int dz = -8*originDistance; dz <= 8*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+
-+ // test ticket level decrease
-+
-+ for (Ticket ticket : list) {
-+ reference.update(ticket, ticket.x, ticket.z, originDistance/2);
-+ test.setSource(ticket.x, ticket.z, originDistance/2);
-+ }
-+ test.propagateUpdates();
-+
-+ for (int dx = -8*originDistance; dx <= 8*originDistance; ++dx) {
-+ for (int dz = -8*originDistance; dz <= 8*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+
-+ // test ticket level increase
-+
-+ for (Ticket ticket : list) {
-+ reference.update(ticket, ticket.x, ticket.z, originDistance*2);
-+ test.setSource(ticket.x, ticket.z, originDistance*2);
-+ }
-+ test.propagateUpdates();
-+
-+ for (int dx = -16*originDistance; dx <= 16*originDistance; ++dx) {
-+ for (int dz = -16*originDistance; dz <= 16*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+
-+ // test ticket remove
-+ for (int i = 0, len = list.size(); i < len; ++i) {
-+ if ((i & 3) != 0) {
-+ continue;
-+ }
-+ Ticket ticket = list.get(i);
-+ reference.remove(ticket);
-+ test.removeSource(ticket.x, ticket.z);
-+ }
-+ test.propagateUpdates();
-+
-+ for (int dx = -16*originDistance; dx <= 16*originDistance; ++dx) {
-+ for (int dz = -16*originDistance; dz <= 16*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+ }
-+
-+ // now test at coordinate offsets
-+ // test offset
-+ {
-+ Ticket originTicket = new Ticket();
-+ int originDistance = 31;
-+ int offX = 54432;
-+ int offZ = -134567;
-+ // test single source
-+ reference.add(originTicket, offX, offZ, originDistance);
-+ test.setSource(offX, offZ, originDistance); test.propagateUpdates(); // set and propagate
-+ for (int dx = -originDistance; dx <= originDistance; ++dx) {
-+ for (int dz = -originDistance; dz <= originDistance; ++dz) {
-+ test(dx + offX, dz + offZ, reference, test);
-+ }
-+ }
-+ // test single source decrease
-+ reference.update(originTicket, offX, offZ, originDistance/2);
-+ test.setSource(offX, offZ, originDistance/2); test.propagateUpdates(); // set and propagate
-+ for (int dx = -originDistance; dx <= originDistance; ++dx) {
-+ for (int dz = -originDistance; dz <= originDistance; ++dz) {
-+ test(dx + offX, dz + offZ, reference, test);
-+ }
-+ }
-+ // test source increase
-+ originDistance = 2*originDistance;
-+ reference.update(originTicket, offX, offZ, originDistance);
-+ test.setSource(offX, offZ, originDistance); test.propagateUpdates(); // set and propagate
-+ for (int dx = -4*originDistance; dx <= 4*originDistance; ++dx) {
-+ for (int dz = -4*originDistance; dz <= 4*originDistance; ++dz) {
-+ test(dx + offX, dz + offZ, reference, test);
-+ }
-+ }
-+
-+ reference.remove(originTicket);
-+ test.removeSource(offX, offZ); test.propagateUpdates();
-+ }
-+
-+ // test multiple sources at origin
-+ {
-+ int originDistance = 31;
-+ int offX = 54432;
-+ int offZ = -134567;
-+ java.util.List<Ticket> list = new java.util.ArrayList<>();
-+ for (int i = 0; i < 10; ++i) {
-+ Ticket a = new Ticket();
-+ list.add(a);
-+ a.x = offX + ((i & 1) == 1 ? -i : i);
-+ a.z = offZ + ((i & 1) == 1 ? -i : i);
-+ }
-+ for (Ticket ticket : list) {
-+ reference.add(ticket, ticket.x, ticket.z, originDistance);
-+ test.setSource(ticket.x, ticket.z, originDistance);
-+ }
-+ test.propagateUpdates();
-+
-+ for (int dx = -8*originDistance; dx <= 8*originDistance; ++dx) {
-+ for (int dz = -8*originDistance; dz <= 8*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+
-+ // test ticket level decrease
-+
-+ for (Ticket ticket : list) {
-+ reference.update(ticket, ticket.x, ticket.z, originDistance/2);
-+ test.setSource(ticket.x, ticket.z, originDistance/2);
-+ }
-+ test.propagateUpdates();
-+
-+ for (int dx = -8*originDistance; dx <= 8*originDistance; ++dx) {
-+ for (int dz = -8*originDistance; dz <= 8*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+
-+ // test ticket level increase
-+
-+ for (Ticket ticket : list) {
-+ reference.update(ticket, ticket.x, ticket.z, originDistance*2);
-+ test.setSource(ticket.x, ticket.z, originDistance*2);
-+ }
-+ test.propagateUpdates();
-+
-+ for (int dx = -16*originDistance; dx <= 16*originDistance; ++dx) {
-+ for (int dz = -16*originDistance; dz <= 16*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+
-+ // test ticket remove
-+ for (int i = 0, len = list.size(); i < len; ++i) {
-+ if ((i & 3) != 0) {
-+ continue;
-+ }
-+ Ticket ticket = list.get(i);
-+ reference.remove(ticket);
-+ test.removeSource(ticket.x, ticket.z);
-+ }
-+ test.propagateUpdates();
-+
-+ for (int dx = -16*originDistance; dx <= 16*originDistance; ++dx) {
-+ for (int dz = -16*originDistance; dz <= 16*originDistance; ++dz) {
-+ test(dx, dz, reference, test);
-+ }
-+ }
-+ }
-+ }
-+ */
-+
-+ // this map is considered "stale" unless updates are propagated.
-+ protected final LevelMap levels = new LevelMap(8192*2, 0.6f);
-+
-+ // this map is never stale
-+ protected final Long2ByteOpenHashMap sources = new Long2ByteOpenHashMap(4096, 0.6f);
-+
-+ // Generally updates to positions are made close to other updates, so we link to decrease cache misses when
-+ // propagating updates
-+ protected final LongLinkedOpenHashSet updatedSources = new LongLinkedOpenHashSet();
-+
-+ @FunctionalInterface
-+ public static interface LevelChangeCallback {
-+
-+ /**
-+ * This can be called for intermediate updates. So do not rely on newLevel being close to or
-+ * the exact level that is expected after a full propagation has occured.
-+ */
-+ public void onLevelUpdate(final long coordinate, final byte oldLevel, final byte newLevel);
-+
-+ }
-+
-+ protected final LevelChangeCallback changeCallback;
-+
-+ public Delayed8WayDistancePropagator2D() {
-+ this(null);
-+ }
-+
-+ public Delayed8WayDistancePropagator2D(final LevelChangeCallback changeCallback) {
-+ this.changeCallback = changeCallback;
-+ }
-+
-+ public int getLevel(final long pos) {
-+ return this.levels.get(pos);
-+ }
-+
-+ public int getLevel(final int x, final int z) {
-+ return this.levels.get(MCUtil.getCoordinateKey(x, z));
-+ }
-+
-+ public void setSource(final int x, final int z, final int level) {
-+ this.setSource(MCUtil.getCoordinateKey(x, z), level);
-+ }
-+
-+ public void setSource(final long coordinate, final int level) {
-+ if ((level & 63) != level || level == 0) {
-+ throw new IllegalArgumentException("Level must be in (0, 63], not " + level);
-+ }
-+
-+ final byte byteLevel = (byte)level;
-+ final byte oldLevel = this.sources.put(coordinate, byteLevel);
-+
-+ if (oldLevel == byteLevel) {
-+ return; // nothing to do
-+ }
-+
-+ // queue to update later
-+ this.updatedSources.add(coordinate);
-+ }
-+
-+ public void removeSource(final int x, final int z) {
-+ this.removeSource(MCUtil.getCoordinateKey(x, z));
-+ }
-+
-+ public void removeSource(final long coordinate) {
-+ if (this.sources.remove(coordinate) != 0) {
-+ this.updatedSources.add(coordinate);
-+ }
-+ }
-+
-+ // queues used for BFS propagating levels
-+ protected final WorkQueue[] levelIncreaseWorkQueues = new WorkQueue[64];
-+ {
-+ for (int i = 0; i < this.levelIncreaseWorkQueues.length; ++i) {
-+ this.levelIncreaseWorkQueues[i] = new WorkQueue();
-+ }
-+ }
-+ protected final WorkQueue[] levelRemoveWorkQueues = new WorkQueue[64];
-+ {
-+ for (int i = 0; i < this.levelRemoveWorkQueues.length; ++i) {
-+ this.levelRemoveWorkQueues[i] = new WorkQueue();
-+ }
-+ }
-+ protected long levelIncreaseWorkQueueBitset;
-+ protected long levelRemoveWorkQueueBitset;
-+
-+ protected final void addToIncreaseWorkQueue(final long coordinate, final byte level) {
-+ final WorkQueue queue = this.levelIncreaseWorkQueues[level];
-+ queue.queuedCoordinates.enqueue(coordinate);
-+ queue.queuedLevels.enqueue(level);
-+
-+ this.levelIncreaseWorkQueueBitset |= (1L << level);
-+ }
-+
-+ protected final void addToIncreaseWorkQueue(final long coordinate, final byte index, final byte level) {
-+ final WorkQueue queue = this.levelIncreaseWorkQueues[index];
-+ queue.queuedCoordinates.enqueue(coordinate);
-+ queue.queuedLevels.enqueue(level);
-+
-+ this.levelIncreaseWorkQueueBitset |= (1L << index);
-+ }
-+
-+ protected final void addToRemoveWorkQueue(final long coordinate, final byte level) {
-+ final WorkQueue queue = this.levelRemoveWorkQueues[level];
-+ queue.queuedCoordinates.enqueue(coordinate);
-+ queue.queuedLevels.enqueue(level);
-+
-+ this.levelRemoveWorkQueueBitset |= (1L << level);
-+ }
-+
-+ public boolean propagateUpdates() {
-+ if (this.updatedSources.isEmpty()) {
-+ return false;
-+ }
-+
-+ boolean ret = false;
-+
-+ for (final LongIterator iterator = this.updatedSources.iterator(); iterator.hasNext();) {
-+ final long coordinate = iterator.nextLong();
-+
-+ final byte currentLevel = this.levels.get(coordinate);
-+ final byte updatedSource = this.sources.get(coordinate);
-+
-+ if (currentLevel == updatedSource) {
-+ continue;
-+ }
-+ ret = true;
-+
-+ if (updatedSource > currentLevel) {
-+ // level increase
-+ this.addToIncreaseWorkQueue(coordinate, updatedSource);
-+ } else {
-+ // level decrease
-+ this.addToRemoveWorkQueue(coordinate, currentLevel);
-+ // if the current coordinate is a source, then the decrease propagation will detect that and queue
-+ // the source propagation
-+ }
-+ }
-+
-+ this.updatedSources.clear();
-+
-+ // propagate source level increases first for performance reasons (in crowded areas hopefully the additions
-+ // make the removes remove less)
-+ this.propagateIncreases();
-+
-+ // now we propagate the decreases (which will then re-propagate clobbered sources)
-+ this.propagateDecreases();
-+
-+ return ret;
-+ }
-+
-+ protected void propagateIncreases() {
-+ for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset);
-+ this.levelIncreaseWorkQueueBitset != 0L;
-+ this.levelIncreaseWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelIncreaseWorkQueueBitset)) {
-+
-+ final WorkQueue queue = this.levelIncreaseWorkQueues[queueIndex];
-+ while (!queue.queuedLevels.isEmpty()) {
-+ final long coordinate = queue.queuedCoordinates.removeFirstLong();
-+ byte level = queue.queuedLevels.removeFirstByte();
-+
-+ final boolean neighbourCheck = level < 0;
-+
-+ final byte currentLevel;
-+ if (neighbourCheck) {
-+ level = (byte)-level;
-+ currentLevel = this.levels.get(coordinate);
-+ } else {
-+ currentLevel = this.levels.putIfGreater(coordinate, level);
-+ }
-+
-+ if (neighbourCheck) {
-+ // used when propagating from decrease to indicate that this level needs to check its neighbours
-+ // this means the level at coordinate could be equal, but would still need neighbours checked
-+
-+ if (currentLevel != level) {
-+ // something caused the level to change, which means something propagated to it (which means
-+ // us propagating here is redundant), or something removed the level (which means we
-+ // cannot propagate further)
-+ continue;
-+ }
-+ } else if (currentLevel >= level) {
-+ // something higher/equal propagated
-+ continue;
-+ }
-+ if (this.changeCallback != null) {
-+ this.changeCallback.onLevelUpdate(coordinate, currentLevel, level);
-+ }
-+
-+ if (level == 1) {
-+ // can't propagate 0 to neighbours
-+ continue;
-+ }
-+
-+ // propagate to neighbours
-+ final byte neighbourLevel = (byte)(level - 1);
-+ final int x = (int)coordinate;
-+ final int z = (int)(coordinate >>> 32);
-+
-+ for (int dx = -1; dx <= 1; ++dx) {
-+ for (int dz = -1; dz <= 1; ++dz) {
-+ if ((dx | dz) == 0) {
-+ // already propagated to coordinate
-+ continue;
-+ }
-+
-+ // sure we can check the neighbour level in the map right now and avoid a propagation,
-+ // but then we would still have to recheck it when popping the value off of the queue!
-+ // so just avoid the double lookup
-+ final long neighbourCoordinate = MCUtil.getCoordinateKey(x + dx, z + dz);
-+ this.addToIncreaseWorkQueue(neighbourCoordinate, neighbourLevel);
-+ }
-+ }
-+ }
-+ }
-+ }
-+
-+ protected void propagateDecreases() {
-+ for (int queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset);
-+ this.levelRemoveWorkQueueBitset != 0L;
-+ this.levelRemoveWorkQueueBitset ^= (1L << queueIndex), queueIndex = 63 ^ Long.numberOfLeadingZeros(this.levelRemoveWorkQueueBitset)) {
-+
-+ final WorkQueue queue = this.levelRemoveWorkQueues[queueIndex];
-+ while (!queue.queuedLevels.isEmpty()) {
-+ final long coordinate = queue.queuedCoordinates.removeFirstLong();
-+ final byte level = queue.queuedLevels.removeFirstByte();
-+
-+ final byte currentLevel = this.levels.removeIfGreaterOrEqual(coordinate, level);
-+ if (currentLevel == 0) {
-+ // something else removed
-+ continue;
-+ }
-+
-+ if (currentLevel > level) {
-+ // something higher propagated here or we hit the propagation of another source
-+ // in the second case we need to re-propagate because we could have just clobbered another source's
-+ // propagation
-+ this.addToIncreaseWorkQueue(coordinate, currentLevel, (byte)-currentLevel); // indicate to the increase code that the level's neighbours need checking
-+ continue;
-+ }
-+
-+ if (this.changeCallback != null) {
-+ this.changeCallback.onLevelUpdate(coordinate, currentLevel, (byte)0);
-+ }
-+
-+ final byte source = this.sources.get(coordinate);
-+ if (source != 0) {
-+ // must re-propagate source later
-+ this.addToIncreaseWorkQueue(coordinate, source);
-+ }
-+
-+ if (level == 0) {
-+ // can't propagate -1 to neighbours
-+ // we have to check neighbours for removing 1 just in case the neighbour is 2
-+ continue;
-+ }
-+
-+ // propagate to neighbours
-+ final byte neighbourLevel = (byte)(level - 1);
-+ final int x = (int)coordinate;
-+ final int z = (int)(coordinate >>> 32);
-+
-+ for (int dx = -1; dx <= 1; ++dx) {
-+ for (int dz = -1; dz <= 1; ++dz) {
-+ if ((dx | dz) == 0) {
-+ // already propagated to coordinate
-+ continue;
-+ }
-+
-+ // sure we can check the neighbour level in the map right now and avoid a propagation,
-+ // but then we would still have to recheck it when popping the value off of the queue!
-+ // so just avoid the double lookup
-+ final long neighbourCoordinate = MCUtil.getCoordinateKey(x + dx, z + dz);
-+ this.addToRemoveWorkQueue(neighbourCoordinate, neighbourLevel);
-+ }
-+ }
-+ }
-+ }
-+
-+ // propagate sources we clobbered in the process
-+ this.propagateIncreases();
-+ }
-+
-+ protected static final class LevelMap extends Long2ByteOpenHashMap {
-+ public LevelMap() {
-+ super();
-+ }
-+
-+ public LevelMap(final int expected, final float loadFactor) {
-+ super(expected, loadFactor);
-+ }
-+
-+ // copied from superclass
-+ private int find(final long k) {
-+ if (k == 0L) {
-+ return this.containsNullKey ? this.n : -(this.n + 1);
-+ } else {
-+ final long[] key = this.key;
-+ long curr;
-+ int pos;
-+ if ((curr = key[pos = (int)HashCommon.mix(k) & this.mask]) == 0L) {
-+ return -(pos + 1);
-+ } else if (k == curr) {
-+ return pos;
-+ } else {
-+ while((curr = key[pos = pos + 1 & this.mask]) != 0L) {
-+ if (k == curr) {
-+ return pos;
-+ }
-+ }
-+
-+ return -(pos + 1);
-+ }
-+ }
-+ }
-+
-+ // copied from superclass
-+ private void insert(final int pos, final long k, final byte v) {
-+ if (pos == this.n) {
-+ this.containsNullKey = true;
-+ }
-+
-+ this.key[pos] = k;
-+ this.value[pos] = v;
-+ if (this.size++ >= this.maxFill) {
-+ this.rehash(HashCommon.arraySize(this.size + 1, this.f));
-+ }
-+ }
-+
-+ // copied from superclass
-+ public byte putIfGreater(final long key, final byte value) {
-+ final int pos = this.find(key);
-+ if (pos < 0) {
-+ if (this.defRetValue < value) {
-+ this.insert(-pos - 1, key, value);
-+ }
-+ return this.defRetValue;
-+ } else {
-+ final byte curr = this.value[pos];
-+ if (value > curr) {
-+ this.value[pos] = value;
-+ return curr;
-+ }
-+ return curr;
-+ }
-+ }
-+
-+ // copied from superclass
-+ private void removeEntry(final int pos) {
-+ --this.size;
-+ this.shiftKeys(pos);
-+ if (this.n > this.minN && this.size < this.maxFill / 4 && this.n > 16) {
-+ this.rehash(this.n / 2);
-+ }
-+ }
-+
-+ // copied from superclass
-+ private void removeNullEntry() {
-+ this.containsNullKey = false;
-+ --this.size;
-+ if (this.n > this.minN && this.size < this.maxFill / 4 && this.n > 16) {
-+ this.rehash(this.n / 2);
-+ }
-+ }
-+
-+ // copied from superclass
-+ public byte removeIfGreaterOrEqual(final long key, final byte value) {
-+ if (key == 0L) {
-+ if (!this.containsNullKey) {
-+ return this.defRetValue;
-+ }
-+ final byte current = this.value[this.n];
-+ if (value >= current) {
-+ this.removeNullEntry();
-+ return current;
-+ }
-+ return current;
-+ } else {
-+ long[] keys = this.key;
-+ byte[] values = this.value;
-+ long curr;
-+ int pos;
-+ if ((curr = keys[pos = (int)HashCommon.mix(key) & this.mask]) == 0L) {
-+ return this.defRetValue;
-+ } else if (key == curr) {
-+ final byte current = values[pos];
-+ if (value >= current) {
-+ this.removeEntry(pos);
-+ return current;
-+ }
-+ return current;
-+ } else {
-+ while((curr = keys[pos = pos + 1 & this.mask]) != 0L) {
-+ if (key == curr) {
-+ final byte current = values[pos];
-+ if (value >= current) {
-+ this.removeEntry(pos);
-+ return current;
-+ }
-+ return current;
-+ }
-+ }
-+
-+ return this.defRetValue;
-+ }
-+ }
-+ }
-+ }
-+
-+ protected static final class WorkQueue {
-+
-+ public final NoResizeLongArrayFIFODeque queuedCoordinates = new NoResizeLongArrayFIFODeque();
-+ public final NoResizeByteArrayFIFODeque queuedLevels = new NoResizeByteArrayFIFODeque();
-+
-+ }
-+
-+ protected static final class NoResizeLongArrayFIFODeque extends LongArrayFIFOQueue {
-+
-+ /**
-+ * Assumes non-empty. If empty, undefined behaviour.
-+ */
-+ public long removeFirstLong() {
-+ // copied from superclass
-+ long t = this.array[this.start];
-+ if (++this.start == this.length) {
-+ this.start = 0;
-+ }
-+
-+ return t;
-+ }
-+ }
-+
-+ protected static final class NoResizeByteArrayFIFODeque extends ByteArrayFIFOQueue {
-+
-+ /**
-+ * Assumes non-empty. If empty, undefined behaviour.
-+ */
-+ public byte removeFirstByte() {
-+ // copied from superclass
-+ byte t = this.array[this.start];
-+ if (++this.start == this.length) {
-+ this.start = 0;
-+ }
-+
-+ return t;
-+ }
-+ }
-+}
diff --git a/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java b/src/main/java/io/papermc/paper/util/player/NearbyPlayers.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3ce8a42dddd76b7189ad5685b23f9d9f8ccadb3
@@ -6081,7 +4124,7 @@ index 3e5a85a7ad6149b04622c254fbc2e174896a4128..3f662692ed4846e026a9d48595e7b3b2
+
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 40adb6117b9e0d5f70103113202a07715e403e2a..b1325e090f2c7aff31d27fc38ca7173efe31ed7c 100644
+index 40adb6117b9e0d5f70103113202a07715e403e2a..9eb987f9d86396d6b7e9d4f3834bea3326640ac7 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -310,6 +310,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -6119,18 +4162,6 @@ index 40adb6117b9e0d5f70103113202a07715e403e2a..b1325e090f2c7aff31d27fc38ca7173e
this.profiler.push("tallying");
long j = Util.getNanos() - i;
int k = this.tickCount % 100;
-@@ -1470,6 +1475,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
- try {
- worldserver.timings.doTick.startTiming(); // Spigot
- worldserver.tick(shouldKeepTicking);
-+ // Paper start
-+ for (final io.papermc.paper.chunk.SingleThreadChunkRegionManager regionManager : worldserver.getChunkSource().chunkMap.regionManagers) {
-+ regionManager.recalculateRegions();
-+ }
-+ // Paper end
- worldserver.timings.doTick.stopTiming(); // Spigot
- } catch (Throwable throwable) {
- CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
index f40a2f348c45a29168ca3d4eef07b5b628060bee..c643bb0daa5cd264fd6ebab7acf0a2bdd7fe7029 100644
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
@@ -6347,10 +4378,10 @@ index f40a2f348c45a29168ca3d4eef07b5b628060bee..c643bb0daa5cd264fd6ebab7acf0a2bd
+ // 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 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b8ef8fe7f 100644
+index 5b920beb39dad8d392b4e5e12a89880720e41942..d3caadb3e884d7d0468daf5eff9abd6629ac4b49 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -170,6 +170,62 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -170,6 +170,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
};
// CraftBukkit end
@@ -6379,31 +4410,6 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
+ }
+ // Paper end
+ // Paper start
-+ public final List<io.papermc.paper.chunk.SingleThreadChunkRegionManager> regionManagers = new java.util.ArrayList<>();
-+ public final io.papermc.paper.chunk.SingleThreadChunkRegionManager dataRegionManager;
-+
-+ public static final class DataRegionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionData {
-+ }
-+
-+ public static final class DataRegionSectionData implements io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSectionData {
-+
-+ @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;
-+ }
-+
-+ @Override
-+ public void addToRegion(final io.papermc.paper.chunk.SingleThreadChunkRegionManager.RegionSection section,
-+ final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region oldRegion,
-+ final io.papermc.paper.chunk.SingleThreadChunkRegionManager.Region newRegion) {
-+ final DataRegionSectionData sectionData = (DataRegionSectionData)section.sectionData;
-+ final DataRegionData oldRegionData = oldRegion == null ? null : (DataRegionData)oldRegion.regionData;
-+ final DataRegionData newRegionData = (DataRegionData)newRegion.regionData;
-+ }
-+ }
-+
+ public final ChunkHolder getUnloadingChunkHolder(int chunkX, int chunkZ) {
+ return this.pendingUnloads.get(io.papermc.paper.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
+ }
@@ -6413,13 +4419,11 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
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(new RegionStorageInfo(session.getLevelId(), world.dimension(), "chunk"), session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync);
this.visibleChunkMap = this.updatingChunkMap.clone();
-@@ -221,8 +277,24 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -221,8 +252,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.poiManager = new PoiManager(new RegionStorageInfo(session.getLevelId(), world.dimension(), "poi"), path.resolve("poi"), dataFixer, dsync, iregistrycustom, world.getServer(), world);
this.setServerViewDistance(viewDistance);
this.worldGenContext = new WorldGenContext(world, chunkGenerator, structureTemplateManager, this.lightEngine, this.mainThreadMailbox);
+ // Paper start
-+ 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);
+ this.nearbyPlayers = new io.papermc.paper.util.player.NearbyPlayers(this.level);
+ // Paper end
+ }
@@ -6438,7 +4442,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
protected ChunkGenerator generator() {
return this.worldGenContext.generator();
}
-@@ -378,9 +450,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -378,9 +423,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
};
stringbuilder.append("Updating:").append(System.lineSeparator());
@@ -6450,7 +4454,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
CrashReport crashreport = CrashReport.forThrowable(exception, "Chunk loading");
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Chunk loading");
-@@ -422,8 +494,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -422,8 +467,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
holder.setTicketLevel(level);
} else {
holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
@@ -6465,7 +4469,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
this.updatingChunkMap.put(pos, holder);
this.modified = true;
}
-@@ -445,7 +523,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -445,7 +496,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
protected void saveAllChunks(boolean flush) {
if (flush) {
@@ -6474,7 +4478,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
MutableBoolean mutableboolean = new MutableBoolean();
do {
-@@ -468,7 +546,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -468,7 +519,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
});
this.flushWorker();
} else {
@@ -6483,7 +4487,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
}
}
-@@ -487,7 +565,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -487,7 +538,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public boolean hasWork() {
@@ -6492,7 +4496,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
}
private void processUnloads(BooleanSupplier shouldKeepTicking) {
-@@ -504,6 +582,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -504,6 +555,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
this.updatingChunkMap.remove(j);
@@ -6500,7 +4504,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
this.pendingUnloads.put(j, playerchunk);
this.modified = true;
++i;
-@@ -523,7 +602,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -523,7 +575,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
int l = 0;
@@ -6509,7 +4513,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
while (l < 20 && shouldKeepTicking.getAsBoolean() && objectiterator.hasNext()) {
if (this.saveChunkIfNeeded((ChunkHolder) objectiterator.next())) {
-@@ -541,7 +620,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -541,7 +593,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} else {
ChunkAccess ichunkaccess = holder.getLatestChunk();
@@ -6522,7 +4526,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
LevelChunk chunk;
if (ichunkaccess instanceof LevelChunk) {
-@@ -559,7 +642,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -559,7 +615,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lightEngine.tryScheduleUpdate();
this.progressListener.onStatusChange(ichunkaccess.getPos(), (ChunkStatus) null);
this.chunkSaveCooldowns.remove(ichunkaccess.getPos().toLong());
@@ -6533,7 +4537,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
}
};
-@@ -896,7 +981,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -896,7 +954,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
}
@@ -6542,7 +4546,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
int j = Mth.clamp(watchDistance, 2, 32);
if (j != this.serverViewDistance) {
-@@ -913,7 +998,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -913,7 +971,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
@@ -6551,7 +4555,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
return Mth.clamp(player.requestedViewDistance(), 2, this.serverViewDistance);
}
-@@ -942,7 +1027,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -942,7 +1000,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public int size() {
@@ -6560,7 +4564,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
}
public DistanceManager getDistanceManager() {
-@@ -950,19 +1035,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -950,19 +1008,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
protected Iterable<ChunkHolder> getChunks() {
@@ -6585,7 +4589,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
Optional<ChunkAccess> optional = Optional.ofNullable(playerchunk.getLatestChunk());
Optional<LevelChunk> optional1 = optional.flatMap((ichunkaccess) -> {
return ichunkaccess instanceof LevelChunk ? Optional.of((LevelChunk) ichunkaccess) : Optional.empty();
-@@ -1083,6 +1168,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1083,6 +1141,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
player.setChunkTrackingView(ChunkTrackingView.EMPTY);
this.updateChunkTracking(player);
@@ -6593,7 +4597,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
} else {
SectionPos sectionposition = player.getLastSectionPos();
-@@ -1091,6 +1177,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1091,6 +1150,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.distanceManager.removePlayer(sectionposition, player);
}
@@ -6601,7 +4605,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
this.applyChunkTrackingView(player, ChunkTrackingView.EMPTY);
}
-@@ -1142,6 +1229,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1142,6 +1202,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.updateChunkTracking(player);
}
@@ -6609,7 +4613,7 @@ index 5b920beb39dad8d392b4e5e12a89880720e41942..319f51eb8adde7584c74780ac0539f4b
}
private void updateChunkTracking(ServerPlayer player) {
-@@ -1385,10 +1473,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1385,10 +1446,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
});
}
@@ -6655,7 +4659,7 @@ index b6cc33943fe7e4667944f3e6f868b3033ea9ca18..27065ffc5473c518acee3a3096b83fac
while (objectiterator.hasNext()) {
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index d39268911ed7c4d60ee6a82178be23245aae58c4..ab57071cc6ce8b79d883f2426855c1abf577e90d 100644
+index d39268911ed7c4d60ee6a82178be23245aae58c4..e9f53f57c363a32106880ea9aad0ccf5a7342509 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -48,6 +48,7 @@ import net.minecraft.world.level.storage.LevelStorageSource;
@@ -6666,81 +4670,30 @@ index d39268911ed7c4d60ee6a82178be23245aae58c4..ab57071cc6ce8b79d883f2426855c1ab
private static final List<ChunkStatus> CHUNK_STATUSES = ChunkStatus.getStatusList();
private final DistanceManager distanceManager;
final ServerLevel level;
-@@ -66,6 +67,14 @@ public class ServerChunkCache extends ChunkSource {
+@@ -66,6 +67,12 @@ public class ServerChunkCache extends ChunkSource {
@Nullable
@VisibleForDebug
private NaturalSpawner.SpawnState lastSpawnState;
+ // Paper start
+ public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<LevelChunk> tickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
+ public final io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<LevelChunk> entityTickingChunks = new io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
-+ final com.destroystokyo.paper.util.concurrent.WeakSeqLock loadedChunkMapSeqLock = new com.destroystokyo.paper.util.concurrent.WeakSeqLock();
-+ final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<LevelChunk> loadedChunkMap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(8192, 0.5f);
++ private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
+ long chunkFutureAwaitCounter;
-+ private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4];
+ // Paper end
public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory) {
this.level = world;
-@@ -91,6 +100,124 @@ public class ServerChunkCache extends ChunkSource {
+@@ -91,6 +98,54 @@ public class ServerChunkCache extends ChunkSource {
return chunk.getFullChunkNow() != null;
}
// CraftBukkit end
+ // Paper start
-+ private static int getChunkCacheKey(int x, int z) {
-+ return x & 3 | ((z & 3) << 2);
-+ }
-+
+ public void addLoadedChunk(LevelChunk chunk) {
-+ this.loadedChunkMapSeqLock.acquireWrite();
-+ try {
-+ this.loadedChunkMap.put(chunk.coordinateKey, chunk);
-+ } finally {
-+ this.loadedChunkMapSeqLock.releaseWrite();
-+ }
-+
-+ // rewrite cache if we have to
-+ // we do this since we also cache null chunks
-+ int cacheKey = getChunkCacheKey(chunk.locX, chunk.locZ);
-+
-+ this.lastLoadedChunks[cacheKey] = chunk;
++ this.fullChunks.put(chunk.coordinateKey, chunk);
+ }
+
+ public void removeLoadedChunk(LevelChunk chunk) {
-+ this.loadedChunkMapSeqLock.acquireWrite();
-+ try {
-+ this.loadedChunkMap.remove(chunk.coordinateKey);
-+ } finally {
-+ this.loadedChunkMapSeqLock.releaseWrite();
-+ }
-+
-+ // rewrite cache if we have to
-+ // we do this since we also cache null chunks
-+ int cacheKey = getChunkCacheKey(chunk.locX, chunk.locZ);
-+
-+ LevelChunk cachedChunk = this.lastLoadedChunks[cacheKey];
-+ if (cachedChunk != null && cachedChunk.coordinateKey == chunk.coordinateKey) {
-+ this.lastLoadedChunks[cacheKey] = null;
-+ }
-+ }
-+
-+ public final LevelChunk getChunkAtIfLoadedMainThread(int x, int z) {
-+ int cacheKey = getChunkCacheKey(x, z);
-+
-+ LevelChunk cachedChunk = this.lastLoadedChunks[cacheKey];
-+ if (cachedChunk != null && cachedChunk.locX == x & cachedChunk.locZ == z) {
-+ return cachedChunk;
-+ }
-+
-+ long chunkKey = ChunkPos.asLong(x, z);
-+
-+ cachedChunk = this.loadedChunkMap.get(chunkKey);
-+ // Skipping a null check to avoid extra instructions to improve inline capability
-+ this.lastLoadedChunks[cacheKey] = cachedChunk;
-+ return cachedChunk;
-+ }
-+
-+ public final LevelChunk getChunkAtIfLoadedMainThreadNoCache(int x, int z) {
-+ return this.loadedChunkMap.get(ChunkPos.asLong(x, z));
++ this.fullChunks.remove(chunk.coordinateKey);
+ }
+
+ @Nullable
@@ -6779,34 +4732,13 @@ index d39268911ed7c4d60ee6a82178be23245aae58c4..ab57071cc6ce8b79d883f2426855c1ab
+
+ @Nullable
+ public LevelChunk getChunkAtIfLoadedImmediately(int x, int z) {
-+ long k = ChunkPos.asLong(x, z);
-+
-+ if (Thread.currentThread() == this.mainThread) {
-+ return this.getChunkAtIfLoadedMainThread(x, z);
-+ }
-+
-+ LevelChunk ret = null;
-+ long readlock;
-+ do {
-+ readlock = this.loadedChunkMapSeqLock.acquireRead();
-+ try {
-+ ret = this.loadedChunkMap.get(k);
-+ } catch (Throwable thr) {
-+ if (thr instanceof ThreadDeath) {
-+ throw (ThreadDeath)thr;
-+ }
-+ // re-try, this means a CME occurred...
-+ continue;
-+ }
-+ } while (!this.loadedChunkMapSeqLock.tryReleaseRead(readlock));
-+
-+ return ret;
++ return this.fullChunks.get(ChunkPos.asLong(x, z));
+ }
+ // Paper end
@Override
public ThreadedLevelLightEngine getLightEngine() {
-@@ -286,7 +413,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -286,7 +341,7 @@ public class ServerChunkCache extends ChunkSource {
return this.mainThreadProcessor.pollTask();
}
@@ -6815,7 +4747,7 @@ index d39268911ed7c4d60ee6a82178be23245aae58c4..ab57071cc6ce8b79d883f2426855c1ab
boolean flag = this.distanceManager.runAllUpdates(this.chunkMap);
boolean flag1 = this.chunkMap.promoteChunkMap();
-@@ -299,6 +426,12 @@ public class ServerChunkCache extends ChunkSource {
+@@ -299,6 +354,12 @@ public class ServerChunkCache extends ChunkSource {
}
}
@@ -7466,120 +5398,21 @@ index a52077f0d93c94b0ea644bc14b9b28e84fd1b154..dcc0acd259920463a4464213b9a5e793
@Nullable
@Override
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 b537e7a079497db428db405edfccde74f32f4208..7898e1aaf82f096fa74bd3f5859f0f4303ea677f 100644
+index b537e7a079497db428db405edfccde74f32f4208..c664021dbfffcf0db3247041270ce9a1ee6940de 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -116,6 +116,109 @@ public class LevelChunk extends ChunkAccess {
+@@ -116,6 +116,10 @@ public class LevelChunk extends ChunkAccess {
public boolean needsDecoration;
// CraftBukkit end
+ // Paper start
-+ public @Nullable net.minecraft.server.level.ChunkHolder playerChunk;
-+
-+ static final int NEIGHBOUR_CACHE_RADIUS = 3;
-+ public static int getNeighbourCacheRadius() {
-+ return NEIGHBOUR_CACHE_RADIUS;
-+ }
-+
+ boolean loadedTicketLevel;
-+ private long neighbourChunksLoadedBitset;
-+ private final LevelChunk[] loadedNeighbourChunks = new LevelChunk[(NEIGHBOUR_CACHE_RADIUS * 2 + 1) * (NEIGHBOUR_CACHE_RADIUS * 2 + 1)];
-+
-+ private static int getNeighbourIndex(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_CACHE_RADIUS * 2 + 1)) + (NEIGHBOUR_CACHE_RADIUS + NEIGHBOUR_CACHE_RADIUS * ((NEIGHBOUR_CACHE_RADIUS * 2 + 1)));
-+ }
-+
-+ public final LevelChunk getRelativeNeighbourIfLoaded(final int relativeX, final int relativeZ) {
-+ return this.loadedNeighbourChunks[getNeighbourIndex(relativeX, relativeZ)];
-+ }
-+
-+ public final boolean isNeighbourLoaded(final int relativeX, final int relativeZ) {
-+ return (this.neighbourChunksLoadedBitset & (1L << getNeighbourIndex(relativeX, relativeZ))) != 0;
-+ }
-+
-+ public final void setNeighbourLoaded(final int relativeX, final int relativeZ, final LevelChunk chunk) {
-+ if (chunk == null) {
-+ throw new IllegalArgumentException("Chunk must be non-null, neighbour: (" + relativeX + "," + relativeZ + "), chunk: " + this.chunkPos);
-+ }
-+ final long before = this.neighbourChunksLoadedBitset;
-+ final int index = getNeighbourIndex(relativeX, relativeZ);
-+ this.loadedNeighbourChunks[index] = chunk;
-+ this.neighbourChunksLoadedBitset |= (1L << index);
-+ this.onNeighbourChange(before, this.neighbourChunksLoadedBitset);
-+ }
-+
-+ public final void setNeighbourUnloaded(final int relativeX, final int relativeZ) {
-+ final long before = this.neighbourChunksLoadedBitset;
-+ final int index = getNeighbourIndex(relativeX, relativeZ);
-+ this.loadedNeighbourChunks[index] = null;
-+ this.neighbourChunksLoadedBitset &= ~(1L << index);
-+ this.onNeighbourChange(before, this.neighbourChunksLoadedBitset);
-+ }
-+
-+ public final void resetNeighbours() {
-+ final long before = this.neighbourChunksLoadedBitset;
-+ this.neighbourChunksLoadedBitset = 0L;
-+ java.util.Arrays.fill(this.loadedNeighbourChunks, null);
-+ this.onNeighbourChange(before, 0L);
-+ }
-+
-+ protected void onNeighbourChange(final long bitsetBefore, final long bitsetAfter) {
-+
-+ }
-+
-+ public final boolean isAnyNeighborsLoaded() {
-+ return neighbourChunksLoadedBitset != 0;
-+ }
-+ public final boolean areNeighboursLoaded(final int radius) {
-+ return LevelChunk.areNeighboursLoaded(this.neighbourChunksLoadedBitset, radius);
-+ }
-+
-+ public static boolean areNeighboursLoaded(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 << getNeighbourIndex(0, 0))) != 0;
-+ }
-+ case 1: {
-+ long mask = 0L;
-+ for (int dx = -1; dx <= 1; ++dx) {
-+ for (int dz = -1; dz <= 1; ++dz) {
-+ mask |= (1L << getNeighbourIndex(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 << getNeighbourIndex(dx, dz));
-+ }
-+ }
-+ return (bitset & mask) == mask;
-+ }
-+ case 3: {
-+ long mask = 0L;
-+ for (int dx = -3; dx <= 3; ++dx) {
-+ for (int dz = -3; dz <= 3; ++dz) {
-+ mask |= (1L << getNeighbourIndex(dx, dz));
-+ }
-+ }
-+ return (bitset & mask) == mask;
-+ }
-+
-+ default:
-+ throw new IllegalArgumentException("Radius not recognized: " + radius);
-+ }
-+ }
+ // Paper end
+
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();
-@@ -181,8 +284,25 @@ public class LevelChunk extends ChunkAccess {
+@@ -181,8 +185,25 @@ public class LevelChunk extends ChunkAccess {
}
}
@@ -7605,7 +5438,7 @@ index b537e7a079497db428db405edfccde74f32f4208..7898e1aaf82f096fa74bd3f5859f0f43
int i = pos.getX();
int j = pos.getY();
int k = pos.getZ();
-@@ -224,6 +344,18 @@ public class LevelChunk extends ChunkAccess {
+@@ -224,6 +245,18 @@ public class LevelChunk extends ChunkAccess {
}
}
@@ -7624,51 +5457,25 @@ index b537e7a079497db428db405edfccde74f32f4208..7898e1aaf82f096fa74bd3f5859f0f43
@Override
public FluidState getFluidState(BlockPos pos) {
return this.getFluidState(pos.getX(), pos.getY(), pos.getZ());
-@@ -549,7 +681,25 @@ public class LevelChunk extends ChunkAccess {
+@@ -549,7 +582,11 @@ public class LevelChunk extends ChunkAccess {
// CraftBukkit start
public void loadCallback() {
-+ // Paper start - neighbour cache
-+ int chunkX = this.chunkPos.x;
-+ int chunkZ = this.chunkPos.z;
-+ net.minecraft.server.level.ServerChunkCache chunkProvider = this.level.getChunkSource();
-+ for (int dx = -NEIGHBOUR_CACHE_RADIUS; dx <= NEIGHBOUR_CACHE_RADIUS; ++dx) {
-+ for (int dz = -NEIGHBOUR_CACHE_RADIUS; dz <= NEIGHBOUR_CACHE_RADIUS; ++dz) {
-+ LevelChunk neighbour = chunkProvider.getChunkAtIfLoadedMainThreadNoCache(chunkX + dx, chunkZ + dz);
-+ if (neighbour != null) {
-+ neighbour.setNeighbourLoaded(-dx, -dz, this);
-+ // should be in cached already
-+ this.setNeighbourLoaded(dx, dz, neighbour);
-+ }
-+ }
-+ }
-+ this.setNeighbourLoaded(0, 0, this);
++ // Paper start
+ this.loadedTicketLevel = true;
-+ // Paper end - neighbour cache
++ // Paper end
org.bukkit.Server server = this.level.getCraftServer();
+ this.level.getChunkSource().addLoadedChunk(this); // Paper
if (server != null) {
/*
* If it's a new world, the first few chunks are generated inside
-@@ -590,6 +740,22 @@ public class LevelChunk extends ChunkAccess {
+@@ -590,6 +627,10 @@ 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();
+ this.level.getChunkSource().removeLoadedChunk(this); // Paper
-+ // Paper start - neighbour cache
-+ int chunkX = this.chunkPos.x;
-+ int chunkZ = this.chunkPos.z;
-+ net.minecraft.server.level.ServerChunkCache chunkProvider = this.level.getChunkSource();
-+ for (int dx = -NEIGHBOUR_CACHE_RADIUS; dx <= NEIGHBOUR_CACHE_RADIUS; ++dx) {
-+ for (int dz = -NEIGHBOUR_CACHE_RADIUS; dz <= NEIGHBOUR_CACHE_RADIUS; ++dz) {
-+ LevelChunk neighbour = chunkProvider.getChunkAtIfLoadedMainThreadNoCache(chunkX + dx, chunkZ + dz);
-+ if (neighbour != null) {
-+ neighbour.setNeighbourUnloaded(-dx, -dz);
-+ }
-+ }
-+ }
++ // Paper start
+ this.loadedTicketLevel = false;
-+ this.resetNeighbours();
+ // Paper end
}
@@ -8150,7 +5957,7 @@ index e08d4a45e313ef1b9005ef00ee0185a188171207..2fc68d129e2fdfd51e310ea5bdfb8332
public static byte toLegacyData(BlockState data) {
diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
-index 5fd6eb754c4edebed6798c65b06507a4e89ca48f..0794d92c42b0db6b367505ae28f09f1fd39fa312 100644
+index 5fd6eb754c4edebed6798c65b06507a4e89ca48f..524b51a0ab808a0629c871ad813115abd4b49dbd 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
@@ -58,6 +58,7 @@ import net.minecraft.world.phys.shapes.VoxelShape;
@@ -8170,19 +5977,19 @@ index 5fd6eb754c4edebed6798c65b06507a4e89ca48f..0794d92c42b0db6b367505ae28f09f1f
+ @Nullable
+ @Override
+ public BlockState getBlockStateIfLoaded(final BlockPos blockposition) {
-+ return null;
++ return this.handle.getBlockStateIfLoaded(blockposition);
+ }
+
+ @Nullable
+ @Override
+ public FluidState getFluidIfLoaded(final BlockPos blockposition) {
-+ return null;
++ return this.handle.getFluidIfLoaded(blockposition);
+ }
+
+ @Nullable
+ @Override
+ public ChunkAccess getChunkIfLoadedImmediately(final int x, final int z) {
-+ return null;
++ return this.handle.getChunkIfLoadedImmediately(x, z);
+ }
+ // Paper end
}
diff --git a/patches/server/0010-Adventure.patch b/patches/server/0010-Adventure.patch
index c6a160a49b..783ccc1503 100644
--- a/patches/server/0010-Adventure.patch
+++ b/patches/server/0010-Adventure.patch
@@ -2606,7 +2606,7 @@ index bb97fdb9aa6167083442a928276ebe4225a586ef..5d1758086ed4fce5b36a5b31df44ccea
@Override
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index b1325e090f2c7aff31d27fc38ca7173efe31ed7c..0742aaf07f37e51d24295e7819ac6cec961c7626 100644
+index 9eb987f9d86396d6b7e9d4f3834bea3326640ac7..25d6be308be03815301cfbefdc4199c898f7b9c0 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -201,6 +201,7 @@ import org.bukkit.craftbukkit.SpigotTimings; // Spigot
@@ -2644,7 +2644,7 @@ index b1325e090f2c7aff31d27fc38ca7173efe31ed7c..0742aaf07f37e51d24295e7819ac6cec
this.profiler.push("commandFunctions");
SpigotTimings.commandFunctionsTimer.startTiming(); // Spigot
this.getFunctions().tick();
-@@ -1805,10 +1806,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1800,10 +1801,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public String getMotd() {
@@ -2666,7 +2666,7 @@ index b1325e090f2c7aff31d27fc38ca7173efe31ed7c..0742aaf07f37e51d24295e7819ac6cec
this.motd = motd;
}
-@@ -2570,23 +2581,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2565,23 +2576,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
public void logChatMessage(Component message, ChatType.Bound params, @Nullable String prefix) {
diff --git a/patches/server/0011-Use-TerminalConsoleAppender-for-console-improvements.patch b/patches/server/0011-Use-TerminalConsoleAppender-for-console-improvements.patch
index 8e5fbfaf15..35237b2e07 100644
--- a/patches/server/0011-Use-TerminalConsoleAppender-for-console-improvements.patch
+++ b/patches/server/0011-Use-TerminalConsoleAppender-for-console-improvements.patch
@@ -260,7 +260,7 @@ index 8323f135d6bf2e1f12525e05094ffa3f2420e7e1..a143ea1e58464a3122fbd8ccafe417bd
}
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 0742aaf07f37e51d24295e7819ac6cec961c7626..e82218de79759dfaa7fe2a5a78e150f135bf01a3 100644
+index 25d6be308be03815301cfbefdc4199c898f7b9c0..b67cb9b027c33494f0ed3c6c6ac354a5c79fbf47 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -154,7 +154,7 @@ import com.mojang.serialization.Dynamic;
@@ -308,7 +308,7 @@ index 0742aaf07f37e51d24295e7819ac6cec961c7626..e82218de79759dfaa7fe2a5a78e150f1
} catch (Exception ignored) {
}
// CraftBukkit end
-@@ -1664,7 +1667,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1659,7 +1662,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public void sendSystemMessage(Component message) {
diff --git a/patches/server/0020-Plugin-remapping.patch b/patches/server/0020-Plugin-remapping.patch
index 8089ed1b6d..dc3aee0b84 100644
--- a/patches/server/0020-Plugin-remapping.patch
+++ b/patches/server/0020-Plugin-remapping.patch
@@ -1553,7 +1553,7 @@ index 0000000000000000000000000000000000000000..badff5d6ae6dd8d209c82bc7e8afe370
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index e82218de79759dfaa7fe2a5a78e150f135bf01a3..202a6510d9d093119ff88b910cef6e47fce2e6b8 100644
+index b67cb9b027c33494f0ed3c6c6ac354a5c79fbf47..cde2e181bf1aaf92f1e96b00e03b7b001a06e6b3 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -643,6 +643,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0023-Timings-v2.patch b/patches/server/0023-Timings-v2.patch
index 44e528fa96..3ed1b8f74e 100644
--- a/patches/server/0023-Timings-v2.patch
+++ b/patches/server/0023-Timings-v2.patch
@@ -714,7 +714,7 @@ index f7197f1347251a37dd0f6d9ffa2f09bc3a4e1233..d0d36a57ec4896bcb74970f8fb24d8f3
} catch (Exception exception) {
if (exception instanceof ReportedException) {
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 202a6510d9d093119ff88b910cef6e47fce2e6b8..4137cf4d716680ff1b1ab0b8a3e8f7cb4bae7dbe 100644
+index cde2e181bf1aaf92f1e96b00e03b7b001a06e6b3..a720743149f9d1f90eda3b7b928b9d25d8c8f553 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -196,7 +196,7 @@ import org.bukkit.craftbukkit.Main;
@@ -861,7 +861,7 @@ index 202a6510d9d093119ff88b910cef6e47fce2e6b8..4137cf4d716680ff1b1ab0b8a3e8f7cb
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
-@@ -1499,24 +1522,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1494,24 +1517,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
this.profiler.popPush("connection");
@@ -978,7 +978,7 @@ index d38ecbc208c34509eaf77751ac45d9ef51a5dce8..b51c3f8c485496734ea58c15377a1215
// CraftBukkit end
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 319f51eb8adde7584c74780ac0539f4b8ef8fe7f..ddadb0f13b96a39ec89cdaeea7bc02ee62ef2a06 100644
+index d3caadb3e884d7d0468daf5eff9abd6629ac4b49..a7de9d6bbbd0b3d60b1d49e116c388b1846f33e4 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1,8 +1,10 @@
@@ -992,7 +992,7 @@ index 319f51eb8adde7584c74780ac0539f4b8ef8fe7f..ddadb0f13b96a39ec89cdaeea7bc02ee
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;
import com.google.common.collect.Sets;
-@@ -1363,6 +1365,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1336,6 +1338,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
List<ServerPlayer> list = Lists.newArrayList();
List<ServerPlayer> list1 = this.level.players();
ObjectIterator objectiterator = this.entityMap.values().iterator();
@@ -1000,7 +1000,7 @@ index 319f51eb8adde7584c74780ac0539f4b8ef8fe7f..ddadb0f13b96a39ec89cdaeea7bc02ee
ChunkMap.TrackedEntity playerchunkmap_entitytracker;
-@@ -1387,14 +1390,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1360,14 +1363,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
playerchunkmap_entitytracker.serverEntity.sendChanges();
}
}
@@ -1019,10 +1019,10 @@ index 319f51eb8adde7584c74780ac0539f4b8ef8fe7f..ddadb0f13b96a39ec89cdaeea7bc02ee
}
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index ab57071cc6ce8b79d883f2426855c1abf577e90d..c627d32e7dbc83b9fc60b89c73bff5c9992ad548 100644
+index e9f53f57c363a32106880ea9aad0ccf5a7342509..0ff45d4ce6ae0b7feac53c7a9f361f6454f8b7c3 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -269,13 +269,15 @@ public class ServerChunkCache extends ChunkSource {
+@@ -197,13 +197,15 @@ public class ServerChunkCache extends ChunkSource {
}
gameprofilerfiller.incrementCounter("getChunkCacheMiss");
@@ -1040,7 +1040,7 @@ index ab57071cc6ce8b79d883f2426855c1abf577e90d..c627d32e7dbc83b9fc60b89c73bff5c9
ChunkResult<ChunkAccess> chunkresult = (ChunkResult) completablefuture.join();
ChunkAccess ichunkaccess1 = (ChunkAccess) chunkresult.orElse(null); // CraftBukkit - decompile error
-@@ -440,7 +442,9 @@ public class ServerChunkCache extends ChunkSource {
+@@ -368,7 +370,9 @@ public class ServerChunkCache extends ChunkSource {
public void save(boolean flush) {
this.runDistanceManagerUpdates();
@@ -1050,7 +1050,7 @@ index ab57071cc6ce8b79d883f2426855c1abf577e90d..c627d32e7dbc83b9fc60b89c73bff5c9
}
@Override
-@@ -482,10 +486,10 @@ public class ServerChunkCache extends ChunkSource {
+@@ -410,10 +414,10 @@ public class ServerChunkCache extends ChunkSource {
this.level.timings.doChunkMap.stopTiming(); // Spigot
this.level.getProfiler().popPush("chunks");
if (tickChunks) {
@@ -1063,7 +1063,7 @@ index ab57071cc6ce8b79d883f2426855c1abf577e90d..c627d32e7dbc83b9fc60b89c73bff5c9
}
this.level.timings.doChunkUnload.startTiming(); // Spigot
-@@ -508,6 +512,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -436,6 +440,7 @@ public class ServerChunkCache extends ChunkSource {
gameprofilerfiller.push("filteringLoadedChunks");
List<ServerChunkCache.ChunkAndHolder> list = Lists.newArrayListWithCapacity(this.chunkMap.size());
Iterator iterator = this.chunkMap.getChunks().iterator();
@@ -1071,7 +1071,7 @@ index ab57071cc6ce8b79d883f2426855c1abf577e90d..c627d32e7dbc83b9fc60b89c73bff5c9
while (iterator.hasNext()) {
ChunkHolder playerchunk = (ChunkHolder) iterator.next();
-@@ -520,8 +525,10 @@ public class ServerChunkCache extends ChunkSource {
+@@ -448,8 +453,10 @@ public class ServerChunkCache extends ChunkSource {
if (this.level.tickRateManager().runsNormally()) {
gameprofilerfiller.popPush("naturalSpawnCount");
@@ -1082,7 +1082,7 @@ index ab57071cc6ce8b79d883f2426855c1abf577e90d..c627d32e7dbc83b9fc60b89c73bff5c9
this.lastSpawnState = spawnercreature_d;
gameprofilerfiller.popPush("spawnAndTick");
-@@ -544,22 +551,25 @@ public class ServerChunkCache extends ChunkSource {
+@@ -472,22 +479,25 @@ public class ServerChunkCache extends ChunkSource {
}
if (this.level.shouldTickBlocksAt(chunkcoordintpair.toLong())) {
@@ -1545,10 +1545,10 @@ index e6c586eb85c6c477a3c130e1e1a37b41f17c30c8..6e35709f2a7c32050908e7e5af5529c9
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 7898e1aaf82f096fa74bd3f5859f0f4303ea677f..05d959ccc424aaaa465ec256213f2ec4d44ef8b5 100644
+index c664021dbfffcf0db3247041270ce9a1ee6940de..332351b78fa22cd354b916c4a29bea5b4b223e40 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -710,6 +710,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -597,6 +597,7 @@ public class LevelChunk extends ChunkAccess {
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(bukkitChunk, this.needsDecoration));
if (this.needsDecoration) {
@@ -1556,7 +1556,7 @@ index 7898e1aaf82f096fa74bd3f5859f0f4303ea677f..05d959ccc424aaaa465ec256213f2ec4
this.needsDecoration = false;
java.util.Random random = new java.util.Random();
random.setSeed(this.level.getSeed());
-@@ -729,6 +730,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -616,6 +617,7 @@ public class LevelChunk extends ChunkAccess {
}
}
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk));
diff --git a/patches/server/0025-Further-improve-server-tick-loop.patch b/patches/server/0025-Further-improve-server-tick-loop.patch
index 0a1f62fc08..0fc8d093bf 100644
--- a/patches/server/0025-Further-improve-server-tick-loop.patch
+++ b/patches/server/0025-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 4137cf4d716680ff1b1ab0b8a3e8f7cb4bae7dbe..8fbc764fd2802f735b9d5fac2dabca6a7cebe58e 100644
+index a720743149f9d1f90eda3b7b928b9d25d8c8f553..c09f3dc29c86fef9edfe7831776a77fc73e52210 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -296,7 +296,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0034-Expose-server-build-information.patch b/patches/server/0034-Expose-server-build-information.patch
index 7b10d6d232..de12c47edc 100644
--- a/patches/server/0034-Expose-server-build-information.patch
+++ b/patches/server/0034-Expose-server-build-information.patch
@@ -472,7 +472,7 @@ index 0000000000000000000000000000000000000000..790bad0494454ca12ee152e3de6da3da
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 8fbc764fd2802f735b9d5fac2dabca6a7cebe58e..3d9c7105bfdb1b0c63a036ba3213d51b2fb6425f 100644
+index c09f3dc29c86fef9edfe7831776a77fc73e52210..6f5b04a59fa1173bfdea2c59a9c13ca3710abb0c 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -43,7 +43,6 @@ import java.util.Set;
@@ -492,7 +492,7 @@ index 8fbc764fd2802f735b9d5fac2dabca6a7cebe58e..3d9c7105bfdb1b0c63a036ba3213d51b
import org.bukkit.event.server.ServerLoadEvent;
// CraftBukkit end
-@@ -1708,7 +1705,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1703,7 +1700,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@DontObfuscate
public String getServerModName() {
diff --git a/patches/server/0038-Prevent-block-entity-and-entity-crashes.patch b/patches/server/0038-Prevent-block-entity-and-entity-crashes.patch
index c4f8050de2..8c0b53d52b 100644
--- a/patches/server/0038-Prevent-block-entity-and-entity-crashes.patch
+++ b/patches/server/0038-Prevent-block-entity-and-entity-crashes.patch
@@ -44,10 +44,10 @@ index 6e35709f2a7c32050908e7e5af5529c9f342b787..d20f71a2098b327423cbdbbc096aa9e3
}
}
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 05d959ccc424aaaa465ec256213f2ec4d44ef8b5..329f2210b73a75fc91a5ba06a1ed7f66c5aa2680 100644
+index 332351b78fa22cd354b916c4a29bea5b4b223e40..7bfe757b605f4e5a16b28214ac3b497e738b45b5 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -1073,11 +1073,11 @@ public class LevelChunk extends ChunkAccess {
+@@ -948,11 +948,11 @@ public class LevelChunk extends ChunkAccess {
gameprofilerfiller.pop();
} catch (Throwable throwable) {
diff --git a/patches/server/0044-Optimize-explosions.patch b/patches/server/0044-Optimize-explosions.patch
index e3fc8cb992..600d98a0a6 100644
--- a/patches/server/0044-Optimize-explosions.patch
+++ b/patches/server/0044-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 3d9c7105bfdb1b0c63a036ba3213d51b2fb6425f..5c82bf57575bbe8bea8d39bead51c6860ac86e37 100644
+index 6f5b04a59fa1173bfdea2c59a9c13ca3710abb0c..0b666bbe75a97a8e1e6153533416d156adb64a0a 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1579,6 +1579,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1574,6 +1574,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.profiler.pop();
this.profiler.pop();
diff --git a/patches/server/0063-Add-exception-reporting-event.patch b/patches/server/0063-Add-exception-reporting-event.patch
index d4df43a405..83206d2117 100644
--- a/patches/server/0063-Add-exception-reporting-event.patch
+++ b/patches/server/0063-Add-exception-reporting-event.patch
@@ -123,10 +123,10 @@ index 6a80479554f0c860a8dd6baa1a6506858fca83e3..6324689f52363f19501143c1649f0885
}
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 329f2210b73a75fc91a5ba06a1ed7f66c5aa2680..3df1ad6a4804832f4edb79a8c8fc60909a1aaa24 100644
+index 7bfe757b605f4e5a16b28214ac3b497e738b45b5..fb953b2172c322e8abf5aa50060adbc00fe92832 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -565,8 +565,13 @@ public class LevelChunk extends ChunkAccess {
+@@ -466,8 +466,13 @@ public class LevelChunk extends ChunkAccess {
BlockState iblockdata = this.getBlockState(blockposition);
if (!iblockdata.hasBlockEntity()) {
@@ -142,7 +142,7 @@ index 329f2210b73a75fc91a5ba06a1ed7f66c5aa2680..3df1ad6a4804832f4edb79a8c8fc6090
} else {
BlockState iblockdata1 = blockEntity.getBlockState();
-@@ -1076,6 +1081,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -951,6 +956,7 @@ public class LevelChunk extends ChunkAccess {
// Paper start - Prevent block 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/0078-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch b/patches/server/0078-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch
index 70d7c34607..cea7703900 100644
--- a/patches/server/0078-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch
+++ b/patches/server/0078-Only-process-BlockPhysicsEvent-if-a-plugin-has-a-lis.patch
@@ -6,7 +6,7 @@ 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 5c82bf57575bbe8bea8d39bead51c6860ac86e37..4fd88b63ae6e6aa3265f72f4f0ae1b1895b70be9 100644
+index 0b666bbe75a97a8e1e6153533416d156adb64a0a..c38f932fe1c22dbe9b2583aab43ae879e4e5d4ad 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1545,6 +1545,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0080-Configurable-Chunk-Inhabited-Time.patch b/patches/server/0080-Configurable-Chunk-Inhabited-Time.patch
index 8af41cf8f6..9eb058e669 100644
--- a/patches/server/0080-Configurable-Chunk-Inhabited-Time.patch
+++ b/patches/server/0080-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 3df1ad6a4804832f4edb79a8c8fc60909a1aaa24..9a94b0513e8b75c8acf0117bc0fbf563655ec3d8 100644
+index fb953b2172c322e8abf5aa50060adbc00fe92832..a0f9fb2ac15f2fa51a3bbe915550155ac2e76649 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -271,6 +271,13 @@ public class LevelChunk extends ChunkAccess {
+@@ -172,6 +172,13 @@ public class LevelChunk extends ChunkAccess {
return new ChunkAccess.TicksToSave(this.blockTicks, this.fluidTicks);
}
diff --git a/patches/server/0089-Configurable-Player-Collision.patch b/patches/server/0089-Configurable-Player-Collision.patch
index 01f37915a5..f64375d8bd 100644
--- a/patches/server/0089-Configurable-Player-Collision.patch
+++ b/patches/server/0089-Configurable-Player-Collision.patch
@@ -18,7 +18,7 @@ index 9a1a961eabd4362c171da78c6be82c867f3696a4..1d0c473442b5c72245c356054440323e
ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buf, this.playerPrefix);
ComponentSerialization.TRUSTED_STREAM_CODEC.encode(buf, this.playerSuffix);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 4fd88b63ae6e6aa3265f72f4f0ae1b1895b70be9..8f0a0384d12b738553a6bec02e3b9438671715a9 100644
+index c38f932fe1c22dbe9b2583aab43ae879e4e5d4ad..785e033064ac8067baba2b471797ca34cf323825 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -640,6 +640,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0094-remove-null-possibility-for-getServer-singleton.patch b/patches/server/0094-remove-null-possibility-for-getServer-singleton.patch
index fb6f4a3728..22d8b6a7c0 100644
--- a/patches/server/0094-remove-null-possibility-for-getServer-singleton.patch
+++ b/patches/server/0094-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 8f0a0384d12b738553a6bec02e3b9438671715a9..fa55700e61d10cc071e55dfe43b48aee2113cd44 100644
+index 785e033064ac8067baba2b471797ca34cf323825..75de08945200289b715d80f27adcfe2d22f42fbd 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -197,6 +197,7 @@ import co.aikar.timings.MinecraftTimings; // Paper
@@ -25,7 +25,7 @@ index 8f0a0384d12b738553a6bec02e3b9438671715a9..fa55700e61d10cc071e55dfe43b48aee
this.metricsRecorder = InactiveMetricsRecorder.INSTANCE;
this.profiler = this.metricsRecorder.getProfiler();
this.onMetricsRecordingStopped = (methodprofilerresults) -> {
-@@ -2554,9 +2556,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2549,9 +2551,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return false;
}
diff --git a/patches/server/0098-Async-GameProfileCache-saving.patch b/patches/server/0098-Async-GameProfileCache-saving.patch
index fae04396e8..b140bba3f6 100644
--- a/patches/server/0098-Async-GameProfileCache-saving.patch
+++ b/patches/server/0098-Async-GameProfileCache-saving.patch
@@ -5,7 +5,7 @@ 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 fa55700e61d10cc071e55dfe43b48aee2113cd44..56d51073eb0cd902fa2c1e790201f74d120b149f 100644
+index 75de08945200289b715d80f27adcfe2d22f42fbd..451b6fef2c13a35eb00370fccd92581c97810137 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -999,7 +999,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0129-Properly-handle-async-calls-to-restart-the-server.patch b/patches/server/0129-Properly-handle-async-calls-to-restart-the-server.patch
index c5a8314eac..6bab23ed9e 100644
--- a/patches/server/0129-Properly-handle-async-calls-to-restart-the-server.patch
+++ b/patches/server/0129-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 56d51073eb0cd902fa2c1e790201f74d120b149f..7335f9bb936eeb585ee077b0b9c461d7946d6134 100644
+index 451b6fef2c13a35eb00370fccd92581c97810137..6c46ce47d1460dcd9b387f02a09d85048a45b049 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -242,6 +242,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0137-Basic-PlayerProfile-API.patch b/patches/server/0137-Basic-PlayerProfile-API.patch
index 6a98ad657e..5cddffd5d6 100644
--- a/patches/server/0137-Basic-PlayerProfile-API.patch
+++ b/patches/server/0137-Basic-PlayerProfile-API.patch
@@ -559,7 +559,7 @@ index 0000000000000000000000000000000000000000..7ac27392a8647ef7d0dc78efe78703e9
+ @NotNull GameProfile buildGameProfile();
+}
diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java
-index eb36bef19e6729c1cc44aefa927317963aba929e..4f5badb9429b34ed999183321c98b60978715638 100644
+index c6c723d9378c593c8608d5940f63c98dff097cd0..02d6c98b7a2212b13ffd9859ebfdc0a8357ebe65 100644
--- a/src/main/java/io/papermc/paper/util/MCUtil.java
+++ b/src/main/java/io/papermc/paper/util/MCUtil.java
@@ -1,5 +1,7 @@
diff --git a/patches/server/0165-PlayerNaturallySpawnCreaturesEvent.patch b/patches/server/0165-PlayerNaturallySpawnCreaturesEvent.patch
index 06e70b667b..e9b4dead15 100644
--- a/patches/server/0165-PlayerNaturallySpawnCreaturesEvent.patch
+++ b/patches/server/0165-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 ddadb0f13b96a39ec89cdaeea7bc02ee62ef2a06..d04b69838c6f5fd1808782cacb31c6e00087bbac 100644
+index a7de9d6bbbd0b3d60b1d49e116c388b1846f33e4..767e3aed729b630d67a702d5e5bda02e9865e6e1 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1101,7 +1101,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1074,7 +1074,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
chunkRange = (chunkRange > this.level.spigotConfig.viewDistance) ? (byte) this.level.spigotConfig.viewDistance : chunkRange;
chunkRange = (chunkRange > 8) ? 8 : chunkRange;
@@ -23,7 +23,7 @@ index ddadb0f13b96a39ec89cdaeea7bc02ee62ef2a06..d04b69838c6f5fd1808782cacb31c6e0
// Spigot end
if (!this.distanceManager.hasPlayersNearby(chunkcoordintpair.toLong())) {
return false;
-@@ -1116,6 +1118,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1089,6 +1091,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
entityplayer = (ServerPlayer) iterator.next();
@@ -40,10 +40,10 @@ index ddadb0f13b96a39ec89cdaeea7bc02ee62ef2a06..d04b69838c6f5fd1808782cacb31c6e0
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 c627d32e7dbc83b9fc60b89c73bff5c9992ad548..f4451bb402abbc8e6506132b9b9702bb5d75e097 100644
+index 0ff45d4ce6ae0b7feac53c7a9f361f6454f8b7c3..035c33b1e1be4f41e5cbe09bf6c3abd617258cad 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -535,6 +535,15 @@ public class ServerChunkCache extends ChunkSource {
+@@ -463,6 +463,15 @@ public class ServerChunkCache extends ChunkSource {
boolean flag = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
Util.shuffle(list, this.level.random);
diff --git a/patches/server/0175-Implement-extended-PaperServerListPingEvent.patch b/patches/server/0175-Implement-extended-PaperServerListPingEvent.patch
index bdf24696e3..4ca3384be1 100644
--- a/patches/server/0175-Implement-extended-PaperServerListPingEvent.patch
+++ b/patches/server/0175-Implement-extended-PaperServerListPingEvent.patch
@@ -162,7 +162,7 @@ index 0000000000000000000000000000000000000000..874dcbefea469440a9028ddc399a37c9
+
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 7335f9bb936eeb585ee077b0b9c461d7946d6134..1c024fb7f682c81c465f59f4ab5fbeac964d73e1 100644
+index 6c46ce47d1460dcd9b387f02a09d85048a45b049..f71b1c2497594b592bcc51be9fa6a6d8fe8e819b 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -3,6 +3,9 @@ package net.minecraft.server;
diff --git a/patches/server/0227-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/patches/server/0227-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
index d443d145a4..df5cc389e6 100644
--- a/patches/server/0227-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
+++ b/patches/server/0227-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add Debug Entities option to debug dupe uuid issues
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index d04b69838c6f5fd1808782cacb31c6e00087bbac..96b7f0ac35a1e87c3f78a24180b207c32749fb71 100644
+index 767e3aed729b630d67a702d5e5bda02e9865e6e1..27eaeea3f0719167d89e39b0b60f91ad9cc9f64e 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1321,6 +1321,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1294,6 +1294,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} else {
ChunkMap.TrackedEntity playerchunkmap_entitytracker = new ChunkMap.TrackedEntity(entity, i, j, entitytypes.trackDeltas());
@@ -16,7 +16,7 @@ index d04b69838c6f5fd1808782cacb31c6e00087bbac..96b7f0ac35a1e87c3f78a24180b207c3
this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
playerchunkmap_entitytracker.updatePlayers(this.level.players());
if (entity instanceof ServerPlayer) {
-@@ -1361,7 +1362,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1334,7 +1335,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (playerchunkmap_entitytracker1 != null) {
playerchunkmap_entitytracker1.broadcastRemoved();
}
diff --git a/patches/server/0228-Add-Early-Warning-Feature-to-WatchDog.patch b/patches/server/0228-Add-Early-Warning-Feature-to-WatchDog.patch
index 6be15047b5..545fb80f1b 100644
--- a/patches/server/0228-Add-Early-Warning-Feature-to-WatchDog.patch
+++ b/patches/server/0228-Add-Early-Warning-Feature-to-WatchDog.patch
@@ -9,7 +9,7 @@ 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 1c024fb7f682c81c465f59f4ab5fbeac964d73e1..3fbe9a4981c682ec602d8ad1c390a10f26505f08 100644
+index f71b1c2497594b592bcc51be9fa6a6d8fe8e819b..6a97ab5e8a435e67df77b1d0480ccb397314ee05 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1108,6 +1108,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0263-Improve-Server-Thread-Pool-and-Thread-Priorities.patch b/patches/server/0263-Improve-Server-Thread-Pool-and-Thread-Priorities.patch
index 645b07b342..9ea7802386 100644
--- a/patches/server/0263-Improve-Server-Thread-Pool-and-Thread-Priorities.patch
+++ b/patches/server/0263-Improve-Server-Thread-Pool-and-Thread-Priorities.patch
@@ -92,7 +92,7 @@ index 54562fa04d14a937451ea7aa9d80194f2c31b471..4cf88f6d815d60cfbf8e4ecf9d96d0cf
return executorService;
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 3fbe9a4981c682ec602d8ad1c390a10f26505f08..51cd2b33c89f2ba92ad926456551e789c8627c3b 100644
+index 6a97ab5e8a435e67df77b1d0480ccb397314ee05..c8b5714ad87b501fdf820ae2ad8e6a4bdef82651 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -323,6 +323,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0264-Optimize-World-Time-Updates.patch b/patches/server/0264-Optimize-World-Time-Updates.patch
index 33b1fad747..a0210bfae8 100644
--- a/patches/server/0264-Optimize-World-Time-Updates.patch
+++ b/patches/server/0264-Optimize-World-Time-Updates.patch
@@ -8,7 +8,7 @@ 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 51cd2b33c89f2ba92ad926456551e789c8627c3b..3d9a333c0f8175fd3b961185f52ea9a83a2fbeb3 100644
+index c8b5714ad87b501fdf820ae2ad8e6a4bdef82651..2e96a4c61f9053c20060e9f45a313a6135ea4f16 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1563,12 +1563,24 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0277-Async-command-map-building.patch b/patches/server/0277-Async-command-map-building.patch
index 43059464da..d6ee248a00 100644
--- a/patches/server/0277-Async-command-map-building.patch
+++ b/patches/server/0277-Async-command-map-building.patch
@@ -53,7 +53,7 @@ index 72756ef14b8ec8afd80313b9f6aaf76722cb18cf..a05aea8561ac102476ee1b3068942b09
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 3d9a333c0f8175fd3b961185f52ea9a83a2fbeb3..6494b92c6a6444a66ea0e5f8f2890c47f334c938 100644
+index 2e96a4c61f9053c20060e9f45a313a6135ea4f16..df5b553b8d1525f14dbb88ea86e1bf9d9c9e6950 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -931,6 +931,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0286-Server-Tick-Events.patch b/patches/server/0286-Server-Tick-Events.patch
index df7f3cf156..7478aa71ac 100644
--- a/patches/server/0286-Server-Tick-Events.patch
+++ b/patches/server/0286-Server-Tick-Events.patch
@@ -6,7 +6,7 @@ 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 6494b92c6a6444a66ea0e5f8f2890c47f334c938..12f530044d918ddc1ba4b2376419f9ed72283b98 100644
+index df5b553b8d1525f14dbb88ea86e1bf9d9c9e6950..ee86f2cb71b2c8dc85c27635efca3666de7a3cbe 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1435,6 +1435,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0312-Tracking-Range-Improvements.patch b/patches/server/0312-Tracking-Range-Improvements.patch
index c72f88f8c8..ee9860cefa 100644
--- a/patches/server/0312-Tracking-Range-Improvements.patch
+++ b/patches/server/0312-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 96b7f0ac35a1e87c3f78a24180b207c32749fb71..795c81c8f6fa59eded8b5a5084a8acb46d118fdb 100644
+index 27eaeea3f0719167d89e39b0b60f91ad9cc9f64e..5b62b6ebfc1b2a9d4673953c20b12a97c50ef3a5 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1613,6 +1613,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1586,6 +1586,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/0315-Optimise-getChunkAt-calls-for-loaded-chunks.patch b/patches/server/0315-Optimise-getChunkAt-calls-for-loaded-chunks.patch
index e832698085..6089b2b613 100644
--- a/patches/server/0315-Optimise-getChunkAt-calls-for-loaded-chunks.patch
+++ b/patches/server/0315-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 f4451bb402abbc8e6506132b9b9702bb5d75e097..ef35e5a9fc0bfcd13f02f9193fdd1e7a811e88fd 100644
+index 035c33b1e1be4f41e5cbe09bf6c3abd617258cad..097a333487d646c773349cde3cd08026986fd038 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -253,6 +253,12 @@ public class ServerChunkCache extends ChunkSource {
+@@ -181,6 +181,12 @@ public class ServerChunkCache extends ChunkSource {
return this.getChunk(x, z, leastStatus, create);
}, this.mainThreadProcessor).join();
} else {
@@ -23,7 +23,7 @@ index f4451bb402abbc8e6506132b9b9702bb5d75e097..ef35e5a9fc0bfcd13f02f9193fdd1e7a
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
gameprofilerfiller.incrementCounter("getChunk");
-@@ -296,33 +302,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -224,33 +230,7 @@ public class ServerChunkCache extends ChunkSource {
if (Thread.currentThread() != this.mainThread) {
return null;
} else {
diff --git a/patches/server/0316-Add-debug-for-sync-chunk-loads.patch b/patches/server/0316-Add-debug-for-sync-chunk-loads.patch
index b6f89a4792..2398618fb3 100644
--- a/patches/server/0316-Add-debug-for-sync-chunk-loads.patch
+++ b/patches/server/0316-Add-debug-for-sync-chunk-loads.patch
@@ -300,10 +300,10 @@ index 0000000000000000000000000000000000000000..95d6022c9cfb2e36ec5a71be6e343540
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index ef35e5a9fc0bfcd13f02f9193fdd1e7a811e88fd..a94833f58eb823332890d07c147161e9e0a938e9 100644
+index 097a333487d646c773349cde3cd08026986fd038..be9604a0f267558c95125852d86761a2f175732a 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -280,6 +280,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -208,6 +208,7 @@ public class ServerChunkCache extends ChunkSource {
Objects.requireNonNull(completablefuture);
if (!completablefuture.isDone()) { // Paper
diff --git a/patches/server/0326-Optimise-Chunk-getFluid.patch b/patches/server/0326-Optimise-Chunk-getFluid.patch
index 955f6f3ed8..0e2e9cccd5 100644
--- a/patches/server/0326-Optimise-Chunk-getFluid.patch
+++ b/patches/server/0326-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 9a94b0513e8b75c8acf0117bc0fbf563655ec3d8..46a5d9270aaaccb2df7131fb9c921cccf914085c 100644
+index a0f9fb2ac15f2fa51a3bbe915550155ac2e76649..c7be1f8fdca34bcc12ecbe40ee5f426e0cd068d7 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -369,18 +369,20 @@ public class LevelChunk extends ChunkAccess {
+@@ -270,18 +270,20 @@ public class LevelChunk extends ChunkAccess {
}
public FluidState getFluidState(int x, int y, int z) {
@@ -38,7 +38,7 @@ index 9a94b0513e8b75c8acf0117bc0fbf563655ec3d8..46a5d9270aaaccb2df7131fb9c921ccc
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "Getting fluid state");
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Block being got");
-@@ -390,6 +392,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -291,6 +293,7 @@ public class LevelChunk extends ChunkAccess {
});
throw new ReportedException(crashreport);
}
diff --git a/patches/server/0328-Add-tick-times-API-and-mspt-command.patch b/patches/server/0328-Add-tick-times-API-and-mspt-command.patch
index 06d5d06807..53ce864287 100644
--- a/patches/server/0328-Add-tick-times-API-and-mspt-command.patch
+++ b/patches/server/0328-Add-tick-times-API-and-mspt-command.patch
@@ -125,7 +125,7 @@ index 72f2e81b9905a0d57ed8e2a88578f62d5235c456..7b58b2d6297800c2dcdbf7539e5ab8e7
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 12f530044d918ddc1ba4b2376419f9ed72283b98..2a2b5205692573c9886a2696f376958809b899ac 100644
+index ee86f2cb71b2c8dc85c27635efca3666de7a3cbe..1e6546637c5c2ddf4aacc7a3ea8623ce55059203 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -258,6 +258,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -152,7 +152,7 @@ index 12f530044d918ddc1ba4b2376419f9ed72283b98..2a2b5205692573c9886a2696f3769588
this.logTickMethodTime(i);
this.profiler.pop();
org.spigotmc.WatchdogThread.tick(); // Spigot
-@@ -2863,4 +2873,30 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2858,4 +2868,30 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public static record ServerResourcePackInfo(UUID id, String url, String hash, boolean isRequired, @Nullable Component prompt) {
}
diff --git a/patches/server/0334-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch b/patches/server/0334-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch
index 2f32768c21..c037677373 100644
--- a/patches/server/0334-Prevent-Double-PlayerChunkMap-adds-crashing-server.patch
+++ b/patches/server/0334-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 795c81c8f6fa59eded8b5a5084a8acb46d118fdb..1709821c73362b2ae54681ec1d59b40bfa9335b3 100644
+index 5b62b6ebfc1b2a9d4673953c20b12a97c50ef3a5..02ea2b2d5463909663bb599b6ca57198b8513456 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1308,6 +1308,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1281,6 +1281,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public void addEntity(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
diff --git a/patches/server/0345-Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch b/patches/server/0345-Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch
index b9a47e9175..08456c3e37 100644
--- a/patches/server/0345-Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch
+++ b/patches/server/0345-Fire-PlayerJoinEvent-when-Player-is-actually-ready.patch
@@ -31,10 +31,10 @@ delays anymore.
public net.minecraft.server.level.ChunkMap addEntity(Lnet/minecraft/world/entity/Entity;)V
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 1709821c73362b2ae54681ec1d59b40bfa9335b3..68a1cc5f4f7f5997dfb7d40647e3e027c23ffb14 100644
+index 02ea2b2d5463909663bb599b6ca57198b8513456..37bfe3d315210462ed69bb1cb161604e9eb85c36 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1315,6 +1315,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1288,6 +1288,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return;
}
// Paper end - ignore and warn about illegal addEntity calls instead of crashing server
diff --git a/patches/server/0354-misc-debugging-dumps.patch b/patches/server/0354-misc-debugging-dumps.patch
index 7d0b8aaa0b..5959f30078 100644
--- a/patches/server/0354-misc-debugging-dumps.patch
+++ b/patches/server/0354-misc-debugging-dumps.patch
@@ -49,7 +49,7 @@ index e25fc35716aff1d1805884b18f67b0eb33d8c05c..8ca9ac8eff9d605baa878ca24e165ac5
StackTraceElement[] astacktraceelement = exception.getStackTrace();
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 2a2b5205692573c9886a2696f376958809b899ac..15938074ad20133f5ccdab0c8566556d7b807d8f 100644
+index 1e6546637c5c2ddf4aacc7a3ea8623ce55059203..af83b1c762239fe7c8c11d3904f30504b0a1ca67 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -916,6 +916,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0361-Wait-for-Async-Tasks-during-shutdown.patch b/patches/server/0361-Wait-for-Async-Tasks-during-shutdown.patch
index 25e00fd77b..e7c0b4c6b0 100644
--- a/patches/server/0361-Wait-for-Async-Tasks-during-shutdown.patch
+++ b/patches/server/0361-Wait-for-Async-Tasks-during-shutdown.patch
@@ -10,7 +10,7 @@ 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 15938074ad20133f5ccdab0c8566556d7b807d8f..095215a5cbf1abeb7836a6ccc87c195019a7f019 100644
+index af83b1c762239fe7c8c11d3904f30504b0a1ca67..badd42d84b6367f6926bcff0ec8426fb6402ae80 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -943,6 +943,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0376-Fix-Per-World-Difficulty-Remembering-Difficulty.patch b/patches/server/0376-Fix-Per-World-Difficulty-Remembering-Difficulty.patch
index f74a80db7f..2ee047cbd9 100644
--- a/patches/server/0376-Fix-Per-World-Difficulty-Remembering-Difficulty.patch
+++ b/patches/server/0376-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 095215a5cbf1abeb7836a6ccc87c195019a7f019..1d141c9ee7b8193d46ba47a8586fc334cabb62a1 100644
+index badd42d84b6367f6926bcff0ec8426fb6402ae80..331ed639496a73dbfdde795b6d6154a9c8ea78b4 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -837,7 +837,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -20,7 +20,7 @@ index 095215a5cbf1abeb7836a6ccc87c195019a7f019..1d141c9ee7b8193d46ba47a8586fc334
this.forceTicks = false;
// CraftBukkit end
-@@ -1854,11 +1854,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1849,11 +1849,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
}
@@ -40,7 +40,7 @@ index 095215a5cbf1abeb7836a6ccc87c195019a7f019..1d141c9ee7b8193d46ba47a8586fc334
}
}
-@@ -1872,7 +1875,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1867,7 +1870,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
diff --git a/patches/server/0410-Cache-block-data-strings.patch b/patches/server/0410-Cache-block-data-strings.patch
index de40dce04c..c73022bfec 100644
--- a/patches/server/0410-Cache-block-data-strings.patch
+++ b/patches/server/0410-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 1d141c9ee7b8193d46ba47a8586fc334cabb62a1..eddb8792be0581d8f1c87d18b5ab7a02571addc7 100644
+index 331ed639496a73dbfdde795b6d6154a9c8ea78b4..5b7f2b2993844cbcc13742f69b0521832d1970da 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2153,6 +2153,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2148,6 +2148,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/0418-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch b/patches/server/0418-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch
index b387ab8612..91a6325196 100644
--- a/patches/server/0418-Fix-deop-kicking-non-whitelisted-player-when-white-l.patch
+++ b/patches/server/0418-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 eddb8792be0581d8f1c87d18b5ab7a02571addc7..b47110e9b5d8effdc9e768488f99a614c1070814 100644
+index 5b7f2b2993844cbcc13742f69b0521832d1970da..e39cfc9750bf1d3f20abb3a2e9d46fa51ccdc248 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2278,13 +2278,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2273,13 +2273,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/0465-Add-ServerResourcesReloadedEvent.patch b/patches/server/0465-Add-ServerResourcesReloadedEvent.patch
index 2268c27b1c..bfc98bc7e5 100644
--- a/patches/server/0465-Add-ServerResourcesReloadedEvent.patch
+++ b/patches/server/0465-Add-ServerResourcesReloadedEvent.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Add ServerResourcesReloadedEvent
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index b47110e9b5d8effdc9e768488f99a614c1070814..691d9d18e72a07b85bf13c6990b44e5cf56dffb5 100644
+index e39cfc9750bf1d3f20abb3a2e9d46fa51ccdc248..3511b578f137c94ed562690901952e81e6f90ee5 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2122,7 +2122,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2117,7 +2117,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.functionManager;
}
@@ -22,7 +22,7 @@ index b47110e9b5d8effdc9e768488f99a614c1070814..691d9d18e72a07b85bf13c6990b44e5c
CompletableFuture<Void> completablefuture = CompletableFuture.supplyAsync(() -> {
Stream<String> stream = dataPacks.stream(); // CraftBukkit - decompile error
PackRepository resourcepackrepository = this.packRepository;
-@@ -2154,6 +2160,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2149,6 +2155,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
org.bukkit.craftbukkit.block.data.CraftBlockData.reloadCache(); // Paper - cache block data strings; they can be defined by datapacks so refresh it here
diff --git a/patches/server/0489-Add-EntityMoveEvent.patch b/patches/server/0489-Add-EntityMoveEvent.patch
index 999a3c35ff..724779feec 100644
--- a/patches/server/0489-Add-EntityMoveEvent.patch
+++ b/patches/server/0489-Add-EntityMoveEvent.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Add EntityMoveEvent
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 691d9d18e72a07b85bf13c6990b44e5cf56dffb5..64a334c368152656f0dbb811220ae20fbd3d5f85 100644
+index 3511b578f137c94ed562690901952e81e6f90ee5..93c92146c2c7961a5de970e1c18eb7a1907c551b 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1608,6 +1608,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0508-forced-whitelist-use-configurable-kick-message.patch b/patches/server/0508-forced-whitelist-use-configurable-kick-message.patch
index 20600f75d1..a4cd1362fc 100644
--- a/patches/server/0508-forced-whitelist-use-configurable-kick-message.patch
+++ b/patches/server/0508-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 64a334c368152656f0dbb811220ae20fbd3d5f85..fa37c6b43f76af0b82b056cf87ba350abf92e7bb 100644
+index 93c92146c2c7961a5de970e1c18eb7a1907c551b..84bc3fac67c8db28a7102f99bffe762cf34a4a7a 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2294,7 +2294,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2289,7 +2289,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/0548-Add-PlayerKickEvent-causes.patch b/patches/server/0548-Add-PlayerKickEvent-causes.patch
index ebf1392ee2..050372991d 100644
--- a/patches/server/0548-Add-PlayerKickEvent-causes.patch
+++ b/patches/server/0548-Add-PlayerKickEvent-causes.patch
@@ -43,10 +43,10 @@ index dbcf183483766f39334d7f7e8336033906625f3f..300929a406905f5ff1ede664d5b99fb0
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index fa37c6b43f76af0b82b056cf87ba350abf92e7bb..ee212ed5538357830ea8b69da650ab67d67634bb 100644
+index 84bc3fac67c8db28a7102f99bffe762cf34a4a7a..695d6265b4a9f98ba630593ba6c47769240bed93 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2294,7 +2294,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2289,7 +2289,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/0607-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch b/patches/server/0607-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
index 976f278b20..b950818c7b 100644
--- a/patches/server/0607-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
+++ b/patches/server/0607-Make-sure-inlined-getChunkAt-has-inlined-logic-for-l.patch
@@ -10,22 +10,22 @@ chunks did get inlined, but the standard CPS.getChunkAt
method was not inlined.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index ed1fc466151ebebf7c3ac135c6893f4ea9a55a52..481248ef82d4257ca4cc88ab28a1a7946e22aef6 100644
+index ed1fc466151ebebf7c3ac135c6893f4ea9a55a52..38bcf9f410e8a9d47c7d486c28dbc16a6225b650 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -352,6 +352,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -352,7 +352,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@Override
public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline
+- return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump
+ // Paper start - Perf: make sure loaded chunks get the inlined variant of this function
+ net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource();
-+ if (cps.mainThread == Thread.currentThread()) {
-+ LevelChunk ifLoaded = cps.getChunkAtIfLoadedMainThread(chunkX, chunkZ);
-+ if (ifLoaded != null) {
-+ return ifLoaded;
-+ }
++ LevelChunk ifLoaded = cps.getChunkAtIfLoadedImmediately(chunkX, chunkZ);
++ if (ifLoaded != null) {
++ return ifLoaded;
+ }
++ return (LevelChunk) cps.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump
+ // Paper end - Perf: make sure loaded chunks get the inlined variant of this function
- return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump
}
+ // Paper start - if loaded
diff --git a/patches/server/0611-Oprimise-map-impl-for-tracked-players.patch b/patches/server/0611-Oprimise-map-impl-for-tracked-players.patch
index 2cadeeb2a0..667d423e0c 100644
--- a/patches/server/0611-Oprimise-map-impl-for-tracked-players.patch
+++ b/patches/server/0611-Oprimise-map-impl-for-tracked-players.patch
@@ -7,10 +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 68a1cc5f4f7f5997dfb7d40647e3e027c23ffb14..77f064fb4437c1d98cf91dde98d4d88b28afa7c8 100644
+index 37bfe3d315210462ed69bb1cb161604e9eb85c36..be0f4a2a9c7fbc359fe79ecf82340db8f7436b22 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1529,7 +1529,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1502,7 +1502,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
final Entity entity;
private final int range;
SectionPos lastSectionPos;
diff --git a/patches/server/0651-Expose-vanilla-BiomeProvider-from-WorldInfo.patch b/patches/server/0651-Expose-vanilla-BiomeProvider-from-WorldInfo.patch
index 8306fb57e8..b31f8fcb58 100644
--- a/patches/server/0651-Expose-vanilla-BiomeProvider-from-WorldInfo.patch
+++ b/patches/server/0651-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 ee212ed5538357830ea8b69da650ab67d67634bb..22f8e49536658fabe6136dbeec6b77eaf4ae5ee8 100644
+index 695d6265b4a9f98ba630593ba6c47769240bed93..2559a40546701fefa7f28603cb637108bfc659da 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -612,7 +612,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0668-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch b/patches/server/0668-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch
index 351cec2218..524fa973b7 100644
--- a/patches/server/0668-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch
+++ b/patches/server/0668-Execute-chunk-tasks-fairly-for-worlds-while-waiting-.patch
@@ -9,7 +9,7 @@ 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 22f8e49536658fabe6136dbeec6b77eaf4ae5ee8..e5017b66b6b8b5f17fc49f0fbc5d342241cd9cad 100644
+index 2559a40546701fefa7f28603cb637108bfc659da..f728c5b75ee7db5522e558891988079c52df5e72 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1371,6 +1371,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0672-Option-to-have-default-CustomSpawners-in-custom-worl.patch b/patches/server/0672-Option-to-have-default-CustomSpawners-in-custom-worl.patch
index eed2fe11bf..fb23e5d34e 100644
--- a/patches/server/0672-Option-to-have-default-CustomSpawners-in-custom-worl.patch
+++ b/patches/server/0672-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 e5017b66b6b8b5f17fc49f0fbc5d342241cd9cad..f979bfd78e2d19b525e973be0ffef5bda0068c4b 100644
+index f728c5b75ee7db5522e558891988079c52df5e72..0ec8f1c94279b75369ab6bae98789c60a971502c 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -632,7 +632,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0673-Put-world-into-worldlist-before-initing-the-world.patch b/patches/server/0673-Put-world-into-worldlist-before-initing-the-world.patch
index 28d59a5fbf..030720dfbf 100644
--- a/patches/server/0673-Put-world-into-worldlist-before-initing-the-world.patch
+++ b/patches/server/0673-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 f979bfd78e2d19b525e973be0ffef5bda0068c4b..833ec409e3f79e1ae2b664418f219adb3af2957f 100644
+index 0ec8f1c94279b75369ab6bae98789c60a971502c..4e2ce30bc67b0033107715a5a2dbb419bdf17a4f 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -644,9 +644,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0674-Custom-Potion-Mixes.patch b/patches/server/0674-Custom-Potion-Mixes.patch
index 62f7870ba8..3b95d09b1a 100644
--- a/patches/server/0674-Custom-Potion-Mixes.patch
+++ b/patches/server/0674-Custom-Potion-Mixes.patch
@@ -96,10 +96,10 @@ index 0000000000000000000000000000000000000000..7ea357ac2f3a93db4ebdf24b5072be7d
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 833ec409e3f79e1ae2b664418f219adb3af2957f..29011d222434cbb2c71797786b8376ea37c8023a 100644
+index 4e2ce30bc67b0033107715a5a2dbb419bdf17a4f..2408e5afb9096aa991742983bb573040fbfdb5cb 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2166,6 +2166,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2161,6 +2161,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.worldData.setDataConfiguration(worlddataconfiguration);
this.resources.managers.updateRegistryTags();
diff --git a/patches/server/0712-Throw-exception-on-world-create-while-being-ticked.patch b/patches/server/0712-Throw-exception-on-world-create-while-being-ticked.patch
index 5d2cb89f00..ccad54896d 100644
--- a/patches/server/0712-Throw-exception-on-world-create-while-being-ticked.patch
+++ b/patches/server/0712-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 29011d222434cbb2c71797786b8376ea37c8023a..4b44447360b75b6dbe93d8176dcba2db02d77f1e 100644
+index 2408e5afb9096aa991742983bb573040fbfdb5cb..56ab40965ca6034ed430d4d5c4be99fa69a3a453 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -318,6 +318,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -36,7 +36,7 @@ index 29011d222434cbb2c71797786b8376ea37c8023a..4b44447360b75b6dbe93d8176dcba2db
while (iterator.hasNext()) {
ServerLevel worldserver = (ServerLevel) iterator.next();
worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
-@@ -1655,6 +1658,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -1650,6 +1653,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.profiler.pop();
worldserver.explosionDensityCache.clear(); // Paper - Optimize explosions
}
diff --git a/patches/server/0730-Warn-on-plugins-accessing-faraway-chunks.patch b/patches/server/0730-Warn-on-plugins-accessing-faraway-chunks.patch
index e97023798f..f3d50c526a 100644
--- a/patches/server/0730-Warn-on-plugins-accessing-faraway-chunks.patch
+++ b/patches/server/0730-Warn-on-plugins-accessing-faraway-chunks.patch
@@ -5,7 +5,7 @@ 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 481248ef82d4257ca4cc88ab28a1a7946e22aef6..b97427789d6162e16b9c3a56b89b2dd08a04297f 100644
+index 38bcf9f410e8a9d47c7d486c28dbc16a6225b650..210c3b6167dac93e550fe849e34b5aa404ab6dce 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -339,7 +339,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0738-Fix-plugin-loggers-on-server-shutdown.patch b/patches/server/0738-Fix-plugin-loggers-on-server-shutdown.patch
index 6914ddec62..896ce1668f 100644
--- a/patches/server/0738-Fix-plugin-loggers-on-server-shutdown.patch
+++ b/patches/server/0738-Fix-plugin-loggers-on-server-shutdown.patch
@@ -37,7 +37,7 @@ index 0000000000000000000000000000000000000000..c1d3bac79bb8b4796c013ff4472f75dc
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 4b44447360b75b6dbe93d8176dcba2db02d77f1e..ba382397e39311598d044faf6b74665810d2e510 100644
+index 56ab40965ca6034ed430d4d5c4be99fa69a3a453..eef519384b5f8f6b2551ed61affe6cb1f3489702 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1239,6 +1239,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0751-Fix-a-bunch-of-vanilla-bugs.patch b/patches/server/0751-Fix-a-bunch-of-vanilla-bugs.patch
index eba9c58e8b..72188bb0c4 100644
--- a/patches/server/0751-Fix-a-bunch-of-vanilla-bugs.patch
+++ b/patches/server/0751-Fix-a-bunch-of-vanilla-bugs.patch
@@ -85,10 +85,10 @@ index 6854ca4d4fec2b4fa541c3fabf63787665572609..e7b444a10b244828827b3c66c5346520
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 77f064fb4437c1d98cf91dde98d4d88b28afa7c8..ccbd527803a2a4e911a01f815cc9c7ab785af836 100644
+index be0f4a2a9c7fbc359fe79ecf82340db8f7436b22..b6b15e8ce98603d6e580a31ba458fedc8b3f663a 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1091,7 +1091,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1064,7 +1064,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// CraftBukkit end
}
diff --git a/patches/server/0778-Fix-premature-player-kicks-on-shutdown.patch b/patches/server/0778-Fix-premature-player-kicks-on-shutdown.patch
index 7f465faed2..1050fc37f7 100644
--- a/patches/server/0778-Fix-premature-player-kicks-on-shutdown.patch
+++ b/patches/server/0778-Fix-premature-player-kicks-on-shutdown.patch
@@ -47,10 +47,10 @@ index 4d9f1fc884050993287adfa4578a87da710623fb..a8dfe7a4b3d01bf75587be078f471d1e
this.disconnect((Component) Component.translatable("multiplayer.disconnect.server_shutdown"));
} catch (ClassCastException classcastexception) {
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index ba382397e39311598d044faf6b74665810d2e510..fcf450f9fefda8cf2391dcb61075cbd855475d6c 100644
+index eef519384b5f8f6b2551ed61affe6cb1f3489702..374c20e530d2abd41674d80c711bd3737c6f6941 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -2099,7 +2099,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2094,7 +2094,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public void executeIfPossible(Runnable runnable) {
if (this.isStopped()) {
diff --git a/patches/server/0780-Player-Entity-Tracking-Events.patch b/patches/server/0780-Player-Entity-Tracking-Events.patch
index dbbfa39ceb..fd863e753b 100644
--- a/patches/server/0780-Player-Entity-Tracking-Events.patch
+++ b/patches/server/0780-Player-Entity-Tracking-Events.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Player Entity Tracking Events
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index ccbd527803a2a4e911a01f815cc9c7ab785af836..e2521e1a56df8dcb1de815e5973de952408d3b8b 100644
+index b6b15e8ce98603d6e580a31ba458fedc8b3f663a..91ca37605bf7ba65875b588fe9764b30214f63b6 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1601,7 +1601,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1574,7 +1574,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// CraftBukkit end
if (flag) {
if (this.seenBy.add(player.connection)) {
diff --git a/patches/server/0826-Fix-block-place-logic.patch b/patches/server/0826-Fix-block-place-logic.patch
index de31264ab7..3796cc773d 100644
--- a/patches/server/0826-Fix-block-place-logic.patch
+++ b/patches/server/0826-Fix-block-place-logic.patch
@@ -22,10 +22,10 @@ index 7d76cdc59984b156628273c8357485eb10046007..7180996027f70aef7afe32fb2adfce64
itemstack.consume(1, entityhuman);
return InteractionResult.sidedSuccess(world.isClientSide);
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index b97427789d6162e16b9c3a56b89b2dd08a04297f..b8d8041aecc2ff008247449cba8d9f192cf51073 100644
+index 210c3b6167dac93e550fe849e34b5aa404ab6dce..ed1c50d31fc077e4e009719fa622a44edefcdf2c 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -554,17 +554,18 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -552,17 +552,18 @@ 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/0851-Folia-scheduler-and-owned-region-API.patch b/patches/server/0851-Folia-scheduler-and-owned-region-API.patch
index 07e4625db1..591f137a5c 100644
--- a/patches/server/0851-Folia-scheduler-and-owned-region-API.patch
+++ b/patches/server/0851-Folia-scheduler-and-owned-region-API.patch
@@ -1148,7 +1148,7 @@ index 0000000000000000000000000000000000000000..d306f911757a4d556c82c0070d4837db
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index fcf450f9fefda8cf2391dcb61075cbd855475d6c..b7254c342501f2d7fbbe8959a6e88a5d1f6e076e 100644
+index 374c20e530d2abd41674d80c711bd3737c6f6941..cbceb0ddea32781f89a19b1edb258bc23b96ee92 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1579,6 +1579,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0855-Only-capture-actual-tree-growth.patch b/patches/server/0855-Only-capture-actual-tree-growth.patch
index 1cba6ddee8..2ca8f263b7 100644
--- a/patches/server/0855-Only-capture-actual-tree-growth.patch
+++ b/patches/server/0855-Only-capture-actual-tree-growth.patch
@@ -29,10 +29,10 @@ index 92be749721f26e9385e592a985db58cf05c67801..1f2e6f57ffb827ef9bf3623bfdde07db
entityhuman.awardStat(Stats.ITEM_USED.get(item)); // SPIGOT-7236 - award stat
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index b8d8041aecc2ff008247449cba8d9f192cf51073..ffaf6e65a7314479a129fed41f58bf2d75ea5dae 100644
+index ed1c50d31fc077e4e009719fa622a44edefcdf2c..8337f2f1d650fc7efb830a7034e3676dc0695ee4 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -1380,4 +1380,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -1378,4 +1378,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
return range <= 0 ? 64.0 * 64.0 : range * range; // 64 is taken from default in ServerLevel#levelEvent
}
// Paper end - respect global sound events gamerule
diff --git a/patches/server/0869-Configurable-entity-tracking-range-by-Y-coordinate.patch b/patches/server/0869-Configurable-entity-tracking-range-by-Y-coordinate.patch
index a437b50f0f..8cb5db78e2 100644
--- a/patches/server/0869-Configurable-entity-tracking-range-by-Y-coordinate.patch
+++ b/patches/server/0869-Configurable-entity-tracking-range-by-Y-coordinate.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Configurable entity tracking range by Y coordinate
Options to configure entity tracking by Y coordinate, also for each entity category.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index e2521e1a56df8dcb1de815e5973de952408d3b8b..6c5557aad2455b79bb2adf8939eb9a6127ccc3c3 100644
+index 91ca37605bf7ba65875b588fe9764b30214f63b6..2943390c0ec7e542d2bb1996dbbd626445c3108e 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1593,6 +1593,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1566,6 +1566,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
double d1 = vec3d.x * vec3d.x + vec3d.z * vec3d.z;
double d2 = d0 * d0;
boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
diff --git a/patches/server/0901-Don-t-check-if-we-can-see-non-visible-entities.patch b/patches/server/0901-Don-t-check-if-we-can-see-non-visible-entities.patch
index 109e1a443b..246f9bf864 100644
--- a/patches/server/0901-Don-t-check-if-we-can-see-non-visible-entities.patch
+++ b/patches/server/0901-Don-t-check-if-we-can-see-non-visible-entities.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Don't check if we can see non-visible entities
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 6c5557aad2455b79bb2adf8939eb9a6127ccc3c3..469f1dcb22c06025681e727e281b5b53f2b21c1f 100644
+index 2943390c0ec7e542d2bb1996dbbd626445c3108e..7bc142fd0ba58846f1b2ac57df0bfc044966f4ed 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1604,7 +1604,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1577,7 +1577,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper end - Configurable entity tracking range by Y
// CraftBukkit start - respect vanish API
diff --git a/patches/server/0915-Don-t-fire-sync-events-during-worldgen.patch b/patches/server/0915-Don-t-fire-sync-events-during-worldgen.patch
index eeeae7474c..7286e5c172 100644
--- a/patches/server/0915-Don-t-fire-sync-events-during-worldgen.patch
+++ b/patches/server/0915-Don-t-fire-sync-events-during-worldgen.patch
@@ -133,7 +133,7 @@ index cf8258e8d46ca7286a66c38fa24af369bd9a279f..0555abfda468c343af8244a122ebe769
// return Optional.empty();
// }
diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
-index 0794d92c42b0db6b367505ae28f09f1fd39fa312..cd7f1309cf01a5f01a28aded03a36fe15adb1756 100644
+index 524b51a0ab808a0629c871ad813115abd4b49dbd..fceed3d08ee6f4c171685986bb19d2be592eedc6 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
@@ -92,15 +92,17 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel {
diff --git a/patches/server/0927-Properly-handle-experience-dropping-on-block-break.patch b/patches/server/0927-Properly-handle-experience-dropping-on-block-break.patch
index 471a743c29..63f20a9c63 100644
--- a/patches/server/0927-Properly-handle-experience-dropping-on-block-break.patch
+++ b/patches/server/0927-Properly-handle-experience-dropping-on-block-break.patch
@@ -7,10 +7,10 @@ This causes spawnAfterBreak to spawn xp by default, removing the need to manuall
For classes that use custom xp amounts, they can drop the resources with disabling
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index ffaf6e65a7314479a129fed41f58bf2d75ea5dae..16478e2b2368636394cec8a9b30c6fb03e190851 100644
+index 8337f2f1d650fc7efb830a7034e3676dc0695ee4..2f1acea765d1b6726863cdc89707ca6148548493 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -619,7 +619,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -617,7 +617,8 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
if (drop) {
BlockEntity tileentity = iblockdata.hasBlockEntity() ? this.getBlockEntity(pos) : null;
diff --git a/patches/server/0930-Reduce-allocation-of-Vec3D-by-entity-tracker.patch b/patches/server/0930-Reduce-allocation-of-Vec3D-by-entity-tracker.patch
index 86bd3e7f29..16af41429a 100644
--- a/patches/server/0930-Reduce-allocation-of-Vec3D-by-entity-tracker.patch
+++ b/patches/server/0930-Reduce-allocation-of-Vec3D-by-entity-tracker.patch
@@ -18,10 +18,10 @@ index a043ac10834562d357ef0b5aded2e916e2a0d056..74276c368016fcc4dbf9579b2ecbadc9
@VisibleForTesting
static long encode(double value) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 469f1dcb22c06025681e727e281b5b53f2b21c1f..2ce7da9707d7c1a48b5609ae51a516d599d7aee8 100644
+index 7bc142fd0ba58846f1b2ac57df0bfc044966f4ed..a03385b1b0a2f9b98319137b87d917856d3c632c 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -1587,10 +1587,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1560,10 +1560,14 @@ 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) {
diff --git a/patches/server/0945-Add-onboarding-message-for-initial-server-start.patch b/patches/server/0945-Add-onboarding-message-for-initial-server-start.patch
index 89e4195a68..e3249e5435 100644
--- a/patches/server/0945-Add-onboarding-message-for-initial-server-start.patch
+++ b/patches/server/0945-Add-onboarding-message-for-initial-server-start.patch
@@ -29,7 +29,7 @@ index cc847dce0116b8260790b890b1d5452c280e186c..2a5453707bc172d8d0efe3f11959cb0b
return instance;
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index b7254c342501f2d7fbbe8959a6e88a5d1f6e076e..837fc12dfc57f36f06bd8e49681bb4b98a87397c 100644
+index cbceb0ddea32781f89a19b1edb258bc23b96ee92..fc0f17cd7ed4fcf10e0396aeeed115280318128d 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1136,6 +1136,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/0951-Fix-creation-of-invalid-block-entity-during-world-ge.patch b/patches/server/0951-Fix-creation-of-invalid-block-entity-during-world-ge.patch
index 8769e0adaf..d810be487e 100644
--- a/patches/server/0951-Fix-creation-of-invalid-block-entity-during-world-ge.patch
+++ b/patches/server/0951-Fix-creation-of-invalid-block-entity-during-world-ge.patch
@@ -36,10 +36,10 @@ index 682c8cfbd917c086072f1756861a340800ea40da..b26a4a38144ec1b171db911bbf949b53
nbttagcompound.putInt("x", pos.getX());
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 46a5d9270aaaccb2df7131fb9c921cccf914085c..8ae6a4311aa52f84130a1eba61a1ad84e2272350 100644
+index c7be1f8fdca34bcc12ecbe40ee5f426e0cd068d7..7f8983a2102787b13e5d28d6981055da6acd1012 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -1081,9 +1081,14 @@ public class LevelChunk extends ChunkAccess {
+@@ -956,9 +956,14 @@ public class LevelChunk extends ChunkAccess {
if (this.blockEntity.getType().isValid(iblockdata)) {
this.ticker.tick(LevelChunk.this.level, this.blockEntity.getBlockPos(), iblockdata, this.blockEntity);
this.loggedInvalidBlockState = false;
diff --git a/patches/server/0955-Per-world-ticks-per-spawn-settings.patch b/patches/server/0955-Per-world-ticks-per-spawn-settings.patch
index bfd6fd26af..ea163724cf 100644
--- a/patches/server/0955-Per-world-ticks-per-spawn-settings.patch
+++ b/patches/server/0955-Per-world-ticks-per-spawn-settings.patch
@@ -5,7 +5,7 @@ Subject: [PATCH] Per world ticks per spawn settings
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 16478e2b2368636394cec8a9b30c6fb03e190851..4d6409875771413de7ae20def2aaad4049709c30 100644
+index 2f1acea765d1b6726863cdc89707ca6148548493..81bdb6e64e04641f741c2c3350236685b097ec7a 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -186,6 +186,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
diff --git a/patches/server/0957-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch b/patches/server/0957-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
index 0fd49bd9ed..8caee3a137 100644
--- a/patches/server/0957-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
+++ b/patches/server/0957-Protect-Bedrock-and-End-Portal-Frames-from-being-des.patch
@@ -25,10 +25,10 @@ index 69914a048987c21ee2ed2c489aab269862fda8f2..bff83fe413c7baef4ba56a3270ea4463
if (!this.level.isInWorldBounds(blockposition)) {
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 4d6409875771413de7ae20def2aaad4049709c30..dd519eacd6d4bea5447bea471f0ac6540d9bb49c 100644
+index 81bdb6e64e04641f741c2c3350236685b097ec7a..c6c9400fa155831ab11d0f059971d0123617e622 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -449,6 +449,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -447,6 +447,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
// CraftBukkit start - tree generation
if (this.captureTreeGeneration) {
diff --git a/patches/server/0975-Brigadier-based-command-API.patch b/patches/server/0975-Brigadier-based-command-API.patch
index e97c314b29..71f121c880 100644
--- a/patches/server/0975-Brigadier-based-command-API.patch
+++ b/patches/server/0975-Brigadier-based-command-API.patch
@@ -2213,7 +2213,7 @@ index 982b2bab27e3d55d0ba07060862c0c3183ad91b0..5fa8a3343ffc11e82c20b78a73205fd8
Component component = message.resolveComponent(commandSourceStack);
CommandSigningContext commandSigningContext = commandSourceStack.getSigningContext();
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 837fc12dfc57f36f06bd8e49681bb4b98a87397c..6915522f669631779c1fb8a8e2db330f4b9fb921 100644
+index fc0f17cd7ed4fcf10e0396aeeed115280318128d..e14c0e1ccf526f81e28db5545d9e2351641e1bc8 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -306,7 +306,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2243,7 +2243,7 @@ index 837fc12dfc57f36f06bd8e49681bb4b98a87397c..6915522f669631779c1fb8a8e2db330f
this.server.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.STARTUP));
this.connection.acceptConnections();
}
-@@ -2187,9 +2189,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2182,9 +2184,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return new MinecraftServer.ReloadableResources(resourcemanager, datapackresources);
});
}).thenAcceptAsync((minecraftserver_reloadableresources) -> {
@@ -2254,7 +2254,7 @@ index 837fc12dfc57f36f06bd8e49681bb4b98a87397c..6915522f669631779c1fb8a8e2db330f
this.packRepository.setSelected(dataPacks);
WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures());
-@@ -2200,8 +2202,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2195,8 +2197,17 @@ 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/0981-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch b/patches/server/0981-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch
index 7c3afdd7bb..dc3fddbc64 100644
--- a/patches/server/0981-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch
+++ b/patches/server/0981-Fix-cancelling-BlockPlaceEvent-calling-onRemove.patch
@@ -21,7 +21,7 @@ index b800b03ae034b276740c3b41555a52b778ad9aad..86197725f0f2ac1e650297ae7a799075
// Brute force all possible updates
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index dd519eacd6d4bea5447bea471f0ac6540d9bb49c..6f822e9487bef5b9766d5ae86ebbd687e4eadc42 100644
+index c6c9400fa155831ab11d0f059971d0123617e622..e27d3547d1e19c137e05e6b8d075127a8bafb237 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -151,6 +151,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@@ -33,10 +33,10 @@ index dd519eacd6d4bea5447bea471f0ac6540d9bb49c..6f822e9487bef5b9766d5ae86ebbd687
public Map<BlockPos, BlockEntity> capturedTileEntities = new java.util.LinkedHashMap<>(); // Paper - Retain block place order when capturing blockstates
public List<ItemEntity> captureDrops;
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 8ae6a4311aa52f84130a1eba61a1ad84e2272350..6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e 100644
+index 7f8983a2102787b13e5d28d6981055da6acd1012..602ad80c2b93d320bf2a25832d25a58cb8c72e4b 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -444,7 +444,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -345,7 +345,7 @@ public class LevelChunk extends ChunkAccess {
boolean flag3 = iblockdata1.hasBlockEntity();
diff --git a/patches/server/0991-Chunk-System-Starlight-from-Moonrise.patch b/patches/server/0991-Chunk-System-Starlight-from-Moonrise.patch
index 3a0997f047..cde583319b 100644
--- a/patches/server/0991-Chunk-System-Starlight-from-Moonrise.patch
+++ b/patches/server/0991-Chunk-System-Starlight-from-Moonrise.patch
@@ -3105,10 +3105,10 @@ index 0000000000000000000000000000000000000000..e95cc73ddf20050aa4a241b0a309240e
+}
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystem.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystem.java
new file mode 100644
-index 0000000000000000000000000000000000000000..532a8c15009a4514d2682f52129785feb34a56f8
+index 0000000000000000000000000000000000000000..c2ff037e180393de6576f12c32c665ef640d6f50
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/ChunkSystem.java
-@@ -0,0 +1,156 @@
+@@ -0,0 +1,162 @@
+package ca.spottedleaf.moonrise.patches.chunk_system;
+
+import ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor;
@@ -3194,22 +3194,28 @@ index 0000000000000000000000000000000000000000..532a8c15009a4514d2682f52129785fe
+ io.papermc.paper.chunk.system.ChunkSystem.onChunkHolderDelete(level, holder);
+ }
+
-+ public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
++ public static void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
+ ((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
+ .moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, chunk);
++ }
++
++ public static void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
+ // TODO move hook
+ io.papermc.paper.chunk.system.ChunkSystem.onChunkBorder(chunk, holder);
+ chunk.loadCallback(); // Paper
+ }
+
+ public static void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
-+ ((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
-+ .moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, null);
+ // TODO move hook
+ io.papermc.paper.chunk.system.ChunkSystem.onChunkNotBorder(chunk, holder);
+ chunk.unloadCallback(); // Paper
+ }
+
++ public static void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
++ ((ChunkSystemServerChunkCache)((ServerLevel)chunk.getLevel()).getChunkSource())
++ .moonrise$setFullChunk(chunk.getPos().x, chunk.getPos().z, null);
++ }
++
+ public static void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
+ // TODO move hook
+ io.papermc.paper.chunk.system.ChunkSystem.onChunkTicking(chunk, holder);
@@ -11034,10 +11040,10 @@ index 0000000000000000000000000000000000000000..faf76a5c2f9fa2eea38d2c7f2ab1c438
+}
diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java
new file mode 100644
-index 0000000000000000000000000000000000000000..05381b2c58c1b60f222eed672dddede047b663c5
+index 0000000000000000000000000000000000000000..d5fc5756ea960096ff23376a6b7ac68a2a462d22
--- /dev/null
+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/scheduling/NewChunkHolder.java
-@@ -0,0 +1,2032 @@
+@@ -0,0 +1,2034 @@
+package ca.spottedleaf.moonrise.patches.chunk_system.scheduling;
+
+import ca.spottedleaf.concurrentutil.completable.Completable;
@@ -12314,6 +12320,7 @@ index 0000000000000000000000000000000000000000..05381b2c58c1b60f222eed672dddede0
+ // state upgrade
+ if (!current.isOrAfter(FullChunkStatus.FULL) && pending.isOrAfter(FullChunkStatus.FULL)) {
+ this.updateCurrentState(FullChunkStatus.FULL);
++ ChunkSystem.onChunkPreBorder(chunk, this.vanillaChunkHolder);
+ this.scheduler.chunkHolderManager.ensureInAutosave(this);
+ this.changeEntityChunkStatus(FullChunkStatus.FULL);
+ ChunkSystem.onChunkBorder(chunk, this.vanillaChunkHolder);
@@ -12351,6 +12358,7 @@ index 0000000000000000000000000000000000000000..05381b2c58c1b60f222eed672dddede0
+ this.onFullChunkLoadChange(false, changedFullStatus);
+ this.changeEntityChunkStatus(FullChunkStatus.INACCESSIBLE);
+ ChunkSystem.onChunkNotBorder(chunk, this.vanillaChunkHolder);
++ ChunkSystem.onChunkPostNotBorder(chunk, this.vanillaChunkHolder);
+ this.updateCurrentState(FullChunkStatus.INACCESSIBLE);
+ }
+ }
@@ -21949,7 +21957,7 @@ index 0000000000000000000000000000000000000000..57692a503e147a00ac4e1586cd78e12b
+ private SaveUtil() {}
+}
diff --git a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
-index 0d0cb3e63acd5156b6f9d6d78cc949b0af36a77b..dd1649abe57d7191c15a9b2862d5fd11ff0706b1 100644
+index a79abe9b26f68d573812e91554124783075ae17a..183d99ec9b94ca20a823c46a2d6bf0a215046d48 100644
--- a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
+++ b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
@@ -25,6 +25,10 @@ import java.util.List;
@@ -21963,7 +21971,7 @@ index 0d0cb3e63acd5156b6f9d6d78cc949b0af36a77b..dd1649abe57d7191c15a9b2862d5fd11
public final class ChunkSystem {
private static final Logger LOGGER = LogUtils.getLogger();
-@@ -35,31 +39,17 @@ public final class ChunkSystem {
+@@ -35,35 +39,17 @@ public final class ChunkSystem {
}
public static void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
@@ -21985,12 +21993,16 @@ index 0d0cb3e63acd5156b6f9d6d78cc949b0af36a77b..dd1649abe57d7191c15a9b2862d5fd11
- }
- scheduleChunkLoad(level, chunkX, chunkZ, ChunkStatus.EMPTY, addTicket, priority, (final ChunkAccess chunk) -> {
- if (chunk == null) {
-- onComplete.accept(null);
+- if (onComplete != null) {
+- onComplete.accept(null);
+- }
- } else {
- if (chunk.getPersistedStatus().isOrAfter(toStatus)) {
- scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
- } else {
-- onComplete.accept(null);
+- if (onComplete != null) {
+- onComplete.accept(null);
+- }
- }
- }
- });
@@ -21998,7 +22010,7 @@ index 0d0cb3e63acd5156b6f9d6d78cc949b0af36a77b..dd1649abe57d7191c15a9b2862d5fd11
}
static final TicketType<Long> CHUNK_LOAD = TicketType.create("chunk_load", Long::compareTo);
-@@ -67,164 +57,29 @@ public final class ChunkSystem {
+@@ -71,160 +57,29 @@ public final class ChunkSystem {
private static long chunkLoadCounter = 0L;
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) {
@@ -22023,8 +22035,6 @@ index 0d0cb3e63acd5156b6f9d6d78cc949b0af36a77b..dd1649abe57d7191c15a9b2862d5fd11
- 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);
@@ -22092,8 +22102,6 @@ index 0d0cb3e63acd5156b6f9d6d78cc949b0af36a77b..dd1649abe57d7191c15a9b2862d5fd11
- 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);
@@ -22169,7 +22177,7 @@ index 0d0cb3e63acd5156b6f9d6d78cc949b0af36a77b..dd1649abe57d7191c15a9b2862d5fd11
}
public static boolean hasAnyChunkHolders(final ServerLevel level) {
-@@ -274,27 +129,19 @@ public final class ChunkSystem {
+@@ -268,27 +123,19 @@ public final class ChunkSystem {
}
public static ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
@@ -22571,7 +22579,7 @@ index c33f85b570f159ab465b5a10a8044a81f2797f43..244a19ecd0234fa1d7a6ecfea2075159
DedicatedServer dedicatedserver1 = new DedicatedServer(optionset, worldLoader.get(), thread, convertable_conversionsession, resourcepackrepository, worldstem, dedicatedserversettings, DataFixers.getDataFixer(), services, LoggerChunkProgressListener::createFromGameruleRadius);
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 6915522f669631779c1fb8a8e2db330f4b9fb921..3aecd55b9a069710c5d383b2f9113b147ad1ab57 100644
+index e14c0e1ccf526f81e28db5545d9e2351641e1bc8..3c230ae060998bfb79d5812fef21a80a9df8c8ff 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -198,7 +198,7 @@ import org.bukkit.event.server.ServerLoadEvent;
@@ -22699,7 +22707,7 @@ index 6915522f669631779c1fb8a8e2db330f4b9fb921..3aecd55b9a069710c5d383b2f9113b14
if (entity.isRemoved()) {
continue;
}
-@@ -2661,6 +2671,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2656,6 +2666,13 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
@@ -23253,7 +23261,7 @@ index d9ad32acdf46a43a649334a3b736aeb7b3af21d1..fae17a075d7efaf24d916877dd5968eb
public static final int RADIUS_AROUND_FULL_CHUNK = FULL_CHUNK_STEP.accumulatedDependencies().getRadius();
public static final int MAX_LEVEL = 33 + RADIUS_AROUND_FULL_CHUNK;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887b8fb816e 100644
+index a03385b1b0a2f9b98319137b87d917856d3c632c..1363dda031d1b541d76241812a957a12521cbc05 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
@@ -23294,9 +23302,9 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
// CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback()
public final CallbackExecutor callbackExecutor = new CallbackExecutor();
-@@ -223,23 +218,21 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
- }
-
+@@ -198,23 +193,21 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ // Paper end
+ // Paper start
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
@@ -23321,7 +23329,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
Path path = session.getDimensionPath(world.dimension());
this.storageName = path.getFileName().toString();
-@@ -270,15 +263,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -245,15 +238,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.chunkStatusListener = chunkStatusChangeListener;
ProcessorMailbox<Runnable> threadedmailbox1 = ProcessorMailbox.create(executor, "light");
@@ -23338,9 +23346,9 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
- this.worldGenContext = new WorldGenContext(world, chunkGenerator, structureTemplateManager, this.lightEngine, this.mainThreadMailbox);
+ this.worldGenContext = new WorldGenContext(world, chunkGenerator, structureTemplateManager, this.lightEngine, null); // Paper - rewrite chunk system
// Paper start
- 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);
-@@ -319,23 +310,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ this.nearbyPlayers = new io.papermc.paper.util.player.NearbyPlayers(this.level);
+ // Paper end
+@@ -292,23 +283,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
boolean isChunkTracked(ServerPlayer player, int chunkX, int chunkZ) {
@@ -23366,7 +23374,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
}
protected ThreadedLevelLightEngine getLightEngine() {
-@@ -344,20 +323,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -317,20 +296,22 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@Nullable
protected ChunkHolder getUpdatingChunkIfPresent(long pos) {
@@ -23396,7 +23404,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
}
public String getChunkDebugData(ChunkPos chunkPos) {
-@@ -386,55 +367,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -359,55 +340,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
private CompletableFuture<ChunkResult<List<ChunkAccess>>> getChunkRangeFuture(ChunkHolder centerChunk, int margin, IntFunction<ChunkStatus> distanceToStatus) {
@@ -23453,7 +23461,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
}
public ReportedException debugFuturesAndCreateReportedException(IllegalStateException exception, String details) {
-@@ -464,93 +397,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -437,93 +370,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public CompletableFuture<ChunkResult<LevelChunk>> prepareEntityTickingChunk(ChunkHolder holder) {
@@ -23553,7 +23561,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
}
protected void tick(BooleanSupplier shouldKeepTicking) {
-@@ -567,134 +430,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -540,134 +403,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public boolean hasWork() {
@@ -23694,7 +23702,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
}
private static boolean isChunkDataValid(CompoundTag nbt) {
-@@ -754,137 +508,44 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -727,137 +481,44 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@Override
public GenerationChunkHolder acquireGeneration(long pos) {
@@ -23841,7 +23849,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
}
public int getTickingGenerated() {
-@@ -892,135 +553,84 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -865,135 +526,84 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
private boolean saveChunkIfNeeded(ChunkHolder chunkHolder) {
@@ -24029,7 +24037,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
@Nullable
public LevelChunk getChunkToSend(long pos) {
ChunkHolder playerchunk = this.getVisibleChunkIfPresent(pos);
-@@ -1086,7 +696,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1059,7 +669,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
// CraftBukkit start
@@ -24038,7 +24046,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
return this.upgradeChunkTag(this.level.getTypeKey(), this.overworldDataStorage, nbttagcompound, this.generator().getTypeNameForDataFixer(), chunkcoordintpair, this.level);
// CraftBukkit end
}
-@@ -1180,7 +790,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1153,7 +763,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
player.setChunkTrackingView(ChunkTrackingView.EMPTY);
@@ -24047,7 +24055,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
this.addPlayerToDistanceMaps(player); // Paper - distance maps
} else {
SectionPos sectionposition = player.getLastSectionPos();
-@@ -1191,7 +801,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1164,7 +774,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
this.removePlayerFromDistanceMaps(player); // Paper - distance maps
@@ -24056,7 +24064,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
}
}
-@@ -1239,71 +849,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1212,71 +822,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.playerMap.unIgnorePlayer(player);
}
@@ -24139,7 +24147,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
}
public void addEntity(Entity entity) {
-@@ -1374,13 +944,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1347,13 +917,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
protected void tick() {
@@ -24154,7 +24162,7 @@ index 2ce7da9707d7c1a48b5609ae51a516d599d7aee8..b849e0cf15f894aa87b1bb397d85b887
List<ServerPlayer> list = Lists.newArrayList();
List<ServerPlayer> list1 = this.level.players();
-@@ -1487,27 +1051,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -1460,27 +1024,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
public void waitForLightBeforeSending(ChunkPos centerPos, int radius) {
@@ -24944,7 +24952,7 @@ index 3dc1daa3c6a04d3ff1a2353773b465fc380994a2..3575782f13a7f3c52e64dc5046803305
}
}
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c51f9a50d 100644
+index be9604a0f267558c95125852d86761a2f175732a..014e7d3c3b9e8f6c2b456d63bcf885f55b01ded9 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -46,7 +46,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
@@ -24956,12 +24964,11 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
public static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper
private static final List<ChunkStatus> CHUNK_STATUSES = ChunkStatus.getStatusList();
-@@ -75,6 +75,62 @@ public class ServerChunkCache extends ChunkSource {
+@@ -73,6 +73,61 @@ public class ServerChunkCache extends ChunkSource {
+ private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
long chunkFutureAwaitCounter;
- private final LevelChunk[] lastLoadedChunks = new LevelChunk[4 * 4];
// Paper end
+ // Paper start - rewrite chunk system
-+ private final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
+
+ @Override
+ public final void moonrise$setFullChunk(final int chunkX, final int chunkZ, final LevelChunk chunk) {
@@ -25019,7 +25026,22 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
public ServerChunkCache(ServerLevel world, LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, StructureTemplateManager structureTemplateManager, Executor workerExecutor, ChunkGenerator chunkGenerator, int viewDistance, int simulationDistance, boolean dsync, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier<DimensionDataStorage> persistentStateManagerFactory) {
this.level = world;
-@@ -248,63 +304,25 @@ public class ServerChunkCache extends ChunkSource {
+@@ -99,13 +154,7 @@ public class ServerChunkCache extends ChunkSource {
+ }
+ // CraftBukkit end
+ // Paper start
+- public void addLoadedChunk(LevelChunk chunk) {
+- this.fullChunks.put(chunk.coordinateKey, chunk);
+- }
+-
+- public void removeLoadedChunk(LevelChunk chunk) {
+- this.fullChunks.remove(chunk.coordinateKey);
+- }
++ // Paper - rewrite chunk system
+
+ @Nullable
+ public ChunkAccess getChunkAtImmediately(int x, int z) {
+@@ -176,63 +225,25 @@ public class ServerChunkCache extends ChunkSource {
@Nullable
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus leastStatus, boolean create) {
@@ -25035,13 +25057,13 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
- }
- // Paper end - Perf: Optimise getChunkAt calls for loaded chunks
- ProfilerFiller gameprofilerfiller = this.level.getProfiler();
--
-- gameprofilerfiller.incrementCounter("getChunk");
-- long k = ChunkPos.asLong(x, z);
+ // Paper start - rewrite chunk system
+ if (leastStatus == ChunkStatus.FULL) {
+ final LevelChunk ret = this.fullChunks.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(x, z));
+- gameprofilerfiller.incrementCounter("getChunk");
+- long k = ChunkPos.asLong(x, z);
+-
- for (int l = 0; l < 4; ++l) {
- if (k == this.lastChunkPos[l] && leastStatus == this.lastChunkStatus[l]) {
- ChunkAccess ichunkaccess = this.lastChunk[l];
@@ -25093,7 +25115,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
}
private void clearCache() {
-@@ -335,56 +353,59 @@ public class ServerChunkCache extends ChunkSource {
+@@ -263,56 +274,59 @@ public class ServerChunkCache extends ChunkSource {
}
private CompletableFuture<ChunkResult<ChunkAccess>> getChunkFutureMainThread(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) {
@@ -25110,7 +25132,14 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
- FullChunkStatus oldChunkState = ChunkLevel.fullStatus(playerchunk.oldTicketLevel);
- FullChunkStatus currentChunkState = ChunkLevel.fullStatus(playerchunk.getTicketLevel());
- currentlyUnloading = (oldChunkState.isOrAfter(FullChunkStatus.FULL) && !currentChunkState.isOrAfter(FullChunkStatus.FULL));
-- }
++ final int minLevel = ChunkLevel.byStatus(leastStatus);
++ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkX, chunkZ);
++
++ final boolean needsFullScheduling = leastStatus == ChunkStatus.FULL && (chunkHolder == null || !chunkHolder.getChunkStatus().isOrAfter(FullChunkStatus.FULL));
++
++ if ((chunkHolder == null || chunkHolder.getTicketLevel() > minLevel || needsFullScheduling) && !create) {
++ return ChunkHolder.UNLOADED_CHUNK_FUTURE;
+ }
- if (create && !currentlyUnloading) {
- // CraftBukkit end
- this.distanceManager.addTicket(TicketType.UNKNOWN, chunkcoordintpair, l, chunkcoordintpair);
@@ -25123,19 +25152,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
- gameprofilerfiller.pop();
- if (this.chunkAbsent(playerchunk, l)) {
- throw (IllegalStateException) Util.pauseInIde(new IllegalStateException("No chunk holder after ticket has been added"));
-- }
-- }
-+ final int minLevel = ChunkLevel.byStatus(leastStatus);
-+ final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkX, chunkZ);
+
-+ final boolean needsFullScheduling = leastStatus == ChunkStatus.FULL && (chunkHolder == null || !chunkHolder.getChunkStatus().isOrAfter(FullChunkStatus.FULL));
-+
-+ if ((chunkHolder == null || chunkHolder.getTicketLevel() > minLevel || needsFullScheduling) && !create) {
-+ return ChunkHolder.UNLOADED_CHUNK_FUTURE;
- }
-
-- return this.chunkAbsent(playerchunk, l) ? GenerationChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.scheduleChunkGenerationTask(leastStatus, this.chunkMap);
-- }
+ final ChunkAccess ifPresent = chunkHolder == null ? null : chunkHolder.getChunkIfPresent(leastStatus);
+ if (needsFullScheduling || ifPresent == null) {
+ // schedule
@@ -25145,17 +25162,21 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
+ ret.complete(ChunkHolder.UNLOADED_CHUNK);
+ } else {
+ ret.complete(ChunkResult.of(chunk));
-+ }
+ }
+- }
+- }
+ };
-- private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) {
-- return holder == null || holder.oldTicketLevel > maxLevel; // CraftBukkit using oldTicketLevel for isLoaded checks
+- return this.chunkAbsent(playerchunk, l) ? GenerationChunkHolder.UNLOADED_CHUNK_FUTURE : playerchunk.scheduleChunkGenerationTask(leastStatus, this.chunkMap);
+- }
+ ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().scheduleChunkLoad(
+ chunkX, chunkZ, leastStatus, true,
+ ca.spottedleaf.concurrentutil.executor.standard.PrioritisedExecutor.Priority.HIGHER,
+ complete
+ );
-+
+
+- private boolean chunkAbsent(@Nullable ChunkHolder holder, int maxLevel) {
+- return holder == null || holder.oldTicketLevel > maxLevel; // CraftBukkit using oldTicketLevel for isLoaded checks
+ return ret;
+ } else {
+ // can return now
@@ -25190,7 +25211,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
}
@Override
-@@ -397,16 +418,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -325,16 +339,7 @@ public class ServerChunkCache extends ChunkSource {
}
public boolean runDistanceManagerUpdates() { // Paper - public
@@ -25208,7 +25229,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
}
// Paper start
-@@ -416,13 +428,14 @@ public class ServerChunkCache extends ChunkSource {
+@@ -344,13 +349,14 @@ public class ServerChunkCache extends ChunkSource {
// Paper end
public boolean isPositionTicking(long pos) {
@@ -25227,7 +25248,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
try (co.aikar.timings.Timing timed = level.timings.chunkSaveData.startTiming()) { // Paper - Timings
this.chunkMap.saveAllChunks(flush);
} // Paper - Timings
-@@ -435,12 +448,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -363,12 +369,7 @@ public class ServerChunkCache extends ChunkSource {
}
public void close(boolean save) throws IOException {
@@ -25241,7 +25262,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
}
// CraftBukkit start - modelled on below
-@@ -468,6 +476,7 @@ public class ServerChunkCache extends ChunkSource {
+@@ -396,6 +397,7 @@ public class ServerChunkCache extends ChunkSource {
this.level.getProfiler().popPush("chunks");
if (tickChunks) {
this.level.timings.chunks.startTiming(); // Paper - timings
@@ -25249,7 +25270,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
this.tickChunks();
this.level.timings.chunks.stopTiming(); // Paper - timings
this.chunkMap.tick();
-@@ -567,11 +576,12 @@ public class ServerChunkCache extends ChunkSource {
+@@ -495,11 +497,12 @@ public class ServerChunkCache extends ChunkSource {
}
private void getFullChunk(long pos, Consumer<LevelChunk> chunkConsumer) {
@@ -25266,7 +25287,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
}
-@@ -665,6 +675,12 @@ public class ServerChunkCache extends ChunkSource {
+@@ -593,6 +596,12 @@ public class ServerChunkCache extends ChunkSource {
this.chunkMap.setServerViewDistance(watchDistance);
}
@@ -25279,7 +25300,7 @@ index a94833f58eb823332890d07c147161e9e0a938e9..cba80945fa0d8ca55b0d422925f8c94c
public void setSimulationDistance(int simulationDistance) {
this.distanceManager.updateSimulationDistance(simulationDistance);
}
-@@ -743,16 +759,14 @@ public class ServerChunkCache extends ChunkSource {
+@@ -671,16 +680,14 @@ public class ServerChunkCache extends ChunkSource {
@Override
// CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task
public boolean pollTask() {
@@ -26763,7 +26784,7 @@ index bd20bea7f76a7307f1698fb2dfef37125032d166..70c2017400168d4fef3c14462798edcf
if (shape.isEmpty()) {
return true;
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 6f822e9487bef5b9766d5ae86ebbd687e4eadc42..77c6613c39e3b266944e28cf2627483d9f32c511 100644
+index e27d3547d1e19c137e05e6b8d075127a8bafb237..557273061fa03ebaa4b9de01ad12ed4ac859c292 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -102,7 +102,7 @@ import org.bukkit.entity.SpawnCategory;
@@ -26842,7 +26863,7 @@ index 6f822e9487bef5b9766d5ae86ebbd687e4eadc42..77c6613c39e3b266944e28cf2627483d
}
// Paper start - Cancel hit for vanished players
-@@ -551,7 +604,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -549,7 +602,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
this.setBlocksDirty(blockposition, iblockdata1, iblockdata2);
}
@@ -26851,7 +26872,7 @@ index 6f822e9487bef5b9766d5ae86ebbd687e4eadc42..77c6613c39e3b266944e28cf2627483d
this.sendBlockUpdated(blockposition, iblockdata1, iblockdata, i);
}
-@@ -951,7 +1004,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -949,7 +1002,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
}
// Paper end - Perf: Optimize capturedTileEntities lookup
// CraftBukkit end
@@ -26860,7 +26881,7 @@ index 6f822e9487bef5b9766d5ae86ebbd687e4eadc42..77c6613c39e3b266944e28cf2627483d
}
public void setBlockEntity(BlockEntity blockEntity) {
-@@ -1041,28 +1094,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -1039,28 +1092,13 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@Override
public List<Entity> getEntities(@Nullable Entity except, AABB box, Predicate<? super Entity> predicate) {
this.getProfiler().incrementCounter("getEntities");
@@ -26894,7 +26915,7 @@ index 6f822e9487bef5b9766d5ae86ebbd687e4eadc42..77c6613c39e3b266944e28cf2627483d
}
@Override
-@@ -1077,36 +1115,77 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
+@@ -1075,36 +1113,77 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
this.getEntities(filter, box, predicate, result, Integer.MAX_VALUE);
}
@@ -27315,7 +27336,7 @@ index 365074be989aa4a178114fd5e9810f1a68640196..4af698930712389881601069a921f054
@Override
public BlockEntity getBlockEntity(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 6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e..c4f87ff9e0c9806a1d7abc4842caa5a4caa357bd 100644
+index 602ad80c2b93d320bf2a25832d25a58cb8c72e4b..443e5e1b1c0e7c93f61c1905c78c29a17860989c 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -53,7 +53,7 @@ import net.minecraft.world.ticks.LevelChunkTicks;
@@ -27327,9 +27348,9 @@ index 6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e..c4f87ff9e0c9806a1d7abc4842caa5a4
static final Logger LOGGER = LogUtils.getLogger();
private static final TickingBlockEntity NULL_TICKER = new TickingBlockEntity() {
-@@ -218,6 +218,14 @@ public class LevelChunk extends ChunkAccess {
- }
- }
+@@ -119,6 +119,14 @@ public class LevelChunk extends ChunkAccess {
+ // Paper start
+ boolean loadedTicketLevel;
// Paper end
+ // Paper start - rewrite chunk system
+ private boolean postProcessingDone;
@@ -27342,7 +27363,7 @@ index 6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e..c4f87ff9e0c9806a1d7abc4842caa5a4
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());
-@@ -247,13 +255,19 @@ public class LevelChunk extends ChunkAccess {
+@@ -148,13 +156,19 @@ public class LevelChunk extends ChunkAccess {
}
}
@@ -27363,7 +27384,7 @@ index 6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e..c4f87ff9e0c9806a1d7abc4842caa5a4
}
@Override
-@@ -436,7 +450,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -337,7 +351,7 @@ public class LevelChunk extends ChunkAccess {
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
gameprofilerfiller.push("updateSkyLightSources");
@@ -27372,15 +27393,21 @@ index 6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e..c4f87ff9e0c9806a1d7abc4842caa5a4
gameprofilerfiller.popPush("queueCheckLight");
this.level.getChunkSource().getLightEngine().checkBlock(blockposition);
gameprofilerfiller.pop();
-@@ -696,6 +710,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -597,11 +611,12 @@ public class LevelChunk extends ChunkAccess {
// 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;
-@@ -723,6 +738,7 @@ public class LevelChunk extends ChunkAccess {
+ // Paper start
+ this.loadedTicketLevel = true;
+ // Paper end
+ org.bukkit.Server server = this.level.getCraftServer();
+- this.level.getChunkSource().addLoadedChunk(this); // Paper
++ // Paper - rewrite chunk system
+ if (server != null) {
+ /*
+ * If it's a new world, the first few chunks are generated inside
+@@ -610,6 +625,7 @@ public class LevelChunk extends ChunkAccess {
*/
org.bukkit.Chunk bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this);
server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(bukkitChunk, this.needsDecoration));
@@ -27388,7 +27415,7 @@ index 6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e..c4f87ff9e0c9806a1d7abc4842caa5a4
if (this.needsDecoration) {
try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper
-@@ -751,9 +767,11 @@ public class LevelChunk extends ChunkAccess {
+@@ -638,13 +654,15 @@ public class LevelChunk extends ChunkAccess {
}
public void unloadCallback() {
@@ -27401,7 +27428,12 @@ index 6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e..c4f87ff9e0c9806a1d7abc4842caa5a4
server.getPluginManager().callEvent(unloadEvent);
// note: saving can be prevented, but not forced if no saving is actually required
this.mustNotSave = !unloadEvent.isSaveChunk();
-@@ -777,8 +795,27 @@ public class LevelChunk extends ChunkAccess {
+- this.level.getChunkSource().removeLoadedChunk(this); // Paper
++ // Paper - rewrite chunk system
+ // Paper start
+ this.loadedTicketLevel = false;
+ // Paper end
+@@ -652,8 +670,27 @@ public class LevelChunk extends ChunkAccess {
@Override
public boolean isUnsaved() {
@@ -27430,7 +27462,7 @@ index 6c0e12c9c9c0fb8377cd1f48a43ca75c9fc3e58e..c4f87ff9e0c9806a1d7abc4842caa5a4
// CraftBukkit end
public boolean isEmpty() {
-@@ -884,6 +921,7 @@ public class LevelChunk extends ChunkAccess {
+@@ -759,6 +796,7 @@ public class LevelChunk extends ChunkAccess {
this.pendingBlockEntities.clear();
this.upgradeData.upgrade(this);
@@ -29064,29 +29096,12 @@ index 5717c0e1d6df07a4613356dc78d970d2101c68d7..cab7ca4218e5903b6a5e518af55457b9
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
-index cd7f1309cf01a5f01a28aded03a36fe15adb1756..43cb7b91945a72ab4bd998a3a3eca3cdcf0432a9 100644
+index fceed3d08ee6f4c171685986bb19d2be592eedc6..bf18f9ad7dec2b09ebfcb5ec6566f2556e842f22 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
-@@ -815,19 +815,26 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel {
- @Nullable
- @Override
- public BlockState getBlockStateIfLoaded(final BlockPos blockposition) {
-- return null;
-+ return this.handle.getBlockStateIfLoaded(blockposition); // Paper - rewrite chunk system
- }
-
- @Nullable
- @Override
- public FluidState getFluidIfLoaded(final BlockPos blockposition) {
-- return null;
-+ return this.handle.getFluidIfLoaded(blockposition); // Paper - rewrite chunk system
- }
-
- @Nullable
- @Override
+@@ -829,5 +829,12 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel {
public ChunkAccess getChunkIfLoadedImmediately(final int x, final int z) {
-- return null;
-+ return this.handle.getChunkIfLoadedImmediately(x, z); // Paper - rewrite chunk system
+ return this.handle.getChunkIfLoadedImmediately(x, z);
}
+
+ // Paper start - rewrite chunk system
diff --git a/patches/server/0994-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch b/patches/server/0994-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
index 2cdcbebb6f..fc25a541d6 100644
--- a/patches/server/0994-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
+++ b/patches/server/0994-Optimize-isInWorldBounds-and-getBlockState-for-inlin.patch
@@ -29,7 +29,7 @@ index 02367ef1371dde94ff6c4cd40bd32e800d6ccaaf..7b0fc7135bc107103dcaed6dc0707b18
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 77c6613c39e3b266944e28cf2627483d9f32c511..b3a433786fabf6f2cfba2cdc8d21f6447191a310 100644
+index 557273061fa03ebaa4b9de01ad12ed4ac859c292..316a72f5019d4ad65237b41ccb4ef3be729246ad 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -394,7 +394,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
diff --git a/patches/server/0996-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch b/patches/server/0996-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch
index 52c1df5a4d..4ef94ee4ce 100644
--- a/patches/server/0996-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch
+++ b/patches/server/0996-Strip-raytracing-for-EntityLiving-hasLineOfSight.patch
@@ -62,7 +62,7 @@ index bb8e962e63c7a2d931f9bd7f7c002aa35cfa5fd3..0fa131a6c98adb498fc8d534e0e39647
default BlockHitResult clip(ClipContext raytrace1, BlockPos blockposition) {
// Paper start - Add predicate for blocks when raytracing
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index b3a433786fabf6f2cfba2cdc8d21f6447191a310..2d318964ef1f54940059b8d62bfc8f8ae87424e5 100644
+index 316a72f5019d4ad65237b41ccb4ef3be729246ad..5ea8f51accdda2387b640d2cff1d6a8baa673bee 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -386,10 +386,87 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
diff --git a/patches/server/1000-Entity-Activation-Range-2.0.patch b/patches/server/1000-Entity-Activation-Range-2.0.patch
index 84b1883a6e..dcb2dabb14 100644
--- a/patches/server/1000-Entity-Activation-Range-2.0.patch
+++ b/patches/server/1000-Entity-Activation-Range-2.0.patch
@@ -340,7 +340,7 @@ index 0b7f52021441d633c37543e8ae485e81c292b747..d7f8464bf3eed0e42a5fc7f14a5b243d
+
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 2d318964ef1f54940059b8d62bfc8f8ae87424e5..23af11c22713ac3e005fd89b4f3b3873bf74e751 100644
+index 5ea8f51accdda2387b640d2cff1d6a8baa673bee..3fa8ae3a9afd81bf757ee1e183684442444376f4 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -156,6 +156,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
diff --git a/patches/server/1001-Optional-per-player-mob-spawns.patch b/patches/server/1001-Optional-per-player-mob-spawns.patch
index f9d00cb631..8905e1ab1c 100644
--- a/patches/server/1001-Optional-per-player-mob-spawns.patch
+++ b/patches/server/1001-Optional-per-player-mob-spawns.patch
@@ -5,10 +5,10 @@ Subject: [PATCH] Optional per player mob spawns
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index b849e0cf15f894aa87b1bb397d85b887b8fb816e..7bebf252887ecc7594b1ce21471fb6ba7aa2c051 100644
+index 1363dda031d1b541d76241812a957a12521cbc05..b24d5b0818abfc8b969ab715ca21959c235a9d70 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -283,8 +283,26 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -256,8 +256,26 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
return this.nearbyPlayers;
}
@@ -37,10 +37,10 @@ index b849e0cf15f894aa87b1bb397d85b887b8fb816e..7bebf252887ecc7594b1ce21471fb6ba
// Paper end
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index cba80945fa0d8ca55b0d422925f8c94c51f9a50d..a5d465a81ba6ba7dea352005bf02ae2ae424ed14 100644
+index 014e7d3c3b9e8f6c2b456d63bcf885f55b01ded9..7bb3277883a326c436ef070eaf285343fe502a32 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -517,7 +517,19 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
+@@ -438,7 +438,19 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
gameprofilerfiller.popPush("naturalSpawnCount");
this.level.timings.countNaturalMobs.startTiming(); // Paper - timings
int k = this.distanceManager.getNaturalSpawnChunkCount();
diff --git a/patches/server/1002-Anti-Xray.patch b/patches/server/1002-Anti-Xray.patch
index 56181f6189..249d9e10fd 100644
--- a/patches/server/1002-Anti-Xray.patch
+++ b/patches/server/1002-Anti-Xray.patch
@@ -1168,7 +1168,7 @@ index 9b1a6d8351fb473eec75a2fd08fb892b770e3586..0d0b07c9199be9ca0d5ac3feb1d44f14
}
// Paper end - Send empty chunk
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 23af11c22713ac3e005fd89b4f3b3873bf74e751..9f2fad2f4b1e4e7eda645d53eb76ed04fc0b3451 100644
+index 3fa8ae3a9afd81bf757ee1e183684442444376f4..392032d749b8a9077b61f4e1c5e413de048c7067 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -171,6 +171,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
@@ -1196,7 +1196,7 @@ index 23af11c22713ac3e005fd89b4f3b3873bf74e751..9f2fad2f4b1e4e7eda645d53eb76ed04
}
// Paper start - Cancel hit for vanished players
-@@ -619,6 +621,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
+@@ -617,6 +619,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
// CraftBukkit end
BlockState iblockdata1 = chunk.setBlockState(pos, state, (flags & 64) != 0, (flags & 1024) == 0); // CraftBukkit custom NO_PLACE flag
@@ -1231,7 +1231,7 @@ index 2822a9b010e6d45f9562950a94f1942784db9784..97f8ef86a0e398b7e4aa3445d5e413ad
}
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 c4f87ff9e0c9806a1d7abc4842caa5a4caa357bd..e5f301f867057ac986725a76c13f260ff40489c4 100644
+index 443e5e1b1c0e7c93f61c1905c78c29a17860989c..b2a06fc1192cd6050d6d7ea8620a4fa5a12182cc 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -91,7 +91,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
diff --git a/patches/server/1004-Add-Alternate-Current-redstone-implementation.patch b/patches/server/1004-Add-Alternate-Current-redstone-implementation.patch
index a5295f6b05..29b12d1dfb 100644
--- a/patches/server/1004-Add-Alternate-Current-redstone-implementation.patch
+++ b/patches/server/1004-Add-Alternate-Current-redstone-implementation.patch
@@ -2035,10 +2035,10 @@ index a463cdd3b9dbda64d52c86223e1f2e1164deac80..798016774df02c3f7ebf909c9cc125f8
EntityCallbacks() {}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 9f2fad2f4b1e4e7eda645d53eb76ed04fc0b3451..96ad3868a93964247790134fa5f4b18e5c07aea8 100644
+index 392032d749b8a9077b61f4e1c5e413de048c7067..55e841269585feb6a083b48ffeecea30cc65f6d6 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -1570,4 +1570,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
+@@ -1568,4 +1568,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
}
}
// Paper end - notify observers even if grow failed
diff --git a/patches/server/1005-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch b/patches/server/1005-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch
index ab570def0a..3e51783f35 100644
--- a/patches/server/1005-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch
+++ b/patches/server/1005-Improve-cancelling-PreCreatureSpawnEvent-with-per-pl.patch
@@ -6,10 +6,10 @@ Subject: [PATCH] Improve cancelling PreCreatureSpawnEvent with per player mob
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
-index 7bebf252887ecc7594b1ce21471fb6ba7aa2c051..df00ea382915480be1279a5347872cf7a1417341 100644
+index b24d5b0818abfc8b969ab715ca21959c235a9d70..c96740a82eac9101f74edeb44edf4b64d1d633e0 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
-@@ -300,8 +300,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+@@ -273,8 +273,25 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
++((ServerPlayer)backingSet[i]).mobCounts[index];
}
}
@@ -37,10 +37,10 @@ index 7bebf252887ecc7594b1ce21471fb6ba7aa2c051..df00ea382915480be1279a5347872cf7
}
// Paper end
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-index a5d465a81ba6ba7dea352005bf02ae2ae424ed14..681fdab250d924a29ca160acffbcbf7f8a3ca78a 100644
+index 7bb3277883a326c436ef070eaf285343fe502a32..82e7f7c3c2f51bc135585f43bc5167bcde2f8a98 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
-@@ -523,7 +523,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
+@@ -444,7 +444,17 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled
// re-set mob counts
for (ServerPlayer player : this.level.players) {
diff --git a/patches/server/1008-Optimize-Hoppers.patch b/patches/server/1008-Optimize-Hoppers.patch
index 28b2e6b92d..aa1c0074e6 100644
--- a/patches/server/1008-Optimize-Hoppers.patch
+++ b/patches/server/1008-Optimize-Hoppers.patch
@@ -50,7 +50,7 @@ index 0000000000000000000000000000000000000000..5c42823726e70ce6c9d0121d07431548
+ }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 3aecd55b9a069710c5d383b2f9113b147ad1ab57..2a65c39feeb03a165ccc4afd974fb6935b2ff546 100644
+index 3c230ae060998bfb79d5812fef21a80a9df8c8ff..64d0e04b6f9c52d90d0bc185b16a8a4768af4ac1 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1659,6 +1659,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
diff --git a/patches/server/1023-Improved-Watchdog-Support.patch b/patches/server/1023-Improved-Watchdog-Support.patch
index 9c83e55b86..94bf0b6858 100644
--- a/patches/server/1023-Improved-Watchdog-Support.patch
+++ b/patches/server/1023-Improved-Watchdog-Support.patch
@@ -71,7 +71,7 @@ index 589a8bf75be6ccc59f1e5dd5d8d9afed41c4772d..b24265573fdef5d9a964bcd76146f345
cause = cause.getCause();
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index 2a65c39feeb03a165ccc4afd974fb6935b2ff546..b9b8a46f1de9867cc2161ac71c2993ab422cdfbf 100644
+index 64d0e04b6f9c52d90d0bc185b16a8a4768af4ac1..68f60e77e0bfd42b6419491c1d59b6432974216b 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -307,7 +307,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -212,7 +212,7 @@ index 2a65c39feeb03a165ccc4afd974fb6935b2ff546..b9b8a46f1de9867cc2161ac71c2993ab
return new TickTask(this.tickCount, runnable);
}
-@@ -2209,7 +2261,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
+@@ -2204,7 +2256,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.worldData.setDataConfiguration(worlddataconfiguration);
this.resources.managers.updateRegistryTags();
this.potionBrewing = this.potionBrewing.reload(this.worldData.enabledFeatures()); // Paper - Custom Potion Mixes
@@ -287,10 +287,10 @@ index 2510589400b3012b827efcab477c6483d9d55901..43487a9ee202c5b0e5a416519939111f
}
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
-index 96ad3868a93964247790134fa5f4b18e5c07aea8..f1e085c712e4f128c813c3cf5340e6d2e4e55ccc 100644
+index 55e841269585feb6a083b48ffeecea30cc65f6d6..fee155c81df385faa474e3aec777a30375ecb07d 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
-@@ -984,6 +984,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
+@@ -982,6 +982,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
try {
tickConsumer.accept(entity);
} catch (Throwable throwable) {
@@ -299,10 +299,10 @@ index 96ad3868a93964247790134fa5f4b18e5c07aea8..f1e085c712e4f128c813c3cf5340e6d2
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 e5f301f867057ac986725a76c13f260ff40489c4..589c1f13006660c28378f447ee373651257169d9 100644
+index b2a06fc1192cd6050d6d7ea8620a4fa5a12182cc..d388fbcbff63928f0e9140c02400a63ba8f19d9c 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
-@@ -1131,6 +1131,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
+@@ -1006,6 +1006,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
gameprofilerfiller.pop();
} catch (Throwable throwable) {