scripting: create `FN_NAME_INTERNAL_AS_COMPONENT` for implicitly converting some types as components

This commit is contained in:
SeanOMik 2024-02-24 15:27:01 -05:00
parent 6731fcd7f2
commit 388f686917
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
6 changed files with 84 additions and 17 deletions

View File

@ -3,9 +3,8 @@ function on_init()
print("Loaded textured cube") print("Loaded textured cube")
local pos = Transform.from_translation(Vec3.new(0, 0, -8.0)) local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
local cube_comp = ModelComponent.new(cube)
local e = world:spawn(pos, cube_comp) local e = world:spawn(pos, cube)
print("spawned entity " .. tostring(e)) print("spawned entity " .. tostring(e))
end end

View File

@ -26,13 +26,52 @@ use lyra_ecs::{
Component, ComponentInfo, World Component, ComponentInfo, World
}; };
use lyra_reflect::{Reflect, TypeRegistry}; use lyra_reflect::{Reflect, TypeRegistry};
use crate::ScriptBorrow;
pub type LuaContext = Mutex<elua::State>; pub type LuaContext = Mutex<elua::State>;
/// Name of a Lua function that is used to Reflect the Userdata, but without a value.
///
/// This is used for reflecting the userdata as an ECS Component or Resource. This **function**
/// returns a [`ScriptBorrow`] with data as `None`.
pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type";
/// Name of a Lua function that is used to Reflect the Userdata.
///
/// This is used for reflecting the userdata as an ECS Component or Resource. This **method**
/// returns a [`ScriptBorrow`] with data as `Some`. **Anything that calls this expects the
/// method to return data**.
pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
use crate::ScriptBorrow; /// Name of a Lua function implemented for Userdata types that can be made into components.
///
/// This is used for types that can be converted into components. When implementing this function,
/// you must return a [`ScriptBorrow`] that contains the component for this userdata.
/// You can return [`elua::Value::Nil`] if for some reason the type could not be converted
/// into a component.
///
/// A good example of this is `LuaResHandle`. The resource handle is requested from the
/// world, and could be a 3d model. The 3d model could then be manually wrapped as
/// [`LuaModelComponent`] with its `new` function. But for quality of life, this internal
/// function was created so that the userdata can be converted into its component
/// type without having to wrap it.
///
/// Without implementing this function:
/// ```lua
/// local cube = world:request_res("assets/cube-texture-embedded.gltf")
/// local cube_comp = ModelComponent.new(cube) -- annoying to write
///
/// local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
/// local e = world:spawn(pos, cube_comp)
/// ```
///
/// With this function:
/// /// ```lua
/// local cube = world:request_res("assets/cube-texture-embedded.gltf")
/// local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
/// local e = world:spawn(pos, cube)
/// ```
pub const FN_NAME_INTERNAL_AS_COMPONENT: &str = "__lyra_internal_refl_as_component";
/// 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 {
@ -140,7 +179,8 @@ impl elua::Userdata for ScriptBorrow {
} }
} }
/// Helper function used for reflecting userdata as a ScriptBorrow
pub fn reflect_user_data(ud: &elua::AnyUserdata) -> ScriptBorrow { pub fn reflect_user_data(ud: &elua::AnyUserdata) -> ScriptBorrow {
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
.expect("Type does not implement '__internal_reflect' properly") .expect("Type does not implement internal reflect method properly")
} }

View File

@ -7,8 +7,7 @@ use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
use super::{ use super::{
reflect_user_data, wrappers::LuaResHandle, DynamicViewIter, LuaTableProxyLookup, reflect_user_data, wrappers::LuaResHandle, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE,
}; };
impl<'lua> elua::FromLua<'lua> for ScriptEntity { impl<'lua> elua::FromLua<'lua> for ScriptEntity {
@ -79,11 +78,22 @@ impl elua::Userdata for ScriptWorldPtr {
Arc::new(elua::Error::runtime("provided component is not userdata")), Arc::new(elua::Error::runtime("provided component is not userdata")),
))?; ))?;
let script_brw = ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?; let comp_borrow = {
let reflect = script_brw.reflect_branch.as_component_unchecked(); if let Ok(as_comp) = ud.get::<_, elua::Function>(FN_NAME_INTERNAL_AS_COMPONENT) {
let ud = match as_comp.exec(ud.clone())? {
elua::Value::Userdata(ud) => ud,
elua::Value::Nil => ud.clone(),
_ => todo!(),
};
let refl_data = script_brw.data.unwrap(); ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
//let refl_data = refl_data.as_ref(); } else {
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
}
};
let reflect = comp_borrow.reflect_branch.as_component_unchecked();
let refl_data = comp_borrow.data.unwrap();
reflect.bundle_insert(&mut bundle, refl_data); reflect.bundle_insert(&mut bundle, refl_data);
} }

View File

@ -22,13 +22,13 @@ impl std::ops::DerefMut for LuaDeltaTime {
} }
impl<'lua> elua::FromLua<'lua> for LuaDeltaTime { impl<'lua> elua::FromLua<'lua> for LuaDeltaTime {
fn from_lua(lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> { fn from_lua(_: &'lua elua::State, _: elua::Value<'lua>) -> elua::Result<Self> {
todo!() todo!()
} }
} }
impl<'lua> elua::AsLua<'lua> for LuaDeltaTime { impl<'lua> elua::AsLua<'lua> for LuaDeltaTime {
fn as_lua(self, lua: &'lua elua::State) -> elua::Result<elua::Value<'lua>> { fn as_lua(self, _: &'lua elua::State) -> elua::Result<elua::Value<'lua>> {
Ok(elua::Value::Number(*self.0 as f64)) Ok(elua::Value::Number(*self.0 as f64))
} }
} }

View File

@ -1,7 +1,7 @@
use std::any::TypeId; use std::any::TypeId;
use std::{cell::Ref, sync::Arc}; use std::{cell::Ref, sync::Arc};
use elua::{AsLua, FromLua}; use elua::FromLua;
use lyra_game::scene::ModelComponent; use lyra_game::scene::ModelComponent;
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use lyra_resource::{Model, ResHandle}; use lyra_resource::{Model, ResHandle};
@ -13,7 +13,7 @@ use crate::{lua::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, Scri
use super::LuaResHandle; use super::LuaResHandle;
#[derive(Clone, Reflect)] #[derive(Clone, Reflect)]
pub struct LuaModelComponent(ModelComponent); pub struct LuaModelComponent(pub ModelComponent);
impl elua::Userdata for LuaModelComponent { impl elua::Userdata for LuaModelComponent {
fn name() -> String { fn name() -> String {
@ -44,7 +44,8 @@ impl elua::Userdata for LuaModelComponent {
}) })
.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { .function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(ScriptBorrow::from_component::<ModelComponent>(None)) Ok(ScriptBorrow::from_component::<ModelComponent>(None))
}).method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { })
.method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(ScriptBorrow::from_component(Some(this.0.clone()))) Ok(ScriptBorrow::from_component(Some(this.0.clone())))
}); });

View File

@ -1,7 +1,12 @@
use std::{ops::Deref, sync::Arc}; use std::{ops::Deref, sync::Arc};
use elua::FromLua; use elua::{AsLua, FromLua};
use lyra_resource::ResourceStorage; use lyra_game::scene::ModelComponent;
use lyra_resource::{Model, ResHandle, ResourceStorage};
use crate::lua::FN_NAME_INTERNAL_AS_COMPONENT;
use super::LuaModelComponent;
#[derive(Clone)] #[derive(Clone)]
pub struct LuaResHandle(pub Arc<dyn ResourceStorage>); pub struct LuaResHandle(pub Arc<dyn ResourceStorage>);
@ -46,6 +51,18 @@ impl elua::Userdata for LuaResHandle {
Ok(this.is_loaded()) Ok(this.is_loaded())
}); });
builder.method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| {
let any = this.0.as_any();
match any.downcast_ref::<ResHandle<Model>>() {
Some(model) => {
LuaModelComponent(ModelComponent(model.clone())).as_lua(lua)
},
None => {
Ok(elua::Value::Nil)
}
}
});
Ok(()) Ok(())
} }
} }