Minecraft Creator Tools provides comprehensive support for reading, modifying, and creating Minecraft worlds through the MCWorld class and NBT parsing utilities.
World Architecture
Minecraft worlds consist of several key components:
World Structure
├── level.dat (NBT format - world metadata)
├── level.dat_old (Backup)
├── levelname.txt (World name)
├── db/ (LevelDB - chunk data)
├── manifest.json (World metadata)
├── world_icon.jpeg (World thumbnail)
├── behavior_packs/ (Embedded packs)
└── resource_packs/ (Embedded packs)
Reference: app/src/minecraft/MCWorld.ts:1
NBT is Minecraft’s proprietary format for hierarchical typed data.
Reading NBT Data
import NbtBinary from "./minecraft/NbtBinary" ;
const nbt = new NbtBinary ();
// Parse NBT from bytes (little-endian for Bedrock)
nbt . fromBinary (
levelDatBytes ,
true , // littleEndian
false , // isVarint
0 , // skipBytes
true // stringsAreASCII
);
// Access the root tag
const root = nbt . singleRoot ;
if ( root ) {
const spawnX = root . find ( "SpawnX" )?. value ;
const spawnY = root . find ( "SpawnY" )?. value ;
const spawnZ = root . find ( "SpawnZ" )?. value ;
}
NBT Tag Types
enum NbtTagType {
end = 0 ,
byte = 1 ,
short = 2 ,
int = 3 ,
long = 4 ,
float = 5 ,
double = 6 ,
byteArray = 7 ,
string = 8 ,
list = 9 ,
compound = 10 ,
intArray = 11 ,
longArray = 12
}
// Find a tag by name
const tag = root . find ( "LevelName" );
if ( tag ) {
// Get value with type checking
const name = tag . valueAsString ;
// Or use type-specific accessors
const intValue = tag . valueAsInt ;
const bigIntValue = tag . valueAsBigInt ;
// Get child tags from compound
const children = tag . getTagChildren ();
}
// Convert to JSON
const json = nbt . getJsonString ();
console . log ( json );
// Convert back to binary
const bytes = nbt . toBinary ();
Reference: app/src/minecraft/NbtBinary.ts:1
Working with MCWorld
Loading a World
Create MCWorld instance
import MCWorld from "./minecraft/MCWorld" ;
// From a file (.mcworld)
const mcworld = await MCWorld . ensureOnFile ( worldFile , project );
// From a folder
const mcworld = await MCWorld . ensureMCWorldOnFolder ( worldFolder , project );
Load metadata
// Load level.dat and other meta files
await mcworld . loadMetaFiles ( false );
// Check if loaded
if ( mcworld . isLoaded ) {
console . log ( `World loaded: ${ mcworld . name } ` );
}
Access world properties
// Basic properties
console . log ( `Spawn: ${ mcworld . spawnX } , ${ mcworld . spawnY } , ${ mcworld . spawnZ } ` );
console . log ( `Game type: ${ mcworld . levelData ?. gameType } ` );
console . log ( `Difficulty: ${ mcworld . levelData ?. difficulty } ` );
// Experiments
console . log ( `Beta APIs: ${ mcworld . betaApisExperiment } ` );
console . log ( `Deferred Preview: ${ mcworld . deferredTechnicalPreviewExperiment } ` );
Modifying World Properties
// Set spawn point
mcworld . spawnX = 100 ;
mcworld . spawnY = 64 ;
mcworld . spawnZ = 200 ;
// Change world name
mcworld . name = "My Custom World" ;
// Enable experiments
mcworld . betaApisExperiment = true ;
mcworld . deferredTechnicalPreviewExperiment = true ;
mcworld . dataDrivenItemsExperiment = true ;
// Modify level data directly
if ( mcworld . levelData ) {
mcworld . levelData . gameType = 1 ; // Creative
mcworld . levelData . difficulty = 0 ; // Peaceful
}
// Save changes
await mcworld . save ();
World Settings
Apply comprehensive settings to a world:
import { Generator } from "./minecraft/WorldLevelDat" ;
const worldSettings = {
generator: Generator . flat , // World type
betaApisExperiment: true , // Enable beta APIs
seed: "minecraft" , // World seed
gameType: 1 , // 0=Survival, 1=Creative, 2=Adventure
difficulty: 2 , // 0=Peaceful, 1=Easy, 2=Normal, 3=Hard
};
await mcworld . applyWorldSettings ( worldSettings );
Generator.default - Default terrain
Generator.flat - Flat world
Generator.infinite - Infinite world
Generator.legacy - Legacy terrain
Generator.void - Void/empty world
Pack Management
Adding Behavior Packs
// Add a behavior pack to the world
const wasAdded = mcworld . ensureBehaviorPack (
"550e8400-e29b-41d4-a716-446655440000" , // Pack UUID
[ 1 , 0 , 0 ], // Version
"My Behavior Pack" , // Pack name
0 // Priority (optional)
);
if ( wasAdded ) {
console . log ( "Behavior pack added" );
}
// Check if pack exists
const bp = mcworld . getBehaviorPack ( "550e8400-e29b-41d4-a716-446655440000" );
if ( bp ) {
console . log ( `Pack version: ${ bp . version . join ( '.' ) } ` );
}
Adding Resource Packs
// Add a resource pack
mcworld . ensureResourcePack (
"660e8400-e29b-41d4-a716-446655440001" ,
[ 1 , 0 , 0 ],
"My Resource Pack" ,
0
);
// Add multiple packs from strings
const packString = "uuid1:1.0.0,uuid2:2.1.0" ;
mcworld . ensureBehaviorPacksFromString ( packString );
mcworld . ensureResourcePacksFromString ( packString );
Pack References
Add complete pack reference sets:
interface IPackageReference {
name : string ;
behaviorPackReferences ?: Array <{
uuid : string ;
version : number [];
priority ?: number ;
}>;
resourcePackReferences ?: Array <{
uuid : string ;
version : number [];
priority ?: number ;
}>;
}
const packRefSet : IPackageReference = {
name: "My Add-on" ,
behaviorPackReferences: [{
uuid: "550e8400-e29b-41d4-a716-446655440000" ,
version: [ 1 , 0 , 0 ],
priority: 0
}],
resourcePackReferences: [{
uuid: "660e8400-e29b-41d4-a716-446655440001" ,
version: [ 1 , 0 , 0 ],
priority: 0
}]
};
mcworld . ensurePackReferenceSet ( packRefSet );
LevelDB and Chunk Data
For advanced world manipulation, you can work with chunk data:
Loading LevelDB
// Load the LevelDB database
const loaded = await mcworld . loadLevelDb ( false , {
maxNumberOfRecordsToProcess: 10000 ,
progressCallback : ( phase , current , total ) => {
console . log ( ` ${ phase } : ${ current } / ${ total } ` );
}
});
if ( loaded ) {
console . log ( `Loaded ${ mcworld . chunkCount } chunks` );
}
Loading LevelDB can be memory-intensive for large worlds. Use progress callbacks and consider cleanup options.
Working with Chunks
// Get block at location
const block = mcworld . getBlock (
new BlockLocation ( 100 , 64 , 200 ),
0 // dimension: 0=overworld, 1=nether, 2=end
);
console . log ( `Block type: ${ block . type } ` );
// Get top block at X,Z
const topBlock = mcworld . getTopBlock ( 100 , 200 );
const topY = mcworld . getTopBlockY ( 100 , 200 );
console . log ( `Top block at (100, 200): ${ topBlock ?. type } at Y= ${ topY } ` );
Iterating Chunks
// Process all chunks
await mcworld . forEachChunk (
async ( chunk , x , z , dimension ) => {
console . log ( `Processing chunk at ${ x } , ${ z } in dimension ${ dimension } ` );
// Access chunk data
const topBlock = chunk . getTopBlock ( 8 , 8 ); // Middle of chunk
// Process blocks, entities, etc.
},
{
dimensionFilter: 0 , // Only overworld
clearCacheAfterProcess: true , // Free memory
progressCallback : async ( processed , total ) => {
console . log ( `Processed ${ processed } / ${ total } chunks` );
}
}
);
Memory Management
Large worlds consume significant memory. Use these methods to manage it: // Clear parsed data but keep raw bytes (recommended)
// Chunks can be re-parsed if needed
mcworld . clearAllChunkCaches ();
// Clear LevelDB data after processing
// WARNING: Cannot reload after this
mcworld . clearLevelDbData ();
// Clear all chunk data (most aggressive)
// WARNING: Chunks cannot be accessed after this
mcworld . clearAllChunkData ();
Best practice: Use clearCacheAfterProcess: true in forEachChunk for automatic cleanup.
Creating New Worlds
Generate a Fresh World
async function createWorld ( project : Project ) {
const mcworld = new MCWorld ();
mcworld . project = project ;
// Ensure storage (ZIP format)
mcworld . ensureZipStorage ();
// Set up level data
const levelDat = mcworld . ensureLevelData ();
levelDat . ensureDefaults ();
// Configure world
mcworld . name = "My New World" ;
mcworld . spawnX = 0 ;
mcworld . spawnY = 64 ;
mcworld . spawnZ = 0 ;
// Apply settings
await mcworld . applyWorldSettings ({
generator: Generator . flat ,
betaApisExperiment: true ,
gameType: 1 ,
difficulty: 0
});
// Add packs
mcworld . ensureBehaviorPack (
project . defaultBehaviorPackUniqueId ,
project . defaultBehaviorPackVersion ,
project . name
);
// Save
await mcworld . save ();
// Export as .mcworld
const bytes = await mcworld . getBytes ();
return bytes ;
}
Clone and Modify Existing World
async function cloneWorld (
sourceWorld : MCWorld ,
newName : string
) {
// Load source
await sourceWorld . loadMetaFiles ( false );
// Create new world
const newWorld = new MCWorld ();
newWorld . ensureZipStorage ();
// Copy to new storage
const targetFolder = newWorld . effectiveRootFolder ;
if ( targetFolder ) {
await sourceWorld . copyAsFolderTo ( targetFolder );
}
// Load and modify
await newWorld . loadMetaFiles ( false );
newWorld . name = newName ;
// Save
await newWorld . save ();
return newWorld ;
}
Practical Examples
async function getWorldInfo ( worldFile : IFile ) {
const mcworld = await MCWorld . ensureOnFile ( worldFile );
await mcworld . loadMetaFiles ( false );
const info = {
name: mcworld . name ,
spawn: {
x: mcworld . spawnX ,
y: mcworld . spawnY ,
z: mcworld . spawnZ
},
gameType: mcworld . levelData ?. gameType ,
difficulty: mcworld . levelData ?. difficulty ,
experiments: {
betaApis: mcworld . betaApisExperiment ,
deferredPreview: mcworld . deferredTechnicalPreviewExperiment ,
dataDrivenItems: mcworld . dataDrivenItemsExperiment
},
behaviorPacks: mcworld . worldBehaviorPacks ?. length ?? 0 ,
resourcePacks: mcworld . worldResourcePacks ?. length ?? 0
};
return info ;
}
Example 2: Bulk Pack Injection
async function injectPacks (
worldFile : IFile ,
packs : Array <{ type : 'behavior' | 'resource' , uuid : string , version : number [], name : string }>
) {
const mcworld = await MCWorld . ensureOnFile ( worldFile );
await mcworld . loadMetaFiles ( false );
for ( const pack of packs ) {
if ( pack . type === 'behavior' ) {
mcworld . ensureBehaviorPack ( pack . uuid , pack . version , pack . name );
} else {
mcworld . ensureResourcePack ( pack . uuid , pack . version , pack . name );
}
}
await mcworld . save ();
// Save back to file
const bytes = await mcworld . getBytes ();
if ( bytes ) {
worldFile . setContent ( bytes );
await worldFile . saveContent ();
}
}
Example 3: World Migration
async function migrateWorld (
oldWorld : MCWorld ,
newWorldSettings : IWorldSettings
) {
// Load old world
await oldWorld . loadMetaFiles ( false );
// Preserve important data
const oldPacks = {
behavior: oldWorld . worldBehaviorPacks || [],
resource: oldWorld . worldResourcePacks || []
};
// Apply new settings
await oldWorld . applyWorldSettings ( newWorldSettings );
// Restore pack references
oldWorld . worldBehaviorPacks = oldPacks . behavior ;
oldWorld . worldResourcePacks = oldPacks . resource ;
// Save migrated world
await oldWorld . save ();
console . log ( "World migrated successfully" );
}
async function analyzeWorld ( worldFolder : IFolder ) {
const mcworld = await MCWorld . ensureMCWorldOnFolder ( worldFolder );
await mcworld . loadLevelDb ( false );
const stats = {
totalChunks: mcworld . chunkCount ,
dimensions: {} as Record < number , number >,
regions: mcworld . regionsByDimension ,
bounds: {
minX: mcworld . minX ,
maxX: mcworld . maxX ,
minZ: mcworld . minZ ,
maxZ: mcworld . maxZ
}
};
// Count chunks per dimension
for ( const [ dim , xMap ] of mcworld . chunks . entries ()) {
let count = 0 ;
for ( const zMap of xMap . values ()) {
count += zMap . size ;
}
stats . dimensions [ dim ] = count ;
}
// Clean up memory
mcworld . clearAllChunkCaches ();
return stats ;
}
Best Practices
Load metadata first Always call loadMetaFiles() before accessing world properties.
Save after modifications Call save() after changing world properties or pack references.
Manage memory Use cache clearing for large worlds to prevent memory issues.
Validate pack UUIDs Ensure pack UUIDs are valid and unique before adding to worlds.
Troubleshooting
Check:
level.dat exists and is valid NBT
File is loaded (await file.loadContent())
File content is Uint8Array for .mcworld files
NBT parsing errors in error messages
Ensure:
await mcworld.save() is called
Storage is saved (await storage.rootFolder.saveAll())
For file-based worlds, save to file after getBytes()
Packs not loading in game
Verify:
Pack UUIDs match manifest.json exactly
Pack versions are valid arrays [major, minor, patch]
Both pack registration and history are updated
Experiments are enabled if packs require them
Out of memory with large worlds
Use memory management: // Process chunks with auto-cleanup
await mcworld . forEachChunk (
async ( chunk ) => { /* process */ },
{ clearCacheAfterProcess: true }
);
// Clear when done
mcworld . clearLevelDbData ();
Set all three coordinates: mcworld . spawnX = 0 ;
mcworld . spawnY = 64 ;
mcworld . spawnZ = 0 ;
await mcworld . save ();