moonshine_spawn/
lib.rs

1#![deprecated(
2    since = "0.2.4",
3    note = "see documentation at https://github.com/Zeenobit/moonshine_spawn for details"
4)]
5#![doc = include_str!("../README.md")]
6
7use std::fmt::{Debug, Formatter, Result as FormatResult};
8use std::hash::{Hash, Hasher};
9use std::sync::Arc;
10
11use bevy_app::prelude::*;
12use bevy_ecs::prelude::*;
13use bevy_ecs::schedule::SystemConfigs;
14use bevy_ecs::system::EntityCommands;
15use bevy_hierarchy::BuildChildren;
16use bevy_reflect::prelude::*;
17use bevy_utils::HashMap;
18
19pub mod prelude {
20    pub use super::{
21        spawn_children, AddSpawnable, Spawn, SpawnChildBuilder, SpawnChildren, SpawnCommands,
22        SpawnKey, SpawnOnce, SpawnPlugin, SpawnWorld, Spawnables, WithChildren,
23    };
24}
25
26pub struct SpawnPlugin;
27
28impl Plugin for SpawnPlugin {
29    fn build(&self, app: &mut App) {
30        app.register_type::<SpawnKey>()
31            .insert_resource(Spawnables::default())
32            .add_systems(First, invoke_spawn_children.run_if(should_spawn_children));
33    }
34}
35
36/// Represents a type which spawns an [`Entity`] exactly once.
37///
38/// # Usage
39/// The output of a spawn is a [`Bundle`] which is inserted into the given spawned [`Entity`].
40///
41/// By default, all bundles implement this trait.
42pub trait SpawnOnce: 'static + Send + Sync {
43    type Output: Bundle;
44
45    fn spawn_once(self, world: &World, entity: Entity) -> Self::Output;
46}
47
48impl<T: Bundle> SpawnOnce for T {
49    type Output = Self;
50
51    fn spawn_once(self, _: &World, _: Entity) -> Self::Output {
52        self
53    }
54}
55
56/// Represents a type which spawns an [`Entity`].
57///
58/// # Usage
59/// The output of a spawn is a [`Bundle`] which is inserted into the given spawned [`Entity`].
60///
61/// By default, anything which implements [`SpawnOnce`] and [`Clone`] also implements this trait.
62/// This includes bundles which can be cloned.
63pub trait Spawn: 'static + Send + Sync {
64    type Output: Bundle;
65
66    fn spawn(&self, world: &World, entity: Entity) -> Self::Output;
67}
68
69impl<T: SpawnOnce + Clone> Spawn for T {
70    type Output = T::Output;
71
72    fn spawn(&self, world: &World, entity: Entity) -> Self::Output {
73        self.clone().spawn_once(world, entity)
74    }
75}
76
77/// Trait used to register a spawnable with an [`App`].
78///
79/// # Usage
80/// A spawnable is any thing which implements [`Spawn`]. A spawnable is registered by a unique [`SpawnKey`].
81/// This spawn key may then be used to spawn a new instance of the spawnable.
82pub trait AddSpawnable {
83    fn add_spawnable(self, key: impl Into<SpawnKey>, _: impl Spawn) -> SpawnKey;
84}
85
86impl AddSpawnable for &mut App {
87    fn add_spawnable(self, key: impl Into<SpawnKey>, spawnable: impl Spawn) -> SpawnKey {
88        self.world_mut()
89            .resource_mut::<Spawnables>()
90            .register(key, spawnable)
91    }
92}
93
94/// Trait used to spawn spawnables either directly or via a [`SpawnKey`] using [`Commands`].
95pub trait SpawnCommands {
96    fn spawn_with(&mut self, _: impl Spawn) -> EntityCommands<'_>;
97
98    fn spawn_once_with(&mut self, _: impl SpawnOnce) -> EntityCommands<'_>;
99
100    fn spawn_key(&mut self, key: impl Into<SpawnKey>) -> EntityCommands<'_>;
101
102    fn spawn_key_with(
103        &mut self,
104        key: impl Into<SpawnKey>,
105        bundle: impl Bundle,
106    ) -> EntityCommands<'_>;
107}
108
109impl SpawnCommands for Commands<'_, '_> {
110    fn spawn_with(&mut self, spawnable: impl Spawn) -> EntityCommands<'_> {
111        let entity = self.spawn_empty().id();
112        self.queue(move |world: &mut World| {
113            Spawnable::spawn(&spawnable, world, entity);
114        });
115        self.entity(entity)
116    }
117
118    fn spawn_once_with(&mut self, spawnable: impl SpawnOnce) -> EntityCommands<'_> {
119        let entity = self.spawn_empty().id();
120        self.queue(move |world: &mut World| {
121            SpawnableOnce::spawn_once(spawnable, world, entity);
122        });
123        self.entity(entity)
124    }
125
126    fn spawn_key(&mut self, key: impl Into<SpawnKey>) -> EntityCommands<'_> {
127        let key: SpawnKey = key.into();
128        let entity = self.spawn_empty().id();
129        self.queue(move |world: &mut World| {
130            key.spawn_once(world, entity);
131        });
132        self.entity(entity)
133    }
134
135    fn spawn_key_with(
136        &mut self,
137        key: impl Into<SpawnKey>,
138        bundle: impl Bundle,
139    ) -> EntityCommands<'_> {
140        let key = key.into();
141        let entity = self.spawn_empty().id();
142        self.queue(move |world: &mut World| {
143            SpawnKeyWith(key, bundle).spawn_once(world, entity);
144        });
145        self.entity(entity)
146    }
147}
148
149/// Trait used to spawn spawnables either directly or via a [`SpawnKey`] using [`World`].
150pub trait SpawnWorld {
151    fn spawn_with(&mut self, _: impl Spawn) -> EntityWorldMut;
152
153    fn spawn_once_with(&mut self, _: impl SpawnOnce) -> EntityWorldMut;
154
155    fn spawn_key(&mut self, key: impl Into<SpawnKey>) -> EntityWorldMut;
156
157    fn spawn_key_with(&mut self, key: impl Into<SpawnKey>, bundle: impl Bundle) -> EntityWorldMut;
158}
159
160impl SpawnWorld for World {
161    fn spawn_with(&mut self, spawnable: impl Spawn) -> EntityWorldMut {
162        let entity = self.spawn_empty().id();
163        Spawnable::spawn(&spawnable, self, entity);
164        invoke_spawn_children(self);
165        self.entity_mut(entity)
166    }
167
168    fn spawn_once_with(&mut self, spawnable: impl SpawnOnce) -> EntityWorldMut {
169        let entity = self.spawn_empty().id();
170        SpawnableOnce::spawn_once(spawnable, self, entity);
171        invoke_spawn_children(self);
172        self.entity_mut(entity)
173    }
174
175    fn spawn_key(&mut self, key: impl Into<SpawnKey>) -> EntityWorldMut {
176        let key: SpawnKey = key.into();
177        let entity = self.spawn_empty().id();
178        key.spawn_once(self, entity);
179        invoke_spawn_children(self);
180        self.entity_mut(entity)
181    }
182
183    fn spawn_key_with(&mut self, key: impl Into<SpawnKey>, bundle: impl Bundle) -> EntityWorldMut {
184        let key = key.into();
185        let entity = self.spawn_empty().id();
186        SpawnKeyWith(key, bundle).spawn_once(self, entity);
187        invoke_spawn_children(self);
188        self.entity_mut(entity)
189    }
190}
191
192/// A [`Resource`] which contains all registered spawnables.
193#[derive(Resource, Default)]
194pub struct Spawnables(HashMap<SpawnKey, Arc<dyn Spawnable>>);
195
196impl Spawnables {
197    /// Registers a spawnable with a unique [`SpawnKey`] and returns it.
198    ///
199    /// # Warning
200    /// This function will panic if the given key is already registered.
201    pub fn register<T>(&mut self, key: impl Into<SpawnKey>, spawnable: T) -> SpawnKey
202    where
203        T: 'static + Spawn + Send + Sync,
204    {
205        let key = key.into();
206        let previous = self.0.insert(key.clone(), Arc::new(spawnable));
207        assert!(previous.is_none(), "spawn key must be unique: {key:?}",);
208        key
209    }
210
211    /// Returns an iterator over all registered [`SpawnKey`]s.
212    pub fn keys(&self) -> impl Iterator<Item = &SpawnKey> {
213        self.0.keys()
214    }
215
216    fn fetch(&self, key: &SpawnKey) -> Option<Arc<dyn Spawnable>> {
217        self.0.get(key).cloned()
218    }
219}
220
221/// A unique string-based identifier used to spawn a spawnable registered with [`Spawnables`].
222#[derive(Clone, Reflect)]
223pub struct SpawnKey(String);
224
225impl SpawnKey {
226    pub fn new(name: impl Into<String>) -> Self {
227        Self(name.into())
228    }
229
230    pub fn name(&self) -> &str {
231        &self.0
232    }
233}
234
235impl PartialEq for SpawnKey {
236    fn eq(&self, other: &Self) -> bool {
237        self.name() == other.name()
238    }
239}
240
241impl Eq for SpawnKey {}
242
243impl Hash for SpawnKey {
244    fn hash<H: Hasher>(&self, state: &mut H) {
245        self.name().hash(state)
246    }
247}
248
249impl Debug for SpawnKey {
250    fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult {
251        f.debug_tuple("SpawnKey").field(&self.name()).finish()
252    }
253}
254
255impl From<String> for SpawnKey {
256    fn from(name: String) -> Self {
257        Self(name)
258    }
259}
260
261impl From<&str> for SpawnKey {
262    fn from(name: &str) -> Self {
263        Self(name.to_owned())
264    }
265}
266
267/// Trait used to attach children to an [`Entity`] using a [`Bundle`].
268///
269/// # Example
270/// ```
271/// # use bevy::prelude::*;
272/// # use moonshine_spawn::prelude::*;
273///
274/// #[derive(Component)]
275/// struct Foo;
276///
277/// #[derive(Component)]
278/// struct Bar;
279///
280/// let mut world = World::default();
281/// world.spawn(Foo.with_children(|foo| {
282///    foo.spawn(Bar);
283/// }));
284/// ```
285pub trait WithChildren: Bundle + Sized {
286    fn with_children(self, f: impl FnOnce(&mut SpawnChildBuilder)) -> (Self, SpawnChildren);
287}
288
289impl<T: Bundle> WithChildren for T {
290    fn with_children(self, f: impl FnOnce(&mut SpawnChildBuilder)) -> (Self, SpawnChildren) {
291        let mut children = SpawnChildren::new();
292        let mut builder = SpawnChildBuilder(&mut children);
293        f(&mut builder);
294        (self, children)
295    }
296}
297
298/// A [`Component`] which stores a list of spawnables to spawn as children of its [`Entity`].
299#[derive(Component)]
300#[component(storage = "SparseSet")]
301pub struct SpawnChildren(Vec<Box<dyn SpawnableOnce>>);
302
303impl SpawnChildren {
304    fn new() -> Self {
305        Self(Vec::new())
306    }
307
308    fn add_child(&mut self, spawnable: impl SpawnableOnce) {
309        self.0.push(Box::new(spawnable));
310    }
311
312    fn add_child_with_key(&mut self, key: SpawnKey) {
313        self.0.push(Box::new(key));
314    }
315
316    fn invoke(world: &mut World, entity: Entity, mut child_spawned: impl FnMut(Entity)) {
317        if let Some(children) = world.entity_mut(entity).take::<SpawnChildren>() {
318            for spawnable in children.0 {
319                let child = world.spawn_empty().id();
320                spawnable.spawn_once_dyn(world, child);
321                child_spawned(child);
322                world.entity_mut(entity).add_child(child);
323            }
324        }
325    }
326}
327
328/// An ergonomic function used to create a [`SpawnChildren`] component.
329#[must_use]
330pub fn spawn_children(f: impl FnOnce(&mut SpawnChildBuilder)) -> SpawnChildren {
331    let mut children = SpawnChildren::new();
332    f(&mut SpawnChildBuilder(&mut children));
333    children
334}
335
336impl Default for SpawnChildren {
337    fn default() -> Self {
338        Self::new()
339    }
340}
341
342pub struct SpawnChildBuilder<'a>(&'a mut SpawnChildren);
343
344impl SpawnChildBuilder<'_> {
345    pub fn spawn(&mut self, spawnable: impl SpawnOnce) -> &mut Self {
346        self.0.add_child(spawnable);
347        self
348    }
349
350    pub fn spawn_key(&mut self, key: impl Into<SpawnKey>) -> &mut Self {
351        self.0.add_child_with_key(key.into());
352        self
353    }
354
355    pub fn spawn_key_with(&mut self, key: impl Into<SpawnKey>, bundle: impl Bundle) -> &mut Self {
356        self.0.add_child(SpawnKeyWith(key.into(), bundle));
357        self
358    }
359}
360
361trait Spawnable: 'static + Send + Sync {
362    fn spawn(&self, world: &mut World, entity: Entity);
363}
364
365impl<T: Spawn> Spawnable for T {
366    fn spawn(&self, world: &mut World, entity: Entity) {
367        let bundle = self.spawn(world, entity);
368        world.entity_mut(entity).insert(bundle);
369    }
370}
371
372trait SpawnableOnce: 'static + Send + Sync {
373    fn spawn_once(self, world: &mut World, entity: Entity);
374
375    fn spawn_once_dyn(self: Box<Self>, world: &mut World, entity: Entity);
376}
377
378impl<T: SpawnOnce> SpawnableOnce for T {
379    fn spawn_once(self, world: &mut World, entity: Entity) {
380        let bundle = self.spawn_once(world, entity);
381        world.entity_mut(entity).insert(bundle);
382    }
383
384    fn spawn_once_dyn(self: Box<Self>, world: &mut World, entity: Entity) {
385        SpawnableOnce::spawn_once(*self, world, entity);
386    }
387}
388
389impl SpawnableOnce for SpawnKey {
390    fn spawn_once(self, world: &mut World, entity: Entity) {
391        if let Some(spawnable) = world.resource::<Spawnables>().fetch(&self) {
392            spawnable.spawn(world, entity);
393        } else {
394            panic!("invalid spawn key: {self:?}");
395        }
396    }
397
398    fn spawn_once_dyn(self: Box<Self>, world: &mut World, entity: Entity) {
399        SpawnableOnce::spawn_once(*self, world, entity);
400    }
401}
402
403struct SpawnKeyWith<T>(SpawnKey, T);
404
405impl<T: Bundle> SpawnableOnce for SpawnKeyWith<T> {
406    fn spawn_once(self, world: &mut World, entity: Entity) {
407        self.0.spawn_once(world, entity);
408        world.entity_mut(entity).insert(self.1);
409    }
410
411    fn spawn_once_dyn(self: Box<Self>, world: &mut World, entity: Entity) {
412        SpawnableOnce::spawn_once(*self, world, entity);
413    }
414}
415
416fn should_spawn_children(query: Query<(), With<SpawnChildren>>) -> bool {
417    !query.is_empty()
418}
419
420fn invoke_spawn_children(world: &mut World) {
421    let mut entities = Vec::new();
422
423    for entity in world.iter_entities() {
424        if entity.contains::<SpawnChildren>() {
425            entities.push(entity.id());
426        }
427    }
428
429    while !entities.is_empty() {
430        let batch = std::mem::take(&mut entities);
431        for entity in batch {
432            SpawnChildren::invoke(world, entity, |child| entities.push(child));
433        }
434    }
435}
436
437/// Returns a [`SystemConfigs`] which immediately spawns all pending [`SpawnChildren`] requests.
438///
439/// # Usage
440/// Typically, the spawn system spawns children automatically during [`First`] schedule.
441/// In some cases, however, it may be necessary to forcibly spawn children due to ordering issues.
442///
443/// # Example
444/// ```
445/// use bevy::prelude::*;
446/// use moonshine_spawn::{prelude::*, force_spawn_children};
447///
448/// #[derive(Component)]
449/// struct Bar;
450///
451/// #[derive(Bundle)]
452/// struct Foo {
453///     children: SpawnChildren,
454/// }
455///
456/// impl Foo {
457///     fn new() -> Self {
458///         Self {
459///             children: spawn_children(|parent| {
460///                 parent.spawn(Bar);
461///             })
462///         }
463///     }
464/// }
465///
466/// fn setup(mut commands: Commands) {
467///     commands.spawn(Foo::new());
468/// }
469///
470/// fn post_setup(bar: Query<&Bar>) {
471///     let _ = bar.single();
472///     // ...
473/// }
474///
475/// App::new()
476///     .add_plugins((MinimalPlugins, SpawnPlugin))
477///     // Without `force_spawn_children()`, `post_setup` would panic!
478///     .add_systems(Startup, (setup, force_spawn_children(), post_setup).chain())
479///     .update();
480/// ```
481pub fn force_spawn_children() -> SystemConfigs {
482    invoke_spawn_children.run_if(should_spawn_children)
483}
484
485#[cfg(test)]
486mod tests {
487    use bevy::{ecs::system::RunSystemOnce, prelude::*};
488
489    use super::*;
490
491    fn app() -> App {
492        let mut app = App::new();
493        app.add_plugins((MinimalPlugins, SpawnPlugin));
494        app
495    }
496
497    #[derive(Component, Clone)]
498    struct Foo;
499
500    #[derive(Component, Clone)]
501    struct Bar;
502
503    #[test]
504    fn spawn_bundle() {
505        let mut app = app();
506        let world = app.world_mut();
507        let entity = world.spawn_once_with(Foo).id();
508        assert!(world.entity(entity).contains::<Foo>());
509    }
510
511    #[test]
512    fn spawn_bundle_deferred() {
513        let mut app = app();
514        let entity = {
515            let world = app.world_mut();
516            world
517                .run_system_once(|mut commands: Commands| commands.spawn_once_with(Foo).id())
518                .unwrap()
519        };
520        app.update();
521        let world = app.world();
522        assert!(world.entity(entity).contains::<Foo>());
523    }
524
525    #[test]
526    fn spawn_with_key() {
527        let mut app = app();
528        app.add_spawnable("FOO", Foo);
529        let world = app.world_mut();
530        let entity = world.spawn_key("FOO").id();
531        assert!(world.entity(entity).contains::<Foo>());
532    }
533
534    #[test]
535    fn spawn_with_key_deferred() {
536        let mut app = app();
537        app.add_spawnable("FOO", Foo);
538        let entity = {
539            let world = app.world_mut();
540            world
541                .run_system_once(|mut commands: Commands| commands.spawn_key("FOO").id())
542                .unwrap()
543        };
544        app.update();
545        let world = app.world();
546        assert!(world.entity(entity).contains::<Foo>());
547    }
548
549    #[test]
550    fn spawn_bundle_with_children() {
551        let mut app = app();
552        let world = app.world_mut();
553        let entity = world
554            .spawn_once_with(Foo.with_children(|foo| {
555                foo.spawn(Bar);
556            }))
557            .id();
558        let children = world.entity(entity).get::<Children>().unwrap();
559        let child = children.iter().copied().next().unwrap();
560        assert!(world.entity(child).contains::<Bar>());
561    }
562
563    #[test]
564    fn spawn_bundle_with_children_deferred() {
565        let mut app = app();
566        let entity = {
567            let world = app.world_mut();
568            world
569                .run_system_once(|mut commands: Commands| {
570                    commands
571                        .spawn_once_with(Foo.with_children(|foo| {
572                            foo.spawn(Bar);
573                        }))
574                        .id()
575                })
576                .unwrap()
577        };
578        app.update();
579        let world = app.world();
580        let children = world.entity(entity).get::<Children>().unwrap();
581        let child = children.iter().copied().next().unwrap();
582        assert!(world.entity(child).contains::<Bar>());
583    }
584
585    #[test]
586    fn spawn_bundle_with_children_with_key() {
587        let mut app = app();
588        app.add_spawnable("BAR", Bar);
589        let world = app.world_mut();
590        let entity = world
591            .spawn_once_with(Foo.with_children(|foo| {
592                foo.spawn_key("BAR");
593            }))
594            .id();
595        let children = world.entity(entity).get::<Children>().unwrap();
596        let child = children.iter().copied().next().unwrap();
597        assert!(world.entity(child).contains::<Bar>());
598    }
599
600    #[test]
601    fn spawn_bundle_with_children_with_key_deferred() {
602        let mut app = app();
603        app.add_spawnable("BAR", Bar);
604        let entity = {
605            let world = app.world_mut();
606            world
607                .run_system_once(|mut commands: Commands| {
608                    commands
609                        .spawn_once_with(Foo.with_children(|foo| {
610                            foo.spawn_key("BAR");
611                        }))
612                        .id()
613                })
614                .unwrap()
615        };
616        app.update();
617        let world = app.world();
618        let children = world.entity(entity).get::<Children>().unwrap();
619        let child = children.iter().copied().next().unwrap();
620        assert!(world.entity(child).contains::<Bar>());
621    }
622}