原料转换
GTTweaker 提供两条静态转换方法,把 CraftTweaker 的 IIngredient 转成 GregTech 5 需要的 ItemStack / FluidStack。全 mod 的 55 台机器类与 RA2 Builder、Recipe Remover 都间接依赖这两条路径。
基本信息
| 属性 | 值 |
|---|---|
| 类 | gttweaker.GTTweaker |
| 方法 1 | public static ItemStack getItemStackOrNull(IIngredient ingredient) |
| 方法 2 | public static FluidStack getFluidStackOrNull(IIngredient ingredient) |
| 行数 | 20(GTTweaker.java:146-159) |
| 使用者 | 56 个类通过方法引用(GTTweaker::getItemStackOrNull)间接调用 |
getItemStackOrNull(:146-153)
Object internal = ingredient.getInternal();
if (internal instanceof ItemStack) return (ItemStack) internal;
else if (internal instanceof String)
return OreDictionary.getOres((String) internal).size() > 0
? OreDictionary.getOres((String) internal).get(0) : null;
return null;
internal 类型 |
返回 | 风险 |
|---|---|---|
ItemStack |
原对象(同一引用,未 copy) | 调用方若修改会污染原 ingredient |
String(矿辞典名) |
OreDictionary.getOres(name).get(0) —— 静默取第一个 |
见下 |
其他(如 FluidStack、List<IItemStack>) |
null |
静默 |
getFluidStackOrNull(:155-159)
Object internal = ingredient.getInternal();
if (internal instanceof FluidStack) return (FluidStack) internal;
return null;
不处理矿辞典:流体在 CraftTweaker 中本就没有 ore dict 形式。IIngredient 若内部是 ILiquidStack 会被 AddMultipleRecipeAction 提前转成 FluidStack 再存入,本方法只处理这一路径。
三种失败模式
| 模式 | 触发条件 | 后果 |
|---|---|---|
| 静默取第一个 | 矿辞典含多个变体 | 配方只用了某一个具体物品,其余变体(可能是更便宜的替代)被无视,无日志 |
返回 null 被 filter 掉 |
矿辞典为空 / 类型不匹配 | 参数数量减少 → 见 Recipe Remover 的过度匹配风险;机器类中则可能 itemInputs(null) NPE |
| 同一引用 | internal instanceof ItemStack |
调用方 recipeBuilder.itemInputs(stack) 若持有引用,后续修改会影响原 ingredient |
数值
| 数值 | 值 |
|---|---|
| 静态转换方法数 | 2 |
支持的 internal 类型 |
ItemStack / String(物品);FluidStack(流体) |
| 矿辞典取法 | getOres(name).get(0) —— 第一个,无排序保证 |
| 空矿辞典 | 返回 null |
| 返回值是否 copy | 否(ItemStack 分支直接返回原引用) |
| 失败时是否有日志 | 否(全部静默) |
| 流体的矿辞典支持 | 无 |
源码缺陷
- 矿辞典静默取第一个变体(
:150-151):OreDictionary.getOres(name).get(0)。矿辞典的注册顺序由各 mod 的加载顺序决定,因此同一脚本在不同 mod 组合下可能生成不同的配方。这在打包整合包中是难以排查的问题。源码无注释、无日志。 - 调用两次
OreDictionary.getOres(:150与:151):getOres每次返回一个新的ArrayList(1.7.10 行为),因此有一次多余的列表分配 + 完整拷贝。size() > 0与get(0)应合并为一次调用。 - 返回
null后由调用方filter(nonNull)静默丢弃:RA2Builder的 4 个数组方法与RecipeRemover.remove都用.filter(Objects::nonNull)。后果分两种:RA2Builder.itemInputs([<不存在的ore>])→ 传入空数组给 builder(不是 null),可能写出无输入的配方。RecipeRemover.remove(map, [<不存在的ore>], [])→ 查询条件被削弱,可能删掉一条与预期完全不同的配方。
ItemStack分支不 copy:return (ItemStack) internal;。GTRecipeBuilder.itemInputs在 GT5 中会保存引用。CraftTweaker 的IItemStack内部ItemStack通常是脚本创建的新对象,但若来自IIngredient的缓存可能被复用。- 无
@Nullable/ 无异常:两条路径都靠返回null表达失败,与AddMultipleRecipeAction里三个抛异常的校验风格相反(同仓库两种失败表达方式并存)。 - 方法名
OrNull表明作者知晓会返回 null,但没有任何调用方检查 null(除了filter)。 getFluidStackOrNull不处理List<FluidStack>:CraftTweaker 的ILiquidStack内部可能是列表(当<ore:...>类写法产生多流体时),本方法直接返回null,与物品侧的矿辞典支持不对称。- 两条方法放在
@Mod主类而非工具类:它们是通用转换逻辑,却绑在模组入口类GTTweaker上,测试与复用不便。对比util/ArrayHelper有独立工具类但反而是死代码。
相关条目
- AddMultipleRecipeAction - 另一条失败表达方式(抛异常)
- RA2 Builder -
IIngredient[]→ItemStack[]的唯一调用方 - Recipe Remover -
filter(nonNull)导致的过度匹配 - OreDict greggedItem - 矿辞典的另一种(更弱的)解析