13 种标签类型
| Id | 标签 | 负载 | SNBT |
| 0 | TAG_End | 无,用于关闭复合标签 | — |
| 1 | TAG_Byte | 1 字节,有符号 −128…127 | 1b |
| 2 | TAG_Short | 2 字节,有符号 | 1s |
| 3 | TAG_Int | 4 字节,有符号 | 1 |
| 4 | TAG_Long | 8 字节,有符号 | 1L |
| 5 | TAG_Float | 4 字节,IEEE 754 | 1.0f |
| 6 | TAG_Double | 8 字节,IEEE 754 | 1.0d |
| 7 | TAG_Byte_Array | int 长度,随后为相应数量的字节 | [B;1b,2b] |
| 8 | TAG_String | unsigned short 长度,随后为 UTF-8 字节 | "text" |
| 9 | TAG_List | 元素类型字节、int 长度,再接无名称的负载 | [1,2] |
| 10 | TAG_Compound | 重复类型 + 名称 + 负载,直到 TAG_End | {a:1} |
| 11 | TAG_Int_Array | int 长度,随后为相应数量的 4 字节 int | [I;1,2] |
| 12 | TAG_Long_Array | int 长度,随后为相应数量的 8 字节 long | [L;1L,2L] |
一个文件只包含一个根 TAG_Compound:先是一字节类型 0x0A 和名称,然后是复合标签的内容。具名标签只会出现在复合标签内;列表元素仅包含负载,因此列表必须由同一种类型的元素组成。
实际使用的五种编码
| 编码 | 整数 | 字符串长度 | 根标签 | 使用位置 |
| Java(经典) | 固定宽度大端序 | 2 字节大端序 | 具名 | Java 世界、结构和原理图 |
| Java 网络格式(1.20.2+) | 固定宽度大端序 | 2 字节大端序 | 无名称 | Java 协议 |
| 基岩版 | 固定宽度小端序 | 2 字节小端序 | 具名 | 基岩版世界、.mcstructure、LevelDB 值 |
基岩版 level.dat | 固定宽度小端序 | 2 字节小端序 | 具名,位于 8 字节文件头之后 | 基岩版世界元数据 |
| 基岩版网络格式 | int 和 long 使用 zigzag varint | 无符号 varint | 具名 | 基岩版协议 |
在 varint 编码中,TAG_Short、TAG_Float 和 TAG_Double 仍是固定宽度小端序;只有 TAG_Int 和 TAG_Long 变为 zigzag varint,数组和列表长度也采用 TAG_Int 对应的编码。字符串长度使用无符号 varint,因此不再受固定宽度编码的 65535 字节上限约束。
字符串:modified UTF-8 与 UTF-8
Java 使用 DataOutputStream.writeUTF 序列化字符串,即 修订版 UTF-8:NUL 字符写为 C0 80 而不是 00,基本多文种平面以外的字符写为两个三字节代理项(CESU-8),而不是一个四字节序列。基岩版使用标准 UTF-8。如果工具对两者都假定同一种编码,保存时会损坏表情符号和部分中日韩文字。本编辑器按格式选择编码,因此含表情符号的世界名称往返保存后仍保持不变。
压缩
NBT 本身不压缩,是否压缩由容器决定。Java 文件通常使用 gzip(魔数 1F 8B),区域文件内的区块负载使用 zlib(魔数 78 01、78 9C 或 78 DA),基岩版则以未压缩形式保存 level.dat 和结构文件。格式通过魔数字节检测,因此同一解析器可处理这三种情况。使用错误压缩方式保存文件,是手动编辑后世界无法加载的最常见原因。
逐字节分析最小文件
标准示例 hello_world.nbt:名为 hello world 的根复合标签,其中包含一个名为 name、值为 Bananrama 的字符串标签。采用 Java 大端序编码时:
0A TAG_Compound
00 0B 68 65 6C 6C 6F 20 77 6F 72 6C 64 name length 11, "hello world"
08 TAG_String
00 04 6E 61 6D 65 name length 4, "name"
00 09 42 61 6E 61 6E 72 61 6D 61 value length 9, "Bananrama"
00 TAG_End
同一文档使用基岩版小端序编码时,仅两个长度字段的字节顺序不同:使用 0B 00 而不是 00 0B。在基岩版网络 NBT 中,长度会变成单字节 varint,即 0B 和 04,完全没有填充。将三种文件中的任意一个拖入上方编辑器,它会报告收到的格式。
SNBT
SNBT 是 NBT 的文本写法,也是命令使用的格式:/data merge entity @s {Invulnerable:1b}。类型后缀使其能够无损表示数据:b 表示 byte,s 表示 short,L 表示 long,f 表示 float,d 表示 double,无后缀表示 int;带类型的数组字面量写为 [B;…]、[I;…]、[L;…]。编辑器可从任何已加载文件导出 SNBT,使两个世界版本能够用普通文本差异工具比较。
常见问题
有官方 NBT 规范吗?
Mojang 没有发布正式规范。Notch 在 2010 年记录了该格式,此后一直由社区维护;本页列出的标签 id 和布局与游戏当前读写的内容一致。
为什么基岩版使用小端序?
基岩版引擎使用 C++ 并以小端序硬件为目标,因此按原生顺序存储值。Java 的序列化使用大端序,因为 Java DataOutput 约定如此规定。
什么是 zigzag varint 编码?
它能用尽可能少的字节写入整数,同时让负数保持较短:符号被折叠到最低位,因此 −1 编码为 1,1 编码为 2。基岩版协议用它表示 int 和 long。
TAG_List 可以包含混合类型吗?
不能。列表会声明一种元素类型,每个项目都必须匹配。混合数据需要使用复合标签列表。
字符串的最大长度是多少?
固定宽度编码的上限为 65535 字节,因为长度字段是 unsigned short。varint 网络编码没有实际限制。