package net.minecraft.tileentity; import java.util.List; import javax.annotation.Nullable; import net.minecraft.block.Block; import net.minecraft.block.BlockChest; import net.minecraft.block.BlockHopper; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.Container; import net.minecraft.inventory.ContainerHopper; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.inventory.ItemStackHelper; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EntitySelectors; import net.minecraft.util.EnumFacing; import net.minecraft.util.ITickable; import net.minecraft.util.NonNullList; import net.minecraft.util.datafix.DataFixer; import net.minecraft.util.datafix.FixTypes; import net.minecraft.util.datafix.walkers.ItemStackDataLists; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; public class TileEntityHopper extends TileEntityLockableLoot implements IHopper, ITickable { private NonNullList inventory = NonNullList.withSize(5, ItemStack.EMPTY); private int transferCooldown = -1; private long tickedGameTime; public static void registerFixesHopper(DataFixer fixer) { fixer.registerWalker(FixTypes.BLOCK_ENTITY, new ItemStackDataLists(TileEntityHopper.class, new String[] {"Items"})); } public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); this.inventory = NonNullList.withSize(this.getSizeInventory(), ItemStack.EMPTY); if (!this.checkLootAndRead(compound)) { ItemStackHelper.loadAllItems(compound, this.inventory); } if (compound.hasKey("CustomName", 8)) { this.customName = compound.getString("CustomName"); } this.transferCooldown = compound.getInteger("TransferCooldown"); } public NBTTagCompound writeToNBT(NBTTagCompound compound) { super.writeToNBT(compound); if (!this.checkLootAndWrite(compound)) { ItemStackHelper.saveAllItems(compound, this.inventory); } compound.setInteger("TransferCooldown", this.transferCooldown); if (this.hasCustomName()) { compound.setString("CustomName", this.customName); } return compound; } /** * Returns the number of slots in the inventory. */ public int getSizeInventory() { return this.inventory.size(); } /** * Removes up to a specified number of items from an inventory slot and returns them in a new stack. */ public ItemStack decrStackSize(int index, int count) { this.fillWithLoot((EntityPlayer)null); ItemStack itemstack = ItemStackHelper.getAndSplit(this.getItems(), index, count); return itemstack; } /** * Sets the given item stack to the specified slot in the inventory (can be crafting or armor sections). */ public void setInventorySlotContents(int index, ItemStack stack) { this.fillWithLoot((EntityPlayer)null); this.getItems().set(index, stack); if (stack.getCount() > this.getInventoryStackLimit()) { stack.setCount(this.getInventoryStackLimit()); } } /** * Get the name of this object. For players this returns their username */ public String getName() { return this.hasCustomName() ? this.customName : "container.hopper"; } /** * Returns the maximum stack size for a inventory slot. Seems to always be 64, possibly will be extended. */ public int getInventoryStackLimit() { return 64; } /** * Like the old updateEntity(), except more generic. */ public void update() { if (this.world != null && !this.world.isRemote) { --this.transferCooldown; this.tickedGameTime = this.world.getTotalWorldTime(); if (!this.isOnTransferCooldown()) { this.setTransferCooldown(0); this.updateHopper(); } } } protected boolean updateHopper() { if (this.world != null && !this.world.isRemote) { if (!this.isOnTransferCooldown() && BlockHopper.isEnabled(this.getBlockMetadata())) { boolean flag = false; if (!this.isInventoryEmpty()) { flag = this.transferItemsOut(); } if (!this.isFull()) { flag = pullItems(this) || flag; } if (flag) { this.setTransferCooldown(8); this.markDirty(); return true; } } return false; } else { return false; } } private boolean isInventoryEmpty() { for (ItemStack itemstack : this.inventory) { if (!itemstack.isEmpty()) { return false; } } return true; } public boolean isEmpty() { return this.isInventoryEmpty(); } private boolean isFull() { for (ItemStack itemstack : this.inventory) { if (itemstack.isEmpty() || itemstack.getCount() != itemstack.getMaxStackSize()) { return false; } } return true; } private boolean transferItemsOut() { if (net.minecraftforge.items.VanillaInventoryCodeHooks.insertHook(this)) { return true; } IInventory iinventory = this.getInventoryForHopperTransfer(); if (iinventory == null) { return false; } else { EnumFacing enumfacing = BlockHopper.getFacing(this.getBlockMetadata()).getOpposite(); if (this.isInventoryFull(iinventory, enumfacing)) { return false; } else { for (int i = 0; i < this.getSizeInventory(); ++i) { if (!this.getStackInSlot(i).isEmpty()) { ItemStack itemstack = this.getStackInSlot(i).copy(); ItemStack itemstack1 = putStackInInventoryAllSlots(this, iinventory, this.decrStackSize(i, 1), enumfacing); if (itemstack1.isEmpty()) { iinventory.markDirty(); return true; } this.setInventorySlotContents(i, itemstack); } } return false; } } } /** * Returns false if the inventory has any room to place items in */ private boolean isInventoryFull(IInventory inventoryIn, EnumFacing side) { if (inventoryIn instanceof ISidedInventory) { ISidedInventory isidedinventory = (ISidedInventory)inventoryIn; int[] aint = isidedinventory.getSlotsForFace(side); for (int k : aint) { ItemStack itemstack1 = isidedinventory.getStackInSlot(k); if (itemstack1.isEmpty() || itemstack1.getCount() != itemstack1.getMaxStackSize()) { return false; } } } else { int i = inventoryIn.getSizeInventory(); for (int j = 0; j < i; ++j) { ItemStack itemstack = inventoryIn.getStackInSlot(j); if (itemstack.isEmpty() || itemstack.getCount() != itemstack.getMaxStackSize()) { return false; } } } return true; } /** * Returns false if the specified IInventory contains any items */ private static boolean isInventoryEmpty(IInventory inventoryIn, EnumFacing side) { if (inventoryIn instanceof ISidedInventory) { ISidedInventory isidedinventory = (ISidedInventory)inventoryIn; int[] aint = isidedinventory.getSlotsForFace(side); for (int i : aint) { if (!isidedinventory.getStackInSlot(i).isEmpty()) { return false; } } } else { int j = inventoryIn.getSizeInventory(); for (int k = 0; k < j; ++k) { if (!inventoryIn.getStackInSlot(k).isEmpty()) { return false; } } } return true; } /** * Pull dropped {@link net.minecraft.entity.item.EntityItem EntityItem}s from the world above the hopper and items * from any inventory attached to this hopper into the hopper's inventory. * * @param hopper the hopper in question * @return whether any items were successfully added to the hopper */ public static boolean pullItems(IHopper hopper) { Boolean ret = net.minecraftforge.items.VanillaInventoryCodeHooks.extractHook(hopper); if (ret != null) return ret; IInventory iinventory = getSourceInventory(hopper); if (iinventory != null) { EnumFacing enumfacing = EnumFacing.DOWN; if (isInventoryEmpty(iinventory, enumfacing)) { return false; } if (iinventory instanceof ISidedInventory) { ISidedInventory isidedinventory = (ISidedInventory)iinventory; int[] aint = isidedinventory.getSlotsForFace(enumfacing); for (int i : aint) { if (pullItemFromSlot(hopper, iinventory, i, enumfacing)) { return true; } } } else { int j = iinventory.getSizeInventory(); for (int k = 0; k < j; ++k) { if (pullItemFromSlot(hopper, iinventory, k, enumfacing)) { return true; } } } } else { for (EntityItem entityitem : getCaptureItems(hopper.getWorld(), hopper.getXPos(), hopper.getYPos(), hopper.getZPos())) { if (putDropInInventoryAllSlots((IInventory)null, hopper, entityitem)) { return true; } } } return false; } /** * Pulls from the specified slot in the inventory and places in any available slot in the hopper. Returns true if * the entire stack was moved */ private static boolean pullItemFromSlot(IHopper hopper, IInventory inventoryIn, int index, EnumFacing direction) { ItemStack itemstack = inventoryIn.getStackInSlot(index); if (!itemstack.isEmpty() && canExtractItemFromSlot(inventoryIn, itemstack, index, direction)) { ItemStack itemstack1 = itemstack.copy(); ItemStack itemstack2 = putStackInInventoryAllSlots(inventoryIn, hopper, inventoryIn.decrStackSize(index, 1), (EnumFacing)null); if (itemstack2.isEmpty()) { inventoryIn.markDirty(); return true; } inventoryIn.setInventorySlotContents(index, itemstack1); } return false; } /** * Attempts to place the passed EntityItem's stack into the inventory using as many slots as possible. Returns false * if the stackSize of the drop was not depleted. */ public static boolean putDropInInventoryAllSlots(IInventory source, IInventory destination, EntityItem entity) { boolean flag = false; if (entity == null) { return false; } else { ItemStack itemstack = entity.getItem().copy(); ItemStack itemstack1 = putStackInInventoryAllSlots(source, destination, itemstack, (EnumFacing)null); if (itemstack1.isEmpty()) { flag = true; entity.setDead(); } else { entity.setItem(itemstack1); } return flag; } } protected net.minecraftforge.items.IItemHandler createUnSidedHandler() { return new net.minecraftforge.items.VanillaHopperItemHandler(this); } /** * Attempts to place the passed stack in the inventory, using as many slots as required. Returns leftover items */ public static ItemStack putStackInInventoryAllSlots(IInventory source, IInventory destination, ItemStack stack, @Nullable EnumFacing direction) { if (destination instanceof ISidedInventory && direction != null) { ISidedInventory isidedinventory = (ISidedInventory)destination; int[] aint = isidedinventory.getSlotsForFace(direction); for (int k = 0; k < aint.length && !stack.isEmpty(); ++k) { stack = insertStack(source, destination, stack, aint[k], direction); } } else { int i = destination.getSizeInventory(); for (int j = 0; j < i && !stack.isEmpty(); ++j) { stack = insertStack(source, destination, stack, j, direction); } } return stack; } /** * Can this hopper insert the specified item from the specified slot on the specified side? */ private static boolean canInsertItemInSlot(IInventory inventoryIn, ItemStack stack, int index, EnumFacing side) { if (!inventoryIn.isItemValidForSlot(index, stack)) { return false; } else { return !(inventoryIn instanceof ISidedInventory) || ((ISidedInventory)inventoryIn).canInsertItem(index, stack, side); } } /** * Can this hopper extract the specified item from the specified slot on the specified side? */ private static boolean canExtractItemFromSlot(IInventory inventoryIn, ItemStack stack, int index, EnumFacing side) { return !(inventoryIn instanceof ISidedInventory) || ((ISidedInventory)inventoryIn).canExtractItem(index, stack, side); } /** * Insert the specified stack to the specified inventory and return any leftover items */ private static ItemStack insertStack(IInventory source, IInventory destination, ItemStack stack, int index, EnumFacing direction) { ItemStack itemstack = destination.getStackInSlot(index); if (canInsertItemInSlot(destination, stack, index, direction)) { boolean flag = false; boolean flag1 = destination.isEmpty(); if (itemstack.isEmpty()) { destination.setInventorySlotContents(index, stack); stack = ItemStack.EMPTY; flag = true; } else if (canCombine(itemstack, stack)) { int i = stack.getMaxStackSize() - itemstack.getCount(); int j = Math.min(stack.getCount(), i); stack.shrink(j); itemstack.grow(j); flag = j > 0; } if (flag) { if (flag1 && destination instanceof TileEntityHopper) { TileEntityHopper tileentityhopper1 = (TileEntityHopper)destination; if (!tileentityhopper1.mayTransfer()) { int k = 0; if (source != null && source instanceof TileEntityHopper) { TileEntityHopper tileentityhopper = (TileEntityHopper)source; if (tileentityhopper1.tickedGameTime >= tileentityhopper.tickedGameTime) { k = 1; } } tileentityhopper1.setTransferCooldown(8 - k); } } destination.markDirty(); } } return stack; } /** * Returns the IInventory that this hopper is pointing into */ private IInventory getInventoryForHopperTransfer() { EnumFacing enumfacing = BlockHopper.getFacing(this.getBlockMetadata()); return getInventoryAtPosition(this.getWorld(), this.getXPos() + (double)enumfacing.getFrontOffsetX(), this.getYPos() + (double)enumfacing.getFrontOffsetY(), this.getZPos() + (double)enumfacing.getFrontOffsetZ()); } /** * Gets the inventory that the provided hopper will transfer items from. */ public static IInventory getSourceInventory(IHopper hopper) { return getInventoryAtPosition(hopper.getWorld(), hopper.getXPos(), hopper.getYPos() + 1.0D, hopper.getZPos()); } public static List getCaptureItems(World worldIn, double p_184292_1_, double p_184292_3_, double p_184292_5_) { return worldIn.getEntitiesWithinAABB(EntityItem.class, new AxisAlignedBB(p_184292_1_ - 0.5D, p_184292_3_, p_184292_5_ - 0.5D, p_184292_1_ + 0.5D, p_184292_3_ + 1.5D, p_184292_5_ + 0.5D), EntitySelectors.IS_ALIVE); } /** * Returns the IInventory (if applicable) of the TileEntity at the specified position */ public static IInventory getInventoryAtPosition(World worldIn, double x, double y, double z) { IInventory iinventory = null; int i = MathHelper.floor(x); int j = MathHelper.floor(y); int k = MathHelper.floor(z); BlockPos blockpos = new BlockPos(i, j, k); net.minecraft.block.state.IBlockState state = worldIn.getBlockState(blockpos); Block block = state.getBlock(); if (block.hasTileEntity(state)) { TileEntity tileentity = worldIn.getTileEntity(blockpos); if (tileentity instanceof IInventory) { iinventory = (IInventory)tileentity; if (iinventory instanceof TileEntityChest && block instanceof BlockChest) { iinventory = ((BlockChest)block).getContainer(worldIn, blockpos, true); } } } if (iinventory == null) { List list = worldIn.getEntitiesInAABBexcluding((Entity)null, new AxisAlignedBB(x - 0.5D, y - 0.5D, z - 0.5D, x + 0.5D, y + 0.5D, z + 0.5D), EntitySelectors.HAS_INVENTORY); if (!list.isEmpty()) { iinventory = (IInventory)list.get(worldIn.rand.nextInt(list.size())); } } return iinventory; } private static boolean canCombine(ItemStack stack1, ItemStack stack2) { if (stack1.getItem() != stack2.getItem()) { return false; } else if (stack1.getMetadata() != stack2.getMetadata()) { return false; } else if (stack1.getCount() > stack1.getMaxStackSize()) { return false; } else { return ItemStack.areItemStackTagsEqual(stack1, stack2); } } /** * Gets the world X position for this hopper entity. */ public double getXPos() { return (double)this.pos.getX() + 0.5D; } /** * Gets the world Y position for this hopper entity. */ public double getYPos() { return (double)this.pos.getY() + 0.5D; } /** * Gets the world Z position for this hopper entity. */ public double getZPos() { return (double)this.pos.getZ() + 0.5D; } public void setTransferCooldown(int ticks) { this.transferCooldown = ticks; } private boolean isOnTransferCooldown() { return this.transferCooldown > 0; } public boolean mayTransfer() { return this.transferCooldown > 8; } public String getGuiID() { return "minecraft:hopper"; } public Container createContainer(InventoryPlayer playerInventory, EntityPlayer playerIn) { this.fillWithLoot(playerIn); return new ContainerHopper(playerInventory, this, playerIn); } protected NonNullList getItems() { return this.inventory; } public long getLastUpdateTime() { return tickedGameTime; } // Forge }