Skip to content

Add MoveBundle command #3717

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 3 commits 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
152 changes: 146 additions & 6 deletions crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,95 @@ impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
self
}

/// Moves a [`Bundle`] to a target entity.
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.

/// {Moves/Clones} a [`{Component/Bundle}`] {to/from} a {target/source} entity.

I've picked this for all the documentation across the MoveBundle and CloneBundle PR. If anyone has some better suggestions I can change it because, as is, this is pretty aburpt.

///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component)]
/// # struct Dummy;
/// #
/// # #[derive(Bundle)]
/// # struct Cargo {
/// # _dummy: Dummy
/// # }
/// #
/// # #[derive(Component)]
/// # struct NextDestination { entity: Entity }
/// #
/// fn transport(mut commands: Commands, query: Query<(Entity, &NextDestination)>) {
/// for (source, NextDestination { entity: target }) in query.iter() {
/// commands.entity(source).move_bundle_to::<Cargo>(*target);
/// }
/// }
/// ```
pub fn move_bundle_to<T: Bundle>(&mut self, target: Entity) -> &mut Self {
assert!(
self.commands.entities.contains(target),
"Attempting to move bundle {:?} from entity {:?} to entity {:?}, which doesn't exist.",
std::any::type_name::<T>(),
self.entity,
target
);
self.commands.add(MoveBundle::<T> {
source: self.entity,
target,
_bundle: PhantomData,
});
self
}

/// Moves a [`Component`] to a target entity.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Component)]
/// # struct Charge;
/// #
/// # #[derive(Component)]
/// # struct ConnectedTo { entity: Entity }
/// #
/// fn conduction(mut commands: Commands, query: Query<(Entity, &ConnectedTo), With<Charge>>) {
/// for (source, ConnectedTo { entity: target }) in query.iter() {
/// commands.entity(source).move_to::<Charge>(*target);
/// }
/// }
/// ```
pub fn move_to<C: Component>(&mut self, target: Entity) -> &mut Self {
self.move_bundle_to::<(C,)>(target)
}

/// Moves a [`Bundle`] from a source entity.
///
/// See [`move_bundle_to`](EntityCommands::move_bundle_to).
pub fn move_bundle_from<T: Bundle>(&mut self, source: Entity) -> &mut Self {
assert!(
self.commands.entities.contains(source),
"Attempting to move bundle {:?} from entity {:?} to entity {:?}, which doesn't exist.",
std::any::type_name::<T>(),
source,
self.entity
);
self.commands.add(MoveBundle::<T> {
source,
target: self.entity,
_bundle: PhantomData,
});
self
}

/// Moves a [`Component`] from a source entity.
///
/// See [`move_to`](EntityCommands::move_to).
pub fn move_from<C: Component>(&mut self, source: Entity) -> &mut Self {
self.move_bundle_from::<(C,)>(source)
}

/// Despawns the entity.
///
/// See [`World::despawn`] for more details.
Expand Down Expand Up @@ -672,6 +761,19 @@ impl Command for Despawn {
}
}

/// Utility method for formatting common command panic messages.
fn panic_fmt<Ty>(
verb: &'static str,
trait_name: &'static str,
preposition: &'static str,
entity: Entity,
) -> ! {
panic!("Could not {} a {} (of type `{}`) {} 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",
verb, trait_name, std::any::type_name::<Ty>(), preposition, entity);
}

pub struct InsertBundle<T> {
pub entity: Entity,
pub bundle: T,
Expand All @@ -685,9 +787,49 @@ where
if let Some(mut entity) = world.get_entity_mut(self.entity) {
entity.insert_bundle(self.bundle);
} else {
panic!("Could not insert a bundle (of type `{}`) for 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.entity);
panic_fmt::<T>("insert", "bundle", "for", self.entity);
}
}
}

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

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

impl<T> Command for MoveBundle<T>
where
T: Bundle + 'static,
{
fn write(self, world: &mut World) {
let bundle_opt = if let Some(mut source) = world.get_entity_mut(self.source) {
source.remove_bundle::<T>()
} else {
panic_fmt::<T>("move", "bundle", "from", self.source);
};

let bundle = if let Some(some) = bundle_opt {
some
} else {
return;
Copy link
Member

Choose a reason for hiding this comment

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

should it panic in this case?

Copy link
Member

Choose a reason for hiding this comment

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

If we're being consistent, yes.

I dislike this behavior, but that's for a different PR.

};

if let Some(mut target) = world.get_entity_mut(self.target) {
target.insert_bundle(bundle);
} else {
panic_fmt::<T>("move", "bundle", "to", self.target);
}
}
}
Expand All @@ -706,9 +848,7 @@ where
if let Some(mut entity) = world.get_entity_mut(self.entity) {
entity.insert(self.component);
} else {
panic!("Could not add a component (of type `{}`) to 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.entity);
panic_fmt::<T>("add", "component", "to", self.entity);
}
}
}
Expand Down