diff --git a/Cargo.lock b/Cargo.lock index b503c59..eb90419 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1803,6 +1803,18 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lua-scripting" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-std", + "fps_counter", + "lyra-engine", + "rand 0.8.5", + "tracing", +] + [[package]] name = "lyra-ecs" version = "0.1.0" @@ -1927,6 +1939,7 @@ dependencies = [ "anyhow", "lyra-ecs", "lyra-math", + "lyra-reflect", ] [[package]] @@ -1934,6 +1947,7 @@ name = "lyra-scripting" version = "0.1.0" dependencies = [ "anyhow", + "atomic_refcell", "elua", "itertools 0.12.0", "lyra-ecs", @@ -1941,6 +1955,7 @@ dependencies = [ "lyra-reflect", "lyra-resource", "lyra-scripting-derive", + "paste", "thiserror", "tracing", "tracing-subscriber", @@ -1950,6 +1965,7 @@ dependencies = [ name = "lyra-scripting-derive" version = "0.1.0" dependencies = [ + "paste", "proc-macro2", "quote", "syn 2.0.51", diff --git a/Cargo.toml b/Cargo.toml index 5a25f9b..8c361d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,14 @@ members = [ "lyra-ecs", "lyra-reflect", "lyra-scripting", - "lyra-game", "lyra-math", "lyra-scene", "examples/many-lights", "examples/fixed-timestep-rotating-model"] + "lyra-game", + "lyra-math", + "lyra-scene", + + "examples/many-lights", + "examples/fixed-timestep-rotating-model", + "examples/lua-scripting" +] [features] scripting = ["dep:lyra-scripting"] diff --git a/examples/lua-scripting/Cargo.toml b/examples/lua-scripting/Cargo.toml new file mode 100644 index 0000000..41cedba --- /dev/null +++ b/examples/lua-scripting/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "lua-scripting" +version = "0.1.0" +edition = "2021" + +[dependencies] +lyra-engine = { path = "../../", features = ["tracy", "lua_scripting"] } +anyhow = "1.0.75" +async-std = "1.12.0" +tracing = "0.1.37" +rand = "0.8.5" +fps_counter = "3.0.0" + +[target.x86_64-unknown-linux-gnu] +linker = "/usr/bin/clang" +rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"] + +[profile.dev] +opt-level = 1 + +[profile.release] +debug = true \ No newline at end of file diff --git a/examples/lua-scripting/scripts/test.lua b/examples/lua-scripting/scripts/test.lua new file mode 100644 index 0000000..bac6e90 --- /dev/null +++ b/examples/lua-scripting/scripts/test.lua @@ -0,0 +1,59 @@ +---Return the userdata's name from its metatable +---@param val userdata +---@return string +function udname(val) + return getmetatable(val).__name +end + +function on_init() + local cube = world:request_res("../assets/cube-texture-embedded.gltf") + print("Loaded textured cube (" .. udname(cube) .. ")") + + cube:wait_until_loaded() + local scenes = cube:scenes() + local cube_scene = scenes[1] + + local pos = Transform.from_translation(Vec3.new(0, 0, -8.0)) + + local e = world:spawn(pos, cube_scene) + print("spawned entity " .. tostring(e)) +end + +--[[ function on_first() + print("Lua's first function was called") +end + +function on_pre_update() + print("Lua's pre-update function was called") +end ]] + +function on_update() + --[[ ---@type number + local dt = world:resource(DeltaTime) + local act = world:resource(ActionHandler) + ---@type number + local move_objs = act:get_axis("ObjectsMoveUpDown") + + world:view(function (t) + if move_objs ~= nil then + t:translate(0, move_objs * 0.35 * dt, 0) + return t + end + end, Transform) ]] + + ---@type number + local dt = world:resource(DeltaTime) + + world:view(function (t) + t:translate(0, 0.15 * dt, 0) + return t + end, Transform) +end + +--[[ function on_post_update() + print("Lua's post-update function was called") +end + +function on_last() + print("Lua's last function was called") +end ]] \ No newline at end of file diff --git a/examples/lua-scripting/src/main.rs b/examples/lua-scripting/src/main.rs new file mode 100644 index 0000000..221a812 --- /dev/null +++ b/examples/lua-scripting/src/main.rs @@ -0,0 +1,151 @@ +use std::ptr::NonNull; + +use lyra_engine::{ + assets::{gltf::Gltf, ResourceManager}, ecs::{ + query::{Res, ResMut, View}, + system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem}, + World, + }, game::Game, input::{ + Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, + InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput, + }, lua::{LuaScript, LuaScriptingPlugin}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{ + CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform, + ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, + ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN, + }, DeltaTime, Script, ScriptList +}; +use tracing::info; + +#[async_std::main] +async fn main() { + let action_handler_plugin = |game: &mut Game| { + let action_handler = ActionHandler::builder() + .add_layout(LayoutId::from(0)) + .add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis)) + .add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis)) + .add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis)) + .add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis)) + .add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis)) + .add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis)) + .add_action("Debug", Action::new(ActionKind::Button)) + .add_mapping( + ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0)) + .bind( + ACTLBL_MOVE_FORWARD_BACKWARD, + &[ + ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0), + ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0), + ], + ) + .bind( + ACTLBL_MOVE_LEFT_RIGHT, + &[ + ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0), + ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0), + ], + ) + .bind( + ACTLBL_MOVE_UP_DOWN, + &[ + ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0), + ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0), + ], + ) + .bind( + ACTLBL_LOOK_LEFT_RIGHT, + &[ + ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(), + ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0), + ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0), + ], + ) + .bind( + ACTLBL_LOOK_UP_DOWN, + &[ + ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(), + ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0), + ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0), + ], + ) + .bind( + ACTLBL_LOOK_ROLL, + &[ + ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0), + ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0), + ], + ) + .bind( + "Debug", + &[ActionSource::Keyboard(KeyCode::B).into_binding()], + ) + .finish(), + ) + .finish(); + + let world = game.world_mut(); + world.add_resource(action_handler); + game.with_plugin(InputActionPlugin); + }; + + Game::initialize() + .await + .with_plugin(lyra_engine::DefaultPlugins) + .with_plugin(setup_scene_plugin) + .with_plugin(action_handler_plugin) + .with_plugin(setup_script_plugin) + //.with_plugin(camera_debug_plugin) + .with_plugin(FreeFlyCameraPlugin) + .run() + .await; +} + +fn setup_scene_plugin(game: &mut Game) { + let world = game.world_mut(); + let resman = world.get_resource_mut::(); + let camera_gltf = resman + .request::("../assets/AntiqueCamera.glb") + .unwrap(); + + camera_gltf.wait_recurse_dependencies_load(); + let camera_mesh = &camera_gltf.data_ref().unwrap().scenes[0]; + drop(resman); + + world.spawn(( + camera_mesh.clone(), + WorldTransform::default(), + Transform::from_xyz(0.0, -5.0, -2.0), + )); + + { + let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0); + light_tran.scale = Vec3::new(0.5, 0.5, 0.5); + light_tran.rotate_x(math::Angle::Degrees(-45.0)); + light_tran.rotate_y(math::Angle::Degrees(25.0)); + world.spawn(( + DirectionalLight { + enabled: true, + color: Vec3::ONE, + intensity: 0.15, //..Default::default() + }, + light_tran, + )); + } + + let mut camera = CameraComponent::new_3d(); + camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5); + world.spawn((camera, FreeFlyCamera::default())); +} + +fn setup_script_plugin(game: &mut Game) { + game.with_plugin(LuaScriptingPlugin); + + let world = game.world_mut(); + let res_man = world.get_resource_mut::(); + let script = res_man.request::("scripts/test.lua").unwrap(); + res_man.watch("scripts/test.lua", false).unwrap(); + drop(res_man); + + let script = Script::new("test.lua", script); + let scripts = ScriptList::new(vec![script]); + world.spawn((scripts,)); +} \ No newline at end of file diff --git a/lyra-scene/Cargo.toml b/lyra-scene/Cargo.toml index 28d1c33..b41201c 100644 --- a/lyra-scene/Cargo.toml +++ b/lyra-scene/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" anyhow = "1.0.81" lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-math = { path = "../lyra-math" } +lyra-reflect = { path = "../lyra-reflect" } diff --git a/lyra-scene/src/lib.rs b/lyra-scene/src/lib.rs index 0e1b1b1..d1134fc 100644 --- a/lyra-scene/src/lib.rs +++ b/lyra-scene/src/lib.rs @@ -1,4 +1,5 @@ mod node; +use lyra_reflect::Reflect; pub use node::*; mod world_transform; @@ -12,6 +13,10 @@ pub(crate) mod lyra_engine { pub(crate) mod ecs { pub use lyra_ecs::*; } + + pub(crate) mod reflect { + pub use lyra_reflect::*; + } } /// A flag spawned on all scene node entities @@ -27,7 +32,9 @@ pub struct SceneNodeRoot; /// This SceneGraph is special in the sense that it is literally just an ECS world with methods /// implemented for it that make it easier to use for a SceneGraph. //#[derive(Default)] +#[derive(Clone, Reflect)] pub struct SceneGraph { + #[reflect(skip)] pub(crate) world: World, root_node: SceneNode, } diff --git a/lyra-scene/src/node.rs b/lyra-scene/src/node.rs index f72e3f9..8536d2f 100644 --- a/lyra-scene/src/node.rs +++ b/lyra-scene/src/node.rs @@ -1,11 +1,14 @@ use lyra_ecs::{query::Entities, relation::{ChildOf, RelationOriginComponent}, Bundle, Entity}; +use lyra_reflect::Reflect; -use crate::SceneGraph; +use crate::{SceneGraph, lyra_engine}; /// A node inside of the Scene -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq, Reflect)] pub struct SceneNode { + #[reflect(skip)] parent: Option, + #[reflect(skip)] entity: Entity } diff --git a/lyra-scripting/Cargo.toml b/lyra-scripting/Cargo.toml index 22d2bd0..d57e2ed 100644 --- a/lyra-scripting/Cargo.toml +++ b/lyra-scripting/Cargo.toml @@ -19,11 +19,13 @@ lyra-game = { path = "../lyra-game" } thiserror = "1.0.50" anyhow = "1.0.77" tracing = "0.1.37" +atomic_refcell = "0.1.13" # enabled with lua feature #mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? elua = { path = "./elua", optional = true } itertools = "0.12.0" +paste = "1.0.14" [dev-dependencies] diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 54c9926..a761f40 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 54c9926a04cdef657289fd67730c0b85d1bdda3e +Subproject commit a761f4094bc18190285b4687ec804161fea874b6 diff --git a/lyra-scripting/lyra-scripting-derive/Cargo.toml b/lyra-scripting/lyra-scripting-derive/Cargo.toml index 9595a9d..cec9b6b 100644 --- a/lyra-scripting/lyra-scripting-derive/Cargo.toml +++ b/lyra-scripting/lyra-scripting-derive/Cargo.toml @@ -12,3 +12,4 @@ proc-macro = true proc-macro2 = "1.0.70" quote = "1.0.33" syn = "2.0.41" +paste = "1.0.14" \ No newline at end of file diff --git a/lyra-scripting/lyra-scripting-derive/src/handle_macro.rs b/lyra-scripting/lyra-scripting-derive/src/handle_macro.rs new file mode 100644 index 0000000..ea50a1f --- /dev/null +++ b/lyra-scripting/lyra-scripting-derive/src/handle_macro.rs @@ -0,0 +1,149 @@ +use quote::{format_ident, quote}; +use syn::{parse_macro_input, Ident, Token}; + +pub(crate) struct HandleWrapUsage { + pub type_path: syn::Path, + /// The extra derives of the type. + pub override_name: Option, + + pub extra_builds: Option, +} + +impl syn::parse::Parse for HandleWrapUsage { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let type_path: syn::Path = input.parse()?; + let mut s = Self { + type_path, + override_name: None, + extra_builds: None, + }; + + while input.peek(Token![,]) { + let _: Token![,] = input.parse()?; + + if input.peek(syn::Ident) { + let ident: syn::Ident = input.parse()?; + let ident_str = ident.to_string(); + let ident_str = ident_str.as_str(); + + match ident_str { + "name" => { + let _eq: Token![=] = input.parse()?; + + let name: Ident = input.parse()?; + s.override_name = Some(name); + }, + _ => { + return Err(syn::Error::new_spanned(ident, "unknown wrapper command")); + } + } + }/* else if input.parse::() { + s.extra_builds = Some(block); + } + } + + Ok(s) + } +} + +pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as HandleWrapUsage); + + let handle_name = &input.type_path.segments.last().unwrap().ident; + + let base_name = input.override_name.unwrap_or_else(|| handle_name.clone()); + let wrapper_name = format_ident!("Lua{}Handle", base_name); + //let wrapper_name = Ident::new(&format!("Lua{}", handle_name.to_string()), handle_name.span()); + let ud_name = format!("{}Handle", base_name.to_string()); + + let extras = input.extra_builds; + + quote! { + #[derive(Clone, Reflect)] + pub struct #wrapper_name(pub ResHandle<#handle_name>); + + impl Deref for #wrapper_name { + type Target = ResHandle<#handle_name>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl From> for #wrapper_name { + fn from(value: ResHandle<#handle_name>) -> Self { + #wrapper_name(value) + } + } + + impl elua::Userdata for #wrapper_name { + fn name() -> String { + #ud_name.to_string() + } + + fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { + 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 = if this.is_loaded() { + "ready" + } else if this.get_error().is_some() { + "error" + } else { "loading" }; + + Ok(name) + }); + + builder.method("is_watched", |_, this, ()| { + Ok(this.is_watched()) + }); + + builder.method("is_loaded", |_, this, ()| { + Ok(this.is_loaded()) + }); + + builder.method("is_loaded", |_, this, ()| { + Ok(this.is_loaded()) + }); + + builder.method("wait_until_loaded", |_, this, ()| { + this.wait_recurse_dependencies_load(); + + Ok(()) + }); + + builder.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { + Ok(ScriptBorrow::from_component::>(None)) + }); + builder.method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { + Ok(ScriptBorrow::from_component(Some(this.0.clone()))) + }); + + #extras + } + } + + impl<'a> FromLua<'a> for #wrapper_name { + 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(#ud_name, &tyname))?; + let ud = ud.as_ref::<#wrapper_name>()?; + + Ok(ud.clone()) + } + } + + impl LuaWrapper for #wrapper_name { + fn wrapped_type_id() -> std::any::TypeId { + TypeId::of::>() + } + } + }.into() +} \ No newline at end of file diff --git a/lyra-scripting/lyra-scripting-derive/src/lib.rs b/lyra-scripting/lyra-scripting-derive/src/lib.rs index cb7d832..e2f7d8b 100644 --- a/lyra-scripting/lyra-scripting-derive/src/lib.rs +++ b/lyra-scripting/lyra-scripting-derive/src/lib.rs @@ -1,3 +1,5 @@ +use handle_macro::lua_wrap_handle_impl; +use lua_macro::wrap_lua_struct_impl; use proc_macro2::{Ident, Span}; use quote::quote; use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced}; @@ -5,8 +7,14 @@ use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punc mod mat_wrapper; use mat_wrapper::MatWrapper; -const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; -const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; +mod lua_macro; + +mod handle_macro; + +// pub use lua_macro::wrap_lua_struct; + +pub(crate) const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; +pub(crate) const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; pub(crate) struct MetaMethod { pub ident: Ident, @@ -720,4 +728,14 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt } } }) +} + +#[proc_macro] +pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + wrap_lua_struct_impl(input) +} + +#[proc_macro] +pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + lua_wrap_handle_impl(input) } \ No newline at end of file diff --git a/lyra-scripting/lyra-scripting-derive/src/lua_macro.rs b/lyra-scripting/lyra-scripting-derive/src/lua_macro.rs new file mode 100644 index 0000000..80a1a52 --- /dev/null +++ b/lyra-scripting/lyra-scripting-derive/src/lua_macro.rs @@ -0,0 +1,164 @@ +use proc_macro2::Span; +use quote::quote; +use syn::{parse_macro_input, Path, Ident}; + +use crate::{VecWrapper, WrapUsage, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}; + +/// Creates a wrapper type for a VecN from the engine math library. +pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as WrapUsage); + + let path: Path = input.type_path; + let type_name = &path.segments.last() + .expect("Failure to find typename in macro usage!") + .ident; + let wrapper_typename = Ident::new(&format!("Lua{}", type_name), Span::call_site()); + + let vec_wrapper = { + let name_str = type_name.to_string(); + if name_str.contains("Vec") { + Some(VecWrapper {}) + } else { + None + } + }; + // TODO: fix this so it doesn't cause a stack overflow + /* let vec_wrapper_fields = vec_wrapper.as_ref().map(|vec| + vec.to_field_tokens(&path, &wrapper_typename)); */ + let vec_wrapper_fields: Option = None; + let vec_wrapper_methods = vec_wrapper.as_ref().map(|vec| + vec.to_method_tokens(&path, &wrapper_typename)); + + let derive_idents_iter = input.derive_idents.iter(); + let custom_methods = input.custom_methods; + let custom_fields = input.custom_fields; + + let field_get_set_pairs = input.field_idents.iter().map(|i| { + let is = i.to_string(); + quote! { + builder.field_getter(#is, |_, this| { + Ok(this.#i) + }); + builder.field_setter(#is, |_, this, #i| { + this.#i = #i; + Ok(()) + }); + } + }); + + let new_fn_idents = { + let idents = if input.new_fn_idents.is_empty() { + input.field_idents.iter() + } else { + input.new_fn_idents.iter() + }; + + let idents_c = idents.clone(); + + if !input.skip_new_fn { + quote! { + builder.function("new", |_, ( #(#idents_c),* )| { + Ok(#wrapper_typename(#path::new( #(#idents),* ))) + }); + } + } else { quote!() } + }; + + let matrix_wrapper_methods = input.matrix.as_ref().map(|m| + m.to_method_tokens(&path, &wrapper_typename)); + let matrix_wrapper_fields = input.matrix.as_ref().map(|m| + m.to_field_tokens(&path, &wrapper_typename)); + + let meta_method_idents = { + let idents = input.meta_method_idents.iter().map(|metamethod| { + metamethod.to_tokens(&wrapper_typename) + }); + + quote! { + #(#idents)* + } + }; + + proc_macro::TokenStream::from(quote! { + #[derive(Clone, Copy, lyra_reflect::Reflect, #(#derive_idents_iter),*)] + pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path); + + impl std::ops::Deref for #wrapper_typename { + type Target = #path; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl std::ops::DerefMut for #wrapper_typename { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + impl<'lua> elua::FromLua<'lua> for #wrapper_typename { + fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { + match value { + elua::Value::Userdata(ud) => Ok(*ud.as_ref::()?), + _ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()), + } + } + } + + impl elua::Userdata for #wrapper_typename { + fn name() -> String { + stringify!(#type_name).to_string() + } + + fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { + #(#field_get_set_pairs)* + + #matrix_wrapper_fields + #vec_wrapper_fields + + #custom_fields + + #new_fn_idents + + builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { + Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone()))) + }); + + builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { + Ok(crate::ScriptBorrow::from_component::<#path>(None)) + }); + + #meta_method_idents + + #matrix_wrapper_methods + #vec_wrapper_methods + + #custom_methods + } + } + + impl lyra_scripting::lua::LuaWrapper for #wrapper_typename { + fn wrapped_type_id() -> std::any::TypeId { + let t = std::any::TypeId::of::<#path>(); + println!("Got id of {}, it is {:?}", stringify!(#path), t); + t + } + } + + impl<'a> elua::FromLuaVec<'a> for #wrapper_typename { + fn from_lua_value_vec(state: &'a elua::State, mut values: elua::ValueVec<'a>) -> elua::Result { + use elua::FromLua; + + if let Some(val) = values.pop_front() { + #wrapper_typename::from_lua(state, val) + } else { + Err(elua::Error::IncorrectArgCount { + arg_expected: 1, + arg_count: values.len() as i32, + }) + } + } + } + }) +} \ No newline at end of file diff --git a/lyra-scripting/src/host.rs b/lyra-scripting/src/host.rs index d51056b..edbf590 100644 --- a/lyra-scripting/src/host.rs +++ b/lyra-scripting/src/host.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, sync::{Arc, Mutex}}; use lyra_ecs::{ResourceObject, Entity, World}; @@ -38,7 +38,7 @@ pub struct ScriptData { } /// Provides an API to a scripting context. -pub trait ScriptApiProvider { +pub trait ScriptApiProvider: Send + Sync { /// The type used as the script's context. type ScriptContext; @@ -62,7 +62,7 @@ pub trait ScriptApiProvider { /// A storage for a [`ScriptHost`]'s api providers #[derive(Default)] pub struct ScriptApiProviders { - pub apis: Vec>>, + pub apis: Vec>>>, } impl ScriptApiProviders { @@ -70,7 +70,7 @@ impl ScriptApiProviders { where P: ScriptApiProvider + 'static { - self.apis.push(Box::new(provider)); + self.apis.push(Arc::new(Mutex::new(provider))); } } diff --git a/lyra-scripting/src/lua/dynamic_iter.rs b/lyra-scripting/src/lua/dynamic_iter.rs index 9a9cc46..364b9fa 100644 --- a/lyra-scripting/src/lua/dynamic_iter.rs +++ b/lyra-scripting/src/lua/dynamic_iter.rs @@ -1,6 +1,6 @@ -use std::{ptr::NonNull, ops::{Range, Deref}}; +use std::{ptr::NonNull, ops::Deref}; -use lyra_ecs::{ComponentColumn, ComponentInfo, Archetype, ArchetypeId, ArchetypeEntityId, query::dynamic::{DynamicType, QueryDynamicType}, query::Fetch, Entity}; +use lyra_ecs::{query::dynamic::DynamicViewStateIter, Entity}; use lyra_reflect::TypeRegistry; #[cfg(feature = "lua")] @@ -8,130 +8,6 @@ use super::ReflectLuaProxy; use crate::ScriptWorldPtr; -/// A reimplementation of lyra_ecs::FetchDynamicType that doesn't store references and -/// uses a pointer instead. -/// This makes it easier to expose to Lua -#[derive(Clone)] -pub struct FetchDynamicType { - col: NonNull, - info: ComponentInfo, -} - -impl<'a> Fetch<'a> for FetchDynamicType { - type Item = DynamicType; - - fn dangling() -> Self { - unreachable!() - } - - unsafe fn get_item(&mut self, entity: ArchetypeEntityId) -> Self::Item { - let ptr = unsafe { self.col.as_ref().borrow_ptr() }; - let ptr = NonNull::new_unchecked(ptr.as_ptr() - .add(entity.0 as usize * self.info.layout().size())); - - DynamicType { - info: self.info, - ptr, - } - } -} - -impl<'a> From> for FetchDynamicType { - fn from(value: lyra_ecs::query::dynamic::FetchDynamicType<'a>) -> Self { - Self { - col: NonNull::from(value.col), - info: value.info, - } - } -} - -pub struct DynamicViewRow { - entity: Entity, - item: Vec, -} - -#[derive(Clone)] -pub struct DynamicViewIter { - world_ptr: ScriptWorldPtr, - queries: Vec, - fetchers: Vec, - archetypes: Vec>, - next_archetype: usize, - component_indices: Range, -} - -impl<'a> From> for DynamicViewIter { - fn from(value: lyra_ecs::query::dynamic::DynamicViewIter<'a>) -> Self { - Self { - world_ptr: ScriptWorldPtr::from_ref(value.world), - queries: value.queries, - fetchers: value.fetchers.into_iter().map(|f| FetchDynamicType::from(f)).collect(), - archetypes: value.archetypes.into_iter().map(|a| NonNull::from(a)).collect(), - next_archetype: value.next_archetype, - component_indices: value.component_indices, - } - } -} - -impl Iterator for DynamicViewIter { - type Item = DynamicViewRow; - - fn next(&mut self) -> Option { - loop { - if let Some(entity_index) = self.component_indices.next() { - let mut fetch_res = vec![]; - - let entity_index = ArchetypeEntityId(entity_index); - for fetcher in self.fetchers.iter_mut() { - if !fetcher.can_visit_item(entity_index) { - break; - } else { - let i = unsafe { fetcher.get_item(entity_index) }; - fetch_res.push(i); - } - } - - if fetch_res.len() != self.fetchers.len() { - continue; - } - - let arch = unsafe { self.archetypes.get_unchecked(self.next_archetype - 1).as_ref() }; - let entity = arch.entity_at_index(entity_index).unwrap(); - let row = DynamicViewRow { - entity, - item: fetch_res, - }; - - return Some(row); - } else { - if self.next_archetype >= self.archetypes.len() { - return None; // ran out of archetypes to go through - } - - let arch_id = self.next_archetype; - self.next_archetype += 1; - let arch = unsafe { self.archetypes.get_unchecked(arch_id).as_ref() }; - - if arch.entity_indexes().len() == 0 { - continue; - } - - if self.queries.iter().any(|q| !q.can_visit_archetype(arch)) { - continue; - } - - let world = self.world_ptr.as_ref(); - - self.fetchers = self.queries.iter() - .map(|q| unsafe { q.fetch(world, ArchetypeId(arch_id as u64), arch) } ) - .map(|f| FetchDynamicType::from(f)) - .collect(); - self.component_indices = 0..arch.entity_indexes().len() as u64; - } - } - } -} - #[cfg(feature = "lua")] pub struct ReflectedItem<'a> { //pub proxy: &'a ReflectLuaProxy, @@ -147,7 +23,7 @@ pub struct ReflectedRow<'a> { pub struct ReflectedIterator { pub world: ScriptWorldPtr, - pub dyn_view: DynamicViewIter, + pub dyn_view: DynamicViewStateIter, pub reflected_components: Option> } @@ -155,8 +31,9 @@ impl ReflectedIterator { #[cfg(feature = "lua")] pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option> { use elua::AsLua; - - let n = self.dyn_view.next(); + + let world = self.world.as_ref(); + let n = self.dyn_view.next(world); if let Some(row) = n { if self.reflected_components.is_none() { @@ -166,7 +43,7 @@ impl ReflectedIterator { } let mut dynamic_row = vec![]; - for d in row.item.iter() { + for d in row.row.iter() { let id = d.info.type_id().as_rust(); let reflected_components = unsafe { self.reflected_components.as_ref().unwrap().as_ref() }; diff --git a/lyra-scripting/src/lua/loader.rs b/lyra-scripting/src/lua/loader.rs index 9571c3c..dda3041 100644 --- a/lyra-scripting/src/lua/loader.rs +++ b/lyra-scripting/src/lua/loader.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use lyra_resource::{ResourceLoader, ResHandle}; +use lyra_resource::{loader::{PinedBoxLoaderFuture, ResourceLoader}, ResHandle, ResourceData}; #[derive(Debug, Clone)] pub struct LuaScript { @@ -8,6 +8,20 @@ pub struct LuaScript { pub(crate) bytes: Vec, } +impl ResourceData for LuaScript { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn dependencies(&self) -> Vec { + vec![] + } +} + #[derive(Default, Clone)] pub struct LuaLoader; @@ -20,24 +34,33 @@ impl ResourceLoader for LuaLoader { &["text/lua"] } - fn load(&self, _resource_manager: &mut lyra_resource::ResourceManager, path: &str) -> Result, lyra_resource::LoaderError> { - let bytes = std::fs::read(path)?; + fn load(&self, _resource_manager: lyra_resource::ResourceManager, path: &str) -> PinedBoxLoaderFuture { + let path = path.to_string(); + Box::pin(async move { + let bytes = std::fs::read(&path)?; - let s = ResHandle::new_ready(path, LuaScript { - bytes - }); - - Ok(Arc::new(s)) + let s = LuaScript { + bytes + }; + + Ok(Box::new(s) as Box) + }) } - fn load_bytes(&self, _resource_manager: &mut lyra_resource::ResourceManager, bytes: Vec, offset: usize, length: usize) -> Result, lyra_resource::LoaderError> { - let end = offset + length; - let bytes = bytes[offset..end].to_vec(); - - let s = ResHandle::new_ready("from bytes", LuaScript { - bytes - }); + fn load_bytes(&self, _resource_manager: lyra_resource::ResourceManager, bytes: Vec, offset: usize, length: usize) -> PinedBoxLoaderFuture { + Box::pin(async move { + let end = offset + length; + let bytes = bytes[offset..end].to_vec(); + + let s = LuaScript { + bytes + }; - Ok(Arc::new(s)) + Ok(Box::new(s) as Box) + }) + } + + fn create_erased_handle(&self) -> Arc { + Arc::from(ResHandle::::new_loading(None)) } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index 7982271..e01bba6 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -1,7 +1,10 @@ -use lyra_ecs::ResourceObject; -use lyra_reflect::Reflect; +use std::any::TypeId; -use crate::{lua::{wrappers::{LuaActionHandler, LuaDeltaTime, LuaModelComponent}, LuaContext, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; +use lyra_ecs::ResourceObject; +use lyra_reflect::{Reflect, TypeRegistry}; +use lyra_resource::gltf::Gltf; + +use crate::{lua::{wrappers::{LuaGltfHandle, LuaActionHandler, LuaDeltaTime, LuaResHandleToComponent, LuaSceneHandle}, LuaContext, ReflectLuaProxy, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; #[derive(Default)] pub struct LyraEcsApiProvider; @@ -11,8 +14,26 @@ impl ScriptApiProvider for LyraEcsApiProvider { fn prepare_world(&mut self, world: &mut lyra_ecs::World) { world.register_lua_convert::(); - world.register_lua_wrapper::(); + world.register_lua_wrapper::(); world.register_lua_wrapper::(); + + let mut registry = world.get_resource_mut::(); + + let reg_type = registry.get_type_or_default(TypeId::of::()); + reg_type.add_data(ReflectLuaProxy::from_lua_proxy::()); + + let l = LuaResHandleToComponent::new( + |lua, res| { + if let Some(gltf) = res.as_typed::() { + Some(lua.create_userdata(LuaGltfHandle(gltf)).unwrap()) + } else { + None + } + } + ); + reg_type.add_data(l); + + //reg_type.add_data(ReflectLuaProxy::from_lua_proxy::()); } fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { @@ -21,7 +42,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::()?)?; + globals.set("SceneComponent", ctx.create_proxy::()?)?; globals.set("ActionHandler", ctx.create_proxy::()?)?; let dt_table = create_reflect_table::(&ctx)?; diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index cbf7258..0a6e515 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -30,6 +30,7 @@ impl ScriptHost for LuaHost { }); for provider in providers.apis.iter_mut() { + let mut provider = provider.lock().unwrap(); provider.expose_api(script_data, &mut ctx)?; } @@ -44,6 +45,7 @@ impl ScriptHost for LuaHost { fn setup_script(&mut self, script_data: &crate::ScriptData, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders) -> Result<(), ScriptError> { for provider in providers.apis.iter_mut() { + let mut provider = provider.lock().unwrap(); provider.setup_script(script_data, ctx)?; } @@ -57,6 +59,7 @@ impl ScriptHost for LuaHost { ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders, function_name: &str) -> Result<(), ScriptError> { for provider in providers.apis.iter_mut() { + let mut provider = provider.lock().unwrap(); provider.update_script_environment(world.clone(), script_data, ctx)?; } diff --git a/lyra-scripting/src/lua/system.rs b/lyra-scripting/src/lua/system.rs index 7b0effc..0dec88b 100644 --- a/lyra-scripting/src/lua/system.rs +++ b/lyra-scripting/src/lua/system.rs @@ -29,7 +29,7 @@ pub fn lua_scripts_create_contexts( let script_name = script.name(); let _span = debug_span!("lua", script = script_name).entered(); - if let Some(script_res) = &script.res_handle().try_data_ref() { + if let Some(script_res) = &script.res_handle().data_ref() { debug!("Loading script..."); let mut script_ctx = host.load_script(&script_res.bytes, &script_data, &mut providers)?; @@ -74,14 +74,14 @@ pub fn lua_scripts_create_contexts( /// Note: This only works if the script is watched. See [`lyra_resource::ResourceManager::watch`]. pub fn lua_scripts_reload_system( mut contexts: ResMut>, - mut resman: ResMut, + resman: ResMut, view: View<&ScriptList>, ) -> anyhow::Result<()> { for scripts in view.into_iter() { for script in scripts.iter() { let handle = script.res_handle(); if handle.is_watched() { - let handle_path = handle.path(); + let handle_path = handle.path().unwrap(); let watch_recv = resman.watcher_event_recv(&handle_path).unwrap(); match watch_recv.try_recv() { @@ -199,7 +199,7 @@ impl Plugin for LuaScriptingPlugin { world.add_resource_default::>(); world.add_resource_default::>(); - let mut loader = world + let loader = world .try_get_resource_mut::() .expect("Add 'ResourceManager' to the world before trying to add this plugin"); loader.register_loader::(); diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 83e316b..b2ae8ca 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -2,12 +2,12 @@ use std::{ptr::NonNull, sync::Arc}; use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr}; use elua::AsLua; -use lyra_ecs::{query::dynamic::QueryDynamicType, CommandQueue, Commands, DynamicBundle, World}; +use lyra_ecs::{query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType}, CommandQueue, Commands, DynamicBundle, World}; use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry}; use lyra_resource::ResourceManager; use super::{ - reflect_user_data, wrappers::LuaResHandle, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE + reflect_user_data, wrappers::LuaResHandleToComponent, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE }; impl<'lua> elua::FromLua<'lua> for ScriptEntity { @@ -114,7 +114,8 @@ impl elua::Userdata for ScriptWorldPtr { } let world = unsafe { this.inner.as_ref() }; - let mut view = world.dynamic_view(); + //let mut view = world.dynamic_view(); + let mut view = DynamicViewState::new(); for (idx, comp) in queries.into_iter().enumerate() { match comp { @@ -160,7 +161,7 @@ impl elua::Userdata for ScriptWorldPtr { let iter = view.into_iter(); let mut reflected_iter = ReflectedIterator { world: this.clone(), - dyn_view: DynamicViewIter::from(iter), + dyn_view: DynamicViewStateIter::from(iter), reflected_components: None, }; @@ -297,12 +298,19 @@ impl elua::Userdata for ScriptWorldPtr { Ok(()) }) - .method_mut("request_res", |_, this, path: String| { - let world = this.as_mut(); - let mut man = world.get_resource_mut::(); + .method_mut("request_res", |lua, this, path: String| { + let world: &mut World = this.as_mut(); + let man = world.get_resource_mut::(); let handle = man.request_raw(&path).unwrap(); - Ok(LuaResHandle::from(handle)) + // get the actual resource handle wrapper + let registry = world.get_resource::(); + let ty = registry.get_type(handle.resource_type_id()) + .expect("Could not find asset type in registry"); + let data = ty.get_data::() + .expect("Asset type does not have 'LuaResHandleToComponent' as TypeData"); + + Ok((data.fn_to_lua)(lua, handle).unwrap()) }); } } diff --git a/lyra-scripting/src/lua/wrappers/asset/mod.rs b/lyra-scripting/src/lua/wrappers/asset/mod.rs new file mode 100644 index 0000000..10c471d --- /dev/null +++ b/lyra-scripting/src/lua/wrappers/asset/mod.rs @@ -0,0 +1,128 @@ +use std::{any::TypeId, ops::Deref}; +use elua::{AnyUserdata, AsLua, FromLua, State, Value}; +use lyra_resource::{gltf::Gltf, ResHandle, UntypedResHandle}; +use lyra_game::scene::SceneGraph; +use lyra_reflect::{Reflect, TypeData}; +use lyra_scripting_derive::lua_wrap_handle; + +use crate::{lua::{LuaWrapper, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, lyra_engine, ScriptBorrow}; + +pub struct LuaResHandleToComponent { + /// Create the userdata component that + pub fn_to_lua: fn(&State, UntypedResHandle) -> Option, +} + +impl LuaResHandleToComponent { + pub fn new(f: fn(&State, UntypedResHandle) -> Option) -> Self { + Self { + fn_to_lua: f + } + } +} + +impl TypeData for LuaResHandleToComponent { + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn boxed_clone(&self) -> Box { + todo!() + } +} + +#[derive(Clone)] +pub struct LuaResHandle(pub UntypedResHandle); + +impl Deref for LuaResHandle { + type Target = UntypedResHandle; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for LuaResHandle { + fn from(value: UntypedResHandle) -> Self { + LuaResHandle(value) + } +} + +impl elua::Userdata for LuaResHandle { + fn name() -> String { + "Handle".to_string() + } + + fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { + 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 = if this.is_loaded() { + "ready" + } else if this.get_error().is_some() { + "error" + } else { "loading" }; + + Ok(name) + }); + + builder.method("is_watched", |_, this, ()| { + Ok(this.is_watched()) + }); + + builder.method("is_loaded", |_, this, ()| { + Ok(this.is_loaded()) + }); + + builder.method("wait_until_loaded", |_, this, ()| { + this.wait_recurse_dependencies_load(); + + Ok(()) + }); + + builder.method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| { + let handle = &this.0; + + if let Some(handle) = handle.as_typed::() { + LuaSceneHandle(handle).as_lua(lua) + } else if let Some(handle) = handle.as_typed::() { + LuaGltfHandle(handle).as_lua(lua) + } else { + Ok(elua::Value::Nil) + } + }); + } +} + +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()) + } +} + +lua_wrap_handle!(SceneGraph, name=Scene, {}); +lua_wrap_handle!(Gltf, { + builder.method("scenes", |lua, this, ()| { + if let Some(data) = this.0.data_ref() { + let table = lua.create_table()?; + + for (i, scene) in data.scenes.iter().enumerate() { + let i = i as i64 + 1; // lua indexes start at 1 + table.raw_seti(i, LuaSceneHandle(scene.clone()))?; + } + + Ok(Value::Table(table)) + } else { + Ok(Value::Nil) + } + }); +}); \ 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 81b3255..db6639a 100644 --- a/lyra-scripting/src/lua/wrappers/mod.rs +++ b/lyra-scripting/src/lua/wrappers/mod.rs @@ -4,11 +4,8 @@ pub use math::*; pub mod delta_time; pub use delta_time::*; -pub mod res_handle; -pub use res_handle::*; - -pub mod model_comp; -pub use model_comp::*; - pub mod input_actions; -pub use input_actions::*; \ No newline at end of file +pub use input_actions::*; + +pub mod asset; +pub use asset::*; \ 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 deleted file mode 100644 index c17df96..0000000 --- a/lyra-scripting/src/lua/wrappers/res_handle.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::{ops::Deref, sync::Arc}; - -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); - -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>(builder: &mut elua::UserdataBuilder<'a, Self>) { - 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()) - }); - - builder.method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| { - let any = this.0.as_any(); - match any.downcast_ref::>() { - Some(model) => { - LuaModelComponent(ModelComponent(model.clone())).as_lua(lua) - }, - None => { - Ok(elua::Value::Nil) - } - } - }); - } -} - -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 diff --git a/lyra-scripting/src/script.rs b/lyra-scripting/src/script.rs index 0327834..713ea13 100644 --- a/lyra-scripting/src/script.rs +++ b/lyra-scripting/src/script.rs @@ -1,20 +1,20 @@ use std::sync::atomic::{AtomicU64, Ordering}; use lyra_ecs::Component; -use lyra_resource::ResHandle; +use lyra_resource::{ResHandle, ResourceData}; use crate::lyra_engine; static SCRIPT_ID_COUNTER: AtomicU64 = AtomicU64::new(0); #[derive(Clone)] -pub struct Script { +pub struct Script { res: ResHandle, name: String, id: u64 } -impl Script { +impl Script { pub fn new(name: &str, script: ResHandle) -> Self { Self { res: script, @@ -38,15 +38,15 @@ impl Script { /// A list of scripts #[derive(Clone, Default, Component)] -pub struct ScriptList(Vec>); +pub struct ScriptList(Vec>); -impl ScriptList { +impl ScriptList { pub fn new(list: Vec>) -> Self { Self(list) } } -impl std::ops::Deref for ScriptList { +impl std::ops::Deref for ScriptList { type Target = Vec>; fn deref(&self) -> &Self::Target { @@ -54,7 +54,7 @@ impl std::ops::Deref for ScriptList { } } -impl std::ops::DerefMut for ScriptList { +impl std::ops::DerefMut for ScriptList { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } diff --git a/shell.nix b/shell.nix index fdd102f..dc9be16 100644 --- a/shell.nix +++ b/shell.nix @@ -16,6 +16,7 @@ pkgs.mkShell.override { buildInputs = [ rust ] ++ (with pkgs; [ + cargo-expand pkg-config openssl udev wasm-pack trunk heaptrack valgrind