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
use core::ptr::NonNull;
use bevy_ptr::{ConstNonNull, MovingPtr};
use crate::{
archetype::{Archetype, ArchetypeCreated, ArchetypeId, SpawnBundleStatus},
bundle::{Bundle, BundleId, BundleInfo, DynamicBundle, InsertMode},
change_detection::{MaybeLocation, Tick},
entity::{Entity, EntityAllocator, EntityLocation},
event::EntityComponentsTrigger,
lifecycle::{Add, Insert, 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> {
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 {
let bundle_info = world.bundles.get_unchecked(bundle_id);
let (new_archetype_id, is_new_created) = 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 {
spawner
.world
.into_deferred()
.trigger(ArchetypeCreated(new_archetype_id));
}
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 = self.bundle_info.as_ref();
let location = {
let table = self.table.as_mut();
let archetype = self.archetype.as_mut();
// SAFETY: Mutable references do not alias and will be dropped after this block
let (sparse_sets, entities) = {
let world = self.world.world_mut();
(&mut world.storages.sparse_sets, &mut world.entities)
};
let table_row = table.allocate(entity);
let location = archetype.allocate(entity, table_row);
bundle_info.write_components(
table,
sparse_sets,
&SpawnBundleStatus,
bundle_info.required_component_constructors.iter(),
entity,
table_row,
self.change_tick,
bundle,
InsertMode::Replace,
caller,
);
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 = 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 Add { entity },
&mut EntityComponentsTrigger {
components: bundle_info.contributed_components(),
},
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 Insert { entity },
&mut EntityComponentsTrigger {
components: bundle_info.contributed_components(),
},
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().allocator }
}
/// # Safety
/// - `Self` must be dropped after running this function as it may invalidate internal pointers.
#[inline]
pub(crate) unsafe fn flush_commands(&mut self) {
// SAFETY: pointers on self can be invalidated,
self.world.world_mut().flush();
}
}