Skip to content

Latest commit

 

History

History
244 lines (200 loc) · 9.43 KB

File metadata and controls

244 lines (200 loc) · 9.43 KB

API Guide

bedrock-world exposes two layers:

  • File-level helpers for level.dat and Bedrock little-endian NBT.
  • A lazy BedrockWorld handle backed by a WorldStorage implementation.

Fast Metadata Path

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.

Lazy World Path

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_blocking
  • classify_keys_blocking
  • list_chunk_positions_blocking
  • parse_chunk_blocking
  • scan_entities_blocking
  • scan_block_entities_blocking
  • scan_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
));

Render Index Path

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_blocking lists chunks that have render records.
  • list_render_chunk_positions_in_region_blocking probes only a viewport or export region using key-only prefix scans.
  • load_render_chunk_blocking, load_render_chunks_blocking, and load_render_region_blocking accept RenderChunkLoadOptions / RenderRegionLoadOptions with threading, pipeline, cancel, progress, and priority policies.

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::RenderChunkRegion {
    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::RenderChunkLoadOptions {
        priority: bedrock_world::RenderChunkPriority::DistanceFrom {
            chunk_x: 0,
            chunk_z: 0,
        },
        ..Default::default()
    },
).await?;

load_render_region_blocking returns RenderRegionData { 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.

RenderChunkLoadOptions::request selects one terrain contract, preventing callers from mixing incompatible surface, raw-height, layer, and biome flags:

  • RenderChunkRequest::ExactSurface loads block data and computes RenderChunkData::column_samples by scanning each X/Z column top-down. Surface block/height, relief support block/height, thin overlay, water context, biome, and source are derived from actual blocks, not raw heightmap records.
  • RenderChunkRequest::RawHeightMap keeps the raw Data2D/Data3D or LegacyTerrain heightmap path for diagnostics and does not build surface samples.
  • RenderChunkRequest::Layer { y } and RenderChunkRequest::Biome { y, .. } load only the data needed for fixed layer/cave or biome reads.

Raw heightmaps remain available through RenderChunkData::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, RenderChunkData::legacy_terrain is populated, the chunk is considered loaded even without Data2D/SubChunkPrefix, and RenderLoadStats::prefix_scans remains 0. Legacy biome samples are exposed through RenderChunkData::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 RenderChunkData.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.

Typed Bedrock Records

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:

  • MapRecordId validates and encodes map_<id> keys.
  • GlobalRecordKind classifies mobevents, Overworld, Nether, TheEnd, scoreboard, LocalPlayer, AutonomousEntities, and preserved unknown global names.
  • ActorUid and ActorDigestKey encode actorprefix<uid> and digp<x><z>[dimension].

Map and global records:

  • read_map_record_blocking, scan_map_records_blocking, write_map_record_blocking, and delete_map_record_blocking.
  • read_global_record_blocking, scan_global_records_blocking, write_global_record_blocking, and delete_global_record_blocking.
  • Async wrappers with the same names are available behind the default async feature.

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, and put_biome_storage_blocking cover Data2D/Data3D heightmap and biome storage.
  • scan_hsa_records_blocking, put_hsa_for_chunk_blocking, and delete_hsa_for_chunk_blocking cover Hardcoded Spawn Areas.
  • block_entities_in_chunk_blocking, put_block_entities_blocking, edit_block_entity_at_blocking, and delete_block_entity_at_blocking cover consecutive block-entity NBT compounds.
  • actors_in_chunk_blocking, put_actor_blocking, delete_actor_blocking, and move_actor_blocking cover legacy inline Entity reads and modern digp -> actorprefix writes.

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. 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()
    },
)?;

Parsing Modes

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.

Error Handling

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.