Skip to content

Commit b2d3371

Browse files
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 <[email protected]>
1 parent aa51959 commit b2d3371

File tree

6 files changed

+205
-2
lines changed

6 files changed

+205
-2
lines changed

crates/bevy_ecs/src/event/base.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::{component::Component, traversal::Traversal};
22
#[cfg(feature = "bevy_reflect")]
33
use bevy_reflect::Reflect;
4+
#[cfg(feature = "track_change_detection")]
5+
use core::panic::Location;
46
use core::{
57
cmp::Ordering,
68
fmt,
@@ -59,6 +61,9 @@ pub struct EventId<E: Event> {
5961
/// Uniquely identifies the event associated with this ID.
6062
// This value corresponds to the order in which each event was added to the world.
6163
pub id: usize,
64+
/// The source code location that triggered this event.
65+
#[cfg(feature = "track_change_detection")]
66+
pub caller: &'static Location<'static>,
6267
#[cfg_attr(feature = "bevy_reflect", reflect(ignore))]
6368
pub(super) _marker: PhantomData<E>,
6469
}

crates/bevy_ecs/src/event/collections.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use bevy_ecs::{
44
system::Resource,
55
};
66
use bevy_utils::detailed_trace;
7+
#[cfg(feature = "track_change_detection")]
8+
use core::panic::Location;
79
use core::{
810
marker::PhantomData,
911
ops::{Deref, DerefMut},
@@ -120,9 +122,24 @@ impl<E: Event> Events<E> {
120122
/// "Sends" an `event` by writing it to the current event buffer.
121123
/// [`EventReader`](super::EventReader)s can then read the event.
122124
/// This method returns the [ID](`EventId`) of the sent `event`.
125+
#[track_caller]
123126
pub fn send(&mut self, event: E) -> EventId<E> {
127+
self.send_with_caller(
128+
event,
129+
#[cfg(feature = "track_change_detection")]
130+
Location::caller(),
131+
)
132+
}
133+
134+
pub(crate) fn send_with_caller(
135+
&mut self,
136+
event: E,
137+
#[cfg(feature = "track_change_detection")] caller: &'static Location<'static>,
138+
) -> EventId<E> {
124139
let event_id = EventId {
125140
id: self.event_count,
141+
#[cfg(feature = "track_change_detection")]
142+
caller,
126143
_marker: PhantomData,
127144
};
128145
detailed_trace!("Events::send() -> id: {}", event_id);
@@ -138,6 +155,7 @@ impl<E: Event> Events<E> {
138155
/// Sends a list of `events` all at once, which can later be read by [`EventReader`](super::EventReader)s.
139156
/// This is more efficient than sending each event individually.
140157
/// This method returns the [IDs](`EventId`) of the sent `events`.
158+
#[track_caller]
141159
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
142160
let last_count = self.event_count;
143161

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

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

302321
impl<E: Event> Extend<E> for Events<E> {
322+
#[track_caller]
303323
fn extend<I>(&mut self, iter: I)
304324
where
305325
I: IntoIterator<Item = E>,
@@ -309,6 +329,8 @@ impl<E: Event> Extend<E> for Events<E> {
309329
let events = iter.into_iter().map(|event| {
310330
let event_id = EventId {
311331
id: event_count,
332+
#[cfg(feature = "track_change_detection")]
333+
caller: Location::caller(),
312334
_marker: PhantomData,
313335
};
314336
event_count += 1;
@@ -377,6 +399,8 @@ impl<E: Event> Iterator for SendBatchIds<E> {
377399

378400
let result = Some(EventId {
379401
id: self.last_count,
402+
#[cfg(feature = "track_change_detection")]
403+
caller: Location::caller(),
380404
_marker: PhantomData,
381405
});
382406

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
1+
#[cfg(feature = "track_change_detection")]
2+
use core::panic::Location;
3+
14
use super::{Event, Events};
25
use crate::world::{Command, World};
36

47
/// A command to send an arbitrary [`Event`], used by [`Commands::send_event`](crate::system::Commands::send_event).
58
pub struct SendEvent<E: Event> {
69
/// The event to send.
710
pub event: E,
11+
/// The source code location that triggered this command.
12+
#[cfg(feature = "track_change_detection")]
13+
pub caller: &'static Location<'static>,
14+
}
15+
16+
// This does not use `From`, as the resulting `Into` is not track_caller
17+
impl<E: Event> SendEvent<E> {
18+
/// Constructs a new `SendEvent` tracking the caller.
19+
pub fn new(event: E) -> Self {
20+
Self {
21+
event,
22+
#[cfg(feature = "track_change_detection")]
23+
caller: Location::caller(),
24+
}
25+
}
826
}
927

1028
impl<E: Event> Command for SendEvent<E> {
1129
fn apply(self, world: &mut World) {
1230
let mut events = world.resource_mut::<Events<E>>();
13-
events.send(self.event);
31+
events.send_with_caller(
32+
self.event,
33+
#[cfg(feature = "track_change_detection")]
34+
self.caller,
35+
);
1436
}
1537
}

crates/bevy_ecs/src/event/writer.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
6969
/// This method returns the [ID](`EventId`) of the sent `event`.
7070
///
7171
/// See [`Events`] for details.
72+
#[track_caller]
7273
pub fn send(&mut self, event: E) -> EventId<E> {
7374
self.events.send(event)
7475
}
@@ -78,6 +79,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
7879
/// This method returns the [IDs](`EventId`) of the sent `events`.
7980
///
8081
/// See [`Events`] for details.
82+
#[track_caller]
8183
pub fn send_batch(&mut self, events: impl IntoIterator<Item = E>) -> SendBatchIds<E> {
8284
self.events.send_batch(events)
8385
}
@@ -86,6 +88,7 @@ impl<'w, E: Event> EventWriter<'w, E> {
8688
/// This method returns the [ID](`EventId`) of the sent `event`.
8789
///
8890
/// See [`Events`] for details.
91+
#[track_caller]
8992
pub fn send_default(&mut self) -> EventId<E>
9093
where
9194
E: Default,

crates/bevy_ecs/src/system/commands/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -986,8 +986,13 @@ impl<'w, 's> Commands<'w, 's> {
986986
/// sent, consider using a typed [`EventWriter`] instead.
987987
///
988988
/// [`EventWriter`]: crate::event::EventWriter
989+
#[track_caller]
989990
pub fn send_event<E: Event>(&mut self, event: E) -> &mut Self {
990-
self.queue(SendEvent { event });
991+
self.queue(SendEvent {
992+
event,
993+
#[cfg(feature = "track_change_detection")]
994+
caller: Location::caller(),
995+
});
991996
self
992997
}
993998

crates/bevy_reflect/src/impls/std.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use core::{
2525
any::Any,
2626
fmt,
2727
hash::{BuildHasher, Hash, Hasher},
28+
panic::Location,
2829
};
2930

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

2282+
impl TypePath for &'static Location<'static> {
2283+
fn type_path() -> &'static str {
2284+
"core::panic::Location"
2285+
}
2286+
2287+
fn short_type_path() -> &'static str {
2288+
"Location"
2289+
}
2290+
}
2291+
2292+
impl PartialReflect for &'static Location<'static> {
2293+
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
2294+
Some(<Self as Typed>::type_info())
2295+
}
2296+
2297+
#[inline]
2298+
fn into_partial_reflect(self: Box<Self>) -> Box<dyn PartialReflect> {
2299+
self
2300+
}
2301+
2302+
fn as_partial_reflect(&self) -> &dyn PartialReflect {
2303+
self
2304+
}
2305+
2306+
fn as_partial_reflect_mut(&mut self) -> &mut dyn PartialReflect {
2307+
self
2308+
}
2309+
2310+
fn try_into_reflect(self: Box<Self>) -> Result<Box<dyn Reflect>, Box<dyn PartialReflect>> {
2311+
Ok(self)
2312+
}
2313+
2314+
fn try_as_reflect(&self) -> Option<&dyn Reflect> {
2315+
Some(self)
2316+
}
2317+
2318+
fn try_as_reflect_mut(&mut self) -> Option<&mut dyn Reflect> {
2319+
Some(self)
2320+
}
2321+
2322+
fn reflect_kind(&self) -> ReflectKind {
2323+
ReflectKind::Opaque
2324+
}
2325+
2326+
fn reflect_ref(&self) -> ReflectRef {
2327+
ReflectRef::Opaque(self)
2328+
}
2329+
2330+
fn reflect_mut(&mut self) -> ReflectMut {
2331+
ReflectMut::Opaque(self)
2332+
}
2333+
2334+
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
2335+
ReflectOwned::Opaque(self)
2336+
}
2337+
2338+
fn clone_value(&self) -> Box<dyn PartialReflect> {
2339+
Box::new(*self)
2340+
}
2341+
2342+
fn reflect_hash(&self) -> Option<u64> {
2343+
let mut hasher = reflect_hasher();
2344+
Hash::hash(&Any::type_id(self), &mut hasher);
2345+
Hash::hash(self, &mut hasher);
2346+
Some(hasher.finish())
2347+
}
2348+
2349+
fn reflect_partial_eq(&self, value: &dyn PartialReflect) -> Option<bool> {
2350+
if let Some(value) = value.try_downcast_ref::<Self>() {
2351+
Some(PartialEq::eq(self, value))
2352+
} else {
2353+
Some(false)
2354+
}
2355+
}
2356+
2357+
fn try_apply(&mut self, value: &dyn PartialReflect) -> Result<(), ApplyError> {
2358+
if let Some(value) = value.try_downcast_ref::<Self>() {
2359+
self.clone_from(value);
2360+
Ok(())
2361+
} else {
2362+
Err(ApplyError::MismatchedTypes {
2363+
from_type: value.reflect_type_path().into(),
2364+
to_type: <Self as DynamicTypePath>::reflect_type_path(self).into(),
2365+
})
2366+
}
2367+
}
2368+
}
2369+
2370+
impl Reflect for &'static Location<'static> {
2371+
fn into_any(self: Box<Self>) -> Box<dyn Any> {
2372+
self
2373+
}
2374+
2375+
fn as_any(&self) -> &dyn Any {
2376+
self
2377+
}
2378+
2379+
fn as_any_mut(&mut self) -> &mut dyn Any {
2380+
self
2381+
}
2382+
2383+
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
2384+
self
2385+
}
2386+
2387+
fn as_reflect(&self) -> &dyn Reflect {
2388+
self
2389+
}
2390+
2391+
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
2392+
self
2393+
}
2394+
2395+
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
2396+
*self = value.take()?;
2397+
Ok(())
2398+
}
2399+
}
2400+
2401+
impl Typed for &'static Location<'static> {
2402+
fn type_info() -> &'static TypeInfo {
2403+
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
2404+
CELL.get_or_set(|| TypeInfo::Opaque(OpaqueInfo::new::<Self>()))
2405+
}
2406+
}
2407+
2408+
impl GetTypeRegistration for &'static Location<'static> {
2409+
fn get_type_registration() -> TypeRegistration {
2410+
let mut registration = TypeRegistration::of::<Self>();
2411+
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
2412+
registration
2413+
}
2414+
}
2415+
2416+
impl FromReflect for &'static Location<'static> {
2417+
fn from_reflect(reflect: &dyn PartialReflect) -> Option<Self> {
2418+
reflect.try_downcast_ref::<Self>().copied()
2419+
}
2420+
}
2421+
2422+
#[cfg(all(feature = "functions", feature = "std"))]
2423+
crate::func::macros::impl_function_traits!(&'static Location<'static>);
2424+
22812425
#[cfg(test)]
22822426
mod tests {
22832427
use crate::{

0 commit comments

Comments
 (0)