diff options
| author | HampusM <hampus@hampusmat.com> | 2025-08-26 16:43:40 +0200 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2025-09-11 18:24:48 +0200 | 
| commit | 09981e0173a2427264e432226804292c91e1f920 (patch) | |
| tree | 70112b6cda98a55da625ec9f6762927f00affe91 | |
| parent | ce1bade2c21cc3129fa8bc2b4bc67bc4dc2c25c3 (diff) | |
feat(ecs): add component changed event
| -rw-r--r-- | ecs/examples/component_changed_event.rs | 80 | ||||
| -rw-r--r-- | ecs/src/component.rs | 59 | ||||
| -rw-r--r-- | ecs/src/entity.rs | 61 | ||||
| -rw-r--r-- | ecs/src/event.rs | 100 | ||||
| -rw-r--r-- | ecs/src/event/component.rs | 7 | ||||
| -rw-r--r-- | ecs/src/lib.rs | 158 | ||||
| -rw-r--r-- | ecs/src/pair.rs | 19 | ||||
| -rw-r--r-- | ecs/src/query.rs | 4 | ||||
| -rw-r--r-- | ecs/src/query/flexible.rs | 4 | ||||
| -rw-r--r-- | ecs/src/query/term.rs | 3 | ||||
| -rw-r--r-- | ecs/src/system.rs | 3 | ||||
| -rw-r--r-- | ecs/src/system/observer.rs | 276 | ||||
| -rw-r--r-- | ecs/src/system/stateful.rs | 127 | 
13 files changed, 807 insertions, 94 deletions
| diff --git a/ecs/examples/component_changed_event.rs b/ecs/examples/component_changed_event.rs new file mode 100644 index 0000000..643f338 --- /dev/null +++ b/ecs/examples/component_changed_event.rs @@ -0,0 +1,80 @@ +use ecs::event::component::OnChanged; +use ecs::pair::Pair; +use ecs::phase::UPDATE as UPDATE_PHASE; +use ecs::system::observer::Observe; +use ecs::{Component, Query, World}; + +#[derive(Component)] +struct SomeData +{ +    num: u64, +} + +#[derive(Component)] +struct Greeting +{ +    greeting: String, +} + +fn say_hello(query: Query<(&SomeData, &mut Greeting)>) +{ +    for (data, mut greeting) in &query { +        println!("{}: {}", greeting.greeting, data.num); + +        if greeting.greeting == "Good evening" { +            greeting.greeting = "Good morning".to_string(); +            greeting.set_changed(); +        } +    } +} + +fn print_changed_greetings(observe: Observe<'_, Pair<OnChanged, Greeting>>) +{ +    println!("\nChanged greetings:"); + +    for ent in &observe { +        let Some(greeting) = ent.get::<Greeting>() else { +            unreachable!(); +        }; + +        println!("A greeting changed to {}", greeting.greeting); +    } + +    println!(""); +} + +fn main() +{ +    let mut world = World::new(); + +    world.register_system(*UPDATE_PHASE, say_hello); + +    world.register_observer(print_changed_greetings); + +    world.create_entity(( +        SomeData { num: 987_654 }, +        Greeting { +            greeting: "Good afternoon".to_string(), +        }, +    )); + +    world.create_entity(( +        SomeData { num: 345 }, +        Greeting { greeting: "Good evening".to_string() }, +    )); + +    world.step(); + +    world.step(); + +    for (mut greeting,) in &world.query::<(&mut Greeting,), ()>() { +        if greeting.greeting == "Good afternoon" { +            greeting.greeting = "Yo yo".to_string(); +            greeting.set_changed(); +        } +    } + +    world.step(); + +    world.step(); +} diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 8f946f0..9e3975f 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -4,6 +4,8 @@ use std::ops::{Deref, DerefMut};  use seq_macro::seq; +use crate::event::component::OnChanged; +use crate::event::Submitter as EventSubmitter;  use crate::lock::{      Error as LockError,      MappedReadGuard, @@ -11,10 +13,11 @@ use crate::lock::{      ReadGuard,      WriteGuard,  }; +use crate::pair::Pair;  use crate::system::Input as SystemInput;  use crate::uid::Uid;  use crate::util::Array; -use crate::EntityComponentRef; +use crate::{EntityComponentRef, World};  pub mod local; @@ -69,12 +72,12 @@ pub trait Sequence  }  #[derive(Debug)] -pub struct Handle<'a, ComponentData: 'static> +pub struct Handle<'a, DataT: 'static>  { -    inner: MappedReadGuard<'a, ComponentData>, +    inner: MappedReadGuard<'a, DataT>,  } -impl<'comp, ComponentData: 'static> Handle<'comp, ComponentData> +impl<'comp, DataT: 'static> Handle<'comp, DataT>  {      /// Creates a new handle instance from a [`EntityComponentRef`].      /// @@ -96,16 +99,16 @@ impl<'comp, ComponentData: 'static> Handle<'comp, ComponentData>      {          Ok(Self {              inner: ReadGuard::try_map(inner, |component| { -                component.downcast_ref::<ComponentData>() +                component.downcast_ref::<DataT>()              })              .map_err(|_| HandleError::IncorrectType)?,          })      }  } -impl<ComponentData: 'static> Deref for Handle<'_, ComponentData> +impl<DataT: 'static> Deref for Handle<'_, DataT>  { -    type Target = ComponentData; +    type Target = DataT;      fn deref(&self) -> &Self::Target      { @@ -114,12 +117,14 @@ impl<ComponentData: 'static> Deref for Handle<'_, ComponentData>  }  #[derive(Debug)] -pub struct HandleMut<'a, ComponentData: 'static> +pub struct HandleMut<'a, DataT: 'static>  { -    inner: MappedWriteGuard<'a, ComponentData>, +    entity_component_ref: EntityComponentRef<'a>, +    inner: MappedWriteGuard<'a, DataT>, +    event_submitter: EventSubmitter<'a>,  } -impl<'comp, ComponentData: 'static> HandleMut<'comp, ComponentData> +impl<'comp, DataT: 'static> HandleMut<'comp, DataT>  {      /// Creates a new handle instance from a [`EntityComponentRef`].      /// @@ -127,32 +132,36 @@ impl<'comp, ComponentData: 'static> HandleMut<'comp, ComponentData>      /// 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>      { -        Self::new( -            entity_component_ref -                .component() -                .write_nonblock() -                .map_err(AcquireLockError)?, -        ) -    } +        let inner = entity_component_ref +            .component() +            .write_nonblock() +            .map_err(AcquireLockError)?; -    // TODO: Make this function private -    pub(crate) fn new(inner: WriteGuard<'comp, Box<dyn Any>>) -        -> Result<Self, HandleError> -    {          Ok(Self { +            entity_component_ref: entity_component_ref.clone(),              inner: WriteGuard::try_map(inner, |component| { -                component.downcast_mut::<ComponentData>() +                component.downcast_mut::<DataT>()              })              .map_err(|_| HandleError::IncorrectType)?, +            event_submitter: world.event_submitter(),          })      } + +    pub fn set_changed(&self) +    { +        self.event_submitter.submit_event( +            &Pair::new::<OnChanged>(self.entity_component_ref.id()), +            self.entity_component_ref.entity_id(), +        ); +    }  } -impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData> +impl<DataT: 'static> Deref for HandleMut<'_, DataT>  { -    type Target = ComponentData; +    type Target = DataT;      fn deref(&self) -> &Self::Target      { @@ -160,7 +169,7 @@ impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData>      }  } -impl<ComponentData: 'static> DerefMut for HandleMut<'_, ComponentData> +impl<DataT: 'static> DerefMut for HandleMut<'_, DataT>  {      fn deref_mut(&mut self) -> &mut Self::Target      { diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs index 34fd19f..ca867f9 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -21,6 +21,7 @@ pub struct Handle<'a>  {      archetype: &'a Archetype,      entity: &'a ArchetypeEntity, +    world: &'a World,  }  impl<'a> Handle<'a> @@ -41,7 +42,7 @@ impl<'a> Handle<'a>      /// - The component's ID is not a component ID      /// - The component is mutably borrowed elsewhere      #[must_use] -    pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'_, ComponentT>> +    pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'a, ComponentT>>      {          assert_eq!(ComponentT::id().kind(), UidKind::Component); @@ -76,12 +77,44 @@ impl<'a> Handle<'a>          let component = self.get_matching_components(ComponentT::id()).next()?;          Some( -            ComponentHandleMut::from_entity_component_ref(&component).unwrap_or_else( -                |err| { +            ComponentHandleMut::from_entity_component_ref(&component, self.world) +                .unwrap_or_else(|err| {                      panic!(                          "Creating handle to component {} failed: {err}",                          type_name::<ComponentT>()                      ); +                }), +        ) +    } + +    /// Returns a reference to the component with the ID `id` in this entity. +    /// `None` is returned if the component isn't found. +    /// +    /// # Panics +    /// Will panic if: +    /// - The ID is not a component/pair ID +    /// - The component is borrowed mutably elsewhere +    /// - The component type is incorrect +    #[must_use] +    pub fn get_with_id<ComponentDataT: 'static>( +        &self, +        id: Uid, +    ) -> Option<ComponentHandle<'a, ComponentDataT>> +    { +        assert!( +            matches!(id.kind(), UidKind::Component | UidKind::Pair), +            "ID {id:?} is not a component/pair ID" +        ); + +        let component = self.get_matching_components(id).next()?; + +        Some( +            ComponentHandle::from_entity_component_ref(&component).unwrap_or_else( +                |err| { +                    panic!( +                        "Creating handle to component {} failed: {err}", +                        type_name::<ComponentDataT>() +                    );                  },              ),          ) @@ -96,10 +129,10 @@ impl<'a> Handle<'a>      /// - The component is borrowed elsewhere      /// - The component type is incorrect      #[must_use] -    pub fn get_with_id_mut<ComponentData: 'static>( +    pub fn get_with_id_mut<ComponentDataT: 'static>(          &self,          id: Uid, -    ) -> Option<ComponentHandleMut<'a, ComponentData>> +    ) -> Option<ComponentHandleMut<'a, ComponentDataT>>      {          assert!(              matches!(id.kind(), UidKind::Component | UidKind::Pair), @@ -109,14 +142,13 @@ impl<'a> Handle<'a>          let component = self.get_matching_components(id).next()?;          Some( -            ComponentHandleMut::from_entity_component_ref(&component).unwrap_or_else( -                |err| { +            ComponentHandleMut::from_entity_component_ref(&component, self.world) +                .unwrap_or_else(|err| {                      panic!(                          "Creating handle to component {} failed: {err}", -                        type_name::<ComponentData>() +                        type_name::<ComponentDataT>()                      ); -                }, -            ), +                }),          )      } @@ -131,9 +163,13 @@ impl<'a> Handle<'a>          }      } -    pub(crate) fn new(archetype: &'a Archetype, entity: &'a ArchetypeEntity) -> Self +    pub(crate) fn new( +        archetype: &'a Archetype, +        entity: &'a ArchetypeEntity, +        world: &'a World, +    ) -> Self      { -        Self { archetype, entity } +        Self { archetype, entity, world }      }  } @@ -155,6 +191,7 @@ impl<'a> Iterator for MatchingComponentIter<'a>          Some(EntityComponentRef::new(              matching_component_id,              self.entity.components().get(index).unwrap(), +            self.entity.uid(),          ))      }  } diff --git a/ecs/src/event.rs b/ecs/src/event.rs index 9cea807..2934b82 100644 --- a/ecs/src/event.rs +++ b/ecs/src/event.rs @@ -1 +1,101 @@ +use crate::lock::Lock; +use crate::pair::Pair; +use crate::uid::{Kind as UidKind, Uid}; +use crate::util::VecExt; +use crate::World; +  pub mod component; + +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct Emitted<'a> +{ +    pub event: Uid, +    pub match_ids: &'a [Uid], +} + +#[derive(Debug)] +pub struct Submitter<'world> +{ +    new_events: &'world Lock<NewEvents>, +} + +impl<'world> Submitter<'world> +{ +    /// Submits a event to be handled later. +    /// +    /// # Panics +    /// Will panic if unable to acquire a read-write lock to the event store. +    pub fn submit_event(&self, event: &Pair<Uid, Uid>, match_id: Uid) +    { +        let mut new_events_lock = self +            .new_events +            .write_nonblock() +            .expect("Failed to acquire read-write lock to new events"); + +        new_events_lock.push_event_match(event, match_id); +    } + +    pub(crate) fn new(world: &'world World) -> Self +    { +        Self { new_events: &world.data.new_events } +    } +} + +#[derive(Debug, Default)] +pub(crate) struct NewEvents +{ +    events: Vec<(Uid, Matches)>, +} + +impl NewEvents +{ +    pub fn push_event_match(&mut self, event: &Pair<Uid, Uid>, match_id: Uid) +    { +        let event_id = event.id(); + +        assert_eq!(event_id.kind(), UidKind::Pair); + +        if let Ok(event_index) = self +            .events +            .binary_search_by_key(&event_id, |(other_event_id, _)| *other_event_id) +        { +            let Some((_, matches)) = self.events.get_mut(event_index) else { +                unreachable!(); +            }; + +            matches.sorted_push(match_id); + +            return; +        } + +        self.events.insert_at_partition_point_by_key( +            (event_id, Matches { match_ids: Vec::from([match_id]) }), +            |(other_event_id, _)| *other_event_id, +        ); +    } + +    pub fn take(&mut self) -> Vec<(Uid, Matches)> +    { +        std::mem::take(&mut self.events) +    } +} + +#[derive(Debug)] +pub(crate) struct Matches +{ +    pub match_ids: Vec<Uid>, +} + +impl Matches +{ +    fn sorted_push(&mut self, match_id: Uid) +    { +        if self.match_ids.binary_search(&match_id).is_ok() { +            return; +        } + +        self.match_ids +            .insert_at_partition_point_by_key(match_id, |other_match_id| *other_match_id); +    } +} diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs index b96f23b..1bc39b1 100644 --- a/ecs/src/event/component.rs +++ b/ecs/src/event/component.rs @@ -1,5 +1,9 @@  //! Component events. +use std::convert::Infallible; + +use crate::Component; +  // TODO: Implement  // /// Pair relation for events emitted when:  // /// a) A entity with the target component is spawned. @@ -13,3 +17,6 @@  // /// b) A entity with the target component is despawned.  // #[derive(Debug, Component)]  // pub struct Removed(Infallible); + +#[derive(Debug, Component)] +pub struct OnChanged(Infallible); diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index ab30980..6f49a91 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -14,10 +14,12 @@ use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComp  use crate::component::storage::Storage as ComponentStorage;  use crate::component::{      Component, +    IntoParts as IntoComponentParts,      Parts as ComponentParts,      Sequence as ComponentSequence,  };  use crate::entity::{Declaration as EntityDeclaration, Handle as EntityHandle}; +use crate::event::{Emitted as EmittedEvent, NewEvents, Submitter as EventSubmitter};  use crate::extension::{Collector as ExtensionCollector, Extension};  use crate::lock::Lock;  use crate::pair::{ChildOf, DependsOn, Pair, Wildcard}; @@ -33,6 +35,7 @@ use crate::query::{  };  use crate::sole::Sole;  use crate::stats::Stats; +use crate::system::observer::{Observer, WrapperComponent as ObserverWrapperComponent};  use crate::system::{Callbacks, Metadata as SystemMetadata, System, SystemComponent};  use crate::uid::{Kind as UidKind, Uid}; @@ -103,18 +106,7 @@ impl World      where          Comps: ComponentSequence,      { -        debug_assert_eq!(entity_uid.kind(), UidKind::Entity); - -        if let Err(err) = self.data.component_storage.create_entity(entity_uid) { -            tracing::warn!("Failed to create entity: {err}"); -            return; -        } - -        Self::add_entity_components( -            entity_uid, -            components.into_parts_array(), -            &mut self.data.component_storage, -        ); +        self.create_ent(entity_uid, components.into_parts_array());      }      pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts) @@ -142,32 +134,40 @@ impl World          self.data.sole_storage.insert(sole)      } -    pub fn register_system<'this, SystemImpl>( +    pub fn register_observer<'this, SystemImpl, ObserverT>(          &'this mut self, -        phase_euid: Uid, -        system: impl System<'this, SystemImpl>, -    ) +        observer: ObserverT, +    ) where +        ObserverT: Observer<'this, SystemImpl>,      { -        let (type_erased_system, mut system_callbacks) = system.finish(); +        let (wrapper_comp, mut system_callbacks) = observer.finish_observer(); -        let system_ent_id = self.create_entity(( -            SystemComponent { system: type_erased_system }, -            Pair::new::<DependsOn>(phase_euid), -        )); +        let ent_id = Uid::new_unique(UidKind::Entity); -        system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id }); +        self.create_ent( +            ent_id, +            [wrapper_comp.into_parts()].into_iter().chain( +                ObserverT::observed_events() +                    .into_iter() +                    .map(IntoComponentParts::into_parts), +            ), +        ); + +        system_callbacks.on_created(self, SystemMetadata { ent_id });      } -    pub fn register_observer_system<'this, SystemImpl>( +    pub fn register_system<'this, SystemImpl>(          &'this mut self, +        phase_euid: Uid,          system: impl System<'this, SystemImpl>, -        event: Pair<Uid, Uid>,      )      {          let (type_erased_system, mut system_callbacks) = system.finish(); -        let system_ent_id = -            self.create_entity((SystemComponent { system: type_erased_system }, event)); +        let system_ent_id = self.create_entity(( +            SystemComponent { system: type_erased_system }, +            Pair::new::<DependsOn>(phase_euid), +        ));          system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id });      } @@ -209,7 +209,12 @@ impl World              unreachable!("Should exist since archetype was found by entity id");          }; -        Some(EntityHandle::new(archetype, entity)) +        Some(EntityHandle::new(archetype, entity, self)) +    } + +    pub fn event_submitter(&self) -> EventSubmitter<'_> +    { +        EventSubmitter::new(self)      }      /// Performs a single tick. @@ -231,6 +236,8 @@ impl World          self.perform_phases(); +        self.emit_new_events(); +          self.data.component_storage.create_imaginary_archetypes();          self.perform_queued_actions(); @@ -309,6 +316,27 @@ impl World          )      } +    #[tracing::instrument(skip_all)] +    fn create_ent( +        &mut self, +        entity_uid: Uid, +        components: impl IntoIterator<Item = ComponentParts>, +    ) +    { +        debug_assert_eq!(entity_uid.kind(), UidKind::Entity); + +        if let Err(err) = self.data.component_storage.create_entity(entity_uid) { +            tracing::warn!("Failed to create entity: {err}"); +            return; +        } + +        Self::add_entity_components( +            entity_uid, +            components, +            &mut self.data.component_storage, +        ); +    } +      fn query_and_run_systems(&self, phase_euid: Uid)      {          let system_query = Query::<(&SystemComponent,)>::from_flexible_query( @@ -363,6 +391,26 @@ impl World          }      } +    fn emit_new_events(&self) +    { +        let new_events = self +            .data +            .new_events +            .write_nonblock() +            .expect("Failed to acquire read-write lock to new events") +            .take(); + +        for (event_id, event_matches) in new_events { +            self.emit_event_observers( +                event_id, +                &EmittedEvent { +                    event: event_id, +                    match_ids: &event_matches.match_ids, +                }, +            ); +        } +    } +      #[tracing::instrument(skip_all)]      fn perform_queued_actions(&mut self)      { @@ -473,6 +521,29 @@ impl World          removed_component_ids      } + +    fn emit_event_observers(&self, event_id: Uid, emitted_event: &EmittedEvent<'_>) +    { +        assert_eq!(event_id.kind(), UidKind::Pair); + +        let query = Query::<(&ObserverWrapperComponent,)>::from_flexible_query( +            self.flexible_query( +                QueryTerms::<QUERY_MAX_TERM_CNT>::builder() +                    .with_required([ObserverWrapperComponent::id(), event_id]) +                    .build(), +            ), +        ); + +        for (observer_ent_id, (observer,)) in query.iter_with_euids() { +            unsafe { +                observer.run( +                    self, +                    SystemMetadata { ent_id: observer_ent_id }, +                    emitted_event.clone(), +                ); +            } +        } +    }  }  impl Default for World @@ -493,31 +564,21 @@ pub enum StepResult      Stop,  } -#[derive(Debug)] +#[derive(Debug, Default)]  pub struct WorldData  {      component_storage: ComponentStorage,      sole_storage: SoleStorage,      action_queue: Rc<ActionQueue>, +    new_events: Lock<NewEvents>,  } -impl Default for WorldData -{ -    fn default() -> Self -    { -        Self { -            component_storage: ComponentStorage::default(), -            sole_storage: SoleStorage::default(), -            action_queue: Rc::new(ActionQueue::default()), -        } -    } -} - -#[derive(Debug)] +#[derive(Debug, Clone)]  pub struct EntityComponentRef<'a>  {      component_id: Uid,      component: &'a ArchetypeEntityComponent, +    entity_id: Uid,  }  impl<'a> EntityComponentRef<'a> @@ -533,9 +594,20 @@ impl<'a> EntityComponentRef<'a>          self.component_id      } -    fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent) -> Self +    #[must_use] +    pub fn entity_id(&self) -> Uid      { -        Self { component_id, component: comp } +        self.entity_id +    } + +    fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent, entity_id: Uid) +        -> Self +    { +        Self { +            component_id, +            component: comp, +            entity_id, +        }      }  } diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs index 7b5f54a..553652e 100644 --- a/ecs/src/pair.rs +++ b/ecs/src/pair.rs @@ -133,7 +133,7 @@ where      fn get_field<'world>(          entity_handle: &EntityHandle<'world>, -        _world: &'world World, +        world: &'world World,      ) -> Self::Field<'world>      {          let target_component = entity_handle @@ -141,12 +141,14 @@ where              .next()              .expect("Not possible"); -        Self::Field::from_entity_component_ref(&target_component).unwrap_or_else(|err| { -            panic!( -                "Creating handle to target component {} failed: {err}", -                type_name::<Target>() -            ); -        }) +        Self::Field::from_entity_component_ref(&target_component, world).unwrap_or_else( +            |err| { +                panic!( +                    "Creating handle to target component {} failed: {err}", +                    type_name::<Target>() +                ); +            }, +        )      }  } @@ -271,7 +273,7 @@ impl WildcardTargetHandle<'_>              unreachable!();          }; -        Some(EntityHandle::new(archetype, archetype_entity)) +        Some(EntityHandle::new(archetype, archetype_entity, self.world))      }      /// Attempts to retrieve the target as a component, returning `None` if the component @@ -317,6 +319,7 @@ impl WildcardTargetHandle<'_>      {          ComponentHandleMut::<ComponentData>::from_entity_component_ref(              &self.component_ref, +            self.world,          )          .map_or_else(              |err| match err { diff --git a/ecs/src/query.rs b/ecs/src/query.rs index dc9b036..5f13579 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -391,7 +391,7 @@ impl<ComponentT: Component> TermWithField for &mut ComponentT      fn get_field<'world>(          entity_handle: &EntityHandle<'world>, -        _world: &'world World, +        world: &'world World,      ) -> Self::Field<'world>      {          assert_eq!(ComponentT::id().kind(), UidKind::Component); @@ -410,7 +410,7 @@ impl<ComponentT: Component> TermWithField for &mut ComponentT              );          }; -        Self::Field::from_entity_component_ref(&component).unwrap_or_else(|err| { +        Self::Field::from_entity_component_ref(&component, world).unwrap_or_else(|err| {              panic!(                  "Creating handle to component {} failed: {err}",                  type_name::<ComponentT>() diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs index 0caaa27..936ab82 100644 --- a/ecs/src/query/flexible.rs +++ b/ecs/src/query/flexible.rs @@ -36,6 +36,7 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT>                              .zip(archetype.entities())                      }) as ComponentIterMapFn,                  ), +            world: self.world,          }      } @@ -65,6 +66,7 @@ impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_T  pub struct Iter<'query>  {      iter: QueryEntityIter<'query>, +    world: &'query World,  }  impl<'query> Iterator for Iter<'query> @@ -75,7 +77,7 @@ impl<'query> Iterator for Iter<'query>      {          let (archetype, entity) = self.iter.next()?; -        Some(EntityHandle::new(archetype, entity)) +        Some(EntityHandle::new(archetype, entity, self.world))      }  } diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs index c8a96b6..0683918 100644 --- a/ecs/src/query/term.rs +++ b/ecs/src/query/term.rs @@ -95,7 +95,7 @@ impl<ComponentT: Component> TermWithField for Option<&mut ComponentT>      fn get_field<'world>(          entity_handle: &crate::entity::Handle<'world>, -        _world: &'world crate::World, +        world: &'world crate::World,      ) -> Self::Field<'world>      {          Some( @@ -103,6 +103,7 @@ impl<ComponentT: Component> TermWithField for Option<&mut ComponentT>                  &entity_handle                      .get_matching_components(ComponentT::id())                      .next()?, +                world,              )              .unwrap_or_else(|err| {                  panic!( diff --git a/ecs/src/system.rs b/ecs/src/system.rs index ae6dd2e..95ab7a8 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -7,6 +7,7 @@ use crate::uid::Uid;  use crate::World;  pub mod initializable; +pub mod observer;  pub mod stateful;  /// Metadata of a system. @@ -60,7 +61,7 @@ seq!(C in 1..16 {      impl_system!(C);  }); -pub trait Into<Impl> +pub trait Into<'world, Impl>  {      type System; diff --git a/ecs/src/system/observer.rs b/ecs/src/system/observer.rs new file mode 100644 index 0000000..5455fd4 --- /dev/null +++ b/ecs/src/system/observer.rs @@ -0,0 +1,276 @@ +use std::fmt::Debug; +use std::marker::PhantomData; +use std::mem::transmute; +use std::slice::Iter as SliceIter; + +use ecs_macros::Component; +use seq_macro::seq; + +use crate::component::Component; +use crate::entity::Handle as EntityHandle; +use crate::event::Emitted as EmittedEvent; +use crate::pair::Pair; +use crate::system::{ +    Metadata, +    NoCallbacks, +    Param, +    System, +    TypeErased as TypeErasedSystem, +}; +use crate::uid::Uid; +use crate::util::Array; +use crate::World; + +pub trait Observed +{ +    type Events: Array<Pair<Uid, Uid>>; + +    fn events() -> Self::Events; +} + +impl<Relation, Target> Observed for Pair<Relation, Target> +where +    Relation: Component, +    Target: Component, +{ +    type Events = [Pair<Uid, Uid>; 1]; + +    fn events() -> Self::Events +    { +        [Pair::new::<Relation>(Target::id())] +    } +} + +/// Observer system. +pub trait Observer<'world, Impl>: System<'world, Impl> +{ +    type ObservedEvents: Array<Pair<Uid, Uid>>; + +    fn observed_events() -> Self::ObservedEvents; + +    fn finish_observer(self) -> (WrapperComponent, Self::Callbacks); +} + +pub struct Observe<'world, ObservedT: Observed> +{ +    _pd: PhantomData<ObservedT>, +    world: &'world World, +    emitted_event: EmittedEvent<'world>, +} + +impl<'world, ObservedT: Observed> Observe<'world, ObservedT> +{ +    pub fn new(world: &'world World, emitted_event: EmittedEvent<'world>) -> Self +    { +        Self { +            _pd: PhantomData, +            world, +            emitted_event, +        } +    } + +    #[must_use] +    pub fn event(&self) -> Uid +    { +        self.emitted_event.event +    } +} + +impl<ObservedT: Observed> Observe<'_, ObservedT> +{ +    #[must_use] +    pub fn iter(&self) -> ObserveIter<'_> +    { +        ObserveIter { +            world: self.world, +            inner: self.emitted_event.match_ids.iter(), +        } +    } +} + +impl<'a, ObservedT: Observed> IntoIterator for &'a Observe<'_, ObservedT> +{ +    type IntoIter = ObserveIter<'a>; +    type Item = EntityHandle<'a>; + +    fn into_iter(self) -> Self::IntoIter +    { +        self.iter() +    } +} + +pub struct ObserveIter<'observe> +{ +    world: &'observe World, +    inner: SliceIter<'observe, Uid>, +} + +impl<'observe> Iterator for ObserveIter<'observe> +{ +    type Item = EntityHandle<'observe>; + +    fn next(&mut self) -> Option<Self::Item> +    { +        let target = *self.inner.next()?; + +        self.world.get_entity(target) +    } +} + +macro_rules! impl_observer { +    ($c: tt) => { +        seq!(I in 0..$c { +            impl<'world, ObservedT, Func, #(TParam~I,)*> System< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Func +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world, Input = ()>,)* +            { +                type Callbacks = NoCallbacks; + +                fn finish(self) -> (TypeErasedSystem, NoCallbacks) +                { +                    unimplemented!(); +                } +            } + +            impl<'world, ObservedT, Func, #(TParam~I,)*> Observer< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Func +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world, Input = ()>,)* +            { +                type ObservedEvents = ObservedT::Events; + +                fn observed_events() -> Self::ObservedEvents +                { +                    ObservedT::events() +                } + +                fn finish_observer(self) -> (WrapperComponent, NoCallbacks) +                { +                    let wrapper_comp = WrapperComponent { +                        run: Box::new(move |world, metadata, emitted_event| { +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let world = unsafe { &*std::ptr::from_ref(world) }; + +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let emitted_event = unsafe { +                                transmute::<EmittedEvent<'_>, EmittedEvent<'_>>( +                                    emitted_event +                                ) +                            }; + +                            self(Observe::new(world, emitted_event), #({ +                                TParam~I::new(world, &metadata) +                            },)*); +                        }), +                    }; + +                    (wrapper_comp, NoCallbacks) +                } +            } +        }); +    }; +} + +seq!(C in 1..16 { +    impl_observer!(C); +}); + +impl<'world, ObservedT, Func> System<'world, fn(Observe<'world, ObservedT>)> for Func +where +    ObservedT: Observed, +    Func: Fn(Observe<'world, ObservedT>) + Copy + 'static, +{ +    type Callbacks = NoCallbacks; + +    fn finish(self) -> (TypeErasedSystem, NoCallbacks) +    { +        const { +            panic!("Observers cannot be used as regular systems"); +        } +    } +} + +impl<'world, ObservedT, Func> Observer<'world, fn(Observe<'world, ObservedT>)> for Func +where +    ObservedT: Observed, +    Func: Fn(Observe<'world, ObservedT>) + Copy + 'static, +{ +    type ObservedEvents = ObservedT::Events; + +    fn observed_events() -> Self::ObservedEvents +    { +        ObservedT::events() +    } + +    fn finish_observer(self) -> (WrapperComponent, NoCallbacks) +    { +        let wrapper_comp = WrapperComponent { +            run: Box::new(move |world, _metadata, emitted_event| { +                // SAFETY: The caller of TypeErased::run ensures the lifetime +                // is correct +                let world = unsafe { &*std::ptr::from_ref(world) }; + +                // SAFETY: The caller of TypeErased::run ensures the lifetime +                // is correct +                let emitted_event = unsafe { +                    transmute::<EmittedEvent<'_>, EmittedEvent<'_>>(emitted_event) +                }; + +                self(Observe::new(world, emitted_event)); +            }), +        }; + +        (wrapper_comp, NoCallbacks) +    } +} + +#[derive(Component)] +pub struct WrapperComponent +{ +    run: Box<RunFn>, +} + +impl WrapperComponent +{ +    pub fn new(run: impl Fn(&World, Metadata, EmittedEvent<'_>) + 'static) -> Self +    { +        Self { run: Box::new(run) } +    } + +    /// Runs the observer system. +    /// +    /// # Safety +    /// `world` must live at least as long as the [`World`] the system belongs to. +    pub unsafe fn run( +        &self, +        world: &World, +        metadata: Metadata, +        emitted_event: EmittedEvent<'_>, +    ) +    { +        (self.run)(world, metadata, emitted_event); +    } +} + +impl Debug for WrapperComponent +{ +    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result +    { +        formatter +            .debug_struct("WrapperComponent") +            .finish_non_exhaustive() +    } +} + +type RunFn = dyn Fn(&World, Metadata, EmittedEvent<'_>); diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index 7b2b608..e74ef31 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,10 +1,18 @@ +use std::mem::transmute;  use std::panic::{RefUnwindSafe, UnwindSafe};  use seq_macro::seq;  use crate::component::local::SystemWithLocalComponents;  use crate::component::Parts as ComponentParts; +use crate::event::Emitted as EmittedEvent;  use crate::system::initializable::{Initializable, MaybeInitializableParamTuple}; +use crate::system::observer::{ +    Observe, +    Observed, +    Observer, +    WrapperComponent as ObserverWrapperComponent, +};  use crate::system::{Into as IntoSystem, Metadata, Param, System, TypeErased};  use crate::World; @@ -68,9 +76,10 @@ macro_rules! impl_system {                  }              } -            impl<Func, #(TParam~I,)*> IntoSystem<fn(#(TParam~I,)*)> +            impl<'world, Func, #(TParam~I,)*> IntoSystem<'world, fn(#(TParam~I,)*)>                  for Func              where +                #(TParam~I: Param<'world>,)*                  Func: Fn(#(TParam~I,)*) + Copy + 'static,              {                  type System = Stateful<Func>; @@ -123,3 +132,119 @@ fn init_initializable_params<'world, SystemT, Params>(  {      Params::init_initializable(system, inputs);  } + +macro_rules! impl_observer { +    ($c: tt) => { +        seq!(I in 0..$c { +            impl<'world, ObservedT, Func, #(TParam~I,)*> System< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Stateful<Func> +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world>,)* +            { +                type Callbacks = Callbacks; + +                fn finish(self) -> (TypeErased, Callbacks) +                { +                    unimplemented!(); +                } +            } + +            impl<'world, ObservedT, Func, #(TParam~I,)*> Initializable< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Stateful<Func> +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world>,)* +                (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> +            { +                type Inputs = < +                    (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self> +                >::Inputs; + +                fn initialize(mut self, inputs: Self::Inputs) -> Self +                { +                    init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs); + +                    self +                } +            } + +            impl<'world, ObservedT, Func, #(TParam~I,)*> Observer< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Stateful<Func> +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world>,)* +            { +                type ObservedEvents = ObservedT::Events; + +                fn observed_events() -> Self::ObservedEvents +                { +                    ObservedT::events() +                } + +                fn finish_observer(self) -> (ObserverWrapperComponent, Callbacks) +                { +                    let Self { func, local_components } = self; + +                    let callbacks = Callbacks { local_components }; + +                    let wrapper_comp = ObserverWrapperComponent::new( +                        move |world, metadata, emitted_event| { +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let world = unsafe { &*std::ptr::from_ref(world) }; + +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let emitted_event = unsafe { +                                transmute::<EmittedEvent<'_>, EmittedEvent<'_>>( +                                    emitted_event +                                ) +                            }; + +                            func(Observe::new(world, emitted_event), #({ +                                TParam~I::new(world, &metadata) +                            },)*); +                        }, +                    ); + +                    (wrapper_comp, callbacks) +                } +            } + +            impl<'world, Func, ObservedT, #(TParam~I,)*> IntoSystem< +                'world, +                fn(Observe<'world, ObservedT>, +                #(TParam~I,)*) +            > for Func +            where +                ObservedT: Observed, +                #(TParam~I: Param<'world>,)* +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +            { +                type System = Stateful<Func>; + +                fn into_system(self) -> Stateful<Func> +                { +                    Stateful { +                        func: self, +                        local_components: Vec::new(), // TODO: Use Vec::with_capacity +                    } +                } +            } +        }); +    }; +} + +seq!(C in 1..16 { +    impl_observer!(C); +}); | 
