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
12 changed files with 337 additions and 143 deletions
Showing only changes of commit 90b821f95c - Show all commits

View File

@ -1,8 +1,13 @@
--[[ function on_init() --local cube: resource = world:request_res("loader", "assets/cube-texture-embedded.gltf")
print("Lua script was initialized!")
--cube = nil
function on_init()
local cube = world:request_res("assets/cube-texture-embedded.gltf")
print("Loaded textured cube")
end end
function on_first() --[[ function on_first()
print("Lua's first function was called") print("Lua's first function was called")
end end
@ -14,11 +19,6 @@ function on_update()
---@type number ---@type number
local dt = world:resource(DeltaTime) local dt = world:resource(DeltaTime)
local v = Vec3.new(10, 10, 10)
v:move_by(50, 50, 50)
v:move_by(Vec3.new(50, 50, 50))
print("v = " .. tostring(v))
world:view(function (t) world:view(function (t)
t:translate(0, 0.5 * dt, 0) t:translate(0, 0.5 * dt, 0)

@ -1 +1 @@
Subproject commit 22b6d218bdb6a5aafc0efd4fb80a67d2c796d0e7 Subproject commit 70e2985cc44fdb30cdf2157c50d2f0e3385e08fa

View File

@ -89,10 +89,10 @@ pub trait ScriptHost: Default + ResourceObject {
fn setup_script(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext, fn setup_script(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext,
providers: &mut ScriptApiProviders<Self>) -> Result<(), ScriptError>; providers: &mut ScriptApiProviders<Self>) -> Result<(), ScriptError>;
/// Executes the update step for the script. /// Calls a event function in the script.
fn call_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData, fn call_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData,
ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>,
function_name: &str) -> Result<(), ScriptError>; event_fn_name: &str) -> Result<(), ScriptError>;
} }
#[derive(Default)] #[derive(Default)]

View File

@ -76,8 +76,8 @@ impl ReflectBranch {
} }
pub struct ScriptBorrow { pub struct ScriptBorrow {
reflect_branch: ReflectBranch, pub(crate) reflect_branch: ReflectBranch,
data: Option<Box<dyn Reflect>>, pub(crate) data: Option<Box<dyn Reflect>>,
} }
impl Clone for ScriptBorrow { impl Clone for ScriptBorrow {
@ -95,7 +95,7 @@ impl ScriptBorrow {
/// Creates a ScriptBorrow from a Component /// Creates a ScriptBorrow from a Component
pub fn from_component<T>(data: Option<T>) -> Self pub fn from_component<T>(data: Option<T>) -> Self
where where
T: Reflect + Component + Default + 'static T: Reflect + Component + 'static
{ {
let data = data.map(|d| Box::new(d) as Box<(dyn Reflect + 'static)>); let data = data.map(|d| Box::new(d) as Box<(dyn Reflect + 'static)>);
@ -108,7 +108,7 @@ impl ScriptBorrow {
/// Creates a ScriptBorrow from a Resource. /// Creates a ScriptBorrow from a Resource.
pub fn from_resource<T>(data: Option<T>) -> Self pub fn from_resource<T>(data: Option<T>) -> Self
where where
T: Reflect + ResourceObject + Default + 'static T: Reflect + ResourceObject + 'static
{ {
let data = data.map(|d| Box::new(d) as Box<(dyn Reflect + 'static)>); let data = data.map(|d| Box::new(d) as Box<(dyn Reflect + 'static)>);

View File

@ -1,7 +1,7 @@
use lyra_ecs::ResourceObject; use lyra_ecs::ResourceObject;
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use crate::{lua::{wrappers::LuaDeltaTime, LuaContext, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; use crate::{lua::{wrappers::{LuaDeltaTime, LuaModelComponent}, LuaContext, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
#[derive(Default)] #[derive(Default)]
pub struct LyraEcsApiProvider; pub struct LyraEcsApiProvider;
@ -11,6 +11,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
fn prepare_world(&mut self, world: &mut lyra_ecs::World) { fn prepare_world(&mut self, world: &mut lyra_ecs::World) {
world.register_lua_convert::<LuaDeltaTime>(); world.register_lua_convert::<LuaDeltaTime>();
world.register_lua_wrapper::<LuaModelComponent>();
} }
fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
@ -19,6 +20,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
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>()?)?;
globals.set("ModelComponent", ctx.create_proxy::<LuaModelComponent>()?)?;
let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?; let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?;
globals.set("DeltaTime", dt_table)?; globals.set("DeltaTime", dt_table)?;

View File

@ -171,7 +171,6 @@ impl elua::Userdata for ScriptDynamicBundle {
let reflect = script_brw.reflect_branch.as_component_unchecked(); let reflect = script_brw.reflect_branch.as_component_unchecked();
let refl_data = script_brw.data.unwrap(); let refl_data = script_brw.data.unwrap();
let refl_data = refl_data.as_ref();
reflect.bundle_insert(&mut this.0, refl_data); reflect.bundle_insert(&mut this.0, refl_data);
Ok(()) Ok(())

View File

@ -55,7 +55,7 @@ impl ScriptHost for LuaHost {
/// Runs the update step of the lua script. /// Runs the update step of the lua script.
/// ///
/// It looks for an `update` function with zero parameters in [`the ScriptContext`] and executes it. /// It looks for an `update` function with zero parameters in the [`ScriptContext`] and executes it.
fn call_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData, fn call_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData,
ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>,
function_name: &str) -> Result<(), ScriptError> { function_name: &str) -> Result<(), ScriptError> {

View File

@ -1,5 +1,7 @@
use std::ptr::NonNull;
use anyhow::anyhow; use anyhow::anyhow;
use lyra_ecs::{query::{Entities, ResMut, View}, World}; use lyra_ecs::{query::{Entities, ResMut, View}, CommandQueue, Commands, World};
use lyra_game::{game::GameStages, plugin::Plugin}; use lyra_game::{game::GameStages, plugin::Plugin};
use lyra_reflect::TypeRegistry; use lyra_reflect::TypeRegistry;
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
@ -10,12 +12,15 @@ use crate::{GameScriptExt, ScriptApiProviders, ScriptContexts, ScriptData, Scrip
use super::{providers::{LyraEcsApiProvider, LyraMathApiProvider, UtilityApiProvider}, LuaContext, LuaHost, LuaLoader, LuaScript}; use super::{providers::{LyraEcsApiProvider, LyraMathApiProvider, UtilityApiProvider}, LuaContext, LuaHost, LuaLoader, LuaScript};
/// A system that creates the script contexts in the world as new scripts are found /// A system that creates the script contexts in the world as new scripts are found
pub fn lua_scripts_create_contexts( pub fn lua_scripts_create_contexts<'a>(
world: &mut World,
mut host: ResMut<LuaHost>, mut host: ResMut<LuaHost>,
mut contexts: ResMut<ScriptContexts<LuaContext>>, mut contexts: ResMut<ScriptContexts<LuaContext>>,
mut providers: ResMut<ScriptApiProviders<LuaHost>>, mut providers: ResMut<ScriptApiProviders<LuaHost>>,
view: View<(Entities, &ScriptList<LuaScript>)>, view: View<(Entities, &ScriptList<LuaScript>)>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
world.add_resource_default::<CommandQueue>();
for (en, scripts) in view.into_iter() { for (en, scripts) in view.into_iter() {
for script in scripts.iter() { for script in scripts.iter() {
if !contexts.has_context(script.id()) { if !contexts.has_context(script.id()) {
@ -39,6 +44,7 @@ pub fn lua_scripts_create_contexts(
trace!("Finished setting up script"); trace!("Finished setting up script");
contexts.add_context(script.id(), script_ctx); contexts.add_context(script.id(), script_ctx);
break;
} else { } else {
trace!("Script is not loaded yet, skipping for now"); trace!("Script is not loaded yet, skipping for now");
} }
@ -46,6 +52,14 @@ pub fn lua_scripts_create_contexts(
} }
} }
let mut ptr = NonNull::from(world);
let world = unsafe { ptr.as_ref() };
let mut queue = world.get_resource_mut::<CommandQueue>();
let mut commands = Commands::new(&mut queue, unsafe { ptr.as_mut() });
commands.execute(unsafe { ptr.as_mut() }).unwrap();
//commands.execute(unsafe { world_ptr.as_mut() } )?;
Ok(()) Ok(())
} }

View File

@ -1,11 +1,15 @@
use std::{ptr::NonNull, sync::Arc}; use std::{ptr::NonNull, sync::Arc};
use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptEntity, ScriptWorldPtr};
use elua::AsLua; use elua::AsLua;
use lyra_ecs::query::dynamic::QueryDynamicType; use lyra_ecs::{query::dynamic::QueryDynamicType, Commands, DynamicBundle};
use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType}; use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
use crate::{ScriptWorldPtr, ScriptEntity, ScriptDynamicBundle, ScriptBorrow}; use lyra_resource::ResourceManager;
use super::{reflect_user_data, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_REFLECT_TYPE}; use super::{
reflect_user_data, wrappers::LuaResHandle, DynamicViewIter, LuaTableProxyLookup,
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 {
fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> { fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
@ -22,7 +26,10 @@ impl elua::Userdata for ScriptEntity {
"Entity".to_string() "Entity".to_string()
} }
fn build<'a>(_: &elua::State, builder: &mut elua::userdata::UserdataBuilder<'a, Self>) -> elua::Result<()> { fn build<'a>(
_: &elua::State,
builder: &mut elua::userdata::UserdataBuilder<'a, Self>,
) -> elua::Result<()> {
builder.meta_method(elua::MetaMethod::ToString, |_, this, ()| { builder.meta_method(elua::MetaMethod::ToString, |_, this, ()| {
Ok(format!("{:?}", this.0)) Ok(format!("{:?}", this.0))
}); });
@ -52,18 +59,27 @@ impl elua::Userdata for ScriptWorldPtr {
"World".to_string() "World".to_string()
} }
fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { fn build<'a>(
_: &elua::State,
builder: &mut elua::UserdataBuilder<'a, Self>,
) -> elua::Result<()> {
builder builder
.method_mut("spawn", |_, this, bundle: ScriptDynamicBundle| { .method_mut("spawn", |_, this, bundle: ScriptDynamicBundle| {
let world = this.as_mut(); 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)| { .method_mut(
"view",
|lua, this, (system, queries): (elua::Function, elua::ValueVec)| {
if queries.is_empty() { if queries.is_empty() {
return Err( return Err(elua::Error::BadArgument {
elua::Error::BadArgument { func: Some("World:view".to_string()), arg_index: 2, arg_name: Some("query...".to_string()), func: Some("World:view".to_string()),
error: Arc::new(elua::Error::other(WorldError::LuaInvalidUsage("no component types provided".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(),
))),
}); });
} }
@ -75,32 +91,39 @@ impl elua::Userdata for ScriptWorldPtr {
elua::Value::Table(t) => { elua::Value::Table(t) => {
let name: String = t.get(elua::MetaMethod::Name)?; let name: String = t.get(elua::MetaMethod::Name)?;
let lookup = world.try_get_resource::<LuaTableProxyLookup>() let lookup = world
.ok_or(elua::Error::runtime("Unable to lookup table proxy, none were ever registered!"))?; .try_get_resource::<LuaTableProxyLookup>()
let info = lookup.comp_info_from_name.get(&name) .ok_or(elua::Error::runtime(
.ok_or_else(|| "Unable to lookup table proxy, none were ever registered!",
elua::Error::BadArgument { ))?;
let info = lookup.comp_info_from_name.get(&name).ok_or_else(
|| elua::Error::BadArgument {
func: Some("World:view".to_string()), func: Some("World:view".to_string()),
arg_index: 2 + idx as i32, arg_index: 2 + idx as i32,
arg_name: Some("query...".to_string()), arg_name: Some("query...".to_string()),
error: Arc::new( error: Arc::new(elua::Error::Runtime(format!(
elua::Error::Runtime(format!("the 'Table' with name {} is unknown to the engine!", name)) "the 'Table' with name {} is unknown to the engine!",
) name
} ))),
},
)?; )?;
let dyn_type = QueryDynamicType::from_info(info.clone()); let dyn_type = QueryDynamicType::from_info(info.clone());
view.push(dyn_type); view.push(dyn_type);
}, }
elua::Value::Userdata(ud) => { elua::Value::Userdata(ud) => {
let reflect = ud.execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) 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!() _ => todo!(),
} }
} }
@ -115,10 +138,13 @@ impl elua::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.comp_val, r.comp_ptr.cast::<()>())) .map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let (values, ptrs) = itertools::multiunzip::<(Vec<elua::Value>, Vec<NonNull<()>>), _>(r); let (values, ptrs) =
itertools::multiunzip::<(Vec<elua::Value>, Vec<NonNull<()>>), _>(r);
let mult_val = elua::ValueVec::from(values); let mult_val = elua::ValueVec::from(values);
let res: elua::ValueVec = system.exec(mult_val)?; let res: elua::ValueVec = system.exec(mult_val)?;
@ -134,17 +160,19 @@ impl elua::Userdata for ScriptWorldPtr {
let lua_typeid = match &comp { let lua_typeid = match &comp {
elua::Value::Userdata(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();
refl_comp.info.type_id.as_rust() refl_comp.info.type_id.as_rust()
}, }
elua::Value::Table(tbl) => { elua::Value::Table(tbl) => {
let name: String = tbl.get(elua::MetaMethod::Name)?; let name: String = tbl.get(elua::MetaMethod::Name)?;
let lookup = world.get_resource::<LuaTableProxyLookup>(); let lookup = world.get_resource::<LuaTableProxyLookup>();
*lookup.typeid_from_name.get(&name).unwrap() *lookup.typeid_from_name.get(&name).unwrap()
}, }
_ => { _ => {
panic!("A userdata or table value was not returned!"); // TODO: Handle properly panic!("A userdata or table value was not returned!");
// TODO: Handle properly
} }
}; };
@ -159,27 +187,31 @@ impl elua::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>() let proxy = reg_type
.get_data::<ReflectLuaProxy>()
// this should actually be safe since the ReflectedIterator // this should actually be safe since the ReflectedIterator
// attempts to get the type data before it is tried here // attempts to get the type data before it is tried here
.expect("Type does not have ReflectLuaProxy as a TypeData"); .expect("Type does not have ReflectLuaProxy as a TypeData");
(proxy.fn_apply)(lua, ptr, &comp)?; (proxy.fn_apply)(lua, ptr, &comp)?;
} }
} else { } else {
let msg = format!("Too many arguments were returned from the World view! let msg = format!(
At most, the expected number of results is {}.", ptrs.len()); "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)); return Err(elua::Error::Runtime(msg));
} }
} }
Ok(()) Ok(())
}) },
)
.method_mut("resource", |lua, this, (ty,): (elua::Value,)| { .method_mut("resource", |lua, this, (ty,): (elua::Value,)| {
let reflect = match ty { let reflect = match ty {
elua::Value::Userdata(ud) => { elua::Value::Userdata(ud) => ud
ud.execute_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"),
}
elua::Value::Table(t) => { elua::Value::Table(t) => {
let f: elua::Function = t.get(FN_NAME_INTERNAL_REFLECT_TYPE)?; let f: elua::Function = t.get(FN_NAME_INTERNAL_REFLECT_TYPE)?;
f.exec::<_, ScriptBorrow>(()) f.exec::<_, ScriptBorrow>(())
@ -195,17 +227,26 @@ impl elua::Userdata for ScriptWorldPtr {
let res = reflect.reflect_branch.as_resource_unchecked(); let res = reflect.reflect_branch.as_resource_unchecked();
if let Some(res_ptr) = res.reflect_ptr(this.as_mut()) { if let Some(res_ptr) = res.reflect_ptr(this.as_mut()) {
let reg_type = this.as_ref().get_type::<RegisteredType>(reflect.reflect_branch.reflect_type_id()) let reg_type = this
.as_ref()
.get_type::<RegisteredType>(reflect.reflect_branch.reflect_type_id())
.unwrap(); .unwrap();
let proxy = reg_type.get_data::<ReflectLuaProxy>() let proxy = reg_type
.get_data::<ReflectLuaProxy>()
.expect("Type does not have ReflectLuaProxy as a TypeData"); .expect("Type does not have ReflectLuaProxy as a TypeData");
(proxy.fn_as_lua)(lua, res_ptr.cast()) (proxy.fn_as_lua)(lua, res_ptr.cast()).and_then(|ud| ud.as_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(elua::Value::Nil) Ok(elua::Value::Nil)
} }
})
.method_mut("request_res", |_, this, path: String| {
let world = this.as_mut();
let mut man = world.get_resource_mut::<ResourceManager>();
let handle = man.request_raw(&path).unwrap();
Ok(LuaResHandle::from(handle))
}); });
Ok(()) Ok(())

View File

@ -3,3 +3,9 @@ pub use math::*;
pub mod delta_time; pub mod delta_time;
pub use delta_time::*; pub use delta_time::*;
pub mod res_handle;
pub use res_handle::*;
pub mod model_comp;
pub use model_comp::*;

View File

@ -0,0 +1,70 @@
use std::any::TypeId;
use std::{cell::Ref, sync::Arc};
use elua::{AsLua, FromLua};
use lyra_game::scene::ModelComponent;
use lyra_reflect::Reflect;
use lyra_resource::{Model, ResHandle};
use crate::lua::LuaWrapper;
use crate::lyra_engine;
use crate::{lua::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow};
use super::LuaResHandle;
#[derive(Clone, Reflect)]
pub struct LuaModelComponent(ModelComponent);
impl elua::Userdata for LuaModelComponent {
fn name() -> String {
"ModelComponent".to_string()
}
fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> {
builder
.function("new", |_, model: Ref<LuaResHandle>| {
let res = model.0.clone();
let res_any = res.as_arc_any();
match res_any.downcast::<ResHandle<Model>>() {
Ok(handle) => {
let res = ResHandle::<Model>::clone(&handle);
Ok(Self(ModelComponent(res)))
},
Err(_) => {
Err(elua::Error::BadArgument {
func: Some("ModelComponent:new".to_string()),
arg_index: 1,
arg_name: Some("model".to_string()),
error: Arc::new(
elua::Error::runtime("resource handle is not a handle to a Model")
)
})
}
}
})
.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(ScriptBorrow::from_component::<ModelComponent>(None))
}).method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(ScriptBorrow::from_component(Some(this.0.clone())))
});
Ok(())
}
}
impl<'a> FromLua<'a> for LuaModelComponent {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("Model", &tyname))?;
let ud = ud.as_ref::<LuaModelComponent>()?;
Ok(ud.clone())
}
}
impl LuaWrapper for LuaModelComponent {
fn wrapped_type_id() -> std::any::TypeId {
TypeId::of::<ModelComponent>()
}
}

View File

@ -0,0 +1,62 @@
use std::{ops::Deref, sync::Arc};
use elua::FromLua;
use lyra_resource::ResourceStorage;
#[derive(Clone)]
pub struct LuaResHandle(pub Arc<dyn ResourceStorage>);
impl Deref for LuaResHandle {
type Target = dyn ResourceStorage;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl From<Arc<dyn ResourceStorage>> for LuaResHandle {
fn from(value: Arc<dyn ResourceStorage>) -> Self {
LuaResHandle(value)
}
}
impl elua::Userdata for LuaResHandle {
fn name() -> String {
"Handle".to_string()
}
fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> {
builder.field_getter("path", |_, this| Ok(this.path()));
builder.field_getter("version", |_, this| Ok(this.version()));
builder.field_getter("uuid", |_, this| Ok(this.uuid().to_string()));
builder.field_getter("state", |_, this| {
let name = match this.state() {
lyra_resource::ResourceState::Loading => "loading",
lyra_resource::ResourceState::Ready => "ready",
};
Ok(name)
});
builder.method("is_watched", |_, this, ()| {
Ok(this.is_watched())
});
builder.method("is_loaded", |_, this, ()| {
Ok(this.is_loaded())
});
Ok(())
}
}
impl<'a> FromLua<'a> for LuaResHandle {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("Handle", &tyname))?;
let handle = ud.as_ref::<LuaResHandle>()?;
Ok(handle.clone())
}
}