diff --git a/sources/src/main/java/net/minecraft/server/Chunk.java b/sources/src/main/java/net/minecraft/server/Chunk.java index e8e23a867..778d898a6 100644 --- a/sources/src/main/java/net/minecraft/server/Chunk.java +++ b/sources/src/main/java/net/minecraft/server/Chunk.java @@ -1548,4 +1548,14 @@ public class Chunk { private EnumTileEntityState() {} } + // FlamePaper start - Hopper item lookup optimization + public int getItemCount(BlockPosition blockPosition) { + int k = MathHelper.floor(blockPosition.getY() / 16.0D); + + k = Math.max(0, k); + k = Math.min(this.entitySlices.length - 1, k); + + return itemCounts[k]; + } + // FlamePaper end - Hopper item lookup optimization } diff --git a/sources/src/main/java/net/minecraft/server/TileEntityHopper.java b/sources/src/main/java/net/minecraft/server/TileEntityHopper.java new file mode 100644 index 000000000..d9cbba32b --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/TileEntityHopper.java @@ -0,0 +1,784 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.event.inventory.InventoryPickupItemEvent; +import org.bukkit.inventory.Inventory; +// CraftBukkit end + +public class TileEntityHopper extends TileEntityLootable implements IHopper, ITickable { + + private NonNullList items; + private int f; + private long g; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityHopper() { + this.items = NonNullList.a(5, ItemStack.a); + this.f = -1; + } + + public static void a(DataConverterManager dataconvertermanager) { + dataconvertermanager.a(DataConverterTypes.BLOCK_ENTITY, (DataInspector) (new DataInspectorItemList(TileEntityHopper.class, new String[] { "Items"}))); + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.items = NonNullList.a(this.getSize(), ItemStack.a); + if (!this.c(nbttagcompound)) { + ContainerUtil.b(nbttagcompound, this.items); + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.o = nbttagcompound.getString("CustomName"); + } + + this.f = nbttagcompound.getInt("TransferCooldown"); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (!this.d(nbttagcompound)) { + ContainerUtil.a(nbttagcompound, this.items); + } + + nbttagcompound.setInt("TransferCooldown", this.f); + if (this.hasCustomName()) { + nbttagcompound.setString("CustomName", this.o); + } + + return nbttagcompound; + } + + public int getSize() { + return this.items.size(); + } + + public ItemStack splitStack(int i, int j) { + this.d((EntityHuman) null); + ItemStack itemstack = ContainerUtil.a(this.q(), i, j); + + return itemstack; + } + + public void setItem(int i, ItemStack itemstack) { + this.d((EntityHuman) null); + this.q().set(i, itemstack); + if (itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + } + + public String getName() { + return this.hasCustomName() ? this.o : "container.hopper"; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void e() { + if (this.world != null && !this.world.isClientSide) { + --this.f; + this.g = this.world.getTime(); + if (!this.J()) { + this.setCooldown(0); + // Spigot start + if (!this.o() && this.world.spigotConfig.hopperCheck > 1) { + this.setCooldown(this.world.spigotConfig.hopperCheck); + } + // Spigot end + } + + } + } + + private boolean o() { + mayAcceptItems = false; // Paper - at the beginning of a tick, assume we can't accept items + if (this.world != null && !this.world.isClientSide) { + if (!this.J() && BlockHopper.f(this.v())) { + boolean flag = false; + + if (!this.p()) { + flag = this.s(); + } + + if (!this.r()) { + mayAcceptItems = true; // Paper - flag this hopper to be able to accept items + flag = a((IHopper) this) || flag; + } + + if (flag) { + this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot + this.update(); + return true; + } + } + return false; + } else { + return false; + } + } + + // Paper start + private boolean mayAcceptItems = false; + + public boolean canAcceptItems() { + return mayAcceptItems; + } + // Paper end + + private boolean p() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public boolean x_() { + return this.p(); + } + + private boolean r() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (!itemstack.isEmpty() && itemstack.getCount() == itemstack.getMaxStackSize()); + + 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(); + 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(); + 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 s() { + IInventory iinventory = this.I(); + + if (iinventory == null) { + return false; + } else { + EnumDirection enumdirection = BlockHopper.b(this.v()).opposite(); + + if (this.a(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(); + // ItemStack itemstack1 = addItem(this, iinventory, this.splitStack(i, 1), enumdirection); + + // CraftBukkit start - Call event when pushing items into other inventories + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.splitStack(i, world.spigotConfig.hopperAmount)); // Spigot + + Inventory destinationInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + destinationInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); + this.getWorld().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.setItem(i, itemstack); + this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemstack1 = addItem(this, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection); + + if (itemstack1.isEmpty()) { + if (event.getItem().equals(oitemstack)) { + iinventory.update(); + } else { + this.setItem(i, itemstack); + } + // CraftBukkit end + return true; + } + + itemstack.subtract(origCount - itemstack1.getCount()); // Spigot + this.setItem(i, itemstack); + } + } + + return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations + } + } + } + + private boolean a(IInventory iinventory, EnumDirection enumdirection) { + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + ItemStack itemstack = iworldinventory.getItem(k); + + if (itemstack.isEmpty() || itemstack.getCount() != itemstack.getMaxStackSize()) { + return false; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + ItemStack itemstack1 = iinventory.getItem(i1); + + if (itemstack1.isEmpty() || itemstack1.getCount() != itemstack1.getMaxStackSize()) { + return false; + } + } + } + + return true; + } + + private static boolean b(IInventory iinventory, EnumDirection enumdirection) { + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + + if (!iworldinventory.getItem(k).isEmpty()) { + return false; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + if (!iinventory.getItem(i1).isEmpty()) { + return false; + } + } + } + + return true; + } + + // Paper start - split methods, and only do entity lookup if in pull mode + public static boolean a(IHopper ihopper) { + IInventory iinventory = getInventory(ihopper, !(ihopper instanceof TileEntityHopper) || !ihopper.getWorld().paperConfig.isHopperPushBased); + + return acceptItem(ihopper, iinventory); + } + + public static boolean acceptItem(IHopper ihopper, IInventory iinventory) { + // Paper end + + if (iinventory != null) { + EnumDirection enumdirection = EnumDirection.DOWN; + + if (b(iinventory, enumdirection)) { + return false; + } + skipPullModeEventFire = skipHopperEvents; // Paper + + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + + if (a(ihopper, iinventory, k, enumdirection)) { + return true; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + if (a(ihopper, iinventory, i1, enumdirection)) { + return true; + } + } + } + } else if (!ihopper.getWorld().paperConfig.isHopperPushBased || !(ihopper instanceof TileEntityHopper)) { // Paper - only search for entities in 'pull mode' + Iterator iterator = a(ihopper.getWorld(), ihopper.E(), ihopper.F(), ihopper.G()).iterator(); // Change getHopperLookupBoundingBox() if this ever changes + + while (iterator.hasNext()) { + EntityItem entityitem = (EntityItem) iterator.next(); + + if (a((IInventory) null, ihopper, entityitem)) { + return true; + } + } + } + + return false; + } + + private static boolean a(IHopper ihopper, IInventory iinventory, int i, EnumDirection enumdirection) { + 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 + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.splitStack(i, ihopper.getWorld().spigotConfig.hopperAmount)); // Spigot + + 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 { + sourceInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false); + + ihopper.getWorld().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + iinventory.setItem(i, itemstack1); + + if (ihopper instanceof TileEntityHopper) { + ((TileEntityHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer); // Spigot + } else if (ihopper instanceof EntityMinecartHopper) { + ((EntityMinecartHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer / 2); // Spigot + } + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemstack2 = addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null); + + if (itemstack2.isEmpty()) { + if (event.getItem().equals(oitemstack)) { + iinventory.update(); + } else { + iinventory.setItem(i, itemstack1); + } + // CraftBukkit end + return true; + } + + itemstack1.subtract(origCount - itemstack2.getCount()); // Spigot + iinventory.setItem(i, itemstack1);*/ // Paper - end commenting out replaced block for Hopper Optimizations + } + + return false; + } + + public static boolean putDropInInventory(IInventory iinventory, IInventory iinventory1, EntityItem entityitem) { return a(iinventory, iinventory1, entityitem); } // Paper - OBFHELPER + public static boolean a(IInventory iinventory, IInventory iinventory1, EntityItem entityitem) { + boolean flag = false; + + if (entityitem == null) { + return false; + } else { + // CraftBukkit start + InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(iinventory1), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); // Paper - avoid snapshot creation + entityitem.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + ItemStack itemstack = entityitem.getItemStack().cloneItemStack(); + ItemStack itemstack1 = addItem(iinventory, iinventory1, itemstack, (EnumDirection) null); + + if (itemstack1.isEmpty()) { + flag = true; + entityitem.die(); + } else { + entityitem.setItemStack(itemstack1); + } + + return flag; + } + } + + public static ItemStack addItem(IInventory iinventory, IInventory iinventory1, ItemStack itemstack, @Nullable EnumDirection enumdirection) { + if (iinventory1 instanceof IWorldInventory && enumdirection != null) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory1; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + + for (int i = 0; i < aint.length && !itemstack.isEmpty(); ++i) { + itemstack = a(iinventory, iinventory1, itemstack, aint[i], enumdirection); + } + } else { + int j = iinventory1.getSize(); + + for (int k = 0; k < j && !itemstack.isEmpty(); ++k) { + itemstack = a(iinventory, iinventory1, itemstack, k, enumdirection); + } + } + + return itemstack; + } + + private static boolean a(IInventory iinventory, ItemStack itemstack, int i, EnumDirection enumdirection) { + return !iinventory.b(i, itemstack) ? false : !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canPlaceItemThroughFace(i, itemstack, enumdirection); + } + + private static boolean b(IInventory iinventory, ItemStack itemstack, int i, EnumDirection enumdirection) { + return !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canTakeItemThroughFace(i, itemstack, enumdirection); + } + + private static ItemStack a(IInventory iinventory, IInventory iinventory1, ItemStack itemstack, int i, EnumDirection enumdirection) { + ItemStack itemstack1 = iinventory1.getItem(i); + + if (a(iinventory1, itemstack, i, enumdirection)) { + boolean flag = false; + boolean flag1 = iinventory1.x_(); + + 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)) { + int j = itemstack.getMaxStackSize() - itemstack1.getCount(); + int k = Math.min(itemstack.getCount(), j); + + itemstack.subtract(k); + itemstack1.add(k); + flag = k > 0; + } + + if (flag) { + if (flag1 && iinventory1 instanceof TileEntityHopper) { + TileEntityHopper tileentityhopper = (TileEntityHopper) iinventory1; + + if (!tileentityhopper.K()) { + byte b0 = 0; + + if (iinventory != null && iinventory instanceof TileEntityHopper) { + TileEntityHopper tileentityhopper1 = (TileEntityHopper) iinventory; + + if (tileentityhopper.g >= tileentityhopper1.g) { + b0 = 1; + } + } + + tileentityhopper.setCooldown(tileentityhopper.world.spigotConfig.hopperTransfer - b0); // Spigot + } + } + + iinventory1.update(); + } + } + + return itemstack; + } + + private IInventory I() { + EnumDirection enumdirection = BlockHopper.b(this.v()); + + // Paper start - don't search for entities in push mode + World world = getWorld(); + return getInventory(world, this.E() + (double) enumdirection.getAdjacentX(), this.F() + (double) enumdirection.getAdjacentY(), this.G() + (double) enumdirection.getAdjacentZ(), !world.paperConfig.isHopperPushBased); + // Paper end + } + + // Paper start - add option to search for entities + public static IInventory b(IHopper hopper) { + return getInventory(hopper, true); + } + + public static IInventory getInventory(IHopper ihopper, boolean searchForEntities) { + return getInventory(ihopper.getWorld(), ihopper.E(), ihopper.F() + 1.0D, ihopper.G(), searchForEntities); + // Paper end + } + + public static List a(World world, double d0, double d1, double d2) { + return world.a(EntityItem.class, new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D), IEntitySelector.a); // Change getHopperLookupBoundingBox(double, double, double) if the bounding box calculation is ever changed + } + + // Paper start + public AxisAlignedBB getHopperLookupBoundingBox() { + return getHopperLookupBoundingBox(this.getX(), this.getY(), this.getZ()); + } + + private static AxisAlignedBB getHopperLookupBoundingBox(double d0, double d1, double d2) { + // Change this if a(World, double, double, double) above ever changes + return new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D); + } + // Paper end + + // Paper start - add option to searchForEntities + public static IInventory b(World world, double d0, double d1, double d2) { + return getInventory(world, d0, d1, d2, true); + } + + public static IInventory getInventory(World world, double d0, double d1, double d2, boolean searchForEntities) { + // Paper end + Object object = null; + int i = MathHelper.floor(d0); + int j = MathHelper.floor(d1); + int k = MathHelper.floor(d2); + BlockPosition blockposition = new BlockPosition(i, j, k); + if ( !world.isLoaded( blockposition ) ) return null; // Spigot + Block block = world.getType(blockposition).getBlock(); + + if (block.isTileEntity()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof IInventory) { + object = (IInventory) tileentity; + if (object instanceof TileEntityChest && block instanceof BlockChest) { + object = ((BlockChest) block).a(world, blockposition, true); + } + } + } + + net.minecraft.server.Chunk chunk = world.getChunkAtWorldCoords(blockposition); + + if (object == null && searchForEntities && !org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(block).isOccluding() && chunk.getItemCount(blockposition) > 0) { // Paper - only if searchForEntities + List list = world.getEntities((Entity) null, new AxisAlignedBB(d0 - 0.5D, d1 - 0.5D, d2 - 0.5D, d0 + 0.5D, d1 + 0.5D, d2 + 0.5D), IEntitySelector.c); + + if (!list.isEmpty()) { + object = (IInventory) list.get(world.random.nextInt(list.size())); + } + } + + return (IInventory) object; + } + + private static boolean a(ItemStack itemstack, ItemStack itemstack1) { + return itemstack.getItem() != itemstack1.getItem() ? false : (itemstack.getData() != itemstack1.getData() ? false : (itemstack.getCount() > itemstack.getMaxStackSize() ? false : ItemStack.equals(itemstack, itemstack1))); + } + + public double E() { + return (double) this.position.getX() + 0.5D; + } + + public double F() { + return (double) this.position.getY() + 0.5D; + } + + public double G() { + return (double) this.position.getZ() + 0.5D; + } + + private void setCooldown(int i) { + this.f = i; + } + + private boolean J() { + return this.f > 0; + } + + private boolean K() { + return this.f > 8; + } + + public String getContainerName() { + return "minecraft:hopper"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + this.d(entityhuman); + return new ContainerHopper(playerinventory, this, entityhuman); + } + + protected NonNullList q() { + return this.items; + } +}