From 0696662254ec0abf8113bec3340cd00d42c609ae Mon Sep 17 00:00:00 2001 From: yueh Date: Fri, 4 Dec 2015 00:31:25 +0100 Subject: [PATCH] Changed NetworkMonitor to update its cache. No longer invalidates and refresh it with every single change. This should now also ensure that updates are send exactly once per network instead of multiple times scaling up with the amount of chained networks. --- .../appeng/me/cache/GridStorageCache.java | 4 +- .../java/appeng/me/cache/NetworkMonitor.java | 365 ++++++++++++++---- .../appeng/parts/misc/PartStorageBus.java | 9 +- 3 files changed, 293 insertions(+), 85 deletions(-) diff --git a/src/main/java/appeng/me/cache/GridStorageCache.java b/src/main/java/appeng/me/cache/GridStorageCache.java index 1b6bc3e4..bdd30e25 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 d61fca35..104ea235 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 6b1eb379..745a3ad3 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 )