ZH:NBT

二进制命名标签（Named Binary Tag，NBT）文件格式是一种非常简单、尽管有些烦人（我们真的需要另一个格式吗？）[见讨论]的结构化二进制格式，Minecraft游戏将它用于多种用途. 因此，一些第三方实用工具也利用了这种格式. 你可以在本文底部找到示例文件.

Mojang还发布了一个参考实现和他们的Anvil转换工具，在此处可用：https://mojang.com/2012/02/new-minecraft-map-format-anvil/

当前用途
NBT格式目前在多个地方使用，主要是：
 * 在协议中作为槽数据的一部分.
 * 多人游戏中保存的服务器列表（ ）.
 * 玩家数据（单人游戏和多人游戏，每个玩家一个文件），包含如物品栏和位置等内容.
 * 已保存的世界（单人游戏和多人游戏）
 * 包含常规信息（出生点、一天中的时间等）的世界索引文件
 * 区块数据（见区域文件）

不幸的是，作为开发者遇到的NBT文件会以三种不同的方式存储，这就有些有趣了.
 * 未压缩
 * gzip压缩,
 * zlib压缩（又名DEFLATE，多了一些字节）

库
有很多很多用于操纵NBT的库，它们用各种语言写成，且每个语言都有好几种，例如：


 * C
 * C#
 * D
 * Go (New)
 * Go (Old, without TagLongArray)
 * Java (Old, without TagLongArray)
 * Java
 * Javascript
 * PHP
 * Python
 * Ruby
 * Rust
 * Scala
 * Kotlin（非常简易，200行）
 * Kotlin Multiplatform
 * 你的想法…

除非你有特定的目标或许可要求，否则强烈建议使用其中一种现有库.

实用工具
几乎每个第三方Minecraft应用程序都在某种程度上使用了NBT. 还有一些专用的NBT编辑器，如果您要开发自己的NBT库，这可能会有帮助，包括：


 * NBTEdit（C#，单一功能），最早的NBT编辑器之一.
 * NEINedit（Obj-C），一个OS X限定的编辑器.
 * nbt2yaml（Python），提供了通过YAML格式使用命令行编辑NBT，以及快速而极简的NBT解析/渲染API的功能.
 * nbted（Rust；CC0），提供了通过你的$EDITOR使用命令行编辑NBT文件的功能.
 * nbt2json（Golang；MIT）NBT至JSON/YAML双向转换的命令行实用工具. 支持MCPE-NBT. 可用以库的形式使用.

规范
NBT文件格式非常简单，编写读写它的库也很简单. 此格式支持13种数据类型，其中一种用于闭合复合标签. 强烈建议你阅读整个部分，否则可能会遇到问题.

这几件简单的事情需要记住：
 * 表示数字的数据类型在Java版中为大字节序，而携带版为小字节序. 除非使用Java，否则你很有可能必须将其交换为小字节序. 见维基百科上有关字节序的文章.
 * 每个NBT文件将总是隐式地包含在复合标签中，并且也以TAG_Compound开头.
 * NBT文件的结构由TAG_List和TAG_Compound类型定义，因为这样的标签本身仅包含负载，但是这取决于标签中包含的内容，它也可能包含其他头. 即，如果位于复合标签内部，则每个标签都将以TAG_id开头，然后是字符串（标签名），最后是负载. 在列表中时，它仅是负载，因为没有名称，并且标签类型在列表的开头给出.

例如，这是磁盘上的 的示例布局：

如果此 曾经在 中，则它仅是负载，因为类型是隐式的且列表第一级中的标签是没有名称的.

示例
Markus最初提供了两个实际上的示例文件（ &  ）用于测试你的实现. 下面提供的示例输出是使用PyNBT的debug-nbt工具生成的.

test.nbt
第一个示例是一个未压缩的“Hello World”NBT示例. 如果你正确地解析了它，你会得到与下列类似的结构：

TAG_Compound('hello world'): 1 entry {   TAG_String('name'): 'Bananrama' }

这里是示例的说明：

bigtest.nbt
第二个示例是每个可用标签的gzip压缩的测试. 如果你的程序可以成功解析此文件，那么你做得很好. 请注意，如上所述，TAG_List下的标签没有名称. TAG_Compound('Level'): 11 entries {   TAG_Compound('nested compound test'): 2 entries {     TAG_Compound('egg'): 2 entries {       TAG_String('name'): 'Eggbert' TAG_Float('value'): 0.5 }     TAG_Compound('ham'): 2 entries {       TAG_String('name'): 'Hampus' TAG_Float('value'): 0.75 }   }    TAG_Int('intTest'): 2147483647 TAG_Byte('byteTest'): 127 TAG_String('stringTest'): 'HELLO WORLD THIS IS A TEST STRING \xc3\x85\xc3\x84\xc3\x96!' TAG_List('listTest (long)'): 5 entries {     TAG_Long(None): 11 TAG_Long(None): 12 TAG_Long(None): 13 TAG_Long(None): 14 TAG_Long(None): 15 }   TAG_Double('doubleTest'): 0.49312871321823148 TAG_Float('floatTest'): 0.49823147058486938 TAG_Long('longTest'): 9223372036854775807L TAG_List('listTest (compound)'): 2 entries {     TAG_Compound(None): 2 entries {       TAG_Long('created-on'): 1264099775885L TAG_String('name'): 'Compound tag #0' }     TAG_Compound(None): 2 entries {       TAG_Long('created-on'): 1264099775885L TAG_String('name'): 'Compound tag #1' }   }    TAG_Byte_Array('byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))'): [1000 bytes] TAG_Short('shortTest'): 32767 }

servers.dat
servers.dat文件包含你已添加到游戏中的多人服务器的列表. 这有点混乱，此文件将总是未压缩的. 以下是在servers.dat中看到的结构示例. TAG_Compound( '' ): 1 entry {   TAG_List('servers'): 2 entries {     TAG_Compound(None): 3 entries {       TAG_Byte('acceptTextures'): 1 (Automatically accept resourcepacks from this server) TAG_String('ip'): '199.167.132.229:25620' TAG_String('name'): 'Dainz1 - Creative' }     TAG_Compound(None): 3 entries {       TAG_String('icon'): 'iVBORw0KGgoAAAANUhEUgAAAEAAAABACA...' (The base64-encoded server icon. Trimmed here for the example's sake) TAG_String('ip'): '76.127.122.65:25565' TAG_String('name'): 'minstarmin4' }   }  }

level.dat
最后一个示例是单人游戏的level.dat，它使用gzip压缩. 注意玩家的物品栏和常规世界细节如出生位置、世界名称和游戏种子. TAG_Compound( '' ): 1 entry {   TAG_Compound('Data'): 17 entries {     TAG_Byte('raining'): 0 TAG_Long('RandomSeed'): 3142388825013346304L TAG_Int('SpawnX'): 0 TAG_Int('SpawnZ'): 0 TAG_Long('LastPlayed'): 1323133681772L TAG_Int('GameType'): 1 TAG_Int('SpawnY'): 63 TAG_Byte('MapFeatures'): 1 TAG_Compound('Player'): 24 entries {       TAG_Int('XpTotal'): 0 TAG_Compound('abilities'): 4 entries {         TAG_Byte('instabuild'): 1 TAG_Byte('flying'): 1 TAG_Byte('mayfly'): 1 TAG_Byte('invulnerable'): 1 }       TAG_Int('XpLevel'): 0 TAG_Int('Score'): 0 TAG_Short('Health'): 20 TAG_List('Inventory'): 13 entries {         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 1 TAG_Byte('Slot'): 0 TAG_Short('id'): 24 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 1 TAG_Byte('Slot'): 1 TAG_Short('id'): 25 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 1 TAG_Byte('Slot'): 2 TAG_Short('id'): 326 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 1 TAG_Byte('Slot'): 3 TAG_Short('id'): 29 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 10 TAG_Byte('Slot'): 4 TAG_Short('id'): 69 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 3 TAG_Byte('Slot'): 5 TAG_Short('id'): 33 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 43 TAG_Byte('Slot'): 6 TAG_Short('id'): 356 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 64 TAG_Byte('Slot'): 7 TAG_Short('id'): 331 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 20 TAG_Byte('Slot'): 8 TAG_Short('id'): 76 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 64 TAG_Byte('Slot'): 9 TAG_Short('id'): 331 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 1 TAG_Byte('Slot'): 10 TAG_Short('id'): 323 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 16 TAG_Byte('Slot'): 11 TAG_Short('id'): 331 TAG_Short('Damage'): 0 }         TAG_Compound(None): 4 entries {           TAG_Byte('Count'): 1 TAG_Byte('Slot'): 12 TAG_Short('id'): 110 TAG_Short('Damage'): 0 }       }        TAG_Short('HurtTime'): 0 TAG_Short('Fire'): -20 TAG_Float('foodExhaustionLevel'): 0.0 TAG_Float('foodSaturationLevel'): 5.0 TAG_Int('foodTickTimer'): 0 TAG_Short('SleepTimer'): 0 TAG_Short('DeathTime'): 0 TAG_List('Rotation'): 2 entries {         TAG_Float(None): 1151.9342041015625 TAG_Float(None): 32.249679565429688 }       TAG_Float('XpP'): 0.0 TAG_Float('FallDistance'): 0.0 TAG_Short('Air'): 300 TAG_List('Motion'): 3 entries {         TAG_Double(None): -2.9778325794951344e-11 TAG_Double(None): -0.078400001525878907 TAG_Double(None): 1.1763942772801152e-11 }       TAG_Int('Dimension'): 0 TAG_Byte('OnGround'): 1 TAG_List('Pos'): 3 entries {         TAG_Double(None): 256.87499499518492 TAG_Double(None): 112.62000000476837 TAG_Double(None): -34.578128612797634 }       TAG_Byte('Sleeping'): 0 TAG_Short('AttackTime'): 0 TAG_Int('foodLevel'): 20 }     TAG_Int('thunderTime'): 2724 TAG_Int('version'): 19132 TAG_Int('rainTime'): 5476 TAG_Long('Time'): 128763 TAG_Byte('thundering'): 1 TAG_Byte('hardcore'): 0 TAG_Long('SizeOnDisk'): 0 TAG_String('LevelName'): 'Sandstone Test World' } }

下载

 * test.nbt/hello_world.nbt（未压缩）
 * bigtest.nbt（gzip压缩）
 * NaN-value-double.dat（已压缩，原始版本未知）