diff --git a/.gitignore b/.gitignore index e9a82deaa..d69f2a8e3 100644 --- a/.gitignore +++ b/.gitignore @@ -117,7 +117,7 @@ features.toml *.local.nbt -.vscode/settings.json +.vscode/ # Documentation docs/.vitepress/dist diff --git a/pumpkin-nbt/src/deserializer.rs b/pumpkin-nbt/src/deserializer.rs index 80e3b96f4..b9ed91e10 100644 --- a/pumpkin-nbt/src/deserializer.rs +++ b/pumpkin-nbt/src/deserializer.rs @@ -1,6 +1,6 @@ use crate::*; use bytes::Buf; -use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor}; +use serde::de::{self, DeserializeSeed, IntoDeserializer, MapAccess, SeqAccess, Visitor}; use serde::{forward_to_deserialize_any, Deserialize}; use std::io::Cursor; @@ -60,7 +60,7 @@ where impl<'de, T: Buf> de::Deserializer<'de> for &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); + forward_to_deserialize_any!(i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 seq char str string bytes byte_buf tuple tuple_struct newtype_struct ignored_any unit unit_struct); fn deserialize_any(self, visitor: V) -> Result where @@ -77,7 +77,7 @@ impl<'de, T: Buf> de::Deserializer<'de> for &mut Deserializer<'de, T> { }; if let Some(list_type) = list_type { - let remaining_values = self.input.get_u32(); + let remaining_values = self.input.get_i32(); return visitor.visit_seq(ListAccess { de: self, list_type, @@ -126,7 +126,8 @@ impl<'de, T: Buf> de::Deserializer<'de> for &mut Deserializer<'de, T> { if self.is_named { // Consume struct name - NbtTag::deserialize(self.input)?; + let len = self.input.get_u16() as usize; + self.input.advance(len); } } @@ -146,6 +147,27 @@ impl<'de, T: Buf> de::Deserializer<'de> for &mut Deserializer<'de, T> { self.deserialize_map(visitor) } + //Only does unit variants for now, until someone decides it is time + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> std::result::Result + where + V: Visitor<'de>, + { + let variant = get_nbt_string(self.input).map_err(|_| Error::Cesu8DecodingError)?; + visitor.visit_enum(variant.into_deserializer()) //Only work for unit variants + } + + fn deserialize_option(self, visitor: V) -> std::result::Result + where + V: Visitor<'de>, + { + visitor.visit_some(self) //None is not encoded, so no need for it + } + fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'de>, @@ -190,7 +212,7 @@ impl<'de, T: Buf> MapAccess<'de> for CompoundAccess<'_, 'de, T> { struct ListAccess<'a, 'de: 'a, T: Buf> { de: &'a mut Deserializer<'de, T>, - remaining_values: u32, + remaining_values: i32, list_type: u8, } @@ -201,7 +223,8 @@ impl<'de, T: Buf> SeqAccess<'de> for ListAccess<'_, 'de, T> { where E: DeserializeSeed<'de>, { - if self.remaining_values == 0 { + // Negative list length is allowed, so we have to check for it. + if self.remaining_values <= 0 { return Ok(None); } diff --git a/pumpkin-nbt/src/lib.rs b/pumpkin-nbt/src/lib.rs index 36d724e96..94d283688 100644 --- a/pumpkin-nbt/src/lib.rs +++ b/pumpkin-nbt/src/lib.rs @@ -204,7 +204,10 @@ impl_array!(BytesArray, "byte"); #[cfg(test)] mod test { use serde::{Deserialize, Serialize}; + use std::vec; + use crate::deserializer::from_bytes; + use crate::serializer::to_bytes; use crate::BytesArray; use crate::IntArray; use crate::LongArray; @@ -236,6 +239,23 @@ mod test { assert_eq!(test, recreated_struct); } + #[test] + fn test_simple_ser_de_named() { + let name = String::from("Test"); + let test = Test { + byte: 123, + short: 1342, + int: 4313, + long: 34, + float: 1.00, + string: "Hello test".to_string(), + }; + let mut bytes = to_bytes(&test, name).unwrap(); + let recreated_struct: Test = from_bytes(&mut bytes).unwrap(); + + assert_eq!(test, recreated_struct); + } + #[derive(Serialize, Deserialize, PartialEq, Debug)] struct TestArray { #[serde(with = "BytesArray")] @@ -258,4 +278,114 @@ mod test { assert_eq!(test, recreated_struct); } + + #[test] + fn test_simple_ser_de_array_named() { + let name = String::from("Test"); + let test = TestArray { + byte_array: vec![0, 3, 2], + int_array: vec![13, 1321, 2], + long_array: vec![1, 0, 200301, 1], + }; + let mut bytes = to_bytes(&test, name).unwrap(); + let recreated_struct: TestArray = from_bytes(&mut bytes).unwrap(); + + assert_eq!(test, recreated_struct); + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Egg { + food: String, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Breakfast { + food: Egg, + } + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct TestList { + option: Option, + nested_compound: Breakfast, + compounds: Vec, + list_string: Vec, + empty: Vec, + } + + #[test] + fn test_list() { + let test1 = Test { + byte: 123, + short: 1342, + int: 4313, + long: 34, + float: 1.00, + string: "Hello test".to_string(), + }; + + let test2 = Test { + byte: 13, + short: 342, + int: -4313, + long: -132334, + float: -69.420, + string: "Hello compounds".to_string(), + }; + + let list_compound = TestList { + option: Some(Egg { + food: "Skibid".to_string(), + }), + nested_compound: Breakfast { + food: Egg { + food: "Over easy".to_string(), + }, + }, + compounds: vec![test1, test2], + list_string: vec!["".to_string(), "abcbcbcbbc".to_string()], + empty: vec![], + }; + + let mut bytes = to_bytes_unnamed(&list_compound).unwrap(); + println!("{:02x?}", bytes.clone().into_iter().collect::>()); + let recreated_struct: TestList = from_bytes_unnamed(&mut bytes).unwrap(); + assert_eq!(list_compound, recreated_struct); + } + + #[test] + fn test_list_named() { + let test1 = Test { + byte: 123, + short: 1342, + int: 4313, + long: 34, + float: 1.00, + string: "Hello test".to_string(), + }; + + let test2 = Test { + byte: 13, + short: 342, + int: -4313, + long: -132334, + float: -69.420, + string: "Hello compounds".to_string(), + }; + + let list_compound = TestList { + option: None, + nested_compound: Breakfast { + food: Egg { + food: "Over easy".to_string(), + }, + }, + compounds: vec![test1, test2], + list_string: vec!["".to_string(), "abcbcbcbbc".to_string()], + empty: vec![], + }; + + let mut bytes = to_bytes(&list_compound, "a".to_string()).unwrap(); + let recreated_struct: TestList = from_bytes(&mut bytes).unwrap(); + assert_eq!(list_compound, recreated_struct); + } } diff --git a/pumpkin-nbt/src/serializer.rs b/pumpkin-nbt/src/serializer.rs index c150d5f32..a5ee4c917 100644 --- a/pumpkin-nbt/src/serializer.rs +++ b/pumpkin-nbt/src/serializer.rs @@ -20,6 +20,7 @@ pub trait SerializeChild { pub struct Serializer { output: BytesMut, state: State, + in_struct: bool, } // NBT has a different order of things, then most other formats @@ -70,6 +71,7 @@ where let mut serializer = Serializer { output: BytesMut::new(), state: State::Root(None), + in_struct: false, }; value.serialize_child(&mut serializer)?; Ok(serializer.output) @@ -83,6 +85,7 @@ where let mut serializer = Serializer { output: BytesMut::new(), state: State::Root(None), + in_struct: false, }; value.serialize(&mut serializer)?; Ok(serializer.output) @@ -105,6 +108,7 @@ where let mut serializer = Serializer { output: BytesMut::new(), state: State::Root(Some(name)), + in_struct: false, }; value.serialize(&mut serializer)?; Ok(serializer.output) @@ -131,6 +135,7 @@ impl ser::Serializer for &mut Serializer { 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(()) @@ -201,10 +206,20 @@ impl ser::Serializer for &mut Serializer { } 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(()); + match self.state { + State::FirstListElement { .. } => { + self.parse_state(STRING_ID)?; + } + State::MapKey => { + self.parse_state(STRING_ID)?; + self.state = State::Named(v.to_string()); + return Ok(()); + } + _ => (), + } + + if self.in_struct { + self.parse_state(STRING_ID)?; } self.output @@ -316,6 +331,11 @@ impl ser::Serializer for &mut Serializer { self.state = State::FirstListElement { len: len.unwrap() as i32, }; + + if len == Some(0) { + self.output.put_u8(END_ID); + self.output.put_i32(0); + } } } @@ -356,24 +376,29 @@ impl ser::Serializer for &mut Serializer { } fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { - self.output.put_u8(COMPOUND_ID); - match &mut self.state { State::Root(root_name) => { + self.output.put_u8(COMPOUND_ID); if let Some(root_name) = root_name { self.output .put(NbtTag::String(root_name.clone()).serialize_data()); } } State::Named(string) => { + self.output.put_u8(COMPOUND_ID); self.output .put(NbtTag::String(string.clone()).serialize_data()); } + State::FirstListElement { .. } => self.parse_state(COMPOUND_ID)?, + State::ListElement => (), + _ => { unimplemented!() } } + self.in_struct = true; + Ok(self) } @@ -424,6 +449,7 @@ impl ser::SerializeStruct for &mut Serializer { fn end(self) -> Result<()> { self.output.put_u8(END_ID); + self.in_struct = false; Ok(()) } } diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index b7ac97395..489ed6439 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -36,9 +36,10 @@ zstd = "0.13.2" file-guard = "0.2" indexmap = "2.7" -enum_dispatch = "0.3" -fastnbt = { git = "https://github.com/owengage/fastnbt.git" } + + +enum_dispatch = "0.3" noise = "0.9" rand = "0.8" diff --git a/pumpkin-world/src/chunk/anvil.rs b/pumpkin-world/src/chunk/anvil.rs index 43c853c70..d01de6b34 100644 --- a/pumpkin-world/src/chunk/anvil.rs +++ b/pumpkin-world/src/chunk/anvil.rs @@ -1,5 +1,4 @@ use bytes::*; -use fastnbt::LongArray; use flate2::bufread::{GzDecoder, GzEncoder, ZlibDecoder, ZlibEncoder}; use indexmap::IndexMap; use pumpkin_config::ADVANCED_CONFIG; @@ -425,7 +424,7 @@ impl AnvilChunkFormat { sections.push(ChunkSection { y: i as i8 - 4, block_states: Some(ChunkSectionBlockStates { - data: Some(LongArray::new(section_longs)), + data: Some(section_longs), palette: palette .into_iter() .map(|entry| PaletteEntry { @@ -446,7 +445,10 @@ impl AnvilChunkFormat { sections, }; - fastnbt::to_bytes(&nbt).map_err(ChunkSerializingError::ErrorSerializingChunk) + match pumpkin_nbt::serializer::to_bytes(&nbt, "ChunkNbt".to_string()) { + Ok(bytes) => Ok(bytes.into_iter().collect()), + Err(e) => Err(ChunkSerializingError::ErrorSerializingChunk(e)), + } } /// Returns the next free writable sector diff --git a/pumpkin-world/src/chunk/mod.rs b/pumpkin-world/src/chunk/mod.rs index 7c2b1abbe..ed6e1890b 100644 --- a/pumpkin-world/src/chunk/mod.rs +++ b/pumpkin-world/src/chunk/mod.rs @@ -2,12 +2,13 @@ use dashmap::{ mapref::one::{Ref, RefMut}, DashMap, }; -use fastnbt::LongArray; use pumpkin_data::chunk::ChunkStatus; +use pumpkin_nbt::{deserializer::from_bytes, LongArray}; use pumpkin_util::math::{ceil_log2, vector2::Vector2}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, + io::Write, iter::repeat_with, path::{Path, PathBuf}, sync::{Arc, LazyLock}, @@ -167,10 +168,10 @@ struct PaletteEntry { #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "UPPERCASE")] pub struct ChunkHeightmaps { - // #[serde(with = "LongArray")] - motion_blocking: LongArray, - // #[serde(with = "LongArray")] - world_surface: LongArray, + #[serde(with = "LongArray")] + motion_blocking: Vec, + #[serde(with = "LongArray")] + world_surface: Vec, } #[derive(Serialize, Deserialize, Debug)] @@ -182,8 +183,8 @@ struct ChunkSection { #[derive(Serialize, Deserialize, Debug, Clone)] struct ChunkSectionBlockStates { - // #[serde(with = "LongArray")] - data: Option, + #[serde(with = "LongArray")] + data: Option>, palette: Vec, } @@ -234,8 +235,8 @@ impl Default for ChunkHeightmaps { fn default() -> Self { Self { // 0 packed into an i64 7 times. - motion_blocking: LongArray::new(vec![0; 37]), - world_surface: LongArray::new(vec![0; 37]), + motion_blocking: vec![0; 37], + world_surface: vec![0; 37], } } } @@ -390,29 +391,29 @@ impl ChunkData { } } -// I can't use an tag because it will break ChunkNBT, but status need to have a big S, so "Status" -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] -pub struct ChunkStatusWrapper { - status: ChunkStatus, -} - impl ChunkData { pub fn from_bytes( - chunk_data: &[u8], + mut chunk_data: &[u8], position: Vector2, ) -> Result { - if fastnbt::from_bytes::(chunk_data) - .map_err(|_| ChunkParsingError::FailedReadStatus)? - .status - != ChunkStatus::Full - { + // if from_bytes_unnamed::(&mut chunk_data) + // .map_err(|_| ChunkParsingError::FailedReadStatus)? + // .status + // != ChunkStatus::Full + // { + // return Err(ChunkParsingError::ChunkNotGenerated); + // } + + let mut file = std::fs::File::create("a.nbt").unwrap(); + file.write_all(chunk_data).unwrap(); + + let chunk_data = from_bytes::(&mut chunk_data) + .map_err(|e| ChunkParsingError::ErrorDeserializingChunk(e.to_string()))?; + + if chunk_data.status != ChunkStatus::Full { return Err(ChunkParsingError::ChunkNotGenerated); } - let chunk_data = fastnbt::from_bytes::(chunk_data) - .map_err(|e| ChunkParsingError::ErrorDeserializingChunk(e.to_string()))?; - if chunk_data.x_pos != position.x || chunk_data.z_pos != position.z { log::error!( "Expected chunk at {}:{}, but got {}:{}", @@ -517,5 +518,5 @@ fn convert_index(index: ChunkRelativeBlockCoordinates) -> usize { #[derive(Error, Debug)] pub enum ChunkSerializingError { #[error("Error serializing chunk: {0}")] - ErrorSerializingChunk(fastnbt::error::Error), + ErrorSerializingChunk(pumpkin_nbt::Error), } diff --git a/pumpkin-world/src/world_info/anvil.rs b/pumpkin-world/src/world_info/anvil.rs index d02b32e27..46a05a012 100644 --- a/pumpkin-world/src/world_info/anvil.rs +++ b/pumpkin-world/src/world_info/anvil.rs @@ -1,10 +1,11 @@ use std::{ fs::OpenOptions, - io::{Read, Write}, + io::{Cursor, Read, Write}, time::{SystemTime, UNIX_EPOCH}, }; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; +use pumpkin_nbt::deserializer::from_bytes; use serde::{Deserialize, Serialize}; use crate::level::LevelFolder; @@ -29,7 +30,9 @@ impl WorldInfoReader for AnvilLevelInfo { let mut decompressed_data = Vec::new(); decoder.read_to_end(&mut decompressed_data)?; - let info = fastnbt::from_bytes::(&decompressed_data) + let mut cursor = Cursor::new(decompressed_data); + + let info = from_bytes::(&mut cursor) .map_err(|e| WorldInfoError::DeserializationError(e.to_string()))?; // todo check version