diff options
Diffstat (limited to 'src/main')
3 files changed, 241 insertions, 43 deletions
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<ItemId, Integer> itemCost; - private final ImmutableMap<Fluid, Integer> 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<ItemId, Integer> itemCost; + protected final ImmutableMap<Fluid, Integer> fluidCost; + + protected final int totalItemCost; + protected final int totalFluidCost; + + protected GT_Single_Recipe_Check( GT_MetaTileEntity_MultiBlockBase multiBlockBase, GT_Recipe recipe, ImmutableMap<ItemId, Integer> itemCost, @@ -176,7 +177,7 @@ public class GT_Single_Recipe_Check { return true; } - private static Map<ItemId, Integer> buildItemMap( + protected static Map<ItemId, Integer> buildItemMap( GT_MetaTileEntity_MultiBlockBase multiBlockBase) { Map<ItemId, Integer> itemMap = new HashMap<>(); for (ItemStack itemStack : multiBlockBase.getStoredInputs()) { @@ -185,7 +186,7 @@ public class GT_Single_Recipe_Check { return itemMap; } - private static Map<Fluid, Integer> buildFluidMap( + protected static Map<Fluid, Integer> buildFluidMap( GT_MetaTileEntity_MultiBlockBase multiBlockBase) { Map<Fluid, Integer> 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<ItemId, Integer> itemCost, + ImmutableMap<Fluid, Integer> 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<ItemStack> items = null; + if (totalItemCost > 0) { + items = multiBlockBase.getStoredInputs(); + + Map<ItemId, Integer> itemMap = new HashMap<>(); + for (ItemStack itemStack : items) { + itemMap.merge(ItemId.createNoCopy(itemStack), itemStack.stackSize, Integer::sum); + } + + for (Map.Entry<ItemId, Integer> entry : itemCost.entrySet()) { + parallel = Math.min(parallel, itemMap.getOrDefault(entry.getKey(), 0) / entry.getValue()); + if (parallel <= 0) { + return 0; + } + } + } + + List<FluidStack> fluids = null; + if (totalFluidCost > 0) { + fluids = multiBlockBase.getStoredFluids(); + + Map<Fluid, Integer> fluidMap = new HashMap<>(); + for (FluidStack fluidStack : fluids) { + fluidMap.merge(fluidStack.getFluid(), fluidStack.amount, Integer::sum); + } + + for (Map.Entry<Fluid, Integer> 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<ItemId, Integer> 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<Fluid, Integer> 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<ItemId, Integer> beforeItems; + private Map<Fluid, Integer> beforeFluids; + private Map<ItemId, Integer> afterItems; + private Map<Fluid, Integer> 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<ItemId, Integer> itemCostBuilder = ImmutableMap.builder(); + for (Map.Entry<ItemId, Integer> entry : beforeItems.entrySet()) { + int cost = entry.getValue() - afterItems.getOrDefault(entry.getKey(), 0); + if (cost > 0) { + itemCostBuilder.put(entry.getKey(), cost); + } + } + + ImmutableMap.Builder<Fluid, Integer> fluidCostBuilder = ImmutableMap.builder(); + for (Map.Entry<Fluid, Integer> 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; } } |