news 2026/9/15 22:08:07

Cataclysm-DDA JSON 样式规范与格式化工具实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cataclysm-DDA JSON 样式规范与格式化工具实战指南

Cataclysm-DDA JSON 样式规范与格式化工具实战指南

【免费下载链接】Cataclysm-DDACataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.项目地址: https://gitcode.com/GitHub_Trending/ca/Cataclysm-DDA

Cataclysm-DDA(《大灾变:黑暗之日》)是一款以海量 JSON 数据驱动玩法内容的后末日回合制生存游戏,其data/jsondata/mods目录下沉淀了数千个玩法定义文件。本文以仓库中的 doc/JSON/JSON_STYLE.md 为骨架,系统讲解官方 JSON 排版规范、120 字符换行规则的底层实现,以及自研格式化工具json_formatter的构建、命令行使用与 IDE 集成方案。读完本文,你将能够把任何 CDDA 数据文件一键格式化为符合官方 CI 校验的风格,并在 Visual Studio / VS Code 中搭建自动 lint 工作流。

JSON 样式规范:与 C++ 代码风格同源的工程约束

与 doc/c++/CODE_STYLE.md 中的代码风格策略一致,Cataclysm-DDA 的 JSON 样式策略是:新增或编辑 JSON 时立即按规范格式化;对存量文件则分小批量逐步处理,避免对开发造成过大扰动。这一策略保证了 PR 评审时 diff 干净、可读。

由于团队始终没有找到令人满意的现成 JSON 格式化工具,他们干脆自己写了一个。该工具位于 tools/format/format.cpp,并直接复用游戏自身的 JSON 解析与输出实现 src/json.cpp 来完成解析(parse)与重排(emit)——这意味着格式化器的行为与游戏加载数据时的行为天然一致,不会出现"格式化后游戏读不了"的偏差。发布版本中通常会附带已编译好的json_formatter.cgi(或 Windows 下的json_formatter.exe)。

核心样式规则速览

以下六条规则构成了 CDDA JSON 的全部排版约定:

  1. 缩进为两个空格(对应 src/json.cpp 中JsonOut::write_indent按缩进层级写入indent_level * 2个空格);
  2. 除逗号(,)和冒号(:)外的所有 JSON 定界符两侧都要有空白(空格或换行);
  3. 逗号和冒号后面必须跟空白(冒号后固定为单个空格,见 src/json.cpp 的write_member_separator);
  4. 对象成员之间永远换行分隔
  5. 数组元素在"结果数组总长(含缩进)超过 120 字符"时才换行分隔,否则保持单行紧凑排列;
  6. 换行只能出现在开括号之后、闭括号之后或元素/成员之后

这些规则全部由格式化工具强制执行,因此手写时不必死记,只需让工具重排即可。

官方示例逐段解读

原文档给出的示例几乎覆盖了全部排版特征(缩进、短数组、短对象、长数组换行、嵌套数组换行):

[ { "type": "foo", "id": "example", "short_array": [ 1, 2, 3, 4, 5 ], "short_object": { "item_a": "a", "item_b": "b" }, "long_array": [ "a really long string to illustrate line wrapping, ", "which occurs if the line is longer than 120 characters" ], "nested_array": [ [ [ "item1", "value1" ], [ "item2", "value2" ], [ "item3", "value3" ], [ "item4", "value4" ], [ "item5", "value5" ], [ "item6", "value6" ] ] ] } ]

逐项解读:

  • 顶层:整个文件是一个数组,每个元素通常是一个带typeid字段的对象定义;
  • short_array[ 1, 2, 3, 4, 5 ]整体不足 120 字符,因此保持单行,且[]两侧留白、元素间以,分隔;
  • short_object:对象成员强制换行,因此即使是两个短键值对也写成多行——这是规则 4 的直接体现;
  • long_array:如果按单行拼接,整行将超过 120 字符,于是每个元素独立成行;
  • nested_array:多层嵌套数组逐层缩进两格,内层[ "item1", "value1" ]这类短对保持单行。

120 字符换行规则的源码实现

"先尝试紧凑排列,超长再回退为逐行展开"这一行为,在 tools/format/format.cpp 的format_collection中实现得非常巧妙:

if( depth > 1 && !force_wrap ) { // 先保存输入输出流的位置与状态 int in_start_pos = jsin.tell(); bool ate_separator = jsin.get_ate_separator(); int out_start_pos = jsout.tell(); bool need_separator = jsout.get_need_separator(); write_func( jsin, jsout, depth, false ); // 先按紧凑模式序列化 if( jsout.tell() - out_start_pos <= 120 ) { return; // 未超 120 字符,紧凑方案直接保留 } else { // 回退:恢复输入输出流位置,改为强制换行模式重新输出 jsin.seek( in_start_pos ); jsin.set_ate_separator( ate_separator ); jsout.seek( out_start_pos ); if( need_separator ) { jsout.set_need_separator(); } } } write_func( jsin, jsout, depth, true ); // force_wrap = true

即:先尝试把数组/对象写在同一行,测量输出流增量长度,超过 120 字符就"撤销"并强制换行重写。这正是文档中"数组元素在结果数组超过 120 字符(含缩进)时换行"的精确语义。

此外,write_object(tools/format/format.cpp)对rowsblueprintpicture三个特殊字段做了内省处理:当这些字段对应的数组多于一个元素时,强制其换行展开。这三个字段正是 CDDA 地图生成(mapgen)JSON 中常见的"逐行地图字符画"结构,强制换行能让这类数据始终可读。

格式化主流程formatter::format(tools/format/format.cpp)按类型分派:数组/对象走上述集合逻辑,字符串、数字、布尔、null分别处理。其中有两个值得注意的细节:

  • 字符串:原样保留转义序列,并将不换行空格(U+00A0)替换为\u00A0转义,避免非 ASCII 空白混入数据造成混淆;
  • 数字:整数与浮点分开序列化。浮点输出会去掉多余尾零,同时保证"要么带小数点,要么小数点后补一个 0"(如5.0),从而稳定地区分整数与浮点类型。

格式化工具:获取与构建

获取json_formatter有三种途径:

  1. 发布包:任何正式发布的构建中都附带已编译好的json_formatter.cgi(Linux/跨平台)或json_formatter.exe(Windows);
  2. 源码构建:执行make style-json,Makefile(Makefile)会先编译出tools/format/json_formatter(Windows 下为.exe)再运行校验;CMake 工程同样提供了json_formatter可执行目标,见 tools/format/CMakeLists.txt,它把 tools/format/format.cpp、tools/format/format_main.cpp 与游戏自身的 src/json.cpp 编译在一起;
  3. Web 工具:仓库自带的 tools/format/format.html 是一个"粘贴 JSON → 点击 Lint → 回填格式化结果"的网页工具,前端通过 POST 把data参数发送给json_formatter.cgi(10 秒超时,分别处理 200/304/400 响应)。

官方建议将工具路径加入PATH环境变量;如果尚未加入,也可以把它放到仓库根目录,方便在任何子目录调用。

命令行使用方式

工具支持单文件格式化、标准输入(stdin)与 CGI 三种运行模式(见 tools/format/format_main.cpp):

# 格式化单个文件 path/to/json_formatter path/to/file/to/format # 不带参数时从标准输入读取,格式化结果写到标准输出 cat data/json/example.json | path/to/json_formatter # 便捷做法:把 json_formatter 放到固定位置并配置 shell 别名/缩写, # 这样在任意目录下都能直接调用

Windows 用户还可以json_formatter.exe拖放到目标文件上,即可一键格式化该文件。

工具的行为逻辑(tools/format/format_main.cpp)值得特别注意:

  • 若文件已符合规范:直接以退出码 0结束,不触碰文件;
  • 若文件需要重排:就地覆写文件,输出Has been linted : <文件名>Please read doc/JSON/JSON_STYLE.md,并以非零退出码结束。

因此它既是格式化器,也是一个可嵌入 CI 的 lint 检查器——非零退出码即表示"此文件未通过样式校验"。

配合 git 与 find 的批量用法(原文档给出的命令,全部保留):

# 用 git 过滤出有未提交改动的 JSON 文件(要求文件/目录名不含空格) git diff --name-only '*.json' | xargs -P 0 -L 1 json_formatter # 用 git 过滤出当前分支相对 master 修改过的 JSON 文件 git diff master --name-only '*.json' | xargs -P 0 -L 1 json_formatter # 按目录批量格式化 find path/to/desired/folder -name "*.json" -print0 | xargs -P 0 -0 -L 1 json_formatter

xargs -P 0表示按 CPU 数量并行执行,格式化数千个 JSON 文件也能在很短时间内完成。

此外,Makefile 还提供了两个更省心的目标(Makefile):

# 格式化 data 目录下全部 JSON(串行) make style-all-json # 并行版,按 CPU 核数加速 make style-all-json-parallel

make style-json只校验/格式化"JSON 校验测试覆盖的文件"(即data目录下的全部*.json),并且已被纳入make checksCHECKS列表(见 Makefile),因此本地跑完整检查时会自动包含 JSON 样式校验。

底层实现:JsonOut 的缩进、分隔与转义

格式化输出的排版细节全部由JsonOut控制,位于 src/json.cpp:

  • 缩进write_indent每级写入indent_level * 2个空格(src/json.cpp);
  • 逗号与换行write_separator(src/json.cpp)先写逗号,然后依据"顶层(indent_level < 2)或处于强制换行容器内"决定补换行还是补空格——这正是"短数组单行、长数组多行"统一由need_wrap栈驱动的实现;
  • 冒号write_member_separator固定输出": "(src/json.cpp);
  • 括号start_pretty/end_pretty(src/json.cpp)在打开/关闭数组或对象时决定换行与缩进,其中"退出包含对象的数组时强制换行"是特殊分支;
  • 字符串转义JsonOut::write(src/json.cpp)对引号、反斜杠、控制字符等做标准 JSON 转义,小于 0x20 的字符统一输出为\u00xx形式。

正是这套实现保证了格式化输出既是合法 JSON,又严格符合上文六条排版规则。

Visual Studio 集成:构建即校验

原文档针对 Windows 开发者给出了完整的 Visual Studio 配置方案,分为三步:

第 1 步:构建 JsonFormatter 工程。在 VS 解决方案中构建整个解决方案或仅构建JsonFormatter工程,会生成tools/format/json_formatter.exe

第 2 步:启用自动 lint。定义一个 MSBuild 变量CDDA_POST_BUILD_JSON_LINT(类似于在 VS 中配置 ccache 的做法)。最简单的方式是在仓库根目录创建Directory.Build.props

<Project> <PropertyGroup> <CDDA_POST_BUILD_JSON_LINT>true</CDDA_POST_BUILD_JSON_LINT> </PropertyGroup> </Project>

配置成功后,构建或运行解决方案时会自动触发 linter,OutputError List窗口会列出不合规的文件。其背后实际执行的是 msvc-full-features/style-json.ps1:该脚本通过git diff --name-only '*.json'及多个上游分支的 merge-base 探测出改动文件,逐个调用json_formatter.exe;从 MSBuild 调用时会把工具输出解析成文件(行,列) : lint warning cddalint01 : 消息的 VS 错误日志格式,并设置了 30 秒超时保护,避免长时间阻塞构建。

第 3 步:添加手动 lint 菜单项。通过Tools>External Tools..>Add添加外部工具:

  • Title:Lint All JSON
  • Command:C:\windows\system32\windowspowershell\v1.0\powershell.exe
  • Arguments:-file $(SolutionDir)\style-json.ps1
  • Initial Directory:$(SolutionDir)
  • Use Output window:勾选

之后即可通过Tools>Lint All JSON菜单手动触发;若想绑定快捷键,可到Tools>Options>Environment>Keyboard搜索Tools.ExternalCommand,找到对应位置的命令(例如列表第一项Tools.ExternalCommand1)并分配快捷键。

Visual Studio Code 集成

在 VS Code 中安装项目推荐的扩展(cdda-toys扩展集)后,会获得cdda-toys.cdda-json-formatter,它对 JSON 文件提供"保存即自动格式化"的能力,无需手动配置命令行参数。

小结

CDDA 的 JSON 样式体系可以概括为"一个自研工具 + 六条排版规则 + 多套集成入口":规则简洁(两空格缩进、定界符留白、对象换行、数组按 120 字符智能换行),工具可靠(复用游戏自身的json.cpp解析器,保证格式与加载行为一致),入口丰富(命令行、git/find 批量管道、Makefile 目标、VS 构建钩子、VS Code 扩展、网页工具)。

如需进一步深入,可继续阅读:

  • 规范原文:doc/JSON/JSON_STYLE.md
  • 格式化器实现:tools/format/format.cpp、tools/format/format_main.cpp
  • 输出排版底层:src/json.cpp
  • Makefile 相关目标:Makefile
  • Windows lint 脚本:msvc-full-features/style-json.ps1
  • C++ 侧代码风格约定:doc/c++/CODE_STYLE.md

【免费下载链接】Cataclysm-DDACataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.项目地址: https://gitcode.com/GitHub_Trending/ca/Cataclysm-DDA

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 22:04:41

C# WinForm批量图片压缩到指定大小:原理与实现

简介&#xff1a;一款基于C# WinForm开发的批量图片压缩工具&#xff0c;支持将图片精确压缩到指定大小&#xff08;KB&#xff09;&#xff0c;并提供完整源码与可直接运行的exe文件。资源包共2000个文件&#xff0c;约62.65MB&#xff0c;主要包含cs工程源码、dll依赖库、xml…

作者头像 李华
网站建设 2026/9/15 21:59:41

数据工程版本控制全攻略:代码、数据与Schema管理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华