palkia/
messages.rs

1//! Data sent to an entity and forwarded to each of its components, mutated along the way.
2
3use std::sync::atomic::{AtomicBool, Ordering};
4
5use crossbeam::channel;
6use downcast::{downcast, Any};
7
8use crate::{
9  entities::EntityLiveness,
10  prelude::{
11    AccessDispatcher, AccessEntityStats, AccessQuery, AccessResources,
12    AccessSpawnEntities, Component, Entity, EntityBuilder, Query, World,
13  },
14  resource::{ReadResource, Resource, ResourceLookupError, WriteResource},
15  world::{dispatch_inner, LazyUpdate},
16};
17
18/// Data that is threaded through components.
19///
20/// When a message is dispatched to an entity, it goes through its components. A component with a handler for this type
21/// registered with [`World::register_component`] gets its listener called, and then the updated event gets passed to the next
22/// component ... and so on. Then, it's returned to the dispatcher.
23pub trait Message: Any {}
24downcast!(dyn Message);
25
26/// A message handler that only needs immutable access to the component.
27pub type MsgHandlerRead<C, E> =
28  fn(this: &C, event: E, owner: Entity, access: &ListenerWorldAccess) -> E;
29/// A message handler that needs mutable access to the component.
30pub type MsgHandlerWrite<C, E> =
31  fn(this: &mut C, event: E, owner: Entity, access: &ListenerWorldAccess) -> E;
32
33pub(crate) enum MsgHandlerInner {
34  Read(
35    Box<
36      dyn Send
37        + Sync
38        + Fn(
39          &dyn Component,
40          Box<dyn Message>,
41          Entity,
42          &ListenerWorldAccess,
43        ) -> Box<dyn Message>,
44    >,
45  ),
46  Write(
47    Box<
48      dyn Send
49        + Sync
50        + Fn(
51          &mut dyn Component,
52          Box<dyn Message>,
53          Entity,
54          &ListenerWorldAccess,
55        ) -> Box<dyn Message>,
56    >,
57  ),
58}
59
60/// Way to access a world from a message listener.
61///
62/// Some of the changes here won't actually apply until `World::finalize` is called.
63pub struct ListenerWorldAccess<'w> {
64  lazy_updates: channel::Sender<LazyUpdate>,
65  queued_message_tx: channel::Sender<(Box<dyn Message>, Entity)>,
66  queued_message_rx: channel::Receiver<(Box<dyn Message>, Entity)>,
67  cancelled: AtomicBool,
68
69  pub(crate) world: &'w World,
70}
71
72impl<'w> ListenerWorldAccess<'w> {
73  pub(crate) fn new(world: &'w World) -> Self {
74    let (tx, rx) = channel::unbounded();
75    Self {
76      lazy_updates: world.lazy_sender.clone(),
77      queued_message_tx: tx,
78      queued_message_rx: rx,
79      cancelled: AtomicBool::new(false),
80      world,
81    }
82  }
83
84  /// Queue dispatching a message to the given entity. That entity will get the message sent to it once the current
85  /// entity is through threading the current message through its components.
86  ///
87  /// Because handling of the new message is delayed, you cannot get the updated value of the message.
88  ///
89  /// This is handy for dispatching messages which would otherwise mutate components currently locked.
90  pub fn queue_dispatch<M: Message>(&self, target: Entity, msg: M) {
91    self
92      .queued_message_tx
93      .send((Box::new(msg), target))
94      .unwrap();
95  }
96
97  /// Set up an entity to be spawned once [`World::finalize`] is called.
98  pub fn lazy_spawn<'a>(&'a self) -> EntityBuilder<'a, 'w> {
99    let entity = self.world.entities.spawn_unfinished();
100    EntityBuilder::new_lazy(self, entity)
101  }
102
103  /// Queue an entity to be despawned when [`World::finalize`] is called.
104  pub fn lazy_despawn(&self, entity: Entity) {
105    self.queue_update(LazyUpdate::DespawnEntity(entity));
106  }
107
108  /// Cancel the message, preventing it from being passed to further components on the entity.
109  ///
110  /// This can be used for control flow, but it's most useful for efficiency if you know no further processing will happen,
111  /// so the world doesn't need to iterate over the remaining components.
112  pub fn cancel(&self) {
113    self.set_cancellation(true)
114  }
115
116  /// Set the cancellation state of the message. See [`ListenerWorldAccess::cancel`].
117  pub fn set_cancellation(&self, cancelled: bool) {
118    self.cancelled.store(cancelled, Ordering::Relaxed);
119  }
120
121  /// Get whether the message is cancelled or not. See [`ListenerWorldAccess::cancel`].
122  ///
123  /// I'm not sure why you would want to call this and it's probably bad code smell if you do,
124  /// but it felt incomplete to not impl it.
125  pub fn is_cancelled(&self) -> bool {
126    self.cancelled.load(Ordering::SeqCst)
127  }
128
129  pub(crate) fn queue_update(&self, update: LazyUpdate) {
130    self.lazy_updates.send(update).unwrap();
131  }
132
133  pub(crate) fn queued_message_rx(
134    &self,
135  ) -> &channel::Receiver<(Box<dyn Message>, Entity)> {
136    &self.queued_message_rx
137  }
138}
139
140impl<'w> AccessDispatcher for ListenerWorldAccess<'w> {
141  fn dispatch<M: Message>(&self, target: Entity, msg: M) -> M {
142    dispatch_inner(self, target, msg)
143  }
144}
145
146impl<'w> AccessEntityStats for ListenerWorldAccess<'w> {
147  fn len(&self) -> usize {
148    self.world.len()
149  }
150
151  fn liveness(&self, entity: Entity) -> EntityLiveness {
152    self.world.liveness(entity)
153  }
154
155  fn len_of(&self, entity: Entity) -> usize {
156    self.world.len_of(entity)
157  }
158
159  fn iter(&self) -> crate::entities::EntityIter<'_> {
160    self.world.iter()
161  }
162}
163
164impl<'w> AccessQuery for ListenerWorldAccess<'w> {
165  fn query<'c, Q: Query<'c>>(
166    &'c self,
167    interrogatee: Entity,
168  ) -> Option<Q::Response> {
169    self.world.query::<Q>(interrogatee)
170  }
171}
172
173impl<'w> AccessResources for ListenerWorldAccess<'w> {
174  fn read_resource<R: Resource>(
175    &self,
176  ) -> Result<ReadResource<'_, R>, ResourceLookupError> {
177    self.world.read_resource()
178  }
179
180  fn write_resource<R: Resource>(
181    &self,
182  ) -> Result<WriteResource<'_, R>, ResourceLookupError> {
183    self.world.write_resource()
184  }
185
186  fn contains_resource<R: Resource>(&self) -> bool {
187    self.world.contains_resource::<R>()
188  }
189}
190
191impl<'w> AccessSpawnEntities for ListenerWorldAccess<'w> {
192  fn spawn_entity(&self) -> EntityBuilder {
193    self.lazy_spawn()
194  }
195}