From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: tr7zw <tr7zw@live.de>
Date: Fri, 26 Jun 2020 01:11:47 +0200
Subject: [PATCH] Optimize Hopper logic


diff --git a/src/main/java/net/minecraft/server/BlockHopper.java b/src/main/java/net/minecraft/server/BlockHopper.java
index a29294fbc7cd6fcfff0df9eadd11de3bd7f1405e..2f66740de68667e5c0054a0bc7990256163087cd 100644
--- a/src/main/java/net/minecraft/server/BlockHopper.java
+++ b/src/main/java/net/minecraft/server/BlockHopper.java
@@ -110,6 +110,12 @@ public class BlockHopper extends BlockTileEntity {
     @Override
     public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1, boolean flag) {
         this.a(world, blockposition, iblockdata);
+        // Yatopia start
+        TileEntity tileEntity = world.getTileEntity(blockposition);
+        if (tileEntity instanceof TileEntityHopper) {
+            ((TileEntityHopper) tileEntity).flushCaches();
+        }
+        // Yatopia end
     }
 
     private void a(World world, BlockPosition blockposition, IBlockData iblockdata) {
diff --git a/src/main/java/net/minecraft/server/TileEntityHopper.java b/src/main/java/net/minecraft/server/TileEntityHopper.java
index 95bede605c6401af10f18b641cd12c9d8ec2f207..0a975642ce0cce4cbce02e9be976427ed42cce65 100644
--- a/src/main/java/net/minecraft/server/TileEntityHopper.java
+++ b/src/main/java/net/minecraft/server/TileEntityHopper.java
@@ -661,14 +661,93 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
 
     @Nullable
     private IInventory l() {
+        // Yatopia start
+        if (!shouldNotCachePush && cachedPush != null) {
+            return cachedPush;
+        }
+        // Yatopia end
         EnumDirection enumdirection = (EnumDirection) this.getBlock().get(BlockHopper.FACING);
 
-        return b(this.getWorld(), this.position.shift(enumdirection));
+        // Yatopia start - replace logic
+        //return b(this.getWorld(), this.position.shift(enumdirection));
+        // basically reimplement TileEntityHopper#b but in a better way in order to
+        // not make them composters bork
+        if (shouldNotCachePush || !cachedPushAir) {
+            BlockPosition shifted = this.position.shift(enumdirection);
+            BlockPosition checkedPosition = new BlockPosition(shifted.getX() + 0.5D, shifted.getY() + 0.5D, shifted.getZ() + 0.5D);
+            if (!world.isLoaded(checkedPosition)) return null;
+            IBlockData blockData = world.getType(checkedPosition);
+            Block block = blockData.getBlock();
+            if (block instanceof IInventoryHolder) {
+                this.cachedPush = ((IInventoryHolder) block).a(blockData, world, checkedPosition);
+                shouldNotCachePush = true;
+            } else if (block.isTileEntity()) {
+                TileEntity tileEntity = world.getTileEntity(checkedPosition);
+                if (tileEntity instanceof IInventory) {
+                    IInventory inv = (IInventory) tileEntity;
+                    if (inv instanceof TileEntityChest && block instanceof BlockChest) {
+                        inv = BlockChest.getInventory((BlockChest) block, blockData, world, checkedPosition, true);
+                    }
+                    if (inv instanceof EntityMinecartContainer) {
+                        shouldNotCachePush = true;
+                    }
+                    this.cachedPush = inv;
+                }
+            }
+        }
+        if (this.cachedPush == null) {
+            this.cachedPushAir = true;
+        }
+        return this.cachedPush;
+        // Yatopia end
     }
 
     @Nullable
     public static IInventory b(IHopper ihopper) {
+        // Yatopia start - replaced logic
+        if (ihopper instanceof TileEntityHopper) {
+            TileEntityHopper hopper = (TileEntityHopper) ihopper;
+            if (!hopper.shouldNotCachePull && hopper.cachedAbove != null) {
+                return hopper.cachedAbove;
+            }
+            // reimplement here too cuz minecarts
+            BlockPosition pos = new BlockPosition(ihopper.getX(), ihopper.getY() + 1.0D, ihopper.getZ());
+            if (hopper.shouldNotCachePull || hopper.cachedPullAir) {
+                if (hopper.world.isLoaded(pos)) return null;
+                IBlockData blockData = hopper.world.getType(pos);
+                Block block = blockData.getBlock();
+                if (block instanceof IInventoryHolder) {
+                    hopper.cachedAbove = ((IInventoryHolder) block).a(blockData, hopper.world, pos);
+                    hopper.shouldNotCachePull = true;
+                } else if (block.isTileEntity()) {
+                    TileEntity tileEntity = hopper.world.getTileEntity(pos);
+                    if (tileEntity instanceof IInventory) {
+                        IInventory inv = (IInventory) tileEntity;
+                        if (inv instanceof TileEntityChest && block instanceof BlockChest) {
+                            inv = BlockChest.getInventory((BlockChest) block, blockData, hopper.world, pos, true);
+                        }
+                        if (inv instanceof EntityMinecartContainer) {
+                            hopper.shouldNotCachePull = true;
+                        }
+                        hopper.cachedAbove = inv;
+                    }
+                }
+            }
+            if (hopper.cachedAbove == null) {
+                List<Entity> list = hopper.world.getEntities((Entity) null, new AxisAlignedBB(pos.getX() - 0.5D, pos.getY() - 0.5D, pos.getZ() - 0.5D, pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D), IEntitySelector.d);
+
+                if (!list.isEmpty()) {
+                    hopper.cachedAbove = (IInventory) list.get(java.util.concurrent.ThreadLocalRandom.current().nextInt(list.size()));
+                }
+            }
+            if (hopper.cachedAbove == null) {
+                hopper.cachedPullAir = true;
+            }
+            return hopper.cachedAbove;
+        } else {
         return a(ihopper.getWorld(), ihopper.x(), ihopper.z() + 1.0D, ihopper.A());
+        }
+        // Yatopia end
     }
 
     public static List<EntityItem> c(IHopper ihopper) {
@@ -779,4 +858,21 @@ public class TileEntityHopper extends TileEntityLootable implements IHopper, ITi
     protected Container createContainer(int i, PlayerInventory playerinventory) {
         return new ContainerHopper(i, playerinventory, this);
     }
+
+    // Yatopia start
+    private IInventory cachedAbove = null;
+    private IInventory cachedPush = null;
+    private boolean cachedPushAir = false;
+    private boolean cachedPullAir = false;
+    private boolean shouldNotCachePush = false;
+    private boolean shouldNotCachePull = false;
+    public void flushCaches() {
+        cachedAbove = null;
+        cachedPush = null;
+        cachedPushAir = false;
+        cachedPullAir = false;
+        shouldNotCachePush = false;
+        shouldNotCachePull = false;
+    }
+    // Yatopia end
 }
