From b2b5c791473202eb8b840a655956a5b86fc68059 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Tue, 13 Aug 2024 22:40:37 +0800 Subject: [PATCH 01/10] done item and itemstacks --- crates/core/component/src/lib.rs | 2 +- crates/core/component/src/map.rs | 81 +++++++- crates/core/item/Cargo.toml | 1 + crates/core/item/src/edcode.rs | 46 +++-- crates/core/item/src/lib.rs | 59 +++--- crates/core/item/src/stack.rs | 340 ++++++++++++++----------------- 6 files changed, 295 insertions(+), 234 deletions(-) diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs index 4ed654dc..385a4385 100644 --- a/crates/core/component/src/lib.rs +++ b/crates/core/component/src/lib.rs @@ -240,7 +240,7 @@ struct UnsafePacketCodec<'a> { #[derive(Debug, Clone, Copy)] struct DynUtil<'a> { clone: fn(&Object<'a>) -> Box>, - eq: fn(&Object<'a>, &Object<'a>) -> bool, + eq: fn(&'_ Object<'a>, &'_ Object<'a>) -> bool, } #[derive(Debug, Clone, Copy)] diff --git a/crates/core/component/src/map.rs b/crates/core/component/src/map.rs index 10a8d6cc..4b2c02c9 100644 --- a/crates/core/component/src/map.rs +++ b/crates/core/component/src/map.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; use crate::{ changes::ComponentChanges, dyn_any, ComponentType, ErasedComponentType, Object, - RawErasedComponentType, UnsafeSerdeCodec, UnsafeDebugIter, + RawErasedComponentType, UnsafeDebugIter, UnsafeSerdeCodec, }; #[repr(transparent)] @@ -43,7 +43,7 @@ where { #[inline] fn default() -> Self { - Self::empty() + Self::EMPTY } } @@ -51,10 +51,14 @@ impl<'a, Cx> ComponentMap<'a, Cx> where Cx: ProvideIdTy, { + /// An empty component map. + pub const EMPTY: Self = Self(MapInner::Empty); + /// Creates an empty component map. + #[deprecated = "use `ComponentMap::EMPTY` instead"] #[inline] pub const fn empty() -> Self { - Self(MapInner::Empty) + Self::EMPTY } /// Creates a **patched** component map with given base map. @@ -67,6 +71,24 @@ where }) } + /// Creates a **patched** component map with given base map and changes. + pub fn with_changes( + base: &'a ComponentMap<'a, Cx>, + changes: ComponentChanges<'a, '_, Cx>, + ) -> Self { + Self(MapInner::Patched { + base, + changes_count: changes.changed.values().map(|v| v.is_some() as isize).sum(), + changes: match changes.changed { + Maybe::Borrowed(c) => c + .iter() + .map(|(k, v)| (CompTyCell(k.0), v.as_deref().map(k.0.f.util.clone))) + .collect(), + Maybe::Owned(c) => c.0, + }, + }) + } + /// Returns a builder for creating a simple component map. #[inline] pub fn builder() -> Builder<'a, Cx> { @@ -87,7 +109,7 @@ where } #[inline] - fn get_raw(&self, ty: &RawErasedComponentType<'a, Cx>) -> Option<&Object<'_>> { + fn get_raw(&self, ty: &RawErasedComponentType<'a, Cx>) -> Option<&Object<'a>> { match &self.0 { MapInner::Empty => None, MapInner::Patched { base, changes, .. } => changes @@ -333,8 +355,8 @@ where Some(ComponentChanges { changed: Maybe::Borrowed(changes), ser_count: changes - .iter() - .filter(|(cell, _)| cell.0.is_serializable()) + .keys() + .filter(|cell| cell.0.is_serializable()) .count(), }) } else { @@ -448,6 +470,53 @@ where } } +impl PartialEq for ComponentMap<'_, Cx> +where + Cx: ProvideIdTy, +{ + fn eq(&self, other: &Self) -> bool { + if self.len() != other.len() { + return false; + } + + self.iter().all(move |(ty, obj)| { + other + .get_raw(&*ty) + .map_or(false, |o| (ty.f.util.eq)(obj, o)) + }) + } +} + +impl Eq for ComponentMap<'_, Cx> where Cx: ProvideIdTy {} + +impl Clone for ComponentMap<'_, Cx> +where + Cx: ProvideIdTy, +{ + fn clone(&self) -> Self { + match &self.0 { + MapInner::Empty => Self::EMPTY, + MapInner::Patched { + base, + changes, + changes_count, + } => Self(MapInner::Patched { + base, + changes: changes + .iter() + .map(|(k, v)| (CompTyCell(k.0), v.as_deref().map(k.0.f.util.clone))) + .collect(), + changes_count: *changes_count, + }), + MapInner::Simple(map) => Self(MapInner::Simple( + map.iter() + .map(|(k, v)| (CompTyCell(k.0), (k.0.f.util.clone)(&**v))) + .collect(), + )), + } + } +} + /// A builder for creating a simple component map. pub struct Builder<'a, Cx> where diff --git a/crates/core/item/Cargo.toml b/crates/core/item/Cargo.toml index 6c6daa11..0c49bc9d 100644 --- a/crates/core/item/Cargo.toml +++ b/crates/core/item/Cargo.toml @@ -20,6 +20,7 @@ rimecraft-registry = { path = "../registry" } rimecraft-fmt = { path = "../../util/fmt" } serde = { version = "1.0", optional = true } edcode2 = { path = "../../util/edcode2", package = "rimecraft-edcode2", optional = true } +component = { path = "../component", package = "rimecraft-component" } [features] default = ["serde", "edcode"] diff --git a/crates/core/item/src/edcode.rs b/crates/core/item/src/edcode.rs index 25b085b5..0fbab5ed 100644 --- a/crates/core/item/src/edcode.rs +++ b/crates/core/item/src/edcode.rs @@ -1,43 +1,51 @@ +use component::{changes::ComponentChanges, map::ComponentMap, RawErasedComponentType}; use edcode2::{Buf, BufExt, BufMut, BufMutExt, Decode, Encode}; -use rimecraft_global_cx::nbt_edcode::{ReadNbt, WriteNbt}; -use rimecraft_registry::{ProvideRegistry, Reg}; +use rimecraft_registry::ProvideRegistry; -use crate::{stack::ItemStackCx, ItemStack, RawItem}; +use crate::{stack::ItemStackCx, Item, ItemSettings, ItemStack, RawItem}; -impl Encode for ItemStack<'_, Cx> +impl<'r, Cx, B> Encode for ItemStack<'r, Cx> where - Cx: ItemStackCx + for<'a> WriteNbt>, + Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>>, B: BufMut, { fn encode(&self, mut buf: B) -> Result<(), edcode2::BoxedError<'static>> { - if self.count() == 0 { - buf.put_bool(false); + if self.is_empty() { + buf.put_variable(0u32); + Ok(()) } else { - buf.put_bool(true); + buf.put_variable(self.count()); let item = self.item(); item.encode(&mut buf)?; self.count().encode(&mut buf)?; - if item.settings().max_damage.is_some() || item.settings().sync_nbt { - Cx::write_nbt(self.nbt(), buf.writer())? - } + self.components() + .changes() + .ok_or("components not patched")? + .encode(buf) } - Ok(()) } } impl<'r, 'de, Cx, B> Decode<'de, B> for ItemStack<'r, Cx> where - Cx: ItemStackCx + ReadNbt> + ProvideRegistry<'r, Cx::Id, RawItem>, + 'r: 'de, + Cx: ItemStackCx Decode<'de, &'b mut B>> + + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>> + + ProvideRegistry<'r, Cx::Id, RawErasedComponentType<'r, Cx>>, B: Buf, { fn decode(mut buf: B) -> Result> { - if buf.get_bool() { - let item = Reg::<'r, Cx::Id, RawItem>::decode(&mut buf)?; - let count = buf.get_u8(); - let nbt = Cx::read_nbt(buf.reader())?; - Ok(ItemStack::with_nbt(item, count, nbt)) - } else { + let count: u32 = buf.get_variable(); + if count == 0 { Ok(ItemStack::empty()) + } else { + let item = Item::<'r, Cx>::decode(&mut buf)?; + let changes = ComponentChanges::<'r, 'r, Cx>::decode(buf)?; + Ok(ItemStack::with_component( + item, + count, + ComponentMap::with_changes(item.settings().components(), changes), + )) } } } diff --git a/crates/core/item/src/lib.rs b/crates/core/item/src/lib.rs index c991a598..ef9c9b0a 100644 --- a/crates/core/item/src/lib.rs +++ b/crates/core/item/src/lib.rs @@ -2,8 +2,9 @@ use std::{marker::PhantomData, num::NonZeroU32}; +use component::map::ComponentMap; use rimecraft_fmt::Formatting; -use rimecraft_global_cx::ProvideIdTy; +use rimecraft_global_cx::{GlobalContext, ProvideIdTy}; use rimecraft_registry::{ProvideRegistry, Reg}; #[cfg(feature = "edcode")] @@ -12,17 +13,40 @@ pub mod stack; pub use stack::ItemStack; +/// Provides settings type for items. +pub trait ProvideSettingsTy: GlobalContext { + /// Settings type of an item. + type Settings<'a>: ItemSettings<'a, Self>; +} + +/// Settings of an item. +pub trait ItemSettings<'a, Cx> { + /// Returns the base settings of the item. + fn base(&self) -> &BaseSettings; + + /// Returns the *base components* of the item. + fn components(&self) -> &'a ComponentMap<'a, Cx> + where + Cx: ProvideIdTy; +} + /// Item containing settings. #[derive(Debug)] -pub struct RawItem { - settings: Settings, +pub struct RawItem<'a, Cx> +where + Cx: ProvideSettingsTy, +{ + settings: Cx::Settings<'a>, _marker: PhantomData, } -impl RawItem { +impl<'a, Cx> RawItem<'a, Cx> +where + Cx: ProvideSettingsTy, +{ /// Creates a new `Item` with the given settings. #[inline] - pub const fn new(settings: Settings) -> Self { + pub const fn new(settings: Cx::Settings<'a>) -> Self { Self { settings, _marker: PhantomData, @@ -31,21 +55,14 @@ impl RawItem { /// Returns the settings of the item. #[inline] - pub fn settings(&self) -> &Settings { + pub fn settings(&self) -> &Cx::Settings<'a> { &self.settings } } -impl From for RawItem { - #[inline] - fn from(settings: Settings) -> Self { - Self::new(settings) - } -} - -impl<'r, K, Cx> ProvideRegistry<'r, K, Self> for RawItem +impl<'r, K, Cx> ProvideRegistry<'r, K, Self> for RawItem<'r, Cx> where - Cx: ProvideRegistry<'r, K, Self>, + Cx: ProvideRegistry<'r, K, Self> + ProvideSettingsTy, { #[inline] fn registry() -> &'r rimecraft_registry::Registry { @@ -54,17 +71,17 @@ where } /// An item usable by players and other entities. -pub type Item<'r, Cx> = Reg<'r, ::Id, RawItem>; +pub type Item<'r, Cx> = Reg<'r, ::Id, RawItem<'r, Cx>>; /// The max item count of an `ItemStack`. pub const MAX_STACK_COUNT: u32 = 64; -/// Settings of an [`Item`]. +/// Base settings of an [`Item`]. /// /// A setting configure behaviors common to all items, such as the /// stack's max count. #[derive(Debug, Clone, Copy, PartialEq)] -pub struct Settings { +pub struct BaseSettings { /// The maximum count of the item that can be stacked in a single slot. pub max_count: NonZeroU32, /// The maximum amount of damage the item can take. @@ -77,7 +94,7 @@ pub struct Settings { pub sync_nbt: bool, } -impl Default for Settings { +impl Default for BaseSettings { #[inline] fn default() -> Self { Self { @@ -88,10 +105,6 @@ impl Default for Settings { } } } - -#[doc(alias = "ItemProperties")] -pub use Settings as ItemSettings; - /// Rarity of an item. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] #[non_exhaustive] diff --git a/crates/core/item/src/stack.rs b/crates/core/item/src/stack.rs index de0a6fdb..d616da62 100644 --- a/crates/core/item/src/stack.rs +++ b/crates/core/item/src/stack.rs @@ -1,16 +1,17 @@ //! Item stack related types and traits. -use rimecraft_global_cx::{ProvideIdTy, ProvideNbtTy}; +use component::map::ComponentMap; +use rimecraft_global_cx::ProvideIdTy; use rimecraft_registry::ProvideRegistry; use std::{fmt::Debug, marker::PhantomData}; -use crate::{Item, RawItem}; +use crate::{Item, ItemSettings, ProvideSettingsTy, RawItem}; /// Global context used for item stacks. -pub trait ItemStackCx: ProvideIdTy + ProvideNbtTy {} +pub trait ItemStackCx: ProvideIdTy + ProvideSettingsTy {} -impl ItemStackCx for T where T: ProvideIdTy + ProvideNbtTy {} +impl ItemStackCx for T where T: ProvideIdTy + ProvideSettingsTy {} /// A stack of items. /// @@ -20,10 +21,9 @@ where Cx: ItemStackCx, { item: Item<'r, Cx>, - count: u8, + count: u32, - /// Item stack's custom NBT. - nbt: Option, + components: ComponentMap<'r, Cx>, } impl<'r, Cx> ItemStack<'r, Cx> @@ -32,31 +32,34 @@ where { /// Creates a new item stack with the given item and count. #[inline] - pub fn new(item: Item<'r, Cx>, count: u8) -> Self { - Self::with_nbt(item, count, None) + pub fn new(item: Item<'r, Cx>, count: u32) -> Self { + Self::with_component(item, count, ComponentMap::new(item.settings().components())) } /// Creates a new item stack with the given item, count, and custom NBT tag. - pub fn with_nbt(item: Item<'r, Cx>, count: u8, nbt: Option) -> Self { - Self { item, count, nbt } + pub fn with_component( + item: Item<'r, Cx>, + count: u32, + components: ComponentMap<'r, Cx>, + ) -> Self { + Self { + item, + count, + components, + } } } impl<'r, Cx> ItemStack<'r, Cx> where - Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem> + 'r, + Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>>, { /// Creates an empty item stack. #[inline] pub fn empty() -> Self { Self::new(Item::default(), 0) } -} -impl<'r, Cx> ItemStack<'r, Cx> -where - Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem> + 'r, -{ /// Returns whether the stack is empty. #[inline] pub fn is_empty(&self) -> bool { @@ -76,50 +79,32 @@ where /// Returns the count of the stack. #[inline] - pub fn count(&self) -> u8 { + pub fn count(&self) -> u32 { self.count } - /// Returns the custom NBT of the stack. + /// Returns the components of the stack. #[inline] - pub fn nbt(&self) -> Option<&Cx::Compound> { - self.nbt.as_ref() + pub fn components(&self) -> &ComponentMap<'r, Cx> { + &self.components } - /// Returns a mutable reference to the custom NBT of the stack. + /// Returns a mutable reference to the components of the stack. #[inline] - pub fn nbt_mut(&mut self) -> Option<&mut Cx::Compound> { - self.nbt.as_mut() + pub fn components_mut(&mut self) -> &mut ComponentMap<'r, Cx> { + &mut self.components } /// Sets the count of the stack. #[inline] - pub fn set_count(&mut self, count: u8) { + pub fn set_count(&mut self, count: u32) { self.count = count; } - - /// Sets the custom NBT of the stack. - #[inline] - pub fn set_nbt(&mut self, nbt: Option) { - self.nbt = nbt; - } -} - -impl ItemStack<'_, Cx> -where - Cx: ItemStackCx, - Cx::Compound: Default, -{ - /// Returns the custom NBT of the stack, create one if it does not exist. - #[inline] - pub fn get_or_create_nbt(&mut self) -> &mut Cx::Compound { - self.nbt.get_or_insert_with(Default::default) - } } impl<'r, Cx> Default for ItemStack<'r, Cx> where - Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem> + 'r, + Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>> + 'r, { #[inline] fn default() -> Self { @@ -150,212 +135,197 @@ where impl PartialEq for ItemStack<'_, Cx> where Cx: ItemStackCx, - Cx::Compound: PartialEq, { #[inline] fn eq(&self, other: &Self) -> bool { - self.item == other.item && self.count == other.count && self.nbt == other.nbt + self.item == other.item && self.count == other.count && self.components == other.components } } -impl Eq for ItemStack<'_, Cx> -where - Cx: ItemStackCx, - Cx::Compound: Eq, -{ -} +impl Eq for ItemStack<'_, Cx> where Cx: ItemStackCx {} impl Clone for ItemStack<'_, Cx> where Cx: ItemStackCx, - Cx::Compound: Clone, { #[inline] fn clone(&self) -> Self { - Self::with_nbt(self.item, self.count, self.nbt.clone()) + Self::with_component(self.item, self.count, self.components.clone()) } } -impl Debug for ItemStack<'_, Cx> +impl<'r, Cx> Debug for ItemStack<'r, Cx> where - Cx: ItemStackCx + Debug, - Cx::Id: Debug, - Cx::Compound: Debug, + Cx: ItemStackCx: Debug> + Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ItemStack") .field("item", &self.item) .field("count", &self.count) - .field("nbt", &self.nbt) + .field("components", &self.components) .finish() } } -#[cfg(feature = "serde")] -pub use _serde::{deserialize_vanilla, serialize_vanilla, DeserItemStack, SerItemStack}; - #[cfg(feature = "serde")] #[allow(clippy::missing_errors_doc)] mod _serde { - use std::hash::Hash; + use std::{hash::Hash, str::FromStr}; + use component::{changes::ComponentChanges, RawErasedComponentType}; use rimecraft_registry::entry::RefEntry; use serde::{Deserialize, Serialize}; use super::*; - /// Global context behavior for serializing item stacks. - pub trait SerItemStack<'r>: ItemStackCx { - /// Serializes the item stack. - fn serialize(serializer: S, stack: &ItemStack<'r, Self>) -> Result - where - S: serde::Serializer; - } - - /// Global context behavior for deserializing item stacks. - pub trait DeserItemStack<'r, 'de>: ItemStackCx { - /// Deserializes the item stack. - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: serde::Deserializer<'de>; - } - - impl<'r, Cx> Serialize for ItemStack<'r, Cx> + impl Serialize for ItemStack<'_, Cx> where - Cx: SerItemStack<'r>, + Cx: ItemStackCx, { - #[inline] fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { - Cx::serialize(serializer, self) + use serde::ser::SerializeStruct; + + let mut state = serializer + .serialize_struct("ItemStack", 2 + self.components.is_empty() as usize)?; + let entry: &RefEntry<_, _> = self.item.into(); + state.serialize_field("id", entry)?; + state.serialize_field("count", &self.count)?; + state.serialize_field( + "components", + &self + .components + .changes() + .ok_or_else(|| serde::ser::Error::custom("components not patched"))?, + )?; + state.end() } } impl<'r, 'de, Cx> Deserialize<'de> for ItemStack<'r, Cx> where - Cx: DeserItemStack<'r, 'de>, + 'r: 'de, + Cx: ItemStackCx + + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>> + + ProvideRegistry<'r, Cx::Id, RawErasedComponentType<'r, Cx>>, + Cx::Id: Deserialize<'de> + FromStr + Hash + Eq, { #[inline] fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { - Cx::deserialize(deserializer) - } - } - - /// Serializes the item stack in vanilla format. - /// - /// This is a helper function for implementing [`SerItemStack`] for a global context. - pub fn serialize_vanilla( - serializer: S, - stack: &ItemStack<'_, Cx>, - ) -> Result - where - S: serde::Serializer, - Cx: ItemStackCx, - Cx::Id: Serialize, - Cx::Compound: Serialize, - { - use serde::ser::SerializeStruct; - - let mut state = - serializer.serialize_struct("ItemStack", 2 + stack.nbt.is_some() as usize)?; - let entry: &RefEntry<_, _> = stack.item.into(); - state.serialize_field("id", entry)?; - state.serialize_field("Count", &stack.count)?; - if let Some(nbt) = &stack.nbt { - state.serialize_field("tag", nbt)?; - } - state.end() - } - - /// Deserializes the item stack in vanilla format. - /// - /// This is a helper function for implementing [`DeserItemStack`] for a global context. - pub fn deserialize_vanilla<'r, 'de, Cx, D>( - deserializer: D, - ) -> Result, D::Error> - where - 'r: 'de, - D: serde::Deserializer<'de>, - Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem>, - Cx::Id: Deserialize<'de> + Hash + Eq, - Cx::Compound: Deserialize<'de>, - { - struct Visitor<'r, Cx> { - _marker: PhantomData, - } - - impl<'r, 'de, Cx> serde::de::Visitor<'de> for Visitor<'r, Cx> - where - 'r: 'de, - Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem>, - Cx::Id: Deserialize<'de> + Hash + Eq, - Cx::Compound: Deserialize<'de>, - { - type Value = ItemStack<'r, Cx>; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("a vanilla item stack structure") + struct Visitor<'r, Cx> { + _marker: PhantomData, } - fn visit_map(self, mut map: A) -> Result + impl<'r, 'de, Cx> serde::de::Visitor<'de> for Visitor<'r, Cx> where - A: serde::de::MapAccess<'de>, + 'r: 'de, + Cx: ItemStackCx + + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>> + + ProvideRegistry<'r, Cx::Id, RawErasedComponentType<'r, Cx>>, + Cx::Id: Deserialize<'de> + FromStr + Hash + Eq, { - let mut id = None; - 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, + type Value = ItemStack<'r, Cx>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("a vanilla item stack structure") } - while let Some(key) = map.next_key()? { - match key { - Field::Id => { - if id.is_some() { - return Err(serde::de::Error::duplicate_field("id")); + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut id = None; + let mut count = 0u32; + let mut components: Option> = None; + + enum Field { + Id, + Count, + Tag, + } + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct FieldVisitor; + + impl serde::de::Visitor<'_> for FieldVisitor { + type Value = Field; + + fn expecting( + &self, + formatter: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + formatter.write_str("`id`, `Count`, or `tag`") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "id" => Ok(Field::Id), + "count" => Ok(Field::Count), + "components" => Ok(Field::Tag), + _ => Err(serde::de::Error::unknown_field( + value, + &["id", "count", "components"], + )), + } + } } - let entry: &RefEntry> = map.next_value()?; - id = Some(Cx::registry().of_raw(entry.raw_id()).unwrap()); - } - Field::Count => { - count = map.next_value::()?; + deserializer.deserialize_identifier(FieldVisitor) } - Field::Tag => { - if tag.is_some() { - return Err(serde::de::Error::duplicate_field("tag")); + } + + while let Some(key) = map.next_key()? { + match key { + Field::Id => { + if id.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + let entry: &RefEntry> = map.next_value()?; + id = Some(Cx::registry().of_raw(entry.raw_id()).unwrap()); + } + Field::Count => { + count = map.next_value::()?; + } + Field::Tag => { + if components.is_some() { + return Err(serde::de::Error::duplicate_field("components")); + } + components = Some(map.next_value()?); } - tag = Some(map.next_value()?); } } - } - Ok(ItemStack { - item: id.ok_or_else(|| serde::de::Error::missing_field("id"))?, - count, - nbt: tag.ok_or_else(|| serde::de::Error::missing_field("tag"))?, - }) + let item = id.ok_or_else(|| serde::de::Error::missing_field("id"))?; + Ok(ItemStack { + item, + count, + components: ComponentMap::with_changes( + item.settings().components(), + components + .ok_or_else(|| serde::de::Error::missing_field("components"))?, + ), + }) + } } - } - deserializer.deserialize_struct( - "ItemStack", - &["id", "Count", "tag"], - Visitor { - _marker: PhantomData, - }, - ) + deserializer.deserialize_struct( + "ItemStack", + &["id", "count", "components"], + Visitor { + _marker: PhantomData, + }, + ) + } } } From 30acd4f75bf4d6a1d582ff8941ff0e9cfbb79b63 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Wed, 14 Aug 2024 11:17:02 +0800 Subject: [PATCH 02/10] components for item --- crates/core/component/src/lib.rs | 9 ++++-- crates/core/component/src/map.rs | 12 ++++++++ crates/core/item/src/edcode.rs | 4 +-- crates/core/item/src/lib.rs | 51 +++++++------------------------- crates/core/item/src/stack.rs | 10 +++++-- 5 files changed, 38 insertions(+), 48 deletions(-) diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs index 385a4385..4e1b89b1 100644 --- a/crates/core/component/src/lib.rs +++ b/crates/core/component/src/lib.rs @@ -43,13 +43,17 @@ impl ComponentType<'_, T> { impl<'a, T> ComponentType<'a, T> where - T: Clone + Eq + Send + Sync + 'a, + T: Clone + Eq + Hash + Send + Sync + 'a, { const UTIL: DynUtil<'a> = DynUtil { clone: |obj| Box::new(unsafe { &*(obj as *const Object<'_> as *const T) }.clone()), eq: |a, b| unsafe { *(a as *const Object<'_> as *const T) == *(b as *const Object<'_> as *const T) }, + hash: |obj, mut state| { + let obj = unsafe { &*(obj as *const Object<'_> as *const T) }; + obj.hash(&mut state); + }, }; } @@ -156,7 +160,7 @@ impl<'a, T, Cx> TypeBuilder<'a, T, Cx> { impl<'a, T, Cx> TypeBuilder<'a, T, Cx> where - T: Clone + Eq + Send + Sync + 'a, + T: Clone + Eq + Hash + Send + Sync + 'a, { /// Builds a new [`ComponentType`] with the given codecs. pub const fn build(self) -> ComponentType<'a, T> { @@ -241,6 +245,7 @@ struct UnsafePacketCodec<'a> { struct DynUtil<'a> { clone: fn(&Object<'a>) -> Box>, eq: fn(&'_ Object<'a>, &'_ Object<'a>) -> bool, + hash: fn(&'_ Object<'a>, &'_ mut dyn std::hash::Hasher), } #[derive(Debug, Clone, Copy)] diff --git a/crates/core/component/src/map.rs b/crates/core/component/src/map.rs index 4b2c02c9..494b48ff 100644 --- a/crates/core/component/src/map.rs +++ b/crates/core/component/src/map.rs @@ -489,6 +489,18 @@ where impl Eq for ComponentMap<'_, Cx> where Cx: ProvideIdTy {} +impl Hash for ComponentMap<'_, Cx> +where + Cx: ProvideIdTy, +{ + fn hash(&self, state: &mut H) { + for (ty, obj) in self.iter() { + ty.hash(state); + (ty.f.util.hash)(obj, state); + } + } +} + impl Clone for ComponentMap<'_, Cx> where Cx: ProvideIdTy, diff --git a/crates/core/item/src/edcode.rs b/crates/core/item/src/edcode.rs index 0fbab5ed..37cd68f1 100644 --- a/crates/core/item/src/edcode.rs +++ b/crates/core/item/src/edcode.rs @@ -1,6 +1,6 @@ use component::{changes::ComponentChanges, map::ComponentMap, RawErasedComponentType}; use edcode2::{Buf, BufExt, BufMut, BufMutExt, Decode, Encode}; -use rimecraft_registry::ProvideRegistry; +use rimecraft_registry::{ProvideRegistry, Reg}; use crate::{stack::ItemStackCx, Item, ItemSettings, ItemStack, RawItem}; @@ -44,7 +44,7 @@ where Ok(ItemStack::with_component( item, count, - ComponentMap::with_changes(item.settings().components(), changes), + ComponentMap::with_changes(Reg::into_inner(item).settings().components(), changes), )) } } diff --git a/crates/core/item/src/lib.rs b/crates/core/item/src/lib.rs index ef9c9b0a..60314fef 100644 --- a/crates/core/item/src/lib.rs +++ b/crates/core/item/src/lib.rs @@ -1,10 +1,10 @@ //! Minecraft item primitives. -use std::{marker::PhantomData, num::NonZeroU32}; +use std::marker::PhantomData; use component::map::ComponentMap; use rimecraft_fmt::Formatting; -use rimecraft_global_cx::{GlobalContext, ProvideIdTy}; +use rimecraft_global_cx::ProvideIdTy; use rimecraft_registry::{ProvideRegistry, Reg}; #[cfg(feature = "edcode")] @@ -14,20 +14,18 @@ pub mod stack; pub use stack::ItemStack; /// Provides settings type for items. -pub trait ProvideSettingsTy: GlobalContext { +pub trait ProvideSettingsTy: ProvideIdTy { /// Settings type of an item. type Settings<'a>: ItemSettings<'a, Self>; } /// Settings of an item. -pub trait ItemSettings<'a, Cx> { - /// Returns the base settings of the item. - fn base(&self) -> &BaseSettings; - - /// Returns the *base components* of the item. - fn components(&self) -> &'a ComponentMap<'a, Cx> - where - Cx: ProvideIdTy; +pub trait ItemSettings<'a, Cx> +where + Cx: ProvideIdTy, +{ + /// Returns components of the item. + fn components(&self) -> &ComponentMap<'a, Cx>; } /// Item containing settings. @@ -74,37 +72,8 @@ where pub type Item<'r, Cx> = Reg<'r, ::Id, RawItem<'r, Cx>>; /// The max item count of an `ItemStack`. -pub const MAX_STACK_COUNT: u32 = 64; - -/// Base settings of an [`Item`]. -/// -/// A setting configure behaviors common to all items, such as the -/// stack's max count. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct BaseSettings { - /// The maximum count of the item that can be stacked in a single slot. - pub max_count: NonZeroU32, - /// The maximum amount of damage the item can take. - pub max_damage: Option, - - /// The rarity of the item. - pub rarity: Rarity, - - /// Whether an item should have its NBT data sent to the client. - pub sync_nbt: bool, -} +pub const MAX_STACK_COUNT: u32 = 64u32; -impl Default for BaseSettings { - #[inline] - fn default() -> Self { - Self { - max_count: NonZeroU32::new(MAX_STACK_COUNT).unwrap(), - max_damage: None, - rarity: Default::default(), - sync_nbt: true, - } - } -} /// Rarity of an item. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)] #[non_exhaustive] diff --git a/crates/core/item/src/stack.rs b/crates/core/item/src/stack.rs index d616da62..d8e2d447 100644 --- a/crates/core/item/src/stack.rs +++ b/crates/core/item/src/stack.rs @@ -2,7 +2,7 @@ use component::map::ComponentMap; use rimecraft_global_cx::ProvideIdTy; -use rimecraft_registry::ProvideRegistry; +use rimecraft_registry::{ProvideRegistry, Reg}; use std::{fmt::Debug, marker::PhantomData}; @@ -33,7 +33,11 @@ where /// Creates a new item stack with the given item and count. #[inline] pub fn new(item: Item<'r, Cx>, count: u32) -> Self { - Self::with_component(item, count, ComponentMap::new(item.settings().components())) + Self::with_component( + item, + count, + ComponentMap::new(Reg::into_inner(item).settings().components()), + ) } /// Creates a new item stack with the given item, count, and custom NBT tag. @@ -311,7 +315,7 @@ mod _serde { item, count, components: ComponentMap::with_changes( - item.settings().components(), + Reg::into_inner(item).settings().components(), components .ok_or_else(|| serde::de::Error::missing_field("components"))?, ), From b11b67872ca811332f35b184a49c128379a1ba83 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sat, 17 Aug 2024 14:47:18 +0800 Subject: [PATCH 03/10] basic components support for BEs --- crates/core/block-entity/Cargo.toml | 7 +- .../core/block-entity/src/components_util.rs | 50 +++++ crates/core/block-entity/src/deser_nbt.rs | 8 +- crates/core/block-entity/src/lib.rs | 177 ++++++++++++++++-- crates/core/component/src/changes.rs | 58 +++++- crates/core/component/src/lib.rs | 16 +- crates/core/component/src/map.rs | 86 +++++++-- crates/core/world/src/chunk/world_chunk.rs | 18 +- crates/util/serde-update/src/erased.rs | 1 + 9 files changed, 368 insertions(+), 53 deletions(-) create mode 100644 crates/core/block-entity/src/components_util.rs diff --git a/crates/core/block-entity/Cargo.toml b/crates/core/block-entity/Cargo.toml index 09e22131..2dd38271 100644 --- a/crates/core/block-entity/Cargo.toml +++ b/crates/core/block-entity/Cargo.toml @@ -16,11 +16,14 @@ rimecraft-global-cx = { path = "../global-cx" } rimecraft-registry = { path = "../registry" } rimecraft-block = { path = "../block" } rimecraft-voxel-math = { path = "../../util/voxel-math" } -rimecraft-downcast = { path = "../../util/downcast" } +typeid = "1.0" ahash = "0.8" serde = "1.0" erased-serde = "0.4" -rimecraft-serde-update = { path = "../../util/serde-update", features = ["erased"] } +rimecraft-serde-update = { path = "../../util/serde-update", features = [ + "erased", +] } +component = { path = "../component", package = "rimecraft-component" } [features] diff --git a/crates/core/block-entity/src/components_util.rs b/crates/core/block-entity/src/components_util.rs new file mode 100644 index 00000000..eb73365c --- /dev/null +++ b/crates/core/block-entity/src/components_util.rs @@ -0,0 +1,50 @@ +use std::fmt::Debug; + +use ahash::AHashSet; +use component::{map::ComponentMap, ComponentType, RawErasedComponentType}; +use rimecraft_global_cx::ProvideIdTy; + +/// Access to components of a block entity. +pub struct ComponentsAccess<'env, 'a, Cx> +where + Cx: ProvideIdTy, +{ + pub(crate) set: &'env mut AHashSet>, + pub(crate) map: &'env ComponentMap<'a, Cx>, +} + +impl<'a, Cx> ComponentsAccess<'_, 'a, Cx> +where + Cx: ProvideIdTy, +{ + /// Gets a component of the given type. + /// + /// # Safety + /// + /// This function could not guarantee lifetime of type `T` is sound. + /// The type `T`'s lifetime parameters should not overlap lifetime `'a`. + pub unsafe fn get(&mut self, ty: &ComponentType<'a, T>) -> Option<&T> { + self.set.insert(RawErasedComponentType::from(ty)); + self.map.get(ty) + } + + /// Reborrow this access. + pub fn reborrow(&mut self) -> ComponentsAccess<'_, 'a, Cx> { + ComponentsAccess { + set: self.set, + map: self.map, + } + } +} + +impl Debug for ComponentsAccess<'_, '_, Cx> +where + Cx: ProvideIdTy + Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ComponentsAccess") + .field("set", &self.set) + .field("map", &self.map) + .finish() + } +} diff --git a/crates/core/block-entity/src/deser_nbt.rs b/crates/core/block-entity/src/deser_nbt.rs index 4a3feaa7..d8d554e9 100644 --- a/crates/core/block-entity/src/deser_nbt.rs +++ b/crates/core/block-entity/src/deser_nbt.rs @@ -8,7 +8,7 @@ use rimecraft_registry::ProvideRegistry; use rimecraft_voxel_math::BlockPos; use serde::{de::DeserializeSeed, Deserialize, Deserializer}; -use crate::{BlockEntity, ProvideBlockEntity, RawBlockEntityTypeDyn}; +use crate::{BlockEntity, DynRawBlockEntityType, ProvideBlockEntity}; /// The dummy value for block entity types. pub const DUMMY: &str = "DUMMY"; @@ -46,7 +46,7 @@ where impl<'w, 'de, Cx> DeserializeSeed<'de> for CreateFromNbt<'w, Cx> where Cx: ProvideBlockStateExtTy - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + ProvideNbtTy, Cx::Id: Deserialize<'de> + Hash + Eq, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, @@ -64,7 +64,7 @@ where impl<'de, 'w, Cx> serde::de::Visitor<'de> for Visitor<'w, Cx> where Cx: ProvideBlockStateExtTy - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + ProvideNbtTy, Cx::Id: Deserialize<'de> + Hash + Eq, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, @@ -205,7 +205,7 @@ where res } else { let id = id.ok_or_else(|| serde::de::Error::missing_field("id"))?; - let registry: &rimecraft_registry::Registry<_, RawBlockEntityTypeDyn<'w, Cx>> = + let registry: &rimecraft_registry::Registry<_, DynRawBlockEntityType<'w, Cx>> = Cx::registry(); let ty = registry.get(&id).ok_or_else(|| { serde::de::Error::custom(format!("unknown block entity type: {}", id)) diff --git a/crates/core/block-entity/src/lib.rs b/crates/core/block-entity/src/lib.rs index 0bd8491d..17189d69 100644 --- a/crates/core/block-entity/src/lib.rs +++ b/crates/core/block-entity/src/lib.rs @@ -2,19 +2,32 @@ use std::{any::TypeId, fmt::Debug}; +use ahash::AHashSet; +use component::{ + changes::ComponentChanges, map::ComponentMap, ErasedComponentType, RawErasedComponentType, +}; 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_serde_update::{erased::ErasedUpdate, update_trait_object}; +use rimecraft_serde_update::erased::ErasedUpdate; use rimecraft_voxel_math::BlockPos; use serde::{Deserializer, Serialize}; -pub use rimecraft_downcast::ToStatic; +//pub use rimecraft_downcast::ToStatic; +mod components_util; pub mod deser_nbt; +pub use components_util::ComponentsAccess; + +/// A trait for providing fundamental built-in component types. +pub trait ProvideBuiltInComponentTypes<'r>: ProvideIdTy { + /// The type of block entity data. + fn block_entity_data() -> ErasedComponentType<'r, Self>; +} + /// A type of [`BlockEntity`]. pub trait RawBlockEntityType: Debug where @@ -32,10 +45,10 @@ where } /// A type of [`BlockEntity`] that can be used in a type erased context. -pub type RawBlockEntityTypeDyn<'r, Cx> = Box + Send + Sync + 'r>; +pub type DynRawBlockEntityType<'r, Cx> = Box + Send + Sync + 'r>; /// A type of [`BlockEntity`]. -pub type BlockEntityType<'r, Cx> = Reg<'r, ::Id, RawBlockEntityTypeDyn<'r, Cx>>; +pub type BlockEntityType<'r, Cx> = Reg<'r, ::Id, DynRawBlockEntityType<'r, Cx>>; /// An object holding extra data about a block in a world. pub struct RawBlockEntity<'a, T: ?Sized, Cx> @@ -46,6 +59,7 @@ where pos: BlockPos, removed: bool, cached_state: BlockState<'a, Cx>, + components: ComponentMap<'a, Cx>, data: T, } @@ -67,11 +81,12 @@ where removed: false, cached_state: state, data, + components: ComponentMap::EMPTY, } } } -impl RawBlockEntity<'_, T, Cx> +impl<'a, T: ?Sized, Cx> RawBlockEntity<'a, T, Cx> where Cx: ProvideBlockStateExtTy, { @@ -93,6 +108,18 @@ where self.pos } + /// Gets the component map of this block entity. + #[inline] + pub fn components(&self) -> &ComponentMap<'a, Cx> { + &self.components + } + + /// Gets the mutable component map of this block entity. + #[inline] + pub fn components_mut(&mut self) -> &mut ComponentMap<'a, Cx> { + &mut self.components + } + /// Marks this block entity as not removed. #[inline] pub fn cancel_removal(&mut self) { @@ -112,6 +139,49 @@ where } } +impl<'a, T: ?Sized, Cx> RawBlockEntity<'a, T, Cx> +where + Cx: ProvideBlockStateExtTy + ProvideBuiltInComponentTypes<'a>, + T: Data<'a, Cx>, +{ + /// Reads components from given pair of default and changed components. + pub fn read_components( + &mut self, + default: &'a ComponentMap<'a, Cx>, + changes: ComponentChanges<'a, '_, Cx>, + ) { + let mut set: AHashSet> = [*Cx::block_entity_data()].into(); + let mut map = ComponentMap::with_changes(default, changes); + self.data.read_components(ComponentsAccess { + set: &mut set, + map: &mut map, + }); + + let Some(changes) = map.changes() else { + unreachable!() + }; + let (added, _) = changes + .retain(|ty| !set.contains(&*ty)) + .into_added_removed_pair(); + self.components = added; + } +} + +impl<'a, T: ?Sized, Cx> RawBlockEntity<'a, T, Cx> +where + Cx: ProvideBlockStateExtTy, + T: Data<'a, Cx>, +{ + /// Creates a component map from the data and inner components + /// of this block entity. + pub fn create_components(&self) -> ComponentMap<'a, Cx> { + let mut builder = ComponentMap::builder(); + builder.extend(self.components.iter()); + self.data.insert_components(&mut builder); + builder.build() + } +} + impl Debug for RawBlockEntity<'_, T, Cx> where Cx: ProvideBlockStateExtTy + Debug, @@ -130,17 +200,75 @@ where } } +/// A trait for generic block entity data types. +pub trait Data<'a, Cx> +where + Cx: ProvideIdTy, +{ + /// Reads components from the given accessor. + #[inline] + fn read_components(&mut self, accessor: ComponentsAccess<'_, 'a, Cx>) { + let _ = accessor; + } + + /// Writes components to the given builder. + #[inline] + fn insert_components(&self, builder: &mut component::map::Builder<'a, Cx>) { + let _ = builder; + } +} + /// Type erased block entity data. -pub trait ErasedData +/// +/// See [`Data`]. +pub trait ErasedData<'a, Cx> where - Self: ErasedSerialize + for<'de> ErasedUpdate<'de> + Send + Sync + Debug + sealed::Sealed, + Self: ErasedSerialize + + for<'de> ErasedUpdate<'de> + + Data<'a, Cx> + + Send + + Sync + + Debug + + sealed::Sealed, + Cx: ProvideIdTy, { /// The [`TypeId`] of this data. - fn type_id(&self) -> TypeId; + fn type_id(&self) -> TypeId { + typeid::of::() + } } -serialize_trait_object! { ErasedData } -update_trait_object! { ErasedData } +// Emit a warning +#[allow(single_use_lifetimes)] +mod ser_dyn_obj { + use super::*; + serialize_trait_object!(<'a, Cx> ErasedData<'a, Cx> where Cx: ProvideIdTy); +} + +impl<'de, Cx> rimecraft_serde_update::Update<'de> for dyn ErasedData<'_, Cx> + '_ +where + Cx: ProvideIdTy, +{ + rimecraft_serde_update::__internal_update_from_erased!(); +} +impl<'de, Cx> rimecraft_serde_update::Update<'de> for dyn ErasedData<'_, Cx> + Send + '_ +where + Cx: ProvideIdTy, +{ + rimecraft_serde_update::__internal_update_from_erased!(); +} +impl<'de, Cx> rimecraft_serde_update::Update<'de> for dyn ErasedData<'_, Cx> + Sync + '_ +where + Cx: ProvideIdTy, +{ + rimecraft_serde_update::__internal_update_from_erased!(); +} +impl<'de, Cx> rimecraft_serde_update::Update<'de> for dyn ErasedData<'_, Cx> + Send + Sync + '_ +where + Cx: ProvideIdTy, +{ + rimecraft_serde_update::__internal_update_from_erased!(); +} mod sealed { pub trait Sealed {} @@ -148,18 +276,19 @@ mod sealed { impl sealed::Sealed for T where T: ErasedSerialize + for<'de> ErasedUpdate<'de> + Send + Sync {} -impl ErasedData for T +impl<'a, T, Cx> ErasedData<'a, Cx> for T where - T: ErasedSerialize + for<'de> ErasedUpdate<'de> + ToStatic + Debug + Send + Sync, + T: ErasedSerialize + for<'de> ErasedUpdate<'de> + Data<'a, Cx> + Debug + Send + Sync, + Cx: ProvideIdTy, { #[inline] fn type_id(&self) -> TypeId { - TypeId::of::<::StaticRepr>() + typeid::of::() } } /// A type-erased variant of [`RawBlockEntity`]. -pub type BlockEntity<'w, Cx> = RawBlockEntity<'w, dyn ErasedData + 'w, Cx>; +pub type BlockEntity<'w, Cx> = RawBlockEntity<'w, dyn ErasedData<'w, Cx> + 'w, Cx>; impl<'w, Cx> BlockEntity<'w, Cx> where @@ -168,7 +297,12 @@ where /// Downcasts this type erased block entity into block entity with a concrete data type. /// /// This function returns an immutable reference if the type matches. - pub fn downcast_ref(&self) -> Option<&RawBlockEntity<'w, T, Cx>> { + /// + /// # Safety + /// + /// This function could not guarantee lifetime of type `T` is sound. + /// The type `T`'s lifetime parameters should not overlap lifetime `'w`. + pub unsafe fn downcast_ref(&self) -> Option<&RawBlockEntity<'w, T, Cx>> { if self.matches_type::() { unsafe { Some(&*(self as *const BlockEntity<'w, Cx> as *const RawBlockEntity<'w, T, Cx>)) @@ -181,7 +315,12 @@ where /// Downcasts this type erased block entity into block entity with a concrete data type. /// /// This function returns a mutable reference if the type matches. - pub fn downcast_mut(&mut self) -> Option<&mut RawBlockEntity<'w, T, Cx>> { + /// + /// # Safety + /// + /// This function could not guarantee lifetime of type `T` is sound. + /// The type `T`'s lifetime parameters should not overlap lifetime `'w`. + pub unsafe fn downcast_mut(&mut self) -> Option<&mut RawBlockEntity<'w, T, Cx>> { if self.matches_type::() { unsafe { Some(&mut *(self as *mut BlockEntity<'w, Cx> as *mut RawBlockEntity<'w, T, Cx>)) @@ -191,11 +330,11 @@ where } } - /// Whether the type of data in this block entity can be safely downcasted + /// Whether the type of data in this block entity can be safely downcast /// into the target type. #[inline] - pub fn matches_type(&self) -> bool { - self.data.type_id() == TypeId::of::() + pub fn matches_type(&self) -> bool { + self.data.type_id() == typeid::of::() } } diff --git a/crates/core/component/src/changes.rs b/crates/core/component/src/changes.rs index ff2b14f7..0c87c544 100644 --- a/crates/core/component/src/changes.rs +++ b/crates/core/component/src/changes.rs @@ -2,7 +2,7 @@ use std::{cell::UnsafeCell, fmt::Debug, marker::PhantomData, str::FromStr, sync::OnceLock}; -use ahash::AHashMap; +use ahash::{AHashMap, AHashSet}; use bytes::{Buf, BufMut}; use edcode2::{BufExt as _, BufMutExt as _, Decode, Encode}; use rimecraft_global_cx::ProvideIdTy; @@ -11,8 +11,9 @@ use rimecraft_registry::{ProvideRegistry, Reg}; use serde::{de::DeserializeSeed, ser::SerializeMap, Deserialize, Serialize}; use crate::{ - map::CompTyCell, ComponentType, ErasedComponentType, Object, RawErasedComponentType, - UnsafeDebugIter, UnsafeSerdeCodec, + map::{CompTyCell, ComponentMap}, + ComponentType, ErasedComponentType, Object, RawErasedComponentType, UnsafeDebugIter, + UnsafeSerdeCodec, }; /// Changes of components. @@ -68,6 +69,57 @@ where pub fn is_empty(&self) -> bool { self.changed.is_empty() } + + /// Retains only the components specified by the predicate. + pub fn retain<'cow, F>(self, mut f: F) -> ComponentChanges<'a, 'cow, Cx> + where + F: FnMut(ErasedComponentType<'a, Cx>) -> bool, + { + let mut this = self.into_owned(); + let Maybe::Owned(SimpleOwned(map)) = &mut this.changed else { + unreachable!() + }; + map.retain(|k, _| f(k.0)); + this + } + + /// Converts the changes into owned version. + pub fn into_owned<'cow>(self) -> ComponentChanges<'a, 'cow, Cx> { + ComponentChanges { + changed: match self.changed { + Maybe::Borrowed(borrowed) => Maybe::Owned(SimpleOwned( + borrowed + .iter() + .map(|(CompTyCell(k), v)| { + (CompTyCell(*k), v.as_deref().map(k.f.util.clone)) + }) + .collect(), + )), + Maybe::Owned(owned) => Maybe::Owned(owned), + }, + ser_count: self.ser_count, + } + } + + /// Converts the changes into a pair of added components and removed component types. + pub fn into_added_removed_pair( + self, + ) -> (ComponentMap<'a, Cx>, AHashSet>) { + if self.is_empty() { + (ComponentMap::EMPTY, AHashSet::new()) + } else { + let mut builder = ComponentMap::builder(); + let mut set = AHashSet::new(); + for (CompTyCell(ty), obj) in self.changed.iter() { + if let Some(obj) = obj { + builder.insert_raw(*ty, (ty.f.util.clone)(&**obj)); + } else { + set.insert(*ty); + } + } + (builder.build(), set) + } + } } impl Serialize for ComponentChanges<'_, '_, Cx> diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs index 4e1b89b1..95ae7cda 100644 --- a/crates/core/component/src/lib.rs +++ b/crates/core/component/src/lib.rs @@ -20,6 +20,8 @@ mod dyn_any; use dyn_any::Any; +pub use ahash::{AHashMap, AHashSet}; + /// Type of a component data. /// /// The type `T` should be unique for each component, as it's used to identify the component. @@ -115,7 +117,10 @@ where { SerdeCodec { codec: UnsafeSerdeCodec { - ser: |obj| unsafe { &*(obj as *const Object<'_> as *const T) }, + ser: |obj| unsafe { + &*(obj as *const Object<'_> as *const T + as *const (dyn erased_serde::Serialize + 'a)) + }, de: |deserializer| { erased_serde::deserialize::(deserializer).map(|v| { let v: Box> = Box::new(v); @@ -221,7 +226,7 @@ pub struct SerdeCodec<'a, T> { #[derive(Debug, Clone, Copy)] #[allow(dead_code)] struct UnsafeSerdeCodec<'a> { - ser: for<'s> fn(&'s Object<'a>) -> &'s dyn erased_serde::Serialize, + ser: for<'s> fn(&'s Object<'a>) -> &'s (dyn erased_serde::Serialize + 'a), de: fn(&mut dyn erased_serde::Deserializer<'_>) -> erased_serde::Result>>, upd: fn(&mut Object<'a>, &mut dyn erased_serde::Deserializer<'a>) -> erased_serde::Result<()>, } @@ -332,6 +337,13 @@ where } } +impl Copy for RawErasedComponentType<'_, Cx> {} +impl Clone for RawErasedComponentType<'_, Cx> { + fn clone(&self) -> Self { + *self + } +} + /// Registration wrapper of [`RawErasedComponentType`]. pub type ErasedComponentType<'a, Cx> = Reg<'a, ::Id, RawErasedComponentType<'a, Cx>>; diff --git a/crates/core/component/src/map.rs b/crates/core/component/src/map.rs index 494b48ff..a6ca1452 100644 --- a/crates/core/component/src/map.rs +++ b/crates/core/component/src/map.rs @@ -97,6 +97,14 @@ where } } + /// Returns a builder for creating a simple component map with given capacity. + #[inline] + pub fn builder_with_capacity(capacity: usize) -> Builder<'a, Cx> { + Builder { + map: AHashMap::with_capacity(capacity), + } + } + /// Gets the component with given type. /// /// # Safety @@ -108,8 +116,10 @@ where .and_then(|val| unsafe { val.downcast_ref() }) } - #[inline] - fn get_raw(&self, ty: &RawErasedComponentType<'a, Cx>) -> Option<&Object<'a>> { + /// Gets the component with given type. + /// + /// This function is similar to `get`, but it returns the raw object instead of the reference. + pub fn get_raw(&self, ty: &RawErasedComponentType<'a, Cx>) -> Option<&Object<'a>> { match &self.0 { MapInner::Empty => None, MapInner::Patched { base, changes, .. } => changes @@ -134,8 +144,10 @@ where .and_then(|(k, v)| v.downcast_ref().map(|v| (k, v))) } - #[inline] - fn get_key_value_raw( + /// Gets the component and its type registration with given type. + /// + /// This function is similar to `get_key_value`, but it returns the raw object instead of the reference. + pub fn get_key_value_raw( &self, ty: &RawErasedComponentType<'a, Cx>, ) -> Option<(ErasedComponentType<'a, Cx>, &Object<'a>)> { @@ -154,8 +166,10 @@ where self.contains_raw(&RawErasedComponentType::from(ty)) } - #[inline] - fn contains_raw(&self, ty: &RawErasedComponentType<'a, Cx>) -> bool { + /// Returns whether a component with given type exist. + /// + /// This function is similar to `contains`, but it receives the raw component type instead of the typed one. + pub fn contains_raw(&self, ty: &RawErasedComponentType<'a, Cx>) -> bool { match &self.0 { MapInner::Empty => false, MapInner::Patched { base, changes, .. } => changes @@ -177,11 +191,10 @@ where .and_then(|val| unsafe { val.downcast_mut() }) } - #[inline] - unsafe fn get_mut_raw( - &mut self, - ty: &RawErasedComponentType<'a, Cx>, - ) -> Option<&mut Object<'a>> { + /// Gets the component with given type, with mutable access. + /// + /// This function is similar to `get_mut`, but it returns the raw object instead of the reference. + pub fn get_mut_raw(&mut self, ty: &RawErasedComponentType<'a, Cx>) -> Option<&mut Object<'a>> { match &mut self.0 { MapInner::Empty => None, MapInner::Patched { base, changes, .. } => { @@ -547,8 +560,7 @@ where /// /// This function panics when the given component type's type information does not match with /// the given static type. - #[inline] - pub fn insert(mut self, ty: ErasedComponentType<'a, Cx>, val: T) -> Self + pub fn insert(&mut self, ty: ErasedComponentType<'a, Cx>, val: T) where T: Send + Sync + 'a, { @@ -558,7 +570,24 @@ where "the component type should matches the type of given value" ); self.map.insert(CompTyCell(ty), Box::new(val)); - self + } + + /// Inserts a component into this map. + /// + /// This function is similar to `insert`, but it receives the raw component type instead of the typed one. + /// + /// # Panics + /// + /// This function panics when the given component type's type information does not match with + /// the type of given value. + #[inline] + pub fn insert_raw(&mut self, ty: ErasedComponentType<'a, Cx>, val: Box>) { + assert_eq!( + ty.ty, + (*val).type_id(), + "the component type should matches the type of given value" + ); + self.map.insert(CompTyCell(ty), val); } /// Builds the component map. @@ -571,6 +600,35 @@ where } } +impl<'a, Cx> Extend<(ErasedComponentType<'a, Cx>, Box>)> for Builder<'a, Cx> +where + Cx: ProvideIdTy, +{ + fn extend, Box>)>>( + &mut self, + iter: T, + ) { + self.map.extend( + iter.into_iter() + .filter(|(k, v)| k.ty == (**v).type_id()) + .map(|(k, v)| (CompTyCell(k), v)), + ); + } +} + +impl<'a, 's, Cx> Extend<(ErasedComponentType<'a, Cx>, &'s Object<'a>)> for Builder<'a, Cx> +where + Cx: ProvideIdTy, +{ + #[inline] + fn extend, &'s Object<'a>)>>( + &mut self, + iter: T, + ) { + self.extend(iter.into_iter().map(|(k, v)| (k, (k.f.util.clone)(v)))); + } +} + impl<'a, Cx> From> for ComponentMap<'a, Cx> where Cx: ProvideIdTy, diff --git a/crates/core/world/src/chunk/world_chunk.rs b/crates/core/world/src/chunk/world_chunk.rs index dd79585c..fb083819 100644 --- a/crates/core/world/src/chunk/world_chunk.rs +++ b/crates/core/world/src/chunk/world_chunk.rs @@ -3,7 +3,7 @@ use parking_lot::RwLock; use rimecraft_block::BlockState; use rimecraft_block_entity::{ - deser_nbt::CreateFromNbt, BlockEntity, ProvideBlockEntity, RawBlockEntityTypeDyn, + deser_nbt::CreateFromNbt, BlockEntity, DynRawBlockEntityType, ProvideBlockEntity, }; use rimecraft_chunk_palette::{Maybe, SimpleOwned}; use rimecraft_fluid::{BsToFs, FluidState}; @@ -83,7 +83,7 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -262,7 +262,7 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -320,7 +320,7 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -378,7 +378,7 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -496,7 +496,7 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -532,7 +532,7 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -546,7 +546,7 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -557,7 +557,7 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, RawBlockEntityTypeDyn<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { diff --git a/crates/util/serde-update/src/erased.rs b/crates/util/serde-update/src/erased.rs index c9c16be2..317bfa07 100644 --- a/crates/util/serde-update/src/erased.rs +++ b/crates/util/serde-update/src/erased.rs @@ -4,6 +4,7 @@ use crate::*; +// Part of the public API. #[doc(hidden)] #[macro_export] macro_rules! __internal_update_from_erased { From 8003850528bea55af45b286634ce5d532e37776b Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sat, 24 Aug 2024 20:29:30 +0800 Subject: [PATCH 04/10] blockentity serde --- crates/core/block-entity/Cargo.toml | 1 + crates/core/block-entity/src/deser_nbt.rs | 229 ----------------- crates/core/block-entity/src/lib.rs | 39 +-- crates/core/block-entity/src/serde.rs | 297 ++++++++++++++++++++++ crates/core/component/Cargo.toml | 2 +- crates/core/component/src/changes.rs | 2 +- crates/core/component/src/lib.rs | 20 +- crates/core/item/src/edcode.rs | 1 - crates/core/item/src/stack.rs | 2 - crates/core/registry/src/lib.rs | 23 +- 10 files changed, 325 insertions(+), 291 deletions(-) delete mode 100644 crates/core/block-entity/src/deser_nbt.rs create mode 100644 crates/core/block-entity/src/serde.rs diff --git a/crates/core/block-entity/Cargo.toml b/crates/core/block-entity/Cargo.toml index 2dd38271..fedbe91b 100644 --- a/crates/core/block-entity/Cargo.toml +++ b/crates/core/block-entity/Cargo.toml @@ -24,6 +24,7 @@ rimecraft-serde-update = { path = "../../util/serde-update", features = [ "erased", ] } component = { path = "../component", package = "rimecraft-component" } +bitflags = "2.6" [features] diff --git a/crates/core/block-entity/src/deser_nbt.rs b/crates/core/block-entity/src/deser_nbt.rs deleted file mode 100644 index d8d554e9..00000000 --- a/crates/core/block-entity/src/deser_nbt.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! Helper module for deserializing NBT data into block entities. - -use std::{fmt::Debug, hash::Hash, marker::PhantomData}; - -use rimecraft_block::{BlockState, ProvideBlockStateExtTy}; -use rimecraft_global_cx::ProvideNbtTy; -use rimecraft_registry::ProvideRegistry; -use rimecraft_voxel_math::BlockPos; -use serde::{de::DeserializeSeed, Deserialize, Deserializer}; - -use crate::{BlockEntity, DynRawBlockEntityType, ProvideBlockEntity}; - -/// The dummy value for block entity types. -pub const DUMMY: &str = "DUMMY"; - -/// 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>, - - /// Whether to respect the [`DUMMY`] value. - pub respect_dummy: bool, -} - -impl 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, DynRawBlockEntityType<'w, Cx>> - + ProvideNbtTy, - Cx::Id: Deserialize<'de> + Hash + Eq, - Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, -{ - type Value = Option>>; - - fn deserialize(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct Visitor<'w, Cx>(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, DynRawBlockEntityType<'w, Cx>> - + ProvideNbtTy, - Cx::Id: Deserialize<'de> + Hash + Eq, - Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, - { - type Value = Option>>; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("a block entity") - } - - fn visit_map(self, mut map: A) -> Result - where - A: serde::de::MapAccess<'de>, - { - use serde::__private::de::Content; - - let mut id: Option = None; - let mut is_dummy = false; - let mut collect: Vec, 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(deserializer: D) -> Result, 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(self, v: &str) -> Result, E> - where - E: serde::de::Error, - { - match v { - "id" => Ok(Field::Id), - _ => Ok(Field::Other(Content::String(v.into()))), - } - } - } - - deserializer.deserialize_identifier(FieldVisitor) - } - } - - #[derive(Deserialize)] - #[serde(untagged)] - enum MaybeDummy { - Dummy(Dummy), - Value(T), - } - - pub struct Dummy; - - impl<'de> Deserialize<'de> for Dummy { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct DummyVisitor; - - impl serde::de::Visitor<'_> for DummyVisitor { - type Value = Dummy; - - fn expecting( - &self, - f: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - write!(f, "a '{}' value", DUMMY) - } - - #[inline] - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - if v == DUMMY { - Ok(Dummy) - } else { - Err(serde::de::Error::custom("expected 'DUMMY'")) - } - } - } - - deserializer.deserialize_str(DummyVisitor) - } - } - - while let Some(key) = map.next_key()? { - match key { - Field::Id => { - if id.is_some() { - return Err(serde::de::Error::duplicate_field("id")); - } - - if self.0.respect_dummy { - let dummy: MaybeDummy = map.next_value()?; - match dummy { - MaybeDummy::Dummy(Dummy) => { - is_dummy = true; - } - MaybeDummy::Value(i) => { - id = Some(i); - } - } - } else { - id = Some(map.next_value()?); - } - } - Field::Other(content) => { - collect.push(Some((content, map.next_value()?))); - } - } - } - - if is_dummy { - let state = self.0.state.state; - let res = if let Some(constructor) = - ProvideBlockEntity::block_entity_constructor(state.data()) - { - Ok(Some(constructor(self.0.pos))) - } else { - Ok(None) - }; - res - } else { - let id = id.ok_or_else(|| serde::de::Error::missing_field("id"))?; - let registry: &rimecraft_registry::Registry<_, DynRawBlockEntityType<'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); - // 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)) - } -} diff --git a/crates/core/block-entity/src/lib.rs b/crates/core/block-entity/src/lib.rs index 17189d69..22342145 100644 --- a/crates/core/block-entity/src/lib.rs +++ b/crates/core/block-entity/src/lib.rs @@ -13,12 +13,9 @@ use rimecraft_global_cx::ProvideIdTy; use rimecraft_registry::{entry::RefEntry, Reg}; use rimecraft_serde_update::erased::ErasedUpdate; use rimecraft_voxel_math::BlockPos; -use serde::{Deserializer, Serialize}; - -//pub use rimecraft_downcast::ToStatic; mod components_util; -pub mod deser_nbt; +pub mod serde; pub use components_util::ComponentsAccess; @@ -338,40 +335,6 @@ where } } -impl 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(&self, serializer: S) -> Result - 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(&mut self, deserializer: D) -> Result<(), >::Error> - where - D: Deserializer<'de>, - { - self.data.update(deserializer) - } -} - /// A trait for providing block entities. /// /// This should be implemented for [`ProvideBlockStateExtTy::BlockStateExt`]s. diff --git a/crates/core/block-entity/src/serde.rs b/crates/core/block-entity/src/serde.rs new file mode 100644 index 00000000..67f549e1 --- /dev/null +++ b/crates/core/block-entity/src/serde.rs @@ -0,0 +1,297 @@ +//! Serialization and deserialization of block entities. + +use std::{fmt::Debug, marker::PhantomData}; + +use bitflags::bitflags; +use component::{map::ComponentMap, RawErasedComponentType}; +use rimecraft_block::{BlockState, ProvideBlockStateExtTy}; +use rimecraft_registry::ProvideRegistry; +use rimecraft_voxel_math::BlockPos; +use serde::{de::DeserializeSeed, Deserialize, Serialize}; + +use crate::{BlockEntity, DynRawBlockEntityType, RawBlockEntity}; + +bitflags! { + /// Essential flags for serializing a block entity. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct Flags: u8 { + /// Serializes the registration identifier. + const ID = 0b00000001; + /// Serializes the position. + const POS = 0b00000010; + /// Serializes the component map. + const COMPONENTS = 0b00000100; + } +} + +impl Flags { + /// Serializes the identifier and position. + #[inline(always)] + pub fn identifying_data() -> Self { + Self::POS | Self::ID + } +} + +impl Default for Flags { + #[inline] + fn default() -> Self { + Self::COMPONENTS + } +} + +/// Data flagged by [`SerializeFlags`], for serialization. +#[derive(Debug)] +pub struct Flagged(pub T, pub Flags); + +impl Serialize for Flagged<&RawBlockEntity<'_, T, Cx>> +where + Cx: ProvideBlockStateExtTy, + T: ?Sized + Serialize, + Cx::Id: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeMap; + let mut map = serializer.serialize_map(None)?; + for flag in self.1.iter() { + match flag { + Flags::COMPONENTS => { + if !self.0.components.is_empty() { + map.serialize_entry(&Field::Components, &self.0.components)? + } + } + Flags::ID => { + map.serialize_entry(&Field::Id, &self.0.ty)?; + } + Flags::POS => { + map.serialize_entry(&Field::X, &self.0.pos.x())?; + map.serialize_entry(&Field::Y, &self.0.pos.y())?; + map.serialize_entry(&Field::Z, &self.0.pos.z())?; + } + _ => {} + } + } + self.0 + .data + .serialize(serde::__private::ser::FlatMapSerializer(&mut map))?; + map.end() + } +} + +impl Serialize for Flagged> +where + Cx: ProvideBlockStateExtTy, + T: Serialize, + Cx::Id: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + Flagged(&self.0, self.1).serialize(serializer) + } +} + +enum Field<'de> { + Id, + Components, + + X, + Y, + Z, + + Other(serde::__private::de::Content<'de>), +} + +impl Serialize for Field<'_> { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(match self { + Field::Id => "id", + Field::Components => "components", + Field::X => "x", + Field::Y => "y", + Field::Z => "z", + Field::Other(_) => unimplemented!(), + }) + } +} + +impl<'de> Deserialize<'de> for Field<'de> { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor<'de>(PhantomData<&'de ()>); + + impl<'de> serde::de::Visitor<'de> for Visitor<'de> { + type Value = Field<'de>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "a field") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match v { + "id" => Ok(Field::Id), + "components" => Ok(Field::Components), + "x" => Ok(Field::X), + "y" => Ok(Field::Y), + "z" => Ok(Field::Z), + other => Ok(Field::Other(serde::__private::de::Content::String( + other.to_owned(), + ))), + } + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: serde::de::Error, + { + match v { + "id" => Ok(Field::Id), + "components" => Ok(Field::Components), + "x" => Ok(Field::X), + "y" => Ok(Field::Y), + "z" => Ok(Field::Z), + other => Ok(Field::Other(serde::__private::de::Content::Str(other))), + } + } + } + + deserializer.deserialize_identifier(Visitor(PhantomData)) + } +} + +/// This serializes the block entity using default value of [`SerializeFlags`]. +impl Serialize for RawBlockEntity<'_, T, Cx> +where + Cx: ProvideBlockStateExtTy, + T: ?Sized + Serialize, + Cx::Id: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + Flagged(self, Flags::default()).serialize(serializer) + } +} + +/// Seed for deserializing a block state. +pub struct Seed<'a, Cx> +where + Cx: ProvideBlockStateExtTy, +{ + /// Position of the block state. + pub pos: BlockPos, + /// State of the block. + pub state: BlockState<'a, Cx>, +} + +impl<'a, 'de, Cx> DeserializeSeed<'de> for Seed<'a, Cx> +where + Cx: ProvideBlockStateExtTy> + + ProvideRegistry<'a, Cx::Id, RawErasedComponentType<'a, Cx>> + + ProvideRegistry<'a, Cx::Id, DynRawBlockEntityType<'a, Cx>>, +{ + type Value = Box>; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor<'a, Cx>(BlockPos, BlockState<'a, Cx>) + where + Cx: ProvideBlockStateExtTy; + + impl<'a, 'de, Cx> serde::de::Visitor<'de> for Visitor<'a, Cx> + where + Cx: ProvideBlockStateExtTy> + + ProvideRegistry<'a, Cx::Id, RawErasedComponentType<'a, Cx>> + + ProvideRegistry<'a, Cx::Id, DynRawBlockEntityType<'a, Cx>>, + { + type Value = Box>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "a block entity") + } + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut id: Option = None; + let mut components: Option> = None; + use serde::__private::de::Content; + let mut collect: Vec, Content<'de>)>> = + Vec::with_capacity(map.size_hint().map_or(0, |i| i - 1)); + + while let Some(field) = map.next_key::>()? { + match field { + Field::Id => id = Some(map.next_value()?), + Field::Components => components = Some(map.next_value()?), + // Skip position information + Field::X | Field::Y | Field::Z => {} + Field::Other(c) => collect.push(Some((c, map.next_value()?))), + } + } + + let id = id.ok_or_else(|| serde::de::Error::missing_field("id"))?; + let components = components.unwrap_or_else(|| ComponentMap::EMPTY); + + 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, self.1) + .ok_or_else(|| serde::de::Error::custom("failed to create block entity"))?; + rimecraft_serde_update::Update::update( + &mut *be, + serde::__private::de::FlatMapDeserializer(&mut collect, PhantomData), + )?; + be.components = components; + + Ok(be) + } + } + + deserializer.deserialize_map(Visitor(self.pos, self.state.clone())) + } +} + +impl<'de, T, Cx> rimecraft_serde_update::Update<'de> for RawBlockEntity<'_, T, Cx> +where + Cx: ProvideBlockStateExtTy, + T: ?Sized + rimecraft_serde_update::Update<'de>, +{ + #[inline] + fn update(&mut self, deserializer: D) -> Result<(), >::Error> + where + D: serde::Deserializer<'de>, + { + self.data.update(deserializer) + } +} + +impl Debug for Seed<'_, Cx> +where + Cx: ProvideBlockStateExtTy + Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DeserializeSeed") + .field("pos", &self.pos) + .field("state", &self.state) + .finish() + } +} diff --git a/crates/core/component/Cargo.toml b/crates/core/component/Cargo.toml index 9f106eba..ad254a2a 100644 --- a/crates/core/component/Cargo.toml +++ b/crates/core/component/Cargo.toml @@ -17,7 +17,7 @@ erased-serde = "0.4" bytes = "1.6" ahash = "0.8" edcode2 = { path = "../../util/edcode2", package = "rimecraft-edcode2" } -rimecraft-registry = { path = "../registry", features = ["serde"] } +rimecraft-registry = { path = "../registry", features = ["serde", "edcode"] } rimecraft-global-cx = { path = "../global-cx", features = ["nbt", "std"] } rimecraft-maybe = { path = "../../util/maybe" } typeid = "1.0" diff --git a/crates/core/component/src/changes.rs b/crates/core/component/src/changes.rs index 0c87c544..7b6ee89d 100644 --- a/crates/core/component/src/changes.rs +++ b/crates/core/component/src/changes.rs @@ -273,7 +273,7 @@ where } } -impl<'a: 'de, 'de, Cx, B> Decode<'de, B> for ComponentChanges<'a, '_, Cx> +impl<'a, 'de, Cx, B> Decode<'de, B> for ComponentChanges<'a, '_, Cx> where Cx: ProvideIdTy Decode<'de, &'b mut B>> + ProvideRegistry<'a, Cx::Id, RawErasedComponentType<'a, Cx>>, diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs index 95ae7cda..d55d22a8 100644 --- a/crates/core/component/src/lib.rs +++ b/crates/core/component/src/lib.rs @@ -43,6 +43,18 @@ impl ComponentType<'_, T> { } } +impl<'a, T> ComponentType<'a, T> { + /// Creates a builder of component type. + #[inline] + pub const fn builder() -> TypeBuilder<'a, T, Cx> { + TypeBuilder { + serde_codec: None, + packet_codec: None, + _marker: PhantomData, + } + } +} + impl<'a, T> ComponentType<'a, T> where T: Clone + Eq + Hash + Send + Sync + 'a, @@ -62,7 +74,11 @@ where /// Creates a new [`PacketCodec`] by encoding and decoding through `edcode2`. pub const fn packet_codec_edcode<'a, T>() -> PacketCodec<'a, T> where - T: for<'b> Encode<&'b mut dyn BufMut> + for<'b> Decode<'a, &'b mut dyn Buf> + Send + Sync + 'a, + T: for<'b> Encode<&'b mut dyn BufMut> + + for<'b> Decode<'static, &'b mut dyn Buf> + + Send + + Sync + + 'a, { PacketCodec { codec: UnsafePacketCodec { @@ -242,7 +258,7 @@ pub struct PacketCodec<'a, T> { #[allow(dead_code)] struct UnsafePacketCodec<'a> { encode: fn(&'_ Object<'a>, &'_ mut dyn BufMut) -> Result<(), edcode2::BoxedError<'static>>, - decode: fn(&'_ mut dyn Buf) -> Result>, edcode2::BoxedError<'a>>, + decode: fn(&'_ mut dyn Buf) -> Result>, edcode2::BoxedError<'static>>, upd: fn(&'_ mut Object<'a>, &'_ mut dyn Buf) -> Result<(), edcode2::BoxedError<'a>>, } diff --git a/crates/core/item/src/edcode.rs b/crates/core/item/src/edcode.rs index 37cd68f1..fb344451 100644 --- a/crates/core/item/src/edcode.rs +++ b/crates/core/item/src/edcode.rs @@ -28,7 +28,6 @@ where impl<'r, 'de, Cx, B> Decode<'de, B> for ItemStack<'r, Cx> where - 'r: 'de, Cx: ItemStackCx Decode<'de, &'b mut B>> + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>> + ProvideRegistry<'r, Cx::Id, RawErasedComponentType<'r, Cx>>, diff --git a/crates/core/item/src/stack.rs b/crates/core/item/src/stack.rs index d8e2d447..644d376b 100644 --- a/crates/core/item/src/stack.rs +++ b/crates/core/item/src/stack.rs @@ -210,7 +210,6 @@ mod _serde { impl<'r, 'de, Cx> Deserialize<'de> for ItemStack<'r, Cx> where - 'r: 'de, Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>> + ProvideRegistry<'r, Cx::Id, RawErasedComponentType<'r, Cx>>, @@ -227,7 +226,6 @@ mod _serde { impl<'r, 'de, Cx> serde::de::Visitor<'de> for Visitor<'r, Cx> where - 'r: 'de, Cx: ItemStackCx + ProvideRegistry<'r, Cx::Id, RawItem<'r, Cx>> + ProvideRegistry<'r, Cx::Id, RawErasedComponentType<'r, Cx>>, diff --git a/crates/core/registry/src/lib.rs b/crates/core/registry/src/lib.rs index 0693c009..5c3e08f7 100644 --- a/crates/core/registry/src/lib.rs +++ b/crates/core/registry/src/lib.rs @@ -581,12 +581,8 @@ mod serde { where S: serde::Serializer, { - if serializer.is_human_readable() { - let entry: &RefEntry<_, _> = self.as_ref(); - entry.key.value().serialize(serializer) - } else { - serializer.serialize_i32(self.raw as i32) - } + let entry: &RefEntry<_, _> = self.as_ref(); + entry.key.value().serialize(serializer) } } @@ -599,17 +595,10 @@ mod serde { where D: serde::Deserializer<'de>, { - if deserializer.is_human_readable() { - let key = K::deserialize(deserializer)?; - T::registry() - .get(&key) - .ok_or_else(|| serde::de::Error::custom("key not found")) - } else { - let raw = i32::deserialize(deserializer)? as usize; - T::registry() - .of_raw(raw) - .ok_or_else(|| serde::de::Error::custom(format!("raw id {} not found", raw))) - } + let key = K::deserialize(deserializer)?; + T::registry() + .get(&key) + .ok_or_else(|| serde::de::Error::custom("key not found")) } } } From ea9e346466fb8196cf0cc1ca9365ba22923277da Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sat, 24 Aug 2024 20:36:34 +0800 Subject: [PATCH 05/10] fix world --- crates/core/block-entity/src/lib.rs | 9 +++-- crates/core/world/src/chunk/world_chunk.rs | 38 ++++++++++++---------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/crates/core/block-entity/src/lib.rs b/crates/core/block-entity/src/lib.rs index 22342145..232fde6c 100644 --- a/crates/core/block-entity/src/lib.rs +++ b/crates/core/block-entity/src/lib.rs @@ -2,10 +2,10 @@ use std::{any::TypeId, fmt::Debug}; -use ahash::AHashSet; -use component::{ +use ::component::{ changes::ComponentChanges, map::ComponentMap, ErasedComponentType, RawErasedComponentType, }; +use ahash::AHashSet; use erased_serde::{serialize_trait_object, Serialize as ErasedSerialize}; use rimecraft_block::{BlockState, ProvideBlockStateExtTy}; @@ -19,6 +19,11 @@ pub mod serde; pub use components_util::ComponentsAccess; +/// Re-export of `rimecraft-component` +pub mod component { + pub use ::component::*; +} + /// A trait for providing fundamental built-in component types. pub trait ProvideBuiltInComponentTypes<'r>: ProvideIdTy { /// The type of block entity data. diff --git a/crates/core/world/src/chunk/world_chunk.rs b/crates/core/world/src/chunk/world_chunk.rs index fb083819..0e48e0d4 100644 --- a/crates/core/world/src/chunk/world_chunk.rs +++ b/crates/core/world/src/chunk/world_chunk.rs @@ -3,7 +3,7 @@ use parking_lot::RwLock; use rimecraft_block::BlockState; use rimecraft_block_entity::{ - deser_nbt::CreateFromNbt, BlockEntity, DynRawBlockEntityType, ProvideBlockEntity, + component::RawErasedComponentType, BlockEntity, DynRawBlockEntityType, ProvideBlockEntity, }; use rimecraft_chunk_palette::{Maybe, SimpleOwned}; use rimecraft_fluid::{BsToFs, FluidState}; @@ -83,7 +83,8 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, RawErasedComponentType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -164,15 +165,13 @@ where nbt: Cx::Compound, ) -> Option> { let be = DeserializeSeed::deserialize( - CreateFromNbt { + rimecraft_block_entity::serde::Seed { pos, state: self.peek_block_state_lf(pos, BlockState::clone).unwrap(), - respect_dummy: true, }, Cx::compound_to_deserializer(&nbt), ) - .ok() - .flatten(); + .ok(); if let Some(be) = be { self.add_block_entity(be); @@ -195,15 +194,13 @@ where nbt: Cx::Compound, ) -> Option> { let be = DeserializeSeed::deserialize( - CreateFromNbt { + rimecraft_block_entity::serde::Seed { pos, state: self.peek_block_state(pos, BlockState::clone).unwrap(), - respect_dummy: true, }, Cx::compound_to_deserializer(&nbt), ) - .ok() - .flatten(); + .ok(); if let Some(be) = be { self.add_block_entity_locked(be); @@ -262,7 +259,8 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, RawErasedComponentType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -320,7 +318,8 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, RawErasedComponentType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -378,7 +377,8 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, RawErasedComponentType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -496,7 +496,8 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, RawErasedComponentType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -532,7 +533,8 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, RawErasedComponentType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -546,7 +548,8 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, RawErasedComponentType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { @@ -557,7 +560,8 @@ where Cx: ChunkCx<'w> + ComputeIndex> + BsToFs<'w> - + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>>, + + ProvideRegistry<'w, Cx::Id, DynRawBlockEntityType<'w, Cx>> + + ProvideRegistry<'w, Cx::Id, RawErasedComponentType<'w, Cx>>, Cx::BlockStateExt: ProvideBlockEntity<'w, Cx>, Cx::Id: for<'de> Deserialize<'de>, { From 11c8c239d0830c99d7ca23ae5b1d4aff006a4898 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sat, 24 Aug 2024 20:45:06 +0800 Subject: [PATCH 06/10] use empty value directly --- crates/core/block-entity/src/serde.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/block-entity/src/serde.rs b/crates/core/block-entity/src/serde.rs index 67f549e1..0aa1f1a3 100644 --- a/crates/core/block-entity/src/serde.rs +++ b/crates/core/block-entity/src/serde.rs @@ -246,7 +246,7 @@ where } let id = id.ok_or_else(|| serde::de::Error::missing_field("id"))?; - let components = components.unwrap_or_else(|| ComponentMap::EMPTY); + let components = components.unwrap_or(ComponentMap::EMPTY); let ty = >>::registry() .get(&id) From 1db2c4650eb4e4ac3e10a2f7be00a1e76a6e77f2 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sat, 24 Aug 2024 21:01:35 +0800 Subject: [PATCH 07/10] tidy up registry codec --- crates/core/registry/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/registry/src/lib.rs b/crates/core/registry/src/lib.rs index 5c3e08f7..c3567199 100644 --- a/crates/core/registry/src/lib.rs +++ b/crates/core/registry/src/lib.rs @@ -582,7 +582,7 @@ mod serde { S: serde::Serializer, { let entry: &RefEntry<_, _> = self.as_ref(); - entry.key.value().serialize(serializer) + entry.serialize(serializer) } } From b9a640f9bd779cff15b32c0f906a6a6a2a4f0772 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sat, 24 Aug 2024 21:08:19 +0800 Subject: [PATCH 08/10] functionality of providing version type for global context --- crates/core/global-cx/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/core/global-cx/src/lib.rs b/crates/core/global-cx/src/lib.rs index 65b0eed1..ae50027c 100644 --- a/crates/core/global-cx/src/lib.rs +++ b/crates/core/global-cx/src/lib.rs @@ -30,6 +30,12 @@ pub trait ProvideIdTy: GlobalContext { type Id: Display + Hash + Eq; } +/// Marker trait for global contexts that provide a version type. +pub trait ProvideVersionTy: GlobalContext { + /// Version type. + type Version: Display + Hash + Eq; +} + /// Marker trait for global contexts that provide a `NbtCompound` type and friends. #[cfg(feature = "nbt")] pub trait ProvideNbtTy: GlobalContext { From 433be96aaf9ac5b7b6c82a07c7ea6a91ee64f0ba Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sat, 24 Aug 2024 23:05:08 +0800 Subject: [PATCH 09/10] add panic doc for `TypeBuilder::build` --- crates/core/component/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/core/component/src/lib.rs b/crates/core/component/src/lib.rs index d55d22a8..684d00d4 100644 --- a/crates/core/component/src/lib.rs +++ b/crates/core/component/src/lib.rs @@ -184,6 +184,10 @@ where T: Clone + Eq + Hash + Send + Sync + 'a, { /// Builds a new [`ComponentType`] with the given codecs. + /// + /// # Panics + /// + /// Panics if the packet codec is not set. pub const fn build(self) -> ComponentType<'a, T> { ComponentType { f: Funcs { From 8c627640b21b7273feb2378e3f51e852e55c5aaa Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sun, 25 Aug 2024 11:26:29 +0800 Subject: [PATCH 10/10] shifting flags --- crates/core/block-entity/src/serde.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/block-entity/src/serde.rs b/crates/core/block-entity/src/serde.rs index 0aa1f1a3..1e5ffabd 100644 --- a/crates/core/block-entity/src/serde.rs +++ b/crates/core/block-entity/src/serde.rs @@ -16,11 +16,11 @@ bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Flags: u8 { /// Serializes the registration identifier. - const ID = 0b00000001; + const ID = 1u8 << 0; /// Serializes the position. - const POS = 0b00000010; + const POS = 1u8 << 1; /// Serializes the component map. - const COMPONENTS = 0b00000100; + const COMPONENTS = 1u8 << 2; } }