diff --git a/src/main/java/appeng/me/cache/NetworkMonitor.java b/src/main/java/appeng/me/cache/NetworkMonitor.java index d61fca35..e423dea5 100644 --- a/src/main/java/appeng/me/cache/NetworkMonitor.java +++ b/src/main/java/appeng/me/cache/NetworkMonitor.java @@ -21,34 +21,295 @@ package appeng.me.cache; import java.util.Collection; import java.util.Deque; +import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedList; +import java.util.Map; import java.util.Map.Entry; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import appeng.api.config.AccessRestriction; +import appeng.api.config.Actionable; import appeng.api.networking.events.MENetworkStorageEvent; import appeng.api.networking.security.BaseActionSource; import appeng.api.storage.IMEInventoryHandler; +import appeng.api.storage.IMEMonitor; import appeng.api.storage.IMEMonitorHandlerReceiver; -import appeng.api.storage.MEMonitorHandler; import appeng.api.storage.StorageChannel; import appeng.api.storage.data.IAEStack; import appeng.api.storage.data.IItemList; import appeng.me.storage.ItemWatcher; -public class NetworkMonitor> extends MEMonitorHandler +public class NetworkMonitor> implements IMEMonitor { + @Nonnull + private static final Deque> GLOBAL_DEPTH = Lists.newLinkedList(); - private static final Deque> DEPTH = new LinkedList>(); + @Nonnull private final GridStorageCache myGridCache; + @Nonnull private final StorageChannel myChannel; + @Nonnull + private final IItemList cachedList; + @Nonnull + private final Map, Object> listeners; + private boolean sendEvent = false; + private boolean hasChanged = false; + @Nonnegative + private int localDepthSemaphore = 0; public NetworkMonitor( final GridStorageCache cache, final StorageChannel chan ) { - super( null, chan ); this.myGridCache = cache; this.myChannel = chan; + this.cachedList = (IItemList) chan.createList(); + this.listeners = new HashMap, Object>(); + } + + @Override + public void addListener( final IMEMonitorHandlerReceiver l, final Object verificationToken ) + { + this.listeners.put( l, verificationToken ); + } + + @Override + public boolean canAccept( final T input ) + { + return this.getHandler().canAccept( input ); + } + + @Override + public T extractItems( final T request, final Actionable mode, final BaseActionSource src ) + { + if( mode == Actionable.SIMULATE ) + { + return this.getHandler().extractItems( request, mode, src ); + } + + localDepthSemaphore++; + final T leftover = this.getHandler().extractItems( request, mode, src ); + localDepthSemaphore--; + + if( localDepthSemaphore == 0 ) + { + this.monitorDifference( request.copy(), leftover, true, src ); + } + + return leftover; + } + + @Override + public AccessRestriction getAccess() + { + return this.getHandler().getAccess(); + } + + @Override + public IItemList getAvailableItems( final IItemList out ) + { + return this.getHandler().getAvailableItems( out ); + } + + @Override + public StorageChannel getChannel() + { + return this.getHandler().getChannel(); + } + + @Override + public int getPriority() + { + return this.getHandler().getPriority(); + } + + @Override + public int getSlot() + { + return this.getHandler().getSlot(); + } + + @Nonnull + @Override + public IItemList getStorageList() + { + if( this.hasChanged ) + { + this.hasChanged = false; + this.cachedList.resetStatus(); + return this.getAvailableItems( this.cachedList ); + } + + return this.cachedList; + } + + @Override + public T injectItems( final T input, final Actionable mode, final BaseActionSource src ) + { + if( mode == Actionable.SIMULATE ) + { + return this.getHandler().injectItems( input, mode, src ); + } + + localDepthSemaphore++; + final T leftover = this.getHandler().injectItems( input, mode, src ); + localDepthSemaphore--; + + if( localDepthSemaphore == 0 ) + { + this.monitorDifference( input.copy(), leftover, false, src ); + } + + return leftover; + } + + @Override + public boolean isPrioritized( final T input ) + { + return this.getHandler().isPrioritized( input ); + } + + @Override + public void removeListener( final IMEMonitorHandlerReceiver l ) + { + this.listeners.remove( l ); + } + + @Override + public boolean validForPass( final int i ) + { + return this.getHandler().validForPass( i ); + } + + @Nullable + @SuppressWarnings( "unchecked" ) + private IMEInventoryHandler getHandler() + { + switch( this.myChannel ) + { + case ITEMS: + return (IMEInventoryHandler) this.myGridCache.getItemInventoryHandler(); + case FLUIDS: + return (IMEInventoryHandler) this.myGridCache.getFluidInventoryHandler(); + default: + } + return null; + } + + private Iterator, Object>> getListeners() + { + return this.listeners.entrySet().iterator(); + } + + private T monitorDifference( final IAEStack original, final T leftOvers, final boolean extraction, final BaseActionSource src ) + { + final T diff = (T) original.copy(); + + if( extraction ) + { + diff.setStackSize( leftOvers == null ? 0 : -leftOvers.getStackSize() ); + } + else if( leftOvers != null ) + { + diff.decStackSize( leftOvers.getStackSize() ); + } + + if( diff.getStackSize() != 0 ) + { + this.postChangesToListeners( ImmutableList.of( diff ), src ); + } + + return leftOvers; + } + + private void notifyListenersOfChange( final Iterable diff, final BaseActionSource src ) + { + this.hasChanged = true; + final Iterator, Object>> i = this.getListeners(); + + while( i.hasNext() ) + { + final Entry, Object> o = i.next(); + final IMEMonitorHandlerReceiver receiver = o.getKey(); + if( receiver.isValid( o.getValue() ) ) + { + receiver.postChange( this, diff, src ); + } + else + { + i.remove(); + } + } + } + + private void postChangesToListeners( final Iterable changes, final BaseActionSource src ) + { + this.postChange( true, changes, src ); + } + + protected void postChange( final boolean add, final Iterable changes, final BaseActionSource src ) + { + if( localDepthSemaphore > 0 || GLOBAL_DEPTH.contains( this ) ) + { + return; + } + + GLOBAL_DEPTH.push( this ); + localDepthSemaphore++; + + this.sendEvent = true; + + this.notifyListenersOfChange( changes, src ); + + for( final T changedItem : changes ) + { + T difference = changedItem; + + if( !add && changedItem != null ) + { + difference = changedItem.copy(); + difference.setStackSize( -changedItem.getStackSize() ); + } + + if( this.myGridCache.getInterestManager().containsKey( changedItem ) ) + { + final Collection list = this.myGridCache.getInterestManager().get( changedItem ); + + if( !list.isEmpty() ) + { + IAEStack fullStack = this.getStorageList().findPrecise( changedItem ); + + if( fullStack == null ) + { + fullStack = changedItem.copy(); + fullStack.setStackSize( 0 ); + } + + this.myGridCache.getInterestManager().enableTransactions(); + + for( final ItemWatcher iw : list ) + { + iw.getHost().onStackChange( this.getStorageList(), fullStack, difference, src, this.getChannel() ); + } + + this.myGridCache.getInterestManager().disableTransactions(); + } + } + } + + final NetworkMonitor last = GLOBAL_DEPTH.pop(); + localDepthSemaphore--; + + if( last != this ) + { + throw new IllegalStateException( "Invalid Access to Networked Storage API detected." ); + } } void forceUpdate() @@ -81,77 +342,4 @@ public class NetworkMonitor> extends MEMonitorHandler } } - @Override - protected IMEInventoryHandler getHandler() - { - switch( this.myChannel ) - { - case ITEMS: - return this.myGridCache.getItemInventoryHandler(); - case FLUIDS: - return this.myGridCache.getFluidInventoryHandler(); - default: - } - return null; - } - - @Override - protected void postChangesToListeners( final Iterable changes, final BaseActionSource src ) - { - this.postChange( true, changes, src ); - } - - protected void postChange( final boolean add, final Iterable changes, final BaseActionSource src ) - { - if( DEPTH.contains( this ) ) - { - return; - } - - DEPTH.push( this ); - - this.sendEvent = true; - this.notifyListenersOfChange( changes, src ); - - final IItemList myStorageList = this.getStorageList(); - - for( final T changedItem : changes ) - { - T difference = changedItem; - - if( !add && changedItem != null ) - { - ( difference = changedItem.copy() ).setStackSize( -changedItem.getStackSize() ); - } - - if( this.myGridCache.getInterestManager().containsKey( changedItem ) ) - { - final Collection list = this.myGridCache.getInterestManager().get( changedItem ); - if( !list.isEmpty() ) - { - IAEStack fullStack = myStorageList.findPrecise( changedItem ); - if( fullStack == null ) - { - fullStack = changedItem.copy(); - fullStack.setStackSize( 0 ); - } - - this.myGridCache.getInterestManager().enableTransactions(); - - for( final ItemWatcher iw : list ) - { - iw.getHost().onStackChange( myStorageList, fullStack, difference, src, this.getChannel() ); - } - - this.myGridCache.getInterestManager().disableTransactions(); - } - } - } - - final NetworkMonitor last = DEPTH.pop(); - if( last != this ) - { - throw new IllegalStateException( "Invalid Access to Networked Storage API detected." ); - } - } } diff --git a/src/main/java/appeng/parts/misc/PartStorageBus.java b/src/main/java/appeng/parts/misc/PartStorageBus.java index 6b1eb379..68950381 100644 --- a/src/main/java/appeng/parts/misc/PartStorageBus.java +++ b/src/main/java/appeng/parts/misc/PartStorageBus.java @@ -412,16 +412,6 @@ public class PartStorageBus extends PartUpgradeable implements IGridTickable, IC return this.handler; } - try - { - // force grid to update handlers... - this.getProxy().getGrid().postEvent( new MENetworkCellArrayUpdate() ); - } - catch( final GridAccessException e ) - { - // :3 - } - this.handlerHash = newHandlerHash; this.handler = null; this.monitor = null; @@ -504,6 +494,16 @@ public class PartStorageBus extends PartUpgradeable implements IGridTickable, IC } } + try + { + // force grid to update handlers... + this.getProxy().getGrid().postEvent( new MENetworkCellArrayUpdate() ); + } + catch( final GridAccessException e ) + { + // :3 + } + return this.handler; }