11 breaking releases

0.21.0 Jul 17, 2021
0.19.0 Jul 4, 2021
0.16.0 Mar 19, 2021
0.14.1 Nov 27, 2020

#7 in Games

Download history 27/week @ 2021-04-08 53/week @ 2021-04-15 13/week @ 2021-04-22 22/week @ 2021-04-29 5/week @ 2021-05-06 14/week @ 2021-05-13 12/week @ 2021-05-20 9/week @ 2021-05-27 14/week @ 2021-06-03 25/week @ 2021-06-10 17/week @ 2021-06-17 46/week @ 2021-06-24 44/week @ 2021-07-01 16/week @ 2021-07-08 42/week @ 2021-07-15 23/week @ 2021-07-22

90 downloads per month
Used in fastnbt-tools


6.5K SLoC

fastnbt project

NBT deserializer and in-browser Rust-to-WASM powered Minecraft map renderer.

Demo of Hermitcraft S7 and more at owengage.com/anvil:

alt rendered map

Useful places

This repository contains multiple related projects.

  • fastnbt: Fast (or trying to be!) deserializer and parser 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 1.17 worlds and 1.16 worlds at the moment. Might work for more. 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.

Serde deserializer example

This example demonstrates printing out a players inventory and ender chest contents from the player dat files found in worlds. We leverage serde's renaming attribute to have rustfmt conformant field names, use lifetimes to save on some 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 = "0.18"
fastanvil = "0.18"

For the anvil executable

cargo install fastnbt-tools


~87K SLoC