bevy/crates/bevy_ecs/src/observer/entity_cloning.rs
Alice Cecile 61a5a37584
Improve module structure of observers code (#19779)
# Objective

While working on #17607, I found myself confused and frustrated by the
tangled web woven by the various modules inside of our observers code.

Rather than tackle that as part of a big rewrite PR, I've decided to do
the mature (if frustrating) thing where you split out your trivial but
noisy refactoring first.

There are a large number of moving parts, especially in terms of
storage, and these are strewn willy-nilly across the module with no
apparent ordering. To make matters worse, this was almost all just
dumped into a multi-thousand LOC mod.rs at the root.

## Solution

I've reshuffled the modules, attempting to:
- reduce the size of the mod.rs file
- organize structs so that smaller structs are found after the larger
structs that contain them
- group related functionality together
- document why modules exist, and their broad organization

No functional changes have been made here, although I've had to increase
the visibility of a few fields from private to pub(crate) or pub(super)
to keep things compiling.

During these changes, I've opted for the lazy private module, public
re-export strategy, to avoid causing any breakages, both within and
outside of `bevy` itself. I think we can do better, but I want to leave
that for a proper cleanup pass at the end. There's no sense maintaining
migration guides and forcing multiple breaking changes throughout the
cycle.

## Testing

No functional changes; relying on existing test suite and the Rust
compiler.
2025-06-22 23:25:25 +00:00

110 lines
3.5 KiB
Rust

//! Logic to track observers when cloning entities.
use crate::{
component::ComponentCloneBehavior,
entity::{ComponentCloneCtx, EntityClonerBuilder, EntityMapper, SourceComponent},
observer::ObservedBy,
world::World,
};
use super::Observer;
impl EntityClonerBuilder<'_> {
/// Sets the option to automatically add cloned entities to the observers targeting source entity.
pub fn add_observers(&mut self, add_observers: bool) -> &mut Self {
if add_observers {
self.override_clone_behavior::<ObservedBy>(ComponentCloneBehavior::Custom(
component_clone_observed_by,
))
} else {
self.remove_clone_behavior_override::<ObservedBy>()
}
}
}
fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentCloneCtx) {
let target = ctx.target();
let source = ctx.source();
ctx.queue_deferred(move |world: &mut World, _mapper: &mut dyn EntityMapper| {
let observed_by = world
.get::<ObservedBy>(source)
.map(|observed_by| observed_by.0.clone())
.expect("Source entity must have ObservedBy");
world
.entity_mut(target)
.insert(ObservedBy(observed_by.clone()));
for observer_entity in observed_by.iter().copied() {
let mut observer_state = world
.get_mut::<Observer>(observer_entity)
.expect("Source observer entity must have Observer");
observer_state.descriptor.entities.push(target);
let event_types = observer_state.descriptor.events.clone();
let components = observer_state.descriptor.components.clone();
for event_type in event_types {
let observers = world.observers.get_observers_mut(event_type);
if components.is_empty() {
if let Some(map) = observers.entity_observers.get(&source).cloned() {
observers.entity_observers.insert(target, map);
}
} else {
for component in &components {
let Some(observers) = observers.component_observers.get_mut(component)
else {
continue;
};
if let Some(map) =
observers.entity_component_observers.get(&source).cloned()
{
observers.entity_component_observers.insert(target, map);
}
}
}
}
}
});
}
#[cfg(test)]
mod tests {
use crate::{
entity::EntityCloner,
event::{EntityEvent, Event},
observer::On,
resource::Resource,
system::ResMut,
world::World,
};
#[derive(Resource, Default)]
struct Num(usize);
#[derive(Event, EntityEvent)]
struct E;
#[test]
fn clone_entity_with_observer() {
let mut world = World::default();
world.init_resource::<Num>();
let e = world
.spawn_empty()
.observe(|_: On<E>, mut res: ResMut<Num>| res.0 += 1)
.id();
world.flush();
world.trigger_targets(E, e);
let e_clone = world.spawn_empty().id();
EntityCloner::build(&mut world)
.add_observers(true)
.clone_entity(e, e_clone);
world.trigger_targets(E, [e, e_clone]);
assert_eq!(world.resource::<Num>().0, 3);
}
}