From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 21 Jul 2018 08:25:40 -0400 Subject: [PATCH] Add Debug Entities option to debug dupe uuid issues Add -Ddebug.entities=true to your JVM flags to gain more information diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java index 18ec6f553ddea775b279658a280fc2a3e5f96fa9..be5ad564964f26c90440bb30464edbf9882ff1e1 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -76,6 +76,8 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper private CraftEntity bukkitEntity; + PlayerChunkMap.EntityTracker tracker; // Paper + Throwable addedToWorldStack; // Paper - entity debug public CraftEntity getBukkitEntity() { if (bukkitEntity == null) { bukkitEntity = CraftEntity.getEntity(world.getServer(), this); diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java index 8bfa412b0d3f881f694abec89101e15aa2d00bb7..6d048fbb48444885051efcb83a55d32047ff6517 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -1094,6 +1094,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } else { PlayerChunkMap.EntityTracker playerchunkmap_entitytracker = new PlayerChunkMap.EntityTracker(entity, i, j, entitytypes.isDeltaTracking()); + entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker this.trackedEntities.put(entity.getId(), playerchunkmap_entitytracker); playerchunkmap_entitytracker.track(this.world.getPlayers()); if (entity instanceof EntityPlayer) { @@ -1135,7 +1136,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { if (playerchunkmap_entitytracker1 != null) { playerchunkmap_entitytracker1.a(); } - + entity.tracker = null; // Paper - We're no longer tracked } protected void g() { diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java index d3c282eb5253351ec4288bffaeca715547089cec..cb1330ef117224aea07208cebff544c714ae313a 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -68,6 +68,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable { public boolean pvpMode; public boolean keepSpawnInMemory = true; public org.bukkit.generator.ChunkGenerator generator; + public static final boolean DEBUG_ENTITIES = Boolean.getBoolean("debug.entities"); // Paper public boolean captureBlockStates = false; public boolean captureTreeGeneration = false; diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java index 89cd1348ab609555d7c49536fa56ba7dcde2d601..f0c088c4c3283725e515e468faab1dee6a04a853 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -88,6 +88,9 @@ public class WorldServer extends World implements GeneratorAccessSeed { public final Convertable.ConversionSession convertable; public final UUID uuid; boolean hasPhysicsEvent = true; // Paper + private static Throwable getAddToWorldStackTrace(Entity entity) { + return new Throwable(entity + " Added to world at " + new java.util.Date()); + } @Override public Chunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI return this.chunkProvider.getChunkAt(x, z, false); @@ -927,8 +930,28 @@ public class WorldServer extends World implements GeneratorAccessSeed { // CraftBukkit start private boolean addEntity0(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) { org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot - if (entity.valid) { MinecraftServer.LOGGER.error("Attempted Double World add on " + entity, new Throwable()); return true; } // Paper + // Paper start + if (entity.valid) { + MinecraftServer.LOGGER.error("Attempted Double World add on " + entity, new Throwable()); + + if (DEBUG_ENTITIES) { + Throwable thr = entity.addedToWorldStack; + if (thr == null) { + MinecraftServer.LOGGER.error("Double add entity has no add stacktrace"); + } else { + MinecraftServer.LOGGER.error("Double add stacktrace: ", thr); + } + } + return true; + } + // Paper end if (entity.dead) { + // Paper start + if (DEBUG_ENTITIES) { + new Throwable("Tried to add entity " + entity + " but it was marked as removed already").printStackTrace(); // CraftBukkit + getAddToWorldStackTrace(entity).printStackTrace(); + } + // Paper end // WorldServer.LOGGER.warn("Tried to add entity {} but it was marked as removed already", EntityTypes.getName(entity.getEntityType())); // CraftBukkit return false; } else if (this.isUUIDTaken(entity)) { @@ -1126,7 +1149,24 @@ public class WorldServer extends World implements GeneratorAccessSeed { } } - this.entitiesByUUID.put(entity.getUniqueID(), entity); + if (DEBUG_ENTITIES) { + entity.addedToWorldStack = getAddToWorldStackTrace(entity); + } + + Entity old = this.entitiesByUUID.put(entity.getUniqueID(), entity); + if (old != null && old.getId() != entity.getId() && old.valid) { + Logger logger = LogManager.getLogger(); + logger.error("Overwrote an existing entity " + old + " with " + entity); + if (DEBUG_ENTITIES) { + if (old.addedToWorldStack != null) { + old.addedToWorldStack.printStackTrace(); + } else { + logger.error("Oddly, the old entity was not added to the world in the normal way. Plugins?"); + } + entity.addedToWorldStack.printStackTrace(); + } + } + this.getChunkProvider().addEntity(entity); // CraftBukkit start - SPIGOT-5278 if (entity instanceof EntityDrowned) {