Extract bundle spawner code
This commit is contained in:
parent
fa878af9f6
commit
0d4f8ea461
@ -5,6 +5,7 @@
|
|||||||
mod impls;
|
mod impls;
|
||||||
mod insert;
|
mod insert;
|
||||||
mod remove;
|
mod remove;
|
||||||
|
mod spawner;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
@ -62,26 +63,20 @@ mod tests;
|
|||||||
pub use bevy_ecs_macros::Bundle;
|
pub use bevy_ecs_macros::Bundle;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
archetype::{
|
archetype::{Archetype, BundleComponentStatus, ComponentStatus},
|
||||||
Archetype, ArchetypeCreated, ArchetypeId, BundleComponentStatus, ComponentStatus,
|
|
||||||
SpawnBundleStatus,
|
|
||||||
},
|
|
||||||
change_detection::MaybeLocation,
|
change_detection::MaybeLocation,
|
||||||
component::{
|
component::{
|
||||||
ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor,
|
ComponentId, Components, ComponentsRegistrator, RequiredComponentConstructor,
|
||||||
RequiredComponents, StorageType, Tick,
|
RequiredComponents, StorageType, Tick,
|
||||||
},
|
},
|
||||||
entity::{Entities, Entity, EntityLocation},
|
entity::Entity,
|
||||||
lifecycle::{ADD, INSERT},
|
|
||||||
prelude::World,
|
|
||||||
query::DebugCheckedUnwrap,
|
query::DebugCheckedUnwrap,
|
||||||
relationship::RelationshipHookMode,
|
|
||||||
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
|
storage::{SparseSetIndex, SparseSets, Storages, Table, TableRow},
|
||||||
world::{unsafe_world_cell::UnsafeWorldCell, EntityWorldMut},
|
world::EntityWorldMut,
|
||||||
};
|
};
|
||||||
use alloc::{boxed::Box, vec, vec::Vec};
|
use alloc::{boxed::Box, vec, vec::Vec};
|
||||||
use bevy_platform::collections::{HashMap, HashSet};
|
use bevy_platform::collections::{HashMap, HashSet};
|
||||||
use bevy_ptr::{ConstNonNull, OwningPtr};
|
use bevy_ptr::OwningPtr;
|
||||||
use bevy_utils::TypeIdMap;
|
use bevy_utils::TypeIdMap;
|
||||||
use core::{any::TypeId, ptr::NonNull};
|
use core::{any::TypeId, ptr::NonNull};
|
||||||
|
|
||||||
@ -610,183 +605,6 @@ pub(crate) enum ArchetypeMoveType {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
|
||||||
// SAFETY: These come from the same world. `world.components_registrator` can't be used since we borrow other fields too.
|
|
||||||
let mut registrator =
|
|
||||||
unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) };
|
|
||||||
let bundle_id = world
|
|
||||||
.bundles
|
|
||||||
.register_info::<T>(&mut registrator, &mut world.storages);
|
|
||||||
// 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 [`BundleInfo`]'s type
|
|
||||||
#[inline]
|
|
||||||
#[track_caller]
|
|
||||||
pub unsafe fn spawn_non_existent<T: DynamicBundle>(
|
|
||||||
&mut self,
|
|
||||||
entity: Entity,
|
|
||||||
bundle: T,
|
|
||||||
caller: MaybeLocation,
|
|
||||||
) -> (EntityLocation, T::Effect) {
|
|
||||||
// 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, after_effect) = {
|
|
||||||
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);
|
|
||||||
let after_effect = bundle_info.write_components(
|
|
||||||
table,
|
|
||||||
sparse_sets,
|
|
||||||
&SpawnBundleStatus,
|
|
||||||
bundle_info.required_components.iter(),
|
|
||||||
entity,
|
|
||||||
table_row,
|
|
||||||
self.change_tick,
|
|
||||||
bundle,
|
|
||||||
InsertMode::Replace,
|
|
||||||
caller,
|
|
||||||
);
|
|
||||||
entities.set(entity.index(), Some(location));
|
|
||||||
entities.mark_spawn_despawn(entity.index(), caller, self.change_tick);
|
|
||||||
(location, after_effect)
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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() {
|
|
||||||
deferred_world.trigger_observers(
|
|
||||||
ADD,
|
|
||||||
Some(entity),
|
|
||||||
bundle_info.iter_contributed_components(),
|
|
||||||
caller,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
deferred_world.trigger_on_insert(
|
|
||||||
archetype,
|
|
||||||
entity,
|
|
||||||
bundle_info.iter_contributed_components(),
|
|
||||||
caller,
|
|
||||||
RelationshipHookMode::Run,
|
|
||||||
);
|
|
||||||
if archetype.has_insert_observer() {
|
|
||||||
deferred_world.trigger_observers(
|
|
||||||
INSERT,
|
|
||||||
Some(entity),
|
|
||||||
bundle_info.iter_contributed_components(),
|
|
||||||
caller,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
(location, after_effect)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
/// `T` must match this [`BundleInfo`]'s type
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn spawn<T: Bundle>(
|
|
||||||
&mut self,
|
|
||||||
bundle: T,
|
|
||||||
caller: MaybeLocation,
|
|
||||||
) -> (Entity, T::Effect) {
|
|
||||||
let entity = self.entities().alloc();
|
|
||||||
// SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type
|
|
||||||
let (_, after_effect) = unsafe { self.spawn_non_existent(entity, bundle, caller) };
|
|
||||||
(entity, after_effect)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn entities(&mut self) -> &mut Entities {
|
|
||||||
// SAFETY: No outstanding references to self.world, changes to entities cannot invalidate our internal pointers
|
|
||||||
unsafe { &mut self.world.world_mut().entities }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world.
|
/// Metadata for bundles. Stores a [`BundleInfo`] for each type of [`Bundle`] in a given world.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Bundles {
|
pub struct Bundles {
|
||||||
|
192
crates/bevy_ecs/src/bundle/spawner.rs
Normal file
192
crates/bevy_ecs/src/bundle/spawner.rs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
|
use bevy_ptr::ConstNonNull;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
archetype::{Archetype, ArchetypeCreated, ArchetypeId, SpawnBundleStatus},
|
||||||
|
bundle::{Bundle, BundleId, BundleInfo, DynamicBundle, InsertMode},
|
||||||
|
change_detection::MaybeLocation,
|
||||||
|
component::{ComponentsRegistrator, Tick},
|
||||||
|
entity::{Entities, Entity, EntityLocation},
|
||||||
|
lifecycle::{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 {
|
||||||
|
// SAFETY: These come from the same world. `world.components_registrator` can't be used since we borrow other fields too.
|
||||||
|
let mut registrator =
|
||||||
|
unsafe { ComponentsRegistrator::new(&mut world.components, &mut world.component_ids) };
|
||||||
|
let bundle_id = world
|
||||||
|
.bundles
|
||||||
|
.register_info::<T>(&mut registrator, &mut world.storages);
|
||||||
|
// 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 [`BundleInfo`]'s type
|
||||||
|
#[inline]
|
||||||
|
#[track_caller]
|
||||||
|
pub unsafe fn spawn_non_existent<T: DynamicBundle>(
|
||||||
|
&mut self,
|
||||||
|
entity: Entity,
|
||||||
|
bundle: T,
|
||||||
|
caller: MaybeLocation,
|
||||||
|
) -> (EntityLocation, T::Effect) {
|
||||||
|
// 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, after_effect) = {
|
||||||
|
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);
|
||||||
|
let after_effect = bundle_info.write_components(
|
||||||
|
table,
|
||||||
|
sparse_sets,
|
||||||
|
&SpawnBundleStatus,
|
||||||
|
bundle_info.required_components.iter(),
|
||||||
|
entity,
|
||||||
|
table_row,
|
||||||
|
self.change_tick,
|
||||||
|
bundle,
|
||||||
|
InsertMode::Replace,
|
||||||
|
caller,
|
||||||
|
);
|
||||||
|
entities.set(entity.index(), Some(location));
|
||||||
|
entities.mark_spawn_despawn(entity.index(), caller, self.change_tick);
|
||||||
|
(location, after_effect)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
deferred_world.trigger_observers(
|
||||||
|
ADD,
|
||||||
|
Some(entity),
|
||||||
|
bundle_info.iter_contributed_components(),
|
||||||
|
caller,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
deferred_world.trigger_on_insert(
|
||||||
|
archetype,
|
||||||
|
entity,
|
||||||
|
bundle_info.iter_contributed_components(),
|
||||||
|
caller,
|
||||||
|
RelationshipHookMode::Run,
|
||||||
|
);
|
||||||
|
if archetype.has_insert_observer() {
|
||||||
|
deferred_world.trigger_observers(
|
||||||
|
INSERT,
|
||||||
|
Some(entity),
|
||||||
|
bundle_info.iter_contributed_components(),
|
||||||
|
caller,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(location, after_effect)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// `T` must match this [`BundleInfo`]'s type
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn spawn<T: Bundle>(
|
||||||
|
&mut self,
|
||||||
|
bundle: T,
|
||||||
|
caller: MaybeLocation,
|
||||||
|
) -> (Entity, T::Effect) {
|
||||||
|
let entity = self.entities().alloc();
|
||||||
|
// SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type
|
||||||
|
let (_, after_effect) = unsafe { self.spawn_non_existent(entity, bundle, caller) };
|
||||||
|
(entity, after_effect)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn entities(&mut self) -> &mut Entities {
|
||||||
|
// SAFETY: No outstanding references to self.world, changes to entities cannot invalidate our internal pointers
|
||||||
|
unsafe { &mut self.world.world_mut().entities }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # 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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user