diff --git a/src/main/java/gregtech/api/util/GT_ParallelHelper.java b/src/main/java/gregtech/api/util/GT_ParallelHelper.java index 2e8867a44bc..6384c407115 100644 --- a/src/main/java/gregtech/api/util/GT_ParallelHelper.java +++ b/src/main/java/gregtech/api/util/GT_ParallelHelper.java @@ -63,6 +63,10 @@ public class GT_ParallelHelper { * The inputs of the machine for current recipe check */ private ItemInventoryLogic itemInputInventory; + /** + * The output item inventory of the machine + */ + private ItemInventoryLogic itemOutputInventory; /** * The outputs of the recipe with the applied parallel */ @@ -75,6 +79,10 @@ public class GT_ParallelHelper { * The inputs of the machine for the current recipe check */ private FluidInventoryLogic fluidInputInventory; + /** + * The output fluid inventory of the machine; + */ + private FluidInventoryLogic fluidOutputInventory; /** * The outputs of the recipe with the applied parallel */ @@ -117,7 +125,7 @@ public class GT_ParallelHelper { * Calculator to use for overclocking */ private GT_OverclockCalculator calculator; - + @Nonnull private CheckRecipeResult result = CheckRecipeResultRegistry.NONE; private Function customItemOutputCalculation; @@ -330,6 +338,21 @@ public GT_ParallelHelper setFluidInputInventory(FluidInventoryLogic fluidInputIn return this; } + /** + * + */ + @Nonnull + public GT_ParallelHelper setItemOutputInventory(ItemInventoryLogic itemOutputInventory) { + this.itemOutputInventory = itemOutputInventory; + return this; + } + + @Nonnull + public GT_ParallelHelper setFluidOutputInventory(FluidInventoryLogic fluidOutputInventory) { + this.fluidOutputInventory = fluidOutputInventory; + return this; + } + /** * Finishes the GT_ParallelHelper. Anything changed after this will not effect anything */ @@ -493,14 +516,17 @@ protected void determineParallel() { // Let's look at how many parallels we can get with void protection if (protectExcessItem || protectExcessFluid) { - if (machine == null) { + if (machine == null && !muteMode) { throw new IllegalStateException("Tried to calculate void protection, but machine is not set"); } VoidProtectionHelper voidProtectionHelper = new VoidProtectionHelper(); - voidProtectionHelper.setMachine(machine) + voidProtectionHelper.setMachine(machine, protectExcessItem, protectExcessFluid) .setItemOutputs(recipe.mOutputs) .setFluidOutputs(recipe.mFluidOutputs) .setMaxParallel(maxParallel) + .setItemOutputInventory(itemOutputInventory) + .setFluidOutputInventory(fluidOutputInventory) + .setMuTEMode(muteMode) .build(); maxParallel = Math.min(voidProtectionHelper.getMaxParallel(), maxParallel); if (maxParallel <= 0) { diff --git a/src/main/java/gregtech/api/util/VoidProtectionHelper.java b/src/main/java/gregtech/api/util/VoidProtectionHelper.java index 67e412bc833..2ba86c4fd87 100644 --- a/src/main/java/gregtech/api/util/VoidProtectionHelper.java +++ b/src/main/java/gregtech/api/util/VoidProtectionHelper.java @@ -10,9 +10,12 @@ import net.minecraftforge.fluids.FluidStack; import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; +import com.gtnewhorizons.modularui.api.fluids.IFluidTankLong; import gregtech.api.interfaces.fluid.IFluidStore; import gregtech.api.interfaces.tileentity.IVoidable; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; /** @@ -44,10 +47,22 @@ public class VoidProtectionHelper { * The fluid outputs to check */ private FluidStack[] fluidOutputs; + /** + * The item output inventory + */ + private ItemInventoryLogic itemOutputInventory; + /** + * The fluid output inventory + */ + private FluidInventoryLogic fluidOutputInventory; /** * Has this helper been built? */ private boolean built; + /** + * Is this helper working for a MuTE? + */ + private boolean muteMode; public VoidProtectionHelper() {} @@ -107,6 +122,21 @@ public VoidProtectionHelper setMaxParallel(int maxParallel) { return this; } + public VoidProtectionHelper setItemOutputInventory(ItemInventoryLogic itemOutputInventory) { + this.itemOutputInventory = itemOutputInventory; + return this; + } + + public VoidProtectionHelper setFluidOutputInventory(FluidInventoryLogic fluidOutputInventory) { + this.fluidOutputInventory = fluidOutputInventory; + return this; + } + + public VoidProtectionHelper setMuTEMode(boolean muteMode) { + this.muteMode = muteMode; + return this; + } + /** * Finishes the VoidProtectionHelper. Anything changed after this will not affect anything */ @@ -149,7 +179,8 @@ private void determineParallel() { maxParallel = Math.min(calculateMaxItemParallels(), maxParallel); } if (protectExcessFluid && fluidOutputs.length > 0 && !machine.canDumpFluidToME()) { - maxParallel = Math.min(calculateMaxFluidParallels(), maxParallel); + maxParallel = Math + .min(muteMode ? calculateMaxFluidParallelsMuTE() : calculateMaxFluidParallels(), maxParallel); } } @@ -228,11 +259,95 @@ private int calculateMaxFluidParallels() { return aParallelQueue.element().batch; } + private int calculateMaxFluidParallelsMuTE() { + if (fluidOutputs.length > fluidOutputInventory.getInventory() + .getTanks()) { + return 0; + } + + // A map to hold the items we will be 'inputting' into the output hatches. These fluidstacks are actually + // the recipe outputs. + Map tFluidOutputMap = new HashMap<>(); + + // Map that keeps track of the number of parallel crafts we can accommodate for each fluid output. + // In the pair, we keep track of number of full crafts plus mb of fluid in a partial craft, to avoid + // issues with floating point math not being completely accurate when summing. + Map tParallels = new HashMap<>(); + + // Iterate over the outputs, calculating require stack spacing they will require. + for (FluidStack aY : fluidOutputs) { + if (aY == null || aY.amount <= 0) { + continue; + } + tFluidOutputMap.merge(aY, aY.amount, Integer::sum); + tParallels.put(aY, new ParallelData(0, 0)); + } + + if (tFluidOutputMap.isEmpty()) { + // nothing to output, bail early + return maxParallel; + } + + for (int i = 0; i < fluidOutputInventory.getInventory() + .getTanks(); i++) { + IFluidTankLong tank = fluidOutputInventory.getInventory() + .getFluidTank(i); + long tSpaceLeft = tank.getCapacityLong() - tank.getFluidAmountLong(); + // check if hatch filled + if (tSpaceLeft <= 0) continue; + // check if hatch is empty and unrestricted + if (tank.getStoredFluid() == null) continue; + + for (Map.Entry entry : tParallels.entrySet()) { + FluidStack tFluidOutput = entry.getKey(); + if (tank.fill(tFluidOutput.getFluid(), tFluidOutput.amount, false) == tFluidOutput.amount) continue; + // this fluid is not prevented by restrictions on output hatch + ParallelData tParallel = entry.getValue(); + Integer tCraftSize = tFluidOutputMap.get(tFluidOutput); + tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize; + tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize; + } + } + // now that all partial/restricted hatches have been counted, create a priority queue for our outputs + // the lowest priority fluid is the number of complete parallel crafts we can support + PriorityQueue> aParallelQueue = new PriorityQueue<>( + Comparator.comparing(i -> i.batch)); + for (Map.Entry entry : tParallels.entrySet()) { + aParallelQueue + .add(new ParallelStackInfo<>(entry.getValue().batch, entry.getValue().partial, entry.getKey())); + } + // add extra parallels for open slots as well + for (int i = 0; i < fluidOutputInventory.getInventory() + .getTanks(); i++) { + IFluidTankLong tank = fluidOutputInventory.getInventory() + .getFluidTank(i); + // partially filled or restricted hatch. done in the last pass + if (tank.getStoredFluid() != null) continue; + + ParallelStackInfo tParallel = aParallelQueue.poll(); + assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings + Integer tCraftSize = tFluidOutputMap.get(tParallel.stack); + long tSpaceLeft = tank.getCapacityLong(); + tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize; + tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize; + aParallelQueue.add(tParallel); + } + + return aParallelQueue.element().batch; + } + /** * Calculates the max parallels one can do with items if void protection is on */ private int calculateMaxItemParallels() { - List busStacks = machine.getItemOutputSlots(itemOutputs); + List busStacks; + + if (muteMode) { + busStacks = itemOutputInventory.getInventory() + .getStacks(); + } else { + busStacks = machine.getItemOutputSlots(itemOutputs); + } // A map to hold the items we will be 'inputting' into the output buses. These itemstacks are actually the // recipe outputs. Map tItemOutputMap = new ItemStackMap<>(); @@ -307,9 +422,9 @@ private int calculateMaxItemParallels() { private static class ParallelData { private int batch; - private int partial; + private long partial; - private ParallelData(int batch, int partial) { + private ParallelData(int batch, long partial) { this.batch = batch; this.partial = partial; } @@ -318,10 +433,10 @@ private ParallelData(int batch, int partial) { private static class ParallelStackInfo { private int batch; - private int partial; + private long partial; private final T stack; - private ParallelStackInfo(int batch, int partial, T stack) { + private ParallelStackInfo(int batch, long partial, T stack) { this.batch = batch; this.partial = partial; this.stack = stack;