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,
component::Tick,
entity::{Entities, Entity, EntityLocation},
event::EntityComponentsTrigger,
lifecycle::{Add, Insert, ADD, INSERT},
relationship::RelationshipHookMode,
storage::Table,
world::{unsafe_world_cell::UnsafeWorldCell, World},
};
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>();
unsafe { Self::new_with_id(world, bundle_id, change_tick) }
}
#[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) {
let (archetype, table) = unsafe { (self.archetype.as_mut(), self.table.as_mut()) };
archetype.reserve(additional);
table.reserve(additional);
}
#[inline]
#[track_caller]
pub unsafe fn spawn_non_existent<T: DynamicBundle>(
&mut self,
entity: Entity,
bundle: MovingPtr<'_, T>,
caller: MaybeLocation,
) -> EntityLocation {
let bundle_info = self.bundle_info.as_ref();
let location = {
let table = self.table.as_mut();
let archetype = self.archetype.as_mut();
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(entity.index(), Some(location));
entities.mark_spawn_despawn(entity.index(), caller, self.change_tick);
location
};
let mut deferred_world = unsafe { self.world.into_deferred() };
let archetype = self.archetype.as_ref();
unsafe {
deferred_world.trigger_on_add(
archetype,
entity,
bundle_info.iter_contributed_components(),
caller,
);
if archetype.has_add_observer() {
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() {
deferred_world.trigger_raw(
INSERT,
&mut Insert { entity },
&mut EntityComponentsTrigger {
components: bundle_info.contributed_components(),
},
caller,
);
}
};
location
}
#[inline]
pub unsafe fn spawn<T: Bundle>(
&mut self,
bundle: MovingPtr<'_, T>,
caller: MaybeLocation,
) -> Entity {
let entity = self.entities().alloc();
unsafe { self.spawn_non_existent::<T>(entity, bundle, caller) };
entity
}
#[inline]
pub(crate) fn entities(&mut self) -> &mut Entities {
unsafe { &mut self.world.world_mut().entities }
}
#[inline]
pub(crate) unsafe fn flush_commands(&mut self) {
self.world.world_mut().flush();
}
}