This entire project was developed entirely by AI.
A lightweight Minecraft world map rendering library written in Rust. It reads a world save, decodes its region/chunk data, resolves terrain and biome colors, and produces a raw RGBA bitmap — one block per pixel.
Minecraft world save
↓
region/chunk data
↓
terrain and biome analysis
↓
RGBA bitmap
conic-worldmap is the map preview backend for the Conic Launcher. It is read-only and intentionally narrow in scope:
- it is not a Minecraft editor,
- it never modifies world data,
- it provides no UI.
The renderer logic is a port of MCA Selector's ChunkRenderer_21w43a and its
post-processing TileImage.shade pass, and its color mapping data is taken
from MCA Selector.
- MCA region reading —
r.*.*.mcafiles in both world layouts: the legacy<world>/regionand the 1.21.6+<world>/dimensions/<ns>/<name>/regionlayout. - Chunk NBT parsing — three eras of the Java section format, detected
structurally rather than by DataVersion:
- 1.9–1.12
Blocks/Datapre-flattening sections, - 1.13–1.17
Palette/BlockStatessections, - 1.18+ root-level
block_states/biomessections, plus bit-packed long arrays and gzip/zlib/uncompressed/LZ4 compression.
- 1.9–1.12
- Biome-based coloring — per-version color tables (block state colors,
property-based state masks, and grass/foliage/water/dry-foliage biome
tints) with vanilla
(base * tint) >> 8tinting. Covers the 1.9–1.12 numeric block-id CSV, the 1.13–1.17 integer-biome-id JSON, and the 1.18+ namespaced-biome JSON. - Top-down terrain rendering — per-column surface scan with water-surface blending, slope shading and altitude shading (toggleable).
- RGBA output — raw RGBA bytes, 1 block = 1 pixel.
- Dimension support — open a specific dimension and enumerate the dimensions available in a world.
- Chunk caching — an in-memory LRU cache over parsed chunks (capacity 320 regions) to keep repeated renders cheap.
- Version detection — the color table is selected automatically from the world's DataVersion (< 1451 pre-flattening CSV, 1451–2833 legacy JSON, 2834+ modern JSON).
Add the dependency:
[dependencies]
conic-worldmap = "0.1"Open a world, request a rectangle around the spawn point, and read the RGBA bytes:
use conic_worldmap::{RenderOptions, RenderRequest, WorldMap};
// The overworld; `open_dimension` accepts e.g. "minecraft:the_nether".
let mut world = WorldMap::open_dimension("saves/MyWorld", "minecraft:overworld")
.expect("failed to open world");
let level = world.level();
println!(
"{} (data version {}) spawn ({}, {})",
level.level_name.as_deref().unwrap_or("(unnamed)"),
level.data_version,
level.spawn_x,
level.spawn_z,
);
let (sx, sz) = world.spawn();
let result = world
.render(&RenderRequest::new(sx, sz, 1024, 1024), &RenderOptions::default())
.expect("render failed");
// result.pixels: RGBA, 1024 * 1024 * 4 bytes, 1 block = 1 pixel.Key API:
WorldMap::open(world_dir)/WorldMap::open_dimension(world_dir, dimension)WorldMap::available_dimensions()— namespaced ids of renderable dimensionsWorldMap::spawn()— world spawn as(x, z)WorldMap::render(&RenderRequest, &RenderOptions) -> RenderResultRenderRequest::new(center_x, center_z, width, height)RenderOptions { water, shading, altitude_shading }(all default totrue)
WorldMap::render takes &mut self because rendering mutates the internal
chunk cache.
- 1 block = 1 pixel. A render is a fixed-size rectangle centered on
(center_x, center_z); the top-left pixel corresponds to(center_x - width / 2, center_z - height / 2). RenderResult.pixelsis raw RGBA bytes in row-major order (width * height * 4). Empty air renders as(0, 0, 0, 0)(transparent); rendered terrain is fully opaque.- The library returns pixel data only. Encode it as PNG/WebP yourself (see the
renderexample, which uses theimagecrate), and implement zoom/pan in the caller by issuing new render requests. The library does not downsample, so rendering an entire large world is a large buffer.
The examples/ directory ships four runnable tools:
render— render one rectangle of one world to a PNG:cargo run --release --example render -- [world_dir] [dimension] [png_path]previews— batch preview generator. Renders a PNG for every world save under a directory into an output folder. It reuses the library render pipeline but picks the view per world: it starts at the spawn, and if the spawn area is sparse it scans the region files for the densest 1024-block cluster (deterministic windowed chunk-density scoring, evaluated over the top candidates) and re-centers there. Near-empty skyblock/void worlds get a small 64×64 island-focus view instead.cargo run --release --example previews -- [worlds_root] [out_dir]diagnose— print a world's chunk formats, sections and non-empty block counts for sparse worlds (skyblock/void) to confirm what actually exists.probe— render a rectangle and print pixel statistics (opacity, vegetation/water/blue sky ratios, top colors) for validating a render.
The crate is split into small, single-purpose modules:
nbt— a minimal, lossless NBT reader (big-endian) that turns raw bytes into ownedNbtvalues. Only what world data needs.region— MCA region container reading: header parsing, chunk offsets, payload decompression (gzip, zlib, raw, LZ4 block streams).chunk— the parsed chunk model (Chunk/Section) and the bit-packed palette-index decode for both blocks and biomes. Mirrors the 1.18+ section format (block_states/biomes, palettes + packed long arrays) and the pre-flattening formats: 1.9–1.12Blocks/Datasections and 1.13–1.17Palette/BlockStatessections, plus the chunk-levelLegacyBiomes(per-column for 1.9–1.14, per-4×4×4-cell for 1.15–1.17).level—level.datparsing intoLevelInfo(DataVersion, spawn, name, game type, version), handling both the legacySpawnX/SpawnZand the 26.xspawn.posformat.color— parses the per-version color mapping into aColorTable: namespaced-block JSON with base64-encoded JavaBitSetstate masks, pre-flattening block-id/biome CSVs, and integer-biome-id JSON, resolving a block into aBlockDef(base color +TintKind) plus biome tint colors.render— the renderer: iterates chunks, scans each block column top-down for the first non-transparent block, applies biome tinting, tracks water depth, and runs the shade pass (water blend + slope/altitude shading).cache— an LRU cache over parsed chunks, keyed by region and indexed by chunk position, so panning renders reuse already-decoded chunks.
Color mapping data is embedded at compile time in src/mapping/:
java_1_9/colors.csv,java_1_9/biome_colors.csv(1.9–1.12)java_1_13/colors_17w47a.json,java_1_14/colors_18w43a.json,java_1_15/colors_19w34a.json,java_1_16/colors_20w06a.json,java_1_17/colors_20w45a.json(1.13–1.17)java_1_18/colors_21w37a.json,java_1_19/colors_22w11a.json,java_1_20/colors_23w12a.json,java_1_21/colors_24w18a.json,java_1_21/colors_25w32a.json,java_26/colors_26.{1,2,3}-snapshot-1.json(1.18+)color_properties.json
- Chunk parsing: Minecraft 1.9 through 26.x, detected structurally:
- 1.9–1.12 —
Level.SectionswithBlocks/Databyte arrays, - 1.13–1.17 —
Level.SectionswithPalette/BlockStates, - 1.18+ — root-level
block_states/biomescompounds (the format that is still current through 26.x).
- 1.9–1.12 —
- Color tables, keyed by minimum DataVersion:
< 1451(1.9–1.12) — the pre-flattening block-id + biome CSVs,1451–2833(1.13–1.17) — JSON with integer biome-id tints,>= 2834(1.18 through 26.3 snapshots) — JSON with namespaced biome tints. The table with the highestmin <= world DataVersionis selected; a DataVersion above the newest table falls back to the newest table.
- World layouts: legacy
<world>/regionand 1.21.6+<world>/dimensions/<namespace>/<name>/region. - Dimensions: any dimension with a region directory. Legacy worlds only have an overworld; opening any other dimension on a legacy world fails.
A 1024×1024 render of a test world, produced by conic-worldmap:
- 1 block = 1 pixel. There is no scaling/downsampling in the renderer.
- Read-only. conic-worldmap never writes to or modifies the world save.
- Versions before 1.9 are unsupported (older
Blocks/Datasection layouts and non-palette formats predate what the parser handles). - Pre-1.18 biomes are chunk-level, not per-column in 1.15–1.17: the 4×4×4 biome grid is sampled at the surface height, which is an approximation.
- Renders per request, synchronously. Large rectangles are slow on big worlds; the in-memory LRU cache only helps repeated/smaller renders.
- No entity, light, cave, slime-chunk or structure rendering.
- Blocks absent from the color table render white (modern) or black (pre-flattening), e.g. blocks added after the embedded table for the world's version.
- Water handling is a top-down approximation (waterlogged blocks, depth
blending and surface shading as in MCA Selector's
shadepass), not a faithful volumetric water model. - No UI, no map stitching/export of huge areas, and no incremental world updates.
These are planned directions, not implemented features:
- Additional rendering styles (e.g. caves, height maps, biome-only maps).
- Versions before 1.9.
- WASM integration so maps can render in a browser.
- An interactive, pannable world viewer built on this crate.
Special thanks to the MCA Selector project.
The map rendering logic and world data processing approach of this project were inspired by MCA Selector. Its excellent implementation provided valuable references for understanding Minecraft region data, chunk parsing, biome handling, and terrain visualization.
This project is an independent Rust implementation focused on providing a lightweight map preview library for Conic Launcher. It does not contain code copied from MCA Selector.
