diff --git a/Spigot-Server-Patches/0403-Fix-zero-tick-instant-grow-farms-MC-113809.patch b/Spigot-Server-Patches/0403-Fix-zero-tick-instant-grow-farms-MC-113809.patch
new file mode 100644
index 0000000000..688c62789d
--- /dev/null
+++ b/Spigot-Server-Patches/0403-Fix-zero-tick-instant-grow-farms-MC-113809.patch
@@ -0,0 +1,99 @@
+From 7e22aa26cdf426fb9dd1094509e21ace67d1cd30 Mon Sep 17 00:00:00 2001
+From: Phoenix616 <mail@moep.tv>
+Date: Sun, 15 Sep 2019 11:32:32 -0500
+Subject: [PATCH] Fix zero-tick instant grow farms MC-113809
+
+
+diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+index 33e251c87..c0af1aaf3 100644
+--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
+@@ -564,6 +564,11 @@ public class PaperWorldConfig {
+         disableRelativeProjectileVelocity = getBoolean("game-mechanics.disable-relative-projectile-velocity", false);
+     }
+ 
++    public boolean fixZeroTickInstantGrowFarms = true;
++    private void fixZeroTickInstantGrowFarms() {
++        fixZeroTickInstantGrowFarms = getBoolean("fix-zero-tick-instant-grow-farms", fixZeroTickInstantGrowFarms);
++    }
++
+     public boolean altItemDespawnRateEnabled;
+     public Map<Material, Integer> altItemDespawnRateMap;
+     private void altItemDespawnRate() {
+diff --git a/src/main/java/net/minecraft/server/Block.java b/src/main/java/net/minecraft/server/Block.java
+index 46490a943..54a61283e 100644
+--- a/src/main/java/net/minecraft/server/Block.java
++++ b/src/main/java/net/minecraft/server/Block.java
+@@ -46,6 +46,7 @@ public class Block implements IMaterial {
+     private final float g;
+     protected final BlockStateList<Block, IBlockData> blockStateList;
+     private IBlockData blockData;
++    public boolean randomTick = false; // Paper - fix MC-113809
+     protected final boolean v;
+     private final boolean i;
+     private final boolean j;
+diff --git a/src/main/java/net/minecraft/server/BlockBamboo.java b/src/main/java/net/minecraft/server/BlockBamboo.java
+index c482aad3e..02c548dd9 100644
+--- a/src/main/java/net/minecraft/server/BlockBamboo.java
++++ b/src/main/java/net/minecraft/server/BlockBamboo.java
+@@ -85,6 +85,7 @@ public class BlockBamboo extends Block implements IBlockFragilePlantElement {
+         if (!iblockdata.canPlace(worldserver, blockposition)) {
+             worldserver.b(blockposition, true);
+         } else if ((Integer) iblockdata.get(BlockBamboo.f) == 0) {
++            if (worldserver.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809
+             if (random.nextInt(Math.max(1, (int) (100.0F / worldserver.spigotConfig.bambooModifier) * 3)) == 0 && worldserver.isEmpty(blockposition.up()) && worldserver.getLightLevel(blockposition.up(), 0) >= 9) { // Spigot
+                 int i = this.b(worldserver, blockposition) + 1;
+ 
+diff --git a/src/main/java/net/minecraft/server/BlockCactus.java b/src/main/java/net/minecraft/server/BlockCactus.java
+index e0974e256..3524fcb92 100644
+--- a/src/main/java/net/minecraft/server/BlockCactus.java
++++ b/src/main/java/net/minecraft/server/BlockCactus.java
+@@ -21,6 +21,7 @@ public class BlockCactus extends Block {
+         if (!iblockdata.canPlace(worldserver, blockposition)) {
+             worldserver.b(blockposition, true);
+         } else {
++            if (worldserver.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809
+             BlockPosition blockposition1 = blockposition.up();
+ 
+             if (worldserver.isEmpty(blockposition1)) {
+diff --git a/src/main/java/net/minecraft/server/BlockChorusFlower.java b/src/main/java/net/minecraft/server/BlockChorusFlower.java
+index d70b52cad..b624cf380 100644
+--- a/src/main/java/net/minecraft/server/BlockChorusFlower.java
++++ b/src/main/java/net/minecraft/server/BlockChorusFlower.java
+@@ -22,6 +22,7 @@ public class BlockChorusFlower extends Block {
+         if (!iblockdata.canPlace(worldserver, blockposition)) {
+             worldserver.b(blockposition, true);
+         } else {
++            if (worldserver.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809
+             BlockPosition blockposition1 = blockposition.up();
+ 
+             if (worldserver.isEmpty(blockposition1) && blockposition1.getY() < 256) {
+diff --git a/src/main/java/net/minecraft/server/BlockReed.java b/src/main/java/net/minecraft/server/BlockReed.java
+index 55b07444e..3bc3c5aa2 100644
+--- a/src/main/java/net/minecraft/server/BlockReed.java
++++ b/src/main/java/net/minecraft/server/BlockReed.java
+@@ -23,6 +23,7 @@ public class BlockReed extends Block {
+         if (!iblockdata.canPlace(worldserver, blockposition)) {
+             worldserver.b(blockposition, true);
+         } else if (worldserver.isEmpty(blockposition.up())) {
++            if (worldserver.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809
+             int i;
+ 
+             for (i = 1; worldserver.getType(blockposition.down(i)).getBlock() == this; ++i) {
+diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
+index 4da34b6dd..722384a91 100644
+--- a/src/main/java/net/minecraft/server/WorldServer.java
++++ b/src/main/java/net/minecraft/server/WorldServer.java
+@@ -678,7 +678,9 @@ public class WorldServer extends World {
+         IBlockData iblockdata = this.getType(nextticklistentry.a);
+ 
+         if (iblockdata.getBlock() == nextticklistentry.b()) {
++            iblockdata.getBlock().randomTick = true; // Paper - fix MC-113809
+             iblockdata.a(this, nextticklistentry.a, this.random);
++            iblockdata.getBlock().randomTick = false; // Paper - fix MC-113809
+         }
+ 
+     }
+-- 
+2.24.1
+
diff --git a/Spigot-Server-Patches/0403-Fix-MC-161754.patch b/Spigot-Server-Patches/0404-Fix-MC-161754.patch
similarity index 94%
rename from Spigot-Server-Patches/0403-Fix-MC-161754.patch
rename to Spigot-Server-Patches/0404-Fix-MC-161754.patch
index b5907a403f..a4a9a57626 100644
--- a/Spigot-Server-Patches/0403-Fix-MC-161754.patch
+++ b/Spigot-Server-Patches/0404-Fix-MC-161754.patch
@@ -1,4 +1,4 @@
-From 65125b3e106030630c0b1703e00c1f1c3936d797 Mon Sep 17 00:00:00 2001
+From 540b6207ac043f0b18905843b06ac9b2f31c6ef7 Mon Sep 17 00:00:00 2001
 From: Spottedleaf <Spottedleaf@users.noreply.github.com>
 Date: Tue, 24 Sep 2019 16:03:00 -0700
 Subject: [PATCH] Fix MC-161754
diff --git a/Spigot-Server-Patches/0404-Performance-improvement-for-Chunk.getEntities.patch b/Spigot-Server-Patches/0405-Performance-improvement-for-Chunk.getEntities.patch
similarity index 96%
rename from Spigot-Server-Patches/0404-Performance-improvement-for-Chunk.getEntities.patch
rename to Spigot-Server-Patches/0405-Performance-improvement-for-Chunk.getEntities.patch
index 67ad5f58dd..671067b614 100644
--- a/Spigot-Server-Patches/0404-Performance-improvement-for-Chunk.getEntities.patch
+++ b/Spigot-Server-Patches/0405-Performance-improvement-for-Chunk.getEntities.patch
@@ -1,4 +1,4 @@
-From 38866c8296404908c68b70f267d8451f786ac6c8 Mon Sep 17 00:00:00 2001
+From 6c486f323f302b16d1edbbd44981886a0b3f8668 Mon Sep 17 00:00:00 2001
 From: wea_ondara <wea_ondara@alpenblock.net>
 Date: Thu, 10 Oct 2019 11:29:42 +0200
 Subject: [PATCH] Performance improvement for Chunk.getEntities
diff --git a/Spigot-Server-Patches/0405-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch b/Spigot-Server-Patches/0406-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch
similarity index 96%
rename from Spigot-Server-Patches/0405-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch
rename to Spigot-Server-Patches/0406-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch
index 38604dbeba..81985697c2 100644
--- a/Spigot-Server-Patches/0405-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch
+++ b/Spigot-Server-Patches/0406-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch
@@ -1,4 +1,4 @@
-From b33a5cb73492f271d67424568394978dc3f40c26 Mon Sep 17 00:00:00 2001
+From 1b6b2b8c524a8742957d2740a6c0415f19ce2dc5 Mon Sep 17 00:00:00 2001
 From: MisterErwin <git@askarian.net>
 Date: Wed, 30 Oct 2019 16:57:54 +0100
 Subject: [PATCH] Fix spawning of hanging entities that are not ItemFrames and
diff --git a/Spigot-Server-Patches/0406-Expose-the-internal-current-tick.patch b/Spigot-Server-Patches/0407-Expose-the-internal-current-tick.patch
similarity index 92%
rename from Spigot-Server-Patches/0406-Expose-the-internal-current-tick.patch
rename to Spigot-Server-Patches/0407-Expose-the-internal-current-tick.patch
index fa93dc1d31..3454ec357c 100644
--- a/Spigot-Server-Patches/0406-Expose-the-internal-current-tick.patch
+++ b/Spigot-Server-Patches/0407-Expose-the-internal-current-tick.patch
@@ -1,4 +1,4 @@
-From a78d4cc3de32d2fe41b7d1cfa8a213a429d4f000 Mon Sep 17 00:00:00 2001
+From 1bf364be98d7ded883550eeca361a4efc79238ed Mon Sep 17 00:00:00 2001
 From: William Blake Galbreath <blake.galbreath@gmail.com>
 Date: Sat, 20 Apr 2019 19:47:34 -0500
 Subject: [PATCH] Expose the internal current tick
diff --git a/Spigot-Server-Patches/0407-Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch b/Spigot-Server-Patches/0408-Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch
similarity index 96%
rename from Spigot-Server-Patches/0407-Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch
rename to Spigot-Server-Patches/0408-Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch
index 1436550572..61a7636d79 100644
--- a/Spigot-Server-Patches/0407-Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch
+++ b/Spigot-Server-Patches/0408-Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch
@@ -1,4 +1,4 @@
-From 84478c066481a43f8bd96e877047ca0d4b52e9be Mon Sep 17 00:00:00 2001
+From ecae0134303ae5583c8b982bcad6b2137f268252 Mon Sep 17 00:00:00 2001
 From: William Blake Galbreath <blake.galbreath@gmail.com>
 Date: Wed, 9 Oct 2019 21:51:43 -0500
 Subject: [PATCH] Fix stuck in sneak when changing worlds (MC-10657)
diff --git a/Spigot-Server-Patches/0408-Add-option-to-disable-pillager-patrols.patch b/Spigot-Server-Patches/0409-Add-option-to-disable-pillager-patrols.patch
similarity index 90%
rename from Spigot-Server-Patches/0408-Add-option-to-disable-pillager-patrols.patch
rename to Spigot-Server-Patches/0409-Add-option-to-disable-pillager-patrols.patch
index 25f9ebc935..f0cd98d62d 100644
--- a/Spigot-Server-Patches/0408-Add-option-to-disable-pillager-patrols.patch
+++ b/Spigot-Server-Patches/0409-Add-option-to-disable-pillager-patrols.patch
@@ -1,14 +1,14 @@
-From fe96ecbaebd3330a84f5840bc0a87093d39a22c0 Mon Sep 17 00:00:00 2001
+From 18bd737e8991b816105348051466a01efdbdbf35 Mon Sep 17 00:00:00 2001
 From: William Blake Galbreath <blake.galbreath@gmail.com>
 Date: Wed, 9 Oct 2019 21:46:15 -0500
 Subject: [PATCH] Add option to disable pillager patrols
 
 
 diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
-index 33e251c87..79716faca 100644
+index c0af1aaf3..dbc645ebb 100644
 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
 +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
-@@ -621,4 +621,9 @@ public class PaperWorldConfig {
+@@ -626,4 +626,9 @@ public class PaperWorldConfig {
      private void generatorSettings() {
          generateFlatBedrock = getBoolean("generator-settings.flat-bedrock", false);
      }
diff --git a/Spigot-Server-Patches/0409-Fix-AssertionError-when-player-hand-set-to-empty-typ.patch b/Spigot-Server-Patches/0410-Fix-AssertionError-when-player-hand-set-to-empty-typ.patch
similarity index 96%
rename from Spigot-Server-Patches/0409-Fix-AssertionError-when-player-hand-set-to-empty-typ.patch
rename to Spigot-Server-Patches/0410-Fix-AssertionError-when-player-hand-set-to-empty-typ.patch
index 9e58986a84..a883d4e1ee 100644
--- a/Spigot-Server-Patches/0409-Fix-AssertionError-when-player-hand-set-to-empty-typ.patch
+++ b/Spigot-Server-Patches/0410-Fix-AssertionError-when-player-hand-set-to-empty-typ.patch
@@ -1,4 +1,4 @@
-From d40b72004ddb31eb29063f990162d63f39617f47 Mon Sep 17 00:00:00 2001
+From cb0fd78af1de7f392b3f0eb9806baca978e20b89 Mon Sep 17 00:00:00 2001
 From: Lukasz Derlatka <toranktto@gmail.com>
 Date: Mon, 11 Nov 2019 16:08:13 +0100
 Subject: [PATCH] Fix AssertionError when player hand set to empty type
diff --git a/removed/1.15/0281-Optimize-Hoppers.patch b/removed/1.15/0281-Optimize-Hoppers.patch
deleted file mode 100644
index a301f0aad4..0000000000
--- a/removed/1.15/0281-Optimize-Hoppers.patch
+++ /dev/null
@@ -1,308 +0,0 @@
-From bd7c380b8e4238b100012405cf46a02a0e05990d Mon Sep 17 00:00:00 2001
-From: Aikar <aikar@aikar.co>
-Date: Wed, 27 Apr 2016 22:09:52 -0400
-Subject: [PATCH] Optimize Hoppers
-
-* Removes unnecessary extra calls to .update() that are very expensive
-* Lots of itemstack cloning removed. Only clone if the item is actually moved
-* Return true when a plugin cancels inventory move item event instead of false, as false causes pulls to cycle through all items.
-  However, pushes do not exhibit the same behavior, so this is not something plugins could of been relying on.
-* Add option (Default on) to cooldown hoppers when they fail to move an item due to full inventory
-* Skip subsequent InventoryMoveItemEvents if a plugin does not use the item after first event fire for an iteration
-
-diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
-index 42d14fac2..80d66c647 100644
---- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
-+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
-@@ -397,4 +397,13 @@ public class PaperWorldConfig {
-         this.armorStandTick = this.getBoolean("armor-stands-tick", this.armorStandTick);
-         log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default");
-     }
-+
-+    public boolean cooldownHopperWhenFull = true;
-+    public boolean disableHopperMoveEvents = false;
-+    private void hopperOptimizations() {
-+        cooldownHopperWhenFull = getBoolean("hopper.cooldown-when-full", cooldownHopperWhenFull);
-+        log("Cooldown Hoppers when Full: " + (cooldownHopperWhenFull ? "enabled" : "disabled"));
-+        disableHopperMoveEvents = getBoolean("hopper.disable-move-event", disableHopperMoveEvents);
-+        log("Hopper Move Item Events: " + (disableHopperMoveEvents ? "disabled" : "enabled"));
-+    }
- }
-diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java
-index 33d9cac4d..627fa465c 100644
---- a/src/main/java/net/minecraft/server/ItemStack.java
-+++ b/src/main/java/net/minecraft/server/ItemStack.java
-@@ -481,11 +481,12 @@ public final class ItemStack {
-         return this.getItem().a(this, entityhuman, entityliving, enumhand);
-     }
- 
--    public ItemStack cloneItemStack() {
-+    public ItemStack cloneItemStack() { return cloneItemStack(false); } // Paper
-+    public ItemStack cloneItemStack(boolean origItem) { // Paper
-         if (this.isEmpty()) {
-             return ItemStack.a;
-         } else {
--            ItemStack itemstack = new ItemStack(this.getItem(), this.count);
-+            ItemStack itemstack = new ItemStack(origItem ? this.item : this.getItem(), this.count); // Paper
- 
-             itemstack.d(this.C());
-             if (this.tag != null) {
-diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
-index dd2d8712e..206a4ad64 100644
---- a/src/main/java/net/minecraft/server/MinecraftServer.java
-+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
-@@ -1168,6 +1168,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
-             WorldServer worldserver = (WorldServer) iterator.next();
- 
-             worldserver.hasPhysicsEvent =  org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper
-+            TileEntityHopper.skipHopperEvents = worldserver.paperConfig.disableHopperMoveEvents || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper
-             if (true || worldserver.worldProvider.getDimensionManager() == DimensionManager.OVERWORLD || this.getAllowNether()) { // CraftBukkit
-                 this.methodProfiler.a(() -> {
-                     return worldserver.getWorldData().getName() + " " + IRegistry.DIMENSION_TYPE.getKey(worldserver.worldProvider.getDimensionManager());
-diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java
-index 958279249..a8e64dfda 100644
---- a/src/main/java/net/minecraft/server/TileEntity.java
-+++ b/src/main/java/net/minecraft/server/TileEntity.java
-@@ -62,6 +62,7 @@ public abstract class TileEntity implements KeyedObject { // Paper
-     public void setCurrentChunk(Chunk chunk) {
-         this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null;
-     }
-+    static boolean IGNORE_TILE_UPDATES = false;
-     // Paper end
- 
-     @Nullable
-@@ -140,6 +141,7 @@ public abstract class TileEntity implements KeyedObject { // Paper
- 
-     public void update() {
-         if (this.world != null) {
-+            if (IGNORE_TILE_UPDATES) return; // Paper
-             this.c = this.world.getType(this.position);
-             this.world.b(this.position, this);
-             if (!this.c.isAir()) {
-diff --git a/src/main/java/net/minecraft/server/TileEntityHopper.java b/src/main/java/net/minecraft/server/TileEntityHopper.java
-index e08faf538..e7cf14d10 100644
---- a/src/main/java/net/minecraft/server/TileEntityHopper.java
-+++ b/src/main/java/net/minecraft/server/TileEntityHopper.java
-@@ -168,6 +168,158 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
-         return false;
-     }
- 
-+    // Paper start - Optimize Hoppers
-+    private static boolean skipPullModeEventFire = false;
-+    private static boolean skipPushModeEventFire = false;
-+    static boolean skipHopperEvents = false;
-+
-+    private boolean hopperPush(IInventory iinventory, EnumDirection enumdirection) {
-+        skipPushModeEventFire = skipHopperEvents;
-+        boolean foundItem = false;
-+        for (int i = 0; i < this.getSize(); ++i) {
-+            if (!this.getItem(i).isEmpty()) {
-+                foundItem = true;
-+                ItemStack origItemStack = this.getItem(i);
-+                ItemStack itemstack = origItemStack;
-+
-+                final int origCount = origItemStack.getCount();
-+                final int moved = Math.min(world.spigotConfig.hopperAmount, origCount);
-+                origItemStack.setCount(moved);
-+
-+                // We only need to fire the event once to give protection plugins a chance to cancel this event
-+                // Because nothing uses getItem, every event call should end up the same result.
-+                if (!skipPushModeEventFire) {
-+                    itemstack = callPushMoveEvent(iinventory, itemstack);
-+                    if (itemstack == null) { // cancelled
-+                        origItemStack.setCount(origCount);
-+                        return false;
-+                    }
-+                }
-+                final ItemStack itemstack2 = addItem(this, iinventory, itemstack, enumdirection);
-+                final int remaining = itemstack2.getCount();
-+                if (remaining != moved) {
-+                    origItemStack = origItemStack.cloneItemStack(true);
-+                    if (!origItemStack.isEmpty()) {
-+                        origItemStack.setCount(origCount - moved + remaining);
-+                    }
-+                    this.setItem(i, origItemStack);
-+                    iinventory.update();
-+                    return true;
-+                }
-+                origItemStack.setCount(origCount);
-+            }
-+        }
-+        if (foundItem && world.paperConfig.cooldownHopperWhenFull) { // Inventory was full - cooldown
-+            this.setCooldown(world.spigotConfig.hopperTransfer);
-+        }
-+        return false;
-+    }
-+
-+    private static boolean hopperPull(IHopper ihopper, IInventory iinventory, int i) {
-+        ItemStack origItemStack = iinventory.getItem(i);
-+        ItemStack itemstack = origItemStack;
-+        final int origCount = origItemStack.getCount();
-+        final World world = ihopper.getWorld();
-+        final int moved = Math.min(world.spigotConfig.hopperAmount, origCount);
-+        itemstack.setCount(moved);
-+
-+        if (!skipPullModeEventFire) {
-+            itemstack = callPullMoveEvent(ihopper, iinventory, itemstack);
-+            if (itemstack == null) { // cancelled
-+                origItemStack.setCount(origCount);
-+                // Drastically improve performance by returning true.
-+                // No plugin could of relied on the behavior of false as the other call
-+                // site for IMIE did not exhibit the same behavior
-+                return true;
-+            }
-+        }
-+
-+        final ItemStack itemstack2 = addItem(iinventory, ihopper, itemstack, null);
-+        final int remaining = itemstack2.getCount();
-+        if (remaining != moved) {
-+            origItemStack = origItemStack.cloneItemStack(true);
-+            if (!origItemStack.isEmpty()) {
-+                origItemStack.setCount(origCount - moved + remaining);
-+            }
-+            IGNORE_TILE_UPDATES = true;
-+            iinventory.setItem(i, origItemStack);
-+            IGNORE_TILE_UPDATES = false;
-+            iinventory.update();
-+            return true;
-+        }
-+        origItemStack.setCount(origCount);
-+
-+        if (world.paperConfig.cooldownHopperWhenFull) {
-+            cooldownHopper(ihopper);
-+        }
-+
-+        return false;
-+    }
-+
-+    private ItemStack callPushMoveEvent(IInventory iinventory, ItemStack itemstack) {
-+        Inventory destinationInventory = getInventory(iinventory);
-+        InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner(false).getInventory(),
-+            CraftItemStack.asCraftMirror(itemstack), destinationInventory, true);
-+        boolean result = event.callEvent();
-+        if (!event.calledGetItem && !event.calledSetItem) {
-+            skipPushModeEventFire = true;
-+        }
-+        if (!result) {
-+            cooldownHopper(this);
-+            return null;
-+        }
-+
-+        if (event.calledSetItem) {
-+            return CraftItemStack.asNMSCopy(event.getItem());
-+        } else {
-+            return itemstack;
-+        }
-+    }
-+
-+    private static ItemStack callPullMoveEvent(IHopper hopper, IInventory iinventory, ItemStack itemstack) {
-+        Inventory sourceInventory = getInventory(iinventory);
-+        Inventory destination = getInventory(hopper);
-+
-+        InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory,
-+            // Mirror is safe as we no plugins ever use this item
-+            CraftItemStack.asCraftMirror(itemstack), destination, false);
-+        boolean result = event.callEvent();
-+        if (!event.calledGetItem && !event.calledSetItem) {
-+            skipPullModeEventFire = true;
-+        }
-+        if (!result) {
-+            cooldownHopper(hopper);
-+            return null;
-+        }
-+
-+        if (event.calledSetItem) {
-+            return CraftItemStack.asNMSCopy(event.getItem());
-+        } else {
-+            return itemstack;
-+        }
-+    }
-+
-+    private static Inventory getInventory(IInventory iinventory) {
-+        Inventory sourceInventory;// Have to special case large chests as they work oddly
-+        if (iinventory instanceof InventoryLargeChest) {
-+            sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
-+        } else if (iinventory instanceof TileEntity) {
-+            sourceInventory = ((TileEntity) iinventory).getOwner(false).getInventory();
-+        } else {
-+            sourceInventory = iinventory.getOwner().getInventory();
-+        }
-+        return sourceInventory;
-+    }
-+
-+    private static void cooldownHopper(IHopper hopper) {
-+        if (hopper instanceof TileEntityHopper) {
-+            ((TileEntityHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer);
-+        } else if (hopper instanceof EntityMinecartHopper) {
-+            ((EntityMinecartHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer / 2);
-+        }
-+    }
-+    // Paper end
-+
-     private boolean j() {
-         IInventory iinventory = this.k();
- 
-@@ -179,6 +331,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
-             if (this.b(iinventory, enumdirection)) {
-                 return false;
-             } else {
-+                return hopperPush(iinventory, enumdirection); /* // Paper - disable rest
-                 for (int i = 0; i < this.getSize(); ++i) {
-                     if (!this.getItem(i).isEmpty()) {
-                         ItemStack itemstack = this.getItem(i).cloneItemStack();
-@@ -216,7 +369,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
-                     }
-                 }
- 
--                return false;
-+                return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations
-             }
-         }
-     }
-@@ -246,6 +399,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
-             EnumDirection enumdirection = EnumDirection.DOWN;
- 
-             return c(iinventory, enumdirection) ? false : a(iinventory, enumdirection).anyMatch((i) -> {
-+                skipPullModeEventFire = skipHopperEvents; // Paper
-                 return a(ihopper, iinventory, i, enumdirection);
-             });
-         } else {
-@@ -269,6 +423,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
-         ItemStack itemstack = iinventory.getItem(i);
- 
-         if (!itemstack.isEmpty() && b(iinventory, itemstack, i, enumdirection)) {
-+            return hopperPull(ihopper, iinventory, i); /* // Paper - disable rest
-             ItemStack itemstack1 = itemstack.cloneItemStack();
-             // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.splitStack(i, 1), (EnumDirection) null);
-             // CraftBukkit start - Call event on collection of items from inventories into the hopper
-@@ -305,7 +460,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
-             }
- 
-             itemstack1.subtract(origCount - itemstack2.getCount()); // Spigot
--            iinventory.setItem(i, itemstack1);
-+            iinventory.setItem(i, itemstack1);*/ // Paper - end commenting out replaced block for Hopper Optimizations
-         }
- 
-         return false;
-@@ -314,7 +469,7 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
-     public static boolean a(IInventory iinventory, EntityItem entityitem) {
-         boolean flag = false;
-         // CraftBukkit start
--        InventoryPickupItemEvent event = new InventoryPickupItemEvent(iinventory.getOwner().getInventory(), (org.bukkit.entity.Item) entityitem.getBukkitEntity());
-+        InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(iinventory), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); // Paper - use getInventory() to avoid snapshot creation
-         entityitem.world.getServer().getPluginManager().callEvent(event);
-         if (event.isCancelled()) {
-             return false;
-@@ -368,7 +523,9 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
-             boolean flag1 = iinventory1.isNotEmpty();
- 
-             if (itemstack1.isEmpty()) {
-+                IGNORE_TILE_UPDATES = true; // Paper
-                 iinventory1.setItem(i, itemstack);
-+                IGNORE_TILE_UPDATES = false; // Paper
-                 itemstack = ItemStack.a;
-                 flag = true;
-             } else if (a(itemstack1, itemstack)) {
--- 
-2.24.1
-