use crate::{
component::{Component, ComponentId, Components, ComponentsRegistrator},
relationship::RelationshipHookMode,
world::EntityWorldMut,
};
use alloc::vec::Vec;
use bevy_ptr::OwningPtr;
use bumpalo::Bump;
use core::{alloc::Layout, ptr::NonNull};
#[derive(Default)]
pub struct BundleScratch {
component_ids: Vec<ComponentId>,
component_ptrs: Vec<NonNull<u8>>,
alloc: Bump,
}
impl BundleScratch {
#[inline]
pub fn writer<'a>(&'a mut self) -> BundleWriter<'a> {
self.component_ids.clear();
self.component_ptrs.clear();
BundleWriter(self)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.component_ids.is_empty()
}
pub unsafe fn manual_drop(&mut self, components: &Components) {
for (id, ptr) in self
.component_ids
.drain(..)
.zip(self.component_ptrs.drain(..))
{
if let Some(info) = components.get_info(id)
&& let Some(drop) = info.drop()
{
unsafe {
let ptr = OwningPtr::new(ptr);
(drop)(ptr);
}
}
}
self.alloc.reset();
}
}
pub struct BundleWriter<'a>(&'a mut BundleScratch);
unsafe impl Send for BundleScratch where Bump: Send {}
impl<'a> BundleWriter<'a> {
pub unsafe fn push_component<C: Component>(
&mut self,
components: &mut ComponentsRegistrator,
component: C,
) {
let id = components.register_component::<C>();
OwningPtr::make(component, |ptr| {
self.push_component_by_id(id, ptr, Layout::new::<C>());
});
}
pub unsafe fn push_component_by_id(
&mut self,
id: ComponentId,
component: OwningPtr<'_>,
layout: Layout,
) {
let ptr = self.0.alloc.alloc_layout(layout);
core::ptr::copy(component.as_ptr(), ptr.as_ptr(), layout.size());
self.0.component_ids.push(id);
self.0.component_ptrs.push(ptr);
}
#[track_caller]
pub unsafe fn write(self, entity: &mut EntityWorldMut) {
self.write_with_relationship_hook_insert_mode(entity, RelationshipHookMode::Run);
}
#[track_caller]
pub unsafe fn write_with_relationship_hook_insert_mode(
self,
entity: &mut EntityWorldMut,
relationship_hook_insert_mode: RelationshipHookMode,
) {
unsafe {
entity.insert_by_ids_internal(
&self.0.component_ids,
self.0
.component_ptrs
.drain(..)
.map(|ptr| OwningPtr::new(ptr)),
relationship_hook_insert_mode,
);
}
self.0.component_ids.clear();
self.0.alloc.reset();
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.component_ids.is_empty()
}
}
#[cfg(test)]
mod tests {
use crate::{bundle::BundleScratch, component::Component, name::Name, world::World};
#[test]
fn write_component() {
#[derive(Component)]
struct X;
let mut world = World::new();
let mut bundle_scratch = BundleScratch::default();
let mut bundle_writer = bundle_scratch.writer();
unsafe {
let mut components = world.components_registrator();
bundle_writer.push_component(&mut components, X);
bundle_writer.push_component(&mut components, Name::new("Hi"));
let mut entity = world.spawn_empty();
bundle_writer.write(&mut entity);
assert_eq!(entity.get::<Name>().unwrap().as_str(), "Hi");
assert!(entity.contains::<X>());
}
}
}