From f74c7cc297d1d19d38a19683cd277ad9ce605d3a Mon Sep 17 00:00:00 2001 From: miozune Date: Mon, 4 Dec 2023 05:34:27 +0900 Subject: Refactor RecipeMap (#2345) * Remove deprecated and unused things * Move recipemap subclasses * Move GT_Recipe_Map to outside and rename to RecipeMap * Move recipemap instances to separated class & remove prepending s * Remove useless GT_Recipe constructors * Always use ModularUI * Rename IGT_RecipeMap -> IRecipeMap * Add RecipeMapBuilder * Remove more deprecated and unused things * Fix RecipeMap type parameters * Use multimap for recipe index * Fix bending recipe error in dev env * Remove mUniqueIdentifier * Update AE2FC * Less edgy texture for NEI recipe background * Add replicator fluid output slot for NEI and machine GUI * Fix fluid fuels not having fuel value in large boilers * Remove GT_RectHandler and NEI_TransferRectHost * Remove RecipeMapHandler * Move NEI energy description from RecipeMapFrontend to Power * Refactor the way to filter fusion recipes * Check restriction for some properties * Remove showVoltageAmperage * Make Power accept GT_Recipe * Fix NPE * Move NEI duration description to Power from Frontend * Directly implement IRecipeProcessingAwareHatch for GT_MetaTileEntity_Hatch_InputBus_ME * Make Power integrated with GT_OverclockCalculator * Rename Power -> OverclockDescriber * Don't modify recipe find logic until postload finishes * Reformat reserved MTE ids * Fix check for too few inputs on recipe addition * Move replicator logic to backend * Stop un-hiding assline recipes * Allow setting custom recipe comparator & implement for fusion * Update AE2FC * Rename getRecipeList and getRecipes -> getRecipeMap * Automatically register recipe catalysts * Cleanup the way to detect recipe collision * Make use of BasicUIProperties for basic machines * Make use of BasicUIProperties for UIHelper * Rename specialHandler -> recipeTransformer * Add way to automatically register handler info * Add recipe category * Add some APIs for addons * Rename blastRecipes -> blastFurnaceRecipes * Remove GT_MetaTileEntity_BasicMachine_GT_Recipe#mSharedTank and #mRequiresFluidForFiltering * Don't require setting duration and EU/t for fuel recipes * Don't require setting EU/t for primitive blast furnace recipes * Revert change to addMultiblockChemicalRecipe * Fix large boiler general desc recipe not being added * Hide duration and EU/t from large boiler * Cleanup recipe stacktrace draw * Extend metadata usage of recipe builder to recipe itself * Implement metadata handling & NEI comparator for PCB factory * Some rename around NEIRecipeInfo * Some toString implementations * Add more APIs for addons & some rename * Infer handler icon from recipe catalyst if one is not set * Also shrink recipe title when OC is not used * Remove rare earth centrifuge recipe * Use metadata for replicator backend * Adjust geothermal generator output slot * Allow having multiple transferrects * Store recipemap reference in backend * Rename vacuumRecipes -> vacuumFreezerRecipes * Add config to tweak visibility of recipe categories * Remove mHideRecyclingRecipes in favor of recipe category config * Fix typo fluidSolidfierRecipes -> fluidSolidifierRecipes * Refactor findRecipe and ProcessingLogic to use Stream * Fix BBF handler icon & remove bronze blast furnace * Add fluent API for findRecipe * Add way to stop adding progressbar * Change arg order for special texture * Avoid overwriting interesting failure with NO_RECIPE * Some changes for FuelBackend * Set space project icon * Remove localization from TT * Remove CNC recipe adder * Move recipe extractor from AE2FC * Minor internal change for ProcessingLogic#applyRecipe * More javadoc on #getAvailableRecipeMaps * Better implementation of #ofSupplier * Move replicator exponent config to GT_Proxy * Remove RC & IC2 macerator handling * Rename StreamUtil -> GT_StreamUtil * Refactor code around RecipeMetadataStorage * Revise #compileRecipe javadoc * Switch extreme diesel recipe loader to downstream recipe map * Optimize #reMap * Rename reload -> reloadNEICache * Minor tweak for drawEnergyInfo * a bit more doc * Adjust recipe catalysts * Add toString implementation for GT_Fluid for debug * Minor revision for OilCrackerBackend * Index replicator recipes by material --------- Co-authored-by: Glease <4586901+Glease@users.noreply.github.com> --- .../api/logic/ComplexParallelProcessingLogic.java | 7 +- .../java/gregtech/api/logic/ProcessingLogic.java | 174 ++++++++++----------- 2 files changed, 90 insertions(+), 91 deletions(-) (limited to 'src/main/java/gregtech/api/logic') diff --git a/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java b/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java index 05d39cd02c..3c7974db9e 100644 --- a/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java +++ b/src/main/java/gregtech/api/logic/ComplexParallelProcessingLogic.java @@ -6,6 +6,7 @@ import net.minecraft.item.ItemStack; import net.minecraftforge.fluids.FluidStack; import gregtech.api.multitileentity.multiblock.base.Controller; +import gregtech.api.recipe.RecipeMap; import gregtech.api.util.GT_OverclockCalculator; import gregtech.api.util.GT_ParallelHelper; import gregtech.api.util.GT_Recipe; @@ -13,7 +14,7 @@ import gregtech.api.util.GT_Recipe; public class ComplexParallelProcessingLogic { protected Controller tileEntity; - protected GT_Recipe.GT_Recipe_Map recipeMap; + protected RecipeMap recipeMap; protected boolean hasPerfectOverclock; protected final int maxComplexParallels; protected final ItemStack[][] outputItems; @@ -30,7 +31,7 @@ public class ComplexParallelProcessingLogic { this(null, maxComplexParallels); } - public ComplexParallelProcessingLogic(GT_Recipe.GT_Recipe_Map recipeMap, int maxComplexParallels) { + public ComplexParallelProcessingLogic(RecipeMap recipeMap, int maxComplexParallels) { this.maxComplexParallels = maxComplexParallels; this.recipeMap = recipeMap; inputItems = new ItemStack[maxComplexParallels][]; @@ -44,7 +45,7 @@ public class ComplexParallelProcessingLogic { isFluidVoidProtected = new boolean[maxComplexParallels]; } - public ComplexParallelProcessingLogic setRecipeMap(GT_Recipe.GT_Recipe_Map recipeMap) { + public ComplexParallelProcessingLogic setRecipeMap(RecipeMap recipeMap) { this.recipeMap = recipeMap; return this; } diff --git a/src/main/java/gregtech/api/logic/ProcessingLogic.java b/src/main/java/gregtech/api/logic/ProcessingLogic.java index 803abafbe3..6b9f2d454f 100644 --- a/src/main/java/gregtech/api/logic/ProcessingLogic.java +++ b/src/main/java/gregtech/api/logic/ProcessingLogic.java @@ -2,6 +2,7 @@ package gregtech.api.logic; import java.util.List; import java.util.function.Supplier; +import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -13,15 +14,13 @@ import org.jetbrains.annotations.NotNull; import gregtech.api.interfaces.tileentity.IRecipeLockable; import gregtech.api.interfaces.tileentity.IVoidable; +import gregtech.api.recipe.RecipeMap; import gregtech.api.recipe.check.CheckRecipeResult; import gregtech.api.recipe.check.CheckRecipeResultRegistry; -import gregtech.api.recipe.check.FindRecipeResult; -import gregtech.api.recipe.check.RecipeValidator; import gregtech.api.recipe.check.SingleRecipeCheck; import gregtech.api.util.GT_OverclockCalculator; import gregtech.api.util.GT_ParallelHelper; import gregtech.api.util.GT_Recipe; -import gregtech.api.util.GT_Recipe.GT_Recipe_Map; /** * Logic class to calculate result of recipe check from inputs, based on recipemap. @@ -31,9 +30,9 @@ public class ProcessingLogic { protected IVoidable machine; protected IRecipeLockable recipeLockableMachine; - protected Supplier recipeMapSupplier; + protected Supplier> recipeMapSupplier; protected GT_Recipe lastRecipe; - protected GT_Recipe_Map lastRecipeMap; + protected RecipeMap lastRecipeMap; protected ItemStack specialSlotItem; protected ItemStack[] inputItems; protected ItemStack[] outputItems; @@ -146,11 +145,11 @@ public class ProcessingLogic { return this; } - public ProcessingLogic setRecipeMap(GT_Recipe_Map recipeMap) { + public ProcessingLogic setRecipeMap(RecipeMap recipeMap) { return setRecipeMapSupplier(() -> recipeMap); } - public ProcessingLogic setRecipeMapSupplier(Supplier supplier) { + public ProcessingLogic setRecipeMapSupplier(Supplier> supplier) { this.recipeMapSupplier = supplier; return this; } @@ -263,7 +262,7 @@ public class ProcessingLogic { */ @Nonnull public CheckRecipeResult process() { - GT_Recipe_Map recipeMap; + RecipeMap recipeMap; if (recipeMapSupplier == null) { recipeMap = null; } else { @@ -278,6 +277,13 @@ public class ProcessingLogic { maxParallel = maxParallelSupplier.get(); } + 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(); @@ -287,55 +293,39 @@ public class ProcessingLogic { return CheckRecipeResultRegistry.NO_RECIPE; } - return processRecipe( + return validateAndCalculateRecipe( recipeLockableMachine.getSingleRecipeCheck() - .getRecipe()); + .getRecipe()).checkRecipeResult; } - FindRecipeResult findRecipeResult = findRecipe(recipeMap); - // If processRecipe is not overridden, advanced recipe validation logic is used, and we can reuse calculations. - if (findRecipeResult.hasRecipeValidator()) { - RecipeValidator recipeValidator = findRecipeResult.getRecipeValidator(); - - // There are two cases: - // 1 - there are actually no matching recipes - // 2 - there are some matching recipes, but we rejected it due to our advanced validation (e.g. OUTPUT_FULL) - if (findRecipeResult.getState() == FindRecipeResult.State.NOT_FOUND - && recipeValidator.getFirstCheckResult() != null) { - // Here we're handling case 2 - // If there are matching recipes but our validation rejected them, - // we should return a first one to display a proper error in the machine GUI - return recipeValidator.getFirstCheckResult(); + Stream matchedRecipes = findRecipeMatches(recipeMap); + Iterable 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 everything is ok, reuse our calculations - if (recipeValidator.isExecutedAtLeastOnce() && findRecipeResult.isSuccessful()) { - return applyRecipe( - findRecipeResult.getRecipeNonNull(), - recipeValidator.getLastParallelHelper(), - recipeValidator.getLastOverclockCalculator(), - recipeValidator.getLastCheckResult()); + if (foundResult.checkRecipeResult != CheckRecipeResultRegistry.NO_RECIPE) { + // Recipe failed in interesting way, so remember that and continue searching + checkRecipeResult = foundResult.checkRecipeResult; } } - - if (!findRecipeResult.isSuccessful()) { - return CheckRecipeResultRegistry.NO_RECIPE; - } - - return processRecipe(findRecipeResult.getRecipeNonNull()); + return checkRecipeResult; } /** - * Checks if supplied recipe is valid for process. - * If so, additionally performs input consumption, output calculation with parallel, and overclock calculation. + * 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 - protected CheckRecipeResult processRecipe(@Nonnull GT_Recipe recipe) { + private CalculationResult validateAndCalculateRecipe(@Nonnull GT_Recipe recipe) { CheckRecipeResult result = validateRecipe(recipe); if (!result.wasSuccessful()) { - return result; + return CalculationResult.ofFailure(result); } GT_ParallelHelper helper = createParallelHelper(recipe); @@ -343,19 +333,20 @@ public class ProcessingLogic { helper.setCalculator(calculator); helper.build(); - return applyRecipe(recipe, helper, calculator, result); + if (!helper.getResult() + .wasSuccessful()) { + return CalculationResult.ofFailure(helper.getResult()); + } + + return CalculationResult.ofSuccess(applyRecipe(recipe, helper, calculator, result)); } /** - * Applies the recipe and calculated parameters + * Check has been succeeded, so it applies the recipe and calculated parameters. + * At this point, inputs have been already consumed. */ private CheckRecipeResult applyRecipe(@NotNull GT_Recipe recipe, GT_ParallelHelper helper, GT_OverclockCalculator calculator, CheckRecipeResult result) { - if (!helper.getResult() - .wasSuccessful()) { - return helper.getResult(); - } - if (recipe.mCanBeBuffered) { lastRecipe = recipe; } else { @@ -398,30 +389,31 @@ public class ProcessingLogic { } /** - * Override if you don't work with regular gt recipe maps + * Finds a list of matched recipes. At this point no additional check to the matched recipe has been done. + *

+ * Override {@link #validateRecipe} to have custom check. + *

+ * Override this method if it doesn't work with normal recipemaps. */ @Nonnull - protected FindRecipeResult findRecipe(@Nullable GT_Recipe_Map map) { - if (map == null) return FindRecipeResult.NOT_FOUND; - - RecipeValidator recipeValidator = new RecipeValidator( - this::validateRecipe, - this::createParallelHelper, - this::createOverclockCalculator); - - FindRecipeResult findRecipeResult = map.findRecipeWithResult( - lastRecipe, - recipeValidator, - false, - false, - amperageOC ? availableVoltage * availableAmperage : availableVoltage, - inputFluids, - specialSlotItem, - inputItems); - - findRecipeResult.setRecipeValidator(recipeValidator); + protected Stream findRecipeMatches(@Nullable RecipeMap map) { + if (map == null) { + return Stream.empty(); + } + return map.findRecipeQuery() + .items(inputItems) + .fluids(inputFluids) + .specialSlot(specialSlotItem) + .cachedRecipe(lastRecipe) + .findAll(); + } - return findRecipeResult; + /** + * Override to do additional check for found recipe if needed. + */ + @Nonnull + protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) { + return CheckRecipeResultRegistry.SUCCESSFUL; } /** @@ -442,24 +434,6 @@ public class ProcessingLogic { .setOutputCalculation(true); } - /** - * Override to do additional check for finding recipe if needed, mainly for special value of the recipe. - */ - @Nonnull - protected CheckRecipeResult validateRecipe(@Nonnull GT_Recipe recipe) { - return CheckRecipeResultRegistry.SUCCESSFUL; - } - - /** - * Use {@link #createOverclockCalculator(GT_Recipe)} - */ - @Nonnull - @Deprecated - protected GT_OverclockCalculator createOverclockCalculator(@Nonnull GT_Recipe recipe, - @Nullable GT_ParallelHelper helper) { - return createOverclockCalculator(recipe); - } - /** * Override to tweak overclock logic if needed. */ @@ -514,4 +488,28 @@ public class ProcessingLogic { } // 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; + } + } } -- cgit