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 /ecs | |
parent | ce1bade2c21cc3129fa8bc2b4bc67bc4dc2c25c3 (diff) |
feat(ecs): add component changed event
Diffstat (limited to 'ecs')
-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); +}); |