--- a/net/minecraft/util/SpawnUtil.java
+++ b/net/minecraft/util/SpawnUtil.java
@@ -1,6 +1,7 @@
 package net.minecraft.util;
 
 import java.util.Optional;
+import java.util.function.Consumer;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
 import net.minecraft.server.level.ServerLevel;
@@ -15,31 +16,49 @@
 import net.minecraft.world.level.block.state.BlockState;
 
 public class SpawnUtil {
-    public static <T extends Mob> Optional<T> trySpawnMob(
-        EntityType<T> entityType,
-        MobSpawnType reason,
-        ServerLevel world,
-        BlockPos pos,
-        int tries,
-        int horizontalRange,
-        int verticalRange,
-        SpawnUtil.Strategy requirements
-    ) {
-        BlockPos.MutableBlockPos mutableBlockPos = pos.mutable();
 
-        for (int i = 0; i < tries; i++) {
-            int j = Mth.randomBetweenInclusive(world.random, -horizontalRange, horizontalRange);
-            int k = Mth.randomBetweenInclusive(world.random, -horizontalRange, horizontalRange);
-            mutableBlockPos.setWithOffset(pos, j, verticalRange, k);
-            if (world.getWorldBorder().isWithinBounds(mutableBlockPos) && moveToPossibleSpawnPosition(world, verticalRange, mutableBlockPos, requirements)) {
-                T mob = (T)entityType.create(world, null, mutableBlockPos, reason, false, false);
-                if (mob != null) {
-                    if (mob.checkSpawnRules(world, reason) && mob.checkSpawnObstruction(world)) {
-                        world.addFreshEntityWithPassengers(mob);
-                        return Optional.of(mob);
+    public SpawnUtil() {}
+
+    public static <T extends Mob> Optional<T> trySpawnMob(EntityType<T> entityType, MobSpawnType reason, ServerLevel world, BlockPos pos, int tries, int horizontalRange, int verticalRange, SpawnUtil.Strategy requirements) {
+        // CraftBukkit start
+        return SpawnUtil.trySpawnMob(entityType, reason, world, pos, tries, horizontalRange, verticalRange, requirements, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT, null); // Paper
+    }
+
+    public static <T extends Mob> Optional<T> trySpawnMob(EntityType<T> entitytypes, MobSpawnType enummobspawn, ServerLevel worldserver, BlockPos blockposition, int i, int j, int k, SpawnUtil.Strategy spawnutil_a, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason, @javax.annotation.Nullable Runnable onAbort) { // Paper
+        // CraftBukkit end
+        BlockPos.MutableBlockPos blockposition_mutableblockposition = blockposition.mutable();
+
+        for (int l = 0; l < i; ++l) {
+            int i1 = Mth.randomBetweenInclusive(worldserver.random, -j, j);
+            int j1 = Mth.randomBetweenInclusive(worldserver.random, -j, j);
+
+            blockposition_mutableblockposition.setWithOffset(blockposition, i1, k, j1);
+            if (worldserver.getWorldBorder().isWithinBounds((BlockPos) blockposition_mutableblockposition) && SpawnUtil.moveToPossibleSpawnPosition(worldserver, k, blockposition_mutableblockposition, spawnutil_a)) {
+                // Paper start - PreCreatureSpawnEvent
+                com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
+                    io.papermc.paper.util.MCUtil.toLocation(worldserver, blockposition),
+                    org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entitytypes),
+                    reason
+                );
+                if (!event.callEvent()) {
+                    if (event.shouldAbortSpawn()) {
+                        if (onAbort != null) {
+                            onAbort.run();
+                        }
+                        return Optional.empty();
                     }
+                    break;
+                }
+                // Paper end - PreCreatureSpawnEvent
+                T t0 = entitytypes.create(worldserver, (Consumer<T>) null, blockposition_mutableblockposition, enummobspawn, false, false); // CraftBukkit - decompile error
 
-                    mob.discard();
+                if (t0 != null) {
+                    if (t0.checkSpawnRules(worldserver, enummobspawn) && t0.checkSpawnObstruction(worldserver)) {
+                        worldserver.addFreshEntityWithPassengers(t0, reason); // CraftBukkit
+                        return Optional.of(t0);
+                    }
+
+                    t0.discard(null); // CraftBukkit - add Bukkit remove cause
                 }
             }
         }
@@ -48,45 +67,35 @@
     }
 
     private static boolean moveToPossibleSpawnPosition(ServerLevel world, int verticalRange, BlockPos.MutableBlockPos pos, SpawnUtil.Strategy requirements) {
-        BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos().set(pos);
-        BlockState blockState = world.getBlockState(mutableBlockPos);
+        BlockPos.MutableBlockPos blockposition_mutableblockposition1 = (new BlockPos.MutableBlockPos()).set(pos);
+        BlockState iblockdata = world.getBlockState(blockposition_mutableblockposition1);
 
-        for (int i = verticalRange; i >= -verticalRange; i--) {
+        for (int j = verticalRange; j >= -verticalRange; --j) {
             pos.move(Direction.DOWN);
-            mutableBlockPos.setWithOffset(pos, Direction.UP);
-            BlockState blockState2 = world.getBlockState(pos);
-            if (requirements.canSpawnOn(world, pos, blockState2, mutableBlockPos, blockState)) {
+            blockposition_mutableblockposition1.setWithOffset(pos, Direction.UP);
+            BlockState iblockdata1 = world.getBlockState(pos);
+
+            if (requirements.canSpawnOn(world, pos, iblockdata1, blockposition_mutableblockposition1, iblockdata)) {
                 pos.move(Direction.UP);
                 return true;
             }
 
-            blockState = blockState2;
+            iblockdata = iblockdata1;
         }
 
         return false;
     }
 
     public interface Strategy {
+
+        /** @deprecated */
         @Deprecated
-        SpawnUtil.Strategy LEGACY_IRON_GOLEM = (world, pos, state, abovePos, aboveState) -> !state.is(Blocks.COBWEB)
-                && !state.is(Blocks.CACTUS)
-                && !state.is(Blocks.GLASS_PANE)
-                && !(state.getBlock() instanceof StainedGlassPaneBlock)
-                && !(state.getBlock() instanceof StainedGlassBlock)
-                && !(state.getBlock() instanceof LeavesBlock)
-                && !state.is(Blocks.CONDUIT)
-                && !state.is(Blocks.ICE)
-                && !state.is(Blocks.TNT)
-                && !state.is(Blocks.GLOWSTONE)
-                && !state.is(Blocks.BEACON)
-                && !state.is(Blocks.SEA_LANTERN)
-                && !state.is(Blocks.FROSTED_ICE)
-                && !state.is(Blocks.TINTED_GLASS)
-                && !state.is(Blocks.GLASS)
-                && (aboveState.isAir() || aboveState.liquid())
-                && (state.isSolid() || state.is(Blocks.POWDER_SNOW));
-        SpawnUtil.Strategy ON_TOP_OF_COLLIDER = (world, pos, state, abovePos, aboveState) -> aboveState.getCollisionShape(world, abovePos).isEmpty()
-                && Block.isFaceFull(state.getCollisionShape(world, pos), Direction.UP);
+        SpawnUtil.Strategy LEGACY_IRON_GOLEM = (worldserver, blockposition, iblockdata, blockposition1, iblockdata1) -> {
+            return !iblockdata.is(Blocks.COBWEB) && !iblockdata.is(Blocks.CACTUS) && !iblockdata.is(Blocks.GLASS_PANE) && !(iblockdata.getBlock() instanceof StainedGlassPaneBlock) && !(iblockdata.getBlock() instanceof StainedGlassBlock) && !(iblockdata.getBlock() instanceof LeavesBlock) && !iblockdata.is(Blocks.CONDUIT) && !iblockdata.is(Blocks.ICE) && !iblockdata.is(Blocks.TNT) && !iblockdata.is(Blocks.GLOWSTONE) && !iblockdata.is(Blocks.BEACON) && !iblockdata.is(Blocks.SEA_LANTERN) && !iblockdata.is(Blocks.FROSTED_ICE) && !iblockdata.is(Blocks.TINTED_GLASS) && !iblockdata.is(Blocks.GLASS) ? (iblockdata1.isAir() || iblockdata1.liquid()) && (iblockdata.isSolid() || iblockdata.is(Blocks.POWDER_SNOW)) : false;
+        };
+        SpawnUtil.Strategy ON_TOP_OF_COLLIDER = (worldserver, blockposition, iblockdata, blockposition1, iblockdata1) -> {
+            return iblockdata1.getCollisionShape(worldserver, blockposition1).isEmpty() && Block.isFaceFull(iblockdata.getCollisionShape(worldserver, blockposition), Direction.UP);
+        };
 
         boolean canSpawnOn(ServerLevel world, BlockPos pos, BlockState state, BlockPos abovePos, BlockState aboveState);
     }
