Skip to content

Commit

Permalink
Expose an EventId for events (#894)
Browse files Browse the repository at this point in the history
* Expose an EventId for events

This can be helpful for correlating the place where an event is created
to the place where the event is processed.
  • Loading branch information
bjorn3 authored Nov 21, 2020
1 parent 85ecab8 commit 0dbbcd9
Showing 1 changed file with 105 additions and 12 deletions.
117 changes: 105 additions & 12 deletions crates/bevy_app/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,44 @@
use bevy_ecs::ResMut;
use std::marker::PhantomData;
use bevy_utils::tracing::trace;
use std::{fmt, marker::PhantomData};

/// An `EventId` uniquely identifies an event.
///
/// An `EventId` can among other things be used to trace the flow of an event from the point it was
/// sent to the point it was processed.
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct EventId<T> {
pub id: usize,
_marker: PhantomData<T>,
}

impl<T> Copy for EventId<T> {}
impl<T> Clone for EventId<T> {
fn clone(&self) -> Self {
*self
}
}

impl<T> fmt::Display for EventId<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}

impl<T> fmt::Debug for EventId<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"event<{}>#{}",
std::any::type_name::<T>().split("::").last().unwrap(),
self.id,
)
}
}

#[derive(Debug)]
struct EventInstance<T> {
pub event_count: usize,
pub event_id: EventId<T>,
pub event: T,
}

Expand Down Expand Up @@ -78,6 +113,10 @@ impl<T> Default for Events<T> {
}
}

fn map_instance_event_with_id<T>(event_instance: &EventInstance<T>) -> (&T, EventId<T>) {
(&event_instance.event, event_instance.event_id)
}

fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
&event_instance.event
}
Expand All @@ -101,6 +140,25 @@ impl<T> EventReader<T> {
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened before now.
pub fn iter<'a>(&mut self, events: &'a Events<T>) -> impl DoubleEndedIterator<Item = &'a T> {
self.iter_with_id(events).map(|(event, _id)| event)
}

/// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events.
pub fn iter_with_id<'a>(
&mut self,
events: &'a Events<T>,
) -> impl DoubleEndedIterator<Item = (&'a T, EventId<T>)> {
self.iter_internal(events).map(|(event, id)| {
trace!("EventReader::iter() -> {}", id);
(event, id)
})
}

/// Like [`iter_with_id`](Self::iter_with_id) except not emitting any traces for read messages.
fn iter_internal<'a>(
&mut self,
events: &'a Events<T>,
) -> impl DoubleEndedIterator<Item = (&'a T, EventId<T>)> {
// if the reader has seen some of the events in a buffer, find the proper index offset.
// otherwise read all events in the buffer
let a_index = if self.last_event_count > events.a_start_event_count {
Expand All @@ -120,36 +178,44 @@ impl<T> EventReader<T> {
.get(b_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event)
.map(map_instance_event_with_id)
.chain(
events
.events_a
.get(a_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event),
.map(map_instance_event_with_id),
),
State::B => events
.events_a
.get(a_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event)
.map(map_instance_event_with_id)
.chain(
events
.events_b
.get(b_index..)
.unwrap_or_else(|| &[])
.iter()
.map(map_instance_event),
.map(map_instance_event_with_id),
),
}
}

/// Retrieves the latest event that this EventReader hasn't seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened before now.
pub fn latest<'a>(&mut self, events: &'a Events<T>) -> Option<&'a T> {
self.iter(events).rev().next()
self.latest_with_id(events).map(|(event, _)| event)
}

/// Like [`latest`](Self::latest), except also returning the [`EventId`] of the event.
pub fn latest_with_id<'a>(&mut self, events: &'a Events<T>) -> Option<(&'a T, EventId<T>)> {
self.iter_internal(events).rev().next().map(|(event, id)| {
trace!("EventReader::latest() -> {}", id);
(event, id)
})
}

/// Retrieves the latest event that matches the given `predicate` that this reader hasn't seen yet. This updates the EventReader's
Expand All @@ -159,23 +225,50 @@ impl<T> EventReader<T> {
events: &'a Events<T>,
predicate: impl FnMut(&&T) -> bool,
) -> Option<&'a T> {
self.iter(events).rev().find(predicate)
self.find_latest_with_id(events, predicate)
.map(|(event, _)| event)
}

/// Like [`find_latest`](Self::find_latest), except also returning the [`EventId`] of the event.
pub fn find_latest_with_id<'a>(
&mut self,
events: &'a Events<T>,
mut predicate: impl FnMut(&&T) -> bool,
) -> Option<(&'a T, EventId<T>)> {
self.iter_internal(events)
.rev()
.find(|(event, _id)| predicate(event))
.map(|(event, id)| {
trace!("EventReader::find_latest() -> {}", id);
(event, id)
})
}

/// Retrieves the earliest event in `events` that this reader hasn't seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened before now.
pub fn earliest<'a>(&mut self, events: &'a Events<T>) -> Option<&'a T> {
self.iter(events).next()
self.earliest_with_id(events).map(|(event, _)| event)
}

/// Like [`earliest`](Self::earliest), except also returning the [`EventId`] of the event.
pub fn earliest_with_id<'a>(&mut self, events: &'a Events<T>) -> Option<(&'a T, EventId<T>)> {
self.iter_internal(events).next().map(|(event, id)| {
trace!("EventReader::earliest() -> {}", id);
(event, id)
})
}
}

impl<T: bevy_ecs::Resource> Events<T> {
/// "Sends" an `event` by writing it to the current event buffer. [EventReader]s can then read the event.
pub fn send(&mut self, event: T) {
let event_instance = EventInstance {
event,
event_count: self.event_count,
let event_id = EventId {
id: self.event_count,
_marker: PhantomData,
};
trace!("Events::send() -> {}", event_id);

let event_instance = EventInstance { event, event_id };

match self.state {
State::A => self.events_a.push(event_instance),
Expand Down

0 comments on commit 0dbbcd9

Please sign in to comment.