Add IItemHandler capability

Add the actual patches that I forgot.

Add simple implementations of IStorage and the factory methods.

Add ItemStackHandler. A simple IItemHandler implementaton.

return nulls, not throw nulls.

Move the vanilla wrappers to a separate class for now.

Minor clean ups of VanillaWrapper code.

Inline static methods.

Add comments.

Minor cleanup of code.

Remove redundant size field and add a validate slot index method.

Minor formatting issues.

Break early If stacksize to insert is 0.

Remove setByte() methods.

Throw exception if IItemHandler can't be modifyed in NBT loading.

Replace event handler with patches

Add capability to mine cart inventory entities.

Change formatting and registration of capability.

Make InventoryPlayer implements IItemHandler because why not. Also added a field to allow mods that add additional player inventory space to publicly expose them.

Reduce patch sizes

Lazy initialization of the item handler for vanilla tiles.

Minor formatting changes.

Create a single vanilla chest item handler that will merge with adjacent chests when detected. Added hooks to reset the cached adjacent value when a block update is detected and when a chunk loads.

Revert "Make InventoryPlayer implements IItemHandler because why not. Also added a field to allow mods that add additional player inventory space to publicly expose them."

This reverts commit 306d4a37fd0e8c8a0754411c013b750dfe8e2c87.

Fix furnace derp

Replace double chest code with a simpler method.

Vanilla wrappers implement IItemHandlerModifiable (since they are modifiable)

Minor code cleanups

Add an onContentsChanged() and onLoad() callback methods.to the default implementation.

Add slot as a parameter in the callback method.

Change IItemHandlerModifiable.setStackInSlot() to void, and added a note about not being intended for cross-mod use.

Improve ItemStackHandler handling of errored NBT.

Make the stacks array protected.

Fix a lot of derps in SlotItemHandler.

Fix derp in ItemStackHandler

Clarify comments on IItemHandler

ItemStackHandler no longer caches the stack array in local variable.

Clean up the Chests code to make intentions clearer

Vanilla hoppers have their cooldown activated when an item is inserted. Made this behavior part of an item handler (rather than the insertion code)

Fix mistake in ItemStackHandler

More documentation of potential edge cases in getStackInSlot()

Make limit checking more resiliant.
This commit is contained in:
rwtema 2016-01-17 16:41:34 +00:00
parent b8238e7f22
commit a1e41f5464
20 changed files with 1299 additions and 1 deletions

View file

@ -0,0 +1,11 @@
--- ../src-base/minecraft/net/minecraft/block/BlockDropper.java
+++ ../src-work/minecraft/net/minecraft/block/BlockDropper.java
@@ -43,7 +43,7 @@
{
ItemStack itemstack = tileentitydispenser.func_70301_a(i);
- if (itemstack != null)
+ if (itemstack != null && net.minecraftforge.items.VanillaInventoryCodeHooks.dropperInsertHook(p_176439_1_, p_176439_2_, tileentitydispenser, i, itemstack))
{
EnumFacing enumfacing = (EnumFacing)p_176439_1_.func_180495_p(p_176439_2_).func_177229_b(field_176441_a);
BlockPos blockpos = p_176439_2_.func_177972_a(enumfacing);

View file

@ -8,3 +8,26 @@
if (!this.field_70170_p.field_72995_K) if (!this.field_70170_p.field_72995_K)
{ {
p_130002_1_.func_71007_a(this); p_130002_1_.func_71007_a(this);
@@ -232,4 +233,22 @@
this.field_94113_a[i] = null;
}
}
+
+ public net.minecraftforge.items.IItemHandler itemHandler = new net.minecraftforge.items.wrapper.InvWrapper(this);
+
+ @Override
+ public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, net.minecraft.util.EnumFacing facing)
+ {
+ if (capability == net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
+ {
+ return (T) itemHandler;
+ }
+ return super.getCapability(capability, facing);
+ }
+
+ @Override
+ public boolean hasCapability(net.minecraftforge.common.capabilities.Capability<?> capability, net.minecraft.util.EnumFacing facing)
+ {
+ return capability == net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY || super.hasCapability(capability, facing);
+ }
}

View file

@ -76,3 +76,22 @@
} }
public boolean[] func_174902_m() public boolean[] func_174902_m()
@@ -386,4 +393,18 @@
this.field_145945_j[i] = null;
}
}
+
+ net.minecraftforge.items.IItemHandler handlerInput = new net.minecraftforge.items.wrapper.SidedInvWrapper(this, net.minecraft.util.EnumFacing.UP);
+ net.minecraftforge.items.IItemHandler handlerOutput = new net.minecraftforge.items.wrapper.SidedInvWrapper(this, net.minecraft.util.EnumFacing.DOWN);
+
+ @Override
+ public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, net.minecraft.util.EnumFacing facing)
+ {
+ if (facing != null && capability == net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
+ if (facing == EnumFacing.UP)
+ return (T) handlerInput;
+ else
+ return (T) handlerOutput;
+ return super.getCapability(capability, facing);
+ }
}

View file

@ -0,0 +1,35 @@
--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityChest.java
+++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityChest.java
@@ -186,6 +186,7 @@
{
super.func_145836_u();
this.field_145984_a = false;
+ doubleChestHandler = null;
}
@SuppressWarnings("incomplete-switch")
@@ -468,4 +469,24 @@
this.field_145985_p[i] = null;
}
}
+
+ public net.minecraftforge.items.VanillaDoubleChestItemHandler doubleChestHandler;
+
+ @Override
+ public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, net.minecraft.util.EnumFacing facing)
+ {
+ if (capability == net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
+ {
+ if(doubleChestHandler == null || doubleChestHandler.needsRefresh())
+ doubleChestHandler = net.minecraftforge.items.VanillaDoubleChestItemHandler.get(this);
+ if (doubleChestHandler != null && doubleChestHandler != net.minecraftforge.items.VanillaDoubleChestItemHandler.NO_ADJACENT_CHESTS_INSTANCE)
+ return (T) doubleChestHandler;
+ }
+ return super.getCapability(capability, facing);
+ }
+
+ public net.minecraftforge.items.IItemHandler getSingleChestHandler()
+ {
+ return super.getCapability(net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
+ }
}

View file

@ -49,3 +49,25 @@
} }
} }
@@ -451,4 +462,21 @@
this.field_145957_n[i] = null;
}
}
+
+ net.minecraftforge.items.IItemHandler handlerTop = new net.minecraftforge.items.wrapper.SidedInvWrapper(this, net.minecraft.util.EnumFacing.UP);
+ net.minecraftforge.items.IItemHandler handlerBottom = new net.minecraftforge.items.wrapper.SidedInvWrapper(this, net.minecraft.util.EnumFacing.DOWN);
+ net.minecraftforge.items.IItemHandler handlerSide = new net.minecraftforge.items.wrapper.SidedInvWrapper(this, net.minecraft.util.EnumFacing.WEST);
+
+ @Override
+ public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, net.minecraft.util.EnumFacing facing)
+ {
+ if (facing != null && capability == net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
+ if (facing == EnumFacing.DOWN)
+ return (T) handlerBottom;
+ else if (facing == EnumFacing.UP)
+ return (T) handlerTop;
+ else
+ return (T) handlerSide;
+ return super.getCapability(capability, facing);
+ }
}

View file

@ -1,6 +1,22 @@
--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityHopper.java --- ../src-base/minecraft/net/minecraft/tileentity/TileEntityHopper.java
+++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityHopper.java +++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityHopper.java
@@ -515,17 +515,31 @@ @@ -260,6 +260,7 @@
private boolean func_145883_k()
{
+ if (net.minecraftforge.items.VanillaInventoryCodeHooks.insertHook(this)) { return true; }
IInventory iinventory = this.func_145895_l();
if (iinventory == null)
@@ -366,6 +367,7 @@
public static boolean func_145891_a(IHopper p_145891_0_)
{
+ if (net.minecraftforge.items.VanillaInventoryCodeHooks.extractHook(p_145891_0_)) { return true; }
IInventory iinventory = func_145884_b(p_145891_0_);
if (iinventory != null)
@@ -515,17 +517,31 @@
if (itemstack == null) if (itemstack == null)
{ {
@ -33,3 +49,14 @@
} }
if (flag) if (flag)
@@ -668,4 +684,10 @@
this.field_145900_a[i] = null;
}
}
+
+
+ protected net.minecraftforge.items.IItemHandler createUnSidedHandler()
+ {
+ return new net.minecraftforge.items.VanillaHopperItemHandler(this);
+ }
}

View file

@ -0,0 +1,28 @@
--- ../src-base/minecraft/net/minecraft/tileentity/TileEntityLockable.java
+++ ../src-work/minecraft/net/minecraft/tileentity/TileEntityLockable.java
@@ -47,4 +47,25 @@
{
return (IChatComponent)(this.func_145818_k_() ? new ChatComponentText(this.func_70005_c_()) : new ChatComponentTranslation(this.func_70005_c_(), new Object[0]));
}
+
+ private net.minecraftforge.items.IItemHandler itemHandler;
+
+ protected net.minecraftforge.items.IItemHandler createUnSidedHandler()
+ {
+ return new net.minecraftforge.items.wrapper.InvWrapper(this);
+ }
+
+ @Override
+ public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, net.minecraft.util.EnumFacing facing)
+ {
+ if (capability == net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
+ return (T) (itemHandler == null ? (itemHandler = createUnSidedHandler()) : itemHandler);
+ return super.getCapability(capability, facing);
+ }
+
+ @Override
+ public boolean hasCapability(net.minecraftforge.common.capabilities.Capability<?> capability, net.minecraft.util.EnumFacing facing)
+ {
+ return capability == net.minecraftforge.items.CapabilityItemHandler.ITEM_HANDLER_CAPABILITY || super.hasCapability(capability, facing);
+ }
}

View file

@ -26,6 +26,7 @@ import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property; import net.minecraftforge.common.config.Property;
import net.minecraftforge.common.network.ForgeNetworkHandler; import net.minecraftforge.common.network.ForgeNetworkHandler;
import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.oredict.OreDictionary; import net.minecraftforge.oredict.OreDictionary;
import net.minecraftforge.oredict.RecipeSorter; import net.minecraftforge.oredict.RecipeSorter;
import net.minecraftforge.server.command.ForgeCommand; import net.minecraftforge.server.command.ForgeCommand;
@ -310,6 +311,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
@Subscribe @Subscribe
public void preInit(FMLPreInitializationEvent evt) public void preInit(FMLPreInitializationEvent evt)
{ {
CapabilityItemHandler.register();
MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER); MinecraftForge.EVENT_BUS.register(MinecraftForge.INTERNAL_HANDLER);
ForgeChunkManager.captureConfig(evt.getModConfigurationDirectory()); ForgeChunkManager.captureConfig(evt.getModConfigurationDirectory());
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);

View file

@ -0,0 +1,70 @@
package net.minecraftforge.items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.common.capabilities.CapabilityManager;
import java.util.concurrent.Callable;
public class CapabilityItemHandler
{
@CapabilityInject(IItemHandler.class)
public static Capability<IItemHandler> ITEM_HANDLER_CAPABILITY = null;
public static void register()
{
CapabilityManager.INSTANCE.register(IItemHandler.class, new Capability.IStorage<IItemHandler>()
{
@Override
public NBTBase writeNBT(Capability<IItemHandler> capability, IItemHandler instance, EnumFacing side)
{
NBTTagList nbtTagList = new NBTTagList();
int size = instance.getSlots();
for (int i = 0; i < size; i++)
{
ItemStack stack = instance.getStackInSlot(i);
if (stack != null)
{
NBTTagCompound itemTag = new NBTTagCompound();
itemTag.setInteger("Slot", i);
stack.writeToNBT(itemTag);
nbtTagList.appendTag(itemTag);
}
}
return nbtTagList;
}
@Override
public void readNBT(Capability<IItemHandler> capability, IItemHandler instance, EnumFacing side, NBTBase base)
{
if (!(instance instanceof IItemHandlerModifiable))
throw new RuntimeException("IItemHandler instance does not implement IItemHandlerModifiable");
IItemHandlerModifiable itemHandlerModifiable = (IItemHandlerModifiable) instance;
NBTTagList tagList = (NBTTagList) base;
for (int i = 0; i < tagList.tagCount(); i++)
{
NBTTagCompound itemTags = tagList.getCompoundTagAt(i);
int j = itemTags.getInteger("Slot");
if (j > 0 && j < instance.getSlots())
{
itemHandlerModifiable.setStackInSlot(j, ItemStack.loadItemStackFromNBT(itemTags));
}
}
}
}, new Callable<ItemStackHandler>()
{
@Override
public ItemStackHandler call() throws Exception
{
return new ItemStackHandler();
}
});
}
}

View file

@ -0,0 +1,57 @@
package net.minecraftforge.items;
import net.minecraft.item.ItemStack;
public interface IItemHandler
{
/**
* Returns the number of slots available
*
* @return The number of slots available
**/
int getSlots();
/**
* Returns the ItemStack in a given slot.
*
* The result's stack size may be greater than the itemstacks max size.
*
* If the result is null, then the slot is empty.
* If the result is not null but the stack size is zero, then it represents
* an empty slot that will only accept* a specific itemstack.
*
* <p/>
* IMPORTANT: This ItemStack MUST NOT be modified. This method is not for
* altering an inventories contents. Any implementers who are able to detect
* modification through this method should throw an exception.
* <p/>
* SERIOUSLY: DO NOT MODIFY THE RETURNED ITEMSTACK
*
* @param slot Slot to query
* @return ItemStack in given slot. May be null.
**/
ItemStack getStackInSlot(int slot);
/**
* Inserts an ItemStack into the given slot and return the remainder.
* Note: This behaviour is subtly different from IFluidHandlers.fill()
*
* @param slot Slot to insert into.
* @param stack ItemStack to insert
* @param simulate If true, the insertion is only simulated
* @return The remaining ItemStack that was not inserted (if the entire stack is accepted, then return null)
**/
ItemStack insertItem(int slot, ItemStack stack, boolean simulate);
/**
* Extracts an ItemStack from the given slot. The returned value must be null
* if nothing is extracted, otherwise it's stack size must not be greater than amount or the
* itemstacks getMaxStackSize().
*
* @param slot Slot to extract from.
* @param amount Amount to extract (may be greater than the current stacks max limit)
* @param simulate If true, the extraction is only simulated
* @return ItemStack extracted from the slot, must be null, if nothing can be extracted
**/
ItemStack extractItem(int slot, int amount, boolean simulate);
}

View file

@ -0,0 +1,19 @@
package net.minecraftforge.items;
import net.minecraft.item.ItemStack;
public interface IItemHandlerModifiable extends IItemHandler
{
/**
* Overrides the stack in the given slot. This method is used by the
* standard Forge helper methods and classes. It is not intended for
* general use by other mods, and the handler may throw an error if it
* is called unexpectedly.
*
* @param slot Slot to modify
* @param stack ItemStack to set slot to (may be null)
* @throws RuntimeException if the handler is called in a way that the handler
* was not expecting.
**/
void setStackInSlot(int slot, ItemStack stack);
}

View file

@ -0,0 +1,43 @@
package net.minecraftforge.items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
public class ItemHandlerHelper
{
public static ItemStack insertItem(IItemHandler dest, ItemStack stack, boolean simulate)
{
if (dest == null || stack == null)
return stack;
for (int i = 0; i < dest.getSlots(); i++)
{
stack = dest.insertItem(i, stack, simulate);
if (stack == null || stack.stackSize <= 0)
{
return null;
}
}
return stack;
}
public static boolean canItemStacksStack(ItemStack a, ItemStack b)
{
if (a == null || !a.isItemEqual(b))
return false;
final NBTTagCompound aTag = a.getTagCompound();
final NBTTagCompound bTag = b.getTagCompound();
return (aTag != null || bTag == null) && (aTag == null || aTag.equals(bTag));
}
public static ItemStack copyStackWithSize(ItemStack itemStack, int size)
{
if (size == 0)
return null;
ItemStack copy = ItemStack.copyItemStack(itemStack);
copy.stackSize = size;
return copy;
}
}

View file

@ -0,0 +1,185 @@
package net.minecraftforge.items;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.common.util.INBTSerializable;
public class ItemStackHandler implements IItemHandler, IItemHandlerModifiable, INBTSerializable<NBTTagCompound>
{
protected ItemStack[] stacks;
public ItemStackHandler()
{
this(1);
}
public ItemStackHandler(int size)
{
stacks = new ItemStack[size];
}
public void setSize(int size)
{
stacks = new ItemStack[size];
}
@Override
public void setStackInSlot(int slot, ItemStack stack)
{
validateSlotIndex(slot);
if (ItemStack.areItemStacksEqual(this.stacks[slot], stack))
return;
this.stacks[slot] = stack;
onContentsChanged(slot);
}
@Override
public int getSlots()
{
return stacks.length;
}
@Override
public ItemStack getStackInSlot(int slot)
{
validateSlotIndex(slot);
return this.stacks[slot];
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate)
{
if (stack == null || stack.stackSize == 0)
return null;
validateSlotIndex(slot);
ItemStack existing = this.stacks[slot];
int limit = getStackLimit(slot, stack);
if (existing != null)
{
if (!ItemHandlerHelper.canItemStacksStack(stack, existing))
return stack;
limit -= existing.stackSize;
}
if (limit <= 0)
return stack;
boolean reachedLimit = stack.stackSize > limit;
if (!simulate)
{
if (existing == null)
{
this.stacks[slot] = reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, limit) : stack;
}
else
{
existing.stackSize += reachedLimit ? limit : stack.stackSize;
}
onContentsChanged(slot);
}
return reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, stack.stackSize - limit) : null;
}
public ItemStack extractItem(int slot, int amount, boolean simulate)
{
if (amount == 0)
return null;
validateSlotIndex(slot);
ItemStack existing = this.stacks[slot];
if (existing == null)
return null;
int toExtract = Math.min(amount, existing.getMaxStackSize());
if (existing.stackSize <= toExtract)
{
if (!simulate)
{
this.stacks[slot] = null;
onContentsChanged(slot);
}
return existing;
}
else
{
if (!simulate)
{
this.stacks[slot] = ItemHandlerHelper.copyStackWithSize(existing, existing.stackSize - toExtract);
onContentsChanged(slot);
}
return ItemHandlerHelper.copyStackWithSize(existing, toExtract);
}
}
protected int getStackLimit(int slot, ItemStack stack)
{
return stack.getMaxStackSize();
}
@Override
public NBTTagCompound serializeNBT()
{
NBTTagList nbtTagList = new NBTTagList();
for (int i = 0; i < stacks.length; i++)
{
if (stacks[i] != null)
{
NBTTagCompound itemTag = new NBTTagCompound();
itemTag.setInteger("Slot", i);
stacks[i].writeToNBT(itemTag);
nbtTagList.appendTag(itemTag);
}
}
NBTTagCompound nbt = new NBTTagCompound();
nbt.setTag("Items", nbtTagList);
nbt.setInteger("Size", stacks.length);
return nbt;
}
@Override
public void deserializeNBT(NBTTagCompound nbt)
{
setSize(nbt.hasKey("Size", Constants.NBT.TAG_INT) ? nbt.getInteger("Size") : stacks.length);
NBTTagList tagList = nbt.getTagList("Items", Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < tagList.tagCount(); i++)
{
NBTTagCompound itemTags = tagList.getCompoundTagAt(i);
int slot = itemTags.getInteger("Slot");
if (slot >= 0 && slot < stacks.length)
{
stacks[slot] = ItemStack.loadItemStackFromNBT(itemTags);
}
}
onLoad();
}
protected void validateSlotIndex(int slot)
{
if (slot < 0 || slot >= stacks.length)
throw new RuntimeException("Slot " + slot + " not in valid range - [0," + stacks.length + ")");
}
protected void onLoad()
{
}
protected void onContentsChanged(int slot)
{
}
}

View file

@ -0,0 +1,75 @@
package net.minecraftforge.items;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryBasic;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
public class SlotItemHandler extends Slot
{
private static IInventory emptyInventory = new InventoryBasic("[Null]", true, 0);
public final IItemHandler itemHandler;
private final int index;
public SlotItemHandler(IItemHandler itemHandler, int index, int xPosition, int yPosition)
{
super(emptyInventory, index, xPosition, yPosition);
this.itemHandler = itemHandler;
this.index = index;
}
@Override
public boolean isItemValid(ItemStack stack)
{
if (stack == null)
return false;
ItemStack remainder = this.itemHandler.insertItem(index, stack, true);
return remainder == null || remainder.stackSize < stack.stackSize;
}
@Override
public ItemStack getStack()
{
return this.itemHandler.getStackInSlot(index);
}
// Override if your IItemHandler does not implement IItemHandlerModifiable
@Override
public void putStack(ItemStack stack)
{
((IItemHandlerModifiable) this.itemHandler).setStackInSlot(index, stack);
this.onSlotChanged();
}
@Override
public void onSlotChange(ItemStack p_75220_1_, ItemStack p_75220_2_)
{
}
@Override
public int getItemStackLimit(ItemStack stack)
{
ItemStack maxAdd = stack.copy();
maxAdd.stackSize = maxAdd.getMaxStackSize();
ItemStack currentStack = this.itemHandler.getStackInSlot(index);
ItemStack remainder = this.itemHandler.insertItem(index, maxAdd, true);
int current = currentStack == null ? 0 : currentStack.stackSize;
int added = maxAdd.stackSize - (remainder != null ? remainder.stackSize : 0);
return current + added;
}
@Override
public boolean canTakeStack(EntityPlayer playerIn)
{
return this.itemHandler.extractItem(index, 1, true) != null;
}
@Override
public ItemStack decrStackSize(int amount)
{
return this.itemHandler.extractItem(index, amount, false);
}
}

View file

@ -0,0 +1,144 @@
package net.minecraftforge.items;
import net.minecraft.block.Block;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityChest;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.World;
import java.lang.ref.WeakReference;
import java.util.Objects;
public class VanillaDoubleChestItemHandler extends WeakReference<TileEntityChest> implements IItemHandler
{
// Dummy cache value to signify that we have checked and definitely found no adjacent chests
public static final VanillaDoubleChestItemHandler NO_ADJACENT_CHESTS_INSTANCE = new VanillaDoubleChestItemHandler(null, null, false);
private final boolean mainChestIsUpper;
private final TileEntityChest mainChest;
private final int hashCode;
public VanillaDoubleChestItemHandler(TileEntityChest mainChest, TileEntityChest other, boolean mainChestIsUpper)
{
super(other);
this.mainChest = mainChest;
this.mainChestIsUpper = mainChestIsUpper;
hashCode = Objects.hashCode(mainChestIsUpper ? mainChest : other) * 31 + Objects.hashCode(!mainChestIsUpper ? mainChest : other);
}
public static VanillaDoubleChestItemHandler get(TileEntityChest chest)
{
World world = chest.getWorld();
BlockPos pos = chest.getPos();
if (world == null || pos == null || !world.isBlockLoaded(pos))
return null; // Still loading
Block blockType = chest.getBlockType();
EnumFacing[] horizontals = EnumFacing.HORIZONTALS;
for (int i = horizontals.length - 1; i >= 0; i--) // Use reverse order so we can return early
{
EnumFacing enumfacing = horizontals[i];
BlockPos blockpos = pos.offset(enumfacing);
Block block = world.getBlockState(blockpos).getBlock();
if (block == blockType)
{
TileEntity otherTE = world.getTileEntity(blockpos);
if (otherTE instanceof TileEntityChest)
{
TileEntityChest otherChest = (TileEntityChest) otherTE;
return new VanillaDoubleChestItemHandler(chest, otherChest,
enumfacing != net.minecraft.util.EnumFacing.WEST && enumfacing != net.minecraft.util.EnumFacing.NORTH);
}
}
}
return NO_ADJACENT_CHESTS_INSTANCE; //All alone
}
public TileEntityChest getChest(boolean accessingUpper)
{
if (accessingUpper == mainChestIsUpper)
return mainChest;
else
{
return getOtherChest();
}
}
private TileEntityChest getOtherChest()
{
TileEntityChest tileEntityChest = get();
return tileEntityChest != null && !tileEntityChest.isInvalid() ? tileEntityChest : null;
}
@Override
public int getSlots()
{
return 27 * 2;
}
@Override
public ItemStack getStackInSlot(int slot)
{
boolean accessingUpperChest = slot < 27;
int targetSlot = accessingUpperChest ? slot : slot - 27;
TileEntityChest chest = getChest(accessingUpperChest);
return chest != null ? chest.getStackInSlot(targetSlot) : null;
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate)
{
boolean accessingUpperChest = slot < 27;
int targetSlot = accessingUpperChest ? slot : slot - 27;
TileEntityChest chest = getChest(accessingUpperChest);
return chest != null ? chest.getSingleChestHandler().insertItem(targetSlot, stack, simulate) : stack;
}
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate)
{
boolean accessingUpperChest = slot < 27;
int targetSlot = accessingUpperChest ? slot : slot - 27;
TileEntityChest chest = getChest(accessingUpperChest);
return chest != null ? chest.getSingleChestHandler().extractItem(targetSlot, amount, simulate) : null;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
VanillaDoubleChestItemHandler that = (VanillaDoubleChestItemHandler) o;
if (hashCode != that.hashCode)
return false;
final TileEntityChest otherChest = getOtherChest();
if (mainChestIsUpper == that.mainChestIsUpper)
return Objects.equals(mainChest, that.mainChest) && Objects.equals(otherChest, that.getOtherChest());
else
return Objects.equals(mainChest, that.getOtherChest()) && Objects.equals(otherChest, that.mainChest);
}
@Override
public int hashCode()
{
return hashCode;
}
public boolean needsRefresh()
{
if (this == NO_ADJACENT_CHESTS_INSTANCE)
return false;
TileEntityChest tileEntityChest = get();
return tileEntityChest == null || tileEntityChest.isInvalid();
}
}

View file

@ -0,0 +1,33 @@
package net.minecraftforge.items;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntityHopper;
import net.minecraftforge.items.wrapper.InvWrapper;
public class VanillaHopperItemHandler extends InvWrapper
{
private final TileEntityHopper hopper;
public VanillaHopperItemHandler(TileEntityHopper hopper)
{
super(hopper);
this.hopper = hopper;
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate)
{
if (stack == null)
return null;
if (simulate || !hopper.mayTransfer())
return super.insertItem(slot, stack, simulate);
int curStackSize = stack.stackSize;
ItemStack itemStack = super.insertItem(slot, stack, false);
if (itemStack == null || curStackSize != itemStack.stackSize)
{
hopper.setTransferCooldown(8);
}
return itemStack;
}
}

View file

@ -0,0 +1,122 @@
package net.minecraftforge.items;
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.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.World;
public class VanillaInventoryCodeHooks
{
public static boolean extractHook(IHopper dest)
{
TileEntity tileEntity = dest.getWorld().getTileEntity(new BlockPos(dest.getXPos(), dest.getYPos() + 1, dest.getZPos()));
if (tileEntity == null || !tileEntity.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.DOWN))
return false;
IItemHandler handler = tileEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.DOWN);
for (int i = 0; i < handler.getSlots(); i++)
{
ItemStack extractItem = handler.extractItem(i, 1, true);
if (extractItem != null)
{
for (int j = 0; j < dest.getSizeInventory(); j++)
{
ItemStack destStack = dest.getStackInSlot(j);
if (destStack == null || destStack.stackSize < destStack.getMaxStackSize() || ItemHandlerHelper.canItemStacksStack(extractItem, destStack))
{
extractItem = handler.extractItem(i, 1, false);
if (destStack == null)
dest.setInventorySlotContents(j, extractItem);
else
{
destStack.stackSize++;
dest.setInventorySlotContents(j, destStack);
}
dest.markDirty();
return true;
}
}
}
}
return true;
}
public static boolean dropperInsertHook(World world, BlockPos pos, TileEntityDispenser dropper, int slot, ItemStack stack)
{
EnumFacing enumfacing = world.getBlockState(pos).getValue(BlockDropper.FACING);
BlockPos offsetPos = pos.offset(enumfacing);
TileEntity tileEntity = world.getTileEntity(offsetPos);
if (tileEntity == null)
return false;
if (!tileEntity.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, enumfacing.getOpposite()))
return true;
IItemHandler capability = tileEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, enumfacing.getOpposite());
ItemStack result = ItemHandlerHelper.insertItem(capability, ItemHandlerHelper.copyStackWithSize(stack, 1), false);
if (result == null)
{
result = stack.copy();
if (--result.stackSize == 0)
{
result = null;
}
}
else
{
result = stack.copy();
}
dropper.setInventorySlotContents(slot, result);
dropper.markDirty();
return false;
}
public static boolean insertHook(TileEntityHopper hopper)
{
return insertHook(hopper, BlockHopper.getFacing(hopper.getBlockMetadata()));
}
public static boolean insertHook(IHopper hopper, EnumFacing facing)
{
TileEntity tileEntity = hopper.getWorld().getTileEntity(
new BlockPos(hopper.getXPos(), hopper.getYPos(), hopper.getZPos()).offset(facing));
if (tileEntity == null)
return false;
if (!tileEntity.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing.getOpposite()))
return false;
IItemHandler handler = tileEntity.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, facing.getOpposite());
for (int i = 0; i < hopper.getSizeInventory(); i++)
{
ItemStack stackInSlot = hopper.getStackInSlot(i);
if (stackInSlot != null)
{
ItemStack insert = stackInSlot.copy();
insert.stackSize = 1;
ItemStack newStack = ItemHandlerHelper.insertItem(handler, insert, true);
if (newStack == null || newStack.stackSize == 0)
{
ItemHandlerHelper.insertItem(handler, hopper.decrStackSize(i, 1), false);
hopper.markDirty();
return true;
}
}
}
return true;
}
}

View file

@ -0,0 +1,33 @@
package net.minecraftforge.items.wrapper;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
public class EmptyHandler implements IItemHandler
{
public static IItemHandler INSTANCE = new EmptyHandler();
@Override
public int getSlots()
{
return 0;
}
@Override
public ItemStack getStackInSlot(int slot)
{
return null;
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate)
{
return stack;
}
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate)
{
return null;
}
}

View file

@ -0,0 +1,166 @@
package net.minecraftforge.items.wrapper;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
public class InvWrapper implements IItemHandlerModifiable
{
public final IInventory inv;
public InvWrapper(IInventory inv)
{
this.inv = inv;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
InvWrapper that = (InvWrapper) o;
return inv.equals(that.inv);
}
@Override
public int hashCode()
{
return inv.hashCode();
}
@Override
public int getSlots()
{
return inv.getSizeInventory();
}
@Override
public ItemStack getStackInSlot(int slot)
{
return inv.getStackInSlot(slot);
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate)
{
if (stack == null)
return null;
if (!inv.isItemValidForSlot(slot, stack))
return stack;
ItemStack stackInSlot = inv.getStackInSlot(slot);
int m;
if (stackInSlot != null)
{
if (!ItemHandlerHelper.canItemStacksStack(stack, stackInSlot))
return stack;
m = Math.min(stack.getMaxStackSize(), inv.getInventoryStackLimit()) - stackInSlot.stackSize;
if (stack.stackSize <= m)
{
if (!simulate)
{
ItemStack copy = stack.copy();
copy.stackSize += stackInSlot.stackSize;
inv.setInventorySlotContents(slot, copy);
inv.markDirty();
}
return null;
}
else
{
if (!simulate)
{
ItemStack copy = stack.splitStack(m);
copy.stackSize += stackInSlot.stackSize;
inv.setInventorySlotContents(slot, copy);
inv.markDirty();
return stack;
}
else
{
stack.stackSize -= m;
return stack;
}
}
}
else
{
m = Math.min(stack.getMaxStackSize(), inv.getInventoryStackLimit());
if (m < stack.stackSize)
{
if (!simulate)
{
inv.setInventorySlotContents(slot, stack.splitStack(m));
inv.markDirty();
return stack;
}
else
{
stack.stackSize -= m;
return stack;
}
}
else
{
if (!simulate)
{
inv.setInventorySlotContents(slot, stack);
inv.markDirty();
}
return null;
}
}
}
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate)
{
if (amount == 0)
return null;
ItemStack stackInSlot = inv.getStackInSlot(slot);
if (stackInSlot == null)
return null;
if (simulate)
{
if (stackInSlot.stackSize < amount)
{
return stackInSlot.copy();
}
else
{
ItemStack copy = stackInSlot.copy();
copy.stackSize = amount;
return copy;
}
}
else
{
int m = Math.min(stackInSlot.stackSize, amount);
ItemStack decrStackSize = inv.decrStackSize(slot, m);
inv.markDirty();
return decrStackSize;
}
}
@Override
public void setStackInSlot(int slot, ItemStack stack)
{
inv.setInventorySlotContents(slot, stack);
}
}

View file

@ -0,0 +1,184 @@
package net.minecraftforge.items.wrapper;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
public class SidedInvWrapper implements IItemHandlerModifiable
{
protected final ISidedInventory inv;
protected final EnumFacing side;
public SidedInvWrapper(ISidedInventory inv, EnumFacing side)
{
this.inv = inv;
this.side = side;
}
public static int getSlot(ISidedInventory inv, int slot, EnumFacing side)
{
int[] slots = inv.getSlotsForFace(side);
if (slot < slots.length)
return slots[slot];
return -1;
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
SidedInvWrapper that = (SidedInvWrapper) o;
return inv.equals(that.inv) && side == that.side;
}
@Override
public int hashCode()
{
int result = inv.hashCode();
result = 31 * result + side.hashCode();
return result;
}
@Override
public int getSlots()
{
return inv.getSlotsForFace(side).length;
}
@Override
public ItemStack getStackInSlot(int slot)
{
int i = getSlot(inv, slot, side);
return i == -1 ? null : inv.getStackInSlot(i);
}
@Override
public ItemStack insertItem(int slot, ItemStack stack, boolean simulate)
{
if (stack == null)
return null;
int slot1 = getSlot(inv, slot, side);
if (slot1 == -1)
return stack;
if (!inv.isItemValidForSlot(slot1, stack) || !inv.canInsertItem(slot1, stack, side))
return stack;
ItemStack stackInSlot = inv.getStackInSlot(slot1);
int m;
if (stackInSlot != null)
{
if (!ItemHandlerHelper.canItemStacksStack(stack, stackInSlot))
return stack;
m = Math.min(stack.getMaxStackSize(), inv.getInventoryStackLimit()) - stackInSlot.stackSize;
if (stack.stackSize <= m)
{
if (!simulate)
{
ItemStack copy = stack.copy();
copy.stackSize += stackInSlot.stackSize;
inv.setInventorySlotContents(slot1, copy);
}
return null;
}
else
{
if (!simulate)
{
ItemStack copy = stack.splitStack(m);
copy.stackSize += stackInSlot.stackSize;
inv.setInventorySlotContents(slot1, copy);
return stack;
}
else
{
stack.stackSize -= m;
return stack;
}
}
}
else
{
m = Math.min(stack.getMaxStackSize(), inv.getInventoryStackLimit());
if (m < stack.stackSize)
{
if (!simulate)
{
inv.setInventorySlotContents(slot1, stack.splitStack(m));
return stack;
}
else
{
stack.stackSize -= m;
return stack;
}
}
else
{
if (!simulate)
inv.setInventorySlotContents(slot1, stack);
return null;
}
}
}
@Override
public void setStackInSlot(int slot, ItemStack stack)
{
inv.setInventorySlotContents(slot, stack);
}
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate)
{
if (amount == 0)
return null;
int slot1 = getSlot(inv, slot, side);
if (slot1 == -1)
return null;
ItemStack stackInSlot = inv.getStackInSlot(slot1);
if (stackInSlot == null)
return null;
if (!inv.canExtractItem(slot1, stackInSlot, side))
return null;
if (simulate)
{
if (stackInSlot.stackSize < amount)
{
return stackInSlot.copy();
}
else
{
ItemStack copy = stackInSlot.copy();
copy.stackSize = amount;
return copy;
}
}
else
{
int m = Math.min(stackInSlot.stackSize, amount);
return inv.decrStackSize(slot1, m);
}
}
}