diff --git a/src/main/java/appeng/me/cache/GridStorageCache.java b/src/main/java/appeng/me/cache/GridStorageCache.java index 1b6bc3e4c..bdd30e25c 100644 --- a/src/main/java/appeng/me/cache/GridStorageCache.java +++ b/src/main/java/appeng/me/cache/GridStorageCache.java @@ -239,8 +239,8 @@ public class GridStorageCache implements IStorageGrid } } - this.itemMonitor.forceUpdate(); - this.fluidMonitor.forceUpdate(); + this.itemMonitor.forceUpdate( false ); + this.fluidMonitor.forceUpdate( false ); tracker.applyChanges(); } diff --git a/src/main/java/appeng/me/cache/NetworkMonitor.java b/src/main/java/appeng/me/cache/NetworkMonitor.java index d61fca357..104ea2356 100644 --- a/src/main/java/appeng/me/cache/NetworkMonitor.java +++ b/src/main/java/appeng/me/cache/NetworkMonitor.java @@ -21,41 +21,321 @@ 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.Nonnull; +import javax.annotation.Nullable; + +import com.google.common.collect.ImmutableList; + +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 = new LinkedList>(); - 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 = new HashMap, Object>(); + private boolean sendEvent = false; + private boolean hasChanged = false; + 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(); } - void forceUpdate() + @Override + public void addListener( final IMEMonitorHandlerReceiver l, final Object verificationToken ) { - this.hasChanged = true; + 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 ) + { + 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 ); + } + + private void updateCache( Iterable changes, boolean add ) + { + for( final T changedItem : changes ) + { + T difference = changedItem; + + if( !add && changedItem != null ) + { + difference = changedItem.copy(); + difference.setStackSize( -changedItem.getStackSize() ); + } + + this.cachedList.add( difference ); + } + } + + 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.updateCache( changes, add ); + 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( boolean reset ) + { + if( reset ) + { + this.hasChanged = true; + this.cachedList.resetStatus(); + this.getAvailableItems( this.cachedList ); + } final Iterator, Object>> i = this.getListeners(); + while( i.hasNext() ) { final Entry, Object> o = i.next(); @@ -81,77 +361,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 6b1eb3794..745a3ad33 100644 --- a/src/main/java/appeng/parts/misc/PartStorageBus.java +++ b/src/main/java/appeng/parts/misc/PartStorageBus.java @@ -378,10 +378,11 @@ public class PartStorageBus extends PartUpgradeable implements IGridTickable, IC final IMEInventory out = this.getInternalHandler(); - if( this.monitor != null ) - { - this.monitor.onTick(); - } + // TODO: evaluate, if this is really (not) necessary. + // if( this.monitor != null ) + // { + // this.monitor.onTick(); + // } IItemList after = AEApi.instance().storage().createItemList(); if( out != null )