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