diff options
Diffstat (limited to 'engine-ecs/src/component.rs')
| -rw-r--r-- | engine-ecs/src/component.rs | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/engine-ecs/src/component.rs b/engine-ecs/src/component.rs new file mode 100644 index 0000000..d424e16 --- /dev/null +++ b/engine-ecs/src/component.rs @@ -0,0 +1,416 @@ +use std::any::{Any, TypeId}; +use std::fmt::Debug; +use std::ops::{Deref, DerefMut}; + +use engine_ecs_macros::Component; +use seq_macro::seq; + +use crate::event::component::Changed; +use crate::event::Submitter as EventSubmitter; +use crate::lock::{ + Error as LockError, + MappedReadGuard, + MappedWriteGuard, + ReadGuard, + WriteGuard, +}; +use crate::pair::{MemberMetadata as PairMemberMetadata, Pair}; +use crate::reflection::Type as TypeReflection; +use crate::system::Input as SystemInput; +use crate::uid::Uid; +use crate::{EntityComponentRef, World}; + +pub mod local; + +pub(crate) mod storage; + +/// A component that is not a pair and is not a sole. +pub trait Component: WithAssociatedId + SystemInput + Any + IntoParts +{ + fn type_reflection() -> Option<&'static TypeReflection> + where + Self: Sized; + + /// Returns the name of this component. + fn name(&self) -> &'static str; +} + +pub trait WithAssociatedId +{ + /// Returns the ID of this component. + fn id() -> Uid; +} + +/// A component that is removable from it's entity. +/// +/// This trait is implemented for [`crate::pair::Pair`] so it does not have [`Component`] +/// as supertrait. +pub trait Removable: WithAssociatedId {} + +impl<ComponentT: Component> Removable for ComponentT {} + +/// Implemented by tuples of types implementing [`Removable`]. +/// +/// This trait is sealed so it cannot be implemented by types outside of this module. +pub trait RemovableTuple: sealed::Sealed +{ + fn ids() -> impl IntoIterator<Item = Uid>; +} + +macro_rules! gen_removable_tuple_impl { + ($c: tt) => { + seq!(I in 0..=$c { + impl<#(RemovableT~I: Removable,)*> RemovableTuple for (#(RemovableT~I,)*) + { + fn ids() -> impl IntoIterator<Item = Uid> + { + [#(RemovableT~I::id(),)*] + } + } + + impl<#(RemovableT~I: Removable,)*> sealed::Sealed for (#(RemovableT~I,)*) + { + } + }); + }; +} + +seq!(C in 0..=16 { + gen_removable_tuple_impl!(C); +}); + +#[derive(Debug)] +pub struct Handle<'a, DataT: ?Sized + 'static> +{ + inner: MappedReadGuard<'a, DataT>, +} + +impl<'comp, DataT: 'static> Handle<'comp, DataT> +{ + /// Creates a new handle instance from a [`EntityComponentRef`]. + /// + /// # Errors + /// Will return `Err` if acquiring the component's lock fails. + pub fn from_entity_component_ref( + entity_component_ref: &EntityComponentRef<'comp>, + ) -> Result<Self, HandleError> + { + Self::new( + entity_component_ref + .component() + .read_nonblock() + .map_err(AcquireLockError)?, + ) + } + + fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> Result<Self, HandleError> + { + Ok(Self { + inner: ReadGuard::try_map(inner, |component| { + component.downcast_ref::<DataT>() + }) + .map_err(|_| HandleError::IncorrectType)?, + }) + } +} + +impl<'comp> Handle<'comp, dyn Any> +{ + /// Creates a handle to a untyped component. + /// + /// # Errors + /// Will return `Err` if acquiring the component's lock fails. + pub fn new_any( + entity_component_ref: &EntityComponentRef<'comp>, + ) -> Result<Self, HandleError> + { + Ok(Self { + inner: ReadGuard::map( + entity_component_ref + .component() + .read_nonblock() + .map_err(AcquireLockError)?, + |component| &**component, + ), + }) + } +} + +impl<DataT: ?Sized + 'static> Deref for Handle<'_, DataT> +{ + type Target = DataT; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +#[derive(Debug)] +pub struct HandleMut<'a, DataT: ?Sized + 'static> +{ + entity_component_ref: EntityComponentRef<'a>, + inner: MappedWriteGuard<'a, DataT>, + event_submitter: EventSubmitter<'a>, +} + +impl<'comp, DataT: 'static> HandleMut<'comp, DataT> +{ + /// Creates a new handle instance from a [`EntityComponentRef`]. + /// + /// # Errors + /// Will return `Err` if acquiring the component's lock fails. + pub fn from_entity_component_ref( + entity_component_ref: &EntityComponentRef<'comp>, + world: &'comp World, + ) -> Result<Self, HandleError> + { + let inner = entity_component_ref + .component() + .write_nonblock() + .map_err(AcquireLockError)?; + + Ok(Self { + entity_component_ref: entity_component_ref.clone(), + inner: WriteGuard::try_map(inner, |component| { + component.downcast_mut::<DataT>() + }) + .map_err(|_| HandleError::IncorrectType)?, + event_submitter: world.event_submitter(), + }) + } +} + +impl<'comp> HandleMut<'comp, dyn Any> +{ + /// Creates a handle to a untyped component. + /// + /// # Errors + /// Will return `Err` if acquiring the component's lock fails. + pub fn new_any( + entity_component_ref: &EntityComponentRef<'comp>, + world: &'comp World, + ) -> Result<Self, HandleError> + { + let inner = entity_component_ref + .component() + .write_nonblock() + .map_err(AcquireLockError)?; + + Ok(Self { + entity_component_ref: entity_component_ref.clone(), + inner: WriteGuard::map(inner, |component| &mut **component), + event_submitter: world.event_submitter(), + }) + } +} + +impl<DataT: ?Sized + 'static> HandleMut<'_, DataT> +{ + pub fn set_changed(&self) + { + self.event_submitter.submit_event( + &Pair::builder() + .relation::<Changed>() + .target_id(self.entity_component_ref.id()) + .build(), + self.entity_component_ref.entity_id(), + ); + } +} + +impl<DataT: ?Sized + 'static> Deref for HandleMut<'_, DataT> +{ + type Target = DataT; + + fn deref(&self) -> &Self::Target + { + &self.inner + } +} + +impl<DataT: ?Sized + 'static> DerefMut for HandleMut<'_, DataT> +{ + fn deref_mut(&mut self) -> &mut Self::Target + { + &mut self.inner + } +} + +#[derive(Debug, thiserror::Error)] +pub enum HandleError +{ + #[error(transparent)] + AcquireLockFailed(#[from] AcquireLockError), + + #[error("Incorrect component type")] + IncorrectType, +} + +#[derive(Debug, thiserror::Error)] +#[error("Failed to acquire component lock")] +pub struct AcquireLockError(#[source] LockError); + +pub trait IntoParts +{ + fn into_parts(self) -> Parts; +} + +/// The parts of a component. +#[derive(Debug)] +#[non_exhaustive] +pub struct Parts +{ + pub id: Uid, + pub name: &'static str, + pub is_sole: bool, + pub type_reflection: Option<&'static TypeReflection>, + pub data: Box<dyn Any>, + pub pair_relation_metadata: Option<PairMemberMetadata>, + pub pair_target_metadata: Option<PairMemberMetadata>, +} + +impl Parts +{ + /// Creates components parts from the component information stored in + /// `component_info`. + /// + /// # Panics + /// This function will panic if the type ID of the value in `data` does not equal + /// to the type ID stored in `component_info`. + #[must_use] + pub fn from_component_info(component_info: &Info, id: Uid, data: Box<dyn Any>) + -> Self + { + assert_eq!((*data).type_id(), component_info.ty_id); + + Self::builder() + .type_reflection(component_info.type_reflection) + .name(component_info.name) + .is_sole(component_info.is_sole) + .build_with_any_data(id, data) + } + + #[must_use] + pub fn builder() -> PartsBuilder + { + PartsBuilder::default() + } +} + +impl IntoParts for Parts +{ + fn into_parts(self) -> Parts + { + self + } +} + +#[derive(Debug)] +pub struct PartsBuilder +{ + name: &'static str, + is_sole: bool, + type_reflection: Option<&'static TypeReflection>, + pair_relation_metadata: Option<PairMemberMetadata>, + pair_target_metadata: Option<PairMemberMetadata>, +} + +impl PartsBuilder +{ + #[must_use] + pub fn name(mut self, name: &'static str) -> Self + { + self.name = name; + self + } + + #[must_use] + pub fn is_sole(mut self, is_sole: bool) -> Self + { + self.is_sole = is_sole; + self + } + + #[must_use] + pub fn type_reflection( + mut self, + type_reflection: Option<&'static TypeReflection>, + ) -> Self + { + self.type_reflection = type_reflection; + self + } + + #[must_use] + pub fn pair_relation_metadata(mut self, metadata: Option<PairMemberMetadata>) + -> Self + { + self.pair_relation_metadata = metadata; + self + } + + #[must_use] + pub fn pair_target_metadata(mut self, metadata: Option<PairMemberMetadata>) -> Self + { + self.pair_target_metadata = metadata; + self + } + + #[must_use] + pub fn build<Data: 'static>(self, id: Uid, data: Data) -> Parts + { + Parts { + id, + name: self.name, + is_sole: self.is_sole, + type_reflection: self.type_reflection, + data: Box::new(data), + pair_relation_metadata: self.pair_relation_metadata, + pair_target_metadata: self.pair_target_metadata, + } + } + + #[must_use] + pub fn build_with_any_data(self, id: Uid, data: Box<dyn Any>) -> Parts + { + Parts { + id, + name: self.name, + is_sole: self.is_sole, + type_reflection: self.type_reflection, + data, + pair_relation_metadata: self.pair_relation_metadata, + pair_target_metadata: self.pair_target_metadata, + } + } +} + +impl Default for PartsBuilder +{ + fn default() -> Self + { + Self { + name: "(unspecified)", + is_sole: false, + type_reflection: None, + pair_relation_metadata: None, + pair_target_metadata: None, + } + } +} + +#[derive(Debug, Clone, Component)] +#[non_exhaustive] +pub struct Info +{ + pub type_reflection: Option<&'static TypeReflection>, + pub ty_id: TypeId, + pub name: &'static str, + pub is_sole: bool, +} + +mod sealed +{ + pub trait Sealed {} +} |
