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
17pub(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 unsafe { Self::new_with_id(world, bundle_id, change_tick) }
33 }
34
35 #[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 let (archetype, table) = unsafe { (self.archetype.as_mut(), self.table.as_mut()) };
76 archetype.reserve(additional);
77 table.reserve(additional);
78 }
79
80 #[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 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 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 let mut deferred_world = unsafe { self.world.into_deferred() };
129 let archetype = self.archetype.as_ref();
131 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 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 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 #[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 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 unsafe { &mut self.world.world_mut().allocator }
198 }
199
200 #[inline]
203 pub(crate) unsafe fn flush_commands(&mut self) {
204 self.world.world_mut().flush();
206 }
207}