aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0243-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
blob: 156e358d93442550310e5c4dbdd2fe44b18402d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
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

1.17: Needs to be reworked for new entity storage system

diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 610a1ce766d826f3fdca595dd4135f96ef9280d8..b64f42b9ca1da5b6d5c290c157e510797e524117 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1366,6 +1366,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
                 } else {
                     ChunkMap.TrackedEntity playerchunkmap_entitytracker = new ChunkMap.TrackedEntity(entity, i, j, entitytypes.trackDeltas());
 
+                    entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker
                     this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
                     playerchunkmap_entitytracker.updatePlayers(this.level.players());
                     if (entity instanceof ServerPlayer) {
@@ -1408,7 +1409,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
         if (playerchunkmap_entitytracker1 != null) {
             playerchunkmap_entitytracker1.broadcastRemoved();
         }
-
+        entity.tracker = null; // Paper - We're no longer tracked
     }
 
     protected void tick() {
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index d4a09cbaac906c766c55577551e539c9840d8d03..abff89e99d9f62a82c65a397016ab956f9ef42aa 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -209,6 +209,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
     public final LevelStorageSource.LevelStorageAccess convertable;
     public final UUID uuid;
     public boolean hasPhysicsEvent = true; // Paper
+    public static Throwable getAddToWorldStackTrace(Entity entity) {
+        return new Throwable(entity + " Added to world at " + new java.util.Date());
+    }
 
     @Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
         return this.chunkSource.getChunk(x, z, false);
@@ -1096,7 +1099,28 @@ public class ServerLevel extends Level implements WorldGenLevel {
     // CraftBukkit start
     private boolean addEntity(Entity entity, CreatureSpawnEvent.SpawnReason spawnReason) {
         org.spigotmc.AsyncCatcher.catchOp("entity add"); // Spigot
+        // 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.isRemoved()) {
+            // 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.getKey(entity.getType())); // CraftBukkit
             return false;
         } else {
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 919b66c88f21606348b20f02cc420d0ff1fa0f8b..ad2341ce26f976b4c45d91e8040bea5aef130f02 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -173,6 +173,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i
     public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
     private CraftEntity bukkitEntity;
 
+    public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
+    public @Nullable Throwable addedToWorldStack; // Paper - entity debug
     public CraftEntity getBukkitEntity() {
         if (this.bukkitEntity == null) {
             this.bukkitEntity = CraftEntity.getEntity(this.level.getCraftServer(), this);
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 67566e5f95f7452d31bea485bdb91a4e1a32e5d5..dc2235824853a0d7ccbff51dd26a71e97fe90ba7 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -142,6 +142,7 @@ public abstract class Level implements LevelAccessor, 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 preventPoiUpdated = false; // CraftBukkit - SPIGOT-5710
     public boolean captureBlockStates = false;
diff --git a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java
index e6b9f812e521abd552e0c7dc0429d5a62f2bfc35..f4731d69946871768ecf16149a47d2ef1cb3e910 100644
--- a/src/main/java/net/minecraft/world/level/entity/EntityLookup.java
+++ b/src/main/java/net/minecraft/world/level/entity/EntityLookup.java
@@ -34,6 +34,26 @@ public class EntityLookup<T extends EntityAccess> {
         UUID uUID = entity.getUUID();
         if (this.byUuid.containsKey(uUID)) {
             LOGGER.warn("Duplicate entity UUID {}: {}", uUID, entity);
+            // Paper start - extra debug info
+            if (entity instanceof net.minecraft.world.entity.Entity entityCast) {
+                if (net.minecraft.server.level.ServerLevel.DEBUG_ENTITIES) {
+                    entityCast.addedToWorldStack = net.minecraft.server.level.ServerLevel.getAddToWorldStackTrace(entityCast);
+                }
+
+                T old = this.byUuid.get(entity.getUUID());
+                if (old instanceof net.minecraft.world.entity.Entity oldCast && old != null && oldCast.getId() != entity.getId() && oldCast.valid) {
+                    LOGGER.error("Overwrote an existing entity " + oldCast + " with " + entity);
+                    if (net.minecraft.server.level.ServerLevel.DEBUG_ENTITIES) {
+                        if (oldCast.addedToWorldStack != null) {
+                            oldCast.addedToWorldStack.printStackTrace();
+                        } else {
+                            LOGGER.error("Oddly, the old entity was not added to the world in the normal way. Plugins?");
+                        }
+                        entityCast.addedToWorldStack.printStackTrace();
+                    }
+                }
+            }
+            // Paper end
         } else {
             this.byUuid.put(uUID, entity);
             this.byId.put(entity.getId(), entity);