From b8cda19a77b6e3a0d085924afc6f4568e49c0623 Mon Sep 17 00:00:00 2001
From: Zach Brown <1254957+zachbr@users.noreply.github.com>
Date: Wed, 9 Aug 2017 18:11:57 -0500
Subject: [PATCH] Fix MC-117075: TE Unload Lag Spike

PaperMC port of https://github.com/MinecraftForge/MinecraftForge/pull/4281
---
 .../Fix-MC-117075-TE-Unload-Lag-Spike.patch   | 99 +++++++++++++++++++
 scripts/importmcdev.sh                        |  1 +
 2 files changed, 100 insertions(+)
 create mode 100644 Spigot-Server-Patches/Fix-MC-117075-TE-Unload-Lag-Spike.patch

diff --git a/Spigot-Server-Patches/Fix-MC-117075-TE-Unload-Lag-Spike.patch b/Spigot-Server-Patches/Fix-MC-117075-TE-Unload-Lag-Spike.patch
new file mode 100644
index 0000000000..21f00acc96
--- /dev/null
+++ b/Spigot-Server-Patches/Fix-MC-117075-TE-Unload-Lag-Spike.patch
@@ -0,0 +1,99 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: mezz <tehgeek@gmail.com>
+Date: Wed, 9 Aug 2017 17:51:22 -0500
+Subject: [PATCH] Fix MC-117075: TE Unload Lag Spike
+
+
+diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
+index ed595955..228792fa 100644
+--- a/src/main/java/net/minecraft/server/Chunk.java
++++ b/src/main/java/net/minecraft/server/Chunk.java
+@@ -0,0 +0,0 @@ public class Chunk {
+ 
+     public void removeEntities() {
+         this.j = false;
++        // Paper start - Fix MC-117075: TE Unload Lag Spike
++        this.world.markTileEntitiesForRemoval(this);
++        /*
+         Iterator iterator = this.tileEntities.values().iterator();
+ 
+         while (iterator.hasNext()) {
+@@ -0,0 +0,0 @@ public class Chunk {
+ 
+             this.world.b(tileentity);
+         }
++        */
++        // Paper end
+ 
+         List[] aentityslice = this.entitySlices; // Spigot
+         int i = aentityslice.length;
+diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
+index 23944088..e8d1a1c6 100644
+--- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
++++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
+@@ -0,0 +0,0 @@ public class ChunkCoordIntPair {
+         this.z = blockposition.getZ() >> 4;
+     }
+ 
++    public static long asLong(int x, int z) { return a(x, z); } // Paper - OBFHELPER
+     public static long a(int i, int j) {
+         return (long) i & 4294967295L | ((long) j & 4294967295L) << 32;
+     }
+diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
+index 12938b9f..7e0ba4b2 100644
+--- a/src/main/java/net/minecraft/server/World.java
++++ b/src/main/java/net/minecraft/server/World.java
+@@ -0,0 +0,0 @@ public abstract class World implements IBlockAccess {
+     private org.spigotmc.TickLimiter tileLimiter;
+     private int tileTickPosition;
+     public final Map<Explosion.CacheKey, Float> explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions
++    private it.unimi.dsi.fastutil.longs.LongCollection tileEntitiesChunkToBeRemoved = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(); // Paper - Fix MC-117075: TE Unload Lag Spike
+ 
+     public CraftWorld getWorld() {
+         return this.world;
+@@ -0,0 +0,0 @@ public abstract class World implements IBlockAccess {
+             this.tileEntityListUnload.clear();
+         }
+         // CraftBukkit end
++        this.removeTileEntitiesForRemovedChunks(); // Paper - Fix MC-117075: TE Unload Lag Spike
+ 
+         // Spigot start
+         // Iterator iterator = this.tileEntityListTick.iterator();
+@@ -0,0 +0,0 @@ public abstract class World implements IBlockAccess {
+     public BlockPosition a(String s, BlockPosition blockposition, boolean flag) {
+         return null;
+     }
++
++    // Paper start - Fix MC-117075: TE Unload Lag Spike
++    public void markTileEntitiesForRemoval(Chunk chunk) {
++        if (!chunk.getTileEntities().isEmpty()) {
++            long pos = net.minecraft.server.ChunkCoordIntPair.asLong(chunk.locX, chunk.locZ);
++            this.tileEntitiesChunkToBeRemoved.add(pos);
++        }
++    }
++
++    private void removeTileEntitiesForRemovedChunks() {
++        if (!this.tileEntitiesChunkToBeRemoved.isEmpty()) {
++            java.util.function.Predicate<TileEntity> isInChunk = (tileEntity) -> {
++                BlockPosition tilePos = tileEntity.getPosition();
++                long tileChunkPos = net.minecraft.server.ChunkCoordIntPair.asLong(tilePos.getX() >> 4, tilePos.getZ() >> 4);
++                final boolean willRemove = this.tileEntitiesChunkToBeRemoved.contains(tileChunkPos);
++                // Brought over from Chunk#removeEntities
++                if (willRemove && tileEntity instanceof IInventory) {
++                    for (org.bukkit.entity.HumanEntity human : Lists.newArrayList(((IInventory) tileEntity).getViewers())) {
++                        if (human instanceof org.bukkit.craftbukkit.entity.CraftHumanEntity) {
++                            ((org.bukkit.craftbukkit.entity.CraftHumanEntity) human).getHandle().closeInventory();
++                        }
++                    }
++                }
++
++                return willRemove;
++            };
++
++            this.tileEntityListTick.removeIf(isInChunk);
++            this.tileEntitiesChunkToBeRemoved.clear();
++        }
++    }
++    // Paper end
+ }
+--
\ No newline at end of file
diff --git a/scripts/importmcdev.sh b/scripts/importmcdev.sh
index 6685298554..a6f85e4930 100755
--- a/scripts/importmcdev.sh
+++ b/scripts/importmcdev.sh
@@ -49,6 +49,7 @@ import BlockPosition
 import BlockSnowBlock
 import BlockStateEnum
 import ChunkCache
+import ChunkCoordIntPair
 import ChunkProviderFlat
 import ChunkProviderGenerate
 import ChunkProviderHell