diff --git a/mvp-effects.md b/mvp-effects.md index 358563d..606ebed 100644 --- a/mvp-effects.md +++ b/mvp-effects.md @@ -20,15 +20,15 @@ - [x] `EntityComponentsWith` # Command Effects -- [ ] `InsertResource` -- [ ] `RemoveResource` -- [ ] `CommandEffect` +- [x] `CommandQueue` +- [ ] `CommandInsertResource` +- [ ] `CommandRemoveResource` # Entity command effects: -- [ ] `SpawnThen` -- [ ] `EntityInsert` -- [ ] `EntityRemove` -- [ ] `EntityDespawn` +- [ ] `CommandSpawnEmptyAnd` +- [ ] `CommandEntityInsert` +- [ ] `CommandEntityRemove` +- [ ] `CommandEntityDespawnRecursive` *For MVP, `CommandEffect` enables hierarchy commands* diff --git a/src/effects/command.rs b/src/effects/command.rs new file mode 100644 index 0000000..c4ddd01 --- /dev/null +++ b/src/effects/command.rs @@ -0,0 +1,67 @@ +use bevy::prelude::*; + +use crate::Effect; + +/// [`Effect`] that pushes a generic command to the command queue. +#[doc = include_str!("defer_command_note.md")] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct CommandQueue(pub C) +where + C: Command; + +impl Effect for CommandQueue +where + C: Command, +{ + type MutParam = Commands<'static, 'static>; + + fn affect(self, param: &mut ::Item<'_, '_>) { + param.queue(self.0) + } +} + +#[cfg(test)] +mod tests { + use proptest::prelude::*; + + use super::*; + use crate::effects::number_data::NumberComponent; + use crate::prelude::affect; + + proptest! { + #[test] + fn command_queue_can_spawn_entities_non_exclusively(component in any::>()) { + let mut app = App::new(); + + let component_count = app.world_mut().query_filtered::<(), With>>().iter(app.world()).count(); + + assert_eq!(component_count, 0); + + let spawn_component_system = move || { + CommandQueue(move |world: &mut World| { + world.spawn(component.clone()); + }) + }; + + + assert!(!IntoSystem::into_system(spawn_component_system.pipe(affect)).is_exclusive()); + + app.add_systems( + Update, + spawn_component_system.pipe(affect), + ); + + app.update(); + + let component_count = app.world_mut().query_filtered::<(), With>>().iter(app.world()).count(); + + assert_eq!(component_count, 1); + + app.update(); + + let component_count = app.world_mut().query_filtered::<(), With>>().iter(app.world()).count(); + + assert_eq!(component_count, 2); + } + } +} diff --git a/src/effects/defer_command_note.md b/src/effects/defer_command_note.md new file mode 100644 index 0000000..26acf24 --- /dev/null +++ b/src/effects/defer_command_note.md @@ -0,0 +1,3 @@ + + *Note: `Command` effects only affect the command queue. + The `World` modifications of the command only take place when the `apply_deferred` system runs.* diff --git a/src/effects/entity_components.rs b/src/effects/entity_components.rs index cc3f6c7..d33e353 100644 --- a/src/effects/entity_components.rs +++ b/src/effects/entity_components.rs @@ -59,7 +59,7 @@ all_tuples!(impl_effect_for_entity_components_put, 1, 15, C, c, r); #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct EntityComponentsWith where - F: for<'a> Fn(C, ::Item<'a>) -> C + Send + Sync, + F: for<'a> FnOnce(C, ::Item<'a>) -> C + Send + Sync, C: Clone, Data: ReadOnlyQueryData, { @@ -71,7 +71,7 @@ where impl EntityComponentsWith where - F: for<'a> Fn(C, ::Item<'a>) -> C + Send + Sync, + F: for<'a> FnOnce(C, ::Item<'a>) -> C + Send + Sync, C: Clone, Data: ReadOnlyQueryData, { @@ -90,7 +90,7 @@ macro_rules! impl_effect_for_entity_components_with { ($(($C:ident, $c:ident, $r:ident)),*) => { impl Effect for EntityComponentsWith where - F: for<'a> Fn(($($C,)*), ::Item<'a>) -> ($($C,)*) + Send + Sync, + F: for<'a> FnOnce(($($C,)*), ::Item<'a>) -> ($($C,)*) + Send + Sync, $($C: Component + Clone,)* Data: ReadOnlyQueryData + 'static, { diff --git a/src/effects/mod.rs b/src/effects/mod.rs index 630f7f6..3740be4 100644 --- a/src/effects/mod.rs +++ b/src/effects/mod.rs @@ -14,6 +14,9 @@ pub use components::{ComponentsPut, ComponentsWith}; mod entity_components; pub use entity_components::{EntityComponentsPut, EntityComponentsWith}; +mod command; +pub use command::CommandQueue; + #[cfg(test)] mod one_way_fn; diff --git a/src/prelude.rs b/src/prelude.rs index 3ab27e2..2ad3666 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,6 +1,7 @@ //! `use bevy_pipe_affect::prelude::*;` to import common items. pub use crate::effects::{ + CommandQueue, ComponentsPut, ComponentsWith, EntityComponentsPut,