--- a/net/minecraft/world/entity/ai/village/VillageSiege.java
+++ b/net/minecraft/world/entity/ai/village/VillageSiege.java
@@ -18,68 +_,72 @@
 
 public class VillageSiege implements CustomSpawner {
     private static final Logger LOGGER = LogUtils.getLogger();
-    private boolean hasSetupSiege;
-    private VillageSiege.State siegeState = VillageSiege.State.SIEGE_DONE;
-    private int zombiesToSpawn;
-    private int nextSpawnTime;
-    private int spawnX;
-    private int spawnY;
-    private int spawnZ;
+    // Folia - region threading
 
     @Override
     public int tick(ServerLevel level, boolean spawnHostiles, boolean spawnPassives) {
+        io.papermc.paper.threadedregions.RegionizedWorldData worldData = level.getCurrentWorldData(); // Folia - region threading
+        // Folia start - region threading
+        // check if the spawn pos is no longer owned by this region
+        if (worldData.villageSiegeState.siegeState != State.SIEGE_DONE
+                && !ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(level, worldData.villageSiegeState.spawnX >> 4, worldData.villageSiegeState.spawnZ >> 4, 8)) {
+            // can't spawn here, just re-set
+            worldData.villageSiegeState = new io.papermc.paper.threadedregions.RegionizedWorldData.VillageSiegeState();
+        }
+        // Folia end - region threading
         if (!level.isDay() && spawnHostiles) {
             float timeOfDay = level.getTimeOfDay(0.0F);
             if (timeOfDay == 0.5) {
-                this.siegeState = level.random.nextInt(10) == 0 ? VillageSiege.State.SIEGE_TONIGHT : VillageSiege.State.SIEGE_DONE;
+                worldData.villageSiegeState.siegeState = level.random.nextInt(10) == 0 ? VillageSiege.State.SIEGE_TONIGHT : VillageSiege.State.SIEGE_DONE; // Folia - region threading
             }
 
-            if (this.siegeState == VillageSiege.State.SIEGE_DONE) {
+            if (worldData.villageSiegeState.siegeState == VillageSiege.State.SIEGE_DONE) { // Folia - region threading
                 return 0;
             } else {
-                if (!this.hasSetupSiege) {
+                if (!worldData.villageSiegeState.hasSetupSiege) { // Folia - region threading
                     if (!this.tryToSetupSiege(level)) {
                         return 0;
                     }
 
-                    this.hasSetupSiege = true;
+                    worldData.villageSiegeState.hasSetupSiege = true; // Folia - region threading
                 }
 
-                if (this.nextSpawnTime > 0) {
-                    this.nextSpawnTime--;
+                if (worldData.villageSiegeState.nextSpawnTime > 0) { // Folia - region threading
+                    worldData.villageSiegeState.nextSpawnTime--; // Folia - region threading
                     return 0;
                 } else {
-                    this.nextSpawnTime = 2;
-                    if (this.zombiesToSpawn > 0) {
+                    worldData.villageSiegeState.nextSpawnTime = 2; // Folia - region threading
+                    if (worldData.villageSiegeState.zombiesToSpawn > 0) { // Folia - region threading
                         this.trySpawn(level);
-                        this.zombiesToSpawn--;
+                        worldData.villageSiegeState.zombiesToSpawn--; // Folia - region threading
                     } else {
-                        this.siegeState = VillageSiege.State.SIEGE_DONE;
+                        worldData.villageSiegeState.siegeState = VillageSiege.State.SIEGE_DONE; // Folia - region threading
                     }
 
                     return 1;
                 }
             }
         } else {
-            this.siegeState = VillageSiege.State.SIEGE_DONE;
-            this.hasSetupSiege = false;
+            worldData.villageSiegeState.siegeState = VillageSiege.State.SIEGE_DONE; // Folia - region threading
+            worldData.villageSiegeState.hasSetupSiege = false; // Folia - region threading
             return 0;
         }
     }
 
     private boolean tryToSetupSiege(ServerLevel level) {
-        for (Player player : level.players()) {
+        io.papermc.paper.threadedregions.RegionizedWorldData worldData = level.getCurrentWorldData(); // Folia - region threading
+        for (Player player : level.getLocalPlayers()) { // Folia - region threading
             if (!player.isSpectator()) {
                 BlockPos blockPos = player.blockPosition();
                 if (level.isVillage(blockPos) && !level.getBiome(blockPos).is(BiomeTags.WITHOUT_ZOMBIE_SIEGES)) {
                     for (int i = 0; i < 10; i++) {
                         float f = level.random.nextFloat() * (float) (Math.PI * 2);
-                        this.spawnX = blockPos.getX() + Mth.floor(Mth.cos(f) * 32.0F);
-                        this.spawnY = blockPos.getY();
-                        this.spawnZ = blockPos.getZ() + Mth.floor(Mth.sin(f) * 32.0F);
-                        if (this.findRandomSpawnPos(level, new BlockPos(this.spawnX, this.spawnY, this.spawnZ)) != null) {
-                            this.nextSpawnTime = 0;
-                            this.zombiesToSpawn = 20;
+                        worldData.villageSiegeState.spawnX = blockPos.getX() + Mth.floor(Mth.cos(f) * 32.0F); // Folia - region threading
+                        worldData.villageSiegeState.spawnY = blockPos.getY(); // Folia - region threading
+                        worldData.villageSiegeState.spawnZ = blockPos.getZ() + Mth.floor(Mth.sin(f) * 32.0F); // Folia - region threading
+                        if (this.findRandomSpawnPos(level, new BlockPos(worldData.villageSiegeState.spawnX, worldData.villageSiegeState.spawnY, worldData.villageSiegeState.spawnZ)) != null) { // Folia - region threading
+                            worldData.villageSiegeState.nextSpawnTime = 0; // Folia - region threading
+                            worldData.villageSiegeState.zombiesToSpawn = 20; // Folia - region threading
                             break;
                         }
                     }
@@ -93,11 +_,13 @@
     }
 
     private void trySpawn(ServerLevel level) {
-        Vec3 vec3 = this.findRandomSpawnPos(level, new BlockPos(this.spawnX, this.spawnY, this.spawnZ));
+        io.papermc.paper.threadedregions.RegionizedWorldData worldData = level.getCurrentWorldData(); // Folia - region threading
+        Vec3 vec3 = this.findRandomSpawnPos(level, new BlockPos(worldData.villageSiegeState.spawnX, worldData.villageSiegeState.spawnY, worldData.villageSiegeState.spawnZ)); // Folia - region threading
         if (vec3 != null) {
             Zombie zombie;
             try {
                 zombie = new Zombie(level);
+                zombie.moveTo(vec3.x, vec3.y, vec3.z, level.random.nextFloat() * 360.0F, 0.0F); // Folia - region threading - move up
                 zombie.finalizeSpawn(level, level.getCurrentDifficultyAt(zombie.blockPosition()), EntitySpawnReason.EVENT, null);
             } catch (Exception var5) {
                 LOGGER.warn("Failed to create zombie for village siege at {}", vec3, var5);
@@ -105,7 +_,7 @@
                 return;
             }
 
-            zombie.moveTo(vec3.x, vec3.y, vec3.z, level.random.nextFloat() * 360.0F, 0.0F);
+            //zombie.moveTo(vec3.x, vec3.y, vec3.z, level.random.nextFloat() * 360.0F, 0.0F); // Folia - region threading - move up
             level.addFreshEntityWithPassengers(zombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_INVASION); // CraftBukkit
         }
     }
@@ -125,7 +_,7 @@
         return null;
     }
 
-    static enum State {
+    public static enum State { // Folia - region threading
         SIEGE_CAN_ACTIVATE,
         SIEGE_TONIGHT,
         SIEGE_DONE;
