credits.json 数据文件

基本信息

属性 值
资源位置 assets/gtnhcredits/credits.json,即 ResourceLocation gtnhcredits:credits.json(CreditsRepository.java:24;路径常量 CreditsLayout.ROOT / CreditsLayout.CREDITS)
实例级覆盖路径 config/gtnh-credits/credits.json(Config.java:36)
覆盖规则 覆盖文件 isFile() 为真时完全取代打包内的资源(CreditsRepository.java:31-37),不做合并
加载类 net.noiraude.gtnhcredits.repository.CreditsRepository(package-private,final)
解析器 net.noiraude.libcredits.parser.CreditsParser(libCredits 子模块)
JSON Schema credits.schema.json(仓库根目录),文档 credits.schema.md 由 ./gradlew generateCreditsSchemaDoc 生成
打包内文件体积 12 个分类 + 76 条人员记录(见下方「打包内示例数据」)

加载失败的行为

两种失败都被吞掉并降级为空文档(CreditsRepository.java:39-45):

异常 触发条件 日志
IOException 文件读不出来 Failed to load credits from {路径},附异常堆栈
CreditsParseException JSON 结构非法(key 不合法、分类项既非字符串也非对象等) Credits data from {路径} is invalid: {原因}

界面照常打开,只是分类列表为空。本 mod 不崩溃、不弹窗。

格式

{
  "version": 2,
  "category": [
    { "id": "dev", "class": ["detail", "person", "role"] }
  ],
  "person": [
    { "name": "Alice", "category": "dev" },
    { "name": "Bob",   "category": { "dev": ["core-dev", "backend"] } }
  ]
}
字段 必填 类型 说明
version 是 integer schema 约束「必须 ≥ 2」。运行期解析器不读取、不校验该字段 —— 全仓库对 "version" 的字面量引用只有 CreditsSerializer.java:42 写出时的 root.addProperty("version", 2)
category[].id 是 string 分类稳定标识,同时作为翻译键后缀与无翻译时的显示回退文本
category[].class 否 string 或 string[] 语义标记:person / role / detail。数组内元素需唯一(credits.schema.json:67 的 uniqueItems);运行期解析器用 LinkedHashSet 接收,重复项静默合并而非报错(CreditsParser.java:79-80)。未知值静默忽略
person[].name 是 string 显示名,schema 约束 1–80 个可打印 UTF-8 字符且不含控制字符
person[].category 是 string / object / array 该人员的分类归属,一条记录内不得重复同一分类 id

person[].category 的三种写法:

写法 含义
"dev" 归属 dev,无角色
{"dev": "core-dev"} 归属 dev,角色 core-dev
{"dev": ["a", "b"]} 归属 dev,两个角色
["dev", {"ops": "sre"}] 同时归属两个分类

键的合法性校验

分类 id 与角色键在运行期由正则 ^[A-Za-z]([A-Za-z0-9 ._-]*[A-Za-z0-9])?$ 校验(CreditsParser.java:30,126-131):必须以字母开头,只能含字母、数字、空格、.、_、-,且必须以字母或数字结尾;schema 另加长度 1–32 的限制。违反则抛 CreditsParseException("invalid category id: \"...\"") / ("invalid role: \"...\"")。

⚠️ person[].name 在运行期完全不做校验 —— parsePerson 直接 obj.get("name").getAsString() 取值(CreditsParser.java:86-89),1–80 字符、无控制字符这些约束只存在于 credits.schema.json(:108-109)中,由构建期测试把关。

⚠️ 分类 id 唯一性、人员的分类引用有效性同样只在构建期校验。实现这两条规则的 CreditsValidator 位于 src/test/java/,只在 ./gradlew test 时运行(CreditsJsonValidationTest.bundledCreditsJsonIsValid);运行期 CreditsParser 不做这两项检查。schema 文档里标注的「build-enforced」即指此。

排序与去重

  • 解析后排序:CreditsParser 解析完成后按去掉 § 颜色码后的名字做不区分大小写的字母序排序(CreditsParser.java:66)。
  • 界面内去重:同一分类下多条同名记录会合并,角色取并集并保留首次出现顺序,合并键同样先剥掉颜色码(CreditsController.java:107-128)。
  • 索引越界保护:getSelectedCategory() 会把索引钳到 size - 1(CreditsController.java:97);分类为空时返回 null,界面正文不渲染任何内容(CreditsContentRenderer.java:58)。

打包内示例数据

src/main/resources/assets/gtnhcredits/credits.json(version: 2)是一份演示 / 测试夹具,不是真实名单 —— 含 loremipsum、Key Test、No-Trans Cat、Empty View、Indexed Detail、NewCat 等测试用分类。

分类 id class 人员条目数
Mod authors detail, person, role 43
contrib detail, person, role 19
support detail, person 7
dev detail, person, role 5
team detail, person, role 2
Key Test detail, person, role 2
No-Trans Cat detail, person 1
thanks detail 0
loremipsum detail 0
Empty View detail, person 0
Indexed Detail detail 0
NewCat (无 class 字段) 0

共 12 个分类、76 条人员记录、43 个不同角色键。人名包括 Ada Lovelace、Alan Turing、Albert Einstein 等示例人物。

真实的 GTNH 名单由整合包通过 config/gtnh-credits/credits.json 覆盖提供;en_US.lang 里已备好 credits.category.project_leadership、credits.category.original_mod_authors、credits.category.mod_development 等正式分类的翻译键,但这些分类并不在打包内的 credits.json 里。

编辑方式

  • 官方推荐用同仓库的独立 Swing 桌面工具 —— 见 GTNH Credits 编辑器。
  • 也可用资源包覆盖 assets/gtnhcredits/credits.json:走标准资源管理器加载,因此资源包能替换打包内文件(README.md 「Resource files」节)。
  • 实例级覆盖 config/gtnh-credits/credits.json 优先于资源包,因为 CreditsRepository 先判断该文件是否存在,存在就直接 FileInputStream 读取,不经过资源管理器(CreditsRepository.java:31-37)。

相关条目