diff --git a/mvp-effects.md b/mvp-effects.md index 1c70ded..06573bd 100644 --- a/mvp-effects.md +++ b/mvp-effects.md @@ -43,7 +43,7 @@ - [ ] `CommandRunSchedule` # Entity command effects: -- [ ] `EntityCommandQueue` +- [x] `EntityCommandQueue` - [ ] `EntityCommandInsert` - [ ] `EntityCommandRemove` diff --git a/src/effects/entity_command.rs b/src/effects/entity_command.rs new file mode 100644 index 0000000..0c660c8 --- /dev/null +++ b/src/effects/entity_command.rs @@ -0,0 +1,86 @@ +use std::marker::PhantomData; + +use bevy::ecs::error::CommandWithEntity; +use bevy::prelude::*; + +use crate::Effect; + +/// [`Effect`] that pushes a generic entity command to the command queue. +#[doc = include_str!("defer_command_note.md")] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct EntityCommandQueue +where + C: EntityCommand + CommandWithEntity, +{ + entity: Entity, + command: C, + entity_command_out: PhantomData, + command_with_entity_out: PhantomData, +} + +impl EntityCommandQueue +where + C: EntityCommand + CommandWithEntity, +{ + /// Construct a new [`EntityCommandQueue`] + pub fn new(entity: Entity, command: C) -> Self { + EntityCommandQueue { + entity, + command, + entity_command_out: PhantomData, + command_with_entity_out: PhantomData, + } + } +} + +impl Effect for EntityCommandQueue +where + C: EntityCommand + CommandWithEntity, +{ + type MutParam = Commands<'static, 'static>; + + fn affect(self, param: &mut ::Item<'_, '_>) { + param.entity(self.entity).queue(self.command); + } +} + +#[cfg(test)] +mod tests { + use proptest::prelude::*; + + use super::*; + use crate::effects::number_data::NumberComponent; + use crate::prelude::affect; + + proptest! { + #[test] + fn entity_command_queue_can_insert_component_non_exclusively(component in any::>()) { + let mut app = App::new(); + + let entity = app.world_mut().spawn(()).id(); + + let actual_component = app.world().get_entity(entity).unwrap().get_components::<&NumberComponent<0>>(); + + assert!(actual_component.is_none()); + + let insert_component_system = move || { + EntityCommandQueue::new(entity, move |mut entity_world: EntityWorldMut<'_>| { + entity_world.insert(component.clone()); + }) + }; + + assert!(!IntoSystem::into_system(insert_component_system.pipe(affect)).is_exclusive()); + + app.add_systems( + Update, + insert_component_system.pipe(affect), + ); + + app.update(); + + let actual_component = app.world().get_entity(entity).unwrap().get_components::<&NumberComponent<0>>().unwrap(); + + assert_eq!(actual_component, &component); + } + } +} diff --git a/src/effects/mod.rs b/src/effects/mod.rs index 5d8b496..c2b1610 100644 --- a/src/effects/mod.rs +++ b/src/effects/mod.rs @@ -17,6 +17,9 @@ pub use entity_components::{EntityComponentsPut, EntityComponentsWith}; mod command; pub use command::{CommandInsertResource, CommandQueue, CommandRemoveResource}; +mod entity_command; +pub use entity_command::EntityCommandQueue; + mod variadic; #[cfg(test)] diff --git a/src/prelude.rs b/src/prelude.rs index 8943327..91ef994 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -6,6 +6,7 @@ pub use crate::effects::{ CommandRemoveResource, ComponentsPut, ComponentsWith, + EntityCommandQueue, EntityComponentsPut, EntityComponentsWith, EventWrite,