Fix #19: Lua crashes when spawning entities in optimized builds #27

Merged
SeanOMik merged 4 commits from fix/scripting-switch-to-mlua into main 2024-09-30 01:39:40 +00:00
27 changed files with 922 additions and 1663 deletions
Showing only changes of commit 8fb686b7fe - Show all commits

18
.vscode/launch.json vendored
View File

@ -4,6 +4,24 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug lyra lua-scripting",
"cargo": {
"args": [
"build",
"--manifest-path", "${workspaceFolder}/examples/lua-scripting/Cargo.toml"
//"--bin=testbed",
],
"filter": {
"name": "lua-scripting",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}/examples/lua-scripting"
},
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",

857
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,21 +2,27 @@
---@param val userdata ---@param val userdata
---@return string ---@return string
function udname(val) function udname(val)
return getmetatable(val).__name local tbl = getmetatable(val)
if type(tbl) == "boolean" then
error("what, got a boolean??: " .. tostring(tbl), 1)
end
return tbl.__name
end end
function on_init() function on_init()
local cube = world:request_res("../assets/cube-texture-embedded.gltf") local cube = world:request_res("../assets/cube-texture-embedded.gltf")
print("Loaded textured cube (" .. udname(cube) .. ")") print("Loaded textured cube (" .. udname(cube) .. ")")
cube:wait_until_loaded() --[[ cube:wait_until_loaded()
local scenes = cube:scenes() local scenes = cube:scenes()
local cube_scene = scenes[1] local cube_scene = scenes[1]
local pos = Transform.from_translation(Vec3.new(0, 0, -8.0)) local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
local e = world:spawn(pos, cube_scene) local e = world:spawn(pos, cube_scene)
print("spawned entity " .. tostring(e)) print("spawned entity " .. tostring(e)) ]]
end end
--[[ function on_first() --[[ function on_first()
@ -42,12 +48,12 @@ function on_update()
end, Transform) ]] end, Transform) ]]
---@type number ---@type number
local dt = world:resource(DeltaTime) --[[ local dt = world:resource(DeltaTime)
world:view(function (t) world:view(function (t)
t:translate(0, 0.15 * dt, 0) t:translate(0, 0.15 * dt, 0)
return t return t
end, Transform) end, Transform) ]]
end end
--[[ function on_post_update() --[[ function on_post_update()

View File

@ -1,6 +1,6 @@
use lyra_engine::{ use lyra_engine::{
assets::{gltf::Gltf, ResourceManager}, assets::{gltf::Gltf, ResourceManager},
game::Game, game::App,
input::{ input::{
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
@ -33,75 +33,73 @@ async fn main() {
.bind( .bind(
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_FORWARD_BACKWARD,
&[ &[
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::KeyW).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::KeyS).into_binding_modifier(-1.0),
], ],
) )
.bind( .bind(
ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_LEFT_RIGHT,
&[ &[
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::KeyA).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::KeyD).into_binding_modifier(1.0),
], ],
) )
.bind( .bind(
ACTLBL_MOVE_UP_DOWN, ACTLBL_MOVE_UP_DOWN,
&[ &[
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::KeyC).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::KeyZ).into_binding_modifier(-1.0),
], ],
) )
.bind( .bind(
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_LEFT_RIGHT,
&[ &[
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(), ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::ArrowLeft).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::ArrowRight).into_binding_modifier(1.0),
], ],
) )
.bind( .bind(
ACTLBL_LOOK_UP_DOWN, ACTLBL_LOOK_UP_DOWN,
&[ &[
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(), ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::ArrowUp).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::ArrowDown).into_binding_modifier(1.0),
], ],
) )
.bind( .bind(
ACTLBL_LOOK_ROLL, ACTLBL_LOOK_ROLL,
&[ &[
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0), ActionSource::Keyboard(KeyCode::KeyE).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::KeyQ).into_binding_modifier(1.0),
], ],
) )
.bind( .bind(
"Debug", "Debug",
&[ActionSource::Keyboard(KeyCode::B).into_binding()], &[ActionSource::Keyboard(KeyCode::KeyB).into_binding()],
) )
.finish(), .finish(),
) )
.finish(); .finish();
let world = game.world_mut(); let world = &mut app.world;
world.add_resource(action_handler); world.add_resource(action_handler);
game.with_plugin(InputActionPlugin); app.with_plugin(InputActionPlugin);
}; };
Game::initialize() let mut app = App::new();
.await app.with_plugin(lyra_engine::DefaultPlugins);
.with_plugin(lyra_engine::DefaultPlugins) app.with_plugin(setup_scene_plugin);
.with_plugin(setup_scene_plugin) app.with_plugin(action_handler_plugin);
.with_plugin(action_handler_plugin) app.with_plugin(setup_script_plugin);
.with_plugin(setup_script_plugin) //app.with_plugin(camera_debug_plugin);
//.with_plugin(camera_debug_plugin) app.with_plugin(FreeFlyCameraPlugin);
.with_plugin(FreeFlyCameraPlugin) app.run();
.run()
.await;
} }
fn setup_scene_plugin(app: &mut App) { fn setup_scene_plugin(app: &mut App) {
let world = game.world_mut(); let world = &mut app.world;
let resman = world.get_resource_mut::<ResourceManager>(); let resman = world.get_resource_mut::<ResourceManager>().unwrap();
let camera_gltf = resman let camera_gltf = resman
.request::<Gltf>("../assets/AntiqueCamera.glb") .request::<Gltf>("../assets/AntiqueCamera.glb")
.unwrap(); .unwrap();
@ -137,10 +135,10 @@ fn setup_scene_plugin(app: &mut App) {
} }
fn setup_script_plugin(app: &mut App) { fn setup_script_plugin(app: &mut App) {
game.with_plugin(LuaScriptingPlugin); app.with_plugin(LuaScriptingPlugin);
let world = game.world_mut(); let world = &mut app.world;
let res_man = world.get_resource_mut::<ResourceManager>(); let res_man = world.get_resource_mut::<ResourceManager>().unwrap();
let script = res_man.request::<LuaScript>("scripts/test.lua").unwrap(); let script = res_man.request::<LuaScript>("scripts/test.lua").unwrap();
res_man.watch("scripts/test.lua", false).unwrap(); res_man.watch("scripts/test.lua", false).unwrap();
drop(res_man); drop(res_man);

View File

@ -37,13 +37,13 @@ impl_reflect_trait_value!(String);
impl_reflect_trait_value!(::core::option::Option<T: Clone + Reflect>); impl_reflect_trait_value!(::core::option::Option<T: Clone + Reflect>);
impl_reflect_trait_value!(::core::result::Result<T: Clone + Reflect, E: Clone + Reflect>); impl_reflect_trait_value!(::core::result::Result<T: Clone + Reflect, E: Clone + Reflect>);
impl_reflect_trait_value!(::core::marker::PhantomData<T: 'static>); impl_reflect_trait_value!(::core::marker::PhantomData<T: Send + Sync + 'static>);
impl_reflect_trait_value!(::std::sync::Arc<T: Reflect>); impl_reflect_trait_value!(::std::sync::Arc<T: Reflect>);
//impl_reflect_trait_value!(::std::sync::Arc<std::sync::Mutex<T: Reflect>>); //impl_reflect_trait_value!(::std::sync::Arc<std::sync::Mutex<T: Reflect>>);
//impl_reflect_trait_value!(::std::sync::RwLock<T: Reflect>); //impl_reflect_trait_value!(::std::sync::RwLock<T: Reflect>);
impl_reflect_trait_value!(::core::ptr::NonNull<T: Reflect>); //impl_reflect_trait_value!(::core::ptr::NonNull<T: Reflect>);
impl<T: Clone + Reflect, const N: usize> Reflect for [T; N] { impl<T: Clone + Reflect, const N: usize> Reflect for [T; N] {
fn name(&self) -> String { fn name(&self) -> String {

View File

@ -52,7 +52,7 @@ pub use registry::*;
pub mod impls; pub mod impls;
pub trait Reflect: Any { pub trait Reflect: Any + Send + Sync {
fn name(&self) -> String; fn name(&self) -> String;
fn type_id(&self) -> TypeId; fn type_id(&self) -> TypeId;

View File

@ -7,8 +7,8 @@ edition = "2021"
[features] [features]
default = ["lua"] default = ["lua"]
lua = ["dep:elua"] lua = ["dep:mlua"]
teal = ["lua", "elua/teal"] #teal = ["lua", "mlua/teal"]
[dependencies] [dependencies]
lyra-scripting-derive = { path = "lyra-scripting-derive" } lyra-scripting-derive = { path = "lyra-scripting-derive" }
@ -22,11 +22,11 @@ tracing = "0.1.37"
atomic_refcell = "0.1.13" atomic_refcell = "0.1.13"
# enabled with lua feature # enabled with lua feature
#mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? mlua = { version = "0.9.9", features = ["lua54", "send"], optional = true } # luajit maybe?
elua = { path = "./elua", optional = true } #elua = { path = "./elua", optional = true }
itertools = "0.12.0" itertools = "0.12.0"
paste = "1.0.14" paste = "1.0.14"
parking_lot = "0.12.3"
[dev-dependencies] [dev-dependencies]
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] } tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }

View File

@ -145,24 +145,24 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
let field_creator = match &g.wrapper_type { let field_creator = match &g.wrapper_type {
Some(wrap) => { Some(wrap) => {
quote!(#wrap(data.#field).as_lua(lua)) quote!(#wrap(data.#field).into_lua(lua))
}, },
None => match &g.body { None => match &g.body {
Some(body) => { Some(body) => {
quote!(#body) quote!(#body)
}, },
None => { None => {
quote!(data.#field.clone().as_lua(lua)) quote!(data.#field.clone().into_lua(lua))
} }
} }
}; };
quote! { quote! {
builder.field_getter(stringify!($field), |lua, this| { fields.add_field_method_get(stringify!($field), |lua, this| {
if let Some(data) = this.0.data_ref() { if let Some(data) = this.0.data_ref() {
#field_creator #field_creator
} else { } else {
Ok(Value::Nil) Ok(mlua::Value::Nil)
} }
}); });
} }
@ -186,16 +186,13 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
} }
} }
impl elua::Userdata for #wrapper_name { impl mlua::UserData for #wrapper_name {
fn name() -> String { fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
#ud_name.to_string() fields.add_field("__name", stringify!(#handle_name));
} fields.add_field_method_get("path", |_, this| Ok(this.path()));
fields.add_field_method_get("version", |_, this| Ok(this.version()));
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { fields.add_field_method_get("uuid", |_, this| Ok(this.uuid().to_string()));
builder.field_getter("path", |_, this| Ok(this.path())); fields.add_field_method_get("state", |_, this| {
builder.field_getter("version", |_, this| Ok(this.version()));
builder.field_getter("uuid", |_, this| Ok(this.uuid().to_string()));
builder.field_getter("state", |_, this| {
let name = if this.is_loaded() { let name = if this.is_loaded() {
"ready" "ready"
} else if this.get_error().is_some() { } else if this.get_error().is_some() {
@ -206,29 +203,31 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
}); });
#(#custom_getters)* #(#custom_getters)*
}
builder.method("is_watched", |_, this, ()| { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("is_watched", |_, this, ()| {
Ok(this.is_watched()) Ok(this.is_watched())
}); });
builder.method("is_loaded", |_, this, ()| { methods.add_method("is_loaded", |_, this, ()| {
Ok(this.is_loaded()) Ok(this.is_loaded())
}); });
builder.method("is_loaded", |_, this, ()| { methods.add_method("is_loaded", |_, this, ()| {
Ok(this.is_loaded()) Ok(this.is_loaded())
}); });
builder.method("wait_until_loaded", |_, this, ()| { methods.add_method("wait_until_loaded", |_, this, ()| {
this.wait_recurse_dependencies_load(); this.wait_recurse_dependencies_load();
Ok(()) Ok(())
}); });
builder.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(ScriptBorrow::from_component::<ResHandle<#handle_name>>(None)) Ok(ScriptBorrow::from_component::<ResHandle<#handle_name>>(None))
}); });
builder.method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { methods.add_method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(ScriptBorrow::from_component(Some(this.0.clone()))) Ok(ScriptBorrow::from_component(Some(this.0.clone())))
}); });
@ -236,12 +235,12 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
} }
} }
impl<'a> FromLua<'a> for #wrapper_name { impl<'a> mlua::FromLua<'a> for #wrapper_name {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> { fn from_lua(val: mlua::Value<'a>, _: &'a mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name(); let tyname = val.type_name();
let ud = val.as_userdata() let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch(#ud_name, &tyname))?; .ok_or(mlua::Error::external(crate::lua::Error::type_mismatch(#ud_name, &tyname)))?;
let ud = ud.as_ref::<#wrapper_name>()?; let ud = ud.borrow::<#wrapper_name>()?;
Ok(ud.clone()) Ok(ud.clone())
} }

View File

@ -156,13 +156,13 @@ impl MetaMethod {
if Self::does_metamethod_have_arg(&self.name) { if Self::does_metamethod_have_arg(&self.name) {
quote! { quote! {
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| { methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| {
#body #body
}); });
} }
} else { } else {
quote! { quote! {
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, ()| { methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, ()| {
#body #body
}); });
} }
@ -173,12 +173,12 @@ impl MetaMethod {
let body = Self::get_body_for_arg(&self.name, first, quote!(v)); let body = Self::get_body_for_arg(&self.name, first, quote!(v));
quote! { quote! {
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| { methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| {
#body #body
}); });
} }
} else { } else {
// an optional match arm that matches elua::Value:Number // an optional match arm that matches mlua::Value:Number
let number_arm = { let number_arm = {
let num_ident = self.arg.iter().find(|i| { let num_ident = self.arg.iter().find(|i| {
let is = i.to_string(); let is = i.to_string();
@ -195,7 +195,7 @@ impl MetaMethod {
let body = Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident)); let body = Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident));
quote! { quote! {
elua::Value::Number(n) => { mlua::Value::Number(n) => {
#body #body
}, },
} }
@ -211,7 +211,7 @@ impl MetaMethod {
let body = Self::get_method_body(&self.name, quote!(other.0)); let body = Self::get_method_body(&self.name, quote!(other.0));
quote! { quote! {
if let Ok(other) = ud.as_ref::<#i>() { if let Ok(other) = ud.borrow::<#i>() {
#body #body
} }
} }
@ -219,30 +219,30 @@ impl MetaMethod {
}); });
quote! { quote! {
elua::Value::Userdata(ud) => { mlua::Value::UserData(ud) => {
#(#if_statements else)* #(#if_statements else)*
// this is the body of the else statement // this is the body of the else statement
{ {
// try to get the name of the userdata for the error message // try to get the name of the userdata for the error message
if let Ok(mt) = ud.get_metatable() { if let Ok(mt) = ud.get_metatable() {
if let Ok(name) = mt.get::<_, String>("__name") { if let Ok(name) = mt.get::<String>("__name") {
return Err(elua::Error::BadArgument { return Err(mlua::Error::BadArgument {
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
arg_index: 2, pos: 2,
arg_name: Some("rhs".to_string()), name: Some("rhs".to_string()),
error: std::sync::Arc::new(elua::Error::Runtime( cause: std::sync::Arc::new(mlua::Error::runtime(
format!("cannot multiply with unknown userdata named {}", name) format!("cannot multiply with unknown userdata named {}", name)
)) ))
}); });
} }
} }
Err(elua::Error::BadArgument { Err(mlua::Error::BadArgument {
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
arg_index: 2, pos: 2,
arg_name: Some("rhs".to_string()), name: Some("rhs".to_string()),
error: std::sync::Arc::new( cause: std::sync::Arc::new(
elua::Error::runtime("cannot multiply with unknown userdata") mlua::Error::runtime("cannot multiply with unknown userdata")
) )
}) })
} }
@ -251,16 +251,16 @@ impl MetaMethod {
}; };
quote! { quote! {
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (elua::Value,)| { methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (mlua::Value,)| {
match v { match v {
#number_arm #number_arm
#userdata_arm #userdata_arm
_ => Err(elua::Error::BadArgument { _ => Err(mlua::Error::BadArgument {
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
arg_index: 2, pos: 2,
arg_name: Some("rhs".to_string()), name: Some("rhs".to_string()),
error: std::sync::Arc::new( cause: std::sync::Arc::new(
elua::Error::Runtime(format!("cannot multiply with {}", v.type_name())) mlua::Error::runtime(format!("cannot multiply with {}", v.type_name()))
) )
}) })
} }
@ -410,10 +410,10 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
let field_get_set_pairs = input.auto_fields.iter().map(|i| { let field_get_set_pairs = input.auto_fields.iter().map(|i| {
let is = i.to_string(); let is = i.to_string();
quote! { quote! {
builder.field_getter(#is, |_, this| { fields.add_field_method_get(#is, |_, this| {
Ok(this.#i) Ok(this.#i)
}); });
builder.field_setter(#is, |_, this, #i| { fields.add_field_method_set(#is, |_, this, #i| {
this.#i = #i; this.#i = #i;
Ok(()) Ok(())
}); });
@ -431,7 +431,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
quote! { quote! {
// arguments for function are not specified since they can be implied from the call // arguments for function are not specified since they can be implied from the call
// to new(...) // to new(...)
builder.function("new", |_, ( #(#arg_names_clone),* )| { methods.add_function("new", |_, ( #(#arg_names_clone),* )| {
Ok(#wrapper_typename(#path::new( #(#arg_names),* ))) Ok(#wrapper_typename(#path::new( #(#arg_names),* )))
}); });
} }
@ -452,11 +452,11 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
quote!() quote!()
} else { } else {
quote! { quote! {
builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { methods.add_method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone()))) Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone())))
}); });
builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { methods.add_function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(crate::ScriptBorrow::from_component::<#path>(None)) Ok(crate::ScriptBorrow::from_component::<#path>(None))
}); });
} }
@ -480,22 +480,21 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
} }
} }
impl<'lua> elua::FromLua<'lua> for #wrapper_typename { impl<'lua> mlua::FromLua<'lua> for #wrapper_typename {
fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> { fn from_lua(value: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> {
match value { match value {
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()), _ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()),
} }
} }
} }
impl elua::Userdata for #wrapper_typename { impl mlua::UserData for #wrapper_typename {
fn name() -> String { fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
stringify!(#type_name).to_string() #(#field_get_set_pairs)*
} }
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
#(#field_get_set_pairs)*
#lua_reflects #lua_reflects
#new_func_tokens #new_func_tokens
@ -509,20 +508,5 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
std::any::TypeId::of::<#path>() std::any::TypeId::of::<#path>()
} }
} }
impl<'a> elua::FromLuaVec<'a> for #wrapper_typename {
fn from_lua_value_vec(state: &'a elua::State, mut values: elua::ValueVec<'a>) -> elua::Result<Self> {
use elua::FromLua;
if let Some(val) = values.pop_front() {
#wrapper_typename::from_lua(state, val)
} else {
Err(elua::Error::IncorrectArgCount {
arg_expected: 1,
arg_count: values.len() as i32,
})
}
}
}
}) })
} }

View File

@ -49,8 +49,8 @@ impl VecWrapper {
if axis_type_name.contains("b") { if axis_type_name.contains("b") {
return quote! { return quote! {
builder.field("FALSE", #wrapper_ident(#wrapped_path::FALSE)); fields.add_field_method_get("FALSE", #wrapper_ident(#wrapped_path::FALSE));
builder.field("TRUE", #wrapper_ident(#wrapped_path::TRUE)); fields.add_field_method_get("TRUE", #wrapper_ident(#wrapped_path::TRUE));
}; };
} }
@ -87,7 +87,7 @@ impl VecWrapper {
let const_name = cnst.to_string(); let const_name = cnst.to_string();
quote! { quote! {
builder.field(#const_name, #wrapper_ident(#wrapped_path::#cnst)); fields.add_field_method_get(#const_name, #wrapper_ident(#wrapped_path::#cnst));
} }
}); });
@ -112,27 +112,37 @@ impl VecWrapper {
optional_methods.push( optional_methods.push(
quote! { quote! {
builder.method("clamp_length", methods.add_method("clamp_length",
|_, this, (min, max): (#type_id, #type_id)| { |_, this, (min, max): (#type_id, #type_id)| {
Ok(#wrapper_ident(this.clamp_length(min, max))) Ok(#wrapper_ident(this.clamp_length(min, max)))
}); });
builder.method("abs_diff_eq", methods.add_method("abs_diff_eq",
|_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| { |_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| {
Ok(this.abs_diff_eq(rhs.0, max_abs_diff)) Ok(this.abs_diff_eq(rhs.0, max_abs_diff))
}); });
builder.method("ceil", methods.add_method("ceil",
|_, this, (): ()| { |_, this, (): ()| {
Ok(#wrapper_ident(this.ceil())) Ok(#wrapper_ident(this.ceil()))
}); });
} }
); );
if vec_size != 4 { if vec_size == 2 {
// angle_between is deprecated for Vec2, must use angle_to instead.
optional_methods.push( optional_methods.push(
quote! { quote! {
builder.method("angle_between", methods.add_method("angle_to",
|_, this, (rhs,): (#wrapper_ident,)| {
Ok(this.angle_to(rhs.0))
});
}
)
} else if vec_size != 4 {
optional_methods.push(
quote! {
methods.add_method("angle_between",
|_, this, (rhs,): (#wrapper_ident,)| { |_, this, (rhs,): (#wrapper_ident,)| {
Ok(this.angle_between(rhs.0)) Ok(this.angle_between(rhs.0))
}); });
@ -144,7 +154,7 @@ impl VecWrapper {
if !axis_type_name.contains("u") { if !axis_type_name.contains("u") {
optional_methods.push( optional_methods.push(
quote! { quote! {
builder.method("abs", methods.add_method("abs",
|_, this, (): ()| { |_, this, (): ()| {
Ok(#wrapper_ident(this.abs())) Ok(#wrapper_ident(this.abs()))
}); });
@ -154,21 +164,18 @@ impl VecWrapper {
let optional_methods_iter = optional_methods.iter(); let optional_methods_iter = optional_methods.iter();
quote! { quote! {
methods.add_method("clamp",
builder.method("clamp",
|_, this, (min, max): (#wrapper_ident, #wrapper_ident)| { |_, this, (min, max): (#wrapper_ident, #wrapper_ident)| {
Ok(#wrapper_ident(this.clamp(min.0, max.0))) Ok(#wrapper_ident(this.clamp(min.0, max.0)))
}); });
// TODO: Not all Vecs have this // TODO: Not all Vecs have this
/* builder.method("clamp_length", /* methods.add_method("clamp_length",
|_, this, (min, max): (f32, f32)| { |_, this, (min, max): (f32, f32)| {
Ok(#wrapper_ident(this.clamp_length(min, max))) Ok(#wrapper_ident(this.clamp_length(min, max)))
}); */ }); */
methods.add_method("to_array",
builder.method("to_array",
|_, this, (): ()| { |_, this, (): ()| {
Ok(this.to_array()) Ok(this.to_array())
}); });

View File

@ -8,16 +8,23 @@ use crate::ScriptWorldPtr;
pub enum ScriptError { pub enum ScriptError {
#[error("{0}")] #[error("{0}")]
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
MluaError(elua::Error), LuaError(crate::lua::Error),
#[error("{0}")] #[error("{0}")]
Other(anyhow::Error), Other(anyhow::Error),
} }
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
impl From<elua::Error> for ScriptError { impl From<mlua::Error> for ScriptError {
fn from(value: elua::Error) -> Self { fn from(value: mlua::Error) -> Self {
ScriptError::MluaError(value) ScriptError::LuaError(crate::lua::Error::Mlua(value))
}
}
#[cfg(feature = "lua")]
impl From<crate::lua::Error> for ScriptError {
fn from(value: crate::lua::Error) -> Self {
ScriptError::LuaError(value)
} }
} }

View File

@ -16,7 +16,7 @@ pub use host::*;
pub mod script; pub mod script;
pub use script::*; pub use script::*;
use lyra_game::game::Game; use lyra_game::game::App;
// required for some proc macros :( // required for some proc macros :(
#[allow(unused_imports)] #[allow(unused_imports)]
@ -136,15 +136,15 @@ pub trait GameScriptExt {
P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static; P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static;
} }
impl GameScriptExt for Game { impl GameScriptExt for App {
fn add_script_api_provider<T, P>(&mut self, mut provider: P) fn add_script_api_provider<T, P>(&mut self, mut provider: P)
where where
T: ScriptHost, T: ScriptHost,
P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static
{ {
let world = self.world_mut(); let world = &mut self.world;
provider.prepare_world(world); provider.prepare_world(world);
let mut providers = world.get_resource_mut::<ScriptApiProviders<T>>(); let mut providers = world.get_resource_mut::<ScriptApiProviders<T>>().unwrap();
providers.add_provider(provider); providers.add_provider(provider);
} }
} }

View File

@ -12,7 +12,7 @@ use crate::ScriptWorldPtr;
pub struct ReflectedItem<'a> { pub struct ReflectedItem<'a> {
//pub proxy: &'a ReflectLuaProxy, //pub proxy: &'a ReflectLuaProxy,
pub comp_ptr: NonNull<u8>, pub comp_ptr: NonNull<u8>,
pub comp_val: elua::Value<'a>, pub comp_val: mlua::Value<'a>,
} }
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
@ -29,21 +29,20 @@ pub struct ReflectedIterator {
impl ReflectedIterator { impl ReflectedIterator {
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option<ReflectedRow<'a>> { pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option<ReflectedRow<'a>> {
use elua::AsLua; use mlua::IntoLua;
let world = self.world.read();
let n = self.dyn_view.next(&world);
let world = self.world.as_ref(); if let Some((en, row)) = n {
let n = self.dyn_view.next(world);
if let Some(row) = n {
if self.reflected_components.is_none() { if self.reflected_components.is_none() {
let world = self.world.as_ref(); self.reflected_components = world.get_resource::<TypeRegistry>()
self.reflected_components = world.try_get_resource::<TypeRegistry>()
.map(|r| NonNull::from(r.deref())); .map(|r| NonNull::from(r.deref()));
} }
let mut dynamic_row = vec![]; let mut dynamic_row = vec![];
for d in row.row.iter() { for d in row.iter() {
let id = d.info.type_id().as_rust(); let id = d.info.type_id().as_rust();
let reflected_components = let reflected_components =
unsafe { self.reflected_components.as_ref().unwrap().as_ref() }; unsafe { self.reflected_components.as_ref().unwrap().as_ref() };
@ -54,7 +53,7 @@ impl ReflectedIterator {
// TODO: properly handle this error // TODO: properly handle this error
.expect("Type does not have ReflectLuaProxy as a TypeData"); .expect("Type does not have ReflectLuaProxy as a TypeData");
let value = (proxy.fn_as_lua)(lua, d.ptr.cast()).unwrap() let value = (proxy.fn_as_lua)(lua, d.ptr.cast()).unwrap()
.as_lua(lua).unwrap(); .into_lua(lua).unwrap();
dynamic_row.push(ReflectedItem { dynamic_row.push(ReflectedItem {
comp_ptr: d.ptr, comp_ptr: d.ptr,
@ -63,7 +62,7 @@ impl ReflectedIterator {
} }
let row = ReflectedRow { let row = ReflectedRow {
entity: row.entity, entity: en,
row: dynamic_row row: dynamic_row
}; };

View File

@ -2,7 +2,7 @@ pub mod dynamic_iter;
pub use dynamic_iter::*; pub use dynamic_iter::*;
pub mod world; pub mod world;
use elua::FromLua; use mlua::AnyUserDataExt;
pub use world::*; pub use world::*;
pub mod script; pub mod script;
@ -22,13 +22,48 @@ pub use system::*;
use std::{any::TypeId, sync::Mutex}; use std::{any::TypeId, sync::Mutex};
use lyra_ecs::{ use lyra_ecs::World;
Component, ComponentInfo, World
};
use lyra_reflect::{Reflect, TypeRegistry}; use lyra_reflect::{Reflect, TypeRegistry};
use crate::ScriptBorrow; use crate::ScriptBorrow;
pub type LuaContext = Mutex<elua::State>; pub type LuaContext = Mutex<mlua::Lua>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("mismatched type, expected `{expected}`, got: `{got}`")]
TypeMismatch {
expected: String,
got: String,
},
#[error("received nil value from Lua")]
Nil,
#[error("{0}")]
Mlua(#[from] mlua::Error)
}
/* impl Into<mlua::Error> for Error {
fn into(self) -> mlua::Error {
match self {
Error::Mlua(error) => error,
_ => mlua::Error::external(self)
}
}
}
*/
impl From<Error> for mlua::Error {
fn from(value: Error) -> Self {
match value {
Error::Mlua(error) => error,
_ => mlua::Error::external(value)
}
}
}
impl Error {
pub fn type_mismatch(expected: &str, got: &str) -> Self {
Self::TypeMismatch { expected: expected.into(), got: got.into() }
}
}
/// Name of a Lua function that is used to Reflect the Userdata, but without a value. /// Name of a Lua function that is used to Reflect the Userdata, but without a value.
/// ///
@ -47,7 +82,7 @@ pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
/// ///
/// This is used for types that can be converted into components. When implementing this function, /// This is used for types that can be converted into components. When implementing this function,
/// you must return a [`ScriptBorrow`] that contains the component for this userdata. /// you must return a [`ScriptBorrow`] that contains the component for this userdata.
/// You can return [`elua::Value::Nil`] if for some reason the type could not be converted /// You can return [`mlua::Value::Nil`] if for some reason the type could not be converted
/// into a component. /// into a component.
/// ///
/// A good example of this is `LuaResHandle`. The resource handle is requested from the /// A good example of this is `LuaResHandle`. The resource handle is requested from the
@ -78,32 +113,26 @@ pub trait RegisterLuaType {
/// Register a type to Lua that **is not wrapped**. /// Register a type to Lua that **is not wrapped**.
fn register_lua_type<'a, T>(&mut self) fn register_lua_type<'a, T>(&mut self)
where where
T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata; T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData;
/// Registers a type to Lua that is wrapped another type. /// Registers a type to Lua that is wrapped another type.
/// This would be used for something like `UserdataRef<T>`. /// This would be used for something like `UserdataRef<T>`.
fn register_lua_wrapper<'a, W>(&mut self) fn register_lua_wrapper<'a, W>(&mut self)
where where
W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata; W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData;
/// Registers a type to Lua that can be converted into and from Lua types. /// Registers a type to Lua that can be converted into and from Lua types.
fn register_lua_convert<T>(&mut self) fn register_lua_convert<T>(&mut self)
where where
T: Clone + for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + LuaWrapper + 'static; T: Clone + for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + LuaWrapper + 'static;
/// Registers a type to Lua that implements [`elua::TableProxy`]
fn register_lua_table_proxy<'a, T, W>(&mut self)
where
T: elua::TableProxy + 'static,
W: Component;
} }
impl RegisterLuaType for World { impl RegisterLuaType for World {
fn register_lua_type<'a, T>(&mut self) fn register_lua_type<'a, T>(&mut self)
where where
T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData
{ {
let mut registry = self.get_resource_mut::<TypeRegistry>(); let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
let type_id = TypeId::of::<T>(); let type_id = TypeId::of::<T>();
@ -113,9 +142,9 @@ impl RegisterLuaType for World {
fn register_lua_wrapper<'a, W>(&mut self) fn register_lua_wrapper<'a, W>(&mut self)
where where
W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData
{ {
let mut registry = self.get_resource_mut::<TypeRegistry>(); let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
let reg_type = registry.get_type_or_default(W::wrapped_type_id()); let reg_type = registry.get_type_or_default(W::wrapped_type_id());
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<W>()); reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<W>());
@ -123,62 +152,30 @@ impl RegisterLuaType for World {
fn register_lua_convert<T>(&mut self) fn register_lua_convert<T>(&mut self)
where where
T: Clone + for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + LuaWrapper + 'static, T: Clone + for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + LuaWrapper + 'static,
{ {
let mut registry = self.get_resource_mut::<TypeRegistry>(); let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
let reg_type = registry.get_type_or_default(T::wrapped_type_id()); let reg_type = registry.get_type_or_default(T::wrapped_type_id());
reg_type.add_data(ReflectLuaProxy::from_as_and_from_lua::<T>()); reg_type.add_data(ReflectLuaProxy::from_as_and_from_lua::<T>());
} }
fn register_lua_table_proxy<'a, T, C>(&mut self)
where
T: elua::TableProxy + 'static,
C: Component
{
let mut registry = self.get_resource_mut::<TypeRegistry>();
let reg_type = registry.get_type_or_default(TypeId::of::<C>());
reg_type.add_data(ReflectLuaProxy::from_table_proxy::<T>());
drop(registry);
let mut lookup = self.get_resource_or_else::<LuaTableProxyLookup, _>(LuaTableProxyLookup::default);
lookup.typeid_from_name.insert(T::table_name(), TypeId::of::<C>());
let info = ComponentInfo::new::<C>();
lookup.comp_info_from_name.insert(T::table_name(), info);
}
} }
impl<'lua> elua::FromLua<'lua> for ScriptBorrow { impl<'lua> mlua::FromLua<'lua> for ScriptBorrow {
fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> { fn from_lua(value: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> {
match value { match value {
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => unreachable!(), _ => unreachable!(),
} }
} }
} }
impl<'lua> elua::FromLuaVec<'lua> for ScriptBorrow { impl mlua::UserData for ScriptBorrow {
fn from_lua_value_vec(state: &'lua elua::State, mut values: elua::ValueVec<'lua>) -> elua::Result<Self> {
if let Some(v) = values.pop_front() {
ScriptBorrow::from_lua(state, v)
} else {
Err(elua::Error::Nil)
}
}
}
impl elua::Userdata for ScriptBorrow {
fn name() -> String {
"ScriptBorrow".to_string()
}
fn build<'a>(_: &mut elua::UserdataBuilder<'a, Self>) { }
} }
/// Helper function used for reflecting userdata as a ScriptBorrow /// Helper function used for reflecting userdata as a ScriptBorrow
pub fn reflect_user_data(ud: &elua::AnyUserdata) -> ScriptBorrow { pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow {
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
.expect("Type does not implement internal reflect method properly") .expect("Type does not implement internal reflect method properly")
} }

View File

@ -17,7 +17,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
world.register_lua_wrapper::<LuaSceneHandle>(); world.register_lua_wrapper::<LuaSceneHandle>();
world.register_lua_wrapper::<LuaActionHandler>(); world.register_lua_wrapper::<LuaActionHandler>();
let mut registry = world.get_resource_mut::<TypeRegistry>(); let mut registry = world.get_resource_mut::<TypeRegistry>().unwrap();
let reg_type = registry.get_type_or_default(TypeId::of::<Gltf>()); let reg_type = registry.get_type_or_default(TypeId::of::<Gltf>());
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>()); reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>());
@ -39,7 +39,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
let ctx = ctx.lock().unwrap(); let ctx = ctx.lock().unwrap();
let globals = ctx.globals()?; let globals = ctx.globals();
globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?; globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?;
globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?; globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?;
globals.set("SceneComponent", ctx.create_proxy::<LuaSceneHandle>()?)?; globals.set("SceneComponent", ctx.create_proxy::<LuaSceneHandle>()?)?;
@ -60,7 +60,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
} }
} }
fn create_reflect_table<T: Reflect + ResourceObject + Default + 'static>(lua: &elua::State) -> elua::Result<elua::Table> { fn create_reflect_table<T: Reflect + ResourceObject + Default + 'static>(lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
let table = lua.create_table()?; let table = lua.create_table()?;
table.set(FN_NAME_INTERNAL_REFLECT_TYPE, lua.create_function(|_, ()| { table.set(FN_NAME_INTERNAL_REFLECT_TYPE, lua.create_function(|_, ()| {
Ok(ScriptBorrow::from_resource::<T>(None)) Ok(ScriptBorrow::from_resource::<T>(None))

View File

@ -23,7 +23,7 @@ impl ScriptApiProvider for LyraMathApiProvider {
/* let bytes = include_bytes!("../../../scripts/lua/math/transform.lua"); /* let bytes = include_bytes!("../../../scripts/lua/math/transform.lua");
ctx.load("lyra/math/transform.lua", bytes.as_slice())?.execute(())?; */ ctx.load("lyra/math/transform.lua", bytes.as_slice())?.execute(())?; */
let globals = ctx.globals()?; let globals = ctx.globals();
globals.set("Vec3", ctx.create_proxy::<LuaVec3>()?)?; globals.set("Vec3", ctx.create_proxy::<LuaVec3>()?)?;
globals.set("Quat", ctx.create_proxy::<LuaQuat>()?)?; globals.set("Quat", ctx.create_proxy::<LuaQuat>()?)?;
globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?; globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?;

View File

@ -1,11 +1,12 @@
use std::sync::{Mutex, Arc}; use std::sync::{Arc, Mutex};
use tracing::{debug_span, debug}; use mlua::AnyUserDataExt;
use tracing::{debug, debug_span};
use crate::{ScriptApiProvider, ScriptData}; use crate::{ScriptApiProvider, ScriptData};
/// This Api provider provides some nice utility functions. /// This Api provider provides some nice utility functions.
/// ///
/// Functions: /// Functions:
/// ```lua /// ```lua
/// ---@param str (string) A format string. /// ---@param str (string) A format string.
@ -17,99 +18,122 @@ use crate::{ScriptApiProvider, ScriptData};
pub struct UtilityApiProvider; pub struct UtilityApiProvider;
impl ScriptApiProvider for UtilityApiProvider { impl ScriptApiProvider for UtilityApiProvider {
type ScriptContext = Mutex<elua::State>; type ScriptContext = Mutex<mlua::Lua>;
fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(
&mut self,
data: &ScriptData,
ctx: &mut Self::ScriptContext,
) -> Result<(), crate::ScriptError> {
let ctx = ctx.lock().unwrap(); let ctx = ctx.lock().unwrap();
//fn printf(lua: &elua::State, (mut text, formats): (String, elua::Variadic<elua::Value>)) -> elua::Result<()> { //fn printf(lua: &mlua::State, (mut text, formats): (String, mlua::Variadic<mlua::Value>)) -> mlua::Result<()> {
let printf = |lua: &elua::State, (mut text, formats): (String, elua::Variadic<elua::Value>)| { let printf =
let mut formatted = String::new(); |lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic<mlua::Value>)| {
let mut arg_num = 0; let mut formatted = String::new();
let mut arg_num = 0;
while let Some(start) = text.find("{}") { while let Some(start) = text.find("{}") {
let val_str = match formats.get(arg_num) { let val_str = match formats.get(arg_num) {
Some(v) => match v { Some(v) => match v {
elua::Value::Nil => "nil".to_string(), mlua::Value::Nil => "nil".to_string(),
elua::Value::Boolean(b) => b.to_string(), mlua::Value::Boolean(b) => b.to_string(),
elua::Value::Number(n) => n.to_string(), mlua::Value::Integer(n) => n.to_string(),
elua::Value::String(s) => s.clone(), mlua::Value::Number(n) => n.to_string(),
elua::Value::Table(_) => { mlua::Value::String(s) => s.to_string_lossy().to_string(),
return Err(elua::Error::runtime("unable to get string representation of Table")); mlua::Value::Table(_) => {
return Err(mlua::Error::runtime(
"unable to get string representation of Table",
));
},
mlua::Value::Function(_) => {
return Err(mlua::Error::runtime(
"unable to get string representation of Function",
));
},
mlua::Value::Thread(_) => {
return Err(mlua::Error::runtime(
"unable to get string representation of Thread",
));
},
mlua::Value::UserData(ud) => {
if let Ok(tos) =
ud.get::<_, mlua::Function>(mlua::MetaMethod::ToString.name())
{
tos.call::<_, String>(())?
} else {
return Err(mlua::Error::runtime(
"UserData does not implement MetaMethod '__tostring'",
));
}
},
mlua::Value::LightUserData(_) => {
return Err(mlua::Error::runtime(
"unable to get string representation of LightUserData",
));
},
mlua::Value::Error(error) => {
return Err(error.clone());
},
}, },
elua::Value::Function(_) => { None => {
return Err(elua::Error::runtime("unable to get string representation of Function")); let got_args = arg_num; // - 1;
},
elua::Value::Thread(_) => { // continue searching for {} to get the number of format spots for the error message.
return Err(elua::Error::runtime("unable to get string representation of Thread")); while let Some(start) = text.find("{}") {
}, text = text[start + 2..].to_string();
elua::Value::Userdata(ud) => { arg_num += 1;
if let Ok(tos) = ud.get::<_, elua::Function>(elua::MetaMethod::ToString) {
tos.exec::<_, String>(())?
} else {
return Err(elua::Error::runtime("UserData does not implement MetaMethod '__tostring'"));
} }
},
elua::Value::None => "None".to_string(),
elua::Value::Multi(_) => {
return Err(elua::Error::runtime("unable to get string representation of ValueVec"));
},
},
None => {
let got_args = arg_num;// - 1;
// continue searching for {} to get the number of format spots for the error message. return Err(mlua::Error::BadArgument {
while let Some(start) = text.find("{}") { to: Some("printf".into()),
text = text[start + 2..].to_string(); pos: 2,
arg_num += 1; name: Some("fmt...".into()),
cause: Arc::new(mlua::Error::runtime(format!(
"not enough args \
given for the amount of format areas in the string. Expected {}, \
got {}.",
arg_num, got_args
))),
});
} }
};
return Err(elua::Error::BadArgument { formatted = format!("{}{}{}", formatted, &text[0..start], val_str);
func: Some("printf".to_string()),
arg_index: 2,
arg_name: Some("fmt...".to_string()),
error: Arc::new(elua::Error::Runtime(format!(
"not enough args \
given for the amount of format areas in the string. Expected {}, \
got {}.", arg_num, got_args
)))
})
},
};
formatted = format!("{}{}{}", formatted, &text[0..start], val_str); text = text[start + 2..].to_string();
text = text[start + 2..].to_string(); arg_num += 1;
}
arg_num += 1; if arg_num < formats.len() {
} return Err(mlua::Error::BadArgument {
to: Some("printf".into()),
pos: 2,
name: Some("fmt...".into()),
cause: Arc::new(mlua::Error::runtime(format!(
"got more args \
than format areas in the string. Expected {}, got {}.",
formats.len(),
arg_num
))),
});
}
if arg_num < formats.len() { formatted = format!("{}{}", formatted, text);
return Err(elua::Error::BadArgument {
func: Some("printf".to_string()),
arg_index: 2,
arg_name: Some("fmt...".to_string()),
error: Arc::new(elua::Error::Runtime(format!(
"got more args \
than format areas in the string. Expected {}, got {}.", formats.len(), arg_num
)))
})
}
formatted = format!("{}{}", formatted, text);
lua.globals()? lua.globals()
.get::<_, elua::Function>("print")? .get::<_, mlua::Function>("print")?
.exec::<_, ()>(formatted)?; .call::<_, ()>(formatted)?;
Ok(()) Ok(())
}; };
let script_name_reg = ctx.registry_insert(data.name.clone())?; let script_name_reg = ctx.create_registry_value(data.name.clone())?;
let printf_func = ctx.create_function(printf)?; let printf_func = ctx.create_function(printf)?;
let print_func = ctx.create_function(move |lua, text: String| { let print_func = ctx.create_function(move |lua, text: String| {
let name = lua.registry_get::<String>(script_name_reg)?; let name = lua.registry_value::<String>(&script_name_reg)?;
let _span = debug_span!("lua", script = &name).entered(); let _span = debug_span!("lua", script = &name).entered();
debug!(target: "lyra_scripting::lua", "{}", text); debug!(target: "lyra_scripting::lua", "{}", text);
@ -117,18 +141,27 @@ impl ScriptApiProvider for UtilityApiProvider {
Ok(()) Ok(())
})?; })?;
let globals = ctx.globals()?; let globals = ctx.globals();
globals.set("printf", printf_func)?; globals.set("printf", printf_func)?;
globals.set("print", print_func)?; globals.set("print", print_func)?;
Ok(()) Ok(())
} }
fn setup_script(&mut self, _data: &ScriptData, _ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn setup_script(
&mut self,
_data: &ScriptData,
_ctx: &mut Self::ScriptContext,
) -> Result<(), crate::ScriptError> {
Ok(()) Ok(())
} }
fn update_script_environment(&mut self, _world: crate::ScriptWorldPtr, _data: &ScriptData, _ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn update_script_environment(
&mut self,
_world: crate::ScriptWorldPtr,
_data: &ScriptData,
_ctx: &mut Self::ScriptContext,
) -> Result<(), crate::ScriptError> {
Ok(()) Ok(())
} }
} }

View File

@ -1,12 +1,12 @@
use std::{any::TypeId, collections::HashMap, ptr::NonNull}; use std::{any::TypeId, collections::HashMap, ptr::NonNull};
use elua::{FromLua, TableProxy, AsLua}; use mlua::{AnyUserDataExt, IntoLua};
use lyra_ecs::{ComponentInfo, DynamicBundle}; use lyra_ecs::{ComponentInfo, DynamicBundle};
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use crate::{ScriptBorrow, ScriptDynamicBundle}; use crate::{ScriptBorrow, ScriptDynamicBundle};
use super::FN_NAME_INTERNAL_REFLECT; use super::{Error, FN_NAME_INTERNAL_REFLECT};
pub trait LuaWrapper { pub trait LuaWrapper {
/// The type id of the wrapped type. /// The type id of the wrapped type.
@ -16,39 +16,39 @@ pub trait LuaWrapper {
/// A trait that used to convert something into lua, or to set something to a value from lua. /// A trait that used to convert something into lua, or to set something to a value from lua.
pub trait LuaProxy { pub trait LuaProxy {
fn as_lua_value<'lua>( fn as_lua_value<'lua>(
lua: &'lua elua::State, lua: &'lua mlua::Lua,
this: &dyn Reflect, this: &dyn Reflect,
) -> elua::Result<elua::Value<'lua>>; ) -> mlua::Result<mlua::Value<'lua>>;
fn apply( fn apply(
lua: &elua::State, lua: &mlua::Lua,
this: &mut dyn Reflect, this: &mut dyn Reflect,
value: &elua::Value, value: &mlua::Value,
) -> elua::Result<()>; ) -> mlua::Result<()>;
} }
impl<'a, T> LuaProxy for T impl<'a, T> LuaProxy for T
where where
T: Reflect + Clone + elua::FromLua<'a> + elua::Userdata T: Reflect + Clone + mlua::FromLua<'a> + mlua::UserData
{ {
fn as_lua_value<'lua>( fn as_lua_value<'lua>(
lua: &'lua elua::State, lua: &'lua mlua::Lua,
this: &dyn Reflect, this: &dyn Reflect,
) -> elua::Result<elua::Value<'lua>> { ) -> mlua::Result<mlua::Value<'lua>> {
let this = this.as_any().downcast_ref::<T>().unwrap(); let this = this.as_any().downcast_ref::<T>().unwrap();
lua.create_userdata(this.clone()) lua.create_userdata(this.clone())
.and_then(|ud| ud.as_lua(lua)) .and_then(|ud| ud.into_lua(lua))
} }
fn apply( fn apply(
_: &elua::State, _: &mlua::Lua,
this: &mut dyn Reflect, this: &mut dyn Reflect,
apply: &elua::Value, apply: &mlua::Value,
) -> elua::Result<()> { ) -> mlua::Result<()> {
let this = this.as_any_mut().downcast_mut::<T>().unwrap(); let this = this.as_any_mut().downcast_mut::<T>().unwrap();
let apply = apply.as_userdata() let apply = apply.as_userdata()
.expect("Somehow a non-userdata Lua Value was provided to a LuaProxy") .expect("Somehow a non-userdata Lua Value was provided to a LuaProxy")
.as_ref::<T>()?; .borrow::<T>()?;
*this = apply.clone(); *this = apply.clone();
@ -67,12 +67,12 @@ pub struct LuaTableProxyLookup {
#[derive(Clone)] #[derive(Clone)]
pub struct ReflectLuaProxy { pub struct ReflectLuaProxy {
pub fn_as_lua: pub fn_as_lua:
for<'a> fn(lua: &'a elua::State, this_ptr: NonNull<()>) -> elua::Result<elua::Value<'a>>, for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull<()>) -> mlua::Result<mlua::Value<'a>>,
pub fn_apply: for<'a> fn( pub fn_apply: for<'a> fn(
lua: &'a elua::State, lua: &'a mlua::Lua,
this_ptr: NonNull<()>, this_ptr: NonNull<()>,
value: &'a elua::Value<'a>, value: &'a mlua::Value<'a>,
) -> elua::Result<()>, ) -> mlua::Result<()>,
} }
impl ReflectLuaProxy { impl ReflectLuaProxy {
@ -82,7 +82,7 @@ impl ReflectLuaProxy {
T: Reflect + LuaProxy T: Reflect + LuaProxy
{ {
Self { Self {
fn_as_lua: |lua, this| -> elua::Result<elua::Value> { fn_as_lua: |lua, this| -> mlua::Result<mlua::Value> {
let this = unsafe { this.cast::<T>().as_ref() }; let this = unsafe { this.cast::<T>().as_ref() };
<T as LuaProxy>::as_lua_value(lua, this) <T as LuaProxy>::as_lua_value(lua, this)
}, },
@ -93,42 +93,19 @@ impl ReflectLuaProxy {
} }
} }
pub fn from_table_proxy<T>() -> Self
where
T: TableProxy
{
Self {
fn_as_lua: |lua, this| -> elua::Result<elua::Value> {
let this = unsafe { this.cast::<T>().as_ref() };
this.as_table(lua)
.and_then(|t| t.as_lua(lua))
},
fn_apply: |lua, ptr, value| {
let this = unsafe { ptr.cast::<T>().as_mut() };
let table = value.as_table()
.expect("Somehow a non-Table Lua Value was provided to a TableProxy");
let new_val = T::from_table(lua, table.clone())?;
*this = new_val;
Ok(())
},
}
}
/// Create from a type that implements FromLua and AsLua /// Create from a type that implements FromLua and AsLua
pub fn from_as_and_from_lua<T>() -> Self pub fn from_as_and_from_lua<T>() -> Self
where where
T: for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + Clone T: for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + Clone
{ {
Self { Self {
fn_as_lua: |lua, this| -> elua::Result<elua::Value> { fn_as_lua: |lua, this| -> mlua::Result<mlua::Value> {
let this = unsafe { this.cast::<T>().as_ref() }; let this = unsafe { this.cast::<T>().as_ref() };
this.clone().as_lua(lua) this.clone().into_lua(lua)
}, },
fn_apply: |lua, ptr, value| { fn_apply: |lua, ptr, value| {
let this = unsafe { ptr.cast::<T>().as_mut() }; let this = unsafe { ptr.cast::<T>().as_mut() };
let new_val = T::from_lua(lua, value.clone())?; let new_val = T::from_lua(value.clone(), lua)?;
*this = new_val; *this = new_val;
@ -138,42 +115,27 @@ impl ReflectLuaProxy {
} }
} }
impl<'lua> elua::FromLua<'lua> for ScriptDynamicBundle { impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle {
fn from_lua(_: &'lua elua::State, val: elua::Value<'lua>) -> elua::Result<Self> { fn from_lua(val: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> {
match val { match val {
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
elua::Value::Nil => Err(elua::Error::Nil), mlua::Value::Nil => Err(Error::Nil.into()),
_ => unreachable!(), _ => unreachable!(),
} }
} }
} }
impl<'lua> elua::FromLuaVec<'lua> for ScriptDynamicBundle { impl mlua::UserData for ScriptDynamicBundle {
fn from_lua_value_vec(state: &'lua elua::State, mut values: elua::ValueVec<'lua>) -> elua::Result<Self> { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
if let Some(v) = values.pop_front() { methods.add_function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new())));
Ok(ScriptDynamicBundle::from_lua(state, v)?) methods.add_method_mut("push", |_, this, comp: mlua::AnyUserData| {
} else { let script_brw = comp.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?;
Err(elua::Error::Nil) let reflect = script_brw.reflect_branch.as_component_unchecked();
}
}
}
impl elua::Userdata for ScriptDynamicBundle { let refl_data = script_brw.data.unwrap();
fn name() -> String { reflect.bundle_insert(&mut this.0, refl_data);
"Bundle".to_string()
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { Ok(())
builder });
.function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new())))
.method_mut("push", |_, this, comp: elua::AnyUserdata| {
let script_brw = comp.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?;
let reflect = script_brw.reflect_branch.as_component_unchecked();
let refl_data = script_brw.data.unwrap();
reflect.bundle_insert(&mut this.0, refl_data);
Ok(())
});
} }
} }

View File

@ -1,31 +1,32 @@
use std::sync::Mutex; use std::sync::Mutex;
use elua::{AsLua, StdLibraries}; use mlua::{IntoLua, StdLib};
use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity};
#[derive(Default)] #[derive(Default)]
pub struct LuaHost; pub struct LuaHost;
fn try_call_lua_function(lua: &elua::State, fn_name: &str) -> Result<(), ScriptError> { fn try_call_lua_function(lua: &mlua::Lua, fn_name: &str) -> Result<(), ScriptError> {
let globals = lua.globals()?; let globals = lua.globals();
if globals.has_key(fn_name)? { if globals.contains_key(fn_name)? {
let lua_fn = globals.get::<_, elua::Function>(fn_name)?; let lua_fn = globals.get::<_, mlua::Function>(fn_name)?;
lua_fn.exec(()) lua_fn.call::<_, ()>(())
.map_err(ScriptError::MluaError)?; .map_err(ScriptError::from)?;
} }
Ok(()) Ok(())
} }
impl ScriptHost for LuaHost { impl ScriptHost for LuaHost {
type ScriptContext = Mutex<elua::State>; type ScriptContext = Mutex<mlua::Lua>;
fn load_script(&mut self, script: &[u8], script_data: &crate::ScriptData, providers: &mut crate::ScriptApiProviders<Self>) -> Result<Self::ScriptContext, crate::ScriptError> { fn load_script(&mut self, script: &[u8], script_data: &crate::ScriptData, providers: &mut crate::ScriptApiProviders<Self>) -> Result<Self::ScriptContext, crate::ScriptError> {
let mut ctx = Mutex::new({ let mut ctx = Mutex::new({
let s = elua::State::new(); // unsafe is required to allow the debug module
s.expose_libraries(StdLibraries::all()); let s = unsafe { mlua::Lua::unsafe_new() };
s.load_from_std_lib(StdLib::ALL)?;
s s
}); });
@ -35,9 +36,9 @@ impl ScriptHost for LuaHost {
} }
let lua = ctx.lock().unwrap(); let lua = ctx.lock().unwrap();
lua.load(&script_data.name, script)? lua.load(script)
.execute(()) .exec()
.map_err(|e| ScriptError::MluaError(e))?; .map_err(|e| ScriptError::LuaError(super::Error::Mlua(e)))?;
drop(lua); drop(lua);
Ok(ctx) Ok(ctx)
@ -65,9 +66,9 @@ impl ScriptHost for LuaHost {
let ctx = ctx.lock().expect("Failure to get Lua ScriptContext"); let ctx = ctx.lock().expect("Failure to get Lua ScriptContext");
let globals = ctx.globals()?; let globals = ctx.globals();
globals.set("world", world.as_lua(&ctx)?)?; globals.set("world", world.into_lua(&ctx)?)?;
globals.set("entity", ScriptEntity(script_data.entity).as_lua(&ctx)?)?; globals.set("entity", ScriptEntity(script_data.entity).into_lua(&ctx)?)?;
try_call_lua_function(&ctx, function_name)?; try_call_lua_function(&ctx, function_name)?;

View File

@ -5,7 +5,7 @@ use lyra_reflect::TypeRegistry;
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
use tracing::{debug, debug_span, error, trace}; use tracing::{debug, debug_span, error, trace};
use crate::{GameScriptExt, ScriptApiProviders, ScriptContexts, ScriptData, ScriptError, ScriptHost, ScriptList, ScriptWorldPtr}; use crate::{GameScriptExt, ScriptApiProviders, ScriptContexts, ScriptData, ScriptError, ScriptHost, ScriptList, ScriptWorldPtrGuard};
use super::{providers::{LyraEcsApiProvider, LyraMathApiProvider, UtilityApiProvider}, LuaContext, LuaHost, LuaLoader, LuaScript}; use super::{providers::{LyraEcsApiProvider, LyraMathApiProvider, UtilityApiProvider}, LuaContext, LuaHost, LuaLoader, LuaScript};
@ -40,7 +40,8 @@ pub fn lua_scripts_create_contexts(
trace!("Finished setting up script"); trace!("Finished setting up script");
// call on_init, handle the error // call on_init, handle the error
let world_ptr = ScriptWorldPtr::from_ref(&world); let world_guard = unsafe { ScriptWorldPtrGuard::new(world) };
let world_ptr = (*world_guard).clone();
match host.call_script( match host.call_script(
world_ptr, world_ptr,
&script_data, &script_data,
@ -50,7 +51,7 @@ pub fn lua_scripts_create_contexts(
) { ) {
Ok(()) => {} Ok(()) => {}
Err(e) => match e { Err(e) => match e {
ScriptError::MluaError(m) => { ScriptError::LuaError(m) => {
error!("Script '{}' ran into an error: {}", script.name(), m); error!("Script '{}' ran into an error: {}", script.name(), m);
} }
ScriptError::Other(_) => return Err(e.into()), ScriptError::Other(_) => return Err(e.into()),
@ -114,10 +115,11 @@ pub fn lua_scripts_reload_system(
} }
fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Result<()> { fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Result<()> {
let world_ptr = ScriptWorldPtr::from_ref(&world); let world_guard = unsafe { ScriptWorldPtrGuard::new(world) };
let mut host = world.get_resource_mut::<LuaHost>(); let world_ptr = (*world_guard).clone();
let mut contexts = world.get_resource_mut::<ScriptContexts<LuaContext>>(); let mut host = world.get_resource_mut::<LuaHost>().unwrap();
let mut providers = world.get_resource_mut::<ScriptApiProviders<LuaHost>>(); let mut contexts = world.get_resource_mut::<ScriptContexts<LuaContext>>().unwrap();
let mut providers = world.get_resource_mut::<ScriptApiProviders<LuaHost>>().unwrap();
for (en, scripts) in world.view_iter::<(Entities, &ScriptList<LuaScript>)>() { for (en, scripts) in world.view_iter::<(Entities, &ScriptList<LuaScript>)>() {
for script in scripts.iter() { for script in scripts.iter() {
@ -143,7 +145,7 @@ fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Resu
) { ) {
Ok(()) => {} Ok(()) => {}
Err(e) => match e { Err(e) => match e {
ScriptError::MluaError(m) => { ScriptError::LuaError(m) => {
error!("Script '{}' ran into an error: {}", script.name(), m); error!("Script '{}' ran into an error: {}", script.name(), m);
} }
ScriptError::Other(_) => return Err(e.into()), ScriptError::Other(_) => return Err(e.into()),
@ -190,8 +192,8 @@ pub fn lua_script_last_stage_system(world: &mut World) -> anyhow::Result<()> {
pub struct LuaScriptingPlugin; pub struct LuaScriptingPlugin;
impl Plugin for LuaScriptingPlugin { impl Plugin for LuaScriptingPlugin {
fn setup(&mut self, game: &mut lyra_game::game::Game) { fn setup(&mut self, app: &mut lyra_game::game::App) {
let world = game.world_mut(); let world = &mut app.world;
world.add_resource_default::<TypeRegistry>(); world.add_resource_default::<TypeRegistry>();
@ -200,16 +202,16 @@ impl Plugin for LuaScriptingPlugin {
world.add_resource_default::<ScriptContexts<LuaContext>>(); world.add_resource_default::<ScriptContexts<LuaContext>>();
let loader = world let loader = world
.try_get_resource_mut::<ResourceManager>() .get_resource_mut::<ResourceManager>()
.expect("Add 'ResourceManager' to the world before trying to add this plugin"); .expect("Add 'ResourceManager' to the world before trying to add this plugin");
loader.register_loader::<LuaLoader>(); loader.register_loader::<LuaLoader>();
drop(loader); drop(loader);
game.add_script_api_provider::<LuaHost, _>(UtilityApiProvider); app.add_script_api_provider::<LuaHost, _>(UtilityApiProvider);
game.add_script_api_provider::<LuaHost, _>(LyraEcsApiProvider); app.add_script_api_provider::<LuaHost, _>(LyraEcsApiProvider);
game.add_script_api_provider::<LuaHost, _>(LyraMathApiProvider); app.add_script_api_provider::<LuaHost, _>(LyraMathApiProvider);
game.add_system_to_stage( app.add_system_to_stage(
GameStages::First, GameStages::First,
"lua_create_contexts", "lua_create_contexts",
lua_scripts_create_contexts, lua_scripts_create_contexts,

View File

@ -1,32 +1,31 @@
use std::{ptr::NonNull, sync::Arc}; use std::{ops::DerefMut, ptr::NonNull, sync::Arc};
use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr}; use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr};
use elua::AsLua; use lyra_ecs::{
use lyra_ecs::{query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType}, CommandQueue, Commands, DynamicBundle, World}; query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType},
CommandQueue, Commands, DynamicBundle, World,
};
use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry}; use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
use mlua::{AnyUserDataExt, IntoLua};
use super::{ use super::{
reflect_user_data, wrappers::LuaResHandleToComponent, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE reflect_user_data, wrappers::LuaResHandleToComponent, Error, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
}; };
impl<'lua> elua::FromLua<'lua> for ScriptEntity { impl<'lua> mlua::FromLua<'lua> for ScriptEntity {
fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> { fn from_lua(value: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> {
match value { match value {
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptEntity", "Nil")), mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptEntity", "Nil"))),
_ => panic!(), _ => panic!(),
} }
} }
} }
impl elua::Userdata for ScriptEntity { impl mlua::UserData for ScriptEntity {
fn name() -> String { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
"Entity".to_string() methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| {
}
fn build<'a>(builder: &mut elua::userdata::UserdataBuilder<'a, Self>) {
builder.meta_method(elua::MetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this.0)) Ok(format!("{:?}", this.0))
}); });
} }
@ -38,279 +37,280 @@ pub enum WorldError {
LuaInvalidUsage(String), LuaInvalidUsage(String),
} }
impl<'a> elua::FromLua<'a> for ScriptWorldPtr { impl<'lua> mlua::FromLua<'lua> for ScriptWorldPtr {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> { fn from_lua(val: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> {
match val { match val {
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptWorldPtr", "Nil")), mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptWorldPtr", "Nil"))),
_ => panic!(), _ => panic!(),
} }
} }
} }
impl elua::Userdata for ScriptWorldPtr { impl mlua::UserData for ScriptWorldPtr {
fn name() -> String { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
"World".to_string() methods.add_method_mut("spawn", |_, this, vals: mlua::MultiValue| {
} let mut world = this.write();
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { let mut bundle = DynamicBundle::new();
builder
.method_mut("spawn", |_, this, vals: elua::ValueVec| {
let world = this.as_mut();
let mut bundle = DynamicBundle::new(); //while let Some(val) = vals.pop_front() {
for (i, val) in vals.into_iter().enumerate() {
let ud = val.as_userdata().ok_or(mlua::Error::BadArgument {
to: Some("World:spawn".into()),
pos: 2 + i, // i starts at 0
name: Some("components...".into()),
cause: Arc::new(mlua::Error::runtime("provided component is not userdata")),
})?;
//while let Some(val) = vals.pop_front() { let comp_borrow = {
for (i, val) in vals.into_iter().enumerate() { if let Ok(as_comp) = ud.get::<_, mlua::Function>(FN_NAME_INTERNAL_AS_COMPONENT)
let ud = val.as_userdata().ok_or( {
elua::Error::bad_arg( let ud = match as_comp.call(ud.clone())? {
Some("World:spawn"), mlua::Value::UserData(ud) => ud,
2 + i as i32, // i starts at 0 mlua::Value::Nil => ud.clone(),
Some("components..."), _ => todo!(),
Arc::new(elua::Error::runtime("provided component is not userdata")), };
))?;
let comp_borrow = {
if let Ok(as_comp) = ud.get::<_, elua::Function>(FN_NAME_INTERNAL_AS_COMPONENT) {
let ud = match as_comp.exec(ud.clone())? {
elua::Value::Userdata(ud) => ud,
elua::Value::Nil => ud.clone(),
_ => todo!(),
};
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())? ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
} else { } else {
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())? ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
} }
}; };
let reflect = comp_borrow.reflect_branch.as_component_unchecked(); let reflect = comp_borrow.reflect_branch.as_component_unchecked();
let refl_data = comp_borrow.data.unwrap(); let refl_data = comp_borrow.data.unwrap();
reflect.bundle_insert(&mut bundle, refl_data); reflect.bundle_insert(&mut bundle, refl_data);
}
// defer the entity spawn
// SAFETY: Commands borrows Entities from World, the resource borrows from the world resources,
// They are borrowing different parts of World.
let world_ptr: *mut World = world.deref_mut();
let mut commands_queue = world.get_resource_mut::<CommandQueue>().unwrap();
let mut commands = Commands::new(&mut commands_queue, unsafe { &mut *world_ptr });
let entity = commands.spawn(bundle);
Ok(ScriptEntity(entity))
});
methods.add_method_mut(
"view",
|lua, this, (system, queries): (mlua::Function, mlua::MultiValue)| {
if queries.is_empty() {
return Err(mlua::Error::BadArgument {
to: Some("World:view".into()),
pos: 2,
name: Some("query...".into()),
cause: Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage(
"no component types provided".into(),
))),
});
} }
// defer the entity spawn let world = this.read();
// safety: Commands borrows Entities from World, the resource borrows from the world resouces, let mut view = DynamicViewState::new();
// they are borrowing different parts of World.
let world_ptr: *mut World = world;
let mut commands_queue = world.get_resource_mut::<CommandQueue>();
let mut commands = Commands::new(&mut commands_queue, unsafe { &mut *world_ptr });
let entity = commands.spawn(bundle);
Ok(ScriptEntity(entity)) for (idx, comp) in queries.into_iter().enumerate() {
}) match comp {
.method_mut( mlua::Value::Table(t) => {
"view", let name: String = t.get(mlua::MetaMethod::Type.name())?;
|lua, this, (system, queries): (elua::Function, elua::ValueVec)| {
if queries.is_empty() {
return Err(elua::Error::BadArgument {
func: Some("World:view".to_string()),
arg_index: 2,
arg_name: Some("query...".to_string()),
error: Arc::new(elua::Error::other(WorldError::LuaInvalidUsage(
"no component types provided".to_string(),
))),
});
}
let world = unsafe { this.inner.as_ref() }; let lookup = world.get_resource::<LuaTableProxyLookup>().ok_or(
//let mut view = world.dynamic_view(); mlua::Error::runtime(
let mut view = DynamicViewState::new(); "Unable to lookup table proxy, none were ever registered!",
),
for (idx, comp) in queries.into_iter().enumerate() { )?;
match comp { let info = lookup.comp_info_from_name.get(&name).ok_or_else(|| {
elua::Value::Table(t) => { mlua::Error::BadArgument {
let name: String = t.get(elua::MetaMethod::Name)?; to: Some("World:view".into()),
pos: 2 + idx,
let lookup = world name: Some("query...".into()),
.try_get_resource::<LuaTableProxyLookup>() cause: Arc::new(mlua::Error::external(
.ok_or(elua::Error::runtime( WorldError::LuaInvalidUsage(format!(
"Unable to lookup table proxy, none were ever registered!",
))?;
let info = lookup.comp_info_from_name.get(&name).ok_or_else(
|| elua::Error::BadArgument {
func: Some("World:view".to_string()),
arg_index: 2 + idx as i32,
arg_name: Some("query...".to_string()),
error: Arc::new(elua::Error::Runtime(format!(
"the 'Table' with name {} is unknown to the engine!", "the 'Table' with name {} is unknown to the engine!",
name name
))), )),
}, )),
)?; }
})?;
let dyn_type = QueryDynamicType::from_info(info.clone()); let dyn_type = QueryDynamicType::from_info(info.clone());
view.push(dyn_type); view.push(dyn_type);
}
elua::Value::Userdata(ud) => {
let reflect = ud
.execute_function::<_, ScriptBorrow>(
FN_NAME_INTERNAL_REFLECT_TYPE,
(),
)
.expect("Type does not implement 'reflect_type' properly");
let refl_comp = reflect.reflect_branch.as_component_unchecked();
let dyn_type = QueryDynamicType::from_info(refl_comp.info);
view.push(dyn_type);
}
_ => todo!(),
} }
} mlua::Value::UserData(ud) => {
let reflect = ud
.call_function::<_, ScriptBorrow>(
FN_NAME_INTERNAL_REFLECT_TYPE,
(),
)
.expect("Type does not implement 'reflect_type' properly");
let refl_comp = reflect.reflect_branch.as_component_unchecked();
let iter = view.into_iter(); let dyn_type = QueryDynamicType::from_info(refl_comp.info);
let mut reflected_iter = ReflectedIterator { view.push(dyn_type);
world: this.clone(),
dyn_view: DynamicViewStateIter::from(iter),
reflected_components: None,
};
let mut current = world.current_tick();
let mut has_ticked = false;
while let Some(row) = reflected_iter.next_lua(lua) {
let r = row
.row
.into_iter()
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
.collect::<Vec<_>>();
let (values, ptrs) =
itertools::multiunzip::<(Vec<elua::Value>, Vec<NonNull<()>>), _>(r);
let mult_val = elua::ValueVec::from(values);
let res: elua::ValueVec = system.exec(mult_val)?;
// if values were returned, find the type in the type registry, and apply the new values
if res.len() <= ptrs.len() {
// we only want to tick one time per system
if !has_ticked {
current = world.tick();
has_ticked = true;
}
for (comp, ptr) in res.into_iter().zip(ptrs) {
let lua_typeid = match &comp {
elua::Value::Userdata(ud) => {
let lua_comp = reflect_user_data(ud);
let refl_comp =
lua_comp.reflect_branch.as_component_unchecked();
refl_comp.info.type_id().as_rust()
},
elua::Value::Table(tbl) => {
let name: String = tbl.get(elua::MetaMethod::Name)?;
let lookup = world.get_resource::<LuaTableProxyLookup>();
*lookup.typeid_from_name.get(&name).unwrap()
},
_ => {
panic!("A userdata or table value was not returned!");
// TODO: Handle properly
}
};
// update the component tick
let world = unsafe { this.inner.as_mut() };
let arch = world.entity_archetype_mut(row.entity).unwrap();
let idx = arch.entity_indexes().get(&row.entity).unwrap().clone();
let c = arch.get_column_mut(lua_typeid).unwrap();
c.entity_ticks[idx.0 as usize] = current;
// apply the new component data
let reg = this.as_ref().get_resource::<TypeRegistry>();
let reg_type = reg.get_type(lua_typeid).unwrap();
let proxy = reg_type
.get_data::<ReflectLuaProxy>()
// this should actually be safe since the ReflectedIterator
// attempts to get the type data before it is tried here
.expect("Type does not have ReflectLuaProxy as a TypeData");
(proxy.fn_apply)(lua, ptr, &comp)?;
}
} else {
let msg = format!(
"Too many arguments were returned from the World view!
At most, the expected number of results is {}.",
ptrs.len()
);
return Err(elua::Error::Runtime(msg));
} }
_ => todo!(),
} }
Ok(())
},
)
.method_mut("resource", |lua, this, (ty,): (elua::Value,)| {
let reflect = match ty {
elua::Value::Userdata(ud) => ud
.execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
.expect("Type does not implement 'reflect_type' properly"),
elua::Value::Table(t) => {
let f: elua::Function = t.get(FN_NAME_INTERNAL_REFLECT_TYPE)?;
f.exec::<_, ScriptBorrow>(())
.expect("Type does not implement 'reflect_type' properly")
}
_ => {
panic!("how");
}
};
/* let reflect = ty
.execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
.expect("Type does not implement 'reflect_type' properly"); */
let res = reflect.reflect_branch.as_resource_unchecked();
if let Some(res_ptr) = res.reflect_ptr(this.as_mut()) {
let reg_type = this
.as_ref()
.get_type::<RegisteredType>(reflect.reflect_branch.reflect_type_id())
.expect("Resource is not type registered!");
let proxy = reg_type
.get_data::<ReflectLuaProxy>()
.expect("Type does not have ReflectLuaProxy as a TypeData");
(proxy.fn_as_lua)(lua, res_ptr.cast()).and_then(|ud| ud.as_lua(lua))
} else {
// if the resource is not found in the world, return nil
Ok(elua::Value::Nil)
} }
})
.method_mut("add_resource", |_, this, res: elua::Value| { let iter = view.into_iter();
let reflect = match res { let mut reflected_iter = ReflectedIterator {
elua::Value::Userdata(ud) => ud world: this.clone(),
.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) dyn_view: DynamicViewStateIter::from(iter),
.expect("Type does not implement 'reflect_type' properly"), reflected_components: None,
elua::Value::Table(t) => {
let f: elua::Function = t.get(FN_NAME_INTERNAL_REFLECT)?;
f.exec::<_, ScriptBorrow>(())
.expect("Type does not implement 'reflect_type' properly")
}
_ => {
panic!("how");
}
}; };
let data = reflect.data let mut current = world.current_tick();
.expect("Its expected that 'FN_NAME_INTERNAL_REFLECT' returns data in World:add_resource"); let mut has_ticked = false;
let res = reflect.reflect_branch.as_resource() while let Some(row) = reflected_iter.next_lua(lua) {
.ok_or(elua::Error::runtime("Provided type is not a resource!"))?; let r = row
.row
.into_iter()
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
.collect::<Vec<_>>();
let (values, ptrs) =
itertools::multiunzip::<(Vec<mlua::Value>, Vec<NonNull<()>>), _>(r);
let mult_val = mlua::MultiValue::from_vec(values);
let res: mlua::MultiValue = system.call(mult_val)?;
let world = this.as_mut(); // if values were returned, find the type in the type registry, and apply the new values
res.insert(world, data); if res.len() <= ptrs.len() {
// we only want to tick one time per system
if !has_ticked {
current = world.tick();
has_ticked = true;
}
for (comp, ptr) in res.into_iter().zip(ptrs) {
let lua_typeid = match &comp {
mlua::Value::UserData(ud) => {
let lua_comp = reflect_user_data(ud);
let refl_comp =
lua_comp.reflect_branch.as_component_unchecked();
refl_comp.info.type_id().as_rust()
}
mlua::Value::Table(tbl) => {
let name: String = tbl.get(mlua::MetaMethod::Type.name())?;
let lookup = world.get_resource::<LuaTableProxyLookup>().unwrap();
*lookup.typeid_from_name.get(&name).unwrap()
}
_ => {
panic!("A userdata or table value was not returned!");
// TODO: Handle properly
}
};
// update the component tick
let mut world = this.write();
let arch = world.entity_archetype_mut(row.entity).unwrap();
let idx = arch.entity_indexes().get(&row.entity).unwrap().clone();
let c = arch.get_column_mut(lua_typeid).unwrap();
c.entity_ticks[idx.0 as usize] = current;
// apply the new component data
let reg = world.get_resource::<TypeRegistry>().unwrap();
let reg_type = reg.get_type(lua_typeid).unwrap();
let proxy = reg_type
.get_data::<ReflectLuaProxy>()
// this should actually be safe since the ReflectedIterator
// attempts to get the type data before it is tried here
.expect("Type does not have ReflectLuaProxy as a TypeData");
(proxy.fn_apply)(lua, ptr, &comp)?;
}
} else {
let msg = format!(
"Too many arguments were returned from the World view!
At most, the expected number of results is {}.",
ptrs.len()
);
return Err(mlua::Error::runtime(msg));
}
}
Ok(()) Ok(())
}) },
.method_mut("request_res", |lua, this, path: String| { );
let world: &mut World = this.as_mut(); methods.add_method_mut("resource", |lua, this, (ty,): (mlua::Value,)| {
let man = world.get_resource_mut::<ResourceManager>(); let reflect = match ty {
let handle = man.request_raw(&path).unwrap(); mlua::Value::UserData(ud) => ud
.call_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
.expect("Type does not implement 'reflect_type' properly"),
mlua::Value::Table(t) => {
let f: mlua::Function = t.get(FN_NAME_INTERNAL_REFLECT_TYPE)?;
f.call::<_, ScriptBorrow>(())
.expect("Type does not implement 'reflect_type' properly")
}
_ => {
panic!("how");
}
};
/* let reflect = ty
.execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
.expect("Type does not implement 'reflect_type' properly"); */
// get the actual resource handle wrapper let mut world = this.write();
let registry = world.get_resource::<TypeRegistry>(); let res = reflect.reflect_branch.as_resource_unchecked();
let ty = registry.get_type(handle.resource_type_id()) if let Some(res_ptr) = res.reflect_ptr(&mut world) {
.expect("Could not find asset type in registry"); let reg_type = world
let data = ty.get_data::<LuaResHandleToComponent>() .get_type::<RegisteredType>(reflect.reflect_branch.reflect_type_id())
.expect("Asset type does not have 'LuaResHandleToComponent' as TypeData"); .expect("Resource is not type registered!");
let proxy = reg_type
.get_data::<ReflectLuaProxy>()
.expect("Type does not have ReflectLuaProxy as a TypeData");
Ok((data.fn_to_lua)(lua, handle).unwrap()) (proxy.fn_as_lua)(lua, res_ptr.cast()).and_then(|ud| ud.into_lua(lua))
}); } else {
// if the resource is not found in the world, return nil
Ok(mlua::Value::Nil)
}
});
methods.add_method_mut("add_resource", |_, this, res: mlua::Value| {
let reflect = match res {
mlua::Value::UserData(ud) => ud
.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
.expect("Type does not implement 'reflect_type' properly"),
mlua::Value::Table(t) => {
let f: mlua::Function = t.get(FN_NAME_INTERNAL_REFLECT)?;
f.call::<_, ScriptBorrow>(())
.expect("Type does not implement 'reflect_type' properly")
}
_ => {
panic!("how");
}
};
let data = reflect.data.expect(
"Its expected that 'FN_NAME_INTERNAL_REFLECT' returns data in World:add_resource",
);
let res = reflect
.reflect_branch
.as_resource()
.ok_or(mlua::Error::runtime("Provided type is not a resource!"))?;
let mut world = this.write();
res.insert(&mut world, data);
Ok(())
});
methods.add_method_mut("request_res", |lua, this, path: String| {
let world = this.write();
let man = world.get_resource_mut::<ResourceManager>().unwrap();
let handle = man.request_raw(&path).unwrap();
// get the actual resource handle wrapper
let registry = world.get_resource::<TypeRegistry>().unwrap();
let ty = registry
.get_type(handle.resource_type_id())
.expect("Could not find asset type in registry");
let data = ty
.get_data::<LuaResHandleToComponent>()
.expect("Asset type does not have 'LuaResHandleToComponent' as TypeData");
Ok((data.fn_to_lua)(lua, handle).unwrap())
});
} }
} }

View File

@ -1,21 +1,23 @@
use std::{any::TypeId, ops::Deref}; use std::{any::TypeId, ops::Deref};
use elua::{AnyUserdata, AsLua, FromLua, State, Value}; //use mlua::{AnyUserData, IntoLua, FromLua, Lua, Value};
use lyra_resource::{gltf::{Gltf, Material, Mesh}, Texture, ResHandle, UntypedResHandle}; use lyra_resource::{gltf::{Gltf, Material, Mesh}, Texture, ResHandle, UntypedResHandle};
use lyra_game::scene::SceneGraph; use lyra_game::scene::SceneGraph;
use lyra_reflect::{Reflect, TypeData}; use lyra_reflect::{Reflect, TypeData};
use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct}; use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct};
use crate::{lua::{LuaWrapper, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, lyra_engine, ScriptBorrow}; use crate::{lua::{Error, LuaWrapper, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, lyra_engine, ScriptBorrow};
use crate as lyra_scripting; use crate as lyra_scripting;
use mlua::IntoLua;
pub struct LuaResHandleToComponent { pub struct LuaResHandleToComponent {
/// Create the userdata component that /// Create the userdata component that
pub fn_to_lua: fn(&State, UntypedResHandle) -> Option<AnyUserdata>, pub fn_to_lua: fn(&mlua::Lua, UntypedResHandle) -> Option<mlua::AnyUserData>,
} }
impl LuaResHandleToComponent { impl LuaResHandleToComponent {
pub fn new(f: fn(&State, UntypedResHandle) -> Option<AnyUserdata>) -> Self { pub fn new(f: fn(&mlua::Lua, UntypedResHandle) -> Option<mlua::AnyUserData>) -> Self {
Self { Self {
fn_to_lua: f fn_to_lua: f
} }
@ -53,16 +55,12 @@ impl From<UntypedResHandle> for LuaResHandle {
} }
} }
impl elua::Userdata for LuaResHandle { impl mlua::UserData for LuaResHandle {
fn name() -> String { fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
"Handle".to_string() fields.add_field_method_get("path", |_, this| Ok(this.path()));
} fields.add_field_method_get("version", |_, this| Ok(this.version()));
fields.add_field_method_get("uuid", |_, this| Ok(this.uuid().to_string()));
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { fields.add_field_method_get("state", |_, this| {
builder.field_getter("path", |_, this| Ok(this.path()));
builder.field_getter("version", |_, this| Ok(this.version()));
builder.field_getter("uuid", |_, this| Ok(this.uuid().to_string()));
builder.field_getter("state", |_, this| {
let name = if this.is_loaded() { let name = if this.is_loaded() {
"ready" "ready"
} else if this.get_error().is_some() { } else if this.get_error().is_some() {
@ -71,41 +69,43 @@ impl elua::Userdata for LuaResHandle {
Ok(name) Ok(name)
}); });
}
builder.method("is_watched", |_, this, ()| { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("is_watched", |_, this, ()| {
Ok(this.is_watched()) Ok(this.is_watched())
}); });
builder.method("is_loaded", |_, this, ()| { methods.add_method("is_loaded", |_, this, ()| {
Ok(this.is_loaded()) Ok(this.is_loaded())
}); });
builder.method("wait_until_loaded", |_, this, ()| { methods.add_method("wait_until_loaded", |_, this, ()| {
this.wait_recurse_dependencies_load(); this.wait_recurse_dependencies_load();
Ok(()) Ok(())
}); });
builder.method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| { methods.add_method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| {
let handle = &this.0; let handle = &this.0;
if let Some(handle) = handle.as_typed::<SceneGraph>() { if let Some(handle) = handle.as_typed::<SceneGraph>() {
LuaSceneHandle(handle).as_lua(lua) LuaSceneHandle(handle).into_lua(lua)
} else if let Some(handle) = handle.as_typed::<Gltf>() { } else if let Some(handle) = handle.as_typed::<Gltf>() {
LuaGltfHandle(handle).as_lua(lua) LuaGltfHandle(handle).into_lua(lua)
} else { } else {
Ok(elua::Value::Nil) Ok(mlua::Value::Nil)
} }
}); });
} }
} }
impl<'a> FromLua<'a> for LuaResHandle { impl<'a> mlua::FromLua<'a> for LuaResHandle {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> { fn from_lua(val: mlua::Value<'a>, _: &'a mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name(); let tyname = val.type_name();
let ud = val.as_userdata() let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("Handle", &tyname))?; .ok_or(mlua::Error::external(Error::type_mismatch("Handle", &tyname)))?;
let handle = ud.as_ref::<LuaResHandle>()?; let handle = ud.borrow::<LuaResHandle>()?;
Ok(handle.clone()) Ok(handle.clone())
} }
@ -121,11 +121,11 @@ lua_wrap_handle!(Mesh,
(material, { (material, {
data.material.clone() data.material.clone()
.map(|v| LuaMaterialHandle(v.clone())) .map(|v| LuaMaterialHandle(v.clone()))
.as_lua(lua) .into_lua(lua)
}) })
}, },
{ {
builder.method("indices", |lua, this, ()| { methods.add_method("indices", |lua, this, ()| {
if let Some(data) = this.0.data_ref() { if let Some(data) = this.0.data_ref() {
let table = lua.create_table()?; let table = lua.create_table()?;
@ -133,21 +133,21 @@ lua_wrap_handle!(Mesh,
Some(lyra_resource::gltf::MeshIndices::U16(v)) => { Some(lyra_resource::gltf::MeshIndices::U16(v)) => {
for (i, ind) in v.iter().enumerate() { for (i, ind) in v.iter().enumerate() {
let i = i as i64 + 1; // lua indexes start at 1 let i = i as i64 + 1; // lua indexes start at 1
table.raw_seti(i, *ind)?; table.raw_set(i, *ind)?;
} }
}, },
Some(lyra_resource::gltf::MeshIndices::U32(v)) => { Some(lyra_resource::gltf::MeshIndices::U32(v)) => {
for (i, ind) in v.iter().enumerate() { for (i, ind) in v.iter().enumerate() {
let i = i as i64 + 1; // lua indexes start at 1 let i = i as i64 + 1; // lua indexes start at 1
table.raw_seti(i, *ind)?; table.raw_set(i, *ind)?;
} }
}, },
None => {}, None => {},
} }
Ok(Value::Table(table)) Ok(mlua::Value::Table(table))
} else { } else {
Ok(Value::Nil) Ok(mlua::Value::Nil)
} }
}); });
@ -167,17 +167,17 @@ lua_wrap_handle!(Material,
(base_color_texture, { (base_color_texture, {
data.base_color_texture.clone() data.base_color_texture.clone()
.map(|v| LuaTextureHandle(v.clone())) .map(|v| LuaTextureHandle(v.clone()))
.as_lua(lua) .into_lua(lua)
}), }),
(metallic_roughness_texture, { (metallic_roughness_texture, {
data.metallic_roughness_texture.clone() data.metallic_roughness_texture.clone()
.map(|v| LuaTextureHandle(v.clone())) .map(|v| LuaTextureHandle(v.clone()))
.as_lua(lua) .into_lua(lua)
}), }),
(pbr_glossiness, { (pbr_glossiness, {
data.pbr_glossiness.clone() data.pbr_glossiness.clone()
.map(|v| LuaPbrGlossiness(v.clone())) .map(|v| LuaPbrGlossiness(v.clone()))
.as_lua(lua) .into_lua(lua)
}), }),
alpha_cutoff, alpha_cutoff,
(alpha_mode, { (alpha_mode, {
@ -186,55 +186,57 @@ lua_wrap_handle!(Material,
lyra_resource::gltf::AlphaMode::Opaque => "opaque", lyra_resource::gltf::AlphaMode::Opaque => "opaque",
lyra_resource::gltf::AlphaMode::Mask => "mask", lyra_resource::gltf::AlphaMode::Mask => "mask",
lyra_resource::gltf::AlphaMode::Blend => "blend", lyra_resource::gltf::AlphaMode::Blend => "blend",
}.as_lua(lua) }.into_lua(lua)
}), }),
(specular, { (specular, {
data.specular.clone() data.specular.clone()
.map(|v| LuaSpecular(v.clone())) .map(|v| LuaSpecular(v.clone()))
.as_lua(lua) .into_lua(lua)
}), }),
} }
); );
lua_wrap_handle!(Gltf, { lua_wrap_handle!(Gltf, {
builder.method("scenes", |lua, this, ()| { methods.add_method("scenes", |lua, this, ()| {
if let Some(data) = this.0.data_ref() { if let Some(data) = this.0.data_ref() {
let table = lua.create_table()?; let table = lua.create_table()?;
for (i, scene) in data.scenes.iter().enumerate() { for (i, scene) in data.scenes.iter().enumerate() {
let i = i as i64 + 1; // lua indexes start at 1 let i = i as i64 + 1; // lua indexes start at 1
table.raw_seti(i, LuaSceneHandle(scene.clone()))?; table.raw_set(i, LuaSceneHandle(scene.clone()))?;
} }
Ok(Value::Table(table)) Ok(mlua::Value::Table(table))
} else { } else {
Ok(Value::Nil) Ok(mlua::Value::Nil)
} }
}).method("materials", |lua, this, ()| { });
methods.add_method("materials", |lua, this, ()| {
if let Some(data) = this.0.data_ref() { if let Some(data) = this.0.data_ref() {
let table = lua.create_table()?; let table = lua.create_table()?;
for (i, mat) in data.materials.iter().enumerate() { for (i, mat) in data.materials.iter().enumerate() {
let i = i as i64 + 1; // lua indexes start at 1 let i = i as i64 + 1; // lua indexes start at 1
table.raw_seti(i, LuaMaterialHandle(mat.clone()))?; table.raw_set(i, LuaMaterialHandle(mat.clone()))?;
} }
Ok(Value::Table(table)) Ok(mlua::Value::Table(table))
} else { } else {
Ok(Value::Nil) Ok(mlua::Value::Nil)
} }
}).method("meshes", |lua, this, ()| { });
methods.add_method("meshes", |lua, this, ()| {
if let Some(data) = this.0.data_ref() { if let Some(data) = this.0.data_ref() {
let table = lua.create_table()?; let table = lua.create_table()?;
for (i, mesh) in data.meshes.iter().enumerate() { for (i, mesh) in data.meshes.iter().enumerate() {
let i = i as i64 + 1; // lua indexes start at 1 let i = i as i64 + 1; // lua indexes start at 1
table.raw_seti(i, LuaMeshHandle(mesh.clone()))?; table.raw_set(i, LuaMeshHandle(mesh.clone()))?;
} }
Ok(Value::Table(table)) Ok(mlua::Value::Table(table))
} else { } else {
Ok(Value::Nil) Ok(mlua::Value::Nil)
} }
}); });
}); });

View File

@ -21,15 +21,15 @@ impl std::ops::DerefMut for LuaDeltaTime {
} }
} }
impl<'lua> elua::FromLua<'lua> for LuaDeltaTime { impl<'lua> mlua::FromLua<'lua> for LuaDeltaTime {
fn from_lua(_: &'lua elua::State, _: elua::Value<'lua>) -> elua::Result<Self> { fn from_lua(_: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> {
todo!() todo!()
} }
} }
impl<'lua> elua::AsLua<'lua> for LuaDeltaTime { impl<'lua> mlua::IntoLua<'lua> for LuaDeltaTime {
fn as_lua(self, _: &'lua elua::State) -> elua::Result<elua::Value<'lua>> { fn into_lua(self, _: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
Ok(elua::Value::Number(*self.0 as f64)) Ok(mlua::Value::Number(*self.0 as f64))
} }
} }

View File

@ -1,5 +1,6 @@
use lyra_game::input::{keycode_from_str, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, ActionState, LayoutId, MouseAxis, MouseInput}; use lyra_game::input::{keycode_from_str, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, ActionState, LayoutId, MouseAxis, MouseInput};
use crate::lyra_engine; use mlua::IntoLua;
use crate::{lua::Error, lyra_engine};
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
@ -10,27 +11,23 @@ pub struct LuaActionHandler {
handler: ActionHandler handler: ActionHandler
} }
impl elua::Userdata for LuaActionHandler { impl mlua::UserData for LuaActionHandler {
fn name() -> String { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
"ActionHandler".to_string() methods.add_function("new", |_, table: mlua::Table| {
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
builder.function("new", |_, table: elua::Table| {
let mut handler = ActionHandler::new(); let mut handler = ActionHandler::new();
// create the layouts and add them to the handler // create the layouts and add them to the handler
let layouts = table.get::<_, elua::Table>("layouts") let layouts = table.get::<_, mlua::Table>("layouts")
.map_err(|_| elua::Error::runtime("missing 'layouts' in ActionHandler table"))?; .map_err(|_| mlua::Error::runtime("missing 'layouts' in ActionHandler table"))?;
for layout_id in layouts.sequence_iter::<u32>() { for layout_id in layouts.sequence_values::<u32>() {
let layout_id = layout_id?; let layout_id = layout_id?;
handler.add_layout(LayoutId(layout_id)); handler.add_layout(LayoutId(layout_id));
} }
// add the actions to the handler // add the actions to the handler
let actions = table.get::<_, elua::Table>("actions") let actions = table.get::<_, mlua::Table>("actions")
.map_err(|_| elua::Error::runtime("missing 'actions' in ActionHandler table"))?; .map_err(|_| mlua::Error::runtime("missing 'actions' in ActionHandler table"))?;
for pair in actions.pairs::<String, String>() { for pair in actions.pairs::<String, String>() {
let (action_lbl, action_type) = pair?; let (action_lbl, action_type) = pair?;
let action_type = action_type.to_lowercase(); let action_type = action_type.to_lowercase();
@ -45,9 +42,9 @@ impl elua::Userdata for LuaActionHandler {
} }
// find the mappings and start processing them // find the mappings and start processing them
let mappings= table.get::<_, elua::Table>("mappings") let mappings= table.get::<_, mlua::Table>("mappings")
.map_err(|_| elua::Error::runtime("missing 'mappings' in ActionHandler table"))?; .map_err(|_| mlua::Error::runtime("missing 'mappings' in ActionHandler table"))?;
for (map_id, tbl) in mappings.sequence_iter::<elua::Table>().enumerate() { for (map_id, tbl) in mappings.sequence_values::<mlua::Table>().enumerate() {
let tbl = tbl?; let tbl = tbl?;
let layout_id = tbl.get::<_, u32>("layout")?; let layout_id = tbl.get::<_, u32>("layout")?;
@ -55,16 +52,16 @@ impl elua::Userdata for LuaActionHandler {
// find the binds and start processing them // find the binds and start processing them
// the keys are used as the action names, and then the value is an array (lua table) // the keys are used as the action names, and then the value is an array (lua table)
let binds_tbl = tbl.get::<_, elua::Table>("binds") let binds_tbl = tbl.get::<_, mlua::Table>("binds")
.map_err(|_| elua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?; .map_err(|_| mlua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?;
for pair in binds_tbl.pairs::<String, elua::Table>() { for pair in binds_tbl.pairs::<String, mlua::Table>() {
let (action_lbl, input_binds) = pair?; let (action_lbl, input_binds) = pair?;
for input in input_binds.sequence_iter::<String>() { for input in input_binds.sequence_values::<String>() {
let input = input?.to_lowercase(); let input = input?.to_lowercase();
let action = handler.action(&action_lbl) let action = handler.action(&action_lbl)
.ok_or(elua::Error::Runtime(format!("Unknown action specified in mapping binds: {}", action_lbl)))?; .ok_or(mlua::Error::runtime(format!("Unknown action specified in mapping binds: {}", action_lbl)))?;
let mut binds = Vec::new(); let mut binds = Vec::new();
@ -77,18 +74,18 @@ impl elua::Userdata for LuaActionHandler {
let axis_name = input_split[2]; let axis_name = input_split[2];
let src = process_axis_string(input_name, axis_name) let src = process_axis_string(input_name, axis_name)
.ok_or(elua::Error::Runtime(format!("invalid bind '{input_name}', unable to find device or axis for device")))?; .ok_or(mlua::Error::runtime(format!("invalid bind '{input_name}', unable to find device or axis for device")))?;
binds.push(src.into_binding()); binds.push(src.into_binding());
} else { } else {
// splits 'down=1' into 'down' and '1' // splits 'down=1' into 'down' and '1'
let (button, val_str) = button.split_once("=") let (button, val_str) = button.split_once("=")
.ok_or(elua::Error::Runtime(format!("invalid bind string for Axis Action: '{input}' (expected '=' with float)")))?; .ok_or(mlua::Error::runtime(format!("invalid bind string for Axis Action: '{input}' (expected '=' with float)")))?;
let val = val_str.parse::<f32>() let val = val_str.parse::<f32>()
.map_err(|e| elua::Error::Runtime(format!("invalid bind string for Axis Action: '{input}' ({e})")))?; .map_err(|e| mlua::Error::runtime(format!("invalid bind string for Axis Action: '{input}' ({e})")))?;
let src = process_keyboard_string(button) let src = process_keyboard_string(button)
.ok_or(elua::Error::Runtime(format!("invalid key in bind: '{button}'")))?; .ok_or(mlua::Error::runtime(format!("invalid key in bind: '{button}'")))?;
binds.push(src.into_binding_modifier(val)); binds.push(src.into_binding_modifier(val));
} }
} else { } else {
@ -105,23 +102,29 @@ impl elua::Userdata for LuaActionHandler {
Ok(LuaActionHandler { Ok(LuaActionHandler {
handler, handler,
}) })
}) });
.method("get_axis", |_, this, action: String| {
methods.add_method("get_axis", |_, this, action: String| {
Ok(this.handler.get_axis_modifier(action)) Ok(this.handler.get_axis_modifier(action))
}) });
.method("is_pressed", |_, this, action: String| {
methods.add_method("is_pressed", |_, this, action: String| {
Ok(this.handler.is_action_pressed(action)) Ok(this.handler.is_action_pressed(action))
}) });
.method("was_just_pressed", |_, this, action: String| {
methods.add_method("was_just_pressed", |_, this, action: String| {
Ok(this.handler.was_action_just_pressed(action)) Ok(this.handler.was_action_just_pressed(action))
}) });
.method("was_just_released", |_, this, action: String| {
methods.add_method("was_just_released", |_, this, action: String| {
Ok(this.handler.was_action_just_released(action)) Ok(this.handler.was_action_just_released(action))
}) });
.method("get_just_pressed", |_, this, action: String| {
methods.add_method("get_just_pressed", |_, this, action: String| {
Ok(this.handler.get_just_pressed_modifier(action)) Ok(this.handler.get_just_pressed_modifier(action))
}) });
.method("get_action_state", |lua, this, action: String| {
methods.add_method("get_action_state", |lua, this, action: String| {
let state = this.handler.get_action_state(action); let state = this.handler.get_action_state(action);
let (name, val) = match state { let (name, val) = match state {
@ -133,28 +136,29 @@ impl elua::Userdata for LuaActionHandler {
ActionState::Other(v) => ("Other", Some(v)), ActionState::Other(v) => ("Other", Some(v)),
}; };
let mut multi = elua::ValueVec::new(); let mut multi = Vec::new();
multi.push_val(lua, name)?; multi.push(name.into_lua(lua)?);
multi.push_val(lua, val)?; multi.push(val.into_lua(lua)?);
Ok(elua::Value::Multi(multi)) Ok(mlua::MultiValue::from_vec(multi))
})
.method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(ScriptBorrow::from_resource::<ActionHandler>(Some(this.handler.clone())))
})
.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(ScriptBorrow::from_resource::<ActionHandler>(None))
}); });
methods.add_method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(ScriptBorrow::from_resource::<ActionHandler>(Some(this.handler.clone())))
});
methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(ScriptBorrow::from_resource::<ActionHandler>(None))
});
} }
} }
impl<'a> elua::FromLua<'a> for LuaActionHandler { impl<'a> mlua::FromLua<'a> for LuaActionHandler {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> { fn from_lua(val: mlua::Value<'a>, _: &'a mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name(); let tyname = val.type_name();
let ud = val.as_userdata() let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("ActionHandler", &tyname))?; .ok_or(mlua::Error::external(Error::type_mismatch("ActionHandler", &tyname)))?;
let handle = ud.as_ref::<LuaActionHandler>()?; let handle = ud.borrow::<LuaActionHandler>()?;
Ok(handle.clone()) Ok(handle.clone())
} }

View File

@ -1,6 +1,7 @@
use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct};
use crate::lyra_engine; use crate::lyra_engine;
use lyra_game::math; use lyra_game::math;
use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct};
use mlua::FromLuaMulti;
use crate as lyra_scripting; use crate as lyra_scripting;
@ -20,13 +21,15 @@ wrap_lua_struct!(
{ {
lua_vec_wrap_extension!(math::Vec2, LuaVec2); lua_vec_wrap_extension!(math::Vec2, LuaVec2);
builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| { methods.add_method_mut("move_by", |lua, this, vals: mlua::MultiValue| {
let vals_clone = vals.clone(); let vals_clone = vals.clone();
if let Some((x, y)) = vals.try_into_vals::<(f32, f32)>(lua)? { if let Ok((x, y)) = <(f32, f32) as FromLuaMulti>::from_lua_multi(vals, lua) {
this.x += x; this.x += x;
this.y += y; this.y += y;
} else if let Some(v) = vals_clone.try_into_vals::<Self>(lua)? { } else if let Ok(v) = Self::from_lua_multi(vals_clone, lua) {
this.0 += v.0; this.0 += v.0;
} else {
todo!("handle invalid argument error");
} }
Ok(()) Ok(())
@ -50,14 +53,16 @@ wrap_lua_struct!(
{ {
lua_vec_wrap_extension!(math::Vec3, LuaVec3); lua_vec_wrap_extension!(math::Vec3, LuaVec3);
builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| { methods.add_method_mut("move_by", |lua, this, vals: mlua::MultiValue| {
let vals_clone = vals.clone(); let vals_clone = vals.clone();
if let Some((x, y, z)) = vals.try_into_vals::<(f32, f32, f32)>(lua)? { if let Ok((x, y, z)) = <(f32, f32, f32) as FromLuaMulti>::from_lua_multi(vals, lua) {
this.x += x; this.x += x;
this.y += y; this.y += y;
this.z += z; this.z += z;
} else if let Some(v) = vals_clone.try_into_vals::<Self>(lua)? { } else if let Ok(v) = Self::from_lua_multi(vals_clone, lua) {
this.0 += v.0; this.0 += v.0;
} else {
todo!("handle invalid argument error");
} }
Ok(()) Ok(())
@ -98,70 +103,70 @@ wrap_lua_struct!(
), ),
{ {
// manually implemented since Quat doesn't have a `new` function // manually implemented since Quat doesn't have a `new` function
builder.function("new", |_, (x, y, z, w)| { methods.add_function("new", |_, (x, y, z, w)| {
Ok(Self(math::Quat::from_xyzw(x, y, z, w))) Ok(Self(math::Quat::from_xyzw(x, y, z, w)))
}); });
builder.function("from_rotation_x", |_, (rad,)| { methods.add_function("from_rotation_x", |_, (rad,)| {
let q = math::Quat::from_rotation_x(rad); let q = math::Quat::from_rotation_x(rad);
Ok(Self(q)) Ok(Self(q))
}); });
builder.function("from_rotation_y", |_, (rad,)| { methods.add_function("from_rotation_y", |_, (rad,)| {
let q = math::Quat::from_rotation_y(rad); let q = math::Quat::from_rotation_y(rad);
Ok(Self(q)) Ok(Self(q))
}); });
builder.function("from_rotation_z", |_, (rad,)| { methods.add_function("from_rotation_z", |_, (rad,)| {
let q = math::Quat::from_rotation_z(rad); let q = math::Quat::from_rotation_z(rad);
Ok(Self(q)) Ok(Self(q))
}); });
builder.method("dot", |_, this, (rhs,): (Self,)| { methods.add_method("dot", |_, this, (rhs,): (Self,)| {
Ok(this.dot(rhs.0)) Ok(this.dot(rhs.0))
}); });
builder.method("length", |_, this, ()| { methods.add_method("length", |_, this, ()| {
Ok(this.length()) Ok(this.length())
}); });
builder.method("length_squared", |_, this, ()| { methods.add_method("length_squared", |_, this, ()| {
Ok(this.length_squared()) Ok(this.length_squared())
}); });
builder.method_mut("normalize", |_, this, ()| { methods.add_method_mut("normalize", |_, this, ()| {
this.0 = this.normalize(); this.0 = this.normalize();
Ok(()) Ok(())
}); });
builder.method_mut("mult_quat", |_, this, (rhs,): (Self,)| { methods.add_method_mut("mult_quat", |_, this, (rhs,): (Self,)| {
this.0 *= rhs.0; this.0 *= rhs.0;
Ok(()) Ok(())
}); });
builder.method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| { methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| {
Ok(LuaVec3(this.0 * rhs.0)) Ok(LuaVec3(this.0 * rhs.0))
}); });
// manually implemented here since multiplying may not return `Self`. // manually implemented here since multiplying may not return `Self`.
builder.meta_method(elua::MetaMethod::Mul, |lua, this, (val,): (elua::Value,)| { methods.add_meta_method(mlua::MetaMethod::Mul, |lua, this, (val,): (mlua::Value,)| {
use elua::AsLua; use mlua::IntoLua;
match val { match val {
elua::Value::Userdata(ud) => { mlua::Value::UserData(ud) => {
if ud.is::<LuaVec3>()? { if ud.is::<LuaVec3>() {
let v3 = ud.as_ref::<LuaVec3>()?; let v3 = ud.borrow::<LuaVec3>()?;
LuaVec3(this.0 * v3.0) LuaVec3(this.0 * v3.0)
.as_lua(lua) .into_lua(lua)
} else { } else {
let quat = ud.as_ref::<LuaQuat>()?; let quat = ud.borrow::<LuaQuat>()?;
LuaQuat(this.0 * quat.0) LuaQuat(this.0 * quat.0)
.as_lua(lua) .into_lua(lua)
} }
}, },
elua::Value::Number(n) => { mlua::Value::Number(n) => {
LuaQuat(this.0 * (n as f32)) LuaQuat(this.0 * (n as f32))
.as_lua(lua) .into_lua(lua)
}, },
_ => { _ => {
todo!() todo!()
@ -169,7 +174,7 @@ wrap_lua_struct!(
} }
}); });
builder.method("lerp", |_, this, (rhs, alpha): (Self, f32)| { methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| {
Ok(Self(this.lerp(*rhs, alpha))) Ok(Self(this.lerp(*rhs, alpha)))
}); });
} }
@ -180,94 +185,94 @@ wrap_lua_struct!(
derives(PartialEq, Copy), derives(PartialEq, Copy),
metamethods(ToString, Eq), metamethods(ToString, Eq),
{ {
builder.function("default", |_, ()| { methods.add_function("default", |_, ()| {
Ok(Self(math::Transform::default())) Ok(Self(math::Transform::default()))
}); });
builder.function("new", |_, (pos, rot, scale): (LuaVec3, LuaQuat, LuaVec3)| { methods.add_function("new", |_, (pos, rot, scale): (LuaVec3, LuaQuat, LuaVec3)| {
Ok(Self(math::Transform::new(*pos, *rot, *scale))) Ok(Self(math::Transform::new(*pos, *rot, *scale)))
}); });
builder.function("from_translation", |_, (pos,): (LuaVec3,)| { methods.add_function("from_translation", |_, (pos,): (LuaVec3,)| {
Ok(Self(math::Transform::from_translation(*pos))) Ok(Self(math::Transform::from_translation(*pos)))
}); });
builder.function("from_xyz", |_, (x, y, z)| { methods.add_function("from_xyz", |_, (x, y, z)| {
Ok(Self(math::Transform::from_xyz(x, y, z))) Ok(Self(math::Transform::from_xyz(x, y, z)))
}); });
builder.method("clone", |_, this, ()| { methods.add_method("clone", |_, this, ()| {
Ok(this.clone()) Ok(this.clone())
}); });
builder.method("forward", |_, this, ()| { methods.add_method("forward", |_, this, ()| {
Ok(LuaVec3(this.forward())) Ok(LuaVec3(this.forward()))
}); });
builder.method("left", |_, this, ()| { methods.add_method("left", |_, this, ()| {
Ok(LuaVec3(this.left())) Ok(LuaVec3(this.left()))
}); });
builder.method("up", |_, this, ()| { methods.add_method("up", |_, this, ()| {
Ok(LuaVec3(this.up())) Ok(LuaVec3(this.up()))
}); });
builder.method_mut("rotate", |_, this, (quat,): (LuaQuat,)| { methods.add_method_mut("rotate", |_, this, (quat,): (LuaQuat,)| {
this.rotate(*quat); this.rotate(*quat);
Ok(()) Ok(())
}); });
builder.method_mut("rotate_x", |_, this, (deg,): (f32,)| { methods.add_method_mut("rotate_x", |_, this, (deg,): (f32,)| {
this.rotate_x(math::Angle::Degrees(deg)); this.rotate_x(math::Angle::Degrees(deg));
Ok(()) Ok(())
}); });
builder.method_mut("rotate_y", |_, this, (deg,): (f32,)| { methods.add_method_mut("rotate_y", |_, this, (deg,): (f32,)| {
this.rotate_y(math::Angle::Degrees(deg)); this.rotate_y(math::Angle::Degrees(deg));
Ok(()) Ok(())
}); });
builder.method_mut("rotate_z", |_, this, (deg,): (f32,)| { methods.add_method_mut("rotate_z", |_, this, (deg,): (f32,)| {
this.rotate_z(math::Angle::Degrees(deg)); this.rotate_z(math::Angle::Degrees(deg));
Ok(()) Ok(())
}); });
builder.method_mut("rotate_x_rad", |_, this, (rad,): (f32,)| { methods.add_method_mut("rotate_x_rad", |_, this, (rad,): (f32,)| {
this.rotate_x(math::Angle::Radians(rad)); this.rotate_x(math::Angle::Radians(rad));
Ok(()) Ok(())
}); });
builder.method_mut("rotate_y_rad", |_, this, (rad,): (f32,)| { methods.add_method_mut("rotate_y_rad", |_, this, (rad,): (f32,)| {
this.rotate_y(math::Angle::Radians(rad)); this.rotate_y(math::Angle::Radians(rad));
Ok(()) Ok(())
}); });
builder.method_mut("rotate_z_rad", |_, this, (rad,): (f32,)| { methods.add_method_mut("rotate_z_rad", |_, this, (rad,): (f32,)| {
this.rotate_z(math::Angle::Radians(rad)); this.rotate_z(math::Angle::Radians(rad));
Ok(()) Ok(())
}); });
builder.method_mut("translate", |_, this, (x, y, z): (f32, f32, f32)| { methods.add_method_mut("translate", |_, this, (x, y, z): (f32, f32, f32)| {
this.translate(x, y, z); this.translate(x, y, z);
Ok(()) Ok(())
}); });
builder.method("lerp", |_, this, (rhs, alpha): (Self, f32)| { methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| {
Ok(Self(this.lerp(*rhs, alpha))) Ok(Self(this.lerp(*rhs, alpha)))
}); });
// rotate a transform // rotate a transform
builder.meta_method(elua::MetaMethod::Mul, |_, this, (quat,): (LuaQuat,)| { methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (quat,): (LuaQuat,)| {
let mut t = *this; let mut t = *this;
t.rotation *= *quat; t.rotation *= *quat;
Ok(t) Ok(t)
}); });
// move a transform // move a transform
builder.meta_method(elua::MetaMethod::Add, |_, this, (pos,): (LuaVec3,)| { methods.add_meta_method(mlua::MetaMethod::Add, |_, this, (pos,): (LuaVec3,)| {
let mut t = *this; let mut t = *this;
t.translation += *pos; t.translation += *pos;
Ok(t) Ok(t)
}); });
} }
); );

View File

@ -1,6 +1,7 @@
use std::ptr::NonNull; use std::{ops::Deref, ptr::NonNull, sync::Arc};
use lyra_ecs::{World, Entity}; use lyra_ecs::{World, Entity};
use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
#[derive(Clone)] #[derive(Clone)]
pub struct ScriptEntity(pub Entity); pub struct ScriptEntity(pub Entity);
@ -14,33 +15,41 @@ impl std::ops::Deref for ScriptEntity {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct ScriptWorldPtr { pub struct ScriptWorldPtr(Arc<RwLock<NonNull<World>>>);
pub inner: NonNull<World>,
}
impl ScriptWorldPtr { impl ScriptWorldPtr {
/// Creates a world pointer from a world borrow. pub fn read(&self) -> MappedRwLockReadGuard<World> {
pub fn from_ref(world: &World) -> Self { RwLockReadGuard::map(self.0.read(), |p| unsafe { p.as_ref() })
Self {
inner: NonNull::from(world),
}
} }
/// Returns a borrow to the world from the ptr. pub fn write(&self) -> MappedRwLockWriteGuard<World> {
pub fn as_ref(&self) -> &World { RwLockWriteGuard::map(self.0.write(), |p| unsafe { p.as_mut() })
unsafe { self.inner.as_ref() }
}
/// Returns a mutable borrow to the world from the ptr.
pub fn as_mut(&mut self) -> &mut World {
unsafe { self.inner.as_mut() }
} }
} }
impl std::ops::Deref for ScriptWorldPtr { // SAFETY: The inner NonNull pointer is wrapped in an Arc<RwLock<>>
type Target = NonNull<World>; unsafe impl Send for ScriptWorldPtr {}
unsafe impl Sync for ScriptWorldPtr {}
#[derive(Clone)]
pub struct ScriptWorldPtrGuard(ScriptWorldPtr);
impl Deref for ScriptWorldPtrGuard {
type Target = ScriptWorldPtr;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.inner &self.0
}
}
impl ScriptWorldPtrGuard {
/// Creates a new world pointer.
///
/// # Safety
/// The original `&mut World` must not be used while this guard is created.
/// The [World] may only be accessed through this guard or one of its cloned [ScriptWorldPtr]s.
#[allow(clippy::arc_with_non_send_sync)]
pub unsafe fn new(world: &mut World) -> Self {
ScriptWorldPtrGuard(ScriptWorldPtr(Arc::new(RwLock::new(NonNull::from(world)))))
} }
} }

View File

@ -9,4 +9,7 @@ impl std::ops::Deref for ScriptDynamicBundle {
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
// SAFETY: todo!()
unsafe impl Send for ScriptDynamicBundle {}