package appeng.me.storage; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.TreeMap; import appeng.api.config.AccessRestriction; import appeng.api.config.Actionable; import appeng.api.config.SecurityPermissions; import appeng.api.networking.IGrid; import appeng.api.networking.IGridNode; import appeng.api.networking.security.BaseActionSource; import appeng.api.networking.security.ISecurityGrid; import appeng.api.networking.security.MachineSource; import appeng.api.networking.security.PlayerSource; import appeng.api.storage.IMEInventoryHandler; import appeng.api.storage.StorageChannel; import appeng.api.storage.data.IAEStack; import appeng.api.storage.data.IItemList; import appeng.me.cache.SecurityCache; import appeng.util.ItemSorters; public class NetworkInventoryHandler> implements IMEInventoryHandler { private final static Comparator prioritySorter = new Comparator() { @Override public int compare(Integer o1, Integer o2) { return ItemSorters.compareInt( o2, o1 ); } }; final StorageChannel myChannel; final SecurityCache security; // final TreeMultimap> prorityInventory; final TreeMap>> prorityInventory; public NetworkInventoryHandler(StorageChannel chan, SecurityCache security) { myChannel = chan; this.security = security; prorityInventory = new TreeMap( prioritySorter ); // TreeMultimap.create( prioritySorter, hashSorter ); } public void addNewStorage(IMEInventoryHandler h) { int priority = h.getPriority(); List> list = prorityInventory.get( priority ); if ( list == null ) prorityInventory.put( priority, list = new ArrayList() ); list.add( h ); } static int currentPass = 0; int myPass = 0; static final ThreadLocal depthMod = new ThreadLocal(); static final ThreadLocal depthSim = new ThreadLocal(); private LinkedList getDepth(Actionable type) { ThreadLocal depth = type == Actionable.MODULATE ? depthMod : depthSim; LinkedList s = depth.get(); if ( s == null ) depth.set( s = new LinkedList() ); return s; } private boolean diveList(NetworkInventoryHandler networkInventoryHandler, Actionable type) { LinkedList cDepth = getDepth( type ); if ( cDepth.contains( networkInventoryHandler ) ) return true; cDepth.push( this ); return false; } private boolean diveIteration(NetworkInventoryHandler networkInventoryHandler, Actionable type) { LinkedList cDepth = getDepth( type ); if ( cDepth.isEmpty() ) { currentPass++; myPass = currentPass; } else { if ( currentPass == myPass ) return true; else myPass = currentPass; } cDepth.push( this ); return false; } private void surface(NetworkInventoryHandler networkInventoryHandler, Actionable type) { if ( getDepth( type ).pop() != this ) throw new RuntimeException( "Invalid Access to Networked Storage API detected." ); } private boolean testPermission(BaseActionSource src, SecurityPermissions permission) { if ( src.isPlayer() ) { if ( !security.hasPermission( ((PlayerSource) src).player, permission ) ) return true; } else if ( src.isMachine() ) { if ( security.isAvailable() ) { IGridNode n = ((MachineSource) src).via.getActionableNode(); if ( n == null ) return true; IGrid gn = n.getGrid(); if ( gn != security.myGrid ) { int playerID = -1; ISecurityGrid sg = gn.getCache( ISecurityGrid.class ); playerID = sg.getOwner(); if ( !security.hasPermission( playerID, permission ) ) return true; } } } return false; } @Override public T injectItems(T input, Actionable type, BaseActionSource src) { if ( diveList( this, type ) ) return input; if ( testPermission( src, SecurityPermissions.INJECT ) ) { surface( this, type ); return input; } Iterator>> i = prorityInventory.values().iterator();// asMap().entrySet().iterator(); while (i.hasNext()) { List> invList = i.next(); Iterator> ii = invList.iterator(); while (ii.hasNext() && input != null) { IMEInventoryHandler inv = ii.next(); if ( inv.validForPass( 1 ) && inv.canAccept( input ) && (inv.isPrioritized( input ) || inv.extractItems( input, Actionable.SIMULATE, src ) != null) ) input = inv.injectItems( input, type, src ); } ii = invList.iterator(); while (ii.hasNext() && input != null) { IMEInventoryHandler inv = ii.next(); if ( inv.validForPass( 2 ) && inv.canAccept( input ) )// ignore crafting on the second pass. input = inv.injectItems( input, type, src ); } } surface( this, type ); return input; } @Override public T extractItems(T request, Actionable mode, BaseActionSource src) { if ( diveList( this, mode ) ) return null; if ( testPermission( src, SecurityPermissions.EXTRACT ) ) { surface( this, mode ); return null; } Iterator>> i = prorityInventory.descendingMap().values().iterator();// prorityInventory.asMap().descendingMap().entrySet().iterator(); T output = request.copy(); request = request.copy(); output.setStackSize( 0 ); long req = request.getStackSize(); while (i.hasNext()) { List> invList = i.next(); Iterator> ii = invList.iterator(); while (ii.hasNext() && output.getStackSize() < req) { IMEInventoryHandler inv = ii.next(); request.setStackSize( req - output.getStackSize() ); output.add( inv.extractItems( request, mode, src ) ); } } surface( this, mode ); if ( output.getStackSize() <= 0 ) return null; return output; } @Override public IItemList getAvailableItems(IItemList out) { if ( diveIteration( this, Actionable.SIMULATE ) ) return out; // for (Entry> h : prorityInventory.entries()) for (List> i : prorityInventory.values()) for (IMEInventoryHandler j : i) out = j.getAvailableItems( out ); surface( this, Actionable.SIMULATE ); return out; } @Override public StorageChannel getChannel() { return myChannel; } @Override public AccessRestriction getAccess() { return AccessRestriction.READ_WRITE; } @Override public boolean isPrioritized(T input) { return false; } @Override public boolean canAccept(T input) { return true; } @Override public int getPriority() { return 0; } @Override public int getSlot() { return 0; } @Override public boolean validForPass(int i) { return true; } }