diff --git a/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java b/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java
index 57fbf227006..d4f0e96a36b 100644
--- a/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java
+++ b/src/main/java/gregtech/api/logic/AbstractProcessingLogic.java
@@ -43,6 +43,7 @@ public abstract class AbstractProcessingLogic
preProcess() {
recipeMap = recipeMapSupplier.get();
}
if (lastRecipeMap != recipeMap) {
+ if (lastRecipeMap != null) needWipeCraftingPatternRecipeCache = true;
lastRecipe = null;
lastRecipeMap = recipeMap;
}
diff --git a/src/main/java/gregtech/api/logic/ProcessingLogic.java b/src/main/java/gregtech/api/logic/ProcessingLogic.java
index 63c8f6494e4..3a381ee2041 100644
--- a/src/main/java/gregtech/api/logic/ProcessingLogic.java
+++ b/src/main/java/gregtech/api/logic/ProcessingLogic.java
@@ -1,6 +1,10 @@
package gregtech.api.logic;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
@@ -10,6 +14,7 @@
import net.minecraftforge.fluids.FluidStack;
import gregtech.api.interfaces.tileentity.IRecipeLockable;
+import gregtech.api.objects.GTDualInputs;
import gregtech.api.recipe.RecipeMap;
import gregtech.api.recipe.check.CheckRecipeResult;
import gregtech.api.recipe.check.CheckRecipeResultRegistry;
@@ -17,6 +22,7 @@
import gregtech.api.util.GTRecipe;
import gregtech.api.util.OverclockCalculator;
import gregtech.api.util.ParallelHelper;
+import gregtech.common.tileentities.machines.IDualInputInventory;
/**
* Logic class to calculate result of recipe check from inputs, based on recipemap.
@@ -29,6 +35,8 @@ public class ProcessingLogic extends AbstractProcessingLogic {
protected ItemStack[] inputItems;
protected FluidStack[] inputFluids;
protected boolean isRecipeLocked;
+ protected IDualInputInventory craftingPattern;
+ protected Map> craftingPatternRecipeCache = new HashMap<>();
public ProcessingLogic() {}
@@ -63,6 +71,33 @@ public ProcessingLogic setSpecialSlotItem(ItemStack specialSlotItem) {
return getThis();
}
+ public boolean craftingPatternHandler(IDualInputInventory slot) {
+ if (needWipeCraftingPatternRecipeCache) {
+ craftingPatternRecipeCache.clear();
+ needWipeCraftingPatternRecipeCache = false;
+ }
+
+ if (craftingPatternRecipeCache.containsKey(slot)) {
+ craftingPattern = slot;
+ return true;
+ } else {
+ GTDualInputs inputs = slot.getPatternInputs();
+ setInputItems(inputs.inputItems);
+ setInputFluids(inputs.inputFluid);
+ Set recipes = findRecipeMatches(preProcess()).collect(Collectors.toSet());
+ if (!recipes.isEmpty()) {
+ craftingPatternRecipeCache.put(slot, recipes);
+ craftingPattern = slot;
+ return true;
+ }
+ return false;
+ }
+ }
+
+ public void removeEntryCraftingPatternRecipeCache(IDualInputInventory slot) {
+ craftingPatternRecipeCache.remove(slot);
+ }
+
/**
* Enables single recipe locking mode.
*/
@@ -85,6 +120,7 @@ public ProcessingLogic clear() {
this.calculatedEut = 0;
this.duration = 0;
this.calculatedParallels = 0;
+ this.craftingPattern = null;
return getThis();
}
@@ -106,6 +142,18 @@ public CheckRecipeResult process() {
inputFluids = new FluidStack[0];
}
+ if (craftingPattern != null) {
+ Set matchedRecipes = craftingPatternRecipeCache.get(craftingPattern);
+ for (GTRecipe matchedRecipe : matchedRecipes) {
+ if (matchedRecipe.maxParallelCalculatedByInputs(1, inputFluids, inputItems) == 1) {
+ CalculationResult foundResult = validateAndCalculateRecipe(matchedRecipe);
+ return foundResult.checkRecipeResult;
+ }
+ }
+ craftingPattern = null;
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
if (isRecipeLocked && recipeLockableMachine != null && recipeLockableMachine.getSingleRecipeCheck() != null) {
// Recipe checker is already built, we'll use it
SingleRecipeCheck singleRecipeCheck = recipeLockableMachine.getSingleRecipeCheck();
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/MTEMultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/MTEMultiBlockBase.java
index 823576d7cb2..755b023b24b 100644
--- a/src/main/java/gregtech/api/metatileentity/implementations/MTEMultiBlockBase.java
+++ b/src/main/java/gregtech/api/metatileentity/implementations/MTEMultiBlockBase.java
@@ -883,16 +883,18 @@ protected boolean supportsCraftingMEBuffer() {
@Nonnull
protected CheckRecipeResult doCheckRecipe() {
CheckRecipeResult result = CheckRecipeResultRegistry.NO_RECIPE;
+
// check crafting input hatches first
- if (supportsCraftingMEBuffer()) {
- for (IDualInputHatch dualInputHatch : mDualInputHatches) {
- for (var it = dualInputHatch.inventories(); it.hasNext();) {
- IDualInputInventory slot = it.next();
- // Reverse order of input items for consistent behavior with standard input buses.
- ItemStack[] inputItems = slot.getItemInputs();
- ArrayUtils.reverse(inputItems);
- processingLogic.setInputItems(inputItems);
+ for (IDualInputHatch dualInputHatch : mDualInputHatches) {
+ ItemStack[] sharedItems = dualInputHatch.getSharedItems();
+ for (var it = dualInputHatch.inventories(); it.hasNext();) {
+ IDualInputInventory slot = it.next();
+
+ if (!slot.isEmpty() && processingLogic.craftingPatternHandler(slot)) {
+
+ processingLogic.setInputItems(ArrayUtils.addAll(sharedItems, slot.getItemInputs()));
processingLogic.setInputFluids(slot.getFluidInputs());
+
CheckRecipeResult foundResult = processingLogic.process();
if (foundResult.wasSuccessful()) {
return foundResult;
@@ -1720,6 +1722,7 @@ public boolean addToMachineList(IGregTechTileEntity aTileEntity, int aBaseCasing
}
if (aMetaTileEntity instanceof IDualInputHatch hatch) {
hatch.updateCraftingIcon(this.getMachineCraftingIcon());
+ hatch.setProcessingLogic(processingLogic);
return mDualInputHatches.add(hatch);
}
if (aMetaTileEntity instanceof ISmartInputHatch hatch) {
diff --git a/src/main/java/gregtech/api/objects/GTDualInputs.java b/src/main/java/gregtech/api/objects/GTDualInputs.java
new file mode 100644
index 00000000000..368892fb8a9
--- /dev/null
+++ b/src/main/java/gregtech/api/objects/GTDualInputs.java
@@ -0,0 +1,10 @@
+package gregtech.api.objects;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+public class GTDualInputs {
+
+ public ItemStack[] inputItems;
+ public FluidStack[] inputFluid;
+}
diff --git a/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java b/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java
index 0660f6b1a1b..31e09d8f461 100644
--- a/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java
+++ b/src/main/java/gregtech/common/tileentities/machines/IDualInputHatch.java
@@ -5,6 +5,8 @@
import net.minecraft.item.ItemStack;
+import gregtech.api.logic.ProcessingLogic;
+
public interface IDualInputHatch {
boolean justUpdated();
@@ -18,4 +20,8 @@ public interface IDualInputHatch {
Optional getFirstNonEmptyInventory();
boolean supportsFluids();
+
+ ItemStack[] getSharedItems();
+
+ void setProcessingLogic(ProcessingLogic pl);
}
diff --git a/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java b/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java
index 01649fe1817..56fedf5e41e 100644
--- a/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java
+++ b/src/main/java/gregtech/common/tileentities/machines/IDualInputInventory.java
@@ -3,9 +3,16 @@
import net.minecraft.item.ItemStack;
import net.minecraftforge.fluids.FluidStack;
+import gregtech.api.objects.GTDualInputs;
+
public interface IDualInputInventory {
+ boolean isEmpty();
+
ItemStack[] getItemInputs();
FluidStack[] getFluidInputs();
+
+ GTDualInputs getPatternInputs();
+
}
diff --git a/src/main/java/gregtech/common/tileentities/machines/MTEHatchCraftingInputME.java b/src/main/java/gregtech/common/tileentities/machines/MTEHatchCraftingInputME.java
index 79c683f4610..fd7d291c29d 100644
--- a/src/main/java/gregtech/common/tileentities/machines/MTEHatchCraftingInputME.java
+++ b/src/main/java/gregtech/common/tileentities/machines/MTEHatchCraftingInputME.java
@@ -41,6 +41,7 @@
import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;
+import com.glodblock.github.common.item.ItemFluidDrop;
import com.glodblock.github.common.item.ItemFluidPacket;
import com.google.common.collect.ImmutableList;
import com.gtnewhorizons.modularui.api.math.Alignment;
@@ -91,8 +92,10 @@
import gregtech.api.interfaces.modularui.IAddGregtechLogo;
import gregtech.api.interfaces.modularui.IAddUIWidgets;
import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
import gregtech.api.metatileentity.MetaTileEntity;
import gregtech.api.metatileentity.implementations.MTEHatchInputBus;
+import gregtech.api.objects.GTDualInputs;
import gregtech.api.render.TextureFactory;
import gregtech.api.util.GTUtility;
import gregtech.api.util.extensions.ArrayExt;
@@ -116,6 +119,7 @@ public interface SharedItemGetter {
private final List itemInventory;
private final List fluidInventory;
private final SharedItemGetter sharedItemGetter;
+ private final GTUtility.ItemId itemId;
public PatternSlot(ItemStack pattern, World world, SharedItemGetter getter) {
this.pattern = pattern;
@@ -124,6 +128,7 @@ public PatternSlot(ItemStack pattern, World world, SharedItemGetter getter) {
this.itemInventory = new ArrayList<>();
this.fluidInventory = new ArrayList<>();
this.sharedItemGetter = getter;
+ this.itemId = GTUtility.ItemId.create(pattern);
}
public PatternSlot(ItemStack pattern, NBTTagCompound nbt, World world, SharedItemGetter getter) {
@@ -133,6 +138,7 @@ public PatternSlot(ItemStack pattern, NBTTagCompound nbt, World world, SharedIte
this.itemInventory = new ArrayList<>();
this.fluidInventory = new ArrayList<>();
this.sharedItemGetter = getter;
+ this.itemId = GTUtility.ItemId.create(pattern);
NBTTagList inv = nbt.getTagList("inventory", Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < inv.tagCount(); i++) {
NBTTagCompound tagItemStack = inv.getCompoundTagAt(i);
@@ -198,14 +204,15 @@ public boolean isFluidEmpty() {
return fluidInventory.isEmpty();
}
+ @Override
public boolean isEmpty() {
return isItemEmpty() && isFluidEmpty();
}
@Override
public ItemStack[] getItemInputs() {
- if (isEmpty()) return new ItemStack[0];
- return ArrayUtils.addAll(itemInventory.toArray(new ItemStack[0]), sharedItemGetter.getSharedItem());
+ if (isItemEmpty()) return new ItemStack[0];
+ return itemInventory.toArray(new ItemStack[0]);
}
@Override
@@ -218,6 +225,43 @@ public ICraftingPatternDetails getPatternDetails() {
return patternDetails;
}
+ @Override
+ public GTDualInputs getPatternInputs() {
+ GTDualInputs dualInputs = new GTDualInputs();
+
+ ItemStack[] inputItems = this.sharedItemGetter.getSharedItem();
+ FluidStack[] inputFluids = new FluidStack[0];
+
+ for (IAEItemStack singleInput : this.getPatternDetails()
+ .getInputs()) {
+ if (singleInput == null) continue;
+ ItemStack singleInputItemStack = singleInput.getItemStack();
+ if (singleInputItemStack.getItem() instanceof ItemFluidDrop) {
+ FluidStack fluidStack = ItemFluidDrop.getFluidStack(singleInputItemStack);
+ if (fluidStack != null) inputFluids = ArrayUtils.addAll(inputFluids, fluidStack);
+ } else {
+ inputItems = ArrayUtils.addAll(inputItems, singleInputItemStack);
+ }
+ }
+
+ dualInputs.inputItems = inputItems;
+ dualInputs.inputFluid = inputFluids;
+ return dualInputs;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PatternSlot that = (PatternSlot) o;
+ return Objects.equals(pattern, that.pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return itemId.hashCode();
+ }
+
public void refund(AENetworkProxy proxy, BaseActionSource src) throws GridAccessException {
IMEMonitor sg = proxy.getStorage()
.getItemInventory();
@@ -324,6 +368,7 @@ public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
private static final int MANUAL_SLOT_WINDOW = 10;
private BaseActionSource requestSource = null;
private @Nullable AENetworkProxy gridProxy = null;
+ public List processingLogics = new ArrayList<>();
// holds all internal inventories
private final PatternSlot[] internalInventory = new PatternSlot[MAX_PATTERN_COUNT];
@@ -785,6 +830,9 @@ private void onPatternChange(int index, ItemStack newItem) {
if (originalPattern.hasChanged(newItem, world)) {
try {
originalPattern.refund(getProxy(), getRequest());
+ for (ProcessingLogic pl : processingLogics) {
+ pl.removeEntryCraftingPatternRecipeCache(originalPattern);
+ }
} catch (GridAccessException ignored) {}
internalInventory[index] = null;
needPatternSync = true;
@@ -803,6 +851,7 @@ private void onPatternChange(int index, ItemStack newItem) {
needPatternSync = true;
}
+ @Override
public ItemStack[] getSharedItems() {
ItemStack[] sharedItems = new ItemStack[SLOT_MANUAL_SIZE + 1];
sharedItems[0] = mInventory[SLOT_CIRCUIT];
@@ -810,6 +859,22 @@ public ItemStack[] getSharedItems() {
return ArrayExt.withoutNulls(sharedItems, ItemStack[]::new);
}
+ @Override
+ public void setProcessingLogic(ProcessingLogic pl) {
+ if (!processingLogics.contains(pl)) {
+ processingLogics.add(Objects.requireNonNull(pl));
+ }
+ }
+
+ private void resetCraftingInputRecipeMap() {
+ for (ProcessingLogic pl : processingLogics) {
+ for (PatternSlot sl : internalInventory) {
+ if (sl == null) continue;
+ pl.removeEntryCraftingPatternRecipeCache(sl);
+ }
+ }
+ }
+
@Override
public void getWailaBody(ItemStack itemStack, List currenttip, IWailaDataAccessor accessor,
IWailaConfigHandler config) {
@@ -982,6 +1047,12 @@ public ItemStack getCrafterIcon() {
return getMachineCraftingIcon();
}
+ @Override
+ public void markDirty() {
+ super.markDirty();
+ resetCraftingInputRecipeMap();
+ }
+
private boolean postMEPatternChange() {
// don't post until it's active
if (!getProxy().isActive()) return false;
@@ -1015,6 +1086,7 @@ protected ModularWindow createSlotManualWindow(final EntityPlayer player) {
.endAtSlot(SLOT_MANUAL_START + SLOT_MANUAL_SIZE - 1)
.phantom(false)
.background(getGUITextureSet().getItemSlot())
+ .widgetCreator(slot -> new SlotWidget(slot).setChangeListener(this::resetCraftingInputRecipeMap))
.build()
.setPos(7, 7));
return builder.build();
diff --git a/src/main/java/gregtech/common/tileentities/machines/MTEHatchCraftingInputSlave.java b/src/main/java/gregtech/common/tileentities/machines/MTEHatchCraftingInputSlave.java
index 35db434a32a..73216f46743 100644
--- a/src/main/java/gregtech/common/tileentities/machines/MTEHatchCraftingInputSlave.java
+++ b/src/main/java/gregtech/common/tileentities/machines/MTEHatchCraftingInputSlave.java
@@ -23,6 +23,7 @@
import gregtech.api.interfaces.IDataCopyable;
import gregtech.api.interfaces.ITexture;
import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.api.logic.ProcessingLogic;
import gregtech.api.metatileentity.MetaTileEntity;
import gregtech.api.metatileentity.implementations.MTEHatchInputBus;
import gregtech.api.render.TextureFactory;
@@ -163,6 +164,11 @@ public boolean supportsFluids() {
return getMaster() != null && getMaster().supportsFluids();
}
+ @Override
+ public ItemStack[] getSharedItems() {
+ return getMaster() != null ? getMaster().getSharedItems() : new ItemStack[0];
+ }
+
@Override
public boolean justUpdated() {
return getMaster() != null && getMaster().justUpdated();
@@ -291,4 +297,9 @@ public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompou
public List getItemsForHoloGlasses() {
return getMaster() != null ? getMaster().getItemsForHoloGlasses() : null;
}
+
+ @Override
+ public void setProcessingLogic(ProcessingLogic pl) {
+ if (getMaster() != null) getMaster().setProcessingLogic(pl);
+ }
}
diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/MTEMultiSolidifier.java b/src/main/java/gregtech/common/tileentities/machines/multi/MTEMultiSolidifier.java
index 65061575a23..ef409934211 100644
--- a/src/main/java/gregtech/common/tileentities/machines/multi/MTEMultiSolidifier.java
+++ b/src/main/java/gregtech/common/tileentities/machines/multi/MTEMultiSolidifier.java
@@ -32,6 +32,7 @@
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.FluidStack;
+import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.NotNull;
import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
@@ -435,12 +436,16 @@ protected CheckRecipeResult doCheckRecipe() {
CheckRecipeResult result = CheckRecipeResultRegistry.NO_RECIPE;
// check crafting input hatches first
- if (supportsCraftingMEBuffer()) {
- for (IDualInputHatch dualInputHatch : mDualInputHatches) {
- for (var it = dualInputHatch.inventories(); it.hasNext();) {
- IDualInputInventory slot = it.next();
- processingLogic.setInputItems(slot.getItemInputs());
+ for (IDualInputHatch dualInputHatch : mDualInputHatches) {
+ ItemStack[] sharedItems = dualInputHatch.getSharedItems();
+ for (var it = dualInputHatch.inventories(); it.hasNext();) {
+ IDualInputInventory slot = it.next();
+
+ if (!slot.isEmpty() && processingLogic.craftingPatternHandler(slot)) {
+
+ processingLogic.setInputItems(ArrayUtils.addAll(sharedItems, slot.getItemInputs()));
processingLogic.setInputFluids(slot.getFluidInputs());
+
CheckRecipeResult foundResult = processingLogic.process();
if (foundResult.wasSuccessful()) {
return foundResult;