Skip to main content
Version: 1.21.4

命名二进制标签(Named Binary Tag,NBT)

NBT 是在 Minecraft 早期由 Notch 本人引入的一种格式。它在 Minecraft 代码库中被广泛用于数据存储。

规范(Specification)

NBT 规范与 JSON 规范类似,但也有一些区别:

  • 对于字节(byte)、短整型(short)、长整型(long)和浮点型(float),NBT 提供了独立的数据类型,分别用 bslf 作为后缀,这与 Java 代码中的表示方式类似。
    • 双精度浮点数(double)也可以使用 d 作为后缀,但这不是必须的,这一点与 Java 代码类似。Java 中整型可选的 i 后缀在 NBT 中不允许使用。
    • 这些后缀对大小写不敏感。例如,64b64B 是一样的,0.5F0.5f 也是一样的。
  • NBT 中没有布尔型(boolean),而是用字节(byte)来表示。true 变为 1bfalse 变为 0b
    • 当前实现会将所有非零值都视为 true,因此 2b 也会被当作 true
  • NBT 中没有 null 的等价物。
  • 键(key)是否加引号是可选的。因此,JSON 属性 "duration": 20 在 NBT 中既可以写作 duration: 20,也可以写作 "duration": 20
  • 在 JSON 中称为子对象(sub-object)的结构,在 NBT 中称为复合标签(compound tag)(或简称 compound)。
  • 与 JSON 不同,NBT 列表(list)内的元素类型不能混合。列表的类型由第一个元素确定,或者在代码中定义。
    • 但列表的列表(list of lists)可以混合不同类型的列表。例如,一个包含两个列表的列表,其中第一个是字符串列表,第二个是字节列表,这是允许的。
  • NBT 存在特殊的**数组(array)**类型,这些类型与列表不同,但同样使用方括号包裹元素。共有三种数组类型:
    • 字节数组(byte array),以 B; 开头。例如:[B;0b,30b]
    • 整数数组(integer array),以 I; 开头。例如:[I;0,-300]
    • 长整型数组(long array),以 L; 开头。例如:[L;0l,240l]
  • 列表、数组和复合标签中的末尾逗号是允许的。

NBT 文件(NBT Files)

Minecraft 广泛使用 .nbt 文件,例如在 数据包 中的结构文件(structure files)。区域文件(.mca)包含某一区域(即多个区块的集合)的内容,以及游戏中在不同位置使用的各种 .dat 文件,实际上也都是 NBT 文件。

NBT 文件通常会使用 GZip 进行压缩。因此它们是二进制文件,无法直接编辑。

代码中的 NBT(NBT in Code)

和 JSON 类似,所有 NBT 对象都是某个封闭对象的子对象。我们可以这样创建一个 NBT 对象:

CompoundTag tag = new CompoundTag();

现在我们可以向这个标签(tag)中写入数据:

tag.putInt("Color", 0xffffff);
tag.putString("Level", "minecraft:overworld");
tag.putDouble("IAmRunningOutOfIdeasForNamesHere", 1d);

这里还提供了一些辅助方法,例如,putIntArray 除了标准的 int[] 版本外,还提供了一个可以直接接受 List<Integer> 的便捷方法。 当然,我们也可以从该标签(tag)中获取数值:

int color = tag.getInt("Color");
String level = tag.getString("Level");
double d = tag.getDouble("IAmRunningOutOfIdeasForNamesHere");

数值类型如果不存在会返回 0。字符串类型如果不存在会返回 ""。更复杂的类型(如列表、数组、复合标签)如果不存在则会抛出异常。

因此,我们通常需要在获取标签元素前进行存在性检查:

boolean hasColor = tag.contains("Color");
boolean hasColorMoreExplicitly = tag.contains("Color", Tag.TAG_INT);

TAG_INT 常量定义在 Tag 中,Tag 是所有标签类型的顶层接口。除了 CompoundTag 以外,大多数标签类型(如 ByteTagStringTag)主要用于内部实现,不过如果你需要,也可以用 CompoundTag#get#put 方法直接操作这些类型。

但这里有一个明显的例外:ListTag。操作 ListTag 时需要注意,通过 CompoundTag#getList 获取列表标签时,必须同时指定列表的类型。例如,获取字符串列表的方式如下:

ListTag list = tag.getList("SomeListHere", Tag.TAG_STRING);

同样地,创建 ListTag 时也需要在创建时指定列表类型:

ListTag list = new ListTag(List.of("Value1", "Value2"), Tag.TAG_STRING);

最后,在一个 CompoundTag 内部操作另一个 CompoundTag,可以直接用 CompoundTag#get#put 方法:

tag.put("Tag", new CompoundTag());
tag.get("Tag");

NBT 的用途(Usages of NBT)

NBT 在 Minecraft 的许多地方都有应用。其中最常见的例子包括 方块实体实体

note

ItemStack 对 NBT 的使用进行了抽象,统一封装为 数据组件

参见(See Also)