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

Track callsite for observers & hooks #15607

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
21 changes: 10 additions & 11 deletions crates/bevy_ecs/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use crate::{
};
use bevy_ptr::{ConstNonNull, OwningPtr};
use bevy_utils::{all_tuples, HashMap, HashSet, TypeIdMap};
#[cfg(feature = "track_change_detection")]
use core::panic::Location;
use core::{any::TypeId, ptr::NonNull};

Expand Down Expand Up @@ -893,7 +892,7 @@ impl<'w> BundleInserter<'w> {
location: EntityLocation,
bundle: T,
insert_mode: InsertMode,
#[cfg(feature = "track_change_detection")] caller: &'static Location<'static>,
caller: &'static Location<'static>,
) -> EntityLocation {
let bundle_info = self.bundle_info.as_ref();
let add_bundle = self.add_bundle.as_ref();
Expand All @@ -913,6 +912,7 @@ impl<'w> BundleInserter<'w> {
ON_REPLACE,
entity,
add_bundle.iter_existing(),
caller,
);
}
}
Expand Down Expand Up @@ -1082,7 +1082,7 @@ impl<'w> BundleInserter<'w> {
unsafe {
deferred_world.trigger_on_add(new_archetype, entity, add_bundle.iter_added());
if new_archetype.has_add_observer() {
deferred_world.trigger_observers(ON_ADD, entity, add_bundle.iter_added());
deferred_world.trigger_observers(ON_ADD, entity, add_bundle.iter_added(), caller);
}
match insert_mode {
InsertMode::Replace => {
Expand All @@ -1097,6 +1097,7 @@ impl<'w> BundleInserter<'w> {
ON_INSERT,
entity,
add_bundle.iter_inserted(),
caller,
);
}
}
Expand All @@ -1113,6 +1114,7 @@ impl<'w> BundleInserter<'w> {
ON_INSERT,
entity,
add_bundle.iter_added(),
caller,
);
}
}
Expand Down Expand Up @@ -1192,7 +1194,7 @@ impl<'w> BundleSpawner<'w> {
&mut self,
entity: Entity,
bundle: T,
#[cfg(feature = "track_change_detection")] caller: &'static Location<'static>,
caller: &'static Location<'static>,
) -> EntityLocation {
// SAFETY: We do not make any structural changes to the archetype graph through self.world so these pointers always remain valid
let bundle_info = self.bundle_info.as_ref();
Expand Down Expand Up @@ -1241,6 +1243,7 @@ impl<'w> BundleSpawner<'w> {
ON_ADD,
entity,
bundle_info.iter_contributed_components(),
caller,
);
}
deferred_world.trigger_on_insert(
Expand All @@ -1253,6 +1256,7 @@ impl<'w> BundleSpawner<'w> {
ON_INSERT,
entity,
bundle_info.iter_contributed_components(),
caller,
);
}
};
Expand All @@ -1266,17 +1270,12 @@ impl<'w> BundleSpawner<'w> {
pub unsafe fn spawn<T: Bundle>(
&mut self,
bundle: T,
#[cfg(feature = "track_change_detection")] caller: &'static Location<'static>,
caller: &'static Location<'static>,
) -> Entity {
let entity = self.entities().alloc();
// SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type
unsafe {
self.spawn_non_existent(
entity,
bundle,
#[cfg(feature = "track_change_detection")]
caller,
);
self.spawn_non_existent(entity, bundle, caller);
}
entity
}
Expand Down
71 changes: 67 additions & 4 deletions crates/bevy_ecs/src/observer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::{
};
use bevy_ptr::Ptr;
use bevy_utils::HashMap;
use core::panic::Location;
use core::{
fmt::Debug,
marker::PhantomData,
Expand Down Expand Up @@ -199,6 +200,9 @@ pub struct ObserverTrigger {

/// The entity the trigger targeted.
pub entity: Entity,

/// The location of the source code that triggered the obserer.
pub caller: &'static Location<'static>,
}

// Map between an observer entity and its runner
Expand Down Expand Up @@ -265,6 +269,7 @@ impl Observers {
components: impl Iterator<Item = ComponentId>,
data: &mut T,
propagate: &mut bool,
caller: &'static Location<'static>,
) {
// SAFETY: You cannot get a mutable reference to `observers` from `DeferredWorld`
let (mut world, observers) = unsafe {
Expand All @@ -286,6 +291,7 @@ impl Observers {
observer,
event_type,
entity,
caller,
},
data.into(),
propagate,
Expand Down Expand Up @@ -377,34 +383,58 @@ impl World {
/// While event types commonly implement [`Copy`],
/// those that don't will be consumed and will no longer be accessible.
/// If you need to use the event after triggering it, use [`World::trigger_ref`] instead.
#[track_caller]
pub fn trigger(&mut self, event: impl Event) {
TriggerEvent { event, targets: () }.trigger(self);
TriggerEvent {
event,
targets: (),
caller: Location::caller(),
}
.trigger(self);
}

/// Triggers the given [`Event`] as a mutable reference, which will run any [`Observer`]s watching for it.
///
/// Compared to [`World::trigger`], this method is most useful when it's necessary to check
/// or use the event after it has been modified by observers.
#[track_caller]
pub fn trigger_ref(&mut self, event: &mut impl Event) {
TriggerEvent { event, targets: () }.trigger_ref(self);
TriggerEvent {
event,
targets: (),
caller: Location::caller(),
}
.trigger_ref(self);
}

/// Triggers the given [`Event`] for the given `targets`, which will run any [`Observer`]s watching for it.
///
/// While event types commonly implement [`Copy`],
/// those that don't will be consumed and will no longer be accessible.
/// If you need to use the event after triggering it, use [`World::trigger_targets_ref`] instead.
#[track_caller]
pub fn trigger_targets(&mut self, event: impl Event, targets: impl TriggerTargets) {
TriggerEvent { event, targets }.trigger(self);
TriggerEvent {
event,
targets,
caller: Location::caller(),
}
.trigger(self);
}

/// Triggers the given [`Event`] as a mutable reference for the given `targets`,
/// which will run any [`Observer`]s watching for it.
///
/// Compared to [`World::trigger_targets`], this method is most useful when it's necessary to check
/// or use the event after it has been modified by observers.
#[track_caller]
pub fn trigger_targets_ref(&mut self, event: &mut impl Event, targets: impl TriggerTargets) {
TriggerEvent { event, targets }.trigger_ref(self);
TriggerEvent {
event,
targets,
caller: Location::caller(),
}
.trigger_ref(self);
}

/// Register an observer to the cache, called when an observer is created
Expand Down Expand Up @@ -529,6 +559,7 @@ impl World {
#[cfg(test)]
mod tests {
use alloc::vec;
use core::panic::Location;

use bevy_ptr::OwningPtr;

Expand Down Expand Up @@ -1234,4 +1265,36 @@ mod tests {

assert!(world.get_resource::<ResA>().is_some());
}

#[test]
#[track_caller]
fn observer_caller_location_event() {
#[derive(Event)]
struct EventA;

let caller = Location::caller();
let mut world = World::new();
world.observe(move |trigger: Trigger<EventA>| {
assert_eq!(trigger.trigger.caller, caller);
});
world.trigger(EventA);
}

#[test]
#[track_caller]
fn observer_caller_location_command_archetype_move() {
#[derive(Component)]
struct Component;

let caller = Location::caller();
let mut world = World::new();
world.observe(move |trigger: Trigger<OnAdd, Component>| {
assert_eq!(trigger.trigger.caller, caller);
});
world.observe(move |trigger: Trigger<OnRemove, Component>| {
assert_eq!(trigger.trigger.caller, caller);
});
world.commands().spawn(Component).clear();
world.flush_commands();
}
}
29 changes: 26 additions & 3 deletions crates/bevy_ecs/src/observer/trigger_event.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::panic::Location;

use crate::{
component::ComponentId,
entity::Entity,
Expand All @@ -12,19 +14,28 @@ pub struct TriggerEvent<E, Targets: TriggerTargets = ()> {

/// The targets to trigger the event for.
pub targets: Targets,

/// The source code that emitted this command.
pub caller: &'static Location<'static>,
}

impl<E: Event, Targets: TriggerTargets> TriggerEvent<E, Targets> {
pub(super) fn trigger(mut self, world: &mut World) {
let event_type = world.register_component::<E>();
trigger_event(world, event_type, &mut self.event, self.targets);
trigger_event(
world,
event_type,
&mut self.event,
self.targets,
self.caller,
);
}
}

impl<E: Event, Targets: TriggerTargets> TriggerEvent<&mut E, Targets> {
pub(super) fn trigger_ref(self, world: &mut World) {
let event_type = world.register_component::<E>();
trigger_event(world, event_type, self.event, self.targets);
trigger_event(world, event_type, self.event, self.targets, self.caller);
}
}

Expand All @@ -41,17 +52,20 @@ pub struct EmitDynamicTrigger<T, Targets: TriggerTargets = ()> {
event_type: ComponentId,
event_data: T,
targets: Targets,
caller: &'static Location<'static>,
}

impl<E, Targets: TriggerTargets> EmitDynamicTrigger<E, Targets> {
/// Sets the event type of the resulting trigger, used for dynamic triggers
/// # Safety
/// Caller must ensure that the component associated with `event_type` is accessible as E
#[track_caller]
pub unsafe fn new_with_id(event_type: ComponentId, event_data: E, targets: Targets) -> Self {
Self {
event_type,
event_data,
targets,
caller: Location::caller(),
}
}
}
Expand All @@ -60,7 +74,13 @@ impl<E: Event, Targets: TriggerTargets + Send + Sync + 'static> Command
for EmitDynamicTrigger<E, Targets>
{
fn apply(mut self, world: &mut World) {
trigger_event(world, self.event_type, &mut self.event_data, self.targets);
trigger_event(
world,
self.event_type,
&mut self.event_data,
self.targets,
self.caller,
);
}
}

Expand All @@ -70,6 +90,7 @@ fn trigger_event<E: Event, Targets: TriggerTargets>(
event_type: ComponentId,
event_data: &mut E,
targets: Targets,
caller: &'static Location<'static>,
) {
let mut world = DeferredWorld::from(world);
if targets.entities().is_empty() {
Expand All @@ -81,6 +102,7 @@ fn trigger_event<E: Event, Targets: TriggerTargets>(
targets.components(),
event_data,
false,
caller,
);
};
} else {
Expand All @@ -93,6 +115,7 @@ fn trigger_event<E: Event, Targets: TriggerTargets>(
targets.components(),
event_data,
E::AUTO_PROPAGATE,
caller,
);
};
}
Expand Down
Loading
Loading