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
15pub(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#[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
63pub(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
127pub(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
169pub fn log_errors<E: Display>(In(res): In<Result<(), E>>) {
171 if let Err(error) = res {
172 tracing::error!("{}", error);
173 }
174}