bedrock-world exposes two layers:
- File-level helpers for
level.datand Bedrock little-endian NBT. - A lazy
BedrockWorldhandle backed by aWorldStorageimplementation.
Use read_level_dat when a launcher or management tool only needs world
metadata. This path does not open LevelDB.
let document = bedrock_world::read_level_dat("world/level.dat".as_ref())?;
println!("level.dat version={}", document.version());Use write_level_dat_atomic for level.dat edits. It validates the serialized
bytes by parsing them back before replacing the file.
BedrockWorld::open(path, OpenOptions::default()) opens a world in read-only
mode and auto-detects db/CURRENT LevelDB worlds or old Pocket Edition
chunks.dat worlds. Open with OpenOptions { read_only: false, ..Default::default() }
only for explicit edit flows; high-level writes call ensure_writable and
return BedrockWorldErrorKind::ReadOnly on read-only handles before any storage
mutation. BedrockWorld::open_blocking exposes the same detection for CLI tools
and examples. Use targeted APIs instead of full-world parsing for UI flows:
list_players_blockingclassify_keys_blockinglist_chunk_positions_blockingparse_chunk_blockingscan_entities_blockingscan_block_entities_blockingscan_items_blocking
Async methods are wrappers over the blocking implementation and use
tokio::task::spawn_blocking.
OpenOptions::format can force WorldFormatHint::LevelDb or
WorldFormatHint::PocketChunksDat; the default Auto reports the result through
BedrockWorld::format().
let world = bedrock_world::BedrockWorld::open_blocking(
"world",
bedrock_world::OpenOptions::default(),
)?;
assert!(matches!(
world.format(),
bedrock_world::WorldFormat::LevelDb
| bedrock_world::WorldFormat::LevelDbLegacyTerrain
| bedrock_world::WorldFormat::PocketChunksDat
));Interactive renderers should not wait for list_chunk_positions_blocking before
painting the first viewport. Use the render-index APIs instead:
list_render_chunk_positions_blockinglists chunks that have render records.list_chunk_positions_in_region_blockingprobes only a viewport or export region using key-only prefix scans.query_chunk_data_blocking,query_chunk_data_many_blocking, andquery_chunk_region_blockingacceptChunkLoadOptions/WorldChunkQueryRegionLoadOptionswiththreading,pipeline,cancel,progress, andprioritypolicies.
Async wrappers with the same names are available behind the default async
feature. They use spawn_blocking and preserve cancellation/progress options.
The blocking implementation uses bounded local parallelism, not Rayon global
pool state.
let region = bedrock_world::WorldChunkQueryRegion {
dimension,
min_chunk_x: -32,
min_chunk_z: -32,
max_chunk_x: 31,
max_chunk_z: 31,
};
let positions = world.list_render_chunk_positions_in_region(
region,
bedrock_world::WorldScanOptions {
pipeline: bedrock_world::WorldPipelineOptions {
queue_depth: 64,
..Default::default()
},
..Default::default()
},
).await?;
let chunks = world.load_render_chunks(
positions,
bedrock_world::ChunkLoadOptions {
priority: bedrock_world::ChunkLoadPriority::DistanceFrom {
chunk_x: 0,
chunk_z: 0,
},
..Default::default()
},
).await?;query_chunk_region_blocking returns WorldChunkQueryRegionData { region, chunks, stats }.
Use stats.worker_threads, stats.queue_wait_ms, and
stats.subchunks_decoded to tune worker budgets without baking fixed time
thresholds into tests.
ChunkLoadOptions::data_request accepts a composable ChunkDataRequest.
Add only the payload that a consumer needs: surface_columns(policy),
layer(y), cave_slice(y), full_3d_indices(), height_map(),
biome(requirement), and block_entities(). The loader unions subchunk keys
for all requested representations, uses packed palette indices for a
surface-only request, and upgrades to full 3D indices only when a layer, cave,
or full-3D requirement needs random access. ChunkDataRequest remains a
legacy mutually-exclusive compatibility contract.
let request = bedrock_world::ChunkDataRequest::new()
.surface_columns(bedrock_world::ExactSurfaceSubchunkPolicy::HintThenVerify)
.height_map()
.biome(bedrock_world::BiomeDataRequirement::SurfaceColumns);
let options = bedrock_world::ChunkLoadOptions::for_data_request(request);Surface samples in ChunkData::column_samples are derived from actual
blocks, including relief support, thin overlays, water context, biome, and
source, rather than raw heightmap records.
Raw heightmaps remain available through ChunkData::height_map, but map
renderers should treat them as hints or diagnostics. Stats expose
computed_surface_columns, raw_height_mismatch_columns,
missing_subchunk_columns, legacy_fallback_columns,
legacy_biome_preferred_columns, and modern_biome_fallback_columns.
For old LevelDB worlds, render exact-batch loading always requests
ChunkRecordTag::LegacyTerrain (0x30). If that record exists,
ChunkData::legacy_terrain is populated, the chunk is considered loaded
even without Data2D/SubChunkPrefix, and ChunkLoadStats::prefix_scans
remains 0.
Legacy biome samples are exposed through ChunkData::legacy_biomes as
[biome_id, red, green, blue]; legacy_biome_colors is retained only as a
compatibility 0x00RRGGBB view. When both LegacyTerrain biome samples and
old Data2D/Data3D biome ids exist, exact surface sampling prefers the saved
legacy RGB sample and treats the numeric biome id as fallback only.
Exact render chunk batches preserve the input key/value association after
deduplication, priority sorting, and parallel decode. Regression fixtures should
shuffle and duplicate ChunkPos values, then assert each returned
ChunkData.pos still carries the matching block, height, and biome
sentinels.
PocketChunksDatStorage is a read-only WorldStorage backend. It maps each
old 82,176-byte chunks.dat terrain payload to an 83,200-byte virtual
LegacyTerrain record by appending default legacy biome samples. put,
delete, and write_batch return UnsupportedChunkFormat.
v0.2 adds typed APIs for BedrockLevelFormat records that map editors usually
need without forcing a full-world parse. The storage boundary remains raw
key/value: bedrock-world owns Bedrock key classification, NBT codecs,
coordinate validation, and write roundtrip checks. This crate does not decide
how callers refresh or invalidate their presentation state after a write;
downstream applications and adapter crates map these semantic writes to their
own update model.
Key helpers:
MapRecordIdvalidates and encodesmap_<id>keys.GlobalRecordKindclassifiesmobevents,Overworld,Nether,TheEnd,scoreboard,LocalPlayer,AutonomousEntities, and preserved unknown global names.ActorUidandActorDigestKeyencodeactorprefix<uid>anddigp<x><z>[dimension].
Map and global records:
read_map_record_blocking,scan_map_records_blocking,write_map_record_blocking, anddelete_map_record_blocking.read_global_record_blocking,scan_global_records_blocking,write_global_record_blocking, anddelete_global_record_blocking.- Async wrappers with the same names are available behind the default
asyncfeature.
ParsedMapData now carries the validated record_id, parsed NBT roots,
known_fields, optional MapPixels, and raw bytes for preservation. The core
crate exposes map pixel buffers only; PNG/image export belongs in an adapter or
feature crate.
Chunk payload helpers:
get_heightmap_blocking,put_heightmap_blocking, andput_biome_storage_blockingcover Data2D/Data3D heightmap and biome storage.scan_hsa_records_blocking,put_hsa_for_chunk_blocking, anddelete_hsa_for_chunk_blockingcover Hardcoded Spawn Areas.block_entities_in_chunk_blocking,put_block_entities_blocking,edit_block_entity_at_blocking, anddelete_block_entity_at_blockingcover consecutive block-entity NBT compounds.actors_in_chunk_blocking,put_actor_blocking,delete_actor_blocking, andmove_actor_blockingcover legacy inlineEntityreads and moderndigp -> actorprefixwrites.delete_chunk_positions_blockingremoves a deduplicated set of chunks and their modern actor records in one atomic storage batch. The lower-levelStorageBatchAPI also exposesdelete_chunk,put_block_entities, andput_hsa_for_chunkfor composing a replacement commit.
All high-level writes validate the serialized value by parsing it back before
committing. Actor writes update actorprefix and digp in one transaction.
Block-entity writes reject coordinates outside the target chunk. chunks.dat
backends stay read-only. Chunk deletion also removes modern actor digest and
actorprefix records owned by the deleted chunks. Write examples should
therefore open a writable world:
let world = bedrock_world::BedrockWorld::open_blocking(
"world",
bedrock_world::OpenOptions {
read_only: false,
..bedrock_world::OpenOptions::default()
},
)?;LevelDB backend writes use synced WAL-backed write options. Use
BedrockWorld::compact_storage_blocking after a large write wave when an editor
needs to force backend compaction before another process opens the world.
McStructureFile reads and writes Bedrock .mcstructure files, which are
uncompressed little-endian NBT files containing a structure size, block index
arrays, a palette, and block entities. The helper can also export a world region
and place a structure into writable world storage:
let structure = bedrock_world::McStructureFile::read_from_path("house.mcstructure".as_ref())?;
let result = structure.write_to_world_blocking(
&world,
bedrock_world::McStructurePlacement {
source_anchor: bedrock_world::ChunkPos { x: 0, z: 0, dimension },
target_anchor: bedrock_world::ChunkPos { x: 8, z: -4, dimension },
origin_y: 64,
rotation: bedrock_world::McStructureRotation::Clockwise90,
mirror_x: false,
mirror_z: false,
},
Some(Box::new(|progress| eprintln!("{progress:?}"))),
)?;
println!("placed chunks={}", result.touched_chunks.len());Placement recomputes the heightmap columns touched by the new blocks and
commits world changes in batches of 16 chunks. WriteChunks progress events
are emitted after each batch is committed, so callers can safely refresh data
after the reported count.
Placement updates subchunk records, preserves supported block entities, and applies horizontal rotation or mirroring to common direction-like block states. Entity placement is intentionally left to downstream tools because entity NBT semantics are not block-grid local.
WorldParseOptions::summary() is the default for large scans. It keeps counters
and summaries while avoiding raw value retention.
WorldParseOptions::structured() keeps structured parsed entries without raw
values.
WorldParseOptions::full_raw() keeps raw values and full subchunk indices. Use
it for offline debugging, not interactive UI.
All public fallible APIs return bedrock_world::Result<T>.
Match BedrockWorldError::kind() for stable categories:
match error.kind() {
bedrock_world::BedrockWorldErrorKind::ReadOnly => {
// Ask the caller to reopen with OpenOptions { read_only: false }.
}
bedrock_world::BedrockWorldErrorKind::Cancelled => {
// A scan observed the caller's cancellation flag.
}
_ => eprintln!("{error}"),
}Avoid parsing display strings; they are meant for humans.