14 breaking releases

0.24.0 Jan 7, 2022
0.23.0 Dec 10, 2021
0.22.0 Nov 12, 2021
0.21.0 Jul 17, 2021
0.14.1 Nov 27, 2020

#5 in Games

Download history 14/week @ 2021-09-25 8/week @ 2021-10-02 23/week @ 2021-10-09 11/week @ 2021-10-16 16/week @ 2021-10-23 9/week @ 2021-10-30 24/week @ 2021-11-06 15/week @ 2021-11-13 16/week @ 2021-11-20 32/week @ 2021-11-27 56/week @ 2021-12-04 32/week @ 2021-12-11 11/week @ 2021-12-18 17/week @ 2021-12-25 17/week @ 2022-01-01 34/week @ 2022-01-08

83 downloads per month
Used in fastnbt-tools



fastnbt project

Latest Version docs-shield Build Status

Serde deserializer for Minecraft: Java Edition's NBT format, and in-browser Rust-to-WASM powered Minecraft map renderer. Supports 1.18.


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

alt rendered map

This repository contains multiple related projects.

  • fastnbt: Fast serde deserializer for Minecraft: Java Edition's NBT data format.
  • fastanvil: For rendering Minecraft worlds to maps.
  • fastnbt-tools: Various tools for NBT/Anvil, notably a map renderer.

Aim to support only the latest version of Minecraft. Works with most versions, including 1.18 worlds. Endevour to support old chunks in worlds, but not extracting textures from older versions due to the added complexity it would require.

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

See fastnbt's README for performance comparison.

Serde deserializer example

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.
use fastnbt::error::Result;
use fastnbt::{de::from_bytes, Value};
use flate2::read::GzDecoder;
use serde::Deserialize;
use std::io::Read;

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct PlayerDat<'a> {
    data_version: i32,

    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);

Development priorities

These are the proirities for the project when it comes to development. Ideally we never sacrifice an earlier priority for the sake of a later one.

  1. Correctness. Worlds are rendered as accurately as possible.
  2. Speed. Worlds are rendered as fast as possible.
  3. Memory. Worlds are rendered without sucking up RAM.


For the libraries

fastnbt = "1"
fastanvil = "0.22"

For the anvil executable

cargo install fastnbt-tools


~64K SLoC