Skip to content

Add CloneBundle command #3820

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

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,105 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
self
}

/// Clones a [`Bundle`] to a target entity.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component, Clone)]
/// # struct Dummy;
/// #
/// # #[derive(Bundle, Clone)]
/// # struct Characteristics {
Copy link
Member

@alice-i-cecile alice-i-cecile Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we should store multiple actual value-ful components here rather than fussing with Dummy. Just toss in strength / dexterity / intelligence perhaps?

/// # _dummy: Dummy
/// # }
/// #
/// # #[derive(Component)]
/// # struct Offspring { entity: Entity }
/// #
/// fn duplicate(mut commands: Commands, query: Query<(Entity, &Offspring)>) {
/// for (source, Offspring { entity: target }) in query.iter() {
/// commands.entity(source).clone_bundle_to::<Characteristics>(*target);
/// }
/// }
/// ```
pub fn clone_bundle_to<T>(&mut self, target: Entity) -> &mut Self
where
T: Bundle + Clone,
{
assert!(
self.commands.entities.contains(target),
"Attempting to clone from entity {:?} to entity {:?}, which doesn't exist.",
self.entity,
target
);
self.commands.add(CloneBundle::<T> {
source: self.entity,
target,
_bundle: PhantomData,
});
self
}

/// Clones a [`Component`] to a target entity.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component, Clone)]
/// # struct Infected;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be much better motivated if it stored a value. Perhaps Infected{remaining_duration: Duration}?

Copy link
Contributor Author

@hlb8122 hlb8122 Feb 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alice-i-cecile At moment I've followed the other examples and hidden the Component in use (see remove/remove_bundle example). Just to clarify: you'd like me to un-# this and give it a field?

/// #
/// # #[derive(Component)]
/// # struct Nearest { entity: Entity }
/// #
/// fn contagion(mut commands: Commands, query: Query<(Entity, &Nearest), With<Infected>>) {
/// for (source, Nearest { entity: target }) in query.iter() {
/// commands.entity(source).clone_to::<Infected>(*target);
/// }
/// }
/// ```
pub fn clone_to<C>(&mut self, target: Entity) -> &mut Self
where
C: Component + Clone,
{
self.clone_bundle_to::<(C,)>(target)
}

/// Clones a [`Bundle`] from a source entity.
///
/// See [`clone_bundle_to`](EntityCommands::clone_bundle_to).
pub fn clone_bundle_from<T>(&mut self, source: Entity) -> &mut Self
where
T: Bundle + Clone,
{
assert!(
self.commands.entities.contains(source),
"Attempting to clone from entity {:?} to entity {:?}, which doesn't exist.",
source,
self.entity
);
self.commands.add(CloneBundle::<T> {
source,
target: self.entity,
_bundle: PhantomData,
});
self
}

/// Clones a [`Component`] from a source entity.
///
/// See [`clone_to`](EntityCommands::clone_to)
pub fn clone_from<C>(&mut self, source: Entity) -> &mut Self
where
C: Component + Clone,
{
self.clone_bundle_from::<(C,)>(source)
}

/// Despawns the entity.
///
/// See [`World::despawn`] for more details.
Expand Down Expand Up @@ -692,6 +791,54 @@ where
}
}

pub struct CloneBundle<T> {
pub source: Entity,
pub target: Entity,
_bundle: PhantomData<T>,
}

impl<T> CloneBundle<T> {
/// Creates a new [`CloneBundle`] with given source and target IDs.
pub fn new(source: Entity, target: Entity) -> Self {
Self {
source,
target,
_bundle: PhantomData,
}
}
}

impl<T> Command for CloneBundle<T>
where
T: Bundle + Clone + 'static,
{
fn write(self, world: &mut World) {
let mut source_mut = if let Some(some) = world.get_entity_mut(self.source) {
some
} else {
panic!("Could not clone a bundle (of type `{}`) from entity {:?} because it doesn't exist in this World.\n\
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::<T>(), self.source);
};

let bundle = if let Some(some) = source_mut.remove_bundle::<T>() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will trigger "removed events" (in RemovedComponents) for each component in the bundle, which is undesirable for a clone operation.

some
} else {
return;
};
source_mut.insert_bundle(bundle.clone());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will trigger "added" and "changed" events for each component in the bundle, which is undesirable for a clone operation.


let mut target_mut = if let Some(some) = world.get_entity_mut(self.target) {
some
} else {
panic!("Could not clone a bundle (of type `{}`) into entity {:?} because it doesn't exist in this World.\n\
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::<T>(), self.source);
};
target_mut.insert_bundle(bundle);
}
}

#[derive(Debug)]
pub struct Insert<T> {
pub entity: Entity,
Expand Down