AddMultipleRecipeAction
基本信息
| 属性 | 值 |
|---|---|
| 类 | gttweaker.mods.AddMultipleRecipeAction |
| 继承 | minetweaker.OneWayAction —— 不可撤销 |
| 使用者 | 55 个机器类 + Fuels(全部 addRecipe 方法) |
| 行数 | 204 |
| 核心职责 | 把 IIngredient(可能是矿辞典 / 多个变体)的笛卡尔积展开成多条 GTRecipe |
| 抽象方法 | applySingleRecipe(ArgIterator i) |
为什么需要它
ZenScript 允许写 addRecipe(<ore:ingotIron>, ...)。矿辞典项内部是一整个列表(可能几十个变体),而 GT5 的 GTRecipeBuilder 只接受确定的 ItemStack。本类把「每种输入的变体组合」全部展开,每个组合调用一次 applySingleRecipe。
笛卡尔积展开(extendByMultiple,:41-49)
private static void extendByMultiple(List args, List<List<Object>> recipesData) {
List<List<Object>> originData = fullCopy(recipesData);
recipesData.clear();
for (Object singleArg : args) {
List<List<Object>> tmp = fullCopy(originData);
extendBySingle(singleArg, tmp);
recipesData.addAll(tmp);
}
}
recipesData 是 List<List<Object>> —— 外层每个元素是一条待生成的配方参数序列。extendBySingle 给每条序列追加一个参数;extendByMultiple 复制出「原序列数 × 变体数」条新序列。
参数分类(addArgument,:62-78)
| 参数 Java 类型 | 处理 | 展开结果 |
|---|---|---|
ILiquidStack |
extendBySingle(单值) |
1 条 |
ILiquidStack[] |
extendBySingle(数组整体当一个参数) |
1 条 |
IItemStack |
extendBySingle |
1 条 |
IItemStack[] |
extendBySingle(数组整体当一个参数) |
1 条 |
IIngredient |
extendByMultiple |
× 该原料变体数 |
IIngredient[] |
extendByMultiple(笛卡尔积) |
× 各元素变体数之积 |
| 其他(int / boolean / long / Object) | extendBySingle |
1 条 |
关键区别:IItemStack[] 走 extendBySingle(数组本身是一个参数),IIngredient[] 走 extendByMultiple(每个元素独立展开)。这解释了 BlastFurnace / ArcFurnace 传 IItemStack[] output 时数组被整体保留(靠 nextItemArr() 取出),而 DistillationTower.addUniversalRecipe 传 ILiquidStack[] fluidOutput 也被整体保留(靠 nextFluidArr())。
ArgIterator
protected static class ArgIterator(:133-172)包装 Iterator<Object>,提供 8 个强类型读取方法:
nextItem() / nextItemArr() / nextFluid() / nextFluidArr() / nextInt() / nextIntArr() / nextBool() / nextLong()
全部是无检查的 (T) iterator.next() 强转:类型不匹配即 ClassCastException,序列耗尽即 NoSuchElementException。
原料校验(getItemStacks,:96-116)
| 校验 | 条件 | 抛出 |
|---|---|---|
any 原料 |
items == null |
AnyIngredientException("Can't add 'any' ingredient.") |
| 空原料 | items.size() == 0 |
EmptyIngredientException("Ingredient '...' is empty. Can't add recipe without items.") |
| 负数数量 | amount < 0 |
RuntimeException("Negative amount for ingredient ...") |
| 超堆叠 | amount > stack.getMaxStackSize() |
OutOfStackSizeException("Impossible add ingredient '...' with amount N") |
三个自定义异常位于 gttweaker/util/exception/,均只覆写 toString()。
数值
| 数值 | 值 |
|---|---|
| 使用者数量 | 55 个机器类 + Fuels |
| 抽象方法 | 1(applySingleRecipe) |
ArgIterator 读取方法 |
8 |
| 触发展开的类型 | IIngredient / IIngredient[] |
| 空矩阵初值 | createNewMatrix(n) 预分配容量 n,内含 1 个空 ArrayList |
| 异常捕获 | 构造器 catch (Exception e) → MineTweakerAPI.logError(e.toString()) |
| 可撤销 | 否(OneWayAction) |
getOverrideKey() |
恒 null |
equals / hashCode |
基于 recipesData(不含 description) |
apply() |
对 recipesData 每个元素调一次 applySingleRecipe(new ArgIterator(...)) |
源码缺陷
- 异常只记
e.toString(),丢堆栈(:127):三个自定义异常的toString()被覆写为友好文案,但MineTweakerAPI.logError(e.toString())不带堆栈,无法定位是哪个IIngredient触发。 - 三个异常只覆写
toString()不覆写getMessage():若将来改为logError(e.getMessage())会得到null。且三者均无serialVersionUID。 ArrayHelper.itemOrNull是死代码(util/ArrayHelper.java,9 行):本类全文无itemOrNull调用。createNewMatrix的双括号初始化(:27-32)产生匿名子类,其中initCount只用作容量。功能正确但产生无谓类,且易被静态分析误报。getItemStacks用Arrays.asList(:104)返回固定大小 list。后续只改元素(合法),但任何add/remove会抛UnsupportedOperationException。equals忽略description(:191-198):两条 description 不同但recipesData相同的动作被视为相等,若 MineTweaker 用它做去重键会误判。getOverrideKey()恒null:MineTweaker 的 override 机制依赖该键,恒 null 意味着本 mod 路径无法使用 override 语义(对比 RA2 Builder 同样返回null)。recipesData规模无上限:3 个各 20 变体的IIngredient→ 8000 条List<Object>,构造期即大量内存占用,源码无上限保护或警告。- 构造器吞异常后对象仍被
apply:若addArgument中途抛异常,recipesData处于部分填充状态,而MineTweakerAPI.apply(...)仍被调用(异常在构造器内被 catch),apply()随后按残缺序列调applySingleRecipe,可能产生参数错位的ClassCastException或写入错误配方。
相关条目
- RA2 Builder - 唯一可撤销的对比路径
- 冶炼类机器 - 55 个使用者之一
- 金属与成型类机器 - 55 个使用者之一
- GT++ 机器 - 55 个使用者之一
- 原料转换 -
IIngredient的两条转换路径