From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar 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/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java index 8699a3bd63e2df83615ae39a6873d9874f0fb8da..05a3d736193775c1aa6ce3759e13a1f76f747d09 100644 --- a/src/main/java/net/minecraft/world/entity/EntityType.java +++ b/src/main/java/net/minecraft/world/entity/EntityType.java @@ -338,6 +338,20 @@ public class EntityType implements EntityTypeTest { @Nullable public T spawn(ServerLevel worldserver, @Nullable CompoundTag nbttagcompound, @Nullable Component ichatbasecomponent, @Nullable Player entityhuman, BlockPos blockposition, MobSpawnType enummobspawn, boolean flag, boolean flag1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { + // Paper start - Call PreCreatureSpawnEvent + org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(EntityType.getKey(this).getPath()); + if (type != null) { + com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event; + event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( + net.minecraft.server.MCUtil.toLocation(worldserver, blockposition), + type, + spawnReason + ); + if (!event.callEvent()) { + return null; + } + } + // Paper end T t0 = this.create(worldserver, nbttagcompound, ichatbasecomponent, entityhuman, blockposition, enummobspawn, flag, flag1); if (t0 != null) { diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java index ac44ba98d0acc9aa2a75088d8a9a145fdbcbd911..ecd7083b40ce6230d49ea4487766a6f06b4e96d0 100644 --- a/src/main/java/net/minecraft/world/entity/npc/Villager.java +++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java @@ -997,6 +997,21 @@ public class Villager extends AbstractVillager implements ReputationEventHandler BlockPos blockposition1 = this.findSpawnPositionForGolemInColumn(blockposition, d0, d1); if (blockposition1 != null) { + // Paper start - Call PreCreatureSpawnEvent + com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event; + event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( + net.minecraft.server.MCUtil.toLocation(level, blockposition1), + org.bukkit.entity.EntityType.IRON_GOLEM, + org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE + ); + if (!event.callEvent()) { + if (event.shouldAbortSpawn()) { + GolemSensor.golemDetected(this); // Set Golem Last Seen to stop it from spawning another one + return null; + } + break; + } + // Paper end IronGolem entityirongolem = (IronGolem) EntityType.IRON_GOLEM.create(world, (CompoundTag) null, (Component) null, (Player) null, blockposition1, MobSpawnType.MOB_SUMMONED, false, false); if (entityirongolem != null) { diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java index 5bf68b03ddfc7c9554c467e2c0588084a796f6fa..18b1b7c05665ee1fb06670ceded719b19f3a4ee3 100644 --- a/src/main/java/net/minecraft/world/level/BaseSpawner.java +++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java @@ -122,6 +122,27 @@ public abstract class BaseSpawner { } else if (!SpawnPlacements.checkSpawnRules((EntityType) optional.get(), world, MobSpawnType.SPAWNER, blockposition1, world.getRandom())) { continue; } + // Paper start + EntityType entityType = optional.get(); + String key = EntityType.getKey(entityType).getPath(); + + 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( + net.minecraft.server.MCUtil.toLocation(world, d0, d1, d2), + type, + org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER + ); + if (!event.callEvent()) { + flag = true; + if (event.shouldAbortSpawn()) { + break; + } + continue; + } + } + // Paper end Entity entity = EntityType.loadEntityRecursive(nbttagcompound, world, (entity1) -> { entity1.moveTo(d0, d1, d2, entity1.getYRot(), entity1.getXRot()); diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java index fee21a585b95448a5edab70002e9c4ea36a5d989..a62c5f48e54d10eb416111448cd250704ef594a0 100644 --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java @@ -211,7 +211,13 @@ public final class NaturalSpawner { j1 = biomesettingsmobs_c.minCount + world.random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount); } - if (NaturalSpawner.isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2) && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) { + // Paper start + Boolean doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2); + if (doSpawning == null) { + return; + } + if (doSpawning && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) { + // Paper end Mob entityinsentient = NaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type); if (entityinsentient == null) { @@ -258,9 +264,25 @@ public final class NaturalSpawner { return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos)); } - private static boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { + private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureFeatureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { // Paper EntityType entitytypes = spawnEntry.type; + // Paper start + com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event; + org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(EntityType.getKey(entitytypes).getPath()); + if (type != null) { + event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( + net.minecraft.server.MCUtil.toLocation(world, pos), + type, SpawnReason.NATURAL + ); + if (!event.callEvent()) { + if (event.shouldAbortSpawn()) { + return null; + } + return false; + } + } + // Paper end if (entitytypes.getCategory() == MobCategory.MISC) { return false; } else if (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance())) {