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