diff --git a/src/main/java/codechicken/nei/FavoriteRecipes.java b/src/main/java/codechicken/nei/FavoriteRecipes.java index 387a2b50..10b429cc 100644 --- a/src/main/java/codechicken/nei/FavoriteRecipes.java +++ b/src/main/java/codechicken/nei/FavoriteRecipes.java @@ -244,7 +244,7 @@ public class FavoriteRecipes { try (FileOutputStream output = new FileOutputStream(favoriteFile)) { IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8); } catch (IOException e) { - NEIClientConfig.logger.error("Filed to save favorites list to file {}", favoriteFile, e); + NEIClientConfig.logger.error("Failed to save favorites list to file {}", favoriteFile, e); } } diff --git a/src/main/java/codechicken/nei/NEIClientConfig.java b/src/main/java/codechicken/nei/NEIClientConfig.java index a44ab846..69366759 100644 --- a/src/main/java/codechicken/nei/NEIClientConfig.java +++ b/src/main/java/codechicken/nei/NEIClientConfig.java @@ -923,6 +923,7 @@ public class NEIClientConfig { }); RecipeCatalysts.loadCatalystInfo(); + SubsetWidget.loadCustomSubsets(); SubsetWidget.loadHidden(); CollapsibleItems.load(); ItemSorter.loadConfig(); diff --git a/src/main/java/codechicken/nei/NEIController.java b/src/main/java/codechicken/nei/NEIController.java index dd44a08c..2b52ba77 100644 --- a/src/main/java/codechicken/nei/NEIController.java +++ b/src/main/java/codechicken/nei/NEIController.java @@ -20,6 +20,7 @@ import codechicken.nei.api.IInfiniteItemHandler; import codechicken.nei.api.INEIGuiHandler; import codechicken.nei.api.ItemInfo; import codechicken.nei.commands.CommandBookmarkAdd; +import codechicken.nei.commands.CommandRecipeId; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerInputHandler; import codechicken.nei.guihook.IContainerSlotClickHandler; @@ -66,6 +67,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu @SideOnly(Side.CLIENT) public static void registerClientCommands() { ClientCommandHandler.instance.registerCommand(new CommandBookmarkAdd()); + ClientCommandHandler.instance.registerCommand(new CommandRecipeId()); } public static void updateUnlimitedItems(InventoryPlayer inventory) { diff --git a/src/main/java/codechicken/nei/PresetsList.java b/src/main/java/codechicken/nei/PresetsList.java index ae504a73..34b019c0 100644 --- a/src/main/java/codechicken/nei/PresetsList.java +++ b/src/main/java/codechicken/nei/PresetsList.java @@ -236,7 +236,7 @@ public class PresetsList { try (FileOutputStream output = new FileOutputStream(presetsFile)) { IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8); } catch (IOException e) { - NEIClientConfig.logger.error("Filed to save presets list to file {}", presetsFile, e); + NEIClientConfig.logger.error("Failed to save presets list to file {}", presetsFile, e); } recipeFilter.cache = null; diff --git a/src/main/java/codechicken/nei/SubsetWidget.java b/src/main/java/codechicken/nei/SubsetWidget.java index 72886501..25c4d0e6 100644 --- a/src/main/java/codechicken/nei/SubsetWidget.java +++ b/src/main/java/codechicken/nei/SubsetWidget.java @@ -1,6 +1,7 @@ package codechicken.nei; import java.awt.Point; +import java.io.File; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; @@ -13,16 +14,20 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; +import java.util.stream.Stream; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.util.EnumChatFormatting; import org.lwjgl.opengl.GL11; +import com.google.gson.JsonParser; + import codechicken.core.gui.GuiScrollSlot; import codechicken.lib.gui.GuiDraw; import codechicken.lib.vec.Rectangle4i; @@ -35,6 +40,12 @@ import codechicken.nei.api.ItemFilter; import codechicken.nei.api.ItemFilter.ItemFilterProvider; import codechicken.nei.guihook.GuiContainerManager; import codechicken.nei.guihook.IContainerTooltipHandler; +import codechicken.nei.recipe.GuiCraftingRecipe; +import codechicken.nei.recipe.GuiRecipeTab; +import codechicken.nei.recipe.ICraftingHandler; +import codechicken.nei.recipe.IRecipeHandler; +import codechicken.nei.recipe.StackInfo; +import codechicken.nei.util.NBTJson; import codechicken.nei.util.NEIMouseUtils; public class SubsetWidget extends Button implements ItemFilterProvider, IContainerTooltipHandler { @@ -535,6 +546,68 @@ public class SubsetWidget extends Button implements ItemFilterProvider, IContain } } + public static void loadCustomSubsets() { + File dir = new File(NEIClientConfig.configDir, "subsets"); + + if (!dir.exists() || !dir.isDirectory()) { + return; + } + + Stream.of(dir.listFiles()).filter(file -> !file.isDirectory()).forEach(file -> { + ClientHandler.loadSettingsFile( + "subsets/" + file.getName(), + lines -> parseFile(file.getName(), lines.collect(Collectors.toList()))); + }); + } + + private static void parseFile(String resource, List itemStrings) { + final JsonParser parser = new JsonParser(); + final int dotIndex = resource.lastIndexOf('.'); + final String subsetNamespace = dotIndex > 0 ? resource.substring(0, dotIndex) : resource; + SubsetTag processedTag = new SubsetTag(subsetNamespace, new ItemStackSet()); + + for (String itemStr : itemStrings) { + try { + if (itemStr.startsWith("; ")) { + addTag(processedTag); + + final String handlerName = itemStr.substring(2); + final IRecipeHandler recipeHandler = Stream + .concat( + GuiCraftingRecipe.craftinghandlers.stream(), + GuiCraftingRecipe.serialCraftingHandlers.stream()) + .filter(handler -> handlerName.equals(getHandlerName(handler))).findFirst().orElse(null); + String recipeName = handlerName; + + if (recipeHandler != null) { + recipeName = recipeHandler.getRecipeName().trim(); + } + + processedTag = new SubsetTag( + subsetNamespace + "." + recipeName.replace(".", ""), + new ItemStackSet()); + } else { + final NBTBase nbt = NBTJson.toNbt(parser.parse(itemStr)); + + if (nbt instanceof NBTTagCompound tag) { + ((ItemStackSet) processedTag.filter).add(StackInfo.loadFromNBT(tag)); + } else { + throw new IllegalArgumentException( + "Expected NBTTagCompound but got " + nbt.getClass().getSimpleName()); + } + } + } catch (Exception e) { + NEIClientConfig.logger.error("Failed to load custom subset items from json string:\n{}", itemStr); + } + } + + addTag(processedTag); + } + + private static String getHandlerName(ICraftingHandler handler) { + return GuiRecipeTab.getHandlerInfo(handler).getHandlerName(); + } + public static void loadHidden() { final List itemList = new LinkedList<>(); diff --git a/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java b/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java index 213a488a..817a261a 100644 --- a/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java +++ b/src/main/java/codechicken/nei/bookmark/BookmarkStorage.java @@ -359,7 +359,7 @@ public class BookmarkStorage { IOUtils.writeLines(strings, "\n", output, StandardCharsets.UTF_8); NEIClientConfig.world.nbt.setTag("bookmark", navigation); } catch (IOException e) { - NEIClientConfig.logger.error("Filed to save bookmarks list to file {}", bookmarkFile, e); + NEIClientConfig.logger.error("Failed to save bookmarks list to file {}", bookmarkFile, e); } } diff --git a/src/main/java/codechicken/nei/commands/CommandBookmarkAdd.java b/src/main/java/codechicken/nei/commands/CommandBookmarkAdd.java index 190d04d5..f66ac46f 100644 --- a/src/main/java/codechicken/nei/commands/CommandBookmarkAdd.java +++ b/src/main/java/codechicken/nei/commands/CommandBookmarkAdd.java @@ -1,7 +1,6 @@ package codechicken.nei.commands; import net.minecraft.command.CommandBase; -import net.minecraft.command.ICommand; import net.minecraft.command.ICommandSender; import net.minecraft.item.ItemStack; import net.minecraft.nbt.JsonToNBT; @@ -12,7 +11,7 @@ import net.minecraft.util.ChatComponentTranslation; import codechicken.nei.ItemPanels; -public class CommandBookmarkAdd extends CommandBase implements ICommand { +public class CommandBookmarkAdd extends CommandBase { @Override public String getCommandName() { diff --git a/src/main/java/codechicken/nei/commands/CommandRecipeId.java b/src/main/java/codechicken/nei/commands/CommandRecipeId.java new file mode 100644 index 00000000..893ff9c9 --- /dev/null +++ b/src/main/java/codechicken/nei/commands/CommandRecipeId.java @@ -0,0 +1,296 @@ +package codechicken.nei.commands; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommandSender; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.ChatStyle; +import net.minecraft.util.EnumChatFormatting; + +import org.apache.commons.io.IOUtils; + +import com.google.gson.JsonParser; + +import codechicken.core.CommonUtils; +import codechicken.nei.ClientHandler; +import codechicken.nei.ItemList; +import codechicken.nei.NEIClientConfig; +import codechicken.nei.NEIClientUtils; +import codechicken.nei.recipe.GuiCraftingRecipe; +import codechicken.nei.recipe.GuiRecipeTab; +import codechicken.nei.recipe.ICraftingHandler; +import codechicken.nei.recipe.Recipe; +import codechicken.nei.recipe.RecipeHandlerQuery; +import codechicken.nei.util.NBTJson; + +public class CommandRecipeId extends CommandBase { + + protected static class ProcessDiffThread extends Thread { + + protected final ICommandSender sender; + protected final File prevFile; + protected final File currFile; + protected final File diffFile; + + public ProcessDiffThread(ICommandSender sender, File prevFile, File currFile, File diffFile) { + this.sender = sender; + this.prevFile = prevFile; + this.currFile = currFile; + this.diffFile = diffFile; + } + + @Override + public void run() { + sendChatInfoMessage(sender, "nei.chat.recipeid.diff.start"); + + final Set prevRecipes = loadFileContent(this.prevFile); + final Set currRecipes = loadFileContent(this.currFile); + final Set notAllowedRecipes = prevRecipes.stream().filter(recipe -> !currRecipes.contains(recipe)) + .collect(Collectors.toCollection(TreeSet::new)); + final List subsetsList = new ArrayList<>(); + + generateSubsets(notAllowedRecipes).forEach((handlerName, recipes) -> { + subsetsList.add("; " + handlerName); + subsetsList.addAll(recipes); + }); + + saveFile(this.diffFile, subsetsList); + saveFile(getFile("not-allowed-recipes"), new ArrayList<>(notAllowedRecipes)); + + sendChatInfoMessage(sender, "nei.chat.recipeid.diff.finish"); + } + + private Set loadFileContent(File file) { + try (FileReader reader = new FileReader(file)) { + return new HashSet<>(IOUtils.readLines(reader)); + } catch (IOException e) { + NEIClientConfig.logger.error("Failed to load '{}' file {}", file.getName(), file, e); + } + return new HashSet<>(); + } + + private Map> generateSubsets(Set notAllowedRecipes) { + final JsonParser parser = new JsonParser(); + final Map> subsetsBuilder = new TreeMap<>(); + + for (String recipeStr : notAllowedRecipes) { + try { + final NBTTagCompound nbtRecipe = (NBTTagCompound) NBTJson.toNbt(parser.parse(recipeStr)); + + if (nbtRecipe.hasKey("result")) { + final NBTTagCompound nbtStack = nbtRecipe.getCompoundTag("result"); + nbtStack.removeTag("Count"); + subsetsBuilder.computeIfAbsent(nbtRecipe.getString("handlerName"), rn -> new TreeSet<>()) + .add(NBTJson.toJson(nbtStack)); + } else { + NEIClientConfig.logger.error("Found Broken RecipeId {}", recipeStr); + } + } catch (Exception ex) { + NEIClientConfig.logger.error("Found Broken RecipeId {}", recipeStr, ex); + } + } + + return subsetsBuilder; + } + + private void saveFile(File file, List content) { + try (FileOutputStream output = new FileOutputStream(file)) { + IOUtils.writeLines(content, "\n", output, StandardCharsets.UTF_8); + } catch (IOException e) { + NEIClientConfig.logger.error("Failed to save recipeid diff list to file {}", file, e); + } + } + + } + + protected static class ProcessDumpThread extends Thread { + + private ArrayList craftinghandlers = new ArrayList<>(); + private ArrayList serialCraftingHandlers = new ArrayList<>(); + protected final ICommandSender sender; + protected final File currFile; + + public ProcessDumpThread(ICommandSender sender, File currFile) { + this.sender = sender; + this.currFile = currFile; + ClientHandler.loadSettingsFile("recipeidblacklist.cfg", lines -> { + final Set names = lines.collect(Collectors.toSet()); + + this.craftinghandlers = GuiCraftingRecipe.craftinghandlers.stream() + .filter(h -> !names.contains(GuiRecipeTab.getHandlerInfo(h).getHandlerName())) + .collect(Collectors.toCollection(ArrayList::new)); + + this.serialCraftingHandlers = GuiCraftingRecipe.serialCraftingHandlers.stream() + .filter(h -> !names.contains(GuiRecipeTab.getHandlerInfo(h).getHandlerName())) + .collect(Collectors.toCollection(ArrayList::new)); + }); + } + + @Override + public void run() { + NEIClientConfig.logger.info("Start processing recipe handlers!"); + sendChatInfoMessage(sender, "nei.chat.recipeid.dump.start"); + + try (BufferedWriter writer = Files.newBufferedWriter(currFile.toPath(), StandardCharsets.UTF_8)) { + int total = ItemList.items.size(); + int count = 0; + + for (ItemStack stack : ItemList.items) { + + if (count % 1000 == 0) { + NEIClientConfig.logger.info( + "({}/{}). Processing {} crafting recipes...", + count, + total, + stack.getDisplayName()); + } + + count++; + + for (ICraftingHandler handler : getCraftingHandlers(stack)) { + for (int index = 0, num = handler.numRecipes(); index < num; index++) { + try { + final Recipe recipe = Recipe.of(handler, index); + if (!recipe.getIngredients().isEmpty() && !recipe.getResults().isEmpty()) { + writer.write(NBTJson.toJson(recipe.getRecipeId().toJsonObject())); + writer.newLine(); + } + } catch (Exception ex) { + NEIClientConfig.logger.error( + "Found Broken RecipeId {}:{}", + GuiRecipeTab.getHandlerInfo(handler).getHandlerName(), + stack, + ex); + } + } + } + } + + } catch (Exception e) { + NEIClientConfig.logger.error("Error dumping RecipeId", e); + } + + NEIClientConfig.logger.info("Finished processing recipe handlers!"); + sendChatInfoMessage(sender, "nei.chat.recipeid.dump.finish"); + } + + private ArrayList getCraftingHandlers(Object... results) { + return new RecipeHandlerQuery<>( + h -> h.getRecipeHandler("item", results), + this.craftinghandlers, + this.serialCraftingHandlers, + "Error while looking up crafting recipe", + "outputId: item", + "results: " + Arrays.toString(results)) + .runWithProfiling(NEIClientUtils.translate("recipe.concurrent.crafting")); + } + + } + + @Override + public String getCommandName() { + return "recipeid"; + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return "/recipeid dump OR /recipeid diff [subset name]"; + } + + @Override + public int getRequiredPermissionLevel() { + return 0; + } + + @Override + public void processCommand(ICommandSender sender, String[] args) { + final String command = args.length == 0 ? null : args[0]; + + if ("dump".equals(command)) { + processDumpCommand(sender, args); + } else if ("diff".equals(command)) { + processDiffCommand(sender, args); + } else { + sendChatErrorMessage(sender, getCommandUsage(sender)); + } + + } + + protected void processDiffCommand(ICommandSender sender, String[] args) { + + if (args.length > 4) { + sendChatErrorMessage(sender, "nei.chat.recipeid.many_params", getCommandUsage(sender)); + return; + } + + if (args.length < 3) { + sendChatErrorMessage(sender, getCommandUsage(sender)); + return; + } + + final File prevFilename = getFile(args[1]); + final File currFilename = getFile(args[2]); + final File diffFilename = getFile(args.length > 3 ? args[3] : "changelog"); + + if (!prevFilename.exists()) { + sendChatErrorMessage(sender, "nei.chat.recipeid.not_found", prevFilename.getName()); + return; + } + + if (!currFilename.exists()) { + sendChatErrorMessage(sender, "nei.chat.recipeid.not_found", currFilename.getName()); + return; + } + + (new ProcessDiffThread(sender, prevFilename, currFilename, diffFilename)).start(); + } + + protected void processDumpCommand(ICommandSender sender, String[] args) { + + if (args.length > 2) { + sendChatErrorMessage(sender, "nei.chat.recipeid.many_params", getCommandUsage(sender)); + return; + } + + final File dir = new File(CommonUtils.getMinecraftDir(), "recipeid"); + final String currFilename = args.length == 2 ? args[1] : "recipeId"; + if (!dir.exists()) dir.mkdirs(); + + (new ProcessDumpThread(sender, getFile(currFilename))).start(); + } + + private static File getFile(String filename) { + return new File(CommonUtils.getMinecraftDir(), "recipeid/" + filename + ".json"); + } + + private static void sendChatInfoMessage(ICommandSender sender, String translationKey, Object... args) { + sender.addChatMessage( + new ChatComponentTranslation(translationKey, args) + .setChatStyle(new ChatStyle().setColor(EnumChatFormatting.AQUA))); + } + + private static void sendChatErrorMessage(ICommandSender sender, String translationKey, Object... args) { + sender.addChatMessage( + new ChatComponentTranslation(translationKey, args) + .setChatStyle(new ChatStyle().setColor(EnumChatFormatting.RED))); + } + +} diff --git a/src/main/java/codechicken/nei/config/HandlerDumper.java b/src/main/java/codechicken/nei/config/HandlerDumper.java index 13d57645..034e0116 100644 --- a/src/main/java/codechicken/nei/config/HandlerDumper.java +++ b/src/main/java/codechicken/nei/config/HandlerDumper.java @@ -97,7 +97,7 @@ public class HandlerDumper extends DataDumper { } writer.close(); } catch (IOException e) { - NEIClientConfig.logger.error("Filed to save dump handler list to file {}", file, e); + NEIClientConfig.logger.error("Failed to save dump handler list to file {}", file, e); } } diff --git a/src/main/java/codechicken/nei/recipe/Recipe.java b/src/main/java/codechicken/nei/recipe/Recipe.java index 9832de85..fc43daaf 100644 --- a/src/main/java/codechicken/nei/recipe/Recipe.java +++ b/src/main/java/codechicken/nei/recipe/Recipe.java @@ -2,9 +2,12 @@ package codechicken.nei.recipe; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.fluids.FluidContainerRegistry; @@ -36,10 +39,15 @@ public class Recipe { this.result = result != null ? StackInfo.withAmount(result, 0) : null; this.handlerName = handlerName; this.ingredients = ingredients; + + if (!isShapedRecipe()) { + // sort to ignore ingredients position in shapeless recipes + this.ingredients.sort(Comparator.comparing(StackInfo::getItemStackGUID)); + } } public static RecipeId of(Object result, String handlerName, Iterable ingredients) { - return new RecipeId(extractItem(result), handlerName, extractItems(ingredients)); + return new RecipeId(extractItem(result), handlerName, extractIngredients(ingredients)); } public static RecipeId of(JsonObject json) { @@ -74,14 +82,14 @@ public class Recipe { return new ArrayList<>(this.ingredients); } - protected static List extractItems(Iterable items) { + protected static List extractIngredients(Iterable items) { final List list = new ArrayList<>(); for (Object item : items) { - ItemStack stack = extractItem(item); + final ItemStack stack = extractItem(item); if (stack != null) { - list.add(stack.copy()); + list.add(prepareIngredient(stack.copy())); } } @@ -115,15 +123,56 @@ public class Recipe { return false; } - for (int index = 0; index < stacks.size(); index++) { - if (!stacks.get(index).containsWithNBT(ingredients.get(index))) { - return false; + if (isShapedRecipe()) { + for (int index = 0; index < stacks.size(); index++) { + if (!containsWithNBT(stacks.get(index), Collections.singletonList(this.ingredients.get(index)))) { + return false; + } + } + } else { + for (PositionedStack pStack : stacks) { + if (!containsWithNBT(pStack, this.ingredients)) { + return false; + } } } return true; } + private static boolean containsWithNBT(PositionedStack pStack, List ingrStacks) { + for (ItemStack item : pStack.items) { + final ItemStack pItem = prepareIngredient(item); + if (ingrStacks.stream().anyMatch(stack -> StackInfo.equalItemAndNBT(pItem, stack, true))) { + return true; + } + } + return false; + } + + private static ItemStack prepareIngredient(ItemStack stack) { + + // hack: remove metadata for "Data Stick" + if (stack.hasTagCompound() && stack.getItemDamage() == 32708 + && "gregtech:gt.metaitem.01".equals(Item.itemRegistry.getNameForObject(stack.getItem()))) { + return new ItemStack(stack.getItem(), stack.getItemDamage()); + } + + // hack: remove metadata for "Circuit Imprint" + if (stack.hasTagCompound() && stack.getItemDamage() == 0 + && "bartworks:gt.bwMetaGeneratedItem0" + .equals(Item.itemRegistry.getNameForObject(stack.getItem()))) { + return new ItemStack(stack.getItem(), stack.getItemDamage()); + } + + return stack; + } + + public boolean isShapedRecipe() { + return this.handlerName.equals("codechicken.nei.recipe.ShapedRecipeHandler") + || this.handlerName.equals("fox.spiteful.avaritia.compat.nei.ExtremeShapedRecipeHandler"); + } + public JsonObject toJsonObject() { final JsonObject json = new JsonObject(); @@ -142,8 +191,8 @@ public class Recipe { protected JsonArray convertItemsToJsonArray(List items) { final JsonArray arr = new JsonArray(); - for (ItemStack nbTag : items) { - arr.add(NBTJson.toJsonObject(StackInfo.itemStackToNBT(nbTag))); + for (ItemStack stack : items) { + arr.add(NBTJson.toJsonObject(StackInfo.itemStackToNBT(stack))); } return arr; diff --git a/src/main/java/codechicken/nei/recipe/RecipeHandlerQuery.java b/src/main/java/codechicken/nei/recipe/RecipeHandlerQuery.java index e3e099ab..82718d19 100644 --- a/src/main/java/codechicken/nei/recipe/RecipeHandlerQuery.java +++ b/src/main/java/codechicken/nei/recipe/RecipeHandlerQuery.java @@ -17,7 +17,7 @@ import codechicken.nei.ItemList; import codechicken.nei.NEIClientConfig; import codechicken.nei.util.AsyncTaskProfiler; -class RecipeHandlerQuery { +public class RecipeHandlerQuery { private final Function recipeHandlerFunction; private final List recipeHandlers; @@ -25,15 +25,15 @@ class RecipeHandlerQuery { private final String[] errorMessage; private boolean error = false; - RecipeHandlerQuery(Function recipeHandlerFunction, List recipeHandlers, List serialRecipeHandlers, - String... errorMessage) { + public RecipeHandlerQuery(Function recipeHandlerFunction, List recipeHandlers, + List serialRecipeHandlers, String... errorMessage) { this.recipeHandlerFunction = recipeHandlerFunction; this.recipeHandlers = recipeHandlers; this.serialRecipeHandlers = serialRecipeHandlers; this.errorMessage = errorMessage; } - ArrayList runWithProfiling(String profilerSection) { + public ArrayList runWithProfiling(String profilerSection) { TaskProfiler profiler = ProfilerRecipeHandler.getProfiler(); // Save the current config state here, as it may be altered diff --git a/src/main/java/codechicken/nei/recipe/StackInfo.java b/src/main/java/codechicken/nei/recipe/StackInfo.java index e2eaa095..db91c06c 100644 --- a/src/main/java/codechicken/nei/recipe/StackInfo.java +++ b/src/main/java/codechicken/nei/recipe/StackInfo.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTBase; import net.minecraft.nbt.NBTTagCompound; @@ -22,6 +23,7 @@ import codechicken.nei.api.ItemInfo; import codechicken.nei.recipe.stackinfo.DefaultStackStringifyHandler; import codechicken.nei.recipe.stackinfo.GTFluidStackStringifyHandler; import codechicken.nei.util.ItemStackKey; +import codechicken.nei.util.NBTHelper; public class StackInfo { @@ -144,7 +146,7 @@ public class StackInfo { final NBTTagCompound nbTag = itemStackToNBT(stack, false); if (nbTag == null) { - return null; + return "unknown"; } nbTag.removeTag("Count"); @@ -157,9 +159,11 @@ public class StackInfo { nbTag.removeTag("tag"); } - if (nbTag.hasKey("strId") && guidfilters.containsKey(nbTag.getString("strId"))) { + if (nbTag.hasKey("strId") && (guidfilters.containsKey(nbTag.getString("strId")) + || guidfilters.containsKey(nbTag.getString("strId") + "&" + nbTag.getShort("Damage")))) { + final String strId = nbTag.getString("strId") + + (guidfilters.containsKey(nbTag.getString("strId")) ? "" : "&" + nbTag.getShort("Damage")); final ArrayList keys = new ArrayList<>(); - final String strId = nbTag.getString("strId"); keys.add(strId); @@ -184,7 +188,7 @@ public class StackInfo { } if (local instanceof NBTBase item) { - keys.add(item.toString()); + keys.add(NBTHelper.toString(item)); } else if (local != null) { keys.add(String.valueOf(local)); } @@ -195,7 +199,7 @@ public class StackInfo { } } else { synchronized (guidcache) { - guidcache.put(stack, nbTag.toString()); + guidcache.put(stack, NBTHelper.toString(nbTag)); } } @@ -218,13 +222,17 @@ public class StackInfo { } public static ItemStack getItemStackWithMinimumDamage(ItemStack[] stacks) { - int damage = Short.MAX_VALUE; ItemStack result = stacks[0]; if (stacks.length > 1) { for (ItemStack stack : stacks) { - if (stack.getItem() != null && !ItemInfo.isHidden(stack) && stack.getItemDamage() < damage) { - damage = stack.getItemDamage(); + // to optimize compare recipes from permutation, an item with a minimum strId and damage is taken so as + // not to check everything permutation items + if (stack.getItem() != null && !ItemInfo.isHidden(stack) + && (stack.getItemDamage() < result.getItemDamage() + || stack.getItemDamage() == result.getItemDamage() + && Item.itemRegistry.getNameForObject(stack.getItem()) + .compareTo(Item.itemRegistry.getNameForObject(result.getItem())) < 0)) { result = stack; } } diff --git a/src/main/java/codechicken/nei/util/NBTHelper.java b/src/main/java/codechicken/nei/util/NBTHelper.java index 331d25d3..64ceb22a 100644 --- a/src/main/java/codechicken/nei/util/NBTHelper.java +++ b/src/main/java/codechicken/nei/util/NBTHelper.java @@ -1,5 +1,9 @@ package codechicken.nei.util; +import java.util.Map; +import java.util.StringJoiner; +import java.util.stream.Collectors; + import javax.annotation.Nullable; import net.minecraft.nbt.NBTBase; @@ -52,4 +56,26 @@ public class NBTHelper { private static NBTBase get(NBTTagList tag, int idx) { return idx >= 0 && idx < tag.tagList.size() ? (NBTBase) tag.tagList.get(idx) : null; } + + public static String toString(NBTBase nbt) { + + if (nbt instanceof NBTTagCompound nbtTagCompound) { + final Map tagMap = (Map) nbtTagCompound.tagMap; + return "{" + tagMap.entrySet().stream().sorted(Map.Entry.comparingByKey()) + .map(nbtEntry -> nbtEntry.getKey() + ":" + toString(nbtEntry.getValue())) + .collect(Collectors.joining(",")) + "}"; + } else if (nbt instanceof NBTTagList list) { + + if (list.tagList.isEmpty()) { + return "[]"; + } else { + final StringJoiner arr = new StringJoiner(","); + list.tagList.forEach(c -> arr.add(toString((NBTBase) c))); + return "[" + arr.toString() + "]"; + } + + } else { + return nbt.toString(); + } + } } diff --git a/src/main/java/codechicken/nei/util/NBTJson.java b/src/main/java/codechicken/nei/util/NBTJson.java index 553cd352..f8953a2d 100644 --- a/src/main/java/codechicken/nei/util/NBTJson.java +++ b/src/main/java/codechicken/nei/util/NBTJson.java @@ -43,12 +43,10 @@ public class NBTJson { // NBTTagCompound final NBTTagCompound nbtTagCompound = (NBTTagCompound) nbt; final Map tagMap = (Map) nbtTagCompound.tagMap; + final JsonObject root = new JsonObject(); - JsonObject root = new JsonObject(); - - for (Map.Entry nbtEntry : tagMap.entrySet()) { - root.add(nbtEntry.getKey(), toJsonObject(nbtEntry.getValue())); - } + tagMap.entrySet().stream().sorted(Map.Entry.comparingByKey()) + .forEach(nbtEntry -> { root.add(nbtEntry.getKey(), toJsonObject(nbtEntry.getValue())); }); return root; } else if (nbt instanceof NBTTagByte) { diff --git a/src/main/resources/assets/nei/cfg/guidfilters.cfg b/src/main/resources/assets/nei/cfg/guidfilters.cfg index 620162b2..0cc3cf20 100644 --- a/src/main/resources/assets/nei/cfg/guidfilters.cfg +++ b/src/main/resources/assets/nei/cfg/guidfilters.cfg @@ -54,4 +54,5 @@ miscutils:personalHealingDevice tectech:item.em.debugContainer,tag.symbols tinkersdefense:Heater Shield,tag.InfiTool tinkersdefense:Round Shield,tag.InfiTool -miscutils:itemCustomMetaCover.miscutils.GtMachineCasings,Damage \ No newline at end of file +miscutils:itemCustomMetaCover.miscutils.GtMachineCasings,Damage +gregtech:gt.metaitem.01&32708,tag.output \ No newline at end of file diff --git a/src/main/resources/assets/nei/cfg/recipeidblacklist.cfg b/src/main/resources/assets/nei/cfg/recipeidblacklist.cfg new file mode 100644 index 00000000..a23aa333 --- /dev/null +++ b/src/main/resources/assets/nei/cfg/recipeidblacklist.cfg @@ -0,0 +1,3 @@ +# Each line in this file should either be a comment (line starts with '#'), handler name or handler id. +# If you delete this file, it will be regenerated with the default handlers list. +Unknown \ No newline at end of file diff --git a/src/main/resources/assets/nei/lang/en_US.lang b/src/main/resources/assets/nei/lang/en_US.lang index f5ea555d..eb1a49cb 100644 --- a/src/main/resources/assets/nei/lang/en_US.lang +++ b/src/main/resources/assets/nei/lang/en_US.lang @@ -138,6 +138,13 @@ nei.chat.chunkoverlay.3=Chunk Overlay: GT Ore Veins nei.chat.item_link.text=<%s> ยง6Click to add bookmark: %s nei.chat.bookmark_added.text=Bookmark added! +nei.chat.recipeid.many_params=Too many parameters! Usage: %s +nei.chat.recipeid.not_found=File %s not found! +nei.chat.recipeid.diff.start=Start processing recipe diff! +nei.chat.recipeid.diff.finish=Finished processing recipe diff! +nei.chat.recipeid.dump.start=Start processing recipe handlers! +nei.chat.recipeid.dump.finish=Finished processing recipe handlers! + nei.options=NEI Options nei.options.back=Back nei.options.save=Save