diff --git a/src/main/java/appeng/core/features/AEFeature.java b/src/main/java/appeng/core/features/AEFeature.java index edfe86917..f7e1e707c 100644 --- a/src/main/java/appeng/core/features/AEFeature.java +++ b/src/main/java/appeng/core/features/AEFeature.java @@ -164,7 +164,6 @@ public enum AEFeature PATTERNS( "Patterns", Constants.CATEGORY_CRAFTING_FEATURES ), CRAFTING_CPU( "CraftingCPU", Constants.CATEGORY_CRAFTING_FEATURES ), CRAFTING_MANAGER_FALLBACK( "CraftingManagerFallback", Constants.CATEGORY_CRAFTING_FEATURES, "Use CraftingManager to find an alternative recipe, after a pattern rejected an ingredient. Should be enabled to avoid issues, but can have a minor performance impact." ), - INSANE_BLOCKING_MODE( "InsaneBlockingMode", Constants.CATEGORY_CRAFTING_FEATURES, "Use the default AE2 blocking mode that doesn't work on any machines" ), BASIC_CARDS( "BasicCards", Constants.CATEGORY_UPGRADES ), ADVANCED_CARDS( "AdvancedCards", Constants.CATEGORY_UPGRADES ), diff --git a/src/main/java/appeng/helpers/DualityInterface.java b/src/main/java/appeng/helpers/DualityInterface.java index 368e0ca0c..39486b5df 100644 --- a/src/main/java/appeng/helpers/DualityInterface.java +++ b/src/main/java/appeng/helpers/DualityInterface.java @@ -19,18 +19,13 @@ package appeng.helpers; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Optional; +import java.util.*; +import java.util.stream.Collectors; import javax.annotation.Nullable; -import appeng.integration.modules.gregtech.GTCEInventoryAdaptor; import appeng.util.*; +import appeng.util.inv.*; import com.google.common.collect.ImmutableSet; import gregtech.api.block.machines.BlockMachine; @@ -93,8 +88,6 @@ import appeng.api.util.AEPartLocation; import appeng.api.util.DimensionalCoord; import appeng.api.util.IConfigManager; import appeng.capabilities.Capabilities; -import appeng.core.AEConfig; -import appeng.core.features.AEFeature; import appeng.core.settings.TickRates; import appeng.me.GridAccessException; import appeng.me.helpers.AENetworkProxy; @@ -106,10 +99,6 @@ import appeng.parts.automation.StackUpgradeInventory; import appeng.parts.automation.UpgradeInventory; import appeng.tile.inventory.AppEngInternalAEInventory; import appeng.tile.inventory.AppEngInternalInventory; -import appeng.util.inv.AdaptorItemHandler; -import appeng.util.inv.IAEAppEngInventory; -import appeng.util.inv.IInventoryDestination; -import appeng.util.inv.InvOperation; import appeng.util.item.AEItemStack; import static gregtech.api.block.machines.BlockMachine.getMetaTileEntity; @@ -147,6 +136,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn private int isWorking = -1; private final Accessor accessor = new Accessor(); private EnumSet visitedFaces = EnumSet.noneOf( EnumFacing.class ); + private EnumMap> waitingToSendFacing = new EnumMap<>(EnumFacing.class); public DualityInterface( final AENetworkProxy networkProxy, final IInterfaceHost ih ) { @@ -240,6 +230,27 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn } } data.setTag( "waitingToSend", waitingToSend ); + + NBTTagCompound sidedWaitList = new NBTTagCompound(); + + if (this.waitingToSendFacing != null) + { + for( EnumFacing s : this.iHost.getTargets() ) + { + NBTTagList waitingListSided = new NBTTagList(); + if (this.waitingToSendFacing.containsKey( s )) + { + for( final ItemStack is : this.waitingToSendFacing.get( s ) ) + { + final NBTTagCompound item = new NBTTagCompound(); + is.writeToNBT( item ); + waitingListSided.appendTag( item ); + } + sidedWaitList.setTag( s.name(), waitingListSided ); + } + } + } + data.setTag( "sidedWaitList", sidedWaitList ); } public void readFromNBT( final NBTTagCompound data ) @@ -259,6 +270,23 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn } } + this.waitingToSendFacing = null; + final NBTTagCompound waitingListSided = data.getCompoundTag("sidedWaitList"); + + for (EnumFacing s : EnumFacing.values()) + if (waitingListSided.hasKey( s.name() )) { + NBTTagList w = waitingListSided.getTagList( s.name(), 10 ); + for( int x = 0; x < w.tagCount(); x++ ) + { + final NBTTagCompound c = w.getCompoundTagAt( x ); + if( c != null ) + { + final ItemStack is = new ItemStack( c ); + this.addToSendListFacing( is , EnumFacing.getFront( s.getIndex() ) ); + } + } + } + this.craftingTracker.readFromNBT( data ); this.upgrades.readFromNBT( data, "upgrades" ); this.config.readFromNBT( data, "config" ); @@ -294,6 +322,30 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn } } + private void addToSendListFacing( final ItemStack is, EnumFacing f ) + { + if( is.isEmpty() ) + { + return; + } + if (this.waitingToSendFacing == null){ + this.waitingToSendFacing = new EnumMap<>(EnumFacing.class); + } + + this.waitingToSendFacing.computeIfAbsent( f, k -> new ArrayList<>() ); + + this.waitingToSendFacing.get( f ).add( is ); + + try + { + this.gridProxy.getTick().wakeDevice( this.gridProxy.getNode() ); + } + catch( final GridAccessException e ) + { + // :P + } + } + private void readConfig() { this.hasConfig = false; @@ -391,24 +443,22 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn } } - private boolean hasWorkToDo() - { - if( this.hasItemsToSend() ) - { + private boolean hasWorkToDo() { + + if (hasItemsToSend()){ return true; } - else - { - for( final IAEItemStack requiredWork : this.requireWork ) - { - if( requiredWork != null ) - { - return true; - } - } - return false; + if(hasItemsToSendFacing()){ + return true; } + + for (final IAEItemStack requiredWork : this.requireWork) { + if (requiredWork != null) { + return true; + } + } + return false; } private void updatePlan( final int slot ) @@ -513,6 +563,21 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn return this.waitingToSend != null && !this.waitingToSend.isEmpty(); } + private boolean hasItemsToSendFacing() + { + if (waitingToSendFacing != null) + { + for( EnumFacing enumFacing : waitingToSendFacing.keySet() ) + { + if( !waitingToSendFacing.get( enumFacing ).isEmpty() ) + { + return true; + } + } + } + return false; + } + @Override public boolean canInsert( final ItemStack stack ) { @@ -584,11 +649,21 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn return TickRateModulation.SLEEP; } + //Previous version might have items saved in this list + //recover them if( this.hasItemsToSend() ) { this.pushItemsOut( this.iHost.getTargets() ); } + if (hasItemsToSendFacing()) + { + for( EnumFacing enumFacing : waitingToSendFacing.keySet() ) + { + this.pushItemsOut( enumFacing ); + } + } + final boolean couldDoWork = this.updateStorage(); return this.hasWorkToDo() ? ( couldDoWork ? TickRateModulation.URGENT : TickRateModulation.SLOWER ) : TickRateModulation.SLEEP; } @@ -649,6 +724,47 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn } } + private void pushItemsOut( final EnumFacing s ) + { + if( this.waitingToSendFacing.get(s) == null || this.waitingToSendFacing.get( s ).isEmpty() ) + { + return; + } + + final TileEntity tile = this.iHost.getTileEntity(); + final World w = tile.getWorld(); + + final TileEntity te = w.getTileEntity( tile.getPos().offset( s ) ); + if( te == null ) + { + return; + } + + final Iterator i = this.waitingToSendFacing.get( s ).iterator(); + while ( i.hasNext() ) + { + ItemStack whatToSend = i.next(); + final InventoryAdaptor ad = InventoryAdaptor.getAdaptor( te, s.getOpposite() ); + if( ad != null ) + { + final ItemStack result = ad.addItems( whatToSend ); + if( !result.isEmpty() ) + { + whatToSend.setCount( whatToSend.getCount() - ( whatToSend.getCount() - result.getCount() ) ); + } + else + { + i.remove(); + } + } + } + + if( this.waitingToSendFacing.get( s ).isEmpty() ) + { + this.waitingToSendFacing.get( s ).clear(); + } + } + private boolean updateStorage() { boolean didSomething = false; @@ -923,7 +1039,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn @Override public boolean pushPattern( final ICraftingPatternDetails patternDetails, final InventoryCrafting table ) { - if( this.hasItemsToSend() || !this.gridProxy.isActive() || !this.craftingList.contains( patternDetails ) ) + if( this.hasItemsToSend() || this.hasItemsToSendFacing() || !this.gridProxy.isActive() || !this.craftingList.contains( patternDetails ) ) { return false; } @@ -931,8 +1047,12 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn final TileEntity tile = this.iHost.getTileEntity(); final World w = tile.getWorld(); - final EnumSet possibleDirections = this.iHost.getTargets(); - for( final EnumFacing s : possibleDirections ) + if( this.visitedFaces.isEmpty() ) + { + this.visitedFaces = this.iHost.getTargets(); + } + + for( final EnumFacing s : visitedFaces ) { final TileEntity te = w.getTileEntity( tile.getPos().offset( s ) ); if( te instanceof IInterfaceHost ) @@ -941,6 +1061,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn { if( ( (IInterfaceHost) te ).getInterfaceDuality().sameGrid( this.gridProxy.getGrid() ) ) { + visitedFaces.remove( s ); continue; } } @@ -957,8 +1078,10 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn { if( cm.pushPattern( patternDetails, table, s.getOpposite() ) ) { + visitedFaces.remove( s ); return true; } + visitedFaces.remove( s ); continue; } } @@ -966,6 +1089,7 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn final InventoryAdaptor ad = InventoryAdaptor.getAdaptor( te, s.getOpposite() ); if( ad != null ) { + boolean isDrawer = te.getBlockType().getRegistryName().getResourceDomain().equals( "storagedrawers" ); if( this.isBlocking() ) { if( invIsBlocked( ad ) ) @@ -973,38 +1097,37 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn continue; } } - } - if( this.acceptsItems( ad, table ) ) + if( this.acceptsItems( ad, patternDetails, isDrawer ) ) { for( int x = 0; x < table.getSizeInventory(); x++ ) { final ItemStack is = table.getStackInSlot( x ); if( !is.isEmpty() ) { - final ItemStack added = ad.addItems( is ); - this.addToSendList( added ); + addToSendListFacing( is, s ); } + pushItemsOut( s ); } - this.pushItemsOut( possibleDirections ); + visitedFaces.remove( s ); return true; } } + visitedFaces.remove( s ); } - return false; } @Override public boolean isBusy() { - if( this.hasItemsToSend() ) + boolean busy = false; + + if( this.hasItemsToSend() || hasItemsToSendFacing() ) { return true; } - boolean busy = false; - if( this.isBlocking() ) { final EnumSet possibleDirections = this.iHost.getTargets(); @@ -1042,23 +1165,85 @@ public class DualityInterface implements IGridTickable, IStorageMonitorable, IIn return this.cm.getSetting( Settings.BLOCK ) == YesNo.YES; } - private boolean acceptsItems( final InventoryAdaptor ad, final InventoryCrafting table ) + private boolean acceptsItems( final InventoryAdaptor ad, final ICraftingPatternDetails patternDetails, boolean isDrawer ) { - for( int x = 0; x < table.getSizeInventory(); x++ ) + List patternedStacks = Arrays.stream( patternDetails.getCondensedInputs() ).map( IAEItemStack::createItemStack ).collect( Collectors.toList() ); + List copiedItemSlots = new ArrayList<>(); + + if( patternedStacks.size() == 1 ) { - final ItemStack is = table.getStackInSlot( x ); - if( is.isEmpty() ) - { - continue; - } - - if( !ad.simulateAdd( is.copy() ).isEmpty() ) - { - return false; - } + return ad.simulateAdd( patternedStacks.get( 0 ) ).isEmpty(); } + else + { + Iterator adit = ad.iterator(); + while ( adit.hasNext() ) + { + ItemSlot is = adit.next(); + //skip storage drawers slot 0 to avoid voiding items due to broken itemhandler implementation + if( isDrawer && is.getSlot() == 0 ) is = adit.next(); - return true; + //some inventory may expose their special slots ( for upgrades, etc ) + //if its empty AND we cant fit any of the items in the recipes, skip it. + if( is.getItemStack().isEmpty() ) + { + boolean validInputSlotForIngredients = false; + for( IAEItemStack aeItemStack : patternDetails.getCondensedInputs() ) + { + if( is.insertItem( aeItemStack.getDefinition() ).isEmpty() ) + { + validInputSlotForIngredients = true; + break; + } + } + if( validInputSlotForIngredients ) + { + copiedItemSlots.add( is.copy() ); + } + } + else if( !is.getItemStack().isEmpty() && is.getItemStack().getCount() < is.getSlotLimit() ) + { + copiedItemSlots.add( is.copy() ); + } + } + Iterator copiedItemSlotIterator = copiedItemSlots.iterator(); + while ( copiedItemSlotIterator.hasNext() ) + { + ItemSlot copiedItemSlot = copiedItemSlotIterator.next(); + + Iterator psi = patternedStacks.iterator(); + while ( psi.hasNext() ) + { + ItemStack patternedStack = psi.next(); + ItemStack remainder = copiedItemSlot.insertItem( patternedStack ); + + if( !remainder.isEmpty() ) + { + patternedStack.setCount( patternedStack.getCount() - ( patternedStack.getCount() - remainder.getCount() ) ); + if( copiedItemSlot.getSlotLimit() == copiedItemSlot.getItemStack().getCount() ) + { + copiedItemSlotIterator.remove(); + break; + } + } + else + { + if( copiedItemSlot.getSlotLimit() == copiedItemSlot.getItemStack().getCount() ) + { + copiedItemSlotIterator.remove(); + } + psi.remove(); + break; + } + } + + if( patternedStacks.size() == 0 ) + { + return true; + } + } + return false; + } } @Override