Crate fastnbt

Source
Expand description

fastnbt aims for fast deserializing and serializing of NBT data from Minecraft: Java Edition. This format is used by the game to store various things, such as the world data and player inventories.

  • For documentation and examples of serde (de)serialization, see ser and de.
  • For a serde_json-like Value type see Value.
  • To easily create values, see the nbt macro.
  • For NBT array types see ByteArray, IntArray, and LongArray.
  • For zero-copy NBT array types see borrow.

Both this and related crates are under one fastnbt Github repository.

[dependencies]
fastnbt = "2"

§Byte, Int and Long array types

The NBT format has 4 sequence types: lists, byte arrays, int arrays and long arrays. To preserve the distinction between NBT lists and arrays, NBT array data cannot be (de)serialized into sequences like Vec. To capture arrays, use ByteArray, IntArray, and LongArray. An actual NBT list can be captured by a Vec or other suitable container.

Use these in your own data structures. They all implement Deref for dereferencing into a slice.

For versions that borrow their data, see borrow.

An example of deserializing a section of a chunk:

use fastnbt::LongArray;
use serde::Deserialize;

#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct Section {
    pub block_states: Option<LongArray>,
    pub y: i8,
}

let buf: &[u8] = unimplemented!("get a buffer from somewhere");
let section: Section = fastnbt::from_bytes(buf).unwrap();
let states = section.block_states.unwrap();

for long in states.iter() {
    // do something
}

§Example: Player inventory

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

Here we

  • use serde’s renaming attributes to have rustfmt conformant field names,
  • use lifetimes to save on some string allocations (see de for more info), and
  • use the Value type to deserialize a field we don’t know the exact structure of.
 use std::borrow::Cow;
 use fastnbt::error::Result;
 use fastnbt::{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,

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

 #[derive(Deserialize, Debug)]
 struct InventorySlot<'a> {
     // We typically avoid allocating a string here.
     // See `fastnbt::de` docs for more info.
     id: Cow<'a, str>,

     // Also get the less structured properties of the object.
     tag: Option<Value>,

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

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

§Stream based parser

A lower level parser also exists in the stream module for use cases not requiring deserialization into Rust objects. You can use from_reader for full deserialization.

Re-exports§

pub use value::from_value;
pub use value::to_value;
pub use value::Value;

Modules§

borrow
This module contains types enabling ‘zero-copy’ capture of the array NBT types. These types retain a reference to the input data when deserializing, meaning the input has to live as long as the deserialized object. This can be hard to manage, but offers potential performance improvements. Measure! Usually the dominating factor in deserialization is decompressing the NBT data.
de
This module contains a serde deserializer. It can do most of the things you would expect of a typical serde deserializer, such as deserializing into:
error
Contains the Error and Result type used by the deserializer.
ser
This module contains a serde serializer for NBT data. This should be able to serialize most structures to NBT. Use to_bytes or to_writer. There are ‘with opts’ functions for more serialization control.
stream
Allows streaming of NBT data without prior knowledge of the structure.
value

Macros§

nbt
Produce a Value using JSON/SNBT-like syntax.

Structs§

ByteArray
NBT ByteArray that owns its data. This type preserves the exact NBT type when (de)serializing. This dereferences into a i8 slice, so should be usable basically anywhere a slice should be.
DeOpts
Options for customizing deserialization.
IntArray
NBT IntArray that owns its data. This type preserves the exact NBT type when (de)serializing. This dereferences into a i32 slice, so should be usable basically anywhere a slice should be.
LongArray
NBT LongArray that owns its data. This type preserves the exact NBT type when (de)serializing. This dereferences into a i64 slice, so should be usable basically anywhere a slice should be.
SerOpts
Options for customizing serialization.

Enums§

Tag
An NBT tag. This does not carry the value or the name of the data.

Functions§

from_bytes
Deserialize into a T from some NBT data. See the de module for more information.
from_bytes_with_opts
Similar to from_bytes but with options.
from_reader
Deserialize into a T from some NBT data. See the de module for more information.
to_bytes
Serialize some T into NBT data. See the ser module for more information.
to_bytes_with_opts
Serialize some T into NBT data. See the ser module for more information. The options allow you to set things like the root name of the compound when serialized.
to_writer
Serialize some T into NBT data. See the ser module for more information.
to_writer_with_opts
Serialize some T into NBT data. See the ser module for more information. The options allow you to set things like the root name of the compound when serialized.