Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/system/commands/entity_command.rs
9367 views
1
//! Contains the definition of the [`EntityCommand`] trait,
2
//! as well as the blanket implementation of the trait for closures.
3
//!
4
//! It also contains functions that return closures for use with
5
//! [`EntityCommands`](crate::system::EntityCommands).
6
7
use alloc::{string::ToString, vec::Vec};
8
#[cfg(not(feature = "trace"))]
9
use log::info;
10
#[cfg(feature = "trace")]
11
use tracing::info;
12
13
use crate::{
14
bundle::{Bundle, InsertMode},
15
change_detection::MaybeLocation,
16
component::{Component, ComponentId},
17
entity::{Entity, EntityClonerBuilder, OptIn, OptOut},
18
name::Name,
19
observer::IntoEntityObserver,
20
relationship::RelationshipHookMode,
21
world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld},
22
};
23
use bevy_ptr::{move_as_ptr, OwningPtr};
24
25
/// A command which gets executed for a given [`Entity`].
26
///
27
/// Should be used with [`EntityCommands::queue`](crate::system::EntityCommands::queue).
28
///
29
/// The `Out` generic parameter is the returned "output" of the command.
30
///
31
/// # Examples
32
///
33
/// ```
34
/// # use std::collections::HashSet;
35
/// # use bevy_ecs::prelude::*;
36
/// use bevy_ecs::system::EntityCommand;
37
/// #
38
/// # #[derive(Component, PartialEq)]
39
/// # struct Name(String);
40
/// # impl Name {
41
/// # fn new(s: String) -> Self { Name(s) }
42
/// # fn as_str(&self) -> &str { &self.0 }
43
/// # }
44
///
45
/// #[derive(Resource, Default)]
46
/// struct Counter(i64);
47
///
48
/// /// A `Command` which names an entity based on a global counter.
49
/// fn count_name(mut entity: EntityWorldMut) {
50
/// // Get the current value of the counter, and increment it for next time.
51
/// let i = {
52
/// let mut counter = entity.resource_mut::<Counter>();
53
/// let i = counter.0;
54
/// counter.0 += 1;
55
/// i
56
/// };
57
/// // Name the entity after the value of the counter.
58
/// entity.insert(Name::new(format!("Entity #{i}")));
59
/// }
60
///
61
/// // App creation boilerplate omitted...
62
/// # let mut world = World::new();
63
/// # world.init_resource::<Counter>();
64
/// #
65
/// # let mut setup_schedule = Schedule::default();
66
/// # setup_schedule.add_systems(setup);
67
/// # let mut assert_schedule = Schedule::default();
68
/// # assert_schedule.add_systems(assert_names);
69
/// #
70
/// # setup_schedule.run(&mut world);
71
/// # assert_schedule.run(&mut world);
72
///
73
/// fn setup(mut commands: Commands) {
74
/// commands.spawn_empty().queue(count_name);
75
/// commands.spawn_empty().queue(count_name);
76
/// }
77
///
78
/// fn assert_names(named: Query<&Name>) {
79
/// // We use a HashSet because we do not care about the order.
80
/// let names: HashSet<_> = named.iter().map(Name::as_str).collect();
81
/// assert_eq!(names, HashSet::from_iter(["Entity #0", "Entity #1"]));
82
/// }
83
/// ```
84
pub trait EntityCommand<Out = ()>: Send + 'static {
85
/// Executes this command for the given [`Entity`].
86
fn apply(self, entity: EntityWorldMut) -> Out;
87
}
88
89
/// An error that occurs when running an [`EntityCommand`] on a specific entity.
90
#[derive(thiserror::Error, Debug)]
91
pub enum EntityCommandError<E> {
92
/// The entity this [`EntityCommand`] tried to run on could not be fetched.
93
#[error(transparent)]
94
EntityFetchError(#[from] EntityMutableFetchError),
95
/// An error that occurred while running the [`EntityCommand`].
96
#[error("{0}")]
97
CommandFailed(E),
98
}
99
100
impl<Out, F> EntityCommand<Out> for F
101
where
102
F: FnOnce(EntityWorldMut) -> Out + Send + 'static,
103
{
104
fn apply(self, entity: EntityWorldMut) -> Out {
105
self(entity)
106
}
107
}
108
109
/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.
110
#[track_caller]
111
pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
112
let caller = MaybeLocation::caller();
113
move |mut entity: EntityWorldMut| {
114
move_as_ptr!(bundle);
115
entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
116
}
117
}
118
119
/// An [`EntityCommand`] that adds a dynamic component to an entity.
120
///
121
/// # Safety
122
///
123
/// - [`ComponentId`] must be from the same world as the target entity.
124
/// - `T` must have the same layout as the one passed during `component_id` creation.
125
#[track_caller]
126
pub unsafe fn insert_by_id<T: Send + 'static>(
127
component_id: ComponentId,
128
value: T,
129
mode: InsertMode,
130
) -> impl EntityCommand {
131
let caller = MaybeLocation::caller();
132
move |mut entity: EntityWorldMut| {
133
// SAFETY:
134
// - `component_id` safety is ensured by the caller
135
// - `ptr` is valid within the `make` block
136
OwningPtr::make(value, |ptr| unsafe {
137
entity.insert_by_id_with_caller(
138
component_id,
139
ptr,
140
mode,
141
caller,
142
RelationshipHookMode::Run,
143
);
144
});
145
}
146
}
147
148
/// An [`EntityCommand`] that adds a component to an entity using
149
/// the component's [`FromWorld`] implementation.
150
///
151
/// `T::from_world` will only be invoked if the component will actually be inserted.
152
/// In other words, `T::from_world` will *not* be invoked if `mode` is [`InsertMode::Keep`]
153
/// and the entity already has the component.
154
#[track_caller]
155
pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
156
let caller = MaybeLocation::caller();
157
move |mut entity: EntityWorldMut| {
158
if !(mode == InsertMode::Keep && entity.contains::<T>()) {
159
let value = entity.world_scope(|world| T::from_world(world));
160
move_as_ptr!(value);
161
entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
162
}
163
}
164
}
165
166
/// An [`EntityCommand`] that adds a component to an entity using
167
/// some function that returns the component.
168
///
169
/// The function will only be invoked if the component will actually be inserted.
170
/// In other words, the function will *not* be invoked if `mode` is [`InsertMode::Keep`]
171
/// and the entity already has the component.
172
#[track_caller]
173
pub fn insert_with<T: Component, F>(component_fn: F, mode: InsertMode) -> impl EntityCommand
174
where
175
F: FnOnce() -> T + Send + 'static,
176
{
177
let caller = MaybeLocation::caller();
178
move |mut entity: EntityWorldMut| {
179
if !(mode == InsertMode::Keep && entity.contains::<T>()) {
180
let bundle = component_fn();
181
move_as_ptr!(bundle);
182
entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
183
}
184
}
185
}
186
187
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
188
#[track_caller]
189
pub fn remove<T: Bundle>() -> impl EntityCommand {
190
let caller = MaybeLocation::caller();
191
move |mut entity: EntityWorldMut| {
192
entity.remove_with_caller::<T>(caller);
193
}
194
}
195
196
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,
197
/// as well as the required components for each component removed.
198
#[track_caller]
199
pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {
200
let caller = MaybeLocation::caller();
201
move |mut entity: EntityWorldMut| {
202
entity.remove_with_requires_with_caller::<T>(caller);
203
}
204
}
205
206
/// An [`EntityCommand`] that removes a dynamic component from an entity.
207
#[track_caller]
208
pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
209
let caller = MaybeLocation::caller();
210
move |mut entity: EntityWorldMut| {
211
entity.remove_by_id_with_caller(component_id, caller);
212
}
213
}
214
215
/// An [`EntityCommand`] that removes all components from an entity.
216
#[track_caller]
217
pub fn clear() -> impl EntityCommand {
218
let caller = MaybeLocation::caller();
219
move |mut entity: EntityWorldMut| {
220
entity.clear_with_caller(caller);
221
}
222
}
223
224
/// An [`EntityCommand`] that removes all components from an entity,
225
/// except for those in the given [`Bundle`].
226
#[track_caller]
227
pub fn retain<T: Bundle>() -> impl EntityCommand {
228
let caller = MaybeLocation::caller();
229
move |mut entity: EntityWorldMut| {
230
entity.retain_with_caller::<T>(caller);
231
}
232
}
233
234
/// An [`EntityCommand`] that despawns an entity.
235
///
236
/// # Note
237
///
238
/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)
239
/// that is configured to despawn descendants.
240
///
241
/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
242
#[track_caller]
243
pub fn despawn() -> impl EntityCommand {
244
let caller = MaybeLocation::caller();
245
move |entity: EntityWorldMut| {
246
entity.despawn_with_caller(caller);
247
}
248
}
249
250
/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)
251
/// watching for an [`EntityEvent`](crate::event::EntityEvent) of type `E` whose
252
/// [`event_target`](crate::event::EntityEvent::event_target) targets this entity.
253
///
254
/// Accepts any type that implements [`IntoEntityObserver`], including:
255
/// - Observer systems (closures or functions implementing [`IntoObserverSystem`](crate::system::IntoObserverSystem))
256
/// - Observer systems with run conditions (via `.run_if()`)
257
#[track_caller]
258
pub fn observe<M>(observer: impl IntoEntityObserver<M>) -> impl EntityCommand {
259
let caller = MaybeLocation::caller();
260
move |mut entity: EntityWorldMut| {
261
entity.observe_with_caller(observer, caller);
262
}
263
}
264
265
/// An [`EntityCommand`] that clones parts of an entity onto another entity,
266
/// configured through [`EntityClonerBuilder`].
267
///
268
/// This builder tries to clone every component from the source entity except
269
/// for components that were explicitly denied, for example by using the
270
/// [`deny`](EntityClonerBuilder<OptOut>::deny) method.
271
///
272
/// Required components are not considered by denied components and must be
273
/// explicitly denied as well if desired.
274
pub fn clone_with_opt_out(
275
target: Entity,
276
config: impl FnOnce(&mut EntityClonerBuilder<OptOut>) + Send + Sync + 'static,
277
) -> impl EntityCommand {
278
move |mut entity: EntityWorldMut| {
279
entity.clone_with_opt_out(target, config);
280
}
281
}
282
283
/// An [`EntityCommand`] that clones parts of an entity onto another entity,
284
/// configured through [`EntityClonerBuilder`].
285
///
286
/// This builder tries to clone every component that was explicitly allowed
287
/// from the source entity, for example by using the
288
/// [`allow`](EntityClonerBuilder<OptIn>::allow) method.
289
///
290
/// Required components are also cloned when the target entity does not contain them.
291
pub fn clone_with_opt_in(
292
target: Entity,
293
config: impl FnOnce(&mut EntityClonerBuilder<OptIn>) + Send + Sync + 'static,
294
) -> impl EntityCommand {
295
move |mut entity: EntityWorldMut| {
296
entity.clone_with_opt_in(target, config);
297
}
298
}
299
300
/// An [`EntityCommand`] that clones the specified components of an entity
301
/// and inserts them into another entity.
302
pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {
303
move |mut entity: EntityWorldMut| {
304
entity.clone_components::<B>(target);
305
}
306
}
307
308
/// An [`EntityCommand`] moves the specified components of this entity into another entity.
309
///
310
/// Components with [`Ignore`] clone behavior will not be moved, while components that
311
/// have a [`Custom`] clone behavior will be cloned using it and then removed from the source entity.
312
/// All other components will be moved without any other special handling.
313
///
314
/// Note that this will trigger `on_remove` hooks/observers on this entity and `on_insert`/`on_add` hooks/observers on the target entity.
315
///
316
/// # Panics
317
///
318
/// The command will panic when applied if the target entity does not exist.
319
///
320
/// [`Ignore`]: crate::component::ComponentCloneBehavior::Ignore
321
/// [`Custom`]: crate::component::ComponentCloneBehavior::Custom
322
pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {
323
move |mut entity: EntityWorldMut| {
324
entity.move_components::<B>(target);
325
}
326
}
327
328
/// An [`EntityCommand`] that logs the components of an entity.
329
pub fn log_components() -> impl EntityCommand {
330
move |entity: EntityWorldMut| {
331
let name = entity.get::<Name>().map(ToString::to_string);
332
let id = entity.id();
333
let mut components: Vec<_> = entity
334
.world()
335
.inspect_entity(id)
336
.expect("Entity existence is verified before an EntityCommand is executed")
337
.map(|info| info.name().to_string())
338
.collect();
339
components.sort();
340
341
#[cfg(not(feature = "debug"))]
342
{
343
let component_count = components.len();
344
#[cfg(feature = "trace")]
345
{
346
if let Some(name) = name {
347
info!(id=?id, name=?name, ?component_count, "log_components. Enable the `debug` feature to log component names.");
348
} else {
349
info!(id=?id, ?component_count, "log_components. Enable the `debug` feature to log component names.");
350
}
351
}
352
#[cfg(not(feature = "trace"))]
353
{
354
let name = name
355
.map(|name| alloc::format!(" ({name})"))
356
.unwrap_or_default();
357
info!("Entity {id}{name}: {component_count} components. Enable the `debug` feature to log component names.");
358
}
359
}
360
361
#[cfg(feature = "debug")]
362
{
363
#[cfg(feature = "trace")]
364
{
365
if let Some(name) = name {
366
info!(id=?id, name=?name, ?components, "log_components");
367
} else {
368
info!(id=?id, ?components, "log_components");
369
}
370
}
371
#[cfg(not(feature = "trace"))]
372
{
373
let name = name
374
.map(|name| alloc::format!(" ({name})"))
375
.unwrap_or_default();
376
info!("Entity {id}{name}: {components:?}");
377
}
378
}
379
}
380
}
381
382