/* * Minecraft Forge * Copyright (c) 2016-2018. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.items; import net.minecraft.block.Block; import net.minecraft.block.BlockDropper; import net.minecraft.block.BlockHopper; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.IHopper; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityDispenser; import net.minecraft.tileentity.TileEntityHopper; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.OptionalCapabilityInstance; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class VanillaInventoryCodeHooks { /** * Copied from TileEntityHopper#captureDroppedItems and added capability support * @return Null if we did nothing {no IItemHandler}, True if we moved an item, False if we moved no items */ @Nullable public static Boolean extractHook(IHopper dest) { return getItemHandler(dest, EnumFacing.UP) .map(itemHandlerResult -> { IItemHandler handler = itemHandlerResult.getKey(); for (int i = 0; i < handler.getSlots(); i++) { ItemStack extractItem = handler.extractItem(i, 1, true); if (!extractItem.isEmpty()) { for (int j = 0; j < dest.getSizeInventory(); j++) { ItemStack destStack = dest.getStackInSlot(j); if (dest.isItemValidForSlot(j, extractItem) && (destStack.isEmpty() || destStack.getCount() < destStack.getMaxStackSize() && destStack.getCount() < dest.getInventoryStackLimit() && ItemHandlerHelper.canItemStacksStack(extractItem, destStack))) { extractItem = handler.extractItem(i, 1, false); if (destStack.isEmpty()) dest.setInventorySlotContents(j, extractItem); else { destStack.grow(1); dest.setInventorySlotContents(j, destStack); } dest.markDirty(); return true; } } } } return false; }) .orElse(null); // TODO bad null } /** * Copied from BlockDropper#dispense and added capability support */ public static boolean dropperInsertHook(World world, BlockPos pos, TileEntityDispenser dropper, int slot, @Nonnull ItemStack stack) { EnumFacing enumfacing = world.getBlockState(pos).get(BlockDropper.FACING); BlockPos blockpos = pos.offset(enumfacing); return getItemHandler(world, (double) blockpos.getX(), (double) blockpos.getY(), (double) blockpos.getZ(), enumfacing.getOpposite()) .map(destinationResult -> { IItemHandler itemHandler = destinationResult.getKey(); Object destination = destinationResult.getValue(); ItemStack dispensedStack = stack.copy().split(1); ItemStack remainder = putStackInInventoryAllSlots(dropper, destination, itemHandler, dispensedStack); if (remainder.isEmpty()) { remainder = stack.copy(); remainder.shrink(1); } else { remainder = stack.copy(); } dropper.setInventorySlotContents(slot, remainder); return false; }) .orElse(false); } /** * Copied from TileEntityHopper#transferItemsOut and added capability support */ public static boolean insertHook(TileEntityHopper hopper) { EnumFacing hopperFacing = hopper.getBlockState().get(BlockHopper.FACING); return getItemHandler(hopper, hopperFacing) .map(destinationResult -> { IItemHandler itemHandler = destinationResult.getKey(); Object destination = destinationResult.getValue(); if (isFull(itemHandler)) { return false; } else { for (int i = 0; i < hopper.getSizeInventory(); ++i) { if (!hopper.getStackInSlot(i).isEmpty()) { ItemStack originalSlotContents = hopper.getStackInSlot(i).copy(); ItemStack insertStack = hopper.decrStackSize(i, 1); ItemStack remainder = putStackInInventoryAllSlots(hopper, destination, itemHandler, insertStack); if (remainder.isEmpty()) { return true; } hopper.setInventorySlotContents(i, originalSlotContents); } } return false; } }) .orElse(false); } private static ItemStack putStackInInventoryAllSlots(TileEntity source, Object destination, IItemHandler destInventory, ItemStack stack) { for (int slot = 0; slot < destInventory.getSlots() && !stack.isEmpty(); slot++) { stack = insertStack(source, destination, destInventory, stack, slot); } return stack; } /** * Copied from TileEntityHopper#insertStack and added capability support */ private static ItemStack insertStack(TileEntity source, Object destination, IItemHandler destInventory, ItemStack stack, int slot) { ItemStack itemstack = destInventory.getStackInSlot(slot); if (destInventory.insertItem(slot, stack, true).isEmpty()) { boolean insertedItem = false; boolean inventoryWasEmpty = isEmpty(destInventory); if (itemstack.isEmpty()) { destInventory.insertItem(slot, stack, false); stack = ItemStack.EMPTY; insertedItem = true; } else if (ItemHandlerHelper.canItemStacksStack(itemstack, stack)) { int originalSize = stack.getCount(); stack = destInventory.insertItem(slot, stack, false); insertedItem = originalSize < stack.getCount(); } if (insertedItem) { if (inventoryWasEmpty && destination instanceof TileEntityHopper) { TileEntityHopper destinationHopper = (TileEntityHopper)destination; if (!destinationHopper.mayTransfer()) { int k = 0; /* TODO TileEntityHopper patches if (source instanceof TileEntityHopper) { if (destinationHopper.getLastUpdateTime() >= ((TileEntityHopper) source).getLastUpdateTime()) { k = 1; } } */ destinationHopper.setTransferCooldown(8 - k); } } } } return stack; } private static OptionalCapabilityInstance> getItemHandler(IHopper hopper, EnumFacing hopperFacing) { double x = hopper.getXPos() + (double) hopperFacing.getXOffset(); double y = hopper.getYPos() + (double) hopperFacing.getYOffset(); double z = hopper.getZPos() + (double) hopperFacing.getZOffset(); return getItemHandler(hopper.getWorld(), x, y, z, hopperFacing.getOpposite()); } private static boolean isFull(IItemHandler itemHandler) { for (int slot = 0; slot < itemHandler.getSlots(); slot++) { ItemStack stackInSlot = itemHandler.getStackInSlot(slot); if (stackInSlot.isEmpty() || stackInSlot.getCount() != stackInSlot.getMaxStackSize()) { return false; } } return true; } private static boolean isEmpty(IItemHandler itemHandler) { for (int slot = 0; slot < itemHandler.getSlots(); slot++) { ItemStack stackInSlot = itemHandler.getStackInSlot(slot); if (stackInSlot.getCount() > 0) { return false; } } return true; } public static OptionalCapabilityInstance> getItemHandler(World worldIn, double x, double y, double z, final EnumFacing side) { 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(/* TODO Block patches // state */)) { TileEntity tileentity = worldIn.getTileEntity(blockpos); if (tileentity != null) { return tileentity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side) .map(capability -> ImmutablePair.of(capability, tileentity)); } } return OptionalCapabilityInstance.empty(); } }