aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/logic/ProcessingLogic.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/gregtech/api/logic/ProcessingLogic.java')
-rw-r--r--src/main/java/gregtech/api/logic/ProcessingLogic.java228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/logic/ProcessingLogic.java b/src/main/java/gregtech/api/logic/ProcessingLogic.java
new file mode 100644
index 0000000000..4d203ed80f
--- /dev/null
+++ b/src/main/java/gregtech/api/logic/ProcessingLogic.java
@@ -0,0 +1,228 @@
+package gregtech.api.logic;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+import gregtech.api.interfaces.tileentity.IRecipeLockable;
+import gregtech.api.recipe.RecipeMap;
+import gregtech.api.recipe.check.CheckRecipeResult;
+import gregtech.api.recipe.check.CheckRecipeResultRegistry;
+import gregtech.api.recipe.check.SingleRecipeCheck;
+import gregtech.api.util.GT_OverclockCalculator;
+import gregtech.api.util.GT_ParallelHelper;
+import gregtech.api.util.GT_Recipe;
+
+/**
+ * Logic class to calculate result of recipe check from inputs, based on recipemap.
+ */
+@SuppressWarnings({ "unused", "UnusedReturnValue" })
+public class ProcessingLogic extends AbstractProcessingLogic<ProcessingLogic> {
+
+ protected IRecipeLockable recipeLockableMachine;
+ protected ItemStack specialSlotItem;
+ protected ItemStack[] inputItems;
+ protected FluidStack[] inputFluids;
+ protected boolean isRecipeLocked;
+
+ public ProcessingLogic() {}
+
+ // #region Setters
+
+ @Nonnull
+ public ProcessingLogic setInputItems(ItemStack... itemInputs) {
+ this.inputItems = itemInputs;
+ return getThis();
+ }
+
+ @Nonnull
+ public ProcessingLogic setInputItems(List<ItemStack> itemOutputs) {
+ this.inputItems = itemOutputs.toArray(new ItemStack[0]);
+ return getThis();
+ }
+
+ @Nonnull
+ public ProcessingLogic setInputFluids(FluidStack... fluidInputs) {
+ this.inputFluids = fluidInputs;
+ return getThis();
+ }
+
+ @Nonnull
+ public ProcessingLogic setInputFluids(List<FluidStack> fluidInputs) {
+ this.inputFluids = fluidInputs.toArray(new FluidStack[0]);
+ return getThis();
+ }
+
+ public ProcessingLogic setSpecialSlotItem(ItemStack specialSlotItem) {
+ this.specialSlotItem = specialSlotItem;
+ return getThis();
+ }
+
+ /**
+ * Enables single recipe locking mode.
+ */
+ public ProcessingLogic setRecipeLocking(IRecipeLockable recipeLockableMachine, boolean isRecipeLocked) {
+ this.recipeLockableMachine = recipeLockableMachine;
+ this.isRecipeLocked = isRecipeLocked;
+ return getThis();
+ }
+
+ /**
+ * Clears calculated results and provided machine inputs to prepare for the next machine operation.
+ */
+
+ public ProcessingLogic clear() {
+ this.inputItems = null;
+ this.inputFluids = null;
+ this.specialSlotItem = null;
+ this.outputItems = null;
+ this.outputFluids = null;
+ this.calculatedEut = 0;
+ this.duration = 0;
+ this.calculatedParallels = 0;
+ return getThis();
+ }
+
+ // #endregion
+
+ // #region Logic
+
+ /**
+ * Executes the recipe check: Find recipe from recipemap, Calculate parallel, overclock and outputs.
+ */
+ @Nonnull
+ public CheckRecipeResult process() {
+ RecipeMap<?> recipeMap = preProcess();
+
+ if (inputItems == null) {
+ inputItems = new ItemStack[0];
+ }
+ if (inputFluids == null) {
+ inputFluids = new FluidStack[0];
+ }
+
+ if (isRecipeLocked && recipeLockableMachine != null && recipeLockableMachine.getSingleRecipeCheck() != null) {
+ // Recipe checker is already built, we'll use it
+ SingleRecipeCheck singleRecipeCheck = recipeLockableMachine.getSingleRecipeCheck();
+ // Validate recipe here, otherwise machine will show "not enough output space"
+ // even if recipe cannot be found
+ if (singleRecipeCheck.checkRecipeInputs(false, 1, inputItems, inputFluids) == 0) {
+ return CheckRecipeResultRegistry.NO_RECIPE;
+ }
+
+ return validateAndCalculateRecipe(
+ recipeLockableMachine.getSingleRecipeCheck()
+ .getRecipe()).checkRecipeResult;
+ }
+ Stream<GT_Recipe> matchedRecipes = findRecipeMatches(recipeMap);
+ Iterable<GT_Recipe> recipeIterable = matchedRecipes::iterator;
+ CheckRecipeResult checkRecipeResult = CheckRecipeResultRegistry.NO_RECIPE;
+ for (GT_Recipe matchedRecipe : recipeIterable) {
+ CalculationResult foundResult = validateAndCalculateRecipe(matchedRecipe);
+ if (foundResult.successfullyConsumedInputs) {
+ // Successfully found and set recipe, so return it
+ return foundResult.checkRecipeResult;
+ }
+ if (foundResult.checkRecipeResult != CheckRecipeResultRegistry.NO_RECIPE) {
+ // Recipe failed in interesting way, so remember that and continue searching
+ checkRecipeResult = foundResult.checkRecipeResult;
+ }
+ }
+ return checkRecipeResult;
+ }
+
+ /**
+ * Checks if supplied recipe is valid for process. This involves voltage check, output full check. If successful,
+ * additionally performs input consumption, output calculation with parallel, and overclock calculation.
+ *
+ * @param recipe The recipe which will be checked and processed
+ */
+ @Nonnull
+ private CalculationResult validateAndCalculateRecipe(@Nonnull GT_Recipe recipe) {
+ CheckRecipeResult result = validateRecipe(recipe);
+ if (!result.wasSuccessful()) {
+ return CalculationResult.ofFailure(result);
+ }
+
+ GT_ParallelHelper helper = createParallelHelper(recipe);
+ GT_OverclockCalculator calculator = createOverclockCalculator(recipe);
+ helper.setCalculator(calculator);
+ helper.build();
+
+ if (!helper.getResult()
+ .wasSuccessful()) {
+ return CalculationResult.ofFailure(helper.getResult());
+ }
+
+ return CalculationResult.ofSuccess(applyRecipe(recipe, helper, calculator, result));
+ }
+
+ /**
+ * Finds a list of matched recipes. At this point no additional check to the matched recipe has been done.
+ * <p>
+ * Override {@link #validateRecipe} to have custom check.
+ * <p>
+ * Override this method if it doesn't work with normal recipemaps.
+ */
+ @Nonnull
+ protected Stream<GT_Recipe> findRecipeMatches(@Nullable RecipeMap<?> map) {
+ if (map == null) {
+ return Stream.empty();
+ }
+ return map.findRecipeQuery()
+ .items(inputItems)
+ .fluids(inputFluids)
+ .specialSlot(specialSlotItem)
+ .cachedRecipe(lastRecipe)
+ .findAll();
+ }
+
+ /**
+ * Override to tweak parallel logic if needed.
+ */
+ @Nonnull
+ protected GT_ParallelHelper createParallelHelper(@Nonnull GT_Recipe recipe) {
+ return new GT_ParallelHelper().setRecipe(recipe)
+ .setItemInputs(inputItems)
+ .setFluidInputs(inputFluids)
+ .setAvailableEUt(availableVoltage * availableAmperage)
+ .setMachine(machine, protectItems, protectFluids)
+ .setRecipeLocked(recipeLockableMachine, isRecipeLocked)
+ .setMaxParallel(maxParallel)
+ .setEUtModifier(euModifier)
+ .enableBatchMode(batchSize)
+ .setConsumption(true)
+ .setOutputCalculation(true);
+ }
+
+ // #endregion
+
+ /**
+ * Represents the status of check recipe calculation. {@link #successfullyConsumedInputs} does not necessarily mean
+ * {@link #checkRecipeResult} being successful, when duration or power is overflowed. Being failure means
+ * recipe cannot meet requirements and recipe search should be continued if possible.
+ */
+ protected final static class CalculationResult {
+
+ public final boolean successfullyConsumedInputs;
+ public final CheckRecipeResult checkRecipeResult;
+
+ public static CalculationResult ofSuccess(CheckRecipeResult checkRecipeResult) {
+ return new CalculationResult(true, checkRecipeResult);
+ }
+
+ public static CalculationResult ofFailure(CheckRecipeResult checkRecipeResult) {
+ return new CalculationResult(false, checkRecipeResult);
+ }
+
+ private CalculationResult(boolean successfullyConsumedInputs, CheckRecipeResult checkRecipeResult) {
+ this.successfullyConsumedInputs = successfullyConsumedInputs;
+ this.checkRecipeResult = checkRecipeResult;
+ }
+ }
+}