summaryrefslogtreecommitdiff
path: root/engine-ecs/src/actions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine-ecs/src/actions.rs')
-rw-r--r--engine-ecs/src/actions.rs174
1 files changed, 174 insertions, 0 deletions
diff --git a/engine-ecs/src/actions.rs b/engine-ecs/src/actions.rs
new file mode 100644
index 0000000..3d8afe6
--- /dev/null
+++ b/engine-ecs/src/actions.rs
@@ -0,0 +1,174 @@
+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, WithUidTuple};
+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<Comps: ComponentSequence>(&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::<Removed>()
+ .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<Comps>(&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<Item = Uid>,
+ )
+ {
+ 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::<Vec<_>>();
+
+ 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::<Removed>()
+ .target_id(*comp_id)
+ .build(),
+ entity_uid,
+ );
+ }
+
+ self.action_queue
+ .push(Action::RemoveComponents(entity_uid, component_ids));
+ }
+
+ /// Queues up removing component(s) from a entity at the end of the current tick.
+ pub fn remove_comps<Ids: WithUidTuple>(&mut self, entity_uid: Uid)
+ {
+ self.remove_components(entity_uid, Ids::uids());
+ }
+
+ /// 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);
+ }
+}
+
+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),
+ }
+ }
+}
+
+/// A action for a [`System`] to perform.
+#[derive(Debug)]
+pub(crate) enum Action
+{
+ Spawn(Uid, Vec<ComponentParts>),
+ Despawn(Uid),
+ AddComponents(Uid, Vec<ComponentParts>),
+ RemoveComponents(Uid, Vec<Uid>),
+ Stop,
+}