Rusty-Quartz / quartz_nbt

Provides support for encoding and decoding Minecraft's NBT format. This crate supports both zlib and gz compression, and also provides tools for converting NBT data to stringified NBT (SNBT) and vice versa.
https://crates.io/crates/quartz_nbt
MIT License
25 stars 10 forks source link

Quartz_nbt Deserializer misses fields depending on the Deserialize implementation of another field. #5

Open Rossterd opened 2 years ago

Rossterd commented 2 years ago

I'm using quartz_nbt together with serde to deserialize chunk information, and I am getting the following possibly erroneous behaviour.

#[derive(Serialize, Deserialize, Debug)]
pub struct ChunkV1_17 {
    #[serde(rename = "Level")]
    pub level: LevelV1_17,
    #[serde(rename = "DataVersion")]
    pub data_version: i32,
}

pub struct LevelV1_17 {
    #[serde(rename = "xPos")]
    x_pos: i32,

    #[serde(rename = "Biomes")]
    biomes: Option<BiomesV1_17>,
}

type BiomesV1_17 = Array3D<u8, 4, 64, 4>;

impl Serialize for BiomesV1_17 {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        todo!()
    }
}

impl<'de> Deserialize<'de> for BiomesV1_17 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {   
        let data: Vec<i32> = Deserialize::deserialize(deserializer)?;

        Ok( BiomesV1_17::default() )

    }
}

Works, but if I don't deserialize any data in my deserialize function like so:

#[derive(Serialize, Deserialize, Debug)]
pub struct ChunkV1_17 {
    #[serde(rename = "Level")]
    pub level: LevelV1_17,
    #[serde(rename = "DataVersion")]
    pub data_version: i32,
}

pub struct LevelV1_17 {
    #[serde(rename = "xPos")]
    x_pos: i32,

    #[serde(rename = "Biomes")]
    biomes: Option<BiomesV1_17>,
}

type BiomesV1_17 = Array3D<u8, 4, 64, 4>;

impl Serialize for BiomesV1_17 {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        todo!()
    }
}

impl<'de> Deserialize<'de> for BiomesV1_17 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {   
        // let data: Vec<i32> = Deserialize::deserialize(deserializer)?;

        Ok( BiomesV1_17::default() )

    }
}

I get the following error: thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom("missing field `xPos`")'

Cassy343 commented 2 years ago

The reason this happens is that the NBT deserializer reads bytes as requested, i.e. it does not parse the NBT before mapping it into Rust's data model. What's happening is that serde matches a NBT field named "biomes" to your BiomesV1_17 type, and then calls your type's Deserialize implementation, expecting it to parse all bytes associated with the value of that field so that the next bytes in the reader are part of the next tag name. In the second example, when it tries to parse the next tag name, it runs into biome data, so it gets confused. All of this is to say that if your Deserialize implementation gets invoked, you need to ensure that all the bytes constituting the value of your type are read.