1use alloc::vec::Vec;
7
8use crate::component::ComponentId;
9use crate::entity::EntityId;
10use crate::event::queue::EventStorage;
11use crate::event::registry::{EventId, EventOptions, EventRegistrationError, EventRegistry};
12use crate::operation::StageOperation;
13use crate::world::{WorldError, WorldOwner};
14
15#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct ComponentAdded {
18 pub entity: EntityId,
20 pub component: ComponentId,
22}
23
24#[derive(Clone, Debug, Eq, PartialEq)]
26pub struct ComponentRemoved {
27 pub entity: EntityId,
29 pub component: ComponentId,
31}
32
33#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34pub(crate) enum LifecycleKind {
35 Added,
36 Removed,
37}
38
39pub(crate) struct ComponentLifecycleRegistry {
40 added_event_indices: Vec<Option<u32>>,
41 removed_event_indices: Vec<Option<u32>>,
42}
43
44impl ComponentLifecycleRegistry {
45 pub fn new() -> Self {
46 Self {
47 added_event_indices: Vec::new(),
48 removed_event_indices: Vec::new(),
49 }
50 }
51
52 pub fn register_component(
53 &mut self,
54 registry: &mut EventRegistry,
55 owner: &WorldOwner,
56 component_index: usize,
57 ) -> Result<(), EventRegistrationError> {
58 self.ensure_slots(component_index);
59 if self.added_event_indices[component_index].is_none() {
60 let id = registry
61 .register_lifecycle::<ComponentAdded>(
62 owner,
63 component_index,
64 LifecycleKind::Added,
65 EventOptions::frame(StageOperation::Update),
66 )
67 .expect("lifecycle added registration is infallible");
68 self.added_event_indices[component_index] = Some(id.index() as u32);
69 }
70 if self.removed_event_indices[component_index].is_none() {
71 let id = registry
72 .register_lifecycle::<ComponentRemoved>(
73 owner,
74 component_index,
75 LifecycleKind::Removed,
76 EventOptions::frame(StageOperation::Update),
77 )
78 .expect("lifecycle removed registration is infallible");
79 self.removed_event_indices[component_index] = Some(id.index() as u32);
80 }
81 Ok(())
82 }
83
84 pub fn ensure_storage_channels(
85 &self,
86 storage: &mut EventStorage,
87 registry: &EventRegistry,
88 owner: &WorldOwner,
89 ) {
90 for index in 0..self.added_event_indices.len() {
91 if let Some(event_index) = self.added_event_indices[index] {
92 let event_id = EventId::new(owner.clone(), event_index);
93 if let Some(options) = registry.options(&event_id) {
94 storage.ensure_channel(event_index as usize, options.retention())
95 }
96 }
97 if let Some(event_index) = self.removed_event_indices[index] {
98 let event_id = EventId::new(owner.clone(), event_index);
99 if let Some(options) = registry.options(&event_id) {
100 storage.ensure_channel(event_index as usize, options.retention())
101 }
102 }
103 }
104 }
105
106 pub fn emit_added(
107 &self,
108 storage: &mut EventStorage,
109 owner: &WorldOwner,
110 entity: EntityId,
111 component_index: usize,
112 ) -> Result<(), WorldError> {
113 let Some(event_index) = self
114 .added_event_indices
115 .get(component_index)
116 .and_then(|id| *id)
117 else {
118 return Ok(());
119 };
120 let event_id = EventId::new(owner.clone(), event_index);
121 let component = ComponentId::new(owner.clone(), component_index as u32);
122 storage.send(&event_id, ComponentAdded { entity, component })
123 }
124
125 pub fn emit_removed(
126 &self,
127 storage: &mut EventStorage,
128 owner: &WorldOwner,
129 entity: EntityId,
130 component_index: usize,
131 ) -> Result<(), WorldError> {
132 let Some(event_index) = self
133 .removed_event_indices
134 .get(component_index)
135 .and_then(|id| *id)
136 else {
137 return Ok(());
138 };
139 let event_id = EventId::new(owner.clone(), event_index);
140 let component = ComponentId::new(owner.clone(), component_index as u32);
141 storage.send(&event_id, ComponentRemoved { entity, component })
142 }
143
144 pub fn added_event_id(&self, owner: &WorldOwner, component_index: usize) -> Option<EventId> {
145 self.added_event_indices
146 .get(component_index)
147 .and_then(|index| *index)
148 .map(|index| EventId::new(owner.clone(), index))
149 }
150
151 pub fn removed_event_id(&self, owner: &WorldOwner, component_index: usize) -> Option<EventId> {
152 self.removed_event_indices
153 .get(component_index)
154 .and_then(|index| *index)
155 .map(|index| EventId::new(owner.clone(), index))
156 }
157
158 fn ensure_slots(&mut self, component_index: usize) {
159 while self.added_event_indices.len() <= component_index {
160 self.added_event_indices.push(None);
161 self.removed_event_indices.push(None);
162 }
163 }
164
165 #[cfg(test)]
166 pub(crate) fn clear_added_event_for_test(&mut self, component_index: usize) {
167 if let Some(slot) = self.added_event_indices.get_mut(component_index) {
168 *slot = None;
169 }
170 }
171
172 #[cfg(test)]
173 pub(crate) fn clear_removed_event_for_test(&mut self, component_index: usize) {
174 if let Some(slot) = self.removed_event_indices.get_mut(component_index) {
175 *slot = None;
176 }
177 }
178}
179
180impl Default for ComponentLifecycleRegistry {
181 fn default() -> Self {
182 Self::new()
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use crate::component::ComponentId;
190
191 #[test]
192 fn default_registry_has_no_slots() {
193 let registry = ComponentLifecycleRegistry::default();
194 assert!(registry.added_event_id(&WorldOwner::new(), 0).is_none());
195 }
196
197 #[test]
198 fn register_component_is_idempotent_for_same_index() {
199 let owner = WorldOwner::new();
200 let mut events = EventRegistry::new();
201 let mut lifecycle = ComponentLifecycleRegistry::new();
202 lifecycle
203 .register_component(&mut events, &owner, 0)
204 .expect("first");
205 lifecycle
206 .register_component(&mut events, &owner, 0)
207 .expect("repeat");
208 assert_eq!(events.len(), 2);
209 }
210
211 #[test]
212 fn emit_without_lifecycle_channel_is_noop() {
213 let owner = WorldOwner::new();
214 let lifecycle = ComponentLifecycleRegistry::new();
215 let mut storage = EventStorage::new(0);
216 let entity = EntityId::from_parts(0, 1);
217 lifecycle
218 .emit_added(&mut storage, &owner, entity, 0)
219 .expect("noop added");
220 lifecycle
221 .emit_removed(&mut storage, &owner, entity, 0)
222 .expect("noop removed");
223 }
224
225 #[test]
226 fn register_component_opens_sparse_lifecycle_slots() {
227 let owner = WorldOwner::new();
228 let mut registry = EventRegistry::new();
229 let mut lifecycle = ComponentLifecycleRegistry::new();
230 lifecycle
231 .register_component(&mut registry, &owner, 4)
232 .expect("register");
233 let mut storage = EventStorage::new(0);
234 lifecycle.ensure_storage_channels(&mut storage, ®istry, &owner);
235 let entity = EntityId::from_parts(0, 1);
236 let component = ComponentId::new(owner.clone(), 4);
237 let added = lifecycle.added_event_id(&owner, 4).expect("added");
238 storage
239 .send(
240 &added,
241 ComponentAdded {
242 entity,
243 component: component.clone(),
244 },
245 )
246 .expect("send added");
247 }
248
249 #[test]
250 fn ensure_storage_channels_opens_registered_lifecycle_events() {
251 let owner = WorldOwner::new();
252 let mut registry = EventRegistry::new();
253 let mut lifecycle = ComponentLifecycleRegistry::new();
254 lifecycle
255 .register_component(&mut registry, &owner, 0)
256 .expect("register");
257 let mut storage = EventStorage::new(2);
258 lifecycle.ensure_storage_channels(&mut storage, ®istry, &owner);
259 let entity = EntityId::from_parts(0, 1);
260 let component = ComponentId::new(owner.clone(), 0);
261 let added = lifecycle.added_event_id(&owner, 0).expect("added");
262 storage
263 .send(
264 &added,
265 ComponentAdded {
266 entity,
267 component: component.clone(),
268 },
269 )
270 .expect("send added");
271 let removed = lifecycle.removed_event_id(&owner, 0).expect("removed");
272 storage
273 .send(&removed, ComponentRemoved { entity, component })
274 .expect("send removed");
275
276 let empty_registry = EventRegistry::new();
277 lifecycle.ensure_storage_channels(&mut storage, &empty_registry, &owner);
278
279 lifecycle.clear_added_event_for_test(0);
280 lifecycle.clear_removed_event_for_test(0);
281 assert!(lifecycle.added_event_id(&owner, 0).is_none());
282 assert!(lifecycle.removed_event_id(&owner, 0).is_none());
283 lifecycle.clear_added_event_for_test(99);
284 lifecycle.clear_removed_event_for_test(99);
285 }
286}