MetaItem 物品键编码

把 ItemStack 压成一个 int,用作 map 键 / 集合元素。配合 Trove 的 TInt* 集合,可在合成与配方查表里避免 ItemStack.equals + NBT 深度比较的开销。

基本信息

属性 值
全限定名 wanion.lib.common.MetaItem
类型 public final class,构造私有,全 static
物品注册表 public static final FMLControlledNamespacedRegistry<Item> itemRegistry = GameData.getItemRegistry();
产出类型 int / int[] / Trove TIntList / TIntSet / TIntIntMap / TIntObjectMap / TIntLongMap
失败表示 0(ItemStack 为 null、物品为 null、或 itemRegistry 里 id ≤ 0)

键值位布局

位段 内容 备注
低 16 位 itemRegistry 的物品 id get(ItemStack) 走 getId(item);get(Item) 走 getIDForObject(item)
第 16 位(65536 / 0x10000) 物品存在标志 get(Item) 恒置位;get(ItemStack) 仅在带 damage 时置位
第 17~31 位 item.getDamage(itemStack) + 1 即 key >> 16

判定 WILDCARD_VALUE(OreDictionary.WILDCARD_VALUE = 32767)的 ItemStack 时只返回裸 id,不置标志位、不带 damage —— 因此矿辞通配键与具体 metadata 键不会撞车。

编码 / 解码

成员 签名 行为
编码(有栈) public static int get(ItemStack itemStack) itemStack 或 item 为 null → 0;id <= 0 → 0;damage 为 WILDCARD_VALUE → 返回 id;否则 id | ((damage + 1) << 16)
编码(仅物品) public static int get(Item item) item 为 null → 0;id <= 0 → 0;否则 id | 65536
解码 public static ItemStack toItemStack(int metaItemKey) metaItemKey > 0 时 new ItemStack(itemRegistry.getRaw(key ^ (key & 65536)), 0, key >> 16);否则 null

⚠️ 源码级 off-by-one:get(ItemStack) 写入高 16 位的是 damage + 1,而 toItemStack 把 key >> 16 原样当作物品 damage 传进 ItemStack 构造,没有减回 1。故 toItemStack(get(stack)) 得到的栈 damage 比原栈大 1。上界截断到 16 位(damage + 1 ≥ 65536 会溢出高 16 位)。库内没有任何代码做 get→toItemStack 往返,故此偏差在库自身范围内不显现。

⚠️ itemRegistry.getRaw(...) 取的是未反混淆名的条目;FMLControlledNamespacedRegistry 的 getRaw 行为与 getIDForObject 对应,正常运行期可用。

集合化

成员 签名 行为
转数组 public static int[] getArray(Collection<ItemStack> itemStackCollection) getList(...).toArray()
转列表 public static TIntList getList(Collection<ItemStack> itemStackCollection) 逐个 get(itemStack),跳过 0(不会把失败项塞进列表)
转集合 public static TIntSet getSet(Collection<ItemStack> itemStackCollection) TIntHashSet 包装 getList(...),天然去重
填 map public static <E> void populateMap(Collection<ItemStack> itemStackCollection, TIntObjectMap<E> map, E defaultValue) 每个键 put(id, defaultValue)
填 map public static void populateMap(Collection<ItemStack> itemStackCollection, TIntLongMap map, long defaultValue) 同上,long 值重载

数组区间统计

两者都遍历 [startIndex, endIndex),都跳过 itemStacks[i] == null,都返回 TIntIntHashMap(键 = meta 键,值 = 数量)。

成员 签名 累加的量 典型场景
按个数 public static TIntIntMap getKeySizeMap(int startIndex, int endIndex, @Nonnull ItemStack[] itemStacks) 出现次数,每次 +1 配方里同种物品的槽位数
按数量 public static TIntIntMap getSmartKeySizeMap(int startIndex, int endIndex, @Nonnull ItemStack[] itemStacks) itemStack.stackSize 累加 配方里同种物品的总件数

⚠️ 若数组中存在 get() 返回 0 的条目(如未注册物品),0 会作为键被统计进去 —— 两个方法都不过滤 0(getList 才过滤)。

在本库内的使用点

配方形状转换 的 rawShapeToShape 三个重载都用 MetaItem.get((ItemStack) objects[index]) 把 ItemStack 转成 Integer 键,再借 Map<Integer, ItemStack> keyStackMap 反查回原栈放进 Forge IRecipe 形状数组。

相关条目