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.
This commit is contained in:
yueh 2015-12-04 00:31:25 +01:00
parent 11f92f6888
commit 0696662254
3 changed files with 293 additions and 85 deletions

View File

@ -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();
}

View File

@ -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<T extends IAEStack<T>> extends MEMonitorHandler<T>
public class NetworkMonitor<T extends IAEStack<T>> implements IMEMonitor<T>
{
@Nonnull
private static final Deque<NetworkMonitor<?>> GLOBAL_DEPTH = new LinkedList<NetworkMonitor<?>>();
private static final Deque<NetworkMonitor<?>> DEPTH = new LinkedList<NetworkMonitor<?>>();
@Nonnull
private final GridStorageCache myGridCache;
@Nonnull
private final StorageChannel myChannel;
@Nonnull
private final IItemList<T> cachedList;
@Nonnull
private final Map<IMEMonitorHandlerReceiver<T>, Object> listeners = new HashMap<IMEMonitorHandlerReceiver<T>, 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<T>) chan.createList();
}
void forceUpdate()
@Override
public void addListener( final IMEMonitorHandlerReceiver<T> 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<T> 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<T> 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<T> l )
{
this.listeners.remove( l );
}
@Override
public boolean validForPass( final int i )
{
return this.getHandler().validForPass( i );
}
@Nullable
@SuppressWarnings( "unchecked" )
private IMEInventoryHandler<T> getHandler()
{
switch( this.myChannel )
{
case ITEMS:
return (IMEInventoryHandler<T>) this.myGridCache.getItemInventoryHandler();
case FLUIDS:
return (IMEInventoryHandler<T>) this.myGridCache.getFluidInventoryHandler();
default:
}
return null;
}
private Iterator<Entry<IMEMonitorHandlerReceiver<T>, 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<T> diff, final BaseActionSource src )
{
final Iterator<Entry<IMEMonitorHandlerReceiver<T>, Object>> i = this.getListeners();
while( i.hasNext() )
{
final Entry<IMEMonitorHandlerReceiver<T>, Object> o = i.next();
final IMEMonitorHandlerReceiver<T> receiver = o.getKey();
if( receiver.isValid( o.getValue() ) )
{
receiver.postChange( this, diff, src );
}
else
{
i.remove();
}
}
}
private void postChangesToListeners( final Iterable<T> changes, final BaseActionSource src )
{
this.postChange( true, changes, src );
}
private void updateCache( Iterable<T> 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<T> 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<ItemWatcher> 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<Entry<IMEMonitorHandlerReceiver<T>, Object>> i = this.getListeners();
while( i.hasNext() )
{
final Entry<IMEMonitorHandlerReceiver<T>, Object> o = i.next();
@ -81,77 +361,4 @@ public class NetworkMonitor<T extends IAEStack<T>> extends MEMonitorHandler<T>
}
}
@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<T> changes, final BaseActionSource src )
{
this.postChange( true, changes, src );
}
protected void postChange( final boolean add, final Iterable<T> changes, final BaseActionSource src )
{
if( DEPTH.contains( this ) )
{
return;
}
DEPTH.push( this );
this.sendEvent = true;
this.notifyListenersOfChange( changes, src );
final IItemList<T> 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<ItemWatcher> 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." );
}
}
}

View File

@ -378,10 +378,11 @@ public class PartStorageBus extends PartUpgradeable implements IGridTickable, IC
final IMEInventory<IAEItemStack> 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<IAEItemStack> after = AEApi.instance().storage().createItemList();
if( out != null )