Event source location tracking (#16778)
# Objective Fixes #16776 ## Solution - reflect `&'static Location` as an opaque type - I've added this to `impls/std.rs` because other core types are there too. Maybe they should be split out into a `core.rs` in another PR. - add source location to `EventId` (behind the `tracking_change_detection` feature flag) ## Testing --- ## Showcase ```rust fn apply_damage_to_health( mut dmg_events: EventReader<DealDamage>, ) { for (event, event_id) in dmg_events.read_with_id() { info!( "Applying {} damage, triggered by {}", event.amount, event_id.caller ); … ``` ``` 2024-12-12T01:21:50.126827Z INFO event: Applying 9 damage, triggered by examples/ecs/event.rs:47:16 ``` ## Migration Guide - If you manually construct a `SendEvent`, use `SendEvent::new()` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
aa519593ff
commit
b2d3371814
@ -1,6 +1,8 @@
|
|||||||
use crate::{component::Component, traversal::Traversal};
|
use crate::{component::Component, traversal::Traversal};
|
||||||
#[cfg(feature = "bevy_reflect")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
|
#[cfg(feature = "track_change_detection")]
|
||||||
|
use core::panic::Location;
|
||||||
use core::{
|
use core::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
fmt,
|
fmt,
|
||||||
@ -59,6 +61,9 @@ pub struct EventId<E: Event> {
|
|||||||
/// Uniquely identifies the event associated with this ID.
|
/// Uniquely identifies the event associated with this ID.
|
||||||
// This value corresponds to the order in which each event was added to the world.
|
// This value corresponds to the order in which each event was added to the world.
|
||||||
pub id: usize,
|
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))]
|
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
|
||||||
pub(super) _marker: PhantomData<E>,
|
pub(super) _marker: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ use bevy_ecs::{
|
|||||||
system::Resource,
|
system::Resource,
|
||||||
};
|
};
|
||||||
use bevy_utils::detailed_trace;
|
use bevy_utils::detailed_trace;
|
||||||
|
#[cfg(feature = "track_change_detection")]
|
||||||
|
use core::panic::Location;
|
||||||
use core::{
|
use core::{
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
@ -120,9 +122,24 @@ impl<E: Event> Events<E> {
|
|||||||
/// "Sends" an `event` by writing it to the current event buffer.
|
/// "Sends" an `event` by writing it to the current event buffer.
|
||||||
/// [`EventReader`](super::EventReader)s can then read the event.
|
/// [`EventReader`](super::EventReader)s can then read the event.
|
||||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||||
|
#[track_caller]
|
||||||
pub fn send(&mut self, event: E) -> EventId<E> {
|
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 {
|
let event_id = EventId {
|
||||||
id: self.event_count,
|
id: self.event_count,
|
||||||
|
#[cfg(feature = "track_change_detection")]
|
||||||
|
caller,
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
};
|
};
|
||||||
detailed_trace!("Events::send() -> id: {}", event_id);
|
detailed_trace!("Events::send() -> id: {}", event_id);
|
||||||
@ -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.
|
/// 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 is more efficient than sending each event individually.
|
||||||
/// This method returns the [IDs](`EventId`) of the sent `events`.
|
/// 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> {
|
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
|
||||||
let last_count = self.event_count;
|
let last_count = self.event_count;
|
||||||
|
|
||||||
@ -152,6 +170,7 @@ impl<E: Event> Events<E> {
|
|||||||
|
|
||||||
/// Sends the default value of the event. Useful when the event is an empty struct.
|
/// 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`.
|
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||||
|
#[track_caller]
|
||||||
pub fn send_default(&mut self) -> EventId<E>
|
pub fn send_default(&mut self) -> EventId<E>
|
||||||
where
|
where
|
||||||
E: Default,
|
E: Default,
|
||||||
@ -300,6 +319,7 @@ impl<E: Event> Events<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Event> Extend<E> for Events<E> {
|
impl<E: Event> Extend<E> for Events<E> {
|
||||||
|
#[track_caller]
|
||||||
fn extend<I>(&mut self, iter: I)
|
fn extend<I>(&mut self, iter: I)
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = E>,
|
I: IntoIterator<Item = E>,
|
||||||
@ -309,6 +329,8 @@ impl<E: Event> Extend<E> for Events<E> {
|
|||||||
let events = iter.into_iter().map(|event| {
|
let events = iter.into_iter().map(|event| {
|
||||||
let event_id = EventId {
|
let event_id = EventId {
|
||||||
id: event_count,
|
id: event_count,
|
||||||
|
#[cfg(feature = "track_change_detection")]
|
||||||
|
caller: Location::caller(),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
};
|
};
|
||||||
event_count += 1;
|
event_count += 1;
|
||||||
@ -377,6 +399,8 @@ impl<E: Event> Iterator for SendBatchIds<E> {
|
|||||||
|
|
||||||
let result = Some(EventId {
|
let result = Some(EventId {
|
||||||
id: self.last_count,
|
id: self.last_count,
|
||||||
|
#[cfg(feature = "track_change_detection")]
|
||||||
|
caller: Location::caller(),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
#[cfg(feature = "track_change_detection")]
|
||||||
|
use core::panic::Location;
|
||||||
|
|
||||||
use super::{Event, Events};
|
use super::{Event, Events};
|
||||||
use crate::world::{Command, World};
|
use crate::world::{Command, World};
|
||||||
|
|
||||||
@ -5,11 +8,30 @@ use crate::world::{Command, World};
|
|||||||
pub struct SendEvent<E: Event> {
|
pub struct SendEvent<E: Event> {
|
||||||
/// The event to send.
|
/// The event to send.
|
||||||
pub event: E,
|
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> {
|
impl<E: Event> Command for SendEvent<E> {
|
||||||
fn apply(self, world: &mut World) {
|
fn apply(self, world: &mut World) {
|
||||||
let mut events = world.resource_mut::<Events<E>>();
|
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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
|
|||||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||||
///
|
///
|
||||||
/// See [`Events`] for details.
|
/// See [`Events`] for details.
|
||||||
|
#[track_caller]
|
||||||
pub fn send(&mut self, event: E) -> EventId<E> {
|
pub fn send(&mut self, event: E) -> EventId<E> {
|
||||||
self.events.send(event)
|
self.events.send(event)
|
||||||
}
|
}
|
||||||
@ -78,6 +79,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
|
|||||||
/// This method returns the [IDs](`EventId`) of the sent `events`.
|
/// This method returns the [IDs](`EventId`) of the sent `events`.
|
||||||
///
|
///
|
||||||
/// See [`Events`] for details.
|
/// See [`Events`] for details.
|
||||||
|
#[track_caller]
|
||||||
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
|
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
|
||||||
self.events.send_batch(events)
|
self.events.send_batch(events)
|
||||||
}
|
}
|
||||||
@ -86,6 +88,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
|
|||||||
/// This method returns the [ID](`EventId`) of the sent `event`.
|
/// This method returns the [ID](`EventId`) of the sent `event`.
|
||||||
///
|
///
|
||||||
/// See [`Events`] for details.
|
/// See [`Events`] for details.
|
||||||
|
#[track_caller]
|
||||||
pub fn send_default(&mut self) -> EventId<E>
|
pub fn send_default(&mut self) -> EventId<E>
|
||||||
where
|
where
|
||||||
E: Default,
|
E: Default,
|
||||||
|
@ -986,8 +986,13 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
/// sent, consider using a typed [`EventWriter`] instead.
|
/// sent, consider using a typed [`EventWriter`] instead.
|
||||||
///
|
///
|
||||||
/// [`EventWriter`]: crate::event::EventWriter
|
/// [`EventWriter`]: crate::event::EventWriter
|
||||||
|
#[track_caller]
|
||||||
pub fn send_event<E: Event>(&mut self, event: E) -> &mut Self {
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ use core::{
|
|||||||
any::Any,
|
any::Any,
|
||||||
fmt,
|
fmt,
|
||||||
hash::{BuildHasher, Hash, Hasher},
|
hash::{BuildHasher, Hash, Hasher},
|
||||||
|
panic::Location,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -2278,6 +2279,149 @@ impl GetTypeRegistration for Cow<'static, Path> {
|
|||||||
#[cfg(all(feature = "functions", feature = "std"))]
|
#[cfg(all(feature = "functions", feature = "std"))]
|
||||||
crate::func::macros::impl_function_traits!(Cow<'static, Path>);
|
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> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
|
Loading…
Reference in New Issue
Block a user