From f4e9f4737fd331e32fec5debd109bcb636f04ba9 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sat, 25 Nov 2017 17:35:32 +0000
Subject: [PATCH] Do not use a snapshot for hoppers

In 1.12, Spigot improved their blockstate implementation to take a full
copy of the TE, this allows for a much better snapshot in that it will
actually retain all of the TE's state, it is a much more expensive
implementation. This is also implicated with their backwards compat
for inventories meaning that accessing of a snapshots inventory of a
placed block will actually access the inventory of the live TE, making
creation of a snapshot redundant if the only intent is to interact with
the TEs inventory.

Hoppers are a horrible hit, every attempt to transfer an ItemStack will
result in two TileEntity snapshots, with two hoppers and a double chest
ontop, I managed to log 380 cases per second where a snapshot would have been
taken in cases where the snapshot is redundant.
---
 ...55-Do-not-use-a-snapshot-for-hoppers.patch | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Spigot-Server-Patches/0255-Do-not-use-a-snapshot-for-hoppers.patch

diff --git a/Spigot-Server-Patches/0255-Do-not-use-a-snapshot-for-hoppers.patch b/Spigot-Server-Patches/0255-Do-not-use-a-snapshot-for-hoppers.patch
new file mode 100644
index 0000000000..fefd4fbff1
--- /dev/null
+++ b/Spigot-Server-Patches/0255-Do-not-use-a-snapshot-for-hoppers.patch
@@ -0,0 +1,67 @@
+From b0456f32941d38b616e2e3f3f37583cb7a5713ef Mon Sep 17 00:00:00 2001
+From: Shane Freeder <theboyetronic@gmail.com>
+Date: Sat, 25 Nov 2017 17:02:33 +0000
+Subject: [PATCH] Do not use a snapshot for hoppers
+
+In 1.12, Spigot improved their blockstate implementation to take a full
+copy of the TE, this allows for a much better snapshot in that it will
+actually retain all of the TE's state, it is a much more expensive
+implementation. This is also implicated with their backwards compat
+for inventories meaning that accessing of a snapshots inventory of a
+placed block will actually access the inventory of the live TE, making
+creation of a snapshot redundant if the only intent is to interact with
+the TEs inventory.
+
+Hoppers are a horrible hit, every attempt to transfer an ItemStack will
+result in two TileEntity state snapshots, with two hoppers and a double chest
+ontop, I managed to log 380 cases per second where a snapshot would have been
+taken in cases where the snapshot is redundant.
+
+diff --git a/src/main/java/net/minecraft/server/TileEntityHopper.java b/src/main/java/net/minecraft/server/TileEntityHopper.java
+index 8ad081316..ebbe5d326 100644
+--- a/src/main/java/net/minecraft/server/TileEntityHopper.java
++++ b/src/main/java/net/minecraft/server/TileEntityHopper.java
+@@ -219,11 +219,15 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
+                         // Have to special case large chests as they work oddly
+                         if (iinventory instanceof InventoryLargeChest) {
+                             destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
++                        // Paper start - avoid redundant snapshot creation of a TE
++                        } else if (iinventory instanceof TileEntity) {
++                            destinationInventory = ((TileEntity) iinventory).getOwner(false).getInventory();
+                         } else {
+                             destinationInventory = iinventory.getOwner().getInventory();
+                         }
+ 
+-                        InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
++                        InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner(false).getInventory(), oitemstack.clone(), destinationInventory, true);
++                        // Paper end - avoid redundant snapshot creation of a TE
+                         this.getWorld().getServer().getPluginManager().callEvent(event);
+                         if (event.isCancelled()) {
+                             this.setItem(i, itemstack);
+@@ -379,12 +383,22 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
+             // Have to special case large chests as they work oddly
+             if (iinventory instanceof InventoryLargeChest) {
+                 sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
++            // Paper start - avoid redundant snapshot creation of a TE
++            } else if (iinventory instanceof TileEntity){
++                sourceInventory = ((TileEntity) iinventory).getOwner(false).getInventory();
+             } else {
+                 sourceInventory = iinventory.getOwner().getInventory();
+             }
+ 
+-            InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
++            Inventory destination;
++            if (ihopper instanceof TileEntity) {
++                destination = ((TileEntity) ihopper).getOwner(false).getInventory();
++            } else {
++                destination = ihopper.getOwner().getInventory();
++            }
+ 
++            InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), destination, false);
++            // Paper end - avoid redundant snapshot creation of a TE
+             ihopper.getWorld().getServer().getPluginManager().callEvent(event);
+             if (event.isCancelled()) {
+                 iinventory.setItem(i, itemstack1);
+-- 
+2.15.0
+