nucleation 0.3.4

A high-performance Minecraft schematic parser and utility library
Documentation

Nucleation

Nucleation is a high-performance Minecraft schematic engine written in Rust, with generated bindings for C, C++, JavaScript/TypeScript (WASM), Kotlin/JVM, Python, and PHP.

Crates.io npm PyPI

What it does

  • Schematic formats: read and write .litematic, Sponge .schem, WorldEdit .schematic, Bedrock .mcstructure, structure .nbt, and a fast binary snapshot format (.nusn), with format auto-detection.
  • World import and export: parse whole worlds (Anvil .mca region files, zipped or on-disk world folders, optionally bounded to a coordinate box) into a schematic, and export schematics back out as playable worlds. A streaming API processes worlds chunk by chunk in constant memory.
  • Cross-version conversion: convert block, block-entity, item, and entity data between Minecraft data versions (a Rust port of PaperMC's DataConverter), with loss reports on lossy down-converts.
  • Schematic building: a template system for building schematics from ASCII or Unicode layer art, a procedural building tool (spheres, cuboids, cylinders, bezier curves, and more, filled by solid, gradient, or shaded brushes), and SDF-based shape and terrain generation.
  • Redstone simulation: tick circuits headlessly via MCHPRS, inject and read signals at arbitrary positions, and drive circuits through a typed executor with named, typed inputs and outputs (booleans, integers, floats, ASCII).
  • Meshing and rendering: turn schematics into GLB/glTF or USDZ meshes using any resource pack, and render PNG previews on the GPU, headlessly.
  • Diffing and fingerprinting: structural diffs between schematics (added, removed, changed, swapped views), translation-invariant fingerprints, signatures, and duplicate detection.
  • Auto-stack: detect the repeating lattice in a build and re-stamp it to a new size, for example a 4-bit adder to 8-bit, or a 32x32 screen to 64x64.
  • Storage: a pluggable byte store (in-memory, filesystem, and S3, Redis, or Postgres behind feature flags) for moving schematics and renders around with a single URI.
  • Embedded scripting: generate schematics from Lua or JavaScript scripts.
  • Block database: a vendored copy of blockpedia (nucleation::blockpedia) — Minecraft block facts with texture-derived colors, palette and gradient generation, block-state queries and transforms, and Java-Bedrock blockstate and block-entity translation via Geyser mappings.

One API, seven languages

Since v0.3.0 every language binding is generated from a single annotated-Rust source of truth (src/bridge/) using Diplomat. The bindings are committed under bindings/, regenerated and diffed in CI so they can never go stale, and every language exposes the same types and methods with per-language casing and idioms:

Language Package Errors Naming
Rust nucleation crate (native API) Result snake_case
C Release archive (include/ + library) result structs Schematic_set_block
C++ Release archive (header-only over C ABI) diplomat::result set_block
JavaScript npm install nucleation exceptions setBlock
Kotlin/JVM Release JAR (JNA) kotlin.Result setBlock
Python pip install nucleation exceptions set_block
PHP Release archive (php/ + FFI) DiplomatError setBlock

Installation

# Rust
cargo add nucleation

# JavaScript / TypeScript (Node >= 18 or a bundler)
npm install nucleation

# Python (CPython 3.12+)
pip install nucleation

For C, C++, Kotlin, and PHP, download the platform archive or JAR from Releases, or build locally:

cargo build --release --lib --features bridge        # core surface
cargo build --release --lib --features bridge-full   # + meshing, simulation, rendering, scripting

What ships in the published packages

Published artifacts (npm, PyPI, release archives, JAR) contain the core feature set: schematics, formats, world import/export and streaming, builder, building tool, definition regions, diff/fingerprint, autostack, NBT helpers, SDF, and the in-memory/filesystem store.

Meshing, rendering, simulation, and scripting are compiled in when you build the native library yourself with --features bridge-full (or any subset, for example --features bridge,simulation). Simulation and meshing also work on WASM. See the per-language docs for details.

Quick start

Rust

use nucleation::UniversalSchematic;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut schematic = UniversalSchematic::new("demo".to_string());
    schematic.set_block_from_string(0, 0, 0, "minecraft:stone")?;
    schematic.set_block_from_string(1, 0, 0, "minecraft:lever[face=floor,facing=east]")?;

    let bytes = nucleation::formats::litematic::to_litematic(&schematic)?;
    let loaded = nucleation::formats::litematic::from_litematic(&bytes)?;
    assert_eq!(
        loaded.get_block(0, 0, 0).map(|b| b.name.as_str()),
        Some("minecraft:stone")
    );
    Ok(())
}

JavaScript

import { Schematic } from "nucleation";

const schematic = Schematic.create("demo");
schematic.setBlock(1, 2, 3, "minecraft:stone");
console.log(schematic.getBlockName(1, 2, 3)); // "minecraft:stone"

// Serialize to litematic bytes (base64 across the WASM boundary)
const bytes = Uint8Array.from(atob(schematic.toLitematicB64()), (c) => c.charCodeAt(0));
const loaded = Schematic.fromLitematic(bytes);

Python

import nucleation

schematic = nucleation.Schematic.create("demo")
schematic.set_block(1, 2, 3, "minecraft:stone")
print(schematic.get_block_name(1, 2, 3))  # "minecraft:stone"

schematic.save_to_file("demo.litematic")
loaded = nucleation.Schematic.load_from_file("demo.litematic")

Kotlin

import at.schem.nucleation.*

val schematic = Schematic.create("demo")
schematic.setBlock(1, 2, 3, "minecraft:stone").getOrThrow()
println(schematic.getBlockName(1, 2, 3).getOrThrow()) // "minecraft:stone"

PHP

<?php
require "php/index.php";

use Stencil\Lib;
use Stencil\Schematic;

Lib::init("/path/to/libnucleation.so");

$schematic = Schematic::create("demo");
$schematic->setBlock(1, 2, 3, "minecraft:stone");
echo $schematic->getBlockName(1, 2, 3); // "minecraft:stone"

C

#include "Schematic.h"
#include <string.h>

int main(void) {
    DiplomatStringView name = {"demo", 4};
    Schematic *s = Schematic_create(name);

    DiplomatStringView stone = {"minecraft:stone", 15};
    Schematic_set_block(s, 1, 2, 3, stone);

    char buf[256];
    DiplomatWrite w = diplomat_simple_write(buf, sizeof(buf));
    Schematic_get_block_name(s, 1, 2, 3, &w);

    Schematic_destroy(s);
    return 0;
}

Documentation

Full documentation lives in docs/:

Development

cargo test                                   # core test suite
cargo build --lib --features bridge          # build the bridge surface

# Regenerate the committed bindings from src/bridge/ (requires the diplomat-tool fork)
./tools/gen-bindings.sh

# End-to-end smoke tests for the generated bindings
./examples/bridge_smoke/c/run.sh
./examples/bridge_smoke/js/run.sh
./examples/bridge_smoke/python/run.sh
php -d ffi.enable=1 -r 'require "examples/bridge_smoke/php/main.php";'

CI regenerates the bindings and fails on any diff, checks coverage of the full pre-0.3.0 FFI surface, and runs the smoke tests on every push.

Minecraft block data

The block database (formerly the standalone blockpedia crate) lives in-tree at src/blockpedia/. Its data ships as gzipped snapshots in data/blockpedia/ — currently pinned to Minecraft 26.2 (Java block states from Mojang's own data generator, official block semantics — kind/base-block/tags/full-cube geometry, Bedrock block states, Geyser blockstate mappings, and a color cache derived from the vanilla texture pack) — and build.rs bakes them into static PHF tables at compile time. Normal builds never touch the network.

To refresh the Java data for a new Minecraft version (needs a JRE new enough for the server jar on PATH; MC 26.x wants Java 25+):

# 1. Vanilla report converter: downloads the server jar, runs Mojang's data
#    generator (--reports), and rebuilds prismarinejs_blocks.json.gz — the
#    report is authoritative for the block list, properties and state ids;
#    enrichment fields (transparency, hardness, light, ...) carry forward
#    from the previous snapshot, and blocks new in the version are enriched
#    from an analogue block or a model-shape heuristic over the client jar
#    (the run prints the added/removed diff and every derived fact).
#    Also rebuilds block_semantics.json.gz from official data only:
#    - kind + base block: the report's definition.type / definition.base_state
#      (stairs), plus a model-texture linkage for the other shape variants
#      (oak_slab renders with block/oak_planks, owned by oak_planks)
#    - tags: every data/minecraft/tags/block/** tag from the server jar's
#      inner (bundler) jar, nested #tag refs resolved
#    - full_cube: blockstate models root in a cube-family template or carry
#      a full 16x16x16 element
#    These drive BlockFacts::{kind, base_block, has_tag, is_full_cube},
#    blocks_by_tag/variants_of, and the BlockFilter/only_solid classifiers
#    (which no longer guess from name substrings).
cargo run --release --bin refresh-block-data --features mc-data-refresh

# 2. Texture colors: downloads the client jar, extracts block textures,
#    regenerates color_cache.json.gz (alpha-weighted averages + biome tints).
cargo run --release --bin fetch-texture-colors --features mc-data-refresh

Both tools take the version as an optional trailing arg (-- 26.2) and default to the manifest's latest release, so a routine bump needs no code edits. A normal cargo build afterwards bakes the new tables in.

The PrismarineJS blocks.json schema is kept as the on-disk format (PrismarineJS itself has no 26.x data). tests/blockpedia_data_refresh.rs guards data currency.

The refresh is also automated: .github/workflows/data-refresh.yml runs weekly (and on manual dispatch, optionally with an explicit version), compares the version manifest's latest.release against data/blockpedia/DATA_VERSION (a plain-text marker refresh-block-data rewrites on every run), and — when Mojang has shipped a new release — regenerates the snapshots and opens/updates a PR on data-refresh/<version>. The PR body carries the added/removed-block diff, file size deltas, and color coverage. Two failure modes are tolerated by design: refresh-bedrock-mappings may fail while GeyserMC's mappings lag the Java release (noted in the PR, previous mappings kept), and cargo test may fail because new blocks need human test updates (the PR is still opened, marked failing, with the failure tail).

Java ↔ Bedrock mappings

  • geyser_mappings.json.gz — regenerated from GeyserMC/mappings (blocks.nbt @ efe0f2c, "Mappings for Minecraft Java 26.2"). GeyserMC retired the old mappings-generator JSON dumps; the canonical data is now gzipped NBT: a bedrock_mappings list with one compound per Java blockstate in runtime state-id order (java side implicit by index; bedrock_identifier absent ⇒ same name as Java, state absent ⇒ bedrock default state). refresh-bedrock-mappings converts that back into the JSON schema build.rs consumes, reconstructing the java side from prismarinejs_blocks.json.gz (state-id enumeration validated 32,366/32,366 against the vanilla 26.2 report). All 32,366 Java 26.2 states are mapped (was 29,671 at the 1.21.x pin; all carried-over entries identical, +2,695 gained, none lost), so the identity fallback for unmapped blocks is currently unused:

    cargo run --release --bin refresh-bedrock-mappings --features mc-data-refresh -- \
        --data-version 4903   # java world data version, for provenance only
    
  • bedrock_block_states.json.gz — PrismarineJS data/bedrock/1.26.30/blockStates.json gzipped verbatim (content-identical to the previous snapshot; the per-state version field 1.21.60.33 is Bedrock's state-format version, which hasn't bumped since — the palette content is current and includes the 26.x cinnabar/sulfur blocks). Every bedrock_identifier emitted by the mappings exists in this palette.

License

MIT. See LICENSE.