flax/
commands.rs

1use core::fmt;
2
3use alloc::{boxed::Box, format, vec::Vec};
4use anyhow::Context;
5
6use crate::{
7    buffer::MultiComponentBuffer,
8    component::{ComponentDesc, ComponentValue},
9    writer::{MissingDyn, SingleComponentWriter, WriteDedupDyn},
10    BatchSpawn, Component, Entity, EntityBuilder, World,
11};
12
13type DeferFn = Box<dyn Fn(&mut World) -> anyhow::Result<()> + Send + Sync>;
14
15/// A recorded action to be applied to the world.
16enum Command {
17    /// Spawn a new entity
18    Spawn(EntityBuilder),
19    AppendTo(EntityBuilder, Entity),
20    SpawnAt(EntityBuilder, Entity),
21    /// Spawn a batch of entities with the same components
22    SpawnBatch(BatchSpawn),
23    SpawnBatchAt(BatchSpawn, Vec<Entity>),
24    /// Set a component for an entity
25    Set {
26        id: Entity,
27        desc: ComponentDesc,
28        offset: usize,
29    },
30    SetDedup {
31        id: Entity,
32        desc: ComponentDesc,
33        offset: usize,
34        cmp: unsafe fn(*const u8, *const u8) -> bool,
35    },
36    SetMissing {
37        id: Entity,
38        desc: ComponentDesc,
39        offset: usize,
40    },
41    /// Despawn an entity
42    Despawn(Entity),
43    /// Remove a component from an entity
44    Remove {
45        id: Entity,
46        desc: ComponentDesc,
47    },
48
49    /// Execute an arbitrary function with a mutable reference to the world.
50    Defer(DeferFn),
51}
52
53impl fmt::Debug for Command {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        match self {
56            Self::Spawn(v) => f.debug_tuple("Spawn").field(v).finish(),
57            Self::SpawnAt(id, v) => f.debug_tuple("SpawnAt").field(&v).field(&id).finish(),
58            Self::AppendTo(id, v) => f.debug_tuple("AppendTo").field(&v).field(&id).finish(),
59            Self::SpawnBatch(batch) => f.debug_tuple("SpawnBatch").field(batch).finish(),
60            Self::SpawnBatchAt(batch, ids) => f
61                .debug_tuple("SpawnBatchAt")
62                .field(&batch)
63                .field(&ids.len())
64                .finish(),
65            Self::Set { id, desc, offset } => f
66                .debug_struct("Set")
67                .field("id", id)
68                .field("desc", desc)
69                .field("offset", offset)
70                .finish(),
71            Self::SetDedup {
72                id,
73                desc,
74                offset,
75                cmp: _,
76            } => f
77                .debug_struct("SetDedup")
78                .field("id", id)
79                .field("desc", desc)
80                .field("offset", offset)
81                .finish(),
82            Self::SetMissing { id, desc, offset } => f
83                .debug_struct("SetMissing")
84                .field("id", id)
85                .field("desc", desc)
86                .field("offset", offset)
87                .finish(),
88            Self::Despawn(arg0) => f.debug_tuple("Despawn").field(arg0).finish(),
89            Self::Remove {
90                id,
91                desc: component,
92            } => f
93                .debug_struct("Remove")
94                .field("id", id)
95                .field("component", component)
96                .finish(),
97            Self::Defer(_) => f.debug_tuple("Defer").field(&"...").finish(),
98        }
99    }
100}
101
102/// Records commands into the world.
103/// Allows insertion and removal of components when the world is not available
104/// mutably, such as in systems or during iteration.
105#[derive(Default)]
106pub struct CommandBuffer {
107    inserts: MultiComponentBuffer,
108    commands: Vec<Command>,
109}
110
111impl fmt::Debug for CommandBuffer {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        f.debug_struct("CommandBuffer")
114            .field("commands", &self.commands)
115            .finish()
116    }
117}
118
119/// Since all components are Send + Sync, the commandbuffer is as well
120unsafe impl Send for CommandBuffer {}
121unsafe impl Sync for CommandBuffer {}
122
123impl CommandBuffer {
124    /// Creates a new commandbuffer
125    pub fn new() -> Self {
126        Self::default()
127    }
128
129    /// Set a component for `id`.
130    pub fn set<T: ComponentValue>(
131        &mut self,
132        id: Entity,
133        component: Component<T>,
134        value: T,
135    ) -> &mut Self {
136        let offset = self.inserts.push(value);
137        self.commands.push(Command::Set {
138            id,
139            desc: component.desc(),
140            offset,
141        });
142
143        self
144    }
145
146    /// Convenience function for only setting the component if Some.
147    pub fn set_opt<T: ComponentValue>(
148        &mut self,
149        id: Entity,
150        component: Component<T>,
151        value: Option<T>,
152    ) -> &mut Self {
153        if let Some(value) = value {
154            self.set(id, component, value);
155        }
156        self
157    }
158
159    /// Set a component for `id`.
160    ///
161    /// Does not trigger a modification event if the value is the same
162    pub fn set_dedup<T: ComponentValue + PartialEq>(
163        &mut self,
164        id: Entity,
165        component: Component<T>,
166        value: T,
167    ) -> &mut Self {
168        let offset = self.inserts.push(value);
169        unsafe fn cmp<T: PartialEq>(a: *const u8, b: *const u8) -> bool {
170            let a = &*(a as *const T);
171            let b = &*(b as *const T);
172
173            a == b
174        }
175        self.commands.push(Command::SetDedup {
176            id,
177            desc: component.desc(),
178            offset,
179            cmp: cmp::<T>,
180        });
181
182        self
183    }
184
185    /// Set a component for `id` if it does not exist when the commandbuffer is applied.
186    ///
187    /// This avoid accidentally overwriting a component that was added by another system.
188    pub fn set_missing<T: ComponentValue>(
189        &mut self,
190        id: Entity,
191        component: Component<T>,
192        value: T,
193    ) -> &mut Self {
194        let offset = self.inserts.push(value);
195        self.commands.push(Command::SetMissing {
196            id,
197            desc: component.desc(),
198            offset,
199        });
200
201        self
202    }
203    /// Deferred removal of a component for `id`.
204    /// Unlike, [`World::remove`] it does not return the old value as that is
205    /// not known at call time.
206    pub fn remove<T: ComponentValue>(&mut self, id: Entity, component: Component<T>) -> &mut Self {
207        self.commands.push(Command::Remove {
208            id,
209            desc: component.desc(),
210        });
211
212        self
213    }
214
215    /// Spawn a new entity with the given components of the builder
216    pub fn spawn(&mut self, entity: impl Into<EntityBuilder>) -> &mut Self {
217        self.commands.push(Command::Spawn(entity.into()));
218
219        self
220    }
221
222    /// Spawn a new entity with the given components of the builder
223    pub fn spawn_at(&mut self, id: Entity, entity: impl Into<EntityBuilder>) -> &mut Self {
224        self.commands.push(Command::SpawnAt(entity.into(), id));
225
226        self
227    }
228
229    /// Append components to an existing entity
230    pub fn append_to(&mut self, id: Entity, entity: impl Into<EntityBuilder>) -> &mut Self {
231        self.commands.push(Command::AppendTo(entity.into(), id));
232
233        self
234    }
235
236    /// Spawn a new batch with the given components of the builder
237    pub fn spawn_batch(&mut self, chunk: impl Into<BatchSpawn>) -> &mut Self {
238        self.commands.push(Command::SpawnBatch(chunk.into()));
239
240        self
241    }
242
243    /// Spawn a new batch with the given components of the builder
244    pub fn spawn_batch_at(&mut self, ids: Vec<Entity>, chunk: impl Into<BatchSpawn>) -> &mut Self {
245        self.commands.push(Command::SpawnBatchAt(chunk.into(), ids));
246
247        self
248    }
249
250    /// Despawn an entity by id
251    pub fn despawn(&mut self, id: Entity) -> &mut Self {
252        self.commands.push(Command::Despawn(id));
253        self
254    }
255
256    /// Defer a function to execute upon the world.
257    ///
258    /// Errors will be propagated.
259    pub fn defer(
260        &mut self,
261        func: impl Fn(&mut World) -> anyhow::Result<()> + Send + Sync + 'static,
262    ) -> &mut Self {
263        self.commands.push(Command::Defer(Box::new(func)));
264        self
265    }
266
267    /// Applies all contents of the command buffer to the world.
268    /// The commandbuffer is cleared and can be reused.
269    pub fn apply(&mut self, world: &mut World) -> anyhow::Result<()> {
270        for cmd in self.commands.drain(..) {
271            match cmd {
272                Command::Spawn(mut entity) => {
273                    entity.spawn(world);
274                }
275                Command::SpawnAt(mut entity, id) => {
276                    entity
277                        .spawn_at(world, id)
278                        .map_err(|v| v.into_anyhow())
279                        .context("Failed to spawn entity")?;
280                }
281                Command::AppendTo(mut entity, id) => {
282                    entity
283                        .append_to(world, id)
284                        .map_err(|v| v.into_anyhow())
285                        .context("Failed to append to entity")?;
286                }
287                Command::SpawnBatch(mut batch) => {
288                    batch.spawn(world);
289                }
290                Command::SpawnBatchAt(mut batch, ids) => {
291                    batch
292                        .spawn_at(world, &ids)
293                        .map_err(|v| v.into_anyhow())
294                        .context("Failed to spawn entity")?;
295                }
296                Command::Set { id, desc, offset } => unsafe {
297                    let value = self.inserts.take_dyn(offset);
298                    world
299                        .set_dyn(id, desc, value)
300                        .map_err(|v| v.into_anyhow())
301                        .with_context(|| format!("Failed to set component {}", desc.name()))?;
302                },
303                Command::SetDedup {
304                    id,
305                    desc,
306                    offset,
307                    cmp,
308                } => unsafe {
309                    let value = self.inserts.take_dyn(offset);
310                    world
311                        .set_with_writer(
312                            id,
313                            SingleComponentWriter::new(desc, WriteDedupDyn { value, cmp }),
314                        )
315                        .map_err(|v| v.into_anyhow())
316                        .with_context(|| format!("Failed to set component {}", desc.name()))?;
317                },
318                Command::SetMissing { id, desc, offset } => unsafe {
319                    let value = self.inserts.take_dyn(offset);
320                    world
321                        .set_with_writer(id, SingleComponentWriter::new(desc, MissingDyn { value }))
322                        .map_err(|v| v.into_anyhow())
323                        .with_context(|| format!("Failed to set component {}", desc.name()))?;
324                },
325                Command::Despawn(id) => world
326                    .despawn(id)
327                    .map_err(|v| v.into_anyhow())
328                    .context("Failed to despawn entity")?,
329                Command::Remove { id, desc } => world
330                    .remove_dyn(id, desc)
331                    .map_err(|v| v.into_anyhow())
332                    .with_context(|| format!("Failed to remove component {}", desc.name()))?,
333                Command::Defer(func) => {
334                    func(world).context("Failed to execute deferred function")?
335                }
336            }
337        }
338
339        self.inserts.clear();
340
341        Ok(())
342    }
343
344    /// Clears all values in the component buffer but keeps allocations around.
345    /// Is automatically called for [`Self::apply`].
346    pub fn clear(&mut self) {
347        self.inserts.clear();
348        self.commands.clear()
349    }
350}
351
352#[cfg(test)]
353mod tests {
354    use crate::{component, FetchExt, Query};
355
356    use super::*;
357
358    #[test]
359    fn set_missing() {
360        use alloc::string::String;
361        use alloc::string::ToString;
362
363        component! {
364            a: String,
365        }
366
367        let mut world = World::new();
368        let mut cmd = CommandBuffer::new();
369
370        let mut query = Query::new((a().modified().satisfied(), a().cloned()));
371
372        let id = EntityBuilder::new().spawn(&mut world);
373
374        assert!(query.collect_vec(&world).is_empty());
375
376        cmd.set_missing(id, a(), "Foo".into())
377            .set_missing(id, a(), "Bar".into());
378
379        cmd.apply(&mut world).unwrap();
380
381        assert_eq!(query.collect_vec(&world), [(true, "Foo".to_string())]);
382        assert_eq!(query.collect_vec(&world), [(false, "Foo".to_string())]);
383
384        cmd.set_missing(id, a(), "Baz".into());
385        cmd.apply(&mut world).unwrap();
386
387        assert_eq!(query.collect_vec(&world), [(false, "Foo".to_string())]);
388    }
389
390    #[test]
391    fn set_dedup() {
392        use alloc::string::String;
393        use alloc::string::ToString;
394
395        component! {
396            a: String,
397        }
398
399        let mut world = World::new();
400        let mut cmd = CommandBuffer::new();
401
402        let mut query = Query::new((a().modified().satisfied(), a().cloned()));
403
404        let id = EntityBuilder::new().spawn(&mut world);
405
406        assert!(query.collect_vec(&world).is_empty());
407
408        cmd.set_dedup(id, a(), "Foo".into())
409            .set_dedup(id, a(), "Bar".into());
410
411        cmd.apply(&mut world).unwrap();
412
413        assert_eq!(query.collect_vec(&world), [(true, "Bar".to_string())]);
414        assert_eq!(query.collect_vec(&world), [(false, "Bar".to_string())]);
415
416        cmd.set_dedup(id, a(), "Baz".into());
417        cmd.apply(&mut world).unwrap();
418
419        assert_eq!(query.collect_vec(&world), [(true, "Baz".to_string())]);
420
421        cmd.set_dedup(id, a(), "Baz".into());
422        cmd.apply(&mut world).unwrap();
423        assert_eq!(query.collect_vec(&world), [(false, "Baz".to_string())]);
424    }
425}