aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0172-PreCreatureSpawnEvent.patch
blob: 4da8e04bb8e68ed5e13434b2f3dec9058714085a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
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/util/SpawnUtil.java b/src/main/java/net/minecraft/util/SpawnUtil.java
index b77ebe04f1018962b85110258c8a0a2db8612485..028d69907a988e191213a17e072ef22710b5bc83 100644
--- a/src/main/java/net/minecraft/util/SpawnUtil.java
+++ b/src/main/java/net/minecraft/util/SpawnUtil.java
@@ -22,10 +22,10 @@ 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) {
         // CraftBukkit start
-        return SpawnUtil.trySpawnMob(entityType, reason, world, pos, tries, horizontalRange, verticalRange, requirements, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
+        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) {
+    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();
 
@@ -35,6 +35,26 @@ public class SpawnUtil {
 
             blockposition_mutableblockposition.setWithOffset(blockposition, i1, k, j1);
             if (worldserver.getWorldBorder().isWithinBounds((BlockPos) blockposition_mutableblockposition) && SpawnUtil.moveToPossibleSpawnPosition(worldserver, k, blockposition_mutableblockposition, spawnutil_a)) {
+                // Paper start
+                String key = EntityType.getKey(entitytypes).getPath();
+                org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(key);
+
+                com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
+                event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
+                    io.papermc.paper.util.MCUtil.toLocation(worldserver, blockposition),
+                    type,
+                    reason
+                );
+                if (!event.callEvent()) {
+                    if (event.shouldAbortSpawn()) {
+                        if (onAbort != null) {
+                            onAbort.run();
+                        }
+                        return Optional.empty();
+                    }
+                    break;
+                }
+                // Paper end
                 T t0 = entitytypes.create(worldserver, (CompoundTag) null, null, blockposition_mutableblockposition, enummobspawn, false, false); // CraftBukkit - decompile error
 
                 if (t0 != null) {
diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java
index 93a43ef867d0961b46f9ecadc2c7be6a4b17c72b..c2aafe4e1afec2793735bf7b0bbd6af94ad393f8 100644
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
@@ -413,6 +413,20 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
     @Nullable
     public T spawn(ServerLevel worldserver, @Nullable CompoundTag nbttagcompound, @Nullable Consumer<T> consumer, BlockPos blockposition, MobSpawnType enummobspawn, boolean flag, boolean flag1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
         // CraftBukkit end
+        // 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(
+                io.papermc.paper.util.MCUtil.toLocation(worldserver, blockposition),
+                type,
+                spawnReason
+            );
+            if (!event.callEvent()) {
+                return null;
+            }
+        }
+        // Paper end
         T t0 = this.create(worldserver, nbttagcompound, consumer, 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 68594d2621267f4b112b4d14d2bec3a0dd6a044a..73a5750dd47cf8869070f92594cfb926193c8761 100644
--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java
+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java
@@ -966,7 +966,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
             }).limit(5L).collect(Collectors.toList());
 
             if (list1.size() >= requiredCount) {
-                if (SpawnUtil.trySpawnMob(EntityType.IRON_GOLEM, MobSpawnType.MOB_SUMMONED, world, this.blockPosition(), 10, 8, 6, SpawnUtil.Strategy.LEGACY_IRON_GOLEM, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE).isPresent()) { // CraftBukkit
+                if (SpawnUtil.trySpawnMob(EntityType.IRON_GOLEM, MobSpawnType.MOB_SUMMONED, world, this.blockPosition(), 10, 8, 6, SpawnUtil.Strategy.LEGACY_IRON_GOLEM, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE, () -> {GolemSensor.golemDetected(this);}).isPresent()) { // CraftBukkit // Paper - Set Golem Last Seen to stop it from spawning another one
                     list.forEach(GolemSensor::golemDetected);
                 }
             }
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
index 20c39481bcf05e0d43c97b7e841ec9f5f6a0d45d..fc7719d12b5f6011aec2e41a36b4bacd77672b6d 100644
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
@@ -128,6 +128,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(
+                                io.papermc.paper.util.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 b2bb9bbd3af414c50ec3f8e3e171a679e95ef75e..5338e0e1a67925da0c386735a545bb31096afbb1 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -213,7 +213,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) {
@@ -261,9 +267,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, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) {
+    private static Boolean isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager 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(
+                io.papermc.paper.util.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())) {