diff --git a/src/main/java/codechicken/nei/FavoriteRecipes.java b/src/main/java/codechicken/nei/FavoriteRecipes.java index f0271d3ba..2cb6d56a7 100644 --- a/src/main/java/codechicken/nei/FavoriteRecipes.java +++ b/src/main/java/codechicken/nei/FavoriteRecipes.java @@ -8,7 +8,6 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -129,8 +128,8 @@ public void execute() { } protected List getOutputs(IRecipeHandler handler, int recipeIndex) { - final PositionedStack pStackResult = handler.getResultStack(recipeIndex); - return pStackResult != null ? Collections.singletonList(pStackResult) : handler.getOtherStacks(recipeIndex); + final List pStackResults = handler.getResultStacks(recipeIndex); + return !pStackResults.isEmpty() ? pStackResults : handler.getOtherStacks(recipeIndex); } }; diff --git a/src/main/java/codechicken/nei/PresetsList.java b/src/main/java/codechicken/nei/PresetsList.java index c18d0895a..d8aef534e 100644 --- a/src/main/java/codechicken/nei/PresetsList.java +++ b/src/main/java/codechicken/nei/PresetsList.java @@ -89,9 +89,9 @@ public boolean matches(IRecipeHandler handler, int recipeIndex) { return false; } - final PositionedStack result = handler.getResultStack(recipeIndex); + final List results = handler.getResultStacks(recipeIndex); - if (result != null && matchPositionedStack(result)) { + if (!results.isEmpty() && matchPositionedStack(results, true)) { return true; } @@ -101,7 +101,13 @@ public boolean matches(IRecipeHandler handler, int recipeIndex) { return true; } - return result == null && others.isEmpty(); + final List extraInputs = handler.getCatalystStacks(recipeIndex); + + if (!extraInputs.isEmpty() && matchPositionedStack(extraInputs, true)) { + return true; + } + + return results.isEmpty() && others.isEmpty(); } private boolean matchPositionedStack(List items, boolean dir) { diff --git a/src/main/java/codechicken/nei/filter/AllOthersRecipeFilter.java b/src/main/java/codechicken/nei/filter/AllOthersRecipeFilter.java index a7ded945b..3b615b1e0 100644 --- a/src/main/java/codechicken/nei/filter/AllOthersRecipeFilter.java +++ b/src/main/java/codechicken/nei/filter/AllOthersRecipeFilter.java @@ -24,6 +24,12 @@ public boolean matches(IRecipeHandler handler, int recipeIndex) { } } + for (PositionedStack pStack : handler.getCatalystStacks(recipeIndex)) { + if (!match(pStack)) { + return false; + } + } + return true; } diff --git a/src/main/java/codechicken/nei/filter/AnyOthersRecipeFilter.java b/src/main/java/codechicken/nei/filter/AnyOthersRecipeFilter.java index f3c4c31f0..08fd9c934 100644 --- a/src/main/java/codechicken/nei/filter/AnyOthersRecipeFilter.java +++ b/src/main/java/codechicken/nei/filter/AnyOthersRecipeFilter.java @@ -24,6 +24,12 @@ public boolean matches(IRecipeHandler handler, int recipeIndex) { } } + for (PositionedStack pStack : handler.getCatalystStacks(recipeIndex)) { + if (match(pStack)) { + return true; + } + } + return false; } diff --git a/src/main/java/codechicken/nei/filter/RecipeFilter.java b/src/main/java/codechicken/nei/filter/RecipeFilter.java index 5e7af833b..62ec17155 100644 --- a/src/main/java/codechicken/nei/filter/RecipeFilter.java +++ b/src/main/java/codechicken/nei/filter/RecipeFilter.java @@ -48,9 +48,7 @@ && matchPositionedStack(handler.getIngredientStacks(recipeIndex), this.anyMatch) } if (this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT) { - final PositionedStack result = handler.getResultStack(recipeIndex); - - if (result != null && matchPositionedStack(result) == this.anyMatch) { + if (matchPositionedStack(handler.getResultStacks(recipeIndex), this.anyMatch)) { return this.anyMatch; } @@ -58,6 +56,10 @@ && matchPositionedStack(handler.getIngredientStacks(recipeIndex), this.anyMatch) return this.anyMatch; } + if (matchPositionedStack(handler.getCatalystStacks(recipeIndex), this.anyMatch)) { + return this.anyMatch; + } + } return !this.anyMatch; diff --git a/src/main/java/codechicken/nei/recipe/FuelRecipeHandler.java b/src/main/java/codechicken/nei/recipe/FuelRecipeHandler.java index 254ad9bf2..d48539ba9 100644 --- a/src/main/java/codechicken/nei/recipe/FuelRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/FuelRecipeHandler.java @@ -38,6 +38,11 @@ public PositionedStack getResult() { public PositionedStack getOtherStack() { return fuel.stack; } + + @Override + public PositionedStack getCatalyst() { + return fuel.stack; + } } private final ArrayList mfurnace = new ArrayList<>(); diff --git a/src/main/java/codechicken/nei/recipe/IRecipeHandler.java b/src/main/java/codechicken/nei/recipe/IRecipeHandler.java index 0afcacd31..65ed505f1 100644 --- a/src/main/java/codechicken/nei/recipe/IRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/IRecipeHandler.java @@ -1,5 +1,6 @@ package codechicken.nei.recipe; +import java.util.Collections; import java.util.List; import net.minecraft.client.gui.inventory.GuiContainer; @@ -82,15 +83,38 @@ default int getRecipeHeight(int recipe) { * @return A list of the other {@link PositionedStack}s in this recipe relative to the top left corner of your * recipe drawing space. For example fuel in furnaces. */ + default List getCatalystStacks(int recipe) { + return Collections.emptyList(); + } + + /** + * Legacy API, define either {@link #getCatalystStacks(int)} or {@link #getResultStacks(int)} + * + * @param recipe The recipe index to get items for. + * @return A list of the other {@link PositionedStack}s in this recipe relative to the top left corner of your + * recipe drawing space. For example fuel in furnaces. + */ List getOtherStacks(int recipe); /** + * Legacy API, use {@link #getResultStacks(int) getResultStacks.get(0)} if you want the primary result * * @param recipe The recipe index to get the result for. * @return The recipe result {@link PositionedStack} relative to the top left corner of your recipe drawing space. */ PositionedStack getResultStack(int recipe); + /** + * + * @param recipe The recipe index to get the result for. + * @return A list of the result {@link PositionedStack}s relative to the top left corner of your recipe drawing + * space. + */ + default List getResultStacks(int recipe) { + final PositionedStack result = getResultStack(recipe); + return result != null ? Collections.singletonList(result) : Collections.emptyList(); + } + /** * A tick function called for updating progress bars and cycling damage items. */ diff --git a/src/main/java/codechicken/nei/recipe/NEIRecipeWidget.java b/src/main/java/codechicken/nei/recipe/NEIRecipeWidget.java index ae2d216de..cc1c2e8c2 100644 --- a/src/main/java/codechicken/nei/recipe/NEIRecipeWidget.java +++ b/src/main/java/codechicken/nei/recipe/NEIRecipeWidget.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; import java.util.function.Function; import java.util.stream.Stream; @@ -36,6 +37,7 @@ import codechicken.nei.recipe.Recipe.RecipeId; import codechicken.nei.recipe.debug.DebugHandlerWidget; import codechicken.nei.util.NEIMouseUtils; +import it.unimi.dsi.fastutil.longs.LongArraySet; public class NEIRecipeWidget extends Widget { @@ -54,6 +56,9 @@ public class NEIRecipeWidget extends Widget { protected boolean showAsWidget = false; protected List recipeButtons = null; + // Handles re-draws for catalysts-outputs duplication to preserve full backward compatibility for otherStacks + private final Set drawnSlots = new LongArraySet(); + public NEIRecipeWidget(RecipeHandlerRef handlerRef) { this.handlerRef = handlerRef; this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.handlerRef.handler); @@ -193,7 +198,20 @@ public void draw(int mouseX, int mouseY) { GuiContainerManager.enableMatrixStackLogging(); - for (PositionedStack pStack : getInputs()) { + final List inputs = getInputs(); + final List catalysts = getCatalysts(); + final List outputs = getOutputs(); + + drawnSlots.clear(); + for (PositionedStack pStack : inputs) { + drawnSlots.add(slotKey(pStack)); + drawItem(pStack, mouseX, mouseY, yShift, false); + } + + for (PositionedStack pStack : catalysts) { + if (!drawnSlots.add(slotKey(pStack))) { + continue; + } if (!this.permutations.containsKey(pStack)) { updatePermutationsFor(pStack); @@ -202,7 +220,10 @@ public void draw(int mouseX, int mouseY) { drawItem(pStack, mouseX, mouseY, yShift, true); } - for (PositionedStack pStack : getCatalysts()) { + for (PositionedStack pStack : outputs) { + if (!drawnSlots.add(slotKey(pStack))) { + continue; + } if (!this.permutations.containsKey(pStack)) { updatePermutationsFor(pStack); @@ -211,10 +232,6 @@ public void draw(int mouseX, int mouseY) { drawItem(pStack, mouseX, mouseY, yShift, true); } - for (PositionedStack pStack : getOutputs()) { - drawItem(pStack, mouseX, mouseY, yShift, false); - } - GuiContainerManager.disableMatrixStackLogging(); this.handlerRef.handler.drawForeground(this.handlerRef.recipeIndex); @@ -246,6 +263,10 @@ public void draw(int mouseX, int mouseY) { DebugHandlerWidget.instance.drawGuiPlaceholder(this); } + private static long slotKey(PositionedStack pStack) { + return ((long) pStack.relx << 32) ^ (pStack.rely & 0xFFFFFFFFL); + } + protected void drawItem(PositionedStack pStack, int mouseX, int mouseY, int yShift, boolean input) { GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item); @@ -656,13 +677,19 @@ protected List getInputs() { } protected List getOutputs() { - final PositionedStack pStackResult = this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex); - return pStackResult != null ? Arrays.asList(pStackResult) + final List pStackResults = this.handlerRef.handler + .getResultStacks(this.handlerRef.recipeIndex); + return !pStackResults.isEmpty() ? pStackResults : this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex); } protected List getCatalysts() { - if (this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex) == null) { + List catalysts = this.handlerRef.handler.getCatalystStacks(this.handlerRef.recipeIndex); + if (!catalysts.isEmpty()) { + return catalysts; + } + + if (this.handlerRef.handler.getResultStacks(this.handlerRef.recipeIndex).isEmpty()) { return Collections.emptyList(); } return this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex); diff --git a/src/main/java/codechicken/nei/recipe/Recipe.java b/src/main/java/codechicken/nei/recipe/Recipe.java index 461547007..8607fb5d9 100644 --- a/src/main/java/codechicken/nei/recipe/Recipe.java +++ b/src/main/java/codechicken/nei/recipe/Recipe.java @@ -54,19 +54,26 @@ public static RecipeId of(Object result, String handlerName, Iterable ingredi public static RecipeId of(IRecipeHandler handler, int recipeIndex) { final List ingredients = handler.getIngredientStacks(recipeIndex); final String handlerName = GuiRecipeTab.getHandlerInfo(handler).getHandlerName(); - PositionedStack pStackResult = handler.getResultStack(recipeIndex); + // Use getResultStacks even though getResult could work because if a handler, like GT for example, doesn't + // return anything from getResult, but will define all the outputs in getResultStacks we can still pick up + // the results here. If a handler still doesn't define getResultStacks, then it will naturally fall back to + // getResult anyway + List pStackResults = handler.getResultStacks(recipeIndex); - if (pStackResult == null) { + if (pStackResults.isEmpty()) { for (PositionedStack otherStack : handler.getOtherStacks(recipeIndex)) { if (!FluidContainerRegistry.isContainer(otherStack.items[0]) || StackInfo.getFluid(otherStack.items[0]) != null) { - pStackResult = otherStack; + pStackResults.add(otherStack); break; } } } - return new RecipeId(extractItem(pStackResult), handlerName, extractIngredients(ingredients)); + return new RecipeId( + extractItem(!pStackResults.isEmpty() ? pStackResults.get(0) : null), + handlerName, + extractIngredients(ingredients)); } public static RecipeId of(JsonObject json) { @@ -389,8 +396,12 @@ public static Recipe of(IRecipeHandler handler, int recipeIndex) { ingredients.add(RecipeIngredient.of(positionedStack)); } - if (handler.getResultStack(recipeIndex) != null) { - results.add(RecipeIngredient.of(handler.getResultStack(recipeIndex))); + final List resultStacks = handler.getResultStacks(recipeIndex); + + if (!resultStacks.isEmpty()) { + for (PositionedStack positionedStack : resultStacks) { + results.add(RecipeIngredient.of(positionedStack)); + } } else { for (PositionedStack positionedStack : handler.getOtherStacks(recipeIndex)) { results.add(RecipeIngredient.of(positionedStack)); diff --git a/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java b/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java index 9e59e8d8d..b53f9c671 100644 --- a/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java +++ b/src/main/java/codechicken/nei/recipe/TemplateRecipeHandler.java @@ -4,6 +4,7 @@ import java.awt.Rectangle; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -132,7 +133,48 @@ public abstract class CachedRecipe { public abstract PositionedStack getResult(); /** - * The ingredients required to produce the result Use this if you have more than one ingredient + * The multiple items produced by this recipe, with position + * + * @return A list of positioned ingredient items. + */ + public List getResults() { + ArrayList stacks = new ArrayList<>(); + try { + PositionedStack stack = getResult(); + if (stack != null) stacks.add(stack); + } catch (ArithmeticException e) { + NEIClientConfig.logger.error("Error in getOtherStacks: " + e); + } + return stacks; + } + + /** + * Return extra items that are not directly involved in the ingredient->result relationship. Eg fuels. + * + * @return A list of positioned ingredient items. + */ + public List getCatalysts() { + ArrayList stacks = new ArrayList<>(); + try { + PositionedStack stack = getCatalyst(); + if (stack != null) stacks.add(stack); + } catch (ArithmeticException e) { + NEIClientConfig.logger.error("Error in getCatalysts: " + e); + } + return stacks; + } + + /** + * Simple utility + * + * @return The another positioned stack + */ + public PositionedStack getCatalyst() { + return null; + } + + /** + * The ingredients required to produce the result. Use this if you have more than one ingredient * * @return A list of positioned ingredient items. */ @@ -151,6 +193,8 @@ public PositionedStack getIngredient() { } /** + * Legacy API + * * Return extra items that are not directly involved in the ingredient->result relationship. Eg fuels. Use this * if you have more than one other stack * @@ -625,13 +669,23 @@ public List getIngredientStacks(int recipe) { } public PositionedStack getResultStack(int recipe) { + List results = getResultStacks(recipe); + if (results == null || results.isEmpty()) return null; + return results.get(0); + } + + public List getResultStacks(int recipe) { try { - return arecipes.get(recipe).getResult(); + return arecipes.get(recipe).getResults(); } catch (ArrayIndexOutOfBoundsException ignored) { - return null; + return Collections.emptyList(); } } + public List getCatalystStacks(int recipe) { + return arecipes.get(recipe).getCatalysts(); + } + public List getOtherStacks(int recipe) { return arecipes.get(recipe).getOtherStacks(); }