--- a/net/minecraft/server/level/ChunkHolder.java
+++ b/net/minecraft/server/level/ChunkHolder.java
@@ -7,7 +7,6 @@
 import java.util.BitSet;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicReferenceArray;
 import java.util.function.IntConsumer;
@@ -35,130 +34,197 @@
 import net.minecraft.world.level.chunk.status.ChunkStatus;
 import net.minecraft.world.level.lighting.LevelLightEngine;
 
+// CraftBukkit start
+import net.minecraft.server.MinecraftServer;
+// CraftBukkit end
+
 public class ChunkHolder {
+
     public static final ChunkResult<ChunkAccess> UNLOADED_CHUNK = ChunkResult.error("Unloaded chunk");
-    public static final CompletableFuture<ChunkResult<ChunkAccess>> UNLOADED_CHUNK_FUTURE = CompletableFuture.completedFuture(UNLOADED_CHUNK);
+    public static final CompletableFuture<ChunkResult<ChunkAccess>> UNLOADED_CHUNK_FUTURE = CompletableFuture.completedFuture(ChunkHolder.UNLOADED_CHUNK);
     public static final ChunkResult<LevelChunk> UNLOADED_LEVEL_CHUNK = ChunkResult.error("Unloaded level chunk");
     public static final ChunkResult<ChunkAccess> NOT_DONE_YET = ChunkResult.error("Not done yet");
-    private static final CompletableFuture<ChunkResult<LevelChunk>> UNLOADED_LEVEL_CHUNK_FUTURE = CompletableFuture.completedFuture(UNLOADED_LEVEL_CHUNK);
+    private static final CompletableFuture<ChunkResult<LevelChunk>> UNLOADED_LEVEL_CHUNK_FUTURE = CompletableFuture.completedFuture(ChunkHolder.UNLOADED_LEVEL_CHUNK);
     private static final List<ChunkStatus> CHUNK_STATUSES = ChunkStatus.getStatusList();
-    private final AtomicReferenceArray<CompletableFuture<ChunkResult<ChunkAccess>>> futures = new AtomicReferenceArray<>(CHUNK_STATUSES.size());
+    // Paper - rewrite chunk system
     private final LevelHeightAccessor levelHeightAccessor;
-    private volatile CompletableFuture<ChunkResult<LevelChunk>> fullChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
-    private volatile CompletableFuture<ChunkResult<LevelChunk>> tickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
-    private volatile CompletableFuture<ChunkResult<LevelChunk>> entityTickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
-    private CompletableFuture<ChunkAccess> chunkToSave = CompletableFuture.completedFuture(null);
+    // Paper - rewrite chunk system
     @Nullable
-    private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory = null;
-    public int oldTicketLevel;
-    private int ticketLevel;
-    private int queueLevel;
+    private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory;
+    // Paper - rewrite chunk system
     public final ChunkPos pos;
     private boolean hasChangedSections;
     private final ShortSet[] changedBlocksPerSection;
-    private final BitSet blockChangedLightSectionFilter = new BitSet();
-    private final BitSet skyChangedLightSectionFilter = new BitSet();
+    private final BitSet blockChangedLightSectionFilter;
+    private final BitSet skyChangedLightSectionFilter;
     private final LevelLightEngine lightEngine;
     private final ChunkHolder.LevelChangeListener onLevelChange;
     public final ChunkHolder.PlayerProvider playerProvider;
-    private boolean wasAccessibleSinceLastSave;
-    private CompletableFuture<Void> pendingFullStateConfirmation = CompletableFuture.completedFuture(null);
-    private CompletableFuture<?> sendSync = CompletableFuture.completedFuture(null);
+    // Paper - rewrite chunk system
 
-    public ChunkHolder(
-        ChunkPos pos,
-        int level,
-        LevelHeightAccessor world,
-        LevelLightEngine lightingProvider,
-        ChunkHolder.LevelChangeListener levelUpdateListener,
-        ChunkHolder.PlayerProvider playersWatchingChunkProvider
-    ) {
+    private final ChunkMap chunkMap; // Paper
+    // Paper start - no-tick view distance
+    public final LevelChunk getSendingChunk() {
+        // it's important that we use getChunkAtIfLoadedImmediately to mirror the chunk sending logic used
+        // in Chunk's neighbour callback
+        LevelChunk ret = this.chunkMap.level.getChunkSource().getChunkAtIfLoadedImmediately(this.pos.x, this.pos.z);
+        if (ret != null && ret.areNeighboursLoaded(1)) {
+            return ret;
+        }
+        return null;
+    }
+    // Paper end - no-tick view distance
+
+    // Paper start
+    public void onChunkAdd() {
+        // Paper start - optimise chunk tick iteration
+        if (this.needsBroadcastChanges()) {
+            this.chunkMap.needsChangeBroadcasting.add(this);
+        }
+        // Paper end - optimise chunk tick iteration
+    }
+
+    public void onChunkRemove() {
+        // Paper start - optimise chunk tick iteration
+        if (this.needsBroadcastChanges()) {
+            this.chunkMap.needsChangeBroadcasting.remove(this);
+        }
+        // Paper end - optimise chunk tick iteration
+    }
+    // Paper end
+
+    public final io.papermc.paper.chunk.system.scheduling.NewChunkHolder newChunkHolder; // Paper - rewrite chunk system
+
+    // Paper start - replace player chunk loader
+    private final com.destroystokyo.paper.util.maplist.ReferenceList<ServerPlayer> playersSentChunkTo = new com.destroystokyo.paper.util.maplist.ReferenceList<>();
+
+    public void addPlayer(ServerPlayer player) {
+        if (!this.playersSentChunkTo.add(player)) {
+            throw new IllegalStateException("Already sent chunk " + this.pos + " in world '" + this.chunkMap.level.getWorld().getName() + "' to player " + player);
+        }
+    }
+
+    public void removePlayer(ServerPlayer player) {
+        if (!this.playersSentChunkTo.remove(player)) {
+            throw new IllegalStateException("Have not sent chunk " + this.pos + " in world '" + this.chunkMap.level.getWorld().getName() + "' to player " + player);
+        }
+    }
+
+    public boolean hasChunkBeenSent() {
+        return this.playersSentChunkTo.size() != 0;
+    }
+
+    public boolean hasBeenSent(ServerPlayer to) {
+        return this.playersSentChunkTo.contains(to);
+    }
+    // Paper end - replace player chunk loader
+    public ChunkHolder(ChunkPos pos, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.PlayerProvider playersWatchingChunkProvider, io.papermc.paper.chunk.system.scheduling.NewChunkHolder newChunkHolder) { // Paper - rewrite chunk system
+        this.newChunkHolder = newChunkHolder; // Paper - rewrite chunk system
+        this.chunkToSaveHistory = null;
+        this.blockChangedLightSectionFilter = new BitSet();
+        this.skyChangedLightSectionFilter = new BitSet();
+        // Paper - rewrite chunk system
         this.pos = pos;
         this.levelHeightAccessor = world;
         this.lightEngine = lightingProvider;
-        this.onLevelChange = levelUpdateListener;
+        this.onLevelChange = null; // Paper - rewrite chunk system
         this.playerProvider = playersWatchingChunkProvider;
-        this.oldTicketLevel = ChunkLevel.MAX_LEVEL + 1;
-        this.ticketLevel = this.oldTicketLevel;
-        this.queueLevel = this.oldTicketLevel;
-        this.setTicketLevel(level);
+        // Paper - rewrite chunk system
         this.changedBlocksPerSection = new ShortSet[world.getSectionsCount()];
+        this.chunkMap = (ChunkMap)playersWatchingChunkProvider; // Paper
+    }
+
+    // Paper start
+    public @Nullable ChunkAccess getAvailableChunkNow() {
+        return this.newChunkHolder.getCurrentChunk(); // Paper - rewrite chunk system
+    }
+    // Paper end
+    // CraftBukkit start
+    public LevelChunk getFullChunkNow() {
+        // Paper start - rewrite chunk system
+        if (!this.isFullChunkReady() || !(this.getAvailableChunkNow() instanceof LevelChunk chunk)) return null; // instanceof to avoid a race condition on off-main threads
+        return chunk;
+        // Paper end - rewrite chunk system
     }
 
+    public LevelChunk getFullChunkNowUnchecked() {
+        // Paper start - rewrite chunk system
+        return this.getAvailableChunkNow() instanceof LevelChunk chunk ? chunk : null;
+        // Paper end - rewrite chunk system
+    }
+    // CraftBukkit end
+
     public CompletableFuture<ChunkResult<ChunkAccess>> getFutureIfPresentUnchecked(ChunkStatus leastStatus) {
-        CompletableFuture<ChunkResult<ChunkAccess>> completableFuture = this.futures.get(leastStatus.getIndex());
-        return completableFuture == null ? UNLOADED_CHUNK_FUTURE : completableFuture;
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
     }
 
     public CompletableFuture<ChunkResult<ChunkAccess>> getFutureIfPresent(ChunkStatus leastStatus) {
-        return ChunkLevel.generationStatus(this.ticketLevel).isOrAfter(leastStatus) ? this.getFutureIfPresentUnchecked(leastStatus) : UNLOADED_CHUNK_FUTURE;
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
     }
 
-    public CompletableFuture<ChunkResult<LevelChunk>> getTickingChunkFuture() {
-        return this.tickingChunkFuture;
+    public final CompletableFuture<ChunkResult<LevelChunk>> getTickingChunkFuture() { // Paper - final for inline
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
     }
 
-    public CompletableFuture<ChunkResult<LevelChunk>> getEntityTickingChunkFuture() {
-        return this.entityTickingChunkFuture;
+    public final CompletableFuture<ChunkResult<LevelChunk>> getEntityTickingChunkFuture() { // Paper - final for inline
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk systemv
     }
 
-    public CompletableFuture<ChunkResult<LevelChunk>> getFullChunkFuture() {
-        return this.fullChunkFuture;
+    public final CompletableFuture<ChunkResult<LevelChunk>> getFullChunkFuture() { // Paper - final for inline
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk systemv
     }
 
     @Nullable
-    public LevelChunk getTickingChunk() {
-        return this.getTickingChunkFuture().getNow(UNLOADED_LEVEL_CHUNK).orElse(null);
+    public final LevelChunk getTickingChunk() { // Paper - final for inline
+        // Paper start - rewrite chunk system
+        if (!this.isTickingReady()) {
+            return null;
+        }
+        return (LevelChunk)this.getAvailableChunkNow();
+        // Paper end - rewrite chunk system
     }
 
     public CompletableFuture<?> getChunkSendSyncFuture() {
-        return this.sendSync;
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
     }
 
     @Nullable
     public LevelChunk getChunkToSend() {
-        return !this.sendSync.isDone() ? null : this.getTickingChunk();
+        return this.getSendingChunk(); // Paper - rewrite chunk system
     }
 
     @Nullable
     public ChunkStatus getLastAvailableStatus() {
-        for (int i = CHUNK_STATUSES.size() - 1; i >= 0; i--) {
-            ChunkStatus chunkStatus = CHUNK_STATUSES.get(i);
-            CompletableFuture<ChunkResult<ChunkAccess>> completableFuture = this.getFutureIfPresentUnchecked(chunkStatus);
-            if (completableFuture.getNow(UNLOADED_CHUNK).isSuccess()) {
-                return chunkStatus;
-            }
-        }
+        return this.newChunkHolder.getCurrentGenStatus(); // Paper - rewrite chunk system
+    }
 
-        return null;
+    // Paper start
+    public @Nullable ChunkStatus getChunkHolderStatus() {
+        return this.newChunkHolder.getCurrentGenStatus(); // Paper - rewrite chunk system
     }
+    // Paper end
 
     @Nullable
     public ChunkAccess getLastAvailable() {
-        for (int i = CHUNK_STATUSES.size() - 1; i >= 0; i--) {
-            ChunkStatus chunkStatus = CHUNK_STATUSES.get(i);
-            CompletableFuture<ChunkResult<ChunkAccess>> completableFuture = this.getFutureIfPresentUnchecked(chunkStatus);
-            if (!completableFuture.isCompletedExceptionally()) {
-                ChunkAccess chunkAccess = completableFuture.getNow(UNLOADED_CHUNK).orElse(null);
-                if (chunkAccess != null) {
-                    return chunkAccess;
-                }
-            }
-        }
-
-        return null;
+        return this.newChunkHolder.getCurrentChunk(); // Paper - rewrite chunk system
     }
 
-    public CompletableFuture<ChunkAccess> getChunkToSave() {
-        return this.chunkToSave;
-    }
+    // Paper - rewrite chunk system
 
     public void blockChanged(BlockPos pos) {
-        LevelChunk levelChunk = this.getTickingChunk();
-        if (levelChunk != null) {
+        // Paper start - replace player chunk loader
+        if (this.playersSentChunkTo.size() == 0) {
+            return;
+        }
+        // Paper end - replace player chunk loader
+        LevelChunk chunk = this.getSendingChunk(); // Paper - no-tick view distance
+
+        if (chunk != null) {
             int i = this.levelHeightAccessor.getSectionIndex(pos.getY());
+
+            if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
             if (this.changedBlocksPerSection[i] == null) {
-                this.hasChangedSections = true;
+                this.hasChangedSections = true; this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
                 this.changedBlocksPerSection[i] = new ShortOpenHashSet();
             }
 
@@ -167,35 +233,58 @@
     }
 
     public void sectionLightChanged(LightLayer lightType, int y) {
-        ChunkAccess chunkAccess = this.getFutureIfPresent(ChunkStatus.INITIALIZE_LIGHT).getNow(UNLOADED_CHUNK).orElse(null);
-        if (chunkAccess != null) {
-            chunkAccess.setUnsaved(true);
-            LevelChunk levelChunk = this.getTickingChunk();
-            if (levelChunk != null) {
-                int i = this.lightEngine.getMinLightSection();
-                int j = this.lightEngine.getMaxLightSection();
-                if (y >= i && y <= j) {
-                    int k = y - i;
+        ChunkAccess ichunkaccess = this.getAvailableChunkNow(); // Paper - rewrite chunk system
+
+        if (ichunkaccess != null) {
+            ichunkaccess.setUnsaved(true);
+            LevelChunk chunk = this.getSendingChunk(); // Paper - rewrite chunk system
+
+            if (this.playersSentChunkTo.size() != 0 && chunk != null) { // Paper - replace player chunk loader
+                int j = this.lightEngine.getMinLightSection();
+                int k = this.lightEngine.getMaxLightSection();
+
+                if (y >= j && y <= k) {
+                    this.addToBroadcastMap(); // Paper - optimise chunk tick iteration
+                    int l = y - j;
+
                     if (lightType == LightLayer.SKY) {
-                        this.skyChangedLightSectionFilter.set(k);
+                        this.skyChangedLightSectionFilter.set(l);
                     } else {
-                        this.blockChangedLightSectionFilter.set(k);
+                        this.blockChangedLightSectionFilter.set(l);
                     }
+
                 }
             }
         }
     }
 
+    // Paper start - starlight
+    public void broadcast(Packet<?> packet, boolean onChunkViewEdge) {
+        this.broadcast(this.getPlayers(onChunkViewEdge), packet); // Paper - rewrite chunk system
+    }
+    // Paper end - starlight
+    // Paper start - optimise chunk tick iteration
+    public final boolean needsBroadcastChanges() {
+        return this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty();
+    }
+
+    private void addToBroadcastMap() {
+        io.papermc.paper.util.TickThread.ensureTickThread(this.chunkMap.level, this.pos, "Asynchronous ChunkHolder update is not allowed");
+        this.chunkMap.needsChangeBroadcasting.add(this);
+    }
+    // Paper end - optimise chunk tick iteration
+
     public void broadcastChanges(LevelChunk chunk) {
-        if (this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) {
-            Level level = chunk.getLevel();
+        if (this.needsBroadcastChanges()) { // Paper - optimise chunk tick iteration; moved into above, other logic needs to call
+            Level world = chunk.getLevel();
+            List list;
+
             if (!this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) {
-                List<ServerPlayer> list = this.playerProvider.getPlayers(this.pos, true);
+                list = this.getPlayers(true); // Paper - rewrite chunk system
                 if (!list.isEmpty()) {
-                    ClientboundLightUpdatePacket clientboundLightUpdatePacket = new ClientboundLightUpdatePacket(
-                        chunk.getPos(), this.lightEngine, this.skyChangedLightSectionFilter, this.blockChangedLightSectionFilter
-                    );
-                    this.broadcast(list, clientboundLightUpdatePacket);
+                    ClientboundLightUpdatePacket packetplayoutlightupdate = new ClientboundLightUpdatePacket(chunk.getPos(), this.lightEngine, this.skyChangedLightSectionFilter, this.blockChangedLightSectionFilter);
+
+                    this.broadcast(list, packetplayoutlightupdate);
                 }
 
                 this.skyChangedLightSectionFilter.clear();
@@ -203,27 +292,34 @@
             }
 
             if (this.hasChangedSections) {
-                List<ServerPlayer> list2 = this.playerProvider.getPlayers(this.pos, false);
+                list = this.getPlayers(false); // Paper - rewrite chunk system
 
-                for (int i = 0; i < this.changedBlocksPerSection.length; i++) {
-                    ShortSet shortSet = this.changedBlocksPerSection[i];
-                    if (shortSet != null) {
+                for (int i = 0; i < this.changedBlocksPerSection.length; ++i) {
+                    ShortSet shortset = this.changedBlocksPerSection[i];
+
+                    if (shortset != null) {
                         this.changedBlocksPerSection[i] = null;
-                        if (!list2.isEmpty()) {
+                        if (!list.isEmpty()) {
                             int j = this.levelHeightAccessor.getSectionYFromSectionIndex(i);
-                            SectionPos sectionPos = SectionPos.of(chunk.getPos(), j);
-                            if (shortSet.size() == 1) {
-                                BlockPos blockPos = sectionPos.relativeToBlockPos(shortSet.iterator().nextShort());
-                                BlockState blockState = level.getBlockState(blockPos);
-                                this.broadcast(list2, new ClientboundBlockUpdatePacket(blockPos, blockState));
-                                this.broadcastBlockEntityIfNeeded(list2, level, blockPos, blockState);
+                            SectionPos sectionposition = SectionPos.of(chunk.getPos(), j);
+
+                            if (shortset.size() == 1) {
+                                BlockPos blockposition = sectionposition.relativeToBlockPos(shortset.iterator().nextShort());
+                                BlockState iblockdata = world.getBlockState(blockposition);
+
+                                this.broadcast(list, new ClientboundBlockUpdatePacket(blockposition, iblockdata));
+                                this.broadcastBlockEntityIfNeeded(list, world, blockposition, iblockdata);
                             } else {
-                                LevelChunkSection levelChunkSection = chunk.getSection(i);
-                                ClientboundSectionBlocksUpdatePacket clientboundSectionBlocksUpdatePacket = new ClientboundSectionBlocksUpdatePacket(
-                                    sectionPos, shortSet, levelChunkSection
-                                );
-                                this.broadcast(list2, clientboundSectionBlocksUpdatePacket);
-                                clientboundSectionBlocksUpdatePacket.runUpdates((pos, state) -> this.broadcastBlockEntityIfNeeded(list2, level, pos, state));
+                                LevelChunkSection chunksection = chunk.getSection(i);
+                                ClientboundSectionBlocksUpdatePacket packetplayoutmultiblockchange = new ClientboundSectionBlocksUpdatePacket(sectionposition, shortset, chunksection);
+
+                                this.broadcast(list, packetplayoutmultiblockchange);
+                                // CraftBukkit start
+                                List finalList = list;
+                                packetplayoutmultiblockchange.runUpdates((blockposition1, iblockdata1) -> {
+                                    this.broadcastBlockEntityIfNeeded(finalList, world, blockposition1, iblockdata1);
+                                    // CraftBukkit end
+                                });
                             }
                         }
                     }
@@ -238,222 +334,95 @@
         if (state.hasBlockEntity()) {
             this.broadcastBlockEntity(players, world, pos);
         }
+
     }
 
     private void broadcastBlockEntity(List<ServerPlayer> players, Level world, BlockPos pos) {
-        BlockEntity blockEntity = world.getBlockEntity(pos);
-        if (blockEntity != null) {
-            Packet<?> packet = blockEntity.getUpdatePacket();
+        BlockEntity tileentity = world.getBlockEntity(pos);
+
+        if (tileentity != null) {
+            Packet<?> packet = tileentity.getUpdatePacket();
+
             if (packet != null) {
                 this.broadcast(players, packet);
             }
         }
-    }
 
-    private void broadcast(List<ServerPlayer> players, Packet<?> packet) {
-        players.forEach(player -> player.connection.send(packet));
     }
 
-    public CompletableFuture<ChunkResult<ChunkAccess>> getOrScheduleFuture(ChunkStatus targetStatus, ChunkMap chunkStorage) {
-        int i = targetStatus.getIndex();
-        CompletableFuture<ChunkResult<ChunkAccess>> completableFuture = this.futures.get(i);
-        if (completableFuture != null) {
-            ChunkResult<ChunkAccess> chunkResult = completableFuture.getNow(NOT_DONE_YET);
-            if (chunkResult == null) {
-                String string = "value in future for status: " + targetStatus + " was incorrectly set to null at chunk: " + this.pos;
-                throw chunkStorage.debugFuturesAndCreateReportedException(new IllegalStateException("null value previously set for chunk status"), string);
-            }
+    // Paper start - rewrite chunk system
+    public List<ServerPlayer> getPlayers(boolean onlyOnWatchDistanceEdge) {
+        List<ServerPlayer> ret = new java.util.ArrayList<>();
 
-            if (chunkResult == NOT_DONE_YET || chunkResult.isSuccess()) {
-                return completableFuture;
+        for (int i = 0, len = this.playersSentChunkTo.size(); i < len; ++i) {
+            ServerPlayer player = this.playersSentChunkTo.getUnchecked(i);
+            if (onlyOnWatchDistanceEdge && !this.chunkMap.level.playerChunkLoader.isChunkSent(player, this.pos.x, this.pos.z, onlyOnWatchDistanceEdge)) {
+                continue;
             }
+            ret.add(player);
         }
 
-        if (ChunkLevel.generationStatus(this.ticketLevel).isOrAfter(targetStatus)) {
-            CompletableFuture<ChunkResult<ChunkAccess>> completableFuture2 = chunkStorage.schedule(this, targetStatus);
-            this.updateChunkToSave(completableFuture2, "schedule " + targetStatus);
-            this.futures.set(i, completableFuture2);
-            return completableFuture2;
-        } else {
-            return completableFuture == null ? UNLOADED_CHUNK_FUTURE : completableFuture;
-        }
-    }
-
-    protected void addSaveDependency(String thenDesc, CompletableFuture<?> then) {
-        if (this.chunkToSaveHistory != null) {
-            this.chunkToSaveHistory.push(new ChunkHolder.ChunkSaveDebug(Thread.currentThread(), then, thenDesc));
-        }
-
-        this.chunkToSave = this.chunkToSave.thenCombine((CompletionStage<? extends Object>)then, (result, thenResult) -> (ChunkAccess)result);
+        return ret;
     }
+    // Paper end - rewrite chunk system
 
-    private void updateChunkToSave(CompletableFuture<? extends ChunkResult<? extends ChunkAccess>> then, String thenDesc) {
-        if (this.chunkToSaveHistory != null) {
-            this.chunkToSaveHistory.push(new ChunkHolder.ChunkSaveDebug(Thread.currentThread(), then, thenDesc));
-        }
 
-        this.chunkToSave = this.chunkToSave
-            .thenCombine(then, (chunk, otherChunk) -> ChunkResult.orElse((ChunkResult<? extends ChunkAccess>)otherChunk, (ChunkAccess)chunk));
+    private void broadcast(List<ServerPlayer> players, Packet<?> packet) {
+        players.forEach((entityplayer) -> {
+            entityplayer.connection.send(packet);
+        });
     }
 
-    public void addSendDependency(CompletableFuture<?> postProcessingFuture) {
-        if (this.sendSync.isDone()) {
-            this.sendSync = postProcessingFuture;
-        } else {
-            this.sendSync = this.sendSync.thenCombine((CompletionStage<? extends Object>)postProcessingFuture, (object, object2) -> null);
-        }
-    }
+    // Paper - rewrite chunk system
 
     public FullChunkStatus getFullStatus() {
-        return ChunkLevel.fullStatus(this.ticketLevel);
+        return this.newChunkHolder.getChunkStatus(); // Paper - rewrite chunk system
     }
 
-    public ChunkPos getPos() {
+    public final ChunkPos getPos() { // Paper - final for inline
         return this.pos;
     }
 
-    public int getTicketLevel() {
-        return this.ticketLevel;
-    }
-
-    public int getQueueLevel() {
-        return this.queueLevel;
-    }
-
-    private void setQueueLevel(int level) {
-        this.queueLevel = level;
-    }
-
-    public void setTicketLevel(int level) {
-        this.ticketLevel = level;
+    public final int getTicketLevel() { // Paper - final for inline
+        return this.newChunkHolder.getTicketLevel(); // Paper - rewrite chunk system
     }
 
-    private void scheduleFullChunkPromotion(
-        ChunkMap chunkMap, CompletableFuture<ChunkResult<LevelChunk>> completableFuture, Executor executor, FullChunkStatus fullChunkStatus
-    ) {
-        this.pendingFullStateConfirmation.cancel(false);
-        CompletableFuture<Void> completableFuture2 = new CompletableFuture<>();
-        completableFuture2.thenRunAsync(() -> chunkMap.onFullChunkStatusChange(this.pos, fullChunkStatus), executor);
-        this.pendingFullStateConfirmation = completableFuture2;
-        completableFuture.thenAccept(chunkResult -> chunkResult.ifSuccess(levelChunk -> completableFuture2.complete(null)));
-    }
+    // Paper - rewrite chunk system
 
-    private void demoteFullChunk(ChunkMap chunkMap, FullChunkStatus fullChunkStatus) {
-        this.pendingFullStateConfirmation.cancel(false);
-        chunkMap.onFullChunkStatusChange(this.pos, fullChunkStatus);
+    public void replaceProtoChunk(ImposterProtoChunk chunk) {
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
     }
 
-    protected void updateFutures(ChunkMap chunkStorage, Executor executor) {
-        ChunkStatus chunkStatus = ChunkLevel.generationStatus(this.oldTicketLevel);
-        ChunkStatus chunkStatus2 = ChunkLevel.generationStatus(this.ticketLevel);
-        boolean bl = ChunkLevel.isLoaded(this.oldTicketLevel);
-        boolean bl2 = ChunkLevel.isLoaded(this.ticketLevel);
-        FullChunkStatus fullChunkStatus = ChunkLevel.fullStatus(this.oldTicketLevel);
-        FullChunkStatus fullChunkStatus2 = ChunkLevel.fullStatus(this.ticketLevel);
-        if (bl) {
-            ChunkResult<ChunkAccess> chunkResult = ChunkResult.error(() -> "Unloaded ticket level " + this.pos);
-
-            for (int i = bl2 ? chunkStatus2.getIndex() + 1 : 0; i <= chunkStatus.getIndex(); i++) {
-                CompletableFuture<ChunkResult<ChunkAccess>> completableFuture = this.futures.get(i);
-                if (completableFuture == null) {
-                    this.futures.set(i, CompletableFuture.completedFuture(chunkResult));
-                }
-            }
-        }
-
-        boolean bl3 = fullChunkStatus.isOrAfter(FullChunkStatus.FULL);
-        boolean bl4 = fullChunkStatus2.isOrAfter(FullChunkStatus.FULL);
-        this.wasAccessibleSinceLastSave |= bl4;
-        if (!bl3 && bl4) {
-            this.fullChunkFuture = chunkStorage.prepareAccessibleChunk(this);
-            this.scheduleFullChunkPromotion(chunkStorage, this.fullChunkFuture, executor, FullChunkStatus.FULL);
-            this.updateChunkToSave(this.fullChunkFuture, "full");
-        }
-
-        if (bl3 && !bl4) {
-            this.fullChunkFuture.complete(UNLOADED_LEVEL_CHUNK);
-            this.fullChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
-        }
-
-        boolean bl5 = fullChunkStatus.isOrAfter(FullChunkStatus.BLOCK_TICKING);
-        boolean bl6 = fullChunkStatus2.isOrAfter(FullChunkStatus.BLOCK_TICKING);
-        if (!bl5 && bl6) {
-            this.tickingChunkFuture = chunkStorage.prepareTickingChunk(this);
-            this.scheduleFullChunkPromotion(chunkStorage, this.tickingChunkFuture, executor, FullChunkStatus.BLOCK_TICKING);
-            this.updateChunkToSave(this.tickingChunkFuture, "ticking");
-        }
-
-        if (bl5 && !bl6) {
-            this.tickingChunkFuture.complete(UNLOADED_LEVEL_CHUNK);
-            this.tickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
-        }
-
-        boolean bl7 = fullChunkStatus.isOrAfter(FullChunkStatus.ENTITY_TICKING);
-        boolean bl8 = fullChunkStatus2.isOrAfter(FullChunkStatus.ENTITY_TICKING);
-        if (!bl7 && bl8) {
-            if (this.entityTickingChunkFuture != UNLOADED_LEVEL_CHUNK_FUTURE) {
-                throw (IllegalStateException)Util.pauseInIde(new IllegalStateException());
-            }
-
-            this.entityTickingChunkFuture = chunkStorage.prepareEntityTickingChunk(this);
-            this.scheduleFullChunkPromotion(chunkStorage, this.entityTickingChunkFuture, executor, FullChunkStatus.ENTITY_TICKING);
-            this.updateChunkToSave(this.entityTickingChunkFuture, "entity ticking");
-        }
-
-        if (bl7 && !bl8) {
-            this.entityTickingChunkFuture.complete(UNLOADED_LEVEL_CHUNK);
-            this.entityTickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE;
-        }
-
-        if (!fullChunkStatus2.isOrAfter(fullChunkStatus)) {
-            this.demoteFullChunk(chunkStorage, fullChunkStatus2);
-        }
-
-        this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel);
-        this.oldTicketLevel = this.ticketLevel;
+    public List<Pair<ChunkStatus, CompletableFuture<ChunkResult<ChunkAccess>>>> getAllFutures() {
+        throw new UnsupportedOperationException(); // Paper - rewrite chunk system
     }
 
-    public boolean wasAccessibleSinceLastSave() {
-        return this.wasAccessibleSinceLastSave;
-    }
+    @FunctionalInterface
+    public interface LevelChangeListener {
 
-    public void refreshAccessibility() {
-        this.wasAccessibleSinceLastSave = ChunkLevel.fullStatus(this.ticketLevel).isOrAfter(FullChunkStatus.FULL);
+        void onLevelChange(ChunkPos pos, IntSupplier levelGetter, int targetLevel, IntConsumer levelSetter);
     }
 
-    public void replaceProtoChunk(ImposterProtoChunk chunk) {
-        for (int i = 0; i < this.futures.length(); i++) {
-            CompletableFuture<ChunkResult<ChunkAccess>> completableFuture = this.futures.get(i);
-            if (completableFuture != null) {
-                ChunkAccess chunkAccess = completableFuture.getNow(UNLOADED_CHUNK).orElse(null);
-                if (chunkAccess instanceof ProtoChunk) {
-                    this.futures.set(i, CompletableFuture.completedFuture(ChunkResult.of(chunk)));
-                }
-            }
-        }
+    public interface PlayerProvider {
 
-        this.updateChunkToSave(CompletableFuture.completedFuture(ChunkResult.of(chunk.getWrapped())), "replaceProto");
+        List<ServerPlayer> getPlayers(ChunkPos chunkPos, boolean onlyOnWatchDistanceEdge);
     }
 
-    public List<Pair<ChunkStatus, CompletableFuture<ChunkResult<ChunkAccess>>>> getAllFutures() {
-        List<Pair<ChunkStatus, CompletableFuture<ChunkResult<ChunkAccess>>>> list = new ArrayList<>();
-
-        for (int i = 0; i < CHUNK_STATUSES.size(); i++) {
-            list.add(Pair.of(CHUNK_STATUSES.get(i), this.futures.get(i)));
-        }
+    private static record ChunkSaveDebug(Thread thread, CompletableFuture<?> future, String source) {
 
-        return list;
     }
 
-    static record ChunkSaveDebug(Thread thread, CompletableFuture<?> future, String source) {
+    // Paper start
+    public final boolean isEntityTickingReady() {
+        return this.newChunkHolder.isEntityTickingReady(); // Paper - rewrite chunk system
     }
 
-    @FunctionalInterface
-    public interface LevelChangeListener {
-        void onLevelChange(ChunkPos pos, IntSupplier levelGetter, int targetLevel, IntConsumer levelSetter);
+    public final boolean isTickingReady() {
+        return this.newChunkHolder.isTickingReady(); // Paper - rewrite chunk system
     }
 
-    public interface PlayerProvider {
-        List<ServerPlayer> getPlayers(ChunkPos chunkPos, boolean onlyOnWatchDistanceEdge);
+    public final boolean isFullChunkReady() {
+        return this.newChunkHolder.isFullChunkReady(); // Paper - rewrite chunk system
     }
+    // Paper end
 }
