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")
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))
end

View File

@ -26,13 +26,52 @@ use lyra_ecs::{
Component, ComponentInfo, World
};
use lyra_reflect::{Reflect, TypeRegistry};
use crate::ScriptBorrow;
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";
/// 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";
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.
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 {
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 super::{
reflect_user_data, wrappers::LuaResHandle, DynamicViewIter, LuaTableProxyLookup,
ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE,
reflect_user_data, wrappers::LuaResHandle, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
};
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")),
))?;
let script_brw = ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?;
let reflect = script_brw.reflect_branch.as_component_unchecked();
let comp_borrow = {
if let Ok(as_comp) = ud.get::<_, elua::Function>(FN_NAME_INTERNAL_AS_COMPONENT) {
let ud = match as_comp.exec(ud.clone())? {
elua::Value::Userdata(ud) => ud,
elua::Value::Nil => ud.clone(),
_ => todo!(),
};
let refl_data = script_brw.data.unwrap();
//let refl_data = refl_data.as_ref();
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
} else {
ud.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?
}
};
let reflect = comp_borrow.reflect_branch.as_component_unchecked();
let refl_data = comp_borrow.data.unwrap();
reflect.bundle_insert(&mut bundle, refl_data);
}

View File

@ -22,13 +22,13 @@ impl std::ops::DerefMut 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!()
}
}
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))
}
}

View File

@ -1,7 +1,7 @@
use std::any::TypeId;
use std::{cell::Ref, sync::Arc};
use elua::{AsLua, FromLua};
use elua::FromLua;
use lyra_game::scene::ModelComponent;
use lyra_reflect::Reflect;
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;
#[derive(Clone, Reflect)]
pub struct LuaModelComponent(ModelComponent);
pub struct LuaModelComponent(pub ModelComponent);
impl elua::Userdata for LuaModelComponent {
fn name() -> String {
@ -44,7 +44,8 @@ impl elua::Userdata for LuaModelComponent {
})
.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
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())))
});

View File

@ -1,7 +1,12 @@
use std::{ops::Deref, sync::Arc};
use elua::FromLua;
use lyra_resource::ResourceStorage;
use elua::{AsLua, FromLua};
use lyra_game::scene::ModelComponent;
use lyra_resource::{Model, ResHandle, ResourceStorage};
use crate::lua::FN_NAME_INTERNAL_AS_COMPONENT;
use super::LuaModelComponent;
#[derive(Clone)]
pub struct LuaResHandle(pub Arc<dyn ResourceStorage>);
@ -46,6 +51,18 @@ impl elua::Userdata for LuaResHandle {
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(())
}
}