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 或写入错误配方。

相关条目