Move to own NBT crate (Mostly) (#100)

* Remove fastnbt as dependency

* Move to own NBT crate

* Added deserialization

Also replaced i8 bool's with bool's

* Use: fastnbt for pumpkin-world

Until i don't figure out why Arrays do not work, we can use fastnbt
This commit is contained in:
Alexander Medvedev
2024-11-23 14:50:54 +01:00
committed by GitHub
parent e83e5d7df8
commit 402f3951f5
23 changed files with 27232 additions and 19064 deletions

View File

@@ -41,6 +41,8 @@ thiserror = "2"
num-traits = "0.2"
num-derive = "0.4"
bytes = "1.8"
# Concurrency/Parallelism and Synchronization
rayon = "1.10.0"
parking_lot = { version = "0.12.3", features = ["send_guard"] }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,12 @@ version.workspace = true
edition.workspace = true
[dependencies]
pumpkin-nbt = { path = "../pumpkin-nbt" }
serde.workspace = true
uuid.workspace = true
num-traits.workspace = true
num-derive.workspace = true
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
colored = "2"
md5 = "0.7.0"

View File

@@ -4,7 +4,6 @@ use std::borrow::Cow;
use click::ClickEvent;
use color::Color;
use colored::Colorize;
use fastnbt::SerOpts;
use hover::HoverEvent;
use serde::{Deserialize, Serialize};
use style::Style;
@@ -113,31 +112,31 @@ impl<'a> TextComponent<'a> {
/// Makes the text bold
pub fn bold(mut self) -> Self {
self.style.bold = Some(1);
self.style.bold = Some(true);
self
}
/// Makes the text italic
pub fn italic(mut self) -> Self {
self.style.italic = Some(1);
self.style.italic = Some(true);
self
}
/// Makes the text underlined
pub fn underlined(mut self) -> Self {
self.style.underlined = Some(1);
self.style.underlined = Some(true);
self
}
/// Makes the text strikethrough
pub fn strikethrough(mut self) -> Self {
self.style.strikethrough = Some(1);
self.style.strikethrough = Some(true);
self
}
/// Makes the text obfuscated
pub fn obfuscated(mut self) -> Self {
self.style.obfuscated = Some(1);
self.style.obfuscated = Some(true);
self
}
@@ -175,7 +174,10 @@ impl<'a> TextComponent<'a> {
};
// dbg!(&serde_json::to_string(&astruct));
fastnbt::to_bytes_with_opts(&astruct, SerOpts::network_nbt()).unwrap()
// TODO
pumpkin_nbt::serializer::to_bytes_unnamed(&astruct)
.unwrap()
.to_vec()
}
}

View File

@@ -13,23 +13,19 @@ pub struct Style<'a> {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub color: Option<Color>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bold: Option<u8>,
pub bold: Option<bool>,
/// Whether to render the content in italic.
/// Keep in mind that booleans are represented as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub italic: Option<u8>,
pub italic: Option<bool>,
/// Whether to render the content in underlined.
/// Keep in mind that booleans are represented as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub underlined: Option<u8>,
pub underlined: Option<bool>,
/// Whether to render the content in strikethrough.
/// Keep in mind that booleans are represented as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub strikethrough: Option<u8>,
pub strikethrough: Option<bool>,
/// Whether to render the content in obfuscated.
/// Keep in mind that booleans are represented as bytes in nbt
#[serde(default, skip_serializing_if = "Option::is_none")]
pub obfuscated: Option<u8>,
pub obfuscated: Option<bool>,
/// When the text is shift-clicked by a player, this string is inserted in their chat input. It does not overwrite any existing text the player was writing. This only works in chat messages
#[serde(default, skip_serializing_if = "Option::is_none")]
pub insertion: Option<String>,
@@ -54,31 +50,31 @@ impl<'a> Style<'a> {
/// Makes the text bold
pub fn bold(mut self) -> Self {
self.bold = Some(1);
self.bold = Some(true);
self
}
/// Makes the text italic
pub fn italic(mut self) -> Self {
self.italic = Some(1);
self.italic = Some(true);
self
}
/// Makes the text underlined
pub fn underlined(mut self) -> Self {
self.underlined = Some(1);
self.underlined = Some(true);
self
}
/// Makes the text strikethrough
pub fn strikethrough(mut self) -> Self {
self.strikethrough = Some(1);
self.strikethrough = Some(true);
self
}
/// Makes the text obfuscated
pub fn obfuscated(mut self) -> Self {
self.obfuscated = Some(1);
self.obfuscated = Some(true);
self
}

10
pumpkin-nbt/Cargo.toml Normal file
View File

@@ -0,0 +1,10 @@
[package]
name = "pumpkin-nbt"
version.workspace = true
edition.workspace = true
[dependencies]
serde.workspace = true
thiserror.workspace = true
bytes.workspace = true
cesu8 = "1.1.0"

163
pumpkin-nbt/src/compound.rs Normal file
View File

@@ -0,0 +1,163 @@
use crate::tag::NbtTag;
use crate::{get_nbt_string, Error, Nbt, END_ID};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use std::io::{Cursor, Write};
use std::vec::IntoIter;
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
pub struct NbtCompound {
pub child_tags: Vec<(String, NbtTag)>,
}
impl NbtCompound {
pub fn new() -> NbtCompound {
NbtCompound {
child_tags: Vec::new(),
}
}
pub fn deserialize_content(bytes: &mut impl Buf) -> Result<NbtCompound, Error> {
let mut compound = NbtCompound::new();
while bytes.has_remaining() {
let tag_id = bytes.get_u8();
if tag_id == END_ID {
break;
}
let name = get_nbt_string(bytes).map_err(|_| Error::Cesu8DecodingError)?;
if let Ok(tag) = NbtTag::deserialize_data(bytes, tag_id) {
compound.put(name, tag);
} else {
break;
}
}
Ok(compound)
}
pub fn deserialize_content_from_cursor(
cursor: &mut Cursor<&[u8]>,
) -> Result<NbtCompound, Error> {
Self::deserialize_content(cursor)
}
pub fn serialize_content(&self) -> Bytes {
let mut bytes = BytesMut::new();
for (name, tag) in &self.child_tags {
bytes.put_u8(tag.get_type_id());
bytes.put(NbtTag::String(name.clone()).serialize_data());
bytes.put(tag.serialize_data());
}
bytes.put_u8(END_ID);
bytes.freeze()
}
pub fn serialize_content_to_writer<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
writer.write_all(&self.serialize_content())?;
Ok(())
}
pub fn put(&mut self, name: String, value: impl Into<NbtTag>) {
if !self.child_tags.iter().any(|(key, _)| key == &name) {
self.child_tags.push((name, value.into()));
}
}
pub fn get_byte(&self, name: &str) -> Option<i8> {
self.get(name).and_then(|tag| tag.extract_byte())
}
#[inline]
pub fn get(&self, name: &str) -> Option<&NbtTag> {
for (key, value) in &self.child_tags {
if key.as_str() == name {
return Some(value);
}
}
None
}
pub fn get_short(&self, name: &str) -> Option<i16> {
self.get(name).and_then(|tag| tag.extract_short())
}
pub fn get_int(&self, name: &str) -> Option<i32> {
self.get(name).and_then(|tag| tag.extract_int())
}
pub fn get_long(&self, name: &str) -> Option<i64> {
self.get(name).and_then(|tag| tag.extract_long())
}
pub fn get_float(&self, name: &str) -> Option<f32> {
self.get(name).and_then(|tag| tag.extract_float())
}
pub fn get_double(&self, name: &str) -> Option<f64> {
self.get(name).and_then(|tag| tag.extract_double())
}
pub fn get_bool(&self, name: &str) -> Option<bool> {
self.get(name).and_then(|tag| tag.extract_bool())
}
pub fn get_string(&self, name: &str) -> Option<&String> {
self.get(name).and_then(|tag| tag.extract_string())
}
pub fn get_list(&self, name: &str) -> Option<&Vec<NbtTag>> {
self.get(name).and_then(|tag| tag.extract_list())
}
pub fn get_compound(&self, name: &str) -> Option<&NbtCompound> {
self.get(name).and_then(|tag| tag.extract_compound())
}
pub fn get_int_array(&self, name: &str) -> Option<&Vec<i32>> {
self.get(name).and_then(|tag| tag.extract_int_array())
}
pub fn get_long_array(&self, name: &str) -> Option<&Vec<i64>> {
self.get(name).and_then(|tag| tag.extract_long_array())
}
}
impl From<Nbt> for NbtCompound {
fn from(value: Nbt) -> Self {
value.root_tag
}
}
impl FromIterator<(String, NbtTag)> for NbtCompound {
fn from_iter<T: IntoIterator<Item = (String, NbtTag)>>(iter: T) -> Self {
let mut compound = NbtCompound::new();
for (key, value) in iter {
compound.put(key, value);
}
compound
}
}
impl IntoIterator for NbtCompound {
type Item = (String, NbtTag);
type IntoIter = IntoIter<(String, NbtTag)>;
fn into_iter(self) -> Self::IntoIter {
self.child_tags.into_iter()
}
}
impl Extend<(String, NbtTag)> for NbtCompound {
fn extend<T: IntoIterator<Item = (String, NbtTag)>>(&mut self, iter: T) {
self.child_tags.extend(iter)
}
}
// Rust's AsRef is currently not reflexive so we need to implement it manually
impl AsRef<NbtCompound> for NbtCompound {
fn as_ref(&self) -> &NbtCompound {
self
}
}

View File

@@ -0,0 +1,212 @@
use crate::*;
use bytes::{Buf, BytesMut};
use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor};
use serde::{forward_to_deserialize_any, Deserialize};
use std::io::Cursor;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Deserializer<'de, T> {
input: &'de mut T,
tag_to_deserialize: Option<u8>,
is_named: bool,
}
impl<'de, T: Buf> Deserializer<'de, T> {
pub fn new(input: &'de mut T, is_named: bool) -> Self {
Deserializer {
input,
tag_to_deserialize: None,
is_named,
}
}
}
/// Deserializes struct using Serde Deserializer from unnamed (network) NBT
pub fn from_bytes<'a, T>(s: &'a mut BytesMut) -> Result<T>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer::new(s, true);
T::deserialize(&mut deserializer)
}
pub fn from_cursor<'a, T>(cursor: &'a mut Cursor<&[u8]>) -> Result<T>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer::new(cursor, true);
T::deserialize(&mut deserializer)
}
/// Deserializes struct using Serde Deserializer from normal NBT
pub fn from_bytes_unnamed<'a, T>(s: &'a mut BytesMut) -> Result<T>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer::new(s, false);
T::deserialize(&mut deserializer)
}
pub fn from_cursor_unnamed<'a, T>(cursor: &'a mut Cursor<&[u8]>) -> Result<T>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer::new(cursor, false);
T::deserialize(&mut deserializer)
}
impl<'de, 'a, T: Buf> de::Deserializer<'de> for &'a mut Deserializer<'de, T> {
type Error = Error;
forward_to_deserialize_any!(i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 seq char str string bytes byte_buf tuple tuple_struct enum ignored_any unit unit_struct option newtype_struct);
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let tag_to_deserialize = self.tag_to_deserialize.unwrap();
let list_type = match tag_to_deserialize {
LIST_ID => Some(self.input.get_u8()),
INT_ARRAY_ID => Some(INT_ID),
LONG_ARRAY_ID => Some(LONG_ID),
BYTE_ARRAY_ID => Some(BYTE_ID),
_ => None,
};
if let Some(list_type) = list_type {
let remaining_values = self.input.get_u32();
return visitor.visit_seq(ListAccess {
de: self,
list_type,
remaining_values,
});
}
let result: Result<V::Value> = Ok(
match NbtTag::deserialize_data(self.input, tag_to_deserialize)? {
NbtTag::Byte(value) => visitor.visit_i8::<Error>(value)?,
NbtTag::Short(value) => visitor.visit_i16::<Error>(value)?,
NbtTag::Int(value) => visitor.visit_i32::<Error>(value)?,
NbtTag::Long(value) => visitor.visit_i64::<Error>(value)?,
NbtTag::Float(value) => visitor.visit_f32::<Error>(value)?,
NbtTag::Double(value) => visitor.visit_f64::<Error>(value)?,
NbtTag::String(value) => visitor.visit_string::<Error>(value)?,
_ => unreachable!(),
},
);
self.tag_to_deserialize = None;
result
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if self.tag_to_deserialize.unwrap() == BYTE_ID {
let value = self.input.get_u8();
if value != 0 {
return visitor.visit_bool(true);
}
}
visitor.visit_bool(false)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if self.tag_to_deserialize.is_none() {
let next_byte = self.input.get_u8();
if next_byte != COMPOUND_ID {
return Err(Error::NoRootCompound(next_byte));
}
if self.is_named {
// Consume struct name
NbtTag::deserialize(self.input)?;
}
}
let value = visitor.visit_map(CompoundAccess { de: self })?;
Ok(value)
}
fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_map(visitor)
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let str = get_nbt_string(&mut self.input).map_err(|_| Error::Cesu8DecodingError)?;
visitor.visit_string(str)
}
fn is_human_readable(&self) -> bool {
false
}
}
struct CompoundAccess<'a, 'de: 'a, T: Buf> {
de: &'a mut Deserializer<'de, T>,
}
impl<'de, 'a, T: Buf> MapAccess<'de> for CompoundAccess<'a, 'de, T> {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
where
K: DeserializeSeed<'de>,
{
let tag = self.de.input.get_u8();
self.de.tag_to_deserialize = Some(tag);
if tag == END_ID {
return Ok(None);
}
seed.deserialize(&mut *self.de).map(Some)
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
where
V: DeserializeSeed<'de>,
{
seed.deserialize(&mut *self.de)
}
}
struct ListAccess<'a, 'de: 'a, T: Buf> {
de: &'a mut Deserializer<'de, T>,
remaining_values: u32,
list_type: u8,
}
impl<'a, 'de, T: Buf> SeqAccess<'de> for ListAccess<'a, 'de, T> {
type Error = Error;
fn next_element_seed<E>(&mut self, seed: E) -> Result<Option<E::Value>>
where
E: DeserializeSeed<'de>,
{
if self.remaining_values == 0 {
return Ok(None);
}
self.remaining_values -= 1;
self.de.tag_to_deserialize = Some(self.list_type);
seed.deserialize(&mut *self.de).map(Some)
}
}

204
pumpkin-nbt/src/lib.rs Normal file
View File

@@ -0,0 +1,204 @@
use std::{
fmt::Display,
io::{self, Cursor, Write},
ops::Deref,
};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use cesu8::Cesu8DecodingError;
use compound::NbtCompound;
use serde::{de, ser};
use serde::{Deserialize, Deserializer};
use tag::NbtTag;
use thiserror::Error;
pub mod compound;
pub mod deserializer;
pub mod serializer;
pub mod tag;
// This NBT crate is inspired from CrabNBT
pub const END_ID: u8 = 0;
pub const BYTE_ID: u8 = 1;
pub const SHORT_ID: u8 = 2;
pub const INT_ID: u8 = 3;
pub const LONG_ID: u8 = 4;
pub const FLOAT_ID: u8 = 5;
pub const DOUBLE_ID: u8 = 6;
pub const BYTE_ARRAY_ID: u8 = 7;
pub const STRING_ID: u8 = 8;
pub const LIST_ID: u8 = 9;
pub const COMPOUND_ID: u8 = 10;
pub const INT_ARRAY_ID: u8 = 11;
pub const LONG_ARRAY_ID: u8 = 12;
#[derive(Error, Debug)]
pub enum Error {
#[error("The root tag of the NBT file is not a compound tag. Received tag id: {0}")]
NoRootCompound(u8),
#[error("Encountered an unknown NBT tag id {0}.")]
UnknownTagId(u8),
#[error("Failed to Cesu 8 Decode")]
Cesu8DecodingError,
#[error("Serde error: {0}")]
SerdeError(String),
#[error("NBT doesn't support this type {0}")]
UnsupportedType(String),
}
impl ser::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
Error::SerdeError(msg.to_string())
}
}
impl de::Error for Error {
fn custom<T: Display>(msg: T) -> Self {
Error::SerdeError(msg.to_string())
}
}
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
pub struct Nbt {
pub name: String,
pub root_tag: NbtCompound,
}
impl Nbt {
pub fn new(name: String, tag: NbtCompound) -> Self {
Nbt {
name,
root_tag: tag,
}
}
pub fn read(bytes: &mut impl Buf) -> Result<Nbt, Error> {
let tag_type_id = bytes.get_u8();
if tag_type_id != COMPOUND_ID {
return Err(Error::NoRootCompound(tag_type_id));
}
Ok(Nbt {
name: get_nbt_string(bytes).map_err(|_| Error::Cesu8DecodingError)?,
root_tag: NbtCompound::deserialize_content(bytes)?,
})
}
pub fn read_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
Self::read(cursor)
}
/// Reads NBT tag, that doesn't contain the name of root compound.
/// Used in [Network NBT](https://wiki.vg/NBT#Network_NBT_(Java_Edition)).
pub fn read_unnamed(bytes: &mut impl Buf) -> Result<Nbt, Error> {
let tag_type_id = bytes.get_u8();
if tag_type_id != COMPOUND_ID {
return Err(Error::NoRootCompound(tag_type_id));
}
Ok(Nbt {
name: String::new(),
root_tag: NbtCompound::deserialize_content(bytes)
.map_err(|_| Error::Cesu8DecodingError)?,
})
}
pub fn read_unnamed_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
Self::read_unnamed(cursor)
}
pub fn write(&self) -> Bytes {
let mut bytes = BytesMut::new();
bytes.put_u8(COMPOUND_ID);
bytes.put(NbtTag::String(self.name.to_string()).serialize_data());
bytes.put(self.root_tag.serialize_content());
bytes.freeze()
}
pub fn write_to_writer<W: Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.write())?;
Ok(())
}
/// Writes NBT tag, without name of root compound.
/// Used in [Network NBT](https://wiki.vg/NBT#Network_NBT_(Java_Edition)).
pub fn write_unnamed(&self) -> Bytes {
let mut bytes = BytesMut::new();
bytes.put_u8(COMPOUND_ID);
bytes.put(self.root_tag.serialize_content());
bytes.freeze()
}
pub fn write_unnamed_to_writer<W: Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.write_unnamed())?;
Ok(())
}
}
impl Deref for Nbt {
type Target = NbtCompound;
fn deref(&self) -> &Self::Target {
&self.root_tag
}
}
impl From<NbtCompound> for Nbt {
fn from(value: NbtCompound) -> Self {
Nbt::new(String::new(), value)
}
}
impl<T> AsRef<T> for Nbt
where
T: ?Sized,
<Nbt as Deref>::Target: AsRef<T>,
{
fn as_ref(&self) -> &T {
self.deref().as_ref()
}
}
impl AsMut<NbtCompound> for Nbt {
fn as_mut(&mut self) -> &mut NbtCompound {
&mut self.root_tag
}
}
pub fn get_nbt_string(bytes: &mut impl Buf) -> Result<String, Cesu8DecodingError> {
let len = bytes.get_u16() as usize;
let string_bytes = bytes.copy_to_bytes(len);
let string = cesu8::from_java_cesu8(&string_bytes)?;
Ok(string.to_string())
}
macro_rules! impl_array {
($name:ident, $variant:expr) => {
pub struct $name;
impl $name {
pub fn serialize<T, S>(input: T, serializer: S) -> Result<S::Ok, S::Error>
where
T: serde::Serialize,
S: serde::Serializer,
{
serializer.serialize_newtype_variant("nbt_array", 0, $variant, &input)
}
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: Deserialize<'de>,
D: Deserializer<'de>,
{
T::deserialize(deserializer)
}
}
};
}
impl_array!(IntArray, "int");
impl_array!(LongArray, "long");
impl_array!(BytesArray, "byte");

View File

@@ -0,0 +1,430 @@
use bytes::{BufMut, BytesMut};
use serde::ser::Impossible;
use serde::{ser, Serialize};
use std::io::Write;
use crate::tag::NbtTag;
use crate::{
Error, BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID,
LIST_ID, LONG_ARRAY_ID, LONG_ID, SHORT_ID, STRING_ID,
};
pub type Result<T> = std::result::Result<T, Error>;
pub struct Serializer {
output: BytesMut,
state: State,
}
// NBT has a different order of things, then most other formats
// So I use State, to keep what serializer has to do, and some information like field name
#[derive(Clone, Debug, PartialEq)]
enum State {
// In network NBT root name is not present
Root(Option<String>),
Named(String),
// Used by maps, to check if key is String
MapKey,
FirstListElement { len: i32 },
ListElement,
Array { name: String, array_type: String },
}
impl Serializer {
fn parse_state(&mut self, tag: u8) -> Result<()> {
match &mut self.state {
State::Named(name) | State::Array { name, .. } => {
self.output.put_u8(tag);
self.output
.put(NbtTag::String(name.clone()).serialize_data());
}
State::FirstListElement { len } => {
self.output.put_u8(tag);
self.output.put_i32(*len);
}
State::MapKey => {
if tag != STRING_ID {
return Err(Error::SerdeError(format!(
"Map key can only be string, not {tag}"
)));
}
}
State::ListElement => {}
_ => return Err(Error::SerdeError("Invalid Serializer state!".to_string())),
};
Ok(())
}
}
/// Serializes struct using Serde Serializer to unnamed (network) NBT
pub fn to_bytes_unnamed<T>(value: &T) -> Result<BytesMut>
where
T: Serialize,
{
let mut serializer = Serializer {
output: BytesMut::new(),
state: State::Root(None),
};
value.serialize(&mut serializer)?;
Ok(serializer.output)
}
pub fn to_writer_unnamed<T, W>(value: &T, mut writer: W) -> Result<()>
where
T: Serialize,
W: Write,
{
writer.write_all(&to_bytes_unnamed(value)?).unwrap();
Ok(())
}
/// Serializes struct using Serde Serializer to normal NBT
pub fn to_bytes<T>(value: &T, name: String) -> Result<BytesMut>
where
T: Serialize,
{
let mut serializer = Serializer {
output: BytesMut::new(),
state: State::Root(Some(name)),
};
value.serialize(&mut serializer)?;
Ok(serializer.output)
}
pub fn to_writer<T, W>(value: &T, name: String, mut writer: W) -> Result<()>
where
T: Serialize,
W: Write,
{
writer.write_all(&to_bytes(value, name)?).unwrap();
Ok(())
}
impl<'a> ser::Serializer for &'a mut Serializer {
type Ok = ();
type Error = Error;
type SerializeSeq = Self;
type SerializeTuple = Impossible<(), Error>;
type SerializeTupleStruct = Impossible<(), Error>;
type SerializeTupleVariant = Impossible<(), Error>;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Impossible<(), Error>;
// NBT doesn't have bool type, but it's most commonly represented as a byte
fn serialize_bool(self, v: bool) -> Result<()> {
self.serialize_i8(v as i8)?;
Ok(())
}
fn serialize_i8(self, v: i8) -> Result<()> {
self.parse_state(BYTE_ID)?;
self.output.put_i8(v);
Ok(())
}
fn serialize_i16(self, v: i16) -> Result<()> {
self.parse_state(SHORT_ID)?;
self.output.put_i16(v);
Ok(())
}
fn serialize_i32(self, v: i32) -> Result<()> {
self.parse_state(INT_ID)?;
self.output.put_i32(v);
Ok(())
}
fn serialize_i64(self, v: i64) -> Result<()> {
self.parse_state(LONG_ID)?;
self.output.put_i64(v);
Ok(())
}
fn serialize_u8(self, v: u8) -> Result<()> {
self.parse_state(BYTE_ID)?;
self.output.put_u8(v);
Ok(())
}
fn serialize_u16(self, v: u16) -> Result<()> {
self.parse_state(SHORT_ID)?;
self.output.put_u16(v);
Ok(())
}
fn serialize_u32(self, v: u32) -> Result<()> {
self.parse_state(INT_ID)?;
self.output.put_u32(v);
Ok(())
}
fn serialize_u64(self, v: u64) -> Result<()> {
self.parse_state(LONG_ID)?;
self.output.put_u64(v);
Ok(())
}
fn serialize_f32(self, v: f32) -> Result<()> {
self.parse_state(FLOAT_ID)?;
self.output.put_f32(v);
Ok(())
}
fn serialize_f64(self, v: f64) -> Result<()> {
self.parse_state(DOUBLE_ID)?;
self.output.put_f64(v);
Ok(())
}
fn serialize_char(self, _v: char) -> Result<()> {
Err(Error::UnsupportedType("char".to_string()))
}
fn serialize_str(self, v: &str) -> Result<()> {
self.parse_state(STRING_ID)?;
if self.state == State::MapKey {
self.state = State::Named(v.to_string());
return Ok(());
}
self.output
.put(NbtTag::String(v.to_string()).serialize_data());
Ok(())
}
fn serialize_bytes(self, v: &[u8]) -> Result<()> {
self.parse_state(LIST_ID)?;
self.output.put_u8(BYTE_ID);
self.output.put_i32(v.len() as i32);
self.output.put_slice(v);
Ok(())
}
// Just skip serializing, if value is none
fn serialize_none(self) -> Result<()> {
Ok(())
}
fn serialize_some<T>(self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<()> {
Ok(())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<()> {
Err(Error::UnsupportedType("unit struct".to_string()))
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<()> {
self.serialize_str(variant)?;
Ok(())
}
fn serialize_newtype_struct<T>(self, _name: &'static str, _value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
Err(Error::UnsupportedType("newtype struct".to_string()))
}
fn serialize_newtype_variant<T>(
self,
name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<()>
where
T: ?Sized + Serialize,
{
if name != "nbt_array" {
return Err(Error::SerdeError(
"new_type variant supports only nbt_array".to_string(),
));
}
let name = match self.state {
State::Named(ref name) => name.clone(),
_ => return Err(Error::SerdeError("Invalid Serializer state!".to_string())),
};
self.state = State::Array {
name,
array_type: variant.to_string(),
};
value.serialize(self)?;
Ok(())
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
if len.is_none() {
return Err(Error::SerdeError(
"Length of the sequence must be known first!".to_string(),
));
}
match &mut self.state {
State::Array { array_type, .. } => {
let id = match array_type.as_str() {
"byte" => BYTE_ARRAY_ID,
"int" => INT_ARRAY_ID,
"long" => LONG_ARRAY_ID,
_ => {
return Err(Error::SerdeError(
"Array supports only byte, int, long".to_string(),
))
}
};
self.parse_state(id)?;
self.output.put_i32(len.unwrap() as i32);
self.state = State::ListElement;
}
_ => {
self.parse_state(LIST_ID)?;
self.state = State::FirstListElement {
len: len.unwrap() as i32,
};
}
}
Ok(self)
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
Err(Error::UnsupportedType("tuple".to_string()))
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct> {
Err(Error::UnsupportedType("tuple struct".to_string()))
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant> {
Err(Error::UnsupportedType("tuple variant".to_string()))
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
self.output.put_u8(COMPOUND_ID);
Ok(self)
}
fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
self.output.put_u8(COMPOUND_ID);
match &mut self.state {
State::Root(root_name) => {
if let Some(root_name) = root_name {
self.output
.put(NbtTag::String(root_name.clone()).serialize_data());
}
}
State::Named(string) => {
self.output
.put(NbtTag::String(string.clone()).serialize_data());
}
_ => {
unimplemented!()
}
}
Ok(self)
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant> {
Err(Error::UnsupportedType("struct variant".to_string()))
}
fn is_human_readable(&self) -> bool {
false
}
}
impl<'a> ser::SerializeSeq for &'a mut Serializer {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)?;
self.state = State::ListElement;
Ok(())
}
fn end(self) -> Result<()> {
Ok(())
}
}
impl<'a> ser::SerializeStruct for &'a mut Serializer {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
self.state = State::Named(key.to_string());
value.serialize(&mut **self)
}
fn end(self) -> Result<()> {
self.output.put_u8(END_ID);
Ok(())
}
}
impl<'a> ser::SerializeMap for &'a mut Serializer {
type Ok = ();
type Error = Error;
fn serialize_key<T>(&mut self, key: &T) -> std::result::Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
self.state = State::MapKey;
key.serialize(&mut **self)
}
fn serialize_value<T>(&mut self, value: &T) -> std::result::Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(&mut **self)
}
fn end(self) -> Result<()> {
self.output.put_u8(END_ID);
Ok(())
}
}

277
pumpkin-nbt/src/tag.rs Normal file
View File

@@ -0,0 +1,277 @@
use std::io::Cursor;
use bytes::{Bytes, BytesMut};
use compound::NbtCompound;
use crate::*;
#[derive(Clone, Debug, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum NbtTag {
End = END_ID,
Byte(i8) = BYTE_ID,
Short(i16) = SHORT_ID,
Int(i32) = INT_ID,
Long(i64) = LONG_ID,
Float(f32) = FLOAT_ID,
Double(f64) = DOUBLE_ID,
ByteArray(Bytes) = BYTE_ARRAY_ID,
String(String) = STRING_ID,
List(Vec<NbtTag>) = LIST_ID,
Compound(NbtCompound) = COMPOUND_ID,
IntArray(Vec<i32>) = INT_ARRAY_ID,
LongArray(Vec<i64>) = LONG_ARRAY_ID,
}
impl NbtTag {
/// Returns the numeric id associated with the data type.
pub const fn get_type_id(&self) -> u8 {
// See https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting
unsafe { *(self as *const Self as *const u8) }
}
pub fn serialize(&self) -> Bytes {
let mut bytes = BytesMut::new();
bytes.put_u8(self.get_type_id());
bytes.put(self.serialize_data());
bytes.freeze()
}
pub fn serialize_data(&self) -> Bytes {
let mut bytes = BytesMut::new();
match self {
NbtTag::End => {}
NbtTag::Byte(byte) => bytes.put_i8(*byte),
NbtTag::Short(short) => bytes.put_i16(*short),
NbtTag::Int(int) => bytes.put_i32(*int),
NbtTag::Long(long) => bytes.put_i64(*long),
NbtTag::Float(float) => bytes.put_f32(*float),
NbtTag::Double(double) => bytes.put_f64(*double),
NbtTag::ByteArray(byte_array) => {
bytes.put_i32(byte_array.len() as i32);
bytes.put_slice(byte_array);
}
NbtTag::String(string) => {
let java_string = cesu8::to_java_cesu8(string);
bytes.put_u16(java_string.len() as u16);
bytes.put_slice(&java_string);
}
NbtTag::List(list) => {
bytes.put_u8(list.first().unwrap_or(&NbtTag::End).get_type_id());
bytes.put_i32(list.len() as i32);
for nbt_tag in list {
bytes.put(nbt_tag.serialize_data())
}
}
NbtTag::Compound(compound) => {
bytes.put(compound.serialize_content());
}
NbtTag::IntArray(int_array) => {
bytes.put_i32(int_array.len() as i32);
for int in int_array {
bytes.put_i32(*int)
}
}
NbtTag::LongArray(long_array) => {
bytes.put_i32(long_array.len() as i32);
for long in long_array {
bytes.put_i64(*long)
}
}
}
bytes.freeze()
}
pub fn deserialize(bytes: &mut impl Buf) -> Result<NbtTag, Error> {
let tag_id = bytes.get_u8();
Self::deserialize_data(bytes, tag_id)
}
pub fn deserialize_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<NbtTag, Error> {
Self::deserialize(cursor)
}
pub fn deserialize_data(bytes: &mut impl Buf, tag_id: u8) -> Result<NbtTag, Error> {
match tag_id {
END_ID => Ok(NbtTag::End),
BYTE_ID => {
let byte = bytes.get_i8();
Ok(NbtTag::Byte(byte))
}
SHORT_ID => {
let short = bytes.get_i16();
Ok(NbtTag::Short(short))
}
INT_ID => {
let int = bytes.get_i32();
Ok(NbtTag::Int(int))
}
LONG_ID => {
let long = bytes.get_i64();
Ok(NbtTag::Long(long))
}
FLOAT_ID => {
let float = bytes.get_f32();
Ok(NbtTag::Float(float))
}
DOUBLE_ID => {
let double = bytes.get_f64();
Ok(NbtTag::Double(double))
}
BYTE_ARRAY_ID => {
let len = bytes.get_i32() as usize;
let byte_array = bytes.copy_to_bytes(len);
Ok(NbtTag::ByteArray(byte_array))
}
STRING_ID => Ok(NbtTag::String(get_nbt_string(bytes).unwrap())),
LIST_ID => {
let tag_type_id = bytes.get_u8();
let len = bytes.get_i32();
let mut list = Vec::with_capacity(len as usize);
for _ in 0..len {
let tag = NbtTag::deserialize_data(bytes, tag_type_id)?;
assert_eq!(tag.get_type_id(), tag_type_id);
list.push(tag);
}
Ok(NbtTag::List(list))
}
COMPOUND_ID => Ok(NbtTag::Compound(NbtCompound::deserialize_content(bytes)?)),
INT_ARRAY_ID => {
let len = bytes.get_i32() as usize;
let mut int_array = Vec::with_capacity(len);
for _ in 0..len {
let int = bytes.get_i32();
int_array.push(int);
}
Ok(NbtTag::IntArray(int_array))
}
LONG_ARRAY_ID => {
let len = bytes.get_i32() as usize;
let mut long_array = Vec::with_capacity(len);
for _ in 0..len {
let long = bytes.get_i64();
long_array.push(long);
}
Ok(NbtTag::LongArray(long_array))
}
_ => Err(Error::UnknownTagId(tag_id)),
}
}
pub fn deserialize_data_from_cursor(
cursor: &mut Cursor<&[u8]>,
tag_id: u8,
) -> Result<NbtTag, Error> {
Self::deserialize_data(cursor, tag_id)
}
pub fn extract_byte(&self) -> Option<i8> {
match self {
NbtTag::Byte(byte) => Some(*byte),
_ => None,
}
}
pub fn extract_short(&self) -> Option<i16> {
match self {
NbtTag::Short(short) => Some(*short),
_ => None,
}
}
pub fn extract_int(&self) -> Option<i32> {
match self {
NbtTag::Int(int) => Some(*int),
_ => None,
}
}
pub fn extract_long(&self) -> Option<i64> {
match self {
NbtTag::Long(long) => Some(*long),
_ => None,
}
}
pub fn extract_float(&self) -> Option<f32> {
match self {
NbtTag::Float(float) => Some(*float),
_ => None,
}
}
pub fn extract_double(&self) -> Option<f64> {
match self {
NbtTag::Double(double) => Some(*double),
_ => None,
}
}
pub fn extract_bool(&self) -> Option<bool> {
match self {
NbtTag::Byte(byte) => Some(*byte != 0),
_ => None,
}
}
pub fn extract_byte_array(&self) -> Option<Bytes> {
match self {
// Note: Bytes are free to clone, so we can hand out an owned type
NbtTag::ByteArray(byte_array) => Some(byte_array.clone()),
_ => None,
}
}
pub fn extract_string(&self) -> Option<&String> {
match self {
NbtTag::String(string) => Some(string),
_ => None,
}
}
pub fn extract_list(&self) -> Option<&Vec<NbtTag>> {
match self {
NbtTag::List(list) => Some(list),
_ => None,
}
}
pub fn extract_compound(&self) -> Option<&NbtCompound> {
match self {
NbtTag::Compound(compound) => Some(compound),
_ => None,
}
}
pub fn extract_int_array(&self) -> Option<&Vec<i32>> {
match self {
NbtTag::IntArray(int_array) => Some(int_array),
_ => None,
}
}
pub fn extract_long_array(&self) -> Option<&Vec<i64>> {
match self {
NbtTag::LongArray(long_array) => Some(long_array),
_ => None,
}
}
}
impl From<&str> for NbtTag {
fn from(value: &str) -> Self {
NbtTag::String(value.to_string())
}
}
impl From<&[u8]> for NbtTag {
fn from(value: &[u8]) -> Self {
NbtTag::ByteArray(Bytes::copy_from_slice(value))
}
}
impl From<bool> for NbtTag {
fn from(value: bool) -> Self {
NbtTag::Byte(value as i8)
}
}

View File

@@ -4,6 +4,7 @@ version.workspace = true
edition.workspace = true
[dependencies]
pumpkin-nbt = { path = "../pumpkin-nbt" }
pumpkin-config = { path = "../pumpkin-config" }
pumpkin-macros = { path = "../pumpkin-macros" }
pumpkin-world = { path = "../pumpkin-world" }
@@ -25,5 +26,3 @@ flate2 = "1.0"
# encryption
aes = "0.8.4"
cfb8 = "0.8.1"
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }

View File

@@ -1,3 +1,4 @@
use bytes::BytesMut;
use pumpkin_macros::client_packet;
use crate::{bytebuf::ByteBuffer, ClientPacket};
@@ -19,7 +20,7 @@ impl<'a> CRegistryData<'a> {
pub struct RegistryEntry<'a> {
pub entry_id: &'a str,
pub data: Vec<u8>,
pub data: BytesMut,
}
impl<'a> ClientPacket for CRegistryData<'a> {

View File

@@ -15,8 +15,7 @@ impl<'a> ClientPacket for CChunkData<'a> {
buf.put_i32(self.0.position.z);
let heightmap_nbt =
fastnbt::to_bytes_with_opts(&self.0.blocks.heightmap, fastnbt::SerOpts::network_nbt())
.unwrap();
pumpkin_nbt::serializer::to_bytes_unnamed(&self.0.blocks.heightmap).unwrap();
// Heightmaps
buf.put_slice(&heightmap_nbt);

View File

@@ -45,7 +45,7 @@ impl<'a> ClientPacket for CUpdateObjectives<'a> {
NumberFormat::Styled(style) => {
p.put_var_int(&VarInt(1));
// TODO
p.put_slice(&fastnbt::to_bytes(style).unwrap());
p.put_slice(&pumpkin_nbt::serializer::to_bytes_unnamed(style).unwrap());
}
NumberFormat::Fixed(text_component) => {
p.put_var_int(&VarInt(2));

View File

@@ -5,6 +5,7 @@ edition.workspace = true
[dependencies]
pumpkin-protocol = { path = "../pumpkin-protocol" }
pumpkin-nbt = { path = "../pumpkin-nbt" }
pumpkin-core = { path = "../pumpkin-core" }
serde.workspace = true
@@ -15,7 +16,4 @@ rayon.workspace = true
num-traits.workspace = true
num-derive.workspace = true
# nbt
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
itertools.workspace = true

View File

@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Biome {
has_precipitation: i8,
has_precipitation: bool,
temperature: f32,
#[serde(skip_serializing_if = "Option::is_none")]
temperature_modifier: Option<String>,
@@ -67,5 +67,5 @@ struct Music {
sound: String,
min_delay: i32,
max_delay: i32,
replace_current_music: i8,
replace_current_music: bool,
}

View File

@@ -3,24 +3,24 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Dimension {
ambient_light: f32,
bed_works: u8,
bed_works: bool,
coordinate_scale: f64,
effects: DimensionEffects,
#[serde(skip_serializing_if = "Option::is_none")]
fixed_time: Option<i64>,
has_ceiling: u8,
has_raids: u8,
has_skylight: u8,
has_ceiling: bool,
has_raids: bool,
has_skylight: bool,
height: i32,
infiniburn: String,
logical_height: i32,
min_y: i32,
monster_spawn_block_light_limit: i32,
monster_spawn_light_level: MonsterSpawnLightLevel,
natural: u8,
piglin_safe: u8,
respawn_anchor_works: u8,
ultrawarm: u8,
natural: bool,
piglin_safe: bool,
respawn_anchor_works: bool,
ultrawarm: bool,
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Default, Debug)]

View File

@@ -6,7 +6,6 @@ use chat_type::ChatType;
use damage_type::DamageType;
use dimension::Dimension;
use enchantment::Enchantment;
use fastnbt::SerOpts;
use instrument::Instrument;
use jukebox_song::JukeboxSong;
use paint::Painting;
@@ -80,7 +79,7 @@ impl Registry {
.iter()
.map(|s| RegistryEntry {
entry_id: s.0,
data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
})
.collect();
let biome = Registry {
@@ -93,7 +92,7 @@ impl Registry {
.iter()
.map(|s| RegistryEntry {
entry_id: s.0,
data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
})
.collect();
let chat_type = Registry {
@@ -106,7 +105,7 @@ impl Registry {
// .iter()
// .map(|s| RegistryEntry {
// entry_id: s.0,
// data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
// data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
// })
// .collect();
// let trim_pattern = Registry {
@@ -119,7 +118,7 @@ impl Registry {
// .iter()
// .map(|s| RegistryEntry {
// entry_id: s.0,
// data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
// data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
// })
// .collect();
// let trim_material = Registry {
@@ -135,7 +134,7 @@ impl Registry {
let varient = s.1.clone();
RegistryEntry {
entry_id: s.0,
data: fastnbt::to_bytes_with_opts(&varient, SerOpts::network_nbt()).unwrap(),
data: pumpkin_nbt::serializer::to_bytes_unnamed(&varient).unwrap(),
}
})
.collect();
@@ -149,7 +148,7 @@ impl Registry {
.iter()
.map(|s| RegistryEntry {
entry_id: s.0,
data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
})
.collect();
let painting_variant = Registry {
@@ -162,7 +161,7 @@ impl Registry {
.iter()
.map(|s| RegistryEntry {
entry_id: s.0,
data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
})
.collect();
let dimension_type = Registry {
@@ -175,7 +174,7 @@ impl Registry {
.iter()
.map(|s| RegistryEntry {
entry_id: s.0,
data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
})
.collect();
let damage_type = Registry {
@@ -188,7 +187,7 @@ impl Registry {
.iter()
.map(|s| RegistryEntry {
entry_id: s.0,
data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
})
.collect();
let banner_pattern = Registry {
@@ -202,7 +201,7 @@ impl Registry {
// .iter()
// .map(|s| RegistryEntry {
// entry_id: s.0,
// data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
// data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
// })
// .collect();
// let enchantment = Registry {
@@ -215,7 +214,7 @@ impl Registry {
// .iter()
// .map(|s| RegistryEntry {
// entry_id: s.0,
// data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
// data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
// })
// .collect();
// let jukebox_song = Registry {
@@ -228,7 +227,7 @@ impl Registry {
// .iter()
// .map(|s| RegistryEntry {
// entry_id: s.0,
// data: fastnbt::to_bytes_with_opts(&s.1, SerOpts::network_nbt()).unwrap(),
// data: pumpkin_nbt::serializer::to_bytes_unnamed(&s.1).unwrap(),
// })
// .collect();
// let instrument = Registry {

View File

@@ -5,5 +5,5 @@ pub struct TrimPattern {
asset_id: String,
template_item: String,
// description: TextComponent<'static>,
decal: u8,
decal: bool,
}

View File

@@ -1,10 +1,9 @@
use std::cmp::max;
use std::collections::HashMap;
use std::ops::Index;
use fastnbt::LongArray;
use pumpkin_core::math::vector2::Vector2;
use serde::{Deserialize, Serialize};
use std::cmp::max;
use std::collections::HashMap;
use std::ops::Index;
use thiserror::Error;
use crate::{
@@ -78,6 +77,7 @@ struct PaletteEntry {
#[derive(Deserialize, Debug, Clone)]
struct ChunkSectionBlockStates {
// #[serde(with = "LongArray")]
data: Option<LongArray>,
palette: Vec<PaletteEntry>,
}
@@ -85,7 +85,9 @@ struct ChunkSectionBlockStates {
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "UPPERCASE")]
pub struct ChunkHeightmaps {
// #[serde(with = "LongArray")]
motion_blocking: LongArray,
// #[serde(with = "LongArray")]
world_surface: LongArray,
}
@@ -269,8 +271,7 @@ impl ChunkData {
continue;
}
Some(d) => d,
}
.into_inner();
};
// How many bits each block has in one of the pallete u64s
let block_bit_size = {