From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Thu, 23 Nov 2023 10:33:25 -0800 Subject: [PATCH] Don't fire sync events during worldgen Fixes EntityPotionEffectEvent Fixes EntityPoseChangeEvent Asynchronous chunk generation provides an opportunity for things to happen async that previously fired synchronous-only events. This patch is for mitigating those issues by various methods. Also fixes correctly marking/clearing the entity generation flag. This patch sets the generation flag anytime an entity is created via StructureTemplate before loading from NBT to catch uses of the flag during the loading logic. This patch clears the generation flag from an entity when added to a ServerLevel for the situation where generation happened directly to a ServerLevel and the entity still has the flag set. diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 79b6602570de0e213faef04f9605f206aa5b7840..0567fbf194d3dfef3f2348c4b1f69ac857dc5648 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -1681,6 +1681,7 @@ public class ServerLevel extends Level implements WorldGenLevel { // CraftBukkit start private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) { org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot + entity.generation = false; // Paper - Don't fire sync event during generation; Reset flag if it was added during a ServerLevel generation process // Paper start if (entity.valid) { MinecraftServer.LOGGER.error("Attempted Double World add on " + entity, new Throwable()); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index ec6a75e266725ce9de09f44cb7ed4096cf23a211..63e445beaeedcbd6f06991e7ef504af9943e0c9f 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -713,7 +713,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S if (pose == this.getPose()) { return; } - this.level.getCraftServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[pose.ordinal()])); + // Paper start - Don't fire sync event during generation + if (!this.generation) { + this.level.getCraftServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[pose.ordinal()])); + } + // Paper end - Don't fire sync event during generation // CraftBukkit end this.entityData.set(Entity.DATA_POSE, pose); } diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java index 940b8d0b89d7e55c938aefbe80ee71b0db3dacb8..abb2a02e0fc1deedb0ad76aec64f74ce355129cc 100644 --- a/src/main/java/net/minecraft/world/entity/EntityType.java +++ b/src/main/java/net/minecraft/world/entity/EntityType.java @@ -588,9 +588,15 @@ public class EntityType implements FeatureElement, EntityTypeT } public static Optional create(CompoundTag nbt, Level world) { + // Paper start - Don't fire sync event during generation + return create(nbt, world, false); + } + public static Optional create(CompoundTag nbt, Level world, boolean generation) { + // Paper end - Don't fire sync event during generation return Util.ifElse(EntityType.by(nbt).map((entitytypes) -> { return entitytypes.create(world); }), (entity) -> { + if (generation) entity.generation = true; // Paper - Don't fire sync event during generation entity.load(nbt); }, () -> { EntityType.LOGGER.warn("Skipping Entity with id {}", nbt.getString("id")); diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java index d90d86f6b6e656bc6680afa07c7a9e99e6325851..f242b2c56bfc2f17e87125e642538d58feb253e0 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java @@ -1133,6 +1133,11 @@ public abstract class LivingEntity extends Entity implements Attackable { } public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause) { + // Paper start - Don't fire sync event during generation + return this.addEffect(mobeffect, entity, cause, true); + } + public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) { + // Paper end - Don't fire sync event during generation // org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API if (this.isTickingEffects) { this.effectsToProcess.add(new ProcessableEffect(mobeffect, cause)); @@ -1152,10 +1157,13 @@ public abstract class LivingEntity extends Entity implements Attackable { override = new MobEffectInstance(mobeffect1).update(mobeffect); } + if (fireEvent) { // Paper - Don't fire sync event during generation EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobeffect1, mobeffect, cause, override); + override = event.isOverride(); // Paper - Don't fire sync event during generation if (event.isCancelled()) { return false; } + } // Paper - Don't fire sync event during generation // CraftBukkit end if (mobeffect1 == null) { @@ -1163,7 +1171,7 @@ public abstract class LivingEntity extends Entity implements Attackable { this.onEffectAdded(mobeffect, entity); flag = true; // CraftBukkit start - } else if (event.isOverride()) { + } else if (override) { // Paper - Don't fire sync event during generation mobeffect1.update(mobeffect); this.onEffectUpdated(mobeffect1, true, entity); // CraftBukkit end diff --git a/src/main/java/net/minecraft/world/entity/monster/Spider.java b/src/main/java/net/minecraft/world/entity/monster/Spider.java index 5a9f4a022c8e7a0804543335bfe91e1328d040e6..8094c133f9e934c98eee09738220bacd87a0a364 100644 --- a/src/main/java/net/minecraft/world/entity/monster/Spider.java +++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java @@ -182,7 +182,7 @@ public class Spider extends Monster { MobEffect mobeffectlist = entityspider_groupdataspider.effect; if (mobeffectlist != null) { - this.addEffect(new MobEffectInstance(mobeffectlist, -1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.SPIDER_SPAWN); // CraftBukkit + this.addEffect(new MobEffectInstance(mobeffectlist, -1), null, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.SPIDER_SPAWN, world instanceof net.minecraft.server.level.ServerLevel); // CraftBukkit // Paper - Don't fire sync event during generation; only if this is happening in a ServerLevel } } diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java index 52c389472e013e658344496218689465350bf8a3..8c4d434bffa640a17d6870080f79cd0e492c7537 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +++ b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java @@ -518,7 +518,7 @@ public class StructureTemplate { private static Optional createEntityIgnoreException(ServerLevelAccessor world, CompoundTag nbt) { // CraftBukkit start // try { - return EntityType.create(nbt, world.getLevel()); + return EntityType.create(nbt, world.getLevel(), true); // Paper - Don't fire sync event during generation // } catch (Exception exception) { // return Optional.empty(); // } diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java index a650411e3fa7e2a045ac55502c77028be348acf1..86a20c91beff6b27e6ec886e49ba902b216106f2 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java +++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java @@ -93,15 +93,17 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel { return this.handle.getLevel(); } - @Override - public void addFreshEntityWithPassengers(Entity arg0, CreatureSpawnEvent.SpawnReason arg1) { - this.handle.addFreshEntityWithPassengers(arg0, arg1); - } - - @Override - public void addFreshEntityWithPassengers(Entity entity) { - this.handle.addFreshEntityWithPassengers(entity); - } + // Paper start - Don't fire sync event during generation; don't override these methods so all entities are run through addFreshEntity + // @Override + // public void addFreshEntityWithPassengers(Entity arg0, CreatureSpawnEvent.SpawnReason arg1) { + // this.handle.addFreshEntityWithPassengers(arg0, arg1); + // } + // + // @Override + // public void addFreshEntityWithPassengers(Entity entity) { + // this.handle.addFreshEntityWithPassengers(entity); + // } + // Paper end - Don't fire sync event during generation; don't override these methods @Override public ServerLevel getMinecraftWorld() { diff --git a/src/main/java/org/bukkit/craftbukkit/util/TransformerGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/TransformerGeneratorAccess.java index b4b297945fb601701aac845d09e88fb74b09c3fa..7482dfe64458320d44089c0778591694202e9f70 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/TransformerGeneratorAccess.java +++ b/src/main/java/org/bukkit/craftbukkit/util/TransformerGeneratorAccess.java @@ -39,21 +39,23 @@ public class TransformerGeneratorAccess extends DelegatedGeneratorAccess { return super.addFreshEntity(arg0, arg1); } - @Override - public void addFreshEntityWithPassengers(Entity entity) { - if (this.structureTransformer != null && !this.structureTransformer.transformEntity(entity)) { - return; - } - super.addFreshEntityWithPassengers(entity); - } - - @Override - public void addFreshEntityWithPassengers(Entity arg0, SpawnReason arg1) { - if (this.structureTransformer != null && !this.structureTransformer.transformEntity(arg0)) { - return; - } - super.addFreshEntityWithPassengers(arg0, arg1); - } + // Paper start - Don't fire sync event during generation; don't override these methods so all entities are run through addFreshEntity + // @Override + // public void addFreshEntityWithPassengers(Entity entity) { + // if (this.structureTransformer != null && !this.structureTransformer.transformEntity(entity)) { + // return; + // } + // super.addFreshEntityWithPassengers(entity); + // } + // + // @Override + // public void addFreshEntityWithPassengers(Entity arg0, SpawnReason arg1) { + // if (this.structureTransformer != null && !this.structureTransformer.transformEntity(arg0)) { + // return; + // } + // super.addFreshEntityWithPassengers(arg0, arg1); + // } + // Paper end - Don't fire sync event during generation; don't override these methods public boolean setCraftBlock(BlockPos position, CraftBlockState craftBlockState, int i, int j) { if (this.structureTransformer != null) {