edict/action/
encoder.rs

1use core::{any::TypeId, iter::FusedIterator};
2
3use alloc::collections::VecDeque;
4use smallvec::SmallVec;
5
6use crate::{
7    bundle::{Bundle, ComponentBundle, DynamicBundle, DynamicComponentBundle},
8    component::{Component, ComponentInfo, ComponentRegistry},
9    entity::{Entity, EntityId, EntityLoc, EntitySet},
10    relation::Relation,
11    type_id,
12    world::{iter_reserve_hint, World},
13};
14
15use super::{buffer::LocalActionBuffer, ActionBuffer, ActionFn, LocalActionFn};
16
17/// Encoder for actions that require mutable access to [`World`],
18/// like spawning/despawning entities and inserting/removing/dropping components and relations.
19///
20/// Systems may declare `ActionEncoder` argument to record actions that will be executed later.
21/// Each system will get its own `ActionEncoder` instance, so no conflicts will be caused by this argument.
22/// In contract `&mut World` argument will cause system to conflict with all other systems, reducing parallelism.
23///
24/// Provided to component and relation hooks.
25pub struct ActionEncoder<'a> {
26    actions: &'a mut VecDeque<ActionFn<'static>>,
27    entities: &'a EntitySet,
28}
29
30impl<'a> ActionEncoder<'a> {
31    /// Returns new [`ActionEncoder`] that records actions into provided [`ActionBuffer`].
32    #[inline(always)]
33    pub(crate) fn new(buffer: &'a mut ActionBuffer, entities: &'a EntitySet) -> Self {
34        ActionEncoder {
35            actions: buffer.actions(),
36            entities,
37        }
38    }
39
40    /// Returns `true` if attached action buffer is empty.
41    /// That is, no actions were recorded.
42    #[inline(always)]
43    pub fn is_empty(&self) -> bool {
44        self.actions.is_empty()
45    }
46
47    /// Allocates new entity id that can be used with following actions.
48    #[inline(always)]
49    pub fn allocate(&self) -> EntityLoc<'_> {
50        self.entities.alloc()
51    }
52
53    /// Allocates new entity id and encodes an action to insert bundle to the entity.
54    #[inline(always)]
55    pub fn spawn_one<T>(&mut self, component: T) -> EntityLoc<'_>
56    where
57        T: Component + Send + 'static,
58    {
59        let entity = self.entities.alloc();
60        self.insert(entity, component);
61        entity
62    }
63
64    /// Allocates new entity id and encodes an action to insert bundle to the entity.
65    #[inline(always)]
66    pub fn spawn<B>(&mut self, bundle: B) -> EntityLoc<'_>
67    where
68        B: DynamicComponentBundle + Send + 'static,
69    {
70        let entity = self.entities.alloc();
71        self.insert_bundle(entity, bundle);
72        entity
73    }
74
75    /// Allocates new entity id and encodes an action to insert bundle to the entity.
76    #[inline(always)]
77    pub fn spawn_external<B>(&mut self, bundle: B) -> EntityLoc<'_>
78    where
79        B: DynamicBundle + Send + 'static,
80    {
81        let entity = self.entities.alloc();
82        self.insert_external_bundle(entity, bundle);
83        entity
84    }
85
86    /// Returns an iterator which encodes action to spawn and yield entities
87    /// using bundles yielded from provided bundles iterator.
88    #[inline(always)]
89    pub fn spawn_batch<I>(&mut self, bundles: I) -> SpawnBatch<I>
90    where
91        I: IntoIterator,
92        I::Item: ComponentBundle + Send + 'static,
93    {
94        self.push_fn(|world| {
95            world.ensure_bundle_registered::<I::Item>();
96        });
97
98        SpawnBatch {
99            bundles,
100            encoder: self.reborrow(),
101        }
102    }
103
104    /// Returns an iterator which encodes action to spawn and yield entities
105    /// using bundles yielded from provided bundles iterator.
106    #[inline(always)]
107    pub fn spawn_external_batch<I>(&mut self, bundles: I) -> SpawnBatch<I>
108    where
109        I: IntoIterator,
110        I::Item: Bundle + Send + 'static,
111    {
112        SpawnBatch {
113            bundles,
114            encoder: self.reborrow(),
115        }
116    }
117
118    /// Encodes an action to despawn specified entity.
119    #[inline(always)]
120    pub fn despawn(&mut self, entity: impl Entity) {
121        let id = entity.id();
122        self.push_fn(move |world| {
123            let _ = world.despawn(id);
124        })
125    }
126
127    /// Encodes an action to despawn entities in batch.
128    #[inline(always)]
129    pub fn despawn_batch(&mut self, entities: impl IntoIterator<Item = EntityId>) {
130        let entities = entities.into_iter();
131
132        match entities.size_hint() {
133            (_, Some(upper)) if upper <= 8 => {
134                let entities = entities.into_iter().collect::<SmallVec<[_; 8]>>();
135                self.push_fn(move |world| {
136                    let _ = world.despawn_batch(entities);
137                });
138            }
139            (_, Some(upper)) if upper <= 16 => {
140                let entities = entities.into_iter().collect::<SmallVec<[_; 16]>>();
141                self.push_fn(move |world| {
142                    let _ = world.despawn_batch(entities);
143                });
144            }
145            (_, Some(upper)) if upper <= 32 => {
146                let entities = entities.into_iter().collect::<SmallVec<[_; 32]>>();
147                self.push_fn(move |world| {
148                    let _ = world.despawn_batch(entities);
149                });
150            }
151            _ => {
152                let entities = entities.into_iter().collect::<SmallVec<[_; 64]>>();
153                self.push_fn(move |world| {
154                    let _ = world.despawn_batch(entities);
155                });
156            }
157        }
158    }
159
160    /// Encodes an action to insert component to the specified entity.
161    #[inline(always)]
162    pub fn insert<T>(&mut self, entity: impl Entity, component: T)
163    where
164        T: Component + Send,
165    {
166        let id = entity.id();
167        self.push_fn(move |world| {
168            let _ = world.insert(id, component);
169        });
170    }
171
172    /// Encodes an action to insert component to the specified entity.
173    #[inline(always)]
174    pub fn insert_external<T>(&mut self, entity: impl Entity, component: T)
175    where
176        T: Send + 'static,
177    {
178        let id = entity.id();
179        self.push_fn(move |world| {
180            let _ = world.insert_external(id, component);
181        });
182    }
183
184    /// Encodes an action to insert component to the specified entity.
185    #[inline(always)]
186    pub fn with<F, T>(&mut self, entity: impl Entity, f: F)
187    where
188        F: FnOnce() -> T + Send + 'static,
189        T: Component,
190    {
191        let id = entity.id();
192        self.push_fn(move |world| {
193            let _ = world.with(id, f);
194        });
195    }
196
197    /// Encodes an action to insert component to the specified entity.
198    #[inline(always)]
199    pub fn with_external<F, T>(&mut self, entity: impl Entity, f: F)
200    where
201        F: FnOnce() -> T + Send + 'static,
202        T: 'static,
203    {
204        let id = entity.id();
205        self.push_fn(move |world| {
206            let _ = world.with_external(id, f);
207        });
208    }
209
210    /// Encodes an action to insert components from bundle to the specified entity.
211    #[inline(always)]
212    pub fn insert_bundle<B>(&mut self, entity: impl Entity, bundle: B)
213    where
214        B: DynamicComponentBundle + Send + 'static,
215    {
216        let id = entity.id();
217        self.push_fn(move |world| {
218            let _ = world.insert_bundle(id, bundle);
219        });
220    }
221
222    /// Encodes an action to insert components from bundle to the specified entity.
223    #[inline(always)]
224    pub fn insert_external_bundle<B>(&mut self, entity: impl Entity, bundle: B)
225    where
226        B: DynamicBundle + Send + 'static,
227    {
228        let id = entity.id();
229        self.push_fn(move |world| {
230            let _ = world.insert_external_bundle(id, bundle);
231        });
232    }
233
234    /// Encodes an action to insert components from bundle to the specified entity.
235    #[inline(always)]
236    pub fn with_bundle<B>(&mut self, entity: impl Entity, bundle: B)
237    where
238        B: DynamicComponentBundle + Send + 'static,
239    {
240        let id = entity.id();
241        self.push_fn(move |world| {
242            let _ = world.with_bundle(id, bundle);
243        });
244    }
245
246    /// Encodes an action to insert components from bundle to the specified entity.
247    #[inline(always)]
248    pub fn with_external_bundle<B>(&mut self, entity: impl Entity, bundle: B)
249    where
250        B: DynamicBundle + Send + 'static,
251    {
252        let id = entity.id();
253        self.push_fn(move |world| {
254            let _ = world.with_external_bundle(id, bundle);
255        });
256    }
257
258    /// Encodes an action to drop component from specified entity.
259    #[inline(always)]
260    pub fn drop<T>(&mut self, entity: impl Entity)
261    where
262        T: 'static,
263    {
264        self.drop_erased(entity, type_id::<T>())
265    }
266
267    /// Encodes an action to drop component from entities in batch.
268    #[inline(always)]
269    pub fn drop_batch<T>(&mut self, entities: impl IntoIterator<Item = EntityId>)
270    where
271        T: 'static,
272    {
273        self.drop_erased_batch(entities, type_id::<T>())
274    }
275
276    /// Encodes an action to drop component from specified entity.
277    #[inline(always)]
278    pub fn drop_erased(&mut self, entity: impl Entity, ty: TypeId) {
279        let id = entity.id();
280        self.push_fn(move |world| {
281            let _ = world.drop_erased(id, ty);
282        })
283    }
284
285    /// Encodes an action to drop component from entities in batch.
286    #[inline(always)]
287    pub fn drop_erased_batch(&mut self, entities: impl IntoIterator<Item = EntityId>, ty: TypeId) {
288        let entities = entities.into_iter();
289
290        match entities.size_hint() {
291            (_, Some(upper)) if upper <= 8 => {
292                let entities = entities.into_iter().collect::<SmallVec<[_; 8]>>();
293                self.push_fn(move |world| {
294                    let _ = world.drop_erased_batch(entities, ty);
295                });
296            }
297            (_, Some(upper)) if upper <= 16 => {
298                let entities = entities.into_iter().collect::<SmallVec<[_; 16]>>();
299                self.push_fn(move |world| {
300                    let _ = world.drop_erased_batch(entities, ty);
301                });
302            }
303            (_, Some(upper)) if upper <= 32 => {
304                let entities = entities.into_iter().collect::<SmallVec<[_; 32]>>();
305                self.push_fn(move |world| {
306                    let _ = world.drop_erased_batch(entities, ty);
307                });
308            }
309            _ => {
310                let entities = entities.into_iter().collect::<SmallVec<[_; 64]>>();
311                self.push_fn(move |world| {
312                    let _ = world.drop_erased_batch(entities, ty);
313                });
314            }
315        }
316    }
317
318    /// Encodes an action to drop bundle of components from specified entity.
319    #[inline(always)]
320    pub fn drop_bundle<B>(&mut self, entity: impl Entity)
321    where
322        B: Bundle,
323    {
324        let id = entity.id();
325        self.push_fn(move |world| {
326            let _ = world.drop_bundle::<B>(id);
327        });
328    }
329
330    /// Encodes an action to add relation between two entities to the [`World`].
331    #[inline(always)]
332    pub fn insert_relation<R>(&mut self, origin: impl Entity, relation: R, target: impl Entity)
333    where
334        R: Relation + Send,
335    {
336        let origin = origin.id();
337        let target = target.id();
338        self.push_fn(move |world| {
339            let _ = world.insert_relation(origin, relation, target);
340        });
341    }
342
343    /// Encodes an action to drop relation between two entities in the [`World`].
344    #[inline(always)]
345    pub fn drop_relation<R>(&mut self, origin: EntityId, target: EntityId)
346    where
347        R: Relation,
348    {
349        self.push_fn(move |world| {
350            let _ = world.remove_relation::<R>(origin, target);
351        });
352    }
353
354    /// Checks if entity is alive.
355    #[inline(always)]
356    pub fn is_alive(&self, entity: impl Entity) -> bool {
357        entity.is_alive(self.entities)
358    }
359
360    /// Encodes action to insert resource instance.
361    #[inline(always)]
362    pub fn insert_resource<T>(&mut self, resource: T)
363    where
364        T: Send + 'static,
365    {
366        self.push_fn(move |world| {
367            world.insert_resource(resource);
368        });
369    }
370
371    /// Encodes an action to drop resource instance.
372    #[inline(always)]
373    pub fn drop_resource<T: 'static>(&mut self) {
374        self.push_fn(move |world| {
375            world.remove_resource::<T>();
376        });
377    }
378
379    /// Encodes a custom action with a closure that takes reference to `World`
380    /// and [`ActionEncoder`] that can be used to record new actions.
381    #[inline(always)]
382    pub fn closure(&mut self, fun: impl FnOnce(&mut World) + Send + 'static) {
383        self.push_fn(fun);
384    }
385
386    /// Creates new [`ActionEncoder`] that records actions into the same [`ActionBuffer`]
387    /// as this one.
388    #[inline(always)]
389    pub fn reborrow(&mut self) -> ActionEncoder {
390        ActionEncoder {
391            actions: self.actions,
392            entities: self.entities,
393        }
394    }
395
396    /// Encodes an action to remove component from specified entity.
397    #[inline(always)]
398    fn push_fn(&mut self, fun: impl FnOnce(&mut World) + Send + 'static) {
399        self.actions.push_back(ActionFn::new(fun));
400    }
401}
402
403/// Spawning iterator. Produced by [`World::spawn_batch`].
404pub struct SpawnBatch<'a, I> {
405    bundles: I,
406    encoder: ActionEncoder<'a>,
407}
408
409impl<B, I> SpawnBatch<'_, I>
410where
411    I: Iterator<Item = B>,
412    B: Bundle + Send + 'static,
413{
414    /// Spawns the rest of the entities, dropping their ids.
415    #[inline(always)]
416    pub fn spawn_all(self) {
417        self.for_each(|_| {});
418    }
419}
420
421impl<'a, B, I> Iterator for SpawnBatch<'a, I>
422where
423    I: Iterator<Item = B>,
424    B: Bundle + Send + 'static,
425{
426    type Item = EntityId;
427
428    #[inline(always)]
429    fn next(&mut self) -> Option<EntityId> {
430        let bundle = self.bundles.next()?;
431        Some(self.encoder.spawn_external(bundle).id())
432    }
433
434    #[inline(always)]
435    fn nth(&mut self, n: usize) -> Option<EntityId> {
436        // `SpawnBatch` explicitly does NOT spawn entities that are skipped.
437        let bundle = self.bundles.nth(n)?;
438        Some(self.encoder.spawn_external(bundle).id())
439    }
440
441    #[inline(always)]
442    fn size_hint(&self) -> (usize, Option<usize>) {
443        self.bundles.size_hint()
444    }
445
446    #[inline(always)]
447    fn fold<T, F>(mut self, init: T, mut f: F) -> T
448    where
449        F: FnMut(T, EntityId) -> T,
450    {
451        let additional = iter_reserve_hint(&self.bundles);
452        self.encoder.push_fn(move |world| {
453            world.spawn_reserve::<B>(additional);
454        });
455
456        self.bundles.fold(init, |acc, bundle| {
457            f(acc, self.encoder.spawn_external(bundle).id())
458        })
459    }
460
461    #[inline(always)]
462    fn collect<T>(mut self) -> T
463    where
464        T: FromIterator<EntityId>,
465    {
466        // `FromIterator::from_iter` would probably just call `fn next()`
467        // until the end of the iterator.
468        //
469        // Hence we should reserve space in archetype here.
470
471        let additional = iter_reserve_hint(&self.bundles);
472        self.encoder.push_fn(move |world| {
473            world.spawn_reserve::<B>(additional);
474        });
475
476        FromIterator::from_iter(self)
477    }
478}
479
480impl<B, I> ExactSizeIterator for SpawnBatch<'_, I>
481where
482    I: ExactSizeIterator<Item = B>,
483    B: Bundle + Send + 'static,
484{
485    #[inline(always)]
486    fn len(&self) -> usize {
487        self.bundles.len()
488    }
489}
490
491impl<'a, B, I> DoubleEndedIterator for SpawnBatch<'a, I>
492where
493    I: DoubleEndedIterator<Item = B>,
494    B: Bundle + Send + 'static,
495{
496    #[inline(always)]
497    fn next_back(&mut self) -> Option<EntityId> {
498        let bundle = self.bundles.next_back()?;
499        Some(self.encoder.spawn_external(bundle).id())
500    }
501
502    #[inline(always)]
503    fn nth_back(&mut self, n: usize) -> Option<EntityId> {
504        // `SpawnBatch` explicitly does NOT spawn entities that are skipped.
505        let bundle = self.bundles.nth_back(n)?;
506        Some(self.encoder.spawn_external(bundle).id())
507    }
508
509    #[inline(always)]
510    fn rfold<T, F>(mut self, init: T, mut f: F) -> T
511    where
512        Self: Sized,
513        F: FnMut(T, EntityId) -> T,
514    {
515        let additional = iter_reserve_hint(&self.bundles);
516        self.encoder.push_fn(move |world| {
517            world.spawn_reserve::<B>(additional);
518        });
519
520        self.bundles.rfold(init, |acc, bundle| {
521            f(acc, self.encoder.spawn_external(bundle).id())
522        })
523    }
524}
525
526impl<B, I> FusedIterator for SpawnBatch<'_, I>
527where
528    I: FusedIterator<Item = B>,
529    B: Bundle + Send + 'static,
530{
531}
532
533/// Encoder for actions that require mutable access to [`World`],
534/// like spawning/despawning entities and inserting/removing/dropping components and relations.
535///
536/// Systems may declare `ActionEncoder` argument to record actions that will be executed later.
537/// Each system will get its own `ActionEncoder` instance, so no conflicts will be caused by this argument.
538/// In contract `&mut World` argument will cause system to conflict with all other systems, reducing parallelism.
539///
540/// Provided to component and relation hooks.
541pub struct LocalActionEncoder<'a> {
542    actions: &'a mut VecDeque<LocalActionFn<'static>>,
543    entities: &'a EntitySet,
544}
545
546impl<'a> LocalActionEncoder<'a> {
547    /// Returns new [`ActionEncoder`] that records actions into provided [`ActionBuffer`].
548    #[inline(always)]
549    pub(crate) fn new(buffer: &'a mut LocalActionBuffer, entities: &'a EntitySet) -> Self {
550        LocalActionEncoder {
551            actions: buffer.actions(),
552            entities,
553        }
554    }
555
556    /// Returns `true` if attached action buffer is empty.
557    /// That is, no actions were recorded.
558    #[inline(always)]
559    pub fn is_empty(&self) -> bool {
560        self.actions.is_empty()
561    }
562
563    /// Allocates new entity id that can be used with following actions.
564    #[inline(always)]
565    pub fn allocate(&self) -> EntityLoc<'_> {
566        self.entities.alloc()
567    }
568
569    /// Allocates new entity id and encodes an action to insert bundle to the entity.
570    #[inline(always)]
571    pub fn spawn_one<T>(&mut self, component: T) -> EntityLoc<'_>
572    where
573        T: Component + 'static,
574    {
575        let entity = self.entities.alloc();
576        self.insert(entity, component);
577        entity
578    }
579
580    /// Allocates new entity id and encodes an action to insert bundle to the entity.
581    #[inline(always)]
582    pub fn spawn<B>(&mut self, bundle: B) -> EntityLoc<'_>
583    where
584        B: DynamicComponentBundle + 'static,
585    {
586        let entity = self.entities.alloc();
587        self.insert_bundle(entity, bundle);
588        entity
589    }
590
591    /// Allocates new entity id and encodes an action to insert bundle to the entity.
592    #[inline(always)]
593    pub fn spawn_external<B>(&mut self, bundle: B) -> EntityLoc<'_>
594    where
595        B: DynamicBundle + 'static,
596    {
597        let entity = self.entities.alloc();
598        self.insert_external_bundle(entity, bundle);
599        entity
600    }
601
602    /// Returns an iterator which encodes action to spawn and yield entities
603    /// using bundles yielded from provided bundles iterator.
604    #[inline(always)]
605    pub fn spawn_batch<I>(&mut self, bundles: I) -> LocalSpawnBatch<I>
606    where
607        I: IntoIterator,
608        I::Item: ComponentBundle + 'static,
609    {
610        self.push_fn(|world| {
611            world.ensure_bundle_registered::<I::Item>();
612        });
613
614        LocalSpawnBatch {
615            bundles,
616            encoder: self.reborrow(),
617        }
618    }
619
620    /// Returns an iterator which encodes action to spawn and yield entities
621    /// using bundles yielded from provided bundles iterator.
622    #[inline(always)]
623    pub fn spawn_external_batch<I>(&mut self, bundles: I) -> LocalSpawnBatch<I>
624    where
625        I: IntoIterator,
626        I::Item: Bundle + 'static,
627    {
628        LocalSpawnBatch {
629            bundles,
630            encoder: self.reborrow(),
631        }
632    }
633
634    /// Encodes an action to despawn specified entity.
635    #[inline(always)]
636    pub fn despawn(&mut self, entity: impl Entity) {
637        let id = entity.id();
638        self.push_fn(move |world| {
639            let _ = world.despawn(id);
640        })
641    }
642
643    /// Encodes an action to despawn entities in batch.
644    #[inline(always)]
645    pub fn despawn_batch(&mut self, entities: impl IntoIterator<Item = EntityId>) {
646        let entities = entities.into_iter();
647
648        match entities.size_hint() {
649            (_, Some(upper)) if upper <= 8 => {
650                let entities = entities.into_iter().collect::<SmallVec<[_; 8]>>();
651                self.push_fn(move |world| {
652                    let _ = world.despawn_batch(entities);
653                });
654            }
655            (_, Some(upper)) if upper <= 16 => {
656                let entities = entities.into_iter().collect::<SmallVec<[_; 16]>>();
657                self.push_fn(move |world| {
658                    let _ = world.despawn_batch(entities);
659                });
660            }
661            (_, Some(upper)) if upper <= 32 => {
662                let entities = entities.into_iter().collect::<SmallVec<[_; 32]>>();
663                self.push_fn(move |world| {
664                    let _ = world.despawn_batch(entities);
665                });
666            }
667            _ => {
668                let entities = entities.into_iter().collect::<SmallVec<[_; 64]>>();
669                self.push_fn(move |world| {
670                    let _ = world.despawn_batch(entities);
671                });
672            }
673        }
674    }
675
676    /// Encodes an action to insert component to the specified entity.
677    #[inline(always)]
678    pub fn insert<T>(&mut self, entity: impl Entity, component: T)
679    where
680        T: Component,
681    {
682        let id = entity.id();
683        self.push_fn(move |world| {
684            let _ = world.insert(id, component);
685        });
686    }
687
688    /// Encodes an action to insert component to the specified entity.
689    #[inline(always)]
690    pub fn insert_external<T>(&mut self, entity: impl Entity, component: T)
691    where
692        T: 'static,
693    {
694        let id = entity.id();
695        self.push_fn(move |world| {
696            let _ = world.insert_external(id, component);
697        });
698    }
699
700    /// Encodes an action to insert component to the specified entity.
701    #[inline(always)]
702    pub fn with<T>(&mut self, entity: impl Entity, f: impl FnOnce() -> T + 'static)
703    where
704        T: Component,
705    {
706        let id = entity.id();
707        self.push_fn(move |world| {
708            let _ = world.with(id, f);
709        });
710    }
711
712    /// Encodes an action to insert component to the specified entity.
713    #[inline(always)]
714    pub fn with_external<T>(&mut self, entity: impl Entity, f: impl FnOnce() -> T + 'static)
715    where
716        T: 'static,
717    {
718        let id = entity.id();
719        self.push_fn(move |world| {
720            let _ = world.with_external(id, f);
721        });
722    }
723
724    /// Encodes an action to insert component to the specified entity.
725    #[inline(always)]
726    pub(crate) fn _with<F, T>(
727        &mut self,
728        entity: impl Entity,
729        f: impl FnOnce() -> T + 'static,
730        replace: bool,
731        get_or_register: F,
732    ) where
733        F: FnOnce(&mut ComponentRegistry) -> &ComponentInfo + 'static,
734        T: 'static,
735    {
736        let id = entity.id();
737        self.push_fn(move |world| {
738            let _ = world._with(id, f, replace, get_or_register);
739        });
740    }
741
742    /// Encodes an action to insert components from bundle to the specified entity.
743    #[inline(always)]
744    pub fn insert_bundle<B>(&mut self, entity: impl Entity, bundle: B)
745    where
746        B: DynamicComponentBundle + 'static,
747    {
748        let id = entity.id();
749        self.push_fn(move |world| {
750            let _ = world.insert_bundle(id, bundle);
751        });
752    }
753
754    /// Encodes an action to insert components from bundle to the specified entity.
755    #[inline(always)]
756    pub fn insert_external_bundle<B>(&mut self, entity: impl Entity, bundle: B)
757    where
758        B: DynamicBundle + 'static,
759    {
760        let id = entity.id();
761        self.push_fn(move |world| {
762            let _ = world.insert_external_bundle(id, bundle);
763        });
764    }
765
766    /// Encodes an action to insert components from bundle to the specified entity.
767    #[inline(always)]
768    pub fn with_bundle<B>(&mut self, entity: impl Entity, bundle: B)
769    where
770        B: DynamicComponentBundle + 'static,
771    {
772        let id = entity.id();
773        self.push_fn(move |world| {
774            let _ = world.with_bundle(id, bundle);
775        });
776    }
777
778    /// Encodes an action to insert components from bundle to the specified entity.
779    #[inline(always)]
780    pub fn with_external_bundle<B>(&mut self, entity: impl Entity, bundle: B)
781    where
782        B: DynamicBundle + 'static,
783    {
784        let id = entity.id();
785        self.push_fn(move |world| {
786            let _ = world.with_external_bundle(id, bundle);
787        });
788    }
789
790    /// Encodes an action to drop component from specified entity.
791    #[inline(always)]
792    pub fn drop<T>(&mut self, entity: impl Entity)
793    where
794        T: 'static,
795    {
796        self.drop_erased(entity, type_id::<T>())
797    }
798
799    /// Encodes an action to drop component from entities in batch.
800    #[inline(always)]
801    pub fn drop_batch<T>(&mut self, entities: impl IntoIterator<Item = EntityId>)
802    where
803        T: 'static,
804    {
805        self.drop_erased_batch(entities, type_id::<T>())
806    }
807
808    /// Encodes an action to drop component from specified entity.
809    #[inline(always)]
810    pub fn drop_erased(&mut self, entity: impl Entity, ty: TypeId) {
811        let id = entity.id();
812        self.push_fn(move |world| {
813            let _ = world.drop_erased(id, ty);
814        })
815    }
816
817    /// Encodes an action to drop component from entities in batch.
818    #[inline(always)]
819    pub fn drop_erased_batch(&mut self, entities: impl IntoIterator<Item = EntityId>, ty: TypeId) {
820        let entities = entities.into_iter();
821
822        match entities.size_hint() {
823            (_, Some(upper)) if upper <= 8 => {
824                let entities = entities.into_iter().collect::<SmallVec<[_; 8]>>();
825                self.push_fn(move |world| {
826                    let _ = world.drop_erased_batch(entities, ty);
827                });
828            }
829            (_, Some(upper)) if upper <= 16 => {
830                let entities = entities.into_iter().collect::<SmallVec<[_; 16]>>();
831                self.push_fn(move |world| {
832                    let _ = world.drop_erased_batch(entities, ty);
833                });
834            }
835            (_, Some(upper)) if upper <= 32 => {
836                let entities = entities.into_iter().collect::<SmallVec<[_; 32]>>();
837                self.push_fn(move |world| {
838                    let _ = world.drop_erased_batch(entities, ty);
839                });
840            }
841            _ => {
842                let entities = entities.into_iter().collect::<SmallVec<[_; 64]>>();
843                self.push_fn(move |world| {
844                    let _ = world.drop_erased_batch(entities, ty);
845                });
846            }
847        }
848    }
849
850    /// Encodes an action to drop bundle of components from specified entity.
851    #[inline(always)]
852    pub fn drop_bundle<B>(&mut self, entity: impl Entity)
853    where
854        B: Bundle,
855    {
856        let id = entity.id();
857        self.push_fn(move |world| {
858            let _ = world.drop_bundle::<B>(id);
859        });
860    }
861
862    /// Encodes an action to add relation between two entities to the [`World`].
863    #[inline(always)]
864    pub fn insert_relation<R>(&mut self, origin: impl Entity, relation: R, target: impl Entity)
865    where
866        R: Relation,
867    {
868        let origin = origin.id();
869        let target = target.id();
870        self.push_fn(move |world| {
871            let _ = world.insert_relation(origin, relation, target);
872        });
873    }
874
875    /// Encodes an action to drop relation between two entities in the [`World`].
876    #[inline(always)]
877    pub fn drop_relation<R>(&mut self, origin: EntityId, target: EntityId)
878    where
879        R: Relation,
880    {
881        self.push_fn(move |world| {
882            let _ = world.remove_relation::<R>(origin, target);
883        });
884    }
885
886    /// Checks if entity is alive.
887    #[inline(always)]
888    pub fn is_alive(&self, entity: impl Entity) -> bool {
889        entity.is_alive(self.entities)
890    }
891
892    /// Encodes action to insert resource instance.
893    #[inline(always)]
894    pub fn insert_resource<T>(&mut self, resource: T)
895    where
896        T: 'static,
897    {
898        self.push_fn(move |world| {
899            world.insert_resource(resource);
900        });
901    }
902
903    /// Encodes an action to drop resource instance.
904    #[inline(always)]
905    pub fn drop_resource<T: 'static>(&mut self) {
906        self.push_fn(move |world| {
907            world.remove_resource::<T>();
908        });
909    }
910
911    /// Encodes a custom action with a closure that takes reference to `World`
912    /// and [`LocalActionEncoder`] that can be used to record new actions.
913    #[inline(always)]
914    pub fn closure(&mut self, fun: impl FnOnce(&mut World) + 'static) {
915        self.push_fn(fun);
916    }
917
918    /// Creates new [`LocalActionEncoder`] that records actions into the same [`ActionBuffer`]
919    /// as this one.
920    #[inline(always)]
921    pub fn reborrow(&mut self) -> LocalActionEncoder {
922        LocalActionEncoder {
923            actions: self.actions,
924            entities: self.entities,
925        }
926    }
927
928    /// Encodes an action to remove component from specified entity.
929    #[inline(always)]
930    fn push_fn(&mut self, fun: impl FnOnce(&mut World) + 'static) {
931        self.actions.push_back(LocalActionFn::new(fun));
932    }
933}
934
935/// Spawning iterator. Produced by [`World::spawn_batch`].
936pub struct LocalSpawnBatch<'a, I> {
937    bundles: I,
938    encoder: LocalActionEncoder<'a>,
939}
940
941impl<B, I> LocalSpawnBatch<'_, I>
942where
943    I: Iterator<Item = B>,
944    B: Bundle + Send + 'static,
945{
946    /// Spawns the rest of the entities, dropping their ids.
947    #[inline(always)]
948    pub fn spawn_all(self) {
949        self.for_each(|_| {});
950    }
951}
952
953impl<'a, B, I> Iterator for LocalSpawnBatch<'a, I>
954where
955    I: Iterator<Item = B>,
956    B: Bundle + Send + 'static,
957{
958    type Item = EntityId;
959
960    #[inline(always)]
961    fn next(&mut self) -> Option<EntityId> {
962        let bundle = self.bundles.next()?;
963        Some(self.encoder.spawn_external(bundle).id())
964    }
965
966    #[inline(always)]
967    fn nth(&mut self, n: usize) -> Option<EntityId> {
968        // `LocalSpawnBatch` explicitly does NOT spawn entities that are skipped.
969        let bundle = self.bundles.nth(n)?;
970        Some(self.encoder.spawn_external(bundle).id())
971    }
972
973    #[inline(always)]
974    fn size_hint(&self) -> (usize, Option<usize>) {
975        self.bundles.size_hint()
976    }
977
978    #[inline(always)]
979    fn fold<T, F>(mut self, init: T, mut f: F) -> T
980    where
981        F: FnMut(T, EntityId) -> T,
982    {
983        let additional = iter_reserve_hint(&self.bundles);
984        self.encoder.push_fn(move |world| {
985            world.spawn_reserve::<B>(additional);
986        });
987
988        self.bundles.fold(init, |acc, bundle| {
989            f(acc, self.encoder.spawn_external(bundle).id())
990        })
991    }
992
993    #[inline(always)]
994    fn collect<T>(mut self) -> T
995    where
996        T: FromIterator<EntityId>,
997    {
998        // `FromIterator::from_iter` would probably just call `fn next()`
999        // until the end of the iterator.
1000        //
1001        // Hence we should reserve space in archetype here.
1002
1003        let additional = iter_reserve_hint(&self.bundles);
1004        self.encoder.push_fn(move |world| {
1005            world.spawn_reserve::<B>(additional);
1006        });
1007
1008        FromIterator::from_iter(self)
1009    }
1010}
1011
1012impl<B, I> ExactSizeIterator for LocalSpawnBatch<'_, I>
1013where
1014    I: ExactSizeIterator<Item = B>,
1015    B: Bundle + Send + 'static,
1016{
1017    #[inline(always)]
1018    fn len(&self) -> usize {
1019        self.bundles.len()
1020    }
1021}
1022
1023impl<'a, B, I> DoubleEndedIterator for LocalSpawnBatch<'a, I>
1024where
1025    I: DoubleEndedIterator<Item = B>,
1026    B: Bundle + Send + 'static,
1027{
1028    #[inline(always)]
1029    fn next_back(&mut self) -> Option<EntityId> {
1030        let bundle = self.bundles.next_back()?;
1031        Some(self.encoder.spawn_external(bundle).id())
1032    }
1033
1034    #[inline(always)]
1035    fn nth_back(&mut self, n: usize) -> Option<EntityId> {
1036        // `LocalSpawnBatch` explicitly does NOT spawn entities that are skipped.
1037        let bundle = self.bundles.nth_back(n)?;
1038        Some(self.encoder.spawn_external(bundle).id())
1039    }
1040
1041    #[inline(always)]
1042    fn rfold<T, F>(mut self, init: T, mut f: F) -> T
1043    where
1044        Self: Sized,
1045        F: FnMut(T, EntityId) -> T,
1046    {
1047        let additional = iter_reserve_hint(&self.bundles);
1048        self.encoder.push_fn(move |world| {
1049            world.spawn_reserve::<B>(additional);
1050        });
1051
1052        self.bundles.rfold(init, |acc, bundle| {
1053            f(acc, self.encoder.spawn_external(bundle).id())
1054        })
1055    }
1056}
1057
1058impl<B, I> FusedIterator for LocalSpawnBatch<'_, I>
1059where
1060    I: FusedIterator<Item = B>,
1061    B: Bundle + Send + 'static,
1062{
1063}