diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c181ae1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lyra-scripting/elua"] + path = lyra-scripting/elua + url = git@git.seanomik.net:SeanOMik/elua.git diff --git a/Cargo.lock b/Cargo.lock index ad8a285..593b602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,16 +432,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "bstr" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.14.0" @@ -742,6 +732,14 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elua" +version = "0.1.0" +dependencies = [ + "mlua-sys", + "thiserror", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1608,13 +1606,13 @@ name = "lyra-scripting" version = "0.1.0" dependencies = [ "anyhow", + "elua", "itertools 0.12.0", "lyra-ecs", "lyra-game", "lyra-reflect", "lyra-resource", "lyra-scripting-derive", - "mlua", "thiserror", "tracing", "tracing-subscriber", @@ -1713,24 +1711,11 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "mlua" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c81f8ac20188feb5461a73eabb22a34dd09d6d58513535eb587e46bff6ba250" -dependencies = [ - "bstr", - "mlua-sys", - "num-traits", - "once_cell", - "rustc-hash", -] - [[package]] name = "mlua-sys" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc29228347d6bdc9e613dc95c69df2817f755434ee0f7f3b27b57755fe238b7f" +checksum = "2847b42764435201d8cbee1f517edb79c4cca4181877b90047587c89e1b7bce4" dependencies = [ "cc", "cfg-if", diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index ffc46c5..2bbbf6d 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -12,18 +12,51 @@ function on_pre_update() print("Lua's pre-update function was called") end ]] +--- +---Recursively dumps a table as a string. +--- +---@param obj table +---@return string +---@nodiscard +function dump_table(obj) + if type(obj) == 'table' then + local s = '{ ' + for k,v in pairs(obj) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. dump_table(v) .. ',' + end + return s .. '} ' + else + return tostring(obj) + end +end + function on_update() --print("Lua's update function was called") - local dt = world:resource(DeltaTime) - --print("DeltaTime was " .. tostring(dt) .. "s") + --local v = Vec3:new(50, 10, 20) + --print("Vec3 = " .. tostring(v)) - world:view(function (t) - --print("Found entity at a really cool place: " .. tostring(t)) - t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) + world:view( + ---@param t Transform + function (t) + print("Found entity at a really cool place: " .. tostring(t)) + t.translation:move_by(0, 0.001, 0) - return t - end, Transform) + return t + end, + Transform + ) + +-- local dt = world:resource(DeltaTime) +-- --print("DeltaTime was " .. tostring(dt) .. "s") +-- +-- world:view(function (t) +-- --print("Found entity at a really cool place: " .. tostring(t)) +-- t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) +-- +-- return t +-- end, Transform) end --[[ function on_post_update() diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 0dabc2f..f9fa6d3 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -84,16 +84,16 @@ async fn main() { let mut resman = world.get_resource_mut::(); //let diffuse_texture = resman.request::("assets/happy-tree.png").unwrap(); - let antique_camera_model = resman.request::("assets/AntiqueCamera.glb").unwrap(); + //let antique_camera_model = resman.request::("assets/AntiqueCamera.glb").unwrap(); //let cube_model = resman.request::("assets/cube-texture-bin.glb").unwrap(); let cube_model = resman.request::("assets/texture-sep/texture-sep.gltf").unwrap(); let crate_model = resman.request::("assets/crate/crate.gltf").unwrap(); drop(resman); - world.spawn(( + /* world.spawn(( ModelComponent(antique_camera_model), Transform::from_xyz(0.0, -5.0, -10.0), - )); + )); */ { let cube_tran = Transform::from_xyz(-3.5, 0.0, -8.0); diff --git a/lyra-game/src/game.rs b/lyra-game/src/game.rs index a3d8666..0a49e45 100755 --- a/lyra-game/src/game.rs +++ b/lyra-game/src/game.rs @@ -342,7 +342,7 @@ impl Game { .with(fmt::layer().with_writer(stdout_layer)) .with(filter::Targets::new() // done by prefix, so it includes all lyra subpackages - .with_target("lyra", Level::DEBUG) + .with_target("lyra", Level::TRACE) .with_target("wgpu", Level::WARN) .with_default(Level::INFO)) .init(); diff --git a/lyra-scripting/Cargo.toml b/lyra-scripting/Cargo.toml index 320ac06..2782c56 100644 --- a/lyra-scripting/Cargo.toml +++ b/lyra-scripting/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [features] default = ["lua"] -lua = ["dep:mlua"] +lua = ["dep:elua"] [dependencies] lyra-scripting-derive = { path = "lyra-scripting-derive" } @@ -20,7 +20,8 @@ anyhow = "1.0.77" tracing = "0.1.37" # enabled with lua feature -mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? +#mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? +elua = { path = "./elua", optional = true } itertools = "0.12.0" diff --git a/lyra-scripting/elua b/lyra-scripting/elua new file mode 160000 index 0000000..8b88612 --- /dev/null +++ b/lyra-scripting/elua @@ -0,0 +1 @@ +Subproject commit 8b88612890f7d67472b56cd97711bb037198ebf9 diff --git a/lyra-scripting/scripts/lua/math/quat.lua b/lyra-scripting/scripts/lua/math/quat.lua index c6aca90..b268834 100644 --- a/lyra-scripting/scripts/lua/math/quat.lua +++ b/lyra-scripting/scripts/lua/math/quat.lua @@ -1,3 +1,8 @@ +---@class Quat +---@field x number +---@field y number +---@field z number +---@field w number Quat = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 } Quat.__index = Quat Quat.__name = "Quat" @@ -22,7 +27,7 @@ end Quat.IDENTITY = Quat:new(0, 0, 0, 1) -function Quat:copy() +function Quat:clone() return Quat:new(self.x, self.y, self.z, self.w) end diff --git a/lyra-scripting/scripts/lua/math/transform.lua b/lyra-scripting/scripts/lua/math/transform.lua index 76273e2..7f3eb14 100644 --- a/lyra-scripting/scripts/lua/math/transform.lua +++ b/lyra-scripting/scripts/lua/math/transform.lua @@ -1,6 +1,7 @@ ---require("math.quat") ---require("math.vec3") - +---@class Transform +---@field translation Vec3 +---@field rotation Quat +---@field Scale Vec3 Transform = { translation = Vec3.ZERO, rotation = Quat.IDENTITY, scale = Vec3.ONE } Transform.__index = Transform Transform.__name = "Transform" @@ -16,14 +17,14 @@ function Transform:new(translation, rotation, scale) return t end -function Transform:copy() - return Transform:new(self.translation:copy(), self.rotation:copy(), self.scale:copy()) +function Transform:clone() + return Transform:new(self.translation:clone(), self.rotation:clone(), self.scale:clone()) end --- Creates a new Transform with the translation at the vec3 --- @param pos Vec3 function Transform:from_vec3(pos) - local t = Transform:copy() -- copy of default transform + local t = Transform:clone() -- copy of default transform t.translation = pos return t end @@ -81,7 +82,7 @@ end --- @param alpha number --- @return Transform function Transform:lerp(rhs, alpha) - local res = self:copy() + local res = self:clone() res.translation = self.translation:lerp(rhs.translation, alpha) res.rotation = self.rotation:lerp(rhs.rotation, alpha) res.scale = self.scale:lerp(rhs.scale, alpha) diff --git a/lyra-scripting/scripts/lua/math/vec3.lua b/lyra-scripting/scripts/lua/math/vec3.lua index cf88af6..5b5ff44 100644 --- a/lyra-scripting/scripts/lua/math/vec3.lua +++ b/lyra-scripting/scripts/lua/math/vec3.lua @@ -1,3 +1,7 @@ +---@class Vec3 +---@field x number +---@field y number +---@field z number Vec3 = { x = 0.0, y = 0.0, z = 0.0 } Vec3.__index = Vec3 Vec3.__name = "Vec3" @@ -14,11 +18,13 @@ function Vec3:new(x, y, z) v.x = x v.y = y v.z = z - + return v end -function Vec3:copy() +---Creates a copy of self +---@return Vec3 +function Vec3:clone() return Vec3:new(self.x, self.y, self.z) end @@ -49,9 +55,10 @@ Vec3.ZERO = Vec3:new(0, 0, 0) Vec3.ONE = Vec3:new(1, 1, 1) --- Computes the absolute value of `self`. ----@return Vec3 function Vec3:abs() - return Vec3:new(math.abs(self.x), math.abs(self.y), math.abs(self.z)) + self.x = math.abs(self.x) + self.y = math.abs(self.y) + self.z = math.abs(self.z) end --- Computes the length of `self`. @@ -60,6 +67,16 @@ function Vec3:length() return math.sqrt(self:dot(self)) end +---Moves `self` by the provided coordinates +---@param x number +---@param y number +---@param z number +function Vec3:move_by(x, y, z) + self.x = self.x + x + self.y = self.y + y + self.z = self.z + z +end + --- Computes the dot product of `self` and `rhs`. ---@param rhs Vec3 ---@return number @@ -80,11 +97,12 @@ function Vec3:min(rhs) return Vec3:new(x, y, z) end ---- Returns `self` normalized to a length 1. ----@return unknown +--- Modifies `self` to be normalized to a length 1. function Vec3:normalize() local len_recip = 1.0 / self:length() - return self * len_recip + self.x = self.x * len_recip + self.y = self.y * len_recip + self.z = self.z * len_recip end --- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`. @@ -97,40 +115,48 @@ function Vec3:lerp(rhs, alpha) -- ensure alpha is [0, 1] local alpha = math.max(0, math.min(1, alpha)) - local res = self:copy() + local res = self:clone() res = res + ((rhs - res) * alpha) return res end function Vec3:__add(rhs) - return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) + if type(rhs) == "Vec3" then + return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) + else + return Vec3:new(self.x + rhs, self.y + rhs, self.z + rhs) + end end function Vec3:__sub(rhs) - return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) + if type(rhs) == "Vec3" then + return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) + else + return Vec3:new(self.x - rhs, self.y - rhs, self.z - rhs) + end end function Vec3:__mul(rhs) - if type(rhs) == "number" then - return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs) - else + if type(rhs) == "Vec3" then return Vec3:new(self.x * rhs.x, self.y * rhs.y, self.z * rhs.z) + else + return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs) end end function Vec3:__div(rhs) - if type(rhs) == "number" then - return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs) - else + if type(rhs) == "Vec3" then return Vec3:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z) + else + return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs) end end function Vec3:__idiv(rhs) - if type(rhs) == "number" then - return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs) - else + if type(rhs) == "Vec3" then return Vec3:new(self.x // rhs.x, self.y // rhs.y, self.z // rhs.z) + else + return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs) end end diff --git a/lyra-scripting/src/host.rs b/lyra-scripting/src/host.rs index 288a5f2..bcca5ba 100644 --- a/lyra-scripting/src/host.rs +++ b/lyra-scripting/src/host.rs @@ -8,15 +8,15 @@ use crate::ScriptWorldPtr; pub enum ScriptError { #[error("{0}")] #[cfg(feature = "lua")] - MluaError(mlua::Error), + MluaError(elua::Error), #[error("{0}")] Other(anyhow::Error), } #[cfg(feature = "lua")] -impl From for ScriptError { - fn from(value: mlua::Error) -> Self { +impl From for ScriptError { + fn from(value: elua::Error) -> Self { ScriptError::MluaError(value) } } diff --git a/lyra-scripting/src/lua/dynamic_iter.rs b/lyra-scripting/src/lua/dynamic_iter.rs index 3834f39..7b8f250 100644 --- a/lyra-scripting/src/lua/dynamic_iter.rs +++ b/lyra-scripting/src/lua/dynamic_iter.rs @@ -134,9 +134,9 @@ impl Iterator for DynamicViewIter { #[cfg(feature = "lua")] pub struct ReflectedItem<'a> { - pub proxy: &'a ReflectLuaProxy, + //pub proxy: &'a ReflectLuaProxy, pub comp_ptr: NonNull, - pub comp_ud: mlua::AnyUserData<'a>, + pub comp_val: elua::Value<'a>, } #[cfg(feature = "lua")] @@ -153,7 +153,11 @@ pub struct ReflectedIterator { impl ReflectedIterator { #[cfg(feature = "lua")] - pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option> { + pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option> { + use elua::AsLua; + + use super::ReflectedLuaTableProxy; + let n = self.dyn_view.next(); @@ -172,17 +176,20 @@ impl ReflectedIterator { let reg_type = reflected_components.get_type(id) .expect("Requested type was not found in TypeRegistry"); - let proxy = reg_type.get_data::() - .expect("Type does not have ReflectLuaProxy as a TypeData"); - - let userdata = (proxy.fn_as_uservalue)(lua, d.ptr).unwrap(); + let value = if let Some(proxy) = reg_type.get_data::() { + (proxy.fn_as_uservalue)(lua, d.ptr).unwrap() + .as_lua(lua).unwrap() + } else if let Some(proxy) = reg_type.get_data::() { + (proxy.fn_as_table)(lua, d.ptr.cast()).unwrap() + .as_lua(lua).unwrap() + } else { + panic!("Type does not have ReflectLuaProxy or ReflectedLuaTableProxy as a TypeData"); + }; dynamic_row.push(ReflectedItem { - proxy, comp_ptr: d.ptr, - comp_ud: userdata + comp_val: value }); - //dynamic_row.push(( (proxy, d.ptr), userdata)); } let row = ReflectedRow { diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index cc2a366..cf11dc6 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -2,6 +2,7 @@ pub mod dynamic_iter; pub use dynamic_iter::*; pub mod world; +use elua::FromLua; pub use world::*; pub mod script; @@ -19,19 +20,14 @@ pub use proxy::*; pub mod system; pub use system::*; -#[cfg(test)] -mod test; - use std::{any::TypeId, sync::Mutex}; use lyra_ecs::{ - DynamicBundle, World, + Component, ComponentInfo, DynamicBundle, World }; use lyra_reflect::{FromType, Reflect, TypeRegistry}; -use mlua::{AnyUserDataExt, Lua}; - -pub type LuaContext = Mutex; +pub type LuaContext = Mutex; pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; @@ -40,21 +36,28 @@ use crate::{ScriptBorrow, ScriptDynamicBundle}; /// A trait used for registering a Lua type with the world. 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) where - T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData; + T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata; - /// Registers a wrapped lua type. + /// Registers a type to Lua that is wrapped another type. + /// This would be used for something like `UserdataRef`. fn register_lua_wrapper<'a, W>(&mut self) where - W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData; + W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata; + + /// Registers a type to Lua that implements [`elua::TableProxy`] + fn register_lua_table_proxy<'a, T, W>(&mut self) + where + T: elua::TableProxy + 'static, + W: Component; } impl RegisterLuaType for World { fn register_lua_type<'a, T>(&mut self) where - T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData + T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata { let mut registry = self.get_resource_mut::(); @@ -67,44 +70,64 @@ impl RegisterLuaType for World { fn register_lua_wrapper<'a, W>(&mut self) where - W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData + W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata { let mut registry = self.get_resource_mut::(); let reg_type = registry.get_type_or_default(W::wrapped_type_id()); reg_type.add_data(>::from_type()); } + + fn register_lua_table_proxy<'a, T, C>(&mut self) + where + T: elua::TableProxy + 'static, + C: Component + { + let mut registry = self.get_resource_mut::(); + + let type_id = TypeId::of::(); + let reg_type = registry.get_type_or_default(TypeId::of::()); + reg_type.add_data(>::from_type()); + drop(registry); + + let mut lookup = self.get_resource_or_else::(LuaTableProxyLookup::default); + lookup.typeid_from_name.insert(T::table_name(), TypeId::of::()); + + let mut info = ComponentInfo::new::(); + lookup.comp_info_from_name.insert(T::table_name(), info); + } } -impl<'lua> mlua::FromLua<'lua> for ScriptBorrow { - fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { +impl<'lua> elua::FromLua<'lua> for ScriptBorrow { + fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), _ => unreachable!(), } } } -impl mlua::UserData for ScriptBorrow {} - -pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow { - ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) - .expect("Type does not implement '__internal_reflect' properly") +impl<'lua> elua::FromLuaVec<'lua> for ScriptBorrow { + fn from_lua_value_vec(state: &'lua elua::State, mut values: elua::ValueVec<'lua>) -> elua::Result { + if let Some(v) = values.pop_front() { + ScriptBorrow::from_lua(state, v) + } else { + Err(elua::Error::Nil) + } + } } -impl mlua::UserData for ScriptDynamicBundle { - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'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(); - let refl_data = refl_data.as_ref(); - reflect.bundle_insert(&mut this.0, refl_data); - - Ok(()) - }); +impl elua::Userdata for ScriptBorrow { + fn name() -> String { + "ScriptBorrow".to_string() } + + fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + Ok(()) + } +} + +pub fn reflect_user_data(ud: &elua::AnyUserdata) -> ScriptBorrow { + ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) + .expect("Type does not implement '__internal_reflect' properly") } \ No newline at end of file diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index 446688c..7b0a0be 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -13,7 +13,7 @@ impl ScriptApiProvider for LyraEcsApiProvider { fn expose_api(&mut self, data: &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::()?)?; globals.set("DynamicBundle", ctx.create_proxy::()?)?; diff --git a/lyra-scripting/src/lua/providers/math.rs b/lyra-scripting/src/lua/providers/math.rs index f95d745..c8118c9 100644 --- a/lyra-scripting/src/lua/providers/math.rs +++ b/lyra-scripting/src/lua/providers/math.rs @@ -1,8 +1,8 @@ +use elua::{TableProxy, Userdata}; use lyra_ecs::World; -use lyra_game::math; +use lyra_game::math::{self, Quat, Transform, Vec3}; use crate::ScriptData; use crate::lua::RegisterLuaType; -use crate::lua::wrappers::{LuaVec3, LuaTransform}; use crate::{ScriptApiProvider, lua::LuaContext}; @@ -13,26 +13,31 @@ impl ScriptApiProvider for LyraMathApiProvider { type ScriptContext = LuaContext; fn prepare_world(&mut self, world: &mut World) { - world.register_lua_wrapper::(); - world.register_lua_wrapper::(); + // TODO + /* world.register_lua_wrapper::(); + world.register_lua_wrapper::(); */ + world.register_lua_table_proxy::(); + world.register_lua_table_proxy::(); + world.register_lua_table_proxy::(); } - fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn expose_api(&mut self, _data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); - /* let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua"); - ctx.load(bytes.to_vec()).exec()?; + let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua"); + ctx.load("lyra/math/vec2.lua", bytes.as_slice())?.execute(())?; let bytes = include_bytes!("../../../scripts/lua/math/quat.lua"); - ctx.load(bytes.to_vec()).exec()?; + ctx.load("lyra/math/quat.lua", bytes.as_slice())?.execute(())?; let bytes = include_bytes!("../../../scripts/lua/math/transform.lua"); - ctx.load(bytes.to_vec()).exec()?; */ + ctx.load("lyra/math/transform.lua", bytes.as_slice())?.execute(())?; - let globals = ctx.globals(); - globals.set("Vec3", ctx.create_proxy::()?)?; - globals.set("Transform", ctx.create_proxy::()?)?; - //globals.set("Vec3", LuaVec3(math::Vec3::ZERO).into_lua(&ctx)?)?; + // TODO + //let globals = ctx.globals()?; + //globals.set("Vec3", elua::Proxy::::from(LuaVec3(Vec3::ZERO)))?; + //globals.set("Vec3", ctx.create_proxy::()?)?; + //globals.set("Transform", ctx.create_proxy::()?)?; Ok(()) } @@ -46,6 +51,94 @@ impl ScriptApiProvider for LyraMathApiProvider { } } +#[derive(Clone, Copy)] +struct LuaVec3(Vec3); + +impl TableProxy for LuaVec3 { + fn from_table<'a>(_: &'a elua::State, table: elua::Table<'a>) -> elua::Result { + let x: f32 = table.get("x")?; + let y: f32 = table.get("y")?; + let z: f32 = table.get("z")?; + + Ok(LuaVec3(Vec3 { + x, + y, + z, + })) + } + + fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { + let globals = state.globals()?; + let vec3: elua::Table = globals.get("Vec3")?; + let new_fn: elua::Function = vec3.get("new")?; + new_fn.exec((vec3, self.0.x, self.0.y, self.0.z)) + } + + fn table_name() -> String { + "Vec3".to_string() + } +} + +#[derive(Clone, Copy)] +struct LuaQuat(Quat); + +impl TableProxy for LuaQuat { + fn from_table<'a>(_: &'a elua::State, table: elua::Table<'a>) -> elua::Result { + let x: f32 = table.get("x")?; + let y: f32 = table.get("y")?; + let z: f32 = table.get("z")?; + let w: f32 = table.get("w")?; + + Ok(LuaQuat(Quat::from_xyzw(x, y, z, w))) + } + + fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { + let globals = state.globals()?; + let quat: elua::Table = globals.get("Quat")?; + let new_fn: elua::Function = quat.get("new")?; + new_fn.exec((quat, self.0.x, self.0.y, self.0.z, self.0.w)) + } + + fn table_name() -> String { + "Quat".to_string() + } +} + +#[derive(Clone, Copy)] +struct LuaTransform(Transform); + +impl TableProxy for LuaTransform { + fn from_table<'a>(lua: &'a elua::State, table: elua::Table<'a>) -> elua::Result { + let translation: elua::Table = table.get("translation")?; + let rotation: elua::Table = table.get("rotation")?; + let scale: elua::Table = table.get("scale")?; + + let translation = LuaVec3::from_table(lua, translation)?; + let rotation = LuaQuat::from_table(lua, rotation)?; + let scale = LuaVec3::from_table(lua, scale)?; + + Ok(LuaTransform(Transform::new(translation.0, rotation.0, scale.0))) + } + + fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { + let globals = state.globals()?; + let transform: elua::Table = globals.get("Transform")?; + let new_fn: elua::Function = transform.get("new")?; + + let translation = LuaVec3(self.0.translation).as_table(state)?; + let rotation = LuaQuat(self.0.rotation).as_table(state)?; + let scale = LuaVec3(self.0.scale).as_table(state)?; + + new_fn.exec((transform, translation, rotation, scale)) + } + + fn table_name() -> String { + "Transform".to_string() + } + + +} + /* #[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)] pub struct LuaVec3(#[reflect(skip)] math::Vec3); diff --git a/lyra-scripting/src/lua/providers/util.rs b/lyra-scripting/src/lua/providers/util.rs index e1277ee..905f123 100644 --- a/lyra-scripting/src/lua/providers/util.rs +++ b/lyra-scripting/src/lua/providers/util.rs @@ -17,47 +17,46 @@ use crate::{ScriptApiProvider, ScriptData}; pub struct UtilityApiProvider; impl ScriptApiProvider for UtilityApiProvider { - type ScriptContext = Mutex; + type ScriptContext = Mutex; fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); - fn printf(lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic)) -> mlua::Result<()> { + //fn printf(lua: &elua::State, (mut text, formats): (String, elua::Variadic)) -> elua::Result<()> { + let printf = |lua: &elua::State, (mut text, formats): (String, elua::Variadic)| { 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 { - mlua::Value::Nil => "nil".to_string(), - mlua::Value::Boolean(b) => b.to_string(), - mlua::Value::LightUserData(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of LightUserData"))); + 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")); }, - mlua::Value::Integer(i) => i.to_string(), - mlua::Value::Number(n) => n.to_string(), - mlua::Value::String(s) => s.to_str().unwrap().to_string(), - mlua::Value::Table(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Table"))); + elua::Value::Function(_) => { + return Err(elua::Error::runtime("unable to get string representation of Function")); }, - mlua::Value::Function(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Function"))); + elua::Value::Thread(_) => { + return Err(elua::Error::runtime("unable to get string representation of Thread")); }, - mlua::Value::Thread(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Thread"))); - }, - mlua::Value::UserData(ud) => { - let metatable = ud.get_metatable()?; - if let Ok(tos) = metatable.get::(mlua::MetaMethod::ToString) { - tos.call::<_, String>((ud.clone(),))? + elua::Value::Userdata(ud) => { + if let Ok(tos) = ud.get::<_, elua::Function>(elua::MetaMethod::ToString) { + tos.exec::<_, String>(())? } else { - return Err(mlua::Error::RuntimeError(format!("UserData does not implement MetaMethod '__tostring'"))); + return Err(elua::Error::runtime("UserData does not implement MetaMethod '__tostring'")); } }, - mlua::Value::Error(e) => e.to_string(), + elua::Value::None => "None".to_string(), + elua::Value::Multi(v) => { + return Err(elua::Error::runtime("unable to get string representation of ValueVec")); + }, }, None => { - let got_args = arg_num; + 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("{}") { @@ -65,13 +64,15 @@ impl ScriptApiProvider for UtilityApiProvider { arg_num += 1; } - return Err(mlua::Error::BadArgument { - to: Some("printf".to_string()), - pos: 2, - name: Some("...".to_string()), - cause: Arc::new(mlua::Error::RuntimeError(format!("not enough 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))) + got {}.", arg_num, got_args + ))) }) }, }; @@ -84,29 +85,31 @@ impl ScriptApiProvider for UtilityApiProvider { } if arg_num < formats.len() { - return Err(mlua::Error::BadArgument { - to: Some("printf".to_string()), - pos: 2, - name: Some("...".to_string()), - cause: Arc::new(mlua::Error::RuntimeError(format!("got more args \ - than format areas in the string. Expected {}, got {}.", formats.len(), arg_num))) + return Err(elua::Error::BadArgument { + func: Some("printf".to_string()), + arg_index: 2, + arg_name: Some("fmt...".to_string()), + error: Arc::new(elua::Error::Runtime(format!( + "got more args \ + than format areas in the string. Expected {}, got {}.", formats.len(), arg_num + ))) }) } formatted = format!("{}{}", formatted, text); - lua.globals() - .get::<_, mlua::Function>("print")? - .call::<_, ()>(formatted)?; + lua.globals()? + .get::<_, elua::Function>("print")? + .exec::<_, ()>(formatted)?; Ok(()) - } + }; - let script_name_reg = ctx.create_registry_value(data.name.clone())?; + let script_name_reg = ctx.registry_insert(data.name.clone())?; let printf_func = ctx.create_function(printf)?; let print_func = ctx.create_function(move |lua, text: String| { - let name = lua.registry_value::(&script_name_reg)?; + let name = lua.registry_get::(script_name_reg)?; let _span = debug_span!("lua", script = &name).entered(); debug!(target: "lyra_scripting::lua", "{}", text); @@ -114,7 +117,7 @@ impl ScriptApiProvider for UtilityApiProvider { Ok(()) })?; - let globals = ctx.globals(); + let globals = ctx.globals()?; globals.set("printf", printf_func)?; globals.set("print", print_func)?; diff --git a/lyra-scripting/src/lua/proxy.rs b/lyra-scripting/src/lua/proxy.rs index e3a79de..5980814 100644 --- a/lyra-scripting/src/lua/proxy.rs +++ b/lyra-scripting/src/lua/proxy.rs @@ -1,46 +1,51 @@ -use std::{any::TypeId, ptr::NonNull}; +use std::{any::TypeId, collections::HashMap, ptr::NonNull}; +use elua::{FromLua, TableProxy}; +use lyra_ecs::{ComponentInfo, DynamicBundle}; use lyra_reflect::{Reflect, FromType}; -use crate::ScriptDynamicBundle; +use crate::{ScriptBorrow, ScriptDynamicBundle}; + +use super::FN_NAME_INTERNAL_REFLECT; pub trait LuaWrapper { /// The type id of the wrapped type. fn wrapped_type_id() -> TypeId; } +/// 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 mlua::Lua, + lua: &'lua elua::State, this: &dyn Reflect, - ) -> mlua::Result>; + ) -> elua::Result>; fn apply( - lua: &mlua::Lua, + lua: &elua::State, this: &mut dyn Reflect, - apply: &mlua::AnyUserData, - ) -> mlua::Result<()>; + apply: &elua::AnyUserdata, + ) -> elua::Result<()>; } impl<'a, T> LuaProxy for T where - T: Reflect + Clone + mlua::FromLua<'a> + mlua::UserData + T: Reflect + Clone + elua::FromLua<'a> + elua::Userdata { fn as_lua_value<'lua>( - lua: &'lua mlua::Lua, + lua: &'lua elua::State, this: &dyn Reflect, - ) -> mlua::Result> { + ) -> elua::Result> { let this = this.as_any().downcast_ref::().unwrap(); lua.create_userdata(this.clone()) } fn apply( - _lua: &mlua::Lua, + _: &elua::State, this: &mut dyn Reflect, - apply: &mlua::AnyUserData, - ) -> mlua::Result<()> { + apply: &elua::AnyUserdata, + ) -> elua::Result<()> { let this = this.as_any_mut().downcast_mut::().unwrap(); - let apply = apply.borrow::()?; + let apply = apply.as_ref::()?; *this = apply.clone(); @@ -48,24 +53,66 @@ where } } +/// A struct that is used for retrieving rust type ids of types that implement `TableProxy`. +#[derive(Default)] +pub struct LuaTableProxyLookup { + pub(crate) typeid_from_name: HashMap, + pub(crate) comp_info_from_name: HashMap, +} + +/// A struct used for converting types that implement `TableProxy` to and from Lua. +#[derive(Clone)] +pub struct ReflectedLuaTableProxy { + pub table_name: String, + pub fn_as_table: + for<'a> fn(lua: &'a elua::State, this_ptr: NonNull<()>) -> elua::Result>, + pub fn_apply: for<'a> fn( + lua: &'a elua::State, + this_ptr: NonNull<()>, + table: &'a elua::Table<'a>, + ) -> elua::Result<()>, +} + +impl<'a, T> FromType for ReflectedLuaTableProxy +where + T: TableProxy +{ + fn from_type() -> Self { + Self { + table_name: T::table_name(), + fn_as_table: |lua, this| -> elua::Result { + let this = unsafe { this.cast::().as_ref() }; + this.as_table(lua) + }, + fn_apply: |lua, ptr, table| { + let this = unsafe { ptr.cast::().as_mut() }; + let tbl = T::from_table(lua, table.clone())?; + *this = tbl; + Ok(()) + }, + } + } +} + +/// A struct used for converting types that implement `LuaProxy` to and from Lua. #[derive(Clone)] pub struct ReflectLuaProxy { pub fn_as_uservalue: - for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull) -> mlua::Result>, + for<'a> fn(lua: &'a elua::State, this_ptr: NonNull) -> elua::Result>, pub fn_apply: for<'a> fn( - lua: &'a mlua::Lua, + lua: &'a elua::State, this_ptr: NonNull, - apply: &'a mlua::AnyUserData<'a>, - ) -> mlua::Result<()>, + apply: &'a elua::AnyUserdata<'a>, + ) -> elua::Result<()>, } impl<'a, T> FromType for ReflectLuaProxy where - T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData + T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata { fn from_type() -> Self { Self { - fn_as_uservalue: |lua, this| -> mlua::Result { + fn_as_uservalue: |lua, this| -> elua::Result { let this = unsafe { this.cast::().as_ref() }; ::as_lua_value(lua, this) }, @@ -77,16 +124,45 @@ where } } -impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle { - fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { - match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), - mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { - from: "Nil", - to: "DynamicBundle", - message: Some("Value was nil".to_string()), - }), - _ => panic!(), +impl<'lua> elua::FromLua<'lua> for ScriptDynamicBundle { + fn from_lua(_: &'lua elua::State, val: elua::Value<'lua>) -> elua::Result { + match val { + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), + elua::Value::Nil => Err(elua::Error::Nil), + _ => unreachable!(), } } +} + +impl<'lua> elua::FromLuaVec<'lua> for ScriptDynamicBundle { + fn from_lua_value_vec(state: &'lua elua::State, mut values: elua::ValueVec<'lua>) -> elua::Result { + 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>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + 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(); + let refl_data = refl_data.as_ref(); + reflect.bundle_insert(&mut this.0, refl_data); + + Ok(()) + }); + + Ok(()) + } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index c7a00ab..b0d1cc6 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -1,6 +1,6 @@ use std::sync::Mutex; -use mlua::IntoLua; +use elua::{AsLua, StdLibraries, StdLibrary}; use tracing::{debug, trace}; use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; @@ -8,40 +8,37 @@ use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; #[derive(Default)] pub struct LuaHost; -fn try_call_lua_function(lua: &mlua::Lua, fn_name: &str) -> Result<(), ScriptError> { - let globals = lua.globals(); - - match globals.get::<_, mlua::Function>(fn_name) { - Ok(init_fn) => { - init_fn.call(()) - .map_err(ScriptError::MluaError)?; - }, - Err(mlua::Error::FromLuaConversionError { from: "nil", to: "function", message: None }) => { - trace!("Function '{}' was not found, ignoring...", fn_name) - // ignore - }, - Err(e) => { - return Err(ScriptError::MluaError(e)); - }, +fn try_call_lua_function(lua: &elua::State, 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)?; } Ok(()) } impl ScriptHost for LuaHost { - type ScriptContext = Mutex; + type ScriptContext = Mutex; fn load_script(&mut self, script: &[u8], script_data: &crate::ScriptData, providers: &mut crate::ScriptApiProviders) -> Result { - let mut ctx = Mutex::new(mlua::Lua::new()); + let mut ctx = Mutex::new({ + let s = elua::State::new(); + s.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]); + s + }); + + //Mutex::new(elua::State::new_with_libraries(StdLibraries::all())); for provider in providers.apis.iter_mut() { provider.expose_api(script_data, &mut ctx)?; } let lua = ctx.lock().unwrap(); - lua.load(script) - .set_name(&script_data.name) - .exec() + lua.load(&script_data.name, script)? + .execute(()) .map_err(|e| ScriptError::MluaError(e))?; drop(lua); @@ -71,9 +68,9 @@ impl ScriptHost for LuaHost { let ctx = ctx.lock().expect("Failure to get Lua ScriptContext"); - let globals = ctx.globals(); - globals.set("world", world.into_lua(&ctx)?)?; - globals.set("entity", ScriptEntity(script_data.entity).into_lua(&ctx)?)?; + let globals = ctx.globals()?; + globals.set("world", world.as_lua(&ctx)?)?; + globals.set("entity", ScriptEntity(script_data.entity).as_lua(&ctx)?)?; try_call_lua_function(&ctx, function_name)?; diff --git a/lyra-scripting/src/lua/test.rs b/lyra-scripting/src/lua/test.rs deleted file mode 100644 index b30880d..0000000 --- a/lyra-scripting/src/lua/test.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::{sync::{Mutex, Arc}, ptr::NonNull}; - -use lyra_ecs::{World, system::{GraphExecutor, IntoSystem}}; -use lyra_resource::ResourceManager; -use mlua::{IntoLua, AnyUserDataExt}; -use tracing_subscriber::{layer::SubscriberExt, fmt, util::SubscriberInitExt}; - -use crate::{ScriptData, ScriptApiProvider, ScriptApiProviders, ScriptError, ScriptList, Script, ScriptContexts}; - -use super::{LuaHost, LuaLoader, LuaScript, lua_script_update_stage_system, lua_scripts_create_contexts, LuaContext}; - -fn enable_tracing() { - tracing_subscriber::registry() - .with(fmt::layer().with_writer(std::io::stdout)) - .init(); -} - -#[derive(Default)] -struct PrintfProvider; - -impl ScriptApiProvider for PrintfProvider { - type ScriptContext = Mutex; - - fn expose_api(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { - let ctx = ctx.lock().unwrap(); - - fn printf(lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic)) -> mlua::Result<()> { - 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 { - mlua::Value::Nil => "nil".to_string(), - mlua::Value::Boolean(b) => b.to_string(), - mlua::Value::LightUserData(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of LightUserData"))); - }, - mlua::Value::Integer(i) => i.to_string(), - mlua::Value::Number(n) => n.to_string(), - mlua::Value::String(s) => s.to_str().unwrap().to_string(), - mlua::Value::Table(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Table"))); - }, - mlua::Value::Function(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Function"))); - }, - mlua::Value::Thread(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Thread"))); - }, - mlua::Value::UserData(ud) => { - if let Ok(tos) = ud.get::<_, mlua::Function>(mlua::MetaMethod::ToString.to_string()) { - tos.call::<_, String>(())? - } else { - return Err(mlua::Error::RuntimeError(format!("UserData does not implement MetaMethod '__tostring'"))); - } - }, - mlua::Value::Error(e) => e.to_string(), - }, - 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".to_string()), - pos: 2, - name: Some("...".to_string()), - cause: Arc::new(mlua::Error::RuntimeError(format!("not enough args \ - given for the amount of format areas in the string. Expected {}, \ - got {}.", arg_num, got_args))) - }) - }, - }; - - formatted = format!("{}{}{}", formatted, &text[0..start], val_str); - - text = text[start + 2..].to_string(); - - arg_num += 1; - } - - if arg_num < formats.len() { - return Err(mlua::Error::BadArgument { - to: Some("printf".to_string()), - pos: 2, - name: Some("...".to_string()), - cause: Arc::new(mlua::Error::RuntimeError(format!("got more args \ - than format areas in the string. Expected {}, got {}.", formats.len(), arg_num))) - }) - } - - formatted = format!("{}{}", formatted, text); - - lua.globals() - .get::<_, mlua::Function>("print") - .unwrap() - .call::<_, ()>(formatted) - .unwrap(); - - Ok(()) - } - - let printf_func = ctx.create_function(printf).unwrap(); - - let globals = ctx.globals(); - globals.set("printf", printf_func).unwrap(); - - Ok(()) - } - - 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> { - let ctx = ctx.lock().unwrap(); - let globals = ctx.globals(); - - let world_lua = world.into_lua(&ctx) - .map_err(ScriptError::MluaError)?; - globals.set("world", world_lua) - .map_err(ScriptError::MluaError)?; - - Ok(()) - } -} - -/// Tests a simple lua script that just prints some test -#[test] -pub fn lua_print() { - enable_tracing(); - - let mut world = World::new(); - - let test_provider = PrintfProvider::default(); - let mut providers = ScriptApiProviders::::default(); - providers.add_provider(test_provider); - - let host = LuaHost::default(); - - world.add_resource(host); - world.add_resource(providers); - world.add_resource(ScriptContexts::::default()); - - let mut res_loader = ResourceManager::new(); - res_loader.register_loader::(); - - let script = - r#" - print("Hello World") - - function update() - print("updated") - printf("I love to eat formatted {}!", "food") - --printf("World is {}", world) - end - "#; - let script = script.as_bytes(); - - let script = res_loader.load_bytes::("test_script.lua", - "text/lua", script.to_vec(), 0, script.len()).unwrap(); - let script = Script::new("text_script.lua", script); - - let scripts = ScriptList::new(vec![script]); - - world.spawn((scripts,)); - - let mut exec = GraphExecutor::new(); - exec.insert_system("lua_create_contexts", lua_scripts_create_contexts.into_system(), &[]); - exec.insert_system("lua_update_scripts", lua_script_update_stage_system.into_system(), &["lua_create_contexts"]); - exec.execute(NonNull::from(&world), true).unwrap(); -} \ No newline at end of file diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 2efd36a..df9f4ee 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,28 +1,33 @@ -use std::{sync::{Arc, RwLock}, ptr::NonNull, any::Any, ops::Deref}; +use std::{any::Any, ptr::NonNull, sync::Arc}; +use elua::AsLua; use lyra_ecs::query::dynamic::QueryDynamicType; use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType}; -use mlua::{AnyUserDataExt, IntoLua, IntoLuaMulti}; - use crate::{ScriptWorldPtr, ScriptEntity, ScriptDynamicBundle, ScriptBorrow}; -use super::{ReflectedIterator, DynamicViewIter, FN_NAME_INTERNAL_REFLECT_TYPE, reflect_user_data, ReflectLuaProxy, FN_NAME_INTERNAL_REFLECT}; +use super::{reflect_user_data, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, ReflectedLuaTableProxy, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}; -impl<'lua> mlua::FromLua<'lua> for ScriptEntity { - fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { +impl<'lua> elua::FromLua<'lua> for ScriptEntity { + fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), - mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { from: "Nil", to: "ScriptEntity", message: Some("Value was nil".to_string()) }), + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), + elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptEntity", "Nil")), _ => panic!(), } } } -impl mlua::UserData for ScriptEntity { - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { +impl elua::Userdata for ScriptEntity { + fn name() -> String { + "Entity".to_string() + } + + fn build<'a>(state: &elua::State, builder: &mut elua::userdata::UserdataBuilder<'a, Self>) -> elua::Result<()> { + builder.meta_method(elua::MetaMethod::ToString, |_, this, ()| { Ok(format!("{:?}", this.0)) - }) + }); + + Ok(()) } } @@ -32,153 +37,182 @@ pub enum WorldError { LuaInvalidUsage(String), } -impl mlua::UserData for ScriptWorldPtr { - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_method_mut("spawn", |_, this, (bundle,): (ScriptDynamicBundle,)| { - let world = unsafe { this.inner.as_mut() }; - - Ok(ScriptEntity(world.spawn(bundle.0))) - }); +impl<'a> elua::FromLua<'a> for ScriptWorldPtr { + fn from_lua(lua: &'a elua::State, val: elua::Value<'a>) -> elua::Result { + match val { + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), + elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptWorldPtr", "Nil")), + _ => panic!(), + } + } +} - methods.add_method("view_iter", |lua, this, queries: mlua::Variadic| { - let world = unsafe { this.inner.as_ref() }; - let mut view = world.dynamic_view(); +impl elua::Userdata for ScriptWorldPtr { + fn name() -> String { + "World".to_string() + } - for comp in queries.into_iter() { - let script_brw = comp.call_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) - .expect("Type does not implement '__internal_reflect_type' properly"); - let refl_comp = script_brw.reflect_branch.as_component_unchecked(); + fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + builder + .method("test", |_, this, ()| { + println!("hgello world"); + Ok(()) + }) + .method_mut("spawn", |_, this, bundle: ScriptDynamicBundle| { + let world = this.as_mut(); - let dyn_type = QueryDynamicType::from_info(refl_comp.info); - view.push(dyn_type); - } - - let iter = view.into_iter(); - let mut reflected_iter = ReflectedIterator { - world: this.clone(), - dyn_view: DynamicViewIter::from(iter), - reflected_components: None, - }; - - let f = lua.create_function_mut(move |lua, ()| { - if let Some(row) = reflected_iter.next_lua(lua) { - let row = row.row.into_iter().map(|i| i.comp_ud.into_lua(lua)) - .collect::>>()?; - Ok(mlua::MultiValue::from_vec(row)) - } else { - Ok(mlua::Value::Nil.into_lua_multi(lua)?) + Ok(ScriptEntity(world.spawn(bundle.0))) + }) + .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()))) + }); } - })?; + + let world = unsafe { this.inner.as_ref() }; + let mut view = world.dynamic_view(); - Ok(f) - }); + for (idx, comp) in queries.into_iter().enumerate() { + match comp { + elua::Value::Table(t) => { + let name: String = t.get("__name")?; - methods.add_method_mut("view", |lua, this, (system, queries): (mlua::Function, mlua::Variadic)| { - if queries.is_empty() { - return Err(mlua::Error::BadArgument { to: Some("world:view".to_string()), pos: 2, name: Some("...".to_string()), cause: - Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage("no component types provided".to_string()))) - }); - } - - let world = unsafe { this.inner.as_ref() }; - let mut view = world.dynamic_view(); + let lookup = world.get_resource::(); + let info = lookup.comp_info_from_name.get(&name) + .ok_or_else(|| + elua::Error::BadArgument { + func: Some("World:view".to_string()), + arg_index: 2 + idx as i32, + arg_name: Some("query...".to_string()), + error: Arc::new( + elua::Error::Runtime(format!("the 'Table' with name {} is unknown to the engine!", 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(); - for comp in queries.into_iter() { - let reflect = comp.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 dyn_type = QueryDynamicType::from_info(refl_comp.info); - view.push(dyn_type); - } - - let iter = view.into_iter(); - let mut reflected_iter = ReflectedIterator { - world: this.clone(), - dyn_view: DynamicViewIter::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.proxy, r.comp_ud, r.comp_ptr)) - .collect::>(); - let (reflects, values, ptrs): - (Vec<&ReflectLuaProxy>, Vec, Vec>) - = itertools::multiunzip(r); - let value_row: Vec<_> = values.into_iter().map(|ud| ud.into_lua(lua)).collect::>>()?; - let mult_val = mlua::MultiValue::from_vec(value_row); - let res: mlua::MultiValue = system.call(mult_val)?; - - // if values were returned, find the type in the type registry, and apply the new values - if res.len() <= reflects.len() { - // we only want to tick one time per system - if !has_ticked { - current = world.tick(); - has_ticked = true; + let dyn_type = QueryDynamicType::from_info(refl_comp.info); + view.push(dyn_type); + }, + _ => todo!() } + } - for (i, comp) in res.into_iter().enumerate() { - let ptr = ptrs[i]; + let iter = view.into_iter(); + let mut reflected_iter = ReflectedIterator { + world: this.clone(), + dyn_view: DynamicViewIter::from(iter), + reflected_components: None, + }; - match comp.as_userdata() { - Some(ud) => { - let lua_comp = reflect_user_data(ud); - let refl_comp = lua_comp.reflect_branch.as_component_unchecked(); - let lua_typeid = refl_comp.info.type_id.as_rust(); + let mut current = world.current_tick(); + let mut has_ticked = false; - // update the component tick - let world = unsafe { this.inner.as_mut() }; - let arch = world.entity_archetype_mut(row.entity).unwrap(); - let idx = arch.entities().get(&row.entity).unwrap().clone(); - let c = arch.get_column_mut(refl_comp.type_id.into()).unwrap(); - c.entity_ticks[idx.0 as usize] = current; + while let Some(row) = reflected_iter.next_lua(lua) { + let r = row.row.into_iter() + .map(|r| (r.comp_val, r.comp_ptr)) + .collect::>(); + let (values, ptrs) = itertools::multiunzip::<(Vec, Vec>), _>(r); + let mult_val = elua::ValueVec::from(values); + let res: elua::ValueVec = system.exec(mult_val)?; - // apply the new component data - let reg = this.as_ref().get_resource::(); - let reg_type = reg.get_type(lua_typeid).unwrap(); - - let proxy = reg_type.get_data::().unwrap(); - (proxy.fn_apply)(lua, ptr, ud)?; - } - None => { - panic!("A userdata value was not returned!"); + // 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 (i, comp) in res.into_iter().enumerate() { + let ptr = ptrs[i]; + + match comp { + elua::Value::Userdata(ud) => { + let lua_comp = reflect_user_data(&ud); + let refl_comp = lua_comp.reflect_branch.as_component_unchecked(); + let lua_typeid = refl_comp.info.type_id.as_rust(); + + // update the component tick + let world = unsafe { this.inner.as_mut() }; + let arch = world.entity_archetype_mut(row.entity).unwrap(); + let idx = arch.entities().get(&row.entity).unwrap().clone(); + let c = arch.get_column_mut(refl_comp.type_id.into()).unwrap(); + c.entity_ticks[idx.0 as usize] = current; + + // apply the new component data + let reg = this.as_ref().get_resource::(); + let reg_type = reg.get_type(lua_typeid).unwrap(); + + let proxy = reg_type.get_data::() + .expect("Type does not have ReflectLuaProxy as a TypeData"); + (proxy.fn_apply)(lua, ptr, &ud)?; + }, + elua::Value::Table(tbl) => { + let name: String = tbl.get("__name")?; + + let lookup = world.get_resource::(); + let tyid = lookup.typeid_from_name.get(&name).unwrap(); + + // update the component tick + let world = unsafe { this.inner.as_mut() }; + let arch = world.entity_archetype_mut(row.entity).unwrap(); + let idx = arch.entities().get(&row.entity).unwrap().clone(); + let c = arch.get_column_mut(tyid.clone().into()).unwrap(); + c.entity_ticks[idx.0 as usize] = current; + + // apply the new component data + let reg = this.as_ref().get_resource::(); + let reg_type = reg.get_type(*tyid).unwrap(); + + let proxy = reg_type.get_data::() + .expect("Type does not have ReflectLuaProxy as a TypeData"); + (proxy.fn_apply)(lua, ptr.cast(), &tbl)?; + }, + _ => { + panic!("A userdata or table value was not returned!"); + } } } + } 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)); } - } else { - let msg = format!("Too many arguments were returned from the World view! - At most, the expected number of results is {}.", reflects.len()); - return Err(mlua::Error::external(WorldError::LuaInvalidUsage(msg))); } - } - Ok(()) - }); + Ok(()) + }) + .method_mut("resource", |lua, this, (ty,): (elua::AnyUserdata,)| { + 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()) { + //.expect("Failed to find resource pointer in world!"); + let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) + .unwrap(); + let proxy = reg_type.get_data::() + .expect("Type does not have ReflectLuaProxy as a TypeData"); - methods.add_method_mut("resource", |lua, this, (ty,): (mlua::AnyUserData,)| { - let reflect = ty - .call_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()) { - //.expect("Failed to find resource pointer in world!"); - let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) - .unwrap(); - let proxy = reg_type.get_data::() - .expect("Type does not have ReflectLuaProxy as a TypeData"); + (proxy.fn_as_uservalue)(lua, res_ptr) + .and_then(|ud| ud.as_lua(lua)) + } else { + // if the resource is not found in the world, return nil + Ok(elua::Value::Nil) + } + }); - (proxy.fn_as_uservalue)(lua, res_ptr) - .and_then(|ud| ud.into_lua(lua)) - } else { - // if the resource is not found in the world, return nil - Ok(mlua::Value::Nil) - } - }); + Ok(()) } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/wrappers/delta_time.rs b/lyra-scripting/src/lua/wrappers/delta_time.rs index a4fd4f6..c02ea4a 100644 --- a/lyra-scripting/src/lua/wrappers/delta_time.rs +++ b/lyra-scripting/src/lua/wrappers/delta_time.rs @@ -1,4 +1,4 @@ -use std::{ops::Deref, any::TypeId}; +use std::any::TypeId; use lyra_game::DeltaTime; use crate::{lyra_engine, lua::{FN_NAME_INTERNAL_REFLECT_TYPE, LuaWrapper}, ScriptBorrow}; @@ -20,28 +20,33 @@ impl std::ops::DerefMut for LuaDeltaTime { } } -impl<'lua> mlua::FromLua<'lua> for LuaDeltaTime { - fn from_lua(value: mlua::prelude::LuaValue<'lua>, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult { +impl<'lua> elua::FromLua<'lua> for LuaDeltaTime { + fn from_lua(lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), _ => unreachable!(), } } } -impl mlua::UserData for LuaDeltaTime { - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_method("get", |_, this, ()| { - Ok(*this.0.deref()) - }); +impl elua::Userdata for LuaDeltaTime { + fn name() -> String { + "DeltaTime".to_string() + } - methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { - Ok(format!("{}", this.0.deref())) - }); + fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + builder + .method("get", |_, this, ()| { + Ok(*this.0) + }) + .meta_method(elua::MetaMethod::ToString, |_, this, ()| { + Ok(format!("{}", *this.0)) + }) + .function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { + Ok(ScriptBorrow::from_resource::(None)) + }); - methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { - Ok(ScriptBorrow::from_resource::(None)) - }); + Ok(()) } } diff --git a/lyra-scripting/src/lua/wrappers/math.rs b/lyra-scripting/src/lua/wrappers/math.rs index 58d8f4a..05a38e7 100644 --- a/lyra-scripting/src/lua/wrappers/math.rs +++ b/lyra-scripting/src/lua/wrappers/math.rs @@ -7,7 +7,7 @@ use lyra_scripting_derive::wrap_math_vec_copy; use crate as lyra_scripting; // f32 types -wrap_math_vec_copy!( +/* wrap_math_vec_copy!( math::Vec2, derives(PartialEq), fields(x, y), @@ -32,7 +32,11 @@ wrap_math_vec_copy!( Mod(LuaVec3, f32), Eq, Unm, ToString ) -); +); */ + + +// ================================================= + /* wrap_math_vec_copy!( math::Vec3A, @@ -379,7 +383,20 @@ wrap_math_vec_copy!( ) ); */ -wrap_math_vec_copy!( + + + + + +// ============================================================ + + + + + + + +/* wrap_math_vec_copy!( math::Quat, derives(PartialEq), no_new, @@ -559,3 +576,4 @@ wrap_math_vec_copy!( }); } ); + */ \ No newline at end of file