导出 JSON 格式

导出物是 Gson 序列化的压缩 JSON。README.md 说明选择压缩格式是为了"keys are limited to only a few characters, and all whitespace/newlines are removed"。

序列化设置

Gson gson = (new GsonBuilder()).create();
saveData(gson.toJson(root));

GsonBuilder 没有调用任何 set* 配置(无缩进、无 serializeNulls、无命名策略、无排除策略)。因此:

  • 输出是单行、无任何空白的压缩 JSON
  • null 字段被省略——这是"NBT 存在性"的表达方式,详见 成分类型
  • 字段名就是 Java 字段名的原样(无驼峰转换)
  • 基本类型字段(int / boolean)始终出现,包括值为 0 / false 时

顶层结构

emitJson 构造 Hashtable<String, Object> root,只放一个键 sources,值是 5 个定长数组元素:

{
  "sources": [
    { "type": "gregtech",     "machines": [ ... ] },
    { "type": "shaped",       "recipes":  [ ... ] },
    { "type": "shapeless",    "recipes":  [ ... ] },
    { "type": "shapedOreDict","recipes":  [ ... ] },
    { "type": "smelting",     "recipes":  [ ... ] }
  ]
}
顺序 type 载荷键 配方条目类型
1 gregtech machines 机器分组对象
2 shaped recipes 有序合成
3 shapeless recipes 无序合成
4 shapedOreDict recipes 矿物词典有序合成
5 smelting recipes 熔炼

顺序由 emitJson 里的 5 次 temp.put 固定写死,不随数据量变化。只有 gregtech 用的载荷键是 machines(因为它装的是机器分组而非配方列表),其余四类都是 recipes。

RecipeExporter 的类注释给出一条维护约定:

Schema for existing recipe sources should not be radically changed unless truly necessary. Adding additional data is acceptable however.

各 type 的字段

shaped / shapeless / shapedOreDict

键 类型 说明
iI 数组 输入成分
o 对象 输出成分

三者结构相同,差别在 iI 元素类型与是否保留空槽,见 配方类型对比。

smelting

键 类型 说明
input 对象 原料
output 对象 产物

唯一不缩写的字段名:FurnaceRecipe 是 Java record FurnaceRecipe(Item input, Item output),Gson 反射序列化 record 的实例字段,键就是组件名 input / output。其余所有类型的字段都缩写成 1~2 个字母。解析 sources 时需要按 type 分别处理。

FurnaceRecipe 带 @Desugar(com.github.bsideup.jabel)——用 record 语法写、按 Java 8 字节码发布(gradle.properties 的 enableModernJavaSyntax = jabel)。

gregtech

两层嵌套,按机器分组:

键 类型 说明
n 字符串 机器名
recs 数组 该机器的配方

配方内:

键 类型 说明
en 布尔 配方是否启用(mEnabled)
dur 整数 持续时间(mDuration)
eut 整数 EU/t(mEUt)
sp 整数 特殊值(mSpecialValue)
iI 数组 物品输入
iO 数组 物品输出
fI 数组 流体输入
fO 数组 流体输出

⚠️ sp 的条件赋值不影响输出。源码写的是 if (rec.mSpecialValue != 0) gtr.sp = rec.mSpecialValue;,但 sp 是 int 基本类型字段,Gson 不会省略值为 0 的基本类型字段——所以 sp 在每条 GT 配方里始终存在,mSpecialValue 为 0 时就是 0。

⚠️ sp 之外还有大量 GT 配方字段不被导出。cloneAndSort 复制了 mSpecialItems、mInputChances、mNeedsEmptyOutput、isNBTSensitive、mCanBeBuffered、mFakeRecipe、mHidden,但导出循环只读 mEnabled / mDuration / mEUt / mSpecialValue 与四组输入输出。后果是:

  • 隐藏配方(mHidden)与伪配方(mFakeRecipe)照样被导出,只是不带标记——en 记录的是 mEnabled,与 mHidden 无关。
  • 概率输入 mInputChances 丢失。
  • 存档/缓冲相关开关 mCanBeBuffered、isNBTSensitive、mNeedsEmptyOutput 丢失。

机器名 n 优先取 StatCollector.translateToLocal(map.unlocalizedName),若结果为 null 或空串则回退到 map.unlocalizedName 本身。分组依据见 GT 配方表遍历。

schema.json 是过时的

仓库根目录有 schema.json,但它与当前代码不一致,不要拿它当格式权威:

差异 schema.json 当前代码
sources 条目数 4(无 smelting) 5
物品/流体唯一标识键 uN id
多出的类型 ItemProgrammedCircuit(含 cfg 字段) 不存在该类
Item 字段 a / uN / lN a / m / id / lN / nbt

以源码为准,字段定义见 成分类型。

相关条目