--- a/net/minecraft/server/level/DistanceManager.java
+++ b/net/minecraft/server/level/DistanceManager.java
@@ -12,6 +12,7 @@
 import it.unimi.dsi.fastutil.longs.Long2IntMaps;
 import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
 import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
+import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
 import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
 import it.unimi.dsi.fastutil.longs.LongIterator;
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
@@ -24,6 +25,7 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Iterator;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
@@ -36,64 +38,33 @@
 import org.slf4j.Logger;
 
 public abstract class DistanceManager {
+
+    // Paper start - rewrite chunk system
+    public io.papermc.paper.chunk.system.scheduling.ChunkHolderManager getChunkHolderManager() {
+        return this.chunkMap.level.chunkTaskScheduler.chunkHolderManager;
+    }
+    // Paper end - rewrite chunk system
+
     static final Logger LOGGER = LogUtils.getLogger();
     static final int PLAYER_TICKET_LEVEL = ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING);
     private static final int INITIAL_TICKET_LIST_CAPACITY = 4;
-    final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap<>();
-    public final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets = new Long2ObjectOpenHashMap<>();
-    private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker();
-    private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
-    private final TickingTracker tickingTicketsTracker = new TickingTracker();
-    private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(32);
-    final Set<ChunkHolder> chunksToUpdateFutures = Sets.newHashSet();
-    final ChunkTaskPriorityQueueSorter ticketThrottler;
-    final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> ticketThrottlerInput;
-    final ProcessorHandle<ChunkTaskPriorityQueueSorter.Release> ticketThrottlerReleaser;
-    final LongSet ticketsToRelease = new LongOpenHashSet();
-    final Executor mainThreadExecutor;
-    private long ticketTickCounter;
-    private int simulationDistance = 10;
+    final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
+    // Paper - rewrite chunk system
+    public static final int MOB_SPAWN_RANGE = 8; //private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8); // Paper - optimise chunk tick iteration
+    // Paper - rewrite chunk system
+    private final ChunkMap chunkMap; // Paper
 
-    protected DistanceManager(Executor workerExecutor, Executor mainThreadExecutor) {
-        ProcessorHandle<Runnable> processorHandle = ProcessorHandle.of("player ticket throttler", mainThreadExecutor::execute);
-        ChunkTaskPriorityQueueSorter chunkTaskPriorityQueueSorter = new ChunkTaskPriorityQueueSorter(ImmutableList.of(processorHandle), workerExecutor, 4);
-        this.ticketThrottler = chunkTaskPriorityQueueSorter;
-        this.ticketThrottlerInput = chunkTaskPriorityQueueSorter.getProcessor(processorHandle, true);
-        this.ticketThrottlerReleaser = chunkTaskPriorityQueueSorter.getReleaseProcessor(processorHandle);
-        this.mainThreadExecutor = mainThreadExecutor;
+    protected DistanceManager(Executor workerExecutor, Executor mainThreadExecutor, ChunkMap chunkMap) {
+        // Paper - rewrite chunk system
+        this.chunkMap = chunkMap; // Paper
     }
 
     protected void purgeStaleTickets() {
-        ++this.ticketTickCounter;
-        ObjectIterator<Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>>> objectIterator = this.tickets.long2ObjectEntrySet().fastIterator();
-
-        while(objectIterator.hasNext()) {
-            Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>> entry = objectIterator.next();
-            Iterator<Ticket<?>> iterator = entry.getValue().iterator();
-            boolean bl = false;
-
-            while(iterator.hasNext()) {
-                Ticket<?> ticket = iterator.next();
-                if (ticket.timedOut(this.ticketTickCounter)) {
-                    iterator.remove();
-                    bl = true;
-                    this.tickingTicketsTracker.removeTicket(entry.getLongKey(), ticket);
-                }
-            }
-
-            if (bl) {
-                this.ticketTracker.update(entry.getLongKey(), getTicketLevelAt(entry.getValue()), false);
-            }
-
-            if (entry.getValue().isEmpty()) {
-                objectIterator.remove();
-            }
-        }
-
+        this.getChunkHolderManager().tick(); // Paper - rewrite chunk system
     }
 
     private static int getTicketLevelAt(SortedArraySet<Ticket<?>> tickets) {
-        return !tickets.isEmpty() ? tickets.first().getTicketLevel() : ChunkLevel.MAX_LEVEL + 1;
+        return !tickets.isEmpty() ? ((Ticket) tickets.first()).getTicketLevel() : ChunkLevel.MAX_LEVEL + 1;
     }
 
     protected abstract boolean isChunkToRemove(long pos);
@@ -102,277 +73,192 @@
     protected abstract ChunkHolder getChunk(long pos);
 
     @Nullable
-    protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int i);
+    protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k);
 
     public boolean runAllUpdates(ChunkMap chunkStorage) {
-        this.naturalSpawnChunkCounter.runAllUpdates();
-        this.tickingTicketsTracker.runAllUpdates();
-        this.playerTicketManager.runAllUpdates();
-        int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
-        boolean bl = i != 0;
-        if (bl) {
-        }
-
-        if (!this.chunksToUpdateFutures.isEmpty()) {
-            this.chunksToUpdateFutures.forEach((holder) -> {
-                holder.updateFutures(chunkStorage, 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.getTickets(l).stream().anyMatch((ticket) -> {
-                        return ticket.getType() == TicketType.PLAYER;
-                    })) {
-                        ChunkHolder chunkHolder = chunkStorage.getUpdatingChunkIfPresent(l);
-                        if (chunkHolder == null) {
-                            throw new IllegalStateException();
-                        }
-
-                        CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> completableFuture = chunkHolder.getEntityTickingChunkFuture();
-                        completableFuture.thenAccept((either) -> {
-                            this.mainThreadExecutor.execute(() -> {
-                                this.ticketThrottlerReleaser.tell(ChunkTaskPriorityQueueSorter.release(() -> {
-                                }, l, false));
-                            });
-                        });
-                    }
-                }
-
-                this.ticketsToRelease.clear();
-            }
-
-            return bl;
-        }
+        return this.getChunkHolderManager().processTicketUpdates(); // Paper - rewrite chunk system
     }
 
-    void addTicket(long position, Ticket<?> ticket) {
-        SortedArraySet<Ticket<?>> sortedArraySet = this.getTickets(position);
-        int i = getTicketLevelAt(sortedArraySet);
-        Ticket<?> ticket2 = sortedArraySet.addOrGet(ticket);
-        ticket2.setCreatedTick(this.ticketTickCounter);
-        if (ticket.getTicketLevel() < i) {
-            this.ticketTracker.update(position, ticket.getTicketLevel(), true);
-        }
-
+    boolean addTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
+        org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::addTicket"); // Paper
+        return this.getChunkHolderManager().addTicketAtLevel((TicketType)ticket.getType(), i, ticket.getTicketLevel(), ticket.key); // Paper - rewrite chunk system
     }
 
-    void removeTicket(long pos, Ticket<?> ticket) {
-        SortedArraySet<Ticket<?>> sortedArraySet = this.getTickets(pos);
-        if (sortedArraySet.remove(ticket)) {
-        }
-
-        if (sortedArraySet.isEmpty()) {
-            this.tickets.remove(pos);
-        }
-
-        this.ticketTracker.update(pos, getTicketLevelAt(sortedArraySet), false);
+    boolean removeTicket(long i, Ticket<?> ticket) { // CraftBukkit - void -> boolean
+        org.spigotmc.AsyncCatcher.catchOp("ChunkMapDistance::removeTicket"); // Paper
+        return this.getChunkHolderManager().removeTicketAtLevel((TicketType)ticket.getType(), i, ticket.getTicketLevel(), ticket.key); // Paper - rewrite chunk system
     }
 
     public <T> void addTicket(TicketType<T> type, ChunkPos pos, int level, T argument) {
-        this.addTicket(pos.toLong(), new Ticket<>(type, level, argument));
+        this.getChunkHolderManager().addTicketAtLevel(type, pos, level, argument); // Paper - rewrite chunk system
     }
 
     public <T> void removeTicket(TicketType<T> type, ChunkPos pos, int level, T argument) {
-        Ticket<T> ticket = new Ticket<>(type, level, argument);
-        this.removeTicket(pos.toLong(), ticket);
+        this.getChunkHolderManager().removeTicketAtLevel(type, pos, level, argument); // Paper - rewrite chunk system
     }
 
     public <T> void addRegionTicket(TicketType<T> type, ChunkPos pos, int radius, T argument) {
-        Ticket<T> ticket = new Ticket<>(type, ChunkLevel.byStatus(FullChunkStatus.FULL) - radius, argument);
-        long l = pos.toLong();
-        this.addTicket(l, ticket);
-        this.tickingTicketsTracker.addTicket(l, ticket);
+        // CraftBukkit start
+        this.addRegionTicketAtDistance(type, pos, radius, argument);
+    }
+
+    public <T> boolean addRegionTicketAtDistance(TicketType<T> tickettype, ChunkPos chunkcoordintpair, int i, T t0) {
+        return this.getChunkHolderManager().addTicketAtLevel(tickettype, chunkcoordintpair, ChunkLevel.byStatus(FullChunkStatus.FULL) - i, t0); // Paper - rewrite chunk system
     }
 
     public <T> void removeRegionTicket(TicketType<T> type, ChunkPos pos, int radius, T argument) {
-        Ticket<T> ticket = new Ticket<>(type, ChunkLevel.byStatus(FullChunkStatus.FULL) - radius, argument);
-        long l = pos.toLong();
-        this.removeTicket(l, ticket);
-        this.tickingTicketsTracker.removeTicket(l, ticket);
+        // CraftBukkit start
+        this.removeRegionTicketAtDistance(type, pos, radius, argument);
     }
 
-    private SortedArraySet<Ticket<?>> getTickets(long position) {
-        return this.tickets.computeIfAbsent(position, (pos) -> {
-            return SortedArraySet.create(4);
-        });
+    public <T> boolean removeRegionTicketAtDistance(TicketType<T> tickettype, ChunkPos chunkcoordintpair, int i, T t0) {
+        return this.getChunkHolderManager().removeTicketAtLevel(tickettype, chunkcoordintpair, ChunkLevel.byStatus(FullChunkStatus.FULL) - i, t0); // Paper - rewrite chunk system
     }
 
+    // Paper - rewrite chunk system
+
     protected void updateChunkForced(ChunkPos pos, boolean forced) {
-        Ticket<ChunkPos> ticket = new Ticket<>(TicketType.FORCED, ChunkMap.FORCED_TICKET_LEVEL, pos);
-        long l = pos.toLong();
+        Ticket<ChunkPos> ticket = new Ticket<>(TicketType.FORCED, ChunkMap.FORCED_TICKET_LEVEL, pos, 0L); // Paper - rewrite chunk system
+        long i = pos.toLong();
+
         if (forced) {
-            this.addTicket(l, ticket);
-            this.tickingTicketsTracker.addTicket(l, ticket);
+            this.addTicket(i, ticket);
+            //this.tickingTicketsTracker.addTicket(i, ticket); // Paper - no longer used
         } else {
-            this.removeTicket(l, ticket);
-            this.tickingTicketsTracker.removeTicket(l, ticket);
+            this.removeTicket(i, ticket);
+            //this.tickingTicketsTracker.removeTicket(i, ticket); // Paper - no longer used
         }
 
     }
 
     public void addPlayer(SectionPos pos, ServerPlayer player) {
-        ChunkPos chunkPos = pos.chunk();
-        long l = chunkPos.toLong();
-        this.playersPerChunk.computeIfAbsent(l, (sectionPos) -> {
-            return new ObjectOpenHashSet();
-        }).add(player);
-        this.naturalSpawnChunkCounter.update(l, 0, true);
-        this.playerTicketManager.update(l, 0, true);
-        this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkPos, this.getPlayerTicketLevel(), chunkPos);
+        ChunkPos chunkcoordintpair = pos.chunk();
+        long i = chunkcoordintpair.toLong();
+
+        // Paper - no longer used
+        //this.naturalSpawnChunkCounter.update(i, 0, true); // Paper - optimise chunk tick iteration
+        //this.playerTicketManager.update(i, 0, true); // Paper - no longer used
+        //this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
     }
 
     public void removePlayer(SectionPos pos, ServerPlayer player) {
-        ChunkPos chunkPos = pos.chunk();
-        long l = chunkPos.toLong();
-        ObjectSet<ServerPlayer> objectSet = this.playersPerChunk.get(l);
-        objectSet.remove(player);
-        if (objectSet.isEmpty()) {
-            this.playersPerChunk.remove(l);
-            this.naturalSpawnChunkCounter.update(l, Integer.MAX_VALUE, false);
-            this.playerTicketManager.update(l, Integer.MAX_VALUE, false);
-            this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkPos, this.getPlayerTicketLevel(), chunkPos);
+        ChunkPos chunkcoordintpair = pos.chunk();
+        long i = chunkcoordintpair.toLong();
+        ObjectSet<ServerPlayer> objectset = (ObjectSet) this.playersPerChunk.get(i);
+        if (objectset == null) return; // CraftBukkit - SPIGOT-6208
+
+        if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully.
+        if (objectset == null || objectset.isEmpty()) { // Paper
+            this.playersPerChunk.remove(i);
+            //this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false); // Paper - optimise chunk tick iteration
+            //this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
+            //this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair); // Paper - no longer used
         }
 
     }
 
-    private int getPlayerTicketLevel() {
-        return Math.max(0, ChunkLevel.byStatus(FullChunkStatus.ENTITY_TICKING) - this.simulationDistance);
-    }
+    // Paper - rewrite chunk system
 
     public boolean inEntityTickingRange(long chunkPos) {
-        return ChunkLevel.isEntityTicking(this.tickingTicketsTracker.getLevel(chunkPos));
+        // Paper start - replace player chunk loader system
+        ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(chunkPos);
+        return holder != null && holder.isEntityTickingReady();
+        // Paper end - replace player chunk loader system
     }
 
     public boolean inBlockTickingRange(long chunkPos) {
-        return ChunkLevel.isBlockTicking(this.tickingTicketsTracker.getLevel(chunkPos));
+        // Paper start - replace player chunk loader system
+        ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(chunkPos);
+        return holder != null && holder.isTickingReady();
+        // Paper end - replace player chunk loader system
     }
 
     protected String getTicketDebugString(long pos) {
-        SortedArraySet<Ticket<?>> sortedArraySet = this.tickets.get(pos);
-        return sortedArraySet != null && !sortedArraySet.isEmpty() ? sortedArraySet.first().toString() : "no_ticket";
+        return this.getChunkHolderManager().getTicketDebugString(pos); // Paper - rewrite chunk system
     }
 
     protected void updatePlayerTickets(int viewDistance) {
-        this.playerTicketManager.updateViewDistance(viewDistance);
+        this.chunkMap.setServerViewDistance(viewDistance);// Paper - route to player chunk manager
     }
 
-    public void updateSimulationDistance(int simulationDistance) {
-        if (simulationDistance != this.simulationDistance) {
-            this.simulationDistance = simulationDistance;
-            this.tickingTicketsTracker.replacePlayerTicketsLevel(this.getPlayerTicketLevel());
-        }
+    // Paper start
+    public int getSimulationDistance() {
+        return this.chunkMap.level.playerChunkLoader.getAPITickDistance();
+    }
+    // Paper end
 
+    public void updateSimulationDistance(int simulationDistance) {
+        this.chunkMap.level.playerChunkLoader.setTickDistance(simulationDistance); // Paper - route to player chunk manager
     }
 
     public int getNaturalSpawnChunkCount() {
-        this.naturalSpawnChunkCounter.runAllUpdates();
-        return this.naturalSpawnChunkCounter.chunks.size();
+        return this.chunkMap.playerMobSpawnMap.size(); // Paper - optimise chunk tick iteration
     }
 
     public boolean hasPlayersNearby(long chunkPos) {
-        this.naturalSpawnChunkCounter.runAllUpdates();
-        return this.naturalSpawnChunkCounter.chunks.containsKey(chunkPos);
+        return this.chunkMap.playerMobSpawnMap.getObjectsInRange(chunkPos) != null; // Paper - optimise chunk tick iteration
     }
 
     public String getDebugStatus() {
-        return this.ticketThrottler.getDebugStatus();
+        return "No DistanceManager stats available"; // Paper - rewrite chunk system
     }
 
-    private void dumpTickets(String path) {
-        try (FileOutputStream fileOutputStream = new FileOutputStream(new File(path))) {
-            for(Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>> entry : this.tickets.long2ObjectEntrySet()) {
-                ChunkPos chunkPos = new ChunkPos(entry.getLongKey());
+    // Paper - rewrite chunk system
 
-                for(Ticket<?> ticket : entry.getValue()) {
-                    fileOutputStream.write((chunkPos.x + "\t" + chunkPos.z + "\t" + ticket.getType() + "\t" + ticket.getTicketLevel() + "\t\n").getBytes(StandardCharsets.UTF_8));
-                }
-            }
-        } catch (IOException var10) {
-            LOGGER.error("Failed to dump tickets to {}", path, var10);
-        }
+    // Paper - replace player chunk loader
 
+    public void removeTicketsOnClosing() {
+        // Paper - rewrite chunk system - this stupid hack ain't needed anymore
     }
 
-    @VisibleForTesting
-    TickingTracker tickingTracker() {
-        return this.tickingTicketsTracker;
+    public boolean hasTickets() {
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
     }
 
-    public void removeTicketsOnClosing() {
-        ImmutableSet<TicketType<?>> immutableSet = ImmutableSet.of(TicketType.UNKNOWN, TicketType.POST_TELEPORT, TicketType.LIGHT);
-        ObjectIterator<Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>>> objectIterator = this.tickets.long2ObjectEntrySet().fastIterator();
-
-        while(objectIterator.hasNext()) {
-            Long2ObjectMap.Entry<SortedArraySet<Ticket<?>>> entry = objectIterator.next();
-            Iterator<Ticket<?>> iterator = entry.getValue().iterator();
-            boolean bl = false;
-
-            while(iterator.hasNext()) {
-                Ticket<?> ticket = iterator.next();
-                if (!immutableSet.contains(ticket.getType())) {
-                    iterator.remove();
-                    bl = true;
-                    this.tickingTicketsTracker.removeTicket(entry.getLongKey(), ticket);
-                }
-            }
-
-            if (bl) {
-                this.ticketTracker.update(entry.getLongKey(), getTicketLevelAt(entry.getValue()), false);
-            }
-
-            if (entry.getValue().isEmpty()) {
-                objectIterator.remove();
-            }
-        }
-
+    // CraftBukkit start
+    public <T> void removeAllTicketsFor(TicketType<T> ticketType, int ticketLevel, T ticketIdentifier) {
+        this.getChunkHolderManager().removeAllTicketsFor(ticketType, ticketLevel, ticketIdentifier); // Paper - rewrite chunk system
     }
+    // CraftBukkit end
 
-    public boolean hasTickets() {
-        return !this.tickets.isEmpty();
-    }
+    /* Paper - rewrite chunk system
+    private class ChunkTicketTracker extends ChunkTracker {
 
-    class ChunkTicketTracker extends ChunkTracker {
         private static final int MAX_LEVEL = ChunkLevel.MAX_LEVEL + 1;
 
         public ChunkTicketTracker() {
-            super(MAX_LEVEL + 1, 16, 256);
+            super(DistanceManager.ChunkTicketTracker.MAX_LEVEL + 1, 16, 256);
         }
 
         @Override
         protected int getLevelFromSource(long id) {
-            SortedArraySet<Ticket<?>> sortedArraySet = DistanceManager.this.tickets.get(id);
-            if (sortedArraySet == null) {
-                return Integer.MAX_VALUE;
-            } else {
-                return sortedArraySet.isEmpty() ? Integer.MAX_VALUE : sortedArraySet.first().getTicketLevel();
-            }
+            SortedArraySet<Ticket<?>> arraysetsorted = (SortedArraySet) DistanceManager.this.tickets.get(id);
+
+            return arraysetsorted == null ? Integer.MAX_VALUE : (arraysetsorted.isEmpty() ? Integer.MAX_VALUE : ((Ticket) arraysetsorted.first()).getTicketLevel());
         }
 
         @Override
         protected int getLevel(long id) {
             if (!DistanceManager.this.isChunkToRemove(id)) {
-                ChunkHolder chunkHolder = DistanceManager.this.getChunk(id);
-                if (chunkHolder != null) {
-                    return chunkHolder.getTicketLevel();
+                ChunkHolder playerchunk = DistanceManager.this.getChunk(id);
+
+                if (playerchunk != null) {
+                    return playerchunk.getTicketLevel();
                 }
             }
 
-            return MAX_LEVEL;
+            return DistanceManager.ChunkTicketTracker.MAX_LEVEL;
         }
 
         @Override
         protected void setLevel(long id, int level) {
-            ChunkHolder chunkHolder = DistanceManager.this.getChunk(id);
-            int i = chunkHolder == null ? MAX_LEVEL : chunkHolder.getTicketLevel();
-            if (i != level) {
-                chunkHolder = DistanceManager.this.updateChunkScheduling(id, level, chunkHolder, i);
-                if (chunkHolder != null) {
-                    DistanceManager.this.chunksToUpdateFutures.add(chunkHolder);
+            ChunkHolder playerchunk = DistanceManager.this.getChunk(id);
+            int k = playerchunk == null ? DistanceManager.ChunkTicketTracker.MAX_LEVEL : playerchunk.getTicketLevel();
+
+            if (k != level) {
+                playerchunk = DistanceManager.this.updateChunkScheduling(id, level, playerchunk, k);
+                if (playerchunk != null) {
+                    DistanceManager.this.chunksToUpdateFutures.add(playerchunk);
                 }
 
             }
@@ -382,15 +268,17 @@
             return this.runUpdates(distance);
         }
     }
+    */ // Paper - rewrite chunk system
+
+    private class FixedPlayerDistanceChunkTracker extends ChunkTracker {
 
-    class FixedPlayerDistanceChunkTracker extends ChunkTracker {
         protected final Long2ByteMap chunks = new Long2ByteOpenHashMap();
         protected final int maxDistance;
 
-        protected FixedPlayerDistanceChunkTracker(int maxDistance) {
-            super(maxDistance + 2, 16, 256);
-            this.maxDistance = maxDistance;
-            this.chunks.defaultReturnValue((byte)(maxDistance + 2));
+        protected FixedPlayerDistanceChunkTracker(int i) {
+            super(i + 2, 16, 256);
+            this.maxDistance = i;
+            this.chunks.defaultReturnValue((byte) (i + 2));
         }
 
         @Override
@@ -400,18 +288,18 @@
 
         @Override
         protected void setLevel(long id, int level) {
-            byte b;
+            byte b0;
+
             if (level > this.maxDistance) {
-                b = this.chunks.remove(id);
+                b0 = this.chunks.remove(id);
             } else {
-                b = this.chunks.put(id, (byte)level);
+                b0 = this.chunks.put(id, (byte) level);
             }
 
-            this.onLevelChange(id, b, level);
+            this.onLevelChange(id, b0, level);
         }
 
-        protected void onLevelChange(long pos, int oldDistance, int distance) {
-        }
+        protected void onLevelChange(long pos, int oldDistance, int distance) {}
 
         @Override
         protected int getLevelFromSource(long id) {
@@ -419,8 +307,9 @@
         }
 
         private boolean havePlayer(long chunkPos) {
-            ObjectSet<ServerPlayer> objectSet = DistanceManager.this.playersPerChunk.get(chunkPos);
-            return objectSet != null && !objectSet.isEmpty();
+            ObjectSet<ServerPlayer> objectset = (ObjectSet) DistanceManager.this.playersPerChunk.get(chunkPos);
+
+            return objectset != null && !objectset.isEmpty();
         }
 
         public void runAllUpdates() {
@@ -428,27 +317,46 @@
         }
 
         private void dumpChunks(String path) {
-            try (FileOutputStream fileOutputStream = new FileOutputStream(new File(path))) {
-                for(Long2ByteMap.Entry entry : this.chunks.long2ByteEntrySet()) {
-                    ChunkPos chunkPos = new ChunkPos(entry.getLongKey());
-                    String string = Byte.toString(entry.getByteValue());
-                    fileOutputStream.write((chunkPos.x + "\t" + chunkPos.z + "\t" + string + "\n").getBytes(StandardCharsets.UTF_8));
+            try {
+                FileOutputStream fileoutputstream = new FileOutputStream(new File(path));
+
+                try {
+                    ObjectIterator objectiterator = this.chunks.long2ByteEntrySet().iterator();
+
+                    while (objectiterator.hasNext()) {
+                        it.unimi.dsi.fastutil.longs.Long2ByteMap.Entry it_unimi_dsi_fastutil_longs_long2bytemap_entry = (it.unimi.dsi.fastutil.longs.Long2ByteMap.Entry) objectiterator.next();
+                        ChunkPos chunkcoordintpair = new ChunkPos(it_unimi_dsi_fastutil_longs_long2bytemap_entry.getLongKey());
+                        String s1 = Byte.toString(it_unimi_dsi_fastutil_longs_long2bytemap_entry.getByteValue());
+
+                        fileoutputstream.write((chunkcoordintpair.x + "\t" + chunkcoordintpair.z + "\t" + s1 + "\n").getBytes(StandardCharsets.UTF_8));
+                    }
+                } catch (Throwable throwable) {
+                    try {
+                        fileoutputstream.close();
+                    } catch (Throwable throwable1) {
+                        throwable.addSuppressed(throwable1);
+                    }
+
+                    throw throwable;
                 }
-            } catch (IOException var9) {
-                DistanceManager.LOGGER.error("Failed to dump chunks to {}", path, var9);
+
+                fileoutputstream.close();
+            } catch (IOException ioexception) {
+                DistanceManager.LOGGER.error("Failed to dump chunks to {}", path, ioexception);
             }
 
         }
     }
 
-    class PlayerTicketTracker extends DistanceManager.FixedPlayerDistanceChunkTracker {
-        private int viewDistance;
+    /* Paper - rewrite chunk system
+    private class PlayerTicketTracker extends DistanceManager.FixedPlayerDistanceChunkTracker {
+
+        private int viewDistance = 0;
         private final Long2IntMap queueLevels = Long2IntMaps.synchronize(new Long2IntOpenHashMap());
         private final LongSet toUpdate = new LongOpenHashSet();
 
         protected PlayerTicketTracker(int i) {
             super(i);
-            this.viewDistance = 0;
             this.queueLevels.defaultReturnValue(i + 2);
         }
 
@@ -458,10 +366,14 @@
         }
 
         public void updateViewDistance(int watchDistance) {
-            for(Long2ByteMap.Entry entry : this.chunks.long2ByteEntrySet()) {
-                byte b = entry.getByteValue();
-                long l = entry.getLongKey();
-                this.onLevelChange(l, b, this.haveTicketFor(b), b <= watchDistance);
+            ObjectIterator objectiterator = this.chunks.long2ByteEntrySet().iterator();
+
+            while (objectiterator.hasNext()) {
+                it.unimi.dsi.fastutil.longs.Long2ByteMap.Entry it_unimi_dsi_fastutil_longs_long2bytemap_entry = (it.unimi.dsi.fastutil.longs.Long2ByteMap.Entry) objectiterator.next();
+                byte b0 = it_unimi_dsi_fastutil_longs_long2bytemap_entry.getByteValue();
+                long j = it_unimi_dsi_fastutil_longs_long2bytemap_entry.getLongKey();
+
+                this.onLevelChange(j, b0, this.haveTicketFor(b0), b0 <= watchDistance);
             }
 
             this.viewDistance = watchDistance;
@@ -470,6 +382,7 @@
         private void onLevelChange(long pos, int distance, boolean oldWithinViewDistance, boolean withinViewDistance) {
             if (oldWithinViewDistance != withinViewDistance) {
                 Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, DistanceManager.PLAYER_TICKET_LEVEL, new ChunkPos(pos));
+
                 if (withinViewDistance) {
                     DistanceManager.this.ticketThrottlerInput.tell(ChunkTaskPriorityQueueSorter.message(() -> {
                         DistanceManager.this.mainThreadExecutor.execute(() -> {
@@ -500,24 +413,25 @@
         public void runAllUpdates() {
             super.runAllUpdates();
             if (!this.toUpdate.isEmpty()) {
-                LongIterator longIterator = this.toUpdate.iterator();
+                LongIterator longiterator = this.toUpdate.iterator();
 
-                while(longIterator.hasNext()) {
-                    long l = longIterator.nextLong();
-                    int i = this.queueLevels.get(l);
-                    int j = this.getLevel(l);
-                    if (i != j) {
-                        DistanceManager.this.ticketThrottler.onLevelChange(new ChunkPos(l), () -> {
-                            return this.queueLevels.get(l);
-                        }, j, (level) -> {
-                            if (level >= this.queueLevels.defaultReturnValue()) {
-                                this.queueLevels.remove(l);
+                while (longiterator.hasNext()) {
+                    long i = longiterator.nextLong();
+                    int j = this.queueLevels.get(i);
+                    int k = this.getLevel(i);
+
+                    if (j != k) {
+                        DistanceManager.this.ticketThrottler.onLevelChange(new ChunkPos(i), () -> {
+                            return this.queueLevels.get(i);
+                        }, k, (l) -> {
+                            if (l >= this.queueLevels.defaultReturnValue()) {
+                                this.queueLevels.remove(i);
                             } else {
-                                this.queueLevels.put(l, level);
+                                this.queueLevels.put(i, l);
                             }
 
                         });
-                        this.onLevelChange(l, j, this.haveTicketFor(i), this.haveTicketFor(j));
+                        this.onLevelChange(i, k, this.haveTicketFor(j), this.haveTicketFor(k));
                     }
                 }
 
@@ -530,4 +444,5 @@
             return distance <= this.viewDistance;
         }
     }
+    */ // Paper - rewrite chunk system
 }
