Fix #19: Lua crashes when spawning entities in optimized builds #27
|
@ -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",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,21 +2,27 @@
|
|||
---@param val userdata
|
||||
---@return string
|
||||
function udname(val)
|
||||
return getmetatable(val).__name
|
||||
local tbl = getmetatable(val)
|
||||
if type(tbl) == "boolean" then
|
||||
error("what, got a boolean??: " .. tostring(tbl), 1)
|
||||
end
|
||||
|
||||
return tbl.__name
|
||||
end
|
||||
|
||||
|
||||
function on_init()
|
||||
local cube = world:request_res("../assets/cube-texture-embedded.gltf")
|
||||
print("Loaded textured cube (" .. udname(cube) .. ")")
|
||||
|
||||
cube:wait_until_loaded()
|
||||
--[[ cube:wait_until_loaded()
|
||||
local scenes = cube:scenes()
|
||||
local cube_scene = scenes[1]
|
||||
|
||||
local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
|
||||
|
||||
local e = world:spawn(pos, cube_scene)
|
||||
print("spawned entity " .. tostring(e))
|
||||
print("spawned entity " .. tostring(e)) ]]
|
||||
end
|
||||
|
||||
--[[ function on_first()
|
||||
|
@ -42,12 +48,12 @@ function on_update()
|
|||
end, Transform) ]]
|
||||
|
||||
---@type number
|
||||
local dt = world:resource(DeltaTime)
|
||||
--[[ local dt = world:resource(DeltaTime)
|
||||
|
||||
world:view(function (t)
|
||||
t:translate(0, 0.15 * dt, 0)
|
||||
return t
|
||||
end, Transform)
|
||||
end, Transform) ]]
|
||||
end
|
||||
|
||||
--[[ function on_post_update()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,11 @@ 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?
|
||||
#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" ] }
|
||||
|
|
|
@ -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,13 @@ 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<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("__name", stringify!(#handle_name));
|
||||
fields.add_field_method_get("path", |_, this| Ok(this.path()));
|
||||
fields.add_field_method_get("version", |_, this| Ok(this.version()));
|
||||
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 +203,31 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
|||
});
|
||||
|
||||
#(#custom_getters)*
|
||||
}
|
||||
|
||||
builder.method("is_watched", |_, this, ()| {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_method("is_watched", |_, this, ()| {
|
||||
Ok(this.is_watched())
|
||||
});
|
||||
|
||||
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 +235,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<'a> mlua::FromLua<'a> for #wrapper_name {
|
||||
fn from_lua(val: mlua::Value<'a>, _: &'a 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())
|
||||
}
|
||||
|
|
|
@ -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(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<'lua> mlua::FromLua<'lua> for #wrapper_typename {
|
||||
fn from_lua(value: mlua::Value<'lua>, _: &'lua 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<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
#(#field_get_set_pairs)*
|
||||
}
|
||||
|
||||
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
|
||||
#(#field_get_set_pairs)*
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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())
|
||||
});
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ use crate::ScriptWorldPtr;
|
|||
pub struct ReflectedItem<'a> {
|
||||
//pub proxy: &'a ReflectLuaProxy,
|
||||
pub comp_ptr: NonNull<u8>,
|
||||
pub comp_val: elua::Value<'a>,
|
||||
pub comp_val: mlua::Value<'a>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "lua")]
|
||||
|
@ -29,21 +29,20 @@ pub struct ReflectedIterator {
|
|||
|
||||
impl ReflectedIterator {
|
||||
#[cfg(feature = "lua")]
|
||||
pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option<ReflectedRow<'a>> {
|
||||
use elua::AsLua;
|
||||
pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option<ReflectedRow<'a>> {
|
||||
use mlua::IntoLua;
|
||||
|
||||
let world = self.world.as_ref();
|
||||
let n = self.dyn_view.next(world);
|
||||
let world = self.world.read();
|
||||
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 = 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 +53,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 +62,7 @@ impl ReflectedIterator {
|
|||
}
|
||||
|
||||
let row = ReflectedRow {
|
||||
entity: row.entity,
|
||||
entity: en,
|
||||
row: dynamic_row
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ pub mod dynamic_iter;
|
|||
pub use dynamic_iter::*;
|
||||
|
||||
pub mod world;
|
||||
use elua::FromLua;
|
||||
use mlua::AnyUserDataExt;
|
||||
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<'a> + 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<'a> + 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 + for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + 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<'a> + mlua::UserData
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>();
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
|
||||
let type_id = TypeId::of::<T>();
|
||||
|
||||
|
@ -113,9 +142,9 @@ 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<'a> + mlua::UserData
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>();
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
|
||||
let reg_type = registry.get_type_or_default(W::wrapped_type_id());
|
||||
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<W>());
|
||||
|
@ -123,62 +152,30 @@ impl RegisterLuaType for World {
|
|||
|
||||
fn register_lua_convert<T>(&mut self)
|
||||
where
|
||||
T: Clone + for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + LuaWrapper + 'static,
|
||||
T: Clone + for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + LuaWrapper + 'static,
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>();
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
|
||||
let reg_type = registry.get_type_or_default(T::wrapped_type_id());
|
||||
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<'lua> mlua::FromLua<'lua> for ScriptBorrow {
|
||||
fn from_lua(value: mlua::Value<'lua>, _: &'lua 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 mlua::UserData for ScriptBorrow {
|
||||
|
||||
impl elua::Userdata for ScriptBorrow {
|
||||
fn name() -> String {
|
||||
"ScriptBorrow".to_string()
|
||||
}
|
||||
|
||||
fn build<'a>(_: &mut elua::UserdataBuilder<'a, Self>) { }
|
||||
}
|
||||
|
||||
/// Helper function used for reflecting userdata as a ScriptBorrow
|
||||
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")
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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>()?)?;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::sync::{Mutex, Arc};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use tracing::{debug_span, debug};
|
||||
use mlua::AnyUserDataExt;
|
||||
use tracing::{debug, debug_span};
|
||||
|
||||
use crate::{ScriptApiProvider, ScriptData};
|
||||
|
||||
|
@ -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.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::<_, mlua::Function>("print")?
|
||||
.call::<_, ()>(formatted)?;
|
||||
|
||||
lua.globals()?
|
||||
.get::<_, elua::Function>("print")?
|
||||
.exec::<_, ()>(formatted)?;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let script_name_reg = ctx.registry_insert(data.name.clone())?;
|
||||
let script_name_reg = ctx.create_registry_value(data.name.clone())?;
|
||||
|
||||
let printf_func = ctx.create_function(printf)?;
|
||||
let 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,27 @@ impl ScriptApiProvider for UtilityApiProvider {
|
|||
Ok(())
|
||||
})?;
|
||||
|
||||
let globals = ctx.globals()?;
|
||||
let globals = ctx.globals();
|
||||
globals.set("printf", printf_func)?;
|
||||
globals.set("print", print_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(())
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
use std::{any::TypeId, collections::HashMap, ptr::NonNull};
|
||||
|
||||
use elua::{FromLua, TableProxy, AsLua};
|
||||
use mlua::{AnyUserDataExt, 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.
|
||||
|
@ -16,39 +16,39 @@ pub trait LuaWrapper {
|
|||
/// A trait that used to convert something into lua, or to set something to a value from lua.
|
||||
pub trait LuaProxy {
|
||||
fn as_lua_value<'lua>(
|
||||
lua: &'lua elua::State,
|
||||
lua: &'lua mlua::Lua,
|
||||
this: &dyn Reflect,
|
||||
) -> elua::Result<elua::Value<'lua>>;
|
||||
) -> mlua::Result<mlua::Value<'lua>>;
|
||||
|
||||
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<'a> + mlua::UserData
|
||||
{
|
||||
fn as_lua_value<'lua>(
|
||||
lua: &'lua elua::State,
|
||||
lua: &'lua mlua::Lua,
|
||||
this: &dyn Reflect,
|
||||
) -> elua::Result<elua::Value<'lua>> {
|
||||
) -> mlua::Result<mlua::Value<'lua>> {
|
||||
let this = this.as_any().downcast_ref::<T>().unwrap();
|
||||
lua.create_userdata(this.clone())
|
||||
.and_then(|ud| ud.as_lua(lua))
|
||||
.and_then(|ud| ud.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 +67,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<'a>>,
|
||||
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<'a>,
|
||||
) -> mlua::Result<()>,
|
||||
}
|
||||
|
||||
impl ReflectLuaProxy {
|
||||
|
@ -82,7 +82,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 +93,19 @@ impl ReflectLuaProxy {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_table_proxy<T>() -> Self
|
||||
where
|
||||
T: TableProxy
|
||||
{
|
||||
Self {
|
||||
fn_as_lua: |lua, this| -> elua::Result<elua::Value> {
|
||||
let this = unsafe { this.cast::<T>().as_ref() };
|
||||
this.as_table(lua)
|
||||
.and_then(|t| t.as_lua(lua))
|
||||
},
|
||||
fn_apply: |lua, ptr, value| {
|
||||
let this = unsafe { ptr.cast::<T>().as_mut() };
|
||||
let table = value.as_table()
|
||||
.expect("Somehow a non-Table Lua Value was provided to a TableProxy");
|
||||
let new_val = T::from_table(lua, table.clone())?;
|
||||
|
||||
*this = new_val;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Create from a type that implements FromLua and AsLua
|
||||
pub fn from_as_and_from_lua<T>() -> Self
|
||||
where
|
||||
T: for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + Clone
|
||||
T: for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + Clone
|
||||
{
|
||||
Self {
|
||||
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 +115,27 @@ impl ReflectLuaProxy {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> elua::FromLua<'lua> for ScriptDynamicBundle {
|
||||
fn from_lua(_: &'lua elua::State, val: elua::Value<'lua>) -> elua::Result<Self> {
|
||||
impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle {
|
||||
fn from_lua(val: mlua::Value<'lua>, _: &'lua 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 elua::Userdata for ScriptDynamicBundle {
|
||||
fn name() -> String {
|
||||
"Bundle".to_string()
|
||||
}
|
||||
|
||||
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(())
|
||||
});
|
||||
impl mlua::UserData for ScriptDynamicBundle {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, 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();
|
||||
|
||||
let refl_data = script_brw.data.unwrap();
|
||||
reflect.bundle_insert(&mut this.0, refl_data);
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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_from_std_lib(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)?;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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::{AnyUserDataExt, IntoLua};
|
||||
|
||||
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<'lua> mlua::FromLua<'lua> for ScriptEntity {
|
||||
fn from_lua(value: mlua::Value<'lua>, _: &'lua 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<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| {
|
||||
Ok(format!("{:?}", this.0))
|
||||
});
|
||||
}
|
||||
|
@ -38,279 +37,280 @@ 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<'lua> mlua::FromLua<'lua> for ScriptWorldPtr {
|
||||
fn from_lua(val: mlua::Value<'lua>, _: &'lua 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<'lua, M: mlua::UserDataMethods<'lua, 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::<_, 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!(),
|
||||
};
|
||||
|
||||
let comp_borrow = {
|
||||
if let Ok(as_comp) = ud.get::<_, elua::Function>(FN_NAME_INTERNAL_AS_COMPONENT) {
|
||||
let ud = match as_comp.exec(ud.clone())? {
|
||||
elua::Value::Userdata(ud) => ud,
|
||||
elua::Value::Nil => ud.clone(),
|
||||
_ => todo!(),
|
||||
};
|
||||
ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
|
||||
} else {
|
||||
ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
|
||||
}
|
||||
};
|
||||
|
||||
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
|
||||
} else {
|
||||
ud.execute_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 {
|
||||
world: this.clone(),
|
||||
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 mut current = world.current_tick();
|
||||
let mut has_ticked = false;
|
||||
|
||||
let res = reflect.reflect_branch.as_resource()
|
||||
.ok_or(elua::Error::runtime("Provided type is not a resource!"))?;
|
||||
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<mlua::Value>, Vec<NonNull<()>>), _>(r);
|
||||
let mult_val = mlua::MultiValue::from_vec(values);
|
||||
let res: mlua::MultiValue = system.call(mult_val)?;
|
||||
|
||||
let world = this.as_mut();
|
||||
res.insert(world, data);
|
||||
// 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 {
|
||||
mlua::Value::UserData(ud) => {
|
||||
let lua_comp = reflect_user_data(ud);
|
||||
let refl_comp =
|
||||
lua_comp.reflect_branch.as_component_unchecked();
|
||||
refl_comp.info.type_id().as_rust()
|
||||
}
|
||||
mlua::Value::Table(tbl) => {
|
||||
let name: String = tbl.get(mlua::MetaMethod::Type.name())?;
|
||||
|
||||
let lookup = world.get_resource::<LuaTableProxyLookup>().unwrap();
|
||||
*lookup.typeid_from_name.get(&name).unwrap()
|
||||
}
|
||||
_ => {
|
||||
panic!("A userdata or table value was not returned!");
|
||||
// TODO: Handle properly
|
||||
}
|
||||
};
|
||||
|
||||
// update the component tick
|
||||
let mut world = this.write();
|
||||
let arch = world.entity_archetype_mut(row.entity).unwrap();
|
||||
let idx = arch.entity_indexes().get(&row.entity).unwrap().clone();
|
||||
let c = arch.get_column_mut(lua_typeid).unwrap();
|
||||
c.entity_ticks[idx.0 as usize] = current;
|
||||
|
||||
// apply the new component data
|
||||
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
||||
let reg_type = reg.get_type(lua_typeid).unwrap();
|
||||
|
||||
let proxy = reg_type
|
||||
.get_data::<ReflectLuaProxy>()
|
||||
// this should actually be safe since the ReflectedIterator
|
||||
// attempts to get the type data before it is tried here
|
||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||
(proxy.fn_apply)(lua, ptr, &comp)?;
|
||||
}
|
||||
} else {
|
||||
let msg = format!(
|
||||
"Too many arguments were returned from the World view!
|
||||
At most, the expected number of results is {}.",
|
||||
ptrs.len()
|
||||
);
|
||||
return Err(mlua::Error::runtime(msg));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.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");
|
||||
}
|
||||
};
|
||||
/* let reflect = ty
|
||||
.execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
|
||||
.expect("Type does not implement 'reflect_type' properly"); */
|
||||
|
||||
// get the actual resource handle wrapper
|
||||
let 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())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<'lua, F: mlua::UserDataFields<'lua, 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<'lua, M: mlua::UserDataMethods<'lua, 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<'a> mlua::FromLua<'a> for LuaResHandle {
|
||||
fn from_lua(val: mlua::Value<'a>, _: &'a 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)
|
||||
}
|
||||
});
|
||||
});
|
|
@ -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<'lua> mlua::FromLua<'lua> for LuaDeltaTime {
|
||||
fn from_lua(_: mlua::Value<'lua>, _: &'lua 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<'lua> mlua::IntoLua<'lua> for LuaDeltaTime {
|
||||
fn into_lua(self, _: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
|
||||
Ok(mlua::Value::Number(*self.0 as f64))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<'lua, M: mlua::UserDataMethods<'lua, 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,9 +42,9 @@ 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")?;
|
||||
|
@ -55,16 +52,16 @@ impl elua::Userdata for LuaActionHandler {
|
|||
|
||||
// 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_vec(multi))
|
||||
});
|
||||
|
||||
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<'a> mlua::FromLua<'a> for LuaActionHandler {
|
||||
fn from_lua(val: mlua::Value<'a>, _: &'a 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())
|
||||
}
|
||||
|
|
|
@ -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,91 +185,91 @@ 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)
|
||||
|
|
|
@ -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)))))
|
||||
}
|
||||
}
|
|
@ -10,3 +10,6 @@ impl std::ops::Deref for ScriptDynamicBundle {
|
|||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: todo!()
|
||||
unsafe impl Send for ScriptDynamicBundle {}
|
Loading…
Reference in New Issue