10 releases
0.2.9 | Mar 19, 2024 |
---|---|
0.2.8 | Jun 2, 2023 |
0.2.7 | Apr 26, 2023 |
0.2.6 | Feb 27, 2022 |
0.1.0 |
|
#292 in Encoding
1,015 downloads per month
Used in 3 crates
230KB
5.5K
SLoC
quartz_nbt
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.
This crate is the standalone NBT crate for Quartz, a Minecraft server implementation in Rust.
Usage
View the documentation here for examples.
lib.rs
:
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!(matches!(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::io::{self, Flavor};
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();
io::write_nbt(&mut binary, Some("root-tag"), &compound, Flavor::Uncompressed);
let read_compound = io::read_nbt(&mut Cursor::new(binary), Flavor::Uncompressed).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).unwrap(), vec![1i8, 2, 3]);
assert!(i16::try_from(tag2).is_err()); // Type mismatch
let mut compound = NbtCompound::new();
compound.insert("foo", 123);
compound.insert("bar", -3.6f32);
assert!(compound.get::<_, i32>("fooz").is_err()); // Missing tag
assert!(compound.get::<_, i32>("bar").is_err()); // Type mismatch
Collection Types and Iteration
The NbtCompound
and NbtList
types are wrappers around Map
s
and Vec
s respectively. Because NbtTag
s 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 inner
, inner_mut
, and/or into_inner
.
Lists
Minecraft's NBT specification currently has special tags for arrays (or Vec
s in rust)
of i8
, i32
, and i64
. Thus, vecs of these types can be directly converted into NbtTag
s.
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);
let list = compound.get::<_, &[i32]>("list");
assert!(list.is_ok());
assert_eq!(list.unwrap(), [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!(matches!(iter.next(), Some(Ok("abc!"))));
assert!(matches!(iter.next(), Some(Ok("ijk!"))));
assert!(matches!(iter.next(), Some(Ok("xyz!"))));
assert!(matches!(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
NbtCompound
s have the same set of utility functions as NbtList
s, 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());
Dependencies
~1.7–2.3MB
~51K SLoC