NBT (Named Binary Tag) ist das binäre Baumformat von Minecraft. Ein Tag besteht aus einem Byte für den Typ, einem längenpräfigierten Namen und Nutzdaten. Compounds werden verschachtelt, bis ein Byte TAG_End sie schließt. In Java und Bedrock werden 13 Tag-Typen und fünf Kodierungen verwendet. Diese Seite beschreibt sie; mit dem Editor oben kannst du jede Angabe an einer echten Datei prüfen.
Byte für Elementtyp, Int-Länge, dann namenlose Nutzdaten
[1,2]
10
TAG_Compound
Typ + Name + Nutzdaten wiederholt bis TAG_End
{a:1}
11
TAG_Int_Array
Int-Länge, danach entsprechend viele 4-Byte-Integer
[I;1,2]
12
TAG_Long_Array
Int-Länge, danach entsprechend viele 8-Byte-Long-Werte
[L;1L,2L]
Eine Datei enthält genau ein Root-TAG_Compound: ein Typ-Byte 0x0A, einen Namen und danach den Inhalt des Compounds. Benannte Tags treten nur in Compounds auf. Listenelemente enthalten ausschließlich Nutzdaten; deshalb ist eine Liste homogen.
Fünf verwendete Kodierungen
Kodierung
Ganzzahlen
String-Länge
Root-Tag
Verwendung
Java (klassisch)
Big-Endian mit fester Breite
2-Byte-Big-Endian
Benannt
Java-Welten, Strukturen und Schematics
Java-Netzwerk (ab 1.20.2)
Big-Endian mit fester Breite
2-Byte-Big-Endian
Namenlos
Java-Protokoll
Bedrock
Little-Endian mit fester Breite
2-Byte-Little-Endian
Benannt
Bedrock-Welten, .mcstructure, LevelDB-Werte
Bedrock level.dat
Little-Endian mit fester Breite
2-Byte-Little-Endian
Benannt, nach einem 8-Byte-Header
Metadaten von Bedrock-Welten
Bedrock-Netzwerk
Zigzag-Varint für Int und Long
Varint ohne Vorzeichen
Benannt
Bedrock-Protokoll
In der Varint-Kodierung bleiben TAG_Short, TAG_Float und TAG_Double Little-Endian-Werte fester Breite. Nur TAG_Int und TAG_Long werden zu Zigzag-Varints; Array- und Listenlängen folgen der Kodierung von TAG_Int. String-Längen sind Varints ohne Vorzeichen. Dadurch entfällt auch die Obergrenze von 65535 Bytes der Kodierungen mit fester Breite.
Strings: modifiziertes UTF-8 und UTF-8
Java serialisiert Strings mit DataOutputStream.writeUTF, also modifiziertem UTF-8. Ein NUL-Zeichen wird als C0 80 statt 00 geschrieben. Zeichen außerhalb der Basic Multilingual Plane werden als zwei Drei-Byte-Surrogathälften (CESU-8) statt als eine Vier-Byte-Sequenz gespeichert. Bedrock verwendet normales UTF-8. Ein Werkzeug, das für beide Formate dieselbe Kodierung annimmt, beschädigt beim Speichern Emojis und bestimmte CJK-Zeichen. Dieser Editor kodiert passend zum jeweiligen Format, sodass auch ein Weltname mit Emoji unverändert bleibt.
Komprimierung
NBT selbst ist nicht komprimiert; dies legt der Container fest. Java-Dateien verwenden meist gzip (Magic Bytes 1F 8B), Chunk-Nutzdaten in Regionsdateien zlib (Magic Bytes 78 01, 78 9C oder 78 DA). Bedrock speichert level.dat und Strukturdateien unkomprimiert. Die Erkennung erfolgt anhand der Magic Bytes, sodass derselbe Parser alle drei Varianten verarbeitet. Eine Datei mit falscher Komprimierung ist der häufigste Grund, warum eine manuell bearbeitete Welt nicht lädt.
Eine Minimaldatei, Byte für Byte
Die kanonische Datei hello_world.nbt enthält ein Root-Compound namens hello world mit einem String-Tag name und dem Wert Bananrama. In Javas Big-Endian-Kodierung:
0A TAG_Compound
00 0B 68 65 6C 6C 6F 20 77 6F 72 6C 64 name length 11, "hello world"
08 TAG_String
00 04 6E 61 6D 65 name length 4, "name"
00 09 42 61 6E 61 6E 72 61 6D 61 value length 9, "Bananrama"
00 TAG_End
Dasselbe Dokument unterscheidet sich in Bedrocks Little-Endian-Kodierung nur durch die Byte-Reihenfolge der beiden Längenfelder: 0B 00 statt 00 0B. In Bedrock-Netzwerk-NBT werden die Längen zu einzelnen Varint-Bytes 0B und 04, ganz ohne Padding. Lege eine der drei Varianten im Editor oben ab; er zeigt die empfangene Kodierung an.
SNBT
SNBT ist als Text geschriebenes NBT und wird von Befehlen verarbeitet: /data merge entity @s {Invulnerable:1b}. Typ-Suffixe machen das Format verlustfrei: b für Byte, s für Short, L für Long, f für Float, d für Double und kein Suffix für Int. Typisierte Array-Literale werden als [B;…], [I;…] und [L;…] geschrieben. Der Editor exportiert jede geladene Datei als SNBT. So lassen sich zwei Versionen einer Welt mit einem normalen Text-Diff vergleichen.
Häufig gestellte Fragen
Gibt es eine offizielle NBT-Spezifikation?
Nein, keine formale Spezifikation von Mojang. Das Format wurde 2010 von Notch beschrieben und seitdem von der Community dokumentiert. Die Tag-IDs und Strukturen auf dieser Seite entsprechen den Daten, die das Spiel heute liest und schreibt.
Warum verwendet Bedrock Little-Endian?
Die Bedrock-Engine ist in C++ geschrieben und zielt auf Little-Endian-Hardware, weshalb Werte in nativer Reihenfolge gespeichert werden. Javas Serialisierung ist Big-Endian, weil der Java-Vertrag für DataOutput dieses Format vorgibt.
Was ist eine Zigzag-Varint-Kodierung?
Sie schreibt Ganzzahlen mit möglichst wenigen Bytes und hält zugleich negative Zahlen kurz. Das Vorzeichen wird in das niedrigste Bit gefaltet, sodass −1 als 1 und 1 als 2 kodiert wird. Das Bedrock-Protokoll verwendet sie für Integer und Long-Werte.
Kann eine TAG_List gemischte Typen enthalten?
Nein. Eine Liste legt genau einen Elementtyp fest, dem jeder Eintrag entsprechen muss. Gemischte Daten benötigen eine Liste aus Compounds.
Wie lang darf ein String maximal sein?
Bei Kodierungen mit fester Breite 65535 Bytes, weil das Längenfeld ein Short ohne Vorzeichen ist. Die Varint-Netzwerkkodierung besitzt keine praktische Grenze.