NBT(二进制命名标签,NamedBinary Tags)格式是一种由众多的标签所组成的树状数据结构。在Minecraft中,其被广泛用于向存档文件中存储数据。所有的标签都有一个独立的数字ID和名称,以及一个负载。
另一种玩家更熟悉的是字符串形式的NBT,通常在命令里使用。这种格式常被称为SNBT(字符串化的二进制命名标签,Stringified NBT)。
SNBT格式[]
SNBT,也被称为数据标签,在Java版命令中很常见。它的简单表示方法为使用花括号将一对键值对括起来。在命令中,数据标签常被用以描述复杂的实体数据。
数据标签可以包含零个或多个属性值对,它们以逗号分隔,被一对花括号包覆。每个属性值对又由标签名和标签值构成,名和值之间用冒号连接。实际上,对于很多值来说,可能其自身就是一个包含众多属性值对的复合标签,这使得数据标签能对分层数据结构进行描述。
- 示例:
{name1:123,name2:"sometext1",name3:{subname1:456,subname2:"sometext2"}}
标签name1,name2,name3
的所在层级为“第一层”,name3
标签下的subname1,subname2
的所在层级为“第二层”。
需要注意的是,JSON格式是与之不同的另一种格式;因此,对于任何嵌入到NBT中的JSON(比如原始JSON文本),都必须包含在 字符串类型的标签中。
数据类型[]
SNBT标签具有不同的类型,可以描述不同性质的数据,其书写格式也因此有所差异。
类型 | 描述 | SNBT格式 | SNBT示例 |
---|---|---|---|
Byte | 字节型,为一个8-bit整数,范围是-128到127(闭区间)。 | <数字>b 或<数字>B
|
34B 、-20b
|
Boolean | 布尔型。NBT本身并没有布尔型数据类型,但是byte值0和1可以分别表示true 和false 。
|
true 、false
|
true
|
Short | 短整型,为一个16-bit整数,范围是-32,768到32,767(闭区间)。 | <数字>s 或<数字>S
|
31415s 、-27183s
|
Int | 整型,为一个23-bit整数,范围是-2,147,483,648到2,147,483,647(闭区间)。 | <整数>
|
31415926
|
Long | 长整型,一个64-bit整数,范围是-9,223,372,036,854,775,808到9,223,372,036,854,775,807(闭区间)。 | <数字>l 或<数字>L
|
31415926l
|
Float | 单精度浮点数类型,为一个32-bit单精度浮点数字,范围是-3.4E+38到+3.4E+38。
参见IEEE floating point以获取详细信息。 |
<数字>f 或<数字>F
|
3.1415926f
|
Double | 双精度浮点数类型,为一个64-bit双精度浮点数字,范围是-1.7E+308是+1.7E+308。
参见IEEE floating point以获取详细信息。 |
<十进制数字> 、<数字>d 或<数字>D
|
3.1415926
|
String | 字符串类型,为一个字符序列。 | 字符串使用引号包围。对字符串来说,仅当其只包含0-9 、A-Z 、a-z 、_ 、- 、。 和+ 字符,并且不与其他数据类型混淆的情况下(例如,使用/data modify 设置某标签值为123b 时,若不指定引号,则会设置为byte类型数字),引号可以省略。引号可以是一个单引号' 或者是双引号" 。通过使用\ 转义字符,可以对字符串中的引号进行嵌套。
|
|
List | 列表类型,为一个包含标签的有序列表。其中的标签必须为同一个类型,类型由列表中的第一个标签决定。 | 用方括号将以逗号分隔的无名标签括起来(即仅包含值)。
|
[3.2,64.5,129.5]
|
Compound | 复合标签类型,为一个包含属性值对的有序列表。
其中的每个标签可以为任意类型。 |
用花括号将以逗号分隔的带名标签括起来。
键名(标签名)若仅含
|
{X:3,Y:64,Z:129}
|
Byte Array | 字节型数组类型,为一个8-bit整数有序列表。注意,[B;1b,2b,3b] 和[1b,2b,3b] 被视为不同的类型,后者为 list。
|
B; 位于列表内的首位置,其后紧随以逗号分隔的byte型数字。标签应该被方括号括起来。
|
[B;1b,2b,3b]
|
Int Array | 整型数组类型,一个32-bit整数有序列表。注意,[I;1,2,3] 和[1,2,3] 被视为不同的类型,后者为 list。
|
I; 位于列表内的首位置,其后紧跟以逗号分隔的int型数字。标签应该被方括号括起来。
|
[I;1,2,3]
|
Long Array | 长整型数组类型,为一个64-bit整数有序列表。注意[L;1l,2l,3l] 和[1l,2l,3l] 被视为不同的类型,后者为 list。
|
L; 位于列表内的首位置,其后紧跟以逗号分隔的long型数字。标签应该被方括号括起来。
|
[L;1l,2l,3l]
|
NBT对象[]
当游戏运行时,处于正在加载的区块中的实体和方块都存储在内存中。这种情况下,它们没有以NBT的形式被存储,而只是程序对象。
在进行NBT操作时,游戏会先从实体/方块实体生成程序化的NBT对象,然后解析SNBT为NBT对象,最后基于提供的NBT对象来修改实体/方块,或者转化NBT对象为SNBT。
生成NBT对象[]
当从实体/方块生成NBT时,实体/方块的属性将被添加到程序化的NBT对象中。
注意,不是所有的属性都会被添加。比如,标识一个玩家是否会打开一个箱子的值将不会被添加到NBT对象中。
值被添加时到对象时,其数据类型也应该是确定的。比如,要添加一个命名空间ID作为值,会先将其转换为一个字符串。
当退出游戏或者自动保存时,这些NBT对象也将会以nbt文件的形式存储到游戏文件中。所以,对于各处的标签来说,不管是NBT标签所描述的数据结构还是每个标签的数据类型,基本上与游戏存档文件中的相同。这些数据类型也在其他文章或命令中被描述过,它们都被期望拥有相同的的属性名称(区分大小写):
对象 | 例子 |
---|---|
方块实体 | 箱子、熔炉、命令方块、刷怪笼、告示牌等。 |
物品 | 在物品栏中的物品(包括魔咒、物品描述、自定义名称等说明信息)。 |
物品和经验球 | 地面上的物品。 |
生物 | 苦力怕、牛、村民等。 |
弹射物 | 火焰弹、被投掷的药水等。 |
交通工具 | 船、矿车等。 |
动态方块 | 点燃的TNT、下落的沙子/沙砾/混凝土粉末/铁砧。 |
其他实体 | 烟花火箭、画和物品展示框。 |
转换为SNBT[]
当尝试用/data get
等命令获取一个程序化的NBT对象时,此对象将会被转换为一个SNBT。
除了 整型,其余类型数字在被转换后,都带有字母尾缀(小写字母b、s、f、d和大写字母L)。比如,3s
是转换后的short,3.2f
是转换后的float,等等。
字符串被转换后总是被单双引号包围。
对于其他数据类型,请在上文的#数据类型表格中查看相关介绍。
从SNBT转换[]
当SNBT被游戏解析时,它将会被转换为一个程序化的NBT对象。
对于带有字母后缀(B、S、L、F、D,或者它们的小写字母)的数字,将被解析为相应的数据类型。比如,对short为3s
(或3S
),对float为3.2f
(或3.2F
)。若没有指定尾缀,则有小数点的数字会将被当做double,而无小数点的32-bit大小内的数字将被当做int,剩余情况下,都将当做string。
包含字面量的方括号将被视作列表,除非使用一个标识符:[I;1,2,3]
为整型数组,[L;1L,2L,3L]
为长整型数组,而[1L,2L,3L]
是列表,其中装有长整型数字。
true
和false
将分别被转换为1b
和0b
。
基于NBT对象修改实体/方块[]
基于程序化的NBT对象修改实体/方块并不是一个简单的过程。在改变实体/方块属性前,需要解析所有指定的标签。注意,只有某些特定属性被修改。比如,当使用/data
命令来修改一个方块实体时,它的坐标是不能改变的。
若某属性需要的值为一个命名空间ID,并得到了一个 string标签,则该字符串将会转换为一个命名空间ID。
若某属性需要的值为一个JSON文本,并得到了一个 string标签,则该字符串将被解析为JSON文本对象。
若某属性需要一个布尔值,并得到了一个数值类型的标签,那么如果该数值非0,就会先将其四舍五入,然后再将其转换为byte型数字,并设该属性为真。
若某属性需要一个布尔值,但得到了一个非数值类型的标签,那么此属性将为假。
若某属性需要某数值类型的数字作为其值,但得到一个与之所需类型不符的数值类型标签(错误的标签),那么其值将被四舍五入后再转换为所需要的类型。
若某属性需要某数值类型的数字作为其值,但得到一个非数值类型的标签,那么该属性将被赋值为0。
若某属性需要一个字符串作为其值,但得到一个非字符串类型的标签,那么该字符串将变为一个空串。
若某属性需要一个列表,或是某类型的数组,但其标签类型与需要的类型不符(错误的标签),则它将得到一个空列表/数组。
若某属性需要一个复合标签,但却得到了一个非复合标签,则它最终会得到一个空复合标签。
二进制格式[]
NBT文件是经过了压缩的复合标签,其中包含名称以及标签ID。压缩包中必须包含一个作为首字节的复合标签。在Minecraft中,有一些文件可能是未压缩的,但在大多数情况下,文件仍遵循Notch的原始说明使用GZip压缩。
标签的定义[]
每个标签在数据树中都是独立的一部分。标签的第一个字节为标签类型(ID),后两个字节是名称的长度(以两个字节的大端序无符号整数记录),最后是UTF-8格式字符串存储的标签名称(注:TAG_End没有名称,不包含第二部分的两个字节,故名称被定义为空)。虽然Minecraft自己不会保存有空格的标签名称,但是实际上名称允许包含空格。根据标签类型的不同,名称部分后的部分就是标签的“负载”。下表所示为在19133标签版本中二进制命名标签格式中所有13个已知的标签类型及它们对应的SNBT格式:
图标 | 描述 | SNBT格式 |
标签类型与ID | 负载 | 存储容量 |
---|---|---|---|---|---|
– | 用于标记 复合标签的结尾。本标签无任何名称所以只有一个零字节。 | – | TAG_End(0) | 无 | N/A |
布尔值,对应Java中的boolean 。
|
|
TAG_Byte(1) | 1字节 / 8位,有符号 | -27到27-1(-128到127) | |
有符号的整值数据类型,对应Java中的byte 。
|
| ||||
有符号的整值数据类型,对应Java中的short 。
|
|
TAG_Short(2) | 2字节 / 16位,有符号,大端序 |
-215到215-1(-32,768到32,767) | |
有符号的整值数据类型,对应Java中的int 。
|
|
TAG_Int(3) | 4字节 / 32位,有符号,大端序 |
-231到231-1(-2,147,483,648到2,147,483,647) | |
有符号的整值数据类型,对应Java中的long 。
|
|
TAG_Long(4) | 8字节 / 64位,有符号,大端序 |
-263到263-1(-9,223,372,036,854,775,808到9,223,372,036,854,775,807) | |
有符号的浮点数据类型,对应Java中的float 。
|
|
TAG_Float(5) | 4字节 / 32位,有符号,大端序 |
数据精度根据数值而定,见单精度浮点数 | |
有符号的浮点数据类型,对应Java中的double 。
|
|
TAG_Double(6) | 8字节 / 64位,有符号,大端序 |
数据精度根据数值而定,见双精度浮点数 | |
存储byte 的数组。
|
|
TAG_Byte_Array(7) |
|
根据JVM的不同,数组成员最大数量可能在231 - 9和231 - 1(2,147,483,639和2,147,483,647)之间。 | |
不包含空结束符的UTF-8字符串,对应Java中的String 。
|
|
TAG_String(8) |
|
可解释为UTF-8字符串的最多65,535个字节(见变种UTF-8;ASCII字符均为1字节,大多数中文字符为3字节) | |
一个标签列表,其成员没有标签ID和标签名称。 |
|
TAG_List(9) |
|
由于JVM的限制以及ArrayList的实现问题,列表成员最大数量为231 - 9(2,147,483,639)。另外, List和 Compound标签的嵌套深度不能超过512。 | |
一系列完整的标签信息,包括ID、名称以及负载等。任意两个标签都不能有相同的名称。 |
|
TAG_Compound(10) |
|
不像列表,Compound标签内的标签数量没有硬性限制(不过仍受JVM分配的内存限制)。另外, List和 Compound标签的嵌套深度不能超过512。 | |
存储int 的数组。
|
|
TAG_Int_Array(11) |
|
根据JVM的不同,数组成员最大数量可能在231 - 9和231 - 1(2,147,483,639和2,147,483,647)之间。 | |
存储long 的数组。
|
|
TAG_Long_Array(12) |
|
根据JVM的不同,数组成员最大数量可能在231 - 9和231 - 1(2,147,483,639和2,147,483,647)之间。 |
复合标签的列表常以嵌套递归的方式出现。另外还需注意,在一个包含List的List中,每个List可以包含不同类型的数据。
在Minecraft中的应用[]
NBT文件格式在Minecraft的应用十分不一致。在某些情况下,空列表可能表示为字节标签列表而不是正确标签类型的列表,或者在较新的Minecraft版本中表示为一系列的End标签(这会导致较旧的NBT工具出现错误)。另外,几乎所有的根标签的名称都是空字符串,并包含唯一一个复合标签存储实际的数据和名称,如下所示:
- 在Minecraft NBT结构中最常看到的根标签。
- SomeName: 在根标签下的唯一一个标签——这一标签拥有名称和实际的数据。
另一点值得注意的是,尽管Notch最初所述允许在标签名称中出现空格,甚至是在例子中也出现了包含空格的标签名称,但是Minecraft目前并没有使用带有空格的标签名称。字母大小写的使用也不一致,包含“小驼峰(camelCase,即首个单词小写,而后的单词首字母全部大写)”、“大驼峰(PascalCase,即所有单词的首字母都大写)”和“蛇形(snake_case,即所有单词小写,以下划线分割)”三种不同的命名法。
应用[]
- level.dat以压缩后的NBT格式存储。
- <player>.dat以压缩后的NBT格式存储。
- idcounts.dat以压缩后的NBT格式存储。
- map_<#>.dat以压缩后的NBT格式存储。
- servers.dat以未压缩的NBT格式存储多人服务器列表。
- 区块以压缩后的NBT格式存储区域文件。
- scoreboard.dat以压缩后的NBT格式存储。
- 保存的结构以压缩后的NBT格式存储。
官方软件[]
Mojang已经公布了Java中的NBT示例类文件,并将其作为Region至Anvil格式转换器源代码的一部分使用。[2]自Java版1.13起,Minecraft内置了SNBT和压缩后的NBT文件的转换器,且通常与官方服务端一同发布。[3]
来自Minecraft的数据生成器能够将输入文件夹中扩展名为.snbt的未压缩字符串化NBT文件转换为输出文件夹中扩展名为.nbt的GZip压缩NBT格式文件,反之亦然。
原版数据生成器可以将任何GZip压缩的NBT格式转换为SNBT格式。只需将文件的扩展名(如level.dat)更改为level.nbt并将其放入输入文件夹,然后生成器将对GZip压缩的NBT数据进行解码。
历史[]
最早的已知NBT格式版本是Java版Beta 1.3中引入的19132;随着Anvil文件格式的引入,增加了一个整型数组变量,版本号也随之更新至19133。NBT标签的历史最早可以追溯到Indev,当时只有0到10这11个标签可用。
Java版 | |||||
---|---|---|---|---|---|
1.0.0 | 2011年9月28日 | Notch致力于“用物品实例来存储任意信息”。 | |||
1.12 | ? | 加入了长整型数组标签。 | |||
1.13 | 18w01a | 为Minecraft Client和默认的多人游戏软件添加了数据生成器。 | |||
1.14 | 19w08a | 除" 外,字符串标签现在可以写在' 之间。[4] | |||
1.16 | 20w21a | 加入了NBT格式和json格式文件的转换功能。 |
外部链接[]
- wiki.vg上的NBT条目
- NBTExplorer,一个用于查看和编辑NBT文件的工具
- webNBT,一个在线的查看和编辑NBT文件的工具
参考[]
版本 | |||||||
---|---|---|---|---|---|---|---|
开发周期 |
| ||||||
技术 |
| ||||||
多人游戏 | |||||||
游戏订制 |
语言