Compare commits

..

2 Commits

Author SHA1 Message Date
SeanOMik ef2b0bf326
ecs,scripting: fix invalid resources being passed to lua
CI / build (pull_request) Failing after 3m35s Details
The issue was World::get_resource_ptr, it was returning a pointer to the AtomicRefCell instead of the actual resource data
2024-09-29 14:35:24 -04:00
SeanOMik fa22a0310c
scripting: switch to latest mlua, create custom impl of lua's `getmetatable` 2024-09-29 15:59:48 -04:00
15 changed files with 141 additions and 124 deletions

10
Cargo.lock generated
View File

@ -2062,22 +2062,20 @@ dependencies = [
[[package]] [[package]]
name = "mlua" name = "mlua"
version = "0.9.9" version = "0.10.0-beta.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/mlua-rs/mlua?rev=4dddf3c18d4590f7c92214fa592c3faca3d2c812#4dddf3c18d4590f7c92214fa592c3faca3d2c812"
checksum = "d111deb18a9c9bd33e1541309f4742523bfab01d276bfa9a27519f6de9c11dc7"
dependencies = [ dependencies = [
"bstr", "bstr",
"mlua-sys", "mlua-sys",
"num-traits", "num-traits",
"once_cell", "parking_lot",
"rustc-hash 2.0.0", "rustc-hash 2.0.0",
] ]
[[package]] [[package]]
name = "mlua-sys" name = "mlua-sys"
version = "0.6.3" version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/mlua-rs/mlua?rev=4dddf3c18d4590f7c92214fa592c3faca3d2c812#4dddf3c18d4590f7c92214fa592c3faca3d2c812"
checksum = "ebe026d6bd1583a9cf9080e189030ddaea7e6f5f0deb366a8e26f8a26c4135b8"
dependencies = [ dependencies = [
"cc", "cc",
"cfg-if", "cfg-if",

View File

@ -1,10 +1,13 @@
---Return the userdata's name from its metatable ---Return the userdata's name from its metatable.
---
---Returns nil if the userdata doesn't have a metatable.
---@param val userdata ---@param val userdata
---@return string ---@return string|nil
function udname(val) function udname(val)
local tbl = getmetatable(val) local tbl = debug.getmetatable(val)
if type(tbl) == "boolean" then
error("what, got a boolean??: " .. tostring(tbl), 1) if tbl == nil then
return nil
end end
return tbl.__name return tbl.__name
@ -15,14 +18,14 @@ function on_init()
local cube = world:request_res("../assets/cube-texture-embedded.gltf") local cube = world:request_res("../assets/cube-texture-embedded.gltf")
print("Loaded textured cube (" .. udname(cube) .. ")") print("Loaded textured cube (" .. udname(cube) .. ")")
--[[ cube:wait_until_loaded() cube:wait_until_loaded()
local scenes = cube:scenes() local scenes = cube:scenes()
local cube_scene = scenes[1] local cube_scene = scenes[1]
local pos = Transform.from_translation(Vec3.new(0, 0, -8.0)) local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
local e = world:spawn(pos, cube_scene) local e = world:spawn(pos, cube_scene)
print("spawned entity " .. tostring(e)) ]] print("spawned entity " .. tostring(e))
end end
--[[ function on_first() --[[ function on_first()
@ -48,12 +51,12 @@ function on_update()
end, Transform) ]] end, Transform) ]]
---@type number ---@type number
--[[ local dt = world:resource(DeltaTime) local dt = world:resource(DeltaTime)
world:view(function (t) world:view(function (t)
t:translate(0, 0.15 * dt, 0) t:translate(0, 0.15 * dt, 0)
return t return t
end, Transform) ]] end, Transform)
end end
--[[ function on_post_update() --[[ function on_post_update()

View File

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

View File

@ -22,7 +22,9 @@ tracing = "0.1.37"
atomic_refcell = "0.1.13" atomic_refcell = "0.1.13"
# enabled with lua feature # enabled with lua feature
mlua = { version = "0.9.9", features = ["lua54", "send"], optional = true } # luajit maybe? #
#mlua = { version = "0.9.9", features = ["lua54", "send"], optional = true } # luajit maybe?
mlua = { git = "https://github.com/mlua-rs/mlua", rev = "4dddf3c18d4590f7c92214fa592c3faca3d2c812", features = ["lua54", "send"], optional = true } # luajit maybe?
#elua = { path = "./elua", optional = true } #elua = { path = "./elua", optional = true }
itertools = "0.12.0" itertools = "0.12.0"
paste = "1.0.14" paste = "1.0.14"

View File

@ -187,7 +187,7 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
} }
impl mlua::UserData for #wrapper_name { impl mlua::UserData for #wrapper_name {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("path", |_, this| Ok(this.path())); fields.add_field_method_get("path", |_, this| Ok(this.path()));
fields.add_field_method_get("version", |_, this| Ok(this.version())); 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("uuid", |_, this| Ok(this.uuid().to_string()));
@ -204,7 +204,7 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
#(#custom_getters)* #(#custom_getters)*
} }
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("is_watched", |_, this, ()| { methods.add_method("is_watched", |_, this, ()| {
Ok(this.is_watched()) Ok(this.is_watched())
}); });
@ -234,8 +234,8 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
} }
} }
impl<'a> mlua::FromLua<'a> for #wrapper_name { impl mlua::FromLua for #wrapper_name {
fn from_lua(val: mlua::Value<'a>, _: &'a mlua::Lua) -> mlua::Result<Self> { fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name(); let tyname = val.type_name();
let ud = val.as_userdata() let ud = val.as_userdata()
.ok_or(mlua::Error::external(crate::lua::Error::type_mismatch(#ud_name, &tyname)))?; .ok_or(mlua::Error::external(crate::lua::Error::type_mismatch(#ud_name, &tyname)))?;

View File

@ -224,7 +224,7 @@ impl MetaMethod {
// this is the body of the else statement // this is the body of the else statement
{ {
// try to get the name of the userdata for the error message // try to get the name of the userdata for the error message
if let Ok(mt) = ud.get_metatable() { if let Ok(mt) = ud.metatable() {
if let Ok(name) = mt.get::<String>("__name") { if let Ok(name) = mt.get::<String>("__name") {
return Err(mlua::Error::BadArgument { return Err(mlua::Error::BadArgument {
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
@ -480,8 +480,8 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
} }
} }
impl<'lua> mlua::FromLua<'lua> for #wrapper_typename { impl mlua::FromLua for #wrapper_typename {
fn from_lua(value: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> { fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match value { match value {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()), _ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()),
@ -490,11 +490,11 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
} }
impl mlua::UserData for #wrapper_typename { impl mlua::UserData for #wrapper_typename {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
#(#field_get_set_pairs)* #(#field_get_set_pairs)*
} }
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
#lua_reflects #lua_reflects
#new_func_tokens #new_func_tokens

View File

@ -6,38 +6,36 @@ use lyra_reflect::TypeRegistry;
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
use super::ReflectLuaProxy; use super::ReflectLuaProxy;
use crate::ScriptWorldPtr;
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
pub struct ReflectedItem<'a> { pub struct ReflectedItem {
//pub proxy: &'a ReflectLuaProxy, //pub proxy: &'a ReflectLuaProxy,
pub comp_ptr: NonNull<u8>, pub comp_ptr: NonNull<u8>,
pub comp_val: mlua::Value<'a>, pub comp_val: mlua::Value,
} }
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
pub struct ReflectedRow<'a> { pub struct ReflectedRow {
pub entity: Entity, pub entity: Entity,
pub row: Vec<ReflectedItem<'a>>, pub row: Vec<ReflectedItem>,
} }
pub struct ReflectedIterator { pub struct ReflectedIterator<'a> {
pub world: ScriptWorldPtr, pub world: &'a lyra_ecs::World,
pub dyn_view: DynamicViewStateIter, pub dyn_view: DynamicViewStateIter,
pub reflected_components: Option<NonNull<TypeRegistry>> pub reflected_components: Option<NonNull<TypeRegistry>>
} }
impl ReflectedIterator { impl<'a> ReflectedIterator<'a> {
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option<ReflectedRow<'a>> { pub fn next_lua(&mut self, lua: &mlua::Lua) -> Option<ReflectedRow> {
use mlua::IntoLua; use mlua::IntoLua;
let world = self.world.read(); //let world = self.world.read();
let n = self.dyn_view.next(&world); let n = self.dyn_view.next(&self.world);
if let Some((en, row)) = n { if let Some((en, row)) = n {
if self.reflected_components.is_none() { if self.reflected_components.is_none() {
self.reflected_components = world.get_resource::<TypeRegistry>() self.reflected_components = self.world.get_resource::<TypeRegistry>()
.map(|r| NonNull::from(r.deref())); .map(|r| NonNull::from(r.deref()));
} }

View File

@ -2,7 +2,7 @@ pub mod dynamic_iter;
pub use dynamic_iter::*; pub use dynamic_iter::*;
pub mod world; pub mod world;
use mlua::AnyUserDataExt; use mlua::ObjectLike;
pub use world::*; pub use world::*;
pub mod script; pub mod script;
@ -113,24 +113,24 @@ pub trait RegisterLuaType {
/// Register a type to Lua that **is not wrapped**. /// Register a type to Lua that **is not wrapped**.
fn register_lua_type<'a, T>(&mut self) fn register_lua_type<'a, T>(&mut self)
where where
T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData; T: Reflect + LuaProxy + Clone + mlua::FromLua + mlua::UserData;
/// Registers a type to Lua that is wrapped another type. /// Registers a type to Lua that is wrapped another type.
/// This would be used for something like `UserdataRef<T>`. /// This would be used for something like `UserdataRef<T>`.
fn register_lua_wrapper<'a, W>(&mut self) fn register_lua_wrapper<'a, W>(&mut self)
where where
W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData; W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua + mlua::UserData;
/// Registers a type to Lua that can be converted into and from Lua types. /// Registers a type to Lua that can be converted into and from Lua types.
fn register_lua_convert<T>(&mut self) fn register_lua_convert<T>(&mut self)
where where
T: Clone + for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + LuaWrapper + 'static; T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static;
} }
impl RegisterLuaType for World { impl RegisterLuaType for World {
fn register_lua_type<'a, T>(&mut self) fn register_lua_type<'a, T>(&mut self)
where where
T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData T: Reflect + LuaProxy + Clone + mlua::FromLua + mlua::UserData
{ {
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap(); let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
@ -142,17 +142,17 @@ impl RegisterLuaType for World {
fn register_lua_wrapper<'a, W>(&mut self) fn register_lua_wrapper<'a, W>(&mut self)
where where
W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua + mlua::UserData
{ {
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap(); let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
let reg_type = registry.get_type_or_default(W::wrapped_type_id()); let reg_type = registry.get_type_or_default(W::wrapped_type_id());
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<W>()); reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<W>());
} }
fn register_lua_convert<T>(&mut self) fn register_lua_convert<T>(&mut self)
where where
T: Clone + for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + LuaWrapper + 'static, T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static,
{ {
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap(); let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
@ -161,8 +161,8 @@ impl RegisterLuaType for World {
} }
} }
impl<'lua> mlua::FromLua<'lua> for ScriptBorrow { impl mlua::FromLua for ScriptBorrow {
fn from_lua(value: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> { fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match value { match value {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => unreachable!(), _ => unreachable!(),
@ -176,6 +176,6 @@ impl mlua::UserData for ScriptBorrow {
/// Helper function used for reflecting userdata as a ScriptBorrow /// Helper function used for reflecting userdata as a ScriptBorrow
pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow { pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow {
ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) ud.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
.expect("Type does not implement internal reflect method properly") .expect("Type does not implement internal reflect method properly")
} }

View File

@ -1,6 +1,6 @@
use std::sync::{Arc, Mutex}; use std::{ops::Deref, sync::{Arc, Mutex}};
use mlua::AnyUserDataExt; use mlua::{AnyUserData, ObjectLike};
use tracing::{debug, debug_span}; use tracing::{debug, debug_span};
use crate::{ScriptApiProvider, ScriptData}; use crate::{ScriptApiProvider, ScriptData};
@ -58,9 +58,9 @@ impl ScriptApiProvider for UtilityApiProvider {
}, },
mlua::Value::UserData(ud) => { mlua::Value::UserData(ud) => {
if let Ok(tos) = if let Ok(tos) =
ud.get::<_, mlua::Function>(mlua::MetaMethod::ToString.name()) ud.get::<mlua::Function>(mlua::MetaMethod::ToString.name())
{ {
tos.call::<_, String>(())? tos.call::<String>(())?
} else { } else {
return Err(mlua::Error::runtime( return Err(mlua::Error::runtime(
"UserData does not implement MetaMethod '__tostring'", "UserData does not implement MetaMethod '__tostring'",
@ -73,7 +73,7 @@ impl ScriptApiProvider for UtilityApiProvider {
)); ));
}, },
mlua::Value::Error(error) => { mlua::Value::Error(error) => {
return Err(error.clone()); return Err(error.deref().clone());
}, },
}, },
None => { None => {
@ -123,8 +123,8 @@ impl ScriptApiProvider for UtilityApiProvider {
formatted = format!("{}{}", formatted, text); formatted = format!("{}{}", formatted, text);
lua.globals() lua.globals()
.get::<_, mlua::Function>("print")? .get::<mlua::Function>("print")?
.call::<_, ()>(formatted)?; .call::<()>(formatted)?;
Ok(()) Ok(())
}; };
@ -141,9 +141,24 @@ impl ScriptApiProvider for UtilityApiProvider {
Ok(()) Ok(())
})?; })?;
// a custom implementation of `getmetatable` is required since mlua protects __metatable,
// making it impossible to get the metatable of userdata.
let getmetatable_func = ctx.create_function(|lua, ud: AnyUserData| {
// the userdata is left on the stack from `lua_getmetatable`, so that needs to be
// included in the returns
let (_ud, table): (mlua::AnyUserData, mlua::Table) = unsafe {
lua.exec_raw(ud, |state| {
mlua::ffi::lua_getmetatable(state, -1);
})
}?;
Ok(table)
})?;
let globals = ctx.globals(); let globals = ctx.globals();
globals.set("printf", printf_func)?; globals.set("printf", printf_func)?;
globals.set("print", print_func)?; globals.set("print", print_func)?;
globals.set("getmetatable2", getmetatable_func)?;
Ok(()) Ok(())
} }

View File

@ -1,6 +1,6 @@
use std::{any::TypeId, collections::HashMap, ptr::NonNull}; use std::{any::TypeId, collections::HashMap, ptr::NonNull};
use mlua::{AnyUserDataExt, IntoLua}; use mlua::{ObjectLike, IntoLua};
use lyra_ecs::{ComponentInfo, DynamicBundle}; use lyra_ecs::{ComponentInfo, DynamicBundle};
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
@ -15,10 +15,10 @@ pub trait LuaWrapper {
/// A trait that used to convert something into lua, or to set something to a value from lua. /// A trait that used to convert something into lua, or to set something to a value from lua.
pub trait LuaProxy { pub trait LuaProxy {
fn as_lua_value<'lua>( fn as_lua_value(
lua: &'lua mlua::Lua, lua: &mlua::Lua,
this: &dyn Reflect, this: &dyn Reflect,
) -> mlua::Result<mlua::Value<'lua>>; ) -> mlua::Result<mlua::Value>;
fn apply( fn apply(
lua: &mlua::Lua, lua: &mlua::Lua,
@ -29,15 +29,14 @@ pub trait LuaProxy {
impl<'a, T> LuaProxy for T impl<'a, T> LuaProxy for T
where where
T: Reflect + Clone + mlua::FromLua<'a> + mlua::UserData T: Reflect + Clone + mlua::FromLua + mlua::UserData
{ {
fn as_lua_value<'lua>( fn as_lua_value(
lua: &'lua mlua::Lua, lua: &mlua::Lua,
this: &dyn Reflect, this: &dyn Reflect,
) -> mlua::Result<mlua::Value<'lua>> { ) -> mlua::Result<mlua::Value> {
let this = this.as_any().downcast_ref::<T>().unwrap(); let this = this.as_any().downcast_ref::<T>().unwrap();
lua.create_userdata(this.clone()) this.clone().into_lua(lua)
.and_then(|ud| ud.into_lua(lua))
} }
fn apply( fn apply(
@ -67,11 +66,11 @@ pub struct LuaTableProxyLookup {
#[derive(Clone)] #[derive(Clone)]
pub struct ReflectLuaProxy { pub struct ReflectLuaProxy {
pub fn_as_lua: pub fn_as_lua:
for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull<()>) -> mlua::Result<mlua::Value<'a>>, for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull<()>) -> mlua::Result<mlua::Value>,
pub fn_apply: for<'a> fn( pub fn_apply: for<'a> fn(
lua: &'a mlua::Lua, lua: &'a mlua::Lua,
this_ptr: NonNull<()>, this_ptr: NonNull<()>,
value: &'a mlua::Value<'a>, value: &'a mlua::Value,
) -> mlua::Result<()>, ) -> mlua::Result<()>,
} }
@ -96,7 +95,7 @@ impl ReflectLuaProxy {
/// Create from a type that implements FromLua and AsLua /// Create from a type that implements FromLua and AsLua
pub fn from_as_and_from_lua<T>() -> Self pub fn from_as_and_from_lua<T>() -> Self
where where
T: for<'a> mlua::FromLua<'a> + for<'a> mlua::IntoLua<'a> + Clone T: mlua::FromLua + mlua::IntoLua + Clone
{ {
Self { Self {
fn_as_lua: |lua, this| -> mlua::Result<mlua::Value> { fn_as_lua: |lua, this| -> mlua::Result<mlua::Value> {
@ -115,8 +114,8 @@ impl ReflectLuaProxy {
} }
} }
impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle { impl mlua::FromLua for ScriptDynamicBundle {
fn from_lua(val: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> { fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match val { match val {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
mlua::Value::Nil => Err(Error::Nil.into()), mlua::Value::Nil => Err(Error::Nil.into()),
@ -126,10 +125,10 @@ impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle {
} }
impl mlua::UserData for ScriptDynamicBundle { impl mlua::UserData for ScriptDynamicBundle {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new()))); methods.add_function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new())));
methods.add_method_mut("push", |_, this, comp: mlua::AnyUserData| { methods.add_method_mut("push", |_, this, comp: mlua::AnyUserData| {
let script_brw = comp.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?; let script_brw = comp.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?;
let reflect = script_brw.reflect_branch.as_component_unchecked(); let reflect = script_brw.reflect_branch.as_component_unchecked();
let refl_data = script_brw.data.unwrap(); let refl_data = script_brw.data.unwrap();

View File

@ -11,8 +11,8 @@ fn try_call_lua_function(lua: &mlua::Lua, fn_name: &str) -> Result<(), ScriptErr
let globals = lua.globals(); let globals = lua.globals();
if globals.contains_key(fn_name)? { if globals.contains_key(fn_name)? {
let lua_fn = globals.get::<_, mlua::Function>(fn_name)?; let lua_fn = globals.get::<mlua::Function>(fn_name)?;
lua_fn.call::<_, ()>(()) lua_fn.call::<()>(())
.map_err(ScriptError::from)?; .map_err(ScriptError::from)?;
} }
@ -26,7 +26,7 @@ impl ScriptHost for LuaHost {
let mut ctx = Mutex::new({ let mut ctx = Mutex::new({
// unsafe is required to allow the debug module // unsafe is required to allow the debug module
let s = unsafe { mlua::Lua::unsafe_new() }; let s = unsafe { mlua::Lua::unsafe_new() };
s.load_from_std_lib(StdLib::ALL)?; s.load_std_libs(StdLib::ALL)?;
s s
}); });

View File

@ -7,14 +7,14 @@ use lyra_ecs::{
}; };
use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry}; use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
use mlua::{AnyUserDataExt, IntoLua}; use mlua::{IntoLua, ObjectLike};
use super::{ use super::{
reflect_user_data, wrappers::LuaResHandleToComponent, Error, 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> mlua::FromLua<'lua> for ScriptEntity { impl mlua::FromLua for ScriptEntity {
fn from_lua(value: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> { fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match value { match value {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptEntity", "Nil"))), mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptEntity", "Nil"))),
@ -24,7 +24,7 @@ impl<'lua> mlua::FromLua<'lua> for ScriptEntity {
} }
impl mlua::UserData for ScriptEntity { impl mlua::UserData for ScriptEntity {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this.0)) Ok(format!("{:?}", this.0))
}); });
@ -37,8 +37,8 @@ pub enum WorldError {
LuaInvalidUsage(String), LuaInvalidUsage(String),
} }
impl<'lua> mlua::FromLua<'lua> for ScriptWorldPtr { impl mlua::FromLua for ScriptWorldPtr {
fn from_lua(val: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> { fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
match val { match val {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptWorldPtr", "Nil"))), mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptWorldPtr", "Nil"))),
@ -48,7 +48,7 @@ impl<'lua> mlua::FromLua<'lua> for ScriptWorldPtr {
} }
impl mlua::UserData for ScriptWorldPtr { impl mlua::UserData for ScriptWorldPtr {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("spawn", |_, this, vals: mlua::MultiValue| { methods.add_method_mut("spawn", |_, this, vals: mlua::MultiValue| {
let mut world = this.write(); let mut world = this.write();
@ -64,7 +64,7 @@ impl mlua::UserData for ScriptWorldPtr {
})?; })?;
let comp_borrow = { let comp_borrow = {
if let Ok(as_comp) = ud.get::<_, mlua::Function>(FN_NAME_INTERNAL_AS_COMPONENT) if let Ok(as_comp) = ud.get::<mlua::Function>(FN_NAME_INTERNAL_AS_COMPONENT)
{ {
let ud = match as_comp.call(ud.clone())? { let ud = match as_comp.call(ud.clone())? {
mlua::Value::UserData(ud) => ud, mlua::Value::UserData(ud) => ud,
@ -72,9 +72,9 @@ impl mlua::UserData for ScriptWorldPtr {
_ => todo!(), _ => todo!(),
}; };
ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())? ud.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
} else { } else {
ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())? ud.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
} }
}; };
@ -139,7 +139,7 @@ impl mlua::UserData for ScriptWorldPtr {
} }
mlua::Value::UserData(ud) => { mlua::Value::UserData(ud) => {
let reflect = ud let reflect = ud
.call_function::<_, ScriptBorrow>( .call_function::<ScriptBorrow>(
FN_NAME_INTERNAL_REFLECT_TYPE, FN_NAME_INTERNAL_REFLECT_TYPE,
(), (),
) )
@ -155,32 +155,34 @@ impl mlua::UserData for ScriptWorldPtr {
let iter = view.into_iter(); let iter = view.into_iter();
let mut reflected_iter = ReflectedIterator { let mut reflected_iter = ReflectedIterator {
world: this.clone(), // SAFETY: bypassing the borrow checker here to get a pointer of the world
// is required since we mutably borrow below. Its safe to do so since
// only the entity ticks are updated. They are accessing different things
// from the world.
world: unsafe { NonNull::from(&*world).as_ref() },
dyn_view: DynamicViewStateIter::from(iter), dyn_view: DynamicViewStateIter::from(iter),
reflected_components: None, reflected_components: None,
}; };
let mut current = world.current_tick(); let current = world.current_tick();
let mut has_ticked = false;
// drop read lock and acquire the write lock.
// dropping must be done to avoid mutex deadlock
drop(world);
let mut world = this.write();
while let Some(row) = reflected_iter.next_lua(lua) { while let Some(row) = reflected_iter.next_lua(lua) {
let r = row let r = row.row.into_iter()
.row
.into_iter() .into_iter()
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>())) .map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let (values, ptrs) = let (values, ptrs) =
itertools::multiunzip::<(Vec<mlua::Value>, Vec<NonNull<()>>), _>(r); itertools::multiunzip::<(Vec<mlua::Value>, Vec<NonNull<()>>), _>(r);
let mult_val = mlua::MultiValue::from_vec(values); let mult_val = mlua::MultiValue::from_iter(values.into_iter());
let res: mlua::MultiValue = system.call(mult_val)?; let res: mlua::MultiValue = system.call(mult_val)?;
// if values were returned, find the type in the type registry, and apply the new values // if values were returned, find the type in the type registry, and apply the new values
if res.len() <= ptrs.len() { 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) { for (comp, ptr) in res.into_iter().zip(ptrs) {
let lua_typeid = match &comp { let lua_typeid = match &comp {
@ -203,7 +205,6 @@ impl mlua::UserData for ScriptWorldPtr {
}; };
// update the component tick // update the component tick
let mut world = this.write();
let arch = world.entity_archetype_mut(row.entity).unwrap(); let arch = world.entity_archetype_mut(row.entity).unwrap();
let idx = arch.entity_indexes().get(&row.entity).unwrap().clone(); let idx = arch.entity_indexes().get(&row.entity).unwrap().clone();
let c = arch.get_column_mut(lua_typeid).unwrap(); let c = arch.get_column_mut(lua_typeid).unwrap();
@ -236,20 +237,17 @@ impl mlua::UserData for ScriptWorldPtr {
methods.add_method_mut("resource", |lua, this, (ty,): (mlua::Value,)| { methods.add_method_mut("resource", |lua, this, (ty,): (mlua::Value,)| {
let reflect = match ty { let reflect = match ty {
mlua::Value::UserData(ud) => ud mlua::Value::UserData(ud) => ud
.call_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) .call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
.expect("Type does not implement 'reflect_type' properly"), .expect("Type does not implement 'reflect_type' properly"),
mlua::Value::Table(t) => { mlua::Value::Table(t) => {
let f: mlua::Function = t.get(FN_NAME_INTERNAL_REFLECT_TYPE)?; let f: mlua::Function = t.get(FN_NAME_INTERNAL_REFLECT_TYPE)?;
f.call::<_, ScriptBorrow>(()) f.call::<ScriptBorrow>(())
.expect("Type does not implement 'reflect_type' properly") .expect("Type does not implement 'reflect_type' properly")
} }
_ => { _ => {
panic!("how"); panic!("how");
} }
}; };
/* let reflect = ty
.execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
.expect("Type does not implement 'reflect_type' properly"); */
let mut world = this.write(); let mut world = this.write();
let res = reflect.reflect_branch.as_resource_unchecked(); let res = reflect.reflect_branch.as_resource_unchecked();
@ -270,11 +268,11 @@ impl mlua::UserData for ScriptWorldPtr {
methods.add_method_mut("add_resource", |_, this, res: mlua::Value| { methods.add_method_mut("add_resource", |_, this, res: mlua::Value| {
let reflect = match res { let reflect = match res {
mlua::Value::UserData(ud) => ud mlua::Value::UserData(ud) => ud
.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) .call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
.expect("Type does not implement 'reflect_type' properly"), .expect("Type does not implement 'reflect_type' properly"),
mlua::Value::Table(t) => { mlua::Value::Table(t) => {
let f: mlua::Function = t.get(FN_NAME_INTERNAL_REFLECT)?; let f: mlua::Function = t.get(FN_NAME_INTERNAL_REFLECT)?;
f.call::<_, ScriptBorrow>(()) f.call::<ScriptBorrow>(())
.expect("Type does not implement 'reflect_type' properly") .expect("Type does not implement 'reflect_type' properly")
} }
_ => { _ => {

View File

@ -56,7 +56,7 @@ impl From<UntypedResHandle> for LuaResHandle {
} }
impl mlua::UserData for LuaResHandle { impl mlua::UserData for LuaResHandle {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("path", |_, this| Ok(this.path())); fields.add_field_method_get("path", |_, this| Ok(this.path()));
fields.add_field_method_get("version", |_, this| Ok(this.version())); 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("uuid", |_, this| Ok(this.uuid().to_string()));
@ -71,7 +71,7 @@ impl mlua::UserData for LuaResHandle {
}); });
} }
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("is_watched", |_, this, ()| { methods.add_method("is_watched", |_, this, ()| {
Ok(this.is_watched()) Ok(this.is_watched())
}); });
@ -100,8 +100,8 @@ impl mlua::UserData for LuaResHandle {
} }
} }
impl<'a> mlua::FromLua<'a> for LuaResHandle { impl mlua::FromLua for LuaResHandle {
fn from_lua(val: mlua::Value<'a>, _: &'a mlua::Lua) -> mlua::Result<Self> { fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name(); let tyname = val.type_name();
let ud = val.as_userdata() let ud = val.as_userdata()
.ok_or(mlua::Error::external(Error::type_mismatch("Handle", &tyname)))?; .ok_or(mlua::Error::external(Error::type_mismatch("Handle", &tyname)))?;

View File

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

View File

@ -12,12 +12,12 @@ pub struct LuaActionHandler {
} }
impl mlua::UserData for LuaActionHandler { impl mlua::UserData for LuaActionHandler {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("new", |_, table: mlua::Table| { methods.add_function("new", |_, table: mlua::Table| {
let mut handler = ActionHandler::new(); let mut handler = ActionHandler::new();
// create the layouts and add them to the handler // create the layouts and add them to the handler
let layouts = table.get::<_, mlua::Table>("layouts") let layouts = table.get::<mlua::Table>("layouts")
.map_err(|_| mlua::Error::runtime("missing 'layouts' in ActionHandler table"))?; .map_err(|_| mlua::Error::runtime("missing 'layouts' in ActionHandler table"))?;
for layout_id in layouts.sequence_values::<u32>() { for layout_id in layouts.sequence_values::<u32>() {
let layout_id = layout_id?; let layout_id = layout_id?;
@ -26,7 +26,7 @@ impl mlua::UserData for LuaActionHandler {
} }
// add the actions to the handler // add the actions to the handler
let actions = table.get::<_, mlua::Table>("actions") let actions = table.get::<mlua::Table>("actions")
.map_err(|_| mlua::Error::runtime("missing 'actions' in ActionHandler table"))?; .map_err(|_| mlua::Error::runtime("missing 'actions' in ActionHandler table"))?;
for pair in actions.pairs::<String, String>() { for pair in actions.pairs::<String, String>() {
let (action_lbl, action_type) = pair?; let (action_lbl, action_type) = pair?;
@ -42,17 +42,17 @@ impl mlua::UserData for LuaActionHandler {
} }
// find the mappings and start processing them // find the mappings and start processing them
let mappings= table.get::<_, mlua::Table>("mappings") let mappings= table.get::<mlua::Table>("mappings")
.map_err(|_| mlua::Error::runtime("missing 'mappings' in ActionHandler table"))?; .map_err(|_| mlua::Error::runtime("missing 'mappings' in ActionHandler table"))?;
for (map_id, tbl) in mappings.sequence_values::<mlua::Table>().enumerate() { for (map_id, tbl) in mappings.sequence_values::<mlua::Table>().enumerate() {
let tbl = tbl?; let tbl = tbl?;
let layout_id = tbl.get::<_, u32>("layout")?; let layout_id = tbl.get::<u32>("layout")?;
let mut mapping = ActionMapping::new(LayoutId(layout_id), ActionMappingId(map_id as u32)); let mut mapping = ActionMapping::new(LayoutId(layout_id), ActionMappingId(map_id as u32));
// find the binds and start processing them // find the binds and start processing them
// the keys are used as the action names, and then the value is an array (lua table) // the keys are used as the action names, and then the value is an array (lua table)
let binds_tbl = tbl.get::<_, mlua::Table>("binds") let binds_tbl = tbl.get::<mlua::Table>("binds")
.map_err(|_| mlua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?; .map_err(|_| mlua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?;
for pair in binds_tbl.pairs::<String, mlua::Table>() { for pair in binds_tbl.pairs::<String, mlua::Table>() {
let (action_lbl, input_binds) = pair?; let (action_lbl, input_binds) = pair?;
@ -140,7 +140,7 @@ impl mlua::UserData for LuaActionHandler {
multi.push(name.into_lua(lua)?); multi.push(name.into_lua(lua)?);
multi.push(val.into_lua(lua)?); multi.push(val.into_lua(lua)?);
Ok(mlua::MultiValue::from_vec(multi)) Ok(mlua::MultiValue::from_iter(multi.into_iter()))
}); });
methods.add_method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { methods.add_method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
@ -153,8 +153,8 @@ impl mlua::UserData for LuaActionHandler {
} }
} }
impl<'a> mlua::FromLua<'a> for LuaActionHandler { impl mlua::FromLua for LuaActionHandler {
fn from_lua(val: mlua::Value<'a>, _: &'a mlua::Lua) -> mlua::Result<Self> { fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let tyname = val.type_name(); let tyname = val.type_name();
let ud = val.as_userdata() let ud = val.as_userdata()
.ok_or(mlua::Error::external(Error::type_mismatch("ActionHandler", &tyname)))?; .ok_or(mlua::Error::external(Error::type_mismatch("ActionHandler", &tyname)))?;