Refactored ComponentHook Parameters into HookContext (#17503)

# Objective

- Make the function signature for `ComponentHook` less verbose

## Solution

- Refactored `Entity`, `ComponentId`, and `Option<&Location>` into a new
`HookContext` struct.

## Testing

- CI

---

## Migration Guide

Update the function signatures for your component hooks to only take 2
arguments, `world` and `context`. Note that because `HookContext` is
plain data with all members public, you can use de-structuring to
simplify migration.

```rust
// Before
fn my_hook(
    mut world: DeferredWorld,
    entity: Entity,
    component_id: ComponentId,
) { ... }

// After
fn my_hook(
    mut world: DeferredWorld,
    HookContext { entity, component_id, caller }: HookContext,
) { ... }
``` 

Likewise, if you were discarding certain parameters, you can use `..` in
the de-structuring:

```rust
// Before
fn my_hook(
    mut world: DeferredWorld,
    entity: Entity,
    _: ComponentId,
) { ... }

// After
fn my_hook(
    mut world: DeferredWorld,
    HookContext { entity, .. }: HookContext,
) { ... }
```
This commit is contained in:
Zachary Harrold 2025-01-23 13:45:24 +11:00 committed by GitHub
parent c7ddec571d
commit 41e79ae826
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 210 additions and 290 deletions

View File

@ -70,11 +70,11 @@ impl Component for OrderIndependentTransparencySettings {
type Mutability = Mutable; type Mutability = Mutable;
fn register_component_hooks(hooks: &mut ComponentHooks) { fn register_component_hooks(hooks: &mut ComponentHooks) {
hooks.on_add(|world, entity, _, caller| { hooks.on_add(|world, context| {
if let Some(value) = world.get::<OrderIndependentTransparencySettings>(entity) { if let Some(value) = world.get::<OrderIndependentTransparencySettings>(context.entity) {
if value.layer_count > 32 { if value.layer_count > 32 {
warn!("{}OrderIndependentTransparencySettings layer_count set to {} might be too high.", warn!("{}OrderIndependentTransparencySettings layer_count set to {} might be too high.",
caller.map(|location|format!("{location}: ")).unwrap_or_default(), context.caller.map(|location|format!("{location}: ")).unwrap_or_default(),
value.layer_count value.layer_count
); );
} }

View File

@ -1704,9 +1704,8 @@ fn sorted_remove<T: Eq + Ord + Copy>(source: &mut Vec<T>, remove: &[T]) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate as bevy_ecs; use crate as bevy_ecs;
use crate::{component::ComponentId, prelude::*, world::DeferredWorld}; use crate::{component::HookContext, prelude::*, world::DeferredWorld};
use alloc::vec; use alloc::vec;
use core::panic::Location;
#[derive(Component)] #[derive(Component)]
struct A; struct A;
@ -1715,39 +1714,19 @@ mod tests {
#[component(on_add = a_on_add, on_insert = a_on_insert, on_replace = a_on_replace, on_remove = a_on_remove)] #[component(on_add = a_on_add, on_insert = a_on_insert, on_replace = a_on_replace, on_remove = a_on_remove)]
struct AMacroHooks; struct AMacroHooks;
fn a_on_add( fn a_on_add(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_: Entity,
_: ComponentId,
_: Option<&'static Location<'static>>,
) {
world.resource_mut::<R>().assert_order(0); world.resource_mut::<R>().assert_order(0);
} }
fn a_on_insert<T1, T2>( fn a_on_insert(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_: T1,
_: T2,
_: Option<&'static Location<'static>>,
) {
world.resource_mut::<R>().assert_order(1); world.resource_mut::<R>().assert_order(1);
} }
fn a_on_replace<T1, T2>( fn a_on_replace(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_: T1,
_: T2,
_: Option<&'static Location<'static>>,
) {
world.resource_mut::<R>().assert_order(2); world.resource_mut::<R>().assert_order(2);
} }
fn a_on_remove<T1, T2>( fn a_on_remove(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_: T1,
_: T2,
_: Option<&'static Location<'static>>,
) {
world.resource_mut::<R>().assert_order(3); world.resource_mut::<R>().assert_order(3);
} }
@ -1780,10 +1759,10 @@ mod tests {
world.init_resource::<R>(); world.init_resource::<R>();
world world
.register_component_hooks::<A>() .register_component_hooks::<A>()
.on_add(|mut world, _, _, _| world.resource_mut::<R>().assert_order(0)) .on_add(|mut world, _| world.resource_mut::<R>().assert_order(0))
.on_insert(|mut world, _, _, _| world.resource_mut::<R>().assert_order(1)) .on_insert(|mut world, _| world.resource_mut::<R>().assert_order(1))
.on_replace(|mut world, _, _, _| world.resource_mut::<R>().assert_order(2)) .on_replace(|mut world, _| world.resource_mut::<R>().assert_order(2))
.on_remove(|mut world, _, _, _| world.resource_mut::<R>().assert_order(3)); .on_remove(|mut world, _| world.resource_mut::<R>().assert_order(3));
let entity = world.spawn(A).id(); let entity = world.spawn(A).id();
world.despawn(entity); world.despawn(entity);
@ -1807,10 +1786,10 @@ mod tests {
world.init_resource::<R>(); world.init_resource::<R>();
world world
.register_component_hooks::<A>() .register_component_hooks::<A>()
.on_add(|mut world, _, _, _| world.resource_mut::<R>().assert_order(0)) .on_add(|mut world, _| world.resource_mut::<R>().assert_order(0))
.on_insert(|mut world, _, _, _| world.resource_mut::<R>().assert_order(1)) .on_insert(|mut world, _| world.resource_mut::<R>().assert_order(1))
.on_replace(|mut world, _, _, _| world.resource_mut::<R>().assert_order(2)) .on_replace(|mut world, _| world.resource_mut::<R>().assert_order(2))
.on_remove(|mut world, _, _, _| world.resource_mut::<R>().assert_order(3)); .on_remove(|mut world, _| world.resource_mut::<R>().assert_order(3));
let mut entity = world.spawn_empty(); let mut entity = world.spawn_empty();
entity.insert(A); entity.insert(A);
@ -1824,8 +1803,8 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world world
.register_component_hooks::<A>() .register_component_hooks::<A>()
.on_replace(|mut world, _, _, _| world.resource_mut::<R>().assert_order(0)) .on_replace(|mut world, _| world.resource_mut::<R>().assert_order(0))
.on_insert(|mut world, _, _, _| { .on_insert(|mut world, _| {
if let Some(mut r) = world.get_resource_mut::<R>() { if let Some(mut r) = world.get_resource_mut::<R>() {
r.assert_order(1); r.assert_order(1);
} }
@ -1846,22 +1825,22 @@ mod tests {
world.init_resource::<R>(); world.init_resource::<R>();
world world
.register_component_hooks::<A>() .register_component_hooks::<A>()
.on_add(|mut world, entity, _, _| { .on_add(|mut world, context| {
world.resource_mut::<R>().assert_order(0); world.resource_mut::<R>().assert_order(0);
world.commands().entity(entity).insert(B); world.commands().entity(context.entity).insert(B);
}) })
.on_remove(|mut world, entity, _, _| { .on_remove(|mut world, context| {
world.resource_mut::<R>().assert_order(2); world.resource_mut::<R>().assert_order(2);
world.commands().entity(entity).remove::<B>(); world.commands().entity(context.entity).remove::<B>();
}); });
world world
.register_component_hooks::<B>() .register_component_hooks::<B>()
.on_add(|mut world, entity, _, _| { .on_add(|mut world, context| {
world.resource_mut::<R>().assert_order(1); world.resource_mut::<R>().assert_order(1);
world.commands().entity(entity).remove::<A>(); world.commands().entity(context.entity).remove::<A>();
}) })
.on_remove(|mut world, _, _, _| { .on_remove(|mut world, _| {
world.resource_mut::<R>().assert_order(3); world.resource_mut::<R>().assert_order(3);
}); });
@ -1878,27 +1857,27 @@ mod tests {
world.init_resource::<R>(); world.init_resource::<R>();
world world
.register_component_hooks::<A>() .register_component_hooks::<A>()
.on_add(|mut world, entity, _, _| { .on_add(|mut world, context| {
world.resource_mut::<R>().assert_order(0); world.resource_mut::<R>().assert_order(0);
world.commands().entity(entity).insert(B).insert(C); world.commands().entity(context.entity).insert(B).insert(C);
}); });
world world
.register_component_hooks::<B>() .register_component_hooks::<B>()
.on_add(|mut world, entity, _, _| { .on_add(|mut world, context| {
world.resource_mut::<R>().assert_order(1); world.resource_mut::<R>().assert_order(1);
world.commands().entity(entity).insert(D); world.commands().entity(context.entity).insert(D);
}); });
world world
.register_component_hooks::<C>() .register_component_hooks::<C>()
.on_add(|mut world, _, _, _| { .on_add(|mut world, _| {
world.resource_mut::<R>().assert_order(3); world.resource_mut::<R>().assert_order(3);
}); });
world world
.register_component_hooks::<D>() .register_component_hooks::<D>()
.on_add(|mut world, _, _, _| { .on_add(|mut world, _| {
world.resource_mut::<R>().assert_order(2); world.resource_mut::<R>().assert_order(2);
}); });

View File

@ -299,7 +299,7 @@ pub use bevy_ecs_macros::require;
/// - `#[component(on_remove = on_remove_function)]` /// - `#[component(on_remove = on_remove_function)]`
/// ///
/// ``` /// ```
/// # use bevy_ecs::component::Component; /// # use bevy_ecs::component::{Component, HookContext};
/// # use bevy_ecs::world::DeferredWorld; /// # use bevy_ecs::world::DeferredWorld;
/// # use bevy_ecs::entity::Entity; /// # use bevy_ecs::entity::Entity;
/// # use bevy_ecs::component::ComponentId; /// # use bevy_ecs::component::ComponentId;
@ -315,12 +315,12 @@ pub use bevy_ecs_macros::require;
/// // #[component(on_replace = my_on_replace_hook, on_remove = my_on_remove_hook)] /// // #[component(on_replace = my_on_replace_hook, on_remove = my_on_remove_hook)]
/// struct ComponentA; /// struct ComponentA;
/// ///
/// fn my_on_add_hook(world: DeferredWorld, entity: Entity, id: ComponentId, caller: Option<&Location>) { /// fn my_on_add_hook(world: DeferredWorld, context: HookContext) {
/// // ... /// // ...
/// } /// }
/// ///
/// // You can also omit writing some types using generics. /// // You can also destructure items directly in the signature
/// fn my_on_insert_hook<T1, T2>(world: DeferredWorld, _: T1, _: T2, caller: Option<&Location>) { /// fn my_on_insert_hook(world: DeferredWorld, HookContext { caller, .. }: HookContext) {
/// // ... /// // ...
/// } /// }
/// ``` /// ```
@ -498,9 +498,18 @@ pub enum StorageType {
} }
/// The type used for [`Component`] lifecycle hooks such as `on_add`, `on_insert` or `on_remove`. /// The type used for [`Component`] lifecycle hooks such as `on_add`, `on_insert` or `on_remove`.
/// The caller location is `Some` if the `track_caller` feature is enabled. pub type ComponentHook = for<'w> fn(DeferredWorld<'w>, HookContext);
pub type ComponentHook =
for<'w> fn(DeferredWorld<'w>, Entity, ComponentId, Option<&'static Location<'static>>); /// Context provided to a [`ComponentHook`].
#[derive(Clone, Copy, Debug)]
pub struct HookContext {
/// The [`Entity`] this hook was invoked for.
pub entity: Entity,
/// The [`ComponentId`] this hook was invoked for.
pub component_id: ComponentId,
/// The caller location is `Some` if the `track_caller` feature is enabled.
pub caller: Option<&'static Location<'static>>,
}
/// [`World`]-mutating functions that run as part of lifecycle events of a [`Component`]. /// [`World`]-mutating functions that run as part of lifecycle events of a [`Component`].
/// ///
@ -537,14 +546,14 @@ pub type ComponentHook =
/// let mut tracked_component_query = world.query::<&MyTrackedComponent>(); /// let mut tracked_component_query = world.query::<&MyTrackedComponent>();
/// assert!(tracked_component_query.iter(&world).next().is_none()); /// assert!(tracked_component_query.iter(&world).next().is_none());
/// ///
/// world.register_component_hooks::<MyTrackedComponent>().on_add(|mut world, entity, _component_id, _caller| { /// world.register_component_hooks::<MyTrackedComponent>().on_add(|mut world, context| {
/// let mut tracked_entities = world.resource_mut::<TrackedEntities>(); /// let mut tracked_entities = world.resource_mut::<TrackedEntities>();
/// tracked_entities.0.insert(entity); /// tracked_entities.0.insert(context.entity);
/// }); /// });
/// ///
/// world.register_component_hooks::<MyTrackedComponent>().on_remove(|mut world, entity, _component_id, _caller| { /// world.register_component_hooks::<MyTrackedComponent>().on_remove(|mut world, context| {
/// let mut tracked_entities = world.resource_mut::<TrackedEntities>(); /// let mut tracked_entities = world.resource_mut::<TrackedEntities>();
/// tracked_entities.0.remove(&entity); /// tracked_entities.0.remove(&context.entity);
/// }); /// });
/// ///
/// let entity = world.spawn(MyTrackedComponent).id(); /// let entity = world.spawn(MyTrackedComponent).id();

View File

@ -14,7 +14,7 @@ use crate::reflect::{
use crate::{ use crate::{
self as bevy_ecs, self as bevy_ecs,
bundle::Bundle, bundle::Bundle,
component::{Component, ComponentId}, component::{Component, HookContext},
entity::{Entity, VisitEntities}, entity::{Entity, VisitEntities},
relationship::{RelatedSpawner, RelatedSpawnerCommands}, relationship::{RelatedSpawner, RelatedSpawnerCommands},
system::EntityCommands, system::EntityCommands,
@ -22,8 +22,8 @@ use crate::{
}; };
use alloc::{format, string::String, vec::Vec}; use alloc::{format, string::String, vec::Vec};
use bevy_ecs_macros::VisitEntitiesMut; use bevy_ecs_macros::VisitEntitiesMut;
use core::ops::Deref;
use core::slice; use core::slice;
use core::{ops::Deref, panic::Location};
use disqualified::ShortName; use disqualified::ShortName;
use log::warn; use log::warn;
@ -268,9 +268,7 @@ impl<'a> EntityCommands<'a> {
/// contains component `C`. This will print a warning if the parent does not contain `C`. /// contains component `C`. This will print a warning if the parent does not contain `C`.
pub fn validate_parent_has_component<C: Component>( pub fn validate_parent_has_component<C: Component>(
world: DeferredWorld, world: DeferredWorld,
entity: Entity, HookContext { entity, caller, .. }: HookContext,
_: ComponentId,
caller: Option<&'static Location<'static>>,
) { ) {
let entity_ref = world.entity(entity); let entity_ref = world.entity(entity);
let Some(child_of) = entity_ref.get::<ChildOf>() else { let Some(child_of) = entity_ref.get::<ChildOf>() else {

View File

@ -2030,8 +2030,8 @@ mod tests {
world.insert_resource(I(0)); world.insert_resource(I(0));
world world
.register_component_hooks::<Y>() .register_component_hooks::<Y>()
.on_add(|mut world, _, _, _| world.resource_mut::<A>().0 += 1) .on_add(|mut world, _| world.resource_mut::<A>().0 += 1)
.on_insert(|mut world, _, _, _| world.resource_mut::<I>().0 += 1); .on_insert(|mut world, _| world.resource_mut::<I>().0 += 1);
// Spawn entity and ensure Y was added // Spawn entity and ensure Y was added
assert!(world.spawn(X).contains::<Y>()); assert!(world.spawn(X).contains::<Y>());
@ -2060,8 +2060,8 @@ mod tests {
world.insert_resource(I(0)); world.insert_resource(I(0));
world world
.register_component_hooks::<Y>() .register_component_hooks::<Y>()
.on_add(|mut world, _, _, _| world.resource_mut::<A>().0 += 1) .on_add(|mut world, _| world.resource_mut::<A>().0 += 1)
.on_insert(|mut world, _, _, _| world.resource_mut::<I>().0 += 1); .on_insert(|mut world, _| world.resource_mut::<I>().0 += 1);
// Spawn entity and ensure Y was added // Spawn entity and ensure Y was added
assert!(world.spawn_empty().insert(X).contains::<Y>()); assert!(world.spawn_empty().insert(X).contains::<Y>());

View File

@ -1,5 +1,7 @@
use crate::{ use crate::{
component::{Component, ComponentCloneHandler, ComponentHooks, Mutable, StorageType}, component::{
Component, ComponentCloneHandler, ComponentHooks, HookContext, Mutable, StorageType,
},
entity::{ComponentCloneCtx, Entity, EntityCloneBuilder}, entity::{ComponentCloneCtx, Entity, EntityCloneBuilder},
observer::ObserverState, observer::ObserverState,
world::{DeferredWorld, World}, world::{DeferredWorld, World},
@ -15,7 +17,7 @@ impl Component for ObservedBy {
type Mutability = Mutable; type Mutability = Mutable;
fn register_component_hooks(hooks: &mut ComponentHooks) { fn register_component_hooks(hooks: &mut ComponentHooks) {
hooks.on_remove(|mut world, entity, _, _| { hooks.on_remove(|mut world, HookContext { entity, .. }| {
let observed_by = { let observed_by = {
let mut component = world.get_mut::<ObservedBy>(entity).unwrap(); let mut component = world.get_mut::<ObservedBy>(entity).unwrap();
core::mem::take(&mut component.0) core::mem::take(&mut component.0)

View File

@ -1,9 +1,8 @@
use alloc::{boxed::Box, vec, vec::Vec}; use alloc::{boxed::Box, vec, vec::Vec};
use core::any::Any; use core::any::Any;
use core::panic::Location;
use crate::{ use crate::{
component::{ComponentHook, ComponentHooks, ComponentId, Mutable, StorageType}, component::{ComponentHook, ComponentHooks, ComponentId, HookContext, Mutable, StorageType},
observer::{ObserverDescriptor, ObserverTrigger}, observer::{ObserverDescriptor, ObserverTrigger},
prelude::*, prelude::*,
query::DebugCheckedUnwrap, query::DebugCheckedUnwrap,
@ -67,12 +66,12 @@ impl Component for ObserverState {
type Mutability = Mutable; type Mutability = Mutable;
fn register_component_hooks(hooks: &mut ComponentHooks) { fn register_component_hooks(hooks: &mut ComponentHooks) {
hooks.on_add(|mut world, entity, _, _| { hooks.on_add(|mut world, HookContext { entity, .. }| {
world.commands().queue(move |world: &mut World| { world.commands().queue(move |world: &mut World| {
world.register_observer(entity); world.register_observer(entity);
}); });
}); });
hooks.on_remove(|mut world, entity, _, _| { hooks.on_remove(|mut world, HookContext { entity, .. }| {
let descriptor = core::mem::take( let descriptor = core::mem::take(
&mut world &mut world
.entity_mut(entity) .entity_mut(entity)
@ -319,12 +318,12 @@ impl Component for Observer {
const STORAGE_TYPE: StorageType = StorageType::SparseSet; const STORAGE_TYPE: StorageType = StorageType::SparseSet;
type Mutability = Mutable; type Mutability = Mutable;
fn register_component_hooks(hooks: &mut ComponentHooks) { fn register_component_hooks(hooks: &mut ComponentHooks) {
hooks.on_add(|world, entity, id, caller| { hooks.on_add(|world, context| {
let Some(observe) = world.get::<Self>(entity) else { let Some(observe) = world.get::<Self>(context.entity) else {
return; return;
}; };
let hook = observe.hook_on_add; let hook = observe.hook_on_add;
hook(world, entity, id, caller); hook(world, context);
}); });
} }
} }
@ -395,9 +394,7 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
/// ensure type parameters match. /// ensure type parameters match.
fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>( fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
mut world: DeferredWorld<'_>, mut world: DeferredWorld<'_>,
entity: Entity, HookContext { entity, .. }: HookContext,
_: ComponentId,
_: Option<&'static Location<'static>>,
) { ) {
world.commands().queue(move |world: &mut World| { world.commands().queue(move |world: &mut World| {
let event_id = E::register_component_id(world); let event_id = E::register_component_id(world);

View File

@ -5,14 +5,13 @@ mod relationship_query;
mod relationship_source_collection; mod relationship_source_collection;
use alloc::format; use alloc::format;
use core::panic::Location;
pub use related_methods::*; pub use related_methods::*;
pub use relationship_query::*; pub use relationship_query::*;
pub use relationship_source_collection::*; pub use relationship_source_collection::*;
use crate::{ use crate::{
component::{Component, ComponentId, Mutable}, component::{Component, HookContext, Mutable},
entity::Entity, entity::Entity,
system::{ system::{
command::HandleError, command::HandleError,
@ -74,12 +73,7 @@ pub trait Relationship: Component + Sized {
fn from(entity: Entity) -> Self; fn from(entity: Entity) -> Self;
/// The `on_insert` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection. /// The `on_insert` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
fn on_insert( fn on_insert(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
mut world: DeferredWorld,
entity: Entity,
_: ComponentId,
caller: Option<&'static Location<'static>>,
) {
let target_entity = world.entity(entity).get::<Self>().unwrap().get(); let target_entity = world.entity(entity).get::<Self>().unwrap().get();
if target_entity == entity { if target_entity == entity {
warn!( warn!(
@ -113,12 +107,7 @@ pub trait Relationship: Component + Sized {
/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection. /// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
// note: think of this as "on_drop" // note: think of this as "on_drop"
fn on_replace( fn on_replace(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
mut world: DeferredWorld,
entity: Entity,
_: ComponentId,
_: Option<&'static Location<'static>>,
) {
let target_entity = world.entity(entity).get::<Self>().unwrap().get(); let target_entity = world.entity(entity).get::<Self>().unwrap().get();
if let Ok(mut target_entity_mut) = world.get_entity_mut(target_entity) { if let Ok(mut target_entity_mut) = world.get_entity_mut(target_entity) {
if let Some(mut relationship_target) = if let Some(mut relationship_target) =
@ -179,12 +168,7 @@ pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
/// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection. /// The `on_replace` component hook that maintains the [`Relationship`] / [`RelationshipTarget`] connection.
// note: think of this as "on_drop" // note: think of this as "on_drop"
fn on_replace( fn on_replace(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
mut world: DeferredWorld,
entity: Entity,
_: ComponentId,
caller: Option<&'static Location<'static>>,
) {
// NOTE: this unsafe code is an optimization. We could make this safe, but it would require // NOTE: this unsafe code is an optimization. We could make this safe, but it would require
// copying the RelationshipTarget collection // copying the RelationshipTarget collection
// SAFETY: This only reads the Self component and queues Remove commands // SAFETY: This only reads the Self component and queues Remove commands
@ -215,12 +199,7 @@ pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized {
/// The `on_despawn` component hook that despawns entities stored in an entity's [`RelationshipTarget`] when /// The `on_despawn` component hook that despawns entities stored in an entity's [`RelationshipTarget`] when
/// that entity is despawned. /// that entity is despawned.
// note: think of this as "on_drop" // note: think of this as "on_drop"
fn on_despawn( fn on_despawn(mut world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
mut world: DeferredWorld,
entity: Entity,
_: ComponentId,
caller: Option<&'static Location<'static>>,
) {
// NOTE: this unsafe code is an optimization. We could make this safe, but it would require // NOTE: this unsafe code is an optimization. We could make this safe, but it would require
// copying the RelationshipTarget collection // copying the RelationshipTarget collection
// SAFETY: This only reads the Self component and queues despawn commands // SAFETY: This only reads the Self component and queues despawn commands

View File

@ -5,7 +5,7 @@ use core::panic::Location;
use crate::{ use crate::{
archetype::Archetype, archetype::Archetype,
change_detection::MutUntyped, change_detection::MutUntyped,
component::{ComponentId, Mutable}, component::{ComponentId, HookContext, Mutable},
entity::Entity, entity::Entity,
event::{Event, EventId, Events, SendBatchIds}, event::{Event, EventId, Events, SendBatchIds},
observer::{Observers, TriggerTargets}, observer::{Observers, TriggerTargets},
@ -538,12 +538,14 @@ impl<'w> DeferredWorld<'w> {
if let Some(hook) = hooks.on_add { if let Some(hook) = hooks.on_add {
hook( hook(
DeferredWorld { world: self.world }, DeferredWorld { world: self.world },
entity, HookContext {
component_id, entity,
#[cfg(feature = "track_location")] component_id,
Some(caller), #[cfg(feature = "track_location")]
#[cfg(not(feature = "track_location"))] caller: Some(caller),
None, #[cfg(not(feature = "track_location"))]
caller: None,
},
); );
} }
} }
@ -569,12 +571,14 @@ impl<'w> DeferredWorld<'w> {
if let Some(hook) = hooks.on_insert { if let Some(hook) = hooks.on_insert {
hook( hook(
DeferredWorld { world: self.world }, DeferredWorld { world: self.world },
entity, HookContext {
component_id, entity,
#[cfg(feature = "track_location")] component_id,
Some(caller), #[cfg(feature = "track_location")]
#[cfg(not(feature = "track_location"))] caller: Some(caller),
None, #[cfg(not(feature = "track_location"))]
caller: None,
},
); );
} }
} }
@ -600,12 +604,14 @@ impl<'w> DeferredWorld<'w> {
if let Some(hook) = hooks.on_replace { if let Some(hook) = hooks.on_replace {
hook( hook(
DeferredWorld { world: self.world }, DeferredWorld { world: self.world },
entity, HookContext {
component_id, entity,
#[cfg(feature = "track_location")] component_id,
Some(caller), #[cfg(feature = "track_location")]
#[cfg(not(feature = "track_location"))] caller: Some(caller),
None, #[cfg(not(feature = "track_location"))]
caller: None,
},
); );
} }
} }
@ -631,12 +637,14 @@ impl<'w> DeferredWorld<'w> {
if let Some(hook) = hooks.on_remove { if let Some(hook) = hooks.on_remove {
hook( hook(
DeferredWorld { world: self.world }, DeferredWorld { world: self.world },
entity, HookContext {
component_id, entity,
#[cfg(feature = "track_location")] component_id,
Some(caller), #[cfg(feature = "track_location")]
#[cfg(not(feature = "track_location"))] caller: Some(caller),
None, #[cfg(not(feature = "track_location"))]
caller: None,
},
); );
} }
} }
@ -662,12 +670,14 @@ impl<'w> DeferredWorld<'w> {
if let Some(hook) = hooks.on_despawn { if let Some(hook) = hooks.on_despawn {
hook( hook(
DeferredWorld { world: self.world }, DeferredWorld { world: self.world },
entity, HookContext {
component_id, entity,
#[cfg(feature = "track_location")] component_id,
Some(caller), #[cfg(feature = "track_location")]
#[cfg(not(feature = "track_location"))] caller: Some(caller),
None, #[cfg(not(feature = "track_location"))]
caller: None,
},
); );
} }
} }

View File

@ -4401,10 +4401,10 @@ mod tests {
use bevy_ptr::{OwningPtr, Ptr}; use bevy_ptr::{OwningPtr, Ptr};
use core::panic::AssertUnwindSafe; use core::panic::AssertUnwindSafe;
use core::panic::Location;
#[cfg(feature = "track_location")] #[cfg(feature = "track_location")]
use std::sync::OnceLock; use {core::panic::Location, std::sync::OnceLock};
use crate::component::HookContext;
use crate::{ use crate::{
self as bevy_ecs, self as bevy_ecs,
change_detection::MutUntyped, change_detection::MutUntyped,
@ -5482,22 +5482,12 @@ mod tests {
#[component(on_add = ord_a_hook_on_add, on_insert = ord_a_hook_on_insert, on_replace = ord_a_hook_on_replace, on_remove = ord_a_hook_on_remove)] #[component(on_add = ord_a_hook_on_add, on_insert = ord_a_hook_on_insert, on_replace = ord_a_hook_on_replace, on_remove = ord_a_hook_on_remove)]
struct OrdA; struct OrdA;
fn ord_a_hook_on_add( fn ord_a_hook_on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
mut world: DeferredWorld,
entity: Entity,
_id: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
world.resource_mut::<TestVec>().0.push("OrdA hook on_add"); world.resource_mut::<TestVec>().0.push("OrdA hook on_add");
world.commands().entity(entity).insert(OrdB); world.commands().entity(entity).insert(OrdB);
} }
fn ord_a_hook_on_insert( fn ord_a_hook_on_insert(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
mut world: DeferredWorld,
entity: Entity,
_id: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
world world
.resource_mut::<TestVec>() .resource_mut::<TestVec>()
.0 .0
@ -5506,24 +5496,14 @@ mod tests {
world.commands().entity(entity).remove::<OrdB>(); world.commands().entity(entity).remove::<OrdB>();
} }
fn ord_a_hook_on_replace( fn ord_a_hook_on_replace(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_entity: Entity,
_id: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
world world
.resource_mut::<TestVec>() .resource_mut::<TestVec>()
.0 .0
.push("OrdA hook on_replace"); .push("OrdA hook on_replace");
} }
fn ord_a_hook_on_remove( fn ord_a_hook_on_remove(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_entity: Entity,
_id: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
world world
.resource_mut::<TestVec>() .resource_mut::<TestVec>()
.0 .0
@ -5550,12 +5530,7 @@ mod tests {
#[component(on_add = ord_b_hook_on_add, on_insert = ord_b_hook_on_insert, on_replace = ord_b_hook_on_replace, on_remove = ord_b_hook_on_remove)] #[component(on_add = ord_b_hook_on_add, on_insert = ord_b_hook_on_insert, on_replace = ord_b_hook_on_replace, on_remove = ord_b_hook_on_remove)]
struct OrdB; struct OrdB;
fn ord_b_hook_on_add( fn ord_b_hook_on_add(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_entity: Entity,
_id: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
world.resource_mut::<TestVec>().0.push("OrdB hook on_add"); world.resource_mut::<TestVec>().0.push("OrdB hook on_add");
world.commands().queue(|world: &mut World| { world.commands().queue(|world: &mut World| {
world world
@ -5565,36 +5540,21 @@ mod tests {
}); });
} }
fn ord_b_hook_on_insert( fn ord_b_hook_on_insert(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_entity: Entity,
_id: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
world world
.resource_mut::<TestVec>() .resource_mut::<TestVec>()
.0 .0
.push("OrdB hook on_insert"); .push("OrdB hook on_insert");
} }
fn ord_b_hook_on_replace( fn ord_b_hook_on_replace(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_entity: Entity,
_id: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
world world
.resource_mut::<TestVec>() .resource_mut::<TestVec>()
.0 .0
.push("OrdB hook on_replace"); .push("OrdB hook on_replace");
} }
fn ord_b_hook_on_remove( fn ord_b_hook_on_remove(mut world: DeferredWorld, _: HookContext) {
mut world: DeferredWorld,
_entity: Entity,
_id: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
world world
.resource_mut::<TestVec>() .resource_mut::<TestVec>()
.0 .0
@ -5734,7 +5694,7 @@ mod tests {
struct C; struct C;
static TRACKED: OnceLock<&'static Location<'static>> = OnceLock::new(); static TRACKED: OnceLock<&'static Location<'static>> = OnceLock::new();
fn get_tracked(world: DeferredWorld, entity: Entity, _: ComponentId, _: Option<&Location>) { fn get_tracked(world: DeferredWorld, HookContext { entity, .. }: HookContext) {
TRACKED.get_or_init(|| { TRACKED.get_or_init(|| {
world world
.entities .entities
@ -5795,35 +5755,35 @@ mod tests {
world.register_component::<Foo>(); world.register_component::<Foo>();
world world
.register_component_hooks::<Foo>() .register_component_hooks::<Foo>()
.on_add(|world, entity, _, _| { .on_add(|world, context| {
ADD_COUNT.fetch_add(1, Ordering::Relaxed); ADD_COUNT.fetch_add(1, Ordering::Relaxed);
assert_eq!( assert_eq!(
world.get(entity), world.get(context.entity),
Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed))) Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))
); );
}) })
.on_remove(|world, entity, _, _| { .on_remove(|world, context| {
REMOVE_COUNT.fetch_add(1, Ordering::Relaxed); REMOVE_COUNT.fetch_add(1, Ordering::Relaxed);
assert_eq!( assert_eq!(
world.get(entity), world.get(context.entity),
Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed))) Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))
); );
}) })
.on_replace(|world, entity, _, _| { .on_replace(|world, context| {
REPLACE_COUNT.fetch_add(1, Ordering::Relaxed); REPLACE_COUNT.fetch_add(1, Ordering::Relaxed);
assert_eq!( assert_eq!(
world.get(entity), world.get(context.entity),
Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed))) Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))
); );
}) })
.on_insert(|world, entity, _, _| { .on_insert(|world, context| {
INSERT_COUNT.fetch_add(1, Ordering::Relaxed); INSERT_COUNT.fetch_add(1, Ordering::Relaxed);
assert_eq!( assert_eq!(
world.get(entity), world.get(context.entity),
Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed))) Some(&Foo(EXPECTED_VALUE.load(Ordering::Relaxed)))
); );
}); });

View File

@ -1,8 +1,6 @@
//! Contains the [`AutoFocus`] component and related machinery. //! Contains the [`AutoFocus`] component and related machinery.
use core::panic::Location; use bevy_ecs::{component::HookContext, prelude::*, world::DeferredWorld};
use bevy_ecs::{component::ComponentId, prelude::*, world::DeferredWorld};
use crate::InputFocus; use crate::InputFocus;
@ -25,12 +23,7 @@ use bevy_reflect::{prelude::*, Reflect};
#[component(on_add = on_auto_focus_added)] #[component(on_add = on_auto_focus_added)]
pub struct AutoFocus; pub struct AutoFocus;
fn on_auto_focus_added( fn on_auto_focus_added(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
mut world: DeferredWorld,
entity: Entity,
_: ComponentId,
_: Option<&'static Location<'static>>,
) {
if let Some(mut input_focus) = world.get_resource_mut::<InputFocus>() { if let Some(mut input_focus) = world.get_resource_mut::<InputFocus>() {
input_focus.set(entity); input_focus.set(entity);
} }

View File

@ -361,26 +361,20 @@ mod tests {
use alloc::string::String; use alloc::string::String;
use bevy_ecs::{ use bevy_ecs::{
component::ComponentId, observer::Trigger, system::RunSystemOnce, world::DeferredWorld, component::HookContext, observer::Trigger, system::RunSystemOnce, world::DeferredWorld,
}; };
use bevy_input::{ use bevy_input::{
keyboard::{Key, KeyCode}, keyboard::{Key, KeyCode},
ButtonState, InputPlugin, ButtonState, InputPlugin,
}; };
use bevy_window::WindowResolution; use bevy_window::WindowResolution;
use core::panic::Location;
use smol_str::SmolStr; use smol_str::SmolStr;
#[derive(Component)] #[derive(Component)]
#[component(on_add = set_focus_on_add)] #[component(on_add = set_focus_on_add)]
struct SetFocusOnAdd; struct SetFocusOnAdd;
fn set_focus_on_add( fn set_focus_on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
mut world: DeferredWorld,
entity: Entity,
_: ComponentId,
_: Option<&Location>,
) {
let mut input_focus = world.resource_mut::<InputFocus>(); let mut input_focus = world.resource_mut::<InputFocus>();
input_focus.set(entity); input_focus.set(entity);
} }

View File

@ -22,7 +22,7 @@ use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
use bevy_derive::{Deref, DerefMut}; use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{ use bevy_ecs::{
change_detection::DetectChanges, change_detection::DetectChanges,
component::{Component, ComponentId}, component::{Component, HookContext},
entity::{Entity, EntityBorrow}, entity::{Entity, EntityBorrow},
event::EventReader, event::EventReader,
prelude::{require, With}, prelude::{require, With},
@ -43,7 +43,6 @@ use bevy_window::{
WindowScaleFactorChanged, WindowScaleFactorChanged,
}; };
use core::ops::Range; use core::ops::Range;
use core::panic::Location;
use derive_more::derive::From; use derive_more::derive::From;
use tracing::warn; use tracing::warn;
use wgpu::{BlendState, TextureFormat, TextureUsages}; use wgpu::{BlendState, TextureFormat, TextureUsages};
@ -333,12 +332,7 @@ pub struct Camera {
pub sub_camera_view: Option<SubCameraView>, pub sub_camera_view: Option<SubCameraView>,
} }
fn warn_on_no_render_graph( fn warn_on_no_render_graph(world: DeferredWorld, HookContext { entity, caller, .. }: HookContext) {
world: DeferredWorld,
entity: Entity,
_: ComponentId,
caller: Option<&'static Location<'static>>,
) {
if !world.entity(entity).contains::<CameraRenderGraph>() { if !world.entity(entity).contains::<CameraRenderGraph>() {
warn!("{}Entity {entity} has a `Camera` component, but it doesn't have a render graph configured. Consider adding a `Camera2d` or `Camera3d` component, or manually adding a `CameraRenderGraph` component if you need a custom render graph.", caller.map(|location|format!("{location}: ")).unwrap_or_default()); warn!("{}Entity {entity} has a `Camera` component, but it doesn't have a render graph configured. Consider adding a `Camera2d` or `Camera3d` component, or manually adding a `CameraRenderGraph` component if you need a custom render graph.", caller.map(|location|format!("{location}: ")).unwrap_or_default());
} }

View File

@ -32,11 +32,11 @@ impl<C: Component> Plugin for SyncComponentPlugin<C> {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.register_required_components::<C, SyncToRenderWorld>(); app.register_required_components::<C, SyncToRenderWorld>();
app.world_mut().register_component_hooks::<C>().on_remove( app.world_mut()
|mut world, entity, _component_id, _caller| { .register_component_hooks::<C>()
.on_remove(|mut world, context| {
let mut pending = world.resource_mut::<PendingSyncEntity>(); let mut pending = world.resource_mut::<PendingSyncEntity>();
pending.push(EntityRecord::ComponentRemoved(entity)); pending.push(EntityRecord::ComponentRemoved(context.entity));
}, });
);
} }
} }

View File

@ -2,9 +2,8 @@ mod range;
mod render_layers; mod render_layers;
use core::any::TypeId; use core::any::TypeId;
use core::panic::Location;
use bevy_ecs::component::ComponentId; use bevy_ecs::component::HookContext;
use bevy_ecs::entity::hash_set::EntityHashSet; use bevy_ecs::entity::hash_set::EntityHashSet;
use bevy_ecs::world::DeferredWorld; use bevy_ecs::world::DeferredWorld;
use derive_more::derive::{Deref, DerefMut}; use derive_more::derive::{Deref, DerefMut};
@ -635,9 +634,7 @@ pub fn check_visibility(
/// ``` /// ```
pub fn add_visibility_class<C>( pub fn add_visibility_class<C>(
mut world: DeferredWorld<'_>, mut world: DeferredWorld<'_>,
entity: Entity, HookContext { entity, .. }: HookContext,
_: ComponentId,
_: Option<&Location>,
) where ) where
C: 'static, C: 'static,
{ {

View File

@ -365,7 +365,7 @@ mod tests {
let mut dst_world = World::new(); let mut dst_world = World::new();
dst_world dst_world
.register_component_hooks::<A>() .register_component_hooks::<A>()
.on_add(|mut world, _, _, _| { .on_add(|mut world, _| {
world.commands().spawn_empty(); world.commands().spawn_empty();
}); });
dst_world.insert_resource(reg.clone()); dst_world.insert_resource(reg.clone());

View File

@ -68,12 +68,14 @@ impl Plugin for ScenePlugin {
// Register component hooks for DynamicSceneRoot // Register component hooks for DynamicSceneRoot
app.world_mut() app.world_mut()
.register_component_hooks::<DynamicSceneRoot>() .register_component_hooks::<DynamicSceneRoot>()
.on_remove(|mut world, entity, _, _| { .on_remove(|mut world, context| {
let Some(handle) = world.get::<DynamicSceneRoot>(entity) else { let Some(handle) = world.get::<DynamicSceneRoot>(context.entity) else {
return; return;
}; };
let id = handle.id(); let id = handle.id();
if let Some(&SceneInstance(scene_instance)) = world.get::<SceneInstance>(entity) { if let Some(&SceneInstance(scene_instance)) =
world.get::<SceneInstance>(context.entity)
{
let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else { let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else {
return; return;
}; };
@ -87,8 +89,10 @@ impl Plugin for ScenePlugin {
// Register component hooks for SceneRoot // Register component hooks for SceneRoot
app.world_mut() app.world_mut()
.register_component_hooks::<SceneRoot>() .register_component_hooks::<SceneRoot>()
.on_remove(|mut world, entity, _, _| { .on_remove(|mut world, context| {
if let Some(&SceneInstance(scene_instance)) = world.get::<SceneInstance>(entity) { if let Some(&SceneInstance(scene_instance)) =
world.get::<SceneInstance>(context.entity)
{
let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else { let Some(mut scene_spawner) = world.get_resource_mut::<SceneSpawner>() else {
return; return;
}; };

View File

@ -14,7 +14,7 @@
//! between components (like hierarchies or parent-child links) and need to maintain correctness. //! between components (like hierarchies or parent-child links) and need to maintain correctness.
use bevy::{ use bevy::{
ecs::component::{ComponentHooks, Mutable, StorageType}, ecs::component::{ComponentHooks, HookContext, Mutable, StorageType},
prelude::*, prelude::*,
}; };
use std::collections::HashMap; use std::collections::HashMap;
@ -63,54 +63,69 @@ fn setup(world: &mut World) {
world world
.register_component_hooks::<MyComponent>() .register_component_hooks::<MyComponent>()
// There are 4 component lifecycle hooks: `on_add`, `on_insert`, `on_replace` and `on_remove` // There are 4 component lifecycle hooks: `on_add`, `on_insert`, `on_replace` and `on_remove`
// A hook has 4 arguments: // A hook has 2 arguments:
// - a `DeferredWorld`, this allows access to resource and component data as well as `Commands` // - a `DeferredWorld`, this allows access to resource and component data as well as `Commands`
// - the entity that triggered the hook // - a `HookContext`, this provides access to the following contextual information:
// - the component id of the triggering component, this is mostly used for dynamic components // - the entity that triggered the hook
// - the location of the code that caused the hook to trigger // - the component id of the triggering component, this is mostly used for dynamic components
// - the location of the code that caused the hook to trigger
// //
// `on_add` will trigger when a component is inserted onto an entity without it // `on_add` will trigger when a component is inserted onto an entity without it
.on_add(|mut world, entity, component_id, caller| { .on_add(
// You can access component data from within the hook |mut world,
let value = world.get::<MyComponent>(entity).unwrap().0; HookContext {
println!( entity,
"{component_id:?} added to {entity} with value {value:?}{}", component_id,
caller caller,
.map(|location| format!("due to {location}")) }| {
.unwrap_or_default() // You can access component data from within the hook
); let value = world.get::<MyComponent>(entity).unwrap().0;
// Or access resources println!(
world "{component_id:?} added to {entity} with value {value:?}{}",
.resource_mut::<MyComponentIndex>() caller
.insert(value, entity); .map(|location| format!("due to {location}"))
// Or send events .unwrap_or_default()
world.send_event(MyEvent); );
}) // Or access resources
world
.resource_mut::<MyComponentIndex>()
.insert(value, entity);
// Or send events
world.send_event(MyEvent);
},
)
// `on_insert` will trigger when a component is inserted onto an entity, // `on_insert` will trigger when a component is inserted onto an entity,
// regardless of whether or not it already had it and after `on_add` if it ran // regardless of whether or not it already had it and after `on_add` if it ran
.on_insert(|world, _, _, _| { .on_insert(|world, _| {
println!("Current Index: {:?}", world.resource::<MyComponentIndex>()); println!("Current Index: {:?}", world.resource::<MyComponentIndex>());
}) })
// `on_replace` will trigger when a component is inserted onto an entity that already had it, // `on_replace` will trigger when a component is inserted onto an entity that already had it,
// and runs before the value is replaced. // and runs before the value is replaced.
// Also triggers when a component is removed from an entity, and runs before `on_remove` // Also triggers when a component is removed from an entity, and runs before `on_remove`
.on_replace(|mut world, entity, _, _| { .on_replace(|mut world, context| {
let value = world.get::<MyComponent>(entity).unwrap().0; let value = world.get::<MyComponent>(context.entity).unwrap().0;
world.resource_mut::<MyComponentIndex>().remove(&value); world.resource_mut::<MyComponentIndex>().remove(&value);
}) })
// `on_remove` will trigger when a component is removed from an entity, // `on_remove` will trigger when a component is removed from an entity,
// since it runs before the component is removed you can still access the component data // since it runs before the component is removed you can still access the component data
.on_remove(|mut world, entity, component_id, caller| { .on_remove(
let value = world.get::<MyComponent>(entity).unwrap().0; |mut world,
println!( HookContext {
"{component_id:?} removed from {entity} with value {value:?}{}", entity,
caller component_id,
.map(|location| format!("due to {location}")) caller,
.unwrap_or_default() }| {
); let value = world.get::<MyComponent>(entity).unwrap().0;
// You can also issue commands through `.commands()` println!(
world.commands().entity(entity).despawn(); "{component_id:?} removed from {entity} with value {value:?}{}",
}); caller
.map(|location| format!("due to {location}"))
.unwrap_or_default()
);
// You can also issue commands through `.commands()`
world.commands().entity(entity).despawn();
},
);
} }
fn trigger_hooks( fn trigger_hooks(

View File

@ -2,7 +2,7 @@
use bevy::{ use bevy::{
ecs::{ ecs::{
component::{ComponentDescriptor, ComponentId, StorageType}, component::{ComponentDescriptor, ComponentId, HookContext, StorageType},
world::DeferredWorld, world::DeferredWorld,
}, },
prelude::*, prelude::*,
@ -10,7 +10,6 @@ use bevy::{
utils::HashMap, utils::HashMap,
}; };
use core::alloc::Layout; use core::alloc::Layout;
use core::panic::Location;
/// This component is mutable, the default case. This is indicated by components /// This component is mutable, the default case. This is indicated by components
/// implementing [`Component`] where [`Component::Mutability`] is [`Mutable`](bevy::ecs::component::Mutable). /// implementing [`Component`] where [`Component::Mutability`] is [`Mutable`](bevy::ecs::component::Mutable).
@ -74,12 +73,7 @@ impl NameIndex {
/// ///
/// Since all mutations to [`Name`] are captured by hooks, we know it is not currently /// Since all mutations to [`Name`] are captured by hooks, we know it is not currently
/// inserted in the index, and its value will not change without triggering a hook. /// inserted in the index, and its value will not change without triggering a hook.
fn on_insert_name( fn on_insert_name(mut world: DeferredWorld<'_>, HookContext { entity, .. }: HookContext) {
mut world: DeferredWorld<'_>,
entity: Entity,
_component: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
let Some(&name) = world.entity(entity).get::<Name>() else { let Some(&name) = world.entity(entity).get::<Name>() else {
unreachable!("OnInsert hook guarantees `Name` is available on entity") unreachable!("OnInsert hook guarantees `Name` is available on entity")
}; };
@ -94,12 +88,7 @@ fn on_insert_name(
/// ///
/// Since all mutations to [`Name`] are captured by hooks, we know it is currently /// Since all mutations to [`Name`] are captured by hooks, we know it is currently
/// inserted in the index. /// inserted in the index.
fn on_replace_name( fn on_replace_name(mut world: DeferredWorld<'_>, HookContext { entity, .. }: HookContext) {
mut world: DeferredWorld<'_>,
entity: Entity,
_component: ComponentId,
_caller: Option<&'static Location<'static>>,
) {
let Some(&name) = world.entity(entity).get::<Name>() else { let Some(&name) = world.entity(entity).get::<Name>() else {
unreachable!("OnReplace hook guarantees `Name` is available on entity") unreachable!("OnReplace hook guarantees `Name` is available on entity")
}; };