summaryrefslogtreecommitdiff
path: root/engine-ecs/src/component.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-05-21 17:55:20 +0200
committerHampusM <hampus@hampusmat.com>2026-05-21 17:55:20 +0200
commit8022e8998290b067b8aa0cb9cba8ba410826bdab (patch)
tree7171e79ce530e03079046ee8fd12167160c45480 /engine-ecs/src/component.rs
parent412cee02c252f91bcf0b70a3f5cc5fca6d2b4c62 (diff)
chore: rename ecs* crates to engine-ecs*HEADmaster
Diffstat (limited to 'engine-ecs/src/component.rs')
-rw-r--r--engine-ecs/src/component.rs324
1 files changed, 324 insertions, 0 deletions
diff --git a/engine-ecs/src/component.rs b/engine-ecs/src/component.rs
new file mode 100644
index 0000000..17b279b
--- /dev/null
+++ b/engine-ecs/src/component.rs
@@ -0,0 +1,324 @@
+use std::any::{type_name, Any};
+use std::fmt::Debug;
+use std::ops::{Deref, DerefMut};
+
+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::Pair;
+use crate::system::Input as SystemInput;
+use crate::uid::Uid;
+use crate::util::Array;
+use crate::{EntityComponentRef, World};
+
+pub mod local;
+
+pub(crate) mod storage;
+
+pub trait Component: SystemInput + Any
+{
+ /// Returns the ID of this component.
+ fn id() -> Uid
+ where
+ Self: Sized;
+
+ /// Returns the name of this component.
+ fn name(&self) -> &'static str;
+}
+
+impl dyn Component
+{
+ pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real>
+ {
+ (self as &mut dyn Any).downcast_mut()
+ }
+
+ pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real>
+ {
+ (self as &dyn Any).downcast_ref()
+ }
+
+ pub fn is<Other: 'static>(&self) -> bool
+ {
+ (self as &dyn Any).is::<Other>()
+ }
+}
+
+impl Debug for dyn Component
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter.debug_struct("Component").finish_non_exhaustive()
+ }
+}
+
+/// A sequence of components.
+pub trait Sequence
+{
+ /// The number of components in this component sequence.
+ const COUNT: usize;
+
+ type PartsArray: Array<Parts>;
+
+ fn into_parts_array(self) -> Self::PartsArray;
+}
+
+#[derive(Debug)]
+pub struct Handle<'a, DataT: '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<DataT: 'static> Deref for Handle<'_, DataT>
+{
+ type Target = DataT;
+
+ fn deref(&self) -> &Self::Target
+ {
+ &self.inner
+ }
+}
+
+#[derive(Debug)]
+pub struct HandleMut<'a, DataT: '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(),
+ })
+ }
+
+ 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: 'static> Deref for HandleMut<'_, DataT>
+{
+ type Target = DataT;
+
+ fn deref(&self) -> &Self::Target
+ {
+ &self.inner
+ }
+}
+
+impl<DataT: '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);
+
+macro_rules! inner {
+ ($c: tt) => {
+ seq!(I in 0..=$c {
+ impl<#(IntoCompParts~I: IntoParts,)*> Sequence for (#(IntoCompParts~I,)*)
+ {
+ const COUNT: usize = $c + 1;
+
+ type PartsArray = [Parts; $c + 1];
+
+ fn into_parts_array(self) -> Self::PartsArray
+ {
+ [#({
+ self.I.into_parts()
+ },)*]
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 0..=16 {
+ inner!(C);
+});
+
+impl Sequence for ()
+{
+ type PartsArray = [Parts; 0];
+
+ const COUNT: usize = 0;
+
+ fn into_parts_array(self) -> Self::PartsArray
+ {
+ []
+ }
+}
+
+pub trait IntoParts
+{
+ fn into_parts(self) -> Parts;
+}
+
+impl<ComponentT> IntoParts for ComponentT
+where
+ ComponentT: Component,
+{
+ fn into_parts(self) -> Parts
+ {
+ Parts::builder()
+ .name(type_name::<Self>())
+ .build(Self::id(), self)
+ }
+}
+
+/// The parts of a component.
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct Parts
+{
+ id: Uid,
+ name: &'static str,
+ data: Box<dyn Any>,
+}
+
+impl Parts
+{
+ #[must_use]
+ pub fn id(&self) -> Uid
+ {
+ self.id
+ }
+
+ #[must_use]
+ pub fn name(&self) -> &'static str
+ {
+ self.name
+ }
+
+ #[must_use]
+ pub fn builder() -> PartsBuilder
+ {
+ PartsBuilder::default()
+ }
+
+ pub(crate) fn into_data(self) -> Box<dyn Any>
+ {
+ self.data
+ }
+}
+
+#[derive(Debug)]
+pub struct PartsBuilder
+{
+ name: &'static str,
+}
+
+impl PartsBuilder
+{
+ #[must_use]
+ pub fn name(mut self, name: &'static str) -> Self
+ {
+ self.name = name;
+ self
+ }
+
+ #[must_use]
+ pub fn build<Data: 'static>(self, id: Uid, data: Data) -> Parts
+ {
+ Parts {
+ id,
+ name: self.name,
+ data: Box::new(data),
+ }
+ }
+
+ #[must_use]
+ pub fn build_with_any_data(self, id: Uid, data: Box<dyn Any>) -> Parts
+ {
+ Parts { id, name: self.name, data }
+ }
+}
+
+impl Default for PartsBuilder
+{
+ fn default() -> Self
+ {
+ Self { name: "(unspecified)" }
+ }
+}