use std::any::type_name; use std::marker::PhantomData; use std::rc::{Rc, Weak}; use crate::component::{Parts as ComponentParts, Sequence as ComponentSequence}; use crate::event::component::Removed; use crate::pair::Pair; use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::uid::{Kind as UidKind, Uid}; use crate::{ActionQueue, World}; /// Used to to queue up actions for a [`World`] to perform. #[derive(Debug)] pub struct Actions<'world> { action_queue: &'world ActionQueue, world: Option<&'world World>, } impl Actions<'_> { /// Queues up a entity to spawn at the end of the current tick, returning the [`Uid`] /// that the entity will have. pub fn spawn(&mut self, components: Comps) -> Uid { let new_entity_uid = Uid::new_unique(UidKind::Entity); self.action_queue.push(Action::Spawn( new_entity_uid, components.into_parts_array().into(), )); new_entity_uid } /// Queues up despawning a entity at the end of the current tick. pub fn despawn(&mut self, entity_uid: Uid) { debug_assert_eq!(entity_uid.kind(), UidKind::Entity); let Some(world) = self.world else { self.action_queue.push(Action::Despawn(entity_uid)); return; }; let Some(ent) = world.get_entity(entity_uid) else { tracing::warn!("Cannot entity that doesn't exist"); return; }; // TODO: Submit all events with a single function call to reduce overhead for comp_id in ent.component_ids() { if comp_id.kind() == UidKind::Pair { continue; } world.event_submitter().submit_event( &Pair::builder() .relation::() .target_id(comp_id) .build(), entity_uid, ); } self.action_queue.push(Action::Despawn(entity_uid)); } /// Queues up adding component(s) to a entity at the end of the current tick. pub fn add_components(&mut self, entity_uid: Uid, components: Comps) where Comps: ComponentSequence, { debug_assert_eq!(entity_uid.kind(), UidKind::Entity); if Comps::COUNT == 0 { return; } self.action_queue.push(Action::AddComponents( entity_uid, components.into_parts_array().into(), )); } /// Queues up removing component(s) from a entity at the end of the current tick. #[tracing::instrument(skip(self, component_ids))] pub fn remove_components( &mut self, entity_uid: Uid, component_ids: impl IntoIterator, ) { debug_assert_eq!(entity_uid.kind(), UidKind::Entity); let mut component_ids = component_ids.into_iter().peekable(); if component_ids.peek().is_none() { return; } let Some(world) = self.world else { self.action_queue.push(Action::RemoveComponents( entity_uid, component_ids.collect(), )); return; }; let Some(ent) = world.get_entity(entity_uid) else { tracing::warn!("Cannot remove components from entity that doesn't exist"); return; }; let component_ids = component_ids .filter(|comp_id| ent.has_component(*comp_id)) .collect::>(); if component_ids.is_empty() { return; } // TODO: Submit all events with a single function call to reduce overhead for comp_id in &component_ids { if comp_id.kind() == UidKind::Pair { continue; } world.event_submitter().submit_event( &Pair::builder() .relation::() .target_id(*comp_id) .build(), entity_uid, ); } self.action_queue .push(Action::RemoveComponents(entity_uid, component_ids)); } /// Stops the [`World`]. The world will finish the current tick and that tick will be /// the last. pub fn stop(&mut self) { self.action_queue.push(Action::Stop); } /// Returns a struct which holds a weak reference to the [`World`] that `Actions` /// references and that can be used to aquire a new `Actions` instance if the /// referenced [`World`] is still alive. /// /// # Panics /// This function will panic if `self` was retrieved from a [`WeakRef`]. #[must_use] pub fn to_weak_ref(&self) -> WeakRef { let world = self.world.unwrap_or_else(|| { panic!( "This function cannot be called if the {} was retrieved from a {}", type_name::(), type_name::() ) }); WeakRef { action_queue: Rc::downgrade(&world.data.action_queue), } } } impl<'world> SystemParam<'world> for Actions<'world> { type Input = (); fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { Self { action_queue: &world.data.action_queue, world: Some(world), } } } #[derive(Debug, Clone)] pub struct WeakRef { action_queue: Weak, } impl WeakRef { /// Returns a struct which can be used to retrieve a [`Actions`]. /// /// Returns [`None`] if the referenced [`World`] has been dropped. #[must_use] pub fn access(&self) -> Option> { Some(Ref { action_queue: self.action_queue.upgrade()?, _pd: PhantomData, }) } } /// Intermediate between [`Actions`] and [`WeakRef`]. Contains a strong reference to /// a world which is not allowed direct access to. #[derive(Debug, Clone)] pub struct Ref<'weak_ref> { action_queue: Rc, _pd: PhantomData<&'weak_ref ()>, } impl Ref<'_> { #[must_use] pub fn to_actions(&self) -> Actions<'_> { Actions { action_queue: &self.action_queue, world: None, } } } /// A action for a [`System`] to perform. #[derive(Debug)] pub(crate) enum Action { Spawn(Uid, Vec), Despawn(Uid), AddComponents(Uid, Vec), RemoveComponents(Uid, Vec), Stop, }