Java tutorial
package com.teambr.bookshelf.common.items; import com.teambr.bookshelf.common.container.IInventoryCallback; import net.minecraft.inventory.ItemStackHelper; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.NonNullList; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.ItemHandlerHelper; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; /** * This file was created for Bookshelf * <p> * Bookshelf is licensed under the * Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License: * http://creativecommons.org/licenses/by-nc-sa/4.0/ * * @author Paul Davis - pauljoda * @since 11/13/17 */ public abstract class InventoryHandlerItem implements IItemHandlerModifiable, ICapabilityProvider { // Variables private ItemStack heldStack; // A list to hold all callback objects private List<IInventoryCallback> callBacks = new ArrayList<>(); // List of Inventory contents public NonNullList<ItemStack> inventoryContents = NonNullList.withSize(getInventorySize(), ItemStack.EMPTY); /** * Creates a handler with given stack * * @param stack Stack to attach to */ public InventoryHandlerItem(ItemStack stack, NBTTagCompound compound) { heldStack = stack; readFromNBT(compound); checkStackTag(); } /******************************************************************************************************************* * Abstract Methods * *******************************************************************************************************************/ /** * The initial size of the inventory * * @return How big to make the inventory on creation */ protected abstract int getInventorySize(); /** * Used to define if an item is valid for a slot * * @param index The slot id * @param stack The stack to check * @return True if you can put this there */ protected abstract boolean isItemValidForSlot(int index, ItemStack stack); /******************************************************************************************************************* * InventoryHandler * *******************************************************************************************************************/ /** * Add a callback to this inventory * * @param iInventoryCallback The callback you wish to add * @return This object, to enable chaining */ public InventoryHandlerItem addCallback(IInventoryCallback iInventoryCallback) { callBacks.add(iInventoryCallback); return this; } /** * Called when the inventory has a change * * @param slot The slot that changed */ protected void onInventoryChanged(int slot) { callBacks.forEach((IInventoryCallback callback) -> callback.onInventoryChanged(this, slot)); writeToNBT(heldStack.getTagCompound()); } /** * Used to copy from an existing inventory * * @param inventory The inventory to copy from */ public void copyFrom(IItemHandler inventory) { for (int i = 0; i < inventory.getSlots(); i++) { if (i < inventoryContents.size()) { ItemStack stack = inventory.getStackInSlot(i); if (!stack.isEmpty()) inventoryContents.set(i, stack.copy()); else inventoryContents.set(i, ItemStack.EMPTY); } } } /** * Makes sure this slot is within our range * * @param slot Which slot */ protected boolean isValidSlot(int slot) { return slot > 0 || slot <= inventoryContents.size(); } /** * Makes sure we always have a valid tag */ protected void checkStackTag() { // Give the stack a tag if (!heldStack.hasTagCompound()) { heldStack.setTagCompound(new NBTTagCompound()); writeToNBT(heldStack.getTagCompound()); } else readFromNBT(heldStack.getTagCompound()); } /** * Used to save the inventory to an NBT tag * * @param compound The tag to save to */ public NBTTagCompound writeToNBT(NBTTagCompound compound) { ItemStackHelper.saveAllItems(compound, inventoryContents); return compound; } /** * Used to read the inventory from an NBT tag compound * * @param compound The tag to read from */ public void readFromNBT(NBTTagCompound compound) { if (compound != null) ItemStackHelper.loadAllItems(compound, inventoryContents); } /******************************************************************************************************************* * IItemHandlerModifiable * *******************************************************************************************************************/ /** * 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. **/ @Override public void setStackInSlot(int slot, ItemStack stack) { if (!isValidSlot(slot)) return; if (ItemStack.areItemStacksEqual(this.inventoryContents.get(slot), stack)) return; this.inventoryContents.set(slot, stack); onInventoryChanged(slot); } /******************************************************************************************************************* * IItemHandler * *******************************************************************************************************************/ /** * Returns the number of slots available * * @return The number of slots available **/ @Override public int getSlots() { checkStackTag(); return inventoryContents.size(); } /** * Returns the ItemStack in a given slot. * <p> * The result's stack size may be greater than the itemstacks max size. * <p> * 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> * <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 not be null. **/ @Override @Nonnull public ItemStack getStackInSlot(int slot) { checkStackTag(); if (!isValidSlot(slot)) return ItemStack.EMPTY; return inventoryContents.get(slot); } /** * Inserts an ItemStack into the given slot and return the remainder. * The ItemStack should not be modified in this function! * 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). * May be the same as the input ItemStack if unchanged, otherwise a new ItemStack. **/ @Nonnull @Override public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) { checkStackTag(); if (isItemValidForSlot(slot, stack) || stack.isEmpty() || !isValidSlot(slot)) return stack; ItemStack existing = this.inventoryContents.get(slot); int limit = getSlotLimit(slot); if (!existing.isEmpty()) { if (!ItemHandlerHelper.canItemStacksStack(stack, existing)) return stack; limit -= existing.getCount(); } if (limit <= 0) return stack; boolean reachedLimit = stack.getCount() > limit; if (!simulate) { if (existing.isEmpty()) { this.inventoryContents.set(slot, reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, limit) : stack); } else { existing.setCount(existing.getCount() + (reachedLimit ? limit : stack.getCount())); } onInventoryChanged(slot); } return reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, stack.getCount() - limit) : ItemStack.EMPTY; } /** * 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 **/ @Nonnull @Override public ItemStack extractItem(int slot, int amount, boolean simulate) { checkStackTag(); if (amount == 0) return ItemStack.EMPTY; if (!isValidSlot(slot)) return ItemStack.EMPTY; ItemStack existing = this.inventoryContents.get(slot); if (existing.isEmpty()) return ItemStack.EMPTY; int toExtract = Math.min(amount, existing.getMaxStackSize()); if (existing.getCount() <= toExtract) { if (!simulate) { this.inventoryContents.set(slot, ItemStack.EMPTY); onInventoryChanged(slot); } return existing; } else { if (!simulate) { this.inventoryContents.set(slot, ItemHandlerHelper.copyStackWithSize(existing, existing.getCount() - toExtract)); onInventoryChanged(slot); } return ItemHandlerHelper.copyStackWithSize(existing, toExtract); } } /** * Retrieves the maximum stack size allowed to exist in the given slot. * * @param slot Slot to query. * @return The maximum stack size allowed in the slot. */ @Override public int getSlotLimit(int slot) { return 64; } /******************************************************************************************************************* * ICapabilityProvider * *******************************************************************************************************************/ /** * Determines if this object has support for the capability in question on the specific side. * The return value of this MIGHT change during runtime if this object gains or looses support * for a capability. * <p> * Example: * A Pipe getting a cover placed on one side causing it loose the Inventory attachment function for that side. * <p> * This is a light weight version of getCapability, intended for metadata uses. * * @param capability The capability to check * @param facing The Side to check from: * CAN BE NULL. Null is defined to represent 'internal' or 'self' * @return True if this object supports the capability. */ @Override public boolean hasCapability(@Nonnull Capability<?> capability, @Nullable EnumFacing facing) { return capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY; } /** * Retrieves the handler for the capability requested on the specific side. * The return value CAN be null if the object does not support the capability. * The return value CAN be the same for multiple faces. * * @param capability The capability to check * @param facing The Side to check from: * CAN BE NULL. Null is defined to represent 'internal' or 'self' * @return The requested capability. Returns null when {@link #hasCapability(Capability, EnumFacing)} would return false. */ @SuppressWarnings("unchecked") @Nullable @Override public <T> T getCapability(@Nonnull Capability<T> capability, @Nullable EnumFacing facing) { if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return (T) this; return null; } }