Crate quartz_nbt[][src]

Expand description

Provides support for encoding and decoding Minecraft’s NBT format. This crate supports both zlib and gz compression, and also provides tools for converting NBT data to stringified NBT (SNBT) and vice versa.

Basic Usage

The basic unit of NBT data is the NbtTag. Larger data structures are represented through a tree of compounds (hash maps) and lists (vecs) of NBT tags.

Creating NBT Data

let mut compound = NbtCompound::new();
compound.insert("foo", 123);
compound.insert("bar", -3.6f32);

let mut list = NbtList::with_capacity(3);
(1i64..=3).for_each(|x| list.push(x));
compound.insert("list", list);

*compound.get_mut::<_, &mut i32>("foo").unwrap() += 1;

assert_eq!(compound.get::<_, i32>("foo"), Ok(124));
assert!(compound.get::<_, f64>("bar").is_err());
assert!(compound.get::<_, &NbtTag>("list").is_ok());

Reading and Writing NBT

use quartz_nbt::{read::read_nbt_uncompressed, write::write_nbt_uncompressed};
use std::io::Cursor;

let mut compound = NbtCompound::new();
compound.insert("foo", 123);
compound.insert("bar", -3.6f32);

let mut binary: Vec<u8> = Vec::new();
write_nbt_uncompressed(&mut binary, "root-tag", &compound);

let read_compound = read_nbt_uncompressed(&mut Cursor::new(binary)).unwrap();
assert_eq!(read_compound.1, "root-tag"); // The root tag's name is generally unused
assert_eq!(read_compound.0, compound);

Querying Tags

Generics are used to make the tag querying process as seamless as possible, however this allows for two types of errors to occur: missing tags (invalid key or index), and tag type mismatches. Thus, methods that would normally return an Option in std collection equivalents return a Result in this crate.

An error converting NBT tags directly into unwrapped values via TryFrom and TryInto is represented by an NbtStructureError. An error querying an NbtCompound or NbtList is represented by an NbtReprError, which is short for “NBT representation error.” See the error’s documentation for details.

use std::convert::TryFrom;

let tag1: NbtTag = vec![1i8, 2, 3].into();
let tag2: NbtTag = "abcde".into();

assert_eq!(Vec::<i8>::try_from(tag1), Ok(vec![1i8, 2, 3]));
assert_eq!(i16::try_from(tag2), Err(NbtStructureError::TypeMismatch));
let mut compound = NbtCompound::new();
compound.insert("foo", 123);
compound.insert("bar", -3.6f32);

assert_eq!(
    compound.get::<_, i32>("fooz"),
    Err(NbtReprError::Structure(NbtStructureError::MissingTag))
);
assert_eq!(
    compound.get::<_, i32>("bar"),
    Err(NbtReprError::Conversion(NbtStructureError::TypeMismatch))
);

Collection Types and Iteration

The NbtCompound and NbtList types are wrappers around HashMaps and Vecs respectively. Because NbtTags obscure the type of data actually stored, these wrappers provide utilities for unpacking tags into concrete types. If greater functionality is required, then the internal collection managed by these wrappers can be accessed through calls to as_ref and as_mut.

Lists

Minecraft’s NBT specification currently has special tags for arrays (or Vecs in rust) of i8, i32, and i64. Thus, vecs of these types can be directly converted into NbtTags. All other NBT-compatible types must be stored in an NbtList.

Obtaining the aforementioned special list types can be done through a regular query.

let mut compound = NbtCompound::new();
compound.insert("list", vec![10i32, 20, 30]);

compound.get_mut::<_, &mut [i32]>("list")
    .unwrap()
    .iter_mut()
    .for_each(|x| *x /= 10);

assert_eq!(compound.get::<_, &[i32]>("list"), Ok([1i32, 2, 3].as_ref()));

Utility methods are provided for NBT lists to iterate over unpacked values. See iter_map and iter_mut_map.

let mut list = NbtList::new();
list.push("abc");
list.push("ijk");
list.push("xyz");

list.iter_mut_map::<&mut String>()
    .for_each(|s| s.unwrap().push('!'));

let mut iter = list.iter_map::<&str>();
assert_eq!(iter.next(), Some(Ok("abc!")));
assert_eq!(iter.next(), Some(Ok("ijk!")));
assert_eq!(iter.next(), Some(Ok("xyz!")));
assert_eq!(iter.next(), None);

NBT lists can be created by cloning data from an iterator (or something which can be converted into an iterator) via clone_from.

let mut list1 = NbtList::new();
list1.push("abc");
list1.push("ijk");
list1.push("xyz");

let list2 = NbtList::clone_from(&["abc", "ijk", "xyz"]);

assert_eq!(list1, list2);

Compounds

NbtCompounds have the same set of utility functions as NbtLists, except for the obvious fact that compounds use string keys instead of indices. Similar to lists, compounds have iter_map and iter_mut_map utility functions, as well as a clone_from constructor. See the documentation for more details.

Stringified NBT (SNBT)

Minecraft also contains a string encoding of NBT data called SNBT. This encoding is basically an extension of JSON with stricter types and looser rules regarding string quotation. See the snbt module documentation for more details.

use quartz_nbt::snbt;

let tag: NbtTag = vec![10i8, 15, 20].into();
assert_eq!(tag.to_snbt(), "[B;10,15,20]");

let mut compound = NbtCompound::new();
compound.insert("short", -10i16);
compound.insert("string", "fizzbuzz");
compound.insert("array", vec![1i64, 1, 2, 3, 5]);

const SNBT: &str = "{short: -10s, string: fizzbuzz, array: [L; 1, 1, 2, 3, 5]}";

assert_eq!(compound, snbt::parse(SNBT).unwrap());

NBT Representation

The NbtRepr trait allows for custom types to be convertible into NbtTags by defining methods for writing and reading to and from an NbtCompound.

#[derive(Debug, PartialEq, Eq)]
struct Example {
    name: String,
    value: i32
}

impl NbtRepr for Example {
    type Error = NbtStructureError;

    fn read_nbt(&mut self, nbt: &NbtCompound) -> Result<(), Self::Error> {
        self.name = nbt.get::<_, &str>("name")?.to_owned();
        self.value = nbt.get("value")?;
        Ok(())
    }

    fn write_nbt(&self, nbt: &mut NbtCompound) {
        nbt.insert("name", &self.name);
        nbt.insert("value", self.value);
    }
}

let ex1 = Example {
    name: "foo".to_owned(),
    value: 10
};

let mut nbt = NbtCompound::new();
nbt.insert("name", "foo");
nbt.insert("value", 10);

let mut ex2 = Example {
    name: "".to_owned(),
    value: 0
};
ex2.read_nbt(&nbt);

assert_eq!(ex1.to_nbt(), nbt);
assert_eq!(ex1, ex2);

Currently, implementing this trait only allows for basic conversion into NbtTags and construction of compounds and lists via the clone_from_repr methods in each. In the future, we plan to create a derive macro for this trait as well as to more thoroughly integrate its use into this crate.

Modules

read

Contains utilities for reading binary NBT data.

snbt

Provides support for parsing stringified NBT data.

write

Contains utilities for writing binary NBT data.

Structs

NbtCompound

The NBT tag compound type which is essentially just a wrapper for a hash map of string keys to tag values.

NbtList

The NBT tag list type which is essentially just a wrapper for a vec of NBT tags.

Enums

NbtReprError

An error associated with the translation of a NBT representation to a concrete type. This can either be a structure error, meaning an error in the structure of the NBT tree, or a conversion error, meaning an error converting a tag into a concrete type.

NbtStructureError

An error associated with the structure of an NBT tag tree. This error represents a conflict between the expected and actual structure of an NBT tag tree.

NbtTag

The generic NBT tag type, containing all supported tag variants which wrap around a corresponding rust type.

Traits

NbtRepr

Defines a type which has a full representation as a NbtCompound.