package appeng.me.storage; import appeng.api.config.Actionable; import appeng.api.exceptions.AppEngException; import appeng.api.implementations.items.IStorageCell; import appeng.api.networking.security.IActionSource; import appeng.api.storage.ICellInventory; import appeng.api.storage.ISaveProvider; import appeng.api.storage.IStorageChannel; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IAEStack; import appeng.core.AEConfig; import appeng.core.AELog; import appeng.util.item.AEStack; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; public class BasicCellInventory> extends AbstractCellInventory { private final IStorageChannel channel; private BasicCellInventory(final IStorageCell cellType, final ItemStack o, final ISaveProvider container) { super(cellType, o, container); this.channel = cellType.getChannel(); } public static > ICellInventory createInventory(final ItemStack o, final ISaveProvider container) { try { if (o == null) { throw new AppEngException("ItemStack was used as a cell, but was not a cell!"); } final Item type = o.getItem(); final IStorageCell cellType; if (type instanceof IStorageCell) { cellType = (IStorageCell) type; } else { throw new AppEngException("ItemStack was used as a cell, but was not a cell!"); } if (!cellType.isStorageCell(o)) { throw new AppEngException("ItemStack was used as a cell, but was not a cell!"); } return new BasicCellInventory(cellType, o, container); } catch (final AppEngException e) { AELog.error(e); return null; } } public static > boolean isCellOfType(final ItemStack input, IStorageChannel channel) { final IStorageCell type = getStorageCell(input); return type != null && type.getChannel() == channel; } public static boolean isCell(final ItemStack input) { return getStorageCell(input) != null; } private boolean isStorageCell(final T input) { if (input instanceof IAEItemStack) { final IAEItemStack stack = (IAEItemStack) input; final IStorageCell type = getStorageCell(stack.getDefinition()); return type != null && !type.storableInStorageCell(); } return false; } private static IStorageCell getStorageCell(final ItemStack input) { if (input != null) { final Item type = input.getItem(); if (type instanceof IStorageCell) { return (IStorageCell) type; } } return null; } @SuppressWarnings({"rawtypes", "unchecked"}) private static boolean isCellEmpty(ICellInventory inv) { if (inv != null) { return inv.getAvailableItems(inv.getChannel().createList()).isEmpty(); } return true; } @Override public T injectItems(T input, Actionable mode, IActionSource src) { if (input == null) { return null; } if (input.getStackSize() == 0) { return null; } if (this.cellType.isBlackListed(this.getItemStack(), input)) { return input; } // This is slightly hacky as it expects a read-only access, but fine for now. // TODO: Guarantee a read-only access. E.g. provide an isEmpty() method and ensure CellInventory does not write // any NBT data for empty cells instead of relying on an empty IItemContainer if (this.isStorageCell(input)) { final ICellInventory meInventory = createInventory(((IAEItemStack) input).createItemStack(), null); if (!isCellEmpty(meInventory)) { return input; } } final T l = this.getCellItems().findPrecise(input); if (l != null) { final long remainingItemCount = this.getRemainingItemCount(); if (remainingItemCount <= 0) { return input; } if (input.getStackSize() > remainingItemCount) { final T r = input.copy(); r.setStackSize(r.getStackSize() - remainingItemCount); if (mode == Actionable.MODULATE) { l.setStackSize(l.getStackSize() + remainingItemCount); this.saveChanges(); } return r; } else { if (mode == Actionable.MODULATE) { l.setStackSize(l.getStackSize() + input.getStackSize()); this.saveChanges(); } return null; } } if (this.canHoldNewItem()) // room for new type, and for at least one item! { final long remainingItemCount = this.getRemainingItemCount() - (long) this.getBytesPerType() * this.itemsPerByte; if (remainingItemCount > 0) { if (input.getStackSize() > remainingItemCount) { final T toReturn = input.copy(); toReturn.setStackSize(input.getStackSize() - remainingItemCount); if (mode == Actionable.MODULATE) { final T toWrite = input.copy(); toWrite.setStackSize(remainingItemCount); this.cellItems.add(toWrite); this.saveChanges(); } return toReturn; } if (mode == Actionable.MODULATE) { this.cellItems.add(input); this.saveChanges(); } return null; } } return input; } @Override public T extractItems(T request, Actionable mode, IActionSource src) { if (request == null) { return null; } final long size = Math.min(Integer.MAX_VALUE, request.getStackSize()); T Results = null; final T l = this.getCellItems().findPrecise(request); if (l != null) { Results = l.copy(); if (l.getStackSize() <= size) { Results.setStackSize(l.getStackSize()); if (mode == Actionable.MODULATE) { l.setStackSize(0); this.saveChanges(); } } else { Results.setStackSize(size); if (mode == Actionable.MODULATE) { l.setStackSize(l.getStackSize() - size); this.saveChanges(); } } } return Results; } @Override public IStorageChannel getChannel() { return this.channel; } @Override protected boolean loadCellItem(NBTTagCompound compoundTag, long stackSize) { // Now load the item stack final T t; try { t = this.getChannel().createFromNBT(compoundTag); if (t == null) { AELog.warn("Removing item " + compoundTag + " from storage cell because the associated item type couldn't be found."); return false; } } catch (Throwable ex) { if (AEConfig.instance().isRemoveCrashingItemsOnLoad()) { AELog.warn(ex, "Removing item " + compoundTag + " from storage cell because loading the ItemStack crashed."); return false; } throw ex; } t.setStackSize(stackSize); t.setCraftable(false); if (stackSize > 0) { this.cellItems.add(t); } return true; } }