Skip to content

owengage/fastnbt

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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. Includes partial support for 1.12 worlds.

An in-browser Rust-to-WASM powered Minecraft map renderer demo is below. Supports 1.19 down to 1.15 inclusive.

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.30"

For the anvil executable

cargo install fastnbt-tools