27 releases (breaking)

0.31.0 Jan 29, 2024
0.30.0 Sep 23, 2023
0.29.0 Aug 14, 2023
0.28.0 Apr 8, 2023
0.14.1 Nov 27, 2020

#87 in Games

Download history 89/week @ 2023-12-18 235/week @ 2023-12-25 73/week @ 2024-01-01 42/week @ 2024-01-08 18/week @ 2024-01-15 5/week @ 2024-01-22 19/week @ 2024-01-29 127/week @ 2024-02-19 113/week @ 2024-02-26 71/week @ 2024-03-04 71/week @ 2024-03-11 31/week @ 2024-03-18 9/week @ 2024-03-25 124/week @ 2024-04-01

244 downloads per month
Used in 3 crates

MIT/Apache

3.5MB
14K SLoC

fastnbt project

fastnbt-shield fastnbt-version-shield fastnbt-docs-shield build-status-shield

fastsnbt-shield fastsnbt-version-shield fastsnbt-docs-shield

fastanvil-shield fastanvil-version-shield fastanvil-docs-shield

FastNBT is a serde serializer and deserializer for Minecraft: Java Edition's NBT format, including Value type and nbt! macro. For stringified NBT (sNBT) see FastSNBT.

FastAnvil allows rendering maps of worlds, and a Region for using the Region file format. Supports 1.20 down to 1.13 inclusive, and slightly flaky support for 1.12.

An in-browser Rust-to-WASM powered Minecraft map renderer demo is below.

Demos

Demo of Hermitcraft season 8 and more at owengage.com/anvil

alt rendered map

The anvil binary from fastnbt-tools can render your world leveraging all of your CPU.

Examples

A bunch of examples can be found in fastnbt/examples, fastanvil/examples and tools/src. Some examples are recreated below.

Example: editing level.dat

The following edits the world spawn to 250, 200, 250 (probably not a good idea!). Full example in fastnbt/examples directory.

#[derive(Serialize, Deserialize)]
struct LevelDat {
    #[serde(rename = "Data")]
    data: Data,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Data {
    spawn_x: i32,
    spawn_y: i32,
    spawn_z: i32,

    #[serde(flatten)]
    other: HashMap<String, Value>,
}

fn main() {
    let args: Vec<_> = std::env::args_os().collect();
    let file = std::fs::File::open(&args[1]).unwrap();
    let mut decoder = GzDecoder::new(file);
    let mut bytes = vec![];
    decoder.read_to_end(&mut bytes).unwrap();

    let mut leveldat: LevelDat = fastnbt::from_bytes(&bytes).unwrap();

    leveldat.data.spawn_x = 250;
    leveldat.data.spawn_y = 200;
    leveldat.data.spawn_z = 250;

    let new_bytes = fastnbt::to_bytes(&leveldat).unwrap();
    let outfile = std::fs::File::create("level.dat").unwrap();
    let mut encoder = GzEncoder::new(outfile, Compression::fast());
    encoder.write_all(&new_bytes).unwrap();
}

Example: print player inventory

This example demonstrates printing out a players inventory and ender chest contents from the player dat files found in worlds. We

  • use serde's renaming attribute to have rustfmt conformant field names,
  • use lifetimes to save on string allocations, and
  • use the Value type to deserialize a field we don't specify the exact structure of.
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct PlayerDat<'a> {
    data_version: i32,

    #[serde(borrow)]
    inventory: Vec<InventorySlot<'a>>,
    ender_items: Vec<InventorySlot<'a>>,
}

#[derive(Deserialize, Debug)]
struct InventorySlot<'a> {
    id: &'a str,        // We avoid allocating a string here.
    tag: Option<Value>, // Also get the less structured properties of the object.

    // We need to rename fields a lot.
    #[serde(rename = "Count")]
    count: i8,
}

fn main() {
    let args: Vec<_> = std::env::args().skip(1).collect();
    let file = std::fs::File::open(args[0].clone()).unwrap();

    // Player dat files are compressed with GZip.
    let mut decoder = GzDecoder::new(file);
    let mut data = vec![];
    decoder.read_to_end(&mut data).unwrap();

    let player: Result<PlayerDat> = from_bytes(data.as_slice());

    println!("{:#?}", player);
}

Usage

For the libraries

[dependencies]
fastnbt = "2"
fastanvil = "0.31"

For the anvil executable

cargo install fastnbt-tools

lib.rs:

For handling Minecraft's region format, Anvil. This crate is mostly to support creating maps of Minecraft worlds and is not stable (per-1.0). The Region struct is probably the most generally useful part in this crate.

This crate also contains a JavaChunk that allows deserializing 1.18 down to about 1.15 chunks into some structs. This doesn't record all information from a chunk however, eg entities are lost. It is not suitable for serializing back into a region.

You can create your own chunk structures to (de)serialize using fastnbt.

Region can be given a Read, Write and Seek type eg a file in order to read and write chunk data.

Crate features

  • render - This feature is enabled by default and encapsulates all world-rendering related functionality.

Dependencies

~2–13MB
~107K SLoC