From b6339fa83e64874ee7f14ad476203ce11e4c008e Mon Sep 17 00:00:00 2001 From: BlueWeabo Date: Mon, 25 Sep 2023 14:12:04 +0300 Subject: [PATCH] Fix void protection for mutes (#2298) - fix conflicts * initial variables * implement working void protection on items and fluids --- .../gregtech/api/util/GT_ParallelHelper.java | 26 +++- .../api/util/VoidProtectionHelper.java | 124 +++++++++++++++++- 2 files changed, 143 insertions(+), 7 deletions(-) diff --git a/src/main/java/gregtech/api/util/GT_ParallelHelper.java b/src/main/java/gregtech/api/util/GT_ParallelHelper.java index ec26a4310b8..b29742da750 100644 --- a/src/main/java/gregtech/api/util/GT_ParallelHelper.java +++ b/src/main/java/gregtech/api/util/GT_ParallelHelper.java @@ -64,6 +64,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 */ @@ -76,6 +80,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 */ @@ -125,7 +133,7 @@ public class GT_ParallelHelper { * Calculator to use for overclocking */ private GT_OverclockCalculator calculator; - + @Nonnull private CheckRecipeResult result = CheckRecipeResultRegistry.NONE; private Function customItemOutputCalculation; @@ -312,6 +320,17 @@ public GT_ParallelHelper setInputConsumer(InputConsumer inputConsumer) { this.inputConsumer = inputConsumer; 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 @@ -455,7 +474,7 @@ 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(); @@ -463,6 +482,9 @@ protected void determineParallel() { .setItemOutputs(truncatedItemOutputs) .setFluidOutputs(truncatedFluidOutputs) .setMaxParallel(maxParallel) + .setItemOutputInventory(itemOutputInventory) + .setFluidOutputInventory(fluidOutputInventory) + .setMuTEMode(muteMode) .build(); maxParallel = Math.min(voidProtectionHelper.getMaxParallel(), maxParallel); if (voidProtectionHelper.isItemFull()) { diff --git a/src/main/java/gregtech/api/util/VoidProtectionHelper.java b/src/main/java/gregtech/api/util/VoidProtectionHelper.java index 3a41bba934c..245fc24bdae 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; /** * Helper class to calculate how many parallels of items / fluids can fit in the output buses / hatches. @@ -51,10 +54,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() {} @@ -93,6 +108,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 */ @@ -241,11 +271,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<>(); @@ -320,9 +434,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; } @@ -331,10 +445,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;