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 形状数组。