From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 14 Jan 2018 17:01:31 -0500
Subject: [PATCH] PreCreatureSpawnEvent

Adds an event to fire before an Entity is created, so that plugins that need to cancel
CreatureSpawnEvent can do so from this event instead.

Cancelling CreatureSpawnEvent rapidly causes a lot of garbage collection and CPU waste
as it's done after the Entity object has been fully created.

Mob Limiting plugins and blanket "ban this type of monster" plugins should use this event
instead and save a lot of server resources.

See: https://github.com/PaperMC/Paper/issues/917

diff --git a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java
index 34ab03fce7ab5c7702234590ffc9ca7fec72a86a..2827026ba4bdd857f231028393726d6c95610072 100644
--- a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java
+++ b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java
@@ -108,6 +108,27 @@ public abstract class MobSpawnerAbstract {
                         WorldServer worldserver = (WorldServer) world;
 
                         if (EntityPositionTypes.a((EntityTypes) optional.get(), worldserver, EnumMobSpawn.SPAWNER, new BlockPosition(d3, d4, d5), world.getRandom())) {
+                            // Paper start
+                            EntityTypes<?> entityType = optional.get();
+                            String key = EntityTypes.getName(entityType).getKey();
+
+                            org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(key);
+                            if (type != null) {
+                                com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
+                                event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
+                                    MCUtil.toLocation(world, d3, d4, d5),
+                                    type,
+                                    org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER
+                                );
+                                if (!event.callEvent()) {
+                                    flag = true;
+                                    if (event.shouldAbortSpawn()) {
+                                        break;
+                                    }
+                                    continue;
+                                }
+                            }
+                            // Paper end
                             Entity entity = EntityTypes.a(nbttagcompound, world, (entity1) -> {
                                 entity1.setPositionRotation(d3, d4, d5, entity1.yaw, entity1.pitch);
                                 return entity1;
diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java
index 137ee1b453b80e96c1a8fa3271f6350f9294cafb..0ecdfd45073235b7b01e84f4490f17088c4ee675 100644
--- a/src/main/java/net/minecraft/server/SpawnerCreature.java
+++ b/src/main/java/net/minecraft/server/SpawnerCreature.java
@@ -184,9 +184,16 @@ public final class SpawnerCreature {
                                         j1 = biomesettingsmobs_c.d + worldserver.random.nextInt(1 + biomesettingsmobs_c.e - biomesettingsmobs_c.d);
                                     }
 
-                                    if (a(worldserver, enumcreaturetype, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2) && spawnercreature_c.test(biomesettingsmobs_c.c, blockposition_mutableblockposition, ichunkaccess)) {
+                                    // Paper start
+                                    Boolean doSpawning = a(worldserver, enumcreaturetype, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2);
+                                    if (doSpawning == null) {
+                                        return;
+                                    }
+                                    if (doSpawning && spawnercreature_c.test(biomesettingsmobs_c.c, blockposition_mutableblockposition, ichunkaccess)) {
+                                        // Paper end
                                         EntityInsentient entityinsentient = a(worldserver, biomesettingsmobs_c.c);
 
+
                                         if (entityinsentient == null) {
                                             return;
                                         }
@@ -239,8 +246,24 @@ public final class SpawnerCreature {
         }
     }
 
-    private static boolean a(WorldServer worldserver, EnumCreatureType enumcreaturetype, StructureManager structuremanager, ChunkGenerator chunkgenerator, BiomeSettingsMobs.c biomesettingsmobs_c, BlockPosition.MutableBlockPosition blockposition_mutableblockposition, double d0) {
+    private static Boolean a(WorldServer worldserver, EnumCreatureType enumcreaturetype, StructureManager structuremanager, ChunkGenerator chunkgenerator, BiomeSettingsMobs.c biomesettingsmobs_c, BlockPosition.MutableBlockPosition blockposition_mutableblockposition, double d0) { // Paper
         EntityTypes<?> entitytypes = biomesettingsmobs_c.c;
+        // Paper start
+        com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
+        org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(EntityTypes.getName(entitytypes).getKey());
+        if (type != null) {
+            event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
+                MCUtil.toLocation(worldserver, blockposition_mutableblockposition),
+                type, SpawnReason.NATURAL
+            );
+            if (!event.callEvent()) {
+                if (event.shouldAbortSpawn()) {
+                    return null;
+                }
+                return false;
+            }
+        }
+        // Paper end
 
         if (entitytypes.e() == EnumCreatureType.MISC) {
             return false;
