--- /dev/null
+++ b/io/papermc/paper/threadedregions/RegionShutdownThread.java
@@ -1,0 +_,226 @@
+package io.papermc.paper.threadedregions;
+
+import ca.spottedleaf.moonrise.common.util.WorldUtil;
+import com.mojang.logging.LogUtils;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.entity.Entity;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.ChunkPos;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.slf4j.Logger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public final class RegionShutdownThread extends ca.spottedleaf.moonrise.common.util.TickThread {
+
+    private static final Logger LOGGER = LogUtils.getClassLogger();
+
+    ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> shuttingDown;
+
+    public RegionShutdownThread(final String name) {
+        super(name);
+        this.setUncaughtExceptionHandler((thread, thr) -> {
+            LOGGER.error("Error shutting down server", thr);
+        });
+    }
+
+    static ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> getRegion() {
+        final Thread currentThread = Thread.currentThread();
+        if (currentThread instanceof RegionShutdownThread shutdownThread) {
+            return shutdownThread.shuttingDown;
+        }
+        return null;
+    }
+
+
+    static RegionizedWorldData getWorldData() {
+        final Thread currentThread = Thread.currentThread();
+        if (currentThread instanceof RegionShutdownThread shutdownThread) {
+            // no fast path for shutting down
+            if (shutdownThread.shuttingDown != null) {
+                return shutdownThread.shuttingDown.getData().world.worldRegionData.get();
+            }
+        }
+        return null;
+    }
+
+    // The region shutdown thread bypasses all tick thread checks, which will allow us to execute global saves
+    // it will not however let us perform arbitrary sync loads, arbitrary world state lookups simply because
+    // the data required to do that is regionised, and we can only access it when we OWN the region, and we do not.
+    // Thus, the only operation that the shutdown thread will perform
+
+    private void saveLevelData(final ServerLevel world) {
+        try {
+            world.saveLevelData(true);
+        } catch (final Throwable thr) {
+            LOGGER.error("Failed to save level data for " + world.getWorld().getName(), thr);
+        }
+    }
+
+    private void finishTeleportations(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region,
+                                      final ServerLevel world) {
+        try {
+            this.shuttingDown = region;
+            final List<ServerLevel.PendingTeleport> pendingTeleports = world.removeAllRegionTeleports();
+            if (pendingTeleports.isEmpty()) {
+                return;
+            }
+            final ChunkPos center = region.getCenterChunk();
+            LOGGER.info("Completing " + pendingTeleports.size() + " pending teleports in region around chunk " + center + " in world '" + region.regioniser.world.getWorld().getName() + "'");
+            for (final ServerLevel.PendingTeleport pendingTeleport : pendingTeleports) {
+                LOGGER.info("Completing teleportation to target position " + pendingTeleport.to());
+
+                // first, add entities to entity chunk so that they will be saved
+                for (final Entity.EntityTreeNode node : pendingTeleport.rootVehicle().getFullTree()) {
+                    // assume that world and position are set to destination here
+                    node.root.setLevel(world); // in case the pending teleport is from a portal before it finds the exact destination
+                    world.moonrise$getEntityLookup().addEntityForShutdownTeleportComplete(node.root);
+                }
+
+                // then, rebuild the passenger tree so that when saving only the root vehicle will be written - and if
+                // there are any player passengers, that the later player saving will save the tree
+                pendingTeleport.rootVehicle().restore();
+
+                // now we are finished
+                LOGGER.info("Completed teleportation to target position " + pendingTeleport.to());
+            }
+        } catch (final Throwable thr) {
+            LOGGER.error("Failed to complete pending teleports", thr);
+        } finally {
+            this.shuttingDown = null;
+        }
+    }
+
+    private void saveRegionChunks(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region,
+                                  final boolean last) {
+        ChunkPos center = null;
+        try {
+            this.shuttingDown = region;
+            center = region.getCenterChunk();
+            LOGGER.info("Saving chunks around region around chunk " + center + " in world '" + region.regioniser.world.getWorld().getName() + "'");
+            region.regioniser.world.moonrise$getChunkTaskScheduler().chunkHolderManager.close(true, true, false, last, false);
+        } catch (final Throwable thr) {
+            LOGGER.error("Failed to save chunks for region around chunk " + center + " in world '" + region.regioniser.world.getWorld().getName() + "'", thr);
+        } finally {
+            this.shuttingDown = null;
+        }
+    }
+
+    private void haltChunkSystem(final ServerLevel world) {
+        try {
+            world.moonrise$getChunkTaskScheduler().chunkHolderManager.close(false, true, true, false, false);
+        } catch (final Throwable thr) {
+            LOGGER.error("Failed to halt chunk system for world '" + world.getWorld().getName() + "'", thr);
+        }
+    }
+
+    private void closePlayerInventories(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region) {
+        ChunkPos center = null;
+        try {
+            this.shuttingDown = region;
+            center = region.getCenterChunk();
+
+            final RegionizedWorldData worldData = region.regioniser.world.worldRegionData.get();
+
+            for (final ServerPlayer player : worldData.getLocalPlayers()) {
+                try {
+                    // close inventory
+                    if (player.containerMenu != player.inventoryMenu) {
+                        player.closeContainer(InventoryCloseEvent.Reason.DISCONNECT);
+                    }
+
+                    // drop carried item
+                    if (!player.containerMenu.getCarried().isEmpty()) {
+                        ItemStack carried = player.containerMenu.getCarried();
+                        player.containerMenu.setCarried(ItemStack.EMPTY);
+                        player.drop(carried, false);
+                    }
+                } catch (final Throwable thr) {
+                    LOGGER.error("Failed to close player inventory for player: " + player, thr);
+                }
+            }
+        } catch (final Throwable thr) {
+            LOGGER.error("Failed to close player inventories for region around chunk " + center + " in world '" + region.regioniser.world.getWorld().getName() + "'", thr);
+        } finally {
+            this.shuttingDown = null;
+        }
+    }
+
+    @Override
+    public final void run() {
+        // await scheduler termination
+        LOGGER.info("Awaiting scheduler termination for 60s...");
+        if (TickRegions.getScheduler().halt(true, TimeUnit.SECONDS.toNanos(60L))) {
+            LOGGER.info("Scheduler halted");
+        } else {
+            LOGGER.warn("Scheduler did not terminate within 60s, proceeding with shutdown anyways");
+            TickRegions.getScheduler().dumpAliveThreadTraces("Did not shut down in time");
+        }
+
+        MinecraftServer.getServer().stopServer(); // stop part 1: most logic, kicking players, plugins, etc
+        // halt all chunk systems first so that any in-progress chunk generation stops
+        LOGGER.info("Halting chunk systems...");
+        for (final ServerLevel world : MinecraftServer.getServer().getAllLevels()) {
+            try {
+                world.moonrise$getChunkTaskScheduler().halt(false, 0L);
+            } catch (final Throwable throwable) {
+                LOGGER.error("Failed to soft halt chunk system for world '" + world.getWorld().getName() + "'", throwable);
+            }
+        }
+        for (final ServerLevel world : MinecraftServer.getServer().getAllLevels()) {
+            this.haltChunkSystem(world);
+        }
+        LOGGER.info("Halted chunk systems");
+
+        LOGGER.info("Finishing pending teleports...");
+        for (final ServerLevel world : MinecraftServer.getServer().getAllLevels()) {
+            final List<ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData>>
+                regions = new ArrayList<>();
+            world.regioniser.computeForAllRegionsUnsynchronised(regions::add);
+
+            for (int i = 0, len = regions.size(); i < len; ++i) {
+                this.finishTeleportations(regions.get(i), world);
+            }
+        }
+        LOGGER.info("Finished pending teleports");
+
+        LOGGER.info("Saving all worlds");
+        for (final ServerLevel world : MinecraftServer.getServer().getAllLevels()) {
+            LOGGER.info("Saving world data for world '" + WorldUtil.getWorldName(world) + "'");
+
+            final List<ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData>>
+                regions = new ArrayList<>();
+            world.regioniser.computeForAllRegionsUnsynchronised(regions::add);
+
+            LOGGER.info("Closing player inventories...");
+            for (int i = 0, len = regions.size(); i < len; ++i) {
+                this.closePlayerInventories(regions.get(i));
+            }
+            LOGGER.info("Closed player inventories");
+
+            LOGGER.info("Saving chunks...");
+            for (int i = 0, len = regions.size(); i < len; ++i) {
+                this.saveRegionChunks(regions.get(i), (i + 1) == len);
+            }
+            LOGGER.info("Saved chunks");
+
+            LOGGER.info("Saving level data...");
+            this.saveLevelData(world);
+            LOGGER.info("Saved level data");
+
+            LOGGER.info("Saved world data for world '" + WorldUtil.getWorldName(world) + "'");
+        }
+        LOGGER.info("Saved all worlds");
+
+        // Note: only save after world data and pending teleportations
+        LOGGER.info("Saving all player data...");
+        MinecraftServer.getServer().getPlayerList().saveAll();
+        LOGGER.info("Saved all player data");
+
+        MinecraftServer.getServer().stopPart2(); // stop part 2: close other resources (io thread, etc)
+        // done, part 2 should call exit()
+    }
+}
