diff options
author | miozune <miozune@gmail.com> | 2023-05-26 21:09:03 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-26 14:09:03 +0200 |
commit | d0e291fa8cb31cedcf9ef8fbafb536f19b216907 (patch) | |
tree | 2e7474220b3f7cbeeb9db8717f010786397934e9 /src/main | |
parent | 497080d591db8af491ece783bf9a480b50a7c16b (diff) | |
download | GT5-Unofficial-d0e291fa8cb31cedcf9ef8fbafb536f19b216907.tar.gz GT5-Unofficial-d0e291fa8cb31cedcf9ef8fbafb536f19b216907.tar.bz2 GT5-Unofficial-d0e291fa8cb31cedcf9ef8fbafb536f19b216907.zip |
Add void protection mode to fluid and ore drillers (#2023)
* Refactor GT_ParallelHelper part 1
* Refactor GT_ParallelHelper part 2
* Move void protection logic to separated class
* Clean doc
* Fix inverted logic in VoidProtectionHelper
* Add void protection mode to fluid and ore drillers
Diffstat (limited to 'src/main')
8 files changed, 651 insertions, 420 deletions
diff --git a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java index 94392d089c..7eacf05b4e 100644 --- a/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java +++ b/src/main/java/gregtech/api/metatileentity/implementations/GT_MetaTileEntity_MultiBlockBase.java @@ -9,6 +9,8 @@ import static mcp.mobius.waila.api.SpecialChars.RESET; import java.util.ArrayList; import java.util.List; +import javax.annotation.Nullable; + import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -120,6 +122,7 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorLow", 5); this.damageFactorHigh = (float) GregTech_API.sMachineFile .get(ConfigCategories.machineconfig, "MultiBlockMachines.damageFactorHigh", 0.6f); + voidExcess = !isVoidExcessButtonEnabled(); } public static boolean isValidMetaTileEntity(MetaTileEntity aMetaTileEntity) { @@ -933,12 +936,19 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity } public boolean depleteInput(FluidStack aLiquid) { + return depleteInput(aLiquid, false); + } + + public boolean depleteInput(FluidStack aLiquid, boolean simulate) { if (aLiquid == null) return false; for (GT_MetaTileEntity_Hatch_Input tHatch : mInputHatches) { tHatch.mRecipeMap = getRecipeMap(); if (isValidMetaTileEntity(tHatch)) { FluidStack tLiquid = tHatch.drain(ForgeDirection.UNKNOWN, aLiquid, false); if (tLiquid != null && tLiquid.amount >= aLiquid.amount) { + if (simulate) { + return true; + } tLiquid = tHatch.drain(ForgeDirection.UNKNOWN, aLiquid, true); return tLiquid != null && tLiquid.amount >= aLiquid.amount; } @@ -1512,6 +1522,38 @@ public abstract class GT_MetaTileEntity_MultiBlockBase extends MetaTileEntity return true; } + /** + * Checks if items can fit in the output buses. If your outputs come from GT_Recipe, + * {@link GT_ParallelHelper} will work better. + */ + protected boolean canFitOutput(ItemStack[] items) { + return canFitOutput(items, null); + } + + /** + * Checks if fluids can fit in the output hatches. If your outputs come from GT_Recipe, + * {@link GT_ParallelHelper} will work better. + */ + protected boolean canFitOutput(FluidStack[] fluids) { + return canFitOutput(null, fluids); + } + + /** + * Checks if items / fluids can fit in the output buses / hatches. If your outputs come from GT_Recipe, + * {@link GT_ParallelHelper} will work better. + */ + protected boolean canFitOutput(@Nullable ItemStack[] items, @Nullable FluidStack[] fluids) { + VoidProtectionHelper voidProtectionHelper = new VoidProtectionHelper().setController(this); + if (items != null) { + voidProtectionHelper.setItemOutputs(items); + } + if (fluids != null) { + voidProtectionHelper.setFluidOutputs(fluids); + } + voidProtectionHelper.build(); + return voidProtectionHelper.getMaxParallel() > 0; + } + @Override public boolean useModularUI() { return true; diff --git a/src/main/java/gregtech/api/util/GT_ParallelHelper.java b/src/main/java/gregtech/api/util/GT_ParallelHelper.java index 4e281e976f..04218f4566 100644 --- a/src/main/java/gregtech/api/util/GT_ParallelHelper.java +++ b/src/main/java/gregtech/api/util/GT_ParallelHelper.java @@ -1,83 +1,93 @@ package gregtech.api.util; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.PriorityQueue; - -import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; -import org.apache.commons.lang3.tuple.MutablePair; -import org.apache.commons.lang3.tuple.MutableTriple; - -import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; - -import gregtech.api.fluid.FluidTankGT; -import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch; -import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch_Output; import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; import gregtech.api.multitileentity.multiblock.base.Controller; import gregtech.api.objects.XSTR; -import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_OutputBus_ME; -import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Output_ME; public class GT_ParallelHelper { /** - * @mMachineMeta a MetaTileEntity Controller + * A MetaTileEntity Controller */ private GT_MetaTileEntity_MultiBlockBase mMachineMeta; /** - * @mMachineMulti a MultiTileEntity Controller + * A MultiTileEntity Controller */ private Controller<?> mMachineMulti; /** - * @mRecipe Recipe used when trying to calculate parallels + * Recipe used when trying to calculate parallels */ private GT_Recipe mRecipe; /** - * @mAvailableEUt EUt available to the multiblock (This should be the total eut available) + * EUt available to the multiblock (This should be the total eut available) */ private long mAvailableEUt; /** - * @mCurrentParallel The current parallel possible for the multiblock - * @mMaxParallel The maximum possible parallel possible for the multiblock - * @mBatchModifier The Batch Modifier applied when batch mode is enabled. 1 does nothing. 2 doubles max possible - * parallel, but also duration + * The current parallel possible for the multiblock + */ + private int mCurrentParallel = 0; + /** + * The maximum possible parallel possible for the multiblock + */ + private int mMaxParallel = 1; + /** + * The Batch Modifier applied when batch mode is enabled. 1 does nothing. 2 doubles max possible + * parallel, but also duration + */ + private int mBatchModifier = 1; + /** + * The inputs of the multiblock for the current recipe check + */ + private ItemStack[] mItemInputs; + /** + * The outputs of the recipe with the applied parallel + */ + private ItemStack[] mItemOutputs; + /** + * The inputs of the multiblock for the current recipe check + */ + private FluidStack[] mFluidInputs; + /** + * The outputs of the recipe with the applied parallel */ - private int mCurrentParallel = 0, mMaxParallel = 1, mBatchModifier = 1; + private FluidStack[] mFluidOutputs; /** - * @mItemInputs The inputs of the multiblock for the current recipe check - * @mItemOutputs The outputs of the recipe with the applied parallel + * Does the multi have void protection enabled */ - private ItemStack[] mItemInputs, mItemOutputs; + private boolean mVoidProtection; /** - * @mFluidInputs The inputs of the multiblock for the current recipe check - * @mFluidOutputs The outputs of the recipe with the applied parallel + * Should the Parallel Helper automatically consume for the multi */ - private FluidStack[] mFluidInputs, mFluidOutputs; + private boolean mConsume; /** - * @mVoidProtection Does the multi have void protection enabled - * @mConsume Should the Parallel Helper automatically consume for the multi - * @mBatchMode Is batch mode turned on? - * @mCalculateOutputs Should the Parallel Helper automatically calculate the outputs of the recipe with current - * parallel - * @mBuilt Has the Parallel Helper been built? + * Is batch mode turned on? */ - private boolean mVoidProtection, mConsume, mBatchMode, mCalculateOutputs, mBuilt; + private boolean mBatchMode; /** - * @mDurationMultiplier What is the duration multiplier with batch mode enabled - * @mEUtModifier Modifier which is applied on the recipe eut. Useful for GT++ machines + * Should the Parallel Helper automatically calculate the outputs of the recipe with current + * parallel */ - private float mDurationMultiplier, mEUtModifier = 1; + private boolean mCalculateOutputs; + /** + * Has the Parallel Helper been built? + */ + private boolean mBuilt; + /** + * What is the duration multiplier with batch mode enabled + */ + private float mDurationMultiplier; + /** + * Modifier which is applied on the recipe eut. Useful for GT++ machines + */ + private float mEUtModifier = 1; public GT_ParallelHelper() {} /** - * Enables void protection on a metatile multiblock. Experimental! Still needs to be worked on + * Enables void protection on a metatile multiblock. */ public GT_ParallelHelper enableVoidProtection(GT_MetaTileEntity_MultiBlockBase aMachineMeta) { mVoidProtection = true; @@ -86,7 +96,7 @@ public class GT_ParallelHelper { } /** - * Enables void protection on a multitile multiblock. Experimental! Still needs to be worked on + * Enables void protection on a multitile multiblock. */ public GT_ParallelHelper enableVoidProtection(Controller<?> aMachineMulti) { mVoidProtection = true; @@ -235,8 +245,6 @@ public class GT_ParallelHelper { } ItemStack[] tItemInputs = null; FluidStack[] tFluidInputs = null; - boolean tMEOutputBus = false; - boolean tMEOutputHatch = false; long tCurrentUsage = 0; // see if people want to consume their inputs with the Parallel Helper or not if (mConsume) { @@ -266,31 +274,17 @@ public class GT_ParallelHelper { } // Let's look at how many parallels we can get with void protection if (mVoidProtection) { + VoidProtectionHelper voidProtectionHelper = new VoidProtectionHelper(); if (mMachineMeta != null) { - for (GT_MetaTileEntity_Hatch tHatch : mMachineMeta.mOutputBusses) { - if (tHatch instanceof GT_MetaTileEntity_Hatch_OutputBus_ME) { - tMEOutputBus = true; - break; - } - } - - for (GT_MetaTileEntity_Hatch tHatch : mMachineMeta.mOutputHatches) { - if (tHatch instanceof GT_MetaTileEntity_Hatch_Output_ME) { - tMEOutputHatch = true; - break; - } - } - if (!tMEOutputBus) { - mMaxParallel = Math.min(calculateMaxParallelsForBusses(), mMaxParallel); - } - - if (!tMEOutputHatch) { - mMaxParallel = Math.min(calculateMaxParallelsForHatches(), mMaxParallel); - } + voidProtectionHelper.setController(mMachineMeta); } else if (mMachineMulti != null) { - mMaxParallel = Math.min(calculateMaxItemParallelsForMuTEs(), mMaxParallel); - mMaxParallel = Math.min(calculateMaxFluidParallelsForMuTEs(), mMaxParallel); + voidProtectionHelper.setController(mMachineMulti); } + voidProtectionHelper.setItemOutputs(mRecipe.mOutputs) + .setFluidOutputs(mRecipe.mFluidOutputs) + .setMaxParallel(mMaxParallel) + .build(); + mMaxParallel = Math.min(voidProtectionHelper.getMaxParallel(), mMaxParallel); } float tRecipeEUt = mRecipe.mEUt * mEUtModifier; @@ -348,324 +342,4 @@ public class GT_ParallelHelper { } } } - - /** - * Calculates the max parallel for fluids if void protection is turned on - */ - private int calculateMaxFluidParallelsForMuTEs() { - if (mMachineMulti != null && mMachineMulti.getOutputTanks() != null - && mMachineMulti.getOutputTanks().length >= mRecipe.mFluidOutputs.length) { - // A map to hold the items we will be 'inputting' into the output hatches. These fluidstacks are actually - // the - // recipe outputs. - Map<FluidStack, Integer> 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<FluidStack, MutablePair<Integer, Integer>> tParallels = new HashMap<>(); - - // Iterate over the outputs, calculating require stack spacing they will require. - for (FluidStack aY : mRecipe.mFluidOutputs) { - if (aY == null) { - continue; - } - tFluidOutputMap.merge(aY, aY.amount, Integer::sum); - tParallels.put(aY, new MutablePair<>(0, 0)); - } - - if (tFluidOutputMap.isEmpty()) { - // nothing to output, bail early - return mMaxParallel; - } - - for (FluidTankGT tHatch : mMachineMulti.getOutputTanks()) { - int tSpaceLeft = tHatch.getCapacity() - tHatch.getFluidAmount(); - - // check if hatch filled - if (tSpaceLeft <= 0) continue; - - // check if hatch is empty and unrestricted - if (tHatch.getFluidAmount() == 0) continue; - - for (Entry<FluidStack, MutablePair<Integer, Integer>> entry : tParallels.entrySet()) { - FluidStack tFluidOutput = entry.getKey(); - // this fluid is not prevented by restrictions on output hatch - if (tHatch.getFluidAmount() == 0 || GT_Utility.areFluidsEqual(tHatch.getFluid(), tFluidOutput)) { - MutablePair<Integer, Integer> tParallel = entry.getValue(); - Integer tCraftSize = tFluidOutputMap.get(tFluidOutput); - tParallel.left += (tParallel.right + tSpaceLeft) / tCraftSize; - tParallel.right = (tParallel.right + 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<MutableTriple<Integer, Integer, FluidStack>> aParallelQueue = new PriorityQueue<>( - Comparator.comparing(MutableTriple::getLeft)); - for (Entry<FluidStack, MutablePair<Integer, Integer>> entry : tParallels.entrySet()) { - aParallelQueue.add(new MutableTriple<>(entry.getValue().left, entry.getValue().right, entry.getKey())); - } - // add extra parallels for open slots as well - for (FluidTankGT tHatch : mMachineMulti.getOutputTanks()) { - // partially filled or restricted hatch. done in last pass - if (tHatch.getFluidAmount() > 0) continue; - - MutableTriple<Integer, Integer, FluidStack> tParallel = aParallelQueue.poll(); - assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings - Integer tCraftSize = tFluidOutputMap.get(tParallel.right); - int tSpaceLeft = tHatch.getCapacity(); - tParallel.left += (tParallel.middle + tSpaceLeft) / tCraftSize; - tParallel.middle = (tParallel.middle + tSpaceLeft) % tCraftSize; - aParallelQueue.add(tParallel); - } - return aParallelQueue.element().left; - } - return 0; - } - - /** - * Calculates the max parallel for fluids if void protection is turned on - */ - private int calculateMaxParallelsForHatches() { - // For now we are gonna ignore MuTEs existence as there are no recipes for them - if (mMachineMeta != null && mMachineMeta.mOutputHatches.size() >= mRecipe.mFluidOutputs.length) { - // A map to hold the items we will be 'inputting' into the output hatches. These fluidstacks are actually - // the - // recipe outputs. - Map<FluidStack, Integer> 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<FluidStack, MutablePair<Integer, Integer>> tParallels = new HashMap<>(); - - // Iterate over the outputs, calculating require stack spacing they will require. - for (FluidStack aY : mRecipe.mFluidOutputs) { - if (aY == null) { - continue; - } - tFluidOutputMap.merge(aY, aY.amount, Integer::sum); - tParallels.put(aY, new MutablePair<>(0, 0)); - } - - if (tFluidOutputMap.isEmpty()) { - // nothing to output, bail early - return mMaxParallel; - } - - for (GT_MetaTileEntity_Hatch_Output tHatch : mMachineMeta.mOutputHatches) { - int tSpaceLeft = tHatch.getCapacity() - tHatch.getFluidAmount(); - - // check if hatch filled - if (tSpaceLeft <= 0) continue; - - // check if hatch is empty and unrestricted - if (tHatch.mMode == 0 && tHatch.getFluidAmount() == 0) continue; - - String tLockedFluidName = tHatch.getLockedFluidName(); - for (Entry<FluidStack, MutablePair<Integer, Integer>> entry : tParallels.entrySet()) { - FluidStack tFluidOutput = entry.getKey(); - if (GT_ModHandler.isSteam(tFluidOutput)) { - if (!tHatch.outputsSteam()) { - continue; - } - } else { - if (!tHatch.outputsLiquids()) { - continue; - } - if (tHatch.isFluidLocked() && tLockedFluidName != null - && !tLockedFluidName.equals( - tFluidOutput.getFluid() - .getName())) { - continue; - } - } - // this fluid is not prevented by restrictions on output hatch - if (tHatch.getFluidAmount() == 0 || GT_Utility.areFluidsEqual(tHatch.getFluid(), tFluidOutput)) { - MutablePair<Integer, Integer> tParallel = entry.getValue(); - Integer tCraftSize = tFluidOutputMap.get(tFluidOutput); - tParallel.left += (tParallel.right + tSpaceLeft) / tCraftSize; - tParallel.right = (tParallel.right + 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<MutableTriple<Integer, Integer, FluidStack>> aParallelQueue = new PriorityQueue<>( - Comparator.comparing(MutableTriple::getLeft)); - for (Entry<FluidStack, MutablePair<Integer, Integer>> entry : tParallels.entrySet()) { - aParallelQueue.add(new MutableTriple<>(entry.getValue().left, entry.getValue().right, entry.getKey())); - } - // add extra parallels for open slots as well - for (GT_MetaTileEntity_Hatch_Output tHatch : mMachineMeta.mOutputHatches) { - // partially filled or restricted hatch. done in last pass - if (tHatch.getFluidAmount() > 0 || tHatch.mMode != 0) continue; - - MutableTriple<Integer, Integer, FluidStack> tParallel = aParallelQueue.poll(); - assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings - Integer tCraftSize = tFluidOutputMap.get(tParallel.right); - int tSpaceLeft = tHatch.getCapacity(); - tParallel.left += (tParallel.middle + tSpaceLeft) / tCraftSize; - tParallel.middle = (tParallel.middle + tSpaceLeft) % tCraftSize; - aParallelQueue.add(tParallel); - } - return aParallelQueue.element().left; - } - return 0; - } - - /** - * Calculates the max parallels one can do with items if void protection is on - */ - private int calculateMaxItemParallelsForMuTEs() { - - // A map to hold the items we will be 'inputting' into the output buses. These itemstacks are actually the - // recipe outputs. - Map<ItemStack, Integer> tItemOutputMap = new ItemStackMap<>(); - - // Map that keeps track of the number of parallel crafts we can accommodate for each item output. - // In the pair, we keep track of number of full crafts plus number of items in a partial craft, to avoid - // issues with floating point math not being completely accurate when summing. - Map<ItemStack, MutablePair<Integer, Integer>> tParallels = new ItemStackMap<>(); - int tSlotsFree = 0; - for (ItemStack tItem : mRecipe.mOutputs) { - tItemOutputMap.merge(tItem, tItem.stackSize, Integer::sum); - tParallels.put(tItem, new MutablePair<>(0, 0)); - } - - if (tItemOutputMap.isEmpty()) { - // nothing to output, bail early - return mMaxParallel; - } - - if (mRecipe.mOutputs.length > 0 && mMachineMulti != null - && mMachineMulti.getOutputInventory() != null - && mMachineMulti.getOutputInventory() - .getSlots() > 0) { - for (int i = 0; i < mMachineMulti.getOutputInventory() - .getSlots(); i++) { - ItemStack tBusStack = mMachineMulti.getOutputInventory() - .getStackInSlot(i); - if (tBusStack == null) { - tSlotsFree++; - } else { - // get the real stack size - // we ignore the bus inventory stack limit here as no one set it to anything other than 64 - int tMaxBusStackSize = tBusStack.getMaxStackSize(); - if (tBusStack.stackSize >= tMaxBusStackSize) - // this bus stack is full. no checking - continue; - int tSpaceLeft = tMaxBusStackSize - tBusStack.stackSize; - Integer tCraftSize = tItemOutputMap.get(tBusStack); - if (tCraftSize == null) { - // we don't have a matching stack to output, ignore this bus stack - continue; - } - MutablePair<Integer, Integer> tParallel = tParallels.get(tBusStack); - tParallel.left += (tParallel.right + tSpaceLeft) / tCraftSize; - tParallel.right = (tParallel.right + tSpaceLeft) % tCraftSize; - } - } - // now that all partial stacks have been counted, create a priority queue for our outputs - // the lowest priority item is the number of complete parallel crafts we can support - PriorityQueue<MutableTriple<Integer, Integer, ItemStack>> aParallelQueue = new PriorityQueue<>( - Comparator.comparing(MutableTriple::getLeft)); - for (Entry<ItemStack, MutablePair<Integer, Integer>> entry : tParallels.entrySet()) { - aParallelQueue.add(new MutableTriple<>(entry.getValue().left, entry.getValue().right, entry.getKey())); - } - - while (tSlotsFree > 0) { - MutableTriple<Integer, Integer, ItemStack> tParallel = aParallelQueue.poll(); - assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings - Integer tCraftSize = tItemOutputMap.get(tParallel.right); - int tStackSize = tParallel.right.getMaxStackSize(); - tParallel.left += (tParallel.middle + tStackSize) / tCraftSize; - tParallel.middle = (tParallel.middle + tStackSize) % tCraftSize; - aParallelQueue.add(tParallel); - --tSlotsFree; - } - - return aParallelQueue.element().left; - } - return 0; - } - - /** - * Calculates the max parallels one can do with items if void protection is on - */ - private int calculateMaxParallelsForBusses() { - // Same thing we are gonna ignore MuTEs existence for now. should be in theory the same later - - // A map to hold the items we will be 'inputting' into the output buses. These itemstacks are actually the - // recipe outputs. - Map<ItemStack, Integer> tItemOutputMap = new ItemStackMap<>(); - - // Map that keeps track of the number of parallel crafts we can accommodate for each item output. - // In the pair, we keep track of number of full crafts plus number of items in a partial craft, to avoid - // issues with floating point math not being completely accurate when summing. - Map<ItemStack, MutablePair<Integer, Integer>> tParallels = new ItemStackMap<>(); - int tSlotsFree = 0; - for (ItemStack tItem : mRecipe.mOutputs) { - tItemOutputMap.merge(tItem, tItem.stackSize, Integer::sum); - tParallels.put(tItem, new MutablePair<>(0, 0)); - } - - if (tItemOutputMap.isEmpty()) { - // nothing to output, bail early - return mMaxParallel; - } - - if (mRecipe.mOutputs.length > 0 && mMachineMeta != null) { - for (final GT_MetaTileEntity_Hatch tBus : mMachineMeta.mOutputBusses) { - if (!GT_MetaTileEntity_MultiBlockBase.isValidMetaTileEntity(tBus)) { - continue; - } - final IInventory tBusInv = tBus.getBaseMetaTileEntity(); - for (int i = 0; i < tBusInv.getSizeInventory(); i++) { - ItemStack tBusStack = tBus.getStackInSlot(i); - if (tBusStack == null) { - tSlotsFree++; - } else { - // get the real stack size - // we ignore the bus inventory stack limit here as no one set it to anything other than 64 - int tMaxBusStackSize = tBusStack.getMaxStackSize(); - if (tBusStack.stackSize >= tMaxBusStackSize) - // this bus stack is full. no checking - continue; - int tSpaceLeft = tMaxBusStackSize - tBusStack.stackSize; - Integer tCraftSize = tItemOutputMap.get(tBusStack); - if (tCraftSize == null) { - // we don't have a matching stack to output, ignore this bus stack - continue; - } - MutablePair<Integer, Integer> tParallel = tParallels.get(tBusStack); - tParallel.left += (tParallel.right + tSpaceLeft) / tCraftSize; - tParallel.right = (tParallel.right + tSpaceLeft) % tCraftSize; - } - } - } - // now that all partial stacks have been counted, create a priority queue for our outputs - // the lowest priority item is the number of complete parallel crafts we can support - PriorityQueue<MutableTriple<Integer, Integer, ItemStack>> aParallelQueue = new PriorityQueue<>( - Comparator.comparing(MutableTriple::getLeft)); - for (Entry<ItemStack, MutablePair<Integer, Integer>> entry : tParallels.entrySet()) { - aParallelQueue.add(new MutableTriple<>(entry.getValue().left, entry.getValue().right, entry.getKey())); - } - - while (tSlotsFree > 0) { - MutableTriple<Integer, Integer, ItemStack> tParallel = aParallelQueue.poll(); - assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings - Integer tCraftSize = tItemOutputMap.get(tParallel.right); - int tStackSize = tParallel.right.getMaxStackSize(); - tParallel.left += (tParallel.middle + tStackSize) / tCraftSize; - tParallel.middle = (tParallel.middle + tStackSize) % tCraftSize; - aParallelQueue.add(tParallel); - --tSlotsFree; - } - - return aParallelQueue.element().left; - } - return 0; - } } diff --git a/src/main/java/gregtech/api/util/ValidationResult.java b/src/main/java/gregtech/api/util/ValidationResult.java new file mode 100644 index 0000000000..497dfe67e5 --- /dev/null +++ b/src/main/java/gregtech/api/util/ValidationResult.java @@ -0,0 +1,24 @@ +package gregtech.api.util; + +public class ValidationResult<T> { + + private final ValidationType type; + private final T result; + + private ValidationResult(ValidationType type, T result) { + this.type = type; + this.result = result; + } + + public ValidationType getType() { + return this.type; + } + + public T getResult() { + return this.result; + } + + public static <T> ValidationResult<T> of(ValidationType result, T value) { + return new ValidationResult<>(result, value); + } +} diff --git a/src/main/java/gregtech/api/util/ValidationType.java b/src/main/java/gregtech/api/util/ValidationType.java new file mode 100644 index 0000000000..4417834430 --- /dev/null +++ b/src/main/java/gregtech/api/util/ValidationType.java @@ -0,0 +1,6 @@ +package gregtech.api.util; + +public enum ValidationType { + VALID, + INVALID +} diff --git a/src/main/java/gregtech/api/util/VoidProtectionHelper.java b/src/main/java/gregtech/api/util/VoidProtectionHelper.java new file mode 100644 index 0000000000..8b09cd5a3d --- /dev/null +++ b/src/main/java/gregtech/api/util/VoidProtectionHelper.java @@ -0,0 +1,402 @@ +package gregtech.api.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.function.BiFunction; +import java.util.function.Function; + +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidTank; + +import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; +import com.gtnewhorizons.modularui.api.forge.IItemHandler; + +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_Hatch; +import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_MultiBlockBase; +import gregtech.api.multitileentity.multiblock.base.Controller; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_OutputBus_ME; +import gregtech.common.tileentities.machines.GT_MetaTileEntity_Hatch_Output_ME; + +/** + * Helper class to calculate how many parallels of items / fluids can fit in the output buses / hatches. + */ +public class VoidProtectionHelper { + + /** + * A MetaTileEntity Controller + */ + private GT_MetaTileEntity_MultiBlockBase machineMeta; + /** + * A MultiTileEntity Controller + */ + private Controller<?> machineMulti; + /** + * The maximum possible parallel possible for the multiblock + */ + private int maxParallel = 1; + /** + * The item outputs to check + */ + private ItemStack[] itemOutputs; + /** + * The fluid outputs to check + */ + private FluidStack[] fluidOutputs; + /** + * Has this helper been built? + */ + private boolean built; + + public VoidProtectionHelper() {} + + /** + * Sets MetaTE controller to use for calculation + */ + public VoidProtectionHelper setController(GT_MetaTileEntity_MultiBlockBase machineMeta) { + this.machineMeta = machineMeta; + return this; + } + + /** + * Sets MuTE controller to use for calculation + */ + public VoidProtectionHelper setController(Controller<?> machineMulti) { + this.machineMulti = machineMulti; + return this; + } + + public VoidProtectionHelper setItemOutputs(ItemStack[] itemOutputs) { + this.itemOutputs = itemOutputs; + return this; + } + + public VoidProtectionHelper setFluidOutputs(FluidStack[] fluidOutputs) { + this.fluidOutputs = fluidOutputs; + return this; + } + + /** + * Sets the MaxParallel a multi can handle + */ + public VoidProtectionHelper setMaxParallel(int maxParallel) { + this.maxParallel = maxParallel; + return this; + } + + /** + * Finishes the VoidProtectionHelper. Anything changed after this will not affect anything + */ + public VoidProtectionHelper build() { + if (built) { + throw new IllegalStateException("Tried to build twice"); + } + built = true; + determineParallel(); + return this; + } + + /** + * @return The current parallels possible by the multiblock + */ + public int getMaxParallel() { + if (!built) { + throw new IllegalStateException("Tried to get parallels before building"); + } + return maxParallel; + } + + /** + * Called by {@link #build()}. Determines the parallels and everything else that needs to be done at build time + */ + private void determineParallel() { + if (itemOutputs == null) { + itemOutputs = new ItemStack[0]; + } + if (fluidOutputs == null) { + fluidOutputs = new FluidStack[0]; + } + + if (machineMeta != null) { + boolean tMEOutputBus = false; + boolean tMEOutputHatch = false; + for (GT_MetaTileEntity_Hatch tHatch : machineMeta.mOutputBusses) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_OutputBus_ME) { + tMEOutputBus = true; + break; + } + } + + for (GT_MetaTileEntity_Hatch tHatch : machineMeta.mOutputHatches) { + if (tHatch instanceof GT_MetaTileEntity_Hatch_Output_ME) { + tMEOutputHatch = true; + break; + } + } + if (!tMEOutputBus) { + maxParallel = Math.min(calculateMaxItemParallelsForMetaTEs(), maxParallel); + } + + if (!tMEOutputHatch) { + maxParallel = Math.min(calculateMaxFluidParallelsForMetaTEs(), maxParallel); + } + } else if (machineMulti != null) { + maxParallel = Math.min(calculateMaxItemParallelsForMuTEs(), maxParallel); + maxParallel = Math.min(calculateMaxFluidParallelsForMuTEs(), maxParallel); + } + } + + /** + * Calculates the max parallel for fluids if void protection is turned on + */ + private int calculateMaxFluidParallelsForMuTEs() { + if (machineMulti == null || machineMulti.getOutputTanks() == null) { + return 0; + } + return calculateMaxFluidParallels( + Arrays.asList(machineMulti.getOutputTanks()), + tHatch -> tHatch.getFluidAmount() == 0, + (tHatch, fluidStack) -> true); + } + + /** + * Calculates the max parallel for fluids if void protection is turned on + */ + private int calculateMaxFluidParallelsForMetaTEs() { + if (machineMeta == null) { + return 0; + } + return calculateMaxFluidParallels( + machineMeta.mOutputHatches, + tHatch -> tHatch.mMode == 0 && tHatch.getFluidAmount() == 0, + (tHatch, fluidStack) -> { + if (GT_ModHandler.isSteam(fluidStack)) { + return tHatch.outputsSteam(); + } else { + if (!tHatch.outputsLiquids()) { + return false; + } + String tLockedFluidName = tHatch.getLockedFluidName(); + return !tHatch.isFluidLocked() || tLockedFluidName == null + || tLockedFluidName.equals( + fluidStack.getFluid() + .getName()); + } + }); + } + + /** + * Calculates the max parallel for fluids if void protection is turned on + */ + private <T extends IFluidTank> int calculateMaxFluidParallels(List<T> hatches, Function<T, Boolean> isEmpty, + BiFunction<T, FluidStack, Boolean> acceptsFluid) { + if (hatches.size() < fluidOutputs.length) { + return 0; + } + + // A map to hold the items we will be 'inputting' into the output hatches. These fluidstacks are actually + // the recipe outputs. + Map<FluidStack, Integer> 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<FluidStack, ParallelData> tParallels = new HashMap<>(); + + // Iterate over the outputs, calculating require stack spacing they will require. + for (FluidStack aY : fluidOutputs) { + if (aY == null) { + 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 (T tHatch : hatches) { + int tSpaceLeft = tHatch.getCapacity() - tHatch.getFluidAmount(); + + // check if hatch filled + if (tSpaceLeft <= 0) continue; + + // check if hatch is empty and unrestricted + if (isEmpty.apply(tHatch)) continue; + + for (Map.Entry<FluidStack, ParallelData> entry : tParallels.entrySet()) { + FluidStack tFluidOutput = entry.getKey(); + if (!acceptsFluid.apply(tHatch, tFluidOutput)) continue; + // this fluid is not prevented by restrictions on output hatch + if (tHatch.getFluidAmount() == 0 || GT_Utility.areFluidsEqual(tHatch.getFluid(), tFluidOutput)) { + 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<ParallelStackInfo<FluidStack>> aParallelQueue = new PriorityQueue<>( + Comparator.comparing(i -> i.batch)); + for (Map.Entry<FluidStack, ParallelData> entry : tParallels.entrySet()) { + aParallelQueue + .add(new ParallelStackInfo<>(entry.getValue().batch, entry.getValue().partial, entry.getKey())); + } + // add extra parallels for open slots as well + for (T tHatch : hatches) { + // partially filled or restricted hatch. done in last pass + if (!isEmpty.apply(tHatch)) continue; + + ParallelStackInfo<FluidStack> tParallel = aParallelQueue.poll(); + assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings + Integer tCraftSize = tFluidOutputMap.get(tParallel.stack); + int tSpaceLeft = tHatch.getCapacity(); + 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 calculateMaxItemParallelsForMuTEs() { + List<ItemStack> busStacks = new ArrayList<>(); + if (machineMulti != null) { + IItemHandler inv = machineMulti.getOutputInventory(); + if (inv != null && inv.getSlots() > 0) { + for (int i = 0; i < inv.getSlots(); i++) { + busStacks.add(inv.getStackInSlot(i)); + } + } + } + return calculateMaxItemParallels(busStacks); + } + + /** + * Calculates the max parallels one can do with items if void protection is on + */ + private int calculateMaxItemParallelsForMetaTEs() { + List<ItemStack> busStacks = new ArrayList<>(); + if (machineMeta != null) { + for (final GT_MetaTileEntity_Hatch tBus : machineMeta.mOutputBusses) { + if (!GT_MetaTileEntity_MultiBlockBase.isValidMetaTileEntity(tBus)) { + continue; + } + final IInventory tBusInv = tBus.getBaseMetaTileEntity(); + for (int i = 0; i < tBusInv.getSizeInventory(); i++) { + busStacks.add(tBus.getStackInSlot(i)); + } + } + } + return calculateMaxItemParallels(busStacks); + } + + /** + * Calculates the max parallels one can do with items if void protection is on + * + * @param busStacks List of itemstacks that are already stored in buses + */ + private int calculateMaxItemParallels(List<ItemStack> busStacks) { + // A map to hold the items we will be 'inputting' into the output buses. These itemstacks are actually the + // recipe outputs. + Map<ItemStack, Integer> tItemOutputMap = new ItemStackMap<>(); + + // Map that keeps track of the number of parallel crafts we can accommodate for each item output. + // In the pair, we keep track of number of full crafts plus number of items in a partial craft, to avoid + // issues with floating point math not being completely accurate when summing. + Map<ItemStack, ParallelData> tParallels = new ItemStackMap<>(); + int tSlotsFree = 0; + for (ItemStack tItem : itemOutputs) { + tItemOutputMap.merge(tItem, tItem.stackSize, Integer::sum); + tParallels.put(tItem, new ParallelData(0, 0)); + } + + if (tItemOutputMap.isEmpty()) { + // nothing to output, bail early + return maxParallel; + } + + if (itemOutputs.length > 0) { + for (ItemStack tBusStack : busStacks) { + if (tBusStack == null) { + tSlotsFree++; + } else { + // get the real stack size + // we ignore the bus inventory stack limit here as no one set it to anything other than 64 + int tMaxBusStackSize = tBusStack.getMaxStackSize(); + if (tBusStack.stackSize >= tMaxBusStackSize) + // this bus stack is full. no checking + continue; + int tSpaceLeft = tMaxBusStackSize - tBusStack.stackSize; + Integer tCraftSize = tItemOutputMap.get(tBusStack); + if (tCraftSize == null) { + // we don't have a matching stack to output, ignore this bus stack + continue; + } + ParallelData tParallel = tParallels.get(tBusStack); + tParallel.batch += (tParallel.partial + tSpaceLeft) / tCraftSize; + tParallel.partial = (tParallel.partial + tSpaceLeft) % tCraftSize; + } + + } + // now that all partial stacks have been counted, create a priority queue for our outputs + // the lowest priority item is the number of complete parallel crafts we can support + PriorityQueue<ParallelStackInfo<ItemStack>> aParallelQueue = new PriorityQueue<>( + Comparator.comparing(i -> i.batch)); + for (Map.Entry<ItemStack, ParallelData> entry : tParallels.entrySet()) { + aParallelQueue + .add(new ParallelStackInfo<>(entry.getValue().batch, entry.getValue().partial, entry.getKey())); + } + + while (tSlotsFree > 0) { + ParallelStackInfo<ItemStack> tParallel = aParallelQueue.poll(); + assert tParallel != null; // will always be true, specifying assert here to avoid IDE/compiler warnings + Integer tCraftSize = tItemOutputMap.get(tParallel.stack); + int tStackSize = tParallel.stack.getMaxStackSize(); + tParallel.batch += (tParallel.partial + tStackSize) / tCraftSize; + tParallel.partial = (tParallel.partial + tStackSize) % tCraftSize; + aParallelQueue.add(tParallel); + --tSlotsFree; + } + + return aParallelQueue.element().batch; + } + return 0; + } + + private static class ParallelData { + + private int batch; + private int partial; + + private ParallelData(int batch, int partial) { + this.batch = batch; + this.partial = partial; + } + } + + private static class ParallelStackInfo<T> { + + private int batch; + private int partial; + private final T stack; + + private ParallelStackInfo(int batch, int partial, T stack) { + this.batch = batch; + this.partial = partial; + this.stack = stack; + } + } +} diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillBase.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillBase.java index 1e7f683bc5..9cab5b4ec6 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillBase.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillBase.java @@ -14,6 +14,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import javax.annotation.Nonnegative; + import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -37,6 +39,8 @@ import gregtech.api.render.TextureFactory; import gregtech.api.util.GT_Log; import gregtech.api.util.GT_Multiblock_Tooltip_Builder; import gregtech.api.util.GT_Utility; +import gregtech.api.util.ValidationResult; +import gregtech.api.util.ValidationType; public abstract class GT_MetaTileEntity_OilDrillBase extends GT_MetaTileEntity_DrillerBase { @@ -195,7 +199,13 @@ public abstract class GT_MetaTileEntity_OilDrillBase extends GT_MetaTileEntity_D mWorkChunkNeedsReload = false; } float speed = computeSpeed(); - FluidStack tFluid = pumpOil(speed); + ValidationResult<FluidStack> pumpResult = tryPumpOil(speed); + if (pumpResult.getType() != ValidationType.VALID) { + mEUt = 0; + mMaxProgresstime = 0; + return false; + } + FluidStack tFluid = pumpResult.getResult(); if (tFluid != null && tFluid.amount > getTotalConfigValue()) { this.mOutputFluids = new FluidStack[] { tFluid }; return true; @@ -265,37 +275,69 @@ public abstract class GT_MetaTileEntity_OilDrillBase extends GT_MetaTileEntity_D return !mOilFieldChunks.isEmpty(); } - protected FluidStack pumpOil(float speed) { + /** + * Tries to pump oil, accounting for output space if void protection is enabled. + * <p> + * If pumped fluid will not fit in output hatches, it returns a result with INVALID. + * <p> + * If vein is depleted, it returns a result with VALID and null fluid. + */ + protected ValidationResult<FluidStack> tryPumpOil(float speed) { if (mOilId <= 0) return null; - FluidStack tFluid, tOil; - tOil = new FluidStack(FluidRegistry.getFluid(mOilId), 0); if (debugDriller) { GT_Log.out.println(" pump speed = " + speed); } + if (!voidExcess) { + FluidStack simulatedOil = pumpOil(speed, true); + if (!canFitOutput(new FluidStack[] { simulatedOil })) { + return ValidationResult.of(ValidationType.INVALID, null); + } + } + + FluidStack pumpedOil = pumpOil(speed, false); + mOilFlow = pumpedOil.amount; + return ValidationResult.of(ValidationType.VALID, pumpedOil.amount == 0 ? null : pumpedOil); + } + + /** + * @param speed Speed to pump oil + * @param simulate If true, it actually does not consume vein + * @return Fluid pumped + */ + protected FluidStack pumpOil(@Nonnegative float speed, boolean simulate) { + if (speed < 0) { + throw new IllegalArgumentException("Don't pass negative speed"); + } + ArrayList<Chunk> emptyChunks = new ArrayList<>(); + FluidStack returnOil = new FluidStack(FluidRegistry.getFluid(mOilId), 0); for (Chunk tChunk : mOilFieldChunks) { - tFluid = undergroundOil(tChunk, speed); + FluidStack pumped = undergroundOil(tChunk, simulate ? -speed : speed); if (debugDriller) { GT_Log.out.println( " chunkX = " + tChunk.getChunkCoordIntPair().chunkXPos + " chunkZ = " + tChunk.getChunkCoordIntPair().chunkZPos); - if (tFluid != null) { - GT_Log.out.println(" Fluid pumped = " + tFluid.amount); + if (pumped != null) { + GT_Log.out.println(" Fluid pumped = " + pumped.amount); } else { GT_Log.out.println(" No fluid pumped "); } } - if (tFluid == null || tFluid.amount < 1) emptyChunks.add(tChunk); - if (tOil.isFluidEqual(tFluid)) tOil.amount += tFluid.amount; + if (pumped == null || pumped.amount < 1) { + emptyChunks.add(tChunk); + continue; + } + if (returnOil.isFluidEqual(pumped)) { + returnOil.amount += pumped.amount; + } } for (Chunk tChunk : emptyChunks) { mOilFieldChunks.remove(tChunk); } - mOilFlow = tOil.amount; - return tOil.amount == 0 ? null : tOil; + return returnOil; } @Override @@ -328,4 +370,9 @@ public abstract class GT_MetaTileEntity_OilDrillBase extends GT_MetaTileEntity_D l.addAll(Arrays.asList(super.getInfoData())); return l.toArray(new String[0]); } + + @Override + protected boolean isVoidExcessButtonEnabled() { + return true; + } } diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillInfinite.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillInfinite.java index 2c86218bfb..1146c39f21 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillInfinite.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OilDrillInfinite.java @@ -50,8 +50,9 @@ public class GT_MetaTileEntity_OilDrillInfinite extends GT_MetaTileEntity_OilDri } @Override - protected FluidStack pumpOil(float speed) { - return super.pumpOil(-speed); + protected FluidStack pumpOil(float speed, boolean simulate) { + // always simulate to not deplete vein + return super.pumpOil(speed, true); } @Override diff --git a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlantBase.java b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlantBase.java index 8e8f16d111..b6a0d92f42 100644 --- a/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlantBase.java +++ b/src/main/java/gregtech/common/tileentities/machines/multi/GT_MetaTileEntity_OreDrillingPlantBase.java @@ -41,7 +41,7 @@ import gregtech.common.blocks.GT_TileEntity_Ores; public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTileEntity_DrillerBase { - private final ArrayList<ChunkPosition> oreBlockPositions = new ArrayList<>(); + private final List<ChunkPosition> oreBlockPositions = new ArrayList<>(); protected int mTier = 1; private int chunkRadiusConfig = getRadiusInChunks(); private boolean replaceWithCobblestone = true; @@ -130,11 +130,31 @@ public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTile // new layer - fill again fillMineListIfEmpty(xDrill, yDrill, zDrill, xPipe, zPipe, yHead); } - return processOreList(); + return tryProcessOreList(); } - private boolean processOreList() { + private boolean tryProcessOreList() { + if (!voidExcess) { + boolean simulateResult = processOreList(true); + if (!simulateResult) { + mEUt = 0; + mMaxProgresstime = 0; + return false; + } + } + boolean result = processOreList(false); + if (!result) { + mEUt = 0; + mMaxProgresstime = 0; + return false; + } + return true; + } + + private boolean processOreList(boolean simulate) { ChunkPosition oreBlockPos = null; + List<ChunkPosition> oreBlockPositions = simulate ? copyOreBlockPositions(this.oreBlockPositions) + : this.oreBlockPositions; int x = 0, y = 0, z = 0; Block oreBlock = null; int oreBlockMetadata = 0; @@ -150,7 +170,7 @@ public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTile .getBlockMetadata(x, y, z); } - if (!tryConsumeDrillingFluid()) { + if (!tryConsumeDrillingFluid(simulate)) { oreBlockPositions.add(0, oreBlockPos); return false; } @@ -163,18 +183,32 @@ public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTile Collection<ItemStack> oreBlockDrops = getBlockDrops(oreBlock, x, y, z); ItemStack cobble = GT_Utility.getCobbleForOre(oreBlock, metaData); - if (replaceWithCobblestone) { - getBaseMetaTileEntity().getWorld() - .setBlock(x, y, z, Block.getBlockFromItem(cobble.getItem()), cobble.getItemDamage(), 3); - } else { - getBaseMetaTileEntity().getWorld() - .setBlockToAir(oreBlockPos.chunkPosX, oreBlockPos.chunkPosY, oreBlockPos.chunkPosZ); + if (!simulate) { + if (replaceWithCobblestone) { + getBaseMetaTileEntity().getWorld() + .setBlock(x, y, z, Block.getBlockFromItem(cobble.getItem()), cobble.getItemDamage(), 3); + } else { + getBaseMetaTileEntity().getWorld() + .setBlockToAir(oreBlockPos.chunkPosX, oreBlockPos.chunkPosY, oreBlockPos.chunkPosZ); + } } - mOutputItems = getOutputByDrops(oreBlockDrops); + ItemStack[] toOutput = getOutputByDrops(oreBlockDrops); + if (simulate && !canFitOutput(toOutput)) { + return false; + } + mOutputItems = toOutput; } return true; } + private static List<ChunkPosition> copyOreBlockPositions(List<ChunkPosition> oreBlockPositions) { + List<ChunkPosition> ret = new ArrayList<>(); + for (ChunkPosition chunkPosition : oreBlockPositions) { + ret.add(new ChunkPosition(chunkPosition.chunkPosX, chunkPosition.chunkPosY, chunkPosition.chunkPosZ)); + } + return ret; + } + @Override protected boolean workingAtBottom(ItemStack aStack, int xDrill, int yDrill, int zDrill, int xPipe, int zPipe, int yHead, int oldYHead) { @@ -199,7 +233,7 @@ public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTile return true; } } - return processOreList(); + return tryProcessOreList(); } private void createInitialWorkingChunk() { @@ -220,7 +254,7 @@ public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTile int yHead, int oldYHead) { if (!mChunkLoadingEnabled || oreBlockPositions.isEmpty()) return super.workingUpward(aStack, xDrill, yDrill, zDrill, xPipe, zPipe, yHead, oldYHead); - boolean result = processOreList(); + boolean result = tryProcessOreList(); if (oreBlockPositions.isEmpty()) GT_ChunkManager.releaseTicket((TileEntity) getBaseMetaTileEntity()); return result; } @@ -331,12 +365,8 @@ public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTile } else return oreBlock.getDrops(getBaseMetaTileEntity().getWorld(), posX, posY, posZ, blockMeta, mTier + 3); } - private boolean tryConsumeDrillingFluid() { - if (!depleteInput(new FluidStack(ItemList.sDrillingFluid, 2000))) { - mMaxProgresstime = 0; - return false; - } - return true; + private boolean tryConsumeDrillingFluid(boolean simulate) { + return depleteInput(new FluidStack(ItemList.sDrillingFluid, 2000), simulate); } private void fillChunkMineList(int yHead, int yDrill) { @@ -433,4 +463,9 @@ public abstract class GT_MetaTileEntity_OreDrillingPlantBase extends GT_MetaTile + " " + StatCollector.translateToLocal("GT5U.machines.chunks") }; } + + @Override + protected boolean isVoidExcessButtonEnabled() { + return true; + } } |