use super::SystemId; use crate::resource::{Resource, Resources}; use bevy_hecs::{Bundle, Component, DynamicBundle, Entity, EntityReserver, World}; use std::marker::PhantomData; /// A [World] mutation pub trait Command: Send + Sync { fn write(self: Box, world: &mut World, resources: &mut Resources); } #[derive(Debug)] pub(crate) struct Spawn where T: DynamicBundle + Send + Sync + 'static, { components: T, } impl Command for Spawn where T: DynamicBundle + Send + Sync + 'static, { fn write(self: Box, world: &mut World, _resources: &mut Resources) { world.spawn(self.components); } } pub(crate) struct SpawnBatch where I: IntoIterator, I::Item: Bundle, { components_iter: I, } impl Command for SpawnBatch where I: IntoIterator + Send + Sync, I::Item: Bundle, { fn write(self: Box, world: &mut World, _resources: &mut Resources) { world.spawn_batch(self.components_iter); } } #[derive(Debug)] pub(crate) struct Despawn { entity: Entity, } impl Command for Despawn { fn write(self: Box, world: &mut World, _resources: &mut Resources) { if let Err(e) = world.despawn(self.entity) { log::debug!("Failed to despawn entity {:?}: {}", self.entity, e); } } } pub struct Insert where T: DynamicBundle + Send + Sync + 'static, { entity: Entity, components: T, } impl Command for Insert where T: DynamicBundle + Send + Sync + 'static, { fn write(self: Box, world: &mut World, _resources: &mut Resources) { world.insert(self.entity, self.components).unwrap(); } } #[derive(Debug)] pub(crate) struct InsertOne where T: Component, { entity: Entity, component: T, } impl Command for InsertOne where T: Component, { fn write(self: Box, world: &mut World, _resources: &mut Resources) { world.insert(self.entity, (self.component,)).unwrap(); } } #[derive(Debug)] pub(crate) struct RemoveOne where T: Component, { entity: Entity, phantom: PhantomData, } impl Command for RemoveOne where T: Component, { fn write(self: Box, world: &mut World, _resources: &mut Resources) { if world.get::(self.entity).is_ok() { world.remove_one::(self.entity).unwrap(); } } } #[derive(Debug)] pub(crate) struct Remove where T: Bundle + Send + Sync + 'static, { entity: Entity, phantom: PhantomData, } impl Command for Remove where T: Bundle + Send + Sync + 'static, { fn write(self: Box, world: &mut World, _resources: &mut Resources) { world.remove::(self.entity).unwrap(); } } pub trait ResourcesWriter: Send + Sync { fn write(self: Box, resources: &mut Resources); } pub struct InsertResource { resource: T, } impl Command for InsertResource { fn write(self: Box, _world: &mut World, resources: &mut Resources) { resources.insert(self.resource); } } #[derive(Debug)] pub(crate) struct InsertLocalResource { resource: T, system_id: SystemId, } impl Command for InsertLocalResource { fn write(self: Box, _world: &mut World, resources: &mut Resources) { resources.insert_local(self.system_id, self.resource); } } #[derive(Default)] pub struct Commands { commands: Vec>, current_entity: Option, entity_reserver: Option, } impl Commands { pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self { let entity = self .entity_reserver .as_ref() .expect("entity reserver has not been set") .reserve_entity(); self.current_entity = Some(entity); self.commands.push(Box::new(Insert { entity, components })); self } pub fn spawn_batch(&mut self, components_iter: I) -> &mut Self where I: IntoIterator + Send + Sync + 'static, I::Item: Bundle, { self.add_command(SpawnBatch { components_iter }) } /// Despawns only the specified entity, ignoring any other consideration. pub fn despawn(&mut self, entity: Entity) -> &mut Self { self.add_command(Despawn { entity }) } pub fn insert( &mut self, entity: Entity, components: impl DynamicBundle + Send + Sync + 'static, ) -> &mut Self { self.add_command(Insert { entity, components }) } pub fn insert_one(&mut self, entity: Entity, component: impl Component) -> &mut Self { self.add_command(InsertOne { entity, component }) } pub fn insert_resource(&mut self, resource: T) -> &mut Self { self.add_command(InsertResource { resource }) } pub fn insert_local_resource( &mut self, system_id: SystemId, resource: T, ) -> &mut Self { self.add_command(InsertLocalResource { system_id, resource, }) } pub fn remove_one(&mut self, entity: Entity) -> &mut Self where T: Component, { self.add_command(RemoveOne:: { entity, phantom: PhantomData, }) } pub fn remove(&mut self, entity: Entity) -> &mut Self where T: Bundle + Send + Sync + 'static, { self.add_command(Remove:: { entity, phantom: PhantomData, }) } pub fn with_bundle( &mut self, components: impl DynamicBundle + Send + Sync + 'static, ) -> &mut Self { let current_entity = self.current_entity.expect("Cannot add components because the 'current entity' is not set. You should spawn an entity first."); self.commands.push(Box::new(Insert { entity: current_entity, components, })); self } pub fn with(&mut self, component: impl Component) -> &mut Self { let current_entity = self.current_entity.expect("Cannot add component because the 'current entity' is not set. You should spawn an entity first."); self.commands.push(Box::new(InsertOne { entity: current_entity, component, })); self } pub fn add_command(&mut self, command: C) -> &mut Self { self.commands.push(Box::new(command)); self } pub fn add_command_boxed(&mut self, command: Box) -> &mut Self { self.commands.push(command); self } pub fn apply(&mut self, world: &mut World, resources: &mut Resources) { for command in self.commands.drain(..) { command.write(world, resources); } } pub fn current_entity(&self) -> Option { self.current_entity } pub fn set_current_entity(&mut self, entity: Entity) { self.current_entity = Some(entity); } pub fn clear_current_entity(&mut self) { self.current_entity = None; } pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self { let current_entity = self .current_entity .expect("The 'current entity' is not set. You should spawn an entity first."); f(current_entity); self } pub fn set_entity_reserver(&mut self, entity_reserver: EntityReserver) { self.entity_reserver = Some(entity_reserver); } } #[cfg(test)] mod tests { use super::Commands; use crate::resource::Resources; use bevy_hecs::World; #[test] fn command_buffer() { let mut world = World::default(); let mut resources = Resources::default(); let mut command_buffer = Commands::default(); command_buffer.set_entity_reserver(world.get_entity_reserver()); command_buffer.spawn((1u32, 2u64)); let entity = command_buffer.current_entity().unwrap(); command_buffer.insert_resource(3.14f32); command_buffer.apply(&mut world, &mut resources); let results = world .query::<(&u32, &u64)>() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results, vec![(1u32, 2u64)]); assert_eq!(*resources.get::().unwrap(), 3.14f32); // test entity despawn command_buffer.despawn(entity); command_buffer.despawn(entity); // double despawn shouldn't panic command_buffer.apply(&mut world, &mut resources); let results2 = world .query::<(&u32, &u64)>() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results2, vec![]); } }