summaryrefslogtreecommitdiff
path: root/engine-ecs/src/component.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine-ecs/src/component.rs')
-rw-r--r--engine-ecs/src/component.rs416
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 {}
+}