/* * This file is part of Applied Energistics 2. * Copyright (c) 2013 - 2015, 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.helpers; import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.annotation.Nullable; import com.google.common.collect.ImmutableSet; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.wrapper.InvWrapper; import appeng.api.AEApi; import appeng.api.config.Actionable; import appeng.api.config.Settings; import appeng.api.config.Upgrades; import appeng.api.config.YesNo; import appeng.api.implementations.ICraftingPatternItem; import appeng.api.implementations.IUpgradeableHost; import appeng.api.implementations.tiles.ICraftingMachine; import appeng.api.networking.GridFlags; import appeng.api.networking.IGrid; import appeng.api.networking.IGridNode; import appeng.api.networking.crafting.ICraftingLink; import appeng.api.networking.crafting.ICraftingPatternDetails; import appeng.api.networking.crafting.ICraftingProvider; import appeng.api.networking.crafting.ICraftingProviderHelper; import appeng.api.networking.energy.IEnergySource; import appeng.api.networking.events.MENetworkCraftingPatternChange; import appeng.api.networking.security.BaseActionSource; import appeng.api.networking.security.IActionHost; import appeng.api.networking.security.MachineSource; import appeng.api.networking.ticking.IGridTickable; import appeng.api.networking.ticking.TickRateModulation; import appeng.api.networking.ticking.TickingRequest; import appeng.api.parts.IPart; import appeng.api.storage.IMEInventory; import appeng.api.storage.IMEMonitor; import appeng.api.storage.IStorageMonitorable; import appeng.api.storage.IStorageMonitorableAccessor; import appeng.api.storage.StorageChannel; import appeng.api.storage.data.IAEFluidStack; import appeng.api.storage.data.IAEItemStack; import appeng.api.util.AECableType; import appeng.api.util.AEPartLocation; import appeng.api.util.DimensionalCoord; import appeng.api.util.IConfigManager; import appeng.capabilities.Capabilities; import appeng.core.settings.TickRates; import appeng.me.GridAccessException; import appeng.me.helpers.AENetworkProxy; import appeng.me.storage.MEMonitorIInventory; import appeng.me.storage.MEMonitorPassThrough; import appeng.me.storage.NullInventory; import appeng.parts.automation.StackUpgradeInventory; import appeng.parts.automation.UpgradeInventory; import appeng.tile.inventory.AppEngInternalAEInventory; import appeng.tile.inventory.AppEngInternalInventory; import appeng.tile.inventory.IAEAppEngInventory; import appeng.tile.inventory.InvOperation; import appeng.util.ConfigManager; import appeng.util.IConfigManagerHost; import appeng.util.InventoryAdaptor; import appeng.util.Platform; import appeng.util.inv.AdaptorIInventory; import appeng.util.inv.IInventoryDestination; import appeng.util.inv.WrapperInvSlot; import appeng.util.item.AEItemStack; public class DualityInterface implements IGridTickable, IStorageMonitorable, IInventoryDestination, IAEAppEngInventory, IConfigManagerHost, ICraftingProvider, IUpgradeableHost, IPriorityHost { public static final int NUMBER_OF_STORAGE_SLOTS = 9; public static final int NUMBER_OF_CONFIG_SLOTS = 9; public static final int NUMBER_OF_PATTERN_SLOTS = 9; private static final Collection<Block> BAD_BLOCKS = new HashSet<Block>( 100 ); private final int[] sides = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; private final IAEItemStack[] requireWork = { null, null, null, null, null, null, null, null, null }; private final MultiCraftingTracker craftingTracker; private final AENetworkProxy gridProxy; private final IInterfaceHost iHost; private final BaseActionSource mySource; private final BaseActionSource interfaceRequestSource; private final ConfigManager cm = new ConfigManager( this ); private final AppEngInternalAEInventory config = new AppEngInternalAEInventory( this, NUMBER_OF_CONFIG_SLOTS ); private final AppEngInternalInventory storage = new AppEngInternalInventory( this, NUMBER_OF_STORAGE_SLOTS ); private final AppEngInternalInventory patterns = new AppEngInternalInventory( this, NUMBER_OF_PATTERN_SLOTS ); private final WrapperInvSlot slotInv = new WrapperInvSlot( this.storage ); private final MEMonitorPassThrough<IAEItemStack> items = new MEMonitorPassThrough<IAEItemStack>( new NullInventory<IAEItemStack>(), StorageChannel.ITEMS ); private final MEMonitorPassThrough<IAEFluidStack> fluids = new MEMonitorPassThrough<IAEFluidStack>( new NullInventory<IAEFluidStack>(), StorageChannel.FLUIDS ); private final UpgradeInventory upgrades; private boolean hasConfig = false; private int priority; private List<ICraftingPatternDetails> craftingList = null; private List<ItemStack> waitingToSend = null; private IMEInventory<IAEItemStack> destination; private boolean isWorking = false; private IItemHandler itemHandler = null; private final Accessor accessor = new Accessor(); public DualityInterface( final AENetworkProxy networkProxy, final IInterfaceHost ih ) { this.gridProxy = networkProxy; this.gridProxy.setFlags( GridFlags.REQUIRE_CHANNEL ); this.upgrades = new StackUpgradeInventory( this.gridProxy.getMachineRepresentation(), this, 1 ); this.cm.registerSetting( Settings.BLOCK, YesNo.NO ); this.cm.registerSetting( Settings.INTERFACE_TERMINAL, YesNo.YES ); this.iHost = ih; this.craftingTracker = new MultiCraftingTracker( this.iHost, 9 ); final MachineSource actionSource = new MachineSource( this.iHost ); this.mySource = actionSource; this.fluids.setChangeSource( actionSource ); this.items.setChangeSource( actionSource ); this.interfaceRequestSource = new InterfaceRequestSource( this.iHost ); } @Override public void saveChanges() { this.iHost.saveChanges(); } @Override public void onChangeInventory( final IInventory inv, final int slot, final InvOperation mc, final ItemStack removed, final ItemStack added ) { if( this.isWorking ) { return; } if( inv == this.config ) { this.readConfig(); } else if( inv == this.patterns && ( removed != null || added != null ) ) { this.updateCraftingList(); } else if( inv == this.storage && slot >= 0 ) { final boolean had = this.hasWorkToDo(); this.updatePlan( slot ); final boolean now = this.hasWorkToDo(); if( had != now ) { try { if( now ) { this.gridProxy.getTick().alertDevice( this.gridProxy.getNode() ); } else { this.gridProxy.getTick().sleepDevice( this.gridProxy.getNode() ); } } catch( final GridAccessException e ) { // :P } } } } public void writeToNBT( final NBTTagCompound data ) { this.config.writeToNBT( data, "config" ); this.patterns.writeToNBT( data, "patterns" ); this.storage.writeToNBT( data, "storage" ); this.upgrades.writeToNBT( data, "upgrades" ); this.cm.writeToNBT( data ); this.craftingTracker.writeToNBT( data ); data.setInteger( "priority", this.priority ); final NBTTagList waitingToSend = new NBTTagList(); if( this.waitingToSend != null ) { for( final ItemStack is : this.waitingToSend ) { final NBTTagCompound item = new NBTTagCompound(); is.writeToNBT( item ); waitingToSend.appendTag( item ); } } data.setTag( "waitingToSend", waitingToSend ); } public void readFromNBT( final NBTTagCompound data ) { this.waitingToSend = null; final NBTTagList waitingList = data.getTagList( "waitingToSend", 10 ); if( waitingList != null ) { for( int x = 0; x < waitingList.tagCount(); x++ ) { final NBTTagCompound c = waitingList.getCompoundTagAt( x ); if( c != null ) { final ItemStack is = ItemStack.loadItemStackFromNBT( c ); this.addToSendList( is ); } } } this.craftingTracker.readFromNBT( data ); this.upgrades.readFromNBT( data, "upgrades" ); this.config.readFromNBT( data, "config" ); this.patterns.readFromNBT( data, "patterns" ); this.storage.readFromNBT( data, "storage" ); this.priority = data.getInteger( "priority" ); this.cm.readFromNBT( data ); this.readConfig(); this.updateCraftingList(); } private void addToSendList( final ItemStack is ) { if( is == null ) { return; } if( this.waitingToSend == null ) { this.waitingToSend = new LinkedList<ItemStack>(); } this.waitingToSend.add( is ); try { this.gridProxy.getTick().wakeDevice( this.gridProxy.getNode() ); } catch( final GridAccessException e ) { // :P } } private void readConfig() { this.hasConfig = false; for( final ItemStack p : this.config ) { if( p != null ) { this.hasConfig = true; break; } } final boolean had = this.hasWorkToDo(); for( int x = 0; x < NUMBER_OF_CONFIG_SLOTS; x++ ) { this.updatePlan( x ); } final boolean has = this.hasWorkToDo(); if( had != has ) { try { if( has ) { this.gridProxy.getTick().alertDevice( this.gridProxy.getNode() ); } else { this.gridProxy.getTick().sleepDevice( this.gridProxy.getNode() ); } } catch( final GridAccessException e ) { // :P } } this.notifyNeighbors(); } private void updateCraftingList() { final Boolean[] accountedFor = { false, false, false, false, false, false, false, false, false }; // 9... assert ( accountedFor.length == this.patterns.getSizeInventory() ); if( !this.gridProxy.isReady() ) { return; } if( this.craftingList != null ) { final Iterator<ICraftingPatternDetails> i = this.craftingList.iterator(); while( i.hasNext() ) { final ICraftingPatternDetails details = i.next(); boolean found = false; for( int x = 0; x < accountedFor.length; x++ ) { final ItemStack is = this.patterns.getStackInSlot( x ); if( details.getPattern() == is ) { accountedFor[x] = found = true; } } if( !found ) { i.remove(); } } } for( int x = 0; x < accountedFor.length; x++ ) { if( !accountedFor[x] ) { this.addToCraftingList( this.patterns.getStackInSlot( x ) ); } } try { this.gridProxy.getGrid().postEvent( new MENetworkCraftingPatternChange( this, this.gridProxy.getNode() ) ); } catch( final GridAccessException e ) { // :P } } private boolean hasWorkToDo() { if( this.hasItemsToSend() ) { return true; } else { for( final IAEItemStack requiredWork : this.requireWork ) { if( requiredWork != null ) { return true; } } return false; } } private void updatePlan( final int slot ) { IAEItemStack req = this.config.getAEStackInSlot( slot ); if( req != null && req.getStackSize() <= 0 ) { this.config.setInventorySlotContents( slot, null ); req = null; } final ItemStack Stored = this.storage.getStackInSlot( slot ); if( req == null && Stored != null ) { final IAEItemStack work = AEApi.instance().storage().createItemStack( Stored ); this.requireWork[slot] = work.setStackSize( -work.getStackSize() ); return; } else if( req != null ) { if( Stored == null ) // need to add stuff! { this.requireWork[slot] = req.copy(); return; } else if( req.isSameType( Stored ) ) // same type ( qty different? )! { if( req.getStackSize() != Stored.stackSize ) { this.requireWork[slot] = req.copy(); this.requireWork[slot].setStackSize( req.getStackSize() - Stored.stackSize ); return; } } else // Stored != null; dispose! { final IAEItemStack work = AEApi.instance().storage().createItemStack( Stored ); this.requireWork[slot] = work.setStackSize( -work.getStackSize() ); return; } } // else this.requireWork[slot] = null; } public void notifyNeighbors() { if( this.gridProxy.isActive() ) { try { this.gridProxy.getGrid().postEvent( new MENetworkCraftingPatternChange( this, this.gridProxy.getNode() ) ); this.gridProxy.getTick().wakeDevice( this.gridProxy.getNode() ); } catch( final GridAccessException e ) { // :P } } final TileEntity te = this.iHost.getTileEntity(); if( te != null && te.getWorld() != null ) { Platform.notifyBlocksOfNeighbors( te.getWorld(), te.getPos() ); } } private void addToCraftingList( final ItemStack is ) { if( is == null ) { return; } if( is.getItem() instanceof ICraftingPatternItem ) { final ICraftingPatternItem cpi = (ICraftingPatternItem) is.getItem(); final ICraftingPatternDetails details = cpi.getPatternForItem( is, this.iHost.getTileEntity().getWorld() ); if( details != null ) { if( this.craftingList == null ) { this.craftingList = new LinkedList<ICraftingPatternDetails>(); } this.craftingList.add( details ); } } } private boolean hasItemsToSend() { return this.waitingToSend != null && !this.waitingToSend.isEmpty(); } @Override public boolean canInsert( final ItemStack stack ) { final IAEItemStack out = this.destination.injectItems( AEApi.instance().storage().createItemStack( stack ), Actionable.SIMULATE, null ); if( out == null ) { return true; } return out.getStackSize() != stack.stackSize; // ItemStack after = adaptor.simulateAdd( stack ); // if ( after == null ) // return true; // return after.stackSize != stack.stackSize; } public IInventory getConfig() { return this.config; } public IInventory getPatterns() { return this.patterns; } public void gridChanged() { try { this.items.setInternal( this.gridProxy.getStorage().getItemInventory() ); this.fluids.setInternal( this.gridProxy.getStorage().getFluidInventory() ); } catch( final GridAccessException gae ) { this.items.setInternal( new NullInventory<IAEItemStack>() ); this.fluids.setInternal( new NullInventory<IAEFluidStack>() ); } this.notifyNeighbors(); } public AECableType getCableConnectionType( final AEPartLocation dir ) { return AECableType.SMART; } public DimensionalCoord getLocation() { return new DimensionalCoord( this.iHost.getTileEntity() ); } public IInventory getInternalInventory() { return this.storage; } public void markDirty() { for( int slot = 0; slot < this.storage.getSizeInventory(); slot++ ) { this.onChangeInventory( this.storage, slot, InvOperation.markDirty, null, null ); } } public int[] getSlotsForFace( final EnumFacing side ) { return this.sides; } @Override public TickingRequest getTickingRequest( final IGridNode node ) { return new TickingRequest( TickRates.Interface.getMin(), TickRates.Interface.getMax(), !this.hasWorkToDo(), true ); } @Override public TickRateModulation tickingRequest( final IGridNode node, final int ticksSinceLastCall ) { if( !this.gridProxy.isActive() ) { return TickRateModulation.SLEEP; } if( this.hasItemsToSend() ) { this.pushItemsOut( this.iHost.getTargets() ); } final boolean couldDoWork = this.updateStorage(); return this.hasWorkToDo() ? ( couldDoWork ? TickRateModulation.URGENT : TickRateModulation.SLOWER ) : TickRateModulation.SLEEP; } private void pushItemsOut( final EnumSet<EnumFacing> possibleDirections ) { if( !this.hasItemsToSend() ) { return; } final TileEntity tile = this.iHost.getTileEntity(); final World w = tile.getWorld(); final Iterator<ItemStack> i = this.waitingToSend.iterator(); while( i.hasNext() ) { ItemStack whatToSend = i.next(); for( final EnumFacing s : possibleDirections ) { final TileEntity te = w.getTileEntity( tile.getPos().offset( s ) ); if( te == null ) { continue; } final InventoryAdaptor ad = InventoryAdaptor.getAdaptor( te, s.getOpposite() ); if( ad != null ) { final ItemStack Result = ad.addItems( whatToSend ); if( Result == null ) { whatToSend = null; } else { whatToSend.stackSize -= whatToSend.stackSize - Result.stackSize; } if( whatToSend == null ) { break; } } } if( whatToSend == null ) { i.remove(); } } if( this.waitingToSend.isEmpty() ) { this.waitingToSend = null; } } private boolean updateStorage() { boolean didSomething = false; for( int x = 0; x < NUMBER_OF_STORAGE_SLOTS; x++ ) { if( this.requireWork[x] != null ) { didSomething = this.usePlan( x, this.requireWork[x] ) || didSomething; } } return didSomething; } private boolean usePlan( final int x, final IAEItemStack itemStack ) { final InventoryAdaptor adaptor = this.getAdaptor( x ); this.isWorking = true; boolean changed = false; try { this.destination = this.gridProxy.getStorage().getItemInventory(); final IEnergySource src = this.gridProxy.getEnergy(); if( this.craftingTracker.isBusy( x ) ) { changed = this.handleCrafting( x, adaptor, itemStack ) || changed; } else if( itemStack.getStackSize() > 0 ) { // make sure strange things didn't happen... if( adaptor.simulateAdd( itemStack.getItemStack() ) != null ) { changed = true; throw new GridAccessException(); } final IAEItemStack acquired = Platform.poweredExtraction( src, this.destination, itemStack, this.interfaceRequestSource ); if( acquired != null ) { changed = true; final ItemStack issue = adaptor.addItems( acquired.getItemStack() ); if( issue != null ) { throw new IllegalStateException( "bad attempt at managing inventory. ( addItems )" ); } } else { changed = this.handleCrafting( x, adaptor, itemStack ) || changed; } } else if( itemStack.getStackSize() < 0 ) { IAEItemStack toStore = itemStack.copy(); toStore.setStackSize( -toStore.getStackSize() ); long diff = toStore.getStackSize(); // make sure strange things didn't happen... final ItemStack canExtract = adaptor.simulateRemove( (int) diff, toStore.getItemStack(), null ); if( canExtract == null || canExtract.stackSize != diff ) { changed = true; throw new GridAccessException(); } toStore = Platform.poweredInsert( src, this.destination, toStore, this.interfaceRequestSource ); if( toStore != null ) { diff -= toStore.getStackSize(); } if( diff != 0 ) { // extract items! changed = true; final ItemStack removed = adaptor.removeItems( (int) diff, null, null ); if( removed == null ) { throw new IllegalStateException( "bad attempt at managing inventory. ( removeItems )" ); } else if( removed.stackSize != diff ) { throw new IllegalStateException( "bad attempt at managing inventory. ( removeItems )" ); } } } // else wtf? } catch( final GridAccessException e ) { // :P } if( changed ) { this.updatePlan( x ); } this.isWorking = false; return changed; } private InventoryAdaptor getAdaptor( final int slot ) { return new AdaptorIInventory( this.slotInv.getWrapper( slot ) ); } private boolean handleCrafting( final int x, final InventoryAdaptor d, final IAEItemStack itemStack ) { try { if( this.getInstalledUpgrades( Upgrades.CRAFTING ) > 0 && itemStack != null ) { return this.craftingTracker.handleCrafting( x, itemStack.getStackSize(), itemStack, d, this.iHost.getTileEntity().getWorld(), this.gridProxy.getGrid(), this.gridProxy.getCrafting(), this.mySource ); } } catch( final GridAccessException e ) { // :P } return false; } @Override public int getInstalledUpgrades( final Upgrades u ) { if( this.upgrades == null ) { return 0; } return this.upgrades.getInstalledUpgrades( u ); } @Override public TileEntity getTile() { return (TileEntity) ( this.iHost instanceof TileEntity ? this.iHost : null ); } @Override public IMEMonitor<IAEItemStack> getItemInventory() { if( this.hasConfig() ) { return new InterfaceInventory( this ); } return this.items; } private boolean hasConfig() { return this.hasConfig; } @Override public IInventory getInventoryByName( final String name ) { if( name.equals( "storage" ) ) { return this.storage; } if( name.equals( "patterns" ) ) { return this.patterns; } if( name.equals( "config" ) ) { return this.config; } if( name.equals( "upgrades" ) ) { return this.upgrades; } return null; } public IInventory getStorage() { return this.storage; } @Override public appeng.api.util.IConfigManager getConfigManager() { return this.cm; } @Override public void updateSetting( final IConfigManager manager, final Enum settingName, final Enum newValue ) { if( this.getInstalledUpgrades( Upgrades.CRAFTING ) == 0 ) { this.cancelCrafting(); } this.markDirty(); } @Override public IMEMonitor<IAEFluidStack> getFluidInventory() { if( this.hasConfig() ) { return null; } return this.fluids; } private void cancelCrafting() { this.craftingTracker.cancel(); } public IStorageMonitorable getMonitorable( final BaseActionSource src, final IStorageMonitorable myInterface ) { if( Platform.canAccess( this.gridProxy, src ) ) { return myInterface; } final DualityInterface di = this; return new IStorageMonitorable(){ @Override public IMEMonitor<IAEItemStack> getItemInventory() { return new InterfaceInventory( di ); } @Override public IMEMonitor<IAEFluidStack> getFluidInventory() { return null; } }; } @Override public boolean pushPattern( final ICraftingPatternDetails patternDetails, final InventoryCrafting table ) { if( this.hasItemsToSend() || !this.gridProxy.isActive() || !this.craftingList.contains( patternDetails ) ) { return false; } final TileEntity tile = this.iHost.getTileEntity(); final World w = tile.getWorld(); final EnumSet<EnumFacing> possibleDirections = this.iHost.getTargets(); for( final EnumFacing s : possibleDirections ) { final TileEntity te = w.getTileEntity( tile.getPos().offset( s ) ); if( te instanceof IInterfaceHost ) { try { if( ( (IInterfaceHost) te ).getInterfaceDuality().sameGrid( this.gridProxy.getGrid() ) ) { continue; } } catch( final GridAccessException e ) { continue; } } if( te instanceof ICraftingMachine ) { final ICraftingMachine cm = (ICraftingMachine) te; if( cm.acceptsPlans() ) { if( cm.pushPattern( patternDetails, table, s.getOpposite() ) ) { return true; } continue; } } final InventoryAdaptor ad = InventoryAdaptor.getAdaptor( te, s.getOpposite() ); if( ad != null ) { if( this.isBlocking() ) { if( ad.simulateRemove( 1, null, null ) != null ) { continue; } } if( this.acceptsItems( ad, table ) ) { for( int x = 0; x < table.getSizeInventory(); x++ ) { final ItemStack is = table.getStackInSlot( x ); if( is != null ) { final ItemStack added = ad.addItems( is ); this.addToSendList( added ); } } this.pushItemsOut( possibleDirections ); return true; } } } return false; } @Override public boolean isBusy() { if( this.hasItemsToSend() ) { return true; } boolean busy = false; if( this.isBlocking() ) { final EnumSet<EnumFacing> possibleDirections = this.iHost.getTargets(); final TileEntity tile = this.iHost.getTileEntity(); final World w = tile.getWorld(); boolean allAreBusy = true; for( final EnumFacing s : possibleDirections ) { final TileEntity te = w.getTileEntity( tile.getPos().offset( s ) ); final InventoryAdaptor ad = InventoryAdaptor.getAdaptor( te, s.getOpposite() ); if( ad != null ) { if( ad.simulateRemove( 1, null, null ) == null ) { allAreBusy = false; break; } } } busy = allAreBusy; } return busy; } private boolean sameGrid( final IGrid grid ) throws GridAccessException { return grid == this.gridProxy.getGrid(); } private boolean isBlocking() { return this.cm.getSetting( Settings.BLOCK ) == YesNo.YES; } private boolean acceptsItems( final InventoryAdaptor ad, final InventoryCrafting table ) { for( int x = 0; x < table.getSizeInventory(); x++ ) { final ItemStack is = table.getStackInSlot( x ); if( is == null ) { continue; } if( ad.simulateAdd( is.copy() ) != null ) { return false; } } return true; } @Override public void provideCrafting( final ICraftingProviderHelper craftingTracker ) { if( this.gridProxy.isActive() && this.craftingList != null ) { for( final ICraftingPatternDetails details : this.craftingList ) { details.setPriority( this.priority ); craftingTracker.addCraftingOption( this, details ); } } } public void addDrops( final List<ItemStack> drops ) { if( this.waitingToSend != null ) { for( final ItemStack is : this.waitingToSend ) { if( is != null ) { drops.add( is ); } } } for( final ItemStack is : this.upgrades ) { if( is != null ) { drops.add( is ); } } for( final ItemStack is : this.storage ) { if( is != null ) { drops.add( is ); } } for( final ItemStack is : this.patterns ) { if( is != null ) { drops.add( is ); } } } public IUpgradeableHost getHost() { if( this.getPart() instanceof IUpgradeableHost ) { return (IUpgradeableHost) this.getPart(); } if( this.getTile() instanceof IUpgradeableHost ) { return (IUpgradeableHost) this.getTile(); } return null; } private IPart getPart() { return (IPart) ( this.iHost instanceof IPart ? this.iHost : null ); } public ImmutableSet<ICraftingLink> getRequestedJobs() { return this.craftingTracker.getRequestedJobs(); } public IAEItemStack injectCraftedItems( final ICraftingLink link, final IAEItemStack acquired, final Actionable mode ) { final int slot = this.craftingTracker.getSlot( link ); if( acquired != null && slot >= 0 && slot <= this.requireWork.length ) { final InventoryAdaptor adaptor = this.getAdaptor( slot ); if( mode == Actionable.SIMULATE ) { return AEItemStack.create( adaptor.simulateAdd( acquired.getItemStack() ) ); } else { final IAEItemStack is = AEItemStack.create( adaptor.addItems( acquired.getItemStack() ) ); this.updatePlan( slot ); return is; } } return acquired; } public void jobStateChange( final ICraftingLink link ) { this.craftingTracker.jobStateChange( link ); } public String getTermName() { final TileEntity hostTile = this.iHost.getTileEntity(); final World hostWorld = hostTile.getWorld(); if( ( (ICustomNameObject) this.iHost ).hasCustomName() ) { return ( (ICustomNameObject) this.iHost ).getCustomName(); } final EnumSet<EnumFacing> possibleDirections = this.iHost.getTargets(); for( final EnumFacing direction : possibleDirections ) { final BlockPos targ = hostTile.getPos().offset( direction ); final TileEntity directedTile = hostWorld.getTileEntity( targ ); if( directedTile == null ) { continue; } if( directedTile instanceof IInterfaceHost ) { try { if( ( (IInterfaceHost) directedTile ).getInterfaceDuality().sameGrid( this.gridProxy.getGrid() ) ) { continue; } } catch( final GridAccessException e ) { continue; } } final InventoryAdaptor adaptor = InventoryAdaptor.getAdaptor( directedTile, direction.getOpposite() ); if( directedTile instanceof ICraftingMachine || adaptor != null ) { if( directedTile instanceof IInventory && ( (IInventory) directedTile ).getSizeInventory() == 0 ) { continue; } if( directedTile instanceof ISidedInventory ) { final int[] sides = ( (ISidedInventory) directedTile ).getSlotsForFace( direction.getOpposite() ); if( sides == null || sides.length == 0 ) { continue; } } final IBlockState directedBlockState = hostWorld.getBlockState( targ ); final Block directedBlock = directedBlockState.getBlock(); ItemStack what = new ItemStack( directedBlock, 1, directedBlock.getMetaFromState( directedBlockState ) ); try { Vec3d from = new Vec3d( hostTile.getPos().getX() + 0.5, hostTile.getPos().getY() + 0.5, hostTile.getPos().getZ() + 0.5 ); from = from.addVector( direction.getFrontOffsetX() * 0.501, direction.getFrontOffsetY() * 0.501, direction.getFrontOffsetZ() * 0.501 ); final Vec3d to = from.addVector( direction.getFrontOffsetX(), direction.getFrontOffsetY(), direction.getFrontOffsetZ() ); final RayTraceResult mop = hostWorld.rayTraceBlocks( from, to, true ); if( mop != null && !BAD_BLOCKS.contains( directedBlock ) ) { if( mop.getBlockPos().equals( directedTile.getPos() ) ) { final ItemStack g = directedBlock.getPickBlock( directedBlockState, mop, hostWorld, directedTile.getPos(), null ); if( g != null ) { what = g; } } } } catch( final Throwable t ) { BAD_BLOCKS.add( directedBlock ); // nope! } if( what.getItem() != null ) { return what.getUnlocalizedName(); } final Item item = Item.getItemFromBlock( directedBlock ); if( item == null ) { return directedBlock.getUnlocalizedName(); } } } return "Nothing"; } public long getSortValue() { final TileEntity te = this.iHost.getTileEntity(); return ( te.getPos().getZ() << 24 ) ^ ( te.getPos().getX() << 8 ) ^ te.getPos().getY(); } public void initialize() { this.updateCraftingList(); } @Override public int getPriority() { return this.priority; } @Override public void setPriority( final int newValue ) { this.priority = newValue; this.markDirty(); try { this.gridProxy.getGrid().postEvent( new MENetworkCraftingPatternChange( this, this.gridProxy.getNode() ) ); } catch( final GridAccessException e ) { // :P } } public boolean hasCapability( Capability<?> capabilityClass, EnumFacing facing ) { return capabilityClass == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY || capabilityClass == Capabilities.STORAGE_MONITORABLE_ACCESSOR; } @SuppressWarnings( "unchecked" ) public <T> T getCapability( Capability<T> capabilityClass, EnumFacing facing ) { if( capabilityClass == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY ) { if( itemHandler == null ) { itemHandler = new InvWrapper( storage ); } return (T) itemHandler; } else if( capabilityClass == Capabilities.STORAGE_MONITORABLE_ACCESSOR ) { return (T) accessor; } return null; } private class InterfaceRequestSource extends MachineSource { public InterfaceRequestSource( final IActionHost v ) { super( v ); } } private class InterfaceInventory extends MEMonitorIInventory { public InterfaceInventory( final DualityInterface tileInterface ) { super( new AdaptorIInventory( tileInterface.storage ) ); this.setActionSource( new MachineSource( DualityInterface.this.iHost ) ); } @Override public IAEItemStack injectItems( final IAEItemStack input, final Actionable type, final BaseActionSource src ) { if( src instanceof InterfaceRequestSource ) { return input; } return super.injectItems( input, type, src ); } @Override public IAEItemStack extractItems( final IAEItemStack request, final Actionable type, final BaseActionSource src ) { if( src instanceof InterfaceRequestSource ) { return null; } return super.extractItems( request, type, src ); } } private class Accessor implements IStorageMonitorableAccessor { @Nullable @Override public IStorageMonitorable getInventory( BaseActionSource src ) { return getMonitorable( src, DualityInterface.this ); } } }