2024-01-04 21:55:01 +01:00
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
|
|
|
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
|
Rework async chunk api implementation
Firstly, the old methods all routed to the CompletableFuture method.
However, the CF method could not guarantee that if the caller
was off-main that the future would be "completed" on-main. Since
the callback methods used the CF one, this meant that the callback
methods did not guarantee that the callbacks were to be called on
the main thread.
Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb)
so that the methods with the callback are guaranteed to invoke
the callback on the main thread. The CF behavior remains unchanged;
it may still appear to complete on main if invoked off-main.
Secondly, remove the scheduleOnMain invocation in the async
chunk completion. This unnecessarily delays the callback
by 1 tick.
Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which
will load chunks within an area. This method is provided as a helper
as keeping all chunks loaded within an area can be complicated to
implement for plugins (due to the lacking ticket API), and is
already implemented internally anyways.
Fourthly, remove the ticket addition that occured with getChunkAt
and getChunkAtAsync. The ticket addition may delay the unloading
of the chunk unnecessarily. It also fixes a very rare timing bug
where the future/callback would be completed after the chunk
unloads.
2024-11-19 07:34:32 +01:00
|
|
|
index 6923624f069c1aeb757386ef91d285769a2923a2..7ab4e93ae11e3a510cb58aeed8029c19dfccf31d 100644
|
2024-01-04 21:55:01 +01:00
|
|
|
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
|
|
|
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
Rework async chunk api implementation
Firstly, the old methods all routed to the CompletableFuture method.
However, the CF method could not guarantee that if the caller
was off-main that the future would be "completed" on-main. Since
the callback methods used the CF one, this meant that the callback
methods did not guarantee that the callbacks were to be called on
the main thread.
Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb)
so that the methods with the callback are guaranteed to invoke
the callback on the main thread. The CF behavior remains unchanged;
it may still appear to complete on main if invoked off-main.
Secondly, remove the scheduleOnMain invocation in the async
chunk completion. This unnecessarily delays the callback
by 1 tick.
Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which
will load chunks within an area. This method is provided as a helper
as keeping all chunks loaded within an area can be complicated to
implement for plugins (due to the lacking ticket API), and is
already implemented internally anyways.
Fourthly, remove the ticket addition that occured with getChunkAt
and getChunkAtAsync. The ticket addition may delay the unloading
of the chunk unnecessarily. It also fixes a very rare timing bug
where the future/callback would be completed after the chunk
unloads.
2024-11-19 07:34:32 +01:00
|
|
|
@@ -1194,6 +1194,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
2024-01-04 21:55:01 +01:00
|
|
|
// CraftBukkit start
|
|
|
|
private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
|
|
|
|
org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
|
2024-01-13 18:34:33 +01:00
|
|
|
+ entity.generation = false; // Paper - Don't fire sync event during generation; Reset flag if it was added during a ServerLevel generation process
|
2024-01-21 17:39:05 +01:00
|
|
|
// Paper start - extra debug info
|
2024-01-04 21:55:01 +01:00
|
|
|
if (entity.valid) {
|
2024-01-21 17:39:05 +01:00
|
|
|
MinecraftServer.LOGGER.error("Attempted Double World add on {}", entity, new Throwable());
|
2024-01-04 21:55:01 +01:00
|
|
|
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
Rework async chunk api implementation
Firstly, the old methods all routed to the CompletableFuture method.
However, the CF method could not guarantee that if the caller
was off-main that the future would be "completed" on-main. Since
the callback methods used the CF one, this meant that the callback
methods did not guarantee that the callbacks were to be called on
the main thread.
Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb)
so that the methods with the callback are guaranteed to invoke
the callback on the main thread. The CF behavior remains unchanged;
it may still appear to complete on main if invoked off-main.
Secondly, remove the scheduleOnMain invocation in the async
chunk completion. This unnecessarily delays the callback
by 1 tick.
Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which
will load chunks within an area. This method is provided as a helper
as keeping all chunks loaded within an area can be complicated to
implement for plugins (due to the lacking ticket API), and is
already implemented internally anyways.
Fourthly, remove the ticket addition that occured with getChunkAt
and getChunkAtAsync. The ticket addition may delay the unloading
of the chunk unnecessarily. It also fixes a very rare timing bug
where the future/callback would be completed after the chunk
unloads.
2024-11-19 07:34:32 +01:00
|
|
|
index 2342892db8b2bb1994727e611abb820f8104e486..12afae25cf60a402f92d1a4054738d50f076348c 100644
|
2024-01-04 21:55:01 +01:00
|
|
|
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
2024-11-04 18:42:38 +01:00
|
|
|
@@ -632,7 +632,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
2024-01-04 21:55:01 +01:00
|
|
|
if (pose == this.getPose()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
- this.level.getCraftServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[pose.ordinal()]));
|
2024-01-13 18:34:33 +01:00
|
|
|
+ // Paper start - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
+ if (!this.generation) {
|
|
|
|
+ this.level.getCraftServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[pose.ordinal()]));
|
|
|
|
+ }
|
2024-01-13 18:34:33 +01:00
|
|
|
+ // Paper end - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
// 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
|
2024-10-27 18:11:15 +01:00
|
|
|
index 64dc0bd1900575e40ac72a98c6df371223bd244c..c2693d530be00af16b2aa4ca4afd1d136db68183 100644
|
2024-01-04 21:55:01 +01:00
|
|
|
--- a/src/main/java/net/minecraft/world/entity/EntityType.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/entity/EntityType.java
|
2024-10-27 18:11:15 +01:00
|
|
|
@@ -649,9 +649,15 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
2024-01-04 21:55:01 +01:00
|
|
|
}
|
|
|
|
|
2024-10-24 12:11:32 +02:00
|
|
|
public static Optional<Entity> create(CompoundTag nbt, Level world, EntitySpawnReason reason) {
|
|
|
|
+ // Paper start - Don't fire sync event during generation
|
2024-10-25 13:34:01 +02:00
|
|
|
+ return create(nbt, world, reason, false);
|
2024-01-04 21:55:01 +01:00
|
|
|
+ }
|
2024-10-24 12:11:32 +02:00
|
|
|
+ public static Optional<Entity> create(CompoundTag nbt, Level world, EntitySpawnReason reason, boolean generation) {
|
|
|
|
+ // Paper end - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
return Util.ifElse(EntityType.by(nbt).map((entitytypes) -> {
|
2024-10-24 12:11:32 +02:00
|
|
|
return entitytypes.create(world, reason);
|
2024-01-04 21:55:01 +01:00
|
|
|
}), (entity) -> {
|
2024-01-13 18:34:33 +01:00
|
|
|
+ if (generation) entity.generation = true; // Paper - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
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
|
Rework async chunk api implementation
Firstly, the old methods all routed to the CompletableFuture method.
However, the CF method could not guarantee that if the caller
was off-main that the future would be "completed" on-main. Since
the callback methods used the CF one, this meant that the callback
methods did not guarantee that the callbacks were to be called on
the main thread.
Now, all methods route to getChunkAtAsync(x, z, gen, urgent, cb)
so that the methods with the callback are guaranteed to invoke
the callback on the main thread. The CF behavior remains unchanged;
it may still appear to complete on main if invoked off-main.
Secondly, remove the scheduleOnMain invocation in the async
chunk completion. This unnecessarily delays the callback
by 1 tick.
Thirdly, add getChunksAtAsync(minX, minZ, maxX, maxZ, ...) which
will load chunks within an area. This method is provided as a helper
as keeping all chunks loaded within an area can be complicated to
implement for plugins (due to the lacking ticket API), and is
already implemented internally anyways.
Fourthly, remove the ticket addition that occured with getChunkAt
and getChunkAtAsync. The ticket addition may delay the unloading
of the chunk unnecessarily. It also fixes a very rare timing bug
where the future/callback would be completed after the chunk
unloads.
2024-11-19 07:34:32 +01:00
|
|
|
index 84623aa24f8c90f0b51094c2d0773ee98c3a8524..3229db46d1efa2c58182043a3d2841040eb021f2 100644
|
2024-01-04 21:55:01 +01:00
|
|
|
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
2024-10-27 18:11:15 +01:00
|
|
|
@@ -1159,6 +1159,11 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
2024-01-04 21:55:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause) {
|
2024-01-13 18:34:33 +01:00
|
|
|
+ // Paper start - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
+ return this.addEffect(mobeffect, entity, cause, true);
|
|
|
|
+ }
|
|
|
|
+ public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) {
|
2024-01-13 18:34:33 +01:00
|
|
|
+ // Paper end - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
// org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API
|
|
|
|
if (this.isTickingEffects) {
|
|
|
|
this.effectsToProcess.add(new ProcessableEffect(mobeffect, cause));
|
2024-10-27 18:11:15 +01:00
|
|
|
@@ -1178,10 +1183,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
2024-01-04 21:55:01 +01:00
|
|
|
override = new MobEffectInstance(mobeffect1).update(mobeffect);
|
|
|
|
}
|
|
|
|
|
2024-01-13 18:34:33 +01:00
|
|
|
+ if (fireEvent) { // Paper - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
EntityPotionEffectEvent event = CraftEventFactory.callEntityPotionEffectChangeEvent(this, mobeffect1, mobeffect, cause, override);
|
2024-01-13 18:34:33 +01:00
|
|
|
+ override = event.isOverride(); // Paper - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
if (event.isCancelled()) {
|
|
|
|
return false;
|
|
|
|
}
|
2024-01-13 18:34:33 +01:00
|
|
|
+ } // Paper - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
// CraftBukkit end
|
|
|
|
|
|
|
|
if (mobeffect1 == null) {
|
2024-10-27 18:11:15 +01:00
|
|
|
@@ -1190,7 +1198,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
2024-01-04 21:55:01 +01:00
|
|
|
flag = true;
|
2024-04-25 01:25:57 +02:00
|
|
|
mobeffect.onEffectAdded(this);
|
2024-01-04 21:55:01 +01:00
|
|
|
// CraftBukkit start
|
|
|
|
- } else if (event.isOverride()) {
|
2024-01-13 18:34:33 +01:00
|
|
|
+ } else if (override) { // Paper - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
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
|
2024-10-24 12:11:32 +02:00
|
|
|
index 6c2d4c2163cf299c0943af21d4dc367b5677c089..72e42605c278028480c368762da18f61806d766a 100644
|
2024-01-04 21:55:01 +01:00
|
|
|
--- a/src/main/java/net/minecraft/world/entity/monster/Spider.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/entity/monster/Spider.java
|
2024-04-25 01:25:57 +02:00
|
|
|
@@ -172,7 +172,7 @@ public class Spider extends Monster {
|
|
|
|
Holder<MobEffect> holder = entityspider_groupdataspider.effect;
|
2024-01-04 21:55:01 +01:00
|
|
|
|
2024-04-25 01:25:57 +02:00
|
|
|
if (holder != null) {
|
|
|
|
- this.addEffect(new MobEffectInstance(holder, -1), org.bukkit.event.entity.EntityPotionEffectEvent.Cause.SPIDER_SPAWN); // CraftBukkit
|
|
|
|
+ this.addEffect(new MobEffectInstance(holder, -1), null, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.SPIDER_SPAWN, world instanceof net.minecraft.server.level.ServerLevel); // CraftBukkit
|
2024-01-04 21:55:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2024-10-24 12:11:32 +02:00
|
|
|
index 734f511d197bc6bf2b02588069eb02c0224781f5..d35b731751e851bee531aa5e7996557658ba6fae 100644
|
2024-01-04 21:55:01 +01:00
|
|
|
--- 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
|
2024-10-24 12:11:32 +02:00
|
|
|
@@ -549,7 +549,7 @@ public class StructureTemplate {
|
2024-01-04 21:55:01 +01:00
|
|
|
private static Optional<Entity> createEntityIgnoreException(ServerLevelAccessor world, CompoundTag nbt) {
|
|
|
|
// CraftBukkit start
|
|
|
|
// try {
|
2024-10-24 12:11:32 +02:00
|
|
|
- return EntityType.create(nbt, world.getLevel(), EntitySpawnReason.STRUCTURE);
|
|
|
|
+ return EntityType.create(nbt, world.getLevel(), EntitySpawnReason.STRUCTURE, true); // Paper - Don't fire sync event during generation
|
2024-01-04 21:55:01 +01:00
|
|
|
// } 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
|
2024-10-24 12:11:32 +02:00
|
|
|
index e444662ee4d9405eeea7caa41b9cd6b36586d840..54c4434662d057a08800918641b95708cda61207 100644
|
2024-01-04 21:55:01 +01:00
|
|
|
--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
|
|
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
|
2024-10-24 12:11:32 +02:00
|
|
|
@@ -90,15 +90,17 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel {
|
2024-01-04 21:55:01 +01:00
|
|
|
return this.handle.getLevel();
|
|
|
|
}
|
|
|
|
|
|
|
|
- @Override
|
2024-10-24 12:11:32 +02:00
|
|
|
- public void addFreshEntityWithPassengers(Entity entity) {
|
|
|
|
- this.handle.addFreshEntityWithPassengers(entity);
|
2024-01-04 21:55:01 +01:00
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @Override
|
2024-10-24 12:11:32 +02:00
|
|
|
- public void addFreshEntityWithPassengers(Entity entity, CreatureSpawnEvent.SpawnReason reason) {
|
|
|
|
- this.handle.addFreshEntityWithPassengers(entity, reason);
|
2024-01-04 21:55:01 +01:00
|
|
|
- }
|
2024-01-13 18:34:33 +01:00
|
|
|
+ // Paper start - Don't fire sync event during generation; don't override these methods so all entities are run through addFreshEntity
|
2024-01-04 21:55:01 +01:00
|
|
|
+ // @Override
|
2024-10-24 12:11:32 +02:00
|
|
|
+ // public void addFreshEntityWithPassengers(Entity entity) {
|
|
|
|
+ // this.handle.addFreshEntityWithPassengers(entity);
|
2024-01-04 21:55:01 +01:00
|
|
|
+ // }
|
|
|
|
+ //
|
|
|
|
+ // @Override
|
2024-10-24 12:11:32 +02:00
|
|
|
+ // public void addFreshEntityWithPassengers(Entity entity, CreatureSpawnEvent.SpawnReason reason) {
|
|
|
|
+ // this.handle.addFreshEntityWithPassengers(entity, reason);
|
2024-01-04 21:55:01 +01:00
|
|
|
+ // }
|
2024-10-24 12:11:32 +02:00
|
|
|
+ // Paper end - Don't fire sync event during generation; don't override these methods so all entities are run through addFreshEntity
|
2024-01-04 21:55:01 +01:00
|
|
|
|
|
|
|
@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
|
2024-04-25 01:25:57 +02:00
|
|
|
index 35ecf6f824aca56a20280dd683123df1d0c7d66e..1d1fdcf10498c421f106158254e052da6d68d8a5 100644
|
2024-01-04 21:55:01 +01:00
|
|
|
--- 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);
|
|
|
|
- }
|
2024-01-13 18:34:33 +01:00
|
|
|
+ // Paper start - Don't fire sync event during generation; don't override these methods so all entities are run through addFreshEntity
|
2024-01-04 21:55:01 +01:00
|
|
|
+ // @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);
|
|
|
|
+ // }
|
2024-01-13 18:34:33 +01:00
|
|
|
+ // Paper end - Don't fire sync event during generation; don't override these methods
|
2024-01-04 21:55:01 +01:00
|
|
|
|
|
|
|
public boolean setCraftBlock(BlockPos position, CraftBlockState craftBlockState, int i, int j) {
|
|
|
|
if (this.structureTransformer != null) {
|