From 07aa59a122cc5e14d2fb2e2c6e3d8f82e4397bde Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 14 Aug 2024 20:05:30 +0200 Subject: feat(ecs): add component added event --- ecs/src/actions.rs | 15 +++--- ecs/src/event.rs | 35 ++++++++++--- ecs/src/event/component.rs | 75 +++++++++++++++++++++++++++ ecs/src/extension.rs | 7 ++- ecs/src/lib.rs | 124 +++++++++++++++++++++++++++++++++++++-------- ecs/src/type_name.rs | 10 ++++ 6 files changed, 229 insertions(+), 37 deletions(-) create mode 100644 ecs/src/event/component.rs (limited to 'ecs') diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs index 7698b45..32fcfb9 100644 --- a/ecs/src/actions.rs +++ b/ecs/src/actions.rs @@ -8,7 +8,6 @@ use crate::component::{ Sequence as ComponentSequence, }; use crate::entity::Uid as EntityUid; -use crate::lock::{Lock, WriteGuard}; use crate::system::{NoInitParamFlag, Param as SystemParam, System}; use crate::{ActionQueue, World}; @@ -16,8 +15,8 @@ use crate::{ActionQueue, World}; #[derive(Debug)] pub struct Actions<'world> { - action_queue: WriteGuard<'world, ActionQueue>, - action_queue_weak: Weak>, + action_queue: &'world ActionQueue, + action_queue_weak: Weak, } impl<'world> Actions<'world> @@ -64,12 +63,10 @@ impl<'world> Actions<'world> } } - fn new(action_queue: &'world Arc>) -> Self + fn new(action_queue: &'world Arc) -> Self { Self { - action_queue: action_queue - .write_nonblock() - .expect("Failed to aquire read-write action queue lock"), + action_queue: &*action_queue, action_queue_weak: Arc::downgrade(action_queue), } } @@ -111,7 +108,7 @@ unsafe impl<'world> SystemParam<'world> for Actions<'world> #[derive(Debug, Clone)] pub struct WeakRef { - action_queue: Weak>, + action_queue: Weak, } impl WeakRef @@ -134,7 +131,7 @@ impl WeakRef #[derive(Debug, Clone)] pub struct Ref<'weak_ref> { - action_queue: Arc>, + action_queue: Arc, _pd: PhantomData<&'weak_ref ()>, } diff --git a/ecs/src/event.rs b/ecs/src/event.rs index 93d4545..0caa56f 100644 --- a/ecs/src/event.rs +++ b/ecs/src/event.rs @@ -1,10 +1,21 @@ use std::any::TypeId; use std::fmt::Debug; -use std::hash::Hash; +use std::hash::{DefaultHasher, Hash, Hasher}; use seq_macro::seq; -pub trait Event: Debug + 'static {} +pub mod component; + +pub trait Event: Debug + 'static +{ + /// Returns the ID of this event. + fn id() -> Id + where + Self: Sized, + { + Id::new::(None) + } +} pub mod start; @@ -13,15 +24,27 @@ pub mod start; pub struct Id { inner: TypeId, + extra: Option, } impl Id { - /// Returns the id of a [`Event`]; #[must_use] - pub fn of() -> Self + pub fn new(extra: Option) -> Self + where + EventT: Event, + Extra: Hash, { - Self { inner: TypeId::of::() } + Self { + inner: TypeId::of::(), + extra: extra.map(|extra| { + let mut hasher = DefaultHasher::new(); + + extra.hash(&mut hasher); + + hasher.finish() + }), + } } } @@ -59,7 +82,7 @@ macro_rules! impl_sequence { fn ids() -> Self::Ids { [#( - Id::of::(), + Event~I::id(), )*] } } diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs new file mode 100644 index 0000000..306d43f --- /dev/null +++ b/ecs/src/event/component.rs @@ -0,0 +1,75 @@ +//! Component events. + +use std::fmt::{Debug, Formatter}; +use std::marker::PhantomData; + +use ecs_macros::Component; + +use crate::component::{Component, Id as ComponentId}; +use crate::event::{Event, Id}; +use crate::tuple::{ReduceElement as TupleReduceElement, With as TupleWith}; + +/// Event emitted when: +/// a) A entity with component `ComponentT` is spawned. +/// b) A component `ComponentT` is added to a entity. +pub struct Added +where + ComponentT: Component, +{ + _pd: PhantomData, +} + +impl Debug for Added +where + ComponentT: Component, +{ + fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result + { + formatter + .debug_struct("Added") + .field("_pd", &self._pd) + .finish() + } +} + +impl Default for Added +where + ComponentT: Component, +{ + fn default() -> Self + { + Self { _pd: PhantomData } + } +} + +impl Event for Added +where + ComponentT: Component, +{ + fn id() -> Id + where + Self: Sized, + { + Id::new::, _>(Some(ComponentId::of::())) + } +} + +pub fn create_added_id(component_id: ComponentId) -> Id +{ + Id::new::, _>(Some(component_id)) +} + +pub struct ComponentToAddedEvent; + +impl + TupleReduceElement for ComponentT +where + Accumulator: TupleWith>, +{ + type Return = Accumulator::With; +} + +use crate as ecs; + +#[derive(Debug, Component)] +struct ComponentForId; diff --git a/ecs/src/extension.rs b/ecs/src/extension.rs index fc5a345..99320cb 100644 --- a/ecs/src/extension.rs +++ b/ecs/src/extension.rs @@ -1,7 +1,9 @@ use crate::component::Sequence as ComponentSequence; -use crate::event::Event; +use crate::event::component::ComponentToAddedEvent; +use crate::event::{Event, Sequence as EventSequence}; use crate::sole::Sole; use crate::system::System; +use crate::tuple::Reduce as TupleReduce; use crate::{SoleAlreadyExistsError, World}; /// A collection of systems, entities & soles that can be added to a [`World`]. @@ -38,7 +40,8 @@ impl<'world> Collector<'world> /// Adds a entity to the [`World`]. pub fn add_entity(&mut self, components: Comps) where - Comps: ComponentSequence, + Comps: ComponentSequence + TupleReduce, + Comps::Out: EventSequence, { self.world.create_entity(components); } diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 04c9b9f..ed2ccef 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,18 +1,21 @@ #![deny(clippy::all, clippy::pedantic)] use std::any::{type_name, TypeId}; +use std::cell::RefCell; use std::collections::HashMap; use std::fmt::Debug; use std::mem::ManuallyDrop; -use std::ops::RangeBounds; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use std::vec::Drain; use crate::actions::Action; use crate::component::storage::Storage as ComponentStorage; use crate::component::{Component, Id as ComponentId, Sequence as ComponentSequence}; use crate::entity::Uid as EntityUid; +use crate::event::component::{ + create_added_id as create_component_added_event_id, + ComponentToAddedEvent, +}; use crate::event::start::Start as StartEvent; use crate::event::{Event, Id as EventId, Ids, Sequence as EventSequence}; use crate::extension::{Collector as ExtensionCollector, Extension}; @@ -20,6 +23,7 @@ use crate::lock::Lock; use crate::query::options::Options as QueryOptions; use crate::sole::Sole; use crate::system::{System, TypeErased as TypeErasedSystem}; +use crate::tuple::Reduce as TupleReduce; use crate::type_name::TypeName; pub mod actions; @@ -64,7 +68,8 @@ impl World /// Will panic if mutable internal lock cannot be acquired. pub fn create_entity(&mut self, components: Comps) -> EntityUid where - Comps: ComponentSequence, + Comps: ComponentSequence + TupleReduce, + Comps::Out: EventSequence, { let (_, entity_uid) = self .data @@ -73,6 +78,10 @@ impl World .expect("Failed to acquire read-write component storage lock") .push_entity(EntityUid::new_unique(), components.into_vec()); + for component_added_event_id in ::ids().iter() { + self.emit_event_by_id(*component_added_event_id); + } + entity_uid } @@ -98,7 +107,7 @@ impl World self.data .events - .entry(EventId::of::()) + .entry(EventT::id()) .or_default() .push(self.systems.len() - 1); @@ -125,7 +134,7 @@ impl World where EventT: Event, { - self.emit_event_by_id(EventId::of::()); + self.emit_event_by_id(EventT::id()); drop(event); } @@ -144,13 +153,22 @@ impl World /// Will panic if a mutable internal lock cannot be acquired. pub fn perform_queued_actions(&self) { - for action in self - .data - .action_queue - .write_nonblock() - .expect("Failed to aquire read-write action queue lock") - .drain(..) + 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 mut has_swapped_active_queue = false; + + for action in active_action_queue.drain(..) { match action { Action::Spawn(components) => { let mut component_storage_lock = @@ -158,8 +176,33 @@ impl World "Failed to acquire read-write component storage lock", ); + let component_ids = components + .iter() + .map(|component| component.id()) + .collect::>(); + component_storage_lock .push_entity(EntityUid::new_unique(), components); + + drop(component_storage_lock); + + if !has_swapped_active_queue { + 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; + } + + for component_id in component_ids { + self.emit_event_by_id(create_component_added_event_id( + component_id, + )); + } } Action::AddComponents(entity_uid, components) => { let mut component_storage_lock = @@ -167,8 +210,33 @@ impl World "Failed to acquire read-write component storage lock", ); + let component_ids = components + .iter() + .map(|component| component.id()) + .collect::>(); + component_storage_lock .add_components_to_entity(entity_uid, components); + + drop(component_storage_lock); + + if !has_swapped_active_queue { + 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; + } + + for component_id in component_ids { + self.emit_event_by_id(create_component_added_event_id( + component_id, + )); + } } Action::RemoveComponents(entity_uid, component_ids) => { let mut component_storage_lock = @@ -237,7 +305,7 @@ pub struct WorldData events: HashMap>, component_storage: Arc>, sole_storage: SoleStorage, - action_queue: Arc>, + action_queue: Arc, } #[derive(Debug)] @@ -261,22 +329,38 @@ impl From> for EntityComponent } } +#[derive(Debug, Default, Clone, Copy)] +enum ActiveActionQueue +{ + #[default] + A, + B, +} + #[derive(Debug, Default)] struct ActionQueue { - queue: Vec, + queue_a: Lock>, + queue_b: Lock>, + active_queue: RefCell, } impl ActionQueue { - fn push(&mut self, action: Action) + fn push(&self, action: Action) { - self.queue.push(action); - } - - fn drain(&mut self, range: impl RangeBounds) -> Drain - { - self.queue.drain(range) + 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), + } } } diff --git a/ecs/src/type_name.rs b/ecs/src/type_name.rs index 5892c6f..54179be 100644 --- a/ecs/src/type_name.rs +++ b/ecs/src/type_name.rs @@ -1,5 +1,15 @@ +use std::any::type_name; + pub trait TypeName { /// Returns the name of this type. fn type_name(&self) -> &'static str; } + +impl TypeName for Vec +{ + fn type_name(&self) -> &'static str + { + type_name::() + } +} -- cgit v1.2.3-18-g5258