diff --git a/src/main/java/codechicken/nei/NEIServerUtils.java b/src/main/java/codechicken/nei/NEIServerUtils.java index ea9acddc..def4a6ca 100644 --- a/src/main/java/codechicken/nei/NEIServerUtils.java +++ b/src/main/java/codechicken/nei/NEIServerUtils.java @@ -5,6 +5,7 @@ import codechicken.core.ServerUtils; import codechicken.lib.inventory.InventoryRange; import codechicken.lib.inventory.InventoryUtils; import codechicken.lib.packet.PacketCustom; +import codechicken.nei.util.NBTHelper; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.registry.GameRegistry; import net.minecraft.command.ICommandSender; @@ -149,6 +150,19 @@ public class NEIServerUtils ItemStack.areItemStackTagsEqual(stack2, stack1)); } + /** + * NBT-friendly version of {@link #areStacksSameType(ItemStack, ItemStack)} + * @param stack1 The {@link ItemStack} being compared. + * @param stack2 The {@link ItemStack} to compare to. + * @return whether the two items are the same in terms of itemID, damage and NBT. + */ + public static boolean areStacksSameTypeWithNBT(ItemStack stack1, ItemStack stack2) { + return stack1 != null && stack2 != null && + stack1.getItem() == stack2.getItem() && + (!stack2.getHasSubtypes() || stack2.getItemDamage() == stack1.getItemDamage()) && + NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound()); + } + /** * {@link ItemStack}s with damage -1 are wildcards allowing all damages. Eg all colours of wool are allowed to create Beds. * @@ -162,6 +176,24 @@ public class NEIServerUtils (stack1.getItemDamage() == stack2.getItemDamage() || stack1.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack2.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack1.getItem().isDamageable()); } + /** + * NBT-friendly version of {@link #areStacksSameTypeCrafting(ItemStack, ItemStack)} + * @param stack1 The {@link ItemStack} being compared. + * @param stack2 The {@link ItemStack} to compare to. + * @return whether the two items are the same from the perspective of a crafting inventory. + */ + public static boolean areStacksSameTypeCraftingWithNBT(ItemStack stack1, ItemStack stack2) { + return stack1 != null && stack2 != null && + stack1.getItem() == stack2.getItem() && + ( + stack1.getItemDamage() == stack2.getItemDamage() || + stack1.getItemDamage() == OreDictionary.WILDCARD_VALUE || + stack2.getItemDamage() == OreDictionary.WILDCARD_VALUE || + stack1.getItem().isDamageable() + ) && + NBTHelper.matchTag(stack1.getTagCompound(), stack2.getTagCompound()); + } + /** * A simple function for comparing ItemStacks in a compatible with comparators. * diff --git a/src/main/java/codechicken/nei/PositionedStack.java b/src/main/java/codechicken/nei/PositionedStack.java index 666ef499..08dfdb56 100644 --- a/src/main/java/codechicken/nei/PositionedStack.java +++ b/src/main/java/codechicken/nei/PositionedStack.java @@ -98,6 +98,14 @@ public class PositionedStack return false; } + public boolean containsWithNBT(ItemStack ingredient) { + for(ItemStack item : items) + if(NEIServerUtils.areStacksSameTypeCraftingWithNBT(item, ingredient)) + return true; + + return false; + } + public boolean contains(Item ingred) { for(ItemStack item : items) if(item.getItem() == ingred) diff --git a/src/main/java/codechicken/nei/recipe/CatalystInfoList.java b/src/main/java/codechicken/nei/recipe/CatalystInfoList.java index 9aa97705..26353947 100644 --- a/src/main/java/codechicken/nei/recipe/CatalystInfoList.java +++ b/src/main/java/codechicken/nei/recipe/CatalystInfoList.java @@ -49,14 +49,14 @@ public class CatalystInfoList extends ForwardingList { } public boolean contains(ItemStack stack) { - return catalystInfoList.stream().anyMatch(c -> NEIServerUtils.areStacksSameType(c.getStack(), stack)); + return catalystInfoList.stream().anyMatch(c -> NEIServerUtils.areStacksSameTypeCraftingWithNBT(c.getStack(), stack)); } public boolean remove(ItemStack stack) { Iterator iter = catalystInfoList.iterator(); while (iter.hasNext()) { ItemStack next = iter.next().getStack(); - if (NEIServerUtils.areStacksSameType(next, stack)) { + if (NEIServerUtils.areStacksSameTypeCraftingWithNBT(next, stack)) { iter.remove(); return true; } diff --git a/src/main/java/codechicken/nei/util/NBTHelper.java b/src/main/java/codechicken/nei/util/NBTHelper.java new file mode 100644 index 00000000..e304dce2 --- /dev/null +++ b/src/main/java/codechicken/nei/util/NBTHelper.java @@ -0,0 +1,59 @@ +package codechicken.nei.util; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagEnd; +import net.minecraft.nbt.NBTTagList; + +import javax.annotation.Nullable; +import java.util.Set; + +public class NBTHelper { + + /* + * Taken from Botania r1.11-379 by Vazkii under the Botania License + * You can obtain one at https://botaniamod.net/license.php + */ + + /** + * Returns true if the `target` tag contains all of the tags and values present in the `template` tag. Recurses into + * compound tags and matches all template keys and values; recurses into list tags and matches the template against + * the first elements of target. Empty lists and compounds in the template will match target lists and compounds of + * any size. + */ + public static boolean matchTag(@Nullable NBTBase template, @Nullable NBTBase target) { + if (template instanceof NBTTagCompound && target instanceof NBTTagCompound) { + return matchTagCompound((NBTTagCompound) template, (NBTTagCompound) target); + } else if (template instanceof NBTTagList && target instanceof NBTTagList) { + return matchTagList((NBTTagList) template, (NBTTagList) target); + } else { + return template == null || (target != null && target.equals(template)); + } + } + + private static boolean matchTagCompound(NBTTagCompound template, NBTTagCompound target) { + if (template.tagMap.size() > target.tagMap.size()) return false; + + //noinspection unchecked + for (String key : (Set) template.func_150296_c()) { + if (!matchTag(template.getTag(key), target.getTag(key))) return false; + } + + return true; + } + + private static boolean matchTagList(NBTTagList template, NBTTagList target) { + if (template.tagCount() > target.tagCount()) return false; + + for (int i = 0; i < template.tagCount(); i++) { + if (!matchTag(get(template, i), get(target, i))) return false; + } + + return true; + } + + private static NBTBase get(NBTTagList tag, int idx) + { + return idx >= 0 && idx < tag.tagList.size() ? (NBTBase)tag.tagList.get(idx) : null; + } +}