diff options
Diffstat (limited to 'src/main/java/gregtech/api/logic/ProcessingLogic.java')
-rw-r--r-- | src/main/java/gregtech/api/logic/ProcessingLogic.java | 228 |
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; + } + } +} |