summaryrefslogtreecommitdiff
path: root/ecs/src/relationship.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-08-01 16:11:15 +0200
committerHampusM <hampus@hampusmat.com>2024-08-02 15:34:54 +0200
commitfd42ca5a25f8bab3ea66252f8bc0db02604f70dd (patch)
tree6d384b5f462e2699342372f6b56791fc4607e9c6 /ecs/src/relationship.rs
parent70c7d745f918dd23343599963a619539f4f880cb (diff)
feat(ecs): add relationships
Diffstat (limited to 'ecs/src/relationship.rs')
-rw-r--r--ecs/src/relationship.rs190
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)
+ }
+}