The NbtBinary class provides functionality for reading and writing NBT (Named Binary Tag) format, which is used throughout Minecraft for structured data storage.
Overview
NBT is a binary format used in:
level.dat - World settings and metadata
- Block actors (tile entities) - Chests, signs, spawners, etc.
- Entities - Mobs, items, armor stands
- Structure files -
.mcstructure files
- Player data
Constructor
const nbt = new NbtBinary();
nbt.context = 'level.dat'; // Optional context for error messages
Loading NBT Data
fromBinary
(data: Uint8Array, littleEndian: boolean, isVarint: boolean, skipBytes?: number, stringsAreASCII?: boolean, processAsList?: boolean) => number
Parses NBT binary data.Parameters:
data - Binary data to parse
littleEndian - Use little-endian byte order (true for Bedrock)
isVarint - Use varint encoding for lengths
skipBytes - Number of bytes to skip at start (e.g., 8 for level.dat)
stringsAreASCII - Optimize for ASCII strings
processAsList - Parse as list of multiple root tags
Returns: Number of bytes readconst nbt = new NbtBinary();
const bytesRead = nbt.fromBinary(
fileBytes,
true, // littleEndian for Bedrock
false, // no varint
8 // skip 8-byte header in level.dat
);
Writing NBT Data
toBinary
() => Uint8Array | undefined
Serializes the NBT structure to binary format.const bytes = nbt.toBinary();
if (bytes) {
await file.setContent(bytes);
}
Accessing Data
Array of root tags. Most files have a single root.if (nbt.roots && nbt.roots.length > 0) {
const root = nbt.roots[0];
}
Convenience getter for the first root tag.const root = nbt.singleRoot;
if (root) {
const levelName = root.find('LevelName');
console.log(levelName?.valueAsString);
}
Creating NBT Structure
Creates or returns the single root compound tag.const root = nbt.ensureSingleRoot();
root.ensureTag('LevelName', NbtTagType.string).value = 'My World';
root.ensureTag('SpawnX', NbtTagType.int).value = 0;
root.ensureTag('SpawnY', NbtTagType.int).value = 100;
root.ensureTag('SpawnZ', NbtTagType.int).value = 0;
JSON Conversion
Converts NBT structure to JSON object.const json = nbt.getJson();
console.log(JSON.stringify(json, null, 2));
Converts NBT structure to JSON string.const jsonStr = nbt.getJsonString();
NbtBinaryTag
Individual NBT tags are represented by NbtBinaryTag:
Properties
Tag type (compound, list, int, string, etc.).
Tag name (empty for list children).
value
string | number | bigint | bigint[] | number[] | boolean | null
Tag value (type depends on tag type).
Methods
find
(name: string) => NbtBinaryTag | null
Recursively searches for a tag by name.const tag = root.find('LevelName');
if (tag) {
console.log(tag.valueAsString);
}
child
(name: string) => NbtBinaryTag | null
Gets a direct child tag by name.const abilities = root.child('abilities');
const mayFly = abilities?.child('mayfly');
ensureTag
(tagName: string, tagType: NbtTagType) => NbtBinaryTag
Gets or creates a tag.const levelName = root.ensureTag('LevelName', NbtTagType.string);
levelName.value = 'New Name';
addTag
(tagType: NbtTagType, tagName?: string) => NbtBinaryTag
Adds a new tag.const newTag = root.addTag(NbtTagType.int, 'MyValue');
newTag.value = 42;
removeTag
(tagName: string) => boolean
Removes a tag by name.root.removeTag('oldProperty');
Gets all child tags (excludes end tag).for (const child of root.getTagChildren()) {
console.log(`${child.name}: ${child.valueAsString}`);
}
Value Accessors
Gets value as string.const name = tag.valueAsString;
Gets value as integer.const x = tag.valueAsInt;
Gets value as bigint.const time = tag.valueAsBigInt;
Gets value as boolean.const enabled = tag.valueAsBoolean;
Gets list children as numeric array.const version = tag.valueAsNumericArray; // [1, 20, 0]
Parses string value as JSON.const layers = tag.valueAsJSONObject;
NBT Tag Types
enum NbtTagType {
end = 0, // End of compound
byte = 1, // 8-bit signed integer
short = 2, // 16-bit signed integer
int = 3, // 32-bit signed integer
long = 4, // 64-bit signed integer
float = 5, // 32-bit float
double = 6, // 64-bit float
byteArray = 7, // Array of bytes
string = 8, // UTF-8 string
list = 9, // List of tags (same type)
compound = 10, // Named tags
intArray = 11, // Array of ints
longArray = 12 // Array of longs
}
Example: Reading level.dat
import { NbtBinary, NbtTagType } from '@minecraft/creator-tools';
async function readLevelDat(file: IFile) {
await file.loadContent();
const bytes = file.content as Uint8Array;
const nbt = new NbtBinary();
nbt.context = 'level.dat';
// level.dat has 8-byte header before NBT data
nbt.fromBinary(bytes, true, false, 8);
const root = nbt.singleRoot;
if (!root) {
console.error('No root tag found');
return;
}
// Read basic properties
const levelName = root.find('LevelName')?.valueAsString;
const spawnX = root.find('SpawnX')?.valueAsInt;
const spawnY = root.find('SpawnY')?.valueAsInt;
const spawnZ = root.find('SpawnZ')?.valueAsInt;
const gameType = root.find('GameType')?.valueAsInt;
console.log(`World: ${levelName}`);
console.log(`Spawn: (${spawnX}, ${spawnY}, ${spawnZ})`);
console.log(`Game Type: ${gameType}`);
// Read experiments
const experiments = root.find('experiments');
if (experiments) {
const betaApis = experiments.child('gametest')?.valueAsBoolean;
console.log(`Beta APIs: ${betaApis}`);
}
}
Example: Modifying NBT
async function modifyLevelDat(file: IFile) {
await file.loadContent();
const bytes = file.content as Uint8Array;
const nbt = new NbtBinary();
nbt.fromBinary(bytes, true, false, 8);
const root = nbt.ensureSingleRoot();
// Modify properties
root.ensureTag('LevelName', NbtTagType.string).value = 'Modified World';
root.ensureTag('commandsEnabled', NbtTagType.byte).value = 1;
root.ensureTag('showcoordinates', NbtTagType.byte).value = 1;
// Enable experiment
const experiments = root.ensureTag('experiments', NbtTagType.compound);
experiments.ensureTag('gametest', NbtTagType.byte).value = 1;
// Save with 8-byte header
const nbtBytes = nbt.toBinary();
if (nbtBytes) {
const fullBytes = new Uint8Array(nbtBytes.length + 8);
fullBytes.set(nbtBytes, 8);
// Write header (version 10, length)
fullBytes[0] = 10; fullBytes[1] = 0; fullBytes[2] = 0; fullBytes[3] = 0;
fullBytes[4] = nbtBytes.length & 0xFF;
fullBytes[5] = (nbtBytes.length >> 8) & 0xFF;
fullBytes[6] = (nbtBytes.length >> 16) & 0xFF;
fullBytes[7] = (nbtBytes.length >> 24) & 0xFF;
file.setContent(fullBytes);
await file.saveContent();
}
}
Example: Reading Block Actor Data
function parseChestData(nbtBytes: Uint8Array) {
const nbt = new NbtBinary();
nbt.context = 'Chest';
nbt.fromBinary(nbtBytes, true, false, 0, true);
const root = nbt.singleRoot;
if (!root) return;
// Get chest position
const x = root.find('x')?.valueAsInt;
const y = root.find('y')?.valueAsInt;
const z = root.find('z')?.valueAsInt;
console.log(`Chest at (${x}, ${y}, ${z})`);
// Get items
const items = root.find('Items');
if (items) {
const itemList = items.getTagChildren();
console.log(`Contains ${itemList.length} items:`);
for (const item of itemList) {
const name = item.find('Name')?.valueAsString;
const count = item.find('Count')?.valueAsInt;
const slot = item.find('Slot')?.valueAsInt;
console.log(` Slot ${slot}: ${name} x${count}`);
}
}
}
Example: Creating NBT Structure
function createStructure() {
const nbt = new NbtBinary();
const root = nbt.ensureSingleRoot();
// Add basic types
root.ensureTag('name', NbtTagType.string).value = 'My Structure';
root.ensureTag('version', NbtTagType.int).value = 1;
root.ensureTag('size', NbtTagType.list).setListFromArray([16, 16, 16]);
// Add compound
const metadata = root.ensureTag('metadata', NbtTagType.compound);
metadata.ensureTag('author', NbtTagType.string).value = 'Player';
metadata.ensureTag('created', NbtTagType.long).value = BigInt(Date.now());
// Add list of compounds
const blocks = root.ensureTag('blocks', NbtTagType.list);
blocks.childTagType = NbtTagType.compound;
for (let i = 0; i < 10; i++) {
const block = blocks.addTag(NbtTagType.compound);
block.ensureTag('pos', NbtTagType.list).setListFromArray([i, 0, 0]);
block.ensureTag('state', NbtTagType.int).value = 0;
}
// Serialize
const bytes = nbt.toBinary();
return bytes;
}
Error Handling
The NBT classes implement IErrorable:
const nbt = new NbtBinary();
nbt.fromBinary(bytes, true, false, 8);
if (nbt.isInErrorState) {
console.error('NBT parsing errors:');
for (const error of nbt.errorMessages || []) {
console.error(` ${error.message}`);
if (error.context) {
console.error(` Context: ${error.context}`);
}
}
}