diff --git a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java index 23018a736ca..dacd7e38f81 100644 --- a/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java +++ b/src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java @@ -43,6 +43,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import static gregtech.api.GTValues.ULV; @@ -261,7 +262,7 @@ public Recipe getPreviousRecipe() { * @return true if recipes should be searched for */ protected boolean shouldSearchForRecipes() { - return canWorkWithInputs() && canFitNewOutputs(); + return canWorkWithInputs() || canFitNewOutputs(); } /** @@ -398,7 +399,8 @@ public void forceRecipeRecheck() { */ protected void trySearchNewRecipe() { long maxVoltage = getMaxVoltage(); - Recipe currentRecipe; + Recipe currentRecipe = null; + Iterator recipeIterator; IItemHandlerModifiable importInventory = getInputInventory(); IMultipleTankHandler importFluids = getInputTank(); @@ -406,19 +408,47 @@ protected void trySearchNewRecipe() { if (checkPreviousRecipe()) { currentRecipe = this.previousRecipe; // If there is no active recipe, then we need to find one. - } else { - currentRecipe = findRecipe(maxVoltage, importInventory, importFluids); } - // If a recipe was found, then inputs were valid. Cache found recipe. - if (currentRecipe != null) { + + // proceed if previous recipe still works. + if (currentRecipe != null && checkRecipe(currentRecipe) && prepareRecipe(currentRecipe)) { this.previousRecipe = currentRecipe; + return; } - this.invalidInputsForRecipes = (currentRecipe == null); - // proceed if we have a usable recipe. - if (currentRecipe != null && checkRecipe(currentRecipe)) { - prepareRecipe(currentRecipe); + recipeIterator = getRecipeIterator(maxVoltage, importInventory, importFluids); + + boolean invalidOutputs = false; + boolean invalidInputs = false; + + // not any recipe was found + if (recipeIterator == null || !recipeIterator.hasNext()) { + this.invalidInputsForRecipes = true; + return; } + + // Search for a new recipe if the previous one is no longer valid + while (recipeIterator.hasNext()) { + Recipe next = recipeIterator.next(); + if (next == null) continue; + + if (checkRecipe(next) && prepareRecipe(next)) { + // If a new recipe was found, cache found recipe. + this.previousRecipe = next; + break; + } else { + // store if recipe has bad IO, then reset for next recipe checks + invalidOutputs = this.isOutputsFull; + invalidInputs = this.invalidInputsForRecipes; + this.isOutputsFull = false; + this.invalidInputsForRecipes = false; + } + } + + // if no valid recipes are found mark the inputs and outputs as invalid so any changes + // will re-trigger a recipe search + this.isOutputsFull = invalidOutputs; + this.invalidInputsForRecipes = invalidInputs; } /** @@ -650,6 +680,25 @@ protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMul return map.findRecipe(maxVoltage, inputs, fluidInputs); } + /** + * Creates a Recipe Iterator using inputs + * + * @param maxVoltage the maximum voltage the recipe can have + * @param inputs the item inputs used to search for the recipe + * @param fluidInputs the fluid inputs used to search for the recipe + * @return the recipe iterator if this.recipeMap is valid, otherwise null + */ + @Nullable + protected Iterator getRecipeIterator(long maxVoltage, IItemHandlerModifiable inputs, + IMultipleTankHandler fluidInputs) { + RecipeMap map = getRecipeMap(); + if (map == null || !isRecipeMapValid(map)) { + return null; + } + + return map.getRecipeIterator(maxVoltage, inputs, fluidInputs); + } + /** * @param recipeMap the recipemap to check * @return true if the recipemap is valid for recipe search diff --git a/src/main/java/gregtech/api/recipes/RecipeIterator.java b/src/main/java/gregtech/api/recipes/RecipeIterator.java new file mode 100644 index 00000000000..fbf91186dac --- /dev/null +++ b/src/main/java/gregtech/api/recipes/RecipeIterator.java @@ -0,0 +1,65 @@ +package gregtech.api.recipes; + +import gregtech.api.recipes.map.AbstractMapIngredient; + +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +public class RecipeIterator implements Iterator { + + int index; + List> ingredients; + @NotNull + RecipeMap recipeMap; + @NotNull + Predicate canHandle; + Recipe cachedNext; + + RecipeIterator(@NotNull RecipeMap recipeMap, List> ingredients, + @NotNull Predicate canHandle) { + this.ingredients = ingredients; + this.recipeMap = recipeMap; + this.canHandle = canHandle; + } + + @Override + public boolean hasNext() { + if (cachedNext != null) return true; + if (ingredients == null || this.index > this.ingredients.size()) return false; + + while (index < ingredients.size()) { + Recipe r = recipeMap.recurseIngredientTreeFindRecipe(ingredients, recipeMap.getLookup(), canHandle, index, + 0, + (1L << index)); + ++index; + if (r != null) { + cachedNext = r; + return true; + } + } + return false; + } + + @Override + public Recipe next() { + if (cachedNext != null) { + Recipe r = cachedNext.copy(); + cachedNext = null; + return r; + } + // couldn't build any inputs to use for search, so no recipe could be found + if (ingredients == null) return null; + // Try each ingredient as a starting point, save current index + Recipe r = null; + while (index < ingredients.size()) { + r = recipeMap.recurseIngredientTreeFindRecipe(ingredients, recipeMap.getLookup(), canHandle, index, 0, + (1L << index)); + ++index; + if (r != null) break; + } + return r; + } +} diff --git a/src/main/java/gregtech/api/recipes/RecipeMap.java b/src/main/java/gregtech/api/recipes/RecipeMap.java index a2c2ef7b1f4..36ef5fce1b0 100644 --- a/src/main/java/gregtech/api/recipes/RecipeMap.java +++ b/src/main/java/gregtech/api/recipes/RecipeMap.java @@ -73,6 +73,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -365,6 +366,10 @@ public RecipeMap> getSmallRecipeMap() { return smallRecipeMap; } + public Branch getLookup() { + return lookup; + } + /** * Internal usage only, use {@link RecipeBuilder#buildAndRegister()} * @@ -624,6 +629,72 @@ public Recipe find(@NotNull Collection items, @NotNull Collection getRecipeIterator(long voltage, IItemHandlerModifiable inputs, + IMultipleTankHandler fluidInputs) { + return this.getRecipeIterator(voltage, GTUtility.itemHandlerToList(inputs), + GTUtility.fluidHandlerToList(fluidInputs)); + } + + /** + * Creates an Iterator of Recipes matching the Fluid and/or ItemStack Inputs. + * + * @param voltage Voltage of the Machine or Long.MAX_VALUE if it has no Voltage + * @param inputs the Item Inputs + * @param fluidInputs the Fluid Inputs + * @return the Recipe Iterator + */ + @NotNull + public Iterator getRecipeIterator(long voltage, List inputs, List fluidInputs) { + return getRecipeIterator(voltage, inputs, fluidInputs, false); + } + + /** + * Creates an Iterator of Recipes matching the Fluid and/or ItemStack Inputs. + * + * @param voltage Voltage of the Machine or Long.MAX_VALUE if it has no Voltage + * @param inputs the Item Inputs + * @param fluidInputs the Fluid Inputs + * @param exactVoltage should require exact voltage matching on recipe. used by craftweaker + * @return the Recipe Iterator + */ + @NotNull + public Iterator getRecipeIterator(long voltage, final List inputs, + final List fluidInputs, + boolean exactVoltage) { + final List items = inputs.stream().filter(s -> !s.isEmpty()).collect(Collectors.toList()); + final List fluids = fluidInputs.stream().filter(f -> f != null && f.amount != 0) + .collect(Collectors.toList()); + + return getRecipeIterator(items, fluids, recipe -> { + if (exactVoltage && recipe.getEUt() != voltage) { + // if exact voltage is required, the recipe is not considered valid + return false; + } + if (recipe.getEUt() > voltage) { + // there is not enough voltage to consider the recipe valid + return false; + } + return recipe.matches(false, inputs, fluidInputs); + }); + } + + /** + * Creates an Iterator of Recipes using Items and Fluids. + * + * @param items a collection of items + * @param fluids a collection of fluids + * @param canHandle a predicate for determining if a recipe is valid + * @return the Recipe Iterator + */ + @NotNull + public Iterator getRecipeIterator(@NotNull Collection items, + @NotNull Collection fluids, + @NotNull Predicate canHandle) { + List> list = prepareRecipeFind(items, fluids); + return new RecipeIterator(this, list, canHandle); + } + /** * Builds a list of unique ItemStacks from the given Collection of ItemStacks. * Used to reduce the number inputs, if for example there is more than one of the same input, @@ -723,9 +794,9 @@ private Recipe recurseIngredientTreeFindRecipe(@NotNull List> ingredients, - @NotNull Branch branchMap, @NotNull Predicate canHandle, - int index, int count, long skip) { + protected Recipe recurseIngredientTreeFindRecipe(@NotNull List> ingredients, + @NotNull Branch branchMap, @NotNull Predicate canHandle, + int index, int count, long skip) { // exhausted all the ingredients, and didn't find anything if (count == ingredients.size()) return null; diff --git a/src/main/java/gregtech/api/recipes/machines/RecipeMapFluidCanner.java b/src/main/java/gregtech/api/recipes/machines/RecipeMapFluidCanner.java index 719cde4c311..466d8bdeda7 100644 --- a/src/main/java/gregtech/api/recipes/machines/RecipeMapFluidCanner.java +++ b/src/main/java/gregtech/api/recipes/machines/RecipeMapFluidCanner.java @@ -16,6 +16,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.Iterator; import java.util.List; @ApiStatus.Internal @@ -83,4 +85,11 @@ public Recipe findRecipe(long voltage, List inputs, List } return null; } + + @Override + @NotNull + public Iterator getRecipeIterator(long voltage, List inputs, List fluidInputs, + boolean exactVoltage) { + return Collections.singleton(this.findRecipe(voltage, inputs, fluidInputs, exactVoltage)).iterator(); + } } diff --git a/src/main/java/gregtech/api/recipes/machines/RecipeMapFormingPress.java b/src/main/java/gregtech/api/recipes/machines/RecipeMapFormingPress.java index 492ae4080c1..f6f250c1fbb 100644 --- a/src/main/java/gregtech/api/recipes/machines/RecipeMapFormingPress.java +++ b/src/main/java/gregtech/api/recipes/machines/RecipeMapFormingPress.java @@ -17,6 +17,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.Iterator; import java.util.List; @ApiStatus.Internal @@ -77,4 +79,11 @@ public Recipe findRecipe(long voltage, List inputs, List } return recipe; } + + @Override + @NotNull + public Iterator getRecipeIterator(long voltage, List inputs, List fluidInputs, + boolean exactVoltage) { + return Collections.singleton(this.findRecipe(voltage, inputs, fluidInputs, exactVoltage)).iterator(); + } } diff --git a/src/main/java/gregtech/api/recipes/machines/RecipeMapFurnace.java b/src/main/java/gregtech/api/recipes/machines/RecipeMapFurnace.java index 2c1b3d442e2..6475721e5b0 100644 --- a/src/main/java/gregtech/api/recipes/machines/RecipeMapFurnace.java +++ b/src/main/java/gregtech/api/recipes/machines/RecipeMapFurnace.java @@ -15,6 +15,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.Iterator; import java.util.List; @ApiStatus.Internal @@ -50,4 +52,11 @@ public Recipe findRecipe(long voltage, List inputs, List return null; } + + @Override + @NotNull + public Iterator getRecipeIterator(long voltage, List inputs, List fluidInputs, + boolean exactVoltage) { + return Collections.singleton(this.findRecipe(voltage, inputs, fluidInputs, exactVoltage)).iterator(); + } } diff --git a/src/main/java/gregtech/api/recipes/machines/RecipeMapScanner.java b/src/main/java/gregtech/api/recipes/machines/RecipeMapScanner.java index f76c7b10814..249ff7f0233 100644 --- a/src/main/java/gregtech/api/recipes/machines/RecipeMapScanner.java +++ b/src/main/java/gregtech/api/recipes/machines/RecipeMapScanner.java @@ -14,6 +14,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; import java.util.List; @ApiStatus.Internal @@ -60,4 +62,11 @@ public Recipe findRecipe(long voltage, List inputs, List } return null; } + + @Override + @NotNull + public Iterator getRecipeIterator(long voltage, List inputs, List fluidInputs, + boolean exactVoltage) { + return Collections.singleton(this.findRecipe(voltage, inputs, fluidInputs, exactVoltage)).iterator(); + } }