--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -184,7 +_,7 @@
     private static final int OVERLOADED_TICKS_THRESHOLD = 20;
     private static final long OVERLOADED_WARNING_INTERVAL_NANOS = 10L * TimeUtil.NANOSECONDS_PER_SECOND;
     private static final int OVERLOADED_TICKS_WARNING_INTERVAL = 100;
-    private static final long STATUS_EXPIRE_TIME_NANOS = 5L * TimeUtil.NANOSECONDS_PER_SECOND;
+    public static final long STATUS_EXPIRE_TIME_NANOS = 5L * TimeUtil.NANOSECONDS_PER_SECOND; // Folia - region threading - public
     private static final long PREPARE_LEVELS_DEFAULT_DELAY_NANOS = 10L * TimeUtil.NANOSECONDS_PER_MILLISECOND;
     private static final int MAX_STATUS_PLAYER_SAMPLE = 12;
     private static final int SPAWN_POSITION_SEARCH_RADIUS = 5;
@@ -222,8 +_,7 @@
     private volatile boolean running = true;
     private volatile boolean isRestarting = false; // Paper - flag to signify we're attempting to restart
     private boolean stopped;
-    private int tickCount;
-    private int ticksUntilAutosave = 6000;
+    // Folia - region threading
     protected final Proxy proxy;
     private boolean onlineMode;
     private boolean preventProxyConnections;
@@ -283,7 +_,7 @@
     public org.bukkit.craftbukkit.CraftServer server;
     public joptsimple.OptionSet options;
     public org.bukkit.command.ConsoleCommandSender console;
-    public static int currentTick; // Paper - improve tick loop
+    //public static int currentTick; // Paper - improve tick loop // Folia - region threading
     public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
     public int autosavePeriod;
     // Paper - don't store the vanilla dispatcher
@@ -304,6 +_,50 @@
     private final Set<String> pluginsBlockingSleep = new java.util.HashSet<>(); // Paper - API to allow/disallow tick sleeping
     public static final long SERVER_INIT = System.nanoTime(); // Paper - Lag compensation
 
+    // Folia start - regionised ticking
+    public final io.papermc.paper.threadedregions.RegionizedServer regionizedServer = new io.papermc.paper.threadedregions.RegionizedServer();
+
+    @Override
+    public <V> CompletableFuture<V> submit(java.util.function.Supplier<V> task) {
+        if (true) {
+            throw new UnsupportedOperationException();
+        }
+        return super.submit(task);
+    }
+
+    @Override
+    public CompletableFuture<Void> submit(Runnable task) {
+        if (true) {
+            throw new UnsupportedOperationException();
+        }
+        return super.submit(task);
+    }
+
+    @Override
+    public void schedule(TickTask task) {
+        if (true) {
+            throw new UnsupportedOperationException();
+        }
+        super.schedule(task);
+    }
+
+    @Override
+    public void executeBlocking(Runnable runnable) {
+        if (true) {
+            throw new UnsupportedOperationException();
+        }
+        super.executeBlocking(runnable);
+    }
+
+    @Override
+    public void execute(Runnable runnable) {
+        if (true) {
+            throw new UnsupportedOperationException();
+        }
+        super.execute(runnable);
+    }
+    // Folia end - regionised ticking
+
     public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
         ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
         AtomicReference<S> atomicReference = new AtomicReference<>();
@@ -332,46 +_,30 @@
     private static final long MAX_CHUNK_EXEC_TIME = 1000L; // 1us
     private static final long TASK_EXECUTION_FAILURE_BACKOFF = 5L * 1000L; // 5us
 
-    private long lastMidTickExecute;
-    private long lastMidTickExecuteFailure;
-
-    private boolean tickMidTickTasks() {
-        // give all worlds a fair chance at by targeting them all.
-        // if we execute too many tasks, that's fine - we have logic to correctly handle overuse of allocated time.
-        boolean executed = false;
-        for (final ServerLevel world : this.getAllLevels()) {
-            long currTime = System.nanoTime();
-            if (currTime - ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)world).moonrise$getLastMidTickFailure() <= TASK_EXECUTION_FAILURE_BACKOFF) {
-                continue;
-            }
-            if (!world.getChunkSource().pollTask()) {
-                // we need to back off if this fails
-                ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)world).moonrise$setLastMidTickFailure(currTime);
-            } else {
-                executed = true;
-            }
-        }
-
-        return executed;
+    // Folia - region threading - moved to regionized data
+
+    private boolean tickMidTickTasks(final io.papermc.paper.threadedregions.RegionizedWorldData worldData) { // Folia - region threading
+        return worldData.world.getChunkSource().pollTask(); // Folia - region threading
     }
 
     @Override
     public final void moonrise$executeMidTickTasks() {
+        io.papermc.paper.threadedregions.RegionizedWorldData worldData = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegionizedWorldData(); // Folia - region threading
         final long startTime = System.nanoTime();
-        if ((startTime - this.lastMidTickExecute) <= CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME || (startTime - this.lastMidTickExecuteFailure) <= TASK_EXECUTION_FAILURE_BACKOFF) {
+        if ((startTime - worldData.lastMidTickExecute) <= CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME || (startTime - worldData.lastMidTickExecuteFailure) <= TASK_EXECUTION_FAILURE_BACKOFF) { // Folia - region threading
             // it's shown to be bad to constantly hit the queue (chunk loads slow to a crawl), even if no tasks are executed.
             // so, backoff to prevent this
             return;
         }
 
         for (;;) {
-            final boolean moreTasks = this.tickMidTickTasks();
+            final boolean moreTasks = this.tickMidTickTasks(worldData); // Folia - region threading
             final long currTime = System.nanoTime();
             final long diff = currTime - startTime;
 
             if (!moreTasks || diff >= MAX_CHUNK_EXEC_TIME) {
                 if (!moreTasks) {
-                    this.lastMidTickExecuteFailure = currTime;
+                    worldData.lastMidTickExecuteFailure = currTime; // Folia - region threading
                 }
 
                 // note: negative values reduce the time
@@ -384,7 +_,7 @@
                 final double overuseCount = (double)overuse/(double)MAX_CHUNK_EXEC_TIME;
                 final long extraSleep = (long)Math.round(overuseCount*CHUNK_TASK_QUEUE_BACKOFF_MIN_TIME);
 
-                this.lastMidTickExecute = currTime + extraSleep;
+                worldData.lastMidTickExecute = currTime + extraSleep; // Folia - region threading
                 return;
             }
         }
@@ -710,7 +_,21 @@
             // Back to the createLevels method without crazy modifications
             primaryLevelData.setModdedInfo(this.getServerModName(), this.getModdedStatus().shouldReportAsModified());
             this.addLevel(serverLevel); // Paper - Put world into worldlist before initing the world; move up
-            this.initWorld(serverLevel, primaryLevelData, this.worldData, worldOptions);
+            // Folia start - region threading
+            // the spawn should be within ~1024 blocks, so we force add ticket levels to ensure the first thread
+            // to init spawn will not run into any ownership issues
+            // move init to start of tickServer
+            int loadRegionRadius = 1024 >> 4;
+            serverLevel.randomSpawnSelection = new ChunkPos(serverLevel.getChunkSource().randomState().sampler().findSpawnPosition());
+            for (int currX = -loadRegionRadius; currX <= loadRegionRadius; ++currX) {
+                for (int currZ = -loadRegionRadius; currZ <= loadRegionRadius; ++currZ) {
+                    ChunkPos pos = new ChunkPos(currX, currZ);
+                    serverLevel.chunkSource.addTicketAtLevel(
+                            net.minecraft.server.level.TicketType.UNKNOWN, pos, ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager.MAX_TICKET_LEVEL, pos
+                    );
+                }
+            }
+            // Folia end - region threading
 
             // Paper - Put world into worldlist before initing the world; move up
             this.getPlayerList().addWorldborderListener(serverLevel);
@@ -723,6 +_,7 @@
         for (ServerLevel serverLevel : this.getAllLevels()) {
             this.prepareLevels(serverLevel.getChunkSource().chunkMap.progressListener, serverLevel);
             // Paper - rewrite chunk system
+            io.papermc.paper.threadedregions.RegionizedServer.getInstance().addWorld(serverLevel); // Folia - region threading
             this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(serverLevel.getWorld()));
         }
 
@@ -804,10 +_,11 @@
                 }
             }
             // CraftBukkit end
-            ChunkPos chunkPos = new ChunkPos(chunkSource.randomState().sampler().findSpawnPosition()); // Paper - Only attempt to find spawn position if there isn't a fixed spawn position set
+            ChunkPos chunkPos = level.randomSpawnSelection; // Paper - Only attempt to find spawn position if there isn't a fixed spawn position set // Folia - region threading
             int spawnHeight = chunkSource.getGenerator().getSpawnHeight(level);
             if (spawnHeight < level.getMinY()) {
                 BlockPos worldPosition = chunkPos.getWorldPosition();
+                level.getChunk(worldPosition.offset(8, 0, 8)); // Folia - region threading - sync load first
                 spawnHeight = level.getHeight(Heightmap.Types.WORLD_SURFACE, worldPosition.getX() + 8, worldPosition.getZ() + 8);
             }
 
@@ -869,14 +_,10 @@
         int _int = serverLevel.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS); // CraftBukkit - per-world
         int i = _int > 0 ? Mth.square(ChunkProgressListener.calculateDiameter(_int)) : 0;
 
-        while (chunkSource.getTickingGenerated() < i) {
-            // CraftBukkit start
-            // this.nextTickTimeNanos = Util.getNanos() + PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
-            this.executeModerately();
-        }
+        // Folia - region threading
 
         // this.nextTickTimeNanos = Util.getNanos() + PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
-        this.executeModerately();
+        //this.executeModerately(); // Folia - region threading
 
         if (true) {
             ServerLevel serverLevel1 = serverLevel;
@@ -895,7 +_,7 @@
 
         // CraftBukkit start
         // this.nextTickTimeNanos = SystemUtils.getNanos() + MinecraftServer.PREPARE_LEVELS_DEFAULT_DELAY_NANOS;
-        this.executeModerately();
+        //this.executeModerately(); // Folia - region threading
         // CraftBukkit end
         listener.stop();
         // CraftBukkit start
@@ -985,7 +_,37 @@
     }
     // CraftBukkit end
 
+    // Folia start - region threading
+    private final java.util.concurrent.atomic.AtomicBoolean hasStartedShutdownThread = new java.util.concurrent.atomic.AtomicBoolean();
+
+    private void haltServerRegionThreading() {
+        if (this.hasStartedShutdownThread.getAndSet(true)) {
+            // already started shutdown
+            return;
+        }
+        new io.papermc.paper.threadedregions.RegionShutdownThread("Region shutdown thread").start();
+    }
+
+    public void haltCurrentRegion() {
+        if (!ca.spottedleaf.moonrise.common.util.TickThread.isShutdownThread()) {
+            throw new IllegalStateException();
+        }
+    }
+    // Folia end - region threading
+
     public void stopServer() {
+        // Folia start - region threading
+        // halt scheduler
+        // don't wait, we may be on a scheduler thread
+        io.papermc.paper.threadedregions.TickRegions.getScheduler().halt(false, 0L);
+        // cannot run shutdown logic on this thread, as it may be a scheduler
+        if (true) {
+            if (!ca.spottedleaf.moonrise.common.util.TickThread.isShutdownThread()) {
+                this.haltServerRegionThreading();
+                return;
+            } // else: fall through to regular stop logic
+        }
+        // Folia end - region threading
         // CraftBukkit start - prevent double stopping on multiple threads
         synchronized(this.stopLock) {
             if (this.hasStopped) return;
@@ -1012,12 +_,19 @@
         this.getConnection().stop();
         this.isSaving = true;
         if (this.playerList != null) {
-            LOGGER.info("Saving players");
-            this.playerList.saveAll();
+            //LOGGER.info("Saving players"); // Folia - move to shutdown thread logic
+            //this.playerList.saveAll(); // Folia - move to shutdown thread logic
             this.playerList.removeAll(this.isRestarting); // Paper
             try { Thread.sleep(100); } catch (InterruptedException ex) {} // CraftBukkit - SPIGOT-625 - give server at least a chance to send packets
         }
 
+        // Folia start - region threading
+        if (true) {
+            // the rest till part 2 is handled by the region shutdown thread
+            return;
+        }
+        // Folia end - region threading
+
         LOGGER.info("Saving worlds");
 
         for (ServerLevel serverLevel : this.getAllLevels()) {
@@ -1040,6 +_,11 @@
         this.saveAllChunks(false, true, false, true); // Paper - rewrite chunk system
 
         this.isSaving = false;
+        // Folia start - region threading
+        this.stopPart2();
+    }
+    public void stopPart2() {
+        // Folia end - region threading
         this.resources.close();
 
         try {
@@ -1098,6 +_,7 @@
         if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper - Debugging
         // Paper end
         this.running = false;
+        this.stopServer(); // Folia - region threading
         if (waitForServer) {
             try {
                 this.serverThread.join();
@@ -1169,6 +_,18 @@
             this.status = this.buildServerStatus();
 
             this.server.spark.enableBeforePlugins(); // Paper - spark
+            // Folia start - region threading
+            if (true) {
+                io.papermc.paper.threadedregions.RegionizedServer.getInstance().init(); // Folia - region threading - only after loading worlds
+                final long actualDoneTimeMs = System.currentTimeMillis() - org.bukkit.craftbukkit.Main.BOOT_TIME.toEpochMilli(); // Paper - Improve startup message
+                LOGGER.info("Done ({})! For help, type \"help\"", String.format(java.util.Locale.ROOT, "%.3fs", actualDoneTimeMs / 1000.00D)); // Paper - Improve startup message
+                for (;;) {
+                    try {
+                        Thread.sleep(Long.MAX_VALUE);
+                    } catch (final InterruptedException ex) {}
+                }
+            }
+            // Folia end - region threading
             // Spigot start
             // Paper start
             LOGGER.info("Running delayed init tasks");
@@ -1218,7 +_,7 @@
                 // Spigot start
                 // Paper start - further improve server tick loop
                 currentTime = Util.getNanos();
-                if (++MinecraftServer.currentTick % MinecraftServer.SAMPLE_INTERVAL == 0) {
+                if (false) { // Folia - region threading
                     final long diff = currentTime - tickSection;
                     final java.math.BigDecimal currentTps = TPS_BASE.divide(new java.math.BigDecimal(diff), 30, java.math.RoundingMode.HALF_UP);
                     tps1.add(currentTps, diff);
@@ -1237,7 +_,7 @@
                 boolean flag = l == 0L;
                 if (this.debugCommandProfilerDelayStart) {
                     this.debugCommandProfilerDelayStart = false;
-                    this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount);
+                    //this.debugCommandProfiler = new MinecraftServer.TimeProfiler(Util.getNanos(), this.tickCount); // Folia - region threading
                 }
 
                 //MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit // Paper - don't overwrite current tick time
@@ -1248,7 +_,7 @@
                     ProfilerFiller profilerFiller = Profiler.get();
                     profilerFiller.push("tick");
                     this.tickFrame.start();
-                    this.tickServer(flag ? () -> false : this::haveTime);
+                    if (true) throw new UnsupportedOperationException(); // Folia - region threading
                     // Paper start - rewrite chunk system
                     final Throwable crash = this.chunkSystemCrash;
                     if (crash != null) {
@@ -1403,28 +_,24 @@
 
     @Override
     public TickTask wrapRunnable(Runnable runnable) {
-        // Paper start - anything that does try to post to main during watchdog crash, run on watchdog
-        if (this.hasStopped && Thread.currentThread().equals(shutdownThread)) {
-            runnable.run();
-            runnable = () -> {};
-        }
-        // Paper end
-        return new TickTask(this.tickCount, runnable);
+        throw new UnsupportedOperationException(); // Folia - region threading
     }
 
     @Override
     protected boolean shouldRun(TickTask runnable) {
-        return runnable.getTick() + 3 < this.tickCount || this.haveTime();
+        throw new UnsupportedOperationException(); // Folia - region threading
     }
 
     @Override
     public boolean pollTask() {
+        if (true) throw new UnsupportedOperationException(); // Folia - region threading
         boolean flag = this.pollTaskInternal();
         this.mayHaveDelayedTasks = flag;
         return flag;
     }
 
     private boolean pollTaskInternal() {
+        if (true) throw new UnsupportedOperationException(); // Folia - region threading
         if (super.pollTask()) {
             this.moonrise$executeMidTickTasks(); // Paper - rewrite chunk system
             return true;
@@ -1444,6 +_,7 @@
 
     @Override
     public void doRunTask(TickTask task) {
+        if (true) throw new UnsupportedOperationException(); // Folia - region threading
         Profiler.get().incrementCounter("runTask");
         super.doRunTask(task);
     }
@@ -1485,12 +_,15 @@
         return false;
     }
 
-    public void tickServer(BooleanSupplier hasTimeLeft) {
+    // Folia start - region threading
+    public void tickServer(long startTime, long scheduledEnd, long targetBuffer,
+                           io.papermc.paper.threadedregions.TickRegions.TickRegionData region) {
+        // Folia end - region threading
         org.spigotmc.WatchdogThread.tick(); // Spigot
-        long nanos = Util.getNanos();
+        long nanos = startTime; // Folia - region threading
         int i = this.pauseWhileEmptySeconds() * 20;
         this.removeDisabledPluginsBlockingSleep(); // Paper - API to allow/disallow tick sleeping
-        if (i > 0) {
+        if (false && i > 0) { // Folia - region threading - this is complicated to implement, and even if done correctly is messy
             if (this.playerList.getPlayerCount() == 0 && !this.tickRateManager.isSprinting() && this.pluginsBlockingSleep.isEmpty()) { // Paper - API to allow/disallow tick sleeping
                 this.emptyTicks++;
             } else {
@@ -1515,24 +_,58 @@
                     level.getChunkSource().tick(() -> true, false);
                 }
                 // Paper end - avoid issues with certain tasks not processing during sleep
-                this.server.spark.executeMainThreadTasks(); // Paper - spark
+                //this.server.spark.executeMainThreadTasks(); // Paper - spark // Folia - region threading
                 this.tickConnection();
                 this.server.spark.tickEnd(((double)(System.nanoTime() - lastTick) / 1000000D)); // Paper - spark
                 return;
             }
         }
 
+        // Folia start - region threading
+        region.world.getCurrentWorldData().updateTickData();
+        if (region.world.checkInitialised.get() != ServerLevel.WORLD_INIT_CHECKED) {
+            synchronized (region.world.checkInitialised) {
+                if (region.world.checkInitialised.compareAndSet(ServerLevel.WORLD_INIT_NOT_CHECKED, ServerLevel.WORLD_INIT_CHECKING)) {
+                    LOGGER.info("Initialising world '" + region.world.getWorld().getName() + "' before it can be ticked...");
+                    this.initWorld(region.world, region.world.serverLevelData, worldData, region.world.serverLevelData.worldGenOptions()); // Folia - delayed until first tick of world
+                    region.world.checkInitialised.set(ServerLevel.WORLD_INIT_CHECKED);
+                    LOGGER.info("Initialised world '" + region.world.getWorld().getName() + "'");
+                } // else: must be checked
+            }
+        }
+        BooleanSupplier hasTimeLeft = () -> {
+            return scheduledEnd - System.nanoTime() > targetBuffer;
+        };
+        // Folia end - region threading
+
         this.server.spark.tickStart(); // Paper - spark
-        new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper - Server Tick Events
-        this.tickCount++;
-        this.tickRateManager.tick();
-        this.tickChildren(hasTimeLeft);
-        if (nanos - this.lastServerStatus >= STATUS_EXPIRE_TIME_NANOS) {
+        new com.destroystokyo.paper.event.server.ServerTickStartEvent((int)region.getCurrentTick()).callEvent(); // Paper - Server Tick Events // Folia - region threading
+        // Folia start - region threading
+        if (region != null) {
+            region.getTaskQueueData().drainTasks();
+            ((io.papermc.paper.threadedregions.scheduler.FoliaRegionScheduler)org.bukkit.Bukkit.getRegionScheduler()).tick();
+            // now run all the entity schedulers
+            // TODO there has got to be a more efficient variant of this crap
+            for (net.minecraft.world.entity.Entity entity : region.world.getCurrentWorldData().getLocalEntitiesCopy()) {
+                if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity) || entity.isRemoved()) {
+                    continue;
+                }
+                org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw();
+                if (bukkit != null) {
+                    bukkit.taskScheduler.executeTick();
+                }
+            }
+        }
+        // Folia end - region threading
+        //this.tickCount++; // Folia - region threading
+        //this.tickRateManager.tick(); // Folia - region threading
+        this.tickChildren(hasTimeLeft, region); // Folia - region threading
+        if (false && nanos - this.lastServerStatus >= STATUS_EXPIRE_TIME_NANOS) { // Folia - region threading
             this.lastServerStatus = nanos;
             this.status = this.buildServerStatus();
         }
 
-        this.ticksUntilAutosave--;
+        //this.ticksUntilAutosave--; // Folia - region threading
         // Paper start - Incremental chunk and player saving
         final ProfilerFiller profiler = Profiler.get();
         int playerSaveInterval = io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.rate;
@@ -1540,15 +_,15 @@
             playerSaveInterval = autosavePeriod;
         }
         profiler.push("save");
-        final boolean fullSave = autosavePeriod > 0 && this.tickCount % autosavePeriod == 0;
+        final boolean fullSave = autosavePeriod > 0 && io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() % autosavePeriod == 0; // Folia - region threading
         try {
             this.isSaving = true;
             if (playerSaveInterval > 0) {
                 this.playerList.saveAll(playerSaveInterval);
             }
-            for (final ServerLevel level : this.getAllLevels()) {
+            for (final ServerLevel level : (region == null ? this.getAllLevels() : Arrays.asList(region.world))) { // Folia - region threading
                 if (level.paperConfig().chunks.autoSaveInterval.value() > 0) {
-                    level.saveIncrementally(fullSave);
+                    level.saveIncrementally(region == null && fullSave); // Folia - region threading - don't save level.dat
                 }
             }
         } finally {
@@ -1558,32 +_,19 @@
         // Paper end - Incremental chunk and player saving
 
         ProfilerFiller profilerFiller = Profiler.get();
-        this.runAllTasks(); // Paper - move runAllTasks() into full server tick (previously for timings)
-        this.server.spark.executeMainThreadTasks(); // Paper - spark
+        //this.runAllTasks(); // Paper - move runAllTasks() into full server tick (previously for timings) // Folia - region threading
+        //this.server.spark.executeMainThreadTasks(); // Paper - spark // Folia - region threading
         // Paper start - Server Tick Events
         long endTime = System.nanoTime();
-        long remaining = (TICK_TIME - (endTime - lastTick)) - catchupTime;
-        new com.destroystokyo.paper.event.server.ServerTickEndEvent(this.tickCount, ((double)(endTime - lastTick) / 1000000D), remaining).callEvent();
+        long remaining = scheduledEnd - endTime; // Folia - region ticking
+        new com.destroystokyo.paper.event.server.ServerTickEndEvent((int)io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick(), ((double)(endTime - startTime) / 1000000D), remaining).callEvent(); // Folia - region ticking
         // Paper end - Server Tick Events
-        this.server.spark.tickEnd(((double)(endTime - lastTick) / 1000000D)); // Paper - spark
-        profilerFiller.push("tallying");
-        long l = Util.getNanos() - nanos;
-        int i1 = this.tickCount % 100;
-        this.aggregatedTickTimesNanos = this.aggregatedTickTimesNanos - this.tickTimesNanos[i1];
-        this.aggregatedTickTimesNanos += l;
-        this.tickTimesNanos[i1] = l;
-        this.smoothedTickTimeMillis = this.smoothedTickTimeMillis * 0.8F + (float)l / (float)TimeUtil.NANOSECONDS_PER_MILLISECOND * 0.19999999F;
-        // Paper start - Add tick times API and /mspt command
-        this.tickTimes5s.add(this.tickCount, l);
-        this.tickTimes10s.add(this.tickCount, l);
-        this.tickTimes60s.add(this.tickCount, l);
-        // Paper end - Add tick times API and /mspt command
-        this.logTickMethodTime(nanos);
-        profilerFiller.pop();
+        this.server.spark.tickEnd(((double)(endTime - startTime) / 1000000D)); // Paper - spark // Folia - region threading
+        // Folia - region threading
     }
 
     private void autoSave() {
-        this.ticksUntilAutosave = this.autosavePeriod; // CraftBukkit
+        //this.ticksUntilAutosave = this.autosavePeriod; // CraftBukkit // Folia - region threading
         LOGGER.debug("Autosave started");
         ProfilerFiller profilerFiller = Profiler.get();
         profilerFiller.push("save");
@@ -1598,30 +_,22 @@
         }
     }
 
-    private int computeNextAutosaveInterval() {
-        float f;
-        if (this.tickRateManager.isSprinting()) {
-            long l = this.getAverageTickTimeNanos() + 1L;
-            f = (float)TimeUtil.NANOSECONDS_PER_SECOND / (float)l;
-        } else {
-            f = this.tickRateManager.tickrate();
-        }
-
-        int i = 300;
-        return Math.max(100, (int)(f * 300.0F));
-    }
+    // Folia - region threading - use absolute time instead of this
 
     public void onTickRateChanged() {
-        int i = this.computeNextAutosaveInterval();
-        if (i < this.ticksUntilAutosave) {
-            this.ticksUntilAutosave = i;
-        }
+        // Folia - region threading - use absolute time instead of this
     }
 
     protected abstract SampleLogger getTickTimeLogger();
 
     public abstract boolean isTickTimeLoggingEnabled();
 
+    // Folia start - region threading
+    public void rebuildServerStatus() {
+        this.status = this.buildServerStatus();
+    }
+    // Folia end - region threading
+
     private ServerStatus buildServerStatus() {
         ServerStatus.Players players = this.buildPlayerStatus();
         return new ServerStatus(
@@ -1634,7 +_,7 @@
     }
 
     private ServerStatus.Players buildPlayerStatus() {
-        List<ServerPlayer> players = this.playerList.getPlayers();
+        List<ServerPlayer> players = new java.util.ArrayList<>(this.playerList.getPlayers()); // Folia - region threading
         int maxPlayers = this.getMaxPlayers();
         if (this.hidesOnlinePlayers()) {
             return new ServerStatus.Players(maxPlayers, players.size(), List.of());
@@ -1653,44 +_,34 @@
         }
     }
 
-    protected void tickChildren(BooleanSupplier hasTimeLeft) {
+    protected void tickChildren(BooleanSupplier hasTimeLeft, io.papermc.paper.threadedregions.TickRegions.TickRegionData region) { // Folia - region threading
+        final io.papermc.paper.threadedregions.RegionizedWorldData regionizedWorldData = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegionizedWorldData(); // Folia - regionised ticking
         ProfilerFiller profilerFiller = Profiler.get();
-        this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing());
-        this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit
+        //this.getPlayerList().getPlayers().forEach(serverPlayer1 -> serverPlayer1.connection.suspendFlushing()); // Folia - region threading
+        //this.server.getScheduler().mainThreadHeartbeat(); // CraftBukkit // Folia - region threading
         // Paper start - Folia scheduler API
-        ((io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler) org.bukkit.Bukkit.getGlobalRegionScheduler()).tick();
-        getAllLevels().forEach(level -> {
-            for (final net.minecraft.world.entity.Entity entity : level.getEntities().getAll()) {
-                if (entity.isRemoved()) {
-                    continue;
-                }
-                final org.bukkit.craftbukkit.entity.CraftEntity bukkit = entity.getBukkitEntityRaw();
-                if (bukkit != null) {
-                    bukkit.taskScheduler.executeTick();
-                }
-            }
-        });
+        // Folia - region threading - moved to global tick - and moved entity scheduler to tickRegion
         // Paper end - Folia scheduler API
-        io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper
+        //io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue(this.tickCount); // Paper // Folia - region threading - moved to global tick
         profilerFiller.push("commandFunctions");
-        this.getFunctions().tick();
+        //this.getFunctions().tick(); // Folia - region threading - TODO Purge functions
         profilerFiller.popPush("levels");
 
         // CraftBukkit start
         // Run tasks that are waiting on processing
-        while (!this.processQueue.isEmpty()) {
+        if (false) while (!this.processQueue.isEmpty()) { // Folia - region threading
             this.processQueue.remove().run();
         }
 
         // Send time updates to everyone, it will get the right time from the world the player is in.
         // Paper start - Perf: Optimize time updates
-        for (final ServerLevel level : this.getAllLevels()) {
+        for (final ServerLevel level : Arrays.asList(region.world)) { // Folia - region threading
             final boolean doDaylight = level.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT);
             final long dayTime = level.getDayTime();
             long worldTime = level.getGameTime();
             final ClientboundSetTimePacket worldPacket = new ClientboundSetTimePacket(worldTime, dayTime, doDaylight);
-            for (Player entityhuman : level.players()) {
-                if (!(entityhuman instanceof ServerPlayer) || (tickCount + entityhuman.getId()) % 20 != 0) {
+            for (Player entityhuman : level.getLocalPlayers()) { // Folia - region threading
+                if (!(entityhuman instanceof ServerPlayer) || (io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() + entityhuman.getId()) % 20 != 0) { // Folia - region threading
                     continue;
                 }
                 ServerPlayer entityplayer = (ServerPlayer) entityhuman;
@@ -1703,12 +_,9 @@
             }
         }
 
-        this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked
-        for (ServerLevel serverLevel : this.getAllLevels()) {
-            serverLevel.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - BlockPhysicsEvent
-            serverLevel.hasEntityMoveEvent = io.papermc.paper.event.entity.EntityMoveEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - Add EntityMoveEvent
-            serverLevel.updateLagCompensationTick(); // Paper - lag compensation
-            net.minecraft.world.level.block.entity.HopperBlockEntity.skipHopperEvents = serverLevel.paperConfig().hopper.disableMoveEvent || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - Perf: Optimize Hoppers
+        //this.isIteratingOverLevels = true; // Paper - Throw exception on world create while being ticked // Folia - region threading
+        for (ServerLevel serverLevel : Arrays.asList(region.world)) { // Folia - region threading
+            // Folia - region threading
             profilerFiller.push(() -> serverLevel + " " + serverLevel.dimension().location());
             /* Drop global time updates
             if (this.tickCount % 20 == 0) {
@@ -1721,7 +_,7 @@
             profilerFiller.push("tick");
 
             try {
-                serverLevel.tick(hasTimeLeft);
+                serverLevel.tick(hasTimeLeft, region); // Folia - region threading
             } catch (Throwable var7) {
                 CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world");
                 serverLevel.fillReportDetails(crashReport);
@@ -1730,27 +_,27 @@
 
             profilerFiller.pop();
             profilerFiller.pop();
-            serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions
+            regionizedWorldData.explosionDensityCache.clear(); // Paper - Optimize explosions // Folia - region threading
         }
-        this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
+        //this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked // Folia - region threading
 
         profilerFiller.popPush("connection");
-        this.tickConnection();
+        regionizedWorldData.tickConnections(); // Folia - region threading
         profilerFiller.popPush("players");
-        this.playerList.tick();
+        //this.playerList.tick(); // Folia - region threading
         if (SharedConstants.IS_RUNNING_IN_IDE && this.tickRateManager.runsNormally()) {
             GameTestTicker.SINGLETON.tick();
         }
 
         profilerFiller.popPush("server gui refresh");
 
-        for (int i = 0; i < this.tickables.size(); i++) {
+        if (false) for (int i = 0; i < this.tickables.size(); i++) { // Folia - region threading - TODO WTF is this?
             this.tickables.get(i).run();
         }
 
         profilerFiller.popPush("send chunks");
 
-        for (ServerPlayer serverPlayer : this.playerList.getPlayers()) {
+        if (false) for (ServerPlayer serverPlayer : this.playerList.getPlayers()) { // Folia - region threading
             serverPlayer.connection.chunkSender.sendNextChunks(serverPlayer);
             serverPlayer.connection.resumeFlushing();
         }
@@ -2073,7 +_,7 @@
     }
 
     public int getTickCount() {
-        return this.tickCount;
+        throw new UnsupportedOperationException(); // Folia - region threading
     }
 
     public int getSpawnProtectionRadius() {
@@ -2128,6 +_,15 @@
     }
 
     public void invalidateStatus() {
+        // Folia start - region threading
+        if (true) {
+            // we don't need this to notify the global tick region
+            io.papermc.paper.threadedregions.RegionizedServer.getInstance().addTaskWithoutNotify(() -> {
+                io.papermc.paper.threadedregions.RegionizedServer.getInstance().invalidateStatus();
+            });
+            return;
+        }
+        // Folia end - region threading
         this.lastServerStatus = 0L;
     }
 
@@ -2142,6 +_,7 @@
 
     @Override
     public void executeIfPossible(Runnable task) {
+        if (true) throw new UnsupportedOperationException(); // Folia - region threading
         if (this.isStopped()) {
             throw new io.papermc.paper.util.ServerStopRejectedExecutionException("Server already shutting down"); // Paper - do not prematurely disconnect players on stop
         } else {
@@ -2455,7 +_,12 @@
     }
 
     public long getAverageTickTimeNanos() {
-        return this.aggregatedTickTimesNanos / Math.min(100, Math.max(this.tickCount, 1));
+        // Folia start - region threading
+        if (io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentTickingTask() instanceof io.papermc.paper.threadedregions.TickRegionScheduler.RegionScheduleHandle handle) {
+            return (long)Math.ceil(handle.getTickReport5s(System.nanoTime()).timePerTickData().segmentAll().average());
+        }
+        return 0L;
+        // Folia end - region threading
     }
 
     public long[] getTickTimesNanos() {
@@ -2705,13 +_,7 @@
     }
 
     public ProfileResults stopTimeProfiler() {
-        if (this.debugCommandProfiler == null) {
-            return EmptyProfileResults.EMPTY;
-        } else {
-            ProfileResults profileResults = this.debugCommandProfiler.stop(Util.getNanos(), this.tickCount);
-            this.debugCommandProfiler = null;
-            return profileResults;
-        }
+        throw new UnsupportedOperationException(); // Folia - region threading
     }
 
     public int getMaxChainedNeighborUpdates() {
@@ -2896,24 +_,15 @@
 
     // Paper start - API to check if the server is sleeping
     public boolean isTickPaused() {
-        return this.emptyTicks > 0 && this.emptyTicks >= this.pauseWhileEmptySeconds() * 20;
+        return false; // Folia - region threading
     }
 
     public void addPluginAllowingSleep(final String pluginName, final boolean value) {
-        if (!value) {
-            this.pluginsBlockingSleep.add(pluginName);
-        } else {
-            this.pluginsBlockingSleep.remove(pluginName);
-        }
+        // Folia - region threading
     }
 
     private void removeDisabledPluginsBlockingSleep() {
-        if (this.pluginsBlockingSleep.isEmpty()) {
-            return;
-        }
-        this.pluginsBlockingSleep.removeIf(plugin -> (
-            !io.papermc.paper.plugin.manager.PaperPluginManagerImpl.getInstance().isPluginEnabled(plugin)
-        ));
+        // Folia - region threading
     }
     // Paper end - API to check if the server is sleeping
 }
