diff --git a/crates/bevy_ecs/hecs/src/bundle.rs b/crates/bevy_ecs/hecs/src/bundle.rs index 519ec16b564f1..ced9312251d3b 100644 --- a/crates/bevy_ecs/hecs/src/bundle.rs +++ b/crates/bevy_ecs/hecs/src/bundle.rs @@ -42,36 +42,6 @@ pub trait DynamicBundle { unsafe fn put(self, f: impl FnMut(*mut u8, ComponentId, usize) -> bool); } -/// A bundle that can be created dynamically at runtime -#[derive(Debug)] -pub struct RuntimeBundle { - /// The information for each component in this bundle - pub components: Vec, - /// The raw data for all of the components in the bundle - pub data: Vec>, -} - -impl DynamicBundle for RuntimeBundle { - fn with_ids(&self, f: impl FnOnce(&[ComponentId]) -> T) -> T { - let ids: Vec<_> = self.components.iter().map(|x| x.id()).collect(); - f(ids.as_slice()) - } - - fn type_info(&self) -> Vec { - self.components.clone() - } - - unsafe fn put(self, mut f: impl FnMut(*mut u8, ComponentId, usize) -> bool) { - for (mut data, info) in self.data.into_iter().zip(self.components.into_iter()) { - debug_assert_eq!(data.len(), info.layout().size()); - - if f(data.as_mut_ptr(), info.id(), info.layout().size()) { - std::mem::forget(data); - } - } - } -} - /// A statically typed collection of components pub trait Bundle: DynamicBundle { #[doc(hidden)] diff --git a/crates/bevy_ecs/hecs/src/entity_builder.rs b/crates/bevy_ecs/hecs/src/entity_builder.rs index 28b24cd0ab498..0ac30d8620e48 100644 --- a/crates/bevy_ecs/hecs/src/entity_builder.rs +++ b/crates/bevy_ecs/hecs/src/entity_builder.rs @@ -25,11 +25,8 @@ use crate::{ }; use bevy_utils::HashSet; -use core::{ - any::TypeId, - mem::{self, MaybeUninit}, - ptr, -}; +use core::{intrinsics::copy_nonoverlapping, mem::MaybeUninit, ptr}; +use ptr::slice_from_raw_parts; use crate::{archetype::TypeInfo, Component, DynamicBundle}; @@ -68,24 +65,40 @@ impl EntityBuilder { /// Add `component` to the entity pub fn add(&mut self, component: T) -> &mut Self { - if !self.id_set.insert(TypeId::of::().into()) { + self.add_dynamic(TypeInfo::of::(), unsafe { + &*slice_from_raw_parts( + &component as *const T as *const u8, + std::mem::size_of::(), + ) + }); + std::mem::forget(component); + self + } + + /// Add a dynamic component to the entity + /// + /// You must provide the type info and a slice containing the component data + pub fn add_dynamic(&mut self, type_info: TypeInfo, data: &[u8]) -> &mut Self { + debug_assert_eq!(type_info.layout().size(), data.len()); + + if !self.id_set.insert(type_info.id) { return self; } - let end = self.cursor + mem::size_of::(); + let end = self.cursor + type_info.layout.size(); if end > self.storage.len() { self.grow(end); } - if mem::size_of::() != 0 { + if type_info.layout.size() != 0 { unsafe { - self.storage - .as_mut_ptr() - .add(self.cursor) - .cast::() - .write_unaligned(component); + copy_nonoverlapping( + data.as_ptr(), + self.storage.as_mut_ptr().add(self.cursor) as *mut u8, + data.len(), + ); } } - self.info.push((TypeInfo::of::(), self.cursor)); - self.cursor += mem::size_of::(); + self.info.push((type_info, self.cursor)); + self.cursor += type_info.layout().size(); self } diff --git a/crates/bevy_ecs/hecs/src/lib.rs b/crates/bevy_ecs/hecs/src/lib.rs index d625250e7d1ae..1c2ed820063bc 100644 --- a/crates/bevy_ecs/hecs/src/lib.rs +++ b/crates/bevy_ecs/hecs/src/lib.rs @@ -77,7 +77,7 @@ mod world; pub use archetype::{Archetype, TypeState}; pub use borrow::{AtomicBorrow, Ref, RefMut}; -pub use bundle::{Bundle, DynamicBundle, MissingComponent, RuntimeBundle}; +pub use bundle::{Bundle, DynamicBundle, MissingComponent}; pub use entities::{Entity, EntityReserver, Location, NoSuchEntity}; pub use entity_builder::{BuiltEntity, EntityBuilder}; pub use query::{ diff --git a/examples/ecs/dynamic_components.rs b/examples/ecs/dynamic_components.rs index fcf56c0faf447..95ff44b75c45b 100644 --- a/examples/ecs/dynamic_components.rs +++ b/examples/ecs/dynamic_components.rs @@ -11,7 +11,7 @@ use std::{alloc::Layout, time::Duration}; use bevy::prelude::*; use bevy_app::ScheduleRunnerPlugin; use bevy_ecs::{ - ComponentId, DynamicComponentInfo, DynamicComponentQuery, DynamicSystemSettings, RuntimeBundle, + ComponentId, DynamicComponentInfo, DynamicComponentQuery, DynamicSystemSettings, EntityBuilder, TypeInfo, }; @@ -24,11 +24,12 @@ fn spawn_scene(world: &mut World, _resources: &mut Resources) { // representing a Position and one representing a Velocity. Each of these will be made up of two // bytes for simplicity, one representing the x and y position/velocity. - // We create our first component bundle - let components1 = RuntimeBundle { - // we must define our components' type information - components: vec![ - // First we define our "Position" component + // We create our first entity + let mut builder = EntityBuilder::new(); + // Then we add our "Position component" + let entity1 = builder + .add_dynamic( + // We need to describe our component's information TypeInfo { // We must provide a unique id for the compoonent id: ComponentId::ExternalId(0), @@ -37,53 +38,62 @@ fn spawn_scene(world: &mut World, _resources: &mut Resources) { // And we must specify a drop function for our component drop: |_| (), }, - // Next we define our "Velocity" component + // And provide the raw byte data data for the component + vec![ + 0, // X position byte + 0, // Y position byte + ] + // And cast the data to a pointer + .as_slice(), + ) + // Next we add our "Velocity component" + .add_dynamic( TypeInfo { - // We must specify a different ID for the velocity component + // This component needs its own unique ID id: ComponentId::ExternalId(1), - // We specify the layout which happens to be the same as "Position" - layout: Layout::from_size_align(2, 1).unwrap(), - // And the drop function + layout: Layout::from_size_align(2 /* size */, 1 /* alignment */).unwrap(), drop: |_| (), }, - ], - - // Data must be a Vector of Vectors of bytes and must contain the raw byte data for - // each of the components we want to add - data: vec![ - // This will be the raw byte data for our position component vec![ 0, // X position byte - 0, // Y position byte - ], - // This will be the raw byte data for our velocity component - vec![ - 1, // X velocity byte - 0, // Y velocity byte - ], - ], - }; - - // Now we create another bundle for our next entity - let components2 = RuntimeBundle { - components: vec![ + 1, // Y position byte + ] + .as_slice(), + ) + .build(); + + // And let's create another entity + let mut builder = EntityBuilder::new(); + let entity2 = builder + .add_dynamic( TypeInfo { id: ComponentId::ExternalId(0), - layout: Layout::from_size_align(2 /* size */, 1 /* alignment */).unwrap(), + layout: Layout::from_size_align(2, 1).unwrap(), drop: |_| (), }, + vec![ + 0, // X position byte + 0, // Y position byte + ] + .as_slice(), + ) + .add_dynamic( TypeInfo { id: ComponentId::ExternalId(1), layout: Layout::from_size_align(2, 1).unwrap(), drop: |_| (), }, - ], - data: vec![vec![0, 0], vec![0, 2]], - }; + vec![ + 2, // X position byte + 0, // Y position byte + ] + .as_slice(), + ) + .build(); // Now we can spawn our entities - world.spawn(components1); - world.spawn(components2); + world.spawn(entity1); + world.spawn(entity2); } fn main() {