From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 23 Jul 2018 22:44:23 -0400 Subject: [PATCH] Add some Debug to Chunk Entity slices If we detect unexpected state, log and try to recover This should hopefully avoid duplicate entities ever being created if the entity was to end up in 2 different chunk slices diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java index a2cc3e58d59ed3d9f443b77c44d8200cc09b4da9..7847078c54154e28ab066ea8a329f929df1e1a37 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java @@ -156,6 +156,7 @@ public abstract class Entity implements Nameable, CommandSource, net.minecraft.s } } }; + public List entitySlice = null; // Paper end public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java index bd9b19d988ecf72e099efeff6ec3483a352174ec..09aa608bd303b618ae2c0ebd237bcbdba60a37a8 100644 --- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java @@ -26,7 +26,9 @@ import net.minecraft.ReportedException; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; @@ -550,6 +552,25 @@ public class LevelChunk implements ChunkAccess { if (k >= this.entitySlices.length) { k = this.entitySlices.length - 1; } + // Paper - remove from any old list if its in one + List nextSlice = this.entitySlices[k]; // the next list to be added to + List currentSlice = entity.entitySlice; + if (nextSlice == currentSlice) { + if (Level.DEBUG_ENTITIES) MinecraftServer.LOGGER.warn("Entity was already in this chunk!" + entity, new Throwable()); + return; // ??? silly plugins + } + if (currentSlice != null && currentSlice.contains(entity)) { + // Still in an old chunk... + if (Level.DEBUG_ENTITIES) MinecraftServer.LOGGER.warn("Entity is still in another chunk!" + entity, new Throwable()); + LevelChunk chunk = entity.getCurrentChunk(); + if (chunk != null) { + chunk.removeEntity(entity); + } else { + removeEntity(entity); + } + currentSlice.remove(entity); // Just incase the above did not remove from the previous slice + } + // Paper end if (!entity.inChunk || entity.getCurrentChunk() != this) entityCounts.increment(entity.getMinecraftKeyString()); // Paper entity.inChunk = true; @@ -559,6 +580,7 @@ public class LevelChunk implements ChunkAccess { entity.zChunk = this.chunkPos.z; this.entities.add(entity); // Paper - per chunk entity list this.entitySlices[k].add(entity); + entity.entitySlice = this.entitySlices[k]; // Paper this.markUnsaved(); // Paper } @@ -584,6 +606,10 @@ public class LevelChunk implements ChunkAccess { // Paper start if (entity.currentChunk != null && entity.currentChunk.get() == this) entity.setCurrentChunk(null); + if (entitySlices[section] == entity.entitySlice) { + entity.entitySlice = null; + entity.inChunk = false; + } if (!this.entitySlices[section].remove(entity)) { return; } @@ -742,7 +768,7 @@ public class LevelChunk implements ChunkAccess { // Paper start - neighbour cache int chunkX = this.chunkPos.x; int chunkZ = this.chunkPos.z; - ChunkProviderServer chunkProvider = ((ServerLevel)this.world).getChunkSource(); + ServerChunkCache chunkProvider = ((ServerLevel)this.world).getChunkSource(); for (int dx = -NEIGHBOUR_CACHE_RADIUS; dx <= NEIGHBOUR_CACHE_RADIUS; ++dx) { for (int dz = -NEIGHBOUR_CACHE_RADIUS; dz <= NEIGHBOUR_CACHE_RADIUS; ++dz) { LevelChunk neighbour = chunkProvider.getChunkAtIfLoadedMainThreadNoCache(chunkX + dx, chunkZ + dz); @@ -802,7 +828,7 @@ public class LevelChunk implements ChunkAccess { // Paper start - neighbour cache int chunkX = this.chunkPos.x; int chunkZ = this.chunkPos.z; - ChunkProviderServer chunkProvider = ((ServerLevel)this.world).getChunkSource(); + ServerChunkCache chunkProvider = ((ServerLevel)this.world).getChunkSource(); for (int dx = -NEIGHBOUR_CACHE_RADIUS; dx <= NEIGHBOUR_CACHE_RADIUS; ++dx) { for (int dz = -NEIGHBOUR_CACHE_RADIUS; dz <= NEIGHBOUR_CACHE_RADIUS; ++dz) { LevelChunk neighbour = chunkProvider.getChunkAtIfLoadedMainThreadNoCache(chunkX + dx, chunkZ + dz);