diff --git a/ecs/src/commands.rs b/ecs/src/commands.rs new file mode 100644 index 0000000..854c63a --- /dev/null +++ b/ecs/src/commands.rs @@ -0,0 +1,68 @@ +use anyhow::Result; +use std::any::{Any, TypeId}; + +use crate::{world::ComponentChange, *}; + +enum CommandsTypes { + InsertEntity(EntityObject), + RemoveEntity(Entity), + UpdateEntity(Entity, ComponentChange), + + Event(TypeId, Box), +} + +#[derive(Default)] +pub struct Commands { + commands: Vec, +} + +impl Commands { + pub fn insert_entity(&mut self, entity_object: EntityObject) -> Entity { + let entity = entity_object.as_entity(); + + self.commands + .push(CommandsTypes::InsertEntity(entity_object)); + + entity + } + + pub fn remove_entity(&mut self, entity: Entity) { + self.commands.push(CommandsTypes::RemoveEntity(entity)); + } + + pub fn insert_component(&mut self, entity: Entity, component: T) { + self.commands.push(CommandsTypes::UpdateEntity( + entity, + ComponentChange::Added(TypeId::of::(), Box::new(component)), + )); + } + + pub fn remove_component(&mut self, entity: Entity) { + self.commands.push(CommandsTypes::UpdateEntity( + entity, + ComponentChange::Removed(TypeId::of::()), + )); + } + + pub fn write_event(&mut self, payload: T) { + self.commands + .push(CommandsTypes::Event(TypeId::of::(), Box::new(payload))); + } + + pub(crate) fn apply_deferred(self, world: &mut World) -> Result<()> { + for command in self.commands { + match command { + CommandsTypes::InsertEntity(entity_object) => { + world.add_entity(entity_object)?; + } + CommandsTypes::RemoveEntity(entity) => world.remove_entity(entity), + CommandsTypes::UpdateEntity(entity, component_change) => { + world.component_change(entity, component_change) + } + CommandsTypes::Event(type_id, any) => world.events.write_payload(type_id, any), + } + } + + Ok(()) + } +} diff --git a/ecs/src/events.rs b/ecs/src/events.rs index 71abc73..4580c2e 100644 --- a/ecs/src/events.rs +++ b/ecs/src/events.rs @@ -72,6 +72,13 @@ impl Events { } } + pub(crate) fn write_payload(&mut self, type_id: TypeId, payload: Box) { + match self.events.get_mut(&type_id) { + Some((payloads, _)) => payloads.push(payload), + None => panic!("register event type first!"), + } + } + pub(crate) fn fire_events(&mut self, world: &mut World) -> anyhow::Result<()> { for (payloads, listeners) in self.events.values_mut() { for payload in payloads.iter_mut() { diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index f916a02..b2ec2b1 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -1,3 +1,4 @@ +mod commands; mod entity; mod entity_object_manager; mod events; @@ -17,5 +18,6 @@ pub use crate::type_map::{ pub use crate::unsafe_component_store::UnsafeComponentStore; pub use crate::updates::*; pub use crate::world::{World, WorldBuilder}; +pub use commands::Commands; pub use get_disjoint_mut::GetDisjointMut; pub use update_macros::Resource; diff --git a/ecs/src/updates.rs b/ecs/src/updates.rs index f3d52f3..3a8ba38 100644 --- a/ecs/src/updates.rs +++ b/ecs/src/updates.rs @@ -14,20 +14,20 @@ use indexmap::IndexMap; use super::super::timings::Timings; use crate::*; -use update_macros::implement_pair_update; +use update_macros::{implement_pair_update, implement_single_update}; macro_rules! impl_singleton_update { - ( $name: ident, $([$var: ident]$(,)?)+ ) => { - impl Archetype { + ( $($var:ident $(,)?)+ ) => { + impl CreateArchetype<( $( $var, )+ ), Func, Filter> for Archetype + where + Func: Fn(&mut Commands, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static, + Filter: CheckFilter + 'static, + $( + $var: EntityComponent + ComponentDebug, + )+ + { paste::item! { - pub fn [](f: F, filter: Filter) -> Self - where - F: Fn(&mut World, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static, - Filter: CheckFilter + 'static, - $( - $var: EntityComponent + ComponentDebug, - )+ - { + fn create(f: Func, filter: Filter) -> Self { $( filter.verify_dedup::<$var>(); )+ @@ -58,8 +58,8 @@ macro_rules! impl_singleton_update { let f = f.clone(); - Ok(Box::new(move |e, scene_contents| { - unsafe { f(scene_contents, e, $([< $var:lower >].as_mut(),)+) } + Ok(Box::new(move |e, commands| { + unsafe { f(commands, e, $([< $var:lower >].as_mut(),)+) } })) }), @@ -69,12 +69,12 @@ macro_rules! impl_singleton_update { } } - impl AddUpdates2<( $( $var, )+ ), Func, Filter> for Updates + impl AddUpdates<( $( $var, )+ ), Func, Filter> for Updates where $( $var: EntityComponent + ComponentDebug, )+ - Func: Fn(& mut World, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static, + Func: Fn(& mut Commands, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static, Filter: CheckFilter + 'static { fn add_update( @@ -85,7 +85,7 @@ macro_rules! impl_singleton_update { filter: Filter ) -> Result<()> { paste::item! { - self.add(name, priority, Update::Single(Archetype::[](func, filter))) + self.add(name, priority, Update::Single(Archetype::create(func, filter))) } } } @@ -95,7 +95,7 @@ macro_rules! impl_singleton_update { $( $var: EntityComponent + ComponentDebug, )+ - Func: Fn(& mut World, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static, + Func: Fn(& mut Commands, Entity, $(&mut $var,)+) -> Result<()> + Send + Sync + Clone + 'static, Filter: CheckFilter + 'static { fn add_update( @@ -123,7 +123,7 @@ macro_rules! impl_pair_update { pub fn [] (f: F, left_filter: LeftFilter, right_filter: RightFilter) -> Self where - F: Fn(&mut World, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static, + F: Fn(&mut Commands, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static, LeftFilter: CheckFilter + 'static, RightFilter: CheckFilter + 'static, $( @@ -188,8 +188,8 @@ macro_rules! impl_pair_update { let f = f.clone(); - Ok(Box::new(move |lhs_e, rhs_e, scene_contents| { - unsafe { f(scene_contents, (lhs_e, $($lhs_little.as_mut(),)+), (rhs_e, $($rhs_little.as_mut(),)+) ) } + Ok(Box::new(move |lhs_e, rhs_e, commands| { + unsafe { f(commands, (lhs_e, $($lhs_little.as_mut(),)+), (rhs_e, $($rhs_little.as_mut(),)+) ) } })) }), @@ -199,7 +199,7 @@ macro_rules! impl_pair_update { } } - impl AddUpdates2<( ($( $lhs_big, )+), ($($rhs_big,)+) ), Func, (LhsFilter, RhsFilter)> for Updates + impl AddUpdates<( ($( $lhs_big, )+), ($($rhs_big,)+) ), Func, (LhsFilter, RhsFilter)> for Updates where $( $rhs_big: EntityComponent + ComponentDebug, @@ -207,7 +207,7 @@ macro_rules! impl_pair_update { $( $lhs_big: EntityComponent + ComponentDebug, )+ - Func: Fn(& mut World, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static, + Func: Fn(&mut Commands, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static, LhsFilter: CheckFilter + 'static, RhsFilter: CheckFilter + 'static { @@ -234,7 +234,7 @@ macro_rules! impl_pair_update { $( $lhs_big: EntityComponent + ComponentDebug, )+ - Func: Fn(& mut World, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static, + Func: Fn(& mut Commands, (Entity, $(&mut $lhs_big,)+), (Entity, $(&mut $rhs_big,)+)) -> Result<()> + Send + Sync + Clone + 'static, LhsFilter: CheckFilter + 'static, RhsFilter: CheckFilter + 'static { @@ -316,8 +316,8 @@ pub trait AddUpdates { fn add_update(&mut self, name: &str, priority: u32, func: Func, filter: Filter) -> Result<()>; } -trait AddUpdates2 { - fn add_update(&mut self, name: &str, priority: u32, func: Func, filter: Filter) -> Result<()>; +trait CreateArchetype { + fn create(f: Func, filter: Filter) -> Self; } pub trait CheckFilter: Send + Sync + Default + Clone { @@ -358,12 +358,15 @@ impl ArchetypeInfo { pub struct Archetype { check_entity: Box bool + Send + Sync>, create_callback: Box< - dyn Fn(&EntityObject) -> Result Result<()> + Send + Sync>> + dyn Fn( + &EntityObject, + ) + -> Result Result<()> + Send + Sync>> + Send + Sync, >, - entities: IndexMap Result<()> + Send + Sync>>, + entities: IndexMap Result<()> + Send + Sync>>, } impl Archetype { @@ -381,9 +384,11 @@ impl Archetype { self.entities.swap_remove(&entity); } - pub fn execute(&self, scene_contents: &mut World) -> Result<()> { + pub fn execute(&self, world: &mut World) -> Result<()> { for (entity, callback) in self.entities.iter() { - callback(*entity, scene_contents)?; + let mut commands = Commands::default(); + callback(*entity, &mut commands)?; + commands.apply_deferred(world)?; } Ok(()) @@ -391,7 +396,7 @@ impl Archetype { pub fn entities( &self, - ) -> &IndexMap Result<()> + Send + Sync>> { + ) -> &IndexMap Result<()> + Send + Sync>> { &self.entities } } @@ -405,14 +410,14 @@ pub struct ArchetypePair { &EntityObject, &EntityObject, ) - -> Result Result<()> + Send + Sync>> + -> Result Result<()> + Send + Sync>> + Send + Sync, >, entities: IndexMap< (Entity, Entity), - Box Result<()> + Send + Sync>, + Box Result<()> + Send + Sync>, >, } @@ -461,9 +466,11 @@ impl ArchetypePair { } } - pub(crate) fn execute(&self, scene_contents: &mut World) -> Result<()> { + pub(crate) fn execute(&self, world: &mut World) -> Result<()> { for ((left_entity, right_entity), callback) in self.entities.iter() { - callback(*left_entity, *right_entity, scene_contents)?; + let mut commands = Commands::default(); + callback(*left_entity, *right_entity, &mut commands)?; + commands.apply_deferred(world)?; } Ok(()) @@ -625,26 +632,8 @@ impl Archetypes { } } -#[rustfmt::skip] -impl_singleton_update!(single, [R]); -#[rustfmt::skip] -impl_singleton_update!(double, [R], [S]); -#[rustfmt::skip] -impl_singleton_update!(triple, [R], [S], [T]); -#[rustfmt::skip] -impl_singleton_update!(quadruple, [R], [S], [T], [U]); -#[rustfmt::skip] -impl_singleton_update!(quintuple, [R], [S], [T], [U], [V]); -#[rustfmt::skip] -impl_singleton_update!(sextuple, [R], [S], [T], [U], [V], [W]); -#[rustfmt::skip] -impl_singleton_update!(septuple, [R], [S], [T], [U], [V], [W], [X]); -#[rustfmt::skip] -impl_singleton_update!(octuple, [R], [S], [T], [U], [V], [W], [X], [Y]); -#[rustfmt::skip] -impl_singleton_update!(ninetuple, [R], [S], [T], [U], [V], [W], [X], [Y], [Z]); - implement_pair_update!(impl_pair_update, 1, 10); +implement_single_update!(impl_singleton_update, 1, 10); #[rustfmt::skip] impl_update_filter!(Monuple, [R, r]); diff --git a/ecs/src/world.rs b/ecs/src/world.rs index 3d0ec8f..b1e4926 100644 --- a/ecs/src/world.rs +++ b/ecs/src/world.rs @@ -58,7 +58,7 @@ impl WorldBuilder { } } -enum ComponentChange { +pub(crate) enum ComponentChange { Added(TypeId, Box), Removed(TypeId), } @@ -249,6 +249,17 @@ impl World { Ok(()) } + pub(crate) fn component_change(&mut self, entity: Entity, change: ComponentChange) { + match self.entities_updates.get_mut(&entity) { + Some(changes) => { + changes.push(change); + } + None => { + self.entities_updates.insert(entity, vec![change]); + } + } + } + pub fn remove_entity(&mut self, entity: Entity) { self.entities_to_remove.push(entity); } diff --git a/update_macros/src/lib.rs b/update_macros/src/lib.rs index 12f4f60..9a7c524 100644 --- a/update_macros/src/lib.rs +++ b/update_macros/src/lib.rs @@ -1,6 +1,11 @@ +mod pair_update; +mod single_update; + +use pair_update::pair_update; +use single_update::single_update; + use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::{format_ident, quote}; +use quote::quote; use syn::{ DeriveInput, Ident, LitInt, Result, parse::{Parse, ParseStream}, @@ -39,81 +44,14 @@ struct TupleType { pub fn implement_pair_update(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as InputInfo); - let mut generic_count = Vec::new(); + pair_update(input) +} - for lhs in input.start..=input.end { - for rhs in input.start..=input.end { - generic_count.push((lhs, rhs)); - } - } +#[proc_macro] +pub fn implement_single_update(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as InputInfo); - let generic_tuples: Vec<(Vec, Vec)> = generic_count - .iter() - .map(|(lhs_count, rhs_count)| { - let lhs = (input.start..(input.start + lhs_count)) - .map(|i| TupleType { - little: format_ident!("l{}", i), - big: format_ident!("L{}", i), - }) - .collect(); - - let rhs = (input.start..(input.start + rhs_count)) - .map(|i| TupleType { - little: format_ident!("r{}", i), - big: format_ident!("R{}", i), - }) - .collect(); - - (lhs, rhs) - }) - .collect(); - - let invocations: Vec = generic_tuples - .iter() - .map(|(lhs, rhs)| { - let lhs_expr = LitInt::new(&format!("{}", lhs.len()), Span::call_site()); - let rhs_expr = LitInt::new(&format!("{}", rhs.len()), Span::call_site()); - - let lhs_args: Vec = lhs - .iter() - .map(|tuple| { - let little = &tuple.little; - let big = &tuple.big; - - quote! { - [#little: #big], - } - }) - .collect(); - - let rhs_args: Vec = rhs - .iter() - .map(|tuple| { - let little = &tuple.little; - let big = &tuple.big; - - quote! { - [#little: #big], - } - }) - .collect(); - - let macro_ident = &input.macro_ident; - - quote! { - #macro_ident!( - #lhs_expr, (#(#lhs_args)*), - #rhs_expr, (#(#rhs_args)*) - ); - } - }) - .collect(); - - TokenStream::from(quote! { - #( - #invocations - )* - }) + single_update(input) } #[proc_macro_derive(Resource)] diff --git a/update_macros/src/pair_update.rs b/update_macros/src/pair_update.rs new file mode 100644 index 0000000..f9c90db --- /dev/null +++ b/update_macros/src/pair_update.rs @@ -0,0 +1,85 @@ +use proc_macro::TokenStream; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{format_ident, quote}; +use syn::LitInt; + +use crate::InputInfo; +use crate::TupleType; + +pub fn pair_update(input: InputInfo) -> TokenStream { + let mut generic_count = Vec::new(); + + for lhs in input.start..=input.end { + for rhs in input.start..=input.end { + generic_count.push((lhs, rhs)); + } + } + + let generic_tuples: Vec<(Vec, Vec)> = generic_count + .iter() + .map(|(lhs_count, rhs_count)| { + let lhs = (input.start..(input.start + lhs_count)) + .map(|i| TupleType { + little: format_ident!("l{}", i), + big: format_ident!("L{}", i), + }) + .collect(); + + let rhs = (input.start..(input.start + rhs_count)) + .map(|i| TupleType { + little: format_ident!("r{}", i), + big: format_ident!("R{}", i), + }) + .collect(); + + (lhs, rhs) + }) + .collect(); + + let invocations: Vec = generic_tuples + .iter() + .map(|(lhs, rhs)| { + let lhs_expr = LitInt::new(&format!("{}", lhs.len()), Span::call_site()); + let rhs_expr = LitInt::new(&format!("{}", rhs.len()), Span::call_site()); + + let lhs_args: Vec = lhs + .iter() + .map(|tuple| { + let little = &tuple.little; + let big = &tuple.big; + + quote! { + [#little: #big], + } + }) + .collect(); + + let rhs_args: Vec = rhs + .iter() + .map(|tuple| { + let little = &tuple.little; + let big = &tuple.big; + + quote! { + [#little: #big], + } + }) + .collect(); + + let macro_ident = &input.macro_ident; + + quote! { + #macro_ident!( + #lhs_expr, (#(#lhs_args)*), + #rhs_expr, (#(#rhs_args)*) + ); + } + }) + .collect(); + + TokenStream::from(quote! { + #( + #invocations + )* + }) +} diff --git a/update_macros/src/single_update.rs b/update_macros/src/single_update.rs new file mode 100644 index 0000000..6441606 --- /dev/null +++ b/update_macros/src/single_update.rs @@ -0,0 +1,33 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; +use syn::Ident; + +use crate::InputInfo; + +pub fn single_update(input: InputInfo) -> TokenStream { + let generate_inputs: Vec> = (input.start..=input.end) + .map(|count| { + (input.start..(input.start + count)) + .map(|i| format_ident!("t{}", i)) + .collect() + }) + .collect(); + + let invocations: Vec = generate_inputs + .iter() + .map(|t| { + let macro_ident = &input.macro_ident; + + quote! { + #macro_ident!(#(#t,)*); + } + }) + .collect(); + + TokenStream::from(quote! { + #( + #invocations + )* + }) +}