bevy_scriptum/src/systems.rs
Jarosław Konik 53479f94b5
Some checks are pending
Book / test (push) Waiting to run
Deploy book / deploy (push) Waiting to run
Rust / build (push) Waiting to run
Ruby support (#19)
Add support for Ruby language
2025-05-27 19:19:52 +02:00

174 lines
5.5 KiB
Rust

use bevy::{log::tracing, prelude::*};
use std::{
fmt::Display,
sync::{Arc, Mutex},
};
use crate::{
Callback, Callbacks, Runtime, ScriptingError,
callback::FunctionCallEvent,
promise::{Promise, PromiseInner},
};
use super::components::Script;
/// Reloads scripts when they are modified.
pub(crate) fn reload_scripts<R: Runtime>(
mut commands: Commands,
mut ev_asset: EventReader<AssetEvent<R::ScriptAsset>>,
mut scripts: Query<(Entity, &mut Script<R::ScriptAsset>)>,
) {
for ev in ev_asset.read() {
if let AssetEvent::Modified { id } = ev {
for (entity, script) in &mut scripts {
if script.script.id() == *id {
commands.entity(entity).remove::<R::ScriptData>();
}
}
}
}
}
/// Processes new scripts. Evaluates them and stores the script data in the entity.
#[allow(clippy::type_complexity)]
pub(crate) fn process_new_scripts<R: Runtime>(
mut commands: Commands,
mut added_scripted_entities: Query<
(Entity, &mut Script<R::ScriptAsset>),
Without<R::ScriptData>,
>,
scripting_runtime: ResMut<R>,
scripts: Res<Assets<R::ScriptAsset>>,
asset_server: Res<AssetServer>,
) -> Result<(), ScriptingError> {
for (entity, script_component) in &mut added_scripted_entities {
tracing::trace!("evaulating a new script");
if let Some(script) = scripts.get(&script_component.script) {
match scripting_runtime.eval(script, entity) {
Ok(script_data) => {
commands.entity(entity).insert(script_data);
}
Err(e) => {
let path = asset_server
.get_path(&script_component.script)
.unwrap_or_default();
tracing::error!("error running script {} {}", path, e);
}
}
}
}
Ok(())
}
/// Initializes callbacks. Registers them in the scripting engine.
pub(crate) fn init_callbacks<R: Runtime>(world: &mut World) -> Result<(), ScriptingError> {
let mut callbacks_resource = world
.get_resource_mut::<Callbacks<R>>()
.ok_or(ScriptingError::NoSettingsResource)?;
let mut callbacks = callbacks_resource
.uninitialized_callbacks
.drain(..)
.collect::<Vec<Callback<R>>>();
for callback in callbacks.iter_mut() {
if let Ok(mut system) = callback.system.lock() {
system.system.initialize(world);
let mut scripting_runtime = world
.get_resource_mut::<R>()
.ok_or(ScriptingError::NoRuntimeResource)?;
tracing::trace!("init_callbacks: registering callback: '{}'", callback.name);
let callback = callback.clone();
let result = scripting_runtime.register_fn(
callback.name,
system.arg_types.clone(),
move |context, params| {
let promise = Promise {
inner: Arc::new(Mutex::new(PromiseInner {
callbacks: vec![],
context,
})),
};
let mut calls = callback
.calls
.lock()
.expect("Failed to lock callback calls mutex");
calls.push(FunctionCallEvent {
promise: promise.clone(),
params,
});
Ok(promise)
},
);
if let Err(e) = result {
tracing::error!("error registering function: {}", e);
}
}
}
let callbacks_resource = world
.get_resource_mut::<Callbacks<R>>()
.ok_or(ScriptingError::NoSettingsResource)?;
callbacks_resource
.callbacks
.lock()
.expect("Failed to lock callbacks mutex")
.append(&mut callbacks.clone());
Ok(())
}
/// Processes calls. Calls the user-defined callback systems
pub(crate) fn process_calls<R: Runtime>(world: &mut World) -> Result<(), ScriptingError> {
let callbacks_resource = world
.get_resource::<Callbacks<R>>()
.ok_or(ScriptingError::NoSettingsResource)?;
let callbacks = callbacks_resource
.callbacks
.lock()
.expect("Failed to lock callbacks mutex")
.clone();
for callback in callbacks.into_iter() {
let calls = callback
.calls
.lock()
.expect("Failed to lock callback calls mutex")
.drain(..)
.collect::<Vec<FunctionCallEvent<R::CallContext, R::Value>>>();
for mut call in calls {
tracing::trace!("process_calls: calling '{}'", callback.name);
let mut system = callback
.system
.lock()
.expect("Failed to lock callback system mutex");
let val = system.call(&call, world);
let mut runtime = world
.get_resource_mut::<R>()
.ok_or(ScriptingError::NoRuntimeResource)?;
let result = call.promise.resolve(runtime.as_mut(), val);
match result {
Ok(_) => {}
Err(e) => {
tracing::error!("error resolving call: {} {}", callback.name, e);
}
}
}
}
Ok(())
}
/// Error logging system
pub fn log_errors<E: Display>(In(res): In<Result<(), E>>) {
if let Err(error) = res {
tracing::error!("{}", error);
}
}