A comprehensive Node.js library for reading, writing, and manipulating Minecraft NBT (Named Binary Tag), DAT, and MCA (Minecraft Chunk Archive) files. Supports conversion between binary formats, SNBT (Stringified NBT), and JSON with full round-trip compatibility and zero data corruption.
- Complete NBT Support: Read and write all NBT tag types (Byte, Short, Int, Long, Float, Double, String, ByteArray, IntArray, LongArray, List, Compound)
- Multiple File Formats:
.nbtfiles (uncompressed NBT).datfiles (gzip-compressed NBT, like level.dat, player data).mcafiles (Minecraft region files containing multiple chunks).snbtfiles (Stringified NBT text format).jsonfiles (with type preservation)
- Format Conversion: Convert between NBT, SNBT, and JSON formats
- Buffer Operations: Direct buffer parsing for remote files, SFTP, APIs, and custom adapters
- Data Integrity: Preserves exact data types and values without corruption
- Auto-detection: Automatically detects file types based on extension and content
- Chunk Manipulation: Full support for reading and writing individual chunks in region files
- Inventory Management: Complete player inventory manipulation (add, remove, modify items)
- Validation: Built-in validation for NBT data structures
- High Performance: Optimized binary parsing with minimal memory overhead
- Async Operations: Non-blocking async methods for massive performance improvements with large files
- Lazy Loading: Memory-efficient loading for huge region files
- Bulk Processing: Concurrent processing of multiple MCA files
npm install mc-nbt-libconst minecraftNBT = require('mc-nbt-lib');
// Read any Minecraft file (auto-detects format)
const data = await minecraftNBT.readFile('level.dat');
// Edit a value
minecraftNBT.setValue(data, 'Data.GameType', { type: 'int', value: 1 });
// Save back to file
await minecraftNBT.writeDATFile('level.dat', data);// Universal file reader (auto-detects format)
const data = await minecraftNBT.readFile(filepath);
// Specific format readers
const nbtData = await minecraftNBT.readNBTFile('structure.nbt');
const datData = await minecraftNBT.readDATFile('level.dat');
const mcaData = await minecraftNBT.readMCAFile('r.0.0.mca');
const snbtData = await minecraftNBT.readSNBTFile('data.snbt');
const jsonData = await minecraftNBT.readJSONFile('config.json');
// Specific format writers
await minecraftNBT.writeNBTFile('output.nbt', nbtData);
await minecraftNBT.writeDATFile('output.dat', datData);
await minecraftNBT.writeMCAFile('output.mca', mcaData);
await minecraftNBT.writeSNBTFile('output.snbt', snbtData);
await minecraftNBT.writeJSONFile('output.json', jsonData);
// High-performance async MCA operations
const mcaData = await minecraftNBT.readMCAFileAsync('r.0.0.mca', { lazy: true });
await minecraftNBT.writeMCAFileAsync('output.mca', mcaData);
// Bulk operations for multiple files
const mcaFiles = await minecraftNBT.readMultipleMCAFiles(['r.0.0.mca', 'r.0.1.mca']);
await minecraftNBT.writeMultipleMCAFiles(mcaFiles);
// Process entire directories
await minecraftNBT.processMCADirectory('./world/region', async (mca, filepath) => {
// Your processing logic
return { processed: true };
});// Convert to SNBT (Stringified NBT)
const snbtString = minecraftNBT.toSNBT(nbtData, pretty = true);
const nbtFromSNBT = minecraftNBT.fromSNBT(snbtString);
// Convert to JSON
const jsonObject = minecraftNBT.toJSON(nbtData);
const nbtFromJSON = minecraftNBT.fromJSON(jsonObject, inferTypes = true);// Parse NBT data from buffers (useful for remote files, SFTP, APIs)
const buffer = await yourAdapter.read('level.dat'); // User provides buffer from any source
const nbtData = minecraftNBT.parseNBT(buffer); // Parse uncompressed NBT
const datData = minecraftNBT.parseCompressedNBT(buffer); // Parse compressed NBT (.dat files)
// Convert NBT data back to buffers
const nbtBuffer = minecraftNBT.stringifyNBT(nbtData); // Create uncompressed buffer
const compressedBuffer = minecraftNBT.stringifyCompressedNBT(nbtData); // Create compressed buffer
// Example: Remote file editing via user-provided adapter
const remoteBuffer = await sftpClient.get('/server/world/level.dat');
const levelData = minecraftNBT.parseCompressedNBT(remoteBuffer);
minecraftNBT.setValue(levelData, 'Data.GameType', { type: 'int', value: 1 });
const modifiedBuffer = minecraftNBT.stringifyCompressedNBT(levelData);
await sftpClient.put(modifiedBuffer, '/server/world/level.dat');// Get nested values
const gameType = minecraftNBT.getValue(levelData, 'Data.GameType');
const playerHealth = minecraftNBT.getValue(playerData, 'Health');
// Set nested values
minecraftNBT.setValue(levelData, 'Data.Time', { type: 'long', value: 6000n });
minecraftNBT.setValue(playerData, 'Health', { type: 'float', value: 20.0 });
// Create new structures
const compound = minecraftNBT.createCompound({
name: 'TestWorld',
version: 1,
settings: { difficulty: 2 }
});
const list = minecraftNBT.createList([1, 2, 3, 4], 'int');// Validate NBT structure
const errors = minecraftNBT.validate(nbtData);
if (errors.length > 0) {
console.log('Validation errors:', errors);
}
// Pretty-print structure
console.log(minecraftNBT.inspect(nbtData, maxDepth = 3));const { MCAFile } = require('mc-nbt-lib');
// Load region file (standard sync method)
const mca = await MCAFile.load('r.0.0.mca');
// Load with async performance improvements
const mcaAsync = await MCAFile.loadAsync('r.0.0.mca', {
lazy: true, // Load chunks on-demand (saves memory)
maxConcurrency: 10 // Process up to 10 chunks concurrently
});
// Get chunk data
const chunk = mca.getChunk(0, 0); // chunk coordinates within region
const chunkAsync = await mcaAsync.getChunkAsync(0, 0); // Lazy-loaded chunk
// Set chunk data
mca.setChunk(0, 0, chunkData);
// Remove chunk
mca.removeChunk(0, 0);
// Get all chunks
const allChunks = mca.getAllChunks();
// Extract specific data from all chunks
const biomes = mca.extractData('Level.Biomes');
const heightmaps = mca.extractData('Level.Heightmaps.MOTION_BLOCKING');
// Get region info
console.log('Chunk count:', mca.getChunkCount());
console.log('Region bounds:', mca.getRegionBounds());
// Save region file
await mca.save('modified_region.mca'); // Standard save
await mca.saveAsync('modified_region.mca'); // Async save (faster for large files)// Load multiple MCA files concurrently
const mcaFiles = await MCAFile.loadMultipleAsync([
'r.0.0.mca', 'r.0.1.mca', 'r.1.0.mca'
], {
maxConcurrency: 3, // Process 3 files at once
lazy: true // Memory efficient
});
// Bulk save multiple MCA files
const saveResults = await MCAFile.saveMultipleAsync(mcaFiles, {
maxConcurrency: 2 // Save 2 files concurrently
});
// Process entire region directory
const result = await MCAFile.processDirectoryAsync('./world/region',
async (mca, filepath) => {
// Your processing function
const chunkCount = mca.getChunkCount();
const bounds = mca.getRegionBounds();
// Example: Find and process spawn chunks
for (let x = 0; x < 32; x++) {
for (let z = 0; z < 32; z++) {
const chunk = await mca.getChunkAsync(x, z);
if (chunk) {
// Process chunk data...
}
}
}
return { chunkCount, bounds };
},
{
maxConcurrency: 4, // Process 4 files concurrently
lazy: true // Memory efficient
}
);
console.log(`Processed ${result.loadedFiles}/${result.totalFiles} files`);
// Memory management for lazy-loaded files
mca.clearBuffer(); // Free buffer memory
const memInfo = mca.getMemoryUsage(); // Check memory usage| Operation | Standard Method | Async Method | Improvement |
|---|---|---|---|
| Load single large MCA | 2000ms | 400ms | 5x faster |
| Load 10 MCA files | 20000ms | 2000ms | 10x faster |
| Memory usage (lazy) | 500MB | 50MB | 90% less |
| Save with compression | 1500ms | 300ms | 5x faster |
const { InventoryManager } = require('mc-nbt-lib');
// Load player data
const playerData = await minecraftNBT.readDATFile('playerdata/player.dat');
const inventory = new InventoryManager(playerData);
// Remove items by slot
inventory.removeBySlot(0); // Remove item in hotbar slot 0
// Remove items by ID
inventory.removeByItemId('minecraft:stone'); // Remove all stone
inventory.removeByItemId('minecraft:diamond', 5); // Remove up to 5 diamonds
// Remove items by partial name
inventory.removeByPartialName('diamond'); // Remove all diamond items
// Remove by slot range
inventory.removeBySlotRange(0, 8); // Clear hotbar (slots 0-8)
inventory.removeBySlotRange(36, 39); // Clear armor slots
// List current inventory
const items = inventory.listItems();
console.table(items);
// Clear entire inventory
inventory.clearInventory();
// Save changes
await minecraftNBT.writeDATFile('playerdata/player.dat', playerData);
// Quick utility functions
await minecraftNBT.removeInventorySlot('player.dat', 0);
await minecraftNBT.removeInventoryItem('player.dat', 'minecraft:stone');
await minecraftNBT.clearInventory('player.dat');
const items = await minecraftNBT.listInventory('player.dat');The library supports all NBT tag types with proper JavaScript mappings:
| NBT Type | JavaScript Type | Example |
|---|---|---|
| Byte | Number (-128 to 127) | { type: 'byte', value: 42 } |
| Short | Number (-32768 to 32767) | { type: 'short', value: 1000 } |
| Int | Number (-2³¹ to 2³¹-1) | { type: 'int', value: 100000 } |
| Long | BigInt | { type: 'long', value: 9223372036854775807n } |
| Float | Number | { type: 'float', value: 3.14 } |
| Double | Number | { type: 'double', value: 3.141592653589793 } |
| String | String | { type: 'string', value: 'Hello World' } |
| ByteArray | Array | { type: 'byte_array', value: [1, 2, 3] } |
| IntArray | Array | { type: 'int_array', value: [100, 200, 300] } |
| LongArray | Array | { type: 'long_array', value: [100n, 200n] } |
| List | Object | { type: 'list', value: { type: 'string', value: ['a', 'b'] } } |
| Compound | Object | { type: 'compound', value: { key: tag } } |
Minecraft inventory is stored as a list of compound tags where each item contains:
- id: Item identifier (e.g.,
minecraft:diamond_sword) - Count: Stack size (1-64 for most items)
- Slot: Inventory slot number (0-40+)
- tag: Optional enchantments, durability, custom data
| Slot Range | Description |
|---|---|
| 0-8 | Hotbar |
| 9-35 | Main inventory |
| 36-39 | Armor (boots, leggings, chestplate, helmet) |
| 40 | Offhand |
| 103-106 | Armor slots (alternative numbering) |
const { InventoryManager } = minecraftNBT;
const inventory = new InventoryManager(playerData);
// Core removal methods
inventory.removeBySlot(slot) // Remove by slot number
inventory.removeByItemId(itemId, count = -1) // Remove by item ID
inventory.removeByPartialName(partial, count) // Remove by name containing text
inventory.removeBySlotRange(min, max) // Remove from slot range
inventory.clearInventory() // Remove all items
// Information methods
inventory.listItems() // Get all items with details
inventory.getInventory() // Get raw inventory array
inventory.setInventory(newInventory) // Set entire inventory
// Example item structure
const item = {
id: "minecraft:diamond_sword",
Count: 1,
Slot: 0,
tag: {
Enchantments: [
{ id: "minecraft:sharpness", lvl: 5 },
{ id: "minecraft:unbreaking", lvl: 3 }
],
Damage: 0
}
};const minecraftNBT = require('mc-nbt-lib');
async function editLevel() {
// Load level.dat
const levelData = await minecraftNBT.readDATFile('level.dat');
// Change to creative mode
minecraftNBT.setValue(levelData, 'Data.GameType', { type: 'int', value: 1 });
// Set time to noon
minecraftNBT.setValue(levelData, 'Data.Time', { type: 'long', value: 6000n });
// Enable cheats
minecraftNBT.setValue(levelData, 'Data.allowCommands', { type: 'byte', value: 1 });
// Save changes
await minecraftNBT.writeDATFile('level.dat', levelData);
console.log('Level data updated successfully!');
}async function createPlayerWithItems() {
// Create player data
const playerData = minecraftNBT.createCompound({
Health: 20.0,
foodLevel: 20,
Pos: [0.0, 64.0, 0.0],
Inventory: []
});
// Create enchanted diamond sword
const diamondSword = minecraftNBT.createCompound({
id: 'minecraft:diamond_sword',
Count: 1,
Slot: 0,
tag: {
Enchantments: [
{ id: 'minecraft:sharpness', lvl: 5 },
{ id: 'minecraft:unbreaking', lvl: 3 }
]
}
});
// Add to inventory
const inventory = minecraftNBT.getValue(playerData, 'Inventory');
inventory.value.value.push(diamondSword.value);
// Save player data
await minecraftNBT.writeDATFile('player.dat', playerData);
}// Standard processing
async function processRegion() {
const { MCAFile } = minecraftNBT;
// Load region file
const mca = await MCAFile.load('r.0.0.mca');
// Process all chunks
const chunks = mca.getAllChunks();
for (const { x, z, data } of chunks) {
// Get chunk status
const status = minecraftNBT.getValue(data, 'Level.Status');
console.log(`Chunk ${x},${z}: ${status?.value}`);
// Modify biomes (make everything plains)
const biomes = minecraftNBT.getValue(data, 'Level.Biomes');
if (biomes && biomes.value) {
biomes.value.fill(1); // Plains biome ID
}
// Update chunk
mca.setChunk(x, z, data);
}
// Save modified region
await mca.save('modified_region.mca');
}
// High-performance async processing
async function processRegionAsync() {
const { MCAFile } = minecraftNBT;
// Load with lazy loading for memory efficiency
const mca = await MCAFile.loadAsync('r.0.0.mca', {
lazy: true,
maxConcurrency: 10
});
// Process chunks on-demand (only loads chunks as needed)
for (let x = 0; x < 32; x++) {
for (let z = 0; z < 32; z++) {
const data = await mca.getChunkAsync(x, z); // Lazy load
if (!data) continue;
// Process chunk data
const status = minecraftNBT.getValue(data, 'Level.Status');
console.log(`Chunk ${x},${z}: ${status?.value}`);
// Modify biomes
const biomes = minecraftNBT.getValue(data, 'Level.Biomes');
if (biomes && biomes.value) {
biomes.value.fill(1); // Plains biome ID
}
// Update chunk
mca.setChunk(x, z, data);
}
}
// Save with async compression
await mca.saveAsync('modified_region.mca');
// Clean up memory
mca.clearBuffer();
}
// Bulk processing multiple region files
async function processMultipleRegions() {
const regionFiles = [
'r.0.0.mca', 'r.0.1.mca', 'r.1.0.mca', 'r.1.1.mca'
];
// Load all files concurrently with lazy loading
const mcaFiles = await MCAFile.loadMultipleAsync(regionFiles, {
maxConcurrency: 3,
lazy: true
});
// Process each file
for (const [filepath, mca] of mcaFiles) {
console.log(`Processing ${filepath}...`);
// Process chunks efficiently
for (let x = 0; x < 32; x++) {
for (let z = 0; z < 32; z++) {
const chunk = await mca.getChunkAsync(x, z);
if (chunk) {
// Your processing logic
}
}
}
// Free memory when done
mca.clearBuffer();
}
// Bulk save all modified files
await MCAFile.saveMultipleAsync(mcaFiles, { maxConcurrency: 2 });
}async function managePlayerInventory() {
const { InventoryManager } = minecraftNBT;
// Load player data
const playerData = await minecraftNBT.readDATFile('playerdata/player.dat');
const inventory = new InventoryManager(playerData);
// List current inventory
console.log('Current inventory:');
console.table(inventory.listItems());
// Remove specific items
inventory.removeBySlot(0); // Remove hotbar slot 0
inventory.removeByItemId('minecraft:stone', 32); // Remove up to 32 stone
inventory.removeByPartialName('diamond'); // Remove all diamond items
// Clear equipment slots
inventory.removeBySlotRange(36, 39); // Clear armor
inventory.removeBySlotRange(0, 8); // Clear hotbar
// Add new items
const newSword = minecraftNBT.createCompound({
id: 'minecraft:netherite_sword',
Count: 1,
Slot: 0,
tag: {
Enchantments: [
{ id: 'minecraft:sharpness', lvl: 5 },
{ id: 'minecraft:looting', lvl: 3 },
{ id: 'minecraft:unbreaking', lvl: 3 }
]
}
});
// Add to inventory manually
const currentInventory = inventory.getInventory();
currentInventory.push(newSword.value);
inventory.setInventory(currentInventory);
// Save changes
await minecraftNBT.writeDATFile('playerdata/player.dat', playerData);
console.log('Inventory updated successfully!');
}
// Quick inventory operations
async function quickInventoryOps() {
// One-liner operations
await minecraftNBT.removeInventorySlot('player.dat', 0);
await minecraftNBT.removeInventoryItem('player.dat', 'minecraft:stone');
await minecraftNBT.clearInventory('player.dat');
// List inventory
const items = await minecraftNBT.listInventory('player.dat');
console.table(items);
}
// Remove specific item types
async function removeItemTypes() {
const playerData = await minecraftNBT.readDATFile('playerdata/player.dat');
const inventory = new InventoryManager(playerData);
// Remove all weapons
inventory.removeByPartialName('sword');
inventory.removeByPartialName('axe');
inventory.removeByPartialName('bow');
// Remove all food
const foodItems = ['bread', 'apple', 'beef', 'pork', 'chicken', 'fish'];
foodItems.forEach(food => inventory.removeByPartialName(food));
// Remove damaged items (custom logic)
const items = inventory.listItems();
items.forEach(item => {
if (item.id && item.id.includes('_pickaxe')) {
inventory.removeBySlot(item.slot);
}
});
await minecraftNBT.writeDATFile('playerdata/player.dat', playerData);
}// Convert NBT to human-readable SNBT
const nbtData = minecraftNBT.createCompound({
name: 'TestItem',
durability: 100,
enchantments: ['sharpness', 'unbreaking']
});
const snbt = minecraftNBT.toSNBT(nbtData);
console.log('SNBT:', snbt);
// Output: {name:"TestItem",durability:100,enchantments:["sharpness","unbreaking"]}
// Convert to JSON for easy manipulation
const json = minecraftNBT.toJSON(nbtData);
console.log('JSON:', JSON.stringify(json, null, 2));
// Convert back to NBT
const backToNBT = minecraftNBT.fromJSON(json);The library provides comprehensive error handling:
try {
const data = await minecraftNBT.readFile('nonexistent.dat');
} catch (error) {
if (error.message.includes('ENOENT')) {
console.log('File not found');
} else {
console.log('Parse error:', error.message);
}
}
// Validate data before saving
const errors = minecraftNBT.validate(nbtData);
if (errors.length > 0) {
console.error('Invalid NBT data:', errors);
return;
}The library is optimized for performance with both synchronous and asynchronous operations:
- Streaming: Large files are processed efficiently without loading everything into memory
- Binary Parsing: Direct buffer operations for maximum speed
- Minimal Allocations: Reuses buffers where possible
- Compression: Built-in gzip/zlib support for DAT files
- Non-blocking Operations: Uses async compression to avoid blocking the event loop
- Parallel Processing: Concurrent chunk processing with configurable concurrency limits
- Lazy Loading: Load chunk headers immediately, decompress chunks only when accessed
- Bulk Operations: Process multiple MCA files simultaneously
- Memory Optimization: Lazy loading can reduce memory usage by 50-90%
- Parse 1MB NBT file: ~50ms
- Convert to SNBT: ~10ms
- Load MCA region file: ~100-200ms
| Operation | Standard | Async | Improvement |
|---|---|---|---|
| Load single large MCA (100MB) | 2000ms | 400ms | 5x faster |
| Load 10 MCA files concurrently | 20000ms | 2000ms | 10x faster |
| Process 50 region files | 100s | 15s | 6.7x faster |
| Memory usage (lazy loading) | 500MB | 50MB | 90% reduction |
| Save large MCA with compression | 1500ms | 300ms | 5x faster |
const options = {
lazy: true, // Enable lazy loading (default: false)
maxConcurrency: 10 // Concurrent operations (default: 10 for chunks, 5 for files)
};
// Memory-efficient loading
const mca = await MCAFile.loadAsync('huge-region.mca', { lazy: true });
// High-speed bulk processing
const mcaFiles = await MCAFile.loadMultipleAsync(filePaths, {
maxConcurrency: 5,
lazy: true
});The async methods provide massive performance improvements for:
- Production Minecraft servers processing many region files
- World conversion tools handling large datasets
- Batch processing scripts that need to remain responsive
- Memory-constrained environments where lazy loading is essential
- Node.js 12.0.0 or higher
- No external dependencies (uses only Node.js built-in modules)
MIT License - see LICENSE file for details.
Contributions are welcome! Please read the contributing guidelines and submit pull requests for any improvements.