package net.minecraftforge.oredict; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import net.minecraft.block.BlockPrismarine; import net.minecraft.util.ResourceLocation; import org.apache.logging.log4j.Level; import net.minecraft.block.Block; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.CraftingManager; import net.minecraft.item.crafting.IRecipe; import net.minecraft.item.crafting.ShapedRecipes; import net.minecraft.item.crafting.ShapelessRecipes; import net.minecraftforge.common.MinecraftForge; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import net.minecraftforge.fml.common.FMLLog; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.eventhandler.Event; import net.minecraftforge.fml.common.registry.GameData; public class OreDictionary { private static boolean hasInit = false; private static List idToName = new ArrayList(); private static Map nameToId = new HashMap(128); private static List> idToStack = Lists.newArrayList(); private static List> idToStackUn = Lists.newArrayList(); private static Map> stackToId = Maps.newHashMapWithExpectedSize((int)(128 * 0.75)); public static final ImmutableList EMPTY_LIST = ImmutableList.of(); /** * Minecraft changed from -1 to Short.MAX_VALUE in 1.5 release for the "block wildcard". Use this in case it * changes again. */ public static final int WILDCARD_VALUE = Short.MAX_VALUE; static { initVanillaEntries(); } private static void initVanillaEntries() { if (!hasInit) { // tree- and wood-related things registerOre("logWood", new ItemStack(Blocks.LOG, 1, WILDCARD_VALUE)); registerOre("logWood", new ItemStack(Blocks.LOG2, 1, WILDCARD_VALUE)); registerOre("plankWood", new ItemStack(Blocks.PLANKS, 1, WILDCARD_VALUE)); registerOre("slabWood", new ItemStack(Blocks.WOODEN_SLAB, 1, WILDCARD_VALUE)); registerOre("stairWood", Blocks.OAK_STAIRS); registerOre("stairWood", Blocks.SPRUCE_STAIRS); registerOre("stairWood", Blocks.BIRCH_STAIRS); registerOre("stairWood", Blocks.JUNGLE_STAIRS); registerOre("stairWood", Blocks.ACACIA_STAIRS); registerOre("stairWood", Blocks.DARK_OAK_STAIRS); registerOre("stickWood", Items.STICK); registerOre("treeSapling", new ItemStack(Blocks.SAPLING, 1, WILDCARD_VALUE)); registerOre("treeLeaves", new ItemStack(Blocks.LEAVES, 1, WILDCARD_VALUE)); registerOre("treeLeaves", new ItemStack(Blocks.LEAVES2, 1, WILDCARD_VALUE)); registerOre("vine", Blocks.VINE); // Ores registerOre("oreGold", Blocks.GOLD_ORE); registerOre("oreIron", Blocks.IRON_ORE); registerOre("oreLapis", Blocks.LAPIS_ORE); registerOre("oreDiamond", Blocks.DIAMOND_ORE); registerOre("oreRedstone", Blocks.REDSTONE_ORE); registerOre("oreEmerald", Blocks.EMERALD_ORE); registerOre("oreQuartz", Blocks.QUARTZ_ORE); registerOre("oreCoal", Blocks.COAL_ORE); // ingots/nuggets registerOre("ingotIron", Items.IRON_INGOT); registerOre("ingotGold", Items.GOLD_INGOT); registerOre("ingotBrick", Items.BRICK); registerOre("ingotBrickNether", Items.NETHERBRICK); registerOre("nuggetGold", Items.GOLD_NUGGET); // gems and dusts registerOre("gemDiamond", Items.DIAMOND); registerOre("gemEmerald", Items.EMERALD); registerOre("gemQuartz", Items.QUARTZ); registerOre("gemPrismarine", Items.PRISMARINE_SHARD); registerOre("dustPrismarine", Items.PRISMARINE_CRYSTALS); registerOre("dustRedstone", Items.REDSTONE); registerOre("dustGlowstone", Items.GLOWSTONE_DUST); registerOre("gemLapis", new ItemStack(Items.DYE, 1, 4)); // storage blocks registerOre("blockGold", Blocks.GOLD_BLOCK); registerOre("blockIron", Blocks.IRON_BLOCK); registerOre("blockLapis", Blocks.LAPIS_BLOCK); registerOre("blockDiamond", Blocks.DIAMOND_BLOCK); registerOre("blockRedstone", Blocks.REDSTONE_BLOCK); registerOre("blockEmerald", Blocks.EMERALD_BLOCK); registerOre("blockQuartz", Blocks.QUARTZ_BLOCK); registerOre("blockCoal", Blocks.COAL_BLOCK); // crops registerOre("cropWheat", Items.WHEAT); registerOre("cropPotato", Items.POTATO); registerOre("cropCarrot", Items.CARROT); registerOre("cropNetherWart", Items.NETHER_WART); registerOre("sugarcane", Items.REEDS); registerOre("blockCactus", Blocks.CACTUS); // misc materials registerOre("dye", new ItemStack(Items.DYE, 1, WILDCARD_VALUE)); registerOre("paper", new ItemStack(Items.PAPER)); // mob drops registerOre("slimeball", Items.SLIME_BALL); registerOre("enderpearl", Items.ENDER_PEARL); registerOre("bone", Items.BONE); registerOre("gunpowder", Items.GUNPOWDER); registerOre("string", Items.STRING); registerOre("netherStar", Items.NETHER_STAR); registerOre("leather", Items.LEATHER); registerOre("feather", Items.FEATHER); registerOre("egg", Items.EGG); // records registerOre("record", Items.RECORD_13); registerOre("record", Items.RECORD_CAT); registerOre("record", Items.RECORD_BLOCKS); registerOre("record", Items.RECORD_CHIRP); registerOre("record", Items.RECORD_FAR); registerOre("record", Items.RECORD_MALL); registerOre("record", Items.RECORD_MELLOHI); registerOre("record", Items.RECORD_STAL); registerOre("record", Items.RECORD_STRAD); registerOre("record", Items.RECORD_WARD); registerOre("record", Items.RECORD_11); registerOre("record", Items.RECORD_WAIT); // blocks registerOre("dirt", Blocks.DIRT); registerOre("grass", Blocks.GRASS); registerOre("stone", Blocks.STONE); registerOre("cobblestone", Blocks.COBBLESTONE); registerOre("gravel", Blocks.GRAVEL); registerOre("sand", new ItemStack(Blocks.SAND, 1, WILDCARD_VALUE)); registerOre("sandstone", new ItemStack(Blocks.SANDSTONE, 1, WILDCARD_VALUE)); registerOre("sandstone", new ItemStack(Blocks.RED_SANDSTONE, 1, WILDCARD_VALUE)); registerOre("netherrack", Blocks.NETHERRACK); registerOre("obsidian", Blocks.OBSIDIAN); registerOre("glowstone", Blocks.GLOWSTONE); registerOre("endstone", Blocks.END_STONE); registerOre("torch", Blocks.TORCH); registerOre("workbench", Blocks.CRAFTING_TABLE); registerOre("blockSlime", Blocks.SLIME_BLOCK); registerOre("blockPrismarine", new ItemStack(Blocks.PRISMARINE, 1, BlockPrismarine.EnumType.ROUGH.getMetadata())); registerOre("blockPrismarineBrick", new ItemStack(Blocks.PRISMARINE, 1, BlockPrismarine.EnumType.BRICKS.getMetadata())); registerOre("blockPrismarineDark", new ItemStack(Blocks.PRISMARINE, 1, BlockPrismarine.EnumType.DARK.getMetadata())); registerOre("stoneGranite", new ItemStack(Blocks.STONE, 1, 1)); registerOre("stoneGranitePolished", new ItemStack(Blocks.STONE, 1, 2)); registerOre("stoneDiorite", new ItemStack(Blocks.STONE, 1, 3)); registerOre("stoneDioritePolished", new ItemStack(Blocks.STONE, 1, 4)); registerOre("stoneAndesite", new ItemStack(Blocks.STONE, 1, 5)); registerOre("stoneAndesitePolished", new ItemStack(Blocks.STONE, 1, 6)); registerOre("blockGlassColorless", Blocks.GLASS); registerOre("blockGlass", Blocks.GLASS); registerOre("blockGlass", new ItemStack(Blocks.STAINED_GLASS, 1, WILDCARD_VALUE)); //blockGlass{Color} is added below with dyes registerOre("paneGlassColorless", Blocks.GLASS_PANE); registerOre("paneGlass", Blocks.GLASS_PANE); registerOre("paneGlass", new ItemStack(Blocks.STAINED_GLASS_PANE, 1, WILDCARD_VALUE)); //paneGlass{Color} is added below with dyes // chests registerOre("chest", Blocks.CHEST); registerOre("chest", Blocks.ENDER_CHEST); registerOre("chest", Blocks.TRAPPED_CHEST); registerOre("chestWood", Blocks.CHEST); registerOre("chestEnder", Blocks.ENDER_CHEST); registerOre("chestTrapped", Blocks.TRAPPED_CHEST); } // Build our list of items to replace with ore tags Map replacements = new HashMap(); // wood-related things replacements.put(new ItemStack(Items.STICK), "stickWood"); replacements.put(new ItemStack(Blocks.PLANKS), "plankWood"); replacements.put(new ItemStack(Blocks.PLANKS, 1, WILDCARD_VALUE), "plankWood"); replacements.put(new ItemStack(Blocks.WOODEN_SLAB, 1, WILDCARD_VALUE), "slabWood"); // ingots/nuggets replacements.put(new ItemStack(Items.GOLD_INGOT), "ingotGold"); replacements.put(new ItemStack(Items.IRON_INGOT), "ingotIron"); // gems and dusts replacements.put(new ItemStack(Items.DIAMOND), "gemDiamond"); replacements.put(new ItemStack(Items.EMERALD), "gemEmerald"); replacements.put(new ItemStack(Items.PRISMARINE_SHARD), "gemPrismarine"); replacements.put(new ItemStack(Items.PRISMARINE_CRYSTALS), "dustPrismarine"); replacements.put(new ItemStack(Items.REDSTONE), "dustRedstone"); replacements.put(new ItemStack(Items.GLOWSTONE_DUST), "dustGlowstone"); // crops replacements.put(new ItemStack(Items.REEDS), "sugarcane"); replacements.put(new ItemStack(Blocks.CACTUS), "blockCactus"); // misc materials replacements.put(new ItemStack(Items.PAPER), "paper"); // mob drops replacements.put(new ItemStack(Items.SLIME_BALL), "slimeball"); replacements.put(new ItemStack(Items.STRING), "string"); replacements.put(new ItemStack(Items.LEATHER), "leather"); replacements.put(new ItemStack(Items.ENDER_PEARL), "enderpearl"); replacements.put(new ItemStack(Items.GUNPOWDER), "gunpowder"); replacements.put(new ItemStack(Items.NETHER_STAR), "netherStar"); replacements.put(new ItemStack(Items.FEATHER), "feather"); replacements.put(new ItemStack(Items.BONE), "bone"); replacements.put(new ItemStack(Items.EGG), "egg"); // blocks replacements.put(new ItemStack(Blocks.STONE), "stone"); replacements.put(new ItemStack(Blocks.COBBLESTONE), "cobblestone"); replacements.put(new ItemStack(Blocks.COBBLESTONE, 1, WILDCARD_VALUE), "cobblestone"); replacements.put(new ItemStack(Blocks.GLOWSTONE), "glowstone"); replacements.put(new ItemStack(Blocks.GLASS), "blockGlassColorless"); replacements.put(new ItemStack(Blocks.PRISMARINE), "prismarine"); replacements.put(new ItemStack(Blocks.STONE, 1, 1), "stoneGranite"); replacements.put(new ItemStack(Blocks.STONE, 1, 2), "stoneGranitePolished"); replacements.put(new ItemStack(Blocks.STONE, 1, 3), "stoneDiorite"); replacements.put(new ItemStack(Blocks.STONE, 1, 4), "stoneDioritePolished"); replacements.put(new ItemStack(Blocks.STONE, 1, 5), "stoneAndesite"); replacements.put(new ItemStack(Blocks.STONE, 1, 6), "stoneAndesitePolished"); // chests replacements.put(new ItemStack(Blocks.CHEST), "chestWood"); replacements.put(new ItemStack(Blocks.ENDER_CHEST), "chestEnder"); replacements.put(new ItemStack(Blocks.TRAPPED_CHEST), "chestTrapped"); // Register dyes String[] dyes = { "Black", "Red", "Green", "Brown", "Blue", "Purple", "Cyan", "LightGray", "Gray", "Pink", "Lime", "Yellow", "LightBlue", "Magenta", "Orange", "White" }; for(int i = 0; i < 16; i++) { ItemStack dye = new ItemStack(Items.DYE, 1, i); ItemStack block = new ItemStack(Blocks.STAINED_GLASS, 1, 15 - i); ItemStack pane = new ItemStack(Blocks.STAINED_GLASS_PANE, 1, 15 - i); if (!hasInit) { registerOre("dye" + dyes[i], dye); registerOre("blockGlass" + dyes[i], block); registerOre("paneGlass" + dyes[i], pane); } replacements.put(dye, "dye" + dyes[i]); replacements.put(block, "blockGlass" + dyes[i]); replacements.put(pane, "paneGlass" + dyes[i]); } hasInit = true; ItemStack[] replaceStacks = replacements.keySet().toArray(new ItemStack[replacements.keySet().size()]); // Ignore recipes for the following items ItemStack[] exclusions = new ItemStack[] { new ItemStack(Blocks.LAPIS_BLOCK), new ItemStack(Items.COOKIE), new ItemStack(Blocks.STONEBRICK), new ItemStack(Blocks.STONE_SLAB, 1, WILDCARD_VALUE), new ItemStack(Blocks.STONE_STAIRS), new ItemStack(Blocks.COBBLESTONE_WALL), new ItemStack(Blocks.OAK_FENCE), new ItemStack(Blocks.OAK_FENCE_GATE), new ItemStack(Blocks.OAK_STAIRS), new ItemStack(Blocks.SPRUCE_FENCE), new ItemStack(Blocks.SPRUCE_FENCE_GATE), new ItemStack(Blocks.SPRUCE_STAIRS), new ItemStack(Blocks.BIRCH_STAIRS), new ItemStack(Blocks.BIRCH_FENCE_GATE), new ItemStack(Blocks.BIRCH_STAIRS), new ItemStack(Blocks.JUNGLE_FENCE), new ItemStack(Blocks.JUNGLE_FENCE_GATE), new ItemStack(Blocks.JUNGLE_STAIRS), new ItemStack(Blocks.ACACIA_FENCE), new ItemStack(Blocks.ACACIA_FENCE_GATE), new ItemStack(Blocks.ACACIA_STAIRS), new ItemStack(Blocks.DARK_OAK_FENCE), new ItemStack(Blocks.DARK_OAK_FENCE_GATE), new ItemStack(Blocks.DARK_OAK_STAIRS), new ItemStack(Blocks.WOODEN_SLAB), new ItemStack(Blocks.GLASS_PANE), null //So the above can have a comma and we don't have to keep editing extra lines. }; List recipes = CraftingManager.getInstance().getRecipeList(); List recipesToRemove = new ArrayList(); List recipesToAdd = new ArrayList(); // Search vanilla recipes for recipes to replace for(Object obj : recipes) { if(obj.getClass() == ShapedRecipes.class) { ShapedRecipes recipe = (ShapedRecipes)obj; ItemStack output = recipe.getRecipeOutput(); if (output != null && containsMatch(false, exclusions, output)) { continue; } if(containsMatch(true, recipe.recipeItems, replaceStacks)) { recipesToRemove.add(recipe); recipesToAdd.add(new ShapedOreRecipe(recipe, replacements)); } } else if(obj.getClass() == ShapelessRecipes.class) { ShapelessRecipes recipe = (ShapelessRecipes)obj; ItemStack output = recipe.getRecipeOutput(); if (output != null && containsMatch(false, exclusions, output)) { continue; } if(containsMatch(true, recipe.recipeItems.toArray(new ItemStack[recipe.recipeItems.size()]), replaceStacks)) { recipesToRemove.add((IRecipe)obj); IRecipe newRecipe = new ShapelessOreRecipe(recipe, replacements); recipesToAdd.add(newRecipe); } } } recipes.removeAll(recipesToRemove); recipes.addAll(recipesToAdd); if (recipesToRemove.size() > 0) { FMLLog.info("Replaced %d ore recipes", recipesToRemove.size()); } } /** * Gets the integer ID for the specified ore name. * If the name does not have a ID it assigns it a new one. * * @param name The unique name for this ore 'oreIron', 'ingotIron', etc.. * @return A number representing the ID for this ore type */ public static int getOreID(String name) { Integer val = nameToId.get(name); if (val == null) { idToName.add(name); val = idToName.size() - 1; //0 indexed nameToId.put(name, val); List back = Lists.newArrayList(); idToStack.add(back); idToStackUn.add(Collections.unmodifiableList(back)); } return val; } /** * Reverse of getOreID, will not create new entries. * * @param id The ID to translate to a string * @return The String name, or "Unknown" if not found. */ public static String getOreName(int id) { return (id >= 0 && id < idToName.size()) ? idToName.get(id) : "Unknown"; } /** * Gets all the integer ID for the ores that the specified item stack is registered to. * If the item stack is not linked to any ore, this will return an empty array and no new entry will be created. * * @param stack The item stack of the ore. * @return An array of ids that this ore is registered as. */ public static int[] getOreIDs(ItemStack stack) { if (stack == null || stack.getItem() == null) throw new IllegalArgumentException("Stack can not be null!"); Set set = new HashSet(); // HACK: use the registry name's ID. It is unique and it knows about substitutions. Fallback to a -1 value (what Item.getIDForItem would have returned) in the case where the registry is not aware of the item yet // IT should be noted that -1 will fail the gate further down, if an entry already exists with value -1 for this name. This is what is broken and being warned about. // APPARENTLY it's quite common to do this. OreDictionary should be considered alongside Recipes - you can't make them properly until you've registered with the game. ResourceLocation registryName = stack.getItem().delegate.name(); int id; if (registryName == null) { FMLLog.log(Level.DEBUG, "Attempted to find the oreIDs for an unregistered object (%s). This won't work very well.", stack); return new int[0]; } else { id = GameData.getItemRegistry().getId(registryName); } List ids = stackToId.get(id); if (ids != null) set.addAll(ids); ids = stackToId.get(id | ((stack.getItemDamage() + 1) << 16)); if (ids != null) set.addAll(ids); Integer[] tmp = set.toArray(new Integer[set.size()]); int[] ret = new int[tmp.length]; for (int x = 0; x < tmp.length; x++) ret[x] = tmp[x]; return ret; } /** * Retrieves the ArrayList of items that are registered to this ore type. * Creates the list as empty if it did not exist. * * The returned List is unmodifiable, but will be updated if a new ore * is registered using registerOre * * @param name The ore name, directly calls getOreID * @return An arrayList containing ItemStacks registered for this ore */ public static List getOres(String name) { return getOres(getOreID(name)); } /** * Retrieves the List of items that are registered to this ore type at this instant. * If the flag is TRUE, then it will create the list as empty if it did not exist. * * This option should be used by modders who are doing blanket scans in postInit. * It greatly reduces clutter in the OreDictionary is the responsible and proper * way to use the dictionary in a large number of cases. * * The other function above is utilized in OreRecipe and is required for the * operation of that code. * * @param name The ore name, directly calls getOreID if the flag is TRUE * @param alwaysCreateEntry Flag - should a new entry be created if empty * @return An arraylist containing ItemStacks registered for this ore */ public static List getOres(String name, boolean alwaysCreateEntry) { if (alwaysCreateEntry) { return getOres(getOreID(name)); } return nameToId.get(name) != null ? getOres(getOreID(name)) : EMPTY_LIST; } /** * Returns whether or not an oreName exists in the dictionary. * This function can be used to safely query the Ore Dictionary without * adding needless clutter to the underlying map structure. * * Please use this when possible and appropriate. * * @param name The ore name * @return Whether or not that name is in the Ore Dictionary. */ public static boolean doesOreNameExist(String name) { return nameToId.get(name) != null; } /** * Retrieves a list of all unique ore names that are already registered. * * @return All unique ore names that are currently registered. */ public static String[] getOreNames() { return idToName.toArray(new String[idToName.size()]); } /** * Retrieves the List of items that are registered to this ore type. * Creates the list as empty if it did not exist. * * @param id The ore ID, see getOreID * @return An List containing ItemStacks registered for this ore */ private static List getOres(int id) { return idToStackUn.size() > id ? idToStackUn.get(id) : EMPTY_LIST; } private static boolean containsMatch(boolean strict, ItemStack[] inputs, ItemStack... targets) { for (ItemStack input : inputs) { for (ItemStack target : targets) { if (itemMatches(target, input, strict)) { return true; } } } return false; } public static boolean containsMatch(boolean strict, List inputs, ItemStack... targets) { for (ItemStack input : inputs) { for (ItemStack target : targets) { if (itemMatches(target, input, strict)) { return true; } } } return false; } public static boolean itemMatches(ItemStack target, ItemStack input, boolean strict) { if (input == null && target != null || input != null && target == null) { return false; } return (target.getItem() == input.getItem() && ((target.getItemDamage() == WILDCARD_VALUE && !strict) || target.getItemDamage() == input.getItemDamage())); } //Convenience functions that make for cleaner code mod side. They all drill down to registerOre(String, int, ItemStack) public static void registerOre(String name, Item ore){ registerOre(name, new ItemStack(ore)); } public static void registerOre(String name, Block ore){ registerOre(name, new ItemStack(ore)); } public static void registerOre(String name, ItemStack ore){ registerOreImpl(name, ore); } /** * Registers a ore item into the dictionary. * Raises the registerOre function in all registered handlers. * * @param name The name of the ore * @param ore The ore's ItemStack */ private static void registerOreImpl(String name, ItemStack ore) { if ("Unknown".equals(name)) return; //prevent bad IDs. if (ore == null || ore.getItem() == null) { FMLLog.bigWarning("Invalid registration attempt for an Ore Dictionary item with name %s has occurred. The registration has been denied to prevent crashes. The mod responsible for the registration needs to correct this.", name); return; //prevent bad ItemStacks. } int oreID = getOreID(name); // HACK: use the registry name's ID. It is unique and it knows about substitutions. Fallback to a -1 value (what Item.getIDForItem would have returned) in the case where the registry is not aware of the item yet // IT should be noted that -1 will fail the gate further down, if an entry already exists with value -1 for this name. This is what is broken and being warned about. // APPARENTLY it's quite common to do this. OreDictionary should be considered alongside Recipes - you can't make them properly until you've registered with the game. ResourceLocation registryName = ore.getItem().delegate.name(); int hash; if (registryName == null) { FMLLog.bigWarning("A broken ore dictionary registration with name %s has occurred. It adds an item (type: %s) which is currently unknown to the game registry. This dictionary item can only support a single value when" + " registered with ores like this, and NO I am not going to turn this spam off. Just register your ore dictionary entries after the GameRegistry.\n" + "TO USERS: YES this is a BUG in the mod "+Loader.instance().activeModContainer().getName()+" report it to them!", name, ore.getItem().getClass()); hash = -1; } else { hash = GameData.getItemRegistry().getId(registryName); } if (ore.getItemDamage() != WILDCARD_VALUE) { hash |= ((ore.getItemDamage() + 1) << 16); // +1 so 0 is significant } //Add things to the baked version, and prevent duplicates List ids = stackToId.get(hash); if (ids != null && ids.contains(oreID)) return; if (ids == null) { ids = Lists.newArrayList(); stackToId.put(hash, ids); } ids.add(oreID); //Add to the unbaked version ore = ore.copy(); idToStack.get(oreID).add(ore); MinecraftForge.EVENT_BUS.post(new OreRegisterEvent(name, ore)); } public static class OreRegisterEvent extends Event { private final String Name; private final ItemStack Ore; public OreRegisterEvent(String name, ItemStack ore) { this.Name = name; this.Ore = ore; } public String getName() { return Name; } public ItemStack getOre() { return Ore; } } public static void rebakeMap() { //System.out.println("Baking OreDictionary:"); stackToId.clear(); for (int id = 0; id < idToStack.size(); id++) { List ores = idToStack.get(id); if (ores == null) continue; for (ItemStack ore : ores) { // HACK: use the registry name's ID. It is unique and it knows about substitutions ResourceLocation name = ore.getItem().delegate.name(); int hash; if (name == null) { FMLLog.log(Level.DEBUG, "Defaulting unregistered ore dictionary entry for ore dictionary %s: type %s to -1", getOreName(id), ore.getItem().getClass()); hash = -1; } else { hash = GameData.getItemRegistry().getId(name); } if (ore.getItemDamage() != WILDCARD_VALUE) { hash |= ((ore.getItemDamage() + 1) << 16); // +1 so meta 0 is significant } List ids = stackToId.get(hash); if (ids == null) { ids = Lists.newArrayList(); stackToId.put(hash, ids); } ids.add(id); //System.out.println(id + " " + getOreName(id) + " " + Integer.toHexString(hash) + " " + ore); } } } }