-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Improved Entity Mapping and Cloning #17687
base: main
Are you sure you want to change the base?
Conversation
c700dcf
to
2f6a747
Compare
Wasm CI issues should be resolved once you upstream from |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly looked at the cloning changes here since this is the part I'm most familiar with.
7bfe05a
to
e3ad84b
Compare
It looks like your PR is a breaking change, but you didn't provide a migration guide. Could you add some context on what users should update when this change get released in a new version of Bevy? |
The entity mapping changes will have implications for our networking ecosystem too. Hopefully positive! I've pulled in a few reviewers from there to get their opinions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, not as bad to review as I was worried about. I have a few cleanup nits, but the general strategy and motivation is sound, and I really like making the clone + despawn behavior a trait constant that we use for both behaviors.
Making sure that VisitEntities
mentions and explains this new behavior is the main thing I want to see improved before this is merged.
Removing Arcs in EntityCloner makes me particularly happy; I'm glad you found a way to do that. Eliminating global cloning config also seems reasonable; I agree with your argument that this decision is extremly contextual.
This easy path for entity mapping will further widen the gap between components and resources, but as laid out in #17485, I don't think further duplicating features like this is sustainable.
I like both of your follow-up suggestions, but unsuprisingly I'd prefer they be done in later PRs :)
target_components_ptrs: &'a mut Vec<PtrMut<'b>>, | ||
target_components_buffer: &'b Bump, | ||
bundle_scratch: &'a mut BundleScratch<'b>, | ||
bump: &'b Bump, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could use a more descriptive variable name; the previous name was much clearer.
@@ -92,6 +105,17 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { | |||
self.component_info | |||
} | |||
|
|||
/// Returns true if the [`EntityCloner`] is configured to recursively clone entities. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like more docs on exactly what "recursive" means, probably on EntityCloner.
/// self.a = entity_mapper.map_entity(self.a); | ||
/// self.b = entity_mapper.map_entity(self.b); | ||
/// self.a = entity_mapper.get_mapped(self.a); | ||
/// self.b = entity_mapper.get_mapped(self.b); | ||
/// } | ||
/// } | ||
/// ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like the docs for MapEntities
should call out the new behavior / possibilities on Component
.
fn map_entity(&mut self, entity: Entity) -> Entity; | ||
/// Returns the "target" entity that maps to the given `source`. | ||
fn get_mapped(&mut self, source: Entity) -> Entity; | ||
/// Maps the `target` entity to the given `source`. For some implementations this might not actually determine the result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Maps the `target` entity to the given `source`. For some implementations this might not actually determine the result | |
/// Maps the `target` entity to the given `source`. For some implementations this might not actually determine the result |
@@ -143,6 +143,8 @@ pub type SourceIter<'w, R> = | |||
/// A [`Component`] containing the collection of entities that relate to this [`Entity`] via the associated `Relationship` type. | |||
/// See the [`Relationship`] documentation for more information. | |||
pub trait RelationshipTarget: Component<Mutability = Mutable> + Sized { | |||
/// If this is true, when despawning or cloning (when [recursion is enabled](crate::entity::EntityClonerBuilder::recursive)), the related entities targeting this entity will also be despawned or cloned. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// If this is true, when despawning or cloning (when [recursion is enabled](crate::entity::EntityClonerBuilder::recursive)), the related entities targeting this entity will also be despawned or cloned. | |
/// If this is true, when despawning or cloning (when [recursion is enabled](crate::entity::EntityClonerBuilder::recursive)), the related entities targeting this entity will also be despawned or cloned. | |
/// | |
/// For example, this is set to `true` for Bevy's built-in parent-child relation, defined by [`ChildOf`](crate::prelude::ChildOf) and [`Children`](crate::prelude::Children). | |
/// This means that when a parent is despawned, any children targeting that parent are also despawned (and the same applies to cloning). | |
/// | |
/// To get around this behavior, you can first break the relationship between entities, and *then* despawn or clone. |
/// This will also queue up clones of the relationship sources if the [`EntityCloner`](crate::entity::EntityCloner) is configured | ||
/// to spawn recursively. | ||
pub fn clone_relationship_target<T: RelationshipTarget>( | ||
_commands: &mut Commands, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why _commands
exists here if this is unused.
@@ -2663,6 +2663,102 @@ mod tests { | |||
World::new().register_component::<A>(); | |||
} | |||
|
|||
#[test] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this feels like a bad place for these tests in general (3k lines?!), but given that all of the other component derive tests are here that's a change for a follow-up PR.
Fixes #17535
Bevy's approach to handling "entity mapping" during spawning and cloning needs some work. The addition of Relations both introduced a new "duplicate entities" bug when spawning scenes in the scene system and made the weaknesses of the current mapping system exceedingly clear:
Additionally, while our new Entity cloning system is already pretty great, it has some areas we can make better:
Scene(World)
(which would give us a nice performance boost: fewer archetype moves, less reflection overhead).Solution
Improved Entity Mapping
First, components now have first-class "entity visiting and mapping" behavior:
Any field with the
#[entities]
annotation will be viewable and mappable when cloning and spawning scenes.Compare that to what was required before!
Additionally, for relationships
#[entities]
is implied, meaning this "just works" in scenes and cloning:Note that Component does not implement
VisitEntities
directly. Instead, it hasComponent::visit_entities
andComponent::visit_entities_mut
methods. This is for a few reasons:VisitEntities for C: Component
because that would conflict with our impl of VisitEntities for anything that implementsIntoIterator<Item=Entity>
. Preserving that impl is more important from a UX perspective.Component: VisitEntities
VisitEntities in the Component derive, as that would increase the burden of manual Component trait implementors.Component
impl, we can make it harder to call naturally / unavailable to autocomplete usingfn visit_entities_mut(this: &mut Self, ...)
.ReflectComponent::apply_or_insert
is nowReflectComponent::apply_or_insert_mapped
. By moving mapping inside this impl, we remove the need to go through the reflection system to do entity mapping, meaning we no longer need to create a clone of the target component, map the entities in that component, and patch those values on top. This will make spawning mapped entities much faster (The defaultComponent::visit_entities_mut
impl is an inlined empty function, so it will incur no overhead for unmapped entities).The Bug Fix
To solve #17535, spawning code now skips entities with the new
ComponentCloneBehavior::Ignore
andComponentCloneBehavior::RelationshipTarget
variants (note RelationshipTarget is a temporary "workaround" variant that allows scenes to skip these components. This is a temporary workaround that can be removed as these cases should really be using EntityCloner logic, which should be done in a followup PR. When that is done,ComponentCloneBehavior::RelationshipTarget
can be merged into the normalComponentCloneBehavior::Custom
).Improved Cloning
Option<ComponentCloneHandler>
has been replaced byComponentCloneBehavior
, which encodes additional intent and context (ex:Default
,Ignore
,Custom
,RelationshipTarget
(this last one is temporary)).RelatonshipTarget::RECURSIVE_SPAWN
is nowRelationshipTarget::LINKED_SPAWN
, and this field is used when cloning relationship targets to determine if cloning should happen recursively. The newLINKED_SPAWN
term was picked to make it more generically applicable across spawning and cloning scenarios.Next Steps
Scene(World)
scenes. This would yield significant performance benefits (no archetype moves, less reflection overhead).