Skip to content

Commit

Permalink
blockentity serde
Browse files Browse the repository at this point in the history
  • Loading branch information
JieningYu committed Mar 28, 2024
1 parent 0547316 commit 3a4bc49
Show file tree
Hide file tree
Showing 14 changed files with 592 additions and 150 deletions.
221 changes: 185 additions & 36 deletions crates/core/block-entity/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
//! Rimecraft block entity primitives.

use std::{any::TypeId, fmt::Debug};
use std::{any::TypeId, fmt::Debug, hash::Hash, marker::PhantomData};

use erased_serde::{serialize_trait_object, Serialize as ErasedSerialize};

use rimecraft_block::{BlockState, ProvideBlockStateExtTy};
use rimecraft_global_cx::ProvideIdTy;
use rimecraft_registry::{entry::RefEntry, Reg};
use rimecraft_global_cx::{ProvideIdTy, ProvideNbtTy};
use rimecraft_registry::{entry::RefEntry, ProvideRegistry, Reg, Registry};
use rimecraft_serde_update::{erased::ErasedUpdate, update_trait_object};
use rimecraft_voxel_math::BlockPos;
use serde::Serialize;
use serde::{de::DeserializeSeed, Deserialize, Deserializer, Serialize};

pub use rimecraft_downcast::ToStatic;

/// A type of [`BlockEntity`].
pub trait RawBlockEntityType<Cx>
pub trait RawBlockEntityType<Cx>: Debug
where
Cx: ProvideBlockStateExtTy,
{
Expand All @@ -26,12 +26,14 @@ where
&self,
pos: BlockPos,
state: BlockState<'w, Cx>,
) -> Option<BlockState<'w, Cx>>;
) -> Option<Box<BlockEntity<'w, Cx>>>;
}

/// A type of [`BlockEntity`] that can be used in a type erased context.
pub type RawBlockEntityTypeDyn<'r, Cx> = Box<dyn RawBlockEntityType<Cx> + Send + Sync + 'r>;

/// A type of [`BlockEntity`].
pub type BlockEntityType<'r, Cx> =
Reg<'r, <Cx as ProvideIdTy>::Id, Box<dyn RawBlockEntityType<Cx> + Send + Sync + 'r>>;
pub type BlockEntityType<'r, Cx> = Reg<'r, <Cx as ProvideIdTy>::Id, RawBlockEntityTypeDyn<'r, Cx>>;

/// An object holding extra data about a block in a world.
pub struct RawBlockEntity<'a, T: ?Sized, Cx>
Expand Down Expand Up @@ -79,34 +81,6 @@ where
}
}

impl<T, Cx> Serialize for RawBlockEntity<'_, T, Cx>
where
Cx: ProvideBlockStateExtTy,
T: Serialize + ?Sized,
{
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.data.serialize(serializer)
}
}

impl<'de, T, Cx> rimecraft_serde_update::Update<'de> for RawBlockEntity<'_, T, Cx>
where
Cx: ProvideBlockStateExtTy,
T: rimecraft_serde_update::Update<'de> + ?Sized,
{
#[inline]
fn update<D>(&mut self, deserializer: D) -> Result<(), <D as serde::Deserializer<'de>>::Error>
where
D: serde::Deserializer<'de>,
{
self.data.update(deserializer)
}
}

impl<T, Cx> Debug for RawBlockEntity<'_, T, Cx>
where
Cx: ProvideBlockStateExtTy + Debug,
Expand Down Expand Up @@ -193,3 +167,178 @@ where
self.data.type_id() == TypeId::of::<T::StaticRepr>()
}
}

impl<T, Cx> Serialize for RawBlockEntity<'_, T, Cx>
where
Cx: ProvideBlockStateExtTy,
T: ?Sized + Serialize,
Cx::Id: Serialize,
{
/// Serializes this block entity's data and type id.
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("id", &<&RefEntry<_, _>>::from(self.ty))?;
self.data
.serialize(serde::__private::ser::FlatMapSerializer(&mut map))?;
map.end()
}
}

impl<'de, T, Cx> rimecraft_serde_update::Update<'de> for RawBlockEntity<'_, T, Cx>
where
Cx: ProvideBlockStateExtTy,
T: rimecraft_serde_update::Update<'de> + ?Sized,
{
#[inline]
fn update<D>(&mut self, deserializer: D) -> Result<(), <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
self.data.update(deserializer)
}
}

/// A [`DeserializeSeed`] for [`BlockEntity`].
///
/// This deserializes the id of the block entity type and its data.
pub struct CreateFromNbt<'w, Cx>
where
Cx: ProvideBlockStateExtTy,
{
/// The position of the block entity.
pub pos: BlockPos,
/// The state of the [`Block`] the block entity belongs to.
pub state: BlockState<'w, Cx>,
}

impl<Cx> Debug for CreateFromNbt<'_, Cx>
where
Cx: ProvideBlockStateExtTy + Debug,
Cx::BlockStateExt: Debug,
Cx::Id: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CreateFromNbt")
.field("pos", &self.pos)
.field("state", &self.state)
.finish()
}
}

impl<'w, 'de, Cx> DeserializeSeed<'de> for CreateFromNbt<'w, Cx>
where
Cx: ProvideBlockStateExtTy
+ ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>
+ ProvideNbtTy,
Cx::Id: Deserialize<'de> + Hash + Eq,
{
type Value = Option<Box<BlockEntity<'w, Cx>>>;

fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor<'a, 'w, Cx>(&'a CreateFromNbt<'w, Cx>)
where
Cx: ProvideBlockStateExtTy;

impl<'de, 'w, Cx> serde::de::Visitor<'de> for Visitor<'_, 'w, Cx>
where
Cx: ProvideBlockStateExtTy
+ ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>
+ ProvideNbtTy,
Cx::Id: Deserialize<'de> + Hash + Eq,
{
type Value = Option<Box<BlockEntity<'w, Cx>>>;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a block entity")
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
use serde::__private::de::Content;

let mut id: Option<Cx::Id> = None;
let mut collect: Vec<Option<(Content<'de>, Content<'de>)>> =
Vec::with_capacity(map.size_hint().map_or(0, |i| i - 1));

enum Field<'de> {
Id,
Other(Content<'de>),
}

impl<'de> Deserialize<'de> for Field<'de> {
fn deserialize<D>(deserializer: D) -> Result<Field<'de>, D::Error>
where
D: Deserializer<'de>,
{
struct FieldVisitor;

impl<'de> serde::de::Visitor<'de> for FieldVisitor {
type Value = Field<'de>;

fn expecting(
&self,
formatter: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
formatter.write_str("a field")
}

fn visit_str<E>(self, v: &str) -> Result<Field<'de>, E>
where
E: serde::de::Error,
{
match v {
"id" => Ok(Field::Id),
_ => Ok(Field::Other(Content::String(v.into()))),
}
}
}

deserializer.deserialize_identifier(FieldVisitor)
}
}

while let Some(key) = map.next_key()? {
match key {
Field::Id => {
if id.is_some() {
return Err(serde::de::Error::duplicate_field("id"));
}

id = Some(map.next_value()?);
}
Field::Other(content) => {
collect.push(Some((content, map.next_value()?)));
}
}
}

let id = id.ok_or_else(|| serde::de::Error::missing_field("id"))?;
let registry: &Registry<_, RawBlockEntityTypeDyn<'w, Cx>> = Cx::registry();
let ty = registry.get(&id).ok_or_else(|| {
serde::de::Error::custom(format!("unknown block entity type: {}", id))
})?;

let mut be = ty.instantiate(self.0.pos, self.0.state.clone());
// Update the block entity data.
if let Some(be) = &mut be {
rimecraft_serde_update::Update::update(
&mut be.data,
serde::__private::de::FlatMapDeserializer(&mut collect, PhantomData),
)?;
}
Ok(be)
}
}

deserializer.deserialize_map(Visitor(&self))
}
}
4 changes: 3 additions & 1 deletion crates/core/global-cx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std;

use core::fmt::Display;

use alloc::boxed::Box;

/// Marker trait for global contexts.
Expand All @@ -21,7 +23,7 @@ pub trait GlobalContext: Sized + 'static {}
/// Marker trait for global contexts that provide an identifier type.
pub trait ProvideIdTy: GlobalContext {
/// Identifier type.
type Id;
type Id: Display;
}

/// Marker trait for global contexts that provide a `NbtCompound` type and friends.
Expand Down
18 changes: 14 additions & 4 deletions crates/core/item/src/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,25 +310,35 @@ mod _serde {
let mut count = 0u8;
let mut tag = None;

#[derive(Deserialize)]
#[serde(field_identifier)]
enum Field {
#[serde(rename = "id")]
Id,
#[serde(rename = "Count")]
Count,
#[serde(rename = "tag")]
Tag,
}

while let Some(key) = map.next_key()? {
match key {
"id" => {
Field::Id => {
if id.is_some() {
return Err(serde::de::Error::duplicate_field("id"));
}
let entry: &RefEntry<Cx::Id, RawItem<Cx>> = map.next_value()?;
id = Some(Cx::registry().of_raw(entry.raw_id()).unwrap());
}
"Count" => {
Field::Count => {
count = map.next_value::<u8>()?;
}
"tag" => {
Field::Tag => {
if tag.is_some() {
return Err(serde::de::Error::duplicate_field("tag"));
}
tag = Some(map.next_value()?);
}
_ => {}
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/core/palette/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ where
.then(|| self.data.palette.get(0))
.flatten()
{
counter(&val, self.data.storage.len());
counter(&*val, self.data.storage.len());
} else {
let mut map = AHashMap::new();
if let Some(array) = self.data.storage.as_array() {
Expand All @@ -152,7 +152,7 @@ where
.into_iter()
.filter_map(|(i, c)| self.data.palette.get(i as usize).map(|i| (i, c)))
{
counter(&obj, c);
counter(&*obj, c);
}
}
}
Expand Down Expand Up @@ -286,7 +286,7 @@ where
.as_array()
.and_then(|array| array.get(i))
.and_then(|i| palette.get(i as usize))
.and_then(|obj| self.palette.index(&obj))
.and_then(|obj| self.palette.index(&*obj))
{
self.storage.as_array_mut().unwrap().swap(i, raw as u32);
}
Expand Down
Loading

0 comments on commit 3a4bc49

Please sign in to comment.