bevy_scriptum/
systems.rs

1use bevy::{log::tracing, prelude::*};
2use std::{
3    fmt::Display,
4    sync::{Arc, Mutex},
5};
6
7use crate::{
8    Callback, Callbacks, Runtime, ScriptingError,
9    callback::FunctionCallEvent,
10    promise::{Promise, PromiseInner},
11};
12
13use super::components::Script;
14
15/// Reloads scripts when they are modified.
16pub(crate) fn reload_scripts<R: Runtime>(
17    mut commands: Commands,
18    mut ev_asset: EventReader<AssetEvent<R::ScriptAsset>>,
19    mut scripts: Query<(Entity, &mut Script<R::ScriptAsset>)>,
20) {
21    for ev in ev_asset.read() {
22        if let AssetEvent::Modified { id } = ev {
23            for (entity, script) in &mut scripts {
24                if script.script.id() == *id {
25                    commands.entity(entity).remove::<R::ScriptData>();
26                }
27            }
28        }
29    }
30}
31
32/// Processes new scripts. Evaluates them and stores the script data in the entity.
33#[allow(clippy::type_complexity)]
34pub(crate) fn process_new_scripts<R: Runtime>(
35    mut commands: Commands,
36    mut added_scripted_entities: Query<
37        (Entity, &mut Script<R::ScriptAsset>),
38        Without<R::ScriptData>,
39    >,
40    scripting_runtime: ResMut<R>,
41    scripts: Res<Assets<R::ScriptAsset>>,
42    asset_server: Res<AssetServer>,
43) -> Result<(), ScriptingError> {
44    for (entity, script_component) in &mut added_scripted_entities {
45        tracing::trace!("evaulating a new script");
46        if let Some(script) = scripts.get(&script_component.script) {
47            match scripting_runtime.eval(script, entity) {
48                Ok(script_data) => {
49                    commands.entity(entity).insert(script_data);
50                }
51                Err(e) => {
52                    let path = asset_server
53                        .get_path(&script_component.script)
54                        .unwrap_or_default();
55                    tracing::error!("error running script {} {}", path, e);
56                }
57            }
58        }
59    }
60    Ok(())
61}
62
63/// Initializes callbacks. Registers them in the scripting engine.
64pub(crate) fn init_callbacks<R: Runtime>(world: &mut World) -> Result<(), ScriptingError> {
65    let mut callbacks_resource = world
66        .get_resource_mut::<Callbacks<R>>()
67        .ok_or(ScriptingError::NoSettingsResource)?;
68
69    let mut callbacks = callbacks_resource
70        .uninitialized_callbacks
71        .drain(..)
72        .collect::<Vec<Callback<R>>>();
73
74    for callback in callbacks.iter_mut() {
75        if let Ok(mut system) = callback.system.lock() {
76            system.system.initialize(world);
77
78            let mut scripting_runtime = world
79                .get_resource_mut::<R>()
80                .ok_or(ScriptingError::NoRuntimeResource)?;
81
82            tracing::trace!("init_callbacks: registering callback: '{}'", callback.name);
83
84            let callback = callback.clone();
85
86            let result = scripting_runtime.register_fn(
87                callback.name,
88                system.arg_types.clone(),
89                move |context, params| {
90                    let promise = Promise {
91                        inner: Arc::new(Mutex::new(PromiseInner {
92                            callbacks: vec![],
93                            context,
94                        })),
95                    };
96
97                    let mut calls = callback
98                        .calls
99                        .lock()
100                        .expect("Failed to lock callback calls mutex");
101
102                    calls.push(FunctionCallEvent {
103                        promise: promise.clone(),
104                        params,
105                    });
106                    Ok(promise)
107                },
108            );
109            if let Err(e) = result {
110                tracing::error!("error registering function: {}", e);
111            }
112        }
113    }
114
115    let callbacks_resource = world
116        .get_resource_mut::<Callbacks<R>>()
117        .ok_or(ScriptingError::NoSettingsResource)?;
118    callbacks_resource
119        .callbacks
120        .lock()
121        .expect("Failed to lock callbacks mutex")
122        .append(&mut callbacks.clone());
123
124    Ok(())
125}
126
127/// Processes calls. Calls the user-defined callback systems
128pub(crate) fn process_calls<R: Runtime>(world: &mut World) -> Result<(), ScriptingError> {
129    let callbacks_resource = world
130        .get_resource::<Callbacks<R>>()
131        .ok_or(ScriptingError::NoSettingsResource)?;
132
133    let callbacks = callbacks_resource
134        .callbacks
135        .lock()
136        .expect("Failed to lock callbacks mutex")
137        .clone();
138
139    for callback in callbacks.into_iter() {
140        let calls = callback
141            .calls
142            .lock()
143            .expect("Failed to lock callback calls mutex")
144            .drain(..)
145            .collect::<Vec<FunctionCallEvent<R::CallContext, R::Value>>>();
146        for mut call in calls {
147            tracing::trace!("process_calls: calling '{}'", callback.name);
148            let mut system = callback
149                .system
150                .lock()
151                .expect("Failed to lock callback system mutex");
152            let val = system.call(&call, world);
153            let mut runtime = world
154                .get_resource_mut::<R>()
155                .ok_or(ScriptingError::NoRuntimeResource)?;
156
157            let result = call.promise.resolve(runtime.as_mut(), val);
158            match result {
159                Ok(_) => {}
160                Err(e) => {
161                    tracing::error!("error resolving call: {} {}", callback.name, e);
162                }
163            }
164        }
165    }
166    Ok(())
167}
168
169/// Error logging system
170pub fn log_errors<E: Display>(In(res): In<Result<(), E>>) {
171    if let Err(error) = res {
172        tracing::error!("{}", error);
173    }
174}