b977ee89ee
* Implemented an adapter for IItemHandler so it can be used by the Storage Bus. * Added update hook for inject/extract to ItemHandlerAdapter. * Implemented ItemHandler and FluidHandler capabilities for the condenser, as replacement for the Void Inventories. * Removed external storage handler, added capability-based way of accessing a monitorable ME network via the storage bus. Removed special case inventories for the matter condenser. * Implemented InventoryAdaptor for IItemHandler. This also now fixes molecular assemblers interaction with part interfaces.
331 lines
8.1 KiB
Java
331 lines
8.1 KiB
Java
/*
|
|
* This file is part of Applied Energistics 2.
|
|
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
|
|
*
|
|
* Applied Energistics 2 is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Applied Energistics 2 is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
|
|
*/
|
|
|
|
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.NavigableMap;
|
|
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<T extends IAEStack<T>> implements IMEInventoryHandler<T>
|
|
{
|
|
|
|
private static final ThreadLocal<LinkedList> DEPTH_MOD = new ThreadLocal<LinkedList>();
|
|
private static final ThreadLocal<LinkedList> DEPTH_SIM = new ThreadLocal<LinkedList>();
|
|
private static final Comparator<Integer> PRIORITY_SORTER = ( o1, o2 ) -> ItemSorters.compareInt( o2, o1 );
|
|
|
|
private static int currentPass = 0;
|
|
private final StorageChannel myChannel;
|
|
private final SecurityCache security;
|
|
private final NavigableMap<Integer, List<IMEInventoryHandler<T>>> priorityInventory;
|
|
private int myPass = 0;
|
|
|
|
public NetworkInventoryHandler( final StorageChannel chan, final SecurityCache security )
|
|
{
|
|
this.myChannel = chan;
|
|
this.security = security;
|
|
this.priorityInventory = new TreeMap<>( PRIORITY_SORTER );
|
|
}
|
|
|
|
public void addNewStorage( final IMEInventoryHandler<T> h )
|
|
{
|
|
final int priority = h.getPriority();
|
|
List<IMEInventoryHandler<T>> list = this.priorityInventory.get( priority );
|
|
if( list == null )
|
|
{
|
|
this.priorityInventory.put( priority, list = new ArrayList<>() );
|
|
}
|
|
|
|
list.add( h );
|
|
}
|
|
|
|
@Override
|
|
public T injectItems( T input, final Actionable type, final BaseActionSource src )
|
|
{
|
|
if( this.diveList( this, type ) )
|
|
{
|
|
return input;
|
|
}
|
|
|
|
if( this.testPermission( src, SecurityPermissions.INJECT ) )
|
|
{
|
|
this.surface( this, type );
|
|
return input;
|
|
}
|
|
|
|
for( final List<IMEInventoryHandler<T>> invList : this.priorityInventory.values() )
|
|
{
|
|
Iterator<IMEInventoryHandler<T>> ii = invList.iterator();
|
|
while( ii.hasNext() && input != null )
|
|
{
|
|
final IMEInventoryHandler<T> 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 );
|
|
}
|
|
}
|
|
|
|
// We need to ignore prioritized inventories in the second pass. If they were not able to store everything
|
|
// during the first pass, they will do so in the second, but as this is stateless we will just report twice
|
|
// the amount of storable items.
|
|
// ignores craftingcache on the second pass.
|
|
ii = invList.iterator();
|
|
while( ii.hasNext() && input != null )
|
|
{
|
|
final IMEInventoryHandler<T> inv = ii.next();
|
|
|
|
if( inv.validForPass( 2 ) && inv.canAccept( input ) && !inv.isPrioritized( input ) )
|
|
{
|
|
input = inv.injectItems( input, type, src );
|
|
}
|
|
}
|
|
}
|
|
|
|
this.surface( this, type );
|
|
|
|
return input;
|
|
}
|
|
|
|
private boolean diveList( final NetworkInventoryHandler<T> networkInventoryHandler, final Actionable type )
|
|
{
|
|
final LinkedList cDepth = this.getDepth( type );
|
|
if( cDepth.contains( networkInventoryHandler ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
cDepth.push( this );
|
|
return false;
|
|
}
|
|
|
|
private boolean testPermission( final BaseActionSource src, final SecurityPermissions permission )
|
|
{
|
|
if( src.isPlayer() )
|
|
{
|
|
if( !this.security.hasPermission( ( (PlayerSource) src ).player, permission ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if( src.isMachine() )
|
|
{
|
|
if( this.security.isAvailable() )
|
|
{
|
|
final IGridNode n = ( (MachineSource) src ).via.getActionableNode();
|
|
if( n == null )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
final IGrid gn = n.getGrid();
|
|
if( gn != this.security.getGrid() )
|
|
{
|
|
|
|
final ISecurityGrid sg = gn.getCache( ISecurityGrid.class );
|
|
final int playerID = sg.getOwner();
|
|
|
|
if( !this.security.hasPermission( playerID, permission ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void surface( final NetworkInventoryHandler<T> networkInventoryHandler, final Actionable type )
|
|
{
|
|
if( this.getDepth( type ).pop() != this )
|
|
{
|
|
throw new IllegalStateException( "Invalid Access to Networked Storage API detected." );
|
|
}
|
|
}
|
|
|
|
private LinkedList getDepth( final Actionable type )
|
|
{
|
|
final ThreadLocal<LinkedList> depth = type == Actionable.MODULATE ? DEPTH_MOD : DEPTH_SIM;
|
|
|
|
LinkedList s = depth.get();
|
|
|
|
if( s == null )
|
|
{
|
|
depth.set( s = new LinkedList() );
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
@Override
|
|
public T extractItems( T request, final Actionable mode, final BaseActionSource src )
|
|
{
|
|
if( this.diveList( this, mode ) )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if( this.testPermission( src, SecurityPermissions.EXTRACT ) )
|
|
{
|
|
this.surface( this, mode );
|
|
return null;
|
|
}
|
|
|
|
final Iterator<List<IMEInventoryHandler<T>>> i = this.priorityInventory.descendingMap().values().iterator();// priorityInventory.asMap().descendingMap().entrySet().iterator();
|
|
|
|
final T output = request.copy();
|
|
request = request.copy();
|
|
output.setStackSize( 0 );
|
|
final long req = request.getStackSize();
|
|
|
|
while( i.hasNext() )
|
|
{
|
|
final List<IMEInventoryHandler<T>> invList = i.next();
|
|
|
|
final Iterator<IMEInventoryHandler<T>> ii = invList.iterator();
|
|
while( ii.hasNext() && output.getStackSize() < req )
|
|
{
|
|
final IMEInventoryHandler<T> inv = ii.next();
|
|
|
|
request.setStackSize( req - output.getStackSize() );
|
|
output.add( inv.extractItems( request, mode, src ) );
|
|
}
|
|
}
|
|
|
|
this.surface( this, mode );
|
|
|
|
if( output.getStackSize() <= 0 )
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
@Override
|
|
public IItemList<T> getAvailableItems( IItemList out )
|
|
{
|
|
if( this.diveIteration( this, Actionable.SIMULATE ) )
|
|
{
|
|
return out;
|
|
}
|
|
|
|
// for (Entry<Integer, IMEInventoryHandler<T>> h : priorityInventory.entries())
|
|
for( final List<IMEInventoryHandler<T>> i : this.priorityInventory.values() )
|
|
{
|
|
for( final IMEInventoryHandler<T> j : i )
|
|
{
|
|
out = j.getAvailableItems( out );
|
|
}
|
|
}
|
|
|
|
this.surface( this, Actionable.SIMULATE );
|
|
|
|
return out;
|
|
}
|
|
|
|
private boolean diveIteration( final NetworkInventoryHandler<T> networkInventoryHandler, final Actionable type )
|
|
{
|
|
final LinkedList cDepth = this.getDepth( type );
|
|
if( cDepth.isEmpty() )
|
|
{
|
|
currentPass++;
|
|
this.myPass = currentPass;
|
|
}
|
|
else
|
|
{
|
|
if( currentPass == this.myPass )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
this.myPass = currentPass;
|
|
}
|
|
}
|
|
|
|
cDepth.push( this );
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public StorageChannel getChannel()
|
|
{
|
|
return this.myChannel;
|
|
}
|
|
|
|
@Override
|
|
public AccessRestriction getAccess()
|
|
{
|
|
return AccessRestriction.READ_WRITE;
|
|
}
|
|
|
|
@Override
|
|
public boolean isPrioritized( final T input )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean canAccept( final T input )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int getPriority()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public int getSlot()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean validForPass( final int i )
|
|
{
|
|
return true;
|
|
}
|
|
}
|