Create an early scripting engine #2

Merged
SeanOMik merged 42 commits from feature/early-scripting into main 2024-03-03 03:28:57 +00:00
19 changed files with 677 additions and 533 deletions
Showing only changes of commit 4247c4f5c1 - Show all commits

View File

@ -12,18 +12,51 @@ function on_pre_update()
print("Lua's pre-update function was called") print("Lua's pre-update function was called")
end ]] 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() function on_update()
--print("Lua's update function was called") --print("Lua's update function was called")
local dt = world:resource(DeltaTime) --local v = Vec3:new(50, 10, 20)
--print("DeltaTime was " .. tostring(dt) .. "s") --print("Vec3 = " .. tostring(v))
world:view(function (t) world:view(
--print("Found entity at a really cool place: " .. tostring(t)) ---@param t Transform
t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) function (t)
print("Found entity at a really cool place: " .. tostring(t))
t.translation:move_by(0, 0.001, 0)
return t return t
end, Transform) 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 end
--[[ function on_post_update() --[[ function on_post_update()

View File

@ -84,16 +84,16 @@ async fn main() {
let mut resman = world.get_resource_mut::<ResourceManager>(); let mut resman = world.get_resource_mut::<ResourceManager>();
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap(); //let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap(); //let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
//let cube_model = resman.request::<Model>("assets/cube-texture-bin.glb").unwrap(); //let cube_model = resman.request::<Model>("assets/cube-texture-bin.glb").unwrap();
let cube_model = resman.request::<Model>("assets/texture-sep/texture-sep.gltf").unwrap(); let cube_model = resman.request::<Model>("assets/texture-sep/texture-sep.gltf").unwrap();
let crate_model = resman.request::<Model>("assets/crate/crate.gltf").unwrap(); let crate_model = resman.request::<Model>("assets/crate/crate.gltf").unwrap();
drop(resman); drop(resman);
world.spawn(( /* world.spawn((
ModelComponent(antique_camera_model), ModelComponent(antique_camera_model),
Transform::from_xyz(0.0, -5.0, -10.0), Transform::from_xyz(0.0, -5.0, -10.0),
)); )); */
{ {
let cube_tran = Transform::from_xyz(-3.5, 0.0, -8.0); let cube_tran = Transform::from_xyz(-3.5, 0.0, -8.0);

View File

@ -342,7 +342,7 @@ impl Game {
.with(fmt::layer().with_writer(stdout_layer)) .with(fmt::layer().with_writer(stdout_layer))
.with(filter::Targets::new() .with(filter::Targets::new()
// done by prefix, so it includes all lyra subpackages // 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_target("wgpu", Level::WARN)
.with_default(Level::INFO)) .with_default(Level::INFO))
.init(); .init();

@ -1 +1 @@
Subproject commit 5694d395abbbce340eda195c99104db3e9c9435a Subproject commit 35bd889b4829446f972292440174e7c0e4fa40a2

View File

@ -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 = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 }
Quat.__index = Quat Quat.__index = Quat
Quat.__name = "Quat" Quat.__name = "Quat"
@ -22,7 +27,7 @@ end
Quat.IDENTITY = Quat:new(0, 0, 0, 1) 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) return Quat:new(self.x, self.y, self.z, self.w)
end end

View File

@ -1,6 +1,7 @@
--require("math.quat") ---@class Transform
--require("math.vec3") ---@field translation Vec3
---@field rotation Quat
---@field Scale Vec3
Transform = { translation = Vec3.ZERO, rotation = Quat.IDENTITY, scale = Vec3.ONE } Transform = { translation = Vec3.ZERO, rotation = Quat.IDENTITY, scale = Vec3.ONE }
Transform.__index = Transform Transform.__index = Transform
Transform.__name = "Transform" Transform.__name = "Transform"
@ -16,14 +17,14 @@ function Transform:new(translation, rotation, scale)
return t return t
end end
function Transform:copy() function Transform:clone()
return Transform:new(self.translation:copy(), self.rotation:copy(), self.scale:copy()) return Transform:new(self.translation:clone(), self.rotation:clone(), self.scale:clone())
end end
--- Creates a new Transform with the translation at the vec3 --- Creates a new Transform with the translation at the vec3
--- @param pos Vec3 --- @param pos Vec3
function Transform:from_vec3(pos) function Transform:from_vec3(pos)
local t = Transform:copy() -- copy of default transform local t = Transform:clone() -- copy of default transform
t.translation = pos t.translation = pos
return t return t
end end
@ -81,7 +82,7 @@ end
--- @param alpha number --- @param alpha number
--- @return Transform --- @return Transform
function Transform:lerp(rhs, alpha) function Transform:lerp(rhs, alpha)
local res = self:copy() local res = self:clone()
res.translation = self.translation:lerp(rhs.translation, alpha) res.translation = self.translation:lerp(rhs.translation, alpha)
res.rotation = self.rotation:lerp(rhs.rotation, alpha) res.rotation = self.rotation:lerp(rhs.rotation, alpha)
res.scale = self.scale:lerp(rhs.scale, alpha) res.scale = self.scale:lerp(rhs.scale, alpha)

View File

@ -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 = { x = 0.0, y = 0.0, z = 0.0 }
Vec3.__index = Vec3 Vec3.__index = Vec3
Vec3.__name = "Vec3" Vec3.__name = "Vec3"
@ -18,7 +22,9 @@ function Vec3:new(x, y, z)
return v return v
end end
function Vec3:copy() ---Creates a copy of self
---@return Vec3
function Vec3:clone()
return Vec3:new(self.x, self.y, self.z) return Vec3:new(self.x, self.y, self.z)
end end
@ -49,9 +55,10 @@ Vec3.ZERO = Vec3:new(0, 0, 0)
Vec3.ONE = Vec3:new(1, 1, 1) Vec3.ONE = Vec3:new(1, 1, 1)
--- Computes the absolute value of `self`. --- Computes the absolute value of `self`.
---@return Vec3
function Vec3:abs() 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 end
--- Computes the length of `self`. --- Computes the length of `self`.
@ -60,6 +67,16 @@ function Vec3:length()
return math.sqrt(self:dot(self)) return math.sqrt(self:dot(self))
end 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`. --- Computes the dot product of `self` and `rhs`.
---@param rhs Vec3 ---@param rhs Vec3
---@return number ---@return number
@ -80,11 +97,12 @@ function Vec3:min(rhs)
return Vec3:new(x, y, z) return Vec3:new(x, y, z)
end end
--- Returns `self` normalized to a length 1. --- Modifies `self` to be normalized to a length 1.
---@return unknown
function Vec3:normalize() function Vec3:normalize()
local len_recip = 1.0 / self:length() 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 end
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`. --- 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] -- ensure alpha is [0, 1]
local alpha = math.max(0, math.min(1, alpha)) local alpha = math.max(0, math.min(1, alpha))
local res = self:copy() local res = self:clone()
res = res + ((rhs - res) * alpha) res = res + ((rhs - res) * alpha)
return res return res
end end
function Vec3:__add(rhs) function Vec3:__add(rhs)
if type(rhs) == "Vec3" then
return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) 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 end
function Vec3:__sub(rhs) function Vec3:__sub(rhs)
if type(rhs) == "Vec3" then
return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) 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 end
function Vec3:__mul(rhs) function Vec3:__mul(rhs)
if type(rhs) == "number" then if type(rhs) == "Vec3" then
return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs)
else
return Vec3:new(self.x * rhs.x, self.y * rhs.y, self.z * rhs.z) 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
end end
function Vec3:__div(rhs) function Vec3:__div(rhs)
if type(rhs) == "number" then if type(rhs) == "Vec3" then
return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs)
else
return Vec3:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z) 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
end end
function Vec3:__idiv(rhs) function Vec3:__idiv(rhs)
if type(rhs) == "number" then if type(rhs) == "Vec3" then
return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs)
else
return Vec3:new(self.x // rhs.x, self.y // rhs.y, self.z // rhs.z) 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
end end

View File

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

View File

@ -134,9 +134,9 @@ impl Iterator for DynamicViewIter {
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
pub struct ReflectedItem<'a> { pub struct ReflectedItem<'a> {
pub proxy: &'a ReflectLuaProxy, //pub proxy: &'a ReflectLuaProxy,
pub comp_ptr: NonNull<u8>, pub comp_ptr: NonNull<u8>,
pub comp_ud: mlua::AnyUserData<'a>, pub comp_val: elua::Value<'a>,
} }
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
@ -153,7 +153,11 @@ pub struct ReflectedIterator {
impl ReflectedIterator { impl ReflectedIterator {
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option<ReflectedRow<'a>> { pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option<ReflectedRow<'a>> {
use elua::AsLua;
use super::ReflectedLuaTableProxy;
let n = self.dyn_view.next(); let n = self.dyn_view.next();
@ -172,17 +176,20 @@ impl ReflectedIterator {
let reg_type = reflected_components.get_type(id) let reg_type = reflected_components.get_type(id)
.expect("Requested type was not found in TypeRegistry"); .expect("Requested type was not found in TypeRegistry");
let proxy = reg_type.get_data::<ReflectLuaProxy>() let value = if let Some(proxy) = reg_type.get_data::<ReflectLuaProxy>() {
.expect("Type does not have ReflectLuaProxy as a TypeData"); (proxy.fn_as_uservalue)(lua, d.ptr).unwrap()
.as_lua(lua).unwrap()
let userdata = (proxy.fn_as_uservalue)(lua, d.ptr).unwrap(); } else if let Some(proxy) = reg_type.get_data::<ReflectedLuaTableProxy>() {
(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 { dynamic_row.push(ReflectedItem {
proxy,
comp_ptr: d.ptr, comp_ptr: d.ptr,
comp_ud: userdata comp_val: value
}); });
//dynamic_row.push(( (proxy, d.ptr), userdata));
} }
let row = ReflectedRow { let row = ReflectedRow {

View File

@ -2,6 +2,7 @@ pub mod dynamic_iter;
pub use dynamic_iter::*; pub use dynamic_iter::*;
pub mod world; pub mod world;
use elua::FromLua;
pub use world::*; pub use world::*;
pub mod script; pub mod script;
@ -19,19 +20,14 @@ pub use proxy::*;
pub mod system; pub mod system;
pub use system::*; pub use system::*;
#[cfg(test)]
mod test;
use std::{any::TypeId, sync::Mutex}; use std::{any::TypeId, sync::Mutex};
use lyra_ecs::{ use lyra_ecs::{
DynamicBundle, World, Component, ComponentInfo, DynamicBundle, World
}; };
use lyra_reflect::{FromType, Reflect, TypeRegistry}; use lyra_reflect::{FromType, Reflect, TypeRegistry};
use mlua::{AnyUserDataExt, Lua}; pub type LuaContext = Mutex<elua::State>;
pub type LuaContext = Mutex<mlua::Lua>;
pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type";
pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; 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. /// A trait used for registering a Lua type with the world.
pub trait RegisterLuaType { 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 + 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<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 + 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 { 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 + elua::FromLua<'a> + elua::Userdata
{ {
let mut registry = self.get_resource_mut::<TypeRegistry>(); let mut registry = self.get_resource_mut::<TypeRegistry>();
@ -67,44 +70,64 @@ 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 + elua::FromLua<'a> + elua::Userdata
{ {
let mut registry = self.get_resource_mut::<TypeRegistry>(); let mut registry = self.get_resource_mut::<TypeRegistry>();
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 as FromType<W>>::from_type()); reg_type.add_data(<ReflectLuaProxy as FromType<W>>::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::<TypeRegistry>();
let type_id = TypeId::of::<T>();
let reg_type = registry.get_type_or_default(TypeId::of::<C>());
reg_type.add_data(<ReflectedLuaTableProxy as FromType<T>>::from_type());
drop(registry);
let mut lookup = self.get_resource_or_else::<LuaTableProxyLookup, _>(LuaTableProxyLookup::default);
lookup.typeid_from_name.insert(T::table_name(), TypeId::of::<C>());
let mut info = ComponentInfo::new::<C>();
lookup.comp_info_from_name.insert(T::table_name(), info);
}
} }
impl<'lua> mlua::FromLua<'lua> for ScriptBorrow { impl<'lua> elua::FromLua<'lua> for ScriptBorrow {
fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result<Self> { fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
match value { match value {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
_ => unreachable!(), _ => unreachable!(),
} }
} }
} }
impl mlua::UserData for ScriptBorrow {} impl<'lua> elua::FromLuaVec<'lua> for ScriptBorrow {
fn from_lua_value_vec(state: &'lua elua::State, mut values: elua::ValueVec<'lua>) -> elua::Result<Self> {
pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow { if let Some(v) = values.pop_front() {
ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) ScriptBorrow::from_lua(state, v)
.expect("Type does not implement '__internal_reflect' properly") } 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")
}

View File

@ -13,7 +13,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
let ctx = ctx.lock().unwrap(); let ctx = ctx.lock().unwrap();
let globals = ctx.globals(); let globals = ctx.globals()?;
globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?; globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?;
globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?; globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?;

View File

@ -1,8 +1,8 @@
use elua::{TableProxy, Userdata};
use lyra_ecs::World; use lyra_ecs::World;
use lyra_game::math; use lyra_game::math::{self, Quat, Transform, Vec3};
use crate::ScriptData; use crate::ScriptData;
use crate::lua::RegisterLuaType; use crate::lua::RegisterLuaType;
use crate::lua::wrappers::{LuaVec3, LuaTransform};
use crate::{ScriptApiProvider, lua::LuaContext}; use crate::{ScriptApiProvider, lua::LuaContext};
@ -13,26 +13,31 @@ impl ScriptApiProvider for LyraMathApiProvider {
type ScriptContext = LuaContext; type ScriptContext = LuaContext;
fn prepare_world(&mut self, world: &mut World) { fn prepare_world(&mut self, world: &mut World) {
world.register_lua_wrapper::<LuaVec3>(); // TODO
world.register_lua_wrapper::<LuaTransform>(); /* world.register_lua_wrapper::<LuaVec3>();
world.register_lua_wrapper::<LuaTransform>(); */
world.register_lua_table_proxy::<LuaVec3, Vec3>();
world.register_lua_table_proxy::<LuaQuat, Quat>();
world.register_lua_table_proxy::<LuaTransform, Transform>();
} }
fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(&mut self, _data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
let ctx = ctx.lock().unwrap(); let ctx = ctx.lock().unwrap();
/* let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua"); let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua");
ctx.load(bytes.to_vec()).exec()?; ctx.load("lyra/math/vec2.lua", bytes.as_slice())?.execute(())?;
let bytes = include_bytes!("../../../scripts/lua/math/quat.lua"); 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"); 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(); // TODO
globals.set("Vec3", ctx.create_proxy::<LuaVec3>()?)?; //let globals = ctx.globals()?;
globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?; //globals.set("Vec3", elua::Proxy::<LuaVec3>::from(LuaVec3(Vec3::ZERO)))?;
//globals.set("Vec3", LuaVec3(math::Vec3::ZERO).into_lua(&ctx)?)?; //globals.set("Vec3", ctx.create_proxy::<LuaVec3>()?)?;
//globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?;
Ok(()) 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<Self> {
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<elua::Table<'a>> {
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<Self> {
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<elua::Table<'a>> {
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<Self> {
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<elua::Table<'a>> {
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)] /* #[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)]
pub struct LuaVec3(#[reflect(skip)] math::Vec3); pub struct LuaVec3(#[reflect(skip)] math::Vec3);

View File

@ -17,47 +17,46 @@ use crate::{ScriptApiProvider, ScriptData};
pub struct UtilityApiProvider; pub struct UtilityApiProvider;
impl ScriptApiProvider for UtilityApiProvider { impl ScriptApiProvider for UtilityApiProvider {
type ScriptContext = Mutex<mlua::Lua>; type ScriptContext = Mutex<elua::State>;
fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
let ctx = ctx.lock().unwrap(); let ctx = ctx.lock().unwrap();
fn printf(lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic<mlua::Value>)) -> mlua::Result<()> { //fn printf(lua: &elua::State, (mut text, formats): (String, elua::Variadic<elua::Value>)) -> elua::Result<()> {
let printf = |lua: &elua::State, (mut text, formats): (String, elua::Variadic<elua::Value>)| {
let mut formatted = String::new(); let mut formatted = String::new();
let mut arg_num = 0; let mut arg_num = 0;
while let Some(start) = text.find("{}") { while let Some(start) = text.find("{}") {
let val_str = match formats.get(arg_num) { let val_str = match formats.get(arg_num) {
Some(v) => match v { Some(v) => match v {
mlua::Value::Nil => "nil".to_string(), elua::Value::Nil => "nil".to_string(),
mlua::Value::Boolean(b) => b.to_string(), elua::Value::Boolean(b) => b.to_string(),
mlua::Value::LightUserData(_) => { elua::Value::Number(n) => n.to_string(),
return Err(mlua::Error::RuntimeError(format!("unable to get string representation of LightUserData"))); 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(), elua::Value::Function(_) => {
mlua::Value::Number(n) => n.to_string(), return Err(elua::Error::runtime("unable to get string representation of Function"));
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(_) => { elua::Value::Thread(_) => {
return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Function"))); return Err(elua::Error::runtime("unable to get string representation of Thread"));
}, },
mlua::Value::Thread(_) => { elua::Value::Userdata(ud) => {
return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Thread"))); if let Ok(tos) = ud.get::<_, elua::Function>(elua::MetaMethod::ToString) {
}, tos.exec::<_, String>(())?
mlua::Value::UserData(ud) => {
let metatable = ud.get_metatable()?;
if let Ok(tos) = metatable.get::<mlua::Function>(mlua::MetaMethod::ToString) {
tos.call::<_, String>((ud.clone(),))?
} else { } 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 => { 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. // continue searching for {} to get the number of format spots for the error message.
while let Some(start) = text.find("{}") { while let Some(start) = text.find("{}") {
@ -65,13 +64,15 @@ impl ScriptApiProvider for UtilityApiProvider {
arg_num += 1; arg_num += 1;
} }
return Err(mlua::Error::BadArgument { return Err(elua::Error::BadArgument {
to: Some("printf".to_string()), func: Some("printf".to_string()),
pos: 2, arg_index: 2,
name: Some("...".to_string()), arg_name: Some("fmt...".to_string()),
cause: Arc::new(mlua::Error::RuntimeError(format!("not enough args \ error: Arc::new(elua::Error::Runtime(format!(
"not enough args \
given for the amount of format areas in the string. Expected {}, \ 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() { if arg_num < formats.len() {
return Err(mlua::Error::BadArgument { return Err(elua::Error::BadArgument {
to: Some("printf".to_string()), func: Some("printf".to_string()),
pos: 2, arg_index: 2,
name: Some("...".to_string()), arg_name: Some("fmt...".to_string()),
cause: Arc::new(mlua::Error::RuntimeError(format!("got more args \ error: Arc::new(elua::Error::Runtime(format!(
than format areas in the string. Expected {}, got {}.", formats.len(), arg_num))) "got more args \
than format areas in the string. Expected {}, got {}.", formats.len(), arg_num
)))
}) })
} }
formatted = format!("{}{}", formatted, text); formatted = format!("{}{}", formatted, text);
lua.globals() lua.globals()?
.get::<_, mlua::Function>("print")? .get::<_, elua::Function>("print")?
.call::<_, ()>(formatted)?; .exec::<_, ()>(formatted)?;
Ok(()) 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 printf_func = ctx.create_function(printf)?;
let print_func = ctx.create_function(move |lua, text: String| { let print_func = ctx.create_function(move |lua, text: String| {
let name = lua.registry_value::<String>(&script_name_reg)?; let name = lua.registry_get::<String>(script_name_reg)?;
let _span = debug_span!("lua", script = &name).entered(); let _span = debug_span!("lua", script = &name).entered();
debug!(target: "lyra_scripting::lua", "{}", text); debug!(target: "lyra_scripting::lua", "{}", text);
@ -114,7 +117,7 @@ impl ScriptApiProvider for UtilityApiProvider {
Ok(()) Ok(())
})?; })?;
let globals = ctx.globals(); let globals = ctx.globals()?;
globals.set("printf", printf_func)?; globals.set("printf", printf_func)?;
globals.set("print", print_func)?; globals.set("print", print_func)?;

View File

@ -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 lyra_reflect::{Reflect, FromType};
use crate::ScriptDynamicBundle; use crate::{ScriptBorrow, ScriptDynamicBundle};
use super::FN_NAME_INTERNAL_REFLECT;
pub trait LuaWrapper { pub trait LuaWrapper {
/// The type id of the wrapped type. /// The type id of the wrapped type.
fn wrapped_type_id() -> TypeId; 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 { pub trait LuaProxy {
fn as_lua_value<'lua>( fn as_lua_value<'lua>(
lua: &'lua mlua::Lua, lua: &'lua elua::State,
this: &dyn Reflect, this: &dyn Reflect,
) -> mlua::Result<mlua::AnyUserData<'lua>>; ) -> elua::Result<elua::AnyUserdata<'lua>>;
fn apply( fn apply(
lua: &mlua::Lua, lua: &elua::State,
this: &mut dyn Reflect, this: &mut dyn Reflect,
apply: &mlua::AnyUserData, apply: &elua::AnyUserdata,
) -> mlua::Result<()>; ) -> elua::Result<()>;
} }
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 + elua::FromLua<'a> + elua::Userdata
{ {
fn as_lua_value<'lua>( fn as_lua_value<'lua>(
lua: &'lua mlua::Lua, lua: &'lua elua::State,
this: &dyn Reflect, this: &dyn Reflect,
) -> mlua::Result<mlua::AnyUserData<'lua>> { ) -> elua::Result<elua::AnyUserdata<'lua>> {
let this = this.as_any().downcast_ref::<T>().unwrap(); let this = this.as_any().downcast_ref::<T>().unwrap();
lua.create_userdata(this.clone()) lua.create_userdata(this.clone())
} }
fn apply( fn apply(
_lua: &mlua::Lua, _: &elua::State,
this: &mut dyn Reflect, this: &mut dyn Reflect,
apply: &mlua::AnyUserData, apply: &elua::AnyUserdata,
) -> mlua::Result<()> { ) -> elua::Result<()> {
let this = this.as_any_mut().downcast_mut::<T>().unwrap(); let this = this.as_any_mut().downcast_mut::<T>().unwrap();
let apply = apply.borrow::<T>()?; let apply = apply.as_ref::<T>()?;
*this = apply.clone(); *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<String, TypeId>,
pub(crate) comp_info_from_name: HashMap<String, ComponentInfo>,
}
/// 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<elua::Table<'a>>,
pub fn_apply: for<'a> fn(
lua: &'a elua::State,
this_ptr: NonNull<()>,
table: &'a elua::Table<'a>,
) -> elua::Result<()>,
}
impl<'a, T> FromType<T> for ReflectedLuaTableProxy
where
T: TableProxy
{
fn from_type() -> Self {
Self {
table_name: T::table_name(),
fn_as_table: |lua, this| -> elua::Result<elua::Table> {
let this = unsafe { this.cast::<T>().as_ref() };
this.as_table(lua)
},
fn_apply: |lua, ptr, table| {
let this = unsafe { ptr.cast::<T>().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)] #[derive(Clone)]
pub struct ReflectLuaProxy { pub struct ReflectLuaProxy {
pub fn_as_uservalue: pub fn_as_uservalue:
for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull<u8>) -> mlua::Result<mlua::AnyUserData<'a>>, for<'a> fn(lua: &'a elua::State, this_ptr: NonNull<u8>) -> elua::Result<elua::AnyUserdata<'a>>,
pub fn_apply: for<'a> fn( pub fn_apply: for<'a> fn(
lua: &'a mlua::Lua, lua: &'a elua::State,
this_ptr: NonNull<u8>, this_ptr: NonNull<u8>,
apply: &'a mlua::AnyUserData<'a>, apply: &'a elua::AnyUserdata<'a>,
) -> mlua::Result<()>, ) -> elua::Result<()>,
} }
impl<'a, T> FromType<T> for ReflectLuaProxy impl<'a, T> FromType<T> for ReflectLuaProxy
where where
T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata
{ {
fn from_type() -> Self { fn from_type() -> Self {
Self { Self {
fn_as_uservalue: |lua, this| -> mlua::Result<mlua::AnyUserData> { fn_as_uservalue: |lua, this| -> elua::Result<elua::AnyUserdata> {
let this = unsafe { this.cast::<T>().as_ref() }; let this = unsafe { this.cast::<T>().as_ref() };
<T as LuaProxy>::as_lua_value(lua, this) <T as LuaProxy>::as_lua_value(lua, this)
}, },
@ -77,16 +124,45 @@ where
} }
} }
impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle { impl<'lua> elua::FromLua<'lua> for ScriptDynamicBundle {
fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> { fn from_lua(_: &'lua elua::State, val: elua::Value<'lua>) -> elua::Result<Self> {
match value { match val {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { elua::Value::Nil => Err(elua::Error::Nil),
from: "Nil", _ => unreachable!(),
to: "DynamicBundle",
message: Some("Value was nil".to_string()),
}),
_ => panic!(),
} }
} }
} }
impl<'lua> elua::FromLuaVec<'lua> for ScriptDynamicBundle {
fn from_lua_value_vec(state: &'lua elua::State, mut values: elua::ValueVec<'lua>) -> elua::Result<Self> {
if let Some(v) = values.pop_front() {
Ok(ScriptDynamicBundle::from_lua(state, v)?)
} else {
Err(elua::Error::Nil)
}
}
}
impl elua::Userdata for ScriptDynamicBundle {
fn name() -> String {
"Bundle".to_string()
}
fn build<'a>(_: &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(())
}
}

View File

@ -1,6 +1,6 @@
use std::sync::Mutex; use std::sync::Mutex;
use mlua::IntoLua; use elua::{AsLua, StdLibraries, StdLibrary};
use tracing::{debug, trace}; use tracing::{debug, trace};
use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity};
@ -8,40 +8,37 @@ use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity};
#[derive(Default)] #[derive(Default)]
pub struct LuaHost; pub struct LuaHost;
fn try_call_lua_function(lua: &mlua::Lua, fn_name: &str) -> Result<(), ScriptError> { fn try_call_lua_function(lua: &elua::State, fn_name: &str) -> Result<(), ScriptError> {
let globals = lua.globals(); let globals = lua.globals()?;
match globals.get::<_, mlua::Function>(fn_name) { if globals.has_key(fn_name)? {
Ok(init_fn) => { let lua_fn = globals.get::<_, elua::Function>(fn_name)?;
init_fn.call(()) lua_fn.exec(())
.map_err(ScriptError::MluaError)?; .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));
},
} }
Ok(()) Ok(())
} }
impl ScriptHost for LuaHost { impl ScriptHost for LuaHost {
type ScriptContext = Mutex<mlua::Lua>; type ScriptContext = Mutex<elua::State>;
fn load_script(&mut self, script: &[u8], script_data: &crate::ScriptData, providers: &mut crate::ScriptApiProviders<Self>) -> Result<Self::ScriptContext, crate::ScriptError> { fn load_script(&mut self, script: &[u8], script_data: &crate::ScriptData, providers: &mut crate::ScriptApiProviders<Self>) -> Result<Self::ScriptContext, crate::ScriptError> {
let mut ctx = Mutex::new(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() { for provider in providers.apis.iter_mut() {
provider.expose_api(script_data, &mut ctx)?; provider.expose_api(script_data, &mut ctx)?;
} }
let lua = ctx.lock().unwrap(); let lua = ctx.lock().unwrap();
lua.load(script) lua.load(&script_data.name, script)?
.set_name(&script_data.name) .execute(())
.exec()
.map_err(|e| ScriptError::MluaError(e))?; .map_err(|e| ScriptError::MluaError(e))?;
drop(lua); drop(lua);
@ -71,9 +68,9 @@ impl ScriptHost for LuaHost {
let ctx = ctx.lock().expect("Failure to get Lua ScriptContext"); let ctx = ctx.lock().expect("Failure to get Lua ScriptContext");
let globals = ctx.globals(); let globals = ctx.globals()?;
globals.set("world", world.into_lua(&ctx)?)?; globals.set("world", world.as_lua(&ctx)?)?;
globals.set("entity", ScriptEntity(script_data.entity).into_lua(&ctx)?)?; globals.set("entity", ScriptEntity(script_data.entity).as_lua(&ctx)?)?;
try_call_lua_function(&ctx, function_name)?; try_call_lua_function(&ctx, function_name)?;

View File

@ -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<mlua::Lua>;
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::Value>)) -> 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::<LuaHost>::default();
providers.add_provider(test_provider);
let host = LuaHost::default();
world.add_resource(host);
world.add_resource(providers);
world.add_resource(ScriptContexts::<LuaContext>::default());
let mut res_loader = ResourceManager::new();
res_loader.register_loader::<LuaLoader>();
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::<LuaScript>("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();
}

View File

@ -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_ecs::query::dynamic::QueryDynamicType;
use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType}; use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType};
use mlua::{AnyUserDataExt, IntoLua, IntoLuaMulti};
use crate::{ScriptWorldPtr, ScriptEntity, ScriptDynamicBundle, ScriptBorrow}; 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 { impl<'lua> elua::FromLua<'lua> for ScriptEntity {
fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> { fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
match value { match value {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { from: "Nil", to: "ScriptEntity", message: Some("Value was nil".to_string()) }), elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptEntity", "Nil")),
_ => panic!(), _ => panic!(),
} }
} }
} }
impl mlua::UserData for ScriptEntity { impl elua::Userdata for ScriptEntity {
fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn name() -> String {
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { "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(format!("{:?}", this.0))
}) });
Ok(())
} }
} }
@ -32,64 +37,74 @@ pub enum WorldError {
LuaInvalidUsage(String), LuaInvalidUsage(String),
} }
impl mlua::UserData for ScriptWorldPtr { impl<'a> elua::FromLua<'a> for ScriptWorldPtr {
fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn from_lua(lua: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
methods.add_method_mut("spawn", |_, this, (bundle,): (ScriptDynamicBundle,)| { match val {
let world = unsafe { this.inner.as_mut() }; elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptWorldPtr", "Nil")),
_ => panic!(),
}
}
}
impl elua::Userdata for ScriptWorldPtr {
fn name() -> String {
"World".to_string()
}
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();
Ok(ScriptEntity(world.spawn(bundle.0))) Ok(ScriptEntity(world.spawn(bundle.0)))
}); })
.method_mut("view", |lua, this, (system, queries): (elua::Function, elua::ValueVec)| {
methods.add_method("view_iter", |lua, this, queries: mlua::Variadic<mlua::AnyUserData>| {
let world = unsafe { this.inner.as_ref() };
let mut view = world.dynamic_view();
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();
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::<mlua::Result<Vec<mlua::Value>>>()?;
Ok(mlua::MultiValue::from_vec(row))
} else {
Ok(mlua::Value::Nil.into_lua_multi(lua)?)
}
})?;
Ok(f)
});
methods.add_method_mut("view", |lua, this, (system, queries): (mlua::Function, mlua::Variadic<mlua::AnyUserData>)| {
if queries.is_empty() { if queries.is_empty() {
return Err(mlua::Error::BadArgument { to: Some("world:view".to_string()), pos: 2, name: Some("...".to_string()), cause: return Err(
Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage("no component types provided".to_string()))) 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 world = unsafe { this.inner.as_ref() };
let mut view = world.dynamic_view(); let mut view = world.dynamic_view();
for comp in queries.into_iter() { for (idx, comp) in queries.into_iter().enumerate() {
let reflect = comp.call_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) match comp {
elua::Value::Table(t) => {
let name: String = t.get("__name")?;
let lookup = world.get_resource::<LuaTableProxyLookup>();
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"); .expect("Type does not implement 'reflect_type' properly");
let refl_comp = reflect.reflect_branch.as_component_unchecked(); let refl_comp = reflect.reflect_branch.as_component_unchecked();
let dyn_type = QueryDynamicType::from_info(refl_comp.info); let dyn_type = QueryDynamicType::from_info(refl_comp.info);
view.push(dyn_type); view.push(dyn_type);
},
_ => todo!()
}
} }
let iter = view.into_iter(); let iter = view.into_iter();
@ -103,19 +118,15 @@ impl mlua::UserData for ScriptWorldPtr {
let mut has_ticked = false; let mut has_ticked = false;
while let Some(row) = reflected_iter.next_lua(lua) { while let Some(row) = reflected_iter.next_lua(lua) {
let r = row.row.into_iter() let r = row.row.into_iter()
.map(|r| (r.proxy, r.comp_ud, r.comp_ptr)) .map(|r| (r.comp_val, r.comp_ptr))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let (reflects, values, ptrs): let (values, ptrs) = itertools::multiunzip::<(Vec<elua::Value>, Vec<NonNull<u8>>), _>(r);
(Vec<&ReflectLuaProxy>, Vec<mlua::AnyUserData>, Vec<NonNull<u8>>) let mult_val = elua::ValueVec::from(values);
= itertools::multiunzip(r); let res: elua::ValueVec = system.exec(mult_val)?;
let value_row: Vec<_> = values.into_iter().map(|ud| ud.into_lua(lua)).collect::<mlua::Result<Vec<mlua::Value>>>()?;
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 values were returned, find the type in the type registry, and apply the new values
if res.len() <= reflects.len() { if res.len() <= ptrs.len() {
// we only want to tick one time per system // we only want to tick one time per system
if !has_ticked { if !has_ticked {
current = world.tick(); current = world.tick();
@ -125,9 +136,9 @@ impl mlua::UserData for ScriptWorldPtr {
for (i, comp) in res.into_iter().enumerate() { for (i, comp) in res.into_iter().enumerate() {
let ptr = ptrs[i]; let ptr = ptrs[i];
match comp.as_userdata() { match comp {
Some(ud) => { elua::Value::Userdata(ud) => {
let lua_comp = reflect_user_data(ud); let lua_comp = reflect_user_data(&ud);
let refl_comp = lua_comp.reflect_branch.as_component_unchecked(); let refl_comp = lua_comp.reflect_branch.as_component_unchecked();
let lua_typeid = refl_comp.info.type_id.as_rust(); let lua_typeid = refl_comp.info.type_id.as_rust();
@ -142,27 +153,48 @@ impl mlua::UserData for ScriptWorldPtr {
let reg = this.as_ref().get_resource::<TypeRegistry>(); let reg = this.as_ref().get_resource::<TypeRegistry>();
let reg_type = reg.get_type(lua_typeid).unwrap(); let reg_type = reg.get_type(lua_typeid).unwrap();
let proxy = reg_type.get_data::<ReflectLuaProxy>().unwrap(); let proxy = reg_type.get_data::<ReflectLuaProxy>()
(proxy.fn_apply)(lua, ptr, ud)?; .expect("Type does not have ReflectLuaProxy as a TypeData");
} (proxy.fn_apply)(lua, ptr, &ud)?;
None => { },
panic!("A userdata value was not returned!"); elua::Value::Table(tbl) => {
let name: String = tbl.get("__name")?;
let lookup = world.get_resource::<LuaTableProxyLookup>();
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::<TypeRegistry>();
let reg_type = reg.get_type(*tyid).unwrap();
let proxy = reg_type.get_data::<ReflectedLuaTableProxy>()
.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 { } else {
let msg = format!("Too many arguments were returned from the World view! let msg = format!("Too many arguments were returned from the World view!
At most, the expected number of results is {}.", reflects.len()); At most, the expected number of results is {}.", ptrs.len());
return Err(mlua::Error::external(WorldError::LuaInvalidUsage(msg))); return Err(elua::Error::Runtime(msg));
} }
} }
Ok(()) Ok(())
}); })
.method_mut("resource", |lua, this, (ty,): (elua::AnyUserdata,)| {
methods.add_method_mut("resource", |lua, this, (ty,): (mlua::AnyUserData,)| {
let reflect = ty let reflect = ty
.call_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) .execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
.expect("Type does not implement 'reflect_type' properly"); .expect("Type does not implement 'reflect_type' properly");
let res = reflect.reflect_branch.as_resource_unchecked(); let res = reflect.reflect_branch.as_resource_unchecked();
@ -174,11 +206,13 @@ impl mlua::UserData for ScriptWorldPtr {
.expect("Type does not have ReflectLuaProxy as a TypeData"); .expect("Type does not have ReflectLuaProxy as a TypeData");
(proxy.fn_as_uservalue)(lua, res_ptr) (proxy.fn_as_uservalue)(lua, res_ptr)
.and_then(|ud| ud.into_lua(lua)) .and_then(|ud| ud.as_lua(lua))
} else { } else {
// if the resource is not found in the world, return nil // if the resource is not found in the world, return nil
Ok(mlua::Value::Nil) Ok(elua::Value::Nil)
} }
}); });
Ok(())
} }
} }

View File

@ -1,4 +1,4 @@
use std::{ops::Deref, any::TypeId}; use std::any::TypeId;
use lyra_game::DeltaTime; use lyra_game::DeltaTime;
use crate::{lyra_engine, lua::{FN_NAME_INTERNAL_REFLECT_TYPE, LuaWrapper}, ScriptBorrow}; 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 { impl<'lua> elua::FromLua<'lua> for LuaDeltaTime {
fn from_lua(value: mlua::prelude::LuaValue<'lua>, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult<Self> { fn from_lua(lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
match value { match value {
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()), elua::Value::Userdata(ud) => Ok(ud.as_ref::<Self>()?.clone()),
_ => unreachable!(), _ => unreachable!(),
} }
} }
} }
impl mlua::UserData for LuaDeltaTime { impl elua::Userdata for LuaDeltaTime {
fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { fn name() -> String {
methods.add_method("get", |_, this, ()| { "DeltaTime".to_string()
Ok(*this.0.deref()) }
});
methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> {
Ok(format!("{}", this.0.deref())) builder
}); .method("get", |_, this, ()| {
Ok(*this.0)
methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { })
.meta_method(elua::MetaMethod::ToString, |_, this, ()| {
Ok(format!("{}", *this.0))
})
.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(ScriptBorrow::from_resource::<DeltaTime>(None)) Ok(ScriptBorrow::from_resource::<DeltaTime>(None))
}); });
Ok(())
} }
} }

View File

@ -7,7 +7,7 @@ use lyra_scripting_derive::wrap_math_vec_copy;
use crate as lyra_scripting; use crate as lyra_scripting;
// f32 types // f32 types
wrap_math_vec_copy!( /* wrap_math_vec_copy!(
math::Vec2, math::Vec2,
derives(PartialEq), derives(PartialEq),
fields(x, y), fields(x, y),
@ -32,7 +32,11 @@ wrap_math_vec_copy!(
Mod(LuaVec3, f32), Mod(LuaVec3, f32),
Eq, Unm, ToString Eq, Unm, ToString
) )
); ); */
// =================================================
/* wrap_math_vec_copy!( /* wrap_math_vec_copy!(
math::Vec3A, math::Vec3A,
@ -379,7 +383,20 @@ wrap_math_vec_copy!(
) )
); */ ); */
wrap_math_vec_copy!(
// ============================================================
/* wrap_math_vec_copy!(
math::Quat, math::Quat,
derives(PartialEq), derives(PartialEq),
no_new, no_new,
@ -559,3 +576,4 @@ wrap_math_vec_copy!(
}); });
} }
); );
*/