mirror of
https://github.com/GTNewHorizons/NotEnoughItems.git
synced 2026-11-08 12:34:46 +00:00
Add getResultStacks/getAuxiliaryStacks to IRecipeHandler (#1105)
This commit is contained in:
1 parent
aea4c2866a
commit
ae0d6c6108
17 files changed
+277
-216
No files matched your search
@@ -9,7 +9,6 @@ import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -33,7 +32,6 @@ import codechicken.nei.api.API;
|
||||
import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipeTab;
|
||||
import codechicken.nei.recipe.ICraftingHandler;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.RecipeHandlerRef;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
@@ -94,7 +92,7 @@ public class FavoriteRecipes {
|
||||
for (int i = 0; i < handler.numRecipes(); i++) {
|
||||
RecipeHandlerRef ref = null;
|
||||
|
||||
for (PositionedStack result : getOutputs(handler, i)) {
|
||||
for (PositionedStack result : handler.getResultStacks(i)) {
|
||||
ItemStack stack = StackInfo.getItemStackWithMinimumDamage(result.items);
|
||||
final FastKey key = new FastKey(stack);
|
||||
|
||||
@@ -129,11 +127,6 @@ public class FavoriteRecipes {
|
||||
updateSubsetVisibility();
|
||||
}
|
||||
|
||||
protected List<PositionedStack> getOutputs(IRecipeHandler handler, int recipeIndex) {
|
||||
final PositionedStack pStackResult = handler.getResultStack(recipeIndex);
|
||||
return pStackResult != null ? Collections.singletonList(pStackResult) : handler.getOtherStacks(recipeIndex);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static final String FAVORITE_ITEM = "FavoriteItem";
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.util.stream.IntStream;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fluids.FluidContainerRegistry;
|
||||
|
||||
import codechicken.lib.vec.Rectangle4i;
|
||||
import codechicken.nei.ItemsGrid.ItemsGridSlot;
|
||||
@@ -24,6 +23,7 @@ import codechicken.nei.recipe.GuiCraftingRecipe;
|
||||
import codechicken.nei.recipe.GuiRecipe;
|
||||
import codechicken.nei.recipe.ICraftingHandler;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.recipe.Recipe;
|
||||
import codechicken.nei.recipe.Recipe.RecipeId;
|
||||
import codechicken.nei.recipe.RecipeInfo;
|
||||
import codechicken.nei.recipe.StackInfo;
|
||||
@@ -378,48 +378,19 @@ public class ItemCraftablesPanel
|
||||
}
|
||||
|
||||
private static ItemStack getCheapResultItem(IRecipeHandler handler, int recipeIndex) {
|
||||
final PositionedStack resultStack = handler.getResultStack(recipeIndex);
|
||||
|
||||
if (resultStack != null) {
|
||||
return resultStack.item;
|
||||
}
|
||||
|
||||
final List<PositionedStack> otherStacks = handler.getOtherStacks(recipeIndex);
|
||||
|
||||
for (PositionedStack otherStack : otherStacks) {
|
||||
if (!FluidContainerRegistry.isContainer(otherStack.items[0])
|
||||
|| StackInfo.getFluid(otherStack.items[0]) != null) {
|
||||
return otherStack.item;
|
||||
}
|
||||
}
|
||||
|
||||
return otherStacks.isEmpty() ? null : otherStacks.get(0).item;
|
||||
final PositionedStack result = Recipe
|
||||
.getMainResult(handler.getResultStacks(recipeIndex), pStack -> pStack.items[0]);
|
||||
return result != null ? result.item : null;
|
||||
}
|
||||
|
||||
private static ItemStack getCheapResult(IRecipeHandler handler, int recipeIndex) {
|
||||
final PositionedStack resultStack = handler.getResultStack(recipeIndex);
|
||||
final List<PositionedStack> results = resultStack != null ? Collections.singletonList(resultStack)
|
||||
: handler.getOtherStacks(recipeIndex);
|
||||
final List<PositionedStack> results = handler.getResultStacks(recipeIndex);
|
||||
|
||||
if (results.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ItemStack stack = null;
|
||||
|
||||
if (results.size() > 1) {
|
||||
for (PositionedStack result : results) {
|
||||
if (!FluidContainerRegistry.isContainer(result.item) || StackInfo.getFluid(result.item) != null) {
|
||||
stack = result.item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stack == null) {
|
||||
stack = results.get(0).item;
|
||||
}
|
||||
|
||||
final ItemStack stack = Recipe.getMainResult(results, pStack -> pStack.items[0]).item;
|
||||
int stackSize = 0;
|
||||
|
||||
for (PositionedStack result : results) {
|
||||
|
||||
@@ -89,19 +89,19 @@ public class PresetsList {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PositionedStack result = handler.getResultStack(recipeIndex);
|
||||
final List<PositionedStack> outputs = handler.getResultStacks(recipeIndex);
|
||||
|
||||
if (result != null && matchPositionedStack(result)) {
|
||||
if (matchPositionedStack(outputs, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final List<PositionedStack> others = handler.getOtherStacks(recipeIndex);
|
||||
final List<PositionedStack> auxiliaries = handler.getAuxiliaryStacks(recipeIndex);
|
||||
|
||||
if (!others.isEmpty() && matchPositionedStack(others, true)) {
|
||||
if (matchPositionedStack(auxiliaries, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return result == null && others.isEmpty();
|
||||
return outputs.isEmpty() && auxiliaries.isEmpty();
|
||||
}
|
||||
|
||||
private boolean matchPositionedStack(List<PositionedStack> items, boolean dir) {
|
||||
|
||||
@@ -24,7 +24,13 @@ public abstract class RecipeSearchField extends TextField implements ItemFilterP
|
||||
field.setMaxStringLength(maxSearchLength);
|
||||
field.setCursorPositionZero();
|
||||
field.setSkipRecipeTokens(true);
|
||||
((SearchTextFormatter) field.getFormatter()).setContextToken("<>");
|
||||
field.setFormatter(new SearchTextFormatter(SearchField.searchParser) {
|
||||
|
||||
@Override
|
||||
protected String getContextToken() {
|
||||
return this.searchParser.getRecipeContextTokens();
|
||||
}
|
||||
});
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ public class SearchTextFormatter implements TextFormatter {
|
||||
this.contextToken = contextToken;
|
||||
}
|
||||
|
||||
protected String getContextToken() {
|
||||
return this.contextToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(String text) {
|
||||
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||
@@ -32,13 +36,14 @@ public class SearchTextFormatter implements TextFormatter {
|
||||
return SearchExpressionUtils.visitRecipeSearchExpression(text, visitor);
|
||||
} else {
|
||||
final StringBuilder formattedText = new StringBuilder();
|
||||
final String contextToken = getContextToken();
|
||||
|
||||
for (String[] orQuery : searchParser.splitByDelimiters(text, "|", false)) {
|
||||
final String filterText = orQuery[1];
|
||||
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.OR + orQuery[0]);
|
||||
|
||||
for (String[] contextPart : searchParser.splitByDelimiters(filterText, this.contextToken, false)) {
|
||||
for (String[] contextPart : searchParser.splitByDelimiters(filterText, contextToken, false)) {
|
||||
|
||||
if (!contextPart[0].isEmpty()) {
|
||||
formattedText.append(
|
||||
@@ -74,7 +79,11 @@ public class SearchTextFormatter implements TextFormatter {
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"");
|
||||
}
|
||||
|
||||
if (!token.rawText.isEmpty()) {
|
||||
if (patternMode == 1 && !token.quotes && isRegexToken(token.rawText)) {
|
||||
formattedText.append(SearchExpressionUtils.HIGHLIGHTS.REGEX + token.rawText.substring(0, 2))
|
||||
.append(tokenColor + token.rawText.substring(2, token.rawText.length() - 1))
|
||||
.append(SearchExpressionUtils.HIGHLIGHTS.REGEX + "/");
|
||||
} else if (!token.rawText.isEmpty()) {
|
||||
formattedText.append(tokenColor + token.rawText);
|
||||
}
|
||||
|
||||
@@ -93,4 +102,8 @@ public class SearchTextFormatter implements TextFormatter {
|
||||
return formattedText.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isRegexToken(String search) {
|
||||
return search.length() >= 3 && search.regionMatches(true, 0, "r/", 0, 2) && search.endsWith("/");
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,7 @@ public class SearchTokenParser {
|
||||
if (patternMode != 3) {
|
||||
return this.filtersCache.computeIfAbsent(skipRecipeTokens + ":" + filterText, _t -> {
|
||||
final List<ItemFilter> searchTokens = new ArrayList<>();
|
||||
final String contextTokens = skipRecipeTokens ? getRecipeContextTokens() : "";
|
||||
|
||||
for (String[] subQuery : splitByDelimiters(filterText, "|", false)) {
|
||||
|
||||
@@ -203,9 +204,10 @@ public class SearchTokenParser {
|
||||
|
||||
final List<ItemFilter> tokens = new ArrayList<>();
|
||||
|
||||
for (String[] contextQuery : splitByDelimiters(subQuery[1], skipRecipeTokens ? "<>" : "", false)) {
|
||||
for (String[] contextQuery : splitByDelimiters(subQuery[1], contextTokens, false)) {
|
||||
|
||||
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")) {
|
||||
if (contextQuery[1].isEmpty() || contextQuery[0].startsWith(">")
|
||||
|| contextQuery[0].startsWith("^")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -277,7 +279,7 @@ public class SearchTokenParser {
|
||||
|
||||
final List<IRecipeFilter> contextList = new ArrayList<>();
|
||||
|
||||
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], "<>", false)) {
|
||||
for (String[] contextQuery : splitByDelimiters(orQueryPart[1], getRecipeContextTokens(), false)) {
|
||||
|
||||
if (contextQuery[1].isEmpty()) {
|
||||
continue;
|
||||
@@ -436,7 +438,9 @@ public class SearchTokenParser {
|
||||
|
||||
final List<String[]> tokens = new ArrayList<>();
|
||||
final int length = input.length();
|
||||
final boolean regexTokens = NEIClientConfig.getIntSetting("inventory.search.patternMode") == 1;
|
||||
boolean insideQuotes = false;
|
||||
boolean insideRegex = false;
|
||||
String token = "";
|
||||
int lastEnd = 0;
|
||||
int index = 0;
|
||||
@@ -444,6 +448,15 @@ public class SearchTokenParser {
|
||||
while (index < length) {
|
||||
final char ch = input.charAt(index);
|
||||
|
||||
if (insideRegex) {
|
||||
if (ch == '/' && input.charAt(index - 1) != '\\') {
|
||||
insideRegex = false;
|
||||
}
|
||||
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!insideQuotes && delimiters.indexOf(ch) >= 0
|
||||
&& (!space || index == 0 || input.charAt(index - 1) == ' ' || lastEnd == index)) {
|
||||
|
||||
@@ -464,6 +477,9 @@ public class SearchTokenParser {
|
||||
|
||||
if (ch == '"' && (index == 0 || input.charAt(index - 1) != '\\')) {
|
||||
insideQuotes = !insideQuotes;
|
||||
} else if (regexTokens && !insideQuotes && isRegexStart(input, index)) {
|
||||
insideRegex = true;
|
||||
index++;
|
||||
}
|
||||
|
||||
index++;
|
||||
@@ -474,6 +490,12 @@ public class SearchTokenParser {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private static boolean isRegexStart(String input, int index) {
|
||||
return Character.toLowerCase(input.charAt(index)) == 'r' && index + 1 < input.length()
|
||||
&& input.charAt(index + 1) == '/'
|
||||
&& (index == 0 || !Character.isLetterOrDigit(input.charAt(index - 1)));
|
||||
}
|
||||
|
||||
public String getPrefixes() {
|
||||
if (this.prefixes == null) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
@@ -490,6 +512,11 @@ public class SearchTokenParser {
|
||||
return this.prefixes;
|
||||
}
|
||||
|
||||
public String getRecipeContextTokens() {
|
||||
// '^' is the start anchor and the negated class in regex mode
|
||||
return NEIClientConfig.getIntSetting("inventory.search.patternMode") == 2 ? "<>" : "<>^";
|
||||
}
|
||||
|
||||
public char getRedefinedPrefix(char prefix) {
|
||||
return this.prefixRedefinitions.getOrDefault(prefix, prefix);
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package codechicken.nei.filter;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
public class AllOthersRecipeFilter implements IRecipeFilter {
|
||||
|
||||
private final ItemFilter filter;
|
||||
|
||||
public AllOthersRecipeFilter(ItemFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IRecipeHandler handler, int recipeIndex) {
|
||||
|
||||
for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) {
|
||||
if (!match(pStack)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean match(PositionedStack pStack) {
|
||||
if (pStack == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ItemStack stack : pStack.items) {
|
||||
if (filter.matches(stack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package codechicken.nei.filter;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import codechicken.nei.PositionedStack;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
|
||||
public class AnyOthersRecipeFilter implements IRecipeFilter {
|
||||
|
||||
private final ItemFilter filter;
|
||||
|
||||
public AnyOthersRecipeFilter(ItemFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(IRecipeHandler handler, int recipeIndex) {
|
||||
|
||||
for (PositionedStack pStack : handler.getOtherStacks(recipeIndex)) {
|
||||
if (match(pStack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean match(PositionedStack pStack) {
|
||||
if (pStack == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ItemStack stack : pStack.items) {
|
||||
if (filter.matches(stack)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,8 @@ public class RecipeFilter implements IRecipeFilter {
|
||||
|
||||
ANY,
|
||||
INPUT,
|
||||
OUTPUT;
|
||||
OUTPUT,
|
||||
AUXILIARY;
|
||||
|
||||
public static FilterContext fromChar(char c) {
|
||||
switch (c) {
|
||||
@@ -24,6 +25,8 @@ public class RecipeFilter implements IRecipeFilter {
|
||||
return INPUT;
|
||||
case '>':
|
||||
return OUTPUT;
|
||||
case '^':
|
||||
return AUXILIARY;
|
||||
default:
|
||||
return ANY;
|
||||
}
|
||||
@@ -48,17 +51,14 @@ public class RecipeFilter implements IRecipeFilter {
|
||||
return this.anyMatch;
|
||||
}
|
||||
|
||||
if (this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT) {
|
||||
final PositionedStack result = handler.getResultStack(recipeIndex);
|
||||
|
||||
if (result != null && matchPositionedStack(result) == this.anyMatch) {
|
||||
return this.anyMatch;
|
||||
}
|
||||
|
||||
if (matchPositionedStack(handler.getOtherStacks(recipeIndex), this.anyMatch)) {
|
||||
return this.anyMatch;
|
||||
}
|
||||
if ((this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT)
|
||||
&& matchPositionedStack(handler.getResultStacks(recipeIndex), this.anyMatch)) {
|
||||
return this.anyMatch;
|
||||
}
|
||||
|
||||
if ((this.context == FilterContext.ANY || this.context == FilterContext.AUXILIARY)
|
||||
&& matchPositionedStack(handler.getAuxiliaryStacks(recipeIndex), this.anyMatch)) {
|
||||
return this.anyMatch;
|
||||
}
|
||||
|
||||
return !this.anyMatch;
|
||||
|
||||
@@ -200,6 +200,22 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
"recipe.search.tooltip.output",
|
||||
SearchExpressionUtils.HIGHLIGHTS.RECIPE + ">"));
|
||||
|
||||
final int patternMode = NEIClientConfig.getIntSetting("inventory.search.patternMode");
|
||||
|
||||
if (patternMode != 2) {
|
||||
tooltip.add(
|
||||
NEIClientUtils.translate(
|
||||
"recipe.search.tooltip.auxiliary",
|
||||
SearchExpressionUtils.HIGHLIGHTS.RECIPE + "^"));
|
||||
}
|
||||
|
||||
if (patternMode == 3) {
|
||||
tooltip.add(
|
||||
NEIClientUtils.translate(
|
||||
"recipe.search.tooltip.all",
|
||||
SearchExpressionUtils.HIGHLIGHTS.RECIPE + "<&"));
|
||||
}
|
||||
|
||||
for (ISearchParserProvider provider : SearchField.searchParser.getProviders()) {
|
||||
if (provider.getSearchMode() == SearchMode.PREFIX) {
|
||||
addPrefixTooltip(tooltip, provider);
|
||||
@@ -222,6 +238,14 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
|
||||
"recipe.search.tooltip.exact",
|
||||
SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\"",
|
||||
SearchExpressionUtils.HIGHLIGHTS.QUOTED + "\""));
|
||||
|
||||
if (patternMode == 1 || patternMode == 3) {
|
||||
tooltip.add(
|
||||
NEIClientUtils.translate(
|
||||
"recipe.search.tooltip.regex",
|
||||
SearchExpressionUtils.HIGHLIGHTS.REGEX + "r/",
|
||||
SearchExpressionUtils.HIGHLIGHTS.REGEX + "/"));
|
||||
}
|
||||
} else {
|
||||
tooltip.add(NEIClientUtils.translate("recipe.search.tooltip.shift"));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package codechicken.nei.recipe;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
@@ -77,19 +78,48 @@ public interface IRecipeHandler {
|
||||
List<PositionedStack> getIngredientStacks(int recipe);
|
||||
|
||||
/**
|
||||
* Callers should read {@link #getResultStacks(int)} and {@link #getAuxiliaryStacks(int)} instead.
|
||||
*
|
||||
* @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.
|
||||
* @return The fuel or catalysts when {@link #getResultStack(int)} is present, otherwise all the results of the
|
||||
* recipe. Relative to the top left corner of your recipe drawing space.
|
||||
*/
|
||||
List<PositionedStack> getOtherStacks(int recipe);
|
||||
default List<PositionedStack> getOtherStacks(int recipe) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callers should read {@link #getResultStacks(int)} instead.
|
||||
*
|
||||
* @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.
|
||||
* @return The single result, or {@code null} when the recipe has several results and returns them from
|
||||
* {@link #getOtherStacks(int)}. Relative to the top left corner of your recipe drawing space.
|
||||
*/
|
||||
PositionedStack getResultStack(int recipe);
|
||||
default PositionedStack getResultStack(int recipe) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Built from {@link #getResultStack(int)} and {@link #getOtherStacks(int)}. Don't override it, return the results
|
||||
* from those methods so mods that still read them see the same results.
|
||||
*
|
||||
* @return The results: the result stack when present, otherwise the other stacks.
|
||||
*/
|
||||
default List<PositionedStack> getResultStacks(int recipe) {
|
||||
final PositionedStack result = getResultStack(recipe);
|
||||
return result != null ? Collections.singletonList(result) : getOtherStacks(recipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override it only when the recipe has several results and also fuel or catalysts: the results then go to
|
||||
* {@link #getOtherStacks(int)} and the catalysts can't be returned from the old methods.
|
||||
*
|
||||
* @return The stacks that are neither ingredients nor results (fuel, catalysts): the other stacks when a result
|
||||
* stack is present.
|
||||
*/
|
||||
default List<PositionedStack> getAuxiliaryStacks(int recipe) {
|
||||
return getResultStack(recipe) != null ? getOtherStacks(recipe) : Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recipe The recipe index to get the group id for.
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.util.Comparator;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
@@ -65,19 +66,11 @@ public class Recipe {
|
||||
|
||||
public static RecipeId of(IRecipeHandler handler, int recipeIndex, List<PositionedStack> ingredients) {
|
||||
final String handlerName = GuiRecipeTab.getHandlerInfo(handler).getHandlerName();
|
||||
PositionedStack pStackResult = handler.getResultStack(recipeIndex);
|
||||
final PositionedStack result = getMainResult(
|
||||
handler.getResultStacks(recipeIndex),
|
||||
pStack -> pStack.items[0]);
|
||||
|
||||
if (pStackResult == null) {
|
||||
for (PositionedStack otherStack : handler.getOtherStacks(recipeIndex)) {
|
||||
if (!FluidContainerRegistry.isContainer(otherStack.items[0])
|
||||
|| StackInfo.getFluid(otherStack.items[0]) != null) {
|
||||
pStackResult = otherStack;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new RecipeId(extractItem(pStackResult), handlerName, extractIngredients(ingredients));
|
||||
return new RecipeId(extractItem(result), handlerName, extractIngredients(ingredients));
|
||||
}
|
||||
|
||||
public static RecipeId of(JsonObject json) {
|
||||
@@ -404,12 +397,8 @@ public class Recipe {
|
||||
ingredients.add(RecipeIngredient.of(positionedStack));
|
||||
}
|
||||
|
||||
if (handler.getResultStack(recipeIndex) != null) {
|
||||
results.add(RecipeIngredient.of(handler.getResultStack(recipeIndex)));
|
||||
} else {
|
||||
for (PositionedStack positionedStack : handler.getOtherStacks(recipeIndex)) {
|
||||
results.add(RecipeIngredient.of(positionedStack));
|
||||
}
|
||||
for (PositionedStack positionedStack : handler.getResultStacks(recipeIndex)) {
|
||||
results.add(RecipeIngredient.of(positionedStack));
|
||||
}
|
||||
|
||||
return new Recipe(handlerName, ingredients, results);
|
||||
@@ -494,18 +483,8 @@ public class Recipe {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (stack == null && this.results.length > 1) {
|
||||
for (RecipeIngredient ingr : this.results) {
|
||||
if (!FluidContainerRegistry.isContainer(ingr.getItemStack())
|
||||
|| StackInfo.getFluid(ingr.getItemStack()) != null) {
|
||||
stack = ingr.getItemStack();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stack == null) {
|
||||
stack = this.results[0].getItemStack();
|
||||
stack = getMainResult(getResults(), RecipeIngredient::getItemStack).getItemStack();
|
||||
}
|
||||
|
||||
int stackSize = 0;
|
||||
@@ -519,6 +498,19 @@ public class Recipe {
|
||||
return StackInfo.withAmount(stack, stackSize);
|
||||
}
|
||||
|
||||
public static <T> T getMainResult(List<T> results, Function<T, ItemStack> stackOf) {
|
||||
|
||||
for (T result : results) {
|
||||
final ItemStack stack = stackOf.apply(result);
|
||||
|
||||
if (!FluidContainerRegistry.isContainer(stack) || StackInfo.getFluid(stack) != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return results.isEmpty() ? null : results.get(0);
|
||||
}
|
||||
|
||||
public RecipeId getRecipeId() {
|
||||
|
||||
if (this.recipeId == null) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.awt.Point;
|
||||
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;
|
||||
@@ -176,6 +177,16 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read only when {@link #getResult()} is {@code null} and {@link #getOtherStacks()} returns the results, see
|
||||
* {@link IRecipeHandler#getAuxiliaryStacks(int)}.
|
||||
*
|
||||
* @return The fuel or catalysts of a recipe with several results.
|
||||
*/
|
||||
public List<PositionedStack> getAuxiliaries() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public List<PositionedStack> getCycledIngredients(int cycle, List<PositionedStack> ingredients) {
|
||||
return ingredients;
|
||||
@@ -641,6 +652,16 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
|
||||
return arecipes.get(recipe).getOtherStacks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PositionedStack> getAuxiliaryStacks(int recipe) {
|
||||
// goes through the handler methods, subclasses may override them instead of the cached recipe ones
|
||||
if (getResultStack(recipe) != null) {
|
||||
return getOtherStacks(recipe);
|
||||
}
|
||||
|
||||
return arecipes.get(recipe).getAuxiliaries();
|
||||
}
|
||||
|
||||
public void onUpdate() {
|
||||
cycleticks++;
|
||||
}
|
||||
|
||||
@@ -225,15 +225,10 @@ public class RecipeGroup {
|
||||
|
||||
protected MemberStacks loadStacks(int member) {
|
||||
final int recipeIndex = getRecipeIndex(member);
|
||||
final List<PositionedStack> inputs = new ArrayList<>(this.handler.getIngredientStacks(recipeIndex));
|
||||
final PositionedStack pStackResult = this.handler.getResultStack(recipeIndex);
|
||||
final List<PositionedStack> others = new ArrayList<>(this.handler.getOtherStacks(recipeIndex));
|
||||
|
||||
if (pStackResult != null) {
|
||||
return new MemberStacks(inputs, Collections.singletonList(pStackResult), others);
|
||||
}
|
||||
|
||||
return new MemberStacks(inputs, others, Collections.emptyList());
|
||||
return new MemberStacks(
|
||||
new ArrayList<>(this.handler.getIngredientStacks(recipeIndex)),
|
||||
new ArrayList<>(this.handler.getResultStacks(recipeIndex)),
|
||||
new ArrayList<>(this.handler.getAuxiliaryStacks(recipeIndex)));
|
||||
}
|
||||
|
||||
// re-reads the member stacks from the handler, keeps the cached instances when nothing changed
|
||||
|
||||
@@ -2,15 +2,12 @@ package codechicken.nei.search;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import codechicken.nei.ItemList.EverythingItemFilter;
|
||||
import codechicken.nei.SearchTokenParser;
|
||||
import codechicken.nei.api.IRecipeFilter;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.filter.AllMultiRecipeFilter;
|
||||
import codechicken.nei.filter.AllOthersRecipeFilter;
|
||||
import codechicken.nei.filter.AnyOthersRecipeFilter;
|
||||
import codechicken.nei.filter.RecipeFilter;
|
||||
import codechicken.nei.filter.RecipeFilter.FilterContext;
|
||||
|
||||
@@ -51,11 +48,7 @@ public class RecipeFilterVisitor extends AbstractSearchExpressionVisitor<IRecipe
|
||||
case 1:
|
||||
return new RecipeFilter(FilterContext.OUTPUT, !ctx.allRecipe, itemFilter);
|
||||
case 2:
|
||||
return getAllOrAnyFilter(
|
||||
ctx.allRecipe,
|
||||
itemFilter,
|
||||
AnyOthersRecipeFilter::new,
|
||||
AllOthersRecipeFilter::new);
|
||||
return new RecipeFilter(FilterContext.AUXILIARY, !ctx.allRecipe, itemFilter);
|
||||
// Doesn't support all by default
|
||||
case 3:
|
||||
return new RecipeFilter(FilterContext.ANY, !ctx.allRecipe, itemFilter);
|
||||
@@ -64,15 +57,6 @@ public class RecipeFilterVisitor extends AbstractSearchExpressionVisitor<IRecipe
|
||||
}
|
||||
}
|
||||
|
||||
private IRecipeFilter getAllOrAnyFilter(boolean allRecipe, ItemFilter itemFilter,
|
||||
Function<ItemFilter, IRecipeFilter> createAnyFilter, Function<ItemFilter, IRecipeFilter> createAllFilter) {
|
||||
if (allRecipe) {
|
||||
return createAllFilter.apply(itemFilter);
|
||||
} else {
|
||||
return createAnyFilter.apply(itemFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private IRecipeFilter constructFilter(List<IRecipeFilter> filters) {
|
||||
if (!filters.isEmpty()) {
|
||||
// Propagate the result up
|
||||
|
||||
@@ -652,6 +652,8 @@ nei.recipe.search.tooltip.name=Filter Syntax
|
||||
nei.recipe.search.tooltip.shift=§7Hold §6Shift§7 for more info
|
||||
nei.recipe.search.tooltip.input=%s§ftext§8 - §7search in inputs
|
||||
nei.recipe.search.tooltip.output=%s§ftext§8 - §7search in outputs
|
||||
nei.recipe.search.tooltip.auxiliary=%s§ftext§8 - §7search in other stacks (fuel, catalysts)
|
||||
nei.recipe.search.tooltip.all=%s§ftext§8 - §7all inputs must match (same with > and ^)
|
||||
nei.recipe.search.tooltip.prefix=%stext§8 - §7%s
|
||||
|
||||
nei.recipe.search.tooltip.prefix.modName=search by mod
|
||||
@@ -665,6 +667,7 @@ nei.recipe.search.tooltip.exclude=%s§ftext§8 - §7exclude items with text
|
||||
nei.recipe.search.tooltip.exclude_recipe=%s§ftext§8 - §7exclude recipes with text
|
||||
nei.recipe.search.tooltip.or=§fa%s§fb§8 - §7match a or b
|
||||
nei.recipe.search.tooltip.exact=%s§ftext%s§8 - §7match exact phrase
|
||||
nei.recipe.search.tooltip.regex=%s§ftext%s§8 - §7match regular expression
|
||||
|
||||
nei.recipe.shaped=Shaped Crafting
|
||||
nei.recipe.shapeless=Shapeless Crafting
|
||||
|
||||
@@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Named.named;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
@@ -32,6 +33,8 @@ import codechicken.nei.ItemList.AnyMultiItemFilter;
|
||||
import codechicken.nei.ItemList.PatternItemFilter;
|
||||
import codechicken.nei.SearchField.SearchParserProvider;
|
||||
import codechicken.nei.api.ItemFilter;
|
||||
import codechicken.nei.api.ItemInfo;
|
||||
import codechicken.nei.recipe.IRecipeHandler;
|
||||
import codechicken.nei.search.IdentifierFilter;
|
||||
|
||||
@DisplayName("Search expression")
|
||||
@@ -65,6 +68,7 @@ public class SearchTokenParserTest {
|
||||
private static final ModeValue[] extendedPlusPatternMode = { patternModes[3] };
|
||||
private static final ModeValue[] extendedPatternModes = { patternModes[1], patternModes[3] };
|
||||
private static final ModeValue[] nonGrammarPatternModes = { patternModes[0], patternModes[1], patternModes[2] };
|
||||
private static final ModeValue[] nonRegexPatternModes = { patternModes[0], patternModes[1], patternModes[3] };
|
||||
|
||||
private static final SearchParserProvider defaultParserProvider = parserProvider(
|
||||
'\0',
|
||||
@@ -140,6 +144,14 @@ public class SearchTokenParserTest {
|
||||
spaceModes,
|
||||
extendedPatternModes);
|
||||
|
||||
private static final List<Arguments> extendedOnlyAllSpaceModesTestProvider = combinationsProvider(
|
||||
spaceModes,
|
||||
new ModeValue[] { patternModes[1] });
|
||||
|
||||
private static final List<Arguments> nonRegexAllSpaceModesTestProvider = combinationsProvider(
|
||||
spaceModes,
|
||||
nonRegexPatternModes);
|
||||
|
||||
private static final List<Arguments> spaceIsAndExtendedPlusModeTestProvider = combinationsProvider(
|
||||
spaceIsAndMode,
|
||||
extendedPlusPatternMode);
|
||||
@@ -176,6 +188,7 @@ public class SearchTokenParserTest {
|
||||
config.when(() -> NEIClientConfig.getBooleanSetting(eq("inventory.search.logSearchExceptions")))
|
||||
.thenReturn(false);
|
||||
|
||||
mockStatic(ItemInfo.class);
|
||||
}
|
||||
|
||||
// All modes tests
|
||||
@@ -321,6 +334,62 @@ public class SearchTokenParserTest {
|
||||
assertTrue(matchedItems.contains(itemABC));
|
||||
}
|
||||
|
||||
@DisplayName("Regex with delimiters")
|
||||
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||
@FieldSource("extendedOnlyAllSpaceModesTestProvider")
|
||||
public void testRegexWithDelimiters(int spaceMode, int patternMode) {
|
||||
SearchTokenParser searchParser = new SearchTokenParser();
|
||||
searchParser.addProvider(defaultParserProvider);
|
||||
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||
spaceMode,
|
||||
patternMode,
|
||||
searchParser,
|
||||
"r/^" + itemA.getDisplayName() + "|^" + itemC.getDisplayName() + "/",
|
||||
true);
|
||||
|
||||
assertEquals(4, matchedItems.size());
|
||||
assertTrue(matchedItems.contains(itemA));
|
||||
assertTrue(matchedItems.contains(itemC));
|
||||
assertTrue(matchedItems.contains(itemAB));
|
||||
assertTrue(matchedItems.contains(itemABC));
|
||||
}
|
||||
|
||||
@DisplayName("Regex followed by delimiter")
|
||||
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||
@FieldSource("extendedAllSpaceModesTestProvider")
|
||||
public void testRegexFollowedByDelimiter(int spaceMode, int patternMode) {
|
||||
SearchTokenParser searchParser = new SearchTokenParser();
|
||||
searchParser.addProvider(defaultParserProvider);
|
||||
Set<ItemStack> matchedItems = testFilterAgainstAllItems(
|
||||
spaceMode,
|
||||
patternMode,
|
||||
searchParser,
|
||||
"r/^" + itemA.getDisplayName() + "$/|" + itemC.getDisplayName());
|
||||
|
||||
assertEquals(3, matchedItems.size());
|
||||
assertTrue(matchedItems.contains(itemA));
|
||||
assertTrue(matchedItems.contains(itemC));
|
||||
assertTrue(matchedItems.contains(itemABC));
|
||||
}
|
||||
|
||||
@DisplayName("Recipe context")
|
||||
@ParameterizedTest(name = "{argumentsWithNames}")
|
||||
@FieldSource("nonRegexAllSpaceModesTestProvider")
|
||||
public void testRecipeContext(int spaceMode, int patternMode) {
|
||||
SearchTokenParser searchParser = new SearchTokenParser();
|
||||
searchParser.addProvider(defaultParserProvider);
|
||||
IRecipeHandler handler = mock(IRecipeHandler.class);
|
||||
when(handler.getIngredientStacks(0)).thenReturn(positionedStacks(itemA));
|
||||
when(handler.getResultStacks(0)).thenReturn(positionedStacks(itemB));
|
||||
when(handler.getAuxiliaryStacks(0)).thenReturn(positionedStacks(itemC));
|
||||
|
||||
assertTrue(testRecipeFilter(spaceMode, patternMode, searchParser, "<" + itemA.getDisplayName(), handler));
|
||||
assertTrue(testRecipeFilter(spaceMode, patternMode, searchParser, ">" + itemB.getDisplayName(), handler));
|
||||
assertTrue(testRecipeFilter(spaceMode, patternMode, searchParser, "^" + itemC.getDisplayName(), handler));
|
||||
assertFalse(testRecipeFilter(spaceMode, patternMode, searchParser, "^" + itemB.getDisplayName(), handler));
|
||||
assertFalse(testRecipeFilter(spaceMode, patternMode, searchParser, ">" + itemC.getDisplayName(), handler));
|
||||
}
|
||||
|
||||
// Extended+ specific tests
|
||||
|
||||
@DisplayName("Sequence")
|
||||
@@ -403,10 +472,14 @@ public class SearchTokenParserTest {
|
||||
|
||||
private static Set<ItemStack> testFilterAgainstAllItems(int spaceMode, int patternMode,
|
||||
SearchTokenParser searchParser, String search) {
|
||||
config.when(() -> NEIClientConfig.getIntSetting(eq("inventory.search.patternMode"))).thenReturn(patternMode);
|
||||
config.when(() -> NEIClientConfig.getIntSetting(eq("inventory.search.spaceMode"))).thenReturn(spaceMode);
|
||||
return testFilterAgainstAllItems(spaceMode, patternMode, searchParser, search, false);
|
||||
}
|
||||
|
||||
ItemFilter filter = searchParser.getFilter(search);
|
||||
private static Set<ItemStack> testFilterAgainstAllItems(int spaceMode, int patternMode,
|
||||
SearchTokenParser searchParser, String search, boolean skipRecipeTokens) {
|
||||
setModes(spaceMode, patternMode);
|
||||
|
||||
ItemFilter filter = searchParser.getFilter(search, skipRecipeTokens);
|
||||
if (filter instanceof SearchTokenParser.IsRegisteredItemFilter) {
|
||||
filter = ((SearchTokenParser.IsRegisteredItemFilter) filter).filter;
|
||||
}
|
||||
@@ -415,6 +488,23 @@ public class SearchTokenParserTest {
|
||||
return matchedItems;
|
||||
}
|
||||
|
||||
private static boolean testRecipeFilter(int spaceMode, int patternMode, SearchTokenParser searchParser,
|
||||
String search, IRecipeHandler handler) {
|
||||
setModes(spaceMode, patternMode);
|
||||
return searchParser.getRecipeFilter(search).matches(handler, 0);
|
||||
}
|
||||
|
||||
private static void setModes(int spaceMode, int patternMode) {
|
||||
config.when(() -> NEIClientConfig.getIntSetting(eq("inventory.search.patternMode"))).thenReturn(patternMode);
|
||||
config.when(() -> NEIClientConfig.getIntSetting(eq("inventory.search.spaceMode"))).thenReturn(spaceMode);
|
||||
}
|
||||
|
||||
private static List<PositionedStack> positionedStacks(ItemStack stack) {
|
||||
PositionedStack pStack = mock(PositionedStack.class);
|
||||
pStack.items = new ItemStack[] { stack };
|
||||
return Collections.singletonList(pStack);
|
||||
}
|
||||
|
||||
private static SearchParserProvider parserProvider(char prefix, String name,
|
||||
SearchTokenParser.SearchMode searchMode, Function<Pattern, ItemFilter> createFilter) {
|
||||
return new SearchParserProvider(prefix, name, EnumChatFormatting.RESET, createFilter) {
|
||||
|
||||
Reference in new issue
Block a user