oxygengine_nodes/
lib.rs

1extern crate oxygengine_core as core;
2
3use core::{
4    app::AppBuilder,
5    ecs::{
6        commands::{SpawnEntity, UniverseCommands},
7        components::Name,
8        hierarchy::{Hierarchy, Parent},
9        pipeline::{PipelineBuilder, PipelineBuilderError},
10        Component, Entity, EntityBuilder, Query, Universe, World,
11    },
12    scripting::{
13        intuicio::{core::prelude::*, data::prelude::*},
14        ScriptFunctionReference, Scripting,
15    },
16};
17use std::borrow::Cow;
18
19const DEFAULT_CAPACITY: usize = 10240;
20
21#[derive(Default, Clone)]
22pub struct ScriptedNodeEntity(AsyncShared<Option<Entity>>);
23
24impl ScriptedNodeEntity {
25    pub fn new(entity: Entity) -> Self {
26        Self(AsyncShared::new(Some(entity)))
27    }
28
29    pub fn find(path: &str, hierarchy: &Hierarchy) -> Self {
30        if let Some(entity) = hierarchy.entity_by_name(path) {
31            Self::new(entity)
32        } else {
33            Self::default()
34        }
35    }
36
37    pub fn find_raw(path: &str, hierarchy: &Hierarchy) -> Option<Entity> {
38        hierarchy.entity_by_name(path)
39    }
40
41    pub fn find_all_of_type<T: 'static>(world: &World) -> Vec<Self> {
42        world
43            .query::<&ScriptedNode>()
44            .iter()
45            .filter_map(move |(entity, node)| {
46                if node.is::<T>() {
47                    Some(Self::new(entity))
48                } else {
49                    None
50                }
51            })
52            .collect()
53    }
54
55    pub fn find_all_of_type_raw<T: 'static>(world: &World) -> Vec<Entity> {
56        world
57            .query::<&ScriptedNode>()
58            .iter()
59            .filter_map(
60                move |(entity, node)| {
61                    if node.is::<T>() {
62                        Some(entity)
63                    } else {
64                        None
65                    }
66                },
67            )
68            .collect()
69    }
70
71    pub fn set(&mut self, entity: Entity) {
72        if let Some(mut data) = self.0.write() {
73            *data = Some(entity);
74        }
75    }
76
77    pub fn get(&self) -> Option<Entity> {
78        self.0.read().and_then(|data| data.as_ref().copied())
79    }
80
81    pub fn is_valid(&self) -> bool {
82        self.0.read().map(|data| data.is_some()).unwrap_or_default()
83    }
84
85    pub fn take(&mut self) -> Option<Entity> {
86        self.0.write().and_then(|mut data| data.take())
87    }
88
89    pub fn node<Q: Query, R>(
90        &self,
91        world: &World,
92        mut f: impl FnMut(&ScriptedNode, Q::Item<'_>) -> R,
93    ) -> Option<R> {
94        let entity = *self.0.read()?.as_ref()?;
95        let mut query = world.query_one::<(&ScriptedNode, Q)>(entity).ok()?;
96        let (node, query) = query.get()?;
97        Some(f(node, query))
98    }
99
100    pub fn node_mut<Q: Query, R>(
101        &self,
102        world: &World,
103        mut f: impl FnMut(&mut ScriptedNode, Q::Item<'_>) -> R,
104    ) -> Option<R> {
105        let entity = *self.0.read()?.as_ref()?;
106        let mut query = world.query_one::<(&mut ScriptedNode, Q)>(entity).ok()?;
107        let (node, query) = query.get()?;
108        Some(f(node, query))
109    }
110
111    pub fn with<T: 'static, Q: Query, R>(
112        &self,
113        world: &World,
114        mut f: impl FnMut(&T, Q::Item<'_>) -> R,
115    ) -> Option<R> {
116        let entity = *self.0.read()?.as_ref()?;
117        let mut query = world.query_one::<(&ScriptedNode, Q)>(entity).ok()?;
118        let (node, query) = query.get()?;
119        let node = node.read::<T>()?;
120        Some(f(&node, query))
121    }
122
123    pub fn with_mut<T: 'static, Q: Query, R>(
124        &self,
125        world: &World,
126        mut f: impl FnMut(&mut T, Q::Item<'_>) -> R,
127    ) -> Option<R> {
128        let entity = *self.0.read()?.as_ref()?;
129        let mut query = world.query_one::<(&mut ScriptedNode, Q)>(entity).ok()?;
130        let (node, query) = query.get()?;
131        let mut node = node.write::<T>()?;
132        Some(f(&mut node, query))
133    }
134}
135
136impl std::fmt::Debug for ScriptedNodeEntity {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        let mut result = f.debug_struct("ScriptedNodeEntity");
139        match self.get() {
140            Some(entity) => result.field("entity", &entity).finish(),
141            None => result.finish_non_exhaustive(),
142        }
143    }
144}
145
146pub enum ScriptedNodesParam {
147    Owned(DynamicManaged),
148    Ref(DynamicManagedRef),
149    RefMut(DynamicManagedRefMut),
150    ScopedRef(DynamicManagedRef, Lifetime),
151    ScopedRefMut(DynamicManagedRefMut, Lifetime),
152}
153
154impl ScriptedNodesParam {
155    pub fn owned<T: 'static>(value: T) -> Self {
156        Self::Owned(DynamicManaged::new(value))
157    }
158
159    pub fn scoped_ref<'a, T: 'static>(value: &'a T) -> Self
160    where
161        Self: 'a,
162    {
163        let lifetime = Lifetime::default();
164        let data = DynamicManagedRef::new(value, lifetime.borrow().unwrap());
165        Self::ScopedRef(data, lifetime)
166    }
167
168    pub fn scoped_ref_mut<'a, T: 'static>(value: &'a mut T) -> Self
169    where
170        Self: 'a,
171    {
172        let lifetime = Lifetime::default();
173        let data = DynamicManagedRefMut::new(value, lifetime.borrow_mut().unwrap());
174        Self::ScopedRefMut(data, lifetime)
175    }
176}
177
178impl From<DynamicManaged> for ScriptedNodesParam {
179    fn from(value: DynamicManaged) -> Self {
180        Self::Owned(value)
181    }
182}
183
184impl From<DynamicManagedRef> for ScriptedNodesParam {
185    fn from(value: DynamicManagedRef) -> Self {
186        Self::Ref(value)
187    }
188}
189
190impl From<DynamicManagedRefMut> for ScriptedNodesParam {
191    fn from(value: DynamicManagedRefMut) -> Self {
192        Self::RefMut(value)
193    }
194}
195
196impl<T: 'static> From<&T> for ScriptedNodesParam {
197    fn from(value: &T) -> Self {
198        Self::scoped_ref(value)
199    }
200}
201
202impl<T: 'static> From<&mut T> for ScriptedNodesParam {
203    fn from(value: &mut T) -> Self {
204        Self::scoped_ref_mut(value)
205    }
206}
207
208pub struct ScriptedNode {
209    pub active: bool,
210    pub object: DynamicManaged,
211}
212
213impl ScriptedNode {
214    pub fn new<T: 'static>(data: T) -> Self {
215        Self::new_raw(DynamicManaged::new(data))
216    }
217
218    pub fn new_raw(object: DynamicManaged) -> Self {
219        Self {
220            active: true,
221            object,
222        }
223    }
224
225    pub fn with_active(mut self, value: bool) -> Self {
226        self.active = value;
227        self
228    }
229
230    pub fn is<T: 'static>(&self) -> bool {
231        self.object.is::<T>()
232    }
233
234    pub fn read<T: 'static>(&self) -> Option<ValueReadAccess<T>> {
235        self.object.read::<T>()
236    }
237
238    pub fn write<T: 'static>(&mut self) -> Option<ValueWriteAccess<T>> {
239        self.object.write::<T>()
240    }
241}
242
243pub struct ScriptedNodeSignal {
244    entity: Option<Entity>,
245    function: ScriptFunctionReference,
246    arguments: Vec<ScriptedNodesParam>,
247    broadcast: bool,
248    bubble: bool,
249    ignore_me: bool,
250}
251
252impl ScriptedNodeSignal {
253    pub fn parse(entity: Option<Entity>, content: &str) -> Result<Self, String> {
254        Ok(Self::new(entity, ScriptFunctionReference::parse(content)?))
255    }
256
257    pub fn new(entity: Option<Entity>, function: ScriptFunctionReference) -> Self {
258        Self {
259            entity,
260            function,
261            arguments: Default::default(),
262            broadcast: false,
263            bubble: false,
264            ignore_me: false,
265        }
266    }
267
268    pub fn arg(mut self, data: impl Into<ScriptedNodesParam>) -> Self {
269        self.arguments.push(data.into());
270        self
271    }
272
273    pub fn broadcast(mut self) -> Self {
274        self.broadcast = true;
275        self
276    }
277
278    pub fn bubble(mut self) -> Self {
279        self.bubble = true;
280        self
281    }
282
283    pub fn ignore_me(mut self) -> Self {
284        self.ignore_me = true;
285        self
286    }
287
288    pub fn dispatch<T: ScriptedNodeComponentPack>(&self, universe: &Universe) {
289        let world = universe.world();
290        let mut nodes = universe.expect_resource_mut::<ScriptedNodes>();
291        let scripting = universe.expect_resource::<Scripting>();
292        let hierarchy = universe.expect_resource::<Hierarchy>();
293
294        if let Some(entity) = self.entity {
295            Self::execute::<T>(
296                entity,
297                &self.function,
298                &self.arguments,
299                self.broadcast,
300                self.bubble,
301                self.ignore_me,
302                &world,
303                &mut nodes,
304                &scripting,
305                &hierarchy,
306            );
307        } else {
308            for (entity, _) in world
309                .query::<()>()
310                .with::<&ScriptedNode>()
311                .without::<&Parent>()
312                .iter()
313            {
314                Self::execute::<T>(
315                    entity,
316                    &self.function,
317                    &self.arguments,
318                    self.broadcast,
319                    self.bubble,
320                    self.ignore_me,
321                    &world,
322                    &mut nodes,
323                    &scripting,
324                    &hierarchy,
325                );
326            }
327        }
328    }
329
330    #[allow(clippy::too_many_arguments)]
331    pub fn execute<T: ScriptedNodeComponentPack>(
332        entity: Entity,
333        function_ref: &ScriptFunctionReference,
334        args: &[ScriptedNodesParam],
335        broadcast: bool,
336        bubble: bool,
337        ignore_me: bool,
338        world: &World,
339        nodes: &mut ScriptedNodes,
340        scripting: &Scripting,
341        hierarchy: &Hierarchy,
342    ) {
343        let token = nodes.context.stack().store();
344        let result = if !ignore_me {
345            if let Ok(mut query) = world.query_one::<(&mut ScriptedNode, T)>(entity) {
346                if let Some((node, pack)) = query.get() {
347                    let mut query = function_ref.query();
348                    if query.struct_query.is_none() {
349                        query.struct_query = Some(StructQuery {
350                            type_hash: Some(*node.object.type_hash()),
351                            ..Default::default()
352                        });
353                    }
354                    if let Some(function) = scripting.registry.find_function(query) {
355                        if let Some(handle) = &function.signature().struct_handle {
356                            if node.object.type_hash() != &handle.type_hash() {
357                                return;
358                            }
359                        }
360                        nodes.context.stack().push(DynamicManaged::new(entity));
361                        let mut compontents_params = vec![];
362                        T::query_param(pack, &mut compontents_params);
363                        for arg in compontents_params.iter().chain(args.iter()).rev() {
364                            match arg {
365                                ScriptedNodesParam::Owned(arg) => {
366                                    nodes.context.stack().push(arg.borrow().unwrap());
367                                }
368                                ScriptedNodesParam::Ref(arg) => {
369                                    nodes.context.stack().push(arg.borrow().unwrap());
370                                }
371                                ScriptedNodesParam::RefMut(arg) => {
372                                    nodes.context.stack().push(arg.borrow_mut().unwrap());
373                                }
374                                ScriptedNodesParam::ScopedRef(arg, _) => {
375                                    nodes.context.stack().push(arg.borrow().unwrap());
376                                }
377                                ScriptedNodesParam::ScopedRefMut(arg, _) => {
378                                    nodes.context.stack().push(arg.borrow_mut().unwrap());
379                                }
380                            }
381                        }
382                        nodes
383                            .context
384                            .stack()
385                            .push(node.object.borrow_mut().unwrap());
386                        Some((function, compontents_params))
387                    } else {
388                        None
389                    }
390                } else {
391                    None
392                }
393            } else {
394                None
395            }
396        } else {
397            None
398        };
399        if let Some((function, _)) = result {
400            function.invoke(&mut nodes.context, &scripting.registry);
401        }
402        nodes.context.stack().restore(token);
403        if broadcast {
404            if let Some(iter) = hierarchy.children(entity) {
405                for entity in iter {
406                    Self::execute::<T>(
407                        entity,
408                        function_ref,
409                        args,
410                        true,
411                        false,
412                        false,
413                        world,
414                        nodes,
415                        scripting,
416                        hierarchy,
417                    );
418                }
419            }
420        }
421        if bubble {
422            if let Some(entity) = hierarchy.parent(entity) {
423                Self::execute::<T>(
424                    entity,
425                    function_ref,
426                    args,
427                    false,
428                    true,
429                    false,
430                    world,
431                    nodes,
432                    scripting,
433                    hierarchy,
434                );
435            }
436        }
437    }
438}
439
440#[derive(Default)]
441pub struct ScriptedNodesSpawns {
442    spawns: Vec<(ScriptedNodesTree, Option<Entity>)>,
443}
444
445impl ScriptedNodesSpawns {
446    pub fn spawn(&mut self, tree: ScriptedNodesTree, parent: Option<Entity>) {
447        self.spawns.push((tree, parent));
448    }
449
450    pub fn spawn_root(&mut self, tree: ScriptedNodesTree) {
451        self.spawn(tree, None)
452    }
453}
454
455#[derive(Default)]
456pub struct ScriptedNodesSignals {
457    #[allow(clippy::type_complexity)]
458    signals: Vec<Box<dyn FnOnce(&Universe) + Send + Sync>>,
459}
460
461impl ScriptedNodesSignals {
462    pub fn signal<T: ScriptedNodeComponentPack>(&mut self, signal: ScriptedNodeSignal) {
463        self.signals
464            .push(Box::new(move |universe| signal.dispatch::<T>(universe)));
465    }
466}
467
468pub struct ScriptedNodes {
469    context: Context,
470}
471
472impl Default for ScriptedNodes {
473    fn default() -> Self {
474        Self::new(DEFAULT_CAPACITY, DEFAULT_CAPACITY, DEFAULT_CAPACITY)
475    }
476}
477
478impl ScriptedNodes {
479    pub fn new(
480        stack_capacity: usize,
481        registers_capacity: usize,
482        heap_page_capacity: usize,
483    ) -> Self {
484        Self {
485            context: Context::new(stack_capacity, registers_capacity, heap_page_capacity),
486        }
487    }
488
489    pub fn maintain(universe: &Universe) {
490        {
491            let mut signals = universe.expect_resource_mut::<ScriptedNodesSignals>();
492            for signal in std::mem::take(&mut signals.signals) {
493                signal(universe);
494            }
495        }
496        {
497            let mut commands = universe.expect_resource_mut::<UniverseCommands>();
498            let mut spawns = universe.expect_resource_mut::<ScriptedNodesSpawns>();
499            for (tree, parent) in std::mem::take(&mut spawns.spawns) {
500                Self::execute_spawn(tree, parent, &mut commands);
501            }
502        }
503    }
504
505    fn execute_spawn(
506        tree: ScriptedNodesTree,
507        parent: Option<Entity>,
508        commands: &mut UniverseCommands,
509    ) {
510        let ScriptedNodesTree {
511            active,
512            object,
513            children,
514            mut components,
515            setup,
516            bind,
517        } = tree;
518        if let Some(entity) = parent {
519            components.add(Parent(entity));
520        }
521        components.add(ScriptedNode { active, object });
522        commands.schedule(
523            SpawnEntity::new(components).on_complete(move |universe, entity| {
524                if let Some(function) = setup {
525                    let mut signals = universe.expect_resource_mut::<ScriptedNodesSignals>();
526                    signals.signal::<()>(ScriptedNodeSignal::new(Some(entity), function));
527                }
528                if let Some(bind) = bind {
529                    (bind)(entity);
530                }
531                let mut commands = universe.expect_resource_mut::<UniverseCommands>();
532                for child in children {
533                    Self::execute_spawn((child)(), Some(entity), &mut commands);
534                }
535            }),
536        );
537    }
538
539    pub fn dispatch<T: ScriptedNodeComponentPack>(
540        &mut self,
541        universe: &Universe,
542        function: ScriptFunctionReference,
543        args: &[ScriptedNodesParam],
544    ) {
545        let world = universe.world();
546        let scripting = universe.expect_resource::<Scripting>();
547        let hierarchy = universe.expect_resource::<Hierarchy>();
548
549        for (entity, _) in world
550            .query::<()>()
551            .with::<&ScriptedNode>()
552            .without::<&Parent>()
553            .iter()
554        {
555            self.execute::<T>(entity, &function, args, &world, &scripting, &hierarchy);
556        }
557    }
558
559    pub fn execute<T: ScriptedNodeComponentPack>(
560        &mut self,
561        entity: Entity,
562        function_ref: &ScriptFunctionReference,
563        args: &[ScriptedNodesParam],
564        world: &World,
565        scripting: &Scripting,
566        hierarchy: &Hierarchy,
567    ) {
568        if let Ok(mut query) = world.query_one::<&ScriptedNode>(entity) {
569            if let Some(node) = query.get() {
570                if !node.active {
571                    return;
572                }
573            }
574        }
575        let token = self.context.stack().store();
576        let result = if let Ok(mut query) = world.query_one::<(&mut ScriptedNode, T)>(entity) {
577            if let Some((node, pack)) = query.get() {
578                let mut query = function_ref.query();
579                if query.struct_query.is_none() {
580                    query.struct_query = Some(StructQuery {
581                        type_hash: Some(*node.object.type_hash()),
582                        ..Default::default()
583                    });
584                }
585                if let Some(function) = scripting.registry.find_function(query) {
586                    if let Some(handle) = &function.signature().struct_handle {
587                        if node.object.type_hash() != &handle.type_hash() {
588                            return;
589                        }
590                    }
591                    self.context.stack().push(DynamicManaged::new(entity));
592                    let mut compontents_params = vec![];
593                    T::query_param(pack, &mut compontents_params);
594                    for arg in compontents_params.iter().chain(args.iter()).rev() {
595                        match arg {
596                            ScriptedNodesParam::Owned(arg) => {
597                                self.context.stack().push(arg.borrow().unwrap());
598                            }
599                            ScriptedNodesParam::Ref(arg) => {
600                                self.context.stack().push(arg.borrow().unwrap());
601                            }
602                            ScriptedNodesParam::RefMut(arg) => {
603                                self.context.stack().push(arg.borrow_mut().unwrap());
604                            }
605                            ScriptedNodesParam::ScopedRef(arg, _) => {
606                                self.context.stack().push(arg.borrow().unwrap());
607                            }
608                            ScriptedNodesParam::ScopedRefMut(arg, _) => {
609                                self.context.stack().push(arg.borrow_mut().unwrap());
610                            }
611                        }
612                    }
613                    self.context.stack().push(node.object.borrow_mut().unwrap());
614                    Some((function, compontents_params))
615                } else {
616                    None
617                }
618            } else {
619                None
620            }
621        } else {
622            None
623        };
624        if let Some((function, _)) = result {
625            function.invoke(&mut self.context, &scripting.registry);
626        }
627        self.context.stack().restore(token);
628        if let Some(iter) = hierarchy.children(entity) {
629            for entity in iter {
630                self.execute::<T>(entity, function_ref, args, world, scripting, hierarchy);
631            }
632        }
633    }
634}
635
636pub struct ScriptedNodesTree {
637    active: bool,
638    object: DynamicManaged,
639    components: EntityBuilder,
640    children: Vec<Box<dyn FnOnce() -> Self + Send + Sync>>,
641    setup: Option<ScriptFunctionReference>,
642    bind: Option<Box<dyn FnOnce(Entity) + Send + Sync>>,
643}
644
645impl ScriptedNodesTree {
646    pub fn empty() -> Self {
647        Self::new(())
648    }
649
650    pub fn new<T: 'static>(data: T) -> Self {
651        Self::new_raw(DynamicManaged::new(data))
652    }
653
654    pub fn new_raw(object: DynamicManaged) -> Self {
655        Self {
656            active: true,
657            object,
658            children: Default::default(),
659            components: Default::default(),
660            setup: None,
661            bind: None,
662        }
663    }
664
665    pub fn name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
666        self.components.add(Name(name.into()));
667        self
668    }
669
670    pub fn inactive(mut self) -> Self {
671        self.active = false;
672        self
673    }
674
675    pub fn component<T: Component>(mut self, component: T) -> Self {
676        self.components.add(component);
677        self
678    }
679
680    pub fn child(mut self, f: impl FnOnce() -> Self + Send + Sync + 'static) -> Self {
681        self.children.push(Box::new(f));
682        self
683    }
684
685    pub fn setup(mut self, function: ScriptFunctionReference) -> Self {
686        self.setup = Some(function);
687        self
688    }
689
690    pub fn bind(mut self, f: impl FnOnce(Entity) + Send + Sync + 'static) -> Self {
691        self.bind = Some(Box::new(f));
692        self
693    }
694}
695
696pub trait ScriptedNodeComponentPack: Query {
697    fn query_param(pack: Self::Item<'_>, list: &mut Vec<ScriptedNodesParam>);
698}
699
700impl ScriptedNodeComponentPack for () {
701    fn query_param(_: (), _: &mut Vec<ScriptedNodesParam>) {}
702}
703
704impl<T: Component> ScriptedNodeComponentPack for &T {
705    fn query_param(pack: Self::Item<'_>, list: &mut Vec<ScriptedNodesParam>) {
706        list.push(ScriptedNodesParam::scoped_ref(pack));
707    }
708}
709
710impl<T: Component> ScriptedNodeComponentPack for &mut T {
711    fn query_param(pack: Self::Item<'_>, list: &mut Vec<ScriptedNodesParam>) {
712        list.push(ScriptedNodesParam::scoped_ref_mut(pack));
713    }
714}
715
716macro_rules! impl_component_tuple {
717    ($($type:ident),+) => {
718        impl<$($type: ScriptedNodeComponentPack),+> ScriptedNodeComponentPack for ($($type,)+) {
719            fn query_param(pack: Self::Item<'_>, list: &mut Vec<ScriptedNodesParam>) {
720                #[allow(non_snake_case)]
721                let ( $($type,)+ ) = pack;
722                $(
723                    $type::query_param($type, list);
724                )+
725            }
726        }
727    };
728}
729
730impl_component_tuple!(A);
731impl_component_tuple!(A, B);
732impl_component_tuple!(A, B, C);
733impl_component_tuple!(A, B, C, D);
734impl_component_tuple!(A, B, C, D, E);
735impl_component_tuple!(A, B, C, D, E, F);
736impl_component_tuple!(A, B, C, D, E, F, G);
737impl_component_tuple!(A, B, C, D, E, F, G, H);
738impl_component_tuple!(A, B, C, D, E, F, G, H, I);
739impl_component_tuple!(A, B, C, D, E, F, G, H, I, J);
740impl_component_tuple!(A, B, C, D, E, F, G, H, I, J, K);
741impl_component_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
742impl_component_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M);
743impl_component_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
744impl_component_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
745
746pub fn bundle_installer<PB>(
747    builder: &mut AppBuilder<PB>,
748    nodes: ScriptedNodes,
749) -> Result<(), PipelineBuilderError>
750where
751    PB: PipelineBuilder,
752{
753    builder.install_resource(nodes);
754    builder.install_resource(ScriptedNodesSpawns::default());
755    builder.install_resource(ScriptedNodesSignals::default());
756    Ok(())
757}