diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml index 2901052..9ea5df7 100644 --- a/ecs/Cargo.toml +++ b/ecs/Cargo.toml @@ -13,6 +13,3 @@ ron.workspace = true utilities.workspace = true update_macros = { path = "../update_macros" } - -[features] -timings = [] diff --git a/ecs/src/updates.rs b/ecs/src/updates.rs index fd9ae4b..cf827b6 100644 --- a/ecs/src/updates.rs +++ b/ecs/src/updates.rs @@ -4,15 +4,10 @@ use std::ops::{Deref, DerefMut}; use std::{any::TypeId, marker::PhantomData}; use std::collections::HashMap; -#[cfg(feature = "timings")] -use std::time::Instant; use anyhow::Result; use indexmap::IndexMap; -#[cfg(feature = "timings")] -use super::super::timings::Timings; - use crate::resources::Resource as ResourceTrait; use crate::*; use update_macros::{implement_pair_update, implement_single_update}; @@ -1152,18 +1147,12 @@ impl From for Update { } pub struct Updates { - #[cfg(feature = "timings")] - timings: Timings, - - updates: Vec<(String, u32, Update)>, + updates: Vec<(u32, Update)>, } impl Default for Updates { fn default() -> Self { Self { - #[cfg(feature = "timings")] - timings: Timings::default, - updates: Vec::new(), } } @@ -1171,22 +1160,9 @@ impl Default for Updates { impl Updates { pub(crate) fn update(&mut self, world: &mut World) -> Result<()> { - #[cfg(feature = "timings")] - if let Some(timings) = self.timings.check_timing(world.now(), None) { - if !timings.is_empty() { - println!("timings: {:#?}", timings); - } - } - - #[cfg(feature = "timings")] - let timings = &mut self.timings; - self.updates .iter() - .try_for_each(|(_name, _, update)| -> Result<()> { - #[cfg(feature = "timings")] - let before = Instant::now(); - + .try_for_each(|(_, update)| -> Result<()> { match update { Update::Single(archetype) => { archetype.execute(world)?; @@ -1196,9 +1172,6 @@ impl Updates { } } - #[cfg(feature = "timings")] - timings.add(_name, Instant::now().duration_since(before)); - Ok(()) })?; @@ -1210,7 +1183,7 @@ impl Updates { entity_object: &EntityObject, entities: &IndexMap, ) -> Result<()> { - for (_, _, update) in self.updates.iter_mut() { + for (_, update) in self.updates.iter_mut() { match update { Update::Single(archetype) => { archetype.add_entity(entity_object)?; @@ -1225,7 +1198,7 @@ impl Updates { } pub(crate) fn remove_entity(&mut self, entity: Entity) { - for (_, _, update) in self.updates.iter_mut() { + for (_, update) in self.updates.iter_mut() { match update { Update::Single(archetype) => { archetype.remove_entity(entity); @@ -1244,13 +1217,10 @@ impl Updates { // self.timings.clear(); // } - pub(crate) fn add(&mut self, name: &str, priority: u32, update: Update) -> Result<()> { - #[cfg(feature = "timings")] - self.timings.add_timing_afterwards(name); - - self.updates.push((name.to_string(), priority, update)); + pub(crate) fn add(&mut self, priority: u32, update: Update) -> Result<()> { + self.updates.push((priority, update)); self.updates - .sort_by(|(_, lhs_prio, _), (_, rhs_prio, _)| lhs_prio.cmp(rhs_prio)); + .sort_by(|(lhs_prio, _), (rhs_prio, _)| lhs_prio.cmp(rhs_prio)); Ok(()) } diff --git a/update_macros/src/update.rs b/update_macros/src/update.rs index 23cd829..950dce5 100644 --- a/update_macros/src/update.rs +++ b/update_macros/src/update.rs @@ -16,46 +16,113 @@ impl Query { } } - pub fn types(&self) -> proc_macro2::TokenStream { + pub fn types(&self) -> TokenStream2 { let component_list = self .components .iter() - .map(|c| quote! { #c, }) + .map(|c| quote! { #c }) .collect::>(); let components = if component_list.len() == 1 { let component = &component_list[0]; quote! { #component } } else { - quote! { ( #(#component_list)* ) } + quote! { ( #( #component_list, )* ) } }; components } + pub fn dedup_checks(&self) -> TokenStream2 { + let components = &self.components; + let filter = &self.filter; + + quote! { + #( #filter::verify_dedup::<#components>(); )* + } + } + + pub fn check_entity(&self, index: usize) -> TokenStream2 { + let components = &self.components; + let filter = &self.filter; + + quote! { + ( + let entity = &entites[#index]; + + #( + if !entity.components.contains::<#components>() { + return false; + } + )* + + if !#filter::check(entity) { + return false; + } + ) + } + } + + pub fn component_store_and_query(&self, index: usize) -> (TokenStream2, TokenStream2, Ident) { + let components = &self.components; + let entity_name = format_ident!("entity_{index}"); + let component_name = self + .components + .iter() + .map(|component| format_ident!("q_{index}_{component}")) + .collect::>(); + let query_ident = format_ident!("query_{index}"); + let entity_ident = format_ident!("entity_{index}"); + + ( + quote! { + let #entity_name = &entities[#index]; + let #entity_ident = entity_name.as_entity(); + + #( + let #component_name = UnsafeComponentStore::from( + entity.get_component::<#components>()? + ); + )* + }, + quote! { + let #query_ident = Query { + entity: #entity_ident, + c: unsafe { ( #( #component_name.as_mut() )* ) }, + filter: PhantomData, + }; + }, + query_ident, + ) + } + + pub fn components(&self) -> &Vec { + &self.components + } + pub fn filter(&self) -> &Ident { &self.filter } } impl ToTokens for Query { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + fn to_tokens(&self, tokens: &mut TokenStream2) { let component_list = self .components .iter() - .map(|c| quote! { &mut #c, }) + .map(|c| quote! { &mut #c }) .collect::>(); let components = if component_list.len() == 1 { let component = &component_list[0]; quote! { #component } } else { - quote! { ( #(#component_list)* ) } + quote! { ( #( #component_list, )* ) } }; let filter = self.filter.clone(); - proc_macro2::TokenStream::from(quote! { + TokenStream2::from(quote! { Query<#components, #filter> }) .to_tokens(tokens); @@ -68,6 +135,10 @@ struct Update { } impl Update { + const MIN_RESOURCE: usize = 0; + const MIN_COMPONENTS: usize = 1; + const MIN_QUERIES: usize = 1; + pub fn new(queries: impl Iterator, resources: usize) -> Self { Self { queries: queries @@ -85,14 +156,208 @@ impl Update { } } - pub fn query_types(&self) + pub fn filter_types(&self) -> TokenStream2 { + let filter_types = self + .queries + .iter() + .map(|query| query.filter()) + .collect::>(); + + if filter_types.len() == 1 { + let filter = &filter_types[0]; + + quote! { + #filter + } + } else { + quote! { + ( #( #filter_types, )* ) + } + } + } + + pub fn filter_requirements(&self) -> TokenStream2 { + let filter_requirements = self + .queries + .iter() + .map(|query| query.filter()) + .collect::>(); + + quote! { + #( #filter_requirements: CheckFilter + 'static, )* + } + } + + pub fn query_types(&self) -> TokenStream2 { + let query_types = self + .queries + .iter() + .map(|query| query.types()) + .collect::>(); + + quote! { + ( #( #query_types, )* ) + } + } + + pub fn component_requirements(&self) -> TokenStream2 { + let component_requirements = self + .queries + .iter() + .map(|query| { + query.components.iter().map(|component| { + quote! { + #component: EntityComponent + ComponentDebug, + } + }) + }) + .flatten() + .collect::>(); + + quote! { + #( #component_requirements, )* + } + } + + pub fn resource_types(&self) -> TokenStream2 { + let resource_types = &self.resources; + + quote! { + ( #( #resource_types, )* ) + } + } + + pub fn reource_requirement(&self) -> TokenStream2 { + let resource_types = &self.resources; + + quote! { + #( #resource_types: ResourceTrait, )* + } + } +} + +impl ToTokens for Update { + fn to_tokens(&self, tokens: &mut TokenStream2) { + // generics to specify trait implementations + let filter_types = self.filter_types(); + let query_types = self.query_types(); + let resource_types = self.resource_types(); + + // function parameter + let queries = &self.queries; + let resources = if self.resources.is_empty() { + quote! {} + } else { + let resources = &self.resources; + + quote! { + #( &mut #resources, )* + } + }; + + // trait requirements + let filter_requirements = self.filter_requirements(); + let component_requirements = self.component_requirements(); + let reource_requirement = self.reource_requirement(); + + let verify_dedup = self + .queries + .iter() + .map(|query| query.dedup_checks()) + .collect::>(); + + let check_entities = self + .queries + .iter() + .enumerate() + .map(|(index, query)| query.check_entity(index)) + .collect::>(); + + let component_stores = self + .queries + .iter() + .enumerate() + .map(|(index, query)| query.component_store_and_query(index)) + .collect::>(); + + TokenStream2::from(quote! { + impl CreateArchetype<#query_types, #resource_types, Func, #filter_types> for Archetype + where + Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static, + #filter_requirements + #component_requirements + #reource_requirement + { + fn create(f: Func) -> Self { + #( #verify_dedup )* + + Self { + check_entity: Box::new(move |entities| { + #( #check_entities )* + + true + }), + + create_callback: Box::new(move |entities| { + let e = entity.as_entity(); + let f = f.clone(); + + Ok(Box::new(move |commands, world| { + + + f(commands ) + })) + }), + + entities: IndexMap::new(), + } + } + } + + impl AddUpdates<#query_types, #resource_types, Func, #filter_types> for Updates + where + Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static, + #filter_requirements + #component_requirements + #reource_requirement + { + fn add_update( + &mut self, + priority: u32, + func: Func, + ) -> Result<()> { + self.add(priority, Archetype::create(func)) + } + } + + impl AddUpdates<#query_types, #resource_types, Func, #filter_types> for WorldBuilder + where + Func: Fn(&mut Commands, #( #queries, )* #resources ) -> Result<()> + Send + Sync + Clone + 'static, + #filter_requirements + #component_requirements + #reource_requirement + { + fn add_update( + &mut self, + priority: u32, + func: Func, + ) -> Result<()> { + self.updates.add_update(priority, func) + } + } + }) + .to_tokens(tokens); + } } pub fn update(max_components: usize, max_queries: usize) -> TokenStream { + let updates = (Update::MIN_QUERIES..max_queries) + .map(|query_count| { + // + }) + .collect::>(); + TokenStream::from(quote! { - impl - - - + #( #updates ) }) }