From 60c139f9b2563b4c2d809031ecac336badc63965 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 2 Sep 2024 19:29:37 -0400 Subject: [PATCH] ecs: create DynamicViewOne --- lyra-ecs/src/entity.rs | 7 ++ lyra-ecs/src/query/dynamic/mod.rs | 3 + lyra-ecs/src/query/dynamic/view.rs | 26 +++--- lyra-ecs/src/query/dynamic/view_one.rs | 123 +++++++++++++++++++++++++ lyra-scripting/elua | 2 +- 5 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 lyra-ecs/src/query/dynamic/view_one.rs diff --git a/lyra-ecs/src/entity.rs b/lyra-ecs/src/entity.rs index 370af54..62d10bb 100644 --- a/lyra-ecs/src/entity.rs +++ b/lyra-ecs/src/entity.rs @@ -12,6 +12,13 @@ pub struct Entity { } impl Entity { + pub fn new(id: EntityId, gen: u64) -> Self { + Self { + id, + generation: gen, + } + } + pub fn id(&self) -> EntityId { self.id } diff --git a/lyra-ecs/src/query/dynamic/mod.rs b/lyra-ecs/src/query/dynamic/mod.rs index fb5522d..b310f65 100644 --- a/lyra-ecs/src/query/dynamic/mod.rs +++ b/lyra-ecs/src/query/dynamic/mod.rs @@ -5,6 +5,9 @@ use crate::{World, ComponentColumn, ComponentInfo}; mod view; pub use view::*; +mod view_one; +pub use view_one::*; + use super::Fetch; /// Data that rust does not know the type of diff --git a/lyra-ecs/src/query/dynamic/view.rs b/lyra-ecs/src/query/dynamic/view.rs index 7d50f18..94221c1 100644 --- a/lyra-ecs/src/query/dynamic/view.rs +++ b/lyra-ecs/src/query/dynamic/view.rs @@ -57,11 +57,17 @@ pub struct DynamicViewStateIter { } impl DynamicViewStateIter { - pub fn next(&mut self, world: &World) -> Option { + pub fn next(&mut self, world: &World) -> Option<(Entity, Vec)> { let archetypes = world.archetypes.values().collect::>(); loop { if let Some(entity_index) = self.component_indices.next() { + let entity = { + let arch_id = self.next_archetype - 1; + let arch = unsafe { archetypes.get_unchecked(arch_id) }; + arch.entity_at_index(ArchetypeEntityId(entity_index)).unwrap() + }; + let mut fetch_res = vec![]; for fetcher in self.fetchers.iter_mut() { @@ -78,11 +84,7 @@ impl DynamicViewStateIter { continue; } - let arch = archetypes.get(self.next_archetype-1).unwrap(); - return Some(DynamicViewItem { - row: fetch_res, - entity: arch.entity_at_index(ArchetypeEntityId(entity_index)).unwrap() - }) + return Some((entity, fetch_res)); } else { if self.next_archetype >= archetypes.len() { return None; // ran out of archetypes to go through @@ -138,7 +140,7 @@ impl<'a> DynamicView<'a> { /// This works great for a embedding with a scripting language (*cough* *cough* WASM) since /// Rust doesn't actually need to know the types of what its iterating over. impl<'a> IntoIterator for DynamicView<'a> { - type Item = DynamicViewItem; + type Item = (Entity, Vec); type IntoIter = DynamicViewIter<'a>; @@ -160,7 +162,7 @@ pub struct DynamicViewIter<'a> { } impl<'a> Iterator for DynamicViewIter<'a> { - type Item = DynamicViewItem; + type Item = (Entity, Vec); fn next(&mut self) -> Option { self.inner.next(&self.world) @@ -193,8 +195,8 @@ mod tests { view.push(query); let mut view_iter = view.into_iter(); - while let Some(view_row) = view_iter.next(&world) { - assert_eq!(view_row.row.len(), 1); + while let Some((_e, view_row)) = view_iter.next(&world) { + assert_eq!(view_row.len(), 1); let mut row_iter = view_row.row.iter(); @@ -222,8 +224,8 @@ mod tests { let mut view = DynamicView::new(&world); view.push(query); - for view_row in view.into_iter() { - assert_eq!(view_row.row.len(), 1); + for (_e, view_row) in view.into_iter() { + assert_eq!(view_row.len(), 1); let mut row_iter = view_row.row.iter(); diff --git a/lyra-ecs/src/query/dynamic/view_one.rs b/lyra-ecs/src/query/dynamic/view_one.rs new file mode 100644 index 0000000..3bdee13 --- /dev/null +++ b/lyra-ecs/src/query/dynamic/view_one.rs @@ -0,0 +1,123 @@ +use crate::{query::Fetch, Entity, World}; + +use super::{DynamicType, FetchDynamicTypeUnsafe, QueryDynamicType}; + +/// A view of dynamic types (types that are not known to Rust). +/// +/// This view gives you the ability to iterate over types that are unknown to Rust, which we call +/// dynamic types. This is great for embedding with a scripting language (*cough* *cough* WASM) +/// since Rust doesn't actually need to know the types of what its iterating over. +pub struct DynamicViewOne<'a> { + world: &'a World, + pub entity: Entity, + pub queries: Vec +} + +impl<'a> DynamicViewOne<'a> { + pub fn new(world: &'a World, entity: Entity) -> Self { + Self { + world, + entity, + queries: vec![], + } + } + + /// Create a new [`DynamicViewOne`] with queries. + pub fn new_with(world: &'a World, entity: Entity, queries: Vec) -> Self { + Self { + world, + entity, + queries + } + } + + pub fn get(self) -> Option> { + let arch = self.world.entity_archetype(self.entity)?; + let aid = arch.entity_indexes().get(&self.entity)?; + + // get all fetchers for the queries + let mut fetchers: Vec = self.queries.iter() + .map(|q| unsafe { q.fetch(self.world, arch.id(), arch) } ) + .collect(); + + let mut fetch_res = vec![]; + for fetcher in fetchers.iter_mut() { + if !fetcher.can_visit_item(*aid) { + return None; + } else { + let i = unsafe { fetcher.get_item(*aid) }; + fetch_res.push(i); + } + } + + if fetch_res.is_empty() { + None + } else { + Some(fetch_res) + } + } +} + +#[cfg(test)] +mod tests { + use std::{alloc::Layout, ptr::NonNull}; + + use crate::{World, ComponentInfo, DynTypeId, DynamicBundle, query::dynamic::QueryDynamicType}; + + use super::DynamicViewOne; + + #[test] + fn single_dynamic_view_one_state() { + let comp_layout = Layout::new::(); + let comp_info = ComponentInfo::new_unknown(Some("u32".to_string()), DynTypeId::Unknown(100), comp_layout); + + let mut dynamic_bundle = DynamicBundle::default(); + let comp = 50u32; + let ptr = NonNull::from(&comp).cast::(); + dynamic_bundle.push_unknown(ptr, comp_info.clone()); + + let mut world = World::new(); + let e = world.spawn(dynamic_bundle); + + let query = QueryDynamicType::from_info(comp_info); + let view = DynamicViewOne::new_with(&world, e, vec![query]); + + let view_row = view.get() + .expect("failed to get entity row"); + assert_eq!(view_row.len(), 1); + + let mut row_iter = view_row.iter(); + let dynamic_type = row_iter.next().unwrap(); + + let component_data = unsafe { dynamic_type.ptr.cast::().as_ref() }; + assert_eq!(*component_data, 50); + } + + #[test] + fn single_dynamic_view_one() { + let comp_layout = Layout::new::(); + let comp_info = ComponentInfo::new_unknown(Some("u32".to_string()), DynTypeId::Unknown(100), comp_layout); + + let mut dynamic_bundle = DynamicBundle::default(); + let comp = 50u32; + let ptr = NonNull::from(&comp).cast::(); + dynamic_bundle.push_unknown(ptr, comp_info.clone()); + + let mut world = World::new(); + let e = world.spawn(dynamic_bundle); + + let query = QueryDynamicType::from_info(comp_info); + let view = DynamicViewOne::new_with(&world, e, vec![query]); + + let view_row = view.get() + .expect("failed to get entity row"); + assert_eq!(view_row.len(), 1); + + let mut row_iter = view_row.iter(); + + let dynamic_type = row_iter.next().unwrap(); + + let component_data = unsafe { dynamic_type.ptr.cast::().as_ref() }; + assert_eq!(*component_data, 50); + } +} \ No newline at end of file diff --git a/lyra-scripting/elua b/lyra-scripting/elua index a761f40..54c9926 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit a761f4094bc18190285b4687ec804161fea874b6 +Subproject commit 54c9926a04cdef657289fd67730c0b85d1bdda3e