Skip to content

Commit 311b639

Browse files
committed
Implement Dynamic Systems and Components
1 parent 0798295 commit 311b639

File tree

21 files changed

+1406
-289
lines changed

21 files changed

+1406
-289
lines changed

Cargo.toml

+15
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ render = ["bevy_pbr", "bevy_render", "bevy_sprite", "bevy_text", "bevy_ui"]
3939
png = ["bevy_render/png"]
4040
hdr = ["bevy_render/hdr"]
4141

42+
# Enable the dynamic systems and components API ( useful for developing scripting solutions )
43+
dynamic-api = ["bevy_ecs/dynamic-api", "bevy_scene/dynamic-api"]
44+
4245
# Audio format support (MP3 is enabled by default)
4346
mp3 = ["bevy_audio/mp3"]
4447
flac = ["bevy_audio/flac"]
@@ -90,6 +93,8 @@ serde = { version = "1", features = ["derive"] }
9093
log = "0.4"
9194
ron = "0.6"
9295
anyhow = "1.0"
96+
lazy_static = "1.4.0"
97+
9398

9499
# bevy (Android)
95100
[target.'cfg(target_os = "android")'.dependencies]
@@ -216,6 +221,16 @@ path = "examples/ecs/parallel_query.rs"
216221
name = "hierarchy"
217222
path = "examples/ecs/hierarchy.rs"
218223

224+
[[example]]
225+
name = "dynamic_systems"
226+
path = "examples/ecs/dynamic_systems.rs"
227+
required-features = ["dynamic-api"]
228+
229+
[[example]]
230+
name = "dynamic_components"
231+
path = "examples/ecs/dynamic_components.rs"
232+
required-features = ["dynamic-api"]
233+
219234
[[example]]
220235
name = "breakout"
221236
path = "examples/game/breakout.rs"

crates/bevy_ecs/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ categories = ["game-engines", "data-structures"]
1515

1616
[features]
1717
profiler = []
18+
dynamic-api = ["bevy_hecs/dynamic-api"]
1819

1920
[dependencies]
2021
bevy_hecs = { path = "hecs", features = ["macros", "serialize"], version = "0.3.0" }

crates/bevy_ecs/hecs/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ std = []
2525
# Enables derive(Bundle)
2626
macros = ["bevy_hecs_macros", "lazy_static"]
2727
serialize = ["serde"]
28+
# Enables the dynamic components and systems APIs
29+
dynamic-api = ["std"]
2830

2931
[dependencies]
3032
bevy_hecs_macros = { path = "macros", version = "0.3.0", optional = true }

crates/bevy_ecs/hecs/macros/src/lib.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,12 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
186186
let query_fn = &query_fns[0..query_count];
187187
let query_fn_mut = &query_fn_muts[0..query_count];
188188
tokens.extend(TokenStream::from(quote! {
189-
impl<#(#lifetime,)* #(#query: HecsQuery,)*> QueryTuple for (#(Query<#lifetime, #query>,)*) {
189+
impl<#(#lifetime,)* #(#query: HecsQuery,)*> QueryTuple for (#(Query<#lifetime, #query>,)*)
190+
where
191+
#(
192+
#query::Fetch: for<'a> Fetch<'a, State = ()>
193+
),*
194+
{
190195
unsafe fn new(world: &World, component_access: &TypeAccess<ArchetypeComponent>) -> Self {
191196
(
192197
#(
@@ -200,12 +205,17 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
200205

201206
fn get_accesses() -> Vec<QueryAccess> {
202207
vec![
203-
#(<#query::Fetch as Fetch>::access(),)*
208+
#(<#query::Fetch as Fetch>::access(&()),)*
204209
]
205210
}
206211
}
207212

208-
impl<#(#lifetime,)* #(#query: HecsQuery,)*> QuerySet<(#(Query<#lifetime, #query>,)*)> {
213+
impl<#(#lifetime,)* #(#query: HecsQuery,)*> QuerySet<(#(Query<#lifetime, #query>,)*)>
214+
where
215+
#(
216+
#query::Fetch: for<'a> Fetch<'a, State = ()>
217+
),*
218+
{
209219
#(#query_fn)*
210220
#(#query_fn_mut)*
211221
}

crates/bevy_ecs/hecs/src/access.rs

+36-33
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use core::{any::TypeId, hash::Hash};
22
use std::{boxed::Box, vec::Vec};
33

4-
use crate::{Archetype, World};
4+
use crate::{Archetype, ComponentId, World};
55
use bevy_utils::HashSet;
66

77
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
@@ -14,52 +14,53 @@ pub enum Access {
1414
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
1515
pub struct ArchetypeComponent {
1616
pub archetype_index: u32,
17-
pub component: TypeId,
17+
pub component: ComponentId,
1818
}
1919

2020
impl ArchetypeComponent {
2121
#[inline]
2222
pub fn new<T: 'static>(archetype_index: u32) -> Self {
2323
ArchetypeComponent {
2424
archetype_index,
25-
component: TypeId::of::<T>(),
25+
component: TypeId::of::<T>().into(),
2626
}
2727
}
2828

2929
#[inline]
30-
pub fn new_ty(archetype_index: u32, component: TypeId) -> Self {
30+
pub fn new_component(archetype_index: u32, component: ComponentId) -> Self {
3131
ArchetypeComponent {
3232
archetype_index,
3333
component,
3434
}
3535
}
3636
}
3737

38+
#[derive(Clone, Debug)]
3839
pub enum QueryAccess {
3940
None,
40-
Read(TypeId, &'static str),
41-
Write(TypeId, &'static str),
41+
Read(ComponentId, &'static str),
42+
Write(ComponentId, &'static str),
4243
Optional(Box<QueryAccess>),
43-
With(TypeId, Box<QueryAccess>),
44-
Without(TypeId, Box<QueryAccess>),
44+
With(ComponentId, Box<QueryAccess>),
45+
Without(ComponentId, Box<QueryAccess>),
4546
Union(Vec<QueryAccess>),
4647
}
4748

4849
impl QueryAccess {
4950
pub fn read<T: 'static>() -> QueryAccess {
50-
QueryAccess::Read(TypeId::of::<T>(), std::any::type_name::<T>())
51+
QueryAccess::Read(TypeId::of::<T>().into(), std::any::type_name::<T>())
5152
}
5253

5354
pub fn write<T: 'static>() -> QueryAccess {
54-
QueryAccess::Write(TypeId::of::<T>(), std::any::type_name::<T>())
55+
QueryAccess::Write(TypeId::of::<T>().into(), std::any::type_name::<T>())
5556
}
5657

5758
pub fn with<T: 'static>(access: QueryAccess) -> QueryAccess {
58-
QueryAccess::With(TypeId::of::<T>(), Box::new(access))
59+
QueryAccess::With(TypeId::of::<T>().into(), Box::new(access))
5960
}
6061

6162
pub fn without<T: 'static>(access: QueryAccess) -> QueryAccess {
62-
QueryAccess::Without(TypeId::of::<T>(), Box::new(access))
63+
QueryAccess::Without(TypeId::of::<T>().into(), Box::new(access))
6364
}
6465

6566
pub fn optional(access: QueryAccess) -> QueryAccess {
@@ -82,29 +83,29 @@ impl QueryAccess {
8283
}
8384
}
8485

85-
pub fn get_type_name(&self, type_id: TypeId) -> Option<&'static str> {
86+
pub fn get_type_name(&self, component_id: ComponentId) -> Option<&'static str> {
8687
match self {
8788
QueryAccess::None => None,
88-
QueryAccess::Read(current_type_id, name) => {
89-
if type_id == *current_type_id {
89+
QueryAccess::Read(current_component_id, name) => {
90+
if component_id == *current_component_id {
9091
Some(*name)
9192
} else {
9293
None
9394
}
9495
}
95-
QueryAccess::Write(current_type_id, name) => {
96-
if type_id == *current_type_id {
96+
QueryAccess::Write(current_component_id, name) => {
97+
if component_id == *current_component_id {
9798
Some(*name)
9899
} else {
99100
None
100101
}
101102
}
102-
QueryAccess::Optional(query_access) => query_access.get_type_name(type_id),
103-
QueryAccess::With(_, query_access) => query_access.get_type_name(type_id),
104-
QueryAccess::Without(_, query_access) => query_access.get_type_name(type_id),
103+
QueryAccess::Optional(query_access) => query_access.get_type_name(component_id),
104+
QueryAccess::With(_, query_access) => query_access.get_type_name(component_id),
105+
QueryAccess::Without(_, query_access) => query_access.get_type_name(component_id),
105106
QueryAccess::Union(query_accesses) => {
106107
for query_access in query_accesses.iter() {
107-
if let Some(name) = query_access.get_type_name(type_id) {
108+
if let Some(name) = query_access.get_type_name(component_id) {
108109
return Some(name);
109110
}
110111
}
@@ -125,19 +126,21 @@ impl QueryAccess {
125126
match self {
126127
QueryAccess::None => Some(Access::None),
127128
QueryAccess::Read(ty, _) => {
128-
if archetype.has_type(*ty) {
129+
if archetype.has_component(*ty) {
129130
if let Some(type_access) = type_access {
130-
type_access.add_read(ArchetypeComponent::new_ty(archetype_index, *ty));
131+
type_access
132+
.add_read(ArchetypeComponent::new_component(archetype_index, *ty));
131133
}
132134
Some(Access::Read)
133135
} else {
134136
None
135137
}
136138
}
137139
QueryAccess::Write(ty, _) => {
138-
if archetype.has_type(*ty) {
140+
if archetype.has_component(*ty) {
139141
if let Some(type_access) = type_access {
140-
type_access.add_write(ArchetypeComponent::new_ty(archetype_index, *ty));
142+
type_access
143+
.add_write(ArchetypeComponent::new_component(archetype_index, *ty));
141144
}
142145
Some(Access::Write)
143146
} else {
@@ -157,14 +160,14 @@ impl QueryAccess {
157160
}
158161
}
159162
QueryAccess::With(ty, query_access) => {
160-
if archetype.has_type(*ty) {
163+
if archetype.has_component(*ty) {
161164
query_access.get_access(archetype, archetype_index, type_access)
162165
} else {
163166
None
164167
}
165168
}
166169
QueryAccess::Without(ty, query_access) => {
167-
if !archetype.has_type(*ty) {
170+
if !archetype.has_component(*ty) {
168171
query_access.get_access(archetype, archetype_index, type_access)
169172
} else {
170173
None
@@ -308,7 +311,7 @@ mod tests {
308311
let e3_c = ArchetypeComponent::new::<C>(e3_archetype);
309312

310313
let mut a_type_access = TypeAccess::default();
311-
<(&A,) as Query>::Fetch::access()
314+
<(&A,) as Query>::Fetch::access(&())
312315
.get_world_archetype_access(&world, Some(&mut a_type_access));
313316

314317
assert_eq!(
@@ -317,7 +320,7 @@ mod tests {
317320
);
318321

319322
let mut a_b_type_access = TypeAccess::default();
320-
<(&A, &B) as Query>::Fetch::access()
323+
<(&A, &B) as Query>::Fetch::access(&())
321324
.get_world_archetype_access(&world, Some(&mut a_b_type_access));
322325

323326
assert_eq!(
@@ -326,7 +329,7 @@ mod tests {
326329
);
327330

328331
let mut a_bmut_type_access = TypeAccess::default();
329-
<(&A, &mut B) as Query>::Fetch::access()
332+
<(&A, &mut B) as Query>::Fetch::access(&())
330333
.get_world_archetype_access(&world, Some(&mut a_bmut_type_access));
331334

332335
assert_eq!(
@@ -335,7 +338,7 @@ mod tests {
335338
);
336339

337340
let mut a_option_bmut_type_access = TypeAccess::default();
338-
<(Entity, &A, Option<&mut B>) as Query>::Fetch::access()
341+
<(Entity, &A, Option<&mut B>) as Query>::Fetch::access(&())
339342
.get_world_archetype_access(&world, Some(&mut a_option_bmut_type_access));
340343

341344
assert_eq!(
@@ -344,7 +347,7 @@ mod tests {
344347
);
345348

346349
let mut a_with_b_type_access = TypeAccess::default();
347-
<With<B, &A> as Query>::Fetch::access()
350+
<With<B, &A> as Query>::Fetch::access(&())
348351
.get_world_archetype_access(&world, Some(&mut a_with_b_type_access));
349352

350353
assert_eq!(
@@ -353,7 +356,7 @@ mod tests {
353356
);
354357

355358
let mut a_with_b_option_c_type_access = TypeAccess::default();
356-
<With<B, (&A, Option<&mut C>)> as Query>::Fetch::access()
359+
<With<B, (&A, Option<&mut C>)> as Query>::Fetch::access(&())
357360
.get_world_archetype_access(&world, Some(&mut a_with_b_option_c_type_access));
358361

359362
assert_eq!(

crates/bevy_ecs/hecs/src/archetype.rs

+30-14
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use bevy_utils::AHasher;
2626
use core::{
2727
any::TypeId,
2828
cell::UnsafeCell,
29-
hash::{BuildHasherDefault, Hasher},
29+
hash::{BuildHasherDefault, Hash, Hasher},
3030
mem,
3131
ptr::{self, NonNull},
3232
};
@@ -243,7 +243,8 @@ impl Archetype {
243243
size: usize,
244244
index: usize,
245245
) -> Option<NonNull<u8>> {
246-
debug_assert!(index < self.len);
246+
// TODO(zicklag): I'm pretty sure that it is valid for the index to be zero
247+
debug_assert!(index < self.len || index == 0);
247248
Some(NonNull::new_unchecked(
248249
(*self.data.get())
249250
.as_ptr()
@@ -500,11 +501,14 @@ impl TypeState {
500501
}
501502

502503
/// Metadata required to store a component
503-
#[derive(Debug, Copy, Clone)]
504+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
504505
pub struct TypeInfo {
505-
id: ComponentId,
506-
layout: Layout,
507-
drop: unsafe fn(*mut u8),
506+
/// The ID unique to the component type
507+
pub(crate) id: ComponentId,
508+
/// The memory layout of the component
509+
pub(crate) layout: Layout,
510+
/// The drop function for the component
511+
pub(crate) drop: unsafe fn(*mut u8),
508512
}
509513

510514
impl TypeInfo {
@@ -521,6 +525,16 @@ impl TypeInfo {
521525
}
522526
}
523527

528+
/// Get the [`TypeInfo`] for an external type with the given layout and drop function
529+
#[cfg(feature = "dynamic-api")]
530+
pub fn of_external(external_id: u64, layout: Layout, drop: unsafe fn(*mut u8)) -> Self {
531+
TypeInfo {
532+
id: ComponentId::ExternalId(external_id),
533+
layout,
534+
drop,
535+
}
536+
}
537+
524538
#[allow(missing_docs)]
525539
#[inline]
526540
pub fn id(&self) -> ComponentId {
@@ -538,6 +552,16 @@ impl TypeInfo {
538552
}
539553
}
540554

555+
#[allow(clippy::derive_hash_xor_eq)]
556+
impl Hash for TypeInfo {
557+
fn hash<H: Hasher>(&self, state: &mut H) {
558+
self.id.hash(state);
559+
state.write_usize(self.layout.size());
560+
state.write_usize(self.layout.align());
561+
self.drop.hash(state);
562+
}
563+
}
564+
541565
impl PartialOrd for TypeInfo {
542566
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
543567
Some(self.cmp(other))
@@ -555,14 +579,6 @@ impl Ord for TypeInfo {
555579
}
556580
}
557581

558-
impl PartialEq for TypeInfo {
559-
fn eq(&self, other: &Self) -> bool {
560-
self.id == other.id
561-
}
562-
}
563-
564-
impl Eq for TypeInfo {}
565-
566582
fn align(x: usize, alignment: usize) -> usize {
567583
debug_assert!(alignment.is_power_of_two());
568584
(x + alignment - 1) & (!alignment + 1)

0 commit comments

Comments
 (0)