diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 7c2af70..75e8dd5 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -1,8 +1,13 @@ ---[[ function on_init() - print("Lua script was initialized!") +--local cube: resource = world:request_res("loader", "assets/cube-texture-embedded.gltf") + +--cube = nil + +function on_init() + local cube = world:request_res("assets/cube-texture-embedded.gltf") + print("Loaded textured cube") end -function on_first() +--[[ function on_first() print("Lua's first function was called") end @@ -14,11 +19,6 @@ function on_update() ---@type number 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) t:translate(0, 0.5 * dt, 0) diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 22b6d21..70e2985 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 22b6d218bdb6a5aafc0efd4fb80a67d2c796d0e7 +Subproject commit 70e2985cc44fdb30cdf2157c50d2f0e3385e08fa diff --git a/lyra-scripting/src/host.rs b/lyra-scripting/src/host.rs index bcca5ba..d51056b 100644 --- a/lyra-scripting/src/host.rs +++ b/lyra-scripting/src/host.rs @@ -89,10 +89,10 @@ pub trait ScriptHost: Default + ResourceObject { fn setup_script(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext, providers: &mut ScriptApiProviders) -> 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, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders, - function_name: &str) -> Result<(), ScriptError>; + event_fn_name: &str) -> Result<(), ScriptError>; } #[derive(Default)] diff --git a/lyra-scripting/src/lib.rs b/lyra-scripting/src/lib.rs index 4b5b42f..4ba4a98 100644 --- a/lyra-scripting/src/lib.rs +++ b/lyra-scripting/src/lib.rs @@ -76,8 +76,8 @@ impl ReflectBranch { } pub struct ScriptBorrow { - reflect_branch: ReflectBranch, - data: Option>, + pub(crate) reflect_branch: ReflectBranch, + pub(crate) data: Option>, } impl Clone for ScriptBorrow { @@ -95,7 +95,7 @@ impl ScriptBorrow { /// Creates a ScriptBorrow from a Component pub fn from_component(data: Option) -> Self where - T: Reflect + Component + Default + 'static + T: Reflect + Component + '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. pub fn from_resource(data: Option) -> Self where - T: Reflect + ResourceObject + Default + 'static + T: Reflect + ResourceObject + 'static { let data = data.map(|d| Box::new(d) as Box<(dyn Reflect + 'static)>); diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index 7b3cb2b..dd72a0e 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -1,7 +1,7 @@ use lyra_ecs::ResourceObject; 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)] pub struct LyraEcsApiProvider; @@ -11,6 +11,7 @@ impl ScriptApiProvider for LyraEcsApiProvider { fn prepare_world(&mut self, world: &mut lyra_ecs::World) { world.register_lua_convert::(); + world.register_lua_wrapper::(); } 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()?; globals.set("World", ctx.create_proxy::()?)?; globals.set("DynamicBundle", ctx.create_proxy::()?)?; + globals.set("ModelComponent", ctx.create_proxy::()?)?; let dt_table = create_reflect_table::(&ctx)?; globals.set("DeltaTime", dt_table)?; diff --git a/lyra-scripting/src/lua/proxy.rs b/lyra-scripting/src/lua/proxy.rs index 8372cb5..1362f4e 100644 --- a/lyra-scripting/src/lua/proxy.rs +++ b/lyra-scripting/src/lua/proxy.rs @@ -171,7 +171,6 @@ impl elua::Userdata for ScriptDynamicBundle { 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(()) diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index 3e4bca7..e344a59 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -55,7 +55,7 @@ impl ScriptHost for LuaHost { /// 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, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders, function_name: &str) -> Result<(), ScriptError> { diff --git a/lyra-scripting/src/lua/system.rs b/lyra-scripting/src/lua/system.rs index 4a5f335..ea4fbfa 100644 --- a/lyra-scripting/src/lua/system.rs +++ b/lyra-scripting/src/lua/system.rs @@ -1,5 +1,7 @@ +use std::ptr::NonNull; + 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_reflect::TypeRegistry; 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}; /// 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, mut contexts: ResMut>, mut providers: ResMut>, view: View<(Entities, &ScriptList)>, ) -> anyhow::Result<()> { + world.add_resource_default::(); + for (en, scripts) in view.into_iter() { for script in scripts.iter() { if !contexts.has_context(script.id()) { @@ -39,6 +44,7 @@ pub fn lua_scripts_create_contexts( trace!("Finished setting up script"); contexts.add_context(script.id(), script_ctx); + break; } else { 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::(); + + 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(()) } diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 00c101c..20e8c52 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,11 +1,15 @@ use std::{ptr::NonNull, sync::Arc}; +use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptEntity, ScriptWorldPtr}; use elua::AsLua; -use lyra_ecs::query::dynamic::QueryDynamicType; -use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType}; -use crate::{ScriptWorldPtr, ScriptEntity, ScriptDynamicBundle, ScriptBorrow}; +use lyra_ecs::{query::dynamic::QueryDynamicType, Commands, DynamicBundle}; +use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry}; +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 { fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { @@ -22,7 +26,10 @@ impl elua::Userdata for ScriptEntity { "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, ()| { Ok(format!("{:?}", this.0)) }); @@ -52,134 +59,159 @@ impl elua::Userdata for ScriptWorldPtr { "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 .method_mut("spawn", |_, this, bundle: ScriptDynamicBundle| { let world = this.as_mut(); Ok(ScriptEntity(world.spawn(bundle.0))) }) - .method_mut("view", |lua, this, (system, queries): (elua::Function, elua::ValueVec)| { - if queries.is_empty() { - return Err( - elua::Error::BadArgument { func: Some("World:view".to_string()), arg_index: 2, arg_name: Some("query...".to_string()), - error: Arc::new(elua::Error::other(WorldError::LuaInvalidUsage("no component types provided".to_string()))) - }); - } - - let world = unsafe { this.inner.as_ref() }; - let mut view = world.dynamic_view(); + .method_mut( + "view", + |lua, this, (system, queries): (elua::Function, elua::ValueVec)| { + if queries.is_empty() { + return Err(elua::Error::BadArgument { + func: Some("World:view".to_string()), + arg_index: 2, + arg_name: Some("query...".to_string()), + error: Arc::new(elua::Error::other(WorldError::LuaInvalidUsage( + "no component types provided".to_string(), + ))), + }); + } - for (idx, comp) in queries.into_iter().enumerate() { - match comp { - elua::Value::Table(t) => { - let name: String = t.get(elua::MetaMethod::Name)?; + let world = unsafe { this.inner.as_ref() }; + let mut view = world.dynamic_view(); - let lookup = world.try_get_resource::() - .ok_or(elua::Error::runtime("Unable to lookup table proxy, none were ever registered!"))?; - let info = lookup.comp_info_from_name.get(&name) - .ok_or_else(|| - elua::Error::BadArgument { + for (idx, comp) in queries.into_iter().enumerate() { + match comp { + elua::Value::Table(t) => { + let name: String = t.get(elua::MetaMethod::Name)?; + + let lookup = world + .try_get_resource::() + .ok_or(elua::Error::runtime( + "Unable to lookup table proxy, none were ever registered!", + ))?; + 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)) - ) - } + error: Arc::new(elua::Error::Runtime(format!( + "the 'Table' with name {} is unknown to the engine!", + name + ))), + }, )?; - - let dyn_type = QueryDynamicType::from_info(info.clone()); - view.push(dyn_type); - }, - elua::Value::Userdata(ud) => { - let reflect = ud.execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) - .expect("Type does not implement 'reflect_type' properly"); - let refl_comp = reflect.reflect_branch.as_component_unchecked(); - let dyn_type = QueryDynamicType::from_info(refl_comp.info); - view.push(dyn_type); - }, - _ => todo!() - } - } + let dyn_type = QueryDynamicType::from_info(info.clone()); + view.push(dyn_type); + } + elua::Value::Userdata(ud) => { + let reflect = ud + .execute_function::<_, ScriptBorrow>( + FN_NAME_INTERNAL_REFLECT_TYPE, + (), + ) + .expect("Type does not implement 'reflect_type' properly"); + let refl_comp = reflect.reflect_branch.as_component_unchecked(); - let iter = view.into_iter(); - let mut reflected_iter = ReflectedIterator { - world: this.clone(), - dyn_view: DynamicViewIter::from(iter), - reflected_components: None, - }; - - let mut current = world.current_tick(); - let mut has_ticked = false; - - while let Some(row) = reflected_iter.next_lua(lua) { - let r = row.row.into_iter() - .map(|r| (r.comp_val, r.comp_ptr.cast::<()>())) - .collect::>(); - let (values, ptrs) = itertools::multiunzip::<(Vec, Vec>), _>(r); - let mult_val = elua::ValueVec::from(values); - let res: elua::ValueVec = system.exec(mult_val)?; - - // if values were returned, find the type in the type registry, and apply the new values - if res.len() <= ptrs.len() { - // we only want to tick one time per system - if !has_ticked { - current = world.tick(); - has_ticked = true; + let dyn_type = QueryDynamicType::from_info(refl_comp.info); + view.push(dyn_type); + } + _ => todo!(), } - - for (comp, ptr) in res.into_iter().zip(ptrs) { - let lua_typeid = match &comp { - elua::Value::Userdata(ud) => { - let lua_comp = reflect_user_data(ud); - let refl_comp = lua_comp.reflect_branch.as_component_unchecked(); - refl_comp.info.type_id.as_rust() - }, - elua::Value::Table(tbl) => { - let name: String = tbl.get(elua::MetaMethod::Name)?; - - let lookup = world.get_resource::(); - *lookup.typeid_from_name.get(&name).unwrap() - }, - _ => { - panic!("A userdata or table value was not returned!"); // TODO: Handle properly - } - }; - - // 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(lua_typeid.into()).unwrap(); - c.entity_ticks[idx.0 as usize] = current; - - // apply the new component data - let reg = this.as_ref().get_resource::(); - let reg_type = reg.get_type(lua_typeid).unwrap(); - - let proxy = reg_type.get_data::() - // this should actually be safe since the ReflectedIterator - // attempts to get the type data before it is tried here - .expect("Type does not have ReflectLuaProxy as a TypeData"); - (proxy.fn_apply)(lua, ptr, &comp)?; - } - } else { - let msg = format!("Too many arguments were returned from the World view! - At most, the expected number of results is {}.", ptrs.len()); - return Err(elua::Error::Runtime(msg)); } - } - Ok(()) - }) + let iter = view.into_iter(); + let mut reflected_iter = ReflectedIterator { + world: this.clone(), + dyn_view: DynamicViewIter::from(iter), + reflected_components: None, + }; + + let mut current = world.current_tick(); + let mut has_ticked = false; + + while let Some(row) = reflected_iter.next_lua(lua) { + let r = row + .row + .into_iter() + .map(|r| (r.comp_val, r.comp_ptr.cast::<()>())) + .collect::>(); + let (values, ptrs) = + itertools::multiunzip::<(Vec, Vec>), _>(r); + let mult_val = elua::ValueVec::from(values); + let res: elua::ValueVec = system.exec(mult_val)?; + + // if values were returned, find the type in the type registry, and apply the new values + if res.len() <= ptrs.len() { + // we only want to tick one time per system + if !has_ticked { + current = world.tick(); + has_ticked = true; + } + + for (comp, ptr) in res.into_iter().zip(ptrs) { + let lua_typeid = match &comp { + elua::Value::Userdata(ud) => { + let lua_comp = reflect_user_data(ud); + let refl_comp = + lua_comp.reflect_branch.as_component_unchecked(); + refl_comp.info.type_id.as_rust() + } + elua::Value::Table(tbl) => { + let name: String = tbl.get(elua::MetaMethod::Name)?; + + let lookup = world.get_resource::(); + *lookup.typeid_from_name.get(&name).unwrap() + } + _ => { + panic!("A userdata or table value was not returned!"); + // TODO: Handle properly + } + }; + + // 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(lua_typeid.into()).unwrap(); + c.entity_ticks[idx.0 as usize] = current; + + // apply the new component data + let reg = this.as_ref().get_resource::(); + let reg_type = reg.get_type(lua_typeid).unwrap(); + + let proxy = reg_type + .get_data::() + // this should actually be safe since the ReflectedIterator + // attempts to get the type data before it is tried here + .expect("Type does not have ReflectLuaProxy as a TypeData"); + (proxy.fn_apply)(lua, ptr, &comp)?; + } + } else { + let msg = format!( + "Too many arguments were returned from the World view! + At most, the expected number of results is {}.", + ptrs.len() + ); + return Err(elua::Error::Runtime(msg)); + } + } + + Ok(()) + }, + ) .method_mut("resource", |lua, this, (ty,): (elua::Value,)| { let reflect = match ty { - elua::Value::Userdata(ud) => { - ud.execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) - .expect("Type does not implement 'reflect_type' properly") - } + elua::Value::Userdata(ud) => ud + .execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) + .expect("Type does not implement 'reflect_type' properly"), elua::Value::Table(t) => { let f: elua::Function = t.get(FN_NAME_INTERNAL_REFLECT_TYPE)?; f.exec::<_, ScriptBorrow>(()) @@ -190,24 +222,33 @@ impl elua::Userdata for ScriptWorldPtr { } }; /* let reflect = ty - .execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) - .expect("Type does not implement 'reflect_type' properly"); */ - + .execute_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) + .expect("Type does not implement 'reflect_type' properly"); */ + let res = reflect.reflect_branch.as_resource_unchecked(); if let Some(res_ptr) = res.reflect_ptr(this.as_mut()) { - let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) + let reg_type = this + .as_ref() + .get_type::(reflect.reflect_branch.reflect_type_id()) .unwrap(); - let proxy = reg_type.get_data::() + let proxy = reg_type + .get_data::() .expect("Type does not have ReflectLuaProxy as a TypeData"); - (proxy.fn_as_lua)(lua, res_ptr.cast()) - .and_then(|ud| ud.as_lua(lua)) + (proxy.fn_as_lua)(lua, res_ptr.cast()).and_then(|ud| ud.as_lua(lua)) } else { // if the resource is not found in the world, return nil Ok(elua::Value::Nil) } + }) + .method_mut("request_res", |_, this, path: String| { + let world = this.as_mut(); + let mut man = world.get_resource_mut::(); + let handle = man.request_raw(&path).unwrap(); + + Ok(LuaResHandle::from(handle)) }); Ok(()) } -} \ No newline at end of file +} diff --git a/lyra-scripting/src/lua/wrappers/mod.rs b/lyra-scripting/src/lua/wrappers/mod.rs index 1fd0c6b..244419d 100644 --- a/lyra-scripting/src/lua/wrappers/mod.rs +++ b/lyra-scripting/src/lua/wrappers/mod.rs @@ -2,4 +2,10 @@ pub mod math; pub use math::*; pub mod delta_time; -pub use delta_time::*; \ No newline at end of file +pub use delta_time::*; + +pub mod res_handle; +pub use res_handle::*; + +pub mod model_comp; +pub use model_comp::*; \ No newline at end of file diff --git a/lyra-scripting/src/lua/wrappers/model_comp.rs b/lyra-scripting/src/lua/wrappers/model_comp.rs new file mode 100644 index 0000000..d9445d9 --- /dev/null +++ b/lyra-scripting/src/lua/wrappers/model_comp.rs @@ -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| { + let res = model.0.clone(); + let res_any = res.as_arc_any(); + match res_any.downcast::>() { + Ok(handle) => { + let res = ResHandle::::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::(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 { + let tyname = val.type_name(); + let ud = val.as_userdata() + .ok_or(elua::Error::type_mismatch("Model", &tyname))?; + let ud = ud.as_ref::()?; + + Ok(ud.clone()) + } +} + +impl LuaWrapper for LuaModelComponent { + fn wrapped_type_id() -> std::any::TypeId { + TypeId::of::() + } +} \ No newline at end of file diff --git a/lyra-scripting/src/lua/wrappers/res_handle.rs b/lyra-scripting/src/lua/wrappers/res_handle.rs new file mode 100644 index 0000000..c3f6d0e --- /dev/null +++ b/lyra-scripting/src/lua/wrappers/res_handle.rs @@ -0,0 +1,62 @@ +use std::{ops::Deref, sync::Arc}; + +use elua::FromLua; +use lyra_resource::ResourceStorage; + +#[derive(Clone)] +pub struct LuaResHandle(pub Arc); + +impl Deref for LuaResHandle { + type Target = dyn ResourceStorage; + + fn deref(&self) -> &Self::Target { + self.0.deref() + } +} + +impl From> for LuaResHandle { + fn from(value: Arc) -> 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 { + let tyname = val.type_name(); + let ud = val.as_userdata() + .ok_or(elua::Error::type_mismatch("Handle", &tyname))?; + let handle = ud.as_ref::()?; + + Ok(handle.clone()) + } +} \ No newline at end of file