diff options
author | NotAPenguin <michiel.vandeginste@gmail.com> | 2024-02-27 16:09:06 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-27 16:09:06 +0100 |
commit | 14fce70432ac1c982006775f88d5451d0b537d0a (patch) | |
tree | 3c1bc9759fdaeef11a0f7023068e4a40e060c488 /src/main/java/net/glease/ggfab/mte | |
parent | 39d9a964a111677301b216044b0fe82231ef8c43 (diff) | |
download | GT5-Unofficial-14fce70432ac1c982006775f88d5451d0b537d0a.tar.gz GT5-Unofficial-14fce70432ac1c982006775f88d5451d0b537d0a.tar.bz2 GT5-Unofficial-14fce70432ac1c982006775f88d5451d0b537d0a.zip |
Add AAL batch mode (#32)
This PR adds support for batch mode to the AAL. A few notes:
- Similarly to regular batch mode, the amount of batches to run is
chosen so the time of each slice is at least 128 ticks. If the time per
slice is already 128 ticks, this does nothing.
- Batch size is also controlled by how many recipes we can do with the
amount of the first item we have. If we want to run 10 batches but only
have enough starting items for 5, it will run 5 batches.
- If there are not enough fluids to run multiple batches, it will not
batch at all and simply process one recipe at a time.
- If a slice cannot extract enough materials to do a full batch, it gets
stuck (similarly to a regular AAL).
- Currently, this only works well with stocking input buses, see the
comment I left on line 616. I don't think this should be a problem since
you will never use input buses with more than one slot to automate an
AAL.
Short demo video (not yet using adaptive batch sizes)
https://streamable.com/hrp1v3
Diffstat (limited to 'src/main/java/net/glease/ggfab/mte')
-rw-r--r-- | src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java | 136 |
1 files changed, 114 insertions, 22 deletions
diff --git a/src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java b/src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java index ed04c2fbce..e540bb4562 100644 --- a/src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java +++ b/src/main/java/net/glease/ggfab/mte/MTE_AdvAssLine.java @@ -36,6 +36,7 @@ import net.glease.ggfab.GGConstants; import net.glease.ggfab.mui.ClickableTextWidget; import net.glease.ggfab.util.OverclockHelper; import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; @@ -189,6 +190,10 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas private boolean sortFluidHatches; private int currentInputLength; private String lastStopReason = ""; + private int currentRecipeParallel = 1; + // Batch mode will increase parallel per slice to try to get as close as possible to this amount of ticks + // per slice, but will never go over this amount. + private static final int BATCH_MODE_DESIRED_TICKS_PER_SLICE = 128; public MTE_AdvAssLine(int aID, String aName, String aNameRegional) { super(aID, aName, aNameRegional); @@ -343,6 +348,8 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas currentRecipe = recipe; currentStick = stick; currentInputLength = recipe.mInputs.length; + // Reset parallel, we need to re-check on next recipe check to see if there are enough items in the first slice + currentRecipeParallel = 1; } private void clearCurrentRecipe() { @@ -375,6 +382,7 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas aNBT.setLong("inputV", inputVoltage); aNBT.setLong("inputEU", inputEUt); aNBT.setLong("baseEU", baseEUt); + aNBT.setInteger("currentParallel", currentRecipeParallel); } } @@ -406,6 +414,7 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas inputVoltage = aNBT.getLong("inputV"); inputEUt = aNBT.getLong("inputEU"); baseEUt = aNBT.getLong("baseEU"); + currentRecipeParallel = aNBT.getInteger("currentParallel"); if (inputVoltage <= 0 || inputEUt <= 0 || baseEUt >= 0) { criticalStopMachine("ggfab.gui.advassline.shutdown.load.energy"); loadedStack = null; @@ -604,8 +613,10 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas } if (getBaseMetaTileEntity().isAllowedToWork()) { - if (hasAllItems(currentRecipe) && hasAllFluids(currentRecipe) && slices[0].start()) { - drainAllFluids(currentRecipe); + if (hasAllItems(currentRecipe, this.currentRecipeParallel) + && hasAllFluids(currentRecipe, this.currentRecipeParallel) + && slices[0].start()) { + drainAllFluids(currentRecipe, this.currentRecipeParallel); mProgresstime = 0; } } @@ -621,6 +632,10 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas if (index < mInputBusses.size()) { GT_MetaTileEntity_Hatch_InputBus bus = mInputBusses.get(index); if (bus.isValid()) { + // This limits items extracted per slice to 64. Normally this is not an issue, but with batch + // mode it can suddenly cause the AAL to get stuck because it thinks there are not enough + // items in the bus. I'm not quite sure how to get around this, even though it should not matter + // in practice since all AAL automation uses stocking buses instead of regular input buses. stuff = bus.getStackInSlot(0); } } @@ -649,11 +664,12 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas // If we run into missing buses/hatches or bad inputs, we go to the next data stick. // This check only happens if we have a valid up-to-date data stick. - // Check item Inputs align - if (!hasAllItems(tRecipe)) return null; + // Check item Inputs align. For this we do not need to consider batch mode parallels yet, this will be done + // later on during recipe start. + if (!hasAllItems(tRecipe, 1)) return null; - // Check Fluid Inputs align - if (!hasAllFluids(tRecipe)) return null; + // Check Fluid Inputs align. Again, do not consider parallels + if (!hasAllFluids(tRecipe, 1)) return null; if (GT_Values.D1) { GT_FML_LOGGER.info("Check overclock"); @@ -664,13 +680,17 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas return tRecipe; } - private boolean hasAllItems(GT_Recipe.GT_Recipe_AssemblyLine tRecipe) { + private boolean hasAllItems(GT_Recipe.GT_Recipe_AssemblyLine tRecipe, int parallel) { int aItemCount = tRecipe.mInputs.length; if (mInputBusses.size() < aItemCount) return false; for (int i = 0; i < aItemCount; i++) { ItemStack tSlotStack = getInputBusContent(i); if (tSlotStack == null) return false; - int tRequiredStackSize = isStackValidIngredient(tSlotStack, tRecipe.mInputs[i], tRecipe.mOreDictAlt[i]); + int tRequiredStackSize = isStackValidIngredient( + tSlotStack, + tRecipe.mInputs[i], + tRecipe.mOreDictAlt[i], + parallel); if (tRequiredStackSize < 0) return false; if (GT_Values.D1) { @@ -680,7 +700,9 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas return true; } - private boolean hasAllFluids(GT_Recipe.GT_Recipe_AssemblyLine tRecipe) { + private boolean hasAllFluids(GT_Recipe.GT_Recipe_AssemblyLine tRecipe, int parallel) { + // TODO: Actually use the parallel parameter here, though this is already checked on recipe check, + // we may need to re-check int aFluidCount = tRecipe.mFluidInputs.length; if (mInputHatches.size() < aFluidCount) return false; for (int i = 0; i < aFluidCount; i++) { @@ -780,8 +802,41 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas mMaxProgresstime = laserOverclock.getDuration(); } } + // Save this for batch mode parallel calculations + int timePerSlice = mMaxProgresstime; // correct the recipe duration mMaxProgresstime *= recipe.mInputs.length; + + // Finally apply batch mode parallels if possible. + // For this we need to verify the first item slot and all fluids slots have enough resources + // to execute parallels. + // Note that we skip this entirely if the time for each slice is more than + // BATCH_MODE_DESIRED_TICKS_PER_SLICE ticks, since in this case the amount of batches will always be 1 + if (super.isBatchModeEnabled() && timePerSlice < BATCH_MODE_DESIRED_TICKS_PER_SLICE) { + // Calculate parallel based on time per slice, and the amount of items in the first slot. + // If there is not enough fluid, no batching will be done. + + ItemStack firstItemSlot = getInputBusContent(0); + int recipesAvailable = Math.floorDiv(firstItemSlot.stackSize, recipe.mInputs[0].stackSize); + // Divide recipes available by the amount of slices in the recipe. This will prevent the AAL from + // batching instead of parallelizing, which would make it effectively slower. + recipesAvailable = Math.floorDiv(recipesAvailable, recipe.mInputs.length); + // Sanity check to avoid this being zero when there is only one recipe available. + recipesAvailable = Math.max(recipesAvailable, 1); + int desiredBatches = Math.floorDiv(BATCH_MODE_DESIRED_TICKS_PER_SLICE, timePerSlice); + // Limit the amount of parallel to both the amount of recipes available and the maximum number + // of batches we want to run. The latter is done to prevent batch mode from ever going above + // BATCH_MODE_DESIRED_TICKS_PER_SLICE ticks per slice (see also where it is defined above). + int parallel = Math.min(recipesAvailable, desiredBatches); + // We no longer need to check if we have enough items in the first slot, as this is + // guaranteed by taking the minimum earlier. + if (hasAllFluids(recipe, parallel)) { + this.currentRecipeParallel = parallel; + // Update recipe duration with final batch mode multiplier + mMaxProgresstime *= this.currentRecipeParallel; + } + } + break; } } @@ -802,9 +857,11 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas // something very very wrong... return CheckRecipeResultRegistry.NONE; } - drainAllFluids(recipe); + drainAllFluids(recipe, this.currentRecipeParallel); - mOutputItems = new ItemStack[] { recipe.mOutput }; + // Apply parallel + mOutputItems = new ItemStack[] { recipe.mOutput.copy() }; + mOutputItems[0].stackSize *= this.currentRecipeParallel; if (this.lEUt > 0) { this.lEUt = -this.lEUt; @@ -904,9 +961,12 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas * Caller is responsible to check and ensure the hatches are there and has all the fluid needed. You will usually * want to ensure hasAllFluid was called right before calling this, otherwise very bad things can happen. */ - private void drainAllFluids(GT_Recipe.GT_Recipe_AssemblyLine recipe) { + private void drainAllFluids(GT_Recipe.GT_Recipe_AssemblyLine recipe, int parallel) { for (int i = 0; i < recipe.mFluidInputs.length; i++) { - mInputHatches.get(i).drain(ForgeDirection.UNKNOWN, recipe.mFluidInputs[i], true); + // Apply parallel + FluidStack fluidInput = recipe.mFluidInputs[i].copy(); + fluidInput.amount *= parallel; + mInputHatches.get(i).drain(ForgeDirection.UNKNOWN, fluidInput, true); } sortFluidHatches = true; } @@ -917,21 +977,43 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas super.stopMachine(); } - private static int isStackValidIngredient(ItemStack aSlotStack, ItemStack aIngredient, ItemStack[] alts) { - if (alts == null || alts.length == 0) return isStackValidIngredient(aSlotStack, aIngredient); + private static int isStackValidIngredient(ItemStack aSlotStack, ItemStack aIngredient, ItemStack[] alts, + int parallel) { + if (alts == null || alts.length == 0) return isStackValidIngredient(aSlotStack, aIngredient, parallel); for (ItemStack tAltStack : alts) { - int i = isStackValidIngredient(aSlotStack, tAltStack); + int i = isStackValidIngredient(aSlotStack, tAltStack, parallel); if (i >= 0) return i; } return -1; } - private static int isStackValidIngredient(ItemStack aSlotStack, ItemStack aIngredient) { - if (GT_Utility.areStacksEqual(aSlotStack, aIngredient, true) && aIngredient.stackSize <= aSlotStack.stackSize) - return aIngredient.stackSize; + private static int isStackValidIngredient(ItemStack aSlotStack, ItemStack aIngredient, int parallel) { + int ingredientStackSizeWithParallel = aIngredient.stackSize * parallel; + if (GT_Utility.areStacksEqual(aSlotStack, aIngredient, true) + && ingredientStackSizeWithParallel <= aSlotStack.stackSize) + return ingredientStackSizeWithParallel; return -1; } + @Override + public boolean supportsBatchMode() { + return true; + } + + @Override + public boolean onWireCutterRightClick(ForgeDirection side, ForgeDirection wrenchingSide, EntityPlayer aPlayer, + float aX, float aY, float aZ) { + if (aPlayer.isSneaking()) { + batchMode = !batchMode; + if (batchMode) { + GT_Utility.sendChatToPlayer(aPlayer, "Batch recipes"); + } else { + GT_Utility.sendChatToPlayer(aPlayer, "Don't batch recipes"); + } + } + return true; + } + private class SliceStatusWidget extends TextWidget implements ISyncedWidget { private final Slice slice; @@ -1008,7 +1090,9 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas if (progress == 0 || --progress == 0) { // id==0 will be end of chain if 1 input, so we need a +1 here if (id + 1 >= currentInputLength) { - if (addOutput(currentRecipe.mOutput) || !voidingMode.protectItem) reset(); + // use previously calculated parallel output + ItemStack output = mOutputItems[0]; + if (addOutput(output) || !voidingMode.protectItem) reset(); else stuck = true; } else { if (slices[id + 1].start()) reset(); @@ -1022,7 +1106,11 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas startRecipeProcessing(); ItemStack stack = getInputBusContent(id); if (stack == null) return false; - int size = isStackValidIngredient(stack, currentRecipe.mInputs[id], currentRecipe.mOreDictAlt[id]); + int size = isStackValidIngredient( + stack, + currentRecipe.mInputs[id], + currentRecipe.mOreDictAlt[id], + currentRecipeParallel); if (size < 0) return false; progress = mMaxProgresstime / currentInputLength; stack.stackSize -= size; @@ -1040,7 +1128,11 @@ public class MTE_AdvAssLine extends GT_MetaTileEntity_ExtendedPowerMultiBlockBas public boolean hasInput() { ItemStack stack = getInputBusContent(id); if (stack == null) return false; - return isStackValidIngredient(stack, currentRecipe.mInputs[id], currentRecipe.mOreDictAlt[id]) >= 0; + return isStackValidIngredient( + stack, + currentRecipe.mInputs[id], + currentRecipe.mOreDictAlt[id], + currentRecipeParallel) >= 0; } @Override |