Skip to content

Commit 5a94beb

Browse files
authored
Extend cloning functionality and add convenience methods to EntityWorldMut and EntityCommands (#16826)
## Objective Thanks to @eugineerd's work on entity cloning (#16132), we now have a robust way to copy components between entities. We can extend this to implement some useful functionality that would have been more complicated before. Closes #15350. ## Solution `EntityCloneBuilder` now automatically includes required components alongside any component added/removed from the component filter. Added the following methods to `EntityCloneBuilder`: - `move_components` - `without_required_components` Added the following methods to `EntityWorldMut` and `EntityCommands`: - `clone_with` - `clone_components` - `move_components` Also added `clone_and_spawn` and `clone_and_spawn_with` to `EntityWorldMut` (`EntityCommands` already had them). ## Showcase ``` assert_eq!(world.entity(entity_a).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_b).get::<B>(), None); world.entity_mut(entity_a).clone_components::<B>(entity_b); assert_eq!(world.entity(entity_a).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_b).get::<B>(), Some(&B)); assert_eq!(world.entity(entity_a).get::<C>(), Some(&C(5))); assert_eq!(world.entity(entity_b).get::<C>(), None); world.entity_mut(entity_a).move_components::<C>(entity_b); assert_eq!(world.entity(entity_a).get::<C>(), None); assert_eq!(world.entity(entity_b).get::<C>(), Some(&C(5))); ```
1 parent 74e793d commit 5a94beb

File tree

3 files changed

+489
-70
lines changed

3 files changed

+489
-70
lines changed

crates/bevy_ecs/src/entity/clone_entities.rs

Lines changed: 133 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub struct EntityCloner {
1818
filter_allows_components: bool,
1919
filter: Arc<HashSet<ComponentId>>,
2020
clone_handlers_overrides: Arc<HashMap<ComponentId, ComponentCloneHandler>>,
21+
move_components: bool,
2122
}
2223

2324
impl EntityCloner {
@@ -35,17 +36,21 @@ impl EntityCloner {
3536
.filter(|id| self.is_cloning_allowed(id)),
3637
);
3738

38-
for component in components {
39+
for component in &components {
3940
let global_handlers = world.components().get_component_clone_handlers();
40-
let handler = match self.clone_handlers_overrides.get(&component) {
41-
None => global_handlers.get_handler(component),
41+
let handler = match self.clone_handlers_overrides.get(component) {
42+
None => global_handlers.get_handler(*component),
4243
Some(ComponentCloneHandler::Default) => global_handlers.get_default_handler(),
4344
Some(ComponentCloneHandler::Ignore) => component_clone_ignore,
4445
Some(ComponentCloneHandler::Custom(handler)) => *handler,
4546
};
46-
self.component_id = Some(component);
47+
self.component_id = Some(*component);
4748
(handler)(&mut world.into(), self);
4849
}
50+
51+
if self.move_components {
52+
world.entity_mut(self.source).remove_by_ids(&components);
53+
}
4954
}
5055

5156
fn is_cloning_allowed(&self, component: &ComponentId) -> bool {
@@ -145,6 +150,8 @@ pub struct EntityCloneBuilder<'w> {
145150
filter_allows_components: bool,
146151
filter: HashSet<ComponentId>,
147152
clone_handlers_overrides: HashMap<ComponentId, ComponentCloneHandler>,
153+
attach_required_components: bool,
154+
move_components: bool,
148155
}
149156

150157
impl<'w> EntityCloneBuilder<'w> {
@@ -155,6 +162,8 @@ impl<'w> EntityCloneBuilder<'w> {
155162
filter_allows_components: false,
156163
filter: Default::default(),
157164
clone_handlers_overrides: Default::default(),
165+
attach_required_components: true,
166+
move_components: false,
158167
}
159168
}
160169

@@ -165,6 +174,7 @@ impl<'w> EntityCloneBuilder<'w> {
165174
filter_allows_components,
166175
filter,
167176
clone_handlers_overrides,
177+
move_components,
168178
..
169179
} = self;
170180

@@ -175,29 +185,49 @@ impl<'w> EntityCloneBuilder<'w> {
175185
filter_allows_components,
176186
filter: Arc::new(filter),
177187
clone_handlers_overrides: Arc::new(clone_handlers_overrides),
188+
move_components,
178189
}
179190
.clone_entity(world);
180191

181192
world.flush_commands();
182193
}
183194

195+
/// By default, any components allowed/denied through the filter will automatically
196+
/// allow/deny all of their required components.
197+
///
198+
/// This method allows for a scoped mode where any changes to the filter
199+
/// will not involve required components.
200+
pub fn without_required_components(
201+
&mut self,
202+
builder: impl FnOnce(&mut EntityCloneBuilder) + Send + Sync + 'static,
203+
) -> &mut Self {
204+
self.attach_required_components = false;
205+
builder(self);
206+
self.attach_required_components = true;
207+
self
208+
}
209+
210+
/// Sets whether the cloner should remove any components that were cloned,
211+
/// effectively moving them from the source entity to the target.
212+
///
213+
/// This is disabled by default.
214+
///
215+
/// The setting only applies to components that are allowed through the filter
216+
/// at the time [`EntityCloneBuilder::clone_entity`] is called.
217+
pub fn move_components(&mut self, enable: bool) -> &mut Self {
218+
self.move_components = enable;
219+
self
220+
}
221+
184222
/// Adds all components of the bundle to the list of components to clone.
185223
///
186224
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
187225
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
188226
pub fn allow<T: Bundle>(&mut self) -> &mut Self {
189-
if self.filter_allows_components {
190-
T::get_component_ids(self.world.components(), &mut |id| {
191-
if let Some(id) = id {
192-
self.filter.insert(id);
193-
}
194-
});
195-
} else {
196-
T::get_component_ids(self.world.components(), &mut |id| {
197-
if let Some(id) = id {
198-
self.filter.remove(&id);
199-
}
200-
});
227+
let bundle = self.world.register_bundle::<T>();
228+
let ids = bundle.explicit_components().to_owned();
229+
for id in ids {
230+
self.filter_allow(id);
201231
}
202232
self
203233
}
@@ -207,12 +237,8 @@ impl<'w> EntityCloneBuilder<'w> {
207237
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
208238
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
209239
pub fn allow_by_ids(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
210-
if self.filter_allows_components {
211-
self.filter.extend(ids);
212-
} else {
213-
ids.into_iter().for_each(|id| {
214-
self.filter.remove(&id);
215-
});
240+
for id in ids {
241+
self.filter_allow(id);
216242
}
217243
self
218244
}
@@ -222,15 +248,10 @@ impl<'w> EntityCloneBuilder<'w> {
222248
/// Note that all components are allowed by default, to clone only explicitly allowed components make sure to call
223249
/// [`deny_all`](`Self::deny_all`) before calling any of the `allow` methods.
224250
pub fn allow_by_type_ids(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
225-
let ids = ids
226-
.into_iter()
227-
.filter_map(|id| self.world.components().get_id(id));
228-
if self.filter_allows_components {
229-
self.filter.extend(ids);
230-
} else {
231-
ids.into_iter().for_each(|id| {
232-
self.filter.remove(&id);
233-
});
251+
for type_id in ids {
252+
if let Some(id) = self.world.components().get_id(type_id) {
253+
self.filter_allow(id);
254+
}
234255
}
235256
self
236257
}
@@ -244,45 +265,28 @@ impl<'w> EntityCloneBuilder<'w> {
244265

245266
/// Disallows all components of the bundle from being cloned.
246267
pub fn deny<T: Bundle>(&mut self) -> &mut Self {
247-
if self.filter_allows_components {
248-
T::get_component_ids(self.world.components(), &mut |id| {
249-
if let Some(id) = id {
250-
self.filter.remove(&id);
251-
}
252-
});
253-
} else {
254-
T::get_component_ids(self.world.components(), &mut |id| {
255-
if let Some(id) = id {
256-
self.filter.insert(id);
257-
}
258-
});
268+
let bundle = self.world.register_bundle::<T>();
269+
let ids = bundle.explicit_components().to_owned();
270+
for id in ids {
271+
self.filter_deny(id);
259272
}
260273
self
261274
}
262275

263276
/// Extends the list of components that shouldn't be cloned.
264277
pub fn deny_by_ids(&mut self, ids: impl IntoIterator<Item = ComponentId>) -> &mut Self {
265-
if self.filter_allows_components {
266-
ids.into_iter().for_each(|id| {
267-
self.filter.remove(&id);
268-
});
269-
} else {
270-
self.filter.extend(ids);
278+
for id in ids {
279+
self.filter_deny(id);
271280
}
272281
self
273282
}
274283

275284
/// Extends the list of components that shouldn't be cloned by type ids.
276285
pub fn deny_by_type_ids(&mut self, ids: impl IntoIterator<Item = TypeId>) -> &mut Self {
277-
let ids = ids
278-
.into_iter()
279-
.filter_map(|id| self.world.components().get_id(id));
280-
if self.filter_allows_components {
281-
ids.into_iter().for_each(|id| {
282-
self.filter.remove(&id);
283-
});
284-
} else {
285-
self.filter.extend(ids);
286+
for type_id in ids {
287+
if let Some(id) = self.world.components().get_id(type_id) {
288+
self.filter_deny(id);
289+
}
286290
}
287291
self
288292
}
@@ -315,11 +319,52 @@ impl<'w> EntityCloneBuilder<'w> {
315319
}
316320
self
317321
}
322+
323+
/// Helper function that allows a component through the filter.
324+
fn filter_allow(&mut self, id: ComponentId) {
325+
if self.filter_allows_components {
326+
self.filter.insert(id);
327+
} else {
328+
self.filter.remove(&id);
329+
}
330+
if self.attach_required_components {
331+
if let Some(info) = self.world.components().get_info(id) {
332+
for required_id in info.required_components().iter_ids() {
333+
if self.filter_allows_components {
334+
self.filter.insert(required_id);
335+
} else {
336+
self.filter.remove(&required_id);
337+
}
338+
}
339+
}
340+
}
341+
}
342+
343+
/// Helper function that disallows a component through the filter.
344+
fn filter_deny(&mut self, id: ComponentId) {
345+
if self.filter_allows_components {
346+
self.filter.remove(&id);
347+
} else {
348+
self.filter.insert(id);
349+
}
350+
if self.attach_required_components {
351+
if let Some(info) = self.world.components().get_info(id) {
352+
for required_id in info.required_components().iter_ids() {
353+
if self.filter_allows_components {
354+
self.filter.remove(&required_id);
355+
} else {
356+
self.filter.insert(required_id);
357+
}
358+
}
359+
}
360+
}
361+
}
318362
}
319363

320364
#[cfg(test)]
321365
mod tests {
322366
use crate::{self as bevy_ecs, component::Component, entity::EntityCloneBuilder, world::World};
367+
use bevy_ecs_macros::require;
323368

324369
#[cfg(feature = "bevy_reflect")]
325370
#[test]
@@ -520,4 +565,34 @@ mod tests {
520565
assert!(world.get::<B>(e_clone).is_none());
521566
assert!(world.get::<C>(e_clone).is_none());
522567
}
568+
569+
#[test]
570+
fn clone_entity_with_required_components() {
571+
#[derive(Component, Clone, PartialEq, Debug)]
572+
#[require(B)]
573+
struct A;
574+
575+
#[derive(Component, Clone, PartialEq, Debug, Default)]
576+
#[require(C(|| C(5)))]
577+
struct B;
578+
579+
#[derive(Component, Clone, PartialEq, Debug)]
580+
struct C(u32);
581+
582+
let mut world = World::default();
583+
584+
let e = world.spawn(A).id();
585+
let e_clone = world.spawn_empty().id();
586+
587+
let mut builder = EntityCloneBuilder::new(&mut world);
588+
builder.deny_all();
589+
builder.without_required_components(|builder| {
590+
builder.allow::<B>();
591+
});
592+
builder.clone_entity(e, e_clone);
593+
594+
assert_eq!(world.entity(e_clone).get::<A>(), None);
595+
assert_eq!(world.entity(e_clone).get::<B>(), Some(&B));
596+
assert_eq!(world.entity(e_clone).get::<C>(), Some(&C(5)));
597+
}
523598
}

0 commit comments

Comments
 (0)