Skip to content

Commit

Permalink
Adding iter_events and clear_events functions
Browse files Browse the repository at this point in the history
  • Loading branch information
recatek committed Jan 13, 2025
1 parent 518f0b7 commit ffbbc7d
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 12 deletions.
44 changes: 32 additions & 12 deletions macros/src/generate/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ pub fn generate_world(world_data: &DataWorld, raw_input: &str) -> TokenStream {
.collect::<Vec<_>>();
let num_archetypes = world_data.archetypes.len();

// Functions
let drain_events = world_drain_events(world_data);

// Generated subsections
let section_archetype = world_data
.archetypes
Expand All @@ -66,6 +63,7 @@ pub fn generate_world(world_data: &DataWorld, raw_input: &str) -> TokenStream {
.iter()
.map(|archetype| with_capacity_new(archetype))
.collect::<Vec<_>>();
let section_events = section_events_world(world_data);

// Documentation helpers
#[rustfmt::skip]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -613,8 +611,8 @@ fn section_archetype(archetype_data: &DataArchetype) -> TokenStream {
.map(|component| format_ident!("{}", to_snake(&component.name)))
.collect::<Vec<_>>();

// Functions
let drain_events = archetype_drain_events();
// Generated subsections
let section_events = section_events_archetype();

// Documentation helpers
let archetype_doc_component_types = archetype_data
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -1082,31 +1080,53 @@ 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<Item = &EcsEvent> {
#iter_body
}

#[inline(always)]
fn drain_events(&mut self) -> impl Iterator<Item = EcsEvent> {
#body
#drain_body
}

#[inline(always)]
fn clear_events(&mut self) {
#(self.#archetype_fields.clear_events();)*
}
)
} else {
quote!()
}
}

fn archetype_drain_events() -> TokenStream {
fn section_events_archetype() -> TokenStream {
if cfg!(feature = "events") {
quote!(
#[inline(always)]
fn iter_events(&self) -> impl Iterator<Item = &EcsEvent> {
self.data.iter_events()
}

#[inline(always)]
fn drain_events(&mut self) -> impl Iterator<Item = EcsEvent> {
self.data.drain_events()
}

#[inline(always)]
fn clear_events(&mut self) {
self.data.clear_events()
}
)
} else {
quote!()
Expand Down
14 changes: 14 additions & 0 deletions src/archetype/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,13 +312,27 @@ 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<Item = &EcsEvent> + '_ {
self.events.iter()
}

/// Drains the active event queue of its entity create/destroy events.
#[cfg(feature = "events")]
#[inline(always)]
pub fn drain_events(&mut self) -> impl Iterator<Item = EcsEvent> + '_ {
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)]
Expand Down
113 changes: 113 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<ArchFoo>((CompA,)); // Create in first archetype
/// let entity_b = world.create::<ArchBar>((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::<Vec<_>>();
///
/// // 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::<Vec<_>>();
/// assert!(events.is_empty() == false);
/// }
/// ```
#[cfg(feature = "events")]
fn iter_events(&self) -> impl Iterator<Item = &EcsEvent>;

/// 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
Expand Down Expand Up @@ -111,6 +160,10 @@ pub trait World: Sized {
#[cfg(feature = "events")]
fn drain_events(&mut self) -> impl Iterator<Item = EcsEvent>;

/// 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.
///
Expand Down Expand Up @@ -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::<ArchFoo>((CompA,)); // Create in first archetype
/// let entity_b = world.create::<ArchBar>((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::<Vec<_>>();
/// let events_b = world.arch_bar.iter_events().collect::<Vec<_>>();
///
/// // 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::<Vec<_>>();
/// let events_b = world.arch_bar.iter_events().collect::<Vec<_>>();
/// assert!(events_a.is_empty() == false);
/// assert!(events_b.is_empty() == false);
/// }
/// ```
#[cfg(feature = "events")]
fn iter_events(&self) -> impl Iterator<Item = &EcsEvent>;

/// 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.
Expand Down Expand Up @@ -327,6 +436,10 @@ where
#[cfg(feature = "events")]
fn drain_events(&mut self) -> impl Iterator<Item = EcsEvent>;

/// 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.
///
Expand Down

0 comments on commit ffbbc7d

Please sign in to comment.