papermc/Spigot-Server-Patches/0007-Store-reference-to-current-Chunk-for-Entity-and-Bloc.patch
Aikar 9dc4d6448b
Numerous fixes to entity related changes
While it wasn't really "broken" before, if plugins use NMS
(which they really should't be) and mess with entity management
themselves, and get it wrong, they could ultimately corrupt our
state expectations.

I've been unable to reproduce any issues locally, but these changes
are the result of me analyzing the code pretty deeply and seeing
about how to make it more durable to abnormal usage.

Any servers seeing oddities, please run with -Ddebug.entities=true
and send me any logs triggered.
2019-04-05 23:08:45 -04:00

194 lines
8 KiB
Diff

From b9facb273ca6721a832a5e9c4f7cf69ab6e5bbf6 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 4 Jul 2018 02:10:36 -0400
Subject: [PATCH] Store reference to current Chunk for Entity and Block
Entities
This enables us a fast reference to the entities current chunk instead
of having to look it up by hashmap lookups.
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
index 417c015e56..95a51ab745 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -36,7 +36,7 @@ public class Chunk implements IChunkAccess {
private final BiomeBase[] f;
private final boolean[] g;
private final Map<BlockPosition, NBTTagCompound> h;
- private boolean i;
+ private boolean i;public boolean isLoaded() { return i; } // Paper - OBFHELPER
public final World world;
public final Map<HeightMap.Type, HeightMap> heightMap;
public final int locX;
@@ -66,7 +66,30 @@ public class Chunk implements IChunkAccess {
// CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking
private int neighbors = 0x1 << 12;
public long chunkKey;
+ // Paper start
+ private class TileEntityHashMap extends java.util.HashMap<BlockPosition, TileEntity> {
+ @Override
+ public TileEntity put(BlockPosition key, TileEntity value) {
+ TileEntity replaced = super.put(key, value);
+ if (replaced != null) {
+ replaced.setCurrentChunk(null);
+ }
+ if (value != null) {
+ value.setCurrentChunk(Chunk.this);
+ }
+ return replaced;
+ }
+ @Override
+ public TileEntity remove(Object key) {
+ TileEntity removed = super.remove(key);
+ if (removed != null) {
+ removed.setCurrentChunk(null);
+ }
+ return removed;
+ }
+ }
+ // Paper end
public boolean areNeighborsLoaded(final int radius) {
switch (radius) {
case 2:
@@ -97,7 +120,7 @@ public class Chunk implements IChunkAccess {
this.g = new boolean[256];
this.h = Maps.newHashMap();
this.heightMap = Maps.newEnumMap(HeightMap.Type.class);
- this.tileEntities = Maps.newHashMap();
+ this.tileEntities = new TileEntityHashMap(); // Paper
this.p = Maps.newHashMap();
this.q = Maps.newHashMap();
this.r = new ShortList[16];
@@ -653,6 +676,7 @@ public class Chunk implements IChunkAccess {
}
entity.inChunk = true;
+ entity.setCurrentChunk(this); // Paper
entity.chunkX = this.locX;
entity.chunkY = k;
entity.chunkZ = this.locZ;
@@ -663,6 +687,7 @@ public class Chunk implements IChunkAccess {
((HeightMap) this.heightMap.get(heightmap_type)).a(along);
}
+ public void removeEntity(Entity entity) { this.b(entity); } // Paper - OBFHELPER
public void b(Entity entity) {
this.a(entity, entity.chunkY);
}
@@ -675,8 +700,12 @@ public class Chunk implements IChunkAccess {
if (i >= this.entitySlices.length) {
i = this.entitySlices.length - 1;
}
-
- this.entitySlices[i].remove(entity);
+ // Paper start
+ if (entity.currentChunk != null && entity.currentChunk.get() == this) entity.setCurrentChunk(null);
+ if (!this.entitySlices[i].remove(entity)) {
+ return;
+ }
+ // Paper end
}
public boolean c(BlockPosition blockposition) {
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index d9c9748cec..bdb4ca7643 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -132,7 +132,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
private static final DataWatcherObject<Boolean> aF = DataWatcher.a(Entity.class, DataWatcherRegistry.i);
private static final DataWatcherObject<Boolean> aG = DataWatcher.a(Entity.class, DataWatcherRegistry.i);
private static final DataWatcherObject<Boolean> aH = DataWatcher.a(Entity.class, DataWatcherRegistry.i);
- public boolean inChunk;
+ public boolean inChunk; public boolean isAddedToChunk() { return inChunk; } // Paper - OBFHELPER
public int chunkX; public int getChunkX() { return chunkX; } // Paper - OBFHELPER
public int chunkY; public int getChunkY() { return chunkY; } // Paper - OBFHELPER
public int chunkZ; public int getChunkZ() { return chunkZ; } // Paper - OBFHELPER
@@ -1776,6 +1776,39 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
}
// Paper start
+ java.lang.ref.WeakReference<Chunk> currentChunk = null;
+
+ public void setCurrentChunk(Chunk chunk) {
+ this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null;
+ }
+ /**
+ * Returns the entities current registered chunk. If the entity is not added to a chunk yet, it will return null
+ */
+ public Chunk getCurrentChunk() {
+ final Chunk chunk = currentChunk != null ? currentChunk.get() : null;
+ return chunk != null && chunk.isLoaded() ? chunk : (isAddedToChunk() ? world.getChunkIfLoaded(getChunkX(), getChunkZ()) : null);
+ }
+ /**
+ * Returns the chunk at the location, using the entities local cache if avail
+ * Will only return null if the location specified is not loaded
+ */
+ public Chunk getCurrentChunkAt(int x, int z) {
+ if (getChunkX() == x && getChunkZ() == z) {
+ Chunk chunk = getCurrentChunk();
+ if (chunk != null) {
+ return chunk;
+ }
+ }
+ return world.getChunkIfLoaded(x, z);
+ }
+ /**
+ * Returns the chunk at the entities current location, using the entities local cache if avail
+ * Will only return null if the location specified is not loaded
+ */
+ public Chunk getChunkAtLocation() {
+ return getCurrentChunkAt((int)Math.floor(locX) >> 4, (int)Math.floor(locZ) >> 4);
+ }
+
private MinecraftKey entityKey;
private String entityKeyString;
diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java
index 7390061bf0..c69209497b 100644
--- a/src/main/java/net/minecraft/server/TileEntity.java
+++ b/src/main/java/net/minecraft/server/TileEntity.java
@@ -41,6 +41,15 @@ public abstract class TileEntity implements KeyedObject { // Paper
getMinecraftKey(); // Try to load if it doesn't exists.
return tileEntityKeyString;
}
+
+ private java.lang.ref.WeakReference<Chunk> currentChunk = null;
+ public Chunk getCurrentChunk() {
+ final Chunk chunk = currentChunk != null ? currentChunk.get() : null;
+ return chunk != null && chunk.isLoaded() ? chunk : null;
+ }
+ public void setCurrentChunk(Chunk chunk) {
+ this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null;
+ }
// Paper end
@Nullable
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 9cdd0331f4..7edbbb106b 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -9,6 +9,7 @@ import java.util.UUID;
import net.minecraft.server.*;
+import org.bukkit.Chunk;
import org.bukkit.EntityEffect;
import org.bukkit.Location;
import org.bukkit.Server;
@@ -44,6 +45,12 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
this.entity = entity;
}
+ @Override
+ public Chunk getChunk() {
+ net.minecraft.server.Chunk currentChunk = entity.getCurrentChunk();
+ return currentChunk != null ? currentChunk.bukkitChunk : getLocation().getChunk();
+ }
+
public static CraftEntity getEntity(CraftServer server, Entity entity) {
/**
* Order is *EXTREMELY* important -- keep it right! =D
--
2.21.0