Eine .nbt-Struktur speichert Palettenindizes, nicht Blocknamen – darum ist sie so klein
Drei Schlüssel enthalten einen ganzen Bau: Größe, Palette und Blöcke. Jeder Block ist eine Ganzzahl, die auf die Palette verweist, sodass ein 32³ Volumen aus Stein einen String und 32.768 Zahlen kostet.
Eine .nbt-Strukturdatei – das, was ein Konstruktionsblock speichert und ein Datenpaket liefert – ist ein viel einfacheres Format, als es von außen aussieht. Alles, was ein Bau benötigt, befindet sich in einer Handvoll Top-Level-Schlüssel, und eine Designentscheidung erklärt, warum die Dateien so klein sind.
Die drei Schlüssel, die den Bau tragen
size [x, y, z] — a list of exactly three integers
palette [ { Name, Properties } … ] — every DISTINCT block state, once
blocks [ { pos, state, nbt } … ] — one entry per placed block
palette enthält jeden einzelnen Blockzustand genau einmal. Ein Eintrag ist ein Name – die Block-ID, wie minecraft:oak_stairs – plus ein optionales Properties-Compound, das seinen Blockzustand enthält, wie facing: north und half: bottom.
blocks wiederholt niemals einen Namen. Jeder Eintrag enthält ein pos aus drei Ganzzahlen und ein state, und state ist ein Index in die Palette, nicht eine Block-ID. Eichenholztreppen, die nach Norden zeigen, sind einmal String-Daten, und jeder weitere davon ist danach eine kleine Ganzzahl.
Das ist der ganze Trick. Ein 32 × 32 × 32 Volumen aus einfachem Stein ist ein Paletteneintrag und 32.768 Indexnummern – nicht 32.768 Kopien des Wortes minecraft:stone. Die Palette wächst mit der Anzahl der verschiedenen Dinge, die Sie gebaut haben, und die Blockliste wächst mit der Größe Ihres Baus. Das sind sehr unterschiedliche Zahlen, und nur eine davon ist normalerweise groß.
Zwei Konsequenzen, die es wert sind, um sie herum zu entwerfen
Blockzustände vervielfachen Ihre Palette, nicht Ihre Blockliste. Eichenholztreppen sind kein Paletteneintrag – sie sind einer pro tatsächlich verwendeter Ausrichtung. Vier Ausrichtungen, obere und untere Hälfte, gerade und sowohl innere als auch äußere Ecken: Derselbe Block kann ein Dutzend Palettenplätze belegen. Ein detailreicher Bau hat also eine große Palette und ein einfacher Bau nicht, unabhängig von der Größe.
Nur platzierte Blöcke erhalten einen Eintrag. Die Blockliste ist eine Liste, kein dichtes 3D-Array, und ein Eintrag, der nicht vorhanden ist, ist einfach nicht vorhanden – der Betrachter auf dieser Seite füllt sein Raster mit -1 und behandelt eine fehlende Koordinate als nichts Platziertes. Ein hohler Bau ist tatsächlich günstiger zu speichern als ein massiver Bau gleicher Abmessungen.
nbt ist pro Block und optional
Jeder Eintrag in blocks kann sein eigenes nbt-Compound enthalten, und die meisten tun dies nicht. Hier leben die Inhalte einer Truhe, der Text eines Schildes, der Mob eines Spawners und die Muster eines Banners – am einzelnen Block angebracht und nicht an der Palette.
Der Unterschied ist wichtig: Palettendaten werden geteilt und Blockdaten nicht. Zwei Truhen sind ein Paletteneintrag, weil sie denselben Blockzustand haben – aber jede behält ihr eigenes Inventar in ihrem eigenen nbt. Ändern Sie den Blockzustand, und Sie berühren die Palette; ändern Sie, was sich darin befindet, und Sie berühren einen Eintrag in der Blockliste.
Schicht für Schicht lesen
Der Betrachter hier ist bewusst 2D. Um Schicht y zu zeichnen, durchläuft er die Blockliste, behält Einträge, deren pos[1] gleich y ist, und sucht den state jedes Eintrags in der Palette nach. Das ist der gesamte Algorithmus, und es lohnt sich, ihn zu kennen, denn so würden Sie auch selbst eine dieser Dateien inspizieren: Es gibt keinen räumlichen Index zu erstellen und keine Dekompression über den Gzip-Wrapper hinaus. Das Format ist eine flache Liste, die Sie filtern.
Das bedeutet auch, dass eine Strukturdatei Ihnen nichts darüber sagt, wo sie in einer Welt hingehört. size ist ein Begrenzungsrahmen und pos-Werte sind relativ zu seiner Ecke. Die Platzierung ist Sache des Konstruktionsblocks, nicht der Datei.