--- a/net/minecraft/world/level/block/NetherPortalBlock.java
+++ b/net/minecraft/world/level/block/NetherPortalBlock.java
@@ -181,6 +_,33 @@
         }
     }
 
+    // Folia start - region threading
+    @Override
+    public boolean portalAsync(ServerLevel sourceWorld, Entity portalTarget, BlockPos portalPos) {
+        if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(portalTarget)) {
+            return false;
+        }
+        if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(sourceWorld, portalPos)) {
+            return false;
+        }
+
+        return portalTarget.netherPortalLogicAsync(portalPos);
+    }
+
+    public static BlockUtil.FoundRectangle findPortalAround(ServerLevel world, BlockPos rough, WorldBorder worldBorder, int searchRadius) {
+        BlockPos found = world.getPortalForcer().findClosestPortalPosition(rough, worldBorder, searchRadius).orElse(null);
+        if (found == null) {
+            return null;
+        }
+
+        BlockState portalState = world.getBlockStateFromEmptyChunk(found);
+
+        return BlockUtil.getLargestRectangleAround(found, portalState.getValue(BlockStateProperties.HORIZONTAL_AXIS), 21, Direction.Axis.Y, 21, (pos) -> {
+            return world.getBlockStateFromEmptyChunk(pos) == portalState;
+        });
+    }
+    // Folia end - region threading
+
     @Nullable
     private TeleportTransition getExitPortal(ServerLevel level, Entity entity, BlockPos pos, BlockPos exitPos, boolean isNether, WorldBorder worldBorder, int searchRadius, boolean canCreatePortal, int createRadius) { // CraftBukkit
         Optional<BlockPos> optional = level.getPortalForcer().findClosestPortalPosition(exitPos, worldBorder, searchRadius); // CraftBukkit
@@ -188,14 +_,14 @@
         TeleportTransition.PostTeleportTransition postTeleportTransition;
         if (optional.isPresent()) {
             BlockPos blockPos = optional.get();
-            BlockState blockState = level.getBlockState(blockPos);
+            BlockState blockState = level.getBlockStateFromEmptyChunk(blockPos); // Folia - region threading
             largestRectangleAround = BlockUtil.getLargestRectangleAround(
                 blockPos,
                 blockState.getValue(BlockStateProperties.HORIZONTAL_AXIS),
                 21,
                 Direction.Axis.Y,
                 21,
-                blockPos1 -> level.getBlockState(blockPos1) == blockState
+                blockPos1 -> level.getBlockStateFromEmptyChunk(blockPos1) == blockState // Folia - region threading
             );
             postTeleportTransition = TeleportTransition.PLAY_PORTAL_SOUND.then(entity1 -> entity1.placePortalTicket(blockPos));
         } else if (canCreatePortal) { // CraftBukkit
@@ -238,7 +_,7 @@
         return createDimensionTransition(level, rectangle, axis, relativePortalPosition, entity, postTeleportTransition);
     }
 
-    private static TeleportTransition createDimensionTransition(
+    public static TeleportTransition createDimensionTransition( // Folia - region threading - public
         ServerLevel level,
         BlockUtil.FoundRectangle rectangle,
         Direction.Axis axis,
