aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0245-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch
blob: 07ef9835abf1457ae07a4f15a0e88bca0277020a (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 09a2680162ed9f1d82830778fea6b05a34ab382b..8d0f7fb501aa5caa36b7dd273a8a7c7f959759cb 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1614,6 +1614,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) {
@@ -1656,7 +1657,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 e13fa5d2ed02b5fe8f9f9d124d15dd5374a7f472..377ebed70c5008d69701bd919f22ad4506dd129c 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -216,6 +216,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);
@@ -1197,7 +1200,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 64261427c5f4abf1a4144eb88a6409560667f70b..0282b575d4ff68a306053f86b47908dd44dc54ed 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -235,6 +235,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
     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 13fcd9d885043444d3a2c5a3d9bd1c906b4c4934..2225921e988cdcaa4fb906b281c237972c6e7c1b 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -148,6 +148,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);