From 25dd8434749412dffeb0e19d91868e66355c0f78 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 6ada923797..9241874139 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -75,6 +75,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 e6c55d87b4..6379d2d84f 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -1014,6 +1014,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) { @@ -1056,7 +1057,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 e780f7af40..627fec10a8 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 IIBlockAccess, GeneratorAccess, AutoClose 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 d8c49d95e1..97049aba9d 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -72,6 +72,9 @@ public class WorldServer extends World { // CraftBukkit start private int tickPosition; boolean hasPhysicsEvent = true; // Paper + private static Throwable getAddToWorldStackTrace(Entity entity) { + return new Throwable(entity + " Added to world at " + new java.util.Date()); + } // Add env and gen to constructor public WorldServer(MinecraftServer minecraftserver, Executor executor, WorldNBTStorage worldnbtstorage, WorldData worlddata, DimensionManager dimensionmanager, GameProfilerFiller gameprofilerfiller, WorldLoadListener worldloadlistener, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { @@ -940,8 +943,28 @@ public class WorldServer extends World { // 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)) { @@ -1107,7 +1130,24 @@ public class WorldServer extends World { } } - 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); if (entity instanceof EntityInsentient) { this.H.add(((EntityInsentient) entity).getNavigation()); -- 2.22.0