From a73802762161188fbd8539caba4164e4024fdeb3 Mon Sep 17 00:00:00 2001 From: D-Cysteine <54219287+D-Cysteine@users.noreply.github.com> Date: Sun, 14 Nov 2021 18:49:05 -0700 Subject: Add single recipe check --- .../multi/GT_MetaTileEntity_PyrolyseOven.java | 49 ++++++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) (limited to 'src/main/java/gregtech/common/tileentities/machines') diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java index ae96b82a23..428931e654 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java @@ -16,6 +16,7 @@ import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMul import gregtech.api.render.TextureFactory; import gregtech.api.util.GT_Multiblock_Tooltip_Builder; import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Single_Recipe_Check; import gregtech.api.util.GT_Utility; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; @@ -129,19 +130,49 @@ public class GT_MetaTileEntity_PyrolyseOven extends GT_MetaTileEntity_EnhancedMu } @Override - public boolean checkRecipe(ItemStack aStack) { - ItemStack[] tInputs = getCompactedInputs(); - FluidStack[] tFluids = getCompactedFluids(); - - if (tInputs.length <= 0) - return false; + public boolean supportsSingleRecipeLocking() { + return true; + } + @Override + public boolean checkRecipe(ItemStack aStack) { long tVoltage = getMaxInputVoltage(); byte tTier = (byte) Math.max(1, GT_Utility.getTier(tVoltage)); - GT_Recipe tRecipe = GT_Recipe.GT_Recipe_Map.sPyrolyseRecipes.findRecipe(getBaseMetaTileEntity(), false, gregtech.api.enums.GT_Values.V[tTier], tFluids, tInputs); + GT_Recipe tRecipe; - if (tRecipe == null || !tRecipe.isRecipeInputEqual(true, tFluids, tInputs)) - return false; + if (mLockedToSingleRecipe && mSingleRecipeCheck != null) { + if (!mSingleRecipeCheck.checkRecipeInputsSingleStack(true)) { + return false; + } + + tRecipe = mSingleRecipeCheck.getRecipe(); + } else { + GT_Single_Recipe_Check.Builder tSingleRecipeCheckBuilder = null; + if (mLockedToSingleRecipe) { + // We're locked to a single recipe, but haven't built the recipe checker yet. + // Build the checker on next successful recipe. + tSingleRecipeCheckBuilder = GT_Single_Recipe_Check.builder(this); + } + + ItemStack[] tInputs = getCompactedInputs(); + FluidStack[] tFluids = getCompactedFluids(); + + if (tInputs.length <= 0) + return false; + + if (mLockedToSingleRecipe) { + tSingleRecipeCheckBuilder.setBefore(); + } + + tRecipe = GT_Recipe.GT_Recipe_Map.sPyrolyseRecipes.findRecipe(getBaseMetaTileEntity(), false, gregtech.api.enums.GT_Values.V[tTier], tFluids, tInputs); + + if (tRecipe == null || !tRecipe.isRecipeInputEqual(true, tFluids, tInputs)) + return false; + + if (mLockedToSingleRecipe) { + mSingleRecipeCheck = tSingleRecipeCheckBuilder.setAfter().setRecipe(tRecipe).build(); + } + } this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); this.mEfficiencyIncrease = 10000; -- cgit From b153c31432d22908097dac0e0c0181a1c097aaa4 Mon Sep 17 00:00:00 2001 From: D-Cysteine <54219287+D-Cysteine@users.noreply.github.com> Date: Mon, 15 Nov 2021 01:30:11 -0700 Subject: Add locking support to LCR and PA --- .../GT_MetaTileEntity_LargeChemicalReactor.java | 131 +++++++++++++-------- .../multi/GT_MetaTileEntity_ProcessingArray.java | 104 ++++++++++++---- .../multi/GT_MetaTileEntity_PyrolyseOven.java | 12 +- 3 files changed, 168 insertions(+), 79 deletions(-) (limited to 'src/main/java/gregtech/common/tileentities/machines') diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java index 63b536fc72..87eddb7e13 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_LargeChemicalReactor.java @@ -11,6 +11,7 @@ import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_EnhancedMul import gregtech.api.render.TextureFactory; import gregtech.api.util.GT_Multiblock_Tooltip_Builder; import gregtech.api.util.GT_Recipe; +import gregtech.api.util.GT_Single_Recipe_Check; import gregtech.api.util.GT_Utility; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; @@ -122,69 +123,99 @@ public class GT_MetaTileEntity_LargeChemicalReactor extends GT_MetaTileEntity_En return true; } + @Override + public boolean supportsSingleRecipeLocking() { + return true; + } + @Override public boolean checkRecipe(ItemStack aStack) { - ArrayList tInputList = getStoredInputs(); - int tInputList_sS = tInputList.size(); - for (int i = 0; i < tInputList_sS - 1; i++) { - for (int j = i + 1; j < tInputList_sS; j++) { - if (GT_Utility.areStacksEqual(tInputList.get(i), tInputList.get(j))) { - if (tInputList.get(i).stackSize >= tInputList.get(j).stackSize) { - tInputList.remove(j--); - tInputList_sS = tInputList.size(); - } else { - tInputList.remove(i--); - tInputList_sS = tInputList.size(); - break; + long tVoltage = getMaxInputVoltage(); + byte tier = (byte) Math.max(1, GT_Utility.getTier(tVoltage)); + GT_Recipe tRecipe; + + if (mLockedToSingleRecipe && mSingleRecipeCheck != null) { + if (!mSingleRecipeCheck.checkRecipeInputsSingleStack(true)) { + return false; + } + + tRecipe = mSingleRecipeCheck.getRecipe(); + } else { + ArrayList tInputList = getStoredInputs(); + int tInputList_sS = tInputList.size(); + for (int i = 0; i < tInputList_sS - 1; i++) { + for (int j = i + 1; j < tInputList_sS; j++) { + if (GT_Utility.areStacksEqual(tInputList.get(i), tInputList.get(j))) { + if (tInputList.get(i).stackSize >= tInputList.get(j).stackSize) { + tInputList.remove(j--); + tInputList_sS = tInputList.size(); + } else { + tInputList.remove(i--); + tInputList_sS = tInputList.size(); + break; + } } } } - } - tInputList.add(mInventory[1]); - ItemStack[] inputs = tInputList.toArray(new ItemStack[0]); - - ArrayList tFluidList = getStoredFluids(); - int tFluidList_sS = tFluidList.size(); - for (int i = 0; i < tFluidList_sS - 1; i++) { - for (int j = i + 1; j < tFluidList_sS; j++) { - if (GT_Utility.areFluidsEqual(tFluidList.get(i), tFluidList.get(j))) { - if (tFluidList.get(i).amount >= tFluidList.get(j).amount) { - tFluidList.remove(j--); - tFluidList_sS = tFluidList.size(); - } else { - tFluidList.remove(i--); - tFluidList_sS = tFluidList.size(); - break; + tInputList.add(mInventory[1]); + ItemStack[] inputs = tInputList.toArray(new ItemStack[0]); + + ArrayList tFluidList = getStoredFluids(); + int tFluidList_sS = tFluidList.size(); + for (int i = 0; i < tFluidList_sS - 1; i++) { + for (int j = i + 1; j < tFluidList_sS; j++) { + if (GT_Utility.areFluidsEqual(tFluidList.get(i), tFluidList.get(j))) { + if (tFluidList.get(i).amount >= tFluidList.get(j).amount) { + tFluidList.remove(j--); + tFluidList_sS = tFluidList.size(); + } else { + tFluidList.remove(i--); + tFluidList_sS = tFluidList.size(); + break; + } } } } - } - FluidStack[] fluids = tFluidList.toArray(new FluidStack[0]); + FluidStack[] fluids = tFluidList.toArray(new FluidStack[0]); + + if (inputs.length == 0 && fluids.length == 0) { + return false; + } - if (inputs.length > 0 || fluids.length > 0) { - long tVoltage = getMaxInputVoltage(); - byte tier = (byte) Math.max(1, GT_Utility.getTier(tVoltage)); - GT_Recipe tRecipe = GT_Recipe.GT_Recipe_Map.sMultiblockChemicalRecipes.findRecipe(getBaseMetaTileEntity(), false, + GT_Single_Recipe_Check.Builder tSingleRecipeCheckBuilder = null; + if (mLockedToSingleRecipe) { + // We're locked to a single recipe, but haven't built the recipe checker yet. + // Build the checker on next successful recipe. + tSingleRecipeCheckBuilder = GT_Single_Recipe_Check.builder(this).setBefore(); + } + + tRecipe = GT_Recipe.GT_Recipe_Map.sMultiblockChemicalRecipes.findRecipe(getBaseMetaTileEntity(), false, false, gregtech.api.enums.GT_Values.V[tier], fluids, inputs); - if (tRecipe != null && tRecipe.isRecipeInputEqual(true, fluids, inputs)) { - this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); - this.mEfficiencyIncrease = 10000; - - calculatePerfectOverclockedNessMulti(tRecipe.mEUt, tRecipe.mDuration, 1, tVoltage); - //In case recipe is too OP for that machine - if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1) - return false; - if (this.mEUt > 0) { - this.mEUt = (-this.mEUt); - } - this.mOutputItems = tRecipe.mOutputs; - this.mOutputFluids = tRecipe.mFluidOutputs; - this.updateSlots(); - return true; + if (tRecipe == null || !tRecipe.isRecipeInputEqual(true, fluids, inputs)) { + return false; + } + + if (mLockedToSingleRecipe) { + mSingleRecipeCheck = tSingleRecipeCheckBuilder.setAfter().setRecipe(tRecipe).build(); } } - return false; + + this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); + this.mEfficiencyIncrease = 10000; + + calculatePerfectOverclockedNessMulti(tRecipe.mEUt, tRecipe.mDuration, 1, tVoltage); + //In case recipe is too OP for that machine + if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1) + return false; + if (this.mEUt > 0) { + this.mEUt = (-this.mEUt); + } + + this.mOutputItems = tRecipe.mOutputs; + this.mOutputFluids = tRecipe.mFluidOutputs; + this.updateSlots(); + return true; } @Override diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java index ad0f5bd7f8..d5649ab0e4 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java @@ -19,6 +19,7 @@ import gregtech.api.util.GT_Multiblock_Tooltip_Builder; import gregtech.api.util.GT_ProcessingArray_Manager; import gregtech.api.util.GT_Recipe; import gregtech.api.util.GT_Recipe.GT_Recipe_Map; +import gregtech.api.util.GT_Single_Recipe_Check; import gregtech.api.util.GT_Utility; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.InventoryPlayer; @@ -50,6 +51,12 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu private boolean mSeparate = false; private String mMachineName = ""; + /** If locked to a single recipe, the locked recipe's amperage. */ + private int mSingleRecipeAmperage; + + /** If locked to a single recipe, the single-block machines that were used to run that single recipe. */ + private ItemStack mSingleRecipeMachineStack; + public GT_MetaTileEntity_ProcessingArray(int aID, String aName, String aNameRegional) { super(aID, aName, aNameRegional); } @@ -144,8 +151,17 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu return aStack != null && aStack.getUnlocalizedName().startsWith("gt.blockmachines.basicmachine."); } + @Override + public boolean supportsSingleRecipeLocking() { + return true; + } + @Override public boolean checkRecipe(ItemStack aStack) { + if (mLockedToSingleRecipe && mSingleRecipeCheck != null) { + return processLockedRecipe(); + } + if (!isCorrectMachinePart(mInventory[1])) { return false; } @@ -228,6 +244,27 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu return false; } + public boolean processLockedRecipe() { + if (!GT_Utility.areStacksEqual(mSingleRecipeMachineStack, mInventory[1])) { + // Machine stack was modified. This is not allowed, so stop processing. + return false; + } + + int machines = Math.min(64, mInventory[1].stackSize << mMult); //Upped max Cap to 64 + int i = 0; + for (; i < machines; i++) { + // TODO we should create a separate single recipe check class just for PAs. + // This class can memorize the amperage and machine stack for us, as well as compute + // the max number of runs in a single iteration over the inputs, rather than requiring + // one iteration per machine in the stack. But let's do that after we've tested this. + if (!mSingleRecipeCheck.checkRecipeInputs(true)) { + break; + } + } + + return processRecipeOutputs(mSingleRecipeCheck.getRecipe(), mSingleRecipeAmperage, i); + } + public boolean processRecipe(ItemStack[] tInputs, FluidStack[] tFluids, GT_Recipe.GT_Recipe_Map map) { if (tInputs.length <= 0 && tFluids.length <= 0) return false; GT_Recipe tRecipe = map.findRecipe(getBaseMetaTileEntity(), mLastRecipe, false, gregtech.api.enums.GT_Values.V[tTier], tFluids, tInputs); @@ -236,54 +273,74 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu !isValidForLowGravity(tRecipe, getBaseMetaTileEntity().getWorld().provider.dimensionId)) return false; + GT_Single_Recipe_Check.Builder tSingleRecipeCheckBuilder = null; + if (mLockedToSingleRecipe) { + // We're locked to a single recipe, but haven't built the recipe checker yet. + // Build the checker on next successful recipe. + tSingleRecipeCheckBuilder = GT_Single_Recipe_Check.builder(this).setBefore(); + } + + boolean recipeLocked = false; mLastRecipe = tRecipe; - this.mEUt = 0; - this.mOutputItems = null; - this.mOutputFluids = null; int machines = Math.min(64, mInventory[1].stackSize << mMult); //Upped max Cap to 64 int i = 0; for (; i < machines; i++) { if (!tRecipe.isRecipeInputEqual(true, tFluids, tInputs)) { - if (i == 0) { - return false; - } break; + } else if (mLockedToSingleRecipe && !recipeLocked) { + // We want to lock to a single run of the recipe. + mSingleRecipeCheck = tSingleRecipeCheckBuilder.setAfter().setRecipe(tRecipe).build(); + mSingleRecipeAmperage = map.mAmperage; + mSingleRecipeMachineStack = mInventory[1].copy(); + recipeLocked = true; } } - this.mMaxProgresstime = tRecipe.mDuration; + + return processRecipeOutputs(tRecipe, map.mAmperage, i); + } + + public boolean processRecipeOutputs(GT_Recipe aRecipe, int aAmperage, int parallel) { + this.mEUt = 0; + this.mOutputItems = null; + this.mOutputFluids = null; + if (parallel == 0) { + return false; + } + + this.mMaxProgresstime = aRecipe.mDuration; this.mEfficiency = (10000 - (getIdealStatus() - getRepairStatus()) * 1000); this.mEfficiencyIncrease = 10000; - calculateOverclockedNessMulti(tRecipe.mEUt, tRecipe.mDuration, map.mAmperage, GT_Values.V[tTier]); + calculateOverclockedNessMulti(aRecipe.mEUt, aRecipe.mDuration, aAmperage, GT_Values.V[tTier]); //In case recipe is too OP for that machine if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1) return false; - this.mEUt = GT_Utility.safeInt(((long) this.mEUt * i) >> mMult, 1); + this.mEUt = GT_Utility.safeInt(((long) this.mEUt * parallel) >> mMult, 1); if (mEUt == Integer.MAX_VALUE - 1) return false; if (this.mEUt > 0) { this.mEUt = (-this.mEUt); } - ItemStack[] tOut = new ItemStack[tRecipe.mOutputs.length]; - for (int h = 0; h < tRecipe.mOutputs.length; h++) { - if (tRecipe.getOutput(h) != null) { - tOut[h] = tRecipe.getOutput(h).copy(); + ItemStack[] tOut = new ItemStack[aRecipe.mOutputs.length]; + for (int h = 0; h < aRecipe.mOutputs.length; h++) { + if (aRecipe.getOutput(h) != null) { + tOut[h] = aRecipe.getOutput(h).copy(); tOut[h].stackSize = 0; } } FluidStack tFOut = null; - if (tRecipe.getFluidOutput(0) != null) tFOut = tRecipe.getFluidOutput(0).copy(); + if (aRecipe.getFluidOutput(0) != null) tFOut = aRecipe.getFluidOutput(0).copy(); for (int f = 0; f < tOut.length; f++) { - if (tRecipe.mOutputs[f] != null && tOut[f] != null) { - for (int g = 0; g < i; g++) { - if (getBaseMetaTileEntity().getRandomNumber(10000) < tRecipe.getOutputChance(f)) - tOut[f].stackSize += tRecipe.mOutputs[f].stackSize; + if (aRecipe.mOutputs[f] != null && tOut[f] != null) { + for (int g = 0; g < parallel; g++) { + if (getBaseMetaTileEntity().getRandomNumber(10000) < aRecipe.getOutputChance(f)) + tOut[f].stackSize += aRecipe.mOutputs[f].stackSize; } } } if (tFOut != null) { int tSize = tFOut.amount; - tFOut.amount = tSize * i; + tFOut.amount = tSize * parallel; } this.mMaxProgresstime = Math.max(1, this.mMaxProgresstime); this.mOutputItems = Arrays.stream(tOut) @@ -353,8 +410,13 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu @Override public final void onScrewdriverRightClick(byte aSide, EntityPlayer aPlayer, float aX, float aY, float aZ) { - mSeparate = !mSeparate; - GT_Utility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("GT5U.machines.separatebus") + " " + mSeparate); + if (aPlayer.isSneaking()) { + // Lock to single recipe + super.onScrewdriverRightClick(aSide, aPlayer, aX, aY, aZ); + } else { + mSeparate = !mSeparate; + GT_Utility.sendChatToPlayer(aPlayer, StatCollector.translateToLocal("GT5U.machines.separatebus") + " " + mSeparate); + } } @Override diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java index 428931e654..9542c54ce0 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_PyrolyseOven.java @@ -147,21 +147,17 @@ public class GT_MetaTileEntity_PyrolyseOven extends GT_MetaTileEntity_EnhancedMu tRecipe = mSingleRecipeCheck.getRecipe(); } else { - GT_Single_Recipe_Check.Builder tSingleRecipeCheckBuilder = null; - if (mLockedToSingleRecipe) { - // We're locked to a single recipe, but haven't built the recipe checker yet. - // Build the checker on next successful recipe. - tSingleRecipeCheckBuilder = GT_Single_Recipe_Check.builder(this); - } - ItemStack[] tInputs = getCompactedInputs(); FluidStack[] tFluids = getCompactedFluids(); if (tInputs.length <= 0) return false; + GT_Single_Recipe_Check.Builder tSingleRecipeCheckBuilder = null; if (mLockedToSingleRecipe) { - tSingleRecipeCheckBuilder.setBefore(); + // We're locked to a single recipe, but haven't built the recipe checker yet. + // Build the checker on next successful recipe. + tSingleRecipeCheckBuilder = GT_Single_Recipe_Check.builder(this).setBefore(); } tRecipe = GT_Recipe.GT_Recipe_Map.sPyrolyseRecipes.findRecipe(getBaseMetaTileEntity(), false, gregtech.api.enums.GT_Values.V[tTier], tFluids, tInputs); -- cgit From 9c933933f1d5e99fc618d2e6d5f07238ccb51ba3 Mon Sep 17 00:00:00 2001 From: D-Cysteine <54219287+D-Cysteine@users.noreply.github.com> Date: Mon, 15 Nov 2021 17:40:00 -0700 Subject: Fully optimize PA check --- .../gregtech/api/util/GT_Single_Recipe_Check.java | 33 ++-- .../GT_Single_Recipe_Check_Processing_Array.java | 212 +++++++++++++++++++++ .../multi/GT_MetaTileEntity_ProcessingArray.java | 39 ++-- 3 files changed, 241 insertions(+), 43 deletions(-) create mode 100644 src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java (limited to 'src/main/java/gregtech/common/tileentities/machines') diff --git a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java index bcf205611e..469f2d64a1 100644 --- a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java +++ b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check.java @@ -17,14 +17,15 @@ import java.util.Map; /** Used by machines that are locked to a single recipe, for fast computation. */ public class GT_Single_Recipe_Check { - private final GT_MetaTileEntity_MultiBlockBase multiBlockBase; - private final GT_Recipe recipe; - private final ImmutableMap itemCost; - private final ImmutableMap fluidCost; - private final int totalItemCost; - private final int totalFluidCost; - - private GT_Single_Recipe_Check( + protected final GT_MetaTileEntity_MultiBlockBase multiBlockBase; + protected final GT_Recipe recipe; + protected final ImmutableMap itemCost; + protected final ImmutableMap fluidCost; + + protected final int totalItemCost; + protected final int totalFluidCost; + + protected GT_Single_Recipe_Check( GT_MetaTileEntity_MultiBlockBase multiBlockBase, GT_Recipe recipe, ImmutableMap itemCost, @@ -176,7 +177,7 @@ public class GT_Single_Recipe_Check { return true; } - private static Map buildItemMap( + protected static Map buildItemMap( GT_MetaTileEntity_MultiBlockBase multiBlockBase) { Map itemMap = new HashMap<>(); for (ItemStack itemStack : multiBlockBase.getStoredInputs()) { @@ -185,7 +186,7 @@ public class GT_Single_Recipe_Check { return itemMap; } - private static Map buildFluidMap( + protected static Map buildFluidMap( GT_MetaTileEntity_MultiBlockBase multiBlockBase) { Map fluidMap = new HashMap<>(); for (FluidStack fluidStack : multiBlockBase.getStoredFluids()) { @@ -256,9 +257,9 @@ public class GT_Single_Recipe_Check { } @AutoValue - abstract static class ItemId { + protected abstract static class ItemId { /** This method copies NBT, as it is mutable. */ - private static ItemId create(ItemStack itemStack) { + protected static ItemId create(ItemStack itemStack) { NBTTagCompound nbt = itemStack.getTagCompound(); if (nbt != null) { nbt = (NBTTagCompound) nbt.copy(); @@ -269,15 +270,15 @@ public class GT_Single_Recipe_Check { } /** This method does not copy NBT in order to save time. Make sure not to mutate it! */ - private static ItemId createNoCopy(ItemStack itemStack) { + protected static ItemId createNoCopy(ItemStack itemStack) { return new AutoValue_GT_Single_Recipe_Check_ItemId( itemStack.getItem(), itemStack.getItemDamage(), itemStack.getTagCompound()); } - abstract Item item(); - abstract int metaData(); + protected abstract Item item(); + protected abstract int metaData(); @Nullable - abstract NBTTagCompound nbt(); + protected abstract NBTTagCompound nbt(); } } diff --git a/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java new file mode 100644 index 0000000000..225e0bd723 --- /dev/null +++ b/src/main/java/gregtech/api/util/GT_Single_Recipe_Check_Processing_Array.java @@ -0,0 +1,212 @@ +package gregtech.api.util; + +import com.google.common.collect.ImmutableMap; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** Processing Array-specialized version of {@link gregtech.api.util.GT_Single_Recipe_Check}. */ +public class GT_Single_Recipe_Check_Processing_Array extends GT_Single_Recipe_Check { + protected final int recipeAmperage; + + /** + * The machine type that the locked recipe is for. + * We will not allow running with other machines. + */ + protected final ItemStack machineStack; + + protected GT_Single_Recipe_Check_Processing_Array( + GT_MetaTileEntity_MultiBlockBase multiBlockBase, + GT_Recipe recipe, + ImmutableMap itemCost, + ImmutableMap fluidCost, + int recipeAmperage, + ItemStack machineStack) { + super(multiBlockBase, recipe, itemCost, fluidCost); + + this.recipeAmperage = recipeAmperage; + this.machineStack = machineStack; + } + + public int getRecipeAmperage() { + return recipeAmperage; + } + + @Override + public boolean checkRecipeInputsSingleStack(boolean consumeInputs) { + throw new UnsupportedOperationException("Use checkRecipeInputs(boolean, int) instead."); + } + + @Override + public boolean checkRecipeInputs(boolean consumeInputs) { + throw new UnsupportedOperationException("Use checkRecipeInputs(boolean, int) instead."); + } + + /** Returns the number of parallel recipes, or 0 if recipe is not satisfied at all. */ + public int checkRecipeInputs(boolean consumeInputs, int maxParallel) { + if (!GT_Utility.areStacksEqual(machineStack, multiBlockBase.mInventory[1])) { + // Machine stack was modified. This is not allowed, so stop processing. + return 0; + } + int parallel = maxParallel; + + List items = null; + if (totalItemCost > 0) { + items = multiBlockBase.getStoredInputs(); + + Map itemMap = new HashMap<>(); + for (ItemStack itemStack : items) { + itemMap.merge(ItemId.createNoCopy(itemStack), itemStack.stackSize, Integer::sum); + } + + for (Map.Entry entry : itemCost.entrySet()) { + parallel = Math.min(parallel, itemMap.getOrDefault(entry.getKey(), 0) / entry.getValue()); + if (parallel <= 0) { + return 0; + } + } + } + + List fluids = null; + if (totalFluidCost > 0) { + fluids = multiBlockBase.getStoredFluids(); + + Map fluidMap = new HashMap<>(); + for (FluidStack fluidStack : fluids) { + fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); + } + + for (Map.Entry entry : fluidCost.entrySet()) { + parallel = Math.min(parallel, fluidMap.getOrDefault(entry.getKey(), 0) / entry.getValue()); + if (parallel <= 0) { + return 0; + } + } + } + + final int finalParallel = parallel; + if (consumeInputs) { + if (totalItemCost > 0) { + int remainingItemCost = totalItemCost * finalParallel; + Map runningItemCost = + itemCost.entrySet().stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue() * finalParallel)); + + for (ItemStack itemStack : items) { + ItemId key = ItemId.createNoCopy(itemStack); + int runningCost = runningItemCost.getOrDefault(key, 0); + int paid = Math.min(itemStack.stackSize, runningCost); + itemStack.stackSize -= paid; + runningItemCost.put(key, runningCost - paid); + + remainingItemCost -= paid; + if (remainingItemCost <= 0) { + break; + } + } + } + + if (totalFluidCost > 0) { + int remainingFluidCost = totalFluidCost * finalParallel; + Map runningFluidCost = + fluidCost.entrySet().stream() + .collect( + Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue() * finalParallel)); + + for (FluidStack fluidStack : fluids) { + Fluid key = fluidStack.getFluid(); + int runningCost = runningFluidCost.getOrDefault(key, 0); + int paid = Math.min(fluidStack.amount, runningCost); + fluidStack.amount -= paid; + runningFluidCost.put(key, runningCost - paid); + + remainingFluidCost -= paid; + if (remainingFluidCost <= 0) { + break; + } + } + } + } + + return finalParallel; + } + + public static Builder processingArrayBuilder(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { + return new Builder(multiBlockBase); + } + + public static final class Builder { + private final GT_MetaTileEntity_MultiBlockBase multiBlockBase; + + // In order to compute which items and fluids are consumed by the recipe, we compare the + // multi-block's items and fluids before and after inputs are consumed by the recipe. + private Map beforeItems; + private Map beforeFluids; + private Map afterItems; + private Map afterFluids; + + private GT_Recipe recipe; + private int recipeAmperage; + + private Builder(GT_MetaTileEntity_MultiBlockBase multiBlockBase) { + this.multiBlockBase = multiBlockBase; + } + + /** Call this before inputs are consumed by the recipe. */ + public Builder setBefore() { + beforeItems = buildItemMap(multiBlockBase); + beforeFluids = buildFluidMap(multiBlockBase); + return this; + } + + /** Call this after inputs are consumed by the recipe. */ + public Builder setAfter() { + afterItems = buildItemMap(multiBlockBase); + afterFluids = buildFluidMap(multiBlockBase); + return this; + } + + public Builder setRecipe(GT_Recipe recipe) { + this.recipe = recipe; + return this; + } + + public Builder setRecipeAmperage(int recipeAmperage) { + this.recipeAmperage = recipeAmperage; + return this; + } + + public GT_Single_Recipe_Check_Processing_Array build() { + ImmutableMap.Builder itemCostBuilder = ImmutableMap.builder(); + for (Map.Entry entry : beforeItems.entrySet()) { + int cost = entry.getValue() - afterItems.getOrDefault(entry.getKey(), 0); + if (cost > 0) { + itemCostBuilder.put(entry.getKey(), cost); + } + } + + ImmutableMap.Builder fluidCostBuilder = ImmutableMap.builder(); + for (Map.Entry entry : beforeFluids.entrySet()) { + int cost = entry.getValue() - afterFluids.getOrDefault(entry.getKey(), 0); + if (cost > 0) { + fluidCostBuilder.put(entry.getKey(), cost); + } + } + + return new GT_Single_Recipe_Check_Processing_Array( + multiBlockBase, recipe, itemCostBuilder.build(), fluidCostBuilder.build(), + recipeAmperage, multiBlockBase.mInventory[1].copy()); + } + } +} \ No newline at end of file diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java index d5649ab0e4..a48f63c60a 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_ProcessingArray.java @@ -19,7 +19,7 @@ import gregtech.api.util.GT_Multiblock_Tooltip_Builder; import gregtech.api.util.GT_ProcessingArray_Manager; import gregtech.api.util.GT_Recipe; import gregtech.api.util.GT_Recipe.GT_Recipe_Map; -import gregtech.api.util.GT_Single_Recipe_Check; +import gregtech.api.util.GT_Single_Recipe_Check_Processing_Array; import gregtech.api.util.GT_Utility; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.InventoryPlayer; @@ -51,12 +51,6 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu private boolean mSeparate = false; private String mMachineName = ""; - /** If locked to a single recipe, the locked recipe's amperage. */ - private int mSingleRecipeAmperage; - - /** If locked to a single recipe, the single-block machines that were used to run that single recipe. */ - private ItemStack mSingleRecipeMachineStack; - public GT_MetaTileEntity_ProcessingArray(int aID, String aName, String aNameRegional) { super(aID, aName, aNameRegional); } @@ -245,24 +239,12 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu } public boolean processLockedRecipe() { - if (!GT_Utility.areStacksEqual(mSingleRecipeMachineStack, mInventory[1])) { - // Machine stack was modified. This is not allowed, so stop processing. - return false; - } + GT_Single_Recipe_Check_Processing_Array tSingleRecipeCheck = (GT_Single_Recipe_Check_Processing_Array) mSingleRecipeCheck; int machines = Math.min(64, mInventory[1].stackSize << mMult); //Upped max Cap to 64 - int i = 0; - for (; i < machines; i++) { - // TODO we should create a separate single recipe check class just for PAs. - // This class can memorize the amperage and machine stack for us, as well as compute - // the max number of runs in a single iteration over the inputs, rather than requiring - // one iteration per machine in the stack. But let's do that after we've tested this. - if (!mSingleRecipeCheck.checkRecipeInputs(true)) { - break; - } - } + int parallel = tSingleRecipeCheck.checkRecipeInputs(true, machines); - return processRecipeOutputs(mSingleRecipeCheck.getRecipe(), mSingleRecipeAmperage, i); + return processRecipeOutputs(tSingleRecipeCheck.getRecipe(), tSingleRecipeCheck.getRecipeAmperage(), parallel); } public boolean processRecipe(ItemStack[] tInputs, FluidStack[] tFluids, GT_Recipe.GT_Recipe_Map map) { @@ -273,11 +255,11 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu !isValidForLowGravity(tRecipe, getBaseMetaTileEntity().getWorld().provider.dimensionId)) return false; - GT_Single_Recipe_Check.Builder tSingleRecipeCheckBuilder = null; + GT_Single_Recipe_Check_Processing_Array.Builder tSingleRecipeCheckBuilder = null; if (mLockedToSingleRecipe) { // We're locked to a single recipe, but haven't built the recipe checker yet. // Build the checker on next successful recipe. - tSingleRecipeCheckBuilder = GT_Single_Recipe_Check.builder(this).setBefore(); + tSingleRecipeCheckBuilder = GT_Single_Recipe_Check_Processing_Array.processingArrayBuilder(this).setBefore(); } boolean recipeLocked = false; @@ -289,9 +271,12 @@ public class GT_MetaTileEntity_ProcessingArray extends GT_MetaTileEntity_CubicMu break; } else if (mLockedToSingleRecipe && !recipeLocked) { // We want to lock to a single run of the recipe. - mSingleRecipeCheck = tSingleRecipeCheckBuilder.setAfter().setRecipe(tRecipe).build(); - mSingleRecipeAmperage = map.mAmperage; - mSingleRecipeMachineStack = mInventory[1].copy(); + mSingleRecipeCheck = + tSingleRecipeCheckBuilder + .setAfter() + .setRecipe(tRecipe) + .setRecipeAmperage(map.mAmperage) + .build(); recipeLocked = true; } } -- cgit