1use crate::simple_world::{EntityId, SimpleWorld};
2use alloc::vec::Vec;
3use soroban_sdk::{Bytes, Env, Symbol};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum ComponentEventKind {
8 Added,
9 Removed,
10}
11
12#[derive(Debug, Clone)]
14pub struct ComponentEvent {
15 pub entity_id: EntityId,
16 pub component_type: Symbol,
17 pub kind: ComponentEventKind,
18}
19
20pub type ObserverFn = fn(event: &ComponentEvent, world: &SimpleWorld, env: &Env);
25
26pub struct ObserverRegistry {
45 observers: Vec<(Symbol, ComponentEventKind, ObserverFn)>,
46}
47
48impl ObserverRegistry {
49 pub fn new() -> Self {
51 Self {
52 observers: Vec::new(),
53 }
54 }
55
56 pub fn on_add(&mut self, component_type: Symbol, observer: ObserverFn) {
58 self.observers
59 .push((component_type, ComponentEventKind::Added, observer));
60 }
61
62 pub fn on_remove(&mut self, component_type: Symbol, observer: ObserverFn) {
64 self.observers
65 .push((component_type, ComponentEventKind::Removed, observer));
66 }
67
68 pub fn fire(&self, event: &ComponentEvent, world: &SimpleWorld, env: &Env) {
70 for (ctype, kind, observer) in &self.observers {
71 if ctype == &event.component_type && kind == &event.kind {
72 observer(event, world, env);
73 }
74 }
75 }
76
77 pub fn observer_count(&self) -> usize {
79 self.observers.len()
80 }
81}
82
83impl Default for ObserverRegistry {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89pub struct ObservedWorld {
111 world: SimpleWorld,
112 observers: ObserverRegistry,
113}
114
115impl ObservedWorld {
116 pub fn new(world: SimpleWorld) -> Self {
118 Self {
119 world,
120 observers: ObserverRegistry::new(),
121 }
122 }
123
124 pub fn with_observers(world: SimpleWorld, observers: ObserverRegistry) -> Self {
126 Self { world, observers }
127 }
128
129 pub fn world(&self) -> &SimpleWorld {
131 &self.world
132 }
133
134 pub fn world_mut(&mut self) -> &mut SimpleWorld {
136 &mut self.world
137 }
138
139 pub fn observers(&self) -> &ObserverRegistry {
141 &self.observers
142 }
143
144 pub fn observers_mut(&mut self) -> &mut ObserverRegistry {
146 &mut self.observers
147 }
148
149 pub fn into_inner(self) -> SimpleWorld {
151 self.world
152 }
153
154 pub fn spawn_entity(&mut self) -> EntityId {
156 self.world.spawn_entity()
157 }
158
159 pub fn add_component(
161 &mut self,
162 entity_id: EntityId,
163 component_type: Symbol,
164 data: Bytes,
165 env: &Env,
166 ) {
167 self.world
168 .add_component(entity_id, component_type.clone(), data);
169 let event = ComponentEvent {
170 entity_id,
171 component_type,
172 kind: ComponentEventKind::Added,
173 };
174 self.observers.fire(&event, &self.world, env);
175 }
176
177 pub fn remove_component(
179 &mut self,
180 entity_id: EntityId,
181 component_type: &Symbol,
182 env: &Env,
183 ) -> bool {
184 let removed = self.world.remove_component(entity_id, component_type);
185 if removed {
186 let event = ComponentEvent {
187 entity_id,
188 component_type: component_type.clone(),
189 kind: ComponentEventKind::Removed,
190 };
191 self.observers.fire(&event, &self.world, env);
192 }
193 removed
194 }
195
196 pub fn get_component(&self, entity_id: EntityId, component_type: &Symbol) -> Option<Bytes> {
198 self.world.get_component(entity_id, component_type)
199 }
200
201 pub fn has_component(&self, entity_id: EntityId, component_type: &Symbol) -> bool {
203 self.world.has_component(entity_id, component_type)
204 }
205
206 pub fn despawn_entity(&mut self, entity_id: EntityId, env: &Env) {
208 if let Some(types) = self.world.entity_components.get(entity_id) {
209 for i in 0..types.len() {
210 if let Some(t) = types.get(i) {
211 let event = ComponentEvent {
212 entity_id,
213 component_type: t,
214 kind: ComponentEventKind::Removed,
215 };
216 self.observers.fire(&event, &self.world, env);
217 }
218 }
219 }
220 self.world.despawn_entity(entity_id);
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 use core::sync::atomic::{AtomicU32, Ordering};
228 use soroban_sdk::symbol_short;
229
230 static ADD_COUNT: AtomicU32 = AtomicU32::new(0);
231 static REMOVE_COUNT: AtomicU32 = AtomicU32::new(0);
232
233 fn counting_add_observer(_event: &ComponentEvent, _world: &SimpleWorld, _env: &Env) {
234 ADD_COUNT.fetch_add(1, Ordering::Relaxed);
235 }
236
237 fn counting_remove_observer(_event: &ComponentEvent, _world: &SimpleWorld, _env: &Env) {
238 REMOVE_COUNT.fetch_add(1, Ordering::Relaxed);
239 }
240
241 fn noop_observer(_event: &ComponentEvent, _world: &SimpleWorld, _env: &Env) {}
242
243 #[test]
244 fn test_registry_new() {
245 let registry = ObserverRegistry::new();
246 assert_eq!(registry.observer_count(), 0);
247 }
248
249 #[test]
250 fn test_registry_register() {
251 let _env = Env::default();
252 let mut registry = ObserverRegistry::new();
253 registry.on_add(symbol_short!("pos"), noop_observer);
254 registry.on_remove(symbol_short!("pos"), noop_observer);
255 assert_eq!(registry.observer_count(), 2);
256 }
257
258 #[test]
259 fn test_observer_fires_on_add() {
260 let env = Env::default();
261 ADD_COUNT.store(0, Ordering::Relaxed);
262
263 let world = SimpleWorld::new(&env);
264 let mut observed = ObservedWorld::new(world);
265 observed
266 .observers_mut()
267 .on_add(symbol_short!("pos"), counting_add_observer);
268
269 let e1 = observed.spawn_entity();
270 let data = Bytes::from_array(&env, &[1, 2, 3]);
271 observed.add_component(e1, symbol_short!("pos"), data, &env);
272
273 assert_eq!(ADD_COUNT.load(Ordering::Relaxed), 1);
274 assert!(observed.has_component(e1, &symbol_short!("pos")));
275 }
276
277 #[test]
278 fn test_observer_fires_on_remove() {
279 let env = Env::default();
280 REMOVE_COUNT.store(0, Ordering::Relaxed);
281
282 let world = SimpleWorld::new(&env);
283 let mut observed = ObservedWorld::new(world);
284 observed
285 .observers_mut()
286 .on_remove(symbol_short!("pos"), counting_remove_observer);
287
288 let e1 = observed.spawn_entity();
289 let data = Bytes::from_array(&env, &[1]);
290 observed.add_component(e1, symbol_short!("pos"), data, &env);
291
292 assert!(observed.remove_component(e1, &symbol_short!("pos"), &env));
293 assert_eq!(REMOVE_COUNT.load(Ordering::Relaxed), 1);
294 }
295
296 #[test]
297 fn test_observer_not_fired_for_wrong_type() {
298 let env = Env::default();
299 ADD_COUNT.store(0, Ordering::Relaxed);
300
301 let world = SimpleWorld::new(&env);
302 let mut observed = ObservedWorld::new(world);
303 observed
304 .observers_mut()
305 .on_add(symbol_short!("vel"), counting_add_observer);
306
307 let e1 = observed.spawn_entity();
308 let data = Bytes::from_array(&env, &[1]);
309 observed.add_component(e1, symbol_short!("pos"), data, &env);
311
312 assert_eq!(ADD_COUNT.load(Ordering::Relaxed), 0);
313 }
314
315 #[test]
316 fn test_observed_world_despawn() {
317 let env = Env::default();
318 let before = REMOVE_COUNT.load(Ordering::Relaxed);
319
320 let world = SimpleWorld::new(&env);
321 let mut observed = ObservedWorld::new(world);
322 observed
323 .observers_mut()
324 .on_remove(symbol_short!("a"), counting_remove_observer);
325 observed
326 .observers_mut()
327 .on_remove(symbol_short!("b"), counting_remove_observer);
328
329 let e1 = observed.spawn_entity();
330 let data = Bytes::from_array(&env, &[1]);
331 observed.add_component(e1, symbol_short!("a"), data.clone(), &env);
332 observed.add_component(e1, symbol_short!("b"), data, &env);
333
334 observed.despawn_entity(e1, &env);
335 let after = REMOVE_COUNT.load(Ordering::Relaxed);
337 assert_eq!(after - before, 2);
338 }
339
340 #[test]
341 fn test_observed_world_into_inner() {
342 let env = Env::default();
343 let world = SimpleWorld::new(&env);
344 let mut observed = ObservedWorld::new(world);
345
346 let e1 = observed.spawn_entity();
347 let data = Bytes::from_array(&env, &[1]);
348 observed.add_component(e1, symbol_short!("test"), data, &env);
349
350 let inner = observed.into_inner();
351 assert!(inner.has_component(e1, &symbol_short!("test")));
352 }
353
354 #[test]
355 fn test_with_observers_constructor() {
356 let env = Env::default();
357 let world = SimpleWorld::new(&env);
358
359 let mut observers = ObserverRegistry::new();
360 observers.on_add(symbol_short!("pos"), noop_observer);
361
362 let observed = ObservedWorld::with_observers(world, observers);
363 assert_eq!(observed.observers().observer_count(), 1);
364 }
365}