Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event source location tracking #16778

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/bevy_ecs/src/event/base.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::{component::Component, traversal::Traversal};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
#[cfg(feature = "track_change_detection")]
use core::panic::Location;
use core::{
cmp::Ordering,
fmt,
Expand Down Expand Up @@ -59,6 +61,9 @@ pub struct EventId<E: Event> {
/// Uniquely identifies the event associated with this ID.
// This value corresponds to the order in which each event was added to the world.
pub id: usize,
/// The source code location that triggered this event.
#[cfg(feature = "track_change_detection")]
pub caller: &'static Location<'static>,
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
pub(super) _marker: PhantomData<E>,
}
Expand Down
24 changes: 24 additions & 0 deletions crates/bevy_ecs/src/event/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use bevy_ecs::{
system::Resource,
};
use bevy_utils::detailed_trace;
#[cfg(feature = "track_change_detection")]
use core::panic::Location;
use core::{
marker::PhantomData,
ops::{Deref, DerefMut},
Expand Down Expand Up @@ -120,9 +122,24 @@ impl<E: Event> Events<E> {
/// "Sends" an `event` by writing it to the current event buffer.
/// [`EventReader`](super::EventReader)s can then read the event.
/// This method returns the [ID](`EventId`) of the sent `event`.
#[track_caller]
pub fn send(&mut self, event: E) -> EventId<E> {
self.send_with_caller(
event,
#[cfg(feature = "track_change_detection")]
Location::caller(),
)
}

pub(crate) fn send_with_caller(
&mut self,
event: E,
#[cfg(feature = "track_change_detection")] caller: &'static Location<'static>,
) -> EventId<E> {
let event_id = EventId {
id: self.event_count,
#[cfg(feature = "track_change_detection")]
caller,
_marker: PhantomData,
};
detailed_trace!("Events::send() -> id: {}", event_id);
Expand All @@ -138,6 +155,7 @@ impl<E: Event> Events<E> {
/// Sends a list of `events` all at once, which can later be read by [`EventReader`](super::EventReader)s.
/// This is more efficient than sending each event individually.
/// This method returns the [IDs](`EventId`) of the sent `events`.
#[track_caller]
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
let last_count = self.event_count;

Expand All @@ -152,6 +170,7 @@ impl<E: Event> Events<E> {

/// Sends the default value of the event. Useful when the event is an empty struct.
/// This method returns the [ID](`EventId`) of the sent `event`.
#[track_caller]
pub fn send_default(&mut self) -> EventId<E>
where
E: Default,
Expand Down Expand Up @@ -300,6 +319,7 @@ impl<E: Event> Events<E> {
}

impl<E: Event> Extend<E> for Events<E> {
#[track_caller]
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = E>,
Expand All @@ -309,6 +329,8 @@ impl<E: Event> Extend<E> for Events<E> {
let events = iter.into_iter().map(|event| {
let event_id = EventId {
id: event_count,
#[cfg(feature = "track_change_detection")]
caller: Location::caller(),
_marker: PhantomData,
};
event_count += 1;
Expand Down Expand Up @@ -377,6 +399,8 @@ impl<E: Event> Iterator for SendBatchIds<E> {

let result = Some(EventId {
id: self.last_count,
#[cfg(feature = "track_change_detection")]
caller: Location::caller(),
_marker: PhantomData,
});

Expand Down
24 changes: 23 additions & 1 deletion crates/bevy_ecs/src/event/send_event.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
#[cfg(feature = "track_change_detection")]
use core::panic::Location;

use super::{Event, Events};
use crate::world::{Command, World};

/// A command to send an arbitrary [`Event`], used by [`Commands::send_event`](crate::system::Commands::send_event).
pub struct SendEvent<E: Event> {
/// The event to send.
pub event: E,
/// The source code location that triggered this command.
#[cfg(feature = "track_change_detection")]
pub caller: &'static Location<'static>,
}

// This does not use `From`, as the resulting `Into` is not track_caller
impl<E: Event> SendEvent<E> {
/// Constructs a new `SendEvent` tracking the caller.
pub fn new(event: E) -> Self {
Self {
event,
#[cfg(feature = "track_change_detection")]
caller: Location::caller(),
}
}
}

impl<E: Event> Command for SendEvent<E> {
fn apply(self, world: &mut World) {
let mut events = world.resource_mut::<Events<E>>();
events.send(self.event);
events.send_with_caller(
self.event,
#[cfg(feature = "track_change_detection")]
self.caller,
);
}
}
3 changes: 3 additions & 0 deletions crates/bevy_ecs/src/event/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
/// This method returns the [ID](`EventId`) of the sent `event`.
///
/// See [`Events`] for details.
#[track_caller]
pub fn send(&mut self, event: E) -> EventId<E> {
self.events.send(event)
}
Expand All @@ -78,6 +79,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
/// This method returns the [IDs](`EventId`) of the sent `events`.
///
/// See [`Events`] for details.
#[track_caller]
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
self.events.send_batch(events)
}
Expand All @@ -86,6 +88,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
/// This method returns the [ID](`EventId`) of the sent `event`.
///
/// See [`Events`] for details.
#[track_caller]
pub fn send_default(&mut self) -> EventId<E>
where
E: Default,
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -986,8 +986,13 @@ impl<'w, 's> Commands<'w, 's> {
/// sent, consider using a typed [`EventWriter`] instead.
///
/// [`EventWriter`]: crate::event::EventWriter
#[track_caller]
pub fn send_event<E: Event>(&mut self, event: E) -> &mut Self {
self.queue(SendEvent { event });
self.queue(SendEvent {
event,
#[cfg(feature = "track_change_detection")]
caller: Location::caller(),
});
self
}

Expand Down
144 changes: 144 additions & 0 deletions crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use core::{
any::Any,
fmt,
hash::{BuildHasher, Hash, Hasher},
panic::Location,
};

#[cfg(feature = "std")]
Expand Down Expand Up @@ -2278,6 +2279,149 @@ impl GetTypeRegistration for Cow<'static, Path> {
#[cfg(all(feature = "functions", feature = "std"))]
crate::func::macros::impl_function_traits!(Cow<'static, Path>);

impl TypePath for &'static Location<'static> {
fn type_path() -> &'static str {
"core::panic::Location"
}

fn short_type_path() -> &'static str {
"Location"
}
}

impl PartialReflect for &'static Location<'static> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh, nice.

fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}

#[inline]
fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {
self
}

fn as_partial_reflect(&self) -> &dyn PartialReflect {
self
}

fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {
self
}

fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {
Ok(self)
}

fn try_as_reflect(&self) -> Option<&dyn Reflect> {
Some(self)
}

fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {
Some(self)
}

fn reflect_kind(&self) -> ReflectKind {
ReflectKind::Opaque
}

fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Opaque(self)
}

fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Opaque(self)
}

fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Opaque(self)
}

fn clone_value(&self) -> Box<dyn PartialReflect> {
Box::new(*self)
}

fn reflect_hash(&self) -> Option<u64> {
let mut hasher = reflect_hasher();
Hash::hash(&Any::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
Some(hasher.finish())
}

fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
if let Some(value) = value.try_downcast_ref::<Self>() {
Some(PartialEq::eq(self, value))
} else {
Some(false)
}
}

fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {
if let Some(value) = value.try_downcast_ref::<Self>() {
self.clone_from(value);
Ok(())
} else {
Err(ApplyError::MismatchedTypes {
from_type: value.reflect_type_path().into(),
to_type: <Self as DynamicTypePath>::reflect_type_path(self).into(),
})
}
}
}

impl Reflect for &'static Location<'static> {
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}

fn as_any(&self) -> &dyn Any {
self
}

fn as_any_mut(&mut self) -> &mut dyn Any {
self
}

fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}

fn as_reflect(&self) -> &dyn Reflect {
self
}

fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}

fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
}

impl Typed for &'static Location<'static> {
fn type_info() -> &'static TypeInfo {
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
CELL.get_or_set(|| TypeInfo::Opaque(OpaqueInfo::new::<Self>()))
}
}

impl GetTypeRegistration for &'static Location<'static> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
registration
}
}

impl FromReflect for &'static Location<'static> {
fn from_reflect(reflect: &dyn PartialReflect) -> Option<Self> {
reflect.try_downcast_ref::<Self>().copied()
}
}

#[cfg(all(feature = "functions", feature = "std"))]
crate::func::macros::impl_function_traits!(&'static Location<'static>);

#[cfg(test)]
mod tests {
use crate::{
Expand Down
Loading