diff options
Diffstat (limited to 'ecs')
34 files changed, 1830 insertions, 807 deletions
diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml index cf35a74..5ea9fc7 100644 --- a/ecs/Cargo.toml +++ b/ecs/Cargo.toml @@ -11,7 +11,6 @@ seq-macro = "0.3.5" paste = "1.0.14" thiserror = "1.0.49" tracing = "0.1.39" -linkme = "0.3.29" hashbrown = "0.15.2" parking_lot = "0.12.3" ecs-macros = { path = "../ecs-macros" } diff --git a/ecs/examples/component_changed_event.rs b/ecs/examples/component_changed_event.rs new file mode 100644 index 0000000..f707255 --- /dev/null +++ b/ecs/examples/component_changed_event.rs @@ -0,0 +1,80 @@ +use ecs::event::component::Changed; +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<Changed, 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/examples/component_relationship.rs b/ecs/examples/component_relationship.rs new file mode 100644 index 0000000..4453e3a --- /dev/null +++ b/ecs/examples/component_relationship.rs @@ -0,0 +1,56 @@ +use ecs::pair::Pair; +use ecs::phase::START as START_PHASE; +use ecs::{Component, Query, World}; + +#[derive(Component)] +struct Person +{ + name: String, +} + +fn print_dog_likers(query: Query<(&Person, Pair<Likes, &Dogs>)>) +{ + for (person, liked_dogs) in &query { + println!( + "{} likes {} dogs!", + person.name, + if liked_dogs.large { "large" } else { "small" }, + ); + } +} + +#[derive(Component)] +struct Likes; + +#[derive(Component)] +struct Cats; + +#[derive(Component)] +struct Dogs +{ + large: bool, +} + +fn main() +{ + let mut world = World::new(); + + world.register_system(*START_PHASE, print_dog_likers); + + world.create_entity(( + Person { name: "Irving".to_string() }, + Pair::new_with_comp_target::<Likes>(Dogs { large: true }), + )); + + world.create_entity(( + Person { name: "Mark".to_string() }, + Pair::new_with_comp_target::<Likes>(Cats), + )); + + world.create_entity(( + Person { name: "Helena".to_string() }, + Pair::new_with_comp_target::<Likes>(Dogs { large: false }), + )); + + world.step(); +} diff --git a/ecs/examples/event_loop.rs b/ecs/examples/event_loop.rs index 61d7ba4..cc2f7f4 100644 --- a/ecs/examples/event_loop.rs +++ b/ecs/examples/event_loop.rs @@ -1,7 +1,7 @@ use ecs::actions::Actions; use ecs::pair::{ChildOf, Pair}; use ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; -use ecs::{static_entity, Component, Query, World}; +use ecs::{declare_entity, Component, Query, World}; #[derive(Component)] struct Wool @@ -65,16 +65,20 @@ fn age(query: Query<(&mut Health, &Name)>, mut actions: Actions) } } -static_entity!(SHEER_PHASE, (Phase, Pair::new::<ChildOf>(*UPDATE_PHASE))); +declare_entity!(SHEER_PHASE, (Phase, Pair::new::<ChildOf>(*UPDATE_PHASE))); -static_entity!(FEED_PHASE, (Phase, Pair::new::<ChildOf>(*SHEER_PHASE))); +declare_entity!(FEED_PHASE, (Phase, Pair::new::<ChildOf>(*SHEER_PHASE))); -static_entity!(AGE_PHASE, (Phase, Pair::new::<ChildOf>(*FEED_PHASE))); +declare_entity!(AGE_PHASE, (Phase, Pair::new::<ChildOf>(*FEED_PHASE))); fn main() { let mut world = World::new(); + world.create_declared_entity(&SHEER_PHASE); + world.create_declared_entity(&FEED_PHASE); + world.create_declared_entity(&AGE_PHASE); + world.register_system(*SHEER_PHASE, sheer); world.register_system(*FEED_PHASE, feed); world.register_system(*AGE_PHASE, age); diff --git a/ecs/examples/relationship.rs b/ecs/examples/relationship.rs index b607398..6ad08e7 100644 --- a/ecs/examples/relationship.rs +++ b/ecs/examples/relationship.rs @@ -1,6 +1,5 @@ -use ecs::pair::Pair; +use ecs::pair::{Pair, Wildcard}; use ecs::phase::START as START_PHASE; -use ecs::uid::Wildcard; use ecs::{Component, Query, World}; #[derive(Component)] @@ -23,10 +22,10 @@ struct Holding; fn print_player_stats(player_query: Query<(&Player, &Health, Pair<Holding, Wildcard>)>) { - for (_, health, sword_relationship) in &player_query { + for (_, health, target_sword) in &player_query { println!("Player health: {}", health.health); - if let Some(sword_ent) = sword_relationship.get_target_entity() { + if let Some(sword_ent) = target_sword.get_entity() { let sword = sword_ent .get::<Sword>() .expect("Sword entity is missing sword component"); diff --git a/ecs/examples/with_local.rs b/ecs/examples/with_local.rs index 4658fc0..7a36d0e 100644 --- a/ecs/examples/with_local.rs +++ b/ecs/examples/with_local.rs @@ -1,6 +1,7 @@ use ecs::component::local::Local; use ecs::phase::UPDATE as UPDATE_PHASE; -use ecs::system::{Into, System}; +use ecs::system::initializable::Initializable; +use ecs::system::Into; use ecs::{Component, Query, World}; #[derive(Component)] diff --git a/ecs/examples/with_sole.rs b/ecs/examples/with_sole.rs index c3feaab..a292f06 100644 --- a/ecs/examples/with_sole.rs +++ b/ecs/examples/with_sole.rs @@ -1,7 +1,7 @@ use ecs::pair::{ChildOf, Pair}; use ecs::phase::{Phase, UPDATE as UPDATE_PHASE}; use ecs::sole::Single; -use ecs::{static_entity, Component, Query, Sole, World}; +use ecs::{declare_entity, Component, Query, Sole, World}; #[derive(Component)] struct Ammo @@ -31,7 +31,7 @@ fn print_total_ammo_count(ammo_counter: Single<AmmoCounter>) assert_eq!(ammo_counter.counter, 19); } -static_entity!( +declare_entity!( PRINT_AMMO_COUNT_PHASE, (Phase, Pair::new::<ChildOf>(*UPDATE_PHASE)) ); @@ -40,6 +40,8 @@ fn main() { let mut world = World::new(); + world.create_declared_entity(&PRINT_AMMO_COUNT_PHASE); + world.register_system(*UPDATE_PHASE, count_ammo); world.register_system(*PRINT_AMMO_COUNT_PHASE, print_total_ammo_count); diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs index e0efeda..2dd68bf 100644 --- a/ecs/src/actions.rs +++ b/ecs/src/actions.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use std::rc::{Rc, Weak}; use crate::component::{Parts as ComponentParts, Sequence as ComponentSequence}; -use crate::system::{Param as SystemParam, System}; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::uid::{Kind as UidKind, Uid}; use crate::{ActionQueue, World}; @@ -100,17 +100,7 @@ impl<'world> SystemParam<'world> for Actions<'world> { type Input = (); - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self + fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { Self::new(&world.data.action_queue) } diff --git a/ecs/src/component.rs b/ecs/src/component.rs index a0ed752..e4ecfce 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -1,10 +1,11 @@ use std::any::{type_name, Any}; -use std::error::Error; 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, @@ -12,6 +13,7 @@ use crate::lock::{ ReadGuard, WriteGuard, }; +use crate::pair::Pair; use crate::system::Input as SystemInput; use crate::uid::Uid; use crate::util::Array; @@ -23,14 +25,6 @@ pub(crate) mod storage; pub trait Component: SystemInput + Any { - type HandleMut<'component>: HandleFromEntityComponentRef<'component> - where - Self: Sized; - - type Handle<'component>: HandleFromEntityComponentRef<'component> - where - Self: Sized; - /// Returns the ID of this component. fn id() -> Uid where @@ -77,87 +71,44 @@ pub trait Sequence fn into_parts_array(self) -> Self::PartsArray; } -/// [`Component`] metadata. -#[derive(Debug, Clone)] -#[non_exhaustive] -pub struct Metadata +#[derive(Debug)] +pub struct Handle<'a, DataT: 'static> { - pub id: Uid, + inner: MappedReadGuard<'a, DataT>, } -impl Metadata +impl<'comp, DataT: 'static> Handle<'comp, DataT> { - #[must_use] - pub fn of<ComponentT: Component>() -> Self - { - Self { id: ComponentT::id() } - } -} - -pub trait HandleFromEntityComponentRef<'comp>: Sized -{ - type Error: Error; - /// Creates a new handle instance from a [`EntityComponentRef`]. /// /// # Errors - /// See the implementation's [`Self::Error`] type. - fn from_entity_component_ref( - entity_component_ref: Option<EntityComponentRef<'comp>>, - world: &'comp World, - ) -> Result<Self, Self::Error>; -} - -#[derive(Debug)] -pub struct Handle<'a, ComponentData: 'static> -{ - inner: MappedReadGuard<'a, ComponentData>, -} - -impl<'a, ComponentData: 'static> Handle<'a, ComponentData> -{ - pub(crate) fn new(inner: ReadGuard<'a, Box<dyn Any>>) -> Self + /// 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 { - inner: inner.map(|component| { - component - .downcast_ref::<ComponentData>() - .unwrap_or_else(|| { - panic!( - "Failed to downcast component to type {}", - type_name::<ComponentData>() - ); - }) - }), - } + Self::new( + entity_component_ref + .component() + .read_nonblock() + .map_err(AcquireLockError)?, + ) } -} - -impl<'comp, ComponentData: 'static> HandleFromEntityComponentRef<'comp> - for Handle<'comp, ComponentData> -{ - type Error = HandleError; - fn from_entity_component_ref( - entity_component_ref: Option<EntityComponentRef<'comp>>, - _world: &'comp World, - ) -> Result<Self, Self::Error> + fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> Result<Self, HandleError> { - let entity_comp = - entity_component_ref.ok_or(HandleError::ComponentDoesNotExist)?; - - Ok(Self::new( - entity_comp - .component() - .read_nonblock() - .map_err(AcquireComponentLockFailed)?, - )) + Ok(Self { + inner: ReadGuard::try_map(inner, |component| { + 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 { @@ -166,55 +117,51 @@ 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<'a, ComponentData: 'static> HandleMut<'a, ComponentData> +impl<'comp, DataT: 'static> HandleMut<'comp, DataT> { - pub(crate) fn new(inner: WriteGuard<'a, Box<dyn Any>>) -> Self + /// 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> { - Self { - inner: inner.map(|component| { - component - .downcast_mut::<ComponentData>() - .unwrap_or_else(|| { - panic!( - "Failed to downcast component to type {}", - type_name::<ComponentData>() - ); - }) - }), - } + 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(), + }) } -} -impl<'comp, ComponentData: 'static> HandleFromEntityComponentRef<'comp> - for HandleMut<'comp, ComponentData> -{ - type Error = HandleError; - - fn from_entity_component_ref( - entity_component_ref: Option<EntityComponentRef<'comp>>, - _world: &'comp World, - ) -> Result<Self, Self::Error> + pub fn set_changed(&self) { - let entity_comp = - entity_component_ref.ok_or(HandleError::ComponentDoesNotExist)?; - - Ok(Self::new( - entity_comp - .component() - .write_nonblock() - .map_err(AcquireComponentLockFailed)?, - )) + self.event_submitter.submit_event( + &Pair::new::<Changed>(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 { @@ -222,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 { @@ -234,15 +181,15 @@ impl<ComponentData: 'static> DerefMut for HandleMut<'_, ComponentData> pub enum HandleError { #[error(transparent)] - AcquireComponentLockFailed(#[from] AcquireComponentLockFailed), + AcquireLockFailed(#[from] AcquireLockError), - #[error("Component does not exist")] - ComponentDoesNotExist, + #[error("Incorrect component type")] + IncorrectType, } #[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct AcquireComponentLockFailed(LockError); +#[error("Failed to acquire component lock")] +pub struct AcquireLockError(#[source] LockError); macro_rules! inner { ($c: tt) => { @@ -357,6 +304,12 @@ impl PartsBuilder 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 diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs index 0f6f641..6b2463f 100644 --- a/ecs/src/component/local.rs +++ b/ecs/src/component/local.rs @@ -1,7 +1,17 @@ +use std::any::type_name; use std::ops::{Deref, DerefMut}; -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::system::{Param as SystemParam, System}; +use ecs_macros::Component; + +use crate::component::{ + Component, + HandleMut as ComponentHandleMut, + IntoParts as _, + Parts as ComponentParts, +}; +use crate::pair::Pair; +use crate::system::initializable::Param as InitializableParam; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::World; /// Holds a component which is local to a single system. @@ -17,27 +27,44 @@ where { type Input = LocalComponent; - fn initialize<SystemImpl>( - system: &mut impl System<'world, SystemImpl>, - input: Self::Input, - ) + fn new(world: &'world World, system_metadata: &SystemMetadata) -> Self { - system.set_local_component(input); - } + let Some(system_ent) = world.get_entity(system_metadata.ent_id) else { + panic!( + "System entity with ID {} does not exist", + system_metadata.ent_id + ); + }; - fn new<SystemImpl>( - system: &'world impl System<'world, SystemImpl>, - _world: &'world World, - ) -> Self - { - let local_component = system - .get_local_component_mut::<LocalComponent>() - .expect("Local component is uninitialized"); + let Some(local_component) = system_ent.get_with_id_mut::<LocalComponent>( + Pair::new::<IsLocalComponent>(LocalComponent::id()).id(), + ) else { + panic!( + "Local component {} of system with ID {} is uninitialized", + type_name::<LocalComponent>(), + system_metadata.ent_id + ); + }; Self { local_component } } } +impl<'world, LocalComponent, SystemT> InitializableParam<'world, SystemT> + for Local<'world, LocalComponent> +where + LocalComponent: Component, + SystemT: SystemWithLocalComponents, + Self: SystemParam<'world, Input = LocalComponent>, +{ + fn initialize(system: &mut SystemT, input: Self::Input) + { + system.add_local_component( + Pair::new_with_comp_target::<IsLocalComponent>(input).into_parts(), + ); + } +} + impl<LocalComponent> Deref for Local<'_, LocalComponent> where LocalComponent: Component, @@ -59,3 +86,11 @@ where &mut self.local_component } } + +pub trait SystemWithLocalComponents +{ + fn add_local_component(&mut self, component_parts: ComponentParts); +} + +#[derive(Component)] +struct IsLocalComponent; diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index b27b552..4ec5222 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -270,7 +270,7 @@ impl Storage entity.insert_component( component_id, - ArchetypeEntityComponent::new(component, component_id, component_name), + ArchetypeEntityComponent::new(component, component_name), add_edge_archetype, ); diff --git a/ecs/src/component/storage/archetype.rs b/ecs/src/component/storage/archetype.rs index a88e0e8..d96632e 100644 --- a/ecs/src/component/storage/archetype.rs +++ b/ecs/src/component/storage/archetype.rs @@ -122,7 +122,7 @@ impl Archetype pub fn get_matching_component_indices( &self, component_id: Uid, - ) -> MatchingComponentIter + ) -> MatchingComponentIter<'_> { assert!( component_id.kind() == UidKind::Component @@ -314,29 +314,18 @@ impl Entity #[derive(Debug)] pub struct EntityComponent { - id: Uid, component: Lock<Box<dyn Any>>, } impl EntityComponent { - pub fn new( - component: Box<dyn Any>, - component_id: Uid, - component_name: &'static str, - ) -> Self + pub fn new(component: Box<dyn Any>, component_name: &'static str) -> Self { Self { - id: component_id, component: Lock::new(component, component_name), } } - pub fn id(&self) -> Uid - { - self.id - } - pub fn component(&self) -> &Lock<Box<dyn Any>> { &self.component @@ -370,11 +359,10 @@ impl Id } for comp_id in component_id_iter { - if prev_component_id.is_some_and(|prev_comp_id| *comp_id < prev_comp_id) { - panic!( - "Cannot create archetype ID from a unsorted component metadata list" - ); - } + assert!( + prev_component_id.is_none_or(|prev_comp_id| *comp_id >= prev_comp_id), + "Cannot create archetype ID from a unsorted component metadata list" + ); prev_component_id = Some(*comp_id); diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs index 29fa937..76200f9 100644 --- a/ecs/src/component/storage/graph.rs +++ b/ecs/src/component/storage/graph.rs @@ -80,7 +80,7 @@ impl Graph pub fn dfs_archetype_add_edges( &self, archetype_id: ArchetypeId, - ) -> Option<ArchetypeAddEdgeDfsIter> + ) -> Option<ArchetypeAddEdgeDfsIter<'_>> { let node = self.get_node_by_id(archetype_id)?; diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs index 562f7ea..bec50cd 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -1,6 +1,6 @@ use std::any::type_name; - -use linkme::distributed_slice; +use std::ops::Deref; +use std::sync::LazyLock; use crate::component::storage::archetype::{ Archetype, @@ -10,19 +10,20 @@ use crate::component::storage::archetype::{ use crate::component::{ Component, Handle as ComponentHandle, - HandleFromEntityComponentRef, HandleMut as ComponentHandleMut, }; use crate::uid::{Kind as UidKind, Uid}; use crate::{EntityComponentRef, World}; +pub mod obtainer; + /// A handle to a entity. #[derive(Debug)] pub struct Handle<'a> { - world: &'a World, archetype: &'a Archetype, entity: &'a ArchetypeEntity, + world: &'a World, } impl<'a> Handle<'a> @@ -43,20 +44,21 @@ 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); let component = self.get_matching_components(ComponentT::id()).next()?; Some( - ComponentHandle::from_entity_component_ref(Some(component), self.world) - .unwrap_or_else(|err| { + ComponentHandle::from_entity_component_ref(&component).unwrap_or_else( + |err| { panic!( - "Taking component {} lock failed: {err}", + "Creating handle to component {} failed: {err}", type_name::<ComponentT>() ); - }), + }, + ), ) } @@ -70,23 +72,88 @@ impl<'a> Handle<'a> #[must_use] pub fn get_mut<ComponentT: Component>( &self, - ) -> Option<ComponentHandleMut<'_, ComponentT>> + ) -> Option<ComponentHandleMut<'a, ComponentT>> { assert_eq!(ComponentT::id().kind(), UidKind::Component); let component = self.get_matching_components(ComponentT::id()).next()?; Some( - ComponentHandleMut::from_entity_component_ref(Some(component), self.world) + ComponentHandleMut::from_entity_component_ref(&component, self.world) .unwrap_or_else(|err| { panic!( - "Taking component {} lock failed: {err}", + "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>() + ); + }, + ), + ) + } + + /// Returns a mutable 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 elsewhere + /// - The component type is incorrect + #[must_use] + pub fn get_with_id_mut<ComponentDataT: 'static>( + &self, + id: Uid, + ) -> Option<ComponentHandleMut<'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( + ComponentHandleMut::from_entity_component_ref(&component, self.world) + .unwrap_or_else(|err| { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentDataT>() + ); + }), + ) + } + #[inline] #[must_use] pub fn get_matching_components(&self, component_uid: Uid) @@ -98,13 +165,21 @@ impl<'a> Handle<'a> } } + /// Returns whether or not this entity contains a component with the specified `Uid`. + #[must_use] + pub fn has_component(&self, component_uid: Uid) -> bool + { + self.archetype + .contains_component_with_exact_id(component_uid) + } + pub(crate) fn new( - world: &'a World, archetype: &'a Archetype, entity: &'a ArchetypeEntity, + world: &'a World, ) -> Self { - Self { world, archetype, entity } + Self { archetype, entity, world } } } @@ -126,35 +201,53 @@ impl<'a> Iterator for MatchingComponentIter<'a> Some(EntityComponentRef::new( matching_component_id, self.entity.components().get(index).unwrap(), + self.entity.uid(), )) } } +/// The data type of a declaration of a entity. +#[derive(Debug)] +pub struct Declaration +{ + uid: LazyLock<Uid>, + create_func: fn(&mut World), +} + +impl Declaration +{ + pub(crate) fn create(&self, world: &mut World) + { + (self.create_func)(world); + } + + #[doc(hidden)] + pub const fn new(create_func: fn(&mut World)) -> Self + { + Self { + uid: LazyLock::new(|| Uid::new_unique(UidKind::Entity)), + create_func, + } + } +} + +impl Deref for Declaration +{ + type Target = Uid; + + fn deref(&self) -> &Self::Target + { + &self.uid + } +} + #[allow(clippy::module_name_repetitions)] #[macro_export] -macro_rules! static_entity { +macro_rules! declare_entity { ($visibility: vis $ident: ident, $components: expr) => { - $visibility static $ident: ::std::sync::LazyLock<$crate::uid::Uid> = - ::std::sync::LazyLock::new(|| { - $crate::uid::Uid::new_unique($crate::uid::Kind::Entity) + $visibility static $ident: $crate::entity::Declaration = + $crate::entity::Declaration::new(|world| { + world.create_entity_with_uid(*$ident, $components); }); - - $crate::private::paste::paste! { - mod [<__ecs_ $ident:lower _static_entity_priv>] { - use super::*; - - #[$crate::private::linkme::distributed_slice( - $crate::entity::CREATE_STATIC_ENTITIES - )] - #[linkme(crate=$crate::private::linkme)] - static CREATE_STATIC_ENTITY: fn(&mut $crate::World) = |world| { - world.create_entity_with_uid($components, *$ident); - }; - } - } } } - -#[distributed_slice] -#[doc(hidden)] -pub static CREATE_STATIC_ENTITIES: [fn(&mut World)]; diff --git a/ecs/src/entity/obtainer.rs b/ecs/src/entity/obtainer.rs new file mode 100644 index 0000000..6c2ea96 --- /dev/null +++ b/ecs/src/entity/obtainer.rs @@ -0,0 +1,29 @@ +use crate::entity::Handle as EntityHandle; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; +use crate::uid::Uid; +use crate::World; + +#[derive(Debug)] +pub struct Obtainer<'world> +{ + world: &'world World, +} + +impl<'world> SystemParam<'world> for Obtainer<'world> +{ + type Input = (); + + fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self + { + Self { world } + } +} + +impl Obtainer<'_> +{ + #[must_use] + pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>> + { + self.world.get_entity(entity_id) + } +} 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 ef09480..421c369 100644 --- a/ecs/src/event/component.rs +++ b/ecs/src/event/component.rs @@ -1,18 +1,22 @@ //! Component events. use std::convert::Infallible; -use std::fmt::Debug; use crate::Component; -/// Pair relation for events emitted when: -/// a) A entity with the target component is spawned. -/// b) The target component is added to a entity. -#[derive(Debug, Component)] -pub struct Added(Infallible); +// TODO: Implement +// /// Pair relation for events emitted when: +// /// a) A entity with the target component is spawned. +// /// b) The target component is added to a entity. +// #[derive(Debug, Component)] +// pub struct Added(Infallible); + +// TODO: Implement +// /// Pair relation for events emitted when: +// /// a) The target component is removed from a entity. +// /// b) A entity with the target component is despawned. +// #[derive(Debug, Component)] +// pub struct Removed(Infallible); -/// Pair relation for events emitted when: -/// a) The target component is removed from a entity. -/// b) A entity with the target component is despawned. #[derive(Debug, Component)] -pub struct Removed(Infallible); +pub struct Changed(Infallible); diff --git a/ecs/src/extension.rs b/ecs/src/extension.rs index 42ebef9..9c6614b 100644 --- a/ecs/src/extension.rs +++ b/ecs/src/extension.rs @@ -1,5 +1,7 @@ use crate::component::Sequence as ComponentSequence; +use crate::entity::Declaration as EntityDeclaration; use crate::sole::Sole; +use crate::system::observer::Observer; use crate::system::System; use crate::uid::Uid; use crate::{SoleAlreadyExistsError, World}; @@ -34,6 +36,15 @@ impl<'world> Collector<'world> self.world.register_system(phase_euid, system); } + /// Adds a observer system to the [`World`]. + pub fn add_observer<'this, SystemImpl>( + &'this mut self, + observer: impl Observer<'this, SystemImpl>, + ) + { + self.world.register_observer(observer); + } + /// Adds a entity to the [`World`]. pub fn add_entity<Comps>(&mut self, components: Comps) where @@ -42,6 +53,12 @@ impl<'world> Collector<'world> self.world.create_entity(components); } + /// Adds a declared entity to the [`World`]. + pub fn add_declared_entity(&mut self, entity_decl: &EntityDeclaration) + { + self.world.create_declared_entity(entity_decl); + } + /// Adds a globally shared singleton value to the [`World`]. /// /// # Errors diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 53abc6b..e9494a7 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,7 +1,6 @@ #![deny(clippy::all, clippy::pedantic)] use std::any::{type_name, Any, TypeId}; -use std::cell::RefCell; use std::fmt::Debug; use std::mem::ManuallyDrop; use std::rc::Rc; @@ -15,31 +14,30 @@ 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::CREATE_STATIC_ENTITIES; -use crate::event::component::{ - Added as ComponentAddedEvent, - Removed as ComponentRemovedEvent, -}; +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}; +use crate::pair::{ChildOf, DependsOn, Pair, Wildcard}; use crate::phase::{Phase, START as START_PHASE}; use crate::query::flexible::Query as FlexibleQuery; use crate::query::term::Without; use crate::query::{ - Iter as QueryIter, TermWithFieldTuple as QueryTermWithFieldTuple, TermWithoutFieldTuple as QueryTermWithoutFieldTuple, Terms as QueryTerms, TermsBuilderInterface, + MAX_TERM_CNT as QUERY_MAX_TERM_CNT, }; -use crate::sole::Sole; +use crate::sole::{Single, Sole}; use crate::stats::Stats; -use crate::system::{System, SystemComponent}; -use crate::uid::{Kind as UidKind, Uid, Wildcard}; +use crate::system::observer::{Observer, WrapperComponent as ObserverWrapperComponent}; +use crate::system::{Callbacks, Metadata as SystemMetadata, System, SystemComponent}; +use crate::uid::{Kind as UidKind, Uid}; pub mod actions; pub mod component; @@ -56,9 +54,6 @@ pub mod tuple; pub mod uid; pub mod util; -#[doc(hidden)] -pub mod private; - mod lock; pub use ecs_macros::{Component, Sole}; @@ -84,49 +79,48 @@ impl World is_first_tick: AtomicBool::new(false), }; - world.add_sole(Stats::default()).ok(); + crate::phase::spawn_entities(&mut world); - for create_static_entity in CREATE_STATIC_ENTITIES { - create_static_entity(&mut world); - } + world.add_sole(Stats::default()).ok(); world } - /// Creates a new entity with the given components. + /// Creates a entity with the given components. A new unique [`Uid`] will be generated + /// for this entity. pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid where Comps: ComponentSequence, { let entity_uid = Uid::new_unique(UidKind::Entity); - self.create_entity_with_uid(components, entity_uid); + self.create_entity_with_uid(entity_uid, components); entity_uid } + /// Creates a entity with the given components. The entity will have the specified + /// [`Uid`]. #[tracing::instrument(skip_all)] - #[doc(hidden)] - pub fn create_entity_with_uid<Comps>(&mut self, components: Comps, entity_uid: Uid) + pub fn create_entity_with_uid<Comps>(&mut self, entity_uid: Uid, components: Comps) 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.create_ent(entity_uid, components.into_parts_array()); + } - let added_component_ids = Self::add_entity_components( - entity_uid, - components.into_parts_array(), + pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts) + { + Self::add_entity_components( + entity_id, + [component_parts], &mut self.data.component_storage, ); + } - for comp_id in added_component_ids { - self.emit_event_by_id::<ComponentAddedEvent>(comp_id); - } + pub fn create_declared_entity(&mut self, entity_decl: &EntityDeclaration) + { + entity_decl.create(self); } /// Adds a globally shared singleton value. @@ -140,28 +134,42 @@ 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>, { - self.create_entity(( - SystemComponent { system: system.into_type_erased() }, - Pair::new::<DependsOn>(phase_euid), - )); + let (wrapper_comp, mut system_callbacks) = observer.finish_observer(); + + let ent_id = Uid::new_unique(UidKind::Entity); + + 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>, ) { - self.create_entity(( - SystemComponent { system: system.into_type_erased() }, - event, + let (type_erased_system, mut system_callbacks) = system.finish(); + + 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 }); } /// Adds a extensions. @@ -172,7 +180,9 @@ impl World extension.collect(extension_collector); } - pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms> + pub fn query<FieldTerms, FieldlessTerms>( + &self, + ) -> Query<'_, FieldTerms, FieldlessTerms> where FieldTerms: QueryTermWithFieldTuple, FieldlessTerms: QueryTermWithoutFieldTuple, @@ -188,6 +198,30 @@ impl World FlexibleQuery::new(self, terms) } + pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>> + { + let archetype = self + .data + .component_storage + .get_entity_archetype(entity_id)?; + + let Some(entity) = archetype.get_entity_by_id(entity_id) else { + unreachable!("Should exist since archetype was found by entity id"); + }; + + Some(EntityHandle::new(archetype, entity, self)) + } + + pub fn get_sole<SoleT: Sole>(&self) -> Option<Single<'_, SoleT>> + { + Some(Single::new(self.data.sole_storage.get::<SoleT>()?)) + } + + pub fn event_submitter(&self) -> EventSubmitter<'_> + { + EventSubmitter::new(self) + } + /// Performs a single tick. /// # Panics /// Will panic if mutable internal lock cannot be acquired. @@ -207,6 +241,8 @@ impl World self.perform_phases(); + self.emit_new_events(); + self.data.component_storage.create_imaginary_archetypes(); self.perform_queued_actions(); @@ -215,17 +251,9 @@ impl World return StepResult::Stop; } - let mut stats_lock = self - .data - .sole_storage - .get::<Stats>() - .expect("No stats sole found") - .write_nonblock() - .expect("Failed to aquire read-write stats sole lock"); - - let stats = stats_lock - .downcast_mut::<Stats>() - .expect("Casting stats sole to Stats type failed"); + let Some(mut stats) = self.get_sole::<Stats>() else { + unreachable!(); // Reason: is added in World::new + }; stats.current_tick += 1; @@ -285,23 +313,46 @@ 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 = self.flexible_query( - QueryTerms::<2>::builder() - .with_required([ - SystemComponent::id(), - Pair::new::<DependsOn>(phase_euid).id(), - ]) - .build(), + let system_query = Query::<(&SystemComponent,)>::from_flexible_query( + self.flexible_query( + QueryTerms::<QUERY_MAX_TERM_CNT>::builder() + .with_required([ + SystemComponent::id(), + Pair::new::<DependsOn>(phase_euid).id(), + ]) + .build(), + ), ); - for (system_component,) in - QueryIter::<(&SystemComponent,), _>::new(self, system_query.iter()) - { + for (system_ent_id, (system_component,)) in system_query.iter_with_euids() { // SAFETY: The world lives long enough unsafe { - system_component.system.run(self); + system_component + .system + .run(self, SystemMetadata { ent_id: system_ent_id }); } } } @@ -337,25 +388,39 @@ impl World } } - #[tracing::instrument(skip_all)] - fn perform_queued_actions(&mut self) + fn emit_new_events(&self) { - let mut active_action_queue = match *self.data.action_queue.active_queue.borrow() - { - ActiveActionQueue::A => &self.data.action_queue.queue_a, - ActiveActionQueue::B => &self.data.action_queue.queue_b, - } - .write_nonblock() - .unwrap_or_else(|err| { - panic!( - "Failed to take read-write action queue lock {:?}: {err}", - self.data.action_queue.active_queue + 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, + }, ); - }); + } + } - let mut has_swapped_active_queue = false; + #[tracing::instrument(skip_all)] + fn perform_queued_actions(&mut self) + { + let mut action_queue_lock = self + .data + .action_queue + .queue + .write_nonblock() + .unwrap_or_else(|err| { + panic!("Failed to take read-write action queue lock: {err}",); + }); - for action in active_action_queue.drain(..) { + for action in action_queue_lock.drain(..) { match action { Action::Spawn(components) => { let new_entity_uid = Uid::new_unique(UidKind::Entity); @@ -367,69 +432,32 @@ impl World continue; } - let added_component_ids = Self::add_entity_components( + Self::add_entity_components( new_entity_uid, components, &mut self.data.component_storage, ); - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for comp_id in added_component_ids { - self.emit_event_by_id::<ComponentAddedEvent>(comp_id); - } } Action::Despawn(entity_uid) => { - let removed_entity = - match self.data.component_storage.remove_entity(entity_uid) { - Ok(components) => components, - Err(err) => { - tracing::error!("Failed to despawn entity: {err}"); - return; - } - }; - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for removed_ent_comp in removed_entity.components() { - self.emit_event_by_id::<ComponentRemovedEvent>( - removed_ent_comp.id(), - ); + if let Err(err) = + self.data.component_storage.remove_entity(entity_uid) + { + tracing::error!("Failed to despawn entity: {err}"); } } Action::AddComponents(entity_uid, components) => { - let added_component_ids = Self::add_entity_components( + Self::add_entity_components( entity_uid, components, &mut self.data.component_storage, ); - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for comp_id in added_component_ids { - self.emit_event_by_id::<ComponentAddedEvent>(comp_id); - } } Action::RemoveComponents(entity_uid, component_ids) => { - let removed_component_ids = Self::remove_entity_components( + Self::remove_entity_components( entity_uid, component_ids, &mut self.data.component_storage, ); - - if !has_swapped_active_queue { - self.swap_event_queue(&mut has_swapped_active_queue); - } - - for comp_id in removed_component_ids { - self.emit_event_by_id::<ComponentRemovedEvent>(comp_id); - } } Action::Stop => { self.stop.store(true, Ordering::Relaxed); @@ -491,36 +519,28 @@ impl World removed_component_ids } - fn emit_event_by_id<Event: Component>(&self, target: Uid) + fn emit_event_observers(&self, event_id: Uid, emitted_event: &EmittedEvent<'_>) { - if target.kind() == UidKind::Pair { - return; - } - - let query = self.flexible_query( - QueryTerms::<2>::builder() - .with_required([SystemComponent::id(), Pair::new::<Event>(target).id()]) - .build(), + 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 (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) { + for (observer_ent_id, (observer,)) in query.iter_with_euids() { unsafe { - system.system.run(self); + observer.run( + self, + SystemMetadata { ent_id: observer_ent_id }, + emitted_event.clone(), + ); } } } - - fn swap_event_queue(&self, has_swapped_active_queue: &mut bool) - { - let mut active_queue = self.data.action_queue.active_queue.borrow_mut(); - - *active_queue = match *active_queue { - ActiveActionQueue::A => ActiveActionQueue::B, - ActiveActionQueue::B => ActiveActionQueue::A, - }; - - *has_swapped_active_queue = true; - } } impl Default for World @@ -541,31 +561,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> @@ -581,56 +591,37 @@ 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 } -} -#[derive(Debug, Default, Clone, Copy)] -enum ActiveActionQueue -{ - #[default] - A, - B, + fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent, entity_id: Uid) + -> Self + { + Self { + component_id, + component: comp, + entity_id, + } + } } -#[derive(Debug)] +#[derive(Debug, Default)] struct ActionQueue { - queue_a: Lock<Vec<Action>>, - queue_b: Lock<Vec<Action>>, - active_queue: RefCell<ActiveActionQueue>, + queue: Lock<Vec<Action>>, } impl ActionQueue { fn push(&self, action: Action) { - match *self.active_queue.borrow() { - ActiveActionQueue::A => self - .queue_a - .write_nonblock() - .expect("Failed to aquire read-write action queue A lock") - .push(action), - ActiveActionQueue::B => self - .queue_b - .write_nonblock() - .expect("Failed to aquire read-write action queue A lock") - .push(action), - } - } -} - -impl Default for ActionQueue -{ - fn default() -> Self - { - Self { - queue_a: Lock::new(Vec::new(), type_name::<Vec<Action>>()), - queue_b: Lock::new(Vec::new(), type_name::<Vec<Action>>()), - active_queue: RefCell::new(ActiveActionQueue::default()), - } + self.queue + .write_nonblock() + .expect("Failed to aquire read-write lock to action queue") + .push(action); } } diff --git a/ecs/src/lock.rs b/ecs/src/lock.rs index 0b36922..fe4e08b 100644 --- a/ecs/src/lock.rs +++ b/ecs/src/lock.rs @@ -1,3 +1,4 @@ +use std::any::type_name; use std::mem::forget; use std::ops::{Deref, DerefMut}; @@ -30,7 +31,7 @@ impl<Value> Lock<Value> /// /// # Errors /// Returns `Err` if unavailable (A mutable handle is hold). - pub fn read_nonblock(&self) -> Result<ReadGuard<Value>, Error> + pub fn read_nonblock(&self) -> Result<ReadGuard<'_, Value>, Error> { let guard = self.inner.try_read().ok_or(Error::ReadUnavailable)?; @@ -46,7 +47,7 @@ impl<Value> Lock<Value> /// /// # Errors /// Returns `Err` if unavailable (A mutable or immutable handle is hold). - pub fn write_nonblock(&self) -> Result<WriteGuard<Value>, Error> + pub fn write_nonblock(&self) -> Result<WriteGuard<'_, Value>, Error> { let guard = self.inner.try_write().ok_or(Error::WriteUnavailable)?; @@ -62,6 +63,14 @@ impl<Value> Lock<Value> } } +impl<Value: Default + 'static> Default for Lock<Value> +{ + fn default() -> Self + { + Self::new(Value::default(), type_name::<Value>()) + } +} + #[derive(Debug, thiserror::Error)] pub enum Error { @@ -81,21 +90,26 @@ pub struct ReadGuard<'guard, Value> impl<'guard, Value> ReadGuard<'guard, Value> { - pub fn map<NewValue>( - self, - func: impl FnOnce(&Value) -> &NewValue, - ) -> MappedReadGuard<'guard, NewValue> + pub fn try_map<NewValue>( + this: Self, + func: impl FnOnce(&Value) -> Option<&NewValue>, + ) -> Result<MappedReadGuard<'guard, NewValue>, Self> { - let value_type_name = self.value_type_name; + let value_type_name = this.value_type_name; // The 'inner' field cannot be moved out of ReadGuard in a normal way since // ReadGuard implements Drop - let inner = unsafe { std::ptr::read(&self.inner) }; - forget(self); - - MappedReadGuard { - inner: RwLockReadGuard::map(inner, func), - value_type_name, + let inner = unsafe { std::ptr::read(&raw const this.inner) }; + forget(this); + + match RwLockReadGuard::try_map(inner, func) { + Ok(mapped_guard) => { + Ok(MappedReadGuard { inner: mapped_guard, value_type_name }) + } + Err(unmapped_guard) => Err(Self { + inner: unmapped_guard, + value_type_name, + }), } } } @@ -155,21 +169,26 @@ pub struct WriteGuard<'guard, Value> impl<'guard, Value> WriteGuard<'guard, Value> { - pub fn map<NewValue>( - self, - func: impl FnOnce(&mut Value) -> &mut NewValue, - ) -> MappedWriteGuard<'guard, NewValue> + pub fn try_map<NewValue>( + this: Self, + func: impl FnOnce(&mut Value) -> Option<&mut NewValue>, + ) -> Result<MappedWriteGuard<'guard, NewValue>, Self> { - let value_type_name = self.value_type_name; + let value_type_name = this.value_type_name; // The 'inner' field cannot be moved out of ReadGuard in a normal way since // ReadGuard implements Drop - let inner = unsafe { std::ptr::read(&self.inner) }; - forget(self); - - MappedWriteGuard { - inner: RwLockWriteGuard::map(inner, func), - value_type_name, + let inner = unsafe { std::ptr::read(&raw const this.inner) }; + forget(this); + + match RwLockWriteGuard::try_map(inner, func) { + Ok(mapped_guard) => { + Ok(MappedWriteGuard { inner: mapped_guard, value_type_name }) + } + Err(unmapped_guard) => Err(Self { + inner: unmapped_guard, + value_type_name, + }), } } } diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs index 2055d5e..553652e 100644 --- a/ecs/src/pair.rs +++ b/ecs/src/pair.rs @@ -1,6 +1,13 @@ +use std::any::type_name; use std::convert::Infallible; -use crate::component::{IntoParts as IntoComponentParts, Parts as ComponentParts}; +use crate::component::{ + Handle as ComponentHandle, + HandleError as ComponentHandleError, + HandleMut as ComponentHandleMut, + IntoParts as IntoComponentParts, + Parts as ComponentParts, +}; use crate::entity::{ Handle as EntityHandle, MatchingComponentIter as EntityMatchingComponentIter, @@ -10,22 +17,22 @@ use crate::query::{ TermsBuilder as QueryTermsBuilder, TermsBuilderInterface, }; -use crate::uid::{PairParams as UidPairParams, Uid, Wildcard, With as WithUid}; -use crate::{Component, World}; +use crate::uid::{PairParams as UidPairParams, Uid, With as WithUid}; +use crate::{Component, EntityComponentRef, World}; #[derive(Debug)] -pub struct Pair<RelationElem: Element, TargetElem: Element> +pub struct Pair<Relation, Target> { - relation: RelationElem, - target: TargetElem, + relation: Relation, + target: Target, } impl Pair<Uid, Uid> { #[must_use] - pub fn new<Relation: WithUid>(target: Uid) -> Self + pub fn new<Relation: Component>(target: Uid) -> Self { - Self { relation: Relation::uid(), target } + Self { relation: Relation::id(), target } } #[must_use] @@ -38,6 +45,20 @@ impl Pair<Uid, Uid> } } +impl<Target> Pair<Uid, Target> +where + Target: Component, +{ + /// Returns a new pair that contains the target component as data. + pub fn new_with_comp_target<Relation: Component>(target_component: Target) -> Self + { + Self { + relation: Relation::uid(), + target: target_component, + } + } +} + impl IntoComponentParts for Pair<Uid, Uid> { fn into_parts(self) -> ComponentParts @@ -46,12 +67,96 @@ impl IntoComponentParts for Pair<Uid, Uid> } } -impl<Relation, Target> QueryTermWithField for Pair<Relation, Target> +impl<Target> IntoComponentParts for Pair<Uid, Target> +where + Target: Component, +{ + fn into_parts(self) -> ComponentParts + { + let id = Uid::new_pair(&UidPairParams { + relation: self.relation, + target: Target::id(), + }); + + ComponentParts::builder() + .name("Pair") + .build(id, self.target) + } +} + +impl<Relation, Target> QueryTermWithField for Pair<Relation, &Target> +where + Relation: Component, + Target: Component, +{ + type Field<'a> = ComponentHandle<'a, Target>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with_required([Self::uid()]); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + _world: &'world World, + ) -> Self::Field<'world> + { + let target_component = entity_handle + .get_matching_components(Self::uid()) + .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>() + ); + }) + } +} + +impl<Relation, Target> QueryTermWithField for Pair<Relation, &mut Target> where - Relation: WithUid, - Target: WithUid, + Relation: Component, + Target: Component, { - type Field<'a> = Handle<'a>; + type Field<'a> = ComponentHandleMut<'a, Target>; + + fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( + terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, + ) + { + terms_builder.with_required([Self::uid()]); + } + + fn get_field<'world>( + entity_handle: &EntityHandle<'world>, + world: &'world World, + ) -> Self::Field<'world> + { + let target_component = entity_handle + .get_matching_components(Self::uid()) + .next() + .expect("Not possible"); + + Self::Field::from_entity_component_ref(&target_component, world).unwrap_or_else( + |err| { + panic!( + "Creating handle to target component {} failed: {err}", + type_name::<Target>() + ); + }, + ) + } +} + +impl<Relation> QueryTermWithField for Pair<Relation, Wildcard> +where + Relation: Component, +{ + type Field<'a> = WildcardTargetHandle<'a>; fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, @@ -70,17 +175,17 @@ where .next() .expect("Not possible"); - Handle { + WildcardTargetHandle { world, - pair_uid: first_matching_comp.id(), + component_ref: first_matching_comp, } } } -impl<Relation, Target> WithUid for Pair<Relation, Target> +impl<Relation, Target> WithUid for Pair<Relation, &Target> where - Relation: WithUid, - Target: WithUid, + Relation: Component, + Target: Component, { fn uid() -> Uid { @@ -91,11 +196,38 @@ where } } +impl<Relation, Target> WithUid for Pair<Relation, &mut Target> +where + Relation: Component, + Target: Component, +{ + fn uid() -> Uid + { + Uid::new_pair(&UidPairParams { + relation: Relation::uid(), + target: Target::uid(), + }) + } +} + +impl<Relation> WithUid for Pair<Relation, Wildcard> +where + Relation: Component, +{ + fn uid() -> Uid + { + Uid::new_pair(&UidPairParams { + relation: Relation::uid(), + target: Wildcard::uid(), + }) + } +} + impl<Relation> QueryTermWithField for &'static [Pair<Relation, Wildcard>] where - Relation: WithUid, + Relation: Component, { - type Field<'a> = HandleIter<'a>; + type Field<'a> = WildcardTargetIter<'a>; fn apply_to_terms_builder<const MAX_TERM_CNT: usize>( terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>, @@ -109,7 +241,7 @@ where world: &'world World, ) -> Self::Field<'world> { - HandleIter { + WildcardTargetIter { inner: entity_handle .get_matching_components(Pair::<Relation, Wildcard>::uid()), world, @@ -117,73 +249,114 @@ where } } -pub struct Handle<'world> +pub struct WildcardTargetHandle<'world> { world: &'world World, - pair_uid: Uid, + component_ref: EntityComponentRef<'world>, } -impl Handle<'_> +impl WildcardTargetHandle<'_> { + /// Attempts to retrieve the target as a entity, returning `None` if not found. #[must_use] - pub fn get_target_entity(&self) -> Option<EntityHandle<'_>> + pub fn get_entity(&self) -> Option<EntityHandle<'_>> { let archetype = self .world .data .component_storage - .get_entity_archetype(self.pair_uid.target_entity())?; + .get_entity_archetype(self.component_ref.id().target_entity())?; let Some(archetype_entity) = - archetype.get_entity_by_id(self.pair_uid.target_entity()) + archetype.get_entity_by_id(self.component_ref.id().target_entity()) else { unreachable!(); }; - Some(EntityHandle::new(self.world, archetype, archetype_entity)) + Some(EntityHandle::new(archetype, archetype_entity, self.world)) + } + + /// Attempts to retrieve the target as a component, returning `None` if the component + /// type is incorrect. + /// + /// # Panics + /// Will panic if: + /// - The component is mutably borrowed elsewhere + #[must_use] + pub fn get_component<ComponentData>( + &self, + ) -> Option<ComponentHandle<'_, ComponentData>> + where + ComponentData: 'static, + { + ComponentHandle::<ComponentData>::from_entity_component_ref(&self.component_ref) + .map_or_else( + |err| match err { + ComponentHandleError::IncorrectType => None, + err @ ComponentHandleError::AcquireLockFailed(_) => { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentData>() + ); + } + }, + Some, + ) + } + + /// Attempts to retrieve the target as a component, returning `None` if the component + /// type is incorrect. + /// + /// # Panics + /// Will panic if: + /// - The component is borrowed elsewhere + #[must_use] + pub fn get_component_mut<ComponentData>( + &self, + ) -> Option<ComponentHandleMut<'_, ComponentData>> + where + ComponentData: 'static, + { + ComponentHandleMut::<ComponentData>::from_entity_component_ref( + &self.component_ref, + self.world, + ) + .map_or_else( + |err| match err { + ComponentHandleError::IncorrectType => None, + err @ ComponentHandleError::AcquireLockFailed(_) => { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentData>() + ); + } + }, + Some, + ) } } -pub struct HandleIter<'a> +pub struct WildcardTargetIter<'a> { inner: EntityMatchingComponentIter<'a>, world: &'a World, } -impl<'a> Iterator for HandleIter<'a> +impl<'a> Iterator for WildcardTargetIter<'a> { - type Item = Handle<'a>; + type Item = WildcardTargetHandle<'a>; fn next(&mut self) -> Option<Self::Item> { let matching_comp = self.inner.next()?; - Some(Handle { + Some(WildcardTargetHandle { world: self.world, - pair_uid: matching_comp.id(), + component_ref: matching_comp, }) } } -pub trait Element: sealed::Sealed -{ - type Value; -} - -impl Element for Uid -{ - type Value = Uid; -} - -impl sealed::Sealed for Uid {} - -impl<WithUidT: WithUid> Element for WithUidT -{ - type Value = Infallible; -} - -impl<WithUidT: WithUid> sealed::Sealed for WithUidT {} - /// Relation denoting a dependency to another entity #[derive(Debug, Default, Clone, Copy, Component)] pub struct DependsOn; @@ -192,7 +365,14 @@ pub struct DependsOn; #[derive(Debug, Default, Clone, Copy, Component)] pub struct ChildOf; -mod sealed +#[derive(Debug)] +pub struct Wildcard(Infallible); + +impl Wildcard { - pub trait Sealed {} + #[must_use] + pub fn uid() -> Uid + { + Uid::wildcard() + } } diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs index 9f47fb8..c13c432 100644 --- a/ecs/src/phase.rs +++ b/ecs/src/phase.rs @@ -1,13 +1,19 @@ use ecs_macros::Component; use crate::pair::{ChildOf, Pair}; -use crate::static_entity; +use crate::{declare_entity, World}; #[derive(Debug, Default, Clone, Copy, Component)] pub struct Phase; -static_entity!(pub START, (Phase,)); +declare_entity!(pub START, (Phase,)); +declare_entity!(pub PRE_UPDATE, (Phase,)); +declare_entity!(pub UPDATE, (Phase, Pair::new::<ChildOf>(*PRE_UPDATE))); -static_entity!(pub PRE_UPDATE, (Phase,)); - -static_entity!(pub UPDATE, (Phase, Pair::new::<ChildOf>(*PRE_UPDATE))); +#[doc(hidden)] +pub(crate) fn spawn_entities(world: &mut World) +{ + world.create_declared_entity(&START); + world.create_declared_entity(&PRE_UPDATE); + world.create_declared_entity(&UPDATE); +} diff --git a/ecs/src/private.rs b/ecs/src/private.rs deleted file mode 100644 index 56a6552..0000000 --- a/ecs/src/private.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[doc(hidden)] -pub use {linkme, paste}; diff --git a/ecs/src/query.rs b/ecs/src/query.rs index 7e10c5b..5f13579 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -6,12 +6,11 @@ use seq_macro::seq; use crate::component::{ Component, Handle as ComponentHandle, - HandleFromEntityComponentRef, HandleMut as ComponentHandleMut, }; use crate::entity::Handle as EntityHandle; use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery}; -use crate::system::{Param as SystemParam, System}; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::uid::{Kind as UidKind, Uid, With as WithUid}; use crate::util::array_vec::ArrayVec; use crate::util::Array; @@ -20,15 +19,16 @@ use crate::World; pub mod flexible; pub mod term; +// A term tuple type can have a maximum of 17 elements +pub const MAX_TERM_CNT: usize = 17; + #[derive(Debug)] pub struct Query<'world, FieldTerms, FieldlessTerms = ()> where FieldTerms: TermWithFieldTuple, FieldlessTerms: TermWithoutFieldTuple, { - world: &'world World, - // A term tuple type can have a maximum of 17 elements - inner: FlexibleQuery<'world, 17>, + inner: FlexibleQuery<'world, MAX_TERM_CNT>, _pd: PhantomData<(FieldTerms, FieldlessTerms)>, } @@ -47,8 +47,8 @@ where tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); Iter { - world: self.world, - iter: self.inner.iter(), + world: self.inner.world(), + inner: self.inner.iter(), comps_pd: PhantomData, } } @@ -63,7 +63,7 @@ where tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); ComponentAndEuidIter { - world: self.world, + world: self.inner.world(), iter: self.inner.iter(), comps_pd: PhantomData, } @@ -85,8 +85,8 @@ where tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); Iter { - world: self.world, - iter: func(self.inner.iter()), + world: self.inner.world(), + inner: func(self.inner.iter()), comps_pd: PhantomData, } } @@ -98,6 +98,25 @@ where Some(self.inner.iter().nth(entity_index)?.uid()) } + /// Returns a new `Query` created from a [`FlexibleQuery`]. + /// + /// # Important notes + /// The terms in `FieldTerms` and `FieldlessTerms` must be compatible with the terms + /// in the given [`FlexibleQuery`], otherwise any method call or iterating might + /// panic. + #[must_use] + pub fn from_flexible_query( + flexible_query: FlexibleQuery<'world, MAX_TERM_CNT>, + ) -> Self + { + // TODO: Check compatability of terms + + Self { + inner: flexible_query, + _pd: PhantomData, + } + } + pub(crate) fn new(world: &'world World) -> Self { let mut terms_builder = Terms::builder(); @@ -106,7 +125,6 @@ where FieldlessTerms::apply_terms_to_builder(&mut terms_builder); Self { - world, inner: world.flexible_query(terms_builder.build()), _pd: PhantomData, } @@ -136,17 +154,7 @@ where { type Input = (); - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self + fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { Self::new(world) } @@ -342,18 +350,26 @@ impl<ComponentT: Component> TermWithField for &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); - Self::Field::from_entity_component_ref( - entity_handle - .get_matching_components(ComponentT::id()) - .next(), - world, - ) - .unwrap_or_else(|err| { + let Some(component) = entity_handle + .get_matching_components(ComponentT::id()) + .next() + else { + panic!( + concat!( + "Component {} was not found in entity {}. There ", + "is most likely a bug in the entity querying" + ), + type_name::<ComponentT>(), + entity_handle.uid() + ); + }; + + Self::Field::from_entity_component_ref(&component).unwrap_or_else(|err| { panic!( "Creating handle to component {} failed: {err}", type_name::<ComponentT>() @@ -380,13 +396,21 @@ impl<ComponentT: Component> TermWithField for &mut ComponentT { assert_eq!(ComponentT::id().kind(), UidKind::Component); - Self::Field::from_entity_component_ref( - entity_handle - .get_matching_components(ComponentT::id()) - .next(), - world, - ) - .unwrap_or_else(|err| { + let Some(component) = entity_handle + .get_matching_components(ComponentT::id()) + .next() + else { + panic!( + concat!( + "Component {} was not found in entity {}. There ", + "is most likely a bug in the entity querying" + ), + type_name::<ComponentT>(), + entity_handle.uid() + ); + }; + + Self::Field::from_entity_component_ref(&component, world).unwrap_or_else(|err| { panic!( "Creating handle to component {} failed: {err}", type_name::<ComponentT>() @@ -422,29 +446,10 @@ where EntityHandleIter: Iterator<Item = EntityHandle<'query>>, { world: &'world World, - iter: EntityHandleIter, + inner: EntityHandleIter, comps_pd: PhantomData<FieldTerms>, } -impl<'query, 'world, FieldTerms, EntityHandleIter> - Iter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, - 'world: 'query, -{ - /// Creates a new iterator from the given entity handle iterator. - /// - /// # Important - /// All of the yielded entities of the entity handle iterator should match the - /// terms `Terms`. The [`Self::next`] function will panic if it encounters a - /// entity that does not match the terms `Terms`. - pub fn new(world: &'world World, iter: EntityHandleIter) -> Self - { - Self { world, iter, comps_pd: PhantomData } - } -} - impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator for Iter<'query, 'world, FieldTerms, EntityHandleIter> where @@ -456,7 +461,7 @@ where fn next(&mut self) -> Option<Self::Item> { - let entity_handle = self.iter.next()?; + let entity_handle = self.inner.next()?; Some(FieldTerms::get_fields(&entity_handle, self.world)) } diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs index 6d65ee0..936ab82 100644 --- a/ecs/src/query/flexible.rs +++ b/ecs/src/query/flexible.rs @@ -22,7 +22,6 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> pub fn iter(&self) -> Iter<'_> { Iter { - world: self.world, iter: self .world .data @@ -37,9 +36,16 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> .zip(archetype.entities()) }) as ComponentIterMapFn, ), + world: self.world, } } + #[must_use] + pub fn world(&self) -> &'world World + { + self.world + } + pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self { Self { world, terms } @@ -59,8 +65,8 @@ impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_T pub struct Iter<'query> { - world: &'query World, iter: QueryEntityIter<'query>, + world: &'query World, } impl<'query> Iterator for Iter<'query> @@ -71,7 +77,7 @@ impl<'query> Iterator for Iter<'query> { let (archetype, entity) = self.iter.next()?; - Some(EntityHandle::new(self.world, archetype, entity)) + Some(EntityHandle::new(archetype, entity, self.world)) } } diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs index 2e1ecca..0683918 100644 --- a/ecs/src/query/term.rs +++ b/ecs/src/query/term.rs @@ -4,7 +4,6 @@ use std::marker::PhantomData; use crate::component::{ Component, Handle as ComponentHandle, - HandleFromEntityComponentRef, HandleMut as ComponentHandleMut, }; use crate::query::{ @@ -65,17 +64,14 @@ impl<ComponentT: Component> TermWithField for Option<&ComponentT> fn get_field<'world>( entity_handle: &crate::entity::Handle<'world>, - world: &'world crate::World, + _world: &'world crate::World, ) -> Self::Field<'world> { Some( ComponentHandle::<'world, ComponentT>::from_entity_component_ref( - Some( - entity_handle - .get_matching_components(ComponentT::id()) - .next()?, - ), - world, + &entity_handle + .get_matching_components(ComponentT::id()) + .next()?, ) .unwrap_or_else(|err| { panic!( @@ -104,11 +100,9 @@ impl<ComponentT: Component> TermWithField for Option<&mut ComponentT> { Some( ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref( - Some( - entity_handle - .get_matching_components(ComponentT::id()) - .next()?, - ), + &entity_handle + .get_matching_components(ComponentT::id()) + .next()?, world, ) .unwrap_or_else(|err| { diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs index 1cce419..7cfcc24 100644 --- a/ecs/src/sole.rs +++ b/ecs/src/sole.rs @@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Weak}; use crate::lock::{Lock, WriteGuard}; -use crate::system::{Param as SystemParam, System}; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::World; /// A type which has a single instance and is shared globally. @@ -64,7 +64,7 @@ where } } - fn new(sole: &'world Arc<Lock<Box<dyn Sole>>>) -> Self + pub(crate) fn new(sole: &'world Arc<Lock<Box<dyn Sole>>>) -> Self { Self { sole: sole.write_nonblock().unwrap_or_else(|_| { @@ -85,17 +85,7 @@ where { type Input = (); - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self + fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { let sole = world.data.sole_storage.get::<SoleT>().unwrap_or_else(|| { panic!("Sole {} was not found in world", type_name::<SoleT>()) diff --git a/ecs/src/system.rs b/ecs/src/system.rs index 603c015..95ab7a8 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -1,37 +1,28 @@ -use std::any::Any; -use std::convert::Infallible; use std::fmt::Debug; use ecs_macros::Component; use seq_macro::seq; -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::tuple::{ReduceElement as TupleReduceElement, Tuple}; +use crate::uid::Uid; use crate::World; +pub mod initializable; +pub mod observer; pub mod stateful; -pub trait System<'world, Impl>: 'static +/// Metadata of a system. +#[derive(Debug)] +#[non_exhaustive] +pub struct Metadata { - type Input; - - #[must_use] - fn initialize(self, input: Self::Input) -> Self; - - fn run<'this>(&'this self, world: &'world World) - where - 'this: 'world; - - fn into_type_erased(self) -> TypeErased; + pub ent_id: Uid, +} - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<LocalComponent>>; +pub trait System<'world, Impl>: 'static +{ + type Callbacks: Callbacks; - fn set_local_component<LocalComponent: Component>( - &mut self, - local_component: LocalComponent, - ); + fn finish(self) -> (TypeErased, Self::Callbacks); } macro_rules! impl_system { @@ -43,58 +34,23 @@ macro_rules! impl_system { Func: Fn(#(TParam~I,)*) + Copy + 'static, #(TParam~I: Param<'world, Input = ()>,)* { - type Input = Infallible; - - fn initialize(self, _input: Self::Input) -> Self - { - self - } + type Callbacks = NoCallbacks; - fn run<'this>(&'this self, world: &'world World) - where - 'this: 'world + fn finish(self) -> (TypeErased, Self::Callbacks) { - let func = *self; - - func(#({ - TParam~I::new(self, world) - },)*); - } - - fn into_type_erased(self) -> TypeErased - { - TypeErased { - data: Box::new(self), - run: Box::new(|data, world| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let data = unsafe { &*std::ptr::from_ref(data) }; - - let me = data - .downcast_ref::<Func>() - .expect("Function downcast failed"); - + let type_erased = TypeErased { + run: Box::new(move |world, metadata| { // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let world = unsafe { &*std::ptr::from_ref(world) }; - me.run(world); + self(#({ + TParam~I::new(world, &metadata) + },)*); }), - } - } + }; - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<LocalComponent>> - { - panic!("System does not have any local components"); - } - - fn set_local_component<LocalComponent: Component>( - &mut self, - _local_component: LocalComponent, - ) { - panic!("System does not have any local components"); + (type_erased, NoCallbacks) } } }); @@ -105,7 +61,7 @@ seq!(C in 1..16 { impl_system!(C); }); -pub trait Into<Impl> +pub trait Into<'world, Impl> { type System; @@ -114,7 +70,6 @@ pub trait Into<Impl> pub struct TypeErased { - data: Box<dyn Any>, run: Box<TypeErasedRunFn>, } @@ -124,12 +79,9 @@ impl TypeErased /// /// # Safety /// `world_data` must live at least as long as the [`World`] the system belongs to. - pub unsafe fn run(&self, world: &World) + pub unsafe fn run(&self, world: &World, metadata: Metadata) { - // You have to dereference for downcasting to work for some reason - let data = &*self.data; - - (self.run)(data, world); + (self.run)(world, metadata); } } @@ -142,41 +94,29 @@ impl Debug for TypeErased } /// Function in [`TypeErased`] used to run the system. -type TypeErasedRunFn = dyn Fn(&dyn Any, &World); +type TypeErasedRunFn = dyn Fn(&World, Metadata); /// A parameter to a [`System`]. pub trait Param<'world> { type Input; - fn initialize<SystemImpl>( - system: &mut impl System<'world, SystemImpl>, - input: Self::Input, - ); - - fn new<SystemImpl>( - system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self; + fn new(world: &'world World, system_metadata: &Metadata) -> Self; } /// A type which can be used as input to a [`System`]. pub trait Input: 'static {} -/// Component tuple reducing operation to get the parameters that takes input. -pub struct ParamWithInputFilter; - -impl<InputT: Input, Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> - for InputT -where - Accumulator: Tuple, +pub trait Callbacks { - type Return = Accumulator::WithElementAtEnd<Self>; + fn on_created(&mut self, world: &mut World, metadata: Metadata); } -impl<Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for () +pub struct NoCallbacks; + +impl Callbacks for NoCallbacks { - type Return = Accumulator; + fn on_created(&mut self, _world: &mut World, _metadata: Metadata) {} } #[derive(Debug, Component)] diff --git a/ecs/src/system/initializable.rs b/ecs/src/system/initializable.rs new file mode 100644 index 0000000..b6ec8e8 --- /dev/null +++ b/ecs/src/system/initializable.rs @@ -0,0 +1,131 @@ +use std::marker::PhantomData; + +use seq_macro::seq; + +use crate::system::{Input, Param as SystemParam, System}; +use crate::tuple::{Reduce as TupleReduce, ReduceElement as TupleReduceElement, Tuple}; + +/// A initializable system. +pub trait Initializable<'world, Impl>: System<'world, Impl> +{ + type Inputs; + + #[must_use] + fn initialize(self, inputs: Self::Inputs) -> Self; +} + +pub trait Param<'world, SystemT>: SystemParam<'world> +{ + fn initialize(system: &mut SystemT, input: Self::Input); +} + +pub struct ParamTupleFilter<'world, SystemT> +{ + _pd: PhantomData<(&'world (), SystemT)>, +} + +impl<'world, SystemT, ParamT, Accumulator> + TupleReduceElement<Accumulator, ParamTupleFilter<'world, SystemT>> for ParamT +where + ParamT: SystemParam< + 'world, + Input: AppendInitializableParam<'world, Accumulator, ParamT, SystemT>, + >, + Accumulator: Tuple, +{ + type Return = <ParamT::Input as AppendInitializableParam< + 'world, + Accumulator, + ParamT, + SystemT, + >>::Return; +} + +pub trait AppendInitializableParam<'world, Accumulator, ParamT, SystemT> +{ + type Return; +} + +impl<'world, InputT, ParamT, Accumulator, SystemT> + AppendInitializableParam<'world, Accumulator, ParamT, SystemT> for InputT +where + InputT: Input, + Accumulator: Tuple, + ParamT: Param<'world, SystemT>, +{ + type Return = Accumulator::WithElementAtEnd<ParamT>; +} + +impl<ParamT, Accumulator, SystemT> + AppendInitializableParam<'_, Accumulator, ParamT, SystemT> for () +where + Accumulator: Tuple, +{ + type Return = Accumulator; +} + +pub trait ParamTuple<'world, SystemT> +{ + type Inputs; + + fn initialize_all(system: &mut SystemT, inputs: Self::Inputs); +} + +macro_rules! impl_initializable_param_tuple { + ($c: tt) => { + seq!(I in 0..$c { + impl<'world, SystemT, #(Param~I,)*> ParamTuple<'world, SystemT> + for (#(Param~I,)*) + where + #(Param~I: Param<'world, SystemT>,)* + { + type Inputs = (#(Param~I::Input,)*); + + fn initialize_all( + system: &mut SystemT, + inputs: Self::Inputs, + ) { + #( + <Param~I as Param<'world, SystemT>>::initialize( + system, + inputs.I + ); + )* + } + } + }); + }; +} + +seq!(C in 1..16 { + impl_initializable_param_tuple!(C); +}); + +impl<SystemT> ParamTuple<'_, SystemT> for () +{ + type Inputs = (); + + fn initialize_all(_system: &mut SystemT, _inputs: Self::Inputs) {} +} + +/// A tuple of system parameters that may or may not be initializable. +pub trait MaybeInitializableParamTuple<'world, SystemT> +{ + /// A tuple of the inputs of the initializable system parameters in this tuple. + type Inputs; + + fn init_initializable(system: &mut SystemT, inputs: Self::Inputs); +} + +impl<'world, SystemT, Params> MaybeInitializableParamTuple<'world, SystemT> for Params +where + Params: + TupleReduce<ParamTupleFilter<'world, SystemT>, Out: ParamTuple<'world, SystemT>>, +{ + type Inputs = <Params::Out as ParamTuple<'world, SystemT>>::Inputs; + + fn init_initializable(system: &mut SystemT, inputs: Self::Inputs) + { + Params::Out::initialize_all(system, inputs); + } +} 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 54f6979..e74ef31 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,152 +1,243 @@ -use std::any::{type_name, Any, TypeId}; +use std::mem::transmute; use std::panic::{RefUnwindSafe, UnwindSafe}; -use hashbrown::HashMap; use seq_macro::seq; -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::lock::Lock; -use crate::system::{ - Into as IntoSystem, - Param, - ParamWithInputFilter, - System, - TypeErased, +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::tuple::{ - Reduce as TupleReduce, - Tuple, - WithAllElemLtStatic as TupleWithAllElemLtStatic, -}; -use crate::uid::Uid; +use crate::system::{Into as IntoSystem, Metadata, Param, System, TypeErased}; use crate::World; /// A stateful system. pub struct Stateful<Func> { func: Func, - local_components: HashMap<Uid, Lock<Box<dyn Any>>>, + local_components: Vec<ComponentParts>, } macro_rules! impl_system { ($c: tt) => { seq!(I in 0..$c { - impl<'world, Func, #(TParam~I,)*> System<'world, fn(&'world (), #(TParam~I,)*)> - for Stateful<Func> + impl<'world, Func, #(TParam~I,)*> + System<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func> where Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, - #(TParam~I: Param<'world>,)* - #(TParam~I::Input: 'static,)* - (#(TParam~I::Input,)*): TupleReduce< - ParamWithInputFilter, - Out: Tuple<InOptions: TupleWithAllElemLtStatic> - >, + #(TParam~I: Param<'world, Input: 'static>,)* { - type Input = - <(#(TParam~I::Input,)*) as TupleReduce<ParamWithInputFilter>>::Out; + type Callbacks = Callbacks; - fn initialize(mut self, input: Self::Input) -> Self + fn finish(self) -> (TypeErased, Self::Callbacks) { - let mut option_input = input.into_in_options(); - - let mut index = 0; - - #( - if TypeId::of::<TParam~I::Input>() != - TypeId::of::<()>() - { - let input = option_input - .get_mut::<Option<TParam~I::Input>>(index) - .expect("Input element index out of range") - .take() - .expect("Input element is already taken"); - - TParam~I::initialize( - &mut self, - input - ); - - #[allow(unused_assignments)] - { - index += 1; - } - } - )* + let Self { func, local_components } = self; - self + let callbacks = Callbacks { local_components }; + + let type_erased = TypeErased { + run: Box::new(move |world, metadata| { + // SAFETY: The caller of TypeErased::run ensures the lifetime + // is correct + let world = unsafe { &*std::ptr::from_ref(world) }; + + func(#({ + TParam~I::new(&world, &metadata) + },)*); + }), + }; + + + (type_erased, callbacks) } + } - fn run<'this>(&'this self, world: &'world World) - where - 'this: 'world + impl<'world, Func, #(TParam~I,)*> + Initializable<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func> + where + Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, + #(TParam~I: Param<'world, Input: 'static>,)* + (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> + { + type Inputs = < + (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self> + >::Inputs; + + fn initialize(mut self, inputs: Self::Inputs) -> Self { - let func = self.func; + init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs); - func(#({ - TParam~I::new(self, &world) - },)*); + self } + } - fn into_type_erased(self) -> TypeErased + 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>; + + fn into_system(self) -> Self::System { - TypeErased { - data: Box::new(self), - run: Box::new(|data, world| { - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let data = unsafe { &*std::ptr::from_ref::<dyn Any>(data) }; + Self::System { + func: self, + local_components: Vec::new(), // TODO: Use Vec::with_capacity + } + } + } + }); + }; +} - let me = data.downcast_ref::<Self>().unwrap(); +seq!(C in 1..16 { + impl_system!(C); +}); - // SAFETY: The caller of TypeErased::run ensures the lifetime - // is correct - let world = unsafe { &*std::ptr::from_ref(world) }; +impl<Func> SystemWithLocalComponents for Stateful<Func> +{ + fn add_local_component(&mut self, component_parts: ComponentParts) + { + self.local_components.push(component_parts); + } +} - me.run(world); - }), - } +#[derive(Debug)] +pub struct Callbacks +{ + local_components: Vec<ComponentParts>, +} + +impl crate::system::Callbacks for Callbacks +{ + fn on_created(&mut self, world: &mut World, metadata: Metadata) + { + for local_comp_parts in self.local_components.drain(..) { + world.add_component(metadata.ent_id, local_comp_parts); + } + } +} + +fn init_initializable_params<'world, SystemT, Params>( + system: &mut SystemT, + inputs: Params::Inputs, +) where + Params: MaybeInitializableParamTuple<'world, SystemT>, +{ + 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!(); } + } - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<LocalComponent>> + 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 { - let local_component = self.local_components - .get(&LocalComponent::id())? - .write_nonblock() - .expect("Failed to aquire read-write local component lock"); + init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs); - Some(ComponentHandleMut::new(local_component)) + 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 set_local_component<LocalComponent: Component>( - &mut self, - local_component: LocalComponent, - ) + fn observed_events() -> Self::ObservedEvents { - self.local_components - .insert( - LocalComponent::id(), - Lock::new( - Box::new(local_component), - type_name::<LocalComponent>() - ) - ); + 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<Func, #(TParam~I,)*> IntoSystem<fn(#(TParam~I,)*)> - for Func + impl<'world, Func, ObservedT, #(TParam~I,)*> IntoSystem< + 'world, + fn(Observe<'world, ObservedT>, + #(TParam~I,)*) + > for Func where - Func: Fn(#(TParam~I,)*) + Copy + 'static, + ObservedT: Observed, + #(TParam~I: Param<'world>,)* + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, { type System = Stateful<Func>; - fn into_system(self) -> Self::System + fn into_system(self) -> Stateful<Func> { - Self::System { + Stateful { func: self, - local_components: HashMap::new(), + local_components: Vec::new(), // TODO: Use Vec::with_capacity } } } @@ -155,5 +246,5 @@ macro_rules! impl_system { } seq!(C in 1..16 { - impl_system!(C); + impl_observer!(C); }); diff --git a/ecs/src/uid.rs b/ecs/src/uid.rs index feed62c..a361882 100644 --- a/ecs/src/uid.rs +++ b/ecs/src/uid.rs @@ -218,7 +218,7 @@ pub struct PairParams pub target: Uid, } -pub trait With: 'static +pub trait With { fn uid() -> Uid; } @@ -230,14 +230,3 @@ impl<ComponentT: Component> With for ComponentT Self::id() } } - -#[derive(Debug)] -pub enum Wildcard {} - -impl With for Wildcard -{ - fn uid() -> Uid - { - Uid::wildcard() - } -} diff --git a/ecs/src/util/array_vec.rs b/ecs/src/util/array_vec.rs index a37b1f9..5d0aac9 100644 --- a/ecs/src/util/array_vec.rs +++ b/ecs/src/util/array_vec.rs @@ -45,8 +45,8 @@ impl<Item, const CAPACITY: usize> ArrayVec<Item, CAPACITY> unsafe { std::ptr::copy( - &self.items[index], - &mut self.items[index + 1], + &raw const self.items[index], + &raw mut self.items[index + 1], self.len - index, ); } diff --git a/ecs/tests/query.rs b/ecs/tests/query.rs index 062fd5a..8615e3a 100644 --- a/ecs/tests/query.rs +++ b/ecs/tests/query.rs @@ -1,4 +1,5 @@ use ecs::component::Component; +use ecs::pair::{Pair, Wildcard}; use ecs::query::term::Without; use ecs::query::{ TermWithFieldTuple as QueryTermWithFieldTuple, @@ -320,3 +321,59 @@ fn query_without_comp_and_archetype_nonexistant() vec![ent_1_id, ent_2_id], ); } + +#[test] +fn query_with_wildcard_target_pair() +{ + setup(); + + let _test_lock = TEST_LOCK.lock(); + + let mut world = World::new(); + + let ent_1_id = world.create_entity((A, C)); + + world.create_entity((B,)); + + let ent_2_id = world.create_entity((B, Pair::new::<G>(ent_1_id))); + + world.create_entity((B, Pair::new::<F>(ent_1_id))); + world.create_entity((B, A, C, Pair::new::<F>(ent_1_id))); + + let ent_3_id = world.create_entity((B, Pair::new::<G>(ent_2_id))); + + let ent_4_id = world.create_entity((B, E, Pair::new_with_comp_target::<G>(D))); + + assert_query_finds_ents( + world.query::<(&B, Pair<G, Wildcard>), ()>(), + vec![ent_2_id, ent_3_id, ent_4_id], + ); +} + +#[test] +fn query_with_component_target_pair() +{ + setup(); + + let _test_lock = TEST_LOCK.lock(); + + let mut world = World::new(); + + let ent_1_id = world.create_entity((A, C)); + + world.create_entity((B,)); + + world.create_entity((B, Pair::new::<G>(ent_1_id))); + + world.create_entity((B, Pair::new::<F>(ent_1_id))); + world.create_entity((B, A, C, Pair::new::<F>(ent_1_id))); + + let ent_2_id = world.create_entity((B, Pair::new_with_comp_target::<G>(F))); + + let ent_3_id = world.create_entity((B, E, Pair::new_with_comp_target::<G>(F))); + + assert_query_finds_ents( + world.query::<(&B, Pair<G, &F>), ()>(), + vec![ent_2_id, ent_3_id], + ); +} |