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
30 changed files with 964 additions and 1682 deletions

18
.vscode/launch.json vendored
View File

@ -4,6 +4,24 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"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",
"request": "launch",

859
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,18 @@
---Return the userdata's name from its metatable
---Return the userdata's name from its metatable.
---
---Returns nil if the userdata doesn't have a metatable.
---@param val userdata
---@return string
---@return string|nil
function udname(val)
return getmetatable(val).__name
local tbl = debug.getmetatable(val)
if tbl == nil then
return nil
end
return tbl.__name
end
function on_init()
local cube = world:request_res("../assets/cube-texture-embedded.gltf")

View File

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

View File

@ -509,7 +509,11 @@ impl World {
/// Attempts to find a resource in the world and returns a NonNull pointer to it
pub unsafe fn get_resource_ptr<T: ResourceObject>(&self) -> Option<NonNull<T>> {
self.resources.get(&TypeId::of::<T>())
.map(|d| unsafe { NonNull::new_unchecked(d.data.as_ptr() as *mut T) })
.map(|d| unsafe {
let data = d.data.borrow();
let ptr = NonNull::from(&data.res);
NonNull::new_unchecked(ptr.as_ptr() as *mut T)
})
}
pub fn archetype_count(&self) -> usize {

View File

@ -72,7 +72,7 @@ impl App {
t.with(filter::Targets::new()
// done by prefix, so it includes all lyra subpackages
.with_target("lyra", Level::DEBUG)
.with_target("wgsl_preprocessor", Level::DEBUG)
.with_target("wgsl_preprocessor", Level::INFO)
.with_target("wgpu", Level::WARN)
.with_target("winit", Level::DEBUG)
.with_default(Level::INFO))

View File

@ -21,6 +21,12 @@ fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &Mo
mouse_scroll_ev.write(event);
}
fn write_key_event(key_buttons: &mut ResMut<InputButtons<winit::keyboard::KeyCode>>, physical_key: winit::keyboard::PhysicalKey, state: winit::event::ElementState) {
if let PhysicalKey::Code(code) = physical_key {
key_buttons.add_input_from_winit(code, state);
}
}
pub fn input_system(
mut key_code_res: ResMut<InputButtons<winit::keyboard::KeyCode>>,
mut mouse_btn_res: ResMut<InputButtons<MouseButton>>,
@ -37,9 +43,7 @@ pub fn input_system(
while let Some(event) = window_ev.read() {
match event.deref() {
WindowEvent::KeyboardInput { event, .. } => {
if let PhysicalKey::Code(code) = event.physical_key {
key_code_res.add_input_from_winit(code, event.state);
}
write_key_event(&mut key_code_res, event.physical_key, event.state);
},
WindowEvent::CursorMoved { position, .. } => {
let exact = MouseExact {
@ -100,6 +104,9 @@ pub fn input_system(
winit::event::DeviceEvent::MouseWheel { delta } => {
write_scroll_delta(&mut mouse_scroll_ev, delta);
},
winit::event::DeviceEvent::Key(key) => {
write_key_event(&mut key_code_res, key.physical_key, key.state);
},
_ => {
todo!("unhandled device event: {:?}", device.event);
}

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::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<std::sync::Mutex<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] {
fn name(&self) -> String {

View File

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

View File

@ -7,8 +7,8 @@ edition = "2021"
[features]
default = ["lua"]
lua = ["dep:elua"]
teal = ["lua", "elua/teal"]
lua = ["dep:mlua"]
#teal = ["lua", "mlua/teal"]
[dependencies]
lyra-scripting-derive = { path = "lyra-scripting-derive" }
@ -22,11 +22,13 @@ tracing = "0.1.37"
atomic_refcell = "0.1.13"
# enabled with lua feature
#mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe?
elua = { path = "./elua", optional = true }
#
#mlua = { version = "0.9.9", features = ["lua54", "send"], optional = true } # luajit maybe?
mlua = { git = "https://github.com/mlua-rs/mlua", rev = "4dddf3c18d4590f7c92214fa592c3faca3d2c812", features = ["lua54", "send"], optional = true } # luajit maybe?
#elua = { path = "./elua", optional = true }
itertools = "0.12.0"
paste = "1.0.14"
parking_lot = "0.12.3"
[dev-dependencies]
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 {
Some(wrap) => {
quote!(#wrap(data.#field).as_lua(lua))
quote!(#wrap(data.#field).into_lua(lua))
},
None => match &g.body {
Some(body) => {
quote!(#body)
},
None => {
quote!(data.#field.clone().as_lua(lua))
quote!(data.#field.clone().into_lua(lua))
}
}
};
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() {
#field_creator
} else {
Ok(Value::Nil)
Ok(mlua::Value::Nil)
}
});
}
@ -186,16 +186,12 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
}
}
impl elua::Userdata for #wrapper_name {
fn name() -> String {
#ud_name.to_string()
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
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| {
impl mlua::UserData for #wrapper_name {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
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()));
fields.add_field_method_get("state", |_, this| {
let name = if this.is_loaded() {
"ready"
} else if this.get_error().is_some() {
@ -206,29 +202,31 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
});
#(#custom_getters)*
}
builder.method("is_watched", |_, this, ()| {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("is_watched", |_, this, ()| {
Ok(this.is_watched())
});
builder.method("is_loaded", |_, this, ()| {
methods.add_method("is_loaded", |_, this, ()| {
Ok(this.is_loaded())
});
builder.method("is_loaded", |_, this, ()| {
methods.add_method("is_loaded", |_, this, ()| {
Ok(this.is_loaded())
});
builder.method("wait_until_loaded", |_, this, ()| {
methods.add_method("wait_until_loaded", |_, this, ()| {
this.wait_recurse_dependencies_load();
Ok(())
});
builder.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
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())))
});
@ -236,12 +234,12 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
}
}
impl<'a> FromLua<'a> for #wrapper_name {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
impl mlua::FromLua for #wrapper_name {
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch(#ud_name, &tyname))?;
let ud = ud.as_ref::<#wrapper_name>()?;
.ok_or(mlua::Error::external(crate::lua::Error::type_mismatch(#ud_name, &tyname)))?;
let ud = ud.borrow::<#wrapper_name>()?;
Ok(ud.clone())
}

View File

@ -156,13 +156,13 @@ impl MetaMethod {
if Self::does_metamethod_have_arg(&self.name) {
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
});
}
} else {
quote! {
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, ()| {
methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, ()| {
#body
});
}
@ -173,12 +173,12 @@ impl MetaMethod {
let body = Self::get_body_for_arg(&self.name, first, quote!(v));
quote! {
builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| {
methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| {
#body
});
}
} else {
// an optional match arm that matches elua::Value:Number
// an optional match arm that matches mlua::Value:Number
let number_arm = {
let num_ident = self.arg.iter().find(|i| {
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));
quote! {
elua::Value::Number(n) => {
mlua::Value::Number(n) => {
#body
},
}
@ -211,7 +211,7 @@ impl MetaMethod {
let body = Self::get_method_body(&self.name, quote!(other.0));
quote! {
if let Ok(other) = ud.as_ref::<#i>() {
if let Ok(other) = ud.borrow::<#i>() {
#body
}
}
@ -219,30 +219,30 @@ impl MetaMethod {
});
quote! {
elua::Value::Userdata(ud) => {
mlua::Value::UserData(ud) => {
#(#if_statements else)*
// this is the body of the else statement
{
// try to get the name of the userdata for the error message
if let Ok(mt) = ud.get_metatable() {
if let Ok(name) = mt.get::<_, String>("__name") {
return Err(elua::Error::BadArgument {
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
arg_index: 2,
arg_name: Some("rhs".to_string()),
error: std::sync::Arc::new(elua::Error::Runtime(
if let Ok(mt) = ud.metatable() {
if let Ok(name) = mt.get::<String>("__name") {
return Err(mlua::Error::BadArgument {
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
pos: 2,
name: Some("rhs".to_string()),
cause: std::sync::Arc::new(mlua::Error::runtime(
format!("cannot multiply with unknown userdata named {}", name)
))
});
}
}
Err(elua::Error::BadArgument {
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
arg_index: 2,
arg_name: Some("rhs".to_string()),
error: std::sync::Arc::new(
elua::Error::runtime("cannot multiply with unknown userdata")
Err(mlua::Error::BadArgument {
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
pos: 2,
name: Some("rhs".to_string()),
cause: std::sync::Arc::new(
mlua::Error::runtime("cannot multiply with unknown userdata")
)
})
}
@ -251,16 +251,16 @@ impl MetaMethod {
};
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 {
#number_arm
#userdata_arm
_ => Err(elua::Error::BadArgument {
func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
arg_index: 2,
arg_name: Some("rhs".to_string()),
error: std::sync::Arc::new(
elua::Error::Runtime(format!("cannot multiply with {}", v.type_name()))
_ => Err(mlua::Error::BadArgument {
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
pos: 2,
name: Some("rhs".to_string()),
cause: std::sync::Arc::new(
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 is = i.to_string();
quote! {
builder.field_getter(#is, |_, this| {
fields.add_field_method_get(#is, |_, this| {
Ok(this.#i)
});
builder.field_setter(#is, |_, this, #i| {
fields.add_field_method_set(#is, |_, this, #i| {
this.#i = #i;
Ok(())
});
@ -431,7 +431,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
quote! {
// arguments for function are not specified since they can be implied from the call
// to new(...)
builder.function("new", |_, ( #(#arg_names_clone),* )| {
methods.add_function("new", |_, ( #(#arg_names_clone),* )| {
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!()
} else {
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())))
});
builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
methods.add_function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
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 {
fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
impl mlua::FromLua for #wrapper_typename {
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
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()),
}
}
}
impl elua::Userdata for #wrapper_typename {
fn name() -> String {
stringify!(#type_name).to_string()
impl mlua::UserData for #wrapper_typename {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
#(#field_get_set_pairs)*
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
#(#field_get_set_pairs)*
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
#lua_reflects
#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>()
}
}
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") {
return quote! {
builder.field("FALSE", #wrapper_ident(#wrapped_path::FALSE));
builder.field("TRUE", #wrapper_ident(#wrapped_path::TRUE));
fields.add_field_method_get("FALSE", #wrapper_ident(#wrapped_path::FALSE));
fields.add_field_method_get("TRUE", #wrapper_ident(#wrapped_path::TRUE));
};
}
@ -87,7 +87,7 @@ impl VecWrapper {
let const_name = cnst.to_string();
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(
quote! {
builder.method("clamp_length",
methods.add_method("clamp_length",
|_, this, (min, max): (#type_id, #type_id)| {
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)| {
Ok(this.abs_diff_eq(rhs.0, max_abs_diff))
});
builder.method("ceil",
methods.add_method("ceil",
|_, this, (): ()| {
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(
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,)| {
Ok(this.angle_between(rhs.0))
});
@ -144,7 +154,7 @@ impl VecWrapper {
if !axis_type_name.contains("u") {
optional_methods.push(
quote! {
builder.method("abs",
methods.add_method("abs",
|_, this, (): ()| {
Ok(#wrapper_ident(this.abs()))
});
@ -154,21 +164,18 @@ impl VecWrapper {
let optional_methods_iter = optional_methods.iter();
quote! {
builder.method("clamp",
methods.add_method("clamp",
|_, this, (min, max): (#wrapper_ident, #wrapper_ident)| {
Ok(#wrapper_ident(this.clamp(min.0, max.0)))
});
// TODO: Not all Vecs have this
/* builder.method("clamp_length",
/* methods.add_method("clamp_length",
|_, this, (min, max): (f32, f32)| {
Ok(#wrapper_ident(this.clamp_length(min, max)))
}); */
builder.method("to_array",
methods.add_method("to_array",
|_, this, (): ()| {
Ok(this.to_array())
});

View File

@ -8,16 +8,23 @@ use crate::ScriptWorldPtr;
pub enum ScriptError {
#[error("{0}")]
#[cfg(feature = "lua")]
MluaError(elua::Error),
LuaError(crate::lua::Error),
#[error("{0}")]
Other(anyhow::Error),
}
#[cfg(feature = "lua")]
impl From<elua::Error> for ScriptError {
fn from(value: elua::Error) -> Self {
ScriptError::MluaError(value)
impl From<mlua::Error> for ScriptError {
fn from(value: mlua::Error) -> Self {
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 use script::*;
use lyra_game::game::Game;
use lyra_game::game::App;
// required for some proc macros :(
#[allow(unused_imports)]
@ -136,15 +136,15 @@ pub trait GameScriptExt {
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)
where
T: ScriptHost,
P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static
{
let world = self.world_mut();
let world = &mut self.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);
}
}

View File

@ -6,44 +6,41 @@ use lyra_reflect::TypeRegistry;
#[cfg(feature = "lua")]
use super::ReflectLuaProxy;
use crate::ScriptWorldPtr;
#[cfg(feature = "lua")]
pub struct ReflectedItem<'a> {
pub struct ReflectedItem {
//pub proxy: &'a ReflectLuaProxy,
pub comp_ptr: NonNull<u8>,
pub comp_val: elua::Value<'a>,
pub comp_val: mlua::Value,
}
#[cfg(feature = "lua")]
pub struct ReflectedRow<'a> {
pub struct ReflectedRow {
pub entity: Entity,
pub row: Vec<ReflectedItem<'a>>,
pub row: Vec<ReflectedItem>,
}
pub struct ReflectedIterator {
pub world: ScriptWorldPtr,
pub struct ReflectedIterator<'a> {
pub world: &'a lyra_ecs::World,
pub dyn_view: DynamicViewStateIter,
pub reflected_components: Option<NonNull<TypeRegistry>>
}
impl ReflectedIterator {
impl<'a> ReflectedIterator<'a> {
#[cfg(feature = "lua")]
pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option<ReflectedRow<'a>> {
use elua::AsLua;
pub fn next_lua(&mut self, lua: &mlua::Lua) -> Option<ReflectedRow> {
use mlua::IntoLua;
//let world = self.world.read();
let n = self.dyn_view.next(&self.world);
let world = self.world.as_ref();
let n = self.dyn_view.next(world);
if let Some(row) = n {
if let Some((en, row)) = n {
if self.reflected_components.is_none() {
let world = self.world.as_ref();
self.reflected_components = world.try_get_resource::<TypeRegistry>()
self.reflected_components = self.world.get_resource::<TypeRegistry>()
.map(|r| NonNull::from(r.deref()));
}
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 reflected_components =
unsafe { self.reflected_components.as_ref().unwrap().as_ref() };
@ -54,7 +51,7 @@ impl ReflectedIterator {
// TODO: properly handle this error
.expect("Type does not have ReflectLuaProxy as a TypeData");
let value = (proxy.fn_as_lua)(lua, d.ptr.cast()).unwrap()
.as_lua(lua).unwrap();
.into_lua(lua).unwrap();
dynamic_row.push(ReflectedItem {
comp_ptr: d.ptr,
@ -63,7 +60,7 @@ impl ReflectedIterator {
}
let row = ReflectedRow {
entity: row.entity,
entity: en,
row: dynamic_row
};

View File

@ -2,7 +2,7 @@ pub mod dynamic_iter;
pub use dynamic_iter::*;
pub mod world;
use elua::FromLua;
use mlua::ObjectLike;
pub use world::*;
pub mod script;
@ -22,13 +22,48 @@ pub use system::*;
use std::{any::TypeId, sync::Mutex};
use lyra_ecs::{
Component, ComponentInfo, World
};
use lyra_ecs::World;
use lyra_reflect::{Reflect, TypeRegistry};
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.
///
@ -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,
/// 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.
///
/// 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**.
fn register_lua_type<'a, T>(&mut self)
where
T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata;
T: Reflect + LuaProxy + Clone + mlua::FromLua + mlua::UserData;
/// Registers a type to Lua that is wrapped another type.
/// This would be used for something like `UserdataRef<T>`.
fn register_lua_wrapper<'a, W>(&mut self)
where
W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata;
W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua + mlua::UserData;
/// Registers a type to Lua that can be converted into and from Lua types.
fn register_lua_convert<T>(&mut self)
where
T: Clone + for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'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;
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static;
}
impl RegisterLuaType for World {
fn register_lua_type<'a, T>(&mut self)
where
T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata
T: Reflect + LuaProxy + Clone + mlua::FromLua + 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>();
@ -113,72 +142,40 @@ impl RegisterLuaType for World {
fn register_lua_wrapper<'a, W>(&mut self)
where
W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata
W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua + 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());
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<W>());
}
}
fn register_lua_convert<T>(&mut self)
where
T: Clone + for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + LuaWrapper + 'static,
T: Clone + mlua::FromLua + mlua::IntoLua + 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());
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 {
fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
impl mlua::FromLua for ScriptBorrow {
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match value {
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => unreachable!(),
}
}
}
impl<'lua> elua::FromLuaVec<'lua> 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>) { }
impl mlua::UserData for ScriptBorrow {
}
/// Helper function used for reflecting userdata as a ScriptBorrow
pub fn reflect_user_data(ud: &elua::AnyUserdata) -> ScriptBorrow {
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow {
ud.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
.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::<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>());
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> {
let ctx = ctx.lock().unwrap();
let globals = ctx.globals()?;
let globals = ctx.globals();
globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?;
globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?;
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()?;
table.set(FN_NAME_INTERNAL_REFLECT_TYPE, lua.create_function(|_, ()| {
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");
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("Quat", ctx.create_proxy::<LuaQuat>()?)?;
globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?;

View File

@ -1,11 +1,12 @@
use std::sync::{Mutex, Arc};
use std::{ops::Deref, sync::{Arc, Mutex}};
use tracing::{debug_span, debug};
use mlua::{AnyUserData, ObjectLike};
use tracing::{debug, debug_span};
use crate::{ScriptApiProvider, ScriptData};
/// This Api provider provides some nice utility functions.
///
///
/// Functions:
/// ```lua
/// ---@param str (string) A format string.
@ -17,99 +18,122 @@ use crate::{ScriptApiProvider, ScriptData};
pub struct 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();
//fn printf(lua: &elua::State, (mut text, formats): (String, elua::Variadic<elua::Value>)) -> elua::Result<()> {
let printf = |lua: &elua::State, (mut text, formats): (String, elua::Variadic<elua::Value>)| {
let mut formatted = String::new();
let mut arg_num = 0;
//fn printf(lua: &mlua::State, (mut text, formats): (String, mlua::Variadic<mlua::Value>)) -> mlua::Result<()> {
let printf =
|lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic<mlua::Value>)| {
let mut formatted = String::new();
let mut arg_num = 0;
while let Some(start) = text.find("{}") {
let val_str = match formats.get(arg_num) {
Some(v) => match v {
elua::Value::Nil => "nil".to_string(),
elua::Value::Boolean(b) => b.to_string(),
elua::Value::Number(n) => n.to_string(),
elua::Value::String(s) => s.clone(),
elua::Value::Table(_) => {
return Err(elua::Error::runtime("unable to get string representation of Table"));
while let Some(start) = text.find("{}") {
let val_str = match formats.get(arg_num) {
Some(v) => match v {
mlua::Value::Nil => "nil".to_string(),
mlua::Value::Boolean(b) => b.to_string(),
mlua::Value::Integer(n) => n.to_string(),
mlua::Value::Number(n) => n.to_string(),
mlua::Value::String(s) => s.to_string_lossy().to_string(),
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.deref().clone());
},
},
elua::Value::Function(_) => {
return Err(elua::Error::runtime("unable to get string representation of Function"));
},
elua::Value::Thread(_) => {
return Err(elua::Error::runtime("unable to get string representation of Thread"));
},
elua::Value::Userdata(ud) => {
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'"));
None => {
let got_args = arg_num; // - 1;
// continue searching for {} to get the number of format spots for the error message.
while let Some(start) = text.find("{}") {
text = text[start + 2..].to_string();
arg_num += 1;
}
},
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.
while let Some(start) = text.find("{}") {
text = text[start + 2..].to_string();
arg_num += 1;
return Err(mlua::Error::BadArgument {
to: Some("printf".into()),
pos: 2,
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 {
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);
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() {
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);
formatted = format!("{}{}", formatted, text);
lua.globals()?
.get::<_, elua::Function>("print")?
.exec::<_, ()>(formatted)?;
lua.globals()
.get::<mlua::Function>("print")?
.call::<()>(formatted)?;
Ok(())
};
let script_name_reg = ctx.registry_insert(data.name.clone())?;
Ok(())
};
let script_name_reg = ctx.create_registry_value(data.name.clone())?;
let printf_func = ctx.create_function(printf)?;
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();
debug!(target: "lyra_scripting::lua", "{}", text);
@ -117,18 +141,42 @@ impl ScriptApiProvider for UtilityApiProvider {
Ok(())
})?;
let globals = ctx.globals()?;
// a custom implementation of `getmetatable` is required since mlua protects __metatable,
// making it impossible to get the metatable of userdata.
let getmetatable_func = ctx.create_function(|lua, ud: AnyUserData| {
// the userdata is left on the stack from `lua_getmetatable`, so that needs to be
// included in the returns
let (_ud, table): (mlua::AnyUserData, mlua::Table) = unsafe {
lua.exec_raw(ud, |state| {
mlua::ffi::lua_getmetatable(state, -1);
})
}?;
Ok(table)
})?;
let globals = ctx.globals();
globals.set("printf", printf_func)?;
globals.set("print", print_func)?;
globals.set("getmetatable2", getmetatable_func)?;
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(())
}
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(())
}
}
}

View File

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

View File

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

View File

@ -5,7 +5,7 @@ use lyra_reflect::TypeRegistry;
use lyra_resource::ResourceManager;
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};
@ -40,7 +40,8 @@ pub fn lua_scripts_create_contexts(
trace!("Finished setting up script");
// 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(
world_ptr,
&script_data,
@ -50,7 +51,7 @@ pub fn lua_scripts_create_contexts(
) {
Ok(()) => {}
Err(e) => match e {
ScriptError::MluaError(m) => {
ScriptError::LuaError(m) => {
error!("Script '{}' ran into an error: {}", script.name(), m);
}
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<()> {
let world_ptr = ScriptWorldPtr::from_ref(&world);
let mut host = world.get_resource_mut::<LuaHost>();
let mut contexts = world.get_resource_mut::<ScriptContexts<LuaContext>>();
let mut providers = world.get_resource_mut::<ScriptApiProviders<LuaHost>>();
let world_guard = unsafe { ScriptWorldPtrGuard::new(world) };
let world_ptr = (*world_guard).clone();
let mut host = world.get_resource_mut::<LuaHost>().unwrap();
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 script in scripts.iter() {
@ -143,7 +145,7 @@ fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Resu
) {
Ok(()) => {}
Err(e) => match e {
ScriptError::MluaError(m) => {
ScriptError::LuaError(m) => {
error!("Script '{}' ran into an error: {}", script.name(), m);
}
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;
impl Plugin for LuaScriptingPlugin {
fn setup(&mut self, game: &mut lyra_game::game::Game) {
let world = game.world_mut();
fn setup(&mut self, app: &mut lyra_game::game::App) {
let world = &mut app.world;
world.add_resource_default::<TypeRegistry>();
@ -200,16 +202,16 @@ impl Plugin for LuaScriptingPlugin {
world.add_resource_default::<ScriptContexts<LuaContext>>();
let loader = world
.try_get_resource_mut::<ResourceManager>()
.get_resource_mut::<ResourceManager>()
.expect("Add 'ResourceManager' to the world before trying to add this plugin");
loader.register_loader::<LuaLoader>();
drop(loader);
game.add_script_api_provider::<LuaHost, _>(UtilityApiProvider);
game.add_script_api_provider::<LuaHost, _>(LyraEcsApiProvider);
game.add_script_api_provider::<LuaHost, _>(LyraMathApiProvider);
app.add_script_api_provider::<LuaHost, _>(UtilityApiProvider);
app.add_script_api_provider::<LuaHost, _>(LyraEcsApiProvider);
app.add_script_api_provider::<LuaHost, _>(LyraMathApiProvider);
game.add_system_to_stage(
app.add_system_to_stage(
GameStages::First,
"lua_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 elua::AsLua;
use lyra_ecs::{query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType}, CommandQueue, Commands, DynamicBundle, World};
use lyra_ecs::{
query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType},
CommandQueue, Commands, DynamicBundle, World,
};
use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
use lyra_resource::ResourceManager;
use mlua::{IntoLua, ObjectLike};
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 {
fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
impl mlua::FromLua for ScriptEntity {
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match value {
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptEntity", "Nil")),
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptEntity", "Nil"))),
_ => panic!(),
}
}
}
impl elua::Userdata for ScriptEntity {
fn name() -> String {
"Entity".to_string()
}
fn build<'a>(builder: &mut elua::userdata::UserdataBuilder<'a, Self>) {
builder.meta_method(elua::MetaMethod::ToString, |_, this, ()| {
impl mlua::UserData for ScriptEntity {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this.0))
});
}
@ -38,279 +37,278 @@ pub enum WorldError {
LuaInvalidUsage(String),
}
impl<'a> elua::FromLua<'a> for ScriptWorldPtr {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
impl mlua::FromLua for ScriptWorldPtr {
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match val {
elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptWorldPtr", "Nil")),
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptWorldPtr", "Nil"))),
_ => panic!(),
}
}
}
impl elua::Userdata for ScriptWorldPtr {
fn name() -> String {
"World".to_string()
}
impl mlua::UserData for ScriptWorldPtr {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("spawn", |_, this, vals: mlua::MultiValue| {
let mut world = this.write();
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
builder
.method_mut("spawn", |_, this, vals: elua::ValueVec| {
let world = this.as_mut();
let mut bundle = DynamicBundle::new();
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() {
for (i, val) in vals.into_iter().enumerate() {
let ud = val.as_userdata().ok_or(
elua::Error::bad_arg(
Some("World:spawn"),
2 + i as i32, // i starts at 0
Some("components..."),
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!(),
};
let comp_borrow = {
if let Ok(as_comp) = ud.get::<mlua::Function>(FN_NAME_INTERNAL_AS_COMPONENT)
{
let ud = match as_comp.call(ud.clone())? {
mlua::Value::UserData(ud) => ud,
mlua::Value::Nil => ud.clone(),
_ => todo!(),
};
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
} else {
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
}
};
ud.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
} else {
ud.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
}
};
let reflect = comp_borrow.reflect_branch.as_component_unchecked();
let refl_data = comp_borrow.data.unwrap();
reflect.bundle_insert(&mut bundle, refl_data);
let reflect = comp_borrow.reflect_branch.as_component_unchecked();
let refl_data = comp_borrow.data.unwrap();
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
// safety: Commands borrows Entities from World, the resource borrows from the world resouces,
// 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);
let world = this.read();
let mut view = DynamicViewState::new();
Ok(ScriptEntity(entity))
})
.method_mut(
"view",
|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(),
))),
});
}
for (idx, comp) in queries.into_iter().enumerate() {
match comp {
mlua::Value::Table(t) => {
let name: String = t.get(mlua::MetaMethod::Type.name())?;
let world = unsafe { this.inner.as_ref() };
//let mut view = world.dynamic_view();
let mut view = DynamicViewState::new();
for (idx, comp) in queries.into_iter().enumerate() {
match comp {
elua::Value::Table(t) => {
let name: String = t.get(elua::MetaMethod::Name)?;
let lookup = world
.try_get_resource::<LuaTableProxyLookup>()
.ok_or(elua::Error::runtime(
"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!(
let lookup = world.get_resource::<LuaTableProxyLookup>().ok_or(
mlua::Error::runtime(
"Unable to lookup table proxy, none were ever registered!",
),
)?;
let info = lookup.comp_info_from_name.get(&name).ok_or_else(|| {
mlua::Error::BadArgument {
to: Some("World:view".into()),
pos: 2 + idx,
name: Some("query...".into()),
cause: Arc::new(mlua::Error::external(
WorldError::LuaInvalidUsage(format!(
"the 'Table' with name {} is unknown to the engine!",
name
))),
},
)?;
)),
)),
}
})?;
let dyn_type = QueryDynamicType::from_info(info.clone());
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!(),
let dyn_type = QueryDynamicType::from_info(info.clone());
view.push(dyn_type);
}
}
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 mut reflected_iter = ReflectedIterator {
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));
let dyn_type = QueryDynamicType::from_info(refl_comp.info);
view.push(dyn_type);
}
_ => 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 reflect = match res {
elua::Value::Userdata(ud) => ud
.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
.expect("Type does not implement 'reflect_type' properly"),
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 iter = view.into_iter();
let mut reflected_iter = ReflectedIterator {
// SAFETY: bypassing the borrow checker here to get a pointer of the world
// is required since we mutably borrow below. Its safe to do so since
// only the entity ticks are updated. They are accessing different things
// from the world.
world: unsafe { NonNull::from(&*world).as_ref() },
dyn_view: DynamicViewStateIter::from(iter),
reflected_components: None,
};
let data = reflect.data
.expect("Its expected that 'FN_NAME_INTERNAL_REFLECT' returns data in World:add_resource");
let current = world.current_tick();
let res = reflect.reflect_branch.as_resource()
.ok_or(elua::Error::runtime("Provided type is not a resource!"))?;
// drop read lock and acquire the write lock.
// dropping must be done to avoid mutex deadlock
drop(world);
let mut world = this.write();
let world = this.as_mut();
res.insert(world, data);
while let Some(row) = reflected_iter.next_lua(lua) {
let r = row.row.into_iter()
.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_iter(values.into_iter());
let res: mlua::MultiValue = system.call(mult_val)?;
// if values were returned, find the type in the type registry, and apply the new values
if res.len() <= ptrs.len() {
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 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(())
})
.method_mut("request_res", |lua, this, path: String| {
let world: &mut World = this.as_mut();
let man = world.get_resource_mut::<ResourceManager>();
let handle = man.request_raw(&path).unwrap();
},
);
methods.add_method_mut("resource", |lua, this, (ty,): (mlua::Value,)| {
let reflect = match ty {
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");
}
};
// get the actual resource handle wrapper
let registry = world.get_resource::<TypeRegistry>();
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");
let mut world = this.write();
let res = reflect.reflect_branch.as_resource_unchecked();
if let Some(res_ptr) = res.reflect_ptr(&mut world) {
let reg_type = world
.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");
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 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_game::scene::SceneGraph;
use lyra_reflect::{Reflect, TypeData};
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 mlua::IntoLua;
pub struct LuaResHandleToComponent {
/// 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 {
pub fn new(f: fn(&State, UntypedResHandle) -> Option<AnyUserdata>) -> Self {
pub fn new(f: fn(&mlua::Lua, UntypedResHandle) -> Option<mlua::AnyUserData>) -> Self {
Self {
fn_to_lua: f
}
@ -53,16 +55,12 @@ impl From<UntypedResHandle> for LuaResHandle {
}
}
impl elua::Userdata for LuaResHandle {
fn name() -> String {
"Handle".to_string()
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
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| {
impl mlua::UserData for LuaResHandle {
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
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()));
fields.add_field_method_get("state", |_, this| {
let name = if this.is_loaded() {
"ready"
} else if this.get_error().is_some() {
@ -71,41 +69,43 @@ impl elua::Userdata for LuaResHandle {
Ok(name)
});
}
builder.method("is_watched", |_, this, ()| {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("is_watched", |_, this, ()| {
Ok(this.is_watched())
});
builder.method("is_loaded", |_, this, ()| {
methods.add_method("is_loaded", |_, this, ()| {
Ok(this.is_loaded())
});
builder.method("wait_until_loaded", |_, this, ()| {
methods.add_method("wait_until_loaded", |_, this, ()| {
this.wait_recurse_dependencies_load();
Ok(())
});
builder.method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| {
methods.add_method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| {
let handle = &this.0;
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>() {
LuaGltfHandle(handle).as_lua(lua)
LuaGltfHandle(handle).into_lua(lua)
} else {
Ok(elua::Value::Nil)
Ok(mlua::Value::Nil)
}
});
}
}
impl<'a> FromLua<'a> for LuaResHandle {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
impl mlua::FromLua for LuaResHandle {
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("Handle", &tyname))?;
let handle = ud.as_ref::<LuaResHandle>()?;
.ok_or(mlua::Error::external(Error::type_mismatch("Handle", &tyname)))?;
let handle = ud.borrow::<LuaResHandle>()?;
Ok(handle.clone())
}
@ -121,11 +121,11 @@ lua_wrap_handle!(Mesh,
(material, {
data.material.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() {
let table = lua.create_table()?;
@ -133,21 +133,21 @@ lua_wrap_handle!(Mesh,
Some(lyra_resource::gltf::MeshIndices::U16(v)) => {
for (i, ind) in v.iter().enumerate() {
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)) => {
for (i, ind) in v.iter().enumerate() {
let i = i as i64 + 1; // lua indexes start at 1
table.raw_seti(i, *ind)?;
table.raw_set(i, *ind)?;
}
},
None => {},
}
Ok(Value::Table(table))
Ok(mlua::Value::Table(table))
} else {
Ok(Value::Nil)
Ok(mlua::Value::Nil)
}
});
@ -167,17 +167,17 @@ lua_wrap_handle!(Material,
(base_color_texture, {
data.base_color_texture.clone()
.map(|v| LuaTextureHandle(v.clone()))
.as_lua(lua)
.into_lua(lua)
}),
(metallic_roughness_texture, {
data.metallic_roughness_texture.clone()
.map(|v| LuaTextureHandle(v.clone()))
.as_lua(lua)
.into_lua(lua)
}),
(pbr_glossiness, {
data.pbr_glossiness.clone()
.map(|v| LuaPbrGlossiness(v.clone()))
.as_lua(lua)
.into_lua(lua)
}),
alpha_cutoff,
(alpha_mode, {
@ -186,55 +186,57 @@ lua_wrap_handle!(Material,
lyra_resource::gltf::AlphaMode::Opaque => "opaque",
lyra_resource::gltf::AlphaMode::Mask => "mask",
lyra_resource::gltf::AlphaMode::Blend => "blend",
}.as_lua(lua)
}.into_lua(lua)
}),
(specular, {
data.specular.clone()
.map(|v| LuaSpecular(v.clone()))
.as_lua(lua)
.into_lua(lua)
}),
}
);
lua_wrap_handle!(Gltf, {
builder.method("scenes", |lua, this, ()| {
methods.add_method("scenes", |lua, this, ()| {
if let Some(data) = this.0.data_ref() {
let table = lua.create_table()?;
for (i, scene) in data.scenes.iter().enumerate() {
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 {
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() {
let table = lua.create_table()?;
for (i, mat) in data.materials.iter().enumerate() {
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 {
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() {
let table = lua.create_table()?;
for (i, mesh) in data.meshes.iter().enumerate() {
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 {
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 {
fn from_lua(_: &'lua elua::State, _: elua::Value<'lua>) -> elua::Result<Self> {
impl mlua::FromLua for LuaDeltaTime {
fn from_lua(_: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
todo!()
}
}
impl<'lua> elua::AsLua<'lua> for LuaDeltaTime {
fn as_lua(self, _: &'lua elua::State) -> elua::Result<elua::Value<'lua>> {
Ok(elua::Value::Number(*self.0 as f64))
impl mlua::IntoLua for LuaDeltaTime {
fn into_lua(self, _: &mlua::Lua) -> mlua::Result<mlua::Value> {
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 crate::lyra_engine;
use mlua::IntoLua;
use crate::{lua::Error, lyra_engine};
use lyra_reflect::Reflect;
@ -10,27 +11,23 @@ pub struct LuaActionHandler {
handler: ActionHandler
}
impl elua::Userdata for LuaActionHandler {
fn name() -> String {
"ActionHandler".to_string()
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
builder.function("new", |_, table: elua::Table| {
impl mlua::UserData for LuaActionHandler {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("new", |_, table: mlua::Table| {
let mut handler = ActionHandler::new();
// create the layouts and add them to the handler
let layouts = table.get::<_, elua::Table>("layouts")
.map_err(|_| elua::Error::runtime("missing 'layouts' in ActionHandler table"))?;
for layout_id in layouts.sequence_iter::<u32>() {
let layouts = table.get::<mlua::Table>("layouts")
.map_err(|_| mlua::Error::runtime("missing 'layouts' in ActionHandler table"))?;
for layout_id in layouts.sequence_values::<u32>() {
let layout_id = layout_id?;
handler.add_layout(LayoutId(layout_id));
}
// add the actions to the handler
let actions = table.get::<_, elua::Table>("actions")
.map_err(|_| elua::Error::runtime("missing 'actions' in ActionHandler table"))?;
let actions = table.get::<mlua::Table>("actions")
.map_err(|_| mlua::Error::runtime("missing 'actions' in ActionHandler table"))?;
for pair in actions.pairs::<String, String>() {
let (action_lbl, action_type) = pair?;
let action_type = action_type.to_lowercase();
@ -45,26 +42,26 @@ impl elua::Userdata for LuaActionHandler {
}
// find the mappings and start processing them
let mappings= table.get::<_, elua::Table>("mappings")
.map_err(|_| elua::Error::runtime("missing 'mappings' in ActionHandler table"))?;
for (map_id, tbl) in mappings.sequence_iter::<elua::Table>().enumerate() {
let mappings= table.get::<mlua::Table>("mappings")
.map_err(|_| mlua::Error::runtime("missing 'mappings' in ActionHandler table"))?;
for (map_id, tbl) in mappings.sequence_values::<mlua::Table>().enumerate() {
let tbl = tbl?;
let layout_id = tbl.get::<_, u32>("layout")?;
let layout_id = tbl.get::<u32>("layout")?;
let mut mapping = ActionMapping::new(LayoutId(layout_id), ActionMappingId(map_id as u32));
// find the binds and start processing them
// 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")
.map_err(|_| elua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?;
for pair in binds_tbl.pairs::<String, elua::Table>() {
let binds_tbl = tbl.get::<mlua::Table>("binds")
.map_err(|_| mlua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?;
for pair in binds_tbl.pairs::<String, mlua::Table>() {
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 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();
@ -77,18 +74,18 @@ impl elua::Userdata for LuaActionHandler {
let axis_name = input_split[2];
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());
} else {
// splits 'down=1' into 'down' and '1'
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>()
.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)
.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));
}
} else {
@ -105,23 +102,29 @@ impl elua::Userdata for LuaActionHandler {
Ok(LuaActionHandler {
handler,
})
})
.method("get_axis", |_, this, action: String| {
});
methods.add_method("get_axis", |_, this, action: String| {
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))
})
.method("was_just_pressed", |_, this, action: String| {
});
methods.add_method("was_just_pressed", |_, this, action: String| {
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))
})
.method("get_just_pressed", |_, this, action: String| {
});
methods.add_method("get_just_pressed", |_, this, action: String| {
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 (name, val) = match state {
@ -133,28 +136,29 @@ impl elua::Userdata for LuaActionHandler {
ActionState::Other(v) => ("Other", Some(v)),
};
let mut multi = elua::ValueVec::new();
multi.push_val(lua, name)?;
multi.push_val(lua, val)?;
let mut multi = Vec::new();
multi.push(name.into_lua(lua)?);
multi.push(val.into_lua(lua)?);
Ok(elua::Value::Multi(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))
Ok(mlua::MultiValue::from_iter(multi.into_iter()))
});
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 {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
impl mlua::FromLua for LuaActionHandler {
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("ActionHandler", &tyname))?;
let handle = ud.as_ref::<LuaActionHandler>()?;
.ok_or(mlua::Error::external(Error::type_mismatch("ActionHandler", &tyname)))?;
let handle = ud.borrow::<LuaActionHandler>()?;
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 lyra_game::math;
use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct};
use mlua::FromLuaMulti;
use crate as lyra_scripting;
@ -20,13 +21,15 @@ wrap_lua_struct!(
{
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();
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.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;
} else {
todo!("handle invalid argument error");
}
Ok(())
@ -50,14 +53,16 @@ wrap_lua_struct!(
{
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();
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.y += y;
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;
} else {
todo!("handle invalid argument error");
}
Ok(())
@ -98,70 +103,70 @@ wrap_lua_struct!(
),
{
// 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)))
});
builder.function("from_rotation_x", |_, (rad,)| {
methods.add_function("from_rotation_x", |_, (rad,)| {
let q = math::Quat::from_rotation_x(rad);
Ok(Self(q))
});
builder.function("from_rotation_y", |_, (rad,)| {
methods.add_function("from_rotation_y", |_, (rad,)| {
let q = math::Quat::from_rotation_y(rad);
Ok(Self(q))
});
builder.function("from_rotation_z", |_, (rad,)| {
methods.add_function("from_rotation_z", |_, (rad,)| {
let q = math::Quat::from_rotation_z(rad);
Ok(Self(q))
});
builder.method("dot", |_, this, (rhs,): (Self,)| {
methods.add_method("dot", |_, this, (rhs,): (Self,)| {
Ok(this.dot(rhs.0))
});
builder.method("length", |_, this, ()| {
methods.add_method("length", |_, this, ()| {
Ok(this.length())
});
builder.method("length_squared", |_, this, ()| {
methods.add_method("length_squared", |_, this, ()| {
Ok(this.length_squared())
});
builder.method_mut("normalize", |_, this, ()| {
methods.add_method_mut("normalize", |_, this, ()| {
this.0 = this.normalize();
Ok(())
});
builder.method_mut("mult_quat", |_, this, (rhs,): (Self,)| {
methods.add_method_mut("mult_quat", |_, this, (rhs,): (Self,)| {
this.0 *= rhs.0;
Ok(())
});
builder.method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| {
methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| {
Ok(LuaVec3(this.0 * rhs.0))
});
// manually implemented here since multiplying may not return `Self`.
builder.meta_method(elua::MetaMethod::Mul, |lua, this, (val,): (elua::Value,)| {
use elua::AsLua;
methods.add_meta_method(mlua::MetaMethod::Mul, |lua, this, (val,): (mlua::Value,)| {
use mlua::IntoLua;
match val {
elua::Value::Userdata(ud) => {
if ud.is::<LuaVec3>()? {
let v3 = ud.as_ref::<LuaVec3>()?;
mlua::Value::UserData(ud) => {
if ud.is::<LuaVec3>() {
let v3 = ud.borrow::<LuaVec3>()?;
LuaVec3(this.0 * v3.0)
.as_lua(lua)
.into_lua(lua)
} else {
let quat = ud.as_ref::<LuaQuat>()?;
let quat = ud.borrow::<LuaQuat>()?;
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))
.as_lua(lua)
.into_lua(lua)
},
_ => {
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)))
});
}
@ -180,94 +185,94 @@ wrap_lua_struct!(
derives(PartialEq, Copy),
metamethods(ToString, Eq),
{
builder.function("default", |_, ()| {
methods.add_function("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)))
});
builder.function("from_translation", |_, (pos,): (LuaVec3,)| {
methods.add_function("from_translation", |_, (pos,): (LuaVec3,)| {
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)))
});
builder.method("clone", |_, this, ()| {
methods.add_method("clone", |_, this, ()| {
Ok(this.clone())
});
builder.method("forward", |_, this, ()| {
methods.add_method("forward", |_, this, ()| {
Ok(LuaVec3(this.forward()))
});
builder.method("left", |_, this, ()| {
methods.add_method("left", |_, this, ()| {
Ok(LuaVec3(this.left()))
});
builder.method("up", |_, this, ()| {
methods.add_method("up", |_, this, ()| {
Ok(LuaVec3(this.up()))
});
builder.method_mut("rotate", |_, this, (quat,): (LuaQuat,)| {
methods.add_method_mut("rotate", |_, this, (quat,): (LuaQuat,)| {
this.rotate(*quat);
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));
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));
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));
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));
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));
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));
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);
Ok(())
});
builder.method("lerp", |_, this, (rhs, alpha): (Self, f32)| {
methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| {
Ok(Self(this.lerp(*rhs, alpha)))
});
// 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;
t.rotation *= *quat;
Ok(t)
});
// 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;
t.translation += *pos;
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 parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
#[derive(Clone)]
pub struct ScriptEntity(pub Entity);
@ -14,33 +15,41 @@ impl std::ops::Deref for ScriptEntity {
}
#[derive(Clone)]
pub struct ScriptWorldPtr {
pub inner: NonNull<World>,
}
pub struct ScriptWorldPtr(Arc<RwLock<NonNull<World>>>);
impl ScriptWorldPtr {
/// Creates a world pointer from a world borrow.
pub fn from_ref(world: &World) -> Self {
Self {
inner: NonNull::from(world),
}
pub fn read(&self) -> MappedRwLockReadGuard<World> {
RwLockReadGuard::map(self.0.read(), |p| unsafe { p.as_ref() })
}
/// Returns a borrow to the world from the ptr.
pub fn as_ref(&self) -> &World {
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() }
pub fn write(&self) -> MappedRwLockWriteGuard<World> {
RwLockWriteGuard::map(self.0.write(), |p| unsafe { p.as_mut() })
}
}
impl std::ops::Deref for ScriptWorldPtr {
type Target = NonNull<World>;
// SAFETY: The inner NonNull pointer is wrapped in an Arc<RwLock<>>
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 {
&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 {
&self.0
}
}
}
// SAFETY: todo!()
unsafe impl Send for ScriptDynamicBundle {}