mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
docs: document pumpkin-nbt (#2648)
This commit is contained in:
committed by
GitHub
parent
a2263c55cc
commit
0fe896892f
@@ -1,3 +1,5 @@
|
||||
//! Storage and convenience methods for NBT compound tags.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Display, Formatter};
|
||||
@@ -11,6 +13,9 @@ use std::collections::hash_map::IntoIter;
|
||||
use std::io::ErrorKind;
|
||||
|
||||
#[macro_export]
|
||||
/// Creates an [`NbtTag::Compound`](crate::tag::NbtTag::Compound) from key-value pairs.
|
||||
///
|
||||
/// The macro also accepts an empty invocation to create an empty compound tag.
|
||||
macro_rules! nbt_compound_tag {
|
||||
{ $($key:literal : $tag:expr),+ $(,)* } => {
|
||||
{
|
||||
@@ -32,10 +37,12 @@ macro_rules! nbt_compound_tag {
|
||||
///
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct NbtCompound {
|
||||
/// Tags in the compound, indexed by their names.
|
||||
pub child_tags: HashMap<Box<str>, NbtTag>,
|
||||
}
|
||||
|
||||
impl NbtCompound {
|
||||
/// Creates an empty compound.
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -43,6 +50,7 @@ impl NbtCompound {
|
||||
}
|
||||
}
|
||||
|
||||
/// Advances a reader past a compound's payload without allocating its tags.
|
||||
pub fn skip_content<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<(), Error> {
|
||||
loop {
|
||||
let tag_id = match reader.get_u8() {
|
||||
@@ -64,6 +72,7 @@ impl NbtCompound {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deserializes a compound payload, starting after the compound's name.
|
||||
pub fn deserialize_content<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<Self, Error> {
|
||||
let mut compound = Self::new();
|
||||
|
||||
@@ -87,6 +96,7 @@ impl NbtCompound {
|
||||
Ok(compound)
|
||||
}
|
||||
|
||||
/// Serializes the compound's entries followed by an end tag.
|
||||
pub fn serialize_content<W: NbtWriteHelper>(self, w: &mut W) -> Result<(), Error> {
|
||||
for (name, tag) in self.child_tags {
|
||||
w.write_u8(tag.get_type_id())?;
|
||||
@@ -97,52 +107,66 @@ impl NbtCompound {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` when the compound contains no child tags.
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.child_tags.is_empty()
|
||||
}
|
||||
|
||||
/// Inserts a tag when `name` is not already present.
|
||||
///
|
||||
/// Existing entries are left unchanged.
|
||||
pub fn put(&mut self, name: &str, value: impl Into<NbtTag>) {
|
||||
if !self.child_tags.contains_key(name) {
|
||||
self.child_tags.insert(name.into(), value.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a string tag when `name` is not already present.
|
||||
pub fn put_string(&mut self, name: &str, value: String) {
|
||||
self.put(name, NbtTag::String(value.into()));
|
||||
}
|
||||
|
||||
/// Inserts a list tag when `name` is not already present.
|
||||
pub fn put_list(&mut self, name: &str, value: Vec<NbtTag>) {
|
||||
self.put(name, NbtTag::List(value));
|
||||
}
|
||||
|
||||
/// Inserts a byte tag when `name` is not already present.
|
||||
pub fn put_byte(&mut self, name: &str, value: i8) {
|
||||
self.put(name, NbtTag::Byte(value));
|
||||
}
|
||||
|
||||
/// Inserts a boolean encoded as a byte tag when `name` is not already present.
|
||||
pub fn put_bool(&mut self, name: &str, value: bool) {
|
||||
self.put(name, NbtTag::Byte(i8::from(value)));
|
||||
}
|
||||
|
||||
/// Inserts a short tag when `name` is not already present.
|
||||
pub fn put_short(&mut self, name: &str, value: i16) {
|
||||
self.put(name, NbtTag::Short(value));
|
||||
}
|
||||
|
||||
/// Inserts an integer tag when `name` is not already present.
|
||||
pub fn put_int(&mut self, name: &str, value: i32) {
|
||||
self.put(name, NbtTag::Int(value));
|
||||
}
|
||||
/// Inserts a long tag when `name` is not already present.
|
||||
pub fn put_long(&mut self, name: &str, value: i64) {
|
||||
self.put(name, NbtTag::Long(value));
|
||||
}
|
||||
|
||||
/// Inserts a float tag when `name` is not already present.
|
||||
pub fn put_float(&mut self, name: &str, value: f32) {
|
||||
self.put(name, NbtTag::Float(value));
|
||||
}
|
||||
|
||||
/// Inserts a double tag when `name` is not already present.
|
||||
pub fn put_double(&mut self, name: &str, value: f64) {
|
||||
self.put(name, NbtTag::Double(value));
|
||||
}
|
||||
|
||||
/// Inserts a compound tag when `name` is not already present.
|
||||
pub fn put_compound(&mut self, name: &str, value: Self) {
|
||||
self.put(name, NbtTag::Compound(value));
|
||||
}
|
||||
@@ -162,73 +186,87 @@ impl NbtCompound {
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns the named byte value, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_byte(&self, name: &str) -> Option<i8> {
|
||||
self.get(name).and_then(super::tag::NbtTag::extract_byte)
|
||||
}
|
||||
|
||||
/// Returns the named tag.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get(&self, name: &str) -> Option<&NbtTag> {
|
||||
self.child_tags.get(name)
|
||||
}
|
||||
|
||||
/// Returns whether the compound contains `name`.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn has(&self, name: &str) -> bool {
|
||||
self.child_tags.contains_key(name)
|
||||
}
|
||||
|
||||
/// Returns the named short value, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_short(&self, name: &str) -> Option<i16> {
|
||||
self.get(name).and_then(super::tag::NbtTag::extract_short)
|
||||
}
|
||||
|
||||
/// Returns the named integer value, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_int(&self, name: &str) -> Option<i32> {
|
||||
self.get(name).and_then(super::tag::NbtTag::extract_int)
|
||||
}
|
||||
|
||||
/// Returns the named long value, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_long(&self, name: &str) -> Option<i64> {
|
||||
self.get(name).and_then(super::tag::NbtTag::extract_long)
|
||||
}
|
||||
|
||||
/// Returns the named float value, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_float(&self, name: &str) -> Option<f32> {
|
||||
self.get(name).and_then(super::tag::NbtTag::extract_float)
|
||||
}
|
||||
|
||||
/// Returns the named double value, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_double(&self, name: &str) -> Option<f64> {
|
||||
self.get(name).and_then(super::tag::NbtTag::extract_double)
|
||||
}
|
||||
|
||||
/// Returns the named byte as a boolean, where zero is `false`.
|
||||
#[must_use]
|
||||
pub fn get_bool(&self, name: &str) -> Option<bool> {
|
||||
self.get(name).and_then(super::tag::NbtTag::extract_bool)
|
||||
}
|
||||
|
||||
/// Returns the named string, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_string(&self, name: &str) -> Option<&str> {
|
||||
self.get(name).and_then(|tag| tag.extract_string())
|
||||
}
|
||||
|
||||
/// Returns the named list, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_list(&self, name: &str) -> Option<&[NbtTag]> {
|
||||
self.get(name).and_then(|tag| tag.extract_list())
|
||||
}
|
||||
|
||||
/// Returns the named compound, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_compound(&self, name: &str) -> Option<&Self> {
|
||||
self.get(name).and_then(|tag| tag.extract_compound())
|
||||
}
|
||||
|
||||
/// Returns the named integer array, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_int_array(&self, name: &str) -> Option<&[i32]> {
|
||||
self.get(name).and_then(|tag| tag.extract_int_array())
|
||||
}
|
||||
|
||||
/// Returns the named long array, or `None` if the tag is absent or has another type.
|
||||
#[must_use]
|
||||
pub fn get_long_array(&self, name: &str) -> Option<&[i64]> {
|
||||
self.get(name).and_then(|tag| tag.extract_long_array())
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Deserialization from Java Edition, unnamed network, and Bedrock NBT.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::io::{Cursor, Seek, SeekFrom};
|
||||
@@ -10,9 +12,14 @@ use io::Read;
|
||||
use serde::de::{self, DeserializeSeed, IntoDeserializer, MapAccess, SeqAccess, Visitor};
|
||||
use serde::{Deserialize, forward_to_deserialize_any};
|
||||
|
||||
/// Result type returned by NBT deserialization operations.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
thread_local! {
|
||||
/// Tag ID of the sequence currently visited by Serde.
|
||||
///
|
||||
/// This is used to preserve the distinction between NBT lists and the
|
||||
/// three specialized NBT array types.
|
||||
pub static CURR_VISITOR_LIST_TYPE: RefCell<Option<u8>> = const { std::cell::RefCell::new(None) };
|
||||
}
|
||||
|
||||
@@ -26,15 +33,28 @@ pub(super) fn set_curr_visitor_seq_list_id(tag: Option<u8>) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Byte source used by NBT read helpers.
|
||||
///
|
||||
/// Implementations may return borrowed strings and byte arrays when the
|
||||
/// underlying storage permits it.
|
||||
pub trait NbtDataSource<'a> {
|
||||
/// Reads one unsigned byte.
|
||||
fn read_u8(&mut self) -> Result<u8>;
|
||||
/// Fills `buf` with bytes from the source.
|
||||
fn read_bytes(&mut self, buf: &mut [u8]) -> Result<()>;
|
||||
/// Moves the current position by `offset` bytes.
|
||||
fn seek_relative(&mut self, offset: i64) -> Result<()>;
|
||||
/// Reads and decodes a string payload of `len` bytes.
|
||||
fn read_string(&mut self, len: usize) -> Result<Cow<'a, str>>;
|
||||
/// Reads a byte-array payload of `len` elements.
|
||||
fn read_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>>;
|
||||
}
|
||||
|
||||
pub struct NbtStreamReader<R>(pub R);
|
||||
/// Adapts a [`Read`] and [`Seek`] stream into an [`NbtDataSource`].
|
||||
pub struct NbtStreamReader<R>(
|
||||
/// Wrapped input stream.
|
||||
pub R,
|
||||
);
|
||||
|
||||
impl<'a, R: Read + Seek> NbtDataSource<'a> for NbtStreamReader<R> {
|
||||
fn read_u8(&mut self) -> Result<u8> {
|
||||
@@ -191,63 +211,88 @@ impl<'a> NbtDataSource<'a> for Cursor<Vec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Format-specific primitive reader used by the NBT parser.
|
||||
pub trait NbtReadHelper<'a> {
|
||||
/// Underlying byte source.
|
||||
type Reader: NbtDataSource<'a>;
|
||||
|
||||
/// Returns the underlying byte source.
|
||||
fn reader(&mut self) -> &mut Self::Reader;
|
||||
|
||||
/// Advances by `count` bytes.
|
||||
fn skip_bytes(&mut self, count: i64) -> Result<()> {
|
||||
self.reader().seek_relative(count)
|
||||
}
|
||||
/// Advances past an unsigned byte.
|
||||
fn skip_u8(&mut self) -> Result<()> {
|
||||
self.skip_bytes(1)
|
||||
}
|
||||
/// Advances past a signed byte.
|
||||
fn skip_i8(&mut self) -> Result<()> {
|
||||
self.skip_bytes(1)
|
||||
}
|
||||
/// Advances past a 16-bit signed integer.
|
||||
fn skip_i16(&mut self) -> Result<()> {
|
||||
self.skip_bytes(2)
|
||||
}
|
||||
/// Advances past a 32-bit signed integer.
|
||||
fn skip_i32(&mut self) -> Result<()> {
|
||||
self.skip_bytes(4)
|
||||
}
|
||||
/// Advances past a 64-bit signed integer.
|
||||
fn skip_i64(&mut self) -> Result<()> {
|
||||
self.skip_bytes(8)
|
||||
}
|
||||
/// Advances past a 32-bit floating-point number.
|
||||
fn skip_f32(&mut self) -> Result<()> {
|
||||
self.skip_bytes(4)
|
||||
}
|
||||
/// Advances past a 64-bit floating-point number.
|
||||
fn skip_f64(&mut self) -> Result<()> {
|
||||
self.skip_bytes(8)
|
||||
}
|
||||
/// Advances past a length-prefixed string.
|
||||
fn skip_string(&mut self) -> Result<()>;
|
||||
|
||||
/// Reads an unsigned byte.
|
||||
fn get_u8(&mut self) -> Result<u8>;
|
||||
/// Reads a signed byte.
|
||||
fn get_i8(&mut self) -> Result<i8>;
|
||||
/// Reads a 16-bit signed integer.
|
||||
fn get_i16(&mut self) -> Result<i16>;
|
||||
/// Reads a 32-bit signed integer.
|
||||
fn get_i32(&mut self) -> Result<i32>;
|
||||
/// Reads a 64-bit signed integer.
|
||||
fn get_i64(&mut self) -> Result<i64>;
|
||||
/// Reads a 32-bit floating-point number.
|
||||
fn get_f32(&mut self) -> Result<f32>;
|
||||
/// Reads a 64-bit floating-point number.
|
||||
fn get_f64(&mut self) -> Result<f64>;
|
||||
/// Reads a length-prefixed string.
|
||||
fn get_string(&mut self) -> Result<Cow<'a, str>>;
|
||||
/// Reads a byte array with the supplied element count.
|
||||
fn get_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>>;
|
||||
}
|
||||
|
||||
/// Reads Java Edition NBT primitives using big-endian numeric encoding.
|
||||
pub struct NbtReadHelperJava<D> {
|
||||
reader: D,
|
||||
}
|
||||
|
||||
impl<D> NbtReadHelperJava<D> {
|
||||
/// Creates a Java Edition reader over `r`.
|
||||
pub const fn new(r: D) -> Self {
|
||||
Self { reader: r }
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads Bedrock network NBT primitives using little-endian and variable-length encoding.
|
||||
pub struct NbtReadHelperBedrock<D> {
|
||||
reader: D,
|
||||
}
|
||||
|
||||
impl<D> NbtReadHelperBedrock<D> {
|
||||
/// Creates a Bedrock network reader over `r`.
|
||||
pub const fn new(r: D) -> Self {
|
||||
Self { reader: r }
|
||||
}
|
||||
@@ -409,6 +454,7 @@ impl<'a, D: NbtDataSource<'a>> NbtReadHelper<'a> for NbtReadHelperBedrock<D> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A Serde deserializer backed by a format-specific NBT reader.
|
||||
pub struct Deserializer<R> {
|
||||
input: R,
|
||||
tag_to_deserialize_stack: Option<u8>,
|
||||
@@ -417,6 +463,10 @@ pub struct Deserializer<R> {
|
||||
}
|
||||
|
||||
impl<R> Deserializer<R> {
|
||||
/// Creates a deserializer.
|
||||
///
|
||||
/// When `is_named` is `true`, the root compound name is consumed from the
|
||||
/// input. Unnamed network NBT must pass `false`.
|
||||
pub const fn new(input: R, is_named: bool) -> Self {
|
||||
Self {
|
||||
input,
|
||||
@@ -427,37 +477,43 @@ impl<R> Deserializer<R> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes struct using Serde Deserializer from normal NBT
|
||||
/// Deserializes a value from named Java Edition NBT.
|
||||
pub fn from_bytes<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> {
|
||||
let mut deserializer = Deserializer::new(NbtReadHelperJava::new(NbtStreamReader(r)), true);
|
||||
T::deserialize(&mut deserializer)
|
||||
}
|
||||
|
||||
/// Deserializes struct using Serde Deserializer from network NBT
|
||||
/// Deserializes a value from unnamed Java Edition network NBT.
|
||||
pub fn from_bytes_unnamed<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> {
|
||||
let mut deserializer = Deserializer::new(NbtReadHelperJava::new(NbtStreamReader(r)), false);
|
||||
T::deserialize(&mut deserializer)
|
||||
}
|
||||
|
||||
/// Deserializes struct using Serde Deserializer from Bedrock network NBT
|
||||
/// Deserializes a value from named Bedrock network NBT.
|
||||
pub fn from_bytes_bedrock<'a, T: Deserialize<'a>>(r: impl Read + Seek) -> Result<T> {
|
||||
let mut deserializer = Deserializer::new(NbtReadHelperBedrock::new(NbtStreamReader(r)), true);
|
||||
T::deserialize(&mut deserializer)
|
||||
}
|
||||
|
||||
/// Deserializes struct using Serde Deserializer from a normal NBT slice (zero-allocation)
|
||||
/// Deserializes a value from a named Java Edition NBT slice.
|
||||
///
|
||||
/// Strings and byte arrays may borrow directly from `slice`.
|
||||
pub fn from_slice<'a, T: Deserialize<'a>>(slice: &'a [u8]) -> Result<T> {
|
||||
let mut deserializer = Deserializer::new(NbtReadHelperJava::new(Cursor::new(slice)), true);
|
||||
T::deserialize(&mut deserializer)
|
||||
}
|
||||
|
||||
/// Deserializes struct using Serde Deserializer from a network NBT slice (zero-allocation)
|
||||
/// Deserializes a value from an unnamed Java Edition network NBT slice.
|
||||
///
|
||||
/// Strings and byte arrays may borrow directly from `slice`.
|
||||
pub fn from_slice_unnamed<'a, T: Deserialize<'a>>(slice: &'a [u8]) -> Result<T> {
|
||||
let mut deserializer = Deserializer::new(NbtReadHelperJava::new(Cursor::new(slice)), false);
|
||||
T::deserialize(&mut deserializer)
|
||||
}
|
||||
|
||||
/// Deserializes struct using Serde Deserializer from a Bedrock network NBT slice (zero-allocation)
|
||||
/// Deserializes a value from a named Bedrock network NBT slice.
|
||||
///
|
||||
/// Strings and byte arrays may borrow directly from `slice`.
|
||||
pub fn from_slice_bedrock<'a, T: Deserialize<'a>>(slice: &'a [u8]) -> Result<T> {
|
||||
let mut deserializer = Deserializer::new(NbtReadHelperBedrock::new(Cursor::new(slice)), true);
|
||||
T::deserialize(&mut deserializer)
|
||||
|
||||
@@ -1,3 +1,34 @@
|
||||
//! Reading, writing, and manipulating Minecraft's Named Binary Tag (NBT) data.
|
||||
//!
|
||||
//! The crate supports the standard Java Edition representation, unnamed network
|
||||
//! NBT, Bedrock network NBT, and gzip-compressed NBT. Data can be handled either
|
||||
//! as [`Nbt`] and [`NbtCompound`] values or through Serde with [`to_bytes`] and
|
||||
//! [`from_slice`].
|
||||
//!
|
||||
//! # Serde round trip
|
||||
//!
|
||||
//! ```
|
||||
//! use pumpkin_nbt::{from_slice, to_bytes};
|
||||
//! use serde::{Deserialize, Serialize};
|
||||
//!
|
||||
//! #[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
//! struct Player {
|
||||
//! name: String,
|
||||
//! health: i16,
|
||||
//! }
|
||||
//!
|
||||
//! let player = Player {
|
||||
//! name: "Steve".to_owned(),
|
||||
//! health: 20,
|
||||
//! };
|
||||
//! let mut encoded = Vec::new();
|
||||
//! to_bytes(&player, &mut encoded)?;
|
||||
//!
|
||||
//! let decoded: Player = from_slice(&encoded)?;
|
||||
//! assert_eq!(decoded, player);
|
||||
//! # Ok::<(), pumpkin_nbt::Error>(())
|
||||
//! ```
|
||||
|
||||
use std::{
|
||||
fmt::Display,
|
||||
io::{self, Write},
|
||||
@@ -11,11 +42,17 @@ use serializer::{NbtWriteHelper, NbtWriteHelperBedrock, NbtWriteHelperJava};
|
||||
use tag::NbtTag;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Compound-tag storage and construction helpers.
|
||||
pub mod compound;
|
||||
/// Serde and low-level NBT deserialization support.
|
||||
pub mod deserializer;
|
||||
/// Reading and writing gzip-compressed NBT.
|
||||
pub mod nbt_compress;
|
||||
/// Integration with Pumpkin's dynamic codec operations.
|
||||
pub mod nbt_ops;
|
||||
/// Serde and low-level NBT serialization support.
|
||||
pub mod serializer;
|
||||
/// The individual NBT tag types.
|
||||
pub mod tag;
|
||||
|
||||
pub use compound::NbtCompound;
|
||||
@@ -27,44 +64,70 @@ pub use serializer::{to_bytes, to_bytes_named, to_bytes_unnamed};
|
||||
|
||||
// This NBT crate is inspired from CrabNBT
|
||||
|
||||
/// Numeric identifier for an end tag.
|
||||
pub const END_ID: u8 = 0x00;
|
||||
/// Numeric identifier for a byte tag.
|
||||
pub const BYTE_ID: u8 = 0x01;
|
||||
/// Numeric identifier for a short tag.
|
||||
pub const SHORT_ID: u8 = 0x02;
|
||||
/// Numeric identifier for an integer tag.
|
||||
pub const INT_ID: u8 = 0x03;
|
||||
/// Numeric identifier for a long tag.
|
||||
pub const LONG_ID: u8 = 0x04;
|
||||
/// Numeric identifier for a float tag.
|
||||
pub const FLOAT_ID: u8 = 0x05;
|
||||
/// Numeric identifier for a double tag.
|
||||
pub const DOUBLE_ID: u8 = 0x06;
|
||||
/// Numeric identifier for a byte-array tag.
|
||||
pub const BYTE_ARRAY_ID: u8 = 0x07;
|
||||
/// Numeric identifier for a string tag.
|
||||
pub const STRING_ID: u8 = 0x08;
|
||||
/// Numeric identifier for a list tag.
|
||||
pub const LIST_ID: u8 = 0x09;
|
||||
/// Numeric identifier for a compound tag.
|
||||
pub const COMPOUND_ID: u8 = 0x0A;
|
||||
/// Numeric identifier for an integer-array tag.
|
||||
pub const INT_ARRAY_ID: u8 = 0x0B;
|
||||
/// Numeric identifier for a long-array tag.
|
||||
pub const LONG_ARRAY_ID: u8 = 0x0C;
|
||||
|
||||
/// Maximum number of elements accepted when decoding a list or array.
|
||||
pub const MAX_ARRAY_LENGTH: usize = 2_000_000;
|
||||
|
||||
/// Errors produced while reading, writing, or converting NBT data.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// The root tag was not a compound tag and contains the reported tag ID.
|
||||
#[error("The root tag of the NBT file is not a compound tag. Received tag id: {0}")]
|
||||
NoRootCompound(u8),
|
||||
/// A tag ID not defined by the NBT format was encountered.
|
||||
#[error("Encountered an unknown NBT tag id: {0}.")]
|
||||
UnknownTagId(u8),
|
||||
/// A Java CESU-8 string could not be decoded.
|
||||
#[error("Failed to Cesu 8 Decode")]
|
||||
Cesu8DecodingError,
|
||||
/// A string could not be decoded as UTF-8.
|
||||
#[error("Failed to UTF-8 Decode")]
|
||||
Utf8DecodingError,
|
||||
/// Serde reported an invalid value or serializer state.
|
||||
#[error("Serde error: {0}")]
|
||||
SerdeError(String),
|
||||
/// The requested Rust type has no NBT representation.
|
||||
#[error("NBT doesn't support this type: {0}")]
|
||||
UnsupportedType(String),
|
||||
/// The underlying reader or writer returned an I/O error.
|
||||
#[error("NBT reading was cut short: {0}")]
|
||||
Incomplete(io::Error),
|
||||
/// A list or array declared a negative element count.
|
||||
#[error("Negative list length: {0}")]
|
||||
NegativeLength(i32),
|
||||
/// A string, list, or array exceeded the supported length.
|
||||
#[error("Length too large: {0}")]
|
||||
LargeLength(usize),
|
||||
/// A Bedrock variable-length integer exceeded its maximum encoded size.
|
||||
#[error("Failed to decode varint - value too large")]
|
||||
VarIntTooLarge,
|
||||
/// A Bedrock variable-length long exceeded its maximum encoded size.
|
||||
#[error("Failed to decode varlong - value too large")]
|
||||
VarLongTooLarge,
|
||||
}
|
||||
@@ -81,13 +144,17 @@ impl de::Error for Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// A complete NBT document containing a named root compound.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct Nbt {
|
||||
/// Name stored alongside the root compound.
|
||||
pub name: String,
|
||||
/// Root compound containing the document's tags.
|
||||
pub root_tag: NbtCompound,
|
||||
}
|
||||
|
||||
impl Nbt {
|
||||
/// Creates a document from a root name and compound.
|
||||
#[must_use]
|
||||
pub const fn new(name: String, tag: NbtCompound) -> Self {
|
||||
Self {
|
||||
@@ -96,6 +163,9 @@ impl Nbt {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a named NBT document from a format-specific reader.
|
||||
///
|
||||
/// Returns [`Error::NoRootCompound`] when the first tag is not a compound.
|
||||
pub fn read<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<Self, Error> {
|
||||
let tag_type_id = reader.get_u8()?;
|
||||
|
||||
@@ -109,7 +179,9 @@ impl Nbt {
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads an NBT tag that doesn't contain the name of the root `Compound`.
|
||||
/// Reads an NBT document that omits the root compound's name.
|
||||
///
|
||||
/// The returned document has an empty [`Self::name`].
|
||||
pub fn read_unnamed<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<Self, Error> {
|
||||
let tag_type_id = reader.get_u8()?;
|
||||
|
||||
@@ -123,6 +195,7 @@ impl Nbt {
|
||||
})
|
||||
}
|
||||
|
||||
/// Serializes this document using the Java Edition NBT representation.
|
||||
#[must_use]
|
||||
pub fn write(self) -> Bytes {
|
||||
let mut bytes = Vec::new();
|
||||
@@ -136,6 +209,7 @@ impl Nbt {
|
||||
bytes.into()
|
||||
}
|
||||
|
||||
/// Serializes this document using the Bedrock network NBT representation.
|
||||
#[must_use]
|
||||
pub fn write_bedrock(self) -> Bytes {
|
||||
let mut bytes = Vec::new();
|
||||
@@ -149,17 +223,19 @@ impl Nbt {
|
||||
bytes.into()
|
||||
}
|
||||
|
||||
/// Writes this document in the Java Edition representation.
|
||||
pub fn write_to_writer<W: Write>(self, mut writer: W) -> Result<(), io::Error> {
|
||||
writer.write_all(&self.write())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes this document in the Bedrock network representation.
|
||||
pub fn write_to_writer_bedrock<W: Write>(self, mut writer: W) -> Result<(), io::Error> {
|
||||
writer.write_all(&self.write_bedrock())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes an NBT tag without a root `Compound` name.
|
||||
/// Serializes this document without the root compound's name.
|
||||
#[must_use]
|
||||
pub fn write_unnamed(self) -> Bytes {
|
||||
let mut bytes = Vec::new();
|
||||
@@ -171,6 +247,7 @@ impl Nbt {
|
||||
bytes.into()
|
||||
}
|
||||
|
||||
/// Writes this document without the root compound's name.
|
||||
pub fn write_unnamed_to_writer<W: Write>(self, mut writer: W) -> Result<(), io::Error> {
|
||||
writer.write_all(&self.write_unnamed())?;
|
||||
Ok(())
|
||||
@@ -215,6 +292,9 @@ pub(crate) const NBT_BYTE_ARRAY_TAG: &str = "__nbt_byte_array";
|
||||
|
||||
macro_rules! impl_array {
|
||||
($name:ident, $variant:expr) => {
|
||||
#[doc = "Serializes a sequence using its specialized NBT array representation."]
|
||||
#[doc = ""]
|
||||
#[doc = "Use this function with Serde's `serialize_with` field attribute."]
|
||||
pub fn $name<T: serde::Serialize, S: serde::Serializer>(
|
||||
input: T,
|
||||
serializer: S,
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
//! Helpers for reading and writing gzip-compressed NBT data.
|
||||
|
||||
use crate::deserializer::NbtReadHelperJava;
|
||||
use crate::{Error, Nbt, NbtCompound, deserializer, serializer};
|
||||
use flate2::{Compression, read::GzDecoder, write::GzEncoder};
|
||||
use std::io::{Cursor, Read, Seek, Write};
|
||||
|
||||
/// Reads a `GZipped` NBT compound tag from any reader.
|
||||
/// Reads a gzip-compressed, named NBT compound from a seekable reader.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `input` - Any type implementing the Read trait containing `GZipped` NBT data
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A Result containing either the parsed `NbtCompound` or an Error
|
||||
/// Decompressed data is limited to 64 MiB.
|
||||
pub fn read_gzip_compound_tag(input: impl Read + Seek) -> Result<NbtCompound, Error> {
|
||||
// Create a GZip decoder and directly chain it to the NBT reader
|
||||
let mut decoder = GzDecoder::new(input).take(64 * 1024 * 1024); // 64 MB limit
|
||||
@@ -24,18 +20,9 @@ pub fn read_gzip_compound_tag(input: impl Read + Seek) -> Result<NbtCompound, Er
|
||||
Ok(nbt.root_tag)
|
||||
}
|
||||
|
||||
/// Writes an NBT compound tag with `GZip` compression.
|
||||
/// Writes a named NBT compound with gzip compression.
|
||||
///
|
||||
/// This function takes an `NbtCompound` and writes it as a `GZipped` byte vector.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `compound` - The `NbtCompound` to serialize and compress
|
||||
/// * `output` - Any type implementing the Write trait where the compressed data will be written
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A Result containing either the compressed data as a byte vector or an Error
|
||||
/// The root name is written as an empty string.
|
||||
pub fn write_gzip_compound_tag(compound: NbtCompound, output: impl Write) -> Result<(), Error> {
|
||||
// Create a GZip encoder that writes to the output
|
||||
let mut encoder = GzEncoder::new(output, Compression::default());
|
||||
@@ -51,22 +38,18 @@ pub fn write_gzip_compound_tag(compound: NbtCompound, output: impl Write) -> Res
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Convenience function that returns compressed bytes
|
||||
/// Serializes a named NBT compound into a gzip-compressed byte vector.
|
||||
///
|
||||
/// The root name is written as an empty string.
|
||||
pub fn write_gzip_compound_tag_to_bytes(compound: NbtCompound) -> Result<Vec<u8>, Error> {
|
||||
let mut buffer = Vec::new();
|
||||
write_gzip_compound_tag(compound, &mut buffer)?;
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
/// Reads a `GZipped` NBT structure into a Rust type.
|
||||
/// Deserializes a value from gzip-compressed, named Java Edition NBT.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `input` - Any type implementing the Read trait containing `GZipped` NBT data
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A Result containing either the deserialized type or an Error
|
||||
/// Decompressed data is limited to 64 MiB.
|
||||
pub fn from_gzip_bytes<'a, T: serde::Deserialize<'a>, R: Read>(input: R) -> Result<T, Error> {
|
||||
// Create a GZip decoder and directly use it for deserialization
|
||||
let mut decoder = GzDecoder::new(input).take(64 * 1024 * 1024); // 64 MB limit
|
||||
@@ -75,16 +58,9 @@ pub fn from_gzip_bytes<'a, T: serde::Deserialize<'a>, R: Read>(input: R) -> Resu
|
||||
deserializer::from_bytes(Cursor::new(buf))
|
||||
}
|
||||
|
||||
/// Writes a Rust type as `GZipped` NBT to any writer.
|
||||
/// Serializes a value as gzip-compressed Java Edition NBT.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `value` - The value to serialize and compress
|
||||
/// * `output` - Any type implementing the Write trait where the compressed data will be written
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A Result indicating success or an Error
|
||||
/// The root name is written as an empty string.
|
||||
pub fn to_gzip_bytes<T: serde::Serialize, W: Write>(value: &T, output: W) -> Result<(), Error> {
|
||||
// Create a GZip encoder that writes to the output
|
||||
let encoder = GzEncoder::new(output, Compression::default());
|
||||
@@ -93,7 +69,9 @@ pub fn to_gzip_bytes<T: serde::Serialize, W: Write>(value: &T, output: W) -> Res
|
||||
serializer::to_bytes(value, encoder)
|
||||
}
|
||||
|
||||
/// Convenience function that returns compressed bytes
|
||||
/// Serializes a value into a gzip-compressed Java Edition NBT byte vector.
|
||||
///
|
||||
/// The root name is written as an empty string.
|
||||
pub fn to_gzip_bytes_vec<T: serde::Serialize>(value: &T) -> Result<Vec<u8>, Error> {
|
||||
let mut buffer = Vec::new();
|
||||
to_gzip_bytes(value, &mut buffer)?;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! NBT support for Pumpkin's dynamic serialization operations.
|
||||
|
||||
use crate::compound::NbtCompound;
|
||||
use crate::tag::NbtTag;
|
||||
use pumpkin_codecs::DataResult;
|
||||
@@ -11,7 +13,7 @@ use std::iter::Map;
|
||||
use std::vec::IntoIter;
|
||||
use tracing::warn;
|
||||
|
||||
/// A [`DynamicOps`] to serialize to/deserialize from NBT data.
|
||||
/// A [`DynamicOps`] implementation that represents values as [`NbtTag`]s.
|
||||
pub struct NbtOps;
|
||||
|
||||
impl DynamicOps for NbtOps {
|
||||
@@ -387,7 +389,7 @@ impl MapLike for NbtMapLike<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementation of [`StructBuilder`] for NBT objects.
|
||||
/// Builds NBT compounds for the [`NbtOps`] dynamic codec implementation.
|
||||
pub struct NbtStructBuilder {
|
||||
builder: DataResult<NbtTag>,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Serialization to Java Edition, unnamed network, and Bedrock NBT.
|
||||
|
||||
use serde::ser::Impossible;
|
||||
use serde::{Serialize, ser};
|
||||
use std::io::Write;
|
||||
@@ -8,6 +10,7 @@ use crate::{
|
||||
NBT_LONG_ARRAY_TAG, SHORT_ID, STRING_ID,
|
||||
};
|
||||
|
||||
/// Result type returned by NBT serialization operations.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
macro_rules! define_write_number_be {
|
||||
@@ -30,30 +33,44 @@ macro_rules! define_write_number_le {
|
||||
};
|
||||
}
|
||||
|
||||
/// Format-specific primitive writer used by the NBT serializer.
|
||||
pub trait NbtWriteHelper {
|
||||
/// Underlying output writer.
|
||||
type Writer: Write;
|
||||
|
||||
/// Returns the underlying output writer.
|
||||
fn writer(&mut self) -> &mut Self::Writer;
|
||||
/// Writes an unsigned byte.
|
||||
fn write_u8(&mut self, value: u8) -> Result<()>;
|
||||
/// Writes a signed byte.
|
||||
fn write_i8(&mut self, value: i8) -> Result<()>;
|
||||
/// Writes a 16-bit signed integer.
|
||||
fn write_i16(&mut self, value: i16) -> Result<()>;
|
||||
/// Writes a 32-bit signed integer.
|
||||
fn write_i32(&mut self, value: i32) -> Result<()>;
|
||||
/// Writes a 64-bit signed integer.
|
||||
fn write_i64(&mut self, value: i64) -> Result<()>;
|
||||
/// Writes a 32-bit floating-point number.
|
||||
fn write_f32(&mut self, value: f32) -> Result<()>;
|
||||
/// Writes a 64-bit floating-point number.
|
||||
fn write_f64(&mut self, value: f64) -> Result<()>;
|
||||
/// Writes a length-prefixed string.
|
||||
fn write_string(&mut self, value: &str) -> Result<()>;
|
||||
|
||||
/// Writes an unmodified byte slice.
|
||||
fn write_slice(&mut self, value: &[u8]) -> Result<()> {
|
||||
self.writer().write_all(value).map_err(Error::Incomplete)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes Java Edition NBT primitives using big-endian numeric encoding.
|
||||
pub struct NbtWriteHelperJava<W: Write> {
|
||||
writer: W,
|
||||
}
|
||||
|
||||
impl<W: Write> NbtWriteHelperJava<W> {
|
||||
/// Creates a Java Edition writer over `w`.
|
||||
pub const fn new(w: W) -> Self {
|
||||
Self { writer: w }
|
||||
}
|
||||
@@ -93,11 +110,13 @@ impl<W: Write> NbtWriteHelper for NbtWriteHelperJava<W> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes Bedrock network NBT primitives using little-endian and variable-length encoding.
|
||||
pub struct NbtWriteHelperBedrock<W: Write> {
|
||||
writer: W,
|
||||
}
|
||||
|
||||
impl<W: Write> NbtWriteHelperBedrock<W> {
|
||||
/// Creates a Bedrock network writer over `w`.
|
||||
pub const fn new(w: W) -> Self {
|
||||
Self { writer: w }
|
||||
}
|
||||
@@ -185,6 +204,7 @@ impl<W: Write> NbtWriteHelper for NbtWriteHelperBedrock<W> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A Serde serializer backed by a format-specific NBT writer.
|
||||
pub struct Serializer<W: NbtWriteHelper> {
|
||||
output: W,
|
||||
state: State,
|
||||
@@ -193,6 +213,9 @@ pub struct Serializer<W: NbtWriteHelper> {
|
||||
}
|
||||
|
||||
impl<W: NbtWriteHelper> Serializer<W> {
|
||||
/// Creates a serializer with an optional root compound name.
|
||||
///
|
||||
/// Passing `None` writes unnamed network NBT.
|
||||
pub const fn new(output: W, name: Option<String>) -> Self {
|
||||
Self {
|
||||
output,
|
||||
@@ -276,31 +299,33 @@ impl<W: NbtWriteHelper> Serializer<W> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes struct using Serde Serializer to unnamed (network) NBT
|
||||
/// Serializes a value as unnamed Java Edition network NBT.
|
||||
pub fn to_bytes_unnamed<T: Serialize>(value: &T, w: impl Write) -> Result<()> {
|
||||
let mut serializer = Serializer::new(NbtWriteHelperJava::new(w), None);
|
||||
value.serialize(&mut serializer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serializes struct using Serde Serializer to normal NBT
|
||||
/// Serializes a value as named Java Edition NBT using `name` for the root compound.
|
||||
pub fn to_bytes_named<T: Serialize>(value: &T, name: String, w: impl Write) -> Result<()> {
|
||||
let mut serializer = Serializer::new(NbtWriteHelperJava::new(w), Some(name));
|
||||
value.serialize(&mut serializer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serializes struct using Serde Serializer to Bedrock network NBT
|
||||
/// Serializes a value as named Bedrock network NBT using `name` for the root compound.
|
||||
pub fn to_bytes_named_bedrock<T: Serialize>(value: &T, name: String, w: impl Write) -> Result<()> {
|
||||
let mut serializer = Serializer::new(NbtWriteHelperBedrock::new(w), Some(name));
|
||||
value.serialize(&mut serializer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serializes a value as Java Edition NBT with an empty root name.
|
||||
pub fn to_bytes<T: Serialize>(value: &T, w: impl Write) -> Result<()> {
|
||||
to_bytes_named(value, String::new(), w)
|
||||
}
|
||||
|
||||
/// Serializes a value as Bedrock network NBT with an empty root name.
|
||||
pub fn to_bytes_bedrock<T: Serialize>(value: &T, w: impl Write) -> Result<()> {
|
||||
to_bytes_named_bedrock(value, String::new(), w)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! The in-memory representation of individual NBT tags.
|
||||
|
||||
use compound::NbtCompound;
|
||||
use deserializer::NbtReadHelper;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -9,21 +11,35 @@ use crate::{
|
||||
nbt_byte_array, nbt_int_array, nbt_long_array, serializer,
|
||||
};
|
||||
|
||||
/// A value represented by one of the tag types defined by the NBT format.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[repr(u8)]
|
||||
pub enum NbtTag {
|
||||
/// Marks the end of a compound.
|
||||
End = END_ID,
|
||||
/// An 8-bit signed integer.
|
||||
Byte(i8) = BYTE_ID,
|
||||
/// A 16-bit signed integer.
|
||||
Short(i16) = SHORT_ID,
|
||||
/// A 32-bit signed integer.
|
||||
Int(i32) = INT_ID,
|
||||
/// A 64-bit signed integer.
|
||||
Long(i64) = LONG_ID,
|
||||
/// A 32-bit floating-point number.
|
||||
Float(f32) = FLOAT_ID,
|
||||
/// A 64-bit floating-point number.
|
||||
Double(f64) = DOUBLE_ID,
|
||||
/// An array of 8-bit signed integers.
|
||||
ByteArray(Box<[i8]>) = BYTE_ARRAY_ID,
|
||||
/// A string.
|
||||
String(Box<str>) = STRING_ID,
|
||||
/// A sequence of tags.
|
||||
List(Vec<Self>) = LIST_ID,
|
||||
/// A map of named tags.
|
||||
Compound(NbtCompound) = COMPOUND_ID,
|
||||
/// An array of 32-bit signed integers.
|
||||
IntArray(Vec<i32>) = INT_ARRAY_ID,
|
||||
/// An array of 64-bit signed integers.
|
||||
LongArray(Vec<i64>) = LONG_ARRAY_ID,
|
||||
}
|
||||
|
||||
@@ -36,6 +52,7 @@ impl NbtTag {
|
||||
unsafe { *std::ptr::from_ref::<Self>(self).cast::<u8>() }
|
||||
}
|
||||
|
||||
/// Serializes the tag's type ID followed by its payload.
|
||||
pub fn serialize<W: NbtWriteHelper>(self, w: &mut W) -> serializer::Result<()> {
|
||||
w.write_u8(self.get_type_id())?;
|
||||
self.serialize_data(w)?;
|
||||
@@ -106,6 +123,7 @@ impl NbtTag {
|
||||
Self::Compound(compound)
|
||||
}
|
||||
|
||||
/// Serializes the tag payload without writing its type ID.
|
||||
pub fn serialize_data<W: NbtWriteHelper>(self, w: &mut W) -> serializer::Result<()> {
|
||||
match self {
|
||||
Self::End => {}
|
||||
@@ -175,11 +193,13 @@ impl NbtTag {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deserializes a type ID and its following payload.
|
||||
pub fn deserialize<'a, R: NbtReadHelper<'a>>(reader: &mut R) -> Result<Self, Error> {
|
||||
let tag_id = reader.get_u8()?;
|
||||
Self::deserialize_data(reader, tag_id)
|
||||
}
|
||||
|
||||
/// Advances a reader past the payload belonging to `tag_id`.
|
||||
pub fn skip_data<'a, R: NbtReadHelper<'a>>(reader: &mut R, tag_id: u8) -> Result<(), Error> {
|
||||
match tag_id {
|
||||
END_ID => Ok(()),
|
||||
@@ -239,6 +259,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes a payload whose type is identified by `tag_id`.
|
||||
pub fn deserialize_data<'a, R: NbtReadHelper<'a>>(
|
||||
reader: &mut R,
|
||||
tag_id: u8,
|
||||
@@ -347,6 +368,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained byte, if this is a byte tag.
|
||||
#[must_use]
|
||||
pub const fn extract_byte(&self) -> Option<i8> {
|
||||
match self {
|
||||
@@ -355,6 +377,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained short, if this is a short tag.
|
||||
#[must_use]
|
||||
pub const fn extract_short(&self) -> Option<i16> {
|
||||
match self {
|
||||
@@ -363,6 +386,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained integer, if this is an integer tag.
|
||||
#[must_use]
|
||||
pub const fn extract_int(&self) -> Option<i32> {
|
||||
match self {
|
||||
@@ -371,6 +395,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained long, if this is a long tag.
|
||||
#[must_use]
|
||||
pub const fn extract_long(&self) -> Option<i64> {
|
||||
match self {
|
||||
@@ -379,6 +404,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained float, if this is a float tag.
|
||||
#[must_use]
|
||||
pub const fn extract_float(&self) -> Option<f32> {
|
||||
match self {
|
||||
@@ -387,6 +413,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained double, if this is a double tag.
|
||||
#[must_use]
|
||||
pub const fn extract_double(&self) -> Option<f64> {
|
||||
match self {
|
||||
@@ -395,6 +422,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained byte as a boolean, where zero is `false`.
|
||||
#[must_use]
|
||||
pub fn extract_bool(&self) -> Option<bool> {
|
||||
match self {
|
||||
@@ -403,6 +431,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained byte array, if this is a byte-array tag.
|
||||
#[must_use]
|
||||
pub fn extract_byte_array(&self) -> Option<&[i8]> {
|
||||
match self {
|
||||
@@ -411,6 +440,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained string, if this is a string tag.
|
||||
#[must_use]
|
||||
pub fn extract_string(&self) -> Option<&str> {
|
||||
match self {
|
||||
@@ -419,6 +449,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained list, if this is a list tag.
|
||||
#[must_use]
|
||||
pub fn extract_list(&self) -> Option<&[Self]> {
|
||||
match self {
|
||||
@@ -427,6 +458,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained compound, if this is a compound tag.
|
||||
#[must_use]
|
||||
pub const fn extract_compound(&self) -> Option<&NbtCompound> {
|
||||
match self {
|
||||
@@ -435,6 +467,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained integer array, if this is an integer-array tag.
|
||||
#[must_use]
|
||||
pub fn extract_int_array(&self) -> Option<&[i32]> {
|
||||
match self {
|
||||
@@ -443,6 +476,7 @@ impl NbtTag {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the contained long array, if this is a long-array tag.
|
||||
#[must_use]
|
||||
pub fn extract_long_array(&self) -> Option<&[i64]> {
|
||||
match self {
|
||||
|
||||
Reference in New Issue
Block a user