--- a/net/minecraft/world/entity/ai/village/poi/PoiManager.java
+++ b/net/minecraft/world/entity/ai/village/poi/PoiManager.java
@@ -40,12 +_,137 @@
 import net.minecraft.world.level.chunk.storage.SectionStorage;
 import net.minecraft.world.level.chunk.storage.SimpleRegionStorage;
 
-public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> {
+public class PoiManager extends SectionStorage<PoiSection, PoiSection.Packed> implements ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiManager { // Paper - rewrite chunk system
     public static final int MAX_VILLAGE_DISTANCE = 6;
     public static final int VILLAGE_SECTION_SIZE = 1;
     private final PoiManager.DistanceTracker distanceTracker;
     private final LongSet loadedChunks = new LongOpenHashSet();
 
+    // Paper start - rewrite chunk system
+    private final net.minecraft.server.level.ServerLevel world;
+
+    // the vanilla tracker needs to be replaced because it does not support level removes, and we need level removes
+    // to support poi unloading
+    private final ca.spottedleaf.moonrise.common.misc.Delayed26WayDistancePropagator3D villageDistanceTracker = new ca.spottedleaf.moonrise.common.misc.Delayed26WayDistancePropagator3D();
+
+    private static final int POI_DATA_SOURCE = 7;
+
+    private static int convertBetweenLevels(final int level) {
+        return POI_DATA_SOURCE - level;
+    }
+
+    private void updateDistanceTracking(long section) {
+        if (this.isVillageCenter(section)) {
+            this.villageDistanceTracker.setSource(section, POI_DATA_SOURCE);
+        } else {
+            this.villageDistanceTracker.removeSource(section);
+        }
+    }
+
+    @Override
+    public Optional<PoiSection> get(final long pos) {
+        final int chunkX = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionX(pos);
+        final int chunkY = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionY(pos);
+        final int chunkZ = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionZ(pos);
+
+        ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
+
+        final ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk ret = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
+
+        return ret == null ? Optional.empty() : ret.getSectionForVanilla(chunkY);
+    }
+
+    @Override
+    public Optional<PoiSection> getOrLoad(final long pos) {
+        final int chunkX = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionX(pos);
+        final int chunkY = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionY(pos);
+        final int chunkZ = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionZ(pos);
+
+        ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
+
+        final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager manager = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
+
+        if (chunkY >= ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this.world) && chunkY <= ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(this.world)) {
+            final ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
+            if (ret != null) {
+                return ret.getSectionForVanilla(chunkY);
+            } else {
+                return manager.loadPoiChunk(chunkX, chunkZ).getSectionForVanilla(chunkY);
+            }
+        }
+        // retain vanilla behavior: do not load section if out of bounds!
+        return Optional.empty();
+    }
+
+    @Override
+    protected PoiSection getOrCreate(final long pos) {
+        final int chunkX = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionX(pos);
+        final int chunkY = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionY(pos);
+        final int chunkZ = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionZ(pos);
+
+        ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Accessing poi chunk off-main");
+
+        final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager manager = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
+
+        final ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk ret = manager.getPoiChunkIfLoaded(chunkX, chunkZ, true);
+        if (ret != null) {
+            return ret.getOrCreateSection(chunkY);
+        } else {
+            return manager.loadPoiChunk(chunkX, chunkZ).getOrCreateSection(chunkY);
+        }
+    }
+
+    @Override
+    public final net.minecraft.server.level.ServerLevel moonrise$getWorld() {
+        return this.world;
+    }
+
+    @Override
+    public final void moonrise$onUnload(final long coordinate) { // Paper - rewrite chunk system
+        final int chunkX = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(coordinate);
+        final int chunkZ = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(coordinate);
+
+        final int minY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this.world);
+        final int maxY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(this.world);
+
+        ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Unloading poi chunk off-main");
+        for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
+            final long sectionPos = SectionPos.asLong(chunkX, sectionY, chunkZ);
+            this.updateDistanceTracking(sectionPos);
+        }
+    }
+
+    @Override
+    public final void moonrise$loadInPoiChunk(final ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk poiChunk) {
+        final int chunkX = poiChunk.chunkX;
+        final int chunkZ = poiChunk.chunkZ;
+
+        final int minY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this.world);
+        final int maxY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(this.world);
+
+        ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread(this.world, chunkX, chunkZ, "Loading poi chunk off-main");
+        for (int sectionY = minY; sectionY <= maxY; ++sectionY) {
+            final PoiSection section = poiChunk.getSection(sectionY);
+            if (section != null && !((ca.spottedleaf.moonrise.patches.chunk_system.level.poi.ChunkSystemPoiSection)section).moonrise$isEmpty()) {
+                this.onSectionLoad(SectionPos.asLong(chunkX, sectionY, chunkZ));
+            }
+        }
+    }
+
+    @Override
+    public final void moonrise$checkConsistency(final net.minecraft.world.level.chunk.ChunkAccess chunk) {
+        final int chunkX = chunk.getPos().x;
+        final int chunkZ = chunk.getPos().z;
+
+        final int minY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(chunk);
+        final int maxY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(chunk);
+        final LevelChunkSection[] sections = chunk.getSections();
+        for (int section = minY; section <= maxY; ++section) {
+            this.checkConsistencyWithBlocks(SectionPos.of(chunkX, section, chunkZ), sections[section - minY]);
+        }
+    }
+    // Paper end - rewrite chunk system
+
     public PoiManager(
         RegionStorageInfo info,
         Path folder,
@@ -66,6 +_,7 @@
             levelHeightAccessor
         );
         this.distanceTracker = new PoiManager.DistanceTracker();
+        this.world = (net.minecraft.server.level.ServerLevel)levelHeightAccessor; // Paper - rewrite chunk system
     }
 
     @Nullable
@@ -131,36 +_,47 @@
     public Optional<BlockPos> find(
         Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int distance, PoiManager.Occupancy status
     ) {
-        return this.findAll(typePredicate, posPredicate, pos, distance, status).findFirst();
+        // Paper start - re-route to faster logic
+        BlockPos ret = io.papermc.paper.util.PoiAccess.findAnyPoiPosition(this, typePredicate, posPredicate, pos, distance, status, false);
+        return Optional.ofNullable(ret);
+        // Paper end
     }
 
     public Optional<BlockPos> findClosest(Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int distance, PoiManager.Occupancy status) {
-        return this.getInRange(typePredicate, pos, distance, status).map(PoiRecord::getPos).min(Comparator.comparingDouble(blockPos -> blockPos.distSqr(pos)));
+        // Paper start - re-route to faster logic
+        BlockPos closestPos = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, null, pos, distance, distance * distance, status, false);
+        return Optional.ofNullable(closestPos);
+        // Paper end - re-route to faster logic
     }
 
     public Optional<Pair<Holder<PoiType>, BlockPos>> findClosestWithType(
         Predicate<Holder<PoiType>> typePredicate, BlockPos pos, int distance, PoiManager.Occupancy status
     ) {
-        return this.getInRange(typePredicate, pos, distance, status)
-            .min(Comparator.comparingDouble(poiRecord -> poiRecord.getPos().distSqr(pos)))
-            .map(poiRecord -> Pair.of(poiRecord.getPoiType(), poiRecord.getPos()));
+        // Paper start - re-route to faster logic
+        return Optional.ofNullable(io.papermc.paper.util.PoiAccess.findClosestPoiDataTypeAndPosition(
+            this, typePredicate, null, pos, distance, distance * distance, status, false
+        ));
+        // Paper end - re-route to faster logic
     }
 
     public Optional<BlockPos> findClosest(
         Predicate<Holder<PoiType>> typePredicate, Predicate<BlockPos> posPredicate, BlockPos pos, int distance, PoiManager.Occupancy status
     ) {
-        return this.getInRange(typePredicate, pos, distance, status)
-            .map(PoiRecord::getPos)
-            .filter(posPredicate)
-            .min(Comparator.comparingDouble(blockPos -> blockPos.distSqr(pos)));
+        // Paper start - re-route to faster logic
+        BlockPos closestPos = io.papermc.paper.util.PoiAccess.findClosestPoiDataPosition(this, typePredicate, posPredicate, pos, distance, distance * distance, status, false);
+        return Optional.ofNullable(closestPos);
+        // Paper end - re-route to faster logic
     }
 
     public Optional<BlockPos> take(
         Predicate<Holder<PoiType>> typePredicate, BiPredicate<Holder<PoiType>, BlockPos> combinedTypePosPredicate, BlockPos pos, int distance
     ) {
-        return this.getInRange(typePredicate, pos, distance, PoiManager.Occupancy.HAS_SPACE)
-            .filter(poiRecord -> combinedTypePosPredicate.test(poiRecord.getPoiType(), poiRecord.getPos()))
-            .findFirst()
+        // Paper start - re-route to faster logic
+        final @javax.annotation.Nullable PoiRecord closest = io.papermc.paper.util.PoiAccess.findClosestPoiDataRecord(
+            this, typePredicate, combinedTypePosPredicate, pos, distance, distance * distance, Occupancy.HAS_SPACE, false
+        );
+        return Optional.ofNullable(closest)
+            // Paper end - re-route to faster logic
             .map(poiRecord -> {
                 poiRecord.acquireTicket();
                 return poiRecord.getPos();
@@ -175,8 +_,21 @@
         int distance,
         RandomSource random
     ) {
-        List<PoiRecord> list = Util.toShuffledList(this.getInRange(typePredicate, pos, distance, status), random);
-        return list.stream().filter(poiRecord -> posPredicate.test(poiRecord.getPos())).findFirst().map(PoiRecord::getPos);
+        // Paper start - re-route to faster logic
+        List<PoiRecord> list = new java.util.ArrayList<>();
+        io.papermc.paper.util.PoiAccess.findAnyPoiRecords(
+            this, typePredicate, posPredicate, pos, distance, status, false, Integer.MAX_VALUE, list
+        );
+
+        // the old method shuffled the list and then tried to find the first element in it that
+        // matched positionPredicate, however we moved positionPredicate into the poi search. This means we can avoid a
+        // shuffle entirely, and just pick a random element from list
+        if (list.isEmpty()) {
+            return Optional.empty();
+        }
+
+        return Optional.of(list.get(random.nextInt(list.size())).getPos());
+        // Paper end - re-route to faster logic
     }
 
     public boolean release(BlockPos pos) {
@@ -200,8 +_,10 @@
     }
 
     public int sectionsToVillage(SectionPos sectionPos) {
-        this.distanceTracker.runAllUpdates();
-        return this.distanceTracker.getLevel(sectionPos.asLong());
+        // Paper start - rewrite chunk system
+        this.villageDistanceTracker.propagateUpdates();
+        return convertBetweenLevels(this.villageDistanceTracker.getLevel(ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionKey(sectionPos)));
+        // Paper end - rewrite chunk system
     }
 
     boolean isVillageCenter(long chunkPos) {
@@ -215,19 +_,26 @@
 
     @Override
     public void tick(BooleanSupplier aheadOfTime) {
-        super.tick(aheadOfTime);
-        this.distanceTracker.runAllUpdates();
+        this.villageDistanceTracker.propagateUpdates(); // Paper - rewrite chunk system
     }
 
     @Override
-    protected void setDirty(long sectionPos) {
-        super.setDirty(sectionPos);
-        this.distanceTracker.update(sectionPos, this.distanceTracker.getLevelFromSource(sectionPos), false);
+    public void setDirty(long sectionPos) { // Paper - public
+        // Paper start - rewrite chunk system
+        final int chunkX = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionX(sectionPos);
+        final int chunkZ = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkSectionZ(sectionPos);
+        final ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager manager = ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager;
+        final ca.spottedleaf.moonrise.patches.chunk_system.level.poi.PoiChunk chunk = manager.getPoiChunkIfLoaded(chunkX, chunkZ, false);
+        if (chunk != null) {
+            chunk.setDirty(true);
+        }
+        this.updateDistanceTracking(sectionPos);
+        // Paper end - rewrite chunk system
     }
 
     @Override
     protected void onSectionLoad(long sectionKey) {
-        this.distanceTracker.update(sectionKey, this.distanceTracker.getLevelFromSource(sectionKey), false);
+        this.updateDistanceTracking(sectionKey); // Paper - rewrite chunk system
     }
 
     public void checkConsistencyWithBlocks(SectionPos sectionPos, LevelChunkSection levelChunkSection) {
@@ -266,7 +_,7 @@
             .map(sectionPos -> Pair.of(sectionPos, this.getOrLoad(sectionPos.asLong())))
             .filter(pair -> !pair.getSecond().map(PoiSection::isValid).orElse(false))
             .map(pair -> pair.getFirst().chunk())
-            .filter(chunkPos -> this.loadedChunks.add(chunkPos.toLong()))
+            // Paper - rewrite chunk system
             .forEach(chunkPos -> levelReader.getChunk(chunkPos.x, chunkPos.z, ChunkStatus.EMPTY));
     }
 
