--- a/net/minecraft/server/level/DistanceManager.java
+++ b/net/minecraft/server/level/DistanceManager.java
@@ -30,30 +_,63 @@
 import net.minecraft.world.level.chunk.LevelChunk;
 import org.slf4j.Logger;
 
-public abstract class DistanceManager {
+public abstract class DistanceManager implements ca.spottedleaf.moonrise.patches.chunk_system.level.chunk.ChunkSystemDistanceManager, ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickDistanceManager { // Paper - rewrite chunk system // Paper - chunk tick iteration optimisation
     private static final Logger LOGGER = LogUtils.getLogger();
     static final int PLAYER_TICKET_LEVEL = ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING);
     final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap<>();
-    private final LoadingChunkTracker loadingChunkTracker;
-    private final SimulationChunkTracker simulationChunkTracker;
-    final TicketStorage ticketStorage;
-    private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
-    private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(32);
-    protected final Set<ChunkHolder> chunksToUpdateFutures = new ReferenceOpenHashSet<>();
-    final ThrottlingChunkTaskDispatcher ticketDispatcher;
-    final LongSet ticketsToRelease = new LongOpenHashSet();
-    final Executor mainThreadExecutor;
-    private int simulationDistance = 10;
+    // Paper - rewrite chunk system
+    public final TicketStorage ticketStorage;
+    // Paper - chunk tick iteration optimisation
+    // Paper - rewrite chunk system
 
     protected DistanceManager(TicketStorage ticketStorage, Executor dispatcher, Executor mainThreadExecutor) {
         this.ticketStorage = ticketStorage;
-        this.loadingChunkTracker = new LoadingChunkTracker(this, ticketStorage);
-        this.simulationChunkTracker = new SimulationChunkTracker(ticketStorage);
+        // Paper - rewrite chunk system
         TaskScheduler<Runnable> taskScheduler = TaskScheduler.wrapExecutor("player ticket throttler", mainThreadExecutor);
-        this.ticketDispatcher = new ThrottlingChunkTaskDispatcher(taskScheduler, dispatcher, 4);
-        this.mainThreadExecutor = mainThreadExecutor;
-    }
-
+        this.ticketStorage.moonrise$setChunkMap(this.moonrise$getChunkMap()); // Paper - rewrite chunk system
+    }
+
+    // Paper start - rewrite chunk system
+    @Override
+    public final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager moonrise$getChunkHolderManager() {
+        return ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getChunkTaskScheduler().chunkHolderManager;
+    }
+    // Paper end - rewrite chunk system
+    // Paper start - chunk tick iteration optimisation
+    private final ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap<ServerPlayer> spawnChunkTracker = new ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap<>();
+    // Note: Cannot do narrow tracking on Paper due to custom spawn range
+
+    @Override
+    public final void moonrise$addPlayer(final ServerPlayer player, final SectionPos pos) {
+        this.spawnChunkTracker.add(player, pos.x(), pos.z(), ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE);
+        // Note: Cannot do narrow tracking on Paper due to custom spawn range
+    }
+
+    @Override
+    public final void moonrise$removePlayer(final ServerPlayer player, final SectionPos pos) {
+        this.spawnChunkTracker.remove(player);
+        // Note: Cannot do narrow tracking on Paper due to custom spawn range
+    }
+
+    @Override
+    public final void moonrise$updatePlayer(final ServerPlayer player,
+                                            final SectionPos oldPos, final SectionPos newPos,
+                                            final boolean oldIgnore, final boolean newIgnore) {
+        if (newIgnore) {
+            this.spawnChunkTracker.remove(player);
+            // Note: Cannot do narrow tracking on Paper due to custom spawn range
+        } else {
+            this.spawnChunkTracker.addOrUpdate(player, newPos.x(), newPos.z(), ca.spottedleaf.moonrise.patches.chunk_tick_iteration.ChunkTickConstants.PLAYER_SPAWN_TRACK_RANGE);
+            // Note: Cannot do narrow tracking on Paper due to custom spawn range
+        }
+    }
+
+    @Override
+    public final boolean moonrise$hasAnyNearbyNarrow(final int chunkX, final int chunkZ) {
+        throw new UnsupportedOperationException(); // Note: Cannot do narrow tracking on Paper due to custom spawn range
+    }
+
+    // Paper end - chunk tick iteration optimisation
     protected abstract boolean isChunkToRemove(long chunkPos);
 
     @Nullable
@@ -63,132 +_,99 @@
     protected abstract ChunkHolder updateChunkScheduling(long chunkPos, int i, @Nullable ChunkHolder newLevel, int holder);
 
     public boolean runAllUpdates(ChunkMap chunkMap) {
-        this.naturalSpawnChunkCounter.runAllUpdates();
-        this.simulationChunkTracker.runAllUpdates();
-        this.playerTicketManager.runAllUpdates();
-        int i = Integer.MAX_VALUE - this.loadingChunkTracker.runDistanceUpdates(Integer.MAX_VALUE);
-        boolean flag = i != 0;
-        if (flag) {
-        }
-
-        if (!this.chunksToUpdateFutures.isEmpty()) {
-            for (ChunkHolder chunkHolder : this.chunksToUpdateFutures) {
-                chunkHolder.updateHighestAllowedStatus(chunkMap);
-            }
-
-            for (ChunkHolder chunkHolder : this.chunksToUpdateFutures) {
-                chunkHolder.updateFutures(chunkMap, this.mainThreadExecutor);
-            }
-
-            this.chunksToUpdateFutures.clear();
-            return true;
-        } else {
-            if (!this.ticketsToRelease.isEmpty()) {
-                LongIterator longIterator = this.ticketsToRelease.iterator();
-
-                while (longIterator.hasNext()) {
-                    long l = longIterator.nextLong();
-                    if (this.ticketStorage.getTickets(l).stream().anyMatch(ticket -> ticket.getType() == TicketType.PLAYER_LOADING)) {
-                        ChunkHolder updatingChunkIfPresent = chunkMap.getUpdatingChunkIfPresent(l);
-                        if (updatingChunkIfPresent == null) {
-                            throw new IllegalStateException();
-                        }
-
-                        CompletableFuture<ChunkResult<LevelChunk>> entityTickingChunkFuture = updatingChunkIfPresent.getEntityTickingChunkFuture();
-                        entityTickingChunkFuture.thenAccept(
-                            chunkResult -> this.mainThreadExecutor.execute(() -> this.ticketDispatcher.release(l, () -> {}, false))
-                        );
-                    }
-                }
-
-                this.ticketsToRelease.clear();
-            }
-
-            return flag;
-        }
+        return this.moonrise$getChunkHolderManager().processTicketUpdates(); // Paper - rewrite chunk system
     }
 
     public void addPlayer(SectionPos sectionPos, ServerPlayer player) {
         ChunkPos chunkPos = sectionPos.chunk();
         long packedChunkPos = chunkPos.toLong();
         this.playersPerChunk.computeIfAbsent(packedChunkPos, l -> new ObjectOpenHashSet<>()).add(player);
-        this.naturalSpawnChunkCounter.update(packedChunkPos, 0, true);
-        this.playerTicketManager.update(packedChunkPos, 0, true);
-        this.ticketStorage.addTicket(new Ticket(TicketType.PLAYER_SIMULATION, this.getPlayerTicketLevel()), chunkPos);
+        // Paper - chunk tick iteration optimisation
+        // Paper - rewrite chunk system
     }
 
     public void removePlayer(SectionPos sectionPos, ServerPlayer player) {
         ChunkPos chunkPos = sectionPos.chunk();
         long packedChunkPos = chunkPos.toLong();
         ObjectSet<ServerPlayer> set = this.playersPerChunk.get(packedChunkPos);
-        set.remove(player);
-        if (set.isEmpty()) {
+        // Paper start - some state corruption happens here, don't crash, clean up gracefully
+        if (set != null) set.remove(player);
+        if (set == null || set.isEmpty()) {
+        // Paper end - some state corruption happens here, don't crash, clean up gracefully
             this.playersPerChunk.remove(packedChunkPos);
-            this.naturalSpawnChunkCounter.update(packedChunkPos, Integer.MAX_VALUE, false);
-            this.playerTicketManager.update(packedChunkPos, Integer.MAX_VALUE, false);
-            this.ticketStorage.removeTicket(new Ticket(TicketType.PLAYER_SIMULATION, this.getPlayerTicketLevel()), chunkPos);
+            // Paper - chunk tick iteration optimisation
+            // Paper - rewrite chunk system
         }
     }
 
     private int getPlayerTicketLevel() {
-        return Math.max(0, ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING) - this.simulationDistance);
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
     }
 
     public boolean inEntityTickingRange(long chunkPos) {
-        return ChunkLevel.isEntityTicking(this.simulationChunkTracker.getLevel(chunkPos));
+        // Paper start - rewrite chunk system
+        final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos);
+        return chunkHolder != null && chunkHolder.isEntityTickingReady();
+        // Paper end - rewrite chunk system
     }
 
     public boolean inBlockTickingRange(long chunkPos) {
-        return ChunkLevel.isBlockTicking(this.simulationChunkTracker.getLevel(chunkPos));
+        // Paper start - rewrite chunk system
+        final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos);
+        return chunkHolder != null && chunkHolder.isTickingReady();
+        // Paper end - rewrite chunk system
     }
 
     public int getChunkLevel(long chunkPos, boolean simulate) {
-        return simulate ? this.simulationChunkTracker.getLevel(chunkPos) : this.loadingChunkTracker.getLevel(chunkPos);
+        // Paper start - rewrite chunk system
+        final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder chunkHolder = this.moonrise$getChunkHolderManager().getChunkHolder(chunkPos);
+        return chunkHolder == null ? ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager.MAX_TICKET_LEVEL + 1 : chunkHolder.getTicketLevel();
+        // Paper end - rewrite chunk system
     }
 
     protected void updatePlayerTickets(int viewDistance) {
-        this.playerTicketManager.updateViewDistance(viewDistance);
+        this.moonrise$getChunkMap().setServerViewDistance(viewDistance); // Paper - rewrite chunk system
     }
 
     public void updateSimulationDistance(int simulationDistance) {
-        if (simulationDistance != this.simulationDistance) {
-            this.simulationDistance = simulationDistance;
-            this.ticketStorage.replaceTicketLevelOfType(this.getPlayerTicketLevel(), TicketType.PLAYER_SIMULATION);
-        }
+        // Paper start - rewrite chunk system
+        // note: vanilla does not clamp to 0, but we do simply because we need a min of 0
+        final int clamped = net.minecraft.util.Mth.clamp(simulationDistance, 0, ca.spottedleaf.moonrise.common.util.MoonriseConstants.MAX_VIEW_DISTANCE);
+
+        ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getPlayerChunkLoader().setTickDistance(clamped);
+        // Paper end - rewrite chunk system
     }
 
     public int getNaturalSpawnChunkCount() {
-        this.naturalSpawnChunkCounter.runAllUpdates();
-        return this.naturalSpawnChunkCounter.chunks.size();
+        return this.spawnChunkTracker.getTotalPositions(); // Paper - chunk tick iteration optimisation
     }
 
     public TriState hasPlayersNearby(long chunkPos) {
-        this.naturalSpawnChunkCounter.runAllUpdates();
-        int level = this.naturalSpawnChunkCounter.getLevel(chunkPos);
-        if (level <= NaturalSpawner.INSCRIBED_SQUARE_SPAWN_DISTANCE_CHUNK) {
-            return TriState.TRUE;
-        } else {
-            return level > 8 ? TriState.FALSE : TriState.DEFAULT;
-        }
+        // Note: Cannot do narrow tracking on Paper due to custom spawn range // Paper - chunk tick iteration optimisation
+        return this.spawnChunkTracker.hasObjectsNear(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(chunkPos), ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(chunkPos)) ? net.minecraft.util.TriState.DEFAULT : net.minecraft.util.TriState.FALSE; // Paper - chunk tick iteration optimisation
     }
 
     public void forEachEntityTickingChunk(LongConsumer action) {
-        for (Entry entry : Long2ByteMaps.fastIterable(this.simulationChunkTracker.chunks)) {
-            byte byteValue = entry.getByteValue();
-            long longKey = entry.getLongKey();
-            if (ChunkLevel.isEntityTicking(byteValue)) {
-                action.accept(longKey);
-            }
+        // Paper start - rewrite chunk system
+        final ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.world.level.chunk.LevelChunk> chunks = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.moonrise$getChunkMap().level).moonrise$getEntityTickingChunks();
+        final LevelChunk[] raw = chunks.getRawDataUnchecked();
+        final int size = chunks.size();
+
+        java.util.Objects.checkFromToIndex(0, size, raw.length);
+        for (int i = 0; i < size; ++i) {
+            final LevelChunk chunk = raw[i];
+
+            action.accept(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunk.getPos()));
         }
+        // Paper end - rewrite chunk system
     }
 
     public LongIterator getSpawnCandidateChunks() {
-        this.naturalSpawnChunkCounter.runAllUpdates();
-        return this.naturalSpawnChunkCounter.chunks.keySet().iterator();
+        return this.spawnChunkTracker.getPositions().iterator(); // Paper - chunk tick iteration optimisation
     }
 
     public String getDebugStatus() {
-        return this.ticketDispatcher.getDebugStatus();
+        return "N/A"; // Paper - rewrite chunk system
     }
 
     public boolean hasTickets() {
@@ -240,6 +_,7 @@
         }
     }
 
+/*  // Paper - rewrite chunk system
     class PlayerTicketTracker extends DistanceManager.FixedPlayerDistanceChunkTracker {
         private int viewDistance;
         private final Long2IntMap queueLevels = Long2IntMaps.synchronize(new Long2IntOpenHashMap());
@@ -318,5 +_,5 @@
         private boolean haveTicketFor(int level) {
             return level <= this.viewDistance;
         }
-    }
+    }*/  // Paper - rewrite chunk system
 }
