命名二进制标签(Named Binary Tag, NBT)
命名二进制标签(Named Binary Tag, NBT)
NBT 是 Minecraft 最早引入的一种格式,由 Notch 亲自编写。它在 Minecraft 代码库中广泛用于数据存储。
规范
NBT 规范与 JSON 规范类似,但有一些区别:
字节、短整型、长整型和浮点数的类型:分别用
b
、s
、l
和f
后缀表示,类似于 Java 代码中的表示方式。双精度浮点数:也可以用
d
后缀表示,但这不是必需的,类似于 Java 代码。Java 中整数的可选i
后缀在 NBT 中不允许。后缀不区分大小写:例如,
64b
与64B
相同,0.5F
与0.5f
相同。布尔值:NBT 中没有布尔类型,它们用字节表示。
true
变为1b
,false
变为0b
。- 当前实现将所有非零值视为
true
,因此2b
也会被视为true
。
- 当前实现将所有非零值视为
空值:NBT 中没有
null
的等价物。键的引号:键的引号是可选的。因此,JSON 属性
"duration": 20
在 NBT 中可以变为duration: 20
或"duration": 20
。复合标签:在 JSON 中称为子对象的内容在 NBT 中称为复合标签(或简称复合)。
列表:NBT 列表不能混合类型,这与 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>
的便捷方法。
当然,我们也可以从该标签中获取值:
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
中定义,它是所有标签类型的超接口。除了 CompoundTag
之外,大多数标签类型主要是内部的,例如 ByteTag
或 StringTag
,尽管如果你偶然遇到一些情况,CompoundTag#get
和 #put
方法可以直接使用它们。
不过,有一个明显的例外: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 的用途
NBT 在 Minecraft 中的许多地方使用。一些最常见的例子包括 BlockEntity
和 Entity
。
注意ItemStack
将 NBT 的使用抽象为数据组件。