diff --git a/Cargo.toml b/Cargo.toml index 6d4240c..014a2b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,6 @@ ron = "0.9.0" syn = { version = "2.0.67", features = ["extra-traits", "full"] } quote = "1.0.35" proc-macro2 = "1.0.86" +itertools = "0.14.0" utilities = { git = "https://gavania.de/hodasemi/utilities.git" } diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml index 9ea5df7..58c26e8 100644 --- a/ecs/Cargo.toml +++ b/ecs/Cargo.toml @@ -11,5 +11,6 @@ serde.workspace = true paste.workspace = true ron.workspace = true utilities.workspace = true +itertools.workspace = true update_macros = { path = "../update_macros" } diff --git a/ecs/src/updates.rs b/ecs/src/updates.rs index c822822..2be4088 100644 --- a/ecs/src/updates.rs +++ b/ecs/src/updates.rs @@ -5,6 +5,7 @@ use std::{any::TypeId, marker::PhantomData}; use anyhow::Result; use indexmap::IndexMap; +use itertools::Itertools; use update_macros::implement_updates; use crate::resources::Resource as ResourceTrait; @@ -136,7 +137,8 @@ impl ArchetypeInfo { } pub struct Archetype { - check_entity: Box bool + Send + Sync>, + queries: usize, + check_entity: Box Option> + Send + Sync>, create_callback: Box< dyn Fn( &[&EntityObject], @@ -154,16 +156,38 @@ impl Archetype { pub fn add_entities( &mut self, new_entity: &EntityObject, - entity_objects: &[&EntityObject], + entity_objects: &IndexMap, ) -> Result<()> { - if !(self.check_entity)(entity_objects) { - return Ok(()); + let mut complying_entities: Vec> = vec![Vec::new(); self.queries]; + + match (self.check_entity)(new_entity) { + Some(complying_queries) => complying_queries + .into_iter() + .for_each(|q| complying_entities[q].push(new_entity)), + None => return Ok(()), } - let cb = (self.create_callback)(entity_objects)?; + for old_entity in entity_objects.values() { + if let Some(complying_queries) = (self.check_entity)(old_entity) { + complying_queries + .into_iter() + .for_each(|q| complying_entities[q].push(old_entity)); + } + } - self.entities - .insert(entity_objects.iter().map(|e| e.as_entity()).collect(), cb); + // check if one of the queries could not be fullfilled + for queryable_entities in complying_entities { + if queryable_entities.is_empty() { + return Ok(()); + } + } + + for combinations in complying_entities.into_iter().multi_cartesian_product() { + let cb = (self.create_callback)(&combinations)?; + + self.entities + .insert(combinations.iter().map(|e| e.as_entity()).collect(), cb); + } Ok(()) } @@ -227,8 +251,7 @@ impl Updates { entities: &IndexMap, ) -> Result<()> { for (_, archetype) in self.updates.iter_mut() { - todo!(); - // archetype.add_entity(entity_object, entities)?; + archetype.add_entities(entity_object, entities)?; } Ok(()) diff --git a/update_macros/src/update.rs b/update_macros/src/update.rs index d26c60b..b75fd75 100644 --- a/update_macros/src/update.rs +++ b/update_macros/src/update.rs @@ -48,16 +48,20 @@ impl Query { quote! { { - let entity = &entities[#index]; + let mut complies = true; #( if !entity.components.contains::<#components>() { - return false; + complies = false; } )* if !#filter::check(entity) { - return false; + complies = false; + } + + if complies { + comply_queries.push(#index); } } } @@ -265,17 +269,21 @@ impl Update { .map(|resource| format_ident!("res_{resource}")) .collect::>(); - ( - quote! { - #[allow(non_snake_case)] - let ( - #( #resource_idents, )* - ): ( - #( &mut #resource_types, )* - ) = world.resources.get_mut()?; - }, - resource_idents, - ) + if self.resources.is_empty() { + (quote! {}, Vec::new()) + } else { + ( + quote! { + #[allow(non_snake_case)] + let ( + #( #resource_idents, )* + ): ( + #( &mut #resource_types, )* + ) = world.resources.get_mut()?; + }, + resource_idents, + ) + } } pub fn resource_types(&self) -> TokenStream2 { @@ -332,6 +340,8 @@ impl ToTokens for Update { let component_requirements = self.component_requirements(); let reource_requirement = self.reource_requirement(); + let query_count = self.queries.len(); + let verify_dedup = self .queries .iter() @@ -376,10 +386,18 @@ impl ToTokens for Update { #( #verify_dedup )* Self { - check_entity: Box::new(move |entities| { + queries: #query_count, + + check_entity: Box::new(move |entity| { + let mut comply_queries = Vec::new(); + #( #check_entities )* - true + if comply_queries.is_empty() { + None + } else { + Some(comply_queries) + } }), create_callback: Box::new(move |entities| { @@ -473,6 +491,12 @@ pub fn update(max_components: usize) -> TokenStream { // updates.push(Update::new(vec![1].into_iter(), 0)); + // let q = quote! { + // #( #updates )* + // }; + + // panic!("{q}"); + TokenStream::from(quote! { #( #updates )* })