GT 配方表遍历

getGregtechRecipes() 是 RecEx 里最大的一块逻辑:它把 GregTech API 生态下所有公开 RecipeMap 的配方全部抓出来,并按机器分组后写成 type: "gregtech" 那一段。

遍历的 5 个 RecipeMap 容器类

类被硬编码在 Arrays.asList(...) 里:

顺序 类
1 gregtech.api.recipe.RecipeMaps
2 gtPlusPlus.api.recipe.GTPPRecipeMaps
3 bartworks.API.recipe.BartWorksRecipeMaps
4 goodgenerator.api.recipe.GoodGeneratorRecipeMaps
5 tectech.recipe.TecTechRecipeMaps

即 GregTech 5U 本体 + GT++ + BartWorks + Good Generator + TecTech。

反射取 map 的方式

for (Class<?> recipeMapClass : recipeMapClasses) {
    for (Field field : recipeMapClass.getDeclaredFields()) {
        if (field.getType() == RecipeMap.class) {
            maps.add((RecipeMap<RecipeMapBackend>) field.get(null));
        }
    }
}

三个特点:

  • 用 getDeclaredFields()——只扫这些类自身声明的字段,不扫父类或接口继承来的
  • 筛选条件是 field.getType() == RecipeMap.class 的精确裸类型相等。泛型参数(RecipeMap<Fluid> 之类)不影响判定,因为 Java 泛型擦除后 getType() 就是 RecipeMap;但若某 map 字段被声明成某个 RecipeMap 的子类型或别名类型,就会被漏掉
  • field.get(null) 直接取静态字段值;失败(IllegalArgumentException / IllegalAccessException)只 printStackTrace() 后继续下一个字段,不中断导出

⚠️ 这 5 个类都是编译期硬依赖。RecipeExporter.java 顶部直接 import 它们的全限定名(bartworks.API.recipe.BartWorksRecipeMaps 等),没有 Loader.isModLoaded 之类的可选加载判断——任何一个 mod 缺失,RecipeExporter 类在加载时就会 NoClassDefFoundError,run() 整体失败。也就是说 RecEx 实际上无法单独运行,必须装齐 GT5U 及其这几个附属。

dependencies.gradle 里只显式声明了 GT5-Unofficial,这 5 个附属随 GT5U 一同提供。

分组规则

每个 map 生成一个 GregtechMachine 对象:

字段 类型 内容
n String 机器名
recs List<GregtechRecipe> 该 map 的全部配方

n 的取值:StatCollector.translateToLocal(map.unlocalizedName),若为 null 或空串则回退到 map.unlocalizedName 原值。

分组是为压缩体积服务的。RecipeExporter 的 Javadoc 解释:

Unlike vanilla recipes, the current schema here groups recipes from each machine together. This is a minor file size improvement. Rather than specifying the machine’s name in every recipe, the machine name is only listed once for the entire file.

即:机器名只写一次,而不是每条配方重复一次。同一段注释还说明这种格式不阻碍 NEP(NotEnoughProduction)加载。

每个 map 处理时打两条 INFO 日志:"Processing recipe map " + 机器名 与 "Finished sorting recipes for map " + 机器名。

配方内容的转换

每条 GTRecipe 先经 cloneAndSort 深拷贝并排序(COMPARE_RECIPE),再逐字段搬进 GregtechRecipe:

GTRecipe 字段 导出字段 处理
mEnabled en 直接赋值
mDuration dur 直接赋值
mEUt eut 直接赋值
mSpecialValue sp != 0 时赋值(但 Gson 仍总会写出该键)
mInputs iI clean() 去 null + 排序 + 逐个 formatGregtechItemStack
mOutputs iO 同上
mFluidInputs fI clean() 去 null + 排序 + formatGregtechFluidStack
mFluidOutputs fO 同上

与 GT 顺序分支不同,这里有 if (item == null) continue; 守卫(clean() 已提前剔除 null,实际是双保险)。

⚠️ 被复制但未被导出的 GT 字段:mSpecialItems、mInputChances、mNeedsEmptyOutput、isNBTSensitive、mCanBeBuffered、mFakeRecipe、mHidden。其中 mHidden / mFakeRecipe 的后果最实际——隐藏配方和伪配方会被正常导出且不带任何标记,消费者只能靠 en(对应 mEnabled)过滤。详见 导出 JSON 格式。

cloneAndSort 顺带把 mInputs/mOutputs/mFluidInputs/mFluidOutputs 换成 clean() 后的紧凑数组,所以导出文件里 GT 配方的材料列表已排序且无空位——与 shaped 保留空槽的做法不同(见 配方类型对比)。

相关条目