命名二进制标签(NBT)

命名二进制标签(NBT)

NBT 是 Minecraft 早期由 Notch 编写的数据格式,在游戏代码中广泛用于数据存储。

格式规范

NBT 与 JSON 格式类似,但存在一些差别:

  • 字节、短整数、长整数和单精度浮点数有独立类型,分别使用 b、s、l、f 后缀,形式类似 Java 中的表示法。
    • 双精度浮点数可选用 d 后缀,与 Java 类似。原文另称 Java 整数可选的 i 后缀在这里不允许使用。
    • 后缀不区分大小写,例如 64b 与 64B 相同,0.5F 与 0.5f 相同。
  • 没有布尔类型,而用字节表示:true 为 1b,false 为 0b。
    • 当前实现将所有非零值视为 true,因此 2b 也表示真。
  • 没有对应 null 的值。
  • 键两侧的引号可省略。例如 JSON 的 "duration": 20,在 NBT 中可写为 duration: 20 或 "duration": 20。
  • JSON 中的子对象在 NBT 中称为复合标签,简称 compound。
  • 与 JSON 不同,NBT 列表不能混用元素类型。列表类型由首个元素确定,或在代码中定义。
    • 但列表组成的列表可以包含不同类型的子列表,例如第一个子列表存字符串,第二个存字节。
  • 另有不同于列表的特殊数组类型,同样用方括号包围元素,共三种:
    • 字节数组,以 B; 开头,例如 [B;0b,30b]。
    • 整数数组,以 I; 开头,例如 [I;0,-300]。
    • 长整数数组,以 L; 开头,例如 [L;0l,240l]。
  • 列表、数组和复合标签允许尾随逗号。

NBT 文件

Minecraft 广泛使用 .nbt 文件,例如数据包中的结构文件。包含一组区块内容的区域文件 .mca,以及游戏各处使用的各种 .dat 文件,也采用 NBT。

NBT 文件通常使用 GZip 压缩。它们是二进制文件,不能直接作为文本编辑。

在代码中使用 NBT

与 JSON 类似,NBT 对象都包含在外层对象中。先创建一个:

CompoundTag tag = new CompoundTag();

然后向标签放入数据:

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

系统提供了一些辅助方法。例如,putIntArray 除接受 int[] 的标准重载外,还提供接受 List<Integer> 的便捷重载。

当然,也可以读取标签中的值:

Optional<Integer> color = tag.getInt("Color");
Optional<String> level = tag.getString("Level");
Optional<Double> d = tag.getDouble("IAmRunningOutOfIdeasForNamesHere");

由于无法保证标签存在,返回值用 Optional 包装。基本类型可通过相应的 *Or* 方法指定默认值。ListTag 可通过 getListOrEmpty 返回空列表默认值,CompoundTag 则使用 getCompoundOrEmpty。基本类型数组没有对应的 *Or* 方法。

int color = tag.getIntOr("Color", 0xffffff);
String level = tag.getStringOr("Level", "minecraft:overworld");
double d = tag.getDoubleOr("IAmRunningOutOfIdeasForNamesHere", 1d);

所有标签类型都实现 Tag。除 CompoundTag 外,多数类型主要供内部使用,例如 ByteTag 和 StringTag。需要时,也可以通过 CompoundTag#get 和 #put 直接操作它们。

一个明显例外是 ListTag。它与特定标签类型关联,类型在内部计算,因此操作方式有所不同:

ListTag newList = new ListTag();
// Adds the tags to the list
newList.add(StringTag.valueOf("Value1"));
newList.add(StringTag.valueOf("Value2"));

// Getting the tag
ListTag getList = tag.getListOrEmpty("SomeListHere");

最后,在 CompoundTag 内部直接操作另一个 CompoundTag 时,使用 CompoundTag#get 和 #put:

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

// Can use regular `get` as well if you want to handle null case instead
tag.getCompoundOrEmpty("Tag");

NBT 的用途

Minecraft 在许多地方使用 NBT。方块实体和实体通过值访问抽象了 NBT 操作,ItemStack 则通过数据组件抽象这些操作。

另见


来源: NeoForged/Documentation(26.1)。中文翻译依据提交 4a680cdd4d15。