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/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java index 88e7fe1d50e644a107aa76c84b7fd1b9b43cd94b..f0347a71b5937f9b68a4024fd0595a2d3e98eef5 100644 --- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java @@ -1138,6 +1138,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) { @@ -1179,7 +1180,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/level/WorldServer.java b/src/main/java/net/minecraft/server/level/WorldServer.java index 4d3e3a4496ab28e0865f2ad871915fcb2ae57e36..70b837ef82dfe46eb24625b96f9bdbe2e7f2cc2d 100644 --- a/src/main/java/net/minecraft/server/level/WorldServer.java +++ b/src/main/java/net/minecraft/server/level/WorldServer.java @@ -197,6 +197,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); @@ -1036,8 +1039,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)) { @@ -1235,7 +1258,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) { diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index a4915d330d9df8773ce2643a17f353045fff7965..94c77ea291b13206c726725def2ecf4e6441bf3f 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -161,6 +161,8 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne 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/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java index f7df55cf3da909c253bab106cdb7149ee9c297d9..6b81392010c216a1c478cae170388d6836e4206e 100644 --- a/src/main/java/net/minecraft/world/level/World.java +++ b/src/main/java/net/minecraft/world/level/World.java @@ -120,6 +120,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;