--- a/net/minecraft/world/level/levelgen/PatrolSpawner.java
+++ b/net/minecraft/world/level/levelgen/PatrolSpawner.java
@@ -7,6 +7,7 @@
 import net.minecraft.util.RandomSource;
 import net.minecraft.world.entity.EntityType;
 import net.minecraft.world.entity.MobSpawnType;
+import net.minecraft.world.entity.SpawnGroupData;
 import net.minecraft.world.entity.monster.PatrollingMonster;
 import net.minecraft.world.entity.player.Player;
 import net.minecraft.world.level.CustomSpawner;
@@ -16,95 +17,135 @@
 import net.minecraft.world.level.block.state.BlockState;
 
 public class PatrolSpawner implements CustomSpawner {
+
     private int nextTick;
 
+    public PatrolSpawner() {}
+
     @Override
     public int tick(ServerLevel world, boolean spawnMonsters, boolean spawnAnimals) {
+        if (world.paperConfig().entities.behavior.pillagerPatrols.disable || world.paperConfig().entities.behavior.pillagerPatrols.spawnChance == 0) return 0; // Paper - Add option to disable pillager patrols & Pillager patrol spawn settings and per player options
         if (!spawnMonsters) {
             return 0;
         } else if (!world.getGameRules().getBoolean(GameRules.RULE_DO_PATROL_SPAWNING)) {
             return 0;
         } else {
-            RandomSource randomSource = world.random;
-            this.nextTick--;
-            if (this.nextTick > 0) {
+            RandomSource randomsource = world.random;
+
+            // Paper start - Pillager patrol spawn settings and per player options
+            // Random player selection moved up for per player spawning and configuration
+            int j = world.players().size();
+            if (j < 1) {
+                return 0;
+            }
+
+            net.minecraft.server.level.ServerPlayer entityhuman = world.players().get(randomsource.nextInt(j));
+            if (entityhuman.isSpectator()) {
                 return 0;
+            }
+
+            int patrolSpawnDelay;
+            if (world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.perPlayer) {
+                --entityhuman.patrolSpawnDelay;
+                patrolSpawnDelay = entityhuman.patrolSpawnDelay;
             } else {
-                this.nextTick = this.nextTick + 12000 + randomSource.nextInt(1200);
-                long l = world.getDayTime() / 24000L;
-                if (l < 5L || !world.isDay()) {
-                    return 0;
-                } else if (randomSource.nextInt(5) != 0) {
-                    return 0;
+                this.nextTick--;
+                patrolSpawnDelay = this.nextTick;
+            }
+
+            if (patrolSpawnDelay > 0) {
+                return 0;
+            } else {
+                long days;
+                if (world.paperConfig().entities.behavior.pillagerPatrols.start.perPlayer) {
+                    days = entityhuman.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.PLAY_TIME)) / 24000L; // PLAY_ONE_MINUTE is actually counting in ticks, a misnomer by Mojang
                 } else {
-                    int i = world.players().size();
-                    if (i < 1) {
+                    days = world.getDayTime() / 24000L;
+                }
+                if (world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.perPlayer) {
+                    entityhuman.patrolSpawnDelay += world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.ticks + randomsource.nextInt(1200);
+                } else {
+                    this.nextTick += world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.ticks + randomsource.nextInt(1200);
+                }
+
+                if (days >= world.paperConfig().entities.behavior.pillagerPatrols.start.day && world.isDay()) {
+                    if (randomsource.nextDouble() >= world.paperConfig().entities.behavior.pillagerPatrols.spawnChance) {
+                        // Paper end - Pillager patrol spawn settings and per player options
                         return 0;
                     } else {
-                        Player player = world.players().get(randomSource.nextInt(i));
-                        if (player.isSpectator()) {
-                            return 0;
-                        } else if (world.isCloseToVillage(player.blockPosition(), 2)) {
+
+                        if (j < 1) {
                             return 0;
                         } else {
-                            int j = (24 + randomSource.nextInt(24)) * (randomSource.nextBoolean() ? -1 : 1);
-                            int k = (24 + randomSource.nextInt(24)) * (randomSource.nextBoolean() ? -1 : 1);
-                            BlockPos.MutableBlockPos mutableBlockPos = player.blockPosition().mutable().move(j, 0, k);
-                            int m = 10;
-                            if (!world.hasChunksAt(
-                                mutableBlockPos.getX() - 10, mutableBlockPos.getZ() - 10, mutableBlockPos.getX() + 10, mutableBlockPos.getZ() + 10
-                            )) {
+
+                            if (entityhuman.isSpectator()) {
+                                return 0;
+                            } else if (world.isCloseToVillage(entityhuman.blockPosition(), 2)) {
                                 return 0;
                             } else {
-                                Holder<Biome> holder = world.getBiome(mutableBlockPos);
-                                if (holder.is(BiomeTags.WITHOUT_PATROL_SPAWNS)) {
+                                int k = (24 + randomsource.nextInt(24)) * (randomsource.nextBoolean() ? -1 : 1);
+                                int l = (24 + randomsource.nextInt(24)) * (randomsource.nextBoolean() ? -1 : 1);
+                                BlockPos.MutableBlockPos blockposition_mutableblockposition = entityhuman.blockPosition().mutable().move(k, 0, l);
+                                boolean flag2 = true;
+
+                                if (!world.hasChunksAt(blockposition_mutableblockposition.getX() - 10, blockposition_mutableblockposition.getZ() - 10, blockposition_mutableblockposition.getX() + 10, blockposition_mutableblockposition.getZ() + 10)) {
                                     return 0;
                                 } else {
-                                    int n = 0;
-                                    int o = (int)Math.ceil((double)world.getCurrentDifficultyAt(mutableBlockPos).getEffectiveDifficulty()) + 1;
+                                    Holder<Biome> holder = world.getBiome(blockposition_mutableblockposition);
 
-                                    for (int p = 0; p < o; p++) {
-                                        n++;
-                                        mutableBlockPos.setY(world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, mutableBlockPos).getY());
-                                        if (p == 0) {
-                                            if (!this.spawnPatrolMember(world, mutableBlockPos, randomSource, true)) {
-                                                break;
+                                    if (holder.is(BiomeTags.WITHOUT_PATROL_SPAWNS)) {
+                                        return 0;
+                                    } else {
+                                        int i1 = 0;
+                                        int j1 = (int) Math.ceil((double) world.getCurrentDifficultyAt(blockposition_mutableblockposition).getEffectiveDifficulty()) + 1;
+
+                                        for (int k1 = 0; k1 < j1; ++k1) {
+                                            ++i1;
+                                            blockposition_mutableblockposition.setY(world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, blockposition_mutableblockposition).getY());
+                                            if (k1 == 0) {
+                                                if (!this.spawnPatrolMember(world, blockposition_mutableblockposition, randomsource, true)) {
+                                                    break;
+                                                }
+                                            } else {
+                                                this.spawnPatrolMember(world, blockposition_mutableblockposition, randomsource, false);
                                             }
-                                        } else {
-                                            this.spawnPatrolMember(world, mutableBlockPos, randomSource, false);
+
+                                            blockposition_mutableblockposition.setX(blockposition_mutableblockposition.getX() + randomsource.nextInt(5) - randomsource.nextInt(5));
+                                            blockposition_mutableblockposition.setZ(blockposition_mutableblockposition.getZ() + randomsource.nextInt(5) - randomsource.nextInt(5));
                                         }
 
-                                        mutableBlockPos.setX(mutableBlockPos.getX() + randomSource.nextInt(5) - randomSource.nextInt(5));
-                                        mutableBlockPos.setZ(mutableBlockPos.getZ() + randomSource.nextInt(5) - randomSource.nextInt(5));
+                                        return i1;
                                     }
-
-                                    return n;
                                 }
                             }
                         }
                     }
+                } else {
+                    return 0;
                 }
             }
         }
     }
 
     private boolean spawnPatrolMember(ServerLevel world, BlockPos pos, RandomSource random, boolean captain) {
-        BlockState blockState = world.getBlockState(pos);
-        if (!NaturalSpawner.isValidEmptySpawnBlock(world, pos, blockState, blockState.getFluidState(), EntityType.PILLAGER)) {
+        BlockState iblockdata = world.getBlockState(pos);
+
+        if (!NaturalSpawner.isValidEmptySpawnBlock(world, pos, iblockdata, iblockdata.getFluidState(), EntityType.PILLAGER)) {
             return false;
         } else if (!PatrollingMonster.checkPatrollingMonsterSpawnRules(EntityType.PILLAGER, world, MobSpawnType.PATROL, pos, random)) {
             return false;
         } else {
-            PatrollingMonster patrollingMonster = EntityType.PILLAGER.create(world);
-            if (patrollingMonster != null) {
+            PatrollingMonster entitymonsterpatrolling = (PatrollingMonster) EntityType.PILLAGER.create(world);
+
+            if (entitymonsterpatrolling != null) {
                 if (captain) {
-                    patrollingMonster.setPatrolLeader(true);
-                    patrollingMonster.findPatrolTarget();
+                    entitymonsterpatrolling.setPatrolLeader(true);
+                    entitymonsterpatrolling.findPatrolTarget();
                 }
 
-                patrollingMonster.setPos((double)pos.getX(), (double)pos.getY(), (double)pos.getZ());
-                patrollingMonster.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.PATROL, null);
-                world.addFreshEntityWithPassengers(patrollingMonster);
+                entitymonsterpatrolling.setPos((double) pos.getX(), (double) pos.getY(), (double) pos.getZ());
+                entitymonsterpatrolling.finalizeSpawn(world, world.getCurrentDifficultyAt(pos), MobSpawnType.PATROL, (SpawnGroupData) null);
+                world.addFreshEntityWithPassengers(entitymonsterpatrolling, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.PATROL); // CraftBukkit
                 return true;
             } else {
                 return false;
