Skip to main content

bevy_ecs/bundle/
spawner.rs

1use core::ptr::NonNull;
2
3use bevy_ptr::{ConstNonNull, MovingPtr};
4
5use crate::{
6    archetype::{Archetype, ArchetypeCreated, ArchetypeId, SpawnBundleStatus},
7    bundle::{Bundle, BundleId, BundleInfo, DynamicBundle, InsertMode},
8    change_detection::{MaybeLocation, Tick},
9    entity::{Entity, EntityAllocator, EntityLocation},
10    event::EntityComponentsTrigger,
11    lifecycle::{Add, Insert, ADD, INSERT},
12    relationship::RelationshipHookMode,
13    storage::Table,
14    world::{unsafe_world_cell::UnsafeWorldCell, World},
15};
16
17// SAFETY: We have exclusive world access so our pointers can't be invalidated externally
18pub(crate) struct BundleSpawner<'w> {
19    world: UnsafeWorldCell<'w>,
20    bundle_info: ConstNonNull<BundleInfo>,
21    table: NonNull<Table>,
22    archetype: NonNull<Archetype>,
23    change_tick: Tick,
24}
25
26impl<'w> BundleSpawner<'w> {
27    #[inline]
28    pub fn new<T: Bundle>(world: &'w mut World, change_tick: Tick) -> Self {
29        let bundle_id = world.register_bundle_info::<T>();
30
31        // SAFETY: we initialized this bundle_id in `init_info`
32        unsafe { Self::new_with_id(world, bundle_id, change_tick) }
33    }
34
35    /// Creates a new [`BundleSpawner`].
36    ///
37    /// # Safety
38    /// Caller must ensure that `bundle_id` exists in `world.bundles`
39    #[inline]
40    pub(crate) unsafe fn new_with_id(
41        world: &'w mut World,
42        bundle_id: BundleId,
43        change_tick: Tick,
44    ) -> Self {
45        let bundle_info = world.bundles.get_unchecked(bundle_id);
46        let (new_archetype_id, is_new_created) = bundle_info.insert_bundle_into_archetype(
47            &mut world.archetypes,
48            &mut world.storages,
49            &world.components,
50            &world.observers,
51            ArchetypeId::EMPTY,
52        );
53
54        let archetype = &mut world.archetypes[new_archetype_id];
55        let table = &mut world.storages.tables[archetype.table_id()];
56        let spawner = Self {
57            bundle_info: bundle_info.into(),
58            table: table.into(),
59            archetype: archetype.into(),
60            change_tick,
61            world: world.as_unsafe_world_cell(),
62        };
63        if is_new_created {
64            spawner
65                .world
66                .into_deferred()
67                .trigger(ArchetypeCreated(new_archetype_id));
68        }
69        spawner
70    }
71
72    #[inline]
73    pub fn reserve_storage(&mut self, additional: usize) {
74        // SAFETY: There are no outstanding world references
75        let (archetype, table) = unsafe { (self.archetype.as_mut(), self.table.as_mut()) };
76        archetype.reserve(additional);
77        table.reserve(additional);
78    }
79
80    /// # Safety
81    /// - `entity` must be allocated (but non-existent),
82    /// - `T` must match this [`BundleSpawner`]'s type
83    /// - If `T::Effect: !NoBundleEffect.`, then [`apply_effect`] must  be called exactly once on `bundle`
84    ///   after this function returns before returning to safe code.
85    /// - The value pointed to by `bundle` must not be accessed for anything other than [`apply_effect`]
86    ///   or dropped.
87    ///
88    /// [`apply_effect`]: crate::bundle::DynamicBundle::apply_effect
89    #[inline]
90    #[track_caller]
91    pub unsafe fn spawn_at<T: DynamicBundle>(
92        &mut self,
93        entity: Entity,
94        bundle: MovingPtr<'_, T>,
95        caller: MaybeLocation,
96    ) -> EntityLocation {
97        // SAFETY: We do not make any structural changes to the archetype graph through self.world so these pointers always remain valid
98        let bundle_info = self.bundle_info.as_ref();
99        let location = {
100            let table = self.table.as_mut();
101            let archetype = self.archetype.as_mut();
102
103            // SAFETY: Mutable references do not alias and will be dropped after this block
104            let (sparse_sets, entities) = {
105                let world = self.world.world_mut();
106                (&mut world.storages.sparse_sets, &mut world.entities)
107            };
108            let table_row = table.allocate(entity);
109            let location = archetype.allocate(entity, table_row);
110            bundle_info.write_components(
111                table,
112                sparse_sets,
113                &SpawnBundleStatus,
114                bundle_info.required_component_constructors.iter(),
115                entity,
116                table_row,
117                self.change_tick,
118                bundle,
119                InsertMode::Replace,
120                caller,
121            );
122            entities.set_location(entity.index(), Some(location));
123            entities.mark_spawned_or_despawned(entity.index(), caller, self.change_tick);
124            location
125        };
126
127        // SAFETY: We have no outstanding mutable references to world as they were dropped
128        let mut deferred_world = unsafe { self.world.into_deferred() };
129        // SAFETY: `DeferredWorld` cannot provide mutable access to `Archetypes`.
130        let archetype = self.archetype.as_ref();
131        // SAFETY: All components in the bundle are guaranteed to exist in the World
132        // as they must be initialized before creating the BundleInfo.
133        unsafe {
134            deferred_world.trigger_on_add(
135                archetype,
136                entity,
137                bundle_info.iter_contributed_components(),
138                caller,
139            );
140            if archetype.has_add_observer() {
141                // SAFETY: the ADD event_key corresponds to the Add event's type
142                deferred_world.trigger_raw(
143                    ADD,
144                    &mut Add { entity },
145                    &mut EntityComponentsTrigger {
146                        components: bundle_info.contributed_components(),
147                    },
148                    caller,
149                );
150            }
151            deferred_world.trigger_on_insert(
152                archetype,
153                entity,
154                bundle_info.iter_contributed_components(),
155                caller,
156                RelationshipHookMode::Run,
157            );
158            if archetype.has_insert_observer() {
159                // SAFETY: the INSERT event_key corresponds to the Insert event's type
160                deferred_world.trigger_raw(
161                    INSERT,
162                    &mut Insert { entity },
163                    &mut EntityComponentsTrigger {
164                        components: bundle_info.contributed_components(),
165                    },
166                    caller,
167                );
168            }
169        };
170
171        location
172    }
173
174    /// # Safety
175    /// - `T` must match this [`BundleSpawner`]'s type
176    /// - If `T::Effect: !NoBundleEffect.`, then [`apply_effect`] must  be called exactly once on `bundle`
177    ///   after this function returns before returning to safe code.
178    /// - The value pointed to by `bundle` must not be accessed for anything other than [`apply_effect`]
179    ///   or dropped.
180    ///
181    /// [`apply_effect`]: crate::bundle::DynamicBundle::apply_effect
182    #[inline]
183    pub unsafe fn spawn<T: Bundle>(
184        &mut self,
185        bundle: MovingPtr<'_, T>,
186        caller: MaybeLocation,
187    ) -> Entity {
188        let entity = self.allocator().alloc();
189        // SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type
190        let _ = unsafe { self.spawn_at(entity, bundle, caller) };
191        entity
192    }
193
194    #[inline]
195    pub(crate) fn allocator(&mut self) -> &'w mut EntityAllocator {
196        // SAFETY: No outstanding references to self.world, changes to entities cannot invalidate our internal pointers
197        unsafe { &mut self.world.world_mut().allocator }
198    }
199
200    /// # Safety
201    /// - `Self` must be dropped after running this function as it may invalidate internal pointers.
202    #[inline]
203    pub(crate) unsafe fn flush_commands(&mut self) {
204        // SAFETY: pointers on self can be invalidated,
205        self.world.world_mut().flush();
206    }
207}