package appeng.me.storage; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.TreeMap; import net.minecraft.item.ItemStack; import appeng.api.AEApi; import appeng.api.config.AccessRestriction; import appeng.api.config.Actionable; import appeng.api.config.StorageFilter; import appeng.api.networking.security.BaseActionSource; import appeng.api.networking.ticking.TickRateModulation; import appeng.api.storage.IMEInventory; import appeng.api.storage.IMEMonitor; import appeng.api.storage.IMEMonitorHandlerReceiver; import appeng.api.storage.StorageChannel; import appeng.api.storage.data.IAEItemStack; import appeng.api.storage.data.IItemList; import appeng.util.InventoryAdaptor; import appeng.util.Platform; import appeng.util.inv.ItemSlot; public class MEMonitorIInventory implements IMEInventory, IMEMonitor { class CachedItemStack { public CachedItemStack(ItemStack is) { if ( is == null ) { itemStack = null; aeStack = null; } else { itemStack = is.copy(); aeStack = AEApi.instance().storage().createItemStack( is ); } } final ItemStack itemStack; final IAEItemStack aeStack; } final InventoryAdaptor adaptor; final TreeMap memory; final IItemList list = AEApi.instance().storage().createItemList(); final HashMap, Object> listeners = new HashMap(); public BaseActionSource mySource; public StorageFilter mode = StorageFilter.EXTRACTABLE_ONLY; @Override public void addListener(IMEMonitorHandlerReceiver l, Object verificationToken) { listeners.put( l, verificationToken ); } @Override public void removeListener(IMEMonitorHandlerReceiver l) { listeners.remove( l ); } public MEMonitorIInventory(InventoryAdaptor adaptor) { this.adaptor = adaptor; memory = new TreeMap(); } @Override public IAEItemStack injectItems(IAEItemStack input, Actionable type, BaseActionSource src) { ItemStack out = null; if ( type == Actionable.SIMULATE ) out = adaptor.simulateAdd( input.getItemStack() ); else out = adaptor.addItems( input.getItemStack() ); onTick(); if ( out == null ) return null; // better then doing construction from scratch :3 IAEItemStack o = input.copy(); o.setStackSize( out.stackSize ); return o; } @Override public AccessRestriction getAccess() { return AccessRestriction.READ_WRITE; } @Override public boolean isPrioritized(IAEItemStack input) { return false; } @Override public boolean canAccept(IAEItemStack input) { return true; } @Override public int getPriority() { return 0; } @Override public int getSlot() { return 0; } @Override public IItemList getStorageList() { return list; } @Override public IItemList getAvailableItems(IItemList out) { for (CachedItemStack is : memory.values()) out.addStorage( is.aeStack ); return out; } @Override public IAEItemStack extractItems(IAEItemStack request, Actionable type, BaseActionSource src) { ItemStack out = null; if ( type == Actionable.SIMULATE ) out = adaptor.simulateRemove( (int) request.getStackSize(), request.getItemStack(), null ); else out = adaptor.removeItems( (int) request.getStackSize(), request.getItemStack(), null ); if ( out == null ) return null; // better then doing construction from scratch :3 IAEItemStack o = request.copy(); o.setStackSize( out.stackSize ); onTick(); return o; } public TickRateModulation onTick() { boolean changed = false; LinkedList changes = new LinkedList(); int high = 0; list.resetStatus(); for (ItemSlot is : adaptor) { CachedItemStack old = memory.get( is.slot ); high = Math.max( high, is.slot ); ItemStack newIS = is.isExtractable == false && mode == StorageFilter.EXTRACTABLE_ONLY ? null : is.getItemStack(); ItemStack oldIS = old == null ? null : old.itemStack; if ( isDifferent( newIS, oldIS ) ) { CachedItemStack cis = new CachedItemStack( is.getItemStack() ); memory.put( is.slot, cis ); if ( old != null && old.aeStack != null ) { old.aeStack.setStackSize( -old.aeStack.getStackSize() ); changes.add( old.aeStack ); } if ( cis.aeStack != null ) { changes.add( cis.aeStack ); list.add( cis.aeStack ); } changed = true; } else { int newSize = (newIS == null ? 0 : newIS.stackSize); int diff = newSize - (oldIS == null ? 0 : oldIS.stackSize); IAEItemStack stack = (old == null || old.aeStack == null ? AEApi.instance().storage().createItemStack( newIS ) : old.aeStack.copy()); if ( stack != null ) { stack.setStackSize( newSize ); list.add( stack ); } if ( diff != 0 && stack != null ) { CachedItemStack cis = new CachedItemStack( is.getItemStack() ); memory.put( is.slot, cis ); IAEItemStack a = stack.copy(); a.setStackSize( diff ); changes.add( a ); changed = true; } } } // detect dropped items; should fix non IISided Inventory Changes. NavigableMap end = memory.tailMap( high, false ); if ( !end.isEmpty() ) { for (CachedItemStack cis : end.values()) { if ( cis != null && cis.aeStack != null ) { IAEItemStack a = cis.aeStack.copy(); a.setStackSize( -a.getStackSize() ); changes.add( a ); changed = true; } } end.clear(); } if ( !changes.isEmpty() ) postDifference( changes ); return changed ? TickRateModulation.URGENT : TickRateModulation.SLOWER; } private boolean isDifferent(ItemStack a, ItemStack b) { if ( a == b && b == null ) return false; if ( (a == null && b != null) || (a != null && b == null) ) return true; return !Platform.isSameItemPrecise( a, b ); } private void postDifference(Iterable a) { // AELog.info( a.getItemStack().getUnlocalizedName() + " @ " + a.getStackSize() ); if ( a != null ) { Iterator, Object>> i = listeners.entrySet().iterator(); while (i.hasNext()) { Entry, Object> l = i.next(); IMEMonitorHandlerReceiver key = l.getKey(); if ( key.isValid( l.getValue() ) ) key.postChange( this, a, mySource ); else i.remove(); } } } @Override public StorageChannel getChannel() { return StorageChannel.ITEMS; } @Override public boolean validForPass(int i) { return true; } }