--- /dev/null
+++ b/ca/spottedleaf/moonrise/paper/util/BaseChunkSystemHooks.java
@@ -1,0 +_,330 @@
+package ca.spottedleaf.moonrise.paper.util;
+
+import ca.spottedleaf.concurrentutil.util.Priority;
+import com.mojang.logging.LogUtils;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ChunkResult;
+import net.minecraft.server.level.FullChunkStatus;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.server.level.TicketType;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.chunk.status.ChunkPyramid;
+import net.minecraft.world.level.chunk.status.ChunkStatus;
+import net.minecraft.world.level.chunk.status.ChunkStep;
+import org.bukkit.Bukkit;
+import org.slf4j.Logger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+public abstract class BaseChunkSystemHooks implements ca.spottedleaf.moonrise.common.util.ChunkSystemHooks {
+
+    private static final Logger LOGGER = LogUtils.getLogger();
+    private static final ChunkStep FULL_CHUNK_STEP = ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FULL);
+    private static final TicketType CHUNK_LOAD = TicketType.CHUNK_LOAD;
+
+    private long chunkLoadCounter = 0L;
+
+    private static int getDistance(final ChunkStatus status) {
+        return FULL_CHUNK_STEP.getAccumulatedRadiusOf(status);
+    }
+
+    @Override
+    public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run) {
+        this.scheduleChunkTask(level, chunkX, chunkZ, run, Priority.NORMAL);
+    }
+
+    @Override
+    public void scheduleChunkTask(final ServerLevel level, final int chunkX, final int chunkZ, final Runnable run, final Priority priority) {
+        level.chunkSource.mainThreadProcessor.execute(run);
+    }
+
+    @Override
+    public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final boolean gen,
+                                  final ChunkStatus toStatus, final boolean addTicket, final Priority priority,
+                                  final Consumer<ChunkAccess> onComplete) {
+        if (gen) {
+            this.scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+            return;
+        }
+        this.scheduleChunkLoad(level, chunkX, chunkZ, ChunkStatus.EMPTY, addTicket, priority, (final ChunkAccess chunk) -> {
+            if (chunk == null) {
+                if (onComplete != null) {
+                    onComplete.accept(null);
+                }
+            } else {
+                if (chunk.getPersistedStatus().isOrAfter(toStatus)) {
+                    BaseChunkSystemHooks.this.scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+                } else {
+                    if (onComplete != null) {
+                        onComplete.accept(null);
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public void scheduleChunkLoad(final ServerLevel level, final int chunkX, final int chunkZ, final ChunkStatus toStatus,
+                                  final boolean addTicket, final Priority priority, final Consumer<ChunkAccess> onComplete) {
+        if (!Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
+            this.scheduleChunkTask(level, chunkX, chunkZ, () -> {
+                BaseChunkSystemHooks.this.scheduleChunkLoad(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+            }, priority);
+            return;
+        }
+
+        final int minLevel = 33 + getDistance(toStatus);
+        final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+
+        if (addTicket) {
+            level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel);
+        }
+        level.chunkSource.runDistanceManagerUpdates();
+
+        final Consumer<ChunkAccess> loadCallback = (final ChunkAccess chunk) -> {
+            try {
+                if (onComplete != null) {
+                    onComplete.accept(chunk);
+                }
+            } catch (final Throwable thr) {
+                LOGGER.error("Exception handling chunk load callback", thr);
+                com.destroystokyo.paper.util.SneakyThrow.sneaky(thr);
+            } finally {
+                if (addTicket) {
+                    level.chunkSource.addTicketAtLevel(net.minecraft.server.level.TicketType.UNKNOWN, chunkPos, minLevel);
+                    level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel);
+                }
+            }
+        };
+
+        final ChunkHolder holder = level.chunkSource.chunkMap.updatingChunkMap.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
+
+        if (holder == null || holder.getTicketLevel() > minLevel) {
+            loadCallback.accept(null);
+            return;
+        }
+
+        final CompletableFuture<ChunkResult<ChunkAccess>> loadFuture = holder.scheduleChunkGenerationTask(toStatus, level.chunkSource.chunkMap);
+
+        if (loadFuture.isDone()) {
+            loadCallback.accept(loadFuture.join().orElse(null));
+            return;
+        }
+
+        loadFuture.whenCompleteAsync((final ChunkResult<ChunkAccess> result, final Throwable thr) -> {
+            if (thr != null) {
+                loadCallback.accept(null);
+                return;
+            }
+            loadCallback.accept(result.orElse(null));
+        }, (final Runnable r) -> {
+            BaseChunkSystemHooks.this.scheduleChunkTask(level, chunkX, chunkZ, r, Priority.HIGHEST);
+        });
+    }
+
+    @Override
+    public void scheduleTickingState(final ServerLevel level, final int chunkX, final int chunkZ,
+                                     final FullChunkStatus toStatus, final boolean addTicket,
+                                     final Priority priority, final Consumer<LevelChunk> onComplete) {
+        // This method goes unused until the chunk system rewrite
+        if (toStatus == FullChunkStatus.INACCESSIBLE) {
+            throw new IllegalArgumentException("Cannot wait for INACCESSIBLE status");
+        }
+
+        if (!Bukkit.isOwnedByCurrentRegion(level.getWorld(), chunkX, chunkZ)) {
+            this.scheduleChunkTask(level, chunkX, chunkZ, () -> {
+                BaseChunkSystemHooks.this.scheduleTickingState(level, chunkX, chunkZ, toStatus, addTicket, priority, onComplete);
+            }, priority);
+            return;
+        }
+
+        final int minLevel = 33 - (toStatus.ordinal() - 1);
+        final int radius = toStatus.ordinal() - 1;
+        final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
+
+        if (addTicket) {
+            level.chunkSource.addTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel);
+        }
+        level.chunkSource.runDistanceManagerUpdates();
+
+        final Consumer<LevelChunk> loadCallback = (final LevelChunk chunk) -> {
+            try {
+                if (onComplete != null) {
+                    onComplete.accept(chunk);
+                }
+            } catch (final Throwable thr) {
+                LOGGER.error("Exception handling chunk load callback", thr);
+                com.destroystokyo.paper.util.SneakyThrow.sneaky(thr);
+            } finally {
+                if (addTicket) {
+                    level.chunkSource.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, minLevel);
+                    level.chunkSource.removeTicketAtLevel(CHUNK_LOAD, chunkPos, minLevel);
+                }
+            }
+        };
+
+        final ChunkHolder holder = level.chunkSource.chunkMap.updatingChunkMap.get(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(chunkX, chunkZ));
+
+        if (holder == null || holder.getTicketLevel() > minLevel) {
+            loadCallback.accept(null);
+            return;
+        }
+
+        final CompletableFuture<ChunkResult<LevelChunk>> tickingState;
+        switch (toStatus) {
+            case FULL: {
+                tickingState = holder.getFullChunkFuture();
+                break;
+            }
+            case BLOCK_TICKING: {
+                tickingState = holder.getTickingChunkFuture();
+                break;
+            }
+            case ENTITY_TICKING: {
+                tickingState = holder.getEntityTickingChunkFuture();
+                break;
+            }
+            default: {
+                throw new IllegalStateException("Cannot reach here");
+            }
+        }
+
+        if (tickingState.isDone()) {
+            loadCallback.accept(tickingState.join().orElse(null));
+            return;
+        }
+
+        tickingState.whenCompleteAsync((final ChunkResult<LevelChunk> result, final Throwable thr) -> {
+            if (thr != null) {
+                loadCallback.accept(null);
+                return;
+            }
+            loadCallback.accept(result.orElse(null));
+        }, (final Runnable r) -> {
+            BaseChunkSystemHooks.this.scheduleChunkTask(level, chunkX, chunkZ, r, Priority.HIGHEST);
+        });
+    }
+
+    @Override
+    public List<ChunkHolder> getVisibleChunkHolders(final ServerLevel level) {
+        return new ArrayList<>(level.chunkSource.chunkMap.visibleChunkMap.values());
+    }
+
+    @Override
+    public List<ChunkHolder> getUpdatingChunkHolders(final ServerLevel level) {
+        return new ArrayList<>(level.chunkSource.chunkMap.updatingChunkMap.values());
+    }
+
+    @Override
+    public int getVisibleChunkHolderCount(final ServerLevel level) {
+        return level.chunkSource.chunkMap.visibleChunkMap.size();
+    }
+
+    @Override
+    public int getUpdatingChunkHolderCount(final ServerLevel level) {
+        return level.chunkSource.chunkMap.updatingChunkMap.size();
+    }
+
+    @Override
+    public boolean hasAnyChunkHolders(final ServerLevel level) {
+        return this.getUpdatingChunkHolderCount(level) != 0;
+    }
+
+    @Override
+    public void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkHolderDelete(final ServerLevel level, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkPreBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkPostNotBorder(final LevelChunk chunk, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkTicking(final LevelChunk chunk, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkNotTicking(final LevelChunk chunk, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public void onChunkNotEntityTicking(final LevelChunk chunk, final ChunkHolder holder) {
+
+    }
+
+    @Override
+    public ChunkHolder getUnloadingChunkHolder(final ServerLevel level, final int chunkX, final int chunkZ) {
+        return level.chunkSource.chunkMap.getUnloadingChunkHolder(chunkX, chunkZ);
+    }
+
+    @Override
+    public int getSendViewDistance(final ServerPlayer player) {
+        return this.getViewDistance(player);
+    }
+
+    @Override
+    public int getViewDistance(final ServerPlayer player) {
+        final ServerLevel level = player.level();
+        if (level == null) {
+            return Bukkit.getViewDistance();
+        }
+        return level.chunkSource.chunkMap.serverViewDistance;
+    }
+
+    @Override
+    public int getTickViewDistance(final ServerPlayer player) {
+        final ServerLevel level = player.level();
+        if (level == null) {
+            return Bukkit.getSimulationDistance();
+        }
+        return level.chunkSource.chunkMap.distanceManager.simulationDistance;
+    }
+
+    @Override
+    public void addPlayerToDistanceMaps(final ServerLevel world, final ServerPlayer player) {
+
+    }
+
+    @Override
+    public void removePlayerFromDistanceMaps(final ServerLevel world, final ServerPlayer player) {
+
+    }
+
+    @Override
+    public void updateMaps(final ServerLevel world, final ServerPlayer player) {
+
+    }
+}
