导出 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 |
以源码为准,字段定义见 成分类型。