diff options
author | HampusM <hampus@hampusmat.com> | 2024-08-01 16:11:15 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2024-08-02 15:34:54 +0200 |
commit | fd42ca5a25f8bab3ea66252f8bc0db02604f70dd (patch) | |
tree | 6d384b5f462e2699342372f6b56791fc4607e9c6 /ecs/src/relationship.rs | |
parent | 70c7d745f918dd23343599963a619539f4f880cb (diff) |
feat(ecs): add relationships
Diffstat (limited to 'ecs/src/relationship.rs')
-rw-r--r-- | ecs/src/relationship.rs | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/ecs/src/relationship.rs b/ecs/src/relationship.rs new file mode 100644 index 0000000..9004f0f --- /dev/null +++ b/ecs/src/relationship.rs @@ -0,0 +1,190 @@ +use std::any::{type_name, Any}; +use std::marker::PhantomData; +use std::sync::{Arc, Weak}; + +use crate::component::storage::Storage as ComponentStorage; +use crate::component::{ + is_optional as component_is_optional, + Component, + FromOptional as ComponentFromOptional, + Id as ComponentId, + Metadata as ComponentMetadata, +}; +use crate::entity::Uid as EntityUid; +use crate::lock::{Lock, ReadGuard}; +use crate::system::{ComponentRefMut, Input as SystemInput}; +use crate::type_name::TypeName; +use crate::{World, WorldData}; + +#[derive(Debug)] +pub struct Relationship<Kind, ComponentT: Component> +{ + entity_uid: EntityUid, + component_storage: Weak<Lock<ComponentStorage>>, + _pd: PhantomData<(Kind, ComponentT)>, +} + +impl<Kind, ComponentT> Relationship<Kind, ComponentT> +where + ComponentT: Component, +{ + pub fn new(world: &World, entity_uid: EntityUid) -> Self + { + Self { + entity_uid, + component_storage: Arc::downgrade(&world.data.component_storage), + _pd: PhantomData, + } + } +} + +impl<Kind, ComponentT> Component for Relationship<Kind, ComponentT> +where + Kind: 'static, + ComponentT: Component, +{ + type Component = Self; + type RefMut<'component> = Relation<'component, Kind, ComponentT>; + + fn id(&self) -> ComponentId + { + ComponentId::of::<Self>() + } + + fn as_any_mut(&mut self) -> &mut dyn Any + { + self + } + + fn as_any(&self) -> &dyn Any + { + self + } + + #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] + fn prepare(world_data: &WorldData) + where + Self: Sized, + { + let mut component_storage_lock = world_data + .component_storage + .write_nonblock() + .expect("Failed to acquire read-write component storage lock"); + + #[cfg(feature = "debug")] + tracing::debug!( + "Adding archetypes lookup entry for component: {}", + std::any::type_name::<ComponentT>() + ); + + component_storage_lock.add_archetype_lookup_entry([ComponentMetadata { + id: ComponentId::of::<ComponentT>(), + is_optional: component_is_optional::<ComponentT>().into(), + }]); + } +} + +impl<Kind, ComponentT> SystemInput for Relationship<Kind, ComponentT> +where + Kind: 'static, + ComponentT: Component, +{ +} + +impl<Kind, ComponentT: Component> TypeName for Relationship<Kind, ComponentT> +where + ComponentT: Component, +{ + fn type_name(&self) -> &'static str + { + type_name::<Self>() + } +} + +pub struct Relation<'rel_comp, Kind, ComponentT> +where + Kind: 'static, + ComponentT: Component, +{ + component_storage_lock: ReadGuard<'static, ComponentStorage>, + relationship_comp: ComponentRefMut<'rel_comp, Relationship<Kind, ComponentT>>, + _component_storage: Arc<Lock<ComponentStorage>>, +} + +impl<'rel_comp, Kind, ComponentT> ComponentFromOptional<'rel_comp> + for Relation<'rel_comp, Kind, ComponentT> +where + ComponentT: Component, +{ + fn from_optional_component( + optional_component: Option< + crate::lock::WriteGuard<'rel_comp, Box<dyn Component>>, + >, + ) -> Self + { + let relationship_comp = + ComponentRefMut::<Relationship<Kind, ComponentT>>::from_optional_component( + optional_component, + ); + + let component_storage = relationship_comp + .component_storage + .upgrade() + .expect("World has been dropped"); + + let component_storage_lock = component_storage + .read_nonblock() + .expect("Failed to aquire read-only component storage lock"); + + Self { + relationship_comp, + // SAFETY: The component lock is not used for longer than the original + // lifetime + component_storage_lock: unsafe { component_storage_lock.upgrade_lifetime() }, + _component_storage: component_storage, + } + } +} + +impl<'rel_comp, Kind, ComponentT> Relation<'rel_comp, Kind, ComponentT> +where + ComponentT: Component, +{ + /// Retrieves the related-to component. + pub fn get(&self) -> Option<ComponentRefMut<'_, ComponentT>> + { + let mut archetype_iter = + self.component_storage_lock + .find_entities([ComponentMetadata { + id: ComponentId::of::<ComponentT>(), + is_optional: component_is_optional::<ComponentT>().into(), + }]); + + let (entity, archetype) = archetype_iter.find_map(|archetype| { + let Some(entity) = archetype.get_entity(self.relationship_comp.entity_uid) + else { + return None; + }; + + Some((entity, archetype)) + })?; + + let component_index = + archetype.get_index_for_component(&ComponentId::of::<ComponentT>())?; + + let component = ComponentRefMut::new( + entity + .get(component_index)? + .component + .write_nonblock() + .unwrap_or_else(|_| { + panic!( + "Failed to aquire read-write lock of component {}", + type_name::<ComponentT>() + ) + }), + ); + + Some(component) + } +} |