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 dd9588cfdb9ea6f50c08af829a533319680c7e59..d0e077084b84e44336878410aa9a44fed7408670 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 8862bbd73b627e37709d46e6aeeee70c89cbd821..4bbcd00950405a4bf3ce391b557049a3b1d4aee8 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -1093,6 +1093,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) { @@ -1134,7 +1135,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 dc00ad1607dc6b05f8cb13470046d72f17aa115b..6ebf3884a289d0be081d43d012ac10796740cc1e 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 81cdfe2351e62461171e5658290d80bb9b735d7a..04e57c81d0e58a801ad8e842c48fc6a143de70f2 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -84,6 +84,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); @@ -922,8 +925,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)) { @@ -1120,7 +1143,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) {