From 2fae759524a8a56cb30d347edcaa0e1c77d52aac 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..b728a44f5f 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];
@@ -657,6 +680,9 @@ public class Chunk implements IChunkAccess {
         entity.chunkY = k;
         entity.chunkZ = this.locZ;
         this.entitySlices[k].add(entity);
+        // Paper start
+        entity.setCurrentChunk(this);
+        // Paper end
     }
 
     public void a(HeightMap.Type heightmap_type, long[] along) {
@@ -675,8 +701,12 @@ public class Chunk implements IChunkAccess {
         if (i >= this.entitySlices.length) {
             i = this.entitySlices.length - 1;
         }
-
-        this.entitySlices[i].remove(entity);
+        // Paper start
+        if (!this.entitySlices[i].remove(entity)) {
+            return;
+        }
+        entity.setCurrentChunk(null);
+        // Paper end
     }
 
     public boolean c(BlockPosition blockposition) {
@@ -885,6 +915,7 @@ public class Chunk implements IChunkAccess {
                     }
                 }
                 // Spigot End
+                entity.setCurrentChunk(null); // Paper
 
                 // Do not pass along players, as doing so can get them stuck outside of time.
                 // (which for example disables inventory icon updates and prevents block breaking)
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index c87d11da5b..faf4d0c0db 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
+    private 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 a3435c11ed..8fb0ecf525 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;
@@ -43,6 +44,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.20.1