package appeng.me.storage; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; 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; 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 int remainingItemCount = (int) this.getRemainingItemCount() - 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, int 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 ); if( stackSize > 0 ) { this.cellItems.add( t ); } return true; } }