diff --git a/macros/src/generate/world.rs b/macros/src/generate/world.rs index 371f18e..dc69b0a 100644 --- a/macros/src/generate/world.rs +++ b/macros/src/generate/world.rs @@ -47,9 +47,6 @@ pub fn generate_world(world_data: &DataWorld, raw_input: &str) -> TokenStream { .collect::>(); let num_archetypes = world_data.archetypes.len(); - // Functions - let drain_events = world_drain_events(world_data); - // Generated subsections let section_archetype = world_data .archetypes @@ -66,6 +63,7 @@ pub fn generate_world(world_data: &DataWorld, raw_input: &str) -> TokenStream { .iter() .map(|archetype| with_capacity_new(archetype)) .collect::>(); + let section_events = section_events_world(world_data); // Documentation helpers #[rustfmt::skip] @@ -138,7 +136,7 @@ pub fn generate_world(world_data: &DataWorld, raw_input: &str) -> TokenStream { type Capacities = #WorldCapacity; // Will only appear if we have the events feature enabled. - #drain_events + #section_events #[inline(always)] fn new() -> Self { @@ -613,8 +611,8 @@ fn section_archetype(archetype_data: &DataArchetype) -> TokenStream { .map(|component| format_ident!("{}", to_snake(&component.name))) .collect::>(); - // Functions - let drain_events = archetype_drain_events(); + // Generated subsections + let section_events = section_events_archetype(); // Documentation helpers let archetype_doc_component_types = archetype_data @@ -655,7 +653,7 @@ fn section_archetype(archetype_data: &DataArchetype) -> TokenStream { type IterMutArgs<'a> = #IterMutArgs; // Will only appear if we have the events feature enabled. - #drain_events + #section_events #[inline(always)] fn new() -> Self { @@ -1071,7 +1069,7 @@ fn with_capacity_new(archetype_data: &DataArchetype) -> TokenStream { } #[allow(non_snake_case)] -fn world_drain_events(world_data: &DataWorld) -> TokenStream { +fn section_events_world(world_data: &DataWorld) -> TokenStream { if cfg!(feature = "events") { let archetype_fields = world_data .archetypes @@ -1082,17 +1080,29 @@ fn world_drain_events(world_data: &DataWorld) -> TokenStream { // We throw a compile error if we don't have any archetypes, so we must have at least one. let archetype = &archetype_fields[0]; - let mut body = quote!(self.#archetype.drain_events()); + let mut iter_body = quote!(self.#archetype.iter_events()); + let mut drain_body = quote!(self.#archetype.drain_events()); // Keep nesting the chain operations for archetype in archetype_fields[1..].iter() { - body = quote!(self.#archetype.drain_events().chain(#body)); + iter_body = quote!(self.#archetype.iter_events().chain(#iter_body)); + drain_body = quote!(self.#archetype.drain_events().chain(#drain_body)); } quote!( + #[inline(always)] + fn iter_events(&self) -> impl Iterator { + #iter_body + } + #[inline(always)] fn drain_events(&mut self) -> impl Iterator { - #body + #drain_body + } + + #[inline(always)] + fn clear_events(&mut self) { + #(self.#archetype_fields.clear_events();)* } ) } else { @@ -1100,13 +1110,23 @@ fn world_drain_events(world_data: &DataWorld) -> TokenStream { } } -fn archetype_drain_events() -> TokenStream { +fn section_events_archetype() -> TokenStream { if cfg!(feature = "events") { quote!( + #[inline(always)] + fn iter_events(&self) -> impl Iterator { + self.data.iter_events() + } + #[inline(always)] fn drain_events(&mut self) -> impl Iterator { self.data.drain_events() } + + #[inline(always)] + fn clear_events(&mut self) { + self.data.clear_events() + } ) } else { quote!() diff --git a/src/archetype/storage.rs b/src/archetype/storage.rs index 22a3f02..1f01f0d 100644 --- a/src/archetype/storage.rs +++ b/src/archetype/storage.rs @@ -312,6 +312,13 @@ macro_rules! declare_storage_dynamic_n { } )* + /// Iterates over all of the entity create/destroy events for this storage. + #[cfg(feature = "events")] + #[inline(always)] + pub fn iter_events(&self) -> impl Iterator + '_ { + self.events.iter() + } + /// Drains the active event queue of its entity create/destroy events. #[cfg(feature = "events")] #[inline(always)] @@ -319,6 +326,13 @@ macro_rules! declare_storage_dynamic_n { self.events.drain(..) } + /// Clears the current event queue of all entity create/destroy events. + #[cfg(feature = "events")] + #[inline(always)] + pub fn clear_events(&mut self) { + self.events.clear() + } + /// Resolves the slot index and data index for a given entity. /// Both indices are guaranteed to point to valid corresponding cells. #[inline(always)] diff --git a/src/traits.rs b/src/traits.rs index e76bf28..b96dabf 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -62,6 +62,55 @@ pub trait World: Sized { /// ``` fn with_capacity(capacity: Self::Capacities) -> Self; + /// Drains and returns all the [`EcsEvent`]s not yet collected for this world. + /// These events will be in order as they occurred to each archetype, and each archetype's + /// events will be ordered as they appear in the [`ecs_world`](gecs::ecs_world) macro that + /// created that ECS world. However, there is no ordering of events between archetypes. + /// + /// # Examples + /// + /// ```rust + /// use gecs::prelude::*; + /// + /// pub struct CompA; + /// + /// ecs_world! { + /// ecs_archetype!(ArchFoo, CompA); + /// ecs_archetype!(ArchBar, CompA); + /// } + /// + /// fn main() { + /// let mut world = EcsWorld::default(); + /// + /// let entity_a = world.create::((CompA,)); // Create in first archetype + /// let entity_b = world.create::((CompA,)); // Create in second archetype + /// world.destroy(entity_a); // Destroy in first archetype + /// world.destroy(entity_b); // Destroy in second archetype + /// + /// // Collect the events for all archetypes + /// let events = world.iter_events().collect::>(); + /// + /// // Observe the order here! Events are grouped by archetype first, and then by + /// // order within that archetype -- there is no cross-archetype ordering awareness. + /// // Note also that we use entity.into() because the events contain EntityAny handles. + /// assert_eq!( + /// events, + /// vec![ + /// &EcsEvent::Created(entity_a.into()), // Create in first archetype + /// &EcsEvent::Destroyed(entity_a.into()), // Destroy in first archetype + /// &EcsEvent::Created(entity_b.into()), // Create in second archetype + /// &EcsEvent::Destroyed(entity_b.into()), // Destroy in second archetype + /// ] + /// ); + /// + /// // The events have not been consumed (we're just iterating them) + /// let events = world.iter_events().collect::>(); + /// assert!(events.is_empty() == false); + /// } + /// ``` + #[cfg(feature = "events")] + fn iter_events(&self) -> impl Iterator; + /// Drains and returns all the [`EcsEvent`]s not yet collected for this world. /// These events will be in order as they occurred to each archetype, and each archetype's /// events will be ordered as they appear in the [`ecs_world`](gecs::ecs_world) macro that @@ -111,6 +160,10 @@ pub trait World: Sized { #[cfg(feature = "events")] fn drain_events(&mut self) -> impl Iterator; + /// Clears the currently stored [`EcsEvent`]s in all archetypes in this world. + #[cfg(feature = "events")] + fn clear_events(&mut self); + /// Creates a new entity with the given components to this archetype storage. /// Returns a typed entity handle pointing to the new entity in the archetype. /// @@ -272,6 +325,62 @@ where /// Returns the generational version of the archetype. Intended for internal use. fn version(&self) -> ArchetypeVersion; + /// Iterates over all of the [`EcsEvent`]s not yet cleared or drained for this archetype. + /// These events will be in order as they occurred to this archetype, but have no + /// ordering awareness of when events happened in other archetypes in the parent world. + /// + /// # Examples + /// + /// ```rust + /// use gecs::prelude::*; + /// + /// pub struct CompA; + /// + /// ecs_world! { + /// ecs_archetype!(ArchFoo, CompA); + /// ecs_archetype!(ArchBar, CompA); + /// } + /// + /// fn main() { + /// let mut world = EcsWorld::default(); + /// + /// let entity_a = world.create::((CompA,)); // Create in first archetype + /// let entity_b = world.create::((CompA,)); // Create in second archetype + /// world.destroy(entity_a); // Destroy in first archetype + /// world.destroy(entity_b); // Destroy in second archetype + /// + /// // Collect the events for each archetype individually + /// let events_a = world.arch_foo.iter_events().collect::>(); + /// let events_b = world.arch_bar.iter_events().collect::>(); + /// + /// // Note that we use entity.into() because the events contain EntityAny handles. + /// assert_eq!( + /// events_a, + /// vec![ + /// &EcsEvent::Created(entity_a.into()), // Create in first archetype + /// &EcsEvent::Destroyed(entity_a.into()), // Destroy in first archetype + /// ] + /// ); + /// + /// assert_eq!( + /// events_b, + /// vec![ + /// &EcsEvent::Created(entity_b.into()), // Create in second archetype + /// &EcsEvent::Destroyed(entity_b.into()), // Destroy in second archetype + /// ] + /// ); + /// + /// + /// // The events have not been consumed (we're just iterating them) + /// let events_a = world.arch_foo.iter_events().collect::>(); + /// let events_b = world.arch_bar.iter_events().collect::>(); + /// assert!(events_a.is_empty() == false); + /// assert!(events_b.is_empty() == false); + /// } + /// ``` + #[cfg(feature = "events")] + fn iter_events(&self) -> impl Iterator; + /// Drains and returns all the [`EcsEvent`]s not yet collected for this archetype. /// These events will be in order as they occurred to this archetype, but have no /// ordering awareness of when events happened in other archetypes in the parent world. @@ -327,6 +436,10 @@ where #[cfg(feature = "events")] fn drain_events(&mut self) -> impl Iterator; + /// Clears the currently stored [`EcsEvent`]s in this archetype. + #[cfg(feature = "events")] + fn clear_events(&mut self); + /// Creates a new entity with the given components to this archetype storage. /// Returns a typed entity handle pointing to the new entity in the archetype. ///