bevy_scriptum/src/runtimes/lua.rs
2025-05-18 16:27:17 +02:00

362 lines
12 KiB
Rust

use bevy::{
asset::Asset,
ecs::{component::Component, entity::Entity, resource::Resource, schedule::ScheduleLabel},
log,
math::Vec3,
reflect::TypePath,
};
use mlua::{
FromLua, Function, IntoLua, IntoLuaMulti, Lua, RegistryKey, UserData, UserDataFields,
UserDataMethods, Variadic,
};
use serde::Deserialize;
use std::sync::{Arc, Mutex};
use crate::{
assets::GetExtensions,
callback::{FromRuntimeValueWithEngine, IntoRuntimeValueWithEngine},
promise::Promise,
FuncArgs, Runtime, ScriptingError, ENTITY_VAR_NAME,
};
type LuaEngine = Arc<Mutex<Lua>>;
#[derive(Clone)]
pub struct LuaValue(pub Arc<RegistryKey>);
impl LuaValue {
fn new<'a, T: IntoLua<'a>>(engine: &'a Lua, value: T) -> Self {
Self(Arc::new(
engine
.create_registry_value(value)
.expect("Error creating a registry key for value"),
))
}
}
#[derive(Resource)]
pub struct LuaRuntime {
engine: LuaEngine,
}
#[derive(Debug, Clone, Copy)]
pub struct BevyEntity(pub Entity);
impl UserData for BevyEntity {}
impl FromLua<'_> for BevyEntity {
fn from_lua(
value: mlua::prelude::LuaValue<'_>,
_lua: &'_ Lua,
) -> mlua::prelude::LuaResult<Self> {
match value {
mlua::Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => panic!("got {:?} instead of BevyEntity", value),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BevyVec3(pub Vec3);
impl BevyVec3 {
pub fn new(x: f32, y: f32, z: f32) -> Self {
BevyVec3(Vec3 { x, y, z })
}
}
impl UserData for BevyVec3 {}
impl FromLua<'_> for BevyVec3 {
fn from_lua(
value: mlua::prelude::LuaValue<'_>,
_lua: &'_ Lua,
) -> mlua::prelude::LuaResult<Self> {
match value {
mlua::Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => panic!("got {:?} instead of BevyVec3", value),
}
}
}
impl Default for LuaRuntime {
fn default() -> Self {
let engine = LuaEngine::default();
{
let engine = engine.lock().expect("Failed to lock engine");
engine
.register_userdata_type::<BevyEntity>(|typ| {
typ.add_field_method_get("index", |_, entity| Ok(entity.0.index()));
})
.expect("Failed to register BevyEntity userdata type");
engine
.register_userdata_type::<Promise<(), LuaValue>>(|typ| {
typ.add_method_mut("and_then", |engine, promise, callback: Function| {
Ok(Promise::then(promise, LuaValue::new(engine, callback)))
});
})
.expect("Failed to register Promise userdata type");
engine
.register_userdata_type::<BevyVec3>(|typ| {
typ.add_field_method_get("x", |_engine, vec| Ok(vec.0.x));
typ.add_field_method_get("y", |_engine, vec| Ok(vec.0.y));
typ.add_field_method_get("z", |_engine, vec| Ok(vec.0.z));
})
.expect("Failed to register BevyVec3 userdata type");
let vec3_constructor = engine
.create_function(|_, (x, y, z)| Ok(BevyVec3(Vec3::new(x, y, z))))
.expect("Failed to create Vec3 constructor");
engine
.globals()
.set("Vec3", vec3_constructor)
.expect("Failed to set Vec3 global");
}
Self { engine }
}
}
#[derive(ScheduleLabel, Clone, PartialEq, Eq, Debug, Hash, Default)]
pub struct LuaSchedule;
#[derive(Asset, Debug, Deserialize, TypePath)]
pub struct LuaScript(pub String);
impl GetExtensions for LuaScript {
fn extensions() -> &'static [&'static str] {
&["lua"]
}
}
impl From<String> for LuaScript {
fn from(value: String) -> Self {
Self(value)
}
}
#[derive(Component)]
pub struct LuaScriptData;
impl Runtime for LuaRuntime {
type Schedule = LuaSchedule;
type ScriptAsset = LuaScript;
type ScriptData = LuaScriptData;
type CallContext = ();
type Value = LuaValue;
type RawEngine = Lua;
fn eval(
&self,
script: &Self::ScriptAsset,
entity: bevy::prelude::Entity,
) -> Result<Self::ScriptData, crate::ScriptingError> {
self.with_engine(|engine| {
engine
.globals()
.set(ENTITY_VAR_NAME, BevyEntity(entity))
.expect("Error setting entity global variable");
let result = engine.load(&script.0).exec();
engine
.globals()
.set(ENTITY_VAR_NAME, mlua::Value::Nil)
.expect("Error clearing entity global variable");
result
})
.map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?;
Ok(LuaScriptData)
}
fn register_fn(
&mut self,
name: String,
_arg_types: Vec<std::any::TypeId>,
f: impl Fn(
Self::CallContext,
Vec<Self::Value>,
) -> Result<
crate::promise::Promise<Self::CallContext, Self::Value>,
crate::ScriptingError,
> + Send
+ Sync
+ 'static,
) -> Result<(), crate::ScriptingError> {
self.with_engine(|engine| {
let func = engine
.create_function(move |engine, args: Variadic<mlua::Value>| {
let args = { args.into_iter().map(|x| LuaValue::new(engine, x)).collect() };
let result = f((), args).unwrap();
Ok(result)
})
.unwrap();
engine
.globals()
.set(name, func)
.expect("Error registering function in global lua scope");
});
Ok(())
}
fn call_fn(
&self,
name: &str,
_script_data: &mut Self::ScriptData,
entity: bevy::prelude::Entity,
args: impl for<'a> FuncArgs<'a, Self::Value, Self>,
) -> Result<Self::Value, crate::ScriptingError> {
self.with_engine(|engine| {
engine
.globals()
.set(ENTITY_VAR_NAME, BevyEntity(entity))
.expect("Error setting entity global variable");
let func = engine
.globals()
.get::<_, Function>(name)
.map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?;
let args = args
.parse(engine)
.into_iter()
.map(|a| engine.registry_value::<mlua::Value>(&a.0).unwrap());
let result = func
.call::<_, mlua::Value>(Variadic::from_iter(args))
.map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?;
engine
.globals()
.set(ENTITY_VAR_NAME, mlua::Value::Nil)
.expect("Error clearing entity global variable");
Ok(LuaValue::new(engine, result))
})
}
fn call_fn_from_value(
&self,
value: &Self::Value,
_context: &Self::CallContext,
args: Vec<Self::Value>,
) -> Result<Self::Value, crate::ScriptingError> {
self.with_engine(|engine| {
let val = engine
.registry_value::<Function>(&value.0)
.map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?;
let args = args
.into_iter()
.map(|a| engine.registry_value::<mlua::Value>(&a.0).unwrap());
let result = val
.call::<_, mlua::Value>(Variadic::from_iter(args))
.map_err(|e| ScriptingError::RuntimeError(Box::new(e)))?;
Ok(LuaValue::new(engine, result))
})
}
fn with_engine_mut<T>(&mut self, f: impl FnOnce(&mut Self::RawEngine) -> T) -> T {
let mut engine = self.engine.lock().unwrap();
f(&mut engine)
}
fn with_engine<T>(&self, f: impl FnOnce(&Self::RawEngine) -> T) -> T {
let engine = self.engine.lock().unwrap();
f(&engine)
}
fn with_engine_thread_mut<T: Send + 'static>(
&mut self,
f: impl FnOnce(&mut Self::RawEngine) -> T + Send + 'static,
) -> T {
log::warn!("runtime can be used on current thread, wil run on current thread");
self.with_engine_mut(f)
}
fn with_engine_thread<T: Send + 'static>(
&self,
f: impl FnOnce(&Self::RawEngine) -> T + Send + 'static,
) -> T {
log::warn!("runtime can be used on current thread, wil run on current thread");
self.with_engine(f)
}
fn is_current_thread() -> bool {
true
}
}
impl<'a, T: IntoLuaMulti<'a>> IntoRuntimeValueWithEngine<'a, T, LuaRuntime> for T {
fn into_runtime_value_with_engine(value: T, engine: &'a Lua) -> LuaValue {
let mut iter = value.into_lua_multi(engine).unwrap().into_iter();
if iter.len() > 1 {
unimplemented!("Returning multiple values from function");
}
LuaValue(Arc::new(engine.create_registry_value(iter.next()).unwrap()))
}
}
impl<'a, T: FromLua<'a>> FromRuntimeValueWithEngine<'a, LuaRuntime> for T {
fn from_runtime_value_with_engine(value: LuaValue, engine: &'a Lua) -> Self {
engine.registry_value(&value.0).unwrap()
}
}
impl FuncArgs<'_, LuaValue, LuaRuntime> for () {
fn parse(self, _engine: &Lua) -> Vec<LuaValue> {
Vec::new()
}
}
impl<'a, T: IntoLua<'a>> FuncArgs<'a, LuaValue, LuaRuntime> for Vec<T> {
fn parse(self, engine: &'a Lua) -> Vec<LuaValue> {
self.into_iter().map(|x| LuaValue::new(engine, x)).collect()
}
}
impl UserData for Promise<(), LuaValue> {}
pub mod prelude {
pub use super::{BevyEntity, BevyVec3, LuaRuntime, LuaScript, LuaScriptData};
}
macro_rules! impl_tuple {
($($idx:tt $t:tt),+) => {
impl<'a, $($t: IntoLua<'a>,)+> FuncArgs<'a, LuaValue, LuaRuntime>
for ($($t,)+)
{
fn parse(self, engine: &'a Lua) -> Vec<LuaValue> {
vec![
$(LuaValue::new(engine, self.$idx), )+
]
}
}
};
}
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X, 24 Y, 25 Z);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X, 24 Y);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W, 23 X);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V, 22 W);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U, 21 V);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T, 20 U);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S, 19 T);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R, 18 S);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q, 17 R);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P, 16 Q);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O, 15 P);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N, 14 O);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M, 13 N);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L, 12 M);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K, 11 L);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J, 10 K);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I, 9 J);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H, 8 I);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G, 7 H);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F, 6 G);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E, 5 F);
impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
impl_tuple!(0 A, 1 B, 2 C, 3 D);
impl_tuple!(0 A, 1 B, 2 C);
impl_tuple!(0 A, 1 B);
impl_tuple!(0 A);