From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 29 Mar 2020 18:26:14 -0400 Subject: [PATCH] Ensure Entity is never double registered If something calls register twice, and the world is ticking, it could be enqueued to add twice. Vs behavior of non ticking of just overwriting state. We will now simply log a warning when this happens instead of crashing the server. diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java index 00df89d65099cbdd96c77ac26c892f05a2c37d79..0dbe2dce111673f94d0618c7ac80262189f2926f 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -59,6 +59,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke } // Paper start + boolean isQueuedForRegister = false; public static Random SHARED_RANDOM = new Random() { private boolean locked = false; @Override diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java index 5173731dc55db416a312e13afd09e4e69d829e45..3fc25183ca41a0a575334d02a4ae9db4da41348b 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -532,6 +532,7 @@ public class WorldServer extends World { try (co.aikar.timings.Timing ignored = this.timings.newEntities.startTiming()) { // Paper - timings while ((entity = (Entity) this.entitiesToAdd.poll()) != null) { + if (!entity.isQueuedForRegister) continue; // Paper - ignore cancelled registers this.registerEntity(entity); } } // Paper - timings @@ -1353,6 +1354,19 @@ public class WorldServer extends World { public void unregisterEntity(Entity entity) { org.spigotmc.AsyncCatcher.catchOp("entity unregister"); // Spigot + // Paper start - fix entity registration issues + if (entity instanceof EntityComplexPart) { + // Usually this is a no-op for complex parts, and ID's should be removed, but go ahead and remove it anyways + // Dragon parts are handled special in register. they don't receive a valid = true or register by UUID etc. + this.entitiesById.remove(entity.getId(), entity); + return; + } + if (!entity.valid) { + // Someone called remove before we ever got added, cancel the add. + entity.isQueuedForRegister = false; + return; + } + // Paper end // Spigot start if ( entity instanceof EntityHuman ) { @@ -1413,9 +1427,21 @@ public class WorldServer extends World { private void registerEntity(Entity entity) { org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot + // Paper start - don't double enqueue entity registration + //noinspection ObjectEquality + if (this.entitiesById.get(entity.getId()) == entity) { + LOGGER.error(entity + " was already registered!"); + new Throwable().printStackTrace(); + return; + } + // Paper end if (this.tickingEntities) { - this.entitiesToAdd.add(entity); + if (!entity.isQueuedForRegister) { // Paper + this.entitiesToAdd.add(entity); + entity.isQueuedForRegister = true; // Paper + } } else { + entity.isQueuedForRegister = false; // Paper this.entitiesById.put(entity.getId(), entity); if (entity instanceof EntityEnderDragon) { EntityComplexPart[] aentitycomplexpart = ((EntityEnderDragon) entity).eo();