--- a/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
+++ b/src/main/java/ca/spottedleaf/moonrise/common/util/TickThread.java
@@ -1,5 +_,11 @@
 package ca.spottedleaf.moonrise.common.util;
 
+import io.papermc.paper.threadedregions.RegionShutdownThread;
+import io.papermc.paper.threadedregions.RegionizedServer;
+import io.papermc.paper.threadedregions.RegionizedWorldData;
+import io.papermc.paper.threadedregions.ThreadedRegionizer;
+import io.papermc.paper.threadedregions.TickRegionScheduler;
+import io.papermc.paper.threadedregions.TickRegions;
 import net.minecraft.core.BlockPos;
 import net.minecraft.world.entity.Entity;
 import net.minecraft.world.level.ChunkPos;
@@ -15,8 +_,26 @@
 
     private static final Logger LOGGER = LoggerFactory.getLogger(TickThread.class);
 
+    private static String getRegionInfo(final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region) {
+        if (region == null) {
+            return "{null}";
+        }
+
+        final ChunkPos center = region.getCenterChunk();
+        final net.minecraft.server.level.ServerLevel world = region.regioniser.world;
+
+        return "{center=" + center + ",world=" + (world == null ? "null" : WorldUtil.getWorldName(world)) + "}";
+    }
+
     private static String getThreadContext() {
-        return "thread=" + Thread.currentThread().getName();
+        final Thread thread = Thread.currentThread();
+
+        if (!(thread instanceof TickThread)) {
+            return "[thread=" + thread + ",class=" + thread.getClass().getName() + "]";
+        }
+
+        return "[thread=" + thread.getName() + ",class=" + thread.getClass().getName() + ",region=" + getRegionInfo(TickRegionScheduler.getCurrentRegion()) + "]";
+
     }
 
     /**
@@ -123,50 +_,157 @@
     }
 
     public static boolean isShutdownThread() {
-        return false;
+        return Thread.currentThread().getClass() == RegionShutdownThread.class;
     }
 
     public static boolean isTickThreadFor(final Level world, final BlockPos pos) {
-        return isTickThread();
+        return isTickThreadFor(world, pos.getX() >> 4, pos.getZ() >> 4);
     }
 
     public static boolean isTickThreadFor(final Level world, final BlockPos pos, final int blockRadius) {
-        return isTickThread();
+        return isTickThreadFor(
+                world,
+                (pos.getX() - blockRadius) >> 4, (pos.getZ() - blockRadius) >> 4,
+                (pos.getX() + blockRadius) >> 4, (pos.getZ() + blockRadius) >> 4
+        );
     }
 
     public static boolean isTickThreadFor(final Level world, final ChunkPos pos) {
-        return isTickThread();
+        return isTickThreadFor(world, pos.x, pos.z);
     }
 
     public static boolean isTickThreadFor(final Level world, final Vec3 pos) {
-        return isTickThread();
+        return isTickThreadFor(world, net.minecraft.util.Mth.floor(pos.x) >> 4, net.minecraft.util.Mth.floor(pos.z) >> 4);
     }
 
     public static boolean isTickThreadFor(final Level world, final int chunkX, final int chunkZ) {
-        return isTickThread();
+        final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region =
+            TickRegionScheduler.getCurrentRegion();
+        if (region == null) {
+            return isShutdownThread();
+        }
+        return ((net.minecraft.server.level.ServerLevel)world).regioniser.getRegionAtUnsynchronised(chunkX, chunkZ) == region;
     }
 
     public static boolean isTickThreadFor(final Level world, final AABB aabb) {
-        return isTickThread();
+        return isTickThreadFor(
+            world,
+            CoordinateUtils.getChunkCoordinate(aabb.minX), CoordinateUtils.getChunkCoordinate(aabb.minZ),
+            CoordinateUtils.getChunkCoordinate(aabb.maxX), CoordinateUtils.getChunkCoordinate(aabb.maxZ)
+        );
     }
 
     public static boolean isTickThreadFor(final Level world, final double blockX, final double blockZ) {
-        return isTickThread();
+        return isTickThreadFor(world, CoordinateUtils.getChunkCoordinate(blockX), CoordinateUtils.getChunkCoordinate(blockZ));
     }
 
     public static boolean isTickThreadFor(final Level world, final Vec3 position, final Vec3 deltaMovement, final int buffer) {
-        return isTickThread();
+        final int fromChunkX = CoordinateUtils.getChunkX(position);
+        final int fromChunkZ = CoordinateUtils.getChunkZ(position);
+
+        final int toChunkX = CoordinateUtils.getChunkCoordinate(position.x + deltaMovement.x);
+        final int toChunkZ = CoordinateUtils.getChunkCoordinate(position.z + deltaMovement.z);
+
+        // expect from < to, but that may not be the case
+        return isTickThreadFor(
+            world,
+            Math.min(fromChunkX, toChunkX) - buffer,
+            Math.min(fromChunkZ, toChunkZ) - buffer,
+            Math.max(fromChunkX, toChunkX) + buffer,
+            Math.max(fromChunkZ, toChunkZ) + buffer
+        );
     }
 
     public static boolean isTickThreadFor(final Level world, final int fromChunkX, final int fromChunkZ, final int toChunkX, final int toChunkZ) {
-        return isTickThread();
+        final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region =
+            TickRegionScheduler.getCurrentRegion();
+        if (region == null) {
+            return isShutdownThread();
+        }
+
+        final int shift = ((net.minecraft.server.level.ServerLevel)world).regioniser.sectionChunkShift;
+
+        final int minSectionX = fromChunkX >> shift;
+        final int maxSectionX = toChunkX >> shift;
+        final int minSectionZ = fromChunkZ >> shift;
+        final int maxSectionZ = toChunkZ >> shift;
+
+        for (int secZ = minSectionZ; secZ <= maxSectionZ; ++secZ) {
+            for (int secX = minSectionX; secX <= maxSectionX; ++secX) {
+                final int lowerLeftCX = secX << shift;
+                final int lowerLeftCZ = secZ << shift;
+                if (((net.minecraft.server.level.ServerLevel)world).regioniser.getRegionAtUnsynchronised(lowerLeftCX, lowerLeftCZ) != region) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
     }
 
     public static boolean isTickThreadFor(final Level world, final int chunkX, final int chunkZ, final int radius) {
-        return isTickThread();
+        return isTickThreadFor(world, chunkX - radius, chunkZ - radius, chunkX + radius, chunkZ + radius);
     }
 
     public static boolean isTickThreadFor(final Entity entity) {
-        return isTickThread();
+        if (entity == null) {
+            return true;
+        }
+        final ThreadedRegionizer.ThreadedRegion<TickRegions.TickRegionData, TickRegions.TickRegionSectionData> region =
+            TickRegionScheduler.getCurrentRegion();
+        if (region == null) {
+            if (RegionizedServer.isGlobalTickThread()) {
+                if (entity instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+                    final net.minecraft.server.network.ServerGamePacketListenerImpl possibleBad = serverPlayer.connection;
+                    if (possibleBad == null) {
+                        return true;
+                    }
+
+                    final net.minecraft.network.PacketListener packetListener = possibleBad.connection.getPacketListener();
+                    if (packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl gamePacketListener) {
+                        return gamePacketListener.waitingForSwitchToConfig;
+                    }
+                    if (packetListener instanceof net.minecraft.server.network.ServerConfigurationPacketListenerImpl configurationPacketListener) {
+                        return !configurationPacketListener.switchToMain;
+                    }
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+            if (isShutdownThread()) {
+                return true;
+            }
+            if (entity instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+                // off-main access to server player is never ok, server player is owned by one of global context or region context always
+                return false;
+            }
+            // only own entities that have not yet been added to the world
+
+            // if the entity is removed, then it was in the world previously - which means that a region containing its location
+            // owns it
+            // if the entity has a callback, then it is contained in a world
+            return entity.hasNullCallback() && !entity.isRemoved();
+        }
+
+        final Level world = entity.level();
+        if (world != region.regioniser.world) {
+            // world mismatch
+            return false;
+        }
+
+        final RegionizedWorldData worldData = io.papermc.paper.threadedregions.TickRegionScheduler.getCurrentRegionizedWorldData();
+
+        // pass through the check if the entity is removed and we own its chunk
+        if (worldData.hasEntity(entity)) {
+            return true;
+        }
+        
+        if (entity instanceof net.minecraft.server.level.ServerPlayer serverPlayer) {
+            net.minecraft.server.network.ServerGamePacketListenerImpl conn = serverPlayer.connection;
+            return conn != null && worldData.connections.contains(conn.connection);
+        } else {
+            return ((entity.hasNullCallback() || entity.isRemoved())) && isTickThreadFor((net.minecraft.server.level.ServerLevel)world, entity.chunkPosition());
+        }
     }
 }
