命名二进制标签(Named Binary Tag,NBT)
NBT 是在 Minecraft 早期由 Notch 本人引入的一种格式。它在 Minecraft 代码库中被广泛用于数据存储。
规范(Specification)
NBT 规范与 JSON 规范类似,但也有一些区别:
- 对于字节(byte)、短整型(short)、长整型(long)和浮点型(float),NBT 提供了独立的数据类型,分别用
b、s、l和f作为后缀,这与 Java 代码中的 表示方式类似。- 双精度浮点数(double)也可以使用
d作为后缀,但这不是必须的,这一点与 Java 代码类似。Java 中整型可选的i后缀在 NBT 中不允许使用。 - 这些后缀对大小写不敏感。例如,
64b与64B是一样的,0.5F与0.5f也是一样的。
- 双精度浮点数(double)也可以使用
- NBT 中没有布尔型(boolean),而是用字节(byte)来表示。
true变为1b,false变为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]
- 字节数组(byte array),以
- 列表、数组和复合标签中的末尾逗号是允许的。
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 以外,大多数标签类型(如 ByteTag 或 StringTag)主要用于内部实现,不过如果你需要,也可以用 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 的许多地方都有应用。其中最常见的例子包括 方块实体 和 实体。
ItemStack 对 NBT 的使用进行了抽象,统一封装为 数据组件。