intuicio_framework_ecs/
actor.rs

1use crate::{
2    archetype::ArchetypeColumnInfo,
3    bundle::{Bundle, BundleColumns},
4    entity::Entity,
5    world::{Relation, World, WorldError},
6    Component, ComponentRef, ComponentRefMut,
7};
8use intuicio_core::{context::Context, function::FunctionHandle, registry::Registry};
9use intuicio_data::{
10    data_stack::DataStackPack,
11    lifetime::Lifetime,
12    managed::{DynamicManaged, DynamicManagedRef},
13};
14use std::collections::HashMap;
15
16pub use intuicio_core::function::Function as ActorMessageFunction;
17
18pub struct ActorChild;
19pub struct ActorParent;
20
21#[derive(Debug, Default, Clone)]
22pub struct ActorMessageListeners(HashMap<String, FunctionHandle>);
23
24#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct Actor(Entity);
26
27impl Actor {
28    pub fn spawn(
29        world: &mut World,
30        bundle: impl Bundle + Send + Sync + 'static,
31    ) -> Result<Self, WorldError> {
32        let entity = world.spawn((
33            ActorMessageListeners::default(),
34            Relation::<ActorChild>::default(),
35            Relation::<ActorParent>::default(),
36        ))?;
37        world.insert(entity, bundle)?;
38        Ok(Self(entity))
39    }
40
41    pub fn despawn(self, world: &mut World) -> Result<(), WorldError> {
42        world.despawn(self.0)
43    }
44
45    pub fn insert(
46        self,
47        world: &mut World,
48        bundle: impl Bundle + Send + Sync + 'static,
49    ) -> Result<(), WorldError> {
50        world.insert(self.0, bundle)
51    }
52
53    pub fn remove<T: BundleColumns>(self, world: &mut World) -> Result<(), WorldError> {
54        world.remove::<T>(self.0)
55    }
56
57    pub fn remove_raw(
58        self,
59        world: &mut World,
60        columns: Vec<ArchetypeColumnInfo>,
61    ) -> Result<(), WorldError> {
62        world.remove_raw(self.0, columns)
63    }
64
65    pub fn exists(self, world: &World) -> bool {
66        world.has_entity(self.0)
67    }
68
69    pub fn entity(self) -> Entity {
70        self.0
71    }
72
73    pub fn component<const LOCKING: bool, T: Component>(
74        self,
75        world: &World,
76    ) -> Result<ComponentRef<LOCKING, T>, WorldError> {
77        world.component::<LOCKING, T>(self.0)
78    }
79
80    pub fn component_mut<const LOCKING: bool, T: Component>(
81        self,
82        world: &World,
83    ) -> Result<ComponentRefMut<LOCKING, T>, WorldError> {
84        world.component_mut::<LOCKING, T>(self.0)
85    }
86
87    pub fn add_child<const LOCKING: bool>(
88        self,
89        world: &mut World,
90        other: Self,
91    ) -> Result<(), WorldError> {
92        world.relate::<LOCKING, _>(ActorChild, self.0, other.0)?;
93        world.relate::<LOCKING, _>(ActorParent, other.0, self.0)?;
94        Ok(())
95    }
96
97    pub fn remove_child<const LOCKING: bool>(
98        self,
99        world: &mut World,
100        other: Self,
101    ) -> Result<(), WorldError> {
102        world.unrelate::<LOCKING, ActorChild>(self.0, other.0)?;
103        world.unrelate::<LOCKING, ActorParent>(other.0, self.0)?;
104        Ok(())
105    }
106
107    pub fn children<const LOCKING: bool>(self, world: &World) -> impl Iterator<Item = Self> + '_ {
108        world
109            .relations_outgoing::<LOCKING, ActorChild>(self.0)
110            .map(|(_, _, entity)| Self(entity))
111    }
112
113    pub fn parents<const LOCKING: bool>(self, world: &World) -> impl Iterator<Item = Self> + '_ {
114        world
115            .relations_outgoing::<LOCKING, ActorParent>(self.0)
116            .map(|(_, _, entity)| Self(entity))
117    }
118
119    pub fn register_message_listener<const LOCKING: bool>(
120        self,
121        world: &World,
122        id: impl ToString,
123        function: ActorMessageFunction,
124    ) -> Result<(), WorldError> {
125        let mut listeners = self.component_mut::<LOCKING, ActorMessageListeners>(world)?;
126        listeners.0.insert(id.to_string(), function.into_handle());
127        Ok(())
128    }
129
130    pub fn unregister_message_listener<const LOCKING: bool>(
131        self,
132        world: &World,
133        id: &str,
134    ) -> Result<(), WorldError> {
135        let mut listeners = self.component_mut::<LOCKING, ActorMessageListeners>(world)?;
136        listeners.0.remove(id);
137        Ok(())
138    }
139
140    pub fn message_listener<const LOCKING: bool>(
141        self,
142        world: &World,
143        id: &str,
144    ) -> Result<Option<FunctionHandle>, WorldError> {
145        let listeners = self.component::<LOCKING, ActorMessageListeners>(world)?;
146        Ok(listeners.0.get(id).cloned())
147    }
148
149    pub fn invoke_message<const LOCKING: bool>(
150        self,
151        world: &World,
152        id: &str,
153        context: &mut Context,
154        registry: &Registry,
155    ) -> Result<(), WorldError> {
156        let listeners = self.component::<LOCKING, ActorMessageListeners>(world)?;
157        if let Some(function) = listeners.0.get(id).cloned() {
158            context.stack().push(DynamicManaged::new(self).unwrap());
159            let lifetime = Lifetime::default();
160            let value = DynamicManagedRef::new(world, lifetime.borrow().unwrap());
161            context.stack().push(value);
162            function.invoke(context, registry);
163        }
164        Ok(())
165    }
166
167    pub fn dispatch_message<const LOCKING: bool, O: DataStackPack, I: DataStackPack>(
168        self,
169        world: &World,
170        id: &str,
171        context: &mut Context,
172        registry: &Registry,
173        inputs: I,
174    ) -> Result<Option<O>, WorldError> {
175        let listeners = self.component::<LOCKING, ActorMessageListeners>(world)?;
176        if let Some(function) = listeners.0.get(id).cloned() {
177            inputs.stack_push_reversed(context.stack());
178            context.stack().push(DynamicManaged::new(self).unwrap());
179            let lifetime = Lifetime::default();
180            let value = DynamicManagedRef::new(world, lifetime.borrow().unwrap());
181            context.stack().push(value);
182            function.invoke(context, registry);
183            Ok(Some(O::stack_pop(context.stack())))
184        } else {
185            Ok(None)
186        }
187    }
188
189    pub fn dispatch_message_hierarchy<const LOCKING: bool, I: DataStackPack + Clone>(
190        self,
191        world: &World,
192        id: &str,
193        context: &mut Context,
194        registry: &Registry,
195        inputs: I,
196    ) -> Result<(), WorldError> {
197        self.dispatch_message::<LOCKING, (), I>(world, id, context, registry, inputs.clone())?;
198        for child in self.children::<LOCKING>(world) {
199            child.dispatch_message_hierarchy::<LOCKING, I>(
200                world,
201                id,
202                context,
203                registry,
204                inputs.clone(),
205            )?;
206        }
207        Ok(())
208    }
209}
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214    use intuicio_core::prelude::*;
215    use intuicio_derive::intuicio_function;
216
217    fn is_async<T: Send + Sync>() {}
218
219    struct Attack(usize);
220
221    struct Lives(usize);
222
223    #[derive(Debug, Default, Clone)]
224    struct Counter {
225        odd: usize,
226        even: usize,
227    }
228
229    #[intuicio_function(transformer = "DynamicManagedValueTransformer")]
230    fn attack(world: &World, this: Actor, other: Actor) {
231        let this_attack = this.component::<true, Attack>(world).unwrap();
232        let mut other_lives = other.component_mut::<true, Lives>(world).unwrap();
233        other_lives.0 = other_lives.0.saturating_sub(this_attack.0);
234    }
235
236    #[test]
237    fn test_actor() {
238        is_async::<Actor>();
239
240        let registry = Registry::default()
241            .with_basic_types()
242            .with_type(NativeStructBuilder::new_uninitialized::<DynamicManaged>().build())
243            .with_type(NativeStructBuilder::new_uninitialized::<DynamicManagedRef>().build());
244        let mut context = Context::new(10240, 10240);
245        let mut world = World::default();
246
247        let player = Actor::spawn(&mut world, ("player".to_owned(), Attack(2), Lives(1))).unwrap();
248        player
249            .register_message_listener::<true>(&world, "attack", attack::define_function(&registry))
250            .unwrap();
251        assert!(player.exists(&world));
252        assert_eq!(player.component::<true, Attack>(&world).unwrap().0, 2);
253        assert_eq!(player.component::<true, Lives>(&world).unwrap().0, 1);
254
255        let enemy = Actor::spawn(&mut world, ("enemy".to_owned(), Attack(1), Lives(2))).unwrap();
256        assert!(enemy.exists(&world));
257        assert_eq!(enemy.component::<true, Attack>(&world).unwrap().0, 1);
258        assert_eq!(enemy.component::<true, Lives>(&world).unwrap().0, 2);
259
260        player
261            .dispatch_message::<true, (), _>(
262                &world,
263                "attack",
264                &mut context,
265                &registry,
266                (DynamicManaged::new(enemy).unwrap(),),
267            )
268            .unwrap();
269        assert_eq!(enemy.component::<true, Lives>(&world).unwrap().0, 0);
270    }
271
272    #[test]
273    fn test_actor_singleton() {
274        let mut world = World::default();
275        let resources = Actor::spawn(&mut world, (Counter::default(),)).unwrap();
276
277        for index in 0..5usize {
278            world.spawn((index,)).unwrap();
279        }
280
281        let mut counter = resources.component_mut::<true, Counter>(&world).unwrap();
282        for value in world.query::<true, &usize>() {
283            if *value % 2 == 0 {
284                counter.even += 1;
285            } else {
286                counter.odd += 1;
287            }
288        }
289        assert_eq!(counter.odd, 2);
290        assert_eq!(counter.even, 3);
291    }
292}