use core::ptr::NonNull;
use bevy::{
ecs::component::{ComponentId, Mutable},
prelude::*,
ptr::OwningPtr,
};
use bumpalo::Bump;
#[derive(Deref)]
pub struct DeferredEntity<'w> {
#[deref]
entity: EntityWorldMut<'w>,
changes: &'w mut DeferredChanges,
}
impl<'w> DeferredEntity<'w> {
pub fn new(entity: EntityWorldMut<'w>, changes: &'w mut DeferredChanges) -> Self {
changes.clear();
Self { entity, changes }
}
pub fn insert<C: Component>(&mut self, component: C) -> &mut Self {
let component_id = self.register_component::<C>();
unsafe { self.changes.insertions.insert(component, component_id) };
self
}
pub fn remove<C: Component>(&mut self) -> &mut Self {
let component_id = self.register_component::<C>();
self.changes.removals.push(component_id);
self
}
#[inline]
pub fn get_mut<C: Component<Mutability = Mutable>>(&mut self) -> Option<Mut<'_, C>> {
self.entity.get_mut()
}
fn register_component<C: Component>(&mut self) -> ComponentId {
unsafe { self.world_mut().register_component::<C>() }
}
pub unsafe fn world_mut(&mut self) -> &mut World {
unsafe { self.entity.world_mut() }
}
pub fn flush(&mut self) {
self.changes.apply(&mut self.entity);
}
}
#[derive(Default)]
pub struct DeferredChanges {
removals: Vec<ComponentId>,
insertions: DeferredInsertions,
}
impl DeferredChanges {
fn apply(&mut self, entity: &mut EntityWorldMut) {
if !self.removals.is_empty() {
entity.remove_by_ids(&self.removals);
}
if !self.insertions.is_empty() {
self.insertions.apply(entity);
}
self.clear();
}
fn clear(&mut self) {
self.removals.clear();
self.insertions.clear();
}
}
#[derive(Default)]
pub(crate) struct DeferredInsertions {
ptrs: Vec<NonNull<u8>>,
ids: Vec<ComponentId>,
bump: Bump,
}
impl DeferredInsertions {
unsafe fn insert<C: Component>(&mut self, component: C, component_id: ComponentId) {
let ptr: NonNull<_> = self.bump.alloc(component).into();
self.ptrs.push(ptr.cast());
self.ids.push(component_id);
}
fn apply(&mut self, entity: &mut EntityWorldMut) {
unsafe { entity.insert_by_ids(&self.ids, self.ptrs.iter().map(|&p| OwningPtr::new(p))) };
}
fn is_empty(&self) -> bool {
self.ids.is_empty()
}
fn clear(&mut self) {
self.ids.clear();
self.ptrs.clear();
self.bump.reset();
}
}
unsafe impl Send for DeferredInsertions {}
#[cfg(test)]
mod tests {
use alloc::sync::Arc;
use core::any::Any;
use super::*;
#[test]
fn buffering() {
let mut world = World::new();
let before_archetypes = world.archetypes().len();
let mut changes = DeferredChanges::default();
let mut entity = DeferredEntity::new(world.spawn_empty(), &mut changes);
entity
.insert(Unit)
.insert(Trivial(1))
.insert(WithVec(vec![2, 3]))
.insert(WithBox(Box::new(Trivial(4))))
.insert(WithArc(Arc::new(Trivial(5))));
entity.flush();
let after_archetypes = entity.world().archetypes().len();
assert!(entity.get::<Unit>().is_some());
assert_eq!(**entity.get::<Trivial>().unwrap(), 1);
assert_eq!(**entity.get::<WithVec>().unwrap(), [2, 3]);
let with_box = entity.get::<WithBox>().unwrap();
assert_eq!(**with_box.downcast_ref::<Trivial>().unwrap(), 4);
let with_arc = entity.get::<WithArc>().unwrap();
assert_eq!(Arc::strong_count(with_arc), 1);
assert_eq!(**with_arc.downcast_ref::<Trivial>().unwrap(), 5);
assert_eq!(
after_archetypes - before_archetypes,
1,
"insertions should batch into one archetype move"
);
entity
.remove::<Unit>()
.remove::<Trivial>()
.remove::<WithVec>()
.remove::<WithBox>()
.remove::<WithArc>();
entity.flush();
assert!(!entity.contains::<Unit>());
assert!(!entity.contains::<Trivial>());
assert!(!entity.contains::<WithVec>());
assert!(!entity.contains::<WithBox>());
assert!(!entity.contains::<WithArc>());
assert_eq!(
entity.world().archetypes().len(),
after_archetypes,
"removals shouldn't create intermediate archetypes"
);
}
#[derive(Component)]
struct Unit;
#[derive(Component, Deref)]
struct Trivial(usize);
#[derive(Component, Deref)]
struct WithVec(Vec<u8>);
#[derive(Component, Deref)]
struct WithBox(Box<dyn Any + Send + Sync>);
#[derive(Component, Deref)]
struct WithArc(Arc<dyn Any + Send + Sync>);
}