Skip to content

Commit a383a18

Browse files
committed
Use EntityBuilder Instead of RuntimeBundle
Gets rid of RuntimeBundle and adds the `add_dynamic` function to EntityBuilder to serve its purpose.
1 parent 84f3d3b commit a383a18

File tree

4 files changed

+75
-82
lines changed

4 files changed

+75
-82
lines changed

crates/bevy_ecs/hecs/src/bundle.rs

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -42,36 +42,6 @@ pub trait DynamicBundle {
4242
unsafe fn put(self, f: impl FnMut(*mut u8, ComponentId, usize) -> bool);
4343
}
4444

45-
/// A bundle that can be created dynamically at runtime
46-
#[derive(Debug)]
47-
pub struct RuntimeBundle {
48-
/// The information for each component in this bundle
49-
pub components: Vec<TypeInfo>,
50-
/// The raw data for all of the components in the bundle
51-
pub data: Vec<Vec<u8>>,
52-
}
53-
54-
impl DynamicBundle for RuntimeBundle {
55-
fn with_ids<T>(&self, f: impl FnOnce(&[ComponentId]) -> T) -> T {
56-
let ids: Vec<_> = self.components.iter().map(|x| x.id()).collect();
57-
f(ids.as_slice())
58-
}
59-
60-
fn type_info(&self) -> Vec<TypeInfo> {
61-
self.components.clone()
62-
}
63-
64-
unsafe fn put(self, mut f: impl FnMut(*mut u8, ComponentId, usize) -> bool) {
65-
for (mut data, info) in self.data.into_iter().zip(self.components.into_iter()) {
66-
debug_assert_eq!(data.len(), info.layout().size());
67-
68-
if f(data.as_mut_ptr(), info.id(), info.layout().size()) {
69-
std::mem::forget(data);
70-
}
71-
}
72-
}
73-
}
74-
7545
/// A statically typed collection of components
7646
pub trait Bundle: DynamicBundle {
7747
#[doc(hidden)]

crates/bevy_ecs/hecs/src/entity_builder.rs

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,8 @@ use crate::{
2525
};
2626

2727
use bevy_utils::HashSet;
28-
use core::{
29-
any::TypeId,
30-
mem::{self, MaybeUninit},
31-
ptr,
32-
};
28+
use core::{intrinsics::copy_nonoverlapping, mem::MaybeUninit, ptr};
29+
use ptr::slice_from_raw_parts;
3330

3431
use crate::{archetype::TypeInfo, Component, DynamicBundle};
3532

@@ -68,24 +65,40 @@ impl EntityBuilder {
6865

6966
/// Add `component` to the entity
7067
pub fn add<T: Component>(&mut self, component: T) -> &mut Self {
71-
if !self.id_set.insert(TypeId::of::<T>().into()) {
68+
self.add_dynamic(TypeInfo::of::<T>(), unsafe {
69+
&*slice_from_raw_parts(
70+
&component as *const T as *const u8,
71+
std::mem::size_of::<T>(),
72+
)
73+
});
74+
std::mem::forget(component);
75+
self
76+
}
77+
78+
/// Add a dynamic component to the entity
79+
///
80+
/// You must provide the type info and a slice containing the component data
81+
pub fn add_dynamic(&mut self, type_info: TypeInfo, data: &[u8]) -> &mut Self {
82+
debug_assert_eq!(type_info.layout().size(), data.len());
83+
84+
if !self.id_set.insert(type_info.id) {
7285
return self;
7386
}
74-
let end = self.cursor + mem::size_of::<T>();
87+
let end = self.cursor + type_info.layout.size();
7588
if end > self.storage.len() {
7689
self.grow(end);
7790
}
78-
if mem::size_of::<T>() != 0 {
91+
if type_info.layout.size() != 0 {
7992
unsafe {
80-
self.storage
81-
.as_mut_ptr()
82-
.add(self.cursor)
83-
.cast::<T>()
84-
.write_unaligned(component);
93+
copy_nonoverlapping(
94+
data.as_ptr(),
95+
self.storage.as_mut_ptr().add(self.cursor) as *mut u8,
96+
data.len(),
97+
);
8598
}
8699
}
87-
self.info.push((TypeInfo::of::<T>(), self.cursor));
88-
self.cursor += mem::size_of::<T>();
100+
self.info.push((type_info, self.cursor));
101+
self.cursor += type_info.layout().size();
89102
self
90103
}
91104

crates/bevy_ecs/hecs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ mod world;
7777

7878
pub use archetype::{Archetype, TypeState};
7979
pub use borrow::{AtomicBorrow, Ref, RefMut};
80-
pub use bundle::{Bundle, DynamicBundle, MissingComponent, RuntimeBundle};
80+
pub use bundle::{Bundle, DynamicBundle, MissingComponent};
8181
pub use entities::{Entity, EntityReserver, Location, NoSuchEntity};
8282
pub use entity_builder::{BuiltEntity, EntityBuilder};
8383
pub use query::{

examples/ecs/dynamic_components.rs

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::{alloc::Layout, time::Duration};
1111
use bevy::prelude::*;
1212
use bevy_app::ScheduleRunnerPlugin;
1313
use bevy_ecs::{
14-
ComponentId, DynamicComponentInfo, DynamicComponentQuery, DynamicSystemSettings, RuntimeBundle,
14+
ComponentId, DynamicComponentInfo, DynamicComponentQuery, DynamicSystemSettings, EntityBuilder,
1515
TypeInfo,
1616
};
1717

@@ -24,11 +24,12 @@ fn spawn_scene(world: &mut World, _resources: &mut Resources) {
2424
// representing a Position and one representing a Velocity. Each of these will be made up of two
2525
// bytes for simplicity, one representing the x and y position/velocity.
2626

27-
// We create our first component bundle
28-
let components1 = RuntimeBundle {
29-
// we must define our components' type information
30-
components: vec![
31-
// First we define our "Position" component
27+
// We create our first entity
28+
let mut builder = EntityBuilder::new();
29+
// Then we add our "Position component"
30+
let entity1 = builder
31+
.add_dynamic(
32+
// We need to describe our component's information
3233
TypeInfo {
3334
// We must provide a unique id for the compoonent
3435
id: ComponentId::ExternalId(0),
@@ -37,53 +38,62 @@ fn spawn_scene(world: &mut World, _resources: &mut Resources) {
3738
// And we must specify a drop function for our component
3839
drop: |_| (),
3940
},
40-
// Next we define our "Velocity" component
41+
// And provide the raw byte data data for the component
42+
vec![
43+
0, // X position byte
44+
0, // Y position byte
45+
]
46+
// And cast the data to a pointer
47+
.as_slice(),
48+
)
49+
// Next we add our "Velocity component"
50+
.add_dynamic(
4151
TypeInfo {
42-
// We must specify a different ID for the velocity component
52+
// This component needs its own unique ID
4353
id: ComponentId::ExternalId(1),
44-
// We specify the layout which happens to be the same as "Position"
45-
layout: Layout::from_size_align(2, 1).unwrap(),
46-
// And the drop function
54+
layout: Layout::from_size_align(2 /* size */, 1 /* alignment */).unwrap(),
4755
drop: |_| (),
4856
},
49-
],
50-
51-
// Data must be a Vector of Vectors of bytes and must contain the raw byte data for
52-
// each of the components we want to add
53-
data: vec![
54-
// This will be the raw byte data for our position component
5557
vec![
5658
0, // X position byte
57-
0, // Y position byte
58-
],
59-
// This will be the raw byte data for our velocity component
60-
vec![
61-
1, // X velocity byte
62-
0, // Y velocity byte
63-
],
64-
],
65-
};
66-
67-
// Now we create another bundle for our next entity
68-
let components2 = RuntimeBundle {
69-
components: vec![
59+
1, // Y position byte
60+
]
61+
.as_slice(),
62+
)
63+
.build();
64+
65+
// And let's create another entity
66+
let mut builder = EntityBuilder::new();
67+
let entity2 = builder
68+
.add_dynamic(
7069
TypeInfo {
7170
id: ComponentId::ExternalId(0),
72-
layout: Layout::from_size_align(2 /* size */, 1 /* alignment */).unwrap(),
71+
layout: Layout::from_size_align(2, 1).unwrap(),
7372
drop: |_| (),
7473
},
74+
vec![
75+
0, // X position byte
76+
0, // Y position byte
77+
]
78+
.as_slice(),
79+
)
80+
.add_dynamic(
7581
TypeInfo {
7682
id: ComponentId::ExternalId(1),
7783
layout: Layout::from_size_align(2, 1).unwrap(),
7884
drop: |_| (),
7985
},
80-
],
81-
data: vec![vec![0, 0], vec![0, 2]],
82-
};
86+
vec![
87+
2, // X position byte
88+
0, // Y position byte
89+
]
90+
.as_slice(),
91+
)
92+
.build();
8393

8494
// Now we can spawn our entities
85-
world.spawn(components1);
86-
world.spawn(components2);
95+
world.spawn(entity1);
96+
world.spawn(entity2);
8797
}
8898

8999
fn main() {

0 commit comments

Comments
 (0)