From 13ad671a5596b067e3039a9654202378262a80d9 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 6 Jan 2024 23:06:00 -0500 Subject: [PATCH 01/41] scripting: update lua scripts on 'update' stage --- Cargo.toml | 1 + examples/testbed/Cargo.toml | 2 +- examples/testbed/scripts/test.lua | 7 ++++ examples/testbed/src/main.rs | 17 ++++++++- lyra-game/src/game.rs | 18 +++++----- lyra-game/src/lib.rs | 3 ++ lyra-resource/src/loader/mod.rs | 2 +- lyra-scripting/src/lua/loader.rs | 2 +- lyra-scripting/src/lua/mod.rs | 59 +++++++++++++++++++++++++++++-- lyra-scripting/src/lua/test.rs | 42 +--------------------- 10 files changed, 95 insertions(+), 58 deletions(-) create mode 100644 examples/testbed/scripts/test.lua diff --git a/Cargo.toml b/Cargo.toml index 3819d32..7bdf3a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ [features] scripting = ["dep:lyra-scripting"] +lua_scripting = ["scripting", "lyra-scripting/lua"] [dependencies] lyra-game = { path = "lyra-game" } diff --git a/examples/testbed/Cargo.toml b/examples/testbed/Cargo.toml index efe8e25..8498541 100644 --- a/examples/testbed/Cargo.toml +++ b/examples/testbed/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lyra-engine = { path = "../../", version = "0.0.1", features = ["scripting"] } +lyra-engine = { path = "../../", version = "0.0.1", features = ["lua_scripting"] } #lyra-ecs = { path = "../../lyra-ecs"} anyhow = "1.0.75" async-std = "1.12.0" diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua new file mode 100644 index 0000000..102e168 --- /dev/null +++ b/examples/testbed/scripts/test.lua @@ -0,0 +1,7 @@ +print("Hello World") + +function update() + print("updated") + --printf("I love to eat formatted {}!", "food") + --printf("World is {}", world) +end \ No newline at end of file diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 6fd6b7f..a06463f 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -1,6 +1,6 @@ use std::ptr::NonNull; -use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis, CommonActionLabel}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{TransformComponent, ModelComponent, CameraComponent}}; +use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis, CommonActionLabel}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{TransformComponent, ModelComponent, CameraComponent}, lua::{LuaScriptingPlugin, LuaScript}, Script, ScriptList}; use lyra_engine::assets::{ResourceManager, Model}; mod free_fly_camera; @@ -297,11 +297,26 @@ async fn main() { game.with_plugin(InputActionPlugin); //game.with_system("input_test", test_system, &[]); }; + + let script_test_plugin = |game: &mut Game| { + game.with_plugin(LuaScriptingPlugin); + + let world = game.world(); + let mut res_man = world.get_resource_mut::(); + let script = res_man.request::("scripts/test.lua").unwrap(); + drop(res_man); + + let script = Script::new("test.lua", script); + let scripts = ScriptList::new(vec![script]); + world.spawn((scripts,)); + + }; Game::initialize().await .with_plugin(lyra_engine::DefaultPlugins) .with_startup_system(setup_sys.into_system()) .with_plugin(action_handler_plugin) + .with_plugin(script_test_plugin) //.with_plugin(fps_plugin) .with_plugin(jiggle_plugin) .with_plugin(FreeFlyCameraPlugin) diff --git a/lyra-game/src/game.rs b/lyra-game/src/game.rs index 4ce2053..0a49e45 100755 --- a/lyra-game/src/game.rs +++ b/lyra-game/src/game.rs @@ -313,12 +313,14 @@ impl Game { self } - /// Add a plugin to the game. These will be executed before the window is initiated and opened + /// Add a plugin to the game. These are executed as they are added. pub fn with_plugin

(&mut self, plugin: P) -> &mut Self where P: Plugin + 'static { - self.plugins.push_back(Box::new(plugin)); + let plugin = Box::new(plugin); + plugin.as_ref().setup(self); + self.plugins.push_back(plugin); self } @@ -339,16 +341,12 @@ impl Game { tracing_subscriber::registry() .with(fmt::layer().with_writer(stdout_layer)) .with(filter::Targets::new() - .with_target("lyra_engine", Level::TRACE) - .with_target("wgpu_core", Level::WARN) - .with_default(Level::DEBUG)) + // done by prefix, so it includes all lyra subpackages + .with_target("lyra", Level::TRACE) + .with_target("wgpu", Level::WARN) + .with_default(Level::INFO)) .init(); - // setup all the plugins - while let Some(plugin) = self.plugins.pop_front() { - plugin.as_ref().setup(self); - } - let world = self.world.take().unwrap_or_default(); // run startup systems diff --git a/lyra-game/src/lib.rs b/lyra-game/src/lib.rs index 1445f40..35ab932 100644 --- a/lyra-game/src/lib.rs +++ b/lyra-game/src/lib.rs @@ -27,4 +27,7 @@ pub mod scene; pub use lyra_resource as assets; pub use lyra_ecs as ecs; +#[cfg(feature = "scripting")] +pub use lyra_scripting as script; + pub use plugin::DefaultPlugins; \ No newline at end of file diff --git a/lyra-resource/src/loader/mod.rs b/lyra-resource/src/loader/mod.rs index 63fef55..a4e9e64 100644 --- a/lyra-resource/src/loader/mod.rs +++ b/lyra-resource/src/loader/mod.rs @@ -30,7 +30,7 @@ impl From for LoaderError { } pub trait ResourceLoader { - /// Returns the extensions that this loader supports. + /// Returns the extensions that this loader supports. Does not include the `.` fn extensions(&self) -> &[&str]; /// Returns the mime types that this loader supports. fn mime_types(&self) -> &[&str]; diff --git a/lyra-scripting/src/lua/loader.rs b/lyra-scripting/src/lua/loader.rs index 95aee98..fe0492d 100644 --- a/lyra-scripting/src/lua/loader.rs +++ b/lyra-scripting/src/lua/loader.rs @@ -13,7 +13,7 @@ pub struct LuaLoader; impl ResourceLoader for LuaLoader { fn extensions(&self) -> &[&str] { - &[".lua"] + &["lua"] } fn mime_types(&self) -> &[&str] { diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index 4cc9a7d..686baf2 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -4,6 +4,7 @@ pub use dynamic_iter::*; pub mod world; use lyra_game::plugin::Plugin; use lyra_resource::ResourceManager; +use tracing::{debug, error}; pub use world::*; pub mod script; @@ -17,7 +18,7 @@ mod test; use std::{ptr::NonNull, sync::Mutex}; -use lyra_ecs::DynamicBundle; +use lyra_ecs::{DynamicBundle, World}; use lyra_reflect::{Reflect, RegisteredType, FromType, AsRegisteredType}; use mlua::{Lua, AnyUserDataExt}; @@ -25,7 +26,7 @@ use mlua::{Lua, AnyUserDataExt}; pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; -use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptApiProviders, ScriptContexts}; +use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptApiProviders, ScriptContexts, ScriptWorldPtr, ScriptList, ScriptData, ScriptHost, ScriptError}; impl<'lua> mlua::FromLua<'lua> for ScriptBorrow { fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { @@ -114,6 +115,54 @@ impl mlua::UserData for ScriptDynamicBundle { } } +/// A system that updates the scripts that exist in the world +pub fn lua_update_scripts(world: &mut World) -> anyhow::Result<()> { + let world_ptr = ScriptWorldPtr::from_ref(&world); + let mut host = world.get_resource_mut::(); + let mut contexts = world.get_resource_mut::>>(); + let mut providers = world.get_resource_mut::>(); + + for scripts in world.view_iter::<&ScriptList>() { + for script in scripts.iter() { + let script_data = ScriptData { + name: script.name().to_string(), + script_id: script.id(), + }; + + if !contexts.has_context(script.id()) { + if let Some(script_res) = &script.res_handle().data { + debug!("Loading script '{}'...", script.name()); + let mut script_ctx = host.load_script(&script_res.bytes, &script_data, &mut providers).unwrap(); + debug!("Finished loading script '{}'", script.name()); + + debug!("Setting up script '{}'...", script.name()); + host.setup_script(&script_data, &mut script_ctx, &mut providers).unwrap(); + debug!("Finished setting up script '{}'...", script.name()); + + contexts.add_context(script.id(), script_ctx); + } else { + debug!("Script '{}' is not yet loaded, skipping", script.name()); + } + } + + let ctx = contexts.get_context_mut(script.id()).unwrap(); + + debug!("Running 'update' function in script '{}'", script.name()); + match host.update_script(world_ptr.clone(), &script_data, ctx, &mut providers) { + Ok(()) => {}, + Err(e) => match e { + ScriptError::MluaError(m) => { + error!("Script '{}' ran into an error: {}", script.name(), m); + }, + ScriptError::Other(_) => return Err(e.into()), + }, + } + } + } + + Ok(()) +} + #[derive(Default)] pub struct LuaScriptingPlugin; @@ -125,7 +174,11 @@ impl Plugin for LuaScriptingPlugin { world.add_resource_default::>(); world.add_resource_default::>>(); - let mut loader = world.get_resource_or_else(ResourceManager::default); + let mut loader = world.try_get_resource_mut::() + .expect("Add 'ResourceManager' to the world before trying to add this plugin"); loader.register_loader::(); + drop(loader); + + game.with_system("lua_update", lua_update_scripts, &[]); } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/test.rs b/lyra-scripting/src/lua/test.rs index 398d7ce..7c1f87c 100644 --- a/lyra-scripting/src/lua/test.rs +++ b/lyra-scripting/src/lua/test.rs @@ -8,7 +8,7 @@ use tracing_subscriber::{layer::SubscriberExt, fmt, filter, util::SubscriberInit use crate::{ScriptHost, ScriptData, ScriptApiProvider, ScriptApiProviders, ScriptError, ScriptWorldPtr, ScriptList, Script, ScriptContexts}; -use super::{LuaHost, LuaLoader, LuaScript}; +use super::{LuaHost, LuaLoader, LuaScript, lua_update_scripts}; use crate::lyra_engine; @@ -176,44 +176,4 @@ pub fn lua_print() { let mut exec = GraphExecutor::new(); exec.insert_system("lua_update_scripts", lua_update_scripts.into_system(), &[]); exec.execute(NonNull::from(&world), true).unwrap(); -} - -fn lua_update_scripts(world: &mut World) -> anyhow::Result<()> { - let world_ptr = ScriptWorldPtr::from_ref(&world); - let mut host = world.get_resource_mut::(); - let mut contexts = world.get_resource_mut::>>(); - let mut providers = world.get_resource_mut::>(); - - for scripts in world.view_iter::<&ScriptList>() { - for script in scripts.iter() { - let script_data = ScriptData { - name: script.name().to_string(), - script_id: script.id(), - }; - - if !contexts.has_context(script.id()) { - if let Some(script_res) = &script.res_handle().data { - let mut script_ctx = host.load_script(&script_res.bytes, &script_data, &mut providers).unwrap(); - host.setup_script(&script_data, &mut script_ctx, &mut providers).unwrap(); - contexts.add_context(script.id(), script_ctx); - } else { - debug!("Script '{}' is not yet loaded, skipping", script.name()); - } - } - - let ctx = contexts.get_context_mut(script.id()).unwrap(); - - match host.update_script(world_ptr.clone(), &script_data, ctx, &mut providers) { - Ok(()) => {}, - Err(e) => match e { - ScriptError::MluaError(m) => { - error!("Script '{}' ran into an error: {}", script.name(), m); - }, - ScriptError::Other(_) => return Err(e.into()), - }, - } - } - } - - Ok(()) } \ No newline at end of file -- 2.40.1 From e49d69dbc1d73c4331529f22e6a2abd9efb798fc Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 7 Jan 2024 00:57:19 -0500 Subject: [PATCH 02/41] scripting: update scripts on all game stages, create utility api provider --- examples/testbed/scripts/test.lua | 28 ++++-- lyra-scripting/src/host.rs | 21 +++-- lyra-scripting/src/lua/mod.rs | 117 ++++++++++++++++++++----- lyra-scripting/src/lua/modules/mod.rs | 120 ++++++++++++++++++++++++++ lyra-scripting/src/lua/script.rs | 18 ++-- lyra-scripting/src/lua/test.rs | 18 ++-- lyra-scripting/src/lua/world.rs | 8 +- lyra-scripting/src/script.rs | 6 +- lyra-scripting/src/world.rs | 8 ++ 9 files changed, 292 insertions(+), 52 deletions(-) create mode 100644 lyra-scripting/src/lua/modules/mod.rs diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 102e168..de8a543 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -1,7 +1,25 @@ print("Hello World") - -function update() - print("updated") - --printf("I love to eat formatted {}!", "food") - --printf("World is {}", world) + +function on_init() + print("Lua script was initialized!") +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() + print("Lua's update function was called") +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/lyra-scripting/src/host.rs b/lyra-scripting/src/host.rs index f422725..b19e316 100644 --- a/lyra-scripting/src/host.rs +++ b/lyra-scripting/src/host.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use lyra_ecs::ResourceObject; +use lyra_ecs::{ResourceObject, Entity}; use crate::ScriptWorldPtr; @@ -29,8 +29,12 @@ impl From for ScriptError { #[derive(Clone, Hash, PartialEq, Eq)] pub struct ScriptData { + /// The script id pub script_id: u64, + /// The name of the script pub name: String, + /// The entity that this script exists on + pub entity: Entity, } /// Provides an API to a scripting context. @@ -41,9 +45,7 @@ pub trait ScriptApiProvider { /// Exposes an API in the provided script context. fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>; - /// Create a script in the script host. - /// - /// This only creates the script for the host, it does not setup the script for execution. See [`ScriptHostProvider::setup_script`]. + /// Setup a script right before its 'init' method is called. fn setup_script(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>; /// A function that is used to update a script's environment. @@ -74,13 +76,18 @@ pub trait ScriptHost: Default + ResourceObject { /// Loads a script and creates a context for it. /// /// Before the script is executed, the API providers are exposed to the script. - fn load_script(&mut self, script: &[u8], script_data: &ScriptData, providers: &mut crate::ScriptApiProviders) -> Result; + fn load_script(&mut self, script: &[u8], script_data: &ScriptData, + providers: &mut crate::ScriptApiProviders) + -> Result; /// Setup a script for execution. - fn setup_script(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext, providers: &mut ScriptApiProviders) -> Result<(), ScriptError>; + fn setup_script(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext, + providers: &mut ScriptApiProviders) -> Result<(), ScriptError>; /// Executes the update step for the script. - fn update_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders) -> Result<(), ScriptError>; + fn call_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData, + ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders, + function_name: &str) -> Result<(), ScriptError>; } #[derive(Default)] diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index 686baf2..ded46ce 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -2,7 +2,7 @@ pub mod dynamic_iter; pub use dynamic_iter::*; pub mod world; -use lyra_game::plugin::Plugin; +use lyra_game::{plugin::Plugin, game::GameStages}; use lyra_resource::ResourceManager; use tracing::{debug, error}; pub use world::*; @@ -13,20 +13,25 @@ pub use script::*; pub mod loader; pub use loader::*; +pub mod modules; +pub use modules::*; + #[cfg(test)] mod test; use std::{ptr::NonNull, sync::Mutex}; -use lyra_ecs::{DynamicBundle, World}; -use lyra_reflect::{Reflect, RegisteredType, FromType, AsRegisteredType}; +use lyra_ecs::{DynamicBundle, World, query::{ResMut, View, Entities}}; +use lyra_reflect::{Reflect, FromType}; use mlua::{Lua, AnyUserDataExt}; +pub type LuaContext = Mutex; + pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; -use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptApiProviders, ScriptContexts, ScriptWorldPtr, ScriptList, ScriptData, ScriptHost, ScriptError}; +use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptApiProviders, ScriptContexts, ScriptWorldPtr, ScriptList, ScriptData, ScriptHost, ScriptError, GameScriptExt}; impl<'lua> mlua::FromLua<'lua> for ScriptBorrow { fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { @@ -115,18 +120,24 @@ impl mlua::UserData for ScriptDynamicBundle { } } -/// A system that updates the scripts that exist in the world -pub fn lua_update_scripts(world: &mut World) -> anyhow::Result<()> { - let world_ptr = ScriptWorldPtr::from_ref(&world); - let mut host = world.get_resource_mut::(); - let mut contexts = world.get_resource_mut::>>(); - let mut providers = world.get_resource_mut::>(); +/// +/* fn lua_script_run_func(world: &mut World, function_name: &str) -> anyhow::Result<()> { - for scripts in world.view_iter::<&ScriptList>() { +} */ + +/// A system that creates the script contexts in the world as new scripts are found +pub fn lua_scripts_create_contexts(mut host: ResMut, + mut contexts: ResMut>, + mut providers: ResMut>, + view: View<(Entities, &ScriptList)>, + ) -> anyhow::Result<()> { + + for (en, scripts) in view.into_iter() { for script in scripts.iter() { let script_data = ScriptData { name: script.name().to_string(), script_id: script.id(), + entity: en, }; if !contexts.has_context(script.id()) { @@ -144,18 +155,37 @@ pub fn lua_update_scripts(world: &mut World) -> anyhow::Result<()> { debug!("Script '{}' is not yet loaded, skipping", script.name()); } } + } + } + + Ok(()) +} - let ctx = contexts.get_context_mut(script.id()).unwrap(); - - debug!("Running 'update' function in script '{}'", script.name()); - match host.update_script(world_ptr.clone(), &script_data, ctx, &mut providers) { - Ok(()) => {}, - Err(e) => match e { - ScriptError::MluaError(m) => { - error!("Script '{}' ran into an error: {}", script.name(), m); +fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Result<()> { + let world_ptr = ScriptWorldPtr::from_ref(&world); + let mut host = world.get_resource_mut::(); + let mut contexts = world.get_resource_mut::>(); + let mut providers = world.get_resource_mut::>(); + + for (en, scripts) in world.view_iter::<(Entities, &ScriptList)>() { + for script in scripts.iter() { + let script_data = ScriptData { + name: script.name().to_string(), + script_id: script.id(), + entity: en, + }; + + if let Some(ctx) = contexts.get_context_mut(script.id()) { + debug!("Running '{}' function in script '{}'", stage_name, script.name()); + match host.call_script(world_ptr.clone(), &script_data, ctx, &mut providers, stage_name) { + Ok(()) => {}, + Err(e) => match e { + ScriptError::MluaError(m) => { + error!("Script '{}' ran into an error: {}", script.name(), m); + }, + ScriptError::Other(_) => return Err(e.into()), }, - ScriptError::Other(_) => return Err(e.into()), - }, + } } } } @@ -163,6 +193,36 @@ pub fn lua_update_scripts(world: &mut World) -> anyhow::Result<()> { Ok(()) } +/// This system executes the 'on_update' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::Update' stage. +pub fn lua_script_update_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_update") +} + +/// This system executes the 'on_pre_update' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::PreUpdate' stage. +pub fn lua_script_pre_update_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_pre_update") +} + +/// This system executes the 'on_post_update' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::PostUpdate' stage. +pub fn lua_script_post_update_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_post_update") +} + +/// This system executes the 'on_first' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::First' stage. +pub fn lua_script_first_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_first") +} + +/// This system executes the 'on_last' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::Last' stage. +pub fn lua_script_last_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_last") +} + #[derive(Default)] pub struct LuaScriptingPlugin; @@ -172,13 +232,24 @@ impl Plugin for LuaScriptingPlugin { world.add_resource_default::(); world.add_resource_default::>(); - world.add_resource_default::>>(); + world.add_resource_default::>(); let mut loader = world.try_get_resource_mut::() .expect("Add 'ResourceManager' to the world before trying to add this plugin"); loader.register_loader::(); drop(loader); - game.with_system("lua_update", lua_update_scripts, &[]); + game.add_script_api_provider::(UtilityApiProvider); + + game + .add_system_to_stage(GameStages::First, "lua_create_contexts", lua_scripts_create_contexts, &[]) + .add_system_to_stage(GameStages::First, "lua_first_stage", lua_script_first_stage_system, &["lua_create_contexts"]) + // cannot depend on 'lua_create_contexts' since it will cause a panic. + // the staged executor separates the executor of a single stage so this system + // cannot depend on the other one. + .add_system_to_stage(GameStages::PreUpdate, "lua_pre_update", lua_script_pre_update_stage_system, &[]) + .add_system_to_stage(GameStages::Update, "lua_update", lua_script_update_stage_system, &[]) + .add_system_to_stage(GameStages::PostUpdate, "lua_post_update", lua_script_post_update_stage_system, &[]) + .add_system_to_stage(GameStages::Last, "lua_last_stage", lua_script_last_stage_system, &[]); } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/modules/mod.rs b/lyra-scripting/src/lua/modules/mod.rs new file mode 100644 index 0000000..1cc50c5 --- /dev/null +++ b/lyra-scripting/src/lua/modules/mod.rs @@ -0,0 +1,120 @@ +use std::sync::{Mutex, Arc}; + +use crate::{ScriptApiProvider, ScriptData}; + +/// This Api provider provides some nice utility functions. +/// +/// Functions: +/// ```lua +/// ---@param str (string) A format string. +/// ---@param ... (any varargs) The variables to format into the string. These values must be +/// primitives, or if UserData, have the '__tostring' meta method +/// function printf(str, ...) +/// ``` +#[derive(Default)] +pub struct UtilityApiProvider; + +impl ScriptApiProvider for UtilityApiProvider { + type ScriptContext = Mutex; + + fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + let ctx = ctx.lock().unwrap(); + + fn printf(lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic)) -> mlua::Result<()> { + let mut formatted = String::new(); + let mut arg_num = 0; + + while let Some(start) = text.find("{}") { + let val_str = match formats.get(arg_num) { + Some(v) => match v { + mlua::Value::Nil => "nil".to_string(), + mlua::Value::Boolean(b) => b.to_string(), + mlua::Value::LightUserData(_) => { + return Err(mlua::Error::RuntimeError(format!("unable to get string representation of LightUserData"))); + }, + mlua::Value::Integer(i) => i.to_string(), + mlua::Value::Number(n) => n.to_string(), + mlua::Value::String(s) => s.to_str().unwrap().to_string(), + mlua::Value::Table(_) => { + return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Table"))); + }, + mlua::Value::Function(_) => { + return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Function"))); + }, + mlua::Value::Thread(_) => { + return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Thread"))); + }, + mlua::Value::UserData(ud) => { + let metatable = ud.get_metatable()?; + if let Ok(tos) = metatable.get::(mlua::MetaMethod::ToString) { + tos.call::<_, String>((ud.clone(),))? + } else { + return Err(mlua::Error::RuntimeError(format!("UserData does not implement MetaMethod '__tostring'"))); + } + }, + mlua::Value::Error(e) => e.to_string(), + }, + None => { + let got_args = arg_num; + + // continue searching for {} to get the number of format spots for the error message. + while let Some(start) = text.find("{}") { + text = text[start + 2..].to_string(); + arg_num += 1; + } + + return Err(mlua::Error::BadArgument { + to: Some("printf".to_string()), + pos: 2, + name: Some("...".to_string()), + cause: Arc::new(mlua::Error::RuntimeError(format!("not enough args \ + given for the amount of format areas in the string. Expected {}, \ + got {}.", arg_num, got_args))) + }) + }, + }; + + formatted = format!("{}{}{}", formatted, &text[0..start], val_str); + + text = text[start + 2..].to_string(); + + arg_num += 1; + } + + if arg_num < formats.len() { + return Err(mlua::Error::BadArgument { + to: Some("printf".to_string()), + pos: 2, + name: Some("...".to_string()), + cause: Arc::new(mlua::Error::RuntimeError(format!("got more args \ + than format areas in the string. Expected {}, got {}.", formats.len(), arg_num))) + }) + } + + formatted = format!("{}{}", formatted, text); + + lua.globals() + .get::<_, mlua::Function>("print") + .unwrap() + .call::<_, ()>(formatted) + .unwrap(); + + Ok(()) + } + + let printf_func = ctx.create_function(printf).unwrap(); + + let globals = ctx.globals(); + globals.set("printf", printf_func).unwrap(); + + Ok(()) + } + + fn setup_script(&mut self, _data: &ScriptData, _ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + Ok(()) + } + + fn update_script_environment(&mut self, _world: crate::ScriptWorldPtr, _data: &ScriptData, _ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + Ok(()) + } +} \ No newline at end of file diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index e74efdd..457042f 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -1,8 +1,9 @@ use std::sync::Mutex; +use mlua::IntoLua; use tracing::debug; -use crate::{ScriptHost, ScriptError, ScriptWorldPtr}; +use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; #[derive(Default)] pub struct LuaHost; @@ -53,7 +54,7 @@ impl ScriptHost for LuaHost { } let ctx = ctx.lock().expect("Failure to get Lua ScriptContext"); - try_call_lua_function(&ctx, "init")?; + try_call_lua_function(&ctx, "on_init")?; Ok(()) } @@ -61,13 +62,20 @@ 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. - fn update_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders) -> Result<(), ScriptError> { + fn call_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData, + ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders, + function_name: &str) -> Result<(), ScriptError> { for provider in providers.apis.iter_mut() { provider.update_script_environment(world.clone(), script_data, ctx)?; } - + let ctx = ctx.lock().expect("Failure to get Lua ScriptContext"); - try_call_lua_function(&ctx, "update")?; + + let globals = ctx.globals(); + globals.set("world", world.into_lua(&ctx)?)?; + globals.set("entity", ScriptEntity(script_data.entity).into_lua(&ctx)?)?; + + try_call_lua_function(&ctx, function_name)?; Ok(()) } diff --git a/lyra-scripting/src/lua/test.rs b/lyra-scripting/src/lua/test.rs index 7c1f87c..79f72d1 100644 --- a/lyra-scripting/src/lua/test.rs +++ b/lyra-scripting/src/lua/test.rs @@ -1,16 +1,13 @@ -use std::{sync::{Mutex, Arc}, alloc::Layout, ptr::NonNull}; +use std::{sync::{Mutex, Arc}, ptr::NonNull}; -use lyra_ecs::{World, query::{Res, View, Entities, ResMut}, Component, system::{GraphExecutor, IntoSystem}}; +use lyra_ecs::{World, system::{GraphExecutor, IntoSystem}}; use lyra_resource::ResourceManager; use mlua::{IntoLua, AnyUserDataExt}; -use tracing::{debug, error}; -use tracing_subscriber::{layer::SubscriberExt, fmt, filter, util::SubscriberInitExt}; +use tracing_subscriber::{layer::SubscriberExt, fmt, util::SubscriberInitExt}; -use crate::{ScriptHost, ScriptData, ScriptApiProvider, ScriptApiProviders, ScriptError, ScriptWorldPtr, ScriptList, Script, ScriptContexts}; +use crate::{ScriptData, ScriptApiProvider, ScriptApiProviders, ScriptError, ScriptList, Script, ScriptContexts}; -use super::{LuaHost, LuaLoader, LuaScript, lua_update_scripts}; - -use crate::lyra_engine; +use super::{LuaHost, LuaLoader, LuaScript, lua_script_update_stage_system, lua_scripts_create_contexts, LuaContext}; fn enable_tracing() { tracing_subscriber::registry() @@ -148,7 +145,7 @@ pub fn lua_print() { world.add_resource(host); world.add_resource(providers); - world.add_resource(ScriptContexts::>::default()); + world.add_resource(ScriptContexts::::default()); let mut res_loader = ResourceManager::new(); res_loader.register_loader::(); @@ -174,6 +171,7 @@ pub fn lua_print() { world.spawn((scripts,)); let mut exec = GraphExecutor::new(); - exec.insert_system("lua_update_scripts", lua_update_scripts.into_system(), &[]); + exec.insert_system("lua_create_contexts", lua_scripts_create_contexts.into_system(), &[]); + exec.insert_system("lua_update_scripts", lua_script_update_stage_system.into_system(), &["lua_create_contexts"]); exec.execute(NonNull::from(&world), true).unwrap(); } \ No newline at end of file diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index a6c6552..d1521ca 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -16,7 +16,13 @@ impl<'lua> mlua::FromLua<'lua> for ScriptEntity { } } -impl mlua::UserData for ScriptEntity {} +impl mlua::UserData for ScriptEntity { + fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { + Ok(format!("{:?}", this.0)) + }) + } +} #[derive(thiserror::Error, Debug, Clone)] pub enum WorldError { diff --git a/lyra-scripting/src/script.rs b/lyra-scripting/src/script.rs index d93877b..0327834 100644 --- a/lyra-scripting/src/script.rs +++ b/lyra-scripting/src/script.rs @@ -1,8 +1,12 @@ +use std::sync::atomic::{AtomicU64, Ordering}; + use lyra_ecs::Component; use lyra_resource::ResHandle; use crate::lyra_engine; +static SCRIPT_ID_COUNTER: AtomicU64 = AtomicU64::new(0); + #[derive(Clone)] pub struct Script { res: ResHandle, @@ -15,7 +19,7 @@ impl Script { Self { res: script, name: name.to_string(), - id: 0 // TODO: make a counter + id: SCRIPT_ID_COUNTER.fetch_add(1, Ordering::AcqRel) } } diff --git a/lyra-scripting/src/world.rs b/lyra-scripting/src/world.rs index a4e1134..c2b2a1f 100644 --- a/lyra-scripting/src/world.rs +++ b/lyra-scripting/src/world.rs @@ -5,6 +5,14 @@ use lyra_ecs::{world::World, Entity}; #[derive(Clone)] pub struct ScriptEntity(pub Entity); +impl std::ops::Deref for ScriptEntity { + type Target = Entity; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[derive(Clone)] pub struct ScriptWorldPtr { pub inner: NonNull, -- 2.40.1 From 29467faf55af80814ca3e718a0ac717f28d95294 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 7 Jan 2024 23:15:44 -0500 Subject: [PATCH 03/41] reflect: Expect doctests to fail compile --- .../lyra-reflect-derive/src/enum_derive.rs | 22 +++++++++---------- .../lyra-reflect-derive/src/struct_derive.rs | 14 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs b/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs index 6a38896..0e4d792 100644 --- a/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs +++ b/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs @@ -32,7 +32,7 @@ impl From<&Variant> for VariantType { /// Generates the following different outputs: /// -/// ```rust +/// ```compile_fail /// // for struct variants /// TestEnum::Error { msg, code } /// @@ -98,7 +98,7 @@ fn gen_variant_if(enum_id: &proc_macro2::Ident, variant: &Variant, if_body: proc /// Generates the following: /// -/// ```rust +/// ```compile_fail /// /// generated one field here /// if name == "msg" { /// return Some(msg); @@ -129,7 +129,7 @@ fn gen_if_field_names(variant: &Variant) -> proc_macro2::TokenStream { /// Generates the following rust code: /// -/// ```rust +/// ```compile_fail /// match name { /// "msg" | "code" => true, /// _ => false, @@ -153,7 +153,7 @@ fn gen_match_names(variant: &Variant) -> proc_macro2::TokenStream { /// Generates the following: /// -/// ```rust +/// ```compile_fail /// /// generated one field here /// if idx == 0 { /// return Some(a); @@ -190,7 +190,7 @@ fn gen_if_field_indices(variant: &Variant) -> proc_macro2::TokenStream { /// Generates the following: /// -/// ```rust +/// ```compile_fail /// /// generated one field here /// if idx == 0 { /// return Some("a"); @@ -226,7 +226,7 @@ fn gen_if_field_indices_names(variant: &Variant) -> proc_macro2::TokenStream { } /// Generates the following: -/// ```rust +/// ```compile_fail /// /// when `by_index` is false: /// /// if let TestEnum::Error{ msg, code} = self { @@ -301,7 +301,7 @@ fn gen_enum_if_stmts(enum_id: &proc_macro2::Ident, data: &DataEnum, by_index: bo /// Generates the following rust code: /// -/// ```rust +/// ```compile_fail /// if let TestEnum::Error { msg, code } = self { /// return match name { /// // expands for continuing struct fields @@ -332,7 +332,7 @@ fn gen_enum_has_field(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_mac /// Generates the following code: /// -/// ```rust +/// ```compile_fail /// match self { /// TestEnum::Start => 0, /// TestEnum::Middle(a, b) => 2, @@ -359,7 +359,7 @@ fn gen_enum_fields_len(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_ma /// Generates the following code: /// -/// ```rust +/// ```compile_fail /// if let TestEnum::Error { msg, code } = self { /// if idx == 0 { /// return Some("msg"); @@ -390,7 +390,7 @@ fn gen_enum_field_name_at(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc } /// Generates the following code: -/// ```rust +/// ```compile_fail /// match self { /// TestEnum::Start => 0, /// TestEnum::Middle(a, b) => 1, @@ -428,7 +428,7 @@ fn gen_enum_variant_name(enum_id: &proc_macro2::Ident, data: &DataEnum, gen_inde /// Generates a match statement that returns the types of the variants of the enum. /// /// Example: -/// ```rust +/// ```compile_fail /// match self { /// TestEnum::Start => EnumType::Unit, /// TestEnum::Middle(a, b) => EnumType::Tuple, diff --git a/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs index b5768ee..f78fb63 100644 --- a/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs +++ b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs @@ -7,7 +7,7 @@ use crate::add_trait_bounds; /// contains a borrow (mutable borrow if `is_mut` is true) to the matching struct field. /// /// Example: -/// ```rust +/// ```compile_fail /// // when `is_mut` = false /// match name { /// "x" => Some(&self.x), @@ -50,7 +50,7 @@ fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::Token /// with the provided `val`. /// /// Example: -/// ```rust +/// ```compile_fail /// match name { /// "x" => self.x = any_val.downcast_ref::() /// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name())) @@ -91,7 +91,7 @@ fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream { /// the type of the field. /// /// Example: -/// ```rust +/// ```compile_fail /// match name { /// "x" => Some("f32"), /// "y" => Some("f32"), @@ -124,7 +124,7 @@ fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream { /// with the provided `val`. /// /// Example: -/// ```rust +/// ```compile_fail /// match name { /// 0 => self.x = any_val.downcast_ref::() /// .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", "f32", val.name())) @@ -165,7 +165,7 @@ fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream /// type of the field. /// /// Example: -/// ```rust +/// ```compile_fail /// match name { /// 0 => Some("f32"), /// 1 => Some("f32"), @@ -196,7 +196,7 @@ fn gen_struct_field_name_match_idx(data: &DataStruct) -> proc_macro2::TokenStrea /// to the matching struct field. /// /// Example: -/// ```rust +/// ```compile_fail /// // when `is_mut` = false /// match idx { /// 0 => Some(&self.x), @@ -238,7 +238,7 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T /// and returns an Option that contains the name of the field. /// /// Example: -/// ```rust +/// ```compile_fail /// match idx { /// 0 => Some("x"), /// 1 => Some("y"), -- 2.40.1 From eb44aba3dcadcd784eb67b42080ed43ad6e52b17 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 8 Jan 2024 20:25:20 -0500 Subject: [PATCH 04/41] reflect: Fix weird panics for rust-analyzer Not sure why they were happening. These panics didn't happen on CI and my machine when building and running the tests. --- .../lyra-reflect-derive/src/enum_derive.rs | 43 ++++++++----------- lyra-reflect/lyra-reflect-derive/src/lib.rs | 18 +++++--- .../lyra-reflect-derive/src/struct_derive.rs | 3 +- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs b/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs index 0e4d792..a80740d 100644 --- a/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs +++ b/lyra-reflect/lyra-reflect-derive/src/enum_derive.rs @@ -113,7 +113,7 @@ fn gen_variant_if(enum_id: &proc_macro2::Ident, variant: &Variant, if_body: proc fn gen_if_field_names(variant: &Variant) -> proc_macro2::TokenStream { let field_ifs = variant.fields.iter().map(|field| { let id = field.ident.as_ref().unwrap(); - let id_str = id.span().source_text().unwrap(); + let id_str = id.to_string(); quote! { if name == #id_str { @@ -140,7 +140,7 @@ fn gen_match_names(variant: &Variant) -> proc_macro2::TokenStream { let field_name_strs = variant.fields.iter().map(|field| { let id = field.ident.as_ref() .expect("Could not find identifier for enum field!"); - id.span().source_text().unwrap() + id.to_string() }); quote! { @@ -292,7 +292,6 @@ fn gen_enum_if_stmts(enum_id: &proc_macro2::Ident, data: &DataEnum, by_index: bo _ => quote! { }, } }); - println!("===="); quote! { #( #struct_vars )* @@ -458,41 +457,35 @@ fn gen_enum_variant_type(enum_id: &proc_macro2::Ident, data: &DataEnum) -> proc_ /// Create a reflect implementation for an enum pub fn derive_reflect_enum(input: &DeriveInput, data_enum: &DataEnum) -> proc_macro2::TokenStream { - let type_path = &input.ident; - let name = type_path.span().source_text().unwrap(); - //println!("Got type path: {}", type_path); + let input_ident = &input.ident; + let ident_str = input.ident.to_string(); let variant_count = data_enum.variants.len(); - /* let mut variants_iter = data_enum.variants.iter(); + let field_ifs = gen_enum_if_stmts(input_ident, data_enum, false); + let field_mut_ifs = gen_enum_if_stmts(input_ident, data_enum, false); - let variant = variants_iter.next().unwrap(); - let variant_name = &variant.ident; */ + let field_at_ifs = gen_enum_if_stmts(input_ident, data_enum, true); + let field_at_mut_ifs = gen_enum_if_stmts(input_ident, data_enum, true); - let field_ifs = gen_enum_if_stmts(type_path, data_enum, false); - let field_mut_ifs = gen_enum_if_stmts(type_path, data_enum, false); - - let field_at_ifs = gen_enum_if_stmts(type_path, data_enum, true); - let field_at_mut_ifs = gen_enum_if_stmts(type_path, data_enum, true); - - let has_field = gen_enum_has_field(type_path, data_enum); - let field_len = gen_enum_fields_len(type_path, data_enum); - let field_name_at = gen_enum_field_name_at(type_path, data_enum); - let variant_name_match = gen_enum_variant_name(type_path, data_enum, false); - let variant_idx_match = gen_enum_variant_name(type_path, data_enum, true); - let variant_type = gen_enum_variant_type(type_path, data_enum); + let has_field = gen_enum_has_field(input_ident, data_enum); + let field_len = gen_enum_fields_len(input_ident, data_enum); + let field_name_at = gen_enum_field_name_at(input_ident, data_enum); + let variant_name_match = gen_enum_variant_name(input_ident, data_enum, false); + let variant_idx_match = gen_enum_variant_name(input_ident, data_enum, true); + let variant_type = gen_enum_variant_type(input_ident, data_enum); let generics = add_trait_bounds(input.generics.clone(), vec![parse_quote!(Reflect), parse_quote!(Clone)]); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); return proc_macro2::TokenStream::from(quote! { - impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause { + impl #impl_generics lyra_engine::reflect::Reflect for #input_ident #ty_generics #where_clause { fn name(&self) -> ::std::string::String { - #name.to_string() + #ident_str.to_string() } fn type_id(&self) -> std::any::TypeId { - std::any::TypeId::of::<#type_path #ty_generics>() + std::any::TypeId::of::<#input_ident #ty_generics>() } fn as_any(&self) -> &dyn std::any::Any { @@ -530,7 +523,7 @@ pub fn derive_reflect_enum(input: &DeriveInput, data_enum: &DataEnum) -> proc_ma } } - impl #impl_generics lyra_engine::reflect::Enum for #type_path #ty_generics #where_clause { + impl #impl_generics lyra_engine::reflect::Enum for #input_ident #ty_generics #where_clause { fn field(&self, name: &str) -> Option<&dyn lyra_engine::reflect::Reflect> { let name = name.to_lowercase(); let name = name.as_str(); diff --git a/lyra-reflect/lyra-reflect-derive/src/lib.rs b/lyra-reflect/lyra-reflect-derive/src/lib.rs index b30e593..2d5b1a3 100644 --- a/lyra-reflect/lyra-reflect-derive/src/lib.rs +++ b/lyra-reflect/lyra-reflect-derive/src/lib.rs @@ -1,4 +1,5 @@ use proc_macro::TokenStream; +use proc_macro2::Ident; use quote::{quote, ToTokens}; use syn::{Generics, Path, Attribute, GenericParam, parse_macro_input, DeriveInput, TypeParamBound}; @@ -12,6 +13,7 @@ use struct_derive::*; #[allow(dead_code)] pub(crate) struct ReflectDef { + //pub ident: Ident, pub type_path: Path, pub generics: Generics, pub attributes: Vec @@ -21,10 +23,12 @@ impl syn::parse::Parse for ReflectDef { fn parse(input: syn::parse::ParseStream) -> syn::Result { let attributes = input.call(Attribute::parse_outer)?; let type_path = Path::parse_mod_style(input)?; + //let ident = type_path. //type_path.require_ident()?; let mut generics = input.parse::()?; generics.where_clause = input.parse()?; Ok(Self { + //ident: ident.clone(), type_path, generics, attributes, @@ -55,18 +59,22 @@ pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream #[proc_macro] pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream { let reflect = syn::parse_macro_input!(input as ReflectDef); - - let type_path = reflect.type_path.clone().into_token_stream(); - let name_id = &reflect.type_path.segments.last().unwrap().ident; - let name = name_id.span().source_text().unwrap(); + let type_path = reflect.type_path; + // convert the type path to a string. This would not create a leading separator + let type_path_str = { + let idents: Vec = type_path.segments.iter() + .map(|segment| segment.ident.to_string()) + .collect(); + idents.join("::") + }; let (impl_generics, ty_generics, where_clause) = reflect.generics.split_for_impl(); TokenStream::from(quote! { impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause { fn name(&self) -> ::std::string::String { - #name.to_string() + #type_path_str.to_string() } fn type_id(&self) -> std::any::TypeId { diff --git a/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs index f78fb63..1661e16 100644 --- a/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs +++ b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs @@ -266,7 +266,8 @@ fn gen_struct_field_name_idx(data: &DataStruct) -> proc_macro2::TokenStream { /// Generates a token stream that implements Reflect and Struct for the provided struct pub fn derive_reflect_struct(input: &DeriveInput, data_struct: &DataStruct) -> proc_macro2::TokenStream { let type_path = &input.ident; - let name = type_path.span().source_text().unwrap(); + let name = type_path.to_string(); + //let name = type_path.span().source_text().unwrap(); let field_len = data_struct.fields.len(); let get_field_match = gen_struct_field_match(data_struct, false); -- 2.40.1 From 544aee4a31cdab34588169cafe23cd5c09859b16 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 12 Jan 2024 14:08:46 -0500 Subject: [PATCH 05/41] reflect: Implement reflect for glam types --- lyra-reflect/Cargo.toml | 6 +- lyra-reflect/lyra-reflect-derive/src/lib.rs | 53 +++- .../lyra-reflect-derive/src/struct_derive.rs | 259 ++++++++++++----- .../lyra-reflect-derive/src/struct_macro.rs | 270 ++++++++++++++++++ lyra-reflect/src/impls/impl_math.rs | 10 + lyra-reflect/src/{ => impls}/impl_std.rs | 2 +- lyra-reflect/src/impls/mod.rs | 4 + lyra-reflect/src/lib.rs | 4 +- lyra-reflect/src/reflected_enum.rs | 1 - lyra-reflect/src/reflected_struct.rs | 1 - lyra-reflect/src/registry.rs | 6 + 11 files changed, 531 insertions(+), 85 deletions(-) create mode 100644 lyra-reflect/lyra-reflect-derive/src/struct_macro.rs create mode 100644 lyra-reflect/src/impls/impl_math.rs rename lyra-reflect/src/{ => impls}/impl_std.rs (98%) create mode 100644 lyra-reflect/src/impls/mod.rs diff --git a/lyra-reflect/Cargo.toml b/lyra-reflect/Cargo.toml index db57f34..89643d2 100644 --- a/lyra-reflect/Cargo.toml +++ b/lyra-reflect/Cargo.toml @@ -5,6 +5,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +math = ["dep:glam"] + [dependencies] lyra-reflect-derive = { path = "lyra-reflect-derive" } -lyra-ecs = { path = "../lyra-ecs" } \ No newline at end of file +lyra-ecs = { path = "../lyra-ecs" } +glam = { version = "0.24.0", optional = true } \ No newline at end of file diff --git a/lyra-reflect/lyra-reflect-derive/src/lib.rs b/lyra-reflect/lyra-reflect-derive/src/lib.rs index 2d5b1a3..a8b8ff0 100644 --- a/lyra-reflect/lyra-reflect-derive/src/lib.rs +++ b/lyra-reflect/lyra-reflect-derive/src/lib.rs @@ -11,6 +11,52 @@ mod struct_derive; #[allow(unused_imports)] use struct_derive::*; +mod struct_macro; + +/* #[proc_macro_attribute(attributes(reflect))] +pub fn reflect(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { + item +} */ + +pub(crate) struct FieldAttributes(Vec); + +impl FieldAttributes { + /// Searches for a usage of the 'reflect' attribute and returns a list of the + /// things used in the usage. + pub fn from_vec(v: &Vec) -> Result { + let s: Result, _> = v.iter().filter_map(|att| match &att.meta { + syn::Meta::Path(_) => None, + syn::Meta::List(l) => { + Some(syn::parse::(l.tokens.clone().into())) + } + syn::Meta::NameValue(_) => None + }).collect(); + + Ok(Self(s?)) + } + + pub fn has_skip(&self) -> bool { + self.0.iter().any(|a| *a == ReflectAttribute::Skip) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum ReflectAttribute { + Skip +} + +impl syn::parse::Parse for ReflectAttribute { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let ident: Ident = input.parse()?; + let ident_str = ident.to_string().to_lowercase(); + + match ident_str.as_str() { + "skip" => Ok(Self::Skip), + _ => Err(syn::Error::new(ident.span(), "Unknown reflect attribute flag")) + } + } +} + #[allow(dead_code)] pub(crate) struct ReflectDef { //pub ident: Ident, @@ -36,7 +82,7 @@ impl syn::parse::Parse for ReflectDef { } } -#[proc_macro_derive(Reflect)] +#[proc_macro_derive(Reflect, attributes(reflect))] pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -127,4 +173,9 @@ pub(crate) fn add_trait_bounds(mut generics: Generics, add_bounds: Vec TokenStream { + struct_macro::impl_reflect_simple_struct(input) } \ No newline at end of file diff --git a/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs index 1661e16..1f54547 100644 --- a/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs +++ b/lyra-reflect/lyra-reflect-derive/src/struct_derive.rs @@ -1,7 +1,28 @@ use quote::{quote, ToTokens}; use syn::{DeriveInput, parse_quote, DataStruct}; -use crate::add_trait_bounds; +use crate::{add_trait_bounds, FieldAttributes}; + +#[derive(Debug, PartialEq, Eq)] +enum StructType { + Unit, + Named, + Tuple, +} + +impl StructType { + pub fn new(data: &DataStruct) -> Self { + if let Some(first) = data.fields.iter().next() { + if first.ident.is_some() { + Self::Named + } else { + Self::Tuple + } + } else { + Self::Unit + } + } +} /// Generates code that matches a string with a struct's field name, and returns an Option that /// contains a borrow (mutable borrow if `is_mut` is true) to the matching struct field. @@ -22,28 +43,42 @@ use crate::add_trait_bounds; /// _ => None, /// } /// ``` +/// +/// If the struct is a unit or tuple struct, None will always be returned fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream { - let mut_tkn = if is_mut { - quote! { - mut - } - } else { quote!{} }; - - let field_arms = data.fields.iter().map(|field| { - let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); - let field_name_str = field_ident.to_string(); - - quote! { - #field_name_str => Some(&#mut_tkn self.#field_ident) - } - }); + let ty = StructType::new(data); - quote! { - match name { - #(#field_arms,)* - _ => None, + if ty == StructType::Named { + let mut_tkn = if is_mut { + quote! { + mut + } + } else { quote!{} }; + + let field_arms = data.fields.iter().map(|field| { + let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); + let field_name_str = field_ident.to_string(); + + let attrs = FieldAttributes::from_vec(&field.attrs) + .expect("Failure to parse reflect attributes"); + if attrs.has_skip() { + quote! { + #field_name_str => None + } + } else { + quote! { + #field_name_str => Some(&#mut_tkn self.#field_ident) + } + } + }); + + quote! { + match name { + #(#field_arms,)* + _ => None, + } } - } + } else { quote!(None) } } /// Generates code that matches a string with a struct's field name, and sets that field value @@ -64,27 +99,41 @@ fn gen_struct_field_match(data: &DataStruct, is_mut: bool) -> proc_macro2::Token /// } /// ``` fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream { - let field_arms = data.fields.iter().map(|field| { - let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); - let field_name_str = field_ident.to_string(); - let field_ty = &field.ty; - - quote! { - #field_name_str => self.#field_ident = any_val.downcast_ref::<#field_ty>() - .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name())) - .clone() //Some(&#mut_tkn self.#field_ident) - } - }); + let ty = StructType::new(data); - quote! { - let any_val = val.as_any(); - match name { - #(#field_arms,)* - _ => { - return false; - }, + if ty == StructType::Named { + let field_arms = data.fields.iter().map(|field| { + let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); + let field_name_str = field_ident.to_string(); + let field_ty = &field.ty; + + let attrs = FieldAttributes::from_vec(&field.attrs) + .expect("Failure to parse reflect attributes"); + if attrs.has_skip() { + quote! { + #field_name_str => {} + } + } else { + quote! { + #field_name_str => self.#field_ident = any_val.downcast_ref::<#field_ty>() + .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name())) + .clone() //Some(&#mut_tkn self.#field_ident) + } + } + }); + + quote! { + let any_val = val.as_any(); + match name { + #(#field_arms,)* + _ => { + return false; + }, + } + + true } - } + } else { quote!(return false;) } } /// Generates code that matches a string with a struct's field name, and returns a string that is @@ -99,25 +148,29 @@ fn gen_struct_set_field_match(data: &DataStruct) -> proc_macro2::TokenStream { /// } /// ``` fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream { - let field_arms = data.fields.iter().map(|field| { - let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); - let field_name_str = field_ident.to_string(); + let ty = StructType::new(data); + + if ty == StructType::Named { + let field_arms = data.fields.iter().map(|field| { + let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); + let field_name_str = field_ident.to_string(); + + let mut field_ty_stream = proc_macro2::TokenStream::new(); + field.ty.to_tokens(&mut field_ty_stream); + let s = field_ty_stream.to_string(); + + quote! { + #field_name_str => Some(#s) + } + }); - let mut field_ty_stream = proc_macro2::TokenStream::new(); - field.ty.to_tokens(&mut field_ty_stream); - let s = field_ty_stream.to_string(); - quote! { - #field_name_str => Some(#s) + match name { + #(#field_arms,)* + _ => None, + } } - }); - - quote! { - match name { - #(#field_arms,)* - _ => None, - } - } + } else { quote!(None) } } /// Generates code that matches a string with a struct's field name, and sets that field value @@ -138,15 +191,38 @@ fn gen_struct_field_name_match(data: &DataStruct) -> proc_macro2::TokenStream { /// } /// ``` fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream { + let ty = StructType::new(data); + + if ty == StructType::Unit { + return quote!( return false; ); + } + let field_arms = data.fields.iter().enumerate().map(|(idx, field)| { - let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); - let field_name_str = field_ident.to_string(); let field_ty = &field.ty; - - quote! { - #idx => self.#field_ident = any_val.downcast_ref::<#field_ty>() - .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", #field_name_str, val.name())) - .clone() + + let attrs = FieldAttributes::from_vec(&field.attrs) + .expect("Failure to parse reflect attributes"); + if attrs.has_skip() { + return quote! { + #idx => {} + }; + } + + if let Some(field_ident) = &field.ident { + let field_name_str = field_ident.to_string(); + + quote! { + #idx => self.#field_ident = any_val.downcast_ref::<#field_ty>() + .expect(&format!("Cannot set struct's field of {} to the provided type of {}", #field_name_str, val.name())) + .clone() + } + } else { + let sidx = syn::Index::from(idx); + quote! { + #idx => self.#sidx = any_val.downcast_ref::<#field_ty>() + .expect(&format!("Cannot set struct's field at {} to the provided type of {}", #idx, val.name())) + .clone() + } } }); @@ -158,6 +234,8 @@ fn gen_struct_set_field_match_idx(data: &DataStruct) -> proc_macro2::TokenStream return false; }, } + + true } } @@ -212,6 +290,12 @@ fn gen_struct_field_name_match_idx(data: &DataStruct) -> proc_macro2::TokenStrea /// } /// ``` fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::TokenStream { + let ty = StructType::new(data); + + if ty == StructType::Unit { + return quote!(None); + } + let mut_tkn = if is_mut { quote! { mut @@ -219,10 +303,23 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T } else { quote!{} }; let field_arms = data.fields.iter().enumerate().map(|(idx, field)| { - let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); + let attrs = FieldAttributes::from_vec(&field.attrs) + .expect("Failure to parse reflect attributes"); + if attrs.has_skip() { + return quote! { + #idx => None + }; + } - quote! { - #idx => Some(&#mut_tkn self.#field_ident) + if let Some(field_ident) = &field.ident { + quote! { + #idx => Some(&#mut_tkn self.#field_ident) + } + } else { + let sidx = syn::Index::from(idx); + quote! { + #idx => Some(&#mut_tkn self.#sidx) + } } }); @@ -246,20 +343,26 @@ fn gen_struct_field_match_idx(data: &DataStruct, is_mut: bool) -> proc_macro2::T /// } /// ``` fn gen_struct_field_name_idx(data: &DataStruct) -> proc_macro2::TokenStream { - let field_arms = data.fields.iter().enumerate().map(|(idx, field)| { - let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); - let field_name_str = field_ident.to_string(); - - quote! { - #idx => Some(#field_name_str) - } - }); + let ty = StructType::new(data); - quote! { - match idx { - #(#field_arms,)* - _ => None, + if ty == StructType::Named { + let field_arms = data.fields.iter().enumerate().map(|(idx, field)| { + let field_ident = field.ident.as_ref().expect("Struct is missing field ident!"); + let field_name_str = field_ident.to_string(); + + quote! { + #idx => Some(#field_name_str) + } + }); + + quote! { + match idx { + #(#field_arms,)* + _ => None, + } } + } else { + quote!(None) } } @@ -361,12 +464,10 @@ pub fn derive_reflect_struct(input: &DeriveInput, data_struct: &DataStruct) -> p fn set_field(&mut self, name: &str, val: &dyn lyra_engine::reflect::Reflect) -> bool { #set_field_named - true } fn set_field_at(&mut self, idx: usize, val: &dyn lyra_engine::reflect::Reflect) -> bool { #set_field_idx - true } fn field_type(&self, name: &str) -> Option<&'static str> { diff --git a/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs b/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs new file mode 100644 index 0000000..d1c9b5a --- /dev/null +++ b/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs @@ -0,0 +1,270 @@ +use quote::quote; +use syn::{Token, parenthesized, punctuated::Punctuated}; + +struct Field { + name: syn::Ident, + _eq: Token![=], + ty: syn::Ident, +} + +impl syn::parse::Parse for Field { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + Ok(Self { + name: input.parse()?, + _eq: input.parse()?, + ty: input.parse()?, + }) + } +} + +struct SimpleStruct { + type_path: syn::Path, + pub generics: syn::Generics, + fields: Vec, +} + +impl syn::parse::Parse for SimpleStruct { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + //let type_path = syn::Path::parse_mod_style(input)?; + let type_path: syn::Path = input.parse()?; + /* let mut generics = input.parse::()?; + generics.where_clause = input.parse()?; */ + + let mut fields = vec![]; + + // parse fields if a comma is found + if input.peek(Token![,]) { + let _: Token![,] = input.parse()?; + let ident: syn::Ident = input.parse()?; + let ident_str = ident.to_string(); + + match ident_str.as_str() { + "fields" => { + let content; + let _parens: syn::token::Paren = parenthesized!(content in input); + + let f: Punctuated = content.parse_terminated(Field::parse, Token![,])?; + fields = f.into_iter().collect(); + }, + _ => return Err(syn::Error::new(ident.span(), "Unknown macro command, expected `fields`")), + } + } + + Ok(Self { + type_path, + generics: syn::Generics::default(), + fields, + }) + + } +} + + +fn gen_match_field_name_arm(simple: &SimpleStruct, default: &proc_macro2::TokenStream, arm_gen: fn(field: &Field) -> proc_macro2::TokenStream) -> proc_macro2::TokenStream { + let field_arms_iter = simple.fields.iter().map(|f| { + let fname = &f.name; + let arm = arm_gen(f); + + quote! { + stringify!(#fname) => #arm + } + }); + + quote! { + match name { + #(#field_arms_iter,)* + _ => #default, + } + } +} + +fn gen_match_field_index_arm(simple: &SimpleStruct, default: &proc_macro2::TokenStream, arm_gen: fn(field: &Field) -> proc_macro2::TokenStream) -> proc_macro2::TokenStream { + let field_arms_iter = simple.fields.iter().enumerate().map(|(idx, f)| { + let arm = arm_gen(f); + + quote! { + #idx => #arm + } + }); + + quote! { + match idx { + #(#field_arms_iter,)* + _ => #default, + } + } +} + +pub(crate) fn impl_reflect_simple_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let simple = syn::parse_macro_input!(input as SimpleStruct); + + let type_path = &simple.type_path; + let (impl_generics, ty_generics, where_clause) = simple.generics.split_for_impl(); + // convert the type path to a string. This would not create a leading separator + /* let type_path_str = { + let idents: Vec = type_path.segments.iter() + .map(|segment| segment.ident.to_string()) + .collect(); + idents.join("::") + }; */ + + let borrow_fn = |field: &Field| { + let name = &field.name; + quote! { + Some(&self.#name) + } + }; + + let borrow_mut_fn = |field: &Field| { + let name = &field.name; + quote! { + Some(&mut self.#name) + } + }; + + let none_default = quote!(None); + let false_return_default = quote!( { return false; }); + + let field_count = simple.fields.len(); + + let field_fn = gen_match_field_name_arm(&simple, &none_default, borrow_fn); + let field_mut_fn = gen_match_field_name_arm(&simple, &none_default, borrow_mut_fn); + let field_at_fn = gen_match_field_index_arm(&simple, &none_default, borrow_fn); + let field_at_mut_fn = gen_match_field_index_arm(&simple, &none_default, borrow_mut_fn); + let field_name_at_fn = gen_match_field_index_arm(&simple, &none_default, |f| { + let name = &f.name; + quote! { + Some(stringify!(#name)) + } + }); + + let set_field_arm = |f: &Field| { + let name = &f.name; + let ty = &f.ty; + quote! { + self.#name = any_val.downcast_ref::<#ty>() + .expect(&format!("Cannot set struct's field of {} type to the provided type of {}", stringify!(#name), val.name())) + .clone() //Some(&#mut_tkn self.#field_ident) + } + }; + + let set_field_fn = gen_match_field_name_arm(&simple, &false_return_default, set_field_arm); + let set_field_at_fn = gen_match_field_index_arm(&simple, &false_return_default, set_field_arm); + + let get_field_ty_arm = |f: &Field| { + let fty = &f.ty; + quote! { + Some(stringify!(#fty)) + } + }; + let field_type_fn = gen_match_field_name_arm(&simple, &none_default, get_field_ty_arm); + let field_type_at_fn = gen_match_field_index_arm(&simple, &none_default, get_field_ty_arm); + + proc_macro::TokenStream::from(quote! { + impl #impl_generics lyra_engine::reflect::Reflect for #type_path #ty_generics #where_clause { + fn name(&self) -> ::std::string::String { + stringify!(#type_path).to_string() + } + + fn type_id(&self) -> std::any::TypeId { + std::any::TypeId::of::<#type_path #ty_generics>() + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) { + let val = val.as_any().downcast_ref::() + .expect("The type of `val` is not the same as `self`"); + *self = val.clone(); + } + + fn clone_inner(&self) -> Box { + Box::new(self.clone()) + } + + fn reflect_ref(&self) -> lyra_engine::reflect::ReflectRef { + lyra_engine::reflect::ReflectRef::Value(self) + } + + fn reflect_mut(&mut self) -> lyra_engine::reflect::ReflectMut { + lyra_engine::reflect::ReflectMut::Value(self) + } + + fn reflect_val(&self) -> &dyn lyra_engine::reflect::Reflect { + self + } + + fn reflect_val_mut(&mut self) -> &mut dyn lyra_engine::reflect::Reflect { + self + } + } + + impl #impl_generics lyra_engine::reflect::Struct for #type_path #ty_generics #where_clause { + fn field(&self, name: &str) -> Option<&dyn Reflect> { + #field_fn + } + + fn field_mut(&mut self, name: &str) -> Option<&mut dyn Reflect> { + #field_mut_fn + } + + fn fields_len(&self) -> usize { + #field_count + } + + fn field_at(&self, idx: usize) -> Option<&dyn Reflect> { + #field_at_fn + } + + fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect> { + #field_at_mut_fn + } + + fn field_name_at(&self, idx: usize) -> Option<&str> { + #field_name_at_fn + } + + fn set_field(&mut self, name: &str, val: &dyn Reflect) -> bool { + let any_val = val.as_any(); + + #set_field_fn + + true + } + + fn set_field_at(&mut self, idx: usize, val: &dyn Reflect) -> bool { + let any_val = val.as_any(); + + #set_field_at_fn + + true + } + + fn field_type(&self, name: &str) -> Option<&'static str> { + #field_type_fn + } + + fn field_type_at(&self, idx: usize) -> Option<&'static str> { + #field_type_at_fn + } + + fn method(&self, name: &str) -> Option<&Method> { + unimplemented!() + } + + fn method_at(&self, idx: usize) -> Option<&Method> { + unimplemented!() + } + + fn methods_len(&self) -> usize { + unimplemented!() + } + } + }) +} diff --git a/lyra-reflect/src/impls/impl_math.rs b/lyra-reflect/src/impls/impl_math.rs new file mode 100644 index 0000000..27a3e0f --- /dev/null +++ b/lyra-reflect/src/impls/impl_math.rs @@ -0,0 +1,10 @@ +use lyra_reflect_derive::{impl_reflect_simple_struct, impl_reflect_trait_value}; + +use crate::{lyra_engine, Reflect, Method}; + +impl_reflect_simple_struct!(glam::Vec2, fields(x=f32, y=f32)); +impl_reflect_simple_struct!(glam::Vec3, fields(x=f32, y=f32, z=f32)); +impl_reflect_simple_struct!(glam::Vec4, fields(x=f32, y=f32, z=f32, w=f32)); +impl_reflect_simple_struct!(glam::Quat, fields(x=f32, y=f32, z=f32, w=f32)); + +impl_reflect_trait_value!(glam::Mat4); \ No newline at end of file diff --git a/lyra-reflect/src/impl_std.rs b/lyra-reflect/src/impls/impl_std.rs similarity index 98% rename from lyra-reflect/src/impl_std.rs rename to lyra-reflect/src/impls/impl_std.rs index d756f89..9c135e0 100644 --- a/lyra-reflect/src/impl_std.rs +++ b/lyra-reflect/src/impls/impl_std.rs @@ -4,7 +4,7 @@ use crate::List; use crate::lyra_engine; -use super::{Reflect, ReflectRef, ReflectMut, util}; +use crate::{Reflect, ReflectRef, ReflectMut, util}; impl_reflect_trait_value!(bool); impl_reflect_trait_value!(char); diff --git a/lyra-reflect/src/impls/mod.rs b/lyra-reflect/src/impls/mod.rs new file mode 100644 index 0000000..64af2fb --- /dev/null +++ b/lyra-reflect/src/impls/mod.rs @@ -0,0 +1,4 @@ +pub mod impl_std; + +#[cfg(feature="math")] +pub mod impl_math; \ No newline at end of file diff --git a/lyra-reflect/src/lib.rs b/lyra-reflect/src/lib.rs index ae5c233..2b6bd24 100644 --- a/lyra-reflect/src/lib.rs +++ b/lyra-reflect/src/lib.rs @@ -6,6 +6,8 @@ use lyra_ecs::{world::World, DynamicBundle, Component, Entity, ComponentInfo}; extern crate self as lyra_reflect; +pub use lyra_reflect_derive::*; + #[allow(unused_imports)] pub(crate) mod lyra_engine { pub(crate) mod reflect { @@ -42,7 +44,7 @@ pub use method::*; pub mod registry; pub use registry::*; -pub mod impl_std; +pub mod impls; pub trait Reflect: Any { fn name(&self) -> String; diff --git a/lyra-reflect/src/reflected_enum.rs b/lyra-reflect/src/reflected_enum.rs index abbdaf9..ebfed11 100644 --- a/lyra-reflect/src/reflected_enum.rs +++ b/lyra-reflect/src/reflected_enum.rs @@ -52,7 +52,6 @@ pub trait Enum: Reflect { #[allow(unused_variables)] #[cfg(test)] mod tests { - use lyra_reflect_derive::Reflect; use super::EnumType; use crate::{lyra_engine, Reflect, ReflectRef}; diff --git a/lyra-reflect/src/reflected_struct.rs b/lyra-reflect/src/reflected_struct.rs index ce3ec1a..72904ca 100644 --- a/lyra-reflect/src/reflected_struct.rs +++ b/lyra-reflect/src/reflected_struct.rs @@ -41,7 +41,6 @@ pub trait Struct: Reflect { #[cfg(test)] mod tests { - use lyra_reflect_derive::Reflect; use crate::{Reflect, ReflectRef, ReflectMut}; use crate::lyra_engine; diff --git a/lyra-reflect/src/registry.rs b/lyra-reflect/src/registry.rs index 0311f10..46651f4 100644 --- a/lyra-reflect/src/registry.rs +++ b/lyra-reflect/src/registry.rs @@ -21,6 +21,12 @@ impl TypeRegistry { self.inner.get_mut(&type_id) } + /// Get a registered type, or register a new type and return it. + pub fn get_type_or_default(&mut self, type_id: TypeId) -> &mut RegisteredType { + self.inner.entry(type_id) + .or_insert(RegisteredType::default()) + } + pub fn register_type(&mut self) where T: AsRegisteredType + 'static -- 2.40.1 From acfd238274f360767faf32b959680a948ccd6528 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 12 Jan 2024 14:09:29 -0500 Subject: [PATCH 06/41] ecs: implement component for some glam types --- lyra-ecs/Cargo.toml | 4 ++++ lyra-ecs/src/lib.rs | 4 ++++ lyra-ecs/src/math.rs | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 lyra-ecs/src/math.rs diff --git a/lyra-ecs/Cargo.toml b/lyra-ecs/Cargo.toml index 5c9afd2..8249429 100644 --- a/lyra-ecs/Cargo.toml +++ b/lyra-ecs/Cargo.toml @@ -5,10 +5,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +math = ["dep:glam"] + [dependencies] lyra-ecs-derive = { path = "./lyra-ecs-derive" } anyhow = "1.0.75" thiserror = "1.0.50" +glam = { version = "0.24.0", optional = true } [dev-dependencies] rand = "0.8.5" # used for tests diff --git a/lyra-ecs/src/lib.rs b/lyra-ecs/src/lib.rs index f42b377..e22f7f8 100644 --- a/lyra-ecs/src/lib.rs +++ b/lyra-ecs/src/lib.rs @@ -34,6 +34,10 @@ pub mod system; pub mod tick; pub use tick::*; +/// Implements Component for glam math types +#[cfg(feature = "math")] +pub mod math; + pub use lyra_ecs_derive::*; #[cfg(test)] diff --git a/lyra-ecs/src/math.rs b/lyra-ecs/src/math.rs new file mode 100644 index 0000000..045b70f --- /dev/null +++ b/lyra-ecs/src/math.rs @@ -0,0 +1,19 @@ +use crate::Component; + +use glam::{Vec3, Quat, Vec2, Vec4, Mat4}; + +macro_rules! impl_component_math { + ($type: ident) => { + impl Component for $type { + fn name() -> &'static str { + stringify!($type) + } + } + }; +} + +impl_component_math!(Vec2); +impl_component_math!(Vec3); +impl_component_math!(Vec4); +impl_component_math!(Mat4); +impl_component_math!(Quat); \ No newline at end of file -- 2.40.1 From d14abcc3e592cff1b7d3f6f046a57ab4c8f615c2 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 12 Jan 2024 14:11:33 -0500 Subject: [PATCH 07/41] Get some math types and some ecs stuff exposed to lua --- lyra-game/Cargo.toml | 4 +- lyra-game/src/game.rs | 2 +- lyra-game/src/math/transform.rs | 3 +- lyra-scripting/Cargo.toml | 7 +- .../lyra-scripting-derive/Cargo.toml | 14 + .../lyra-scripting-derive/src/lib.rs | 606 ++++++++++++++++++ .../lyra-scripting-derive/src/mat_wrapper.rs | 142 ++++ lyra-scripting/scripts/lua/init.lua | 0 lyra-scripting/scripts/lua/math/quat.lua | 177 +++++ lyra-scripting/scripts/lua/math/transform.lua | 94 +++ lyra-scripting/scripts/lua/math/vec3.lua | 161 +++++ lyra-scripting/src/host.rs | 5 +- lyra-scripting/src/lib.rs | 21 +- lyra-scripting/src/lua/dynamic_iter.rs | 2 +- lyra-scripting/src/lua/mod.rs | 50 +- lyra-scripting/src/lua/providers/ecs.rs | 26 + lyra-scripting/src/lua/providers/math.rs | 108 ++++ lyra-scripting/src/lua/providers/mod.rs | 8 + .../lua/{modules/mod.rs => providers/util.rs} | 0 lyra-scripting/src/lua/script.rs | 4 +- lyra-scripting/src/lua/world.rs | 6 +- lyra-scripting/src/lua/wrappers/mod.rs | 365 +++++++++++ 22 files changed, 1784 insertions(+), 21 deletions(-) create mode 100644 lyra-scripting/lyra-scripting-derive/Cargo.toml create mode 100644 lyra-scripting/lyra-scripting-derive/src/lib.rs create mode 100644 lyra-scripting/lyra-scripting-derive/src/mat_wrapper.rs create mode 100644 lyra-scripting/scripts/lua/init.lua create mode 100644 lyra-scripting/scripts/lua/math/quat.lua create mode 100644 lyra-scripting/scripts/lua/math/transform.lua create mode 100644 lyra-scripting/scripts/lua/math/vec3.lua create mode 100644 lyra-scripting/src/lua/providers/ecs.rs create mode 100644 lyra-scripting/src/lua/providers/math.rs create mode 100644 lyra-scripting/src/lua/providers/mod.rs rename lyra-scripting/src/lua/{modules/mod.rs => providers/util.rs} (100%) create mode 100644 lyra-scripting/src/lua/wrappers/mod.rs diff --git a/lyra-game/Cargo.toml b/lyra-game/Cargo.toml index 87d853f..3579672 100644 --- a/lyra-game/Cargo.toml +++ b/lyra-game/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" [dependencies] lyra-resource = { path = "../lyra-resource" } -lyra-ecs = { path = "../lyra-ecs" } -lyra-reflect = { path = "../lyra-reflect" } +lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } +lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] } winit = "0.28.1" tracing = "0.1.37" diff --git a/lyra-game/src/game.rs b/lyra-game/src/game.rs index 0a49e45..a3d8666 100755 --- a/lyra-game/src/game.rs +++ b/lyra-game/src/game.rs @@ -342,7 +342,7 @@ impl Game { .with(fmt::layer().with_writer(stdout_layer)) .with(filter::Targets::new() // done by prefix, so it includes all lyra subpackages - .with_target("lyra", Level::TRACE) + .with_target("lyra", Level::DEBUG) .with_target("wgpu", Level::WARN) .with_default(Level::INFO)) .init(); diff --git a/lyra-game/src/math/transform.rs b/lyra-game/src/math/transform.rs index 9e02ae1..f2aa027 100755 --- a/lyra-game/src/math/transform.rs +++ b/lyra-game/src/math/transform.rs @@ -89,8 +89,7 @@ impl Transform { /// Performs a linear interpolation between `self` and `rhs` based on the value `alpha`. /// /// When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`, the result - /// will be equal to `rhs`. When `alpha` is outside of range `[0, 1]`, the result is linearly - /// extrapolated. + /// will be equal to `rhs`. pub fn lerp(&self, rhs: Transform, alpha: f32) -> Self { if alpha.is_finite() { diff --git a/lyra-scripting/Cargo.toml b/lyra-scripting/Cargo.toml index 4e2aec6..46539a6 100644 --- a/lyra-scripting/Cargo.toml +++ b/lyra-scripting/Cargo.toml @@ -10,8 +10,9 @@ default = ["lua"] lua = ["dep:mlua"] [dependencies] -lyra-ecs = { path = "../lyra-ecs" } -lyra-reflect = { path = "../lyra-reflect" } +lyra-scripting-derive = { path = "lyra-scripting-derive" } +lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } +lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] } lyra-resource = { path = "../lyra-resource" } lyra-game = { path = "../lyra-game" } thiserror = "1.0.50" @@ -23,4 +24,4 @@ mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit may [dev-dependencies] -tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] } \ No newline at end of file +tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] } diff --git a/lyra-scripting/lyra-scripting-derive/Cargo.toml b/lyra-scripting/lyra-scripting-derive/Cargo.toml new file mode 100644 index 0000000..9595a9d --- /dev/null +++ b/lyra-scripting/lyra-scripting-derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lyra-scripting-derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.70" +quote = "1.0.33" +syn = "2.0.41" diff --git a/lyra-scripting/lyra-scripting-derive/src/lib.rs b/lyra-scripting/lyra-scripting-derive/src/lib.rs new file mode 100644 index 0000000..a0f40c1 --- /dev/null +++ b/lyra-scripting/lyra-scripting-derive/src/lib.rs @@ -0,0 +1,606 @@ +use proc_macro2::{Ident, Span}; +use quote::quote; +use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced, bracketed}; + +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"; + +pub(crate) struct MetaMethod { + pub ident: Ident, + pub mods: Vec, +} + +impl syn::parse::Parse for MetaMethod { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let ident: Ident = input.parse()?; + + let mods = if input.peek(token::Paren) { + let content; + let _parens: token::Paren = parenthesized!(content in input); + content.parse_terminated(Ident::parse, Token![,])? + .into_iter().collect() + } else { vec![] }; + + Ok(Self { + ident, + mods, + }) + } +} + +pub(crate) struct VecWrapper { + +} + +impl VecWrapper { + fn vec_size(&self, wrapper_ident: &Ident) -> usize { + let name = wrapper_ident.to_string(); + name[name.len() - 1..].parse::() + .or_else(|_| name[name.len() - 2.. name.len() - 1].parse::()) + .expect("Failure to grab Vec size from ident name") + } + + /// Returns the token stream of the type of the axis of the vec (Vec2 vs IVec2 vs I64Vec2, etc.) + fn vec_axis_type(&self, wrapper_ident: &Ident) -> &'static str { + let name = wrapper_ident.to_string(); + let start = name.find("Vec").unwrap(); + + let before = &name[start - 1.. start]; + match before { + "D" => return "f64", + "I" => return "i32", + "U" => return "u32", + "B" => return "bool", + _ => {}, + } + //println!("before is {before}"); + + let three_before = &name[start - 3.. start]; + match three_before { + "I64" => return "i64", + "U64" => return "u64", + _ => {}, + } + //println!("three before is {three_before}"); + + "f32" + } + + pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { + let mut consts = vec![quote!(ZERO), quote!(ONE), quote!(X), + quote!(Y), ]; // , quote!(AXES) + + let vec_size = self.vec_size(wrapper_ident); + let axis_type_name = self.vec_axis_type(wrapper_ident); + + if axis_type_name.contains("b") { + return quote! { + fields.add_field("FALSE", #wrapper_ident(#wrapped_path::FALSE)); + fields.add_field("TRUE", #wrapper_ident(#wrapped_path::TRUE)); + }; + } + + if vec_size >= 3 { + consts.push(quote!(Z)); + + // no negative numbers for unsigned vecs + if !axis_type_name.contains("u") { + consts.push(quote!(NEG_Z)); + } + } + + if vec_size == 4 { + consts.push(quote!(W)); + + // no negative numbers for unsigned vecs + if !axis_type_name.contains("u") { + consts.push(quote!(NEG_W)); + } + } + + // no negative numbers for unsigned vecs + if !axis_type_name.contains("u") { + consts.push(quote!(NEG_X)); + consts.push(quote!(NEG_Y)); + consts.push(quote!(NEG_ONE)); + } + + if axis_type_name.contains("f") { + consts.push(quote!(NAN)) + } + + let const_tokens = consts.iter().map(|cnst| { + let const_name = cnst.to_string(); + + quote! { + fields.add_field(#const_name, #wrapper_ident(#wrapped_path::#cnst)); + } + }); + + quote! { + #(#const_tokens)* + } + } + + pub fn to_method_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { + let vec_size = self.vec_size(wrapper_ident); + let axis_type_name = self.vec_axis_type(wrapper_ident); + // methods that only some vecs have + let mut optional_methods = vec![]; + + // boolean vectors dont have much :( + if axis_type_name.contains("b") { + return quote!(); // TODO: all, any, bitmask, splat + } + + if axis_type_name.contains("f") { + let type_id = Ident::new(axis_type_name, Span::call_site()); + + optional_methods.push( + quote! { + methods.add_method("clamp_length", + |_, this, (min, max): (#type_id, #type_id)| { + Ok(#wrapper_ident(this.clamp_length(min, max))) + }); + + methods.add_method("abs_diff_eq", + |_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| { + Ok(this.abs_diff_eq(rhs.0, max_abs_diff)) + }); + + methods.add_method("ceil", + |_, this, (): ()| { + Ok(#wrapper_ident(this.ceil())) + }); + } + ); + + if vec_size != 4 { + optional_methods.push( + quote! { + methods.add_method("angle_between", + |_, this, (rhs,): (#wrapper_ident,)| { + Ok(this.angle_between(rhs.0)) + }); + } + ) + } + } + + if !axis_type_name.contains("u") { + optional_methods.push( + quote! { + methods.add_method("abs", + |_, this, (): ()| { + Ok(#wrapper_ident(this.abs())) + }); + } + ) + } + + let optional_methods_iter = optional_methods.iter(); + quote! { + + + methods.add_method("clamp", + |_, this, (min, max): (#wrapper_ident, #wrapper_ident)| { + Ok(#wrapper_ident(this.clamp(min.0, max.0))) + }); + + // TODO: Not all Vecs have this + /* methods.add_method("clamp_length", + |_, this, (min, max): (f32, f32)| { + Ok(#wrapper_ident(this.clamp_length(min, max))) + }); */ + + + methods.add_method("to_array", + |_, this, (): ()| { + Ok(this.to_array()) + }); + + #(#optional_methods_iter)* + } + } +} + +pub(crate) struct WrapUsage { + pub type_path: Path, + /// The extra derives of the type. + pub derive_idents: Punctuated, + /// The field idents of the type that will be exposed with gets and sets + pub field_idents: Punctuated, + pub skip_new_fn: bool, + /// The identifiers that are taken as parameters in the types 'new' function + pub new_fn_idents: Punctuated, + pub meta_method_idents: Punctuated, + + pub matrix: Option, + pub vec: Option, +} + +impl syn::parse::Parse for WrapUsage { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let type_path: Path = input.parse()?; + let mut s = Self { + type_path, + derive_idents: Punctuated::default(), + field_idents: Punctuated::default(), + skip_new_fn: false, + new_fn_idents: Punctuated::default(), + meta_method_idents: Punctuated::default(), + matrix: None, + vec: None, + }; + /* let mut derive_idents = None; + let mut field_idents = None; + let mut new_fn_idents = None; */ + + while input.peek(Token![,]) { + let _: Token![,] = input.parse()?; + //println!("Peeked a , ({:?})", input); + + if input.peek(syn::Ident) { + let ident: Ident = input.parse()?; + let ident_str = ident.to_string(); + let ident_str = ident_str.as_str(); + + match ident_str { + "derives" => { + if input.peek(token::Paren) { + let content; + let _parens: token::Paren = parenthesized!(content in input); + + let derives: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + s.derive_idents = derives; + //println!("read derives: {:?}", s.derive_idents); + } + }, + "fields" => { + if input.peek(token::Paren) { + let content; + let _parens: token::Paren = parenthesized!(content in input); + + let fields: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + s.field_idents = fields; + //println!("read fields: {:?}", s.field_idents); + } + }, + "new" => { + if input.peek(token::Paren) { + let content; + let _parens: token::Paren = parenthesized!(content in input); + + let fields: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + s.new_fn_idents = fields; + //println!("read fields: {:?}", s.new_fn_idents); + } + }, + "no_new" => { + s.skip_new_fn = true; + }, + "matrix" => { + if input.peek(token::Brace) { + let content; + let _braces = braced!(content in input); + s.matrix = Some(content.parse()?); + } + }, + "metamethods" => { + if input.peek(token::Paren) { + let content; + let _bracket: token::Paren = parenthesized!(content in input); + + let meta_methods: Punctuated = content.parse_terminated(MetaMethod::parse, Token![,])?; + s.meta_method_idents = meta_methods; + } + }, + _ => { + return Err(syn::Error::new_spanned(ident, "unknown wrapper command")); + } + } + } + } + + Ok(s) + } +} + +/// Creates a wrapper type for a VecN from the engine math library. +#[proc_macro] +pub fn wrap_math_vec_copy(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 + } + }; + /* 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 field_get_set_pairs = input.field_idents.iter().map(|i| { + let is = i.to_string(); + quote! { + fields.add_field_method_get(#is, |_, this| { + Ok(this.#i) + }); + fields.add_field_method_set(#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! { + methods.add_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| { + let metamethod_ident = &metamethod.ident; + let mm_str = metamethod.ident.to_string(); + let mm_str = mm_str.as_str(); + match mm_str { + "Add" | "Sub" | "Div" | "Mul" | "Mod" => { + let symbol = match mm_str { + "Add" => quote!(+), + "Sub" => quote!(-), + "Div" => quote!(/), + "Mul" => quote!(*), + "Mod" => quote!(%), + _ => unreachable!(), + }; + + // create a temporary vec to chain with metamethod.mods. If no parameters + // were provided, add the wrapper to the list of parameters. + let t = if metamethod.mods.is_empty() { + vec![wrapper_typename.clone()] + } else { vec![] }; + + let mods = metamethod.mods.iter().chain(t.iter()).map(|param| { + let other = if param.to_string().starts_with("Lua") { + quote!(other.0) + } else { + quote!(other) + }; + + quote! { + methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, + |_, this, (other,): (#param,)| { + Ok(#wrapper_typename(this.0 #symbol #other)) + }); + } + }); + + quote! { + #(#mods)* + } + }, + "Unm" => { + quote! { + methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, |_, this, ()| { + Ok(#wrapper_typename(-this.0)) + }); + } + }, + // Eq meta method has a different implementation than the above methods. + "Eq" => { + quote! { + methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, + |_, this, (other,): (#wrapper_typename,)| { + Ok(this.0 == other.0) + }); + } + }, + "Shl" => { + // create a temporary vec to chain with metamethod.mods. If no parameters + // were provided, add the wrapper to the list of parameters. + let t = if metamethod.mods.is_empty() { + vec![wrapper_typename.clone()] + } else { vec![] }; + + let mods = metamethod.mods.iter().chain(t.iter()).map(|param| { + let other = if param.to_string().starts_with("Lua") { + quote!(other.0) + } else { + quote!(other) + }; + + quote! { + methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, + |_, this, (other,): (#param,)| { + Ok(#wrapper_typename(this.0 << #other)) + }); + } + }); + + quote! { + #(#mods)* + } + } + "Shr" => { + // create a temporary vec to chain with metamethod.mods. If no parameters + // were provided, add the wrapper to the list of parameters. + let t = if metamethod.mods.is_empty() { + vec![wrapper_typename.clone()] + } else { vec![] }; + + let mods = metamethod.mods.iter().chain(t.iter()).map(|param| { + let other = if param.to_string().starts_with("Lua") { + quote!(other.0) + } else { + quote!(other) + }; + + quote! { + methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, + |_, this, (other,): (#param,)| { + Ok(#wrapper_typename(this.0 >> #other)) + }); + } + }); + + quote! { + #(#mods)* + } + }, + "BAnd" | "BOr" | "BXor" => { + let symbol = match mm_str { + "BAnd" => { + quote!(&) + }, + "BOr" => { + quote!(|) + }, + "BXor" => { + quote!(^) + }, + _ => unreachable!() // the string was just checked to be one of these + }; + + // create a temporary vec to chain with metamethod.mods. If no parameters + // were provided, add the wrapper to the list of parameters. + let t = if metamethod.mods.is_empty() { + vec![wrapper_typename.clone()] + } else { vec![] }; + + let mods = metamethod.mods.iter().chain(t.iter()).map(|param| { + let other = if param.to_string().starts_with("Lua") { + quote!(other.0) + } else { + quote!(other) + }; + + quote! { + methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, + |_, this, (other,): (#param,)| { + Ok(#wrapper_typename(this.0 #symbol #other)) + }); + } + }); + + quote! { + #(#mods)* + } + }, + "BNot" => { + quote! { + methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, |_, this, ()| { + Ok(#wrapper_typename(!this.0)) + }); + } + }, + "ToString" => { + quote! { + methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { + Ok(format!("{:?}", this.0)) + }); + } + }, + _ => syn::Error::new_spanned(metamethod_ident, + "unsupported auto implementation of metamethod").to_compile_error(), + } + }); + + 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> mlua::FromLua<'lua> for #wrapper_typename { + fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { + match value { + mlua::Value::UserData(ud) => Ok(*ud.borrow::()?), + _ => unreachable!(), + } + } + } + + impl mlua::UserData for #wrapper_typename { + fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) { + #(#field_get_set_pairs)* + + #matrix_wrapper_fields + #vec_wrapper_fields + } + + fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + #new_fn_idents + + methods.add_method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { + Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone()))) + }); + + methods.add_function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { + Ok(crate::ScriptBorrow::from_component::<#path>(None)) + }); + + #meta_method_idents + + #matrix_wrapper_methods + #vec_wrapper_methods + } + } + + impl lyra_scripting::lua::LuaWrapper for #wrapper_typename { + fn wrapped_type_id() -> std::any::TypeId { + std::any::TypeId::of::<#path>() + } + } + }) +} \ No newline at end of file diff --git a/lyra-scripting/lyra-scripting-derive/src/mat_wrapper.rs b/lyra-scripting/lyra-scripting-derive/src/mat_wrapper.rs new file mode 100644 index 0000000..fdb0e39 --- /dev/null +++ b/lyra-scripting/lyra-scripting-derive/src/mat_wrapper.rs @@ -0,0 +1,142 @@ +use proc_macro2::Ident; +use quote::quote; +use syn::{Path, Token}; + +pub(crate) struct MatWrapper { + pub column_type: Ident, +} + +impl MatWrapper { + pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { + quote! { + fields.add_field("ZERO", #wrapper_ident(#wrapped_path::ZERO)); + fields.add_field("IDENTITY", #wrapper_ident(#wrapped_path::IDENTITY)); + fields.add_field("NAN", #wrapper_ident(#wrapped_path::NAN)); + } + } + + pub fn to_method_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { + let column_type = &self.column_type; + + let column_size = { + let ty_str = column_type.to_string(); + ty_str[ty_str.len() - 1..].parse::() + .expect("Failure to parse number from token type") + }; + let column_size_xtwo = column_size * 2; + + let element_ty = quote!(f32); + + quote! { + methods.add_function("from_cols", + |_, (x_axis, y_axis): (#column_type, #column_type)| { + Ok(#wrapper_ident(#wrapped_path::from_cols(x_axis.0, y_axis.0))) + }); + + methods.add_function("from_cols_array", + |_, (arr,): ([#element_ty; #column_size_xtwo],)| { + Ok(#wrapper_ident(#wrapped_path::from_cols_array(&arr))) + }); + + methods.add_function("from_cols_array_2d", + |_, (arr,): ([[#element_ty; #column_size]; #column_size],)| { + Ok(#wrapper_ident(#wrapped_path::from_cols_array_2d(&arr))) + }); + + methods.add_function("from_diagonal", + |_, (diag,): (#column_type,)| { + Ok(#wrapper_ident(#wrapped_path::from_diagonal(diag.0))) + }); + + methods.add_method("col", + |_, this, (idx,): (usize,)| { + Ok(#column_type(this.col(idx))) + }); + + methods.add_method("row", + |_, this, (idx,): (usize,)| { + Ok(#column_type(this.row(idx))) + }); + + methods.add_method_mut("set_col", + |_, this, (idx, newc): (usize, #column_type)| { + let col = this.col_mut(idx); + *col = newc.0; + + Ok(()) + }); + + methods.add_method("is_finite", + |_, this, (): ()| { + Ok(this.is_finite()) + }); + + methods.add_method("is_nan", + |_, this, (): ()| { + Ok(this.is_nan()) + }); + + methods.add_method("transpose", + |_, this, (): ()| { + Ok(#wrapper_ident(this.0.transpose())) + }); + + methods.add_method("determinant", + |_, this, (): ()| { + Ok(this.determinant()) + }); + + methods.add_method("inverse", + |_, this, (): ()| { + Ok(#wrapper_ident(this.inverse())) + }); + + methods.add_method("abs_diff_eq", + |_, this, (rhs, max_abs_diff): (#wrapper_ident, f32)| { + Ok(this.abs_diff_eq(rhs.0, max_abs_diff)) + }); + + // TODO: After all DMat's are implemented + /* methods.add_method("as_dmat", + |_, this, (rhs, max_abs_diff): (#wrapper_ident, f32)| { + Ok(D#wrapper_ident(this.as_dmat)) + }); */ + } + } +} + +impl syn::parse::Parse for MatWrapper { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut column_type = None; + + // cba to remove the use of this bool + let mut first = true; + + while input.peek(Token![,]) || first { + if !first { + let _: Token![,] = input.parse()?; + } + + if input.peek(syn::Ident) { + let ident: Ident = input.parse()?; + let ident_str = ident.to_string(); + let ident_str = ident_str.as_str(); + + match ident_str { + "col_type" => { + let _eq: Token![=] = input.parse()?; + column_type = Some(input.parse()?); + }, + _ => return Err(syn::Error::new_spanned(ident, "unknown matrix wrapper command")), + } + } + + first = false; + } + + Ok(Self { + column_type: column_type.ok_or_else(|| syn::Error::new(input.span(), + "expected `col_type`"))?, + }) + } +} \ No newline at end of file diff --git a/lyra-scripting/scripts/lua/init.lua b/lyra-scripting/scripts/lua/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/lyra-scripting/scripts/lua/math/quat.lua b/lyra-scripting/scripts/lua/math/quat.lua new file mode 100644 index 0000000..c6aca90 --- /dev/null +++ b/lyra-scripting/scripts/lua/math/quat.lua @@ -0,0 +1,177 @@ +Quat = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 } +Quat.__index = Quat +Quat.__name = "Quat" + +--- Constructs a new Quaternion from x, y, z, and w. +---@param x number +---@param y number +---@param z number +---@param w number +---@return Quat +function Quat:new(x, y, z, w) + local q = {} + setmetatable(q, Quat) + + q.x = x + q.y = y + q.z = z + q.w = w + + return q +end + +Quat.IDENTITY = Quat:new(0, 0, 0, 1) + +function Quat:copy() + return Quat:new(self.x, self.y, self.z, self.w) +end + +--- Creates a quaternion from the angle, in radians, around the x axis. +--- @param rad number +--- @return Quat +function Quat:from_rotation_x(rad) + local sin = math.sin(rad * 0.5) + local cos = math.cos(rad * 0.5) + return Quat:new(sin, 0, 0, cos) +end + +--- Creates a quaternion from the angle, in radians, around the y axis. +--- @param rad number +--- @return Quat +function Quat:from_rotation_y(rad) + local sin = math.sin(rad * 0.5) + local cos = math.cos(rad * 0.5) + return Quat:new(0, sin, 0, cos) +end + +--- Creates a quaternion from the angle, in radians, around the z axis. +--- @param rad number +--- @return Quat +function Quat:from_rotation_z(rad) + local sin = math.sin(rad * 0.5) + local cos = math.cos(rad * 0.5) + return Quat:new(0, 0, sin, cos) +end + +--- Computes the dot product of `self`. +---@param rhs Quat +---@return number +function Quat:dot(rhs) + return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w) +end + +--- Computes the length of `self`. +---@return number +function Quat:length() + return math.sqrt(self:dot(self)) +end + +--- Compute the length of `self` squared. +---@return number +function Quat:length_squared() + return self:length() ^ 2 +end + +--- Normalizes `self` and returns the new Quat +---@return unknown +function Quat:normalize() + local length = self:length() + return self / length +end + +--- Multiplies two Quaternions together. Keep in mind that Quaternion multiplication is NOT +--- commutative so the order in which you multiply the quaternions matters. +---@param rhs Quat +---@return Quat +function Quat:mult_quat(rhs) + local x1, y1, z1, w1 = self.x, self.y, self.z, self.w + local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w + + local x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2 + local y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2 + local z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2 + local w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * x2 + + return Quat:new(x, y, z, w) +end + +--- Multiplies `self` by a Vec3, returning the rotated Vec3 +---@param vec Vec3 +---@return Vec3 +function Quat:mult_vec3(vec) + local vec_quat = Quat:new(vec.x, vec.y, vec.z, 0) + local quat = self:mult_quat(vec_quat) + return Vec3:new(quat.x, quat.y, quat.z) +end + +--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`. +--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result +--- will be equal to `rhs` +--- @param rhs Quat +--- @param alpha number +--- @return Quat +function Quat:lerp(rhs, alpha) + -- ensure alpha is [0, 1] + local alpha = math.max(0, math.min(1, alpha)) + + local x1, y1, z1, w1 = self.x, self.y, self.z, self.w + local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w + + local x = (1 - alpha) * x1 + alpha * x2 + local y = (1 - alpha) * y1 + alpha * y2 + local z = (1 - alpha) * z1 + alpha * z2 + local w = (1 - alpha) * w1 + alpha * w2 + + return Quat:new(x, y, z, w):normalize() +end + +function Quat:__add(rhs) + return Quat:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z, self.w + rhs.w) +end + +function Quat:__sub(rhs) + return Quat:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z, self.w - rhs.w) +end + +function Quat:__mul(rhs) + if type(rhs) == "number" then + return Quat:new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs) + elseif type(rhs) == "table" then + local name = rhs.__name + + if name == "Vec3" then + return self:mult_vec3(rhs) + elseif name == "Quat" then + return self:mult_quat(rhs) + else + assert(false, "Unknown usertype of rhs" .. name) + end + else + assert(false, "Unknown type of rhs" .. type(rhs)) + end +end + +function Quat:__div(rhs) + if type(rhs) == "number" then + return Quat:new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs) + else + assert(rhs.__name == "Quat", "Attempted to divide Quat by unknown type " .. rhs.__name) + return Quat:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z, self.w / rhs.w) + end +end + +function Quat:__eq(rhs) + return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z and self.w == rhs.w +end + +function Quat:__lt(rhs) + return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z and self.w < rhs.w +end + +function Quat:__le(rhs) + return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z and self.w <= rhs.w +end + +function Quat:__tostring() + return "Quat(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ", " .. self.w .. ")" +end \ No newline at end of file diff --git a/lyra-scripting/scripts/lua/math/transform.lua b/lyra-scripting/scripts/lua/math/transform.lua new file mode 100644 index 0000000..76273e2 --- /dev/null +++ b/lyra-scripting/scripts/lua/math/transform.lua @@ -0,0 +1,94 @@ +--require("math.quat") +--require("math.vec3") + +Transform = { translation = Vec3.ZERO, rotation = Quat.IDENTITY, scale = Vec3.ONE } +Transform.__index = Transform +Transform.__name = "Transform" + +function Transform:new(translation, rotation, scale) + local t = {} + setmetatable(t, Transform) + + t.translation = translation + t.rotation = rotation + t.scale = scale + + return t +end + +function Transform:copy() + return Transform:new(self.translation:copy(), self.rotation:copy(), self.scale:copy()) +end + +--- Creates a new Transform with the translation at the vec3 +--- @param pos Vec3 +function Transform:from_vec3(pos) + local t = Transform:copy() -- copy of default transform + t.translation = pos + return t +end + +function Transform:from_xyz(x, y, z) + Transform:from_vec3(Vec3:new(x, y, z)) +end + +--- Calculates the forward vector of the Transform. +--- @return Vec3 +function Transform:forward() + return (self.rotation * Vec3.NEG_Z):normalize() +end + +--- Calculates the left vector of the Transform. +--- @return Vec3 +function Transform:left() + return (self.rotation * Vec3.X):normalize() +end + +--- Calculates the up vector of the Transform. +--- @return Vec3 +function Transform:up() + return (self.rotation * Vec3.Y):normalize() +end + +--- Rotates `self` using a Quaternion +--- @param quat Quat +function Transform:rotate(quat) + self.rotation = (quat * self.rotation):normalize() +end + +--- Rotates `self` around the x-axis +--- @param rad number +function Transform:rotate_x(rad) + self:rotate(Quat:from_rotation_x(rad)) +end + +--- Rotates `self` around the y-axis +--- @param rad number +function Transform:rotate_y(rad) + self:rotate(Quat:from_rotation_y(rad)) +end + +--- Rotates `self` around the z-axis +--- @param rad number +function Transform:rotate_z(rad) + self:rotate(Quat:from_rotation_z(rad)) +end + +--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`. +--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result +--- will be equal to `rhs` +--- @param rhs Transform +--- @param alpha number +--- @return Transform +function Transform:lerp(rhs, alpha) + local res = self:copy() + res.translation = self.translation:lerp(rhs.translation, alpha) + res.rotation = self.rotation:lerp(rhs.rotation, alpha) + res.scale = self.scale:lerp(rhs.scale, alpha) + return res +end + +function Transform:__tostring() + return "Transform(pos=" .. tostring(self.translation) .. ", rot=" + .. tostring(self.rotation) .. ", scale=" .. tostring(self.scale) .. ")" +end \ No newline at end of file diff --git a/lyra-scripting/scripts/lua/math/vec3.lua b/lyra-scripting/scripts/lua/math/vec3.lua new file mode 100644 index 0000000..cf88af6 --- /dev/null +++ b/lyra-scripting/scripts/lua/math/vec3.lua @@ -0,0 +1,161 @@ +Vec3 = { x = 0.0, y = 0.0, z = 0.0 } +Vec3.__index = Vec3 +Vec3.__name = "Vec3" + +--- Constructs a new vector +---@param x number +---@param y number +---@param z number +---@return Vec3 +function Vec3:new(x, y, z) + local v = {} + setmetatable(v, Vec3) + + v.x = x + v.y = y + v.z = z + + return v +end + +function Vec3:copy() + return Vec3:new(self.x, self.y, self.z) +end + +--- Constructs a vector with all elements as parameter `x`. +---@param x number +---@return Vec3 +function Vec3:all(x) + return Vec3:new(x, x, x) +end + +--- A unit-length vector pointing alongside the positive X axis. +Vec3.X = Vec3:new(1, 0, 0) +--- A unit-length vector pointing alongside the positive Y axis. +Vec3.Y = Vec3:new(0, 1, 0) +--- A unit-length vector pointing alongside the positive Z axis. +Vec3.Z = Vec3:new(0, 0, 1) + +--- A unit-length vector pointing alongside the negative X axis. +Vec3.NEG_X = Vec3:new(-1, 0, 0) +--- A unit-length vector pointing alongside the negative Y axis. +Vec3.NEG_Y = Vec3:new(0, -1, 0) +--- A unit-length vector pointing alongside the negative Z axis. +Vec3.NEG_Z = Vec3:new(0, 0, -1) + +--- A vector of all zeros +Vec3.ZERO = Vec3:new(0, 0, 0) +--- A vector of all ones +Vec3.ONE = Vec3:new(1, 1, 1) + +--- Computes the absolute value of `self`. +---@return Vec3 +function Vec3:abs() + return Vec3:new(math.abs(self.x), math.abs(self.y), math.abs(self.z)) +end + +--- Computes the length of `self`. +---@return number +function Vec3:length() + return math.sqrt(self:dot(self)) +end + +--- Computes the dot product of `self` and `rhs`. +---@param rhs Vec3 +---@return number +function Vec3:dot(rhs) + assert(rhs.__name == "Vec3") + + return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) +end + +--- Returns a vector that has the minimum value of each element of `self` and `rhs` +---@param rhs Vec3 +---@return Vec3 +function Vec3:min(rhs) + local x = math.min(self.x, rhs.x) + local y = math.min(self.y, rhs.y) + local z = math.min(self.z, rhs.z) + + return Vec3:new(x, y, z) +end + +--- Returns `self` normalized to a length 1. +---@return unknown +function Vec3:normalize() + local len_recip = 1.0 / self:length() + return self * len_recip +end + +--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`. +--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result +--- will be equal to `rhs` +--- @param rhs Vec3 +--- @param alpha number +--- @return Vec3 +function Vec3:lerp(rhs, alpha) + -- ensure alpha is [0, 1] + local alpha = math.max(0, math.min(1, alpha)) + + local res = self:copy() + res = res + ((rhs - res) * alpha) + return res +end + +function Vec3:__add(rhs) + return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) +end + +function Vec3:__sub(rhs) + return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) +end + +function Vec3:__mul(rhs) + if type(rhs) == "number" then + return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs) + else + return Vec3:new(self.x * rhs.x, self.y * rhs.y, self.z * rhs.z) + end +end + +function Vec3:__div(rhs) + if type(rhs) == "number" then + return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs) + else + return Vec3:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z) + end +end + +function Vec3:__idiv(rhs) + if type(rhs) == "number" then + return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs) + else + return Vec3:new(self.x // rhs.x, self.y // rhs.y, self.z // rhs.z) + end +end + +function Vec3:__unm() + return Vec3:new(-self.x, -self.y, -self.z) +end + +function Vec3:__pow(rhs) + if type(rhs) == "number" then + return Vec3:new(self.x ^ rhs, self.y ^ rhs, self.z ^ rhs) + end +end + +function Vec3:__eq(rhs) + return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z +end + +function Vec3:__lt(rhs) + return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z +end + +function Vec3:__le(rhs) + return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z +end + +function Vec3:__tostring() + return "Vec3(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")" +end \ No newline at end of file diff --git a/lyra-scripting/src/host.rs b/lyra-scripting/src/host.rs index b19e316..b9cb4b9 100644 --- a/lyra-scripting/src/host.rs +++ b/lyra-scripting/src/host.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use lyra_ecs::{ResourceObject, Entity}; +use lyra_ecs::{ResourceObject, Entity, World}; use crate::ScriptWorldPtr; @@ -42,6 +42,9 @@ pub trait ScriptApiProvider { /// The type used as the script's context. type ScriptContext; + /// Prepare the ECS world for this api. Things like registering types with the type registry happen here. + fn prepare_world(&mut self, world: &mut World) {} + /// Exposes an API in the provided script context. fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>; diff --git a/lyra-scripting/src/lib.rs b/lyra-scripting/src/lib.rs index f08e7b4..197e95e 100644 --- a/lyra-scripting/src/lib.rs +++ b/lyra-scripting/src/lib.rs @@ -2,6 +2,7 @@ pub mod lua; pub mod world; +use lyra_ecs::Component; pub use world::*; pub mod wrap; @@ -18,9 +19,10 @@ use lyra_game::game::Game; #[allow(unused_imports)] pub(crate) mod lyra_engine { pub use lyra_ecs as ecs; + pub use lyra_reflect as reflect; } -use lyra_reflect::{ReflectedComponent, Reflect}; +use lyra_reflect::{ReflectedComponent, Reflect, FromType}; #[derive(Clone)] pub enum ReflectBranch { @@ -60,6 +62,20 @@ impl Clone for ScriptBorrow { } } +impl ScriptBorrow { + pub fn from_component(data: Option) -> Self + where + T: Reflect + Component + Default + 'static + { + let data = data.map(|d| Box::new(d) as Box<(dyn Reflect + 'static)>); + + Self { + reflect_branch: ReflectBranch::Component(>::from_type()), + data, + } + } +} + /// An extension trait that adds some helpful methods that makes it easier to do scripting things pub trait GameScriptExt { fn add_script_api_provider(&mut self, provider: P) @@ -69,12 +85,13 @@ pub trait GameScriptExt { } impl GameScriptExt for Game { - fn add_script_api_provider(&mut self, provider: P) + fn add_script_api_provider(&mut self, mut provider: P) where T: ScriptHost, P: ScriptApiProvider + 'static { let world = self.world(); + provider.prepare_world(world); let mut providers = world.get_resource_mut::>(); providers.add_provider(provider); } diff --git a/lyra-scripting/src/lua/dynamic_iter.rs b/lyra-scripting/src/lua/dynamic_iter.rs index 5b6b300..4e6e2cc 100644 --- a/lyra-scripting/src/lua/dynamic_iter.rs +++ b/lyra-scripting/src/lua/dynamic_iter.rs @@ -149,7 +149,7 @@ impl ReflectedIterator { unsafe { self.reflected_components.as_ref().unwrap().as_ref() }; let reg_type = reflected_components.get_type(id) - .expect("Could not find type for dynamic view!"); + .expect("Requested type was not found in TypeRegistry"); let proxy = reg_type.get_data::() .expect("Type does not have ReflectLuaProxy as a TypeData"); diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index ded46ce..3b3d6fb 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -4,7 +4,7 @@ pub use dynamic_iter::*; pub mod world; use lyra_game::{plugin::Plugin, game::GameStages}; use lyra_resource::ResourceManager; -use tracing::{debug, error}; +use tracing::{debug, error, trace}; pub use world::*; pub mod script; @@ -13,16 +13,16 @@ pub use script::*; pub mod loader; pub use loader::*; -pub mod modules; -pub use modules::*; +pub mod providers; +pub mod wrappers; #[cfg(test)] mod test; -use std::{ptr::NonNull, sync::Mutex}; +use std::{ptr::NonNull, sync::Mutex, any::TypeId}; use lyra_ecs::{DynamicBundle, World, query::{ResMut, View, Entities}}; -use lyra_reflect::{Reflect, FromType}; +use lyra_reflect::{Reflect, FromType, RegisteredType, TypeRegistry}; use mlua::{Lua, AnyUserDataExt}; @@ -33,6 +33,35 @@ pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptApiProviders, ScriptContexts, ScriptWorldPtr, ScriptList, ScriptData, ScriptHost, ScriptError, GameScriptExt}; +use self::providers::{UtilityApiProvider, LyraMathApiProvider, LyraEcsApiProvider}; + +pub trait RegisterLuaType { + /// Register a lua type that **is not wrapped**. + fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self); + /// Registers a wrapped lua type. + /// You provide the wrapper as `W`, and the type that the wrapper wraps, as `T`. + fn register_lua_wrapper<'a, W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self); +} + +impl RegisterLuaType for World { + fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self) { + let mut registry = self.get_resource_mut::(); + + let type_id = TypeId::of::(); + + let reg_type = registry.get_type_or_default(type_id); + reg_type.add_data(>::from_type()); + //reg_type.add_data(>::from_type()); + } + + fn register_lua_wrapper<'a, W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self) { + let mut registry = self.get_resource_mut::(); + + let reg_type = registry.get_type_or_default(W::wrapped_type_id()); + reg_type.add_data(>::from_type()); + } +} + impl<'lua> mlua::FromLua<'lua> for ScriptBorrow { fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { match value { @@ -49,6 +78,11 @@ pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow { .expect("Type does not implement '__internal_reflect' properly") } +pub trait LuaWrapper { + /// The type id of the wrapped type. + fn wrapped_type_id() -> TypeId; +} + pub trait LuaProxy { fn as_lua_value<'lua>(lua: &'lua mlua::Lua, this: &dyn Reflect) -> mlua::Result>; fn apply(lua: &mlua::Lua, this: &mut dyn Reflect, apply: &mlua::AnyUserData) -> mlua::Result<()>; @@ -176,7 +210,7 @@ fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Resu }; if let Some(ctx) = contexts.get_context_mut(script.id()) { - debug!("Running '{}' function in script '{}'", stage_name, script.name()); + trace!("Running '{}' function in script '{}'", stage_name, script.name()); match host.call_script(world_ptr.clone(), &script_data, ctx, &mut providers, stage_name) { Ok(()) => {}, Err(e) => match e { @@ -230,6 +264,8 @@ impl Plugin for LuaScriptingPlugin { fn setup(&self, game: &mut lyra_game::game::Game) { let world = game.world(); + world.add_resource_default::(); + world.add_resource_default::(); world.add_resource_default::>(); world.add_resource_default::>(); @@ -240,6 +276,8 @@ impl Plugin for LuaScriptingPlugin { drop(loader); game.add_script_api_provider::(UtilityApiProvider); + game.add_script_api_provider::(LyraEcsApiProvider); + game.add_script_api_provider::(LyraMathApiProvider); game .add_system_to_stage(GameStages::First, "lua_create_contexts", lua_scripts_create_contexts, &[]) diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs new file mode 100644 index 0000000..def90e8 --- /dev/null +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -0,0 +1,26 @@ +use crate::{lua::LuaContext, ScriptApiProvider, ScriptWorldPtr, ScriptDynamicBundle}; + +#[derive(Default)] +pub struct LyraEcsApiProvider; + +impl ScriptApiProvider for LyraEcsApiProvider { + type ScriptContext = LuaContext; + + fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + let ctx = ctx.lock().unwrap(); + + let globals = ctx.globals(); + globals.set("World", ctx.create_proxy::()?)?; + globals.set("DynamicBundle", ctx.create_proxy::()?)?; + + Ok(()) + } + + fn setup_script(&mut self, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + Ok(()) + } + + fn update_script_environment(&mut self, world: crate::ScriptWorldPtr, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + Ok(()) + } +} \ No newline at end of file diff --git a/lyra-scripting/src/lua/providers/math.rs b/lyra-scripting/src/lua/providers/math.rs new file mode 100644 index 0000000..a4be794 --- /dev/null +++ b/lyra-scripting/src/lua/providers/math.rs @@ -0,0 +1,108 @@ +use lyra_ecs::World; +use lyra_game::math; +use crate::lua::RegisterLuaType; +use crate::lua::wrappers::LuaVec3; + +use crate::{ScriptApiProvider, lua::LuaContext}; + +#[derive(Default)] +pub struct LyraMathApiProvider; + +impl ScriptApiProvider for LyraMathApiProvider { + type ScriptContext = LuaContext; + + fn prepare_world(&mut self, world: &mut World) { + world.register_lua_wrapper::(); + } + + fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + let ctx = ctx.lock().unwrap(); + + /* let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua"); + ctx.load(bytes.to_vec()).exec()?; + + let bytes = include_bytes!("../../../scripts/lua/math/quat.lua"); + ctx.load(bytes.to_vec()).exec()?; + + let bytes = include_bytes!("../../../scripts/lua/math/transform.lua"); + ctx.load(bytes.to_vec()).exec()?; */ + + let globals = ctx.globals(); + globals.set("Vec3", ctx.create_proxy::()?)?; + //globals.set("Vec3", LuaVec3(math::Vec3::ZERO).into_lua(&ctx)?)?; + + Ok(()) + } + + fn setup_script(&mut self, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + Ok(()) + } + + fn update_script_environment(&mut self, world: crate::ScriptWorldPtr, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + Ok(()) + } +} + +/* #[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)] +pub struct LuaVec3(#[reflect(skip)] math::Vec3); + +impl From for LuaVec3 { + fn from(value: math::Vec3) -> Self { + Self(value) + } +} + +impl<'lua> mlua::IntoLua<'lua> for LuaVec3 { + fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult> { + let globals = lua.globals(); + + let v3 = globals.get::<_, mlua::Table>("Vec3")?; + let v3_new = v3.get::<_, mlua::Function>("new")?; + v3_new.call::<_, mlua::Table>((v3, self.0.x, self.0.y, self.0.z)) + .and_then(|t| t.into_lua(lua)) + } +} + +#[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)] +pub struct LuaQuat(#[reflect(skip)] math::Quat); + +impl From for LuaQuat { + fn from(value: math::Quat) -> Self { + Self(value) + } +} + +impl<'lua> mlua::IntoLua<'lua> for LuaQuat { + fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult> { + let globals = lua.globals(); + + let q = globals.get::<_, mlua::Table>("Quat")?; + let q_new = q.get::<_, mlua::Function>("new")?; + q_new.call::<_, mlua::Table>((q, self.0.x, self.0.y, self.0.z, self.0.w)) + .and_then(|t| t.into_lua(lua)) + } +} + +#[derive(Clone, Copy, Debug, lyra_reflect::Reflect)] +pub struct LuaTransform(#[reflect(skip)] math::Transform); + +impl From for LuaTransform { + fn from(value: math::Transform) -> Self { + Self(value) + } +} + +impl<'lua> mlua::IntoLua<'lua> for LuaTransform { + fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult> { + let globals = lua.globals(); + + let translation = LuaVec3(self.0.translation).into_lua(lua)?; + let rot = LuaQuat(self.0.rotation).into_lua(lua)?; + let scale = LuaVec3(self.0.scale).into_lua(lua)?; + + let transf = globals.get::<_, mlua::Table>("Transform")?; + let transf_new = transf.get::<_, mlua::Function>("new")?; + transf_new.call::<_, mlua::Table>((transf, translation, rot, scale)) + .and_then(|t| t.into_lua(lua)) + } +} */ \ No newline at end of file diff --git a/lyra-scripting/src/lua/providers/mod.rs b/lyra-scripting/src/lua/providers/mod.rs new file mode 100644 index 0000000..d8a3f49 --- /dev/null +++ b/lyra-scripting/src/lua/providers/mod.rs @@ -0,0 +1,8 @@ +pub mod util; +pub use util::*; + +pub mod math; +pub use math::*; + +pub mod ecs; +pub use ecs::*; \ No newline at end of file diff --git a/lyra-scripting/src/lua/modules/mod.rs b/lyra-scripting/src/lua/providers/util.rs similarity index 100% rename from lyra-scripting/src/lua/modules/mod.rs rename to lyra-scripting/src/lua/providers/util.rs diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index 457042f..612772e 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -1,7 +1,7 @@ use std::sync::Mutex; use mlua::IntoLua; -use tracing::debug; +use tracing::{debug, trace}; use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; @@ -17,7 +17,7 @@ fn try_call_lua_function(lua: &mlua::Lua, fn_name: &str) -> Result<(), ScriptErr .map_err(ScriptError::MluaError)?; }, Err(mlua::Error::FromLuaConversionError { from: "nil", to: "function", message: None }) => { - debug!("Function '{}' was not found, ignoring...", fn_name) + trace!("Function '{}' was not found, ignoring...", fn_name) // ignore }, Err(e) => { diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index d1521ca..7011d21 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use lyra_ecs::query::dynamic::QueryDynamicType; use lyra_reflect::TypeRegistry; use mlua::{AnyUserDataExt, IntoLua, IntoLuaMulti}; @@ -73,7 +75,9 @@ impl mlua::UserData for ScriptWorldPtr { methods.add_method("view", |lua, this, (system, queries): (mlua::Function, mlua::Variadic)| { if queries.is_empty() { - panic!("No components were provided!"); + return Err(mlua::Error::BadArgument { to: Some("world:view".to_string()), pos: 2, name: Some("...".to_string()), cause: + Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage("no component types provided".to_string()))) + }); } let world = unsafe { this.inner.as_ref() }; diff --git a/lyra-scripting/src/lua/wrappers/mod.rs b/lyra-scripting/src/lua/wrappers/mod.rs new file mode 100644 index 0000000..1ff5e93 --- /dev/null +++ b/lyra-scripting/src/lua/wrappers/mod.rs @@ -0,0 +1,365 @@ +use lyra_game::math; +use lyra_scripting_derive::wrap_math_vec_copy; +use crate::lyra_engine; + +use crate as lyra_scripting; + +// f32 types +/* wrap_math_vec_copy!( + math::Vec2, + derives(PartialEq), + fields(x, y), + metamethods( + Add(LuaVec2, f32), + Sub(LuaVec2, f32), + Div(LuaVec2, f32), + Mul(LuaVec2, f32), + Mod(LuaVec2, f32), + Eq, Unm + ) +); */ +wrap_math_vec_copy!( + math::Vec3, + derives(PartialEq), + fields(x, y, z), + metamethods( + ToString, + Eq, Unm + ) + /* metamethods( + Add(LuaVec3, f32), + Sub(LuaVec3, f32), + Div(LuaVec3, f32), + Mul(LuaVec3, f32), + Mod(LuaVec3, f32), + Eq, Unm + ) */ +); +/* wrap_math_vec_copy!( + math::Vec3A, + derives(PartialEq), + fields(x, y, z), + metamethods( + Add(LuaVec3A, f32), + Sub(LuaVec3A, f32), + Div(LuaVec3A, f32), + Mul(LuaVec3A, f32), + Mod(LuaVec3A, f32), + Eq, Unm + ) +); +wrap_math_vec_copy!( + math::Vec4, + derives(PartialEq), + fields(w, x, y, z), + metamethods( + Add(LuaVec4, f32), + Sub(LuaVec4, f32), + Div(LuaVec4, f32), + Mul(LuaVec4, f32), + Mod(LuaVec4, f32), + Eq, Unm + ) +); + +// f64 types +wrap_math_vec_copy!( + math::DVec2, + derives(PartialEq), + fields(x, y), + metamethods( + Add(LuaDVec2, f64), + Sub(LuaDVec2, f64), + Div(LuaDVec2, f64), + Mul(LuaDVec2, f64), + Mod(LuaDVec2, f64), + Eq, Unm + ) +); +wrap_math_vec_copy!( + math::DVec3, + derives(PartialEq), + fields(x, y, z), + metamethods( + Add(LuaDVec3, f64), + Sub(LuaDVec3, f64), + Div(LuaDVec3, f64), + Mul(LuaDVec3, f64), + Mod(LuaDVec3, f64), + Eq, Unm + ) +); +wrap_math_vec_copy!( + math::DVec4, + derives(PartialEq), + fields(w, x, y, z), + metamethods( + Add(LuaDVec4, f64), + Sub(LuaDVec4, f64), + Div(LuaDVec4, f64), + Mul(LuaDVec4, f64), + Mod(LuaDVec4, f64), + Eq, Unm + ) +); + +// i32 types +wrap_math_vec_copy!( + math::IVec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods( + Add(LuaIVec2, i32), + Sub(LuaIVec2, i32), + Div(LuaIVec2, i32), + Mul(LuaIVec2, i32), + Mod(LuaIVec2, i32), + Shl(LuaIVec2, LuaUVec2, i32), + Shr(LuaIVec2, LuaUVec2, i32), + BAnd(LuaIVec2, i32), + BOr(LuaIVec2, i32), + BXor(LuaIVec2, i32), + Eq, Unm, BNot + ) +); +wrap_math_vec_copy!( + math::IVec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods( + Add(LuaIVec3, i32), + Sub(LuaIVec3, i32), + Div(LuaIVec3, i32), + Mul(LuaIVec3, i32), + Mod(LuaIVec3, i32), + Shl(LuaIVec3, LuaUVec3, i32), + Shr(LuaIVec3, LuaUVec3, i32), + BAnd(LuaIVec3, i32), + BOr(LuaIVec3, i32), + BXor(LuaIVec3, i32), + Eq, Unm, BNot + ) +); +wrap_math_vec_copy!( + math::IVec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods( + Add(LuaIVec4, i32), + Sub(LuaIVec4, i32), + Div(LuaIVec4, i32), + Mul(LuaIVec4, i32), + Mod(LuaIVec4, i32), + Shl(LuaIVec4, LuaUVec4, i32), + Shr(LuaIVec4, LuaUVec4, i32), + BAnd(LuaIVec4, i32), + BOr(LuaIVec4, i32), + BXor(LuaIVec4, i32), + Eq, Unm, BNot + ) +); + +// u32 types +wrap_math_vec_copy!( + math::UVec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods( + Add(LuaUVec2, u32), + Sub(LuaUVec2, u32), + Div(LuaUVec2, u32), + Mul(LuaUVec2, u32), + Mod(LuaUVec2, u32), + Shl(LuaUVec2, LuaIVec2, i32), + Shr(LuaUVec2, LuaIVec2, i32), + BAnd(LuaUVec2, u32), + BOr(LuaUVec2, u32), + BXor(LuaUVec2, u32), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::UVec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods( + Add(LuaUVec3, u32), + Sub(LuaUVec3, u32), + Div(LuaUVec3, u32), + Mul(LuaUVec3, u32), + Mod(LuaUVec3, u32), + Shl(LuaUVec3, LuaIVec3, i32), + Shr(LuaUVec3, LuaIVec3, i32), + BAnd(LuaUVec3, u32), + BOr(LuaUVec3, u32), + BXor(LuaUVec3, u32), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::UVec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods( + Add(LuaUVec4, u32), + Sub(LuaUVec4, u32), + Div(LuaUVec4, u32), + Mul(LuaUVec4, u32), + Mod(LuaUVec4, u32), + Shl(LuaUVec4, LuaIVec4, i32), + Shr(LuaUVec4, LuaIVec4, i32), + BAnd(LuaUVec4, u32), + BOr(LuaUVec4, u32), + BXor(LuaUVec4, u32), + Eq, BNot + ) +); + +// i64 types +wrap_math_vec_copy!( + math::I64Vec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods( + Add(LuaI64Vec2, i64), + Sub(LuaI64Vec2, i64), + Div(LuaI64Vec2, i64), + Mul(LuaI64Vec2, i64), + Mod(LuaI64Vec2, i64), + Shl(i64), + Shr(i64), + BAnd(LuaI64Vec2, i64), + BOr(LuaI64Vec2, i64), + BXor(LuaI64Vec2, i64), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::I64Vec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods( + Add(LuaI64Vec3, i64), + Sub(LuaI64Vec3, i64), + Div(LuaI64Vec3, i64), + Mul(LuaI64Vec3, i64), + Mod(LuaI64Vec3, i64), + Shl(i64), + Shr(i64), + BAnd(LuaI64Vec3, i64), + BOr(LuaI64Vec3, i64), + BXor(LuaI64Vec3, i64), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::I64Vec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods( + Add(LuaI64Vec4, i64), + Sub(LuaI64Vec4, i64), + Div(LuaI64Vec4, i64), + Mul(LuaI64Vec4, i64), + Mod(LuaI64Vec4, i64), + Shl(i64), + Shr(i64), + BAnd(LuaI64Vec4, i64), + BOr(LuaI64Vec4, i64), + BXor(LuaI64Vec4, i64), + Eq, BNot + ) +); + +// u64 types +wrap_math_vec_copy!( + math::U64Vec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods( + Add(LuaU64Vec2, u64), + Sub(LuaU64Vec2, u64), + Div(LuaU64Vec2, u64), + Mul(LuaU64Vec2, u64), + Mod(LuaU64Vec2, u64), + Shl(i64), + Shr(i64), + BAnd(LuaU64Vec2, u64), + BOr(LuaU64Vec2, u64), + BXor(LuaU64Vec2, u64), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::U64Vec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods( + Add(LuaU64Vec3, u64), + Sub(LuaU64Vec3, u64), + Div(LuaU64Vec3, u64), + Mul(LuaU64Vec3, u64), + Mod(LuaU64Vec3, u64), + Shl(i64), + Shr(i64), + BAnd(LuaU64Vec3, u64), + BOr(LuaU64Vec3, u64), + BXor(LuaU64Vec3, u64), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::U64Vec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods( + Add(LuaU64Vec4, u64), + Sub(LuaU64Vec4, u64), + Div(LuaU64Vec4, u64), + Mul(LuaU64Vec4, u64), + Mod(LuaU64Vec4, u64), + Shl(i64), + Shr(i64), + BAnd(LuaU64Vec4, u64), + BOr(LuaU64Vec4, u64), + BXor(LuaU64Vec4, u64), + Eq, BNot + ) +); + +// bool types +wrap_math_vec_copy!( + math::BVec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods(Eq, BAnd, BOr, BXor, BNot) +); +wrap_math_vec_copy!( + math::BVec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods(Eq, BAnd, BOr, BXor, BNot) +); +wrap_math_vec_copy!( + math::BVec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods(Eq, BAnd, BOr, BXor, BNot) +); + +// mat2 +wrap_math_vec_copy!( + math::Mat2, + derives(PartialEq), + no_new, + matrix { + col_type = LuaVec2 + }, + metamethods( + Eq, + Add, + Sub, + Mul(LuaMat2, f32), + Unm + ) +); */ \ No newline at end of file -- 2.40.1 From 189d05e32337abdf431bc6aea6a80dae417e4692 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 13 Jan 2024 11:51:16 -0500 Subject: [PATCH 08/41] split the math module into a lyra-math crate, implement Reflect and Component for Transform --- Cargo.lock | 32 ++++++++++++++++++- Cargo.toml | 4 +-- lyra-ecs/Cargo.toml | 4 +-- lyra-ecs/src/math.rs | 15 +++++---- lyra-game/Cargo.toml | 1 + lyra-game/src/lib.rs | 2 +- lyra-game/src/render/light/mod.rs | 14 ++++---- lyra-game/src/render/renderer.rs | 10 +++--- lyra-game/src/scene/mod.rs | 3 -- lyra-game/src/scene/transform.rs | 26 --------------- lyra-math/Cargo.toml | 9 ++++++ .../src/math => lyra-math/src}/angle.rs | 0 .../src/math/mod.rs => lyra-math/src/lib.rs | 0 .../src/math => lyra-math/src}/transform.rs | 18 +++++++++-- lyra-reflect/Cargo.toml | 4 +-- .../lyra-reflect-derive/src/struct_macro.rs | 2 +- lyra-reflect/src/impls/impl_math.rs | 21 ++++++++---- 17 files changed, 100 insertions(+), 65 deletions(-) delete mode 100755 lyra-game/src/scene/transform.rs create mode 100644 lyra-math/Cargo.toml rename {lyra-game/src/math => lyra-math/src}/angle.rs (100%) rename lyra-game/src/math/mod.rs => lyra-math/src/lib.rs (100%) mode change 100755 => 100644 rename {lyra-game/src/math => lyra-math/src}/transform.rs (86%) diff --git a/Cargo.lock b/Cargo.lock index e2e17a2..091300d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1258,6 +1258,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -1413,6 +1422,7 @@ version = "0.1.0" dependencies = [ "anyhow", "lyra-ecs-derive", + "lyra-math", "rand", "thiserror", ] @@ -1447,8 +1457,9 @@ dependencies = [ "glam", "image", "instant", - "itertools", + "itertools 0.11.0", "lyra-ecs", + "lyra-math", "lyra-reflect", "lyra-resource", "quote", @@ -1463,11 +1474,19 @@ dependencies = [ "winit", ] +[[package]] +name = "lyra-math" +version = "0.1.0" +dependencies = [ + "glam", +] + [[package]] name = "lyra-reflect" version = "0.1.0" dependencies = [ "lyra-ecs", + "lyra-math", "lyra-reflect-derive", ] @@ -1503,16 +1522,27 @@ name = "lyra-scripting" version = "0.1.0" dependencies = [ "anyhow", + "itertools 0.12.0", "lyra-ecs", "lyra-game", "lyra-reflect", "lyra-resource", + "lyra-scripting-derive", "mlua", "thiserror", "tracing", "tracing-subscriber", ] +[[package]] +name = "lyra-scripting-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "mach2" version = "0.4.2" diff --git a/Cargo.toml b/Cargo.toml index 7bdf3a1..36a4517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ "lyra-ecs", "lyra-reflect", "lyra-scripting", - "lyra-game"] + "lyra-game", "lyra-math"] [features] scripting = ["dep:lyra-scripting"] @@ -18,4 +18,4 @@ lua_scripting = ["scripting", "lyra-scripting/lua"] [dependencies] lyra-game = { path = "lyra-game" } -lyra-scripting = { path = "lyra-scripting", optional = true } \ No newline at end of file +lyra-scripting = { path = "lyra-scripting", optional = true } diff --git a/lyra-ecs/Cargo.toml b/lyra-ecs/Cargo.toml index 8249429..9e49722 100644 --- a/lyra-ecs/Cargo.toml +++ b/lyra-ecs/Cargo.toml @@ -6,13 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -math = ["dep:glam"] +math = ["dep:lyra-math"] [dependencies] lyra-ecs-derive = { path = "./lyra-ecs-derive" } +lyra-math = { path = "../lyra-math", optional = true } anyhow = "1.0.75" thiserror = "1.0.50" -glam = { version = "0.24.0", optional = true } [dev-dependencies] rand = "0.8.5" # used for tests diff --git a/lyra-ecs/src/math.rs b/lyra-ecs/src/math.rs index 045b70f..43c1a90 100644 --- a/lyra-ecs/src/math.rs +++ b/lyra-ecs/src/math.rs @@ -1,8 +1,8 @@ use crate::Component; -use glam::{Vec3, Quat, Vec2, Vec4, Mat4}; +use lyra_math::{Vec3, Quat, Vec2, Vec4, Mat4, Transform}; -macro_rules! impl_component_math { +macro_rules! impl_external_component { ($type: ident) => { impl Component for $type { fn name() -> &'static str { @@ -12,8 +12,9 @@ macro_rules! impl_component_math { }; } -impl_component_math!(Vec2); -impl_component_math!(Vec3); -impl_component_math!(Vec4); -impl_component_math!(Mat4); -impl_component_math!(Quat); \ No newline at end of file +impl_external_component!(Vec2); +impl_external_component!(Vec3); +impl_external_component!(Vec4); +impl_external_component!(Mat4); +impl_external_component!(Quat); +impl_external_component!(Transform); \ No newline at end of file diff --git a/lyra-game/Cargo.toml b/lyra-game/Cargo.toml index 3579672..070fc85 100644 --- a/lyra-game/Cargo.toml +++ b/lyra-game/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" lyra-resource = { path = "../lyra-resource" } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-reflect = { path = "../lyra-reflect", features = [ "math" ] } +lyra-math = { path = "../lyra-math" } winit = "0.28.1" tracing = "0.1.37" diff --git a/lyra-game/src/lib.rs b/lyra-game/src/lib.rs index 35ab932..3c70bc4 100644 --- a/lyra-game/src/lib.rs +++ b/lyra-game/src/lib.rs @@ -7,7 +7,6 @@ extern crate self as lyra_engine; pub mod game; pub mod render; pub mod resources; -pub mod math; pub mod input; pub mod castable_any; pub mod plugin; @@ -26,6 +25,7 @@ pub mod scene; pub use lyra_resource as assets; pub use lyra_ecs as ecs; +pub use lyra_math as math; #[cfg(feature = "scripting")] pub use lyra_scripting as script; diff --git a/lyra-game/src/render/light/mod.rs b/lyra-game/src/render/light/mod.rs index d89cbd2..fb4728b 100644 --- a/lyra-game/src/render/light/mod.rs +++ b/lyra-game/src/render/light/mod.rs @@ -12,7 +12,7 @@ use tracing::debug; use std::mem; -use crate::{math::Transform, scene::TransformComponent}; +use crate::math::Transform; use self::directional::DirectionalLight; @@ -169,29 +169,29 @@ impl LightUniformBuffers { pub fn update_lights(&mut self, queue: &wgpu::Queue, world_tick: Tick, world: &World) { for (entity, point_light, transform, light_epoch, transform_epoch) - in world.view_iter::<(Entities, &PointLight, &TransformComponent, TickOf, TickOf)>() { + in world.view_iter::<(Entities, &PointLight, &Transform, TickOf, TickOf)>() { if !self.point_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick { - let uniform = PointLightUniform::from_bundle(&point_light, &transform.transform); + let uniform = PointLightUniform::from_bundle(&point_light, &transform); self.point_lights.update_or_add(&mut self.lights_uniform.point_lights, entity, uniform); debug!("Updated point light"); } } for (entity, spot_light, transform, light_epoch, transform_epoch) - in world.view_iter::<(Entities, &SpotLight, &TransformComponent, TickOf, TickOf)>() { + in world.view_iter::<(Entities, &SpotLight, &Transform, TickOf, TickOf)>() { if !self.spot_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick { - let uniform = SpotLightUniform::from_bundle(&spot_light, &transform.transform); + let uniform = SpotLightUniform::from_bundle(&spot_light, &transform); self.spot_lights.update_or_add(&mut self.lights_uniform.spot_lights, entity, uniform); //debug!("Updated spot light"); } } if let Some((dir_light, transform)) = - world.view_iter::<(&DirectionalLight, &TransformComponent)>().next() { + world.view_iter::<(&DirectionalLight, &Transform)>().next() { - let uniform = DirectionalLightUniform::from_bundle(&dir_light, &transform.transform); + let uniform = DirectionalLightUniform::from_bundle(&dir_light, &transform); self.lights_uniform.directional_light = uniform; } diff --git a/lyra-game/src/render/renderer.rs b/lyra-game/src/render/renderer.rs index 9e250ee..b7aa4a4 100755 --- a/lyra-game/src/render/renderer.rs +++ b/lyra-game/src/render/renderer.rs @@ -16,7 +16,7 @@ use winit::window::Window; use crate::math::Transform; use crate::render::material::MaterialUniform; use crate::render::render_buffer::BufferWrapperBuilder; -use crate::scene::{ModelComponent, TransformComponent, CameraComponent}; +use crate::scene::{ModelComponent, CameraComponent}; use super::camera::{RenderCamera, CameraUniform}; use super::desc_buf_lay::DescVertexBufferLayout; @@ -395,13 +395,13 @@ impl Renderer for BasicRenderer { let now_inst = Instant::now(); - for (entity, model, model_epoch, transform, transform_epoch) in main_world.view_iter::<(Entities, &ModelComponent, TickOf, &TransformComponent, TickOf)>() { + for (entity, model, model_epoch, transform, transform_epoch) in main_world.view_iter::<(Entities, &ModelComponent, TickOf, &Transform, TickOf)>() { alive_entities.insert(entity); let cached = match self.entity_last_transforms.get_mut(&entity) { Some(last) if transform_epoch == last_epoch => { last.from_transform = last.to_transform; - last.to_transform = transform.transform; + last.to_transform = *transform; last.last_updated_at = Some(last.cached_at); last.cached_at = now_inst; @@ -412,8 +412,8 @@ impl Renderer for BasicRenderer { let cached = CachedTransform { last_updated_at: None, cached_at: now_inst, - from_transform: transform.transform, - to_transform: transform.transform, + from_transform: *transform, + to_transform: *transform, }; self.entity_last_transforms.insert(entity, cached.clone()); cached diff --git a/lyra-game/src/scene/mod.rs b/lyra-game/src/scene/mod.rs index 5d743fa..47898a1 100644 --- a/lyra-game/src/scene/mod.rs +++ b/lyra-game/src/scene/mod.rs @@ -4,9 +4,6 @@ pub use mesh::*; pub mod model; pub use model::*; -pub mod transform; -pub use transform::*; - pub mod camera; pub use camera::*; diff --git a/lyra-game/src/scene/transform.rs b/lyra-game/src/scene/transform.rs deleted file mode 100755 index 516f6f8..0000000 --- a/lyra-game/src/scene/transform.rs +++ /dev/null @@ -1,26 +0,0 @@ -use lyra_ecs::Component; - -use crate::math::Transform; - -#[derive(Clone, Component, Default)] -pub struct TransformComponent { - pub transform: Transform, -} - -impl From for TransformComponent { - fn from(transform: Transform) -> Self { - Self { - transform - } - } -} - -impl TransformComponent { - pub fn new() -> Self { - Self::default() - } - - pub fn from_transform(transform: Transform) -> Self { - Self::from(transform) - } -} \ No newline at end of file diff --git a/lyra-math/Cargo.toml b/lyra-math/Cargo.toml new file mode 100644 index 0000000..463fe9b --- /dev/null +++ b/lyra-math/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "lyra-math" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +glam = { version = "0.24.0" } \ No newline at end of file diff --git a/lyra-game/src/math/angle.rs b/lyra-math/src/angle.rs similarity index 100% rename from lyra-game/src/math/angle.rs rename to lyra-math/src/angle.rs diff --git a/lyra-game/src/math/mod.rs b/lyra-math/src/lib.rs old mode 100755 new mode 100644 similarity index 100% rename from lyra-game/src/math/mod.rs rename to lyra-math/src/lib.rs diff --git a/lyra-game/src/math/transform.rs b/lyra-math/src/transform.rs similarity index 86% rename from lyra-game/src/math/transform.rs rename to lyra-math/src/transform.rs index f2aa027..f154423 100755 --- a/lyra-game/src/math/transform.rs +++ b/lyra-math/src/transform.rs @@ -1,9 +1,9 @@ use glam::{Vec3, Mat4, Quat}; -use crate::math::angle::Angle; +use super::Angle; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct Transform { pub translation: Vec3, pub rotation: Quat, @@ -103,4 +103,18 @@ impl Transform { *self } } +} + +/// Adds a transform to another one +/// +/// The Translations of each transform is added and rotation and scale is multiplied. +impl std::ops::Add for Transform { + type Output = Transform; + + fn add(mut self, rhs: Self) -> Self::Output { + self.translation += rhs.translation; + self.rotation *= rhs.rotation; + self.scale *= rhs.scale; + self + } } \ No newline at end of file diff --git a/lyra-reflect/Cargo.toml b/lyra-reflect/Cargo.toml index 89643d2..0977970 100644 --- a/lyra-reflect/Cargo.toml +++ b/lyra-reflect/Cargo.toml @@ -6,9 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -math = ["dep:glam"] +math = ["dep:lyra-math"] [dependencies] lyra-reflect-derive = { path = "lyra-reflect-derive" } lyra-ecs = { path = "../lyra-ecs" } -glam = { version = "0.24.0", optional = true } \ No newline at end of file +lyra-math = { path = "../lyra-math", optional = true } \ No newline at end of file diff --git a/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs b/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs index d1c9b5a..d4971f8 100644 --- a/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs +++ b/lyra-reflect/lyra-reflect-derive/src/struct_macro.rs @@ -4,7 +4,7 @@ use syn::{Token, parenthesized, punctuated::Punctuated}; struct Field { name: syn::Ident, _eq: Token![=], - ty: syn::Ident, + ty: syn::Path, } impl syn::parse::Parse for Field { diff --git a/lyra-reflect/src/impls/impl_math.rs b/lyra-reflect/src/impls/impl_math.rs index 27a3e0f..2381203 100644 --- a/lyra-reflect/src/impls/impl_math.rs +++ b/lyra-reflect/src/impls/impl_math.rs @@ -1,10 +1,19 @@ use lyra_reflect_derive::{impl_reflect_simple_struct, impl_reflect_trait_value}; -use crate::{lyra_engine, Reflect, Method}; +use crate::{lyra_engine, Method, Reflect}; -impl_reflect_simple_struct!(glam::Vec2, fields(x=f32, y=f32)); -impl_reflect_simple_struct!(glam::Vec3, fields(x=f32, y=f32, z=f32)); -impl_reflect_simple_struct!(glam::Vec4, fields(x=f32, y=f32, z=f32, w=f32)); -impl_reflect_simple_struct!(glam::Quat, fields(x=f32, y=f32, z=f32, w=f32)); +impl_reflect_simple_struct!(lyra_math::Vec2, fields(x = f32, y = f32)); +impl_reflect_simple_struct!(lyra_math::Vec3, fields(x = f32, y = f32, z = f32)); +impl_reflect_simple_struct!(lyra_math::Vec4, fields(x = f32, y = f32, z = f32, w = f32)); +impl_reflect_simple_struct!(lyra_math::Quat, fields(x = f32, y = f32, z = f32, w = f32)); -impl_reflect_trait_value!(glam::Mat4); \ No newline at end of file +impl_reflect_simple_struct!( + lyra_math::Transform, + fields( + translation = lyra_math::Vec3, + rotation = lyra_math::Quat, + scale = lyra_math::Vec3 + ) +); + +impl_reflect_trait_value!(lyra_math::Mat4); -- 2.40.1 From e5599e1d275aab13c3a4279d43ec7bd470a48933 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 13 Jan 2024 11:52:20 -0500 Subject: [PATCH 09/41] scripting, ecs: expose Transform to lua, update component ticks on changes from lua --- examples/testbed/scripts/test.lua | 17 +- examples/testbed/src/main.rs | 32 +-- lyra-ecs/src/archetype.rs | 24 +- lyra-ecs/src/world.rs | 10 + lyra-scripting/Cargo.toml | 1 + .../lyra-scripting-derive/src/lib.rs | 27 ++- lyra-scripting/src/lua/dynamic_iter.rs | 58 ++++- lyra-scripting/src/lua/providers/math.rs | 4 +- lyra-scripting/src/lua/world.rs | 38 ++- lyra-scripting/src/lua/wrappers/mod.rs | 221 +++++++++++++++++- 10 files changed, 374 insertions(+), 58 deletions(-) diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index de8a543..bd2bd06 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -1,6 +1,6 @@ print("Hello World") -function on_init() +--[[ function on_init() print("Lua script was initialized!") end @@ -10,16 +10,23 @@ end function on_pre_update() print("Lua's pre-update function was called") -end +end ]] function on_update() - print("Lua's update function was called") + --print("Lua's update function was called") + + world:view(function (t) + print("Found entity at " .. tostring(t)) + t.translation = t.translation + Vec3.new(0, 0.0008, 0) + + return t + end, Transform) end -function on_post_update() +--[[ 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 +end ]] \ No newline at end of file diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index a06463f..66ecc00 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -1,6 +1,6 @@ use std::ptr::NonNull; -use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis, CommonActionLabel}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{TransformComponent, ModelComponent, CameraComponent}, lua::{LuaScriptingPlugin, LuaScript}, Script, ScriptList}; +use lyra_engine::{math::{self, Vec3}, math::Transform, input::{KeyCode, ActionHandler, Action, ActionKind, LayoutId, ActionMapping, ActionSource, ActionMappingId, InputActionPlugin, MouseInput, MouseAxis, CommonActionLabel}, game::Game, render::{window::{CursorGrabMode, WindowOptions}, light::{PointLight, directional::DirectionalLight, SpotLight}}, change_tracker::Ct, ecs::{system::{Criteria, CriteriaSchedule, BatchedSystem, IntoSystem}, world::World, Component}, DeltaTime, scene::{ModelComponent, CameraComponent}, lua::{LuaScriptingPlugin, LuaScript}, Script, ScriptList}; use lyra_engine::assets::{ResourceManager, Model}; mod free_fly_camera; @@ -77,9 +77,9 @@ struct CubeFlag; async fn main() { let setup_sys = |world: &mut World| -> anyhow::Result<()> { { - let mut window_options = world.get_resource_mut::>(); + /* let mut window_options = world.get_resource_mut::>(); window_options.cursor_grab = CursorGrabMode::Confined; - window_options.cursor_visible = false; + window_options.cursor_visible = false; */ } let mut resman = world.get_resource_mut::(); @@ -92,14 +92,14 @@ async fn main() { world.spawn(( ModelComponent(antique_camera_model), - TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)), + Transform::from_xyz(0.0, -5.0, -10.0), )); { let cube_tran = Transform::from_xyz(-3.5, 0.0, -8.0); //cube_tran.rotate_y(math::Angle::Degrees(180.0)); world.spawn(( - TransformComponent::from(cube_tran), + cube_tran, ModelComponent(crate_model.clone()), CubeFlag, )); @@ -117,7 +117,7 @@ async fn main() { diffuse: 1.0, specular: 1.3, }, - TransformComponent::from(light_tran), + light_tran, ModelComponent(cube_model.clone()), )); } @@ -139,7 +139,7 @@ async fn main() { diffuse: 7.0, specular: 1.0, }, - TransformComponent::from(light_tran), + Transform::from(light_tran), ModelComponent(cube_model.clone()), )); } @@ -183,7 +183,7 @@ async fn main() { diffuse: 1.0, specular: 1.3, }, - TransformComponent::from(light_tran), + Transform::from(light_tran), ModelComponent(cube_model), )); } @@ -214,13 +214,13 @@ async fn main() { const SPEED: f32 = 4.0; let delta_time = **world.get_resource::(); - for (mut transform, _) in world.view_iter::<(&mut TransformComponent, &CubeFlag)>() { - let t = &mut transform.transform; + for (mut transform, _) in world.view_iter::<(&mut Transform, &CubeFlag)>() { + let t = &mut transform; t.rotate_y(math::Angle::Degrees(SPEED * delta_time)); } - for (mut transform, _s) in world.view_iter::<(&mut TransformComponent, &mut SpotLight)>() { - let t = &mut transform.transform; + for (mut transform, _s) in world.view_iter::<(&mut Transform, &mut SpotLight)>() { + let t = &mut transform; t.rotate_x(math::Angle::Degrees(SPEED * delta_time)); } @@ -235,8 +235,8 @@ async fn main() { sys.with_system(spin_system.into_system()); //sys.with_system(fps_system); - game.with_system("fixed", sys, &[]); - fps_plugin(game); + //game.with_system("fixed", sys, &[]); + //fps_plugin(game); }; let action_handler_plugin = |game: &mut Game| { @@ -293,7 +293,9 @@ async fn main() { Ok(()) }; */ - game.world().add_resource(action_handler); + let world = game.world(); + world.add_resource(action_handler); + world.spawn((Vec3::new(0.5, 0.1, 3.0),)); game.with_plugin(InputActionPlugin); //game.with_system("input_test", test_system, &[]); }; diff --git a/lyra-ecs/src/archetype.rs b/lyra-ecs/src/archetype.rs index 52e1760..f91f512 100644 --- a/lyra-ecs/src/archetype.rs +++ b/lyra-ecs/src/archetype.rs @@ -210,6 +210,8 @@ impl ArchetypeId { pub struct Archetype { pub id: ArchetypeId, pub(crate) entities: HashMap, + /// map an Archetype entity id to an entity + pub(crate) ids_to_entity: HashMap, pub(crate) columns: Vec, pub capacity: usize, } @@ -226,6 +228,7 @@ impl Archetype { Archetype { id: new_id, entities: HashMap::new(), + ids_to_entity: HashMap::new(), columns, capacity: DEFAULT_CAPACITY, } @@ -246,16 +249,17 @@ impl Archetype { self.capacity = new_cap; } - let entity_index = self.entities.len(); - self.entities.insert(entity, ArchetypeEntityId(entity_index as u64)); + let entity_index = ArchetypeEntityId(self.entities.len() as u64); + self.entities.insert(entity, entity_index); + self.ids_to_entity.insert(entity_index, entity); bundle.take(|data, type_id, _size| { let col = self.get_column_mut(type_id).unwrap(); - unsafe { col.set_at(entity_index, data, *tick); } + unsafe { col.set_at(entity_index.0 as usize, data, *tick); } col.len += 1; }); - ArchetypeEntityId(entity_index as u64) + entity_index } /// Removes an entity from the Archetype and frees its components. Returns the entity record that took its place in the component column. @@ -286,6 +290,7 @@ impl Archetype { // safe from the .expect at the start of this method. self.entities.remove(&entity).unwrap(); + self.ids_to_entity.remove(&entity_index).unwrap(); // now change the ArchetypeEntityId to be the index that the moved entity was moved into. removed_entity.map(|(e, _a)| (e, entity_index)) @@ -351,14 +356,15 @@ impl Archetype { self.capacity = new_cap; } - let entity_index = self.entities.len(); - self.entities.insert(entity, ArchetypeEntityId(entity_index as u64)); + let entity_index = ArchetypeEntityId(self.entities.len() as u64); + self.entities.insert(entity, entity_index); + self.ids_to_entity.insert(entity_index, entity); for col in self.columns.iter_mut() { col.len += 1; } - ArchetypeEntityId(entity_index as u64) + entity_index } /// Moves the entity from this archetype into another one. @@ -401,6 +407,10 @@ impl Archetype { pub fn entities(&self) -> &HashMap { &self.entities } + + pub fn entity_of_index(&self, id: ArchetypeEntityId) -> Option { + self.ids_to_entity.get(&id).cloned() + } } #[cfg(test)] diff --git a/lyra-ecs/src/world.rs b/lyra-ecs/src/world.rs index 6d2cb98..f6a3274 100644 --- a/lyra-ecs/src/world.rs +++ b/lyra-ecs/src/world.rs @@ -209,6 +209,16 @@ impl World { current_arch.remove_entity(entity, &tick); } + pub fn entity_archetype(&self, entity: Entity) -> Option<&Archetype> { + self.entity_index.get(&entity.id) + .and_then(|record| self.archetypes.get(&record.id)) + } + + pub fn entity_archetype_mut(&mut self, entity: Entity) -> Option<&mut Archetype> { + self.entity_index.get_mut(&entity.id) + .and_then(|record| self.archetypes.get_mut(&record.id)) + } + /// View into the world for a set of entities that satisfy the queries. pub fn view_iter(&self) -> ViewIter { let archetypes = self.archetypes.values().collect(); diff --git a/lyra-scripting/Cargo.toml b/lyra-scripting/Cargo.toml index 46539a6..320ac06 100644 --- a/lyra-scripting/Cargo.toml +++ b/lyra-scripting/Cargo.toml @@ -21,6 +21,7 @@ tracing = "0.1.37" # enabled with lua feature mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? +itertools = "0.12.0" [dev-dependencies] diff --git a/lyra-scripting/lyra-scripting-derive/src/lib.rs b/lyra-scripting/lyra-scripting-derive/src/lib.rs index a0f40c1..ed6e7b1 100644 --- a/lyra-scripting/lyra-scripting-derive/src/lib.rs +++ b/lyra-scripting/lyra-scripting-derive/src/lib.rs @@ -220,6 +220,9 @@ pub(crate) struct WrapUsage { pub matrix: Option, pub vec: Option, + + pub custom_methods: Option, + pub custom_fields: Option, } impl syn::parse::Parse for WrapUsage { @@ -234,6 +237,8 @@ impl syn::parse::Parse for WrapUsage { meta_method_idents: Punctuated::default(), matrix: None, vec: None, + custom_methods: None, + custom_fields: None, }; /* let mut derive_idents = None; let mut field_idents = None; @@ -298,6 +303,14 @@ impl syn::parse::Parse for WrapUsage { s.meta_method_idents = meta_methods; } }, + "custom_methods" => { + let methods_block = input.parse()?; + s.custom_methods = Some(methods_block); + }, + "custom_fields" => { + let block = input.parse()?; + s.custom_fields = Some(block); + } _ => { return Err(syn::Error::new_spanned(ident, "unknown wrapper command")); } @@ -335,6 +348,8 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt 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(); @@ -566,7 +581,7 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { match value { mlua::Value::UserData(ud) => Ok(*ud.borrow::()?), - _ => unreachable!(), + _ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()), } } } @@ -577,6 +592,8 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt #matrix_wrapper_fields #vec_wrapper_fields + + #custom_fields } fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { @@ -591,15 +608,19 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt }); #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 { - std::any::TypeId::of::<#path>() + let t = std::any::TypeId::of::<#path>(); + println!("Got id of {}, it is {:?}", stringify!(#path), t); + t } } }) diff --git a/lyra-scripting/src/lua/dynamic_iter.rs b/lyra-scripting/src/lua/dynamic_iter.rs index 4e6e2cc..ad833b6 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 lyra_ecs::{ComponentColumn, ComponentInfo, Archetype, ArchetypeId, ArchetypeEntityId, query::dynamic::{DynamicType, QueryDynamicType}, query::Fetch}; +use lyra_ecs::{ComponentColumn, ComponentInfo, Archetype, ArchetypeId, ArchetypeEntityId, query::dynamic::{DynamicType, QueryDynamicType}, query::Fetch, Entity}; use lyra_reflect::TypeRegistry; #[cfg(feature = "lua")] @@ -45,6 +45,11 @@ impl<'a> From> for FetchDynamicTy } } +pub struct DynamicViewRow { + entity: Entity, + item: Vec, +} + #[derive(Clone)] pub struct DynamicViewIter { world_ptr: ScriptWorldPtr, @@ -69,15 +74,15 @@ impl<'a> From> for DynamicViewIter } impl Iterator for DynamicViewIter { - type Item = Vec; + 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() { - let entity_index = ArchetypeEntityId(entity_index); if !fetcher.can_visit_item(entity_index) { break; } else { @@ -90,7 +95,14 @@ impl Iterator for DynamicViewIter { continue; } - return Some(fetch_res); + let arch = unsafe { self.archetypes.get_unchecked(self.next_archetype - 1).as_ref() }; + let entity = arch.entity_of_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 @@ -121,6 +133,19 @@ impl Iterator for DynamicViewIter { } } +#[cfg(feature = "lua")] +pub struct ReflectedItem<'a> { + pub proxy: &'a ReflectLuaProxy, + pub comp_ptr: NonNull, + pub comp_ud: mlua::AnyUserData<'a>, +} + +#[cfg(feature = "lua")] +pub struct ReflectedRow<'a> { + pub entity: Entity, + pub row: Vec>, +} + pub struct ReflectedIterator { pub world: ScriptWorldPtr, pub dyn_view: DynamicViewIter, @@ -131,7 +156,7 @@ impl ReflectedIterator { #[cfg(feature = "lua")] - pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option), mlua::AnyUserData<'a>)>> { + pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option> { let n = self.dyn_view.next(); @@ -142,8 +167,13 @@ impl ReflectedIterator { .map(|r| NonNull::from(r.deref())); } - let mut dynamic_row = Vec::new(); - for d in row.iter() { + /* let mut row = ReflectedRow { + entity: row.entity, + row: row.item, + }; */ + + let mut dynamic_row = vec![]; + for d in row.item.iter() { let id = d.info.type_id.as_rust(); let reflected_components = unsafe { self.reflected_components.as_ref().unwrap().as_ref() }; @@ -155,10 +185,20 @@ impl ReflectedIterator { let userdata = (proxy.fn_as_uservalue)(lua, d.ptr).unwrap(); - dynamic_row.push(( (proxy, d.ptr), userdata)); + dynamic_row.push(ReflectedItem { + proxy, + comp_ptr: d.ptr, + comp_ud: userdata + }); + //dynamic_row.push(( (proxy, d.ptr), userdata)); } - Some(dynamic_row) + let row = ReflectedRow { + entity: row.entity, + row: dynamic_row + }; + + Some(row) } else { None } diff --git a/lyra-scripting/src/lua/providers/math.rs b/lyra-scripting/src/lua/providers/math.rs index a4be794..d7d37cd 100644 --- a/lyra-scripting/src/lua/providers/math.rs +++ b/lyra-scripting/src/lua/providers/math.rs @@ -1,7 +1,7 @@ use lyra_ecs::World; use lyra_game::math; use crate::lua::RegisterLuaType; -use crate::lua::wrappers::LuaVec3; +use crate::lua::wrappers::{LuaVec3, LuaTransform}; use crate::{ScriptApiProvider, lua::LuaContext}; @@ -13,6 +13,7 @@ impl ScriptApiProvider for LyraMathApiProvider { fn prepare_world(&mut self, world: &mut World) { world.register_lua_wrapper::(); + world.register_lua_wrapper::(); } fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { @@ -29,6 +30,7 @@ impl ScriptApiProvider for LyraMathApiProvider { let globals = ctx.globals(); globals.set("Vec3", ctx.create_proxy::()?)?; + globals.set("Transform", ctx.create_proxy::()?)?; //globals.set("Vec3", LuaVec3(math::Vec3::ZERO).into_lua(&ctx)?)?; Ok(()) diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 7011d21..6c1cfbb 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, ptr::NonNull}; use lyra_ecs::query::dynamic::QueryDynamicType; use lyra_reflect::TypeRegistry; @@ -62,7 +62,7 @@ impl mlua::UserData for ScriptWorldPtr { let f = lua.create_function_mut(move |lua, ()| { if let Some(row) = reflected_iter.next_lua(lua) { - let row = row.into_iter().map(|(_, ud)| ud.into_lua(lua)) + let row = row.row.into_iter().map(|i| i.comp_ud.into_lua(lua)) .collect::>>()?; Ok(mlua::MultiValue::from_vec(row)) } else { @@ -73,7 +73,7 @@ impl mlua::UserData for ScriptWorldPtr { Ok(f) }); - methods.add_method("view", |lua, this, (system, queries): (mlua::Function, mlua::Variadic)| { + methods.add_method_mut("view", |lua, this, (system, queries): (mlua::Function, mlua::Variadic)| { if queries.is_empty() { return Err(mlua::Error::BadArgument { to: Some("world:view".to_string()), pos: 2, name: Some("...".to_string()), cause: Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage("no component types provided".to_string()))) @@ -99,29 +99,51 @@ impl mlua::UserData for ScriptWorldPtr { reflected_components: None, }; - let reg = this.as_ref().get_resource::(); + let mut current = world.current_tick(); + let mut has_ticked = false; while let Some(row) = reflected_iter.next_lua(lua) { - let (reflects, values): (Vec<(_, _)>, Vec<_>) = row.into_iter().unzip(); + let r = row.row.into_iter() + .map(|r| (r.proxy, r.comp_ud, r.comp_ptr)) + .collect::>(); + let (reflects, values, ptrs): + (Vec<&ReflectLuaProxy>, Vec, Vec>) + = itertools::multiunzip(r); let value_row: Vec<_> = values.into_iter().map(|ud| ud.into_lua(lua)).collect::>>()?; let mult_val = mlua::MultiValue::from_vec(value_row); let res: mlua::MultiValue = system.call(mult_val)?; // if values were returned, find the type in the type registry, and apply the new values if res.len() <= reflects.len() { + // we only want to tick one time per system + if !has_ticked { + current = world.tick(); + has_ticked = true; + } + for (i, comp) in res.into_iter().enumerate() { - let (_proxy, ptr) = reflects[i]; + let ptr = ptrs[i]; match comp.as_userdata() { Some(ud) => { let lua_comp = reflect_user_data(ud); let refl_comp = lua_comp.reflect_branch.as_component_unchecked(); let lua_typeid = refl_comp.info.type_id.as_rust(); - let reg_type = reg.get_type(lua_typeid).unwrap(); + // 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(refl_comp.type_id.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::().unwrap(); - (proxy.fn_apply)(lua, ptr, ud)? + (proxy.fn_apply)(lua, ptr, ud)?; } None => { panic!("A userdata value was not returned!"); diff --git a/lyra-scripting/src/lua/wrappers/mod.rs b/lyra-scripting/src/lua/wrappers/mod.rs index 1ff5e93..22a2317 100644 --- a/lyra-scripting/src/lua/wrappers/mod.rs +++ b/lyra-scripting/src/lua/wrappers/mod.rs @@ -5,7 +5,7 @@ use crate::lyra_engine; use crate as lyra_scripting; // f32 types -/* wrap_math_vec_copy!( +wrap_math_vec_copy!( math::Vec2, derives(PartialEq), fields(x, y), @@ -17,23 +17,27 @@ use crate as lyra_scripting; Mod(LuaVec2, f32), Eq, Unm ) -); */ +); wrap_math_vec_copy!( math::Vec3, derives(PartialEq), fields(x, y, z), metamethods( - ToString, - Eq, Unm - ) - /* metamethods( - Add(LuaVec3, f32), + Add(LuaVec3), Sub(LuaVec3, f32), Div(LuaVec3, f32), Mul(LuaVec3, f32), Mod(LuaVec3, f32), - Eq, Unm - ) */ + Eq, Unm, ToString, + ), + custom_methods { + /* methods.add_meta_method(mlua::MetaMethod::Add, |_, this, (trans,): (LuaTransform,)| { + println!("Adding"); + let mut t = *trans; + t.translation += **this; + Ok(LuaTransform(t)) + }); */ + } ); /* wrap_math_vec_copy!( math::Vec3A, @@ -362,4 +366,201 @@ wrap_math_vec_copy!( Mul(LuaMat2, f32), Unm ) -); */ \ No newline at end of file +); */ + +/* wrap_math_vec_copy!( + math::Mat4, + derives(PartialEq), + no_new, + matrix { + col_type = LuaVec4 + }, + metamethods( + Eq, + Add, + Sub, + Mul(LuaMat4, f32), + Unm + ) +); */ + +wrap_math_vec_copy!( + math::Quat, + derives(PartialEq), + no_new, + metamethods( + Eq, + // __mul for LuaVec3 is manually implemented below since it doesn't return Self + Mul(LuaQuat, f32), + Add, + Sub, + Div(f32), + ), + custom_methods { + methods.add_function("new", |_, (x, y, z, w)| { + Ok(Self(math::Quat::from_xyzw(x, y, z, w))) + }); + + methods.add_function("from_rotation_x", |_, (rad,)| { + let q = math::Quat::from_rotation_x(rad); + Ok(Self(q)) + }); + + methods.add_function("from_rotation_y", |_, (rad,)| { + let q = math::Quat::from_rotation_y(rad); + Ok(Self(q)) + }); + + methods.add_function("from_rotation_z", |_, (rad,)| { + let q = math::Quat::from_rotation_z(rad); + Ok(Self(q)) + }); + + methods.add_method("dot", |_, this, (rhs,): (Self,)| { + Ok(this.dot(rhs.0)) + }); + + methods.add_method("length", |_, this, ()| { + Ok(this.length()) + }); + + methods.add_method("length_squared", |_, this, ()| { + Ok(this.length_squared()) + }); + + methods.add_method("normalize", |_, this, ()| { + Ok(Self(this.normalize())) + }); + + methods.add_method("mult_quat", |_, this, (rhs,): (Self,)| { + Ok(Self(this.0 * rhs.0)) + }); + + methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| { + Ok(LuaVec3(this.0 * rhs.0)) + }); + + // manually implemented here since it doesn't return `Self` + methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (rhs,): (LuaVec3,)| { + Ok(LuaVec3(this.0 * rhs.0)) + }); + + methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { + Ok(Self(this.lerp(*rhs, alpha))) + }); + } +); + +wrap_math_vec_copy!( + math::Transform, + //derives(PartialEq), + no_new, + metamethods(ToString, Eq), + custom_fields { + fields.add_field_method_get("translation", |_, this| { + Ok(LuaVec3(this.translation)) + }); + fields.add_field_method_set("translation", |_, this, v: LuaVec3| { + this.translation = *v; + Ok(()) + }); + + fields.add_field_method_get("rotation", |_, this| { + Ok(LuaQuat(this.rotation)) + }); + fields.add_field_method_set("rotation", |_, this, v: LuaQuat| { + this.rotation = *v; + Ok(()) + }); + + fields.add_field_method_get("scale", |_, this| { + Ok(LuaVec3(this.scale)) + }); + fields.add_field_method_set("scale", |_, this, v: LuaVec3| { + this.scale = *v; + Ok(()) + }); + }, + custom_methods { + methods.add_function("default", |_, ()| { + Ok(Self(math::Transform::default())) + }); + + methods.add_function("new", |_, (pos, rot, scale): (LuaVec3, LuaQuat, LuaVec3)| { + Ok(Self(math::Transform::new(*pos, *rot, *scale))) + }); + + methods.add_function("from_translation", |_, (pos,): (LuaVec3,)| { + Ok(Self(math::Transform::from_translation(*pos))) + }); + + methods.add_function("from_xyz", |_, (x, y, z)| { + Ok(Self(math::Transform::from_xyz(x, y, z))) + }); + + methods.add_method("forward", |_, this, ()| { + Ok(LuaVec3(this.forward())) + }); + + methods.add_method("left", |_, this, ()| { + Ok(LuaVec3(this.left())) + }); + + methods.add_method("up", |_, this, ()| { + Ok(LuaVec3(this.up())) + }); + + methods.add_method_mut("rotate", |_, this, (quat,): (LuaQuat,)| { + this.rotate(*quat); + Ok(()) + }); + + methods.add_method_mut("rotate_x", |_, this, (deg,): (f32,)| { + this.rotate_x(math::Angle::Degrees(deg)); + Ok(()) + }); + + methods.add_method_mut("rotate_y", |_, this, (deg,): (f32,)| { + this.rotate_y(math::Angle::Degrees(deg)); + Ok(()) + }); + + methods.add_method_mut("rotate_z", |_, this, (deg,): (f32,)| { + this.rotate_z(math::Angle::Degrees(deg)); + Ok(()) + }); + + methods.add_method_mut("rotate_x_rad", |_, this, (rad,): (f32,)| { + this.rotate_x(math::Angle::Radians(rad)); + Ok(()) + }); + + methods.add_method_mut("rotate_y_rad", |_, this, (rad,): (f32,)| { + this.rotate_y(math::Angle::Radians(rad)); + Ok(()) + }); + + methods.add_method_mut("rotate_z_rad", |_, this, (rad,): (f32,)| { + this.rotate_z(math::Angle::Radians(rad)); + Ok(()) + }); + + methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { + Ok(Self(this.lerp(*rhs, alpha))) + }); + + // rotate a transform + methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (quat,): (LuaQuat,)| { + let mut t = *this; + t.rotation *= *quat; + Ok(t) + }); + + // move a transform + methods.add_meta_method(mlua::MetaMethod::Add, |_, this, (pos,): (LuaVec3,)| { + let mut t = *this; + t.translation += *pos; + Ok(t) + }); + } +); \ No newline at end of file -- 2.40.1 From 47451a0571bad9532dba5e79aad79e46206ede06 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 15 Jan 2024 18:07:47 -0500 Subject: [PATCH 10/41] resource: reloading of resources --- lyra-resource/Cargo.toml | 3 + lyra-resource/src/loader/image.rs | 6 +- lyra-resource/src/loader/model.rs | 8 +- lyra-resource/src/resource.rs | 143 ++++++++++++++++++--- lyra-resource/src/resource_manager.rs | 173 +++++++++++++++++++++++--- 5 files changed, 298 insertions(+), 35 deletions(-) diff --git a/lyra-resource/Cargo.toml b/lyra-resource/Cargo.toml index bbc45e8..ffdef11 100644 --- a/lyra-resource/Cargo.toml +++ b/lyra-resource/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] anyhow = "1.0.75" base64 = "0.21.4" +crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] } edict = "0.5.0" glam = "0.24.1" gltf = { version = "1.3.0", features = ["KHR_materials_pbrSpecularGlossiness", "KHR_materials_specular"] } @@ -15,6 +16,8 @@ image = "0.24.7" # not using custom matcher, or file type from file path infer = { version = "0.15.0", default-features = false } mime = "0.3.17" +notify = "6.1.1" +#notify = { version = "6.1.1", default-features = false, features = [ "fsevent-sys", "macos_fsevent" ]} # disables crossbeam-channel percent-encoding = "2.3.0" thiserror = "1.0.48" tracing = "0.1.37" diff --git a/lyra-resource/src/loader/image.rs b/lyra-resource/src/loader/image.rs index 8a1940b..d7c3df7 100644 --- a/lyra-resource/src/loader/image.rs +++ b/lyra-resource/src/loader/image.rs @@ -2,7 +2,7 @@ use std::{fs::File, sync::Arc, io::Read}; use image::ImageError; -use crate::{resource_manager::ResourceStorage, texture::Texture, resource::Resource, ResourceManager}; +use crate::{resource_manager::ResourceStorage, texture::Texture, resource::ResHandle, ResourceManager}; use super::{LoaderError, ResourceLoader}; @@ -62,7 +62,7 @@ impl ResourceLoader for ImageLoader { let texture = Texture { image, }; - let res = Resource::with_data(path, texture); + let res = ResHandle::with_data(path, texture); Ok(Arc::new(res)) } @@ -76,7 +76,7 @@ impl ResourceLoader for ImageLoader { let texture = Texture { image, }; - let res = Resource::with_data(&uuid::Uuid::new_v4().to_string(), texture); + let res = ResHandle::with_data(&uuid::Uuid::new_v4().to_string(), texture); Ok(Arc::new(res)) } diff --git a/lyra-resource/src/loader/model.rs b/lyra-resource/src/loader/model.rs index c9caf4b..396eae5 100644 --- a/lyra-resource/src/loader/model.rs +++ b/lyra-resource/src/loader/model.rs @@ -2,7 +2,7 @@ use std::{sync::Arc, path::PathBuf}; use thiserror::Error; -use crate::{ResourceLoader, LoaderError, Mesh, Model, MeshVertexAttribute, VertexAttributeData, Resource, Material, MeshIndices, ResourceManager, util}; +use crate::{ResourceLoader, LoaderError, Mesh, Model, MeshVertexAttribute, VertexAttributeData, ResHandle, Material, MeshIndices, ResourceManager, util}; use tracing::debug; @@ -189,7 +189,7 @@ impl ResourceLoader for ModelLoader { .collect(); debug!("Loaded {} meshes, and {} materials from '{}'", meshes.len(), materials.len(), path); - Ok(Arc::new(Resource::with_data(path, Model::new(meshes)))) + Ok(Arc::new(ResHandle::with_data(path, Model::new(meshes)))) } #[allow(unused_variables)] @@ -216,8 +216,8 @@ mod tests { let mut manager = ResourceManager::new(); let loader = ModelLoader::default(); let model = loader.load(&mut manager, &path).unwrap(); - let model = Arc::downcast::>(model.as_arc_any()).unwrap(); - let model = model.data.as_ref().unwrap(); + let model = Arc::downcast::>(model.as_arc_any()).unwrap(); + let model = model.data_ref(); assert_eq!(model.meshes.len(), 1); // There should only be 1 mesh let mesh = &model.meshes[0]; assert!(mesh.position().unwrap().len() > 0); diff --git a/lyra-resource/src/resource.rs b/lyra-resource/src/resource.rs index 2dbb2a6..d36c82f 100644 --- a/lyra-resource/src/resource.rs +++ b/lyra-resource/src/resource.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::sync::{Arc, RwLock}; use uuid::Uuid; @@ -8,25 +8,140 @@ pub enum ResourceState { Ready, } -#[derive(Clone)] -pub struct Resource { - pub path: String, - pub data: Option>, - pub uuid: Uuid, - pub state: ResourceState, +pub struct ResourceDataRef<'a, T> { + guard: std::sync::RwLockReadGuard<'a, Resource>, } -/// A helper type to make it easier to use resources -pub type ResHandle = Arc>; +impl<'a, T> std::ops::Deref for ResourceDataRef<'a, T> { + type Target = T; -impl Resource { + fn deref(&self) -> &Self::Target { + // safety: this struct must only be created if the resource is loaded + self.guard.data.as_ref().unwrap() + } +} + +pub(crate) struct Resource { + path: String, + pub(crate) data: Option, + pub(crate) version: usize, + pub(crate) state: ResourceState, + uuid: Uuid, +} + +/// A handle to a resource. +/// +/// # Note +/// This struct has an inner [`RwLock`] to the resource data, so most methods may be blocking. +/// However, the only times it will be blocking is if another thread is reloading the resource +/// and has a write lock on the data. This means that most of the time, it is not blocking. +pub struct ResHandle { + pub(crate) data: Arc>>, +} + +impl Clone for ResHandle { + fn clone(&self) -> Self { + Self { data: self.data.clone() } + } +} + +impl ResHandle { /// Create the resource with data, its assumed the state is `Ready` pub fn with_data(path: &str, data: T) -> Self { - Self { + let res_version = Resource { path: path.to_string(), - data: Some(Arc::new(data)), - uuid: Uuid::new_v4(), + data: Some(data), + version: 0, state: ResourceState::Ready, + uuid: Uuid::new_v4(), + }; + + Self { + data: Arc::new(RwLock::new(res_version)), } } -} \ No newline at end of file + + /// Returns a boolean indicated if this resource is loaded + pub fn is_loaded(&self) -> bool { + let d = self.data.read().expect("Resource mutex was poisoned!"); + d.state == ResourceState::Ready + } + + /// Returns the current state of the resource. + pub fn state(&self) -> ResourceState { + let d = self.data.read().expect("Resource mutex was poisoned!"); + d.state + } + + /// Returns the path that the resource was loaded from. + pub fn path(&self) -> String { + let d = self.data.read().expect("Resource mutex was poisoned!"); + d.path.to_string() + } + + /// Returns the uuid of the resource. + pub fn uuid(&self) -> Uuid { + let d = self.data.read().expect("Resource mutex was poisoned!"); + d.uuid + } + + /// Retrieves the current version of the resource. This gets incremented when the resource + /// is reloaded. + pub fn version(&self) -> usize { + let d = self.data.read().expect("Resource mutex was poisoned!"); + d.version + } + + /// Get a reference to the data in the resource + /// + /// # Panics + /// Panics if the resource was not loaded yet. + pub fn data_ref<'a>(&'a self) -> ResourceDataRef<'a, T> { + let d = self.data.read().expect("Resource mutex was poisoned!"); + ResourceDataRef { + guard: d + } + } + + /// Attempt to get a borrow to the resource data. Returns `None` if the resource is not loaded. + pub fn try_data_ref<'a>(&'a self) -> Option> { + if self.is_loaded() { + let d = self.data.read().expect("Resource mutex was poisoned!"); + Some(ResourceDataRef { + guard: d + }) + } else { + None + } + } + + /* /// Get a reference to the data in the resource + /// + /// # Panics + /// Panics if the resource was not loaded yet. + pub fn data_ref(&self) -> &T { + self.data.as_ref() + .expect("Resource is not loaded yet (use try_data_ref, or wait until its loaded)!") + } + + /// If the resource is loaded, returns `Some` reference to the data in the resource, + /// else it will return `None` + pub fn try_data_ref(&self) -> Option<&T> { + self.data.as_ref() + } + + /// Get a **mutable** reference to the data in the resource + /// + /// # Panics + /// Panics if the resource was not loaded yet. + pub fn data_mut(&mut self) -> &mut T { + self.data.as_mut() + .expect("Resource is not loaded yet (use try_data_ref, or wait until its loaded)!") + } + + /// If the resource is loaded, returns `Some` **mutable** reference to the data in the resource, + /// else it will return `None` + pub fn try_data_mut(&mut self) -> Option<&mut T> { + self.data.as_mut() + } */ +} diff --git a/lyra-resource/src/resource_manager.rs b/lyra-resource/src/resource_manager.rs index 0224269..593c0d4 100644 --- a/lyra-resource/src/resource_manager.rs +++ b/lyra-resource/src/resource_manager.rs @@ -1,13 +1,16 @@ -use std::{sync::Arc, collections::HashMap, any::Any}; +use std::{sync::{Arc, RwLock}, collections::{HashMap, VecDeque}, any::Any, thread::{Thread, JoinHandle}, rc::Rc, path::Path, ops::Deref}; +use crossbeam::channel::{Receiver, Sender}; +use notify::{Watcher, RecommendedWatcher}; use thiserror::Error; -use crate::{resource::Resource, loader::{ResourceLoader, LoaderError, image::ImageLoader, model::ModelLoader}}; +use crate::{resource::ResHandle, loader::{ResourceLoader, LoaderError, image::ImageLoader, model::ModelLoader}}; pub trait ResourceStorage: Send + Sync + Any + 'static { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; fn as_arc_any(self: Arc) -> Arc; + fn as_box_any(self: Box) -> Box; } /// Implements this trait for anything that fits the type bounds @@ -23,6 +26,10 @@ impl ResourceStorage for T { fn as_arc_any(self: Arc) -> Arc { self.clone() } + + fn as_box_any(self: Box) -> Box { + self + } } #[derive(Error, Debug)] @@ -46,9 +53,16 @@ impl From for RequestError { /// A struct that stores all Manager data. This is requried for sending //struct ManagerStorage +/// A struct that +pub struct ResourceWatcher { + watcher: Arc>, + events_recv: Receiver>, +} + pub struct ResourceManager { resources: HashMap>, loaders: Vec>, + watchers: HashMap, } impl Default for ResourceManager { @@ -62,6 +76,7 @@ impl ResourceManager { Self { resources: HashMap::new(), loaders: vec![ Arc::new(ImageLoader), Arc::new(ModelLoader) ], + watchers: HashMap::new(), } } @@ -73,11 +88,15 @@ impl ResourceManager { self.loaders.push(Arc::new(L::default())); } - pub fn request(&mut self, path: &str) -> Result>, RequestError> { + pub fn request(&mut self, path: &str) -> Result, RequestError> + where + T: Send + Sync + Any + 'static + { match self.resources.get(&path.to_string()) { Some(res) => { let res = res.clone().as_arc_any(); - let res = res.downcast::>().expect("Failure to downcast resource"); + let res: Arc> = res.downcast::>().expect("Failure to downcast resource"); + let res = ResHandle::::clone(&res); Ok(res) }, @@ -88,12 +107,14 @@ impl ResourceManager { // Load the resource and store it let loader = Arc::clone(loader); // stop borrowing from self let res = loader.load(self, path)?; + let res: Arc = Arc::from(res); self.resources.insert(path.to_string(), res.clone()); // cast Arc to Arc let res = res.as_arc_any(); - let res = res.downcast::>() + let res = res.downcast::>() .expect("Failure to downcast resource! Does the loader return an `Arc>`?"); + let res = ResHandle::::clone(&res); Ok(res) } else { @@ -112,18 +133,23 @@ impl ResourceManager { /// * `bytes` - The bytes to store. /// /// Returns: The `Arc` to the now stored resource - pub fn load_bytes(&mut self, ident: &str, mime_type: &str, bytes: Vec, offset: usize, length: usize) -> Result>, RequestError> { + pub fn load_bytes(&mut self, ident: &str, mime_type: &str, bytes: Vec, offset: usize, length: usize) -> Result, RequestError> + where + T: Send + Sync + Any + 'static + { if let Some(loader) = self.loaders.iter() .find(|l| l.does_support_mime(mime_type)) { let loader = loader.clone(); let res = loader.load_bytes(self, bytes, offset, length)?; + let res: Arc = Arc::from(res); self.resources.insert(ident.to_string(), res.clone()); // code here... // cast Arc to Arc let res = res.as_arc_any(); - let res = res.downcast::>() + let res = res.downcast::>() .expect("Failure to downcast resource! Does the loader return an `Arc>`?"); + let res = ResHandle::::clone(&res); Ok(res) } else { @@ -132,11 +158,14 @@ impl ResourceManager { } /// Requests bytes from the manager. - pub fn request_loaded_bytes(&mut self, ident: &str) -> Result>, RequestError> { + pub fn request_loaded_bytes(&mut self, ident: &str) -> Result>, RequestError> + where + T: Send + Sync + Any + 'static + { match self.resources.get(&ident.to_string()) { Some(res) => { let res = res.clone().as_arc_any(); - let res = res.downcast::>().expect("Failure to downcast resource"); + let res = res.downcast::>().expect("Failure to downcast resource"); Ok(res) }, @@ -145,6 +174,81 @@ impl ResourceManager { } } } + + /// Start watching a path for changes. Returns a mspc channel that will send events + pub fn watch(&mut self, path: &str, recursive: bool) -> notify::Result>> { + let (send, recv) = crossbeam::channel::bounded(15); + let mut watcher = RecommendedWatcher::new(send, notify::Config::default())?; + + let recurse_mode = match recursive { + true => notify::RecursiveMode::Recursive, + false => notify::RecursiveMode::NonRecursive, + }; + watcher.watch(path.as_ref(), recurse_mode)?; + + let watcher = Arc::new(RwLock::new(watcher)); + let watcher = ResourceWatcher { + watcher, + events_recv: recv.clone(), + }; + + self.watchers.insert(path.to_string(), watcher); + + Ok(recv) + } + + /// Stops watching a path + pub fn stop_watching(&mut self, path: &str) -> notify::Result<()> { + if let Some(watcher) = self.watchers.get(path) { + let mut watcher = watcher.watcher.write().unwrap(); + watcher.unwatch(Path::new(path))?; + } + + Ok(()) + } + + /// Returns a mspc receiver for watcher events of a specific path. The path must already + /// be watched with [`ResourceManager::watch`] for this to return `Some`. + pub fn watcher_event_recv(&self, path: &str) -> Option>> { + self.watchers.get(&path.to_string()) + .map(|w| w.events_recv.clone()) + } + + /// Reloads a resource. The data inside the resource will be updated, the state may + pub fn reload(&mut self, resource: ResHandle) -> Result<(), RequestError> + where + T: Send + Sync + Any + 'static + { + let path = resource.path(); + if let Some(loader) = self.loaders.iter() + .find(|l| l.does_support_file(&path)) { + let loader = Arc::clone(loader); // stop borrowing from self + let loaded = loader.load(self, &path)?; + let loaded = loaded.as_arc_any(); + + let loaded = loaded.downcast::>() + .unwrap(); + let loaded = match Arc::try_unwrap(loaded) { + Ok(v) => v, + Err(_) => panic!("Seems impossible that this would happen, the resource was just loaded!"), + }; + let loaded = loaded.data; + let loaded = match Arc::try_unwrap(loaded) { + Ok(v) => v, + Err(_) => panic!("Seems impossible that this would happen, the resource was just loaded!"), + }; + let loaded = loaded.into_inner().unwrap(); + + let res_lock = &resource.data; + let mut res_lock = res_lock.write().unwrap(); + let version = res_lock.version; + // safe since loaded was JUST loaded, it will be unlocked and not poisoned + *res_lock = loaded; + res_lock.version = version + 1; + } + + Ok(()) + } } #[cfg(test)] @@ -165,20 +269,20 @@ mod tests { fn load_image() { let mut man = ResourceManager::new(); let res = man.request::(&get_image("squiggles.png")).unwrap(); - assert_eq!(res.state, ResourceState::Ready); - let img = res.data.as_ref(); + assert_eq!(res.state(), ResourceState::Ready); + let img = res.try_data_ref(); img.unwrap(); } - /// Ensures that only one copy of the same thing made + /// Ensures that only one copy of the same data was made #[test] fn ensure_single() { let mut man = ResourceManager::new(); let res = man.request::(&get_image("squiggles.png")).unwrap(); - assert_eq!(Arc::strong_count(&res), 2); + assert_eq!(Arc::strong_count(&res.data), 2); let resagain = man.request::(&get_image("squiggles.png")).unwrap(); - assert_eq!(Arc::strong_count(&resagain), 3); + assert_eq!(Arc::strong_count(&resagain.data), 3); } /// Ensures that an error is returned when a file that doesn't exist is requested @@ -196,4 +300,45 @@ mod tests { } ); } + + #[test] + fn reload_image() { + let mut man = ResourceManager::new(); + let res = man.request::(&get_image("squiggles.png")).unwrap(); + assert_eq!(res.state(), ResourceState::Ready); + let img = res.try_data_ref(); + img.unwrap(); + + man.reload(res.clone()).unwrap(); + assert_eq!(res.version(), 1); + + man.reload(res.clone()).unwrap(); + assert_eq!(res.version(), 2); + } + + #[test] + fn watch_image() { + let image_path = get_image("squiggles.png"); + let image_bytes = std::fs::read(&image_path).unwrap(); + + let mut man = ResourceManager::new(); + let res = man.request::(&image_path).unwrap(); + assert_eq!(res.state(), ResourceState::Ready); + let img = res.try_data_ref(); + img.unwrap(); + + let recv = man.watch(&image_path, false).unwrap(); + + std::fs::remove_file(&image_path).unwrap(); + + let event = recv.recv().unwrap(); + let event = event.unwrap(); + + std::fs::write(image_path, image_bytes).unwrap(); + + println!("Event kind: {:?}", event.kind); + + // for some reason, + assert!(event.kind.is_remove() || event.kind.is_modify()); + } } \ No newline at end of file -- 2.40.1 From a9705b3f81c2925b55d83a3ed39bc01bbca5b902 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 15 Jan 2024 21:30:05 -0500 Subject: [PATCH 11/41] scripting, game: make fixes for changes to resource crate --- Cargo.lock | 116 ++++++++++++++++++++++++++++++- lyra-game/src/render/material.rs | 12 ++-- lyra-game/src/render/renderer.rs | 2 +- lyra-game/src/render/texture.rs | 6 +- lyra-scripting/src/lua/loader.rs | 6 +- lyra-scripting/src/lua/mod.rs | 2 +- 6 files changed, 129 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 091300d..7b962c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -590,6 +590,19 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + [[package]] name = "crossbeam-channel" version = "0.5.10" @@ -622,6 +635,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.18" @@ -814,6 +836,18 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + [[package]] name = "flate2" version = "1.0.28" @@ -854,6 +888,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3aaba7ff514ee9d802b562927f80b1e94e93d8e74c31b134c9c3762dabf1a36b" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -940,7 +983,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0af1827b7dd2f36d740ae804c1b3ea0d64c12533fb61ff91883005143a0e8c5a" dependencies = [ "core-foundation", - "inotify", + "inotify 0.10.2", "io-kit-sys", "js-sys", "libc", @@ -1196,6 +1239,17 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + [[package]] name = "inotify" version = "0.10.2" @@ -1317,6 +1371,26 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -1505,12 +1579,14 @@ version = "0.0.1" dependencies = [ "anyhow", "base64 0.21.5", + "crossbeam", "edict", "glam", "gltf", "image", "infer", "mime", + "notify", "percent-encoding", "thiserror", "tracing", @@ -1736,6 +1812,25 @@ dependencies = [ "libc", ] +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.4.1", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify 0.9.6", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2206,6 +2301,15 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -2680,6 +2784,16 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/lyra-game/src/render/material.rs b/lyra-game/src/render/material.rs index 162c561..dec5b12 100755 --- a/lyra-game/src/render/material.rs +++ b/lyra-game/src/render/material.rs @@ -11,11 +11,11 @@ pub struct MaterialSpecular { impl MaterialSpecular { pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc, value: &lyra_resource::Specular) -> Self { - let tex = value.texture.as_ref().map(|t| &t.data.as_ref().unwrap().image) - .map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), i, None).unwrap()); + let tex = value.texture.as_ref().map(|t| t.data_ref()) + .map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), &i.image, None).unwrap()); - let color_tex = value.color_texture.as_ref().map(|t| &t.data.as_ref().unwrap().image) - .map(|i| RenderTexture::from_image(device, queue, bg_layout, i, None).unwrap()); + let color_tex = value.color_texture.as_ref().map(|t| t.data_ref()) + .map(|i| RenderTexture::from_image(device, queue, bg_layout, &i.image, None).unwrap()); Self { factor: value.factor, @@ -39,8 +39,8 @@ pub struct Material { impl Material { pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc, value: &lyra_resource::Material) -> Self { - let diffuse_texture = value.base_color_texture.as_ref().map(|t| &t.data.as_ref().unwrap().image) - .map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), i, None).unwrap()); + let diffuse_texture = value.base_color_texture.as_ref().map(|t| t.data_ref()) + .map(|i| RenderTexture::from_image(device, queue, bg_layout.clone(), &i.image, None).unwrap()); let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s)); diff --git a/lyra-game/src/render/renderer.rs b/lyra-game/src/render/renderer.rs index b7aa4a4..a513d8f 100755 --- a/lyra-game/src/render/renderer.rs +++ b/lyra-game/src/render/renderer.rs @@ -430,7 +430,7 @@ impl Renderer for BasicRenderer { let transform_val = cached.from_transform.lerp(cached.to_transform, alpha); - let model = model.data.as_ref().unwrap().as_ref(); + let model = model.data_ref(); for mesh in model.meshes.iter() { if !self.process_mesh(entity, transform_val, mesh) && model_epoch == last_epoch { self.update_mesh_buffers(entity, mesh); diff --git a/lyra-game/src/render/texture.rs b/lyra-game/src/render/texture.rs index c1b019f..aa99388 100755 --- a/lyra-game/src/render/texture.rs +++ b/lyra-game/src/render/texture.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use image::GenericImageView; -use lyra_resource::{Resource, Texture}; +use lyra_resource::{ResHandle, Texture}; use super::render_buffer::BindGroupPair; @@ -153,8 +153,8 @@ impl RenderTexture { }) } - pub fn update_texture(&mut self, _device: &wgpu::Device, queue: &wgpu::Queue, texture: &Arc>) { - let texture = &texture.data.as_ref().unwrap().image; + pub fn update_texture(&mut self, _device: &wgpu::Device, queue: &wgpu::Queue, texture: &Arc>) { + let texture = &texture.data_ref().image; let rgba = texture.to_rgba8(); let dimensions = texture.dimensions(); let size = wgpu::Extent3d { diff --git a/lyra-scripting/src/lua/loader.rs b/lyra-scripting/src/lua/loader.rs index fe0492d..eea2efd 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, Resource}; +use lyra_resource::{ResourceLoader, ResHandle}; #[derive(Debug, Clone)] pub struct LuaScript { @@ -23,7 +23,7 @@ impl ResourceLoader for LuaLoader { fn load(&self, _resource_manager: &mut lyra_resource::ResourceManager, path: &str) -> Result, lyra_resource::LoaderError> { let bytes = std::fs::read(path)?; - let s = Resource::with_data(path, LuaScript { + let s = ResHandle::with_data(path, LuaScript { bytes }); @@ -34,7 +34,7 @@ impl ResourceLoader for LuaLoader { let end = offset + length; let bytes = bytes[offset..end].to_vec(); - let s = Resource::with_data("from bytes", LuaScript { + let s = ResHandle::with_data("from bytes", LuaScript { bytes }); diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index 3b3d6fb..d159a38 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -175,7 +175,7 @@ pub fn lua_scripts_create_contexts(mut host: ResMut, }; if !contexts.has_context(script.id()) { - if let Some(script_res) = &script.res_handle().data { + if let Some(script_res) = &script.res_handle().try_data_ref() { debug!("Loading script '{}'...", script.name()); let mut script_ctx = host.load_script(&script_res.bytes, &script_data, &mut providers).unwrap(); debug!("Finished loading script '{}'", script.name()); -- 2.40.1 From db77ca43881c9a6cceb6725295b7918638882c5f Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 15 Jan 2024 23:22:21 -0500 Subject: [PATCH 12/41] scripting: lua script auto-reloading --- Cargo.lock | 24 +++ examples/testbed/scripts/test.lua | 2 +- examples/testbed/src/main.rs | 1 + lyra-game/src/render/light/mod.rs | 4 +- lyra-resource/Cargo.toml | 1 + lyra-resource/src/lib.rs | 5 +- lyra-resource/src/resource.rs | 10 +- lyra-resource/src/resource_manager.rs | 49 +++-- lyra-scripting/src/host.rs | 12 +- lyra-scripting/src/lua/mod.rs | 261 +++++++++++++++++++------- 10 files changed, 279 insertions(+), 90 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b962c2..ad8a285 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -836,6 +836,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "file-id" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "filetime" version = "0.2.23" @@ -1587,6 +1596,7 @@ dependencies = [ "infer", "mime", "notify", + "notify-debouncer-full", "percent-encoding", "thiserror", "tracing", @@ -1831,6 +1841,20 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "notify-debouncer-full" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154" +dependencies = [ + "crossbeam-channel", + "file-id", + "log", + "notify", + "parking_lot", + "walkdir", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index bd2bd06..ea5bdc6 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -16,7 +16,7 @@ function on_update() --print("Lua's update function was called") world:view(function (t) - print("Found entity at " .. tostring(t)) + --print("Found entity at a really cool place: " .. tostring(t)) t.translation = t.translation + Vec3.new(0, 0.0008, 0) return t diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 66ecc00..0dabc2f 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -306,6 +306,7 @@ async fn main() { let world = game.world(); let mut 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); diff --git a/lyra-game/src/render/light/mod.rs b/lyra-game/src/render/light/mod.rs index fb4728b..adbe0b5 100644 --- a/lyra-game/src/render/light/mod.rs +++ b/lyra-game/src/render/light/mod.rs @@ -8,8 +8,6 @@ pub use spotlight::*; use std::{collections::{VecDeque, HashMap}, marker::PhantomData}; -use tracing::debug; - use std::mem; use crate::math::Transform; @@ -174,7 +172,7 @@ impl LightUniformBuffers { if !self.point_lights.has_light(entity) || light_epoch == world_tick || transform_epoch == world_tick { let uniform = PointLightUniform::from_bundle(&point_light, &transform); self.point_lights.update_or_add(&mut self.lights_uniform.point_lights, entity, uniform); - debug!("Updated point light"); + //debug!("Updated point light"); } } diff --git a/lyra-resource/Cargo.toml b/lyra-resource/Cargo.toml index ffdef11..0f6adf3 100644 --- a/lyra-resource/Cargo.toml +++ b/lyra-resource/Cargo.toml @@ -17,6 +17,7 @@ image = "0.24.7" infer = { version = "0.15.0", default-features = false } mime = "0.3.17" notify = "6.1.1" +notify-debouncer-full = "0.3.1" #notify = { version = "6.1.1", default-features = false, features = [ "fsevent-sys", "macos_fsevent" ]} # disables crossbeam-channel percent-encoding = "2.3.0" thiserror = "1.0.48" diff --git a/lyra-resource/src/lib.rs b/lyra-resource/src/lib.rs index f78b8ab..65fe735 100644 --- a/lyra-resource/src/lib.rs +++ b/lyra-resource/src/lib.rs @@ -16,4 +16,7 @@ pub use model::*; pub mod material; pub use material::*; -pub(crate) mod util; \ No newline at end of file +pub(crate) mod util; + +pub use crossbeam::channel as channel; +pub use notify; \ No newline at end of file diff --git a/lyra-resource/src/resource.rs b/lyra-resource/src/resource.rs index d36c82f..ef85222 100644 --- a/lyra-resource/src/resource.rs +++ b/lyra-resource/src/resource.rs @@ -27,6 +27,7 @@ pub(crate) struct Resource { pub(crate) version: usize, pub(crate) state: ResourceState, uuid: Uuid, + pub(crate) is_watched: bool, } /// A handle to a resource. @@ -54,6 +55,7 @@ impl ResHandle { version: 0, state: ResourceState::Ready, uuid: Uuid::new_v4(), + is_watched: false, }; Self { @@ -61,7 +63,13 @@ impl ResHandle { } } - /// Returns a boolean indicated if this resource is loaded + /// Returns a boolean indicating if this resource's path is being watched. + pub fn is_watched(&self) -> bool { + let d = self.data.read().expect("Resource mutex was poisoned!"); + d.is_watched + } + + /// Returns a boolean indicating if this resource is loaded pub fn is_loaded(&self) -> bool { let d = self.data.read().expect("Resource mutex was poisoned!"); d.state == ResourceState::Ready diff --git a/lyra-resource/src/resource_manager.rs b/lyra-resource/src/resource_manager.rs index 593c0d4..dcf7e8b 100644 --- a/lyra-resource/src/resource_manager.rs +++ b/lyra-resource/src/resource_manager.rs @@ -1,7 +1,8 @@ -use std::{sync::{Arc, RwLock}, collections::{HashMap, VecDeque}, any::Any, thread::{Thread, JoinHandle}, rc::Rc, path::Path, ops::Deref}; +use std::{sync::{Arc, RwLock}, collections::HashMap, any::Any, path::Path, time::Duration}; -use crossbeam::channel::{Receiver, Sender}; +use crossbeam::channel::Receiver; use notify::{Watcher, RecommendedWatcher}; +use notify_debouncer_full::{DebouncedEvent, FileIdMap}; use thiserror::Error; use crate::{resource::ResHandle, loader::{ResourceLoader, LoaderError, image::ImageLoader, model::ModelLoader}}; @@ -11,10 +12,11 @@ pub trait ResourceStorage: Send + Sync + Any + 'static { fn as_any_mut(&mut self) -> &mut dyn Any; fn as_arc_any(self: Arc) -> Arc; fn as_box_any(self: Box) -> Box; + fn set_watched(&self, watched: bool); } /// Implements this trait for anything that fits the type bounds -impl ResourceStorage for T { +impl ResourceStorage for ResHandle { fn as_any(&self) -> &dyn Any { self } @@ -30,6 +32,11 @@ impl ResourceStorage for T { fn as_box_any(self: Box) -> Box { self } + + fn set_watched(&self, watched: bool) { + let mut w = self.data.write().unwrap(); + w.is_watched = watched; + } } #[derive(Error, Debug)] @@ -55,8 +62,8 @@ impl From for RequestError { /// A struct that pub struct ResourceWatcher { - watcher: Arc>, - events_recv: Receiver>, + debouncer: Arc>>, + events_recv: Receiver, Vec>>, } pub struct ResourceManager { @@ -176,32 +183,40 @@ impl ResourceManager { } /// Start watching a path for changes. Returns a mspc channel that will send events - pub fn watch(&mut self, path: &str, recursive: bool) -> notify::Result>> { + pub fn watch(&mut self, path: &str, recursive: bool) -> notify::Result, Vec>>> { let (send, recv) = crossbeam::channel::bounded(15); - let mut watcher = RecommendedWatcher::new(send, notify::Config::default())?; + let mut watcher = notify_debouncer_full::new_debouncer(Duration::from_millis(1000), None, send)?; let recurse_mode = match recursive { true => notify::RecursiveMode::Recursive, false => notify::RecursiveMode::NonRecursive, }; - watcher.watch(path.as_ref(), recurse_mode)?; + watcher.watcher().watch(path.as_ref(), recurse_mode)?; let watcher = Arc::new(RwLock::new(watcher)); let watcher = ResourceWatcher { - watcher, + debouncer: watcher, events_recv: recv.clone(), }; self.watchers.insert(path.to_string(), watcher); + let res = self.resources.get(&path.to_string()) + .expect("The path that was watched has not been loaded as a resource yet"); + res.set_watched(true); + Ok(recv) } /// Stops watching a path pub fn stop_watching(&mut self, path: &str) -> notify::Result<()> { if let Some(watcher) = self.watchers.get(path) { - let mut watcher = watcher.watcher.write().unwrap(); - watcher.unwatch(Path::new(path))?; + let mut watcher = watcher.debouncer.write().unwrap(); + watcher.watcher().unwatch(Path::new(path))?; + + // unwrap is safe since only loaded resources can be watched + let res = self.resources.get(&path.to_string()).unwrap(); + res.set_watched(false); } Ok(()) @@ -209,7 +224,7 @@ impl ResourceManager { /// Returns a mspc receiver for watcher events of a specific path. The path must already /// be watched with [`ResourceManager::watch`] for this to return `Some`. - pub fn watcher_event_recv(&self, path: &str) -> Option>> { + pub fn watcher_event_recv(&self, path: &str) -> Option, Vec>>> { self.watchers.get(&path.to_string()) .map(|w| w.events_recv.clone()) } @@ -242,8 +257,9 @@ impl ResourceManager { let res_lock = &resource.data; let mut res_lock = res_lock.write().unwrap(); let version = res_lock.version; - // safe since loaded was JUST loaded, it will be unlocked and not poisoned - *res_lock = loaded; + + res_lock.data = loaded.data; + res_lock.state = loaded.state; res_lock.version = version + 1; } @@ -336,9 +352,6 @@ mod tests { std::fs::write(image_path, image_bytes).unwrap(); - println!("Event kind: {:?}", event.kind); - - // for some reason, - assert!(event.kind.is_remove() || event.kind.is_modify()); + assert!(event.iter().any(|ev| ev.kind.is_remove() || ev.kind.is_modify())); } } \ No newline at end of file diff --git a/lyra-scripting/src/host.rs b/lyra-scripting/src/host.rs index b9cb4b9..e25a56d 100644 --- a/lyra-scripting/src/host.rs +++ b/lyra-scripting/src/host.rs @@ -43,7 +43,9 @@ pub trait ScriptApiProvider { type ScriptContext; /// Prepare the ECS world for this api. Things like registering types with the type registry happen here. - fn prepare_world(&mut self, world: &mut World) {} + fn prepare_world(&mut self, world: &mut World) { + let _ = world; // remove compiler warning + } /// Exposes an API in the provided script context. fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>; @@ -120,4 +122,12 @@ impl ScriptContexts { pub fn has_context(&self, script_id: u64) -> bool { self.contexts.contains_key(&script_id) } + + pub fn remove_context(&mut self, script_id: u64) -> Option { + self.contexts.remove(&script_id) + } + + pub fn len(&self) -> usize { + self.contexts.len() + } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index d159a38..7c9f6af 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -1,8 +1,9 @@ pub mod dynamic_iter; +use anyhow::anyhow; pub use dynamic_iter::*; pub mod world; -use lyra_game::{plugin::Plugin, game::GameStages}; +use lyra_game::{game::GameStages, plugin::Plugin}; use lyra_resource::ResourceManager; use tracing::{debug, error, trace}; pub use world::*; @@ -19,34 +20,49 @@ pub mod wrappers; #[cfg(test)] mod test; -use std::{ptr::NonNull, sync::Mutex, any::TypeId}; +use std::{any::TypeId, ptr::NonNull, sync::Mutex}; -use lyra_ecs::{DynamicBundle, World, query::{ResMut, View, Entities}}; -use lyra_reflect::{Reflect, FromType, RegisteredType, TypeRegistry}; +use lyra_ecs::{ + query::{Entities, ResMut, View}, + DynamicBundle, World, +}; +use lyra_reflect::{FromType, Reflect, TypeRegistry}; -use mlua::{Lua, AnyUserDataExt}; +use mlua::{AnyUserDataExt, Lua}; pub type LuaContext = Mutex; pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; -use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptApiProviders, ScriptContexts, ScriptWorldPtr, ScriptList, ScriptData, ScriptHost, ScriptError, GameScriptExt}; +use crate::{ + GameScriptExt, ScriptApiProviders, ScriptBorrow, ScriptContexts, ScriptData, + ScriptDynamicBundle, ScriptError, ScriptHost, ScriptList, ScriptWorldPtr, +}; -use self::providers::{UtilityApiProvider, LyraMathApiProvider, LyraEcsApiProvider}; +use self::providers::{LyraEcsApiProvider, LyraMathApiProvider, UtilityApiProvider}; pub trait RegisterLuaType { /// Register a lua type that **is not wrapped**. - fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self); + fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>( + &mut self, + ); /// Registers a wrapped lua type. /// You provide the wrapper as `W`, and the type that the wrapper wraps, as `T`. - fn register_lua_wrapper<'a, W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self); + fn register_lua_wrapper< + 'a, + W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData, + >( + &mut self, + ); } impl RegisterLuaType for World { - fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self) { + fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>( + &mut self, + ) { let mut registry = self.get_resource_mut::(); - + let type_id = TypeId::of::(); let reg_type = registry.get_type_or_default(type_id); @@ -54,9 +70,14 @@ impl RegisterLuaType for World { //reg_type.add_data(>::from_type()); } - fn register_lua_wrapper<'a, W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData>(&mut self) { + fn register_lua_wrapper< + 'a, + W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData, + >( + &mut self, + ) { let mut registry = self.get_resource_mut::(); - + let reg_type = registry.get_type_or_default(W::wrapped_type_id()); reg_type.add_data(>::from_type()); } @@ -84,17 +105,31 @@ pub trait LuaWrapper { } pub trait LuaProxy { - fn as_lua_value<'lua>(lua: &'lua mlua::Lua, this: &dyn Reflect) -> mlua::Result>; - fn apply(lua: &mlua::Lua, this: &mut dyn Reflect, apply: &mlua::AnyUserData) -> mlua::Result<()>; + fn as_lua_value<'lua>( + lua: &'lua mlua::Lua, + this: &dyn Reflect, + ) -> mlua::Result>; + fn apply( + lua: &mlua::Lua, + this: &mut dyn Reflect, + apply: &mlua::AnyUserData, + ) -> mlua::Result<()>; } impl<'a, T: Reflect + Clone + mlua::FromLua<'a> + mlua::UserData> LuaProxy for T { - fn as_lua_value<'lua>(lua: &'lua mlua::Lua, this: &dyn Reflect) -> mlua::Result> { + fn as_lua_value<'lua>( + lua: &'lua mlua::Lua, + this: &dyn Reflect, + ) -> mlua::Result> { let this = this.as_any().downcast_ref::().unwrap(); lua.create_userdata(this.clone()) } - fn apply(_lua: &mlua::Lua, this: &mut dyn Reflect, apply: &mlua::AnyUserData) -> mlua::Result<()> { + fn apply( + _lua: &mlua::Lua, + this: &mut dyn Reflect, + apply: &mlua::AnyUserData, + ) -> mlua::Result<()> { let this = this.as_any_mut().downcast_mut::().unwrap(); let apply = apply.borrow::()?; @@ -106,11 +141,18 @@ impl<'a, T: Reflect + Clone + mlua::FromLua<'a> + mlua::UserData> LuaProxy for T #[derive(Clone)] pub struct ReflectLuaProxy { - fn_as_uservalue: for<'a> fn(lua: &'a Lua, this_ptr: NonNull) -> mlua::Result>, - fn_apply: for<'a> fn(lua: &'a Lua, this_ptr: NonNull, apply: &'a mlua::AnyUserData<'a>) -> mlua::Result<()>, + fn_as_uservalue: + for<'a> fn(lua: &'a Lua, this_ptr: NonNull) -> mlua::Result>, + fn_apply: for<'a> fn( + lua: &'a Lua, + this_ptr: NonNull, + apply: &'a mlua::AnyUserData<'a>, + ) -> mlua::Result<()>, } -impl<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData> FromType for ReflectLuaProxy { +impl<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData> FromType + for ReflectLuaProxy +{ fn from_type() -> Self { Self { fn_as_uservalue: |lua, this| -> mlua::Result { @@ -120,7 +162,7 @@ impl<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData> Fro fn_apply: |lua, ptr, apply| { let this = unsafe { ptr.cast::().as_mut() }; ::apply(lua, this, apply) - } + }, } } } @@ -129,7 +171,11 @@ impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle { fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { match value { mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), - mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { from: "Nil", to: "DynamicBundle", message: Some("Value was nil".to_string()) }), + mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { + from: "Nil", + to: "DynamicBundle", + message: Some("Value was nil".to_string()), + }), _ => panic!(), } } @@ -137,9 +183,7 @@ impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle { impl mlua::UserData for ScriptDynamicBundle { fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_function("new", |_, ()| { - Ok(ScriptDynamicBundle(DynamicBundle::new())) - }); + methods.add_function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new()))); methods.add_method_mut("push", |_, this, (comp,): (mlua::AnyUserData,)| { let script_brw = comp.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?; @@ -154,44 +198,84 @@ impl mlua::UserData for ScriptDynamicBundle { } } -/// -/* fn lua_script_run_func(world: &mut World, function_name: &str) -> anyhow::Result<()> { - -} */ - /// A system that creates the script contexts in the world as new scripts are found -pub fn lua_scripts_create_contexts(mut host: ResMut, - mut contexts: ResMut>, - mut providers: ResMut>, - view: View<(Entities, &ScriptList)>, - ) -> anyhow::Result<()> { - +pub fn lua_scripts_create_contexts( + mut host: ResMut, + mut contexts: ResMut>, + mut providers: ResMut>, + view: View<(Entities, &ScriptList)>, +) -> anyhow::Result<()> { for (en, scripts) in view.into_iter() { for script in scripts.iter() { - let script_data = ScriptData { - name: script.name().to_string(), - script_id: script.id(), - entity: en, - }; - if !contexts.has_context(script.id()) { + let script_data = ScriptData { + name: script.name().to_string(), + script_id: script.id(), + entity: en, + }; + if let Some(script_res) = &script.res_handle().try_data_ref() { debug!("Loading script '{}'...", script.name()); - let mut script_ctx = host.load_script(&script_res.bytes, &script_data, &mut providers).unwrap(); - debug!("Finished loading script '{}'", script.name()); + let mut script_ctx = + host.load_script(&script_res.bytes, &script_data, &mut providers)?; + trace!("Finished loading script '{}'", script.name()); debug!("Setting up script '{}'...", script.name()); - host.setup_script(&script_data, &mut script_ctx, &mut providers).unwrap(); - debug!("Finished setting up script '{}'...", script.name()); + host.setup_script(&script_data, &mut script_ctx, &mut providers)?; + trace!("Finished setting up script '{}'...", script.name()); contexts.add_context(script.id(), script_ctx); } else { - debug!("Script '{}' is not yet loaded, skipping", script.name()); + trace!("Script '{}' is not loaded yet, skipping for now", script.name()); } } } } - + + Ok(()) +} + +/// A system that triggers a reload of watched script resources. +/// +/// 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, + 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 watch_recv = resman.watcher_event_recv(&handle_path).unwrap(); + + match watch_recv.try_recv() { + Ok(ev) => { + let evs = + ev.map_err(|e| anyhow!("Script watcher ran into errors: {:?}", e))?; + + if evs.iter().any(|ev| ev.event.kind.is_modify()) { + debug!( + "Detected change of '{}' script, triggering reload", + handle_path + ); + + contexts.remove_context(script.id()).unwrap(); + resman.reload(handle)?; + } + } + Err(e) => match e { + lyra_resource::channel::TryRecvError::Empty => {} + lyra_resource::channel::TryRecvError::Disconnected => { + resman.stop_watching(&handle_path).unwrap(); + } + }, + } + } + } + } + Ok(()) } @@ -210,20 +294,31 @@ fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Resu }; if let Some(ctx) = contexts.get_context_mut(script.id()) { - trace!("Running '{}' function in script '{}'", stage_name, script.name()); - match host.call_script(world_ptr.clone(), &script_data, ctx, &mut providers, stage_name) { - Ok(()) => {}, + trace!( + "Running '{}' function in script '{}'", + stage_name, + script.name() + ); + + match host.call_script( + world_ptr.clone(), + &script_data, + ctx, + &mut providers, + stage_name, + ) { + Ok(()) => {} Err(e) => match e { ScriptError::MluaError(m) => { error!("Script '{}' ran into an error: {}", script.name(), m); - }, + } ScriptError::Other(_) => return Err(e.into()), }, } } } } - + Ok(()) } @@ -270,7 +365,8 @@ impl Plugin for LuaScriptingPlugin { world.add_resource_default::>(); world.add_resource_default::>(); - let mut loader = world.try_get_resource_mut::() + let mut loader = world + .try_get_resource_mut::() .expect("Add 'ResourceManager' to the world before trying to add this plugin"); loader.register_loader::(); drop(loader); @@ -279,15 +375,50 @@ impl Plugin for LuaScriptingPlugin { game.add_script_api_provider::(LyraEcsApiProvider); game.add_script_api_provider::(LyraMathApiProvider); - game - .add_system_to_stage(GameStages::First, "lua_create_contexts", lua_scripts_create_contexts, &[]) - .add_system_to_stage(GameStages::First, "lua_first_stage", lua_script_first_stage_system, &["lua_create_contexts"]) - // cannot depend on 'lua_create_contexts' since it will cause a panic. - // the staged executor separates the executor of a single stage so this system - // cannot depend on the other one. - .add_system_to_stage(GameStages::PreUpdate, "lua_pre_update", lua_script_pre_update_stage_system, &[]) - .add_system_to_stage(GameStages::Update, "lua_update", lua_script_update_stage_system, &[]) - .add_system_to_stage(GameStages::PostUpdate, "lua_post_update", lua_script_post_update_stage_system, &[]) - .add_system_to_stage(GameStages::Last, "lua_last_stage", lua_script_last_stage_system, &[]); + game.add_system_to_stage( + GameStages::First, + "lua_create_contexts", + lua_scripts_create_contexts, + &[], + ) + .add_system_to_stage( + GameStages::First, + "lua_reload_scripts", + lua_scripts_reload_system, + &["lua_create_contexts"], + ) + .add_system_to_stage( + GameStages::First, + "lua_first_stage", + lua_script_first_stage_system, + &["lua_reload_scripts"], + ) + // cannot depend on 'lua_create_contexts' since it will cause a panic. + // the staged executor separates the executor of a single stage so this system + // cannot depend on the other one. + .add_system_to_stage( + GameStages::PreUpdate, + "lua_pre_update", + lua_script_pre_update_stage_system, + &[], + ) + .add_system_to_stage( + GameStages::Update, + "lua_update", + lua_script_update_stage_system, + &[], + ) + .add_system_to_stage( + GameStages::PostUpdate, + "lua_post_update", + lua_script_post_update_stage_system, + &[], + ) + .add_system_to_stage( + GameStages::Last, + "lua_last_stage", + lua_script_last_stage_system, + &[], + ); } -} \ No newline at end of file +} -- 2.40.1 From 94abf2ddf011ea5d6d34c53afc72c493427be67c Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Tue, 16 Jan 2024 23:04:17 -0500 Subject: [PATCH 13/41] scripting: override lua's print method --- lyra-scripting/src/host.rs | 2 +- lyra-scripting/src/lua/mod.rs | 15 ++++++++------ lyra-scripting/src/lua/providers/ecs.rs | 4 ++-- lyra-scripting/src/lua/providers/math.rs | 3 ++- lyra-scripting/src/lua/providers/util.rs | 25 +++++++++++++++++------- lyra-scripting/src/lua/script.rs | 2 +- lyra-scripting/src/lua/test.rs | 2 +- 7 files changed, 34 insertions(+), 19 deletions(-) diff --git a/lyra-scripting/src/host.rs b/lyra-scripting/src/host.rs index e25a56d..288a5f2 100644 --- a/lyra-scripting/src/host.rs +++ b/lyra-scripting/src/host.rs @@ -48,7 +48,7 @@ pub trait ScriptApiProvider { } /// Exposes an API in the provided script context. - fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>; + fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>; /// Setup a script right before its 'init' method is called. fn setup_script(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>; diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index 7c9f6af..5c92cb8 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -5,7 +5,7 @@ pub use dynamic_iter::*; pub mod world; use lyra_game::{game::GameStages, plugin::Plugin}; use lyra_resource::ResourceManager; -use tracing::{debug, error, trace}; +use tracing::{debug, error, trace, debug_span}; pub use world::*; pub mod script; @@ -214,19 +214,22 @@ pub fn lua_scripts_create_contexts( entity: en, }; + 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() { - debug!("Loading script '{}'...", script.name()); + debug!("Loading script..."); let mut script_ctx = host.load_script(&script_res.bytes, &script_data, &mut providers)?; - trace!("Finished loading script '{}'", script.name()); + trace!("Finished loading script"); - debug!("Setting up script '{}'...", script.name()); + debug!("Setting up script..."); host.setup_script(&script_data, &mut script_ctx, &mut providers)?; - trace!("Finished setting up script '{}'...", script.name()); + trace!("Finished setting up script"); contexts.add_context(script.id(), script_ctx); } else { - trace!("Script '{}' is not loaded yet, skipping for now", script.name()); + trace!("Script is not loaded yet, skipping for now"); } } } diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index def90e8..0e8ad5b 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -1,4 +1,4 @@ -use crate::{lua::LuaContext, ScriptApiProvider, ScriptWorldPtr, ScriptDynamicBundle}; +use crate::{lua::LuaContext, ScriptApiProvider, ScriptWorldPtr, ScriptDynamicBundle, ScriptData}; #[derive(Default)] pub struct LyraEcsApiProvider; @@ -6,7 +6,7 @@ pub struct LyraEcsApiProvider; impl ScriptApiProvider for LyraEcsApiProvider { type ScriptContext = LuaContext; - fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); let globals = ctx.globals(); diff --git a/lyra-scripting/src/lua/providers/math.rs b/lyra-scripting/src/lua/providers/math.rs index d7d37cd..f95d745 100644 --- a/lyra-scripting/src/lua/providers/math.rs +++ b/lyra-scripting/src/lua/providers/math.rs @@ -1,5 +1,6 @@ use lyra_ecs::World; use lyra_game::math; +use crate::ScriptData; use crate::lua::RegisterLuaType; use crate::lua::wrappers::{LuaVec3, LuaTransform}; @@ -16,7 +17,7 @@ impl ScriptApiProvider for LyraMathApiProvider { world.register_lua_wrapper::(); } - fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); /* let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua"); diff --git a/lyra-scripting/src/lua/providers/util.rs b/lyra-scripting/src/lua/providers/util.rs index 1cc50c5..e1277ee 100644 --- a/lyra-scripting/src/lua/providers/util.rs +++ b/lyra-scripting/src/lua/providers/util.rs @@ -1,5 +1,7 @@ use std::sync::{Mutex, Arc}; +use tracing::{debug_span, debug, span, Level}; + use crate::{ScriptApiProvider, ScriptData}; /// This Api provider provides some nice utility functions. @@ -17,7 +19,7 @@ pub struct UtilityApiProvider; impl ScriptApiProvider for UtilityApiProvider { type ScriptContext = Mutex; - fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); fn printf(lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic)) -> mlua::Result<()> { @@ -94,18 +96,27 @@ impl ScriptApiProvider for UtilityApiProvider { formatted = format!("{}{}", formatted, text); lua.globals() - .get::<_, mlua::Function>("print") - .unwrap() - .call::<_, ()>(formatted) - .unwrap(); + .get::<_, mlua::Function>("print")? + .call::<_, ()>(formatted)?; Ok(()) } - let printf_func = ctx.create_function(printf).unwrap(); + let script_name_reg = ctx.create_registry_value(data.name.clone())?; + + let printf_func = ctx.create_function(printf)?; + let print_func = ctx.create_function(move |lua, text: String| { + let name = lua.registry_value::(&script_name_reg)?; + let _span = debug_span!("lua", script = &name).entered(); + + debug!(target: "lyra_scripting::lua", "{}", text); + + Ok(()) + })?; let globals = ctx.globals(); - globals.set("printf", printf_func).unwrap(); + globals.set("printf", printf_func)?; + globals.set("print", print_func)?; Ok(()) } diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index 612772e..c7a00ab 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -35,7 +35,7 @@ impl ScriptHost for LuaHost { let mut ctx = Mutex::new(mlua::Lua::new()); for provider in providers.apis.iter_mut() { - provider.expose_api(&mut ctx)?; + provider.expose_api(script_data, &mut ctx)?; } let lua = ctx.lock().unwrap(); diff --git a/lyra-scripting/src/lua/test.rs b/lyra-scripting/src/lua/test.rs index 79f72d1..b30880d 100644 --- a/lyra-scripting/src/lua/test.rs +++ b/lyra-scripting/src/lua/test.rs @@ -21,7 +21,7 @@ struct PrintfProvider; impl ScriptApiProvider for PrintfProvider { type ScriptContext = Mutex; - fn expose_api(&mut self, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn expose_api(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); fn printf(lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic)) -> mlua::Result<()> { -- 2.40.1 From e88758f59d27b304f17a863ab1c99d2ad1efb892 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 20 Jan 2024 00:54:36 -0500 Subject: [PATCH 14/41] scripting: expose resources to the world --- examples/testbed/scripts/test.lua | 5 +- lyra-ecs/src/resource.rs | 2 +- lyra-ecs/src/world.rs | 8 +- lyra-game/src/delta_time.rs | 5 +- lyra-game/src/lib.rs | 1 + lyra-reflect/src/component.rs | 70 ++ lyra-reflect/src/lib.rs | 101 ++- lyra-reflect/src/resource.rs | 50 ++ lyra-scripting/src/lib.rs | 50 +- lyra-scripting/src/lua/mod.rs | 123 +--- lyra-scripting/src/lua/providers/ecs.rs | 8 +- lyra-scripting/src/lua/proxy.rs | 92 +++ lyra-scripting/src/lua/world.rs | 25 +- lyra-scripting/src/lua/wrappers/delta_time.rs | 52 ++ lyra-scripting/src/lua/wrappers/math.rs | 605 ++++++++++++++++++ lyra-scripting/src/lua/wrappers/mod.rs | 569 +--------------- 16 files changed, 1030 insertions(+), 736 deletions(-) create mode 100644 lyra-reflect/src/component.rs create mode 100644 lyra-reflect/src/resource.rs create mode 100644 lyra-scripting/src/lua/proxy.rs create mode 100644 lyra-scripting/src/lua/wrappers/delta_time.rs create mode 100644 lyra-scripting/src/lua/wrappers/math.rs diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index ea5bdc6..ffc46c5 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -15,9 +15,12 @@ end ]] function on_update() --print("Lua's update function was called") + local dt = world:resource(DeltaTime) + --print("DeltaTime was " .. tostring(dt) .. "s") + world:view(function (t) --print("Found entity at a really cool place: " .. tostring(t)) - t.translation = t.translation + Vec3.new(0, 0.0008, 0) + t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) return t end, Transform) diff --git a/lyra-ecs/src/resource.rs b/lyra-ecs/src/resource.rs index 019067b..ae48fc6 100644 --- a/lyra-ecs/src/resource.rs +++ b/lyra-ecs/src/resource.rs @@ -6,7 +6,7 @@ impl ResourceObject for T {} /// A type erased storage for a Resource. pub struct ResourceData { - data: Box>, + pub(crate) data: Box>, type_id: TypeId, } diff --git a/lyra-ecs/src/world.rs b/lyra-ecs/src/world.rs index f6a3274..cbc1695 100644 --- a/lyra-ecs/src/world.rs +++ b/lyra-ecs/src/world.rs @@ -1,6 +1,6 @@ use std::{collections::{HashMap, VecDeque}, any::TypeId, cell::{Ref, RefMut}, ptr::NonNull}; -use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, query::{Query, ViewIter, View, AsQuery}, resource::ResourceData, query::{dynamic::DynamicView, ViewOne}, ComponentInfo, DynTypeId, TickTracker, Tick}; +use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, query::{Query, ViewIter, View, AsQuery}, resource::ResourceData, query::{dynamic::DynamicView, ViewOne}, ComponentInfo, DynTypeId, TickTracker, Tick, ResourceObject}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct EntityId(pub u64); @@ -306,6 +306,12 @@ impl World { pub fn tick_tracker(&self) -> &TickTracker { &self.tracker } + + /// Attempts to find a resource in the world and returns a NonNull pointer to it + pub unsafe fn try_get_resource_ptr(&self) -> Option> { + self.resources.get(&TypeId::of::()) + .map(|d| unsafe { NonNull::new_unchecked(d.data.as_ptr() as *mut T) }) + } } #[cfg(test)] diff --git a/lyra-game/src/delta_time.rs b/lyra-game/src/delta_time.rs index 9dbbff7..f64735a 100644 --- a/lyra-game/src/delta_time.rs +++ b/lyra-game/src/delta_time.rs @@ -1,10 +1,11 @@ use instant::Instant; use lyra_ecs::{Component, world::World}; +use lyra_reflect::Reflect; use crate::{plugin::Plugin, game::GameStages}; -#[derive(Clone, Component)] -pub struct DeltaTime(f32, Option); +#[derive(Clone, Component, Default, Reflect)] +pub struct DeltaTime(f32, #[reflect(skip)] Option); impl std::ops::Deref for DeltaTime { type Target = f32; diff --git a/lyra-game/src/lib.rs b/lyra-game/src/lib.rs index 3c70bc4..85b2922 100644 --- a/lyra-game/src/lib.rs +++ b/lyra-game/src/lib.rs @@ -26,6 +26,7 @@ pub mod scene; pub use lyra_resource as assets; pub use lyra_ecs as ecs; pub use lyra_math as math; +pub use lyra_reflect as reflect; #[cfg(feature = "scripting")] pub use lyra_scripting as script; diff --git a/lyra-reflect/src/component.rs b/lyra-reflect/src/component.rs new file mode 100644 index 0000000..ca160a5 --- /dev/null +++ b/lyra-reflect/src/component.rs @@ -0,0 +1,70 @@ +use std::{any::TypeId, cell::{Ref, RefMut}}; + +use lyra_ecs::{Component, ComponentInfo, World, Entity, DynamicBundle}; + +use crate::{Reflect, FromType}; + +#[derive(Clone)] +pub struct ReflectedComponent { + pub type_id: TypeId, + pub info: ComponentInfo, + //value: Value, + //from_world: + + //from_world: for<'a> fn (world: &'a mut World) -> Box, + /// Inserts component into entity in the world + fn_insert: for<'a> fn (world: &'a mut World, entity: Entity, component: &dyn Reflect), + /// Inserts component into a bundle + fn_bundle_insert: for<'a> fn (dynamic_bundle: &'a mut DynamicBundle, component: &dyn Reflect), + fn_reflect: for<'a> fn (world: &'a World, entity: Entity) -> Option>, + fn_reflect_mut: for<'a> fn (world: &'a mut World, entity: Entity) -> Option>, +} + +impl ReflectedComponent { + /// Insert the reflected component into an entity. + pub fn insert(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { + (self.fn_insert)(world, entity, component); + } + + /// Insert this component into a DynamicBundle + pub fn bundle_insert(&self, dynamic_bundle: &mut DynamicBundle, component: &dyn Reflect) { + (self.fn_bundle_insert)(dynamic_bundle, component) + } + + /// Retrieves a reflected component from an entity. + pub fn reflect<'a>(&'a self, world: &'a World, entity: Entity) -> Option> { + (self.fn_reflect)(world, entity) + } + + /// Retrieves a reflected component from an entity. + pub fn reflect_mut<'a>(&'a mut self, world: &'a mut World, entity: Entity) -> Option> { + (self.fn_reflect_mut)(world, entity) + } +} + +impl FromType for ReflectedComponent { + fn from_type() -> Self { + ReflectedComponent { + type_id: TypeId::of::(), + info: ComponentInfo::new::(), + fn_insert: |world: &mut World, entity: Entity, component: &dyn Reflect| { + let mut c = C::default(); + c.apply(component); + world.insert(entity, (c,)); + }, + fn_bundle_insert: |bundle: &mut DynamicBundle, component: &dyn Reflect| { + let mut c = C::default(); + c.apply(component); + bundle.push(c); + }, + fn_reflect: |world: &World, entity: Entity| { + world.view_one::<&C>(entity) + .get().map(|c| c as Ref) + }, + fn_reflect_mut: |world: &mut World, entity: Entity| { + world.view_one::<&mut C>(entity) + .get().map(|c| c as RefMut) + }, + } + } +} \ No newline at end of file diff --git a/lyra-reflect/src/lib.rs b/lyra-reflect/src/lib.rs index 2b6bd24..0b8437f 100644 --- a/lyra-reflect/src/lib.rs +++ b/lyra-reflect/src/lib.rs @@ -36,6 +36,12 @@ pub use dynamic_tuple::*; pub mod reflected_field; pub use reflected_field::*; +pub mod component; +pub use component::*; + +pub mod resource; +pub use resource::*; + pub mod util; pub mod field; pub use field::*; @@ -237,67 +243,48 @@ pub trait FromType { fn from_type() -> Self; } -#[derive(Clone)] -pub struct ReflectedComponent { - pub type_id: TypeId, - pub info: ComponentInfo, - //value: Value, - //from_world: - - //from_world: for<'a> fn (world: &'a mut World) -> Box, - /// Inserts component into entity in the world - fn_insert: for<'a> fn (world: &'a mut World, entity: Entity, component: &dyn Reflect), - /// Inserts component into a bundle - fn_bundle_insert: for<'a> fn (dynamic_bundle: &'a mut DynamicBundle, component: &dyn Reflect), - fn_reflect: for<'a> fn (world: &'a World, entity: Entity) -> Option>, - fn_reflect_mut: for<'a> fn (world: &'a mut World, entity: Entity) -> Option>, +pub trait ReflectWorldExt { + /// Retrieves the type registry from the world. + fn get_type_registry(&self) -> Ref; + /// Retrieves the type registry mutably from the world. + fn get_type_registry_mut(&self) -> RefMut; + /// Get a registered type from the type registry. Returns `None` if the type is not registered + fn get_type(&self, type_id: TypeId) -> Option>; + /// Get a mutable registered type from the type registry in the world. returns `None` if the type is not registered. + fn get_type_mut(&self, type_id: TypeId) -> Option>; + /// Get a registered type, or register a new type and return it. + fn get_type_or_default(&self, type_id: TypeId) -> RefMut; } -impl ReflectedComponent { - /// Insert the reflected component into an entity. - pub fn insert(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { - (self.fn_insert)(world, entity, component); +impl ReflectWorldExt for World { + fn get_type_registry(&self) -> Ref { + self.get_resource::() } - - /// Insert this component into a DynamicBundle - pub fn bundle_insert(&self, dynamic_bundle: &mut DynamicBundle, component: &dyn Reflect) { - (self.fn_bundle_insert)(dynamic_bundle, component) + + fn get_type_registry_mut(&self) -> RefMut { + self.get_resource_mut::() } - - /// Retrieves a reflected component from an entity. - pub fn reflect<'a>(&'a self, world: &'a World, entity: Entity) -> Option> { - (self.fn_reflect)(world, entity) - } - - /// Retrieves a reflected component from an entity. - pub fn reflect_mut<'a>(&'a mut self, world: &'a mut World, entity: Entity) -> Option> { - (self.fn_reflect_mut)(world, entity) - } -} - -impl FromType for ReflectedComponent { - fn from_type() -> Self { - ReflectedComponent { - type_id: TypeId::of::(), - info: ComponentInfo::new::(), - fn_insert: |world: &mut World, entity: Entity, component: &dyn Reflect| { - let mut c = C::default(); - c.apply(component); - world.insert(entity, (c,)); - }, - fn_bundle_insert: |bundle: &mut DynamicBundle, component: &dyn Reflect| { - let mut c = C::default(); - c.apply(component); - bundle.push(c); - }, - fn_reflect: |world: &World, entity: Entity| { - world.view_one::<&C>(entity) - .get().map(|c| c as Ref) - }, - fn_reflect_mut: |world: &mut World, entity: Entity| { - world.view_one::<&mut C>(entity) - .get().map(|c| c as RefMut) - }, + + fn get_type(&self, type_id: TypeId) -> Option> { + let r = self.get_resource::(); + if r.has_type(type_id) { + Some(Ref::map(r, |tr| tr.get_type(type_id).unwrap())) + } else { + None } } + + fn get_type_mut(&self, type_id: TypeId) -> Option> { + let r = self.get_resource_mut::(); + if r.has_type(type_id) { + Some(RefMut::map(r, |tr| tr.get_type_mut(type_id).unwrap())) + } else { + None + } + } + + fn get_type_or_default(&self, type_id: TypeId) -> RefMut { + let r = self.get_resource_mut::(); + RefMut::map(r, |tr| tr.get_type_or_default(type_id)) + } } \ No newline at end of file diff --git a/lyra-reflect/src/resource.rs b/lyra-reflect/src/resource.rs new file mode 100644 index 0000000..aa95293 --- /dev/null +++ b/lyra-reflect/src/resource.rs @@ -0,0 +1,50 @@ +use std::{any::TypeId, cell::{Ref, RefMut}, ptr::NonNull}; + +use lyra_ecs::{World, ResourceObject}; + +use crate::{Reflect, FromType}; + +#[derive(Clone)] +pub struct ReflectedResource { + pub type_id: TypeId, + + fn_reflect: for<'a> fn (world: &'a World) -> Option>, + fn_reflect_mut: for<'a> fn (world: &'a mut World) -> Option>, + fn_reflect_ptr: fn (world: &mut World) -> Option>, +} + +impl ReflectedResource { + /// Retrieves the reflected resource from the world. + pub fn reflect<'a>(&self, world: &'a World) -> Option> { + (self.fn_reflect)(world) + } + + /// Retrieves a mutable reflected resource from the world. + pub fn reflect_mut<'a>(&self, world: &'a mut World) -> Option> { + (self.fn_reflect_mut)(world) + } + + pub fn reflect_ptr(&self, world: &mut World) -> Option> { + (self.fn_reflect_ptr)(world) + } +} + +impl FromType for ReflectedResource { + fn from_type() -> Self { + Self { + type_id: TypeId::of::(), + fn_reflect: |world: &World| { + world.try_get_resource::() + .map(|r| r as Ref) + }, + fn_reflect_mut: |world: &mut World| { + world.try_get_resource_mut::() + .map(|r| r as RefMut) + }, + fn_reflect_ptr: |world: &mut World| unsafe { + world.try_get_resource_ptr::() + .map(|ptr| ptr.cast::()) + }, + } + } +} \ No newline at end of file diff --git a/lyra-scripting/src/lib.rs b/lyra-scripting/src/lib.rs index 197e95e..640d227 100644 --- a/lyra-scripting/src/lib.rs +++ b/lyra-scripting/src/lib.rs @@ -2,7 +2,9 @@ pub mod lua; pub mod world; -use lyra_ecs::Component; +use std::any::TypeId; + +use lyra_ecs::{Component, ResourceObject}; pub use world::*; pub mod wrap; @@ -16,17 +18,19 @@ pub use script::*; use lyra_game::game::Game; +// required for some proc macros :( #[allow(unused_imports)] pub(crate) mod lyra_engine { pub use lyra_ecs as ecs; pub use lyra_reflect as reflect; } -use lyra_reflect::{ReflectedComponent, Reflect, FromType}; +use lyra_reflect::{ReflectedComponent, Reflect, FromType, ReflectedResource}; #[derive(Clone)] pub enum ReflectBranch { Component(ReflectedComponent), + Resource(ReflectedResource), } impl ReflectBranch { @@ -37,13 +41,38 @@ impl ReflectBranch { pub fn as_component_unchecked(&self) -> &ReflectedComponent { match self { ReflectBranch::Component(c) => c, - //_ => panic!("`self` is not an instance of `ReflectBranch::Component`") + _ => panic!("`self` is not an instance of `ReflectBranch::Component`") } } + /// Returns a boolean indicating if `self` is a reflection of a Component. pub fn is_component(&self) -> bool { matches!(self, ReflectBranch::Component(_)) } + + /// Gets self as a [`ReflectedResource`]. + /// + /// # Panics + /// If `self` is not a variant of [`ReflectBranch::Resource`]. + pub fn as_resource_unchecked(&self) -> &ReflectedResource { + match self { + ReflectBranch::Resource(v) => v, + _ => panic!("`self` is not an instance of `ReflectBranch::Component`") + } + } + + /// Returns a boolean indicating if `self` is a reflection of a Resource. + pub fn is_resource(&self) -> bool { + matches!(self, ReflectBranch::Resource(_)) + } + + /// Returns the type id of the reflected thing + pub fn reflect_type_id(&self) -> TypeId { + match self { + ReflectBranch::Component(c) => c.type_id, + ReflectBranch::Resource(r) => r.type_id, + } + } } pub struct ScriptBorrow { @@ -63,6 +92,7 @@ impl Clone for ScriptBorrow { } impl ScriptBorrow { + /// Creates a ScriptBorrow from a Component pub fn from_component(data: Option) -> Self where T: Reflect + Component + Default + 'static @@ -74,10 +104,24 @@ impl ScriptBorrow { data, } } + + /// Creates a ScriptBorrow from a Resource. + pub fn from_resource(data: Option) -> Self + where + T: Reflect + ResourceObject + Default + 'static + { + let data = data.map(|d| Box::new(d) as Box<(dyn Reflect + 'static)>); + + Self { + reflect_branch: ReflectBranch::Resource(>::from_type()), + data, + } + } } /// An extension trait that adds some helpful methods that makes it easier to do scripting things pub trait GameScriptExt { + /// A helper method for adding a ScriptApiProvider into the world. fn add_script_api_provider(&mut self, provider: P) where T: ScriptHost, diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index 5c92cb8..add893c 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -5,7 +5,7 @@ pub use dynamic_iter::*; pub mod world; use lyra_game::{game::GameStages, plugin::Plugin}; use lyra_resource::ResourceManager; -use tracing::{debug, error, trace, debug_span}; +use tracing::{debug, debug_span, error, trace}; pub use world::*; pub mod script; @@ -17,6 +17,9 @@ pub use loader::*; pub mod providers; pub mod wrappers; +pub mod proxy; +pub use proxy::*; + #[cfg(test)] mod test; @@ -43,24 +46,22 @@ use crate::{ use self::providers::{LyraEcsApiProvider, LyraMathApiProvider, UtilityApiProvider}; pub trait RegisterLuaType { - /// Register a lua type that **is not wrapped**. - fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>( - &mut self, - ); + /// Register a type to lua that **is not wrapped**. + fn register_lua_type<'a, T>(&mut self) + where + T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData; + /// Registers a wrapped lua type. - /// You provide the wrapper as `W`, and the type that the wrapper wraps, as `T`. - fn register_lua_wrapper< - 'a, - W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData, - >( - &mut self, - ); + fn register_lua_wrapper<'a, W>(&mut self) + where + W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData; } impl RegisterLuaType for World { - fn register_lua_type<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData>( - &mut self, - ) { + fn register_lua_type<'a, T>(&mut self) + where + T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData + { let mut registry = self.get_resource_mut::(); let type_id = TypeId::of::(); @@ -70,12 +71,10 @@ impl RegisterLuaType for World { //reg_type.add_data(>::from_type()); } - fn register_lua_wrapper< - 'a, - W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData, - >( - &mut self, - ) { + fn register_lua_wrapper<'a, W>(&mut self) + where + W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData + { let mut registry = self.get_resource_mut::(); let reg_type = registry.get_type_or_default(W::wrapped_type_id()); @@ -99,88 +98,6 @@ pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow { .expect("Type does not implement '__internal_reflect' properly") } -pub trait LuaWrapper { - /// The type id of the wrapped type. - fn wrapped_type_id() -> TypeId; -} - -pub trait LuaProxy { - fn as_lua_value<'lua>( - lua: &'lua mlua::Lua, - this: &dyn Reflect, - ) -> mlua::Result>; - fn apply( - lua: &mlua::Lua, - this: &mut dyn Reflect, - apply: &mlua::AnyUserData, - ) -> mlua::Result<()>; -} - -impl<'a, T: Reflect + Clone + mlua::FromLua<'a> + mlua::UserData> LuaProxy for T { - fn as_lua_value<'lua>( - lua: &'lua mlua::Lua, - this: &dyn Reflect, - ) -> mlua::Result> { - let this = this.as_any().downcast_ref::().unwrap(); - lua.create_userdata(this.clone()) - } - - fn apply( - _lua: &mlua::Lua, - this: &mut dyn Reflect, - apply: &mlua::AnyUserData, - ) -> mlua::Result<()> { - let this = this.as_any_mut().downcast_mut::().unwrap(); - let apply = apply.borrow::()?; - - *this = apply.clone(); - - Ok(()) - } -} - -#[derive(Clone)] -pub struct ReflectLuaProxy { - fn_as_uservalue: - for<'a> fn(lua: &'a Lua, this_ptr: NonNull) -> mlua::Result>, - fn_apply: for<'a> fn( - lua: &'a Lua, - this_ptr: NonNull, - apply: &'a mlua::AnyUserData<'a>, - ) -> mlua::Result<()>, -} - -impl<'a, T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData> FromType - for ReflectLuaProxy -{ - fn from_type() -> Self { - Self { - fn_as_uservalue: |lua, this| -> mlua::Result { - let this = unsafe { this.cast::().as_ref() }; - ::as_lua_value(lua, this) - }, - fn_apply: |lua, ptr, apply| { - let this = unsafe { ptr.cast::().as_mut() }; - ::apply(lua, this, apply) - }, - } - } -} - -impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle { - fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { - match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), - mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { - from: "Nil", - to: "DynamicBundle", - message: Some("Value was nil".to_string()), - }), - _ => panic!(), - } - } -} - impl mlua::UserData for ScriptDynamicBundle { fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new()))); diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index 0e8ad5b..446688c 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -1,4 +1,4 @@ -use crate::{lua::LuaContext, ScriptApiProvider, ScriptWorldPtr, ScriptDynamicBundle, ScriptData}; +use crate::{lua::{LuaContext, wrappers::LuaDeltaTime, RegisterLuaType}, ScriptApiProvider, ScriptWorldPtr, ScriptDynamicBundle, ScriptData}; #[derive(Default)] pub struct LyraEcsApiProvider; @@ -6,6 +6,10 @@ pub struct LyraEcsApiProvider; impl ScriptApiProvider for LyraEcsApiProvider { type ScriptContext = LuaContext; + fn prepare_world(&mut self, world: &mut lyra_ecs::World) { + world.register_lua_wrapper::(); + } + fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); @@ -13,6 +17,8 @@ impl ScriptApiProvider for LyraEcsApiProvider { globals.set("World", ctx.create_proxy::()?)?; globals.set("DynamicBundle", ctx.create_proxy::()?)?; + globals.set("DeltaTime", ctx.create_proxy::()?)?; + Ok(()) } diff --git a/lyra-scripting/src/lua/proxy.rs b/lyra-scripting/src/lua/proxy.rs new file mode 100644 index 0000000..e3a79de --- /dev/null +++ b/lyra-scripting/src/lua/proxy.rs @@ -0,0 +1,92 @@ +use std::{any::TypeId, ptr::NonNull}; + +use lyra_reflect::{Reflect, FromType}; + +use crate::ScriptDynamicBundle; + +pub trait LuaWrapper { + /// The type id of the wrapped type. + fn wrapped_type_id() -> TypeId; +} + +pub trait LuaProxy { + fn as_lua_value<'lua>( + lua: &'lua mlua::Lua, + this: &dyn Reflect, + ) -> mlua::Result>; + + fn apply( + lua: &mlua::Lua, + this: &mut dyn Reflect, + apply: &mlua::AnyUserData, + ) -> mlua::Result<()>; +} + +impl<'a, T> LuaProxy for T +where + T: Reflect + Clone + mlua::FromLua<'a> + mlua::UserData +{ + fn as_lua_value<'lua>( + lua: &'lua mlua::Lua, + this: &dyn Reflect, + ) -> mlua::Result> { + let this = this.as_any().downcast_ref::().unwrap(); + lua.create_userdata(this.clone()) + } + + fn apply( + _lua: &mlua::Lua, + this: &mut dyn Reflect, + apply: &mlua::AnyUserData, + ) -> mlua::Result<()> { + let this = this.as_any_mut().downcast_mut::().unwrap(); + let apply = apply.borrow::()?; + + *this = apply.clone(); + + Ok(()) + } +} + +#[derive(Clone)] +pub struct ReflectLuaProxy { + pub fn_as_uservalue: + for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull) -> mlua::Result>, + pub fn_apply: for<'a> fn( + lua: &'a mlua::Lua, + this_ptr: NonNull, + apply: &'a mlua::AnyUserData<'a>, + ) -> mlua::Result<()>, +} + +impl<'a, T> FromType for ReflectLuaProxy +where + T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData +{ + fn from_type() -> Self { + Self { + fn_as_uservalue: |lua, this| -> mlua::Result { + let this = unsafe { this.cast::().as_ref() }; + ::as_lua_value(lua, this) + }, + fn_apply: |lua, ptr, apply| { + let this = unsafe { ptr.cast::().as_mut() }; + ::apply(lua, this, apply) + }, + } + } +} + +impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle { + fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { + match value { + mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), + mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { + from: "Nil", + to: "DynamicBundle", + message: Some("Value was nil".to_string()), + }), + _ => panic!(), + } + } +} \ No newline at end of file diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 6c1cfbb..95ec3e8 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,7 +1,7 @@ -use std::{sync::Arc, ptr::NonNull}; +use std::{sync::Arc, ptr::NonNull, any::Any, ops::Deref}; use lyra_ecs::query::dynamic::QueryDynamicType; -use lyra_reflect::TypeRegistry; +use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType}; use mlua::{AnyUserDataExt, IntoLua, IntoLuaMulti}; use crate::{ScriptWorldPtr, ScriptEntity, ScriptDynamicBundle, ScriptBorrow}; @@ -159,5 +159,26 @@ impl mlua::UserData for ScriptWorldPtr { Ok(()) }); + + methods.add_method_mut("resource", |lua, this, (ty,): (mlua::AnyUserData,)| { + let reflect = ty + .call_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()) { + //.expect("Failed to find resource pointer in world!"); + let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) + .unwrap(); + let proxy = reg_type.get_data::() + .expect("Type does not have ReflectLuaProxy as a TypeData"); + + (proxy.fn_as_uservalue)(lua, res_ptr) + .and_then(|ud| ud.into_lua(lua)) + } else { + // if the resource is not found in the world, return nil + Ok(mlua::Value::Nil) + } + }); } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/wrappers/delta_time.rs b/lyra-scripting/src/lua/wrappers/delta_time.rs new file mode 100644 index 0000000..a4fd4f6 --- /dev/null +++ b/lyra-scripting/src/lua/wrappers/delta_time.rs @@ -0,0 +1,52 @@ +use std::{ops::Deref, any::TypeId}; + +use lyra_game::DeltaTime; +use crate::{lyra_engine, lua::{FN_NAME_INTERNAL_REFLECT_TYPE, LuaWrapper}, ScriptBorrow}; + +#[derive(Clone, lyra_reflect::Reflect, Default)] +pub struct LuaDeltaTime(#[reflect(skip)] pub(crate) DeltaTime); + +impl std::ops::Deref for LuaDeltaTime { + type Target = DeltaTime; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for LuaDeltaTime { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'lua> mlua::FromLua<'lua> for LuaDeltaTime { + fn from_lua(value: mlua::prelude::LuaValue<'lua>, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult { + match value { + mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), + _ => unreachable!(), + } + } +} + +impl mlua::UserData for LuaDeltaTime { + fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method("get", |_, this, ()| { + Ok(*this.0.deref()) + }); + + methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { + Ok(format!("{}", this.0.deref())) + }); + + methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { + Ok(ScriptBorrow::from_resource::(None)) + }); + } +} + +impl LuaWrapper for LuaDeltaTime { + fn wrapped_type_id() -> std::any::TypeId { + TypeId::of::() + } +} \ No newline at end of file diff --git a/lyra-scripting/src/lua/wrappers/math.rs b/lyra-scripting/src/lua/wrappers/math.rs new file mode 100644 index 0000000..a439fa1 --- /dev/null +++ b/lyra-scripting/src/lua/wrappers/math.rs @@ -0,0 +1,605 @@ +use std::sync::Arc; + +use crate::lyra_engine; +use lyra_game::math; +use lyra_scripting_derive::wrap_math_vec_copy; + +use crate as lyra_scripting; + +// f32 types +wrap_math_vec_copy!( + math::Vec2, + derives(PartialEq), + fields(x, y), + metamethods( + Add(LuaVec2, f32), + Sub(LuaVec2, f32), + Div(LuaVec2, f32), + Mul(LuaVec2, f32), + Mod(LuaVec2, f32), + Eq, + Unm + ) +); +wrap_math_vec_copy!( + math::Vec3, + derives(PartialEq), + fields(x, y, z), + metamethods( + Add(LuaVec3), + Sub(LuaVec3, f32), + Div(LuaVec3, f32), + //Mul(LuaVec3, f32), + Mod(LuaVec3, f32), + Eq, Unm, ToString, + ), + custom_methods { + methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (v,): (mlua::Value,)| { + match v { + mlua::Value::Number(n) => { + Ok(Self(this.0 * (n as f32))) + }, + mlua::Value::UserData(ud) => { + if let Ok(other_this) = ud.borrow::() { + Ok(Self(this.0 * other_this.0)) + } else { + if let Ok(mt) = ud.get_metatable() { + if let Ok(name) = mt.get::("__name") { + return Err(mlua::Error::BadArgument { + to: Some("LuaVec3.__mul".to_string()), + pos: 2, + name: Some("rhs".to_string()), + cause: Arc::new(mlua::Error::RuntimeError( + format!("cannot multiply with unknown userdata named {}", name) + )) + }); + } + } + + Err(mlua::Error::BadArgument { + to: Some("LuaVec3.__mul".to_string()), + pos: 2, + name: Some("rhs".to_string()), + cause: Arc::new( + mlua::Error::runtime("cannot multiply with unknown userdata") + ) + }) + } + }, + _ => Err(mlua::Error::BadArgument { + to: Some("LuaVec3.__mul".to_string()), + pos: 2, + name: Some("rhs".to_string()), + cause: Arc::new( + mlua::Error::RuntimeError(format!("cannot multiply with {}", v.type_name())) + ) + }) + } + }); + } +); +/* wrap_math_vec_copy!( + math::Vec3A, + derives(PartialEq), + fields(x, y, z), + metamethods( + Add(LuaVec3A, f32), + Sub(LuaVec3A, f32), + Div(LuaVec3A, f32), + Mul(LuaVec3A, f32), + Mod(LuaVec3A, f32), + Eq, Unm + ) +); +wrap_math_vec_copy!( + math::Vec4, + derives(PartialEq), + fields(w, x, y, z), + metamethods( + Add(LuaVec4, f32), + Sub(LuaVec4, f32), + Div(LuaVec4, f32), + Mul(LuaVec4, f32), + Mod(LuaVec4, f32), + Eq, Unm + ) +); + +// f64 types +wrap_math_vec_copy!( + math::DVec2, + derives(PartialEq), + fields(x, y), + metamethods( + Add(LuaDVec2, f64), + Sub(LuaDVec2, f64), + Div(LuaDVec2, f64), + Mul(LuaDVec2, f64), + Mod(LuaDVec2, f64), + Eq, Unm + ) +); +wrap_math_vec_copy!( + math::DVec3, + derives(PartialEq), + fields(x, y, z), + metamethods( + Add(LuaDVec3, f64), + Sub(LuaDVec3, f64), + Div(LuaDVec3, f64), + Mul(LuaDVec3, f64), + Mod(LuaDVec3, f64), + Eq, Unm + ) +); +wrap_math_vec_copy!( + math::DVec4, + derives(PartialEq), + fields(w, x, y, z), + metamethods( + Add(LuaDVec4, f64), + Sub(LuaDVec4, f64), + Div(LuaDVec4, f64), + Mul(LuaDVec4, f64), + Mod(LuaDVec4, f64), + Eq, Unm + ) +); + +// i32 types +wrap_math_vec_copy!( + math::IVec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods( + Add(LuaIVec2, i32), + Sub(LuaIVec2, i32), + Div(LuaIVec2, i32), + Mul(LuaIVec2, i32), + Mod(LuaIVec2, i32), + Shl(LuaIVec2, LuaUVec2, i32), + Shr(LuaIVec2, LuaUVec2, i32), + BAnd(LuaIVec2, i32), + BOr(LuaIVec2, i32), + BXor(LuaIVec2, i32), + Eq, Unm, BNot + ) +); +wrap_math_vec_copy!( + math::IVec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods( + Add(LuaIVec3, i32), + Sub(LuaIVec3, i32), + Div(LuaIVec3, i32), + Mul(LuaIVec3, i32), + Mod(LuaIVec3, i32), + Shl(LuaIVec3, LuaUVec3, i32), + Shr(LuaIVec3, LuaUVec3, i32), + BAnd(LuaIVec3, i32), + BOr(LuaIVec3, i32), + BXor(LuaIVec3, i32), + Eq, Unm, BNot + ) +); +wrap_math_vec_copy!( + math::IVec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods( + Add(LuaIVec4, i32), + Sub(LuaIVec4, i32), + Div(LuaIVec4, i32), + Mul(LuaIVec4, i32), + Mod(LuaIVec4, i32), + Shl(LuaIVec4, LuaUVec4, i32), + Shr(LuaIVec4, LuaUVec4, i32), + BAnd(LuaIVec4, i32), + BOr(LuaIVec4, i32), + BXor(LuaIVec4, i32), + Eq, Unm, BNot + ) +); + +// u32 types +wrap_math_vec_copy!( + math::UVec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods( + Add(LuaUVec2, u32), + Sub(LuaUVec2, u32), + Div(LuaUVec2, u32), + Mul(LuaUVec2, u32), + Mod(LuaUVec2, u32), + Shl(LuaUVec2, LuaIVec2, i32), + Shr(LuaUVec2, LuaIVec2, i32), + BAnd(LuaUVec2, u32), + BOr(LuaUVec2, u32), + BXor(LuaUVec2, u32), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::UVec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods( + Add(LuaUVec3, u32), + Sub(LuaUVec3, u32), + Div(LuaUVec3, u32), + Mul(LuaUVec3, u32), + Mod(LuaUVec3, u32), + Shl(LuaUVec3, LuaIVec3, i32), + Shr(LuaUVec3, LuaIVec3, i32), + BAnd(LuaUVec3, u32), + BOr(LuaUVec3, u32), + BXor(LuaUVec3, u32), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::UVec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods( + Add(LuaUVec4, u32), + Sub(LuaUVec4, u32), + Div(LuaUVec4, u32), + Mul(LuaUVec4, u32), + Mod(LuaUVec4, u32), + Shl(LuaUVec4, LuaIVec4, i32), + Shr(LuaUVec4, LuaIVec4, i32), + BAnd(LuaUVec4, u32), + BOr(LuaUVec4, u32), + BXor(LuaUVec4, u32), + Eq, BNot + ) +); + +// i64 types +wrap_math_vec_copy!( + math::I64Vec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods( + Add(LuaI64Vec2, i64), + Sub(LuaI64Vec2, i64), + Div(LuaI64Vec2, i64), + Mul(LuaI64Vec2, i64), + Mod(LuaI64Vec2, i64), + Shl(i64), + Shr(i64), + BAnd(LuaI64Vec2, i64), + BOr(LuaI64Vec2, i64), + BXor(LuaI64Vec2, i64), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::I64Vec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods( + Add(LuaI64Vec3, i64), + Sub(LuaI64Vec3, i64), + Div(LuaI64Vec3, i64), + Mul(LuaI64Vec3, i64), + Mod(LuaI64Vec3, i64), + Shl(i64), + Shr(i64), + BAnd(LuaI64Vec3, i64), + BOr(LuaI64Vec3, i64), + BXor(LuaI64Vec3, i64), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::I64Vec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods( + Add(LuaI64Vec4, i64), + Sub(LuaI64Vec4, i64), + Div(LuaI64Vec4, i64), + Mul(LuaI64Vec4, i64), + Mod(LuaI64Vec4, i64), + Shl(i64), + Shr(i64), + BAnd(LuaI64Vec4, i64), + BOr(LuaI64Vec4, i64), + BXor(LuaI64Vec4, i64), + Eq, BNot + ) +); + +// u64 types +wrap_math_vec_copy!( + math::U64Vec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods( + Add(LuaU64Vec2, u64), + Sub(LuaU64Vec2, u64), + Div(LuaU64Vec2, u64), + Mul(LuaU64Vec2, u64), + Mod(LuaU64Vec2, u64), + Shl(i64), + Shr(i64), + BAnd(LuaU64Vec2, u64), + BOr(LuaU64Vec2, u64), + BXor(LuaU64Vec2, u64), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::U64Vec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods( + Add(LuaU64Vec3, u64), + Sub(LuaU64Vec3, u64), + Div(LuaU64Vec3, u64), + Mul(LuaU64Vec3, u64), + Mod(LuaU64Vec3, u64), + Shl(i64), + Shr(i64), + BAnd(LuaU64Vec3, u64), + BOr(LuaU64Vec3, u64), + BXor(LuaU64Vec3, u64), + Eq, BNot + ) +); +wrap_math_vec_copy!( + math::U64Vec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods( + Add(LuaU64Vec4, u64), + Sub(LuaU64Vec4, u64), + Div(LuaU64Vec4, u64), + Mul(LuaU64Vec4, u64), + Mod(LuaU64Vec4, u64), + Shl(i64), + Shr(i64), + BAnd(LuaU64Vec4, u64), + BOr(LuaU64Vec4, u64), + BXor(LuaU64Vec4, u64), + Eq, BNot + ) +); + +// bool types +wrap_math_vec_copy!( + math::BVec2, + derives(PartialEq, Eq, Hash), + fields(x, y), + metamethods(Eq, BAnd, BOr, BXor, BNot) +); +wrap_math_vec_copy!( + math::BVec3, + derives(PartialEq, Eq, Hash), + fields(x, y, z), + metamethods(Eq, BAnd, BOr, BXor, BNot) +); +wrap_math_vec_copy!( + math::BVec4, + derives(PartialEq, Eq, Hash), + fields(w, x, y, z), + metamethods(Eq, BAnd, BOr, BXor, BNot) +); + +// mat2 +wrap_math_vec_copy!( + math::Mat2, + derives(PartialEq), + no_new, + matrix { + col_type = LuaVec2 + }, + metamethods( + Eq, + Add, + Sub, + Mul(LuaMat2, f32), + Unm + ) +); */ + +/* wrap_math_vec_copy!( + math::Mat4, + derives(PartialEq), + no_new, + matrix { + col_type = LuaVec4 + }, + metamethods( + Eq, + Add, + Sub, + Mul(LuaMat4, f32), + Unm + ) +); */ + +wrap_math_vec_copy!( + math::Quat, + derives(PartialEq), + no_new, + metamethods( + Eq, + // __mul for LuaVec3 is manually implemented below since it doesn't return Self + Mul(LuaQuat, f32), + Add, + Sub, + Div(f32), + ), + custom_methods { + methods.add_function("new", |_, (x, y, z, w)| { + Ok(Self(math::Quat::from_xyzw(x, y, z, w))) + }); + + methods.add_function("from_rotation_x", |_, (rad,)| { + let q = math::Quat::from_rotation_x(rad); + Ok(Self(q)) + }); + + methods.add_function("from_rotation_y", |_, (rad,)| { + let q = math::Quat::from_rotation_y(rad); + Ok(Self(q)) + }); + + methods.add_function("from_rotation_z", |_, (rad,)| { + let q = math::Quat::from_rotation_z(rad); + Ok(Self(q)) + }); + + methods.add_method("dot", |_, this, (rhs,): (Self,)| { + Ok(this.dot(rhs.0)) + }); + + methods.add_method("length", |_, this, ()| { + Ok(this.length()) + }); + + methods.add_method("length_squared", |_, this, ()| { + Ok(this.length_squared()) + }); + + methods.add_method("normalize", |_, this, ()| { + Ok(Self(this.normalize())) + }); + + methods.add_method("mult_quat", |_, this, (rhs,): (Self,)| { + Ok(Self(this.0 * rhs.0)) + }); + + methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| { + Ok(LuaVec3(this.0 * rhs.0)) + }); + + // manually implemented here since it doesn't return `Self` + methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (rhs,): (LuaVec3,)| { + Ok(LuaVec3(this.0 * rhs.0)) + }); + + methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { + Ok(Self(this.lerp(*rhs, alpha))) + }); + } +); + +wrap_math_vec_copy!( + math::Transform, + //derives(PartialEq), + no_new, + metamethods(ToString, Eq), + custom_fields { + fields.add_field_method_get("translation", |_, this| { + Ok(LuaVec3(this.translation)) + }); + fields.add_field_method_set("translation", |_, this, v: LuaVec3| { + this.translation = *v; + Ok(()) + }); + + fields.add_field_method_get("rotation", |_, this| { + Ok(LuaQuat(this.rotation)) + }); + fields.add_field_method_set("rotation", |_, this, v: LuaQuat| { + this.rotation = *v; + Ok(()) + }); + + fields.add_field_method_get("scale", |_, this| { + Ok(LuaVec3(this.scale)) + }); + fields.add_field_method_set("scale", |_, this, v: LuaVec3| { + this.scale = *v; + Ok(()) + }); + }, + custom_methods { + methods.add_function("default", |_, ()| { + Ok(Self(math::Transform::default())) + }); + + methods.add_function("new", |_, (pos, rot, scale): (LuaVec3, LuaQuat, LuaVec3)| { + Ok(Self(math::Transform::new(*pos, *rot, *scale))) + }); + + methods.add_function("from_translation", |_, (pos,): (LuaVec3,)| { + Ok(Self(math::Transform::from_translation(*pos))) + }); + + methods.add_function("from_xyz", |_, (x, y, z)| { + Ok(Self(math::Transform::from_xyz(x, y, z))) + }); + + methods.add_method("forward", |_, this, ()| { + Ok(LuaVec3(this.forward())) + }); + + methods.add_method("left", |_, this, ()| { + Ok(LuaVec3(this.left())) + }); + + methods.add_method("up", |_, this, ()| { + Ok(LuaVec3(this.up())) + }); + + methods.add_method_mut("rotate", |_, this, (quat,): (LuaQuat,)| { + this.rotate(*quat); + Ok(()) + }); + + methods.add_method_mut("rotate_x", |_, this, (deg,): (f32,)| { + this.rotate_x(math::Angle::Degrees(deg)); + Ok(()) + }); + + methods.add_method_mut("rotate_y", |_, this, (deg,): (f32,)| { + this.rotate_y(math::Angle::Degrees(deg)); + Ok(()) + }); + + methods.add_method_mut("rotate_z", |_, this, (deg,): (f32,)| { + this.rotate_z(math::Angle::Degrees(deg)); + Ok(()) + }); + + methods.add_method_mut("rotate_x_rad", |_, this, (rad,): (f32,)| { + this.rotate_x(math::Angle::Radians(rad)); + Ok(()) + }); + + methods.add_method_mut("rotate_y_rad", |_, this, (rad,): (f32,)| { + this.rotate_y(math::Angle::Radians(rad)); + Ok(()) + }); + + methods.add_method_mut("rotate_z_rad", |_, this, (rad,): (f32,)| { + this.rotate_z(math::Angle::Radians(rad)); + Ok(()) + }); + + methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { + Ok(Self(this.lerp(*rhs, alpha))) + }); + + // rotate a transform + methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (quat,): (LuaQuat,)| { + let mut t = *this; + t.rotation *= *quat; + Ok(t) + }); + + // move a transform + methods.add_meta_method(mlua::MetaMethod::Add, |_, this, (pos,): (LuaVec3,)| { + let mut t = *this; + t.translation += *pos; + Ok(t) + }); + } +); diff --git a/lyra-scripting/src/lua/wrappers/mod.rs b/lyra-scripting/src/lua/wrappers/mod.rs index 22a2317..1fd0c6b 100644 --- a/lyra-scripting/src/lua/wrappers/mod.rs +++ b/lyra-scripting/src/lua/wrappers/mod.rs @@ -1,566 +1,5 @@ -use lyra_game::math; -use lyra_scripting_derive::wrap_math_vec_copy; -use crate::lyra_engine; +pub mod math; +pub use math::*; -use crate as lyra_scripting; - -// f32 types -wrap_math_vec_copy!( - math::Vec2, - derives(PartialEq), - fields(x, y), - metamethods( - Add(LuaVec2, f32), - Sub(LuaVec2, f32), - Div(LuaVec2, f32), - Mul(LuaVec2, f32), - Mod(LuaVec2, f32), - Eq, Unm - ) -); -wrap_math_vec_copy!( - math::Vec3, - derives(PartialEq), - fields(x, y, z), - metamethods( - Add(LuaVec3), - Sub(LuaVec3, f32), - Div(LuaVec3, f32), - Mul(LuaVec3, f32), - Mod(LuaVec3, f32), - Eq, Unm, ToString, - ), - custom_methods { - /* methods.add_meta_method(mlua::MetaMethod::Add, |_, this, (trans,): (LuaTransform,)| { - println!("Adding"); - let mut t = *trans; - t.translation += **this; - Ok(LuaTransform(t)) - }); */ - } -); -/* wrap_math_vec_copy!( - math::Vec3A, - derives(PartialEq), - fields(x, y, z), - metamethods( - Add(LuaVec3A, f32), - Sub(LuaVec3A, f32), - Div(LuaVec3A, f32), - Mul(LuaVec3A, f32), - Mod(LuaVec3A, f32), - Eq, Unm - ) -); -wrap_math_vec_copy!( - math::Vec4, - derives(PartialEq), - fields(w, x, y, z), - metamethods( - Add(LuaVec4, f32), - Sub(LuaVec4, f32), - Div(LuaVec4, f32), - Mul(LuaVec4, f32), - Mod(LuaVec4, f32), - Eq, Unm - ) -); - -// f64 types -wrap_math_vec_copy!( - math::DVec2, - derives(PartialEq), - fields(x, y), - metamethods( - Add(LuaDVec2, f64), - Sub(LuaDVec2, f64), - Div(LuaDVec2, f64), - Mul(LuaDVec2, f64), - Mod(LuaDVec2, f64), - Eq, Unm - ) -); -wrap_math_vec_copy!( - math::DVec3, - derives(PartialEq), - fields(x, y, z), - metamethods( - Add(LuaDVec3, f64), - Sub(LuaDVec3, f64), - Div(LuaDVec3, f64), - Mul(LuaDVec3, f64), - Mod(LuaDVec3, f64), - Eq, Unm - ) -); -wrap_math_vec_copy!( - math::DVec4, - derives(PartialEq), - fields(w, x, y, z), - metamethods( - Add(LuaDVec4, f64), - Sub(LuaDVec4, f64), - Div(LuaDVec4, f64), - Mul(LuaDVec4, f64), - Mod(LuaDVec4, f64), - Eq, Unm - ) -); - -// i32 types -wrap_math_vec_copy!( - math::IVec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods( - Add(LuaIVec2, i32), - Sub(LuaIVec2, i32), - Div(LuaIVec2, i32), - Mul(LuaIVec2, i32), - Mod(LuaIVec2, i32), - Shl(LuaIVec2, LuaUVec2, i32), - Shr(LuaIVec2, LuaUVec2, i32), - BAnd(LuaIVec2, i32), - BOr(LuaIVec2, i32), - BXor(LuaIVec2, i32), - Eq, Unm, BNot - ) -); -wrap_math_vec_copy!( - math::IVec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods( - Add(LuaIVec3, i32), - Sub(LuaIVec3, i32), - Div(LuaIVec3, i32), - Mul(LuaIVec3, i32), - Mod(LuaIVec3, i32), - Shl(LuaIVec3, LuaUVec3, i32), - Shr(LuaIVec3, LuaUVec3, i32), - BAnd(LuaIVec3, i32), - BOr(LuaIVec3, i32), - BXor(LuaIVec3, i32), - Eq, Unm, BNot - ) -); -wrap_math_vec_copy!( - math::IVec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods( - Add(LuaIVec4, i32), - Sub(LuaIVec4, i32), - Div(LuaIVec4, i32), - Mul(LuaIVec4, i32), - Mod(LuaIVec4, i32), - Shl(LuaIVec4, LuaUVec4, i32), - Shr(LuaIVec4, LuaUVec4, i32), - BAnd(LuaIVec4, i32), - BOr(LuaIVec4, i32), - BXor(LuaIVec4, i32), - Eq, Unm, BNot - ) -); - -// u32 types -wrap_math_vec_copy!( - math::UVec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods( - Add(LuaUVec2, u32), - Sub(LuaUVec2, u32), - Div(LuaUVec2, u32), - Mul(LuaUVec2, u32), - Mod(LuaUVec2, u32), - Shl(LuaUVec2, LuaIVec2, i32), - Shr(LuaUVec2, LuaIVec2, i32), - BAnd(LuaUVec2, u32), - BOr(LuaUVec2, u32), - BXor(LuaUVec2, u32), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::UVec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods( - Add(LuaUVec3, u32), - Sub(LuaUVec3, u32), - Div(LuaUVec3, u32), - Mul(LuaUVec3, u32), - Mod(LuaUVec3, u32), - Shl(LuaUVec3, LuaIVec3, i32), - Shr(LuaUVec3, LuaIVec3, i32), - BAnd(LuaUVec3, u32), - BOr(LuaUVec3, u32), - BXor(LuaUVec3, u32), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::UVec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods( - Add(LuaUVec4, u32), - Sub(LuaUVec4, u32), - Div(LuaUVec4, u32), - Mul(LuaUVec4, u32), - Mod(LuaUVec4, u32), - Shl(LuaUVec4, LuaIVec4, i32), - Shr(LuaUVec4, LuaIVec4, i32), - BAnd(LuaUVec4, u32), - BOr(LuaUVec4, u32), - BXor(LuaUVec4, u32), - Eq, BNot - ) -); - -// i64 types -wrap_math_vec_copy!( - math::I64Vec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods( - Add(LuaI64Vec2, i64), - Sub(LuaI64Vec2, i64), - Div(LuaI64Vec2, i64), - Mul(LuaI64Vec2, i64), - Mod(LuaI64Vec2, i64), - Shl(i64), - Shr(i64), - BAnd(LuaI64Vec2, i64), - BOr(LuaI64Vec2, i64), - BXor(LuaI64Vec2, i64), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::I64Vec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods( - Add(LuaI64Vec3, i64), - Sub(LuaI64Vec3, i64), - Div(LuaI64Vec3, i64), - Mul(LuaI64Vec3, i64), - Mod(LuaI64Vec3, i64), - Shl(i64), - Shr(i64), - BAnd(LuaI64Vec3, i64), - BOr(LuaI64Vec3, i64), - BXor(LuaI64Vec3, i64), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::I64Vec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods( - Add(LuaI64Vec4, i64), - Sub(LuaI64Vec4, i64), - Div(LuaI64Vec4, i64), - Mul(LuaI64Vec4, i64), - Mod(LuaI64Vec4, i64), - Shl(i64), - Shr(i64), - BAnd(LuaI64Vec4, i64), - BOr(LuaI64Vec4, i64), - BXor(LuaI64Vec4, i64), - Eq, BNot - ) -); - -// u64 types -wrap_math_vec_copy!( - math::U64Vec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods( - Add(LuaU64Vec2, u64), - Sub(LuaU64Vec2, u64), - Div(LuaU64Vec2, u64), - Mul(LuaU64Vec2, u64), - Mod(LuaU64Vec2, u64), - Shl(i64), - Shr(i64), - BAnd(LuaU64Vec2, u64), - BOr(LuaU64Vec2, u64), - BXor(LuaU64Vec2, u64), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::U64Vec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods( - Add(LuaU64Vec3, u64), - Sub(LuaU64Vec3, u64), - Div(LuaU64Vec3, u64), - Mul(LuaU64Vec3, u64), - Mod(LuaU64Vec3, u64), - Shl(i64), - Shr(i64), - BAnd(LuaU64Vec3, u64), - BOr(LuaU64Vec3, u64), - BXor(LuaU64Vec3, u64), - Eq, BNot - ) -); -wrap_math_vec_copy!( - math::U64Vec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods( - Add(LuaU64Vec4, u64), - Sub(LuaU64Vec4, u64), - Div(LuaU64Vec4, u64), - Mul(LuaU64Vec4, u64), - Mod(LuaU64Vec4, u64), - Shl(i64), - Shr(i64), - BAnd(LuaU64Vec4, u64), - BOr(LuaU64Vec4, u64), - BXor(LuaU64Vec4, u64), - Eq, BNot - ) -); - -// bool types -wrap_math_vec_copy!( - math::BVec2, - derives(PartialEq, Eq, Hash), - fields(x, y), - metamethods(Eq, BAnd, BOr, BXor, BNot) -); -wrap_math_vec_copy!( - math::BVec3, - derives(PartialEq, Eq, Hash), - fields(x, y, z), - metamethods(Eq, BAnd, BOr, BXor, BNot) -); -wrap_math_vec_copy!( - math::BVec4, - derives(PartialEq, Eq, Hash), - fields(w, x, y, z), - metamethods(Eq, BAnd, BOr, BXor, BNot) -); - -// mat2 -wrap_math_vec_copy!( - math::Mat2, - derives(PartialEq), - no_new, - matrix { - col_type = LuaVec2 - }, - metamethods( - Eq, - Add, - Sub, - Mul(LuaMat2, f32), - Unm - ) -); */ - -/* wrap_math_vec_copy!( - math::Mat4, - derives(PartialEq), - no_new, - matrix { - col_type = LuaVec4 - }, - metamethods( - Eq, - Add, - Sub, - Mul(LuaMat4, f32), - Unm - ) -); */ - -wrap_math_vec_copy!( - math::Quat, - derives(PartialEq), - no_new, - metamethods( - Eq, - // __mul for LuaVec3 is manually implemented below since it doesn't return Self - Mul(LuaQuat, f32), - Add, - Sub, - Div(f32), - ), - custom_methods { - methods.add_function("new", |_, (x, y, z, w)| { - Ok(Self(math::Quat::from_xyzw(x, y, z, w))) - }); - - methods.add_function("from_rotation_x", |_, (rad,)| { - let q = math::Quat::from_rotation_x(rad); - Ok(Self(q)) - }); - - methods.add_function("from_rotation_y", |_, (rad,)| { - let q = math::Quat::from_rotation_y(rad); - Ok(Self(q)) - }); - - methods.add_function("from_rotation_z", |_, (rad,)| { - let q = math::Quat::from_rotation_z(rad); - Ok(Self(q)) - }); - - methods.add_method("dot", |_, this, (rhs,): (Self,)| { - Ok(this.dot(rhs.0)) - }); - - methods.add_method("length", |_, this, ()| { - Ok(this.length()) - }); - - methods.add_method("length_squared", |_, this, ()| { - Ok(this.length_squared()) - }); - - methods.add_method("normalize", |_, this, ()| { - Ok(Self(this.normalize())) - }); - - methods.add_method("mult_quat", |_, this, (rhs,): (Self,)| { - Ok(Self(this.0 * rhs.0)) - }); - - methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| { - Ok(LuaVec3(this.0 * rhs.0)) - }); - - // manually implemented here since it doesn't return `Self` - methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (rhs,): (LuaVec3,)| { - Ok(LuaVec3(this.0 * rhs.0)) - }); - - methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { - Ok(Self(this.lerp(*rhs, alpha))) - }); - } -); - -wrap_math_vec_copy!( - math::Transform, - //derives(PartialEq), - no_new, - metamethods(ToString, Eq), - custom_fields { - fields.add_field_method_get("translation", |_, this| { - Ok(LuaVec3(this.translation)) - }); - fields.add_field_method_set("translation", |_, this, v: LuaVec3| { - this.translation = *v; - Ok(()) - }); - - fields.add_field_method_get("rotation", |_, this| { - Ok(LuaQuat(this.rotation)) - }); - fields.add_field_method_set("rotation", |_, this, v: LuaQuat| { - this.rotation = *v; - Ok(()) - }); - - fields.add_field_method_get("scale", |_, this| { - Ok(LuaVec3(this.scale)) - }); - fields.add_field_method_set("scale", |_, this, v: LuaVec3| { - this.scale = *v; - Ok(()) - }); - }, - custom_methods { - methods.add_function("default", |_, ()| { - Ok(Self(math::Transform::default())) - }); - - methods.add_function("new", |_, (pos, rot, scale): (LuaVec3, LuaQuat, LuaVec3)| { - Ok(Self(math::Transform::new(*pos, *rot, *scale))) - }); - - methods.add_function("from_translation", |_, (pos,): (LuaVec3,)| { - Ok(Self(math::Transform::from_translation(*pos))) - }); - - methods.add_function("from_xyz", |_, (x, y, z)| { - Ok(Self(math::Transform::from_xyz(x, y, z))) - }); - - methods.add_method("forward", |_, this, ()| { - Ok(LuaVec3(this.forward())) - }); - - methods.add_method("left", |_, this, ()| { - Ok(LuaVec3(this.left())) - }); - - methods.add_method("up", |_, this, ()| { - Ok(LuaVec3(this.up())) - }); - - methods.add_method_mut("rotate", |_, this, (quat,): (LuaQuat,)| { - this.rotate(*quat); - Ok(()) - }); - - methods.add_method_mut("rotate_x", |_, this, (deg,): (f32,)| { - this.rotate_x(math::Angle::Degrees(deg)); - Ok(()) - }); - - methods.add_method_mut("rotate_y", |_, this, (deg,): (f32,)| { - this.rotate_y(math::Angle::Degrees(deg)); - Ok(()) - }); - - methods.add_method_mut("rotate_z", |_, this, (deg,): (f32,)| { - this.rotate_z(math::Angle::Degrees(deg)); - Ok(()) - }); - - methods.add_method_mut("rotate_x_rad", |_, this, (rad,): (f32,)| { - this.rotate_x(math::Angle::Radians(rad)); - Ok(()) - }); - - methods.add_method_mut("rotate_y_rad", |_, this, (rad,): (f32,)| { - this.rotate_y(math::Angle::Radians(rad)); - Ok(()) - }); - - methods.add_method_mut("rotate_z_rad", |_, this, (rad,): (f32,)| { - this.rotate_z(math::Angle::Radians(rad)); - Ok(()) - }); - - methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { - Ok(Self(this.lerp(*rhs, alpha))) - }); - - // rotate a transform - methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (quat,): (LuaQuat,)| { - let mut t = *this; - t.rotation *= *quat; - Ok(t) - }); - - // move a transform - methods.add_meta_method(mlua::MetaMethod::Add, |_, this, (pos,): (LuaVec3,)| { - let mut t = *this; - t.translation += *pos; - Ok(t) - }); - } -); \ No newline at end of file +pub mod delta_time; +pub use delta_time::*; \ No newline at end of file -- 2.40.1 From 6465ed2e0b0d008b9e6dcdadc1c9a46011d6498c Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 20 Jan 2024 11:46:42 -0500 Subject: [PATCH 15/41] scripting: support 'overloaded' like methods in wrapper macro --- .../lyra-scripting-derive/src/lib.rs | 382 ++++++++++-------- lyra-scripting/src/lua/wrappers/math.rs | 58 +-- 2 files changed, 225 insertions(+), 215 deletions(-) diff --git a/lyra-scripting/lyra-scripting-derive/src/lib.rs b/lyra-scripting/lyra-scripting-derive/src/lib.rs index ed6e7b1..beda599 100644 --- a/lyra-scripting/lyra-scripting-derive/src/lib.rs +++ b/lyra-scripting/lyra-scripting-derive/src/lib.rs @@ -1,6 +1,6 @@ use proc_macro2::{Ident, Span}; use quote::quote; -use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced, bracketed}; +use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced}; mod mat_wrapper; use mat_wrapper::MatWrapper; @@ -31,6 +31,221 @@ impl syn::parse::Parse for MetaMethod { } } +impl MetaMethod { + /// Returns a boolean if an identifier is a lua wrapper, and therefore also userdata + fn is_arg_wrapper(ident: &Ident) -> bool { + let s = ident.to_string(); + s.starts_with("Lua") + } + + /// returns the tokens of the body of the metamethod + /// + /// Parameters + /// * `metamethod` - The ident of the metamethod that is being implemented. + /// * `other` - The tokens of the argument used in the metamethod. + fn get_method_body(metamethod: &Ident, other: proc_macro2::TokenStream) -> proc_macro2::TokenStream { + let mm_str = metamethod.to_string(); + let mm_str = mm_str.as_str(); + match mm_str { + "Add" | "Sub" | "Div" | "Mul" | "Mod" => { + let symbol = match mm_str { + "Add" => quote!(+), + "Sub" => quote!(-), + "Div" => quote!(/), + "Mul" => quote!(*), + "Mod" => quote!(%), + _ => unreachable!(), // the string was just checked to be one of these + }; + + quote! { + Ok(Self(this.0 #symbol #other)) + } + }, + "Unm" => { + quote! { + Ok(Self(-this.0)) + } + }, + "Eq" => { + quote! { + Ok(this.0 == #other) + } + }, + "Shl" => { + quote! { + Ok(Self(this.0 << #other)) + } + } + "Shr" => { + quote! { + Ok(Self(this.0 >> #other)) + } + }, + "BAnd" | "BOr" | "BXor" => { + let symbol = match mm_str { + "BAnd" => { + quote!(&) + }, + "BOr" => { + quote!(|) + }, + "BXor" => { + quote!(^) + }, + _ => unreachable!() // the string was just checked to be one of these + }; + + quote! { + Ok(Self(this.0 #symbol #other)) + } + }, + "BNot" => { + quote! { + Ok(Self(!this.0)) + } + }, + "ToString" => { + quote! { + Ok(format!("{:?}", this.0)) + } + }, + _ => syn::Error::new_spanned(metamethod, + "unsupported auto implementation of metamethod").to_compile_error(), + } + } + + fn get_body_for_arg(mt_ident: &Ident, arg_ident: &Ident, arg_param: proc_macro2::TokenStream) -> proc_macro2::TokenStream { + let other: proc_macro2::TokenStream = if Self::is_arg_wrapper(arg_ident) { + // Lua wrappers must be dereferenced + quote! { + #arg_param.0 + } + } else { + quote! { + #arg_param + } + }; + Self::get_method_body(&mt_ident, other) + } + + pub fn to_tokens(&self, wrapper_ident: &Ident) -> proc_macro2::TokenStream { + let wrapped_str = &wrapper_ident.to_string()[3..]; // removes starting 'Lua' from name + let mt_ident = &self.ident; + let mt_lua_name = mt_ident.to_string().to_lowercase(); + + if self.mods.is_empty() { + let other = quote! { + v.0 + }; + let body = Self::get_method_body(&self.ident, other); + + quote! { + methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| { + #body + }); + } + } else if self.mods.len() == 1 { + let first = self.mods.iter().next().unwrap(); + let body = Self::get_body_for_arg(&self.ident, first, quote!(v)); + + quote! { + methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| { + #body + }); + } + } else { + // an optional match arm that matches mlua::Value:Number + let number_arm = { + let num_ident = self.mods.iter().find(|i| { + let is = i.to_string(); + let is = is.as_str(); + match is { + "u8" | "u16" | "u32" | "u64" | "u128" + | "i8" | "i16" | "i32" | "i64" | "i128" + | "f32" | "f64" => true, + _ => false, + } + }); + + if let Some(num_ident) = num_ident { + let body = Self::get_body_for_arg(&self.ident, num_ident, quote!(n as #num_ident)); + + quote! { + mlua::Value::Number(n) => { + #body + }, + } + } else { quote!() } + }; + + let userdata_arm = { + let wrappers: Vec<&Ident> = self.mods.iter() + .filter(|i| Self::is_arg_wrapper(i)) + .collect(); + + let if_statements = wrappers.iter().map(|i| { + let body = Self::get_method_body(&self.ident, quote!(other.0)); + + quote! { + if let Ok(other) = ud.borrow::<#i>() { + #body + } + } + + }); + + quote! { + mlua::Value::UserData(ud) => { + #(#if_statements else)* + // this is the body of the else statement + { + // try to get the name of the userdata for the error message + if let Ok(mt) = ud.get_metatable() { + if let Ok(name) = mt.get::("__name") { + return Err(mlua::Error::BadArgument { + to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + pos: 2, + name: Some("rhs".to_string()), + cause: Arc::new(mlua::Error::RuntimeError( + format!("cannot multiply with unknown userdata named {}", name) + )) + }); + } + } + + Err(mlua::Error::BadArgument { + to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + pos: 2, + name: Some("rhs".to_string()), + cause: Arc::new( + mlua::Error::runtime("cannot multiply with unknown userdata") + ) + }) + } + }, + } + }; + + quote! { + methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (mlua::Value,)| { + match v { + #number_arm + #userdata_arm + _ => Err(mlua::Error::BadArgument { + to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + pos: 2, + name: Some("rhs".to_string()), + cause: Arc::new( + mlua::Error::RuntimeError(format!("cannot multiply with {}", v.type_name())) + ) + }) + } + }); + } + } + } +} + pub(crate) struct VecWrapper { } @@ -341,6 +556,7 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt 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; @@ -389,169 +605,7 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt let meta_method_idents = { let idents = input.meta_method_idents.iter().map(|metamethod| { - let metamethod_ident = &metamethod.ident; - let mm_str = metamethod.ident.to_string(); - let mm_str = mm_str.as_str(); - match mm_str { - "Add" | "Sub" | "Div" | "Mul" | "Mod" => { - let symbol = match mm_str { - "Add" => quote!(+), - "Sub" => quote!(-), - "Div" => quote!(/), - "Mul" => quote!(*), - "Mod" => quote!(%), - _ => unreachable!(), - }; - - // create a temporary vec to chain with metamethod.mods. If no parameters - // were provided, add the wrapper to the list of parameters. - let t = if metamethod.mods.is_empty() { - vec![wrapper_typename.clone()] - } else { vec![] }; - - let mods = metamethod.mods.iter().chain(t.iter()).map(|param| { - let other = if param.to_string().starts_with("Lua") { - quote!(other.0) - } else { - quote!(other) - }; - - quote! { - methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, - |_, this, (other,): (#param,)| { - Ok(#wrapper_typename(this.0 #symbol #other)) - }); - } - }); - - quote! { - #(#mods)* - } - }, - "Unm" => { - quote! { - methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, |_, this, ()| { - Ok(#wrapper_typename(-this.0)) - }); - } - }, - // Eq meta method has a different implementation than the above methods. - "Eq" => { - quote! { - methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, - |_, this, (other,): (#wrapper_typename,)| { - Ok(this.0 == other.0) - }); - } - }, - "Shl" => { - // create a temporary vec to chain with metamethod.mods. If no parameters - // were provided, add the wrapper to the list of parameters. - let t = if metamethod.mods.is_empty() { - vec![wrapper_typename.clone()] - } else { vec![] }; - - let mods = metamethod.mods.iter().chain(t.iter()).map(|param| { - let other = if param.to_string().starts_with("Lua") { - quote!(other.0) - } else { - quote!(other) - }; - - quote! { - methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, - |_, this, (other,): (#param,)| { - Ok(#wrapper_typename(this.0 << #other)) - }); - } - }); - - quote! { - #(#mods)* - } - } - "Shr" => { - // create a temporary vec to chain with metamethod.mods. If no parameters - // were provided, add the wrapper to the list of parameters. - let t = if metamethod.mods.is_empty() { - vec![wrapper_typename.clone()] - } else { vec![] }; - - let mods = metamethod.mods.iter().chain(t.iter()).map(|param| { - let other = if param.to_string().starts_with("Lua") { - quote!(other.0) - } else { - quote!(other) - }; - - quote! { - methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, - |_, this, (other,): (#param,)| { - Ok(#wrapper_typename(this.0 >> #other)) - }); - } - }); - - quote! { - #(#mods)* - } - }, - "BAnd" | "BOr" | "BXor" => { - let symbol = match mm_str { - "BAnd" => { - quote!(&) - }, - "BOr" => { - quote!(|) - }, - "BXor" => { - quote!(^) - }, - _ => unreachable!() // the string was just checked to be one of these - }; - - // create a temporary vec to chain with metamethod.mods. If no parameters - // were provided, add the wrapper to the list of parameters. - let t = if metamethod.mods.is_empty() { - vec![wrapper_typename.clone()] - } else { vec![] }; - - let mods = metamethod.mods.iter().chain(t.iter()).map(|param| { - let other = if param.to_string().starts_with("Lua") { - quote!(other.0) - } else { - quote!(other) - }; - - quote! { - methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, - |_, this, (other,): (#param,)| { - Ok(#wrapper_typename(this.0 #symbol #other)) - }); - } - }); - - quote! { - #(#mods)* - } - }, - "BNot" => { - quote! { - methods.add_meta_method(mlua::MetaMethod::#metamethod_ident, |_, this, ()| { - Ok(#wrapper_typename(!this.0)) - }); - } - }, - "ToString" => { - quote! { - methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { - Ok(format!("{:?}", this.0)) - }); - } - }, - _ => syn::Error::new_spanned(metamethod_ident, - "unsupported auto implementation of metamethod").to_compile_error(), - } + metamethod.to_tokens(&wrapper_typename) }); quote! { diff --git a/lyra-scripting/src/lua/wrappers/math.rs b/lyra-scripting/src/lua/wrappers/math.rs index a439fa1..58d8f4a 100644 --- a/lyra-scripting/src/lua/wrappers/math.rs +++ b/lyra-scripting/src/lua/wrappers/math.rs @@ -17,8 +17,7 @@ wrap_math_vec_copy!( Div(LuaVec2, f32), Mul(LuaVec2, f32), Mod(LuaVec2, f32), - Eq, - Unm + Eq, Unm, ToString ) ); wrap_math_vec_copy!( @@ -26,58 +25,15 @@ wrap_math_vec_copy!( derives(PartialEq), fields(x, y, z), metamethods( - Add(LuaVec3), + Add(LuaVec3, f32), Sub(LuaVec3, f32), Div(LuaVec3, f32), - //Mul(LuaVec3, f32), + Mul(LuaVec3, f32), Mod(LuaVec3, f32), - Eq, Unm, ToString, - ), - custom_methods { - methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (v,): (mlua::Value,)| { - match v { - mlua::Value::Number(n) => { - Ok(Self(this.0 * (n as f32))) - }, - mlua::Value::UserData(ud) => { - if let Ok(other_this) = ud.borrow::() { - Ok(Self(this.0 * other_this.0)) - } else { - if let Ok(mt) = ud.get_metatable() { - if let Ok(name) = mt.get::("__name") { - return Err(mlua::Error::BadArgument { - to: Some("LuaVec3.__mul".to_string()), - pos: 2, - name: Some("rhs".to_string()), - cause: Arc::new(mlua::Error::RuntimeError( - format!("cannot multiply with unknown userdata named {}", name) - )) - }); - } - } - - Err(mlua::Error::BadArgument { - to: Some("LuaVec3.__mul".to_string()), - pos: 2, - name: Some("rhs".to_string()), - cause: Arc::new( - mlua::Error::runtime("cannot multiply with unknown userdata") - ) - }) - } - }, - _ => Err(mlua::Error::BadArgument { - to: Some("LuaVec3.__mul".to_string()), - pos: 2, - name: Some("rhs".to_string()), - cause: Arc::new( - mlua::Error::RuntimeError(format!("cannot multiply with {}", v.type_name())) - ) - }) - } - }); - } + Eq, Unm, ToString + ) ); + /* wrap_math_vec_copy!( math::Vec3A, derives(PartialEq), @@ -492,7 +448,7 @@ wrap_math_vec_copy!( wrap_math_vec_copy!( math::Transform, - //derives(PartialEq), + derives(PartialEq), no_new, metamethods(ToString, Eq), custom_fields { -- 2.40.1 From 747d4dcf19327acf62c33f6817a31342ec944f4d Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 12 Feb 2024 20:04:50 -0500 Subject: [PATCH 16/41] scripting: implement querying for resources from lua --- lyra-scripting/src/lua/world.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 95ec3e8..f1875e5 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, ptr::NonNull, any::Any, ops::Deref}; +use std::{sync::{Arc, RwLock}, ptr::NonNull, any::Any, ops::Deref}; use lyra_ecs::query::dynamic::QueryDynamicType; use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType}; @@ -6,7 +6,7 @@ use mlua::{AnyUserDataExt, IntoLua, IntoLuaMulti}; use crate::{ScriptWorldPtr, ScriptEntity, ScriptDynamicBundle, ScriptBorrow}; -use super::{ReflectedIterator, DynamicViewIter, FN_NAME_INTERNAL_REFLECT_TYPE, reflect_user_data, ReflectLuaProxy}; +use super::{ReflectedIterator, DynamicViewIter, FN_NAME_INTERNAL_REFLECT_TYPE, reflect_user_data, ReflectLuaProxy, FN_NAME_INTERNAL_REFLECT}; impl<'lua> mlua::FromLua<'lua> for ScriptEntity { fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { @@ -165,6 +165,24 @@ impl mlua::UserData for ScriptWorldPtr { .call_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) .expect("Type does not implement 'reflect_type' properly"); + //if let Some(data) = reflect.data { + /* let res = reflect.reflect_branch.as_resource_unchecked(); + if let Some(res_ptr) = res.reflect_arc(this.as_mut()) { + let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) + .unwrap(); + let proxy = reg_type.get_data::() + .expect("Type does not have ReflectLuaProxy as a TypeData"); + + //let res = Arc::new(RwLock::new(data.as_any_box())) + (proxy.fn_as_uservalue_ref)(lua, res_ptr) + .and_then(|ud| ud.into_lua(lua)) + + //Ok(mlua::Value::Nil) + } else { + Ok(mlua::Value::Nil) + } */ + + let res = reflect.reflect_branch.as_resource_unchecked(); if let Some(res_ptr) = res.reflect_ptr(this.as_mut()) { //.expect("Failed to find resource pointer in world!"); -- 2.40.1 From 82c13a7dd2b043e02cf8a6cd4f15bb187f9638f7 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 12 Feb 2024 20:23:44 -0500 Subject: [PATCH 17/41] scripting: do a bit of code cleanup --- lyra-scripting/src/lua/dynamic_iter.rs | 8 - lyra-scripting/src/lua/mod.rs | 248 +------------------------ lyra-scripting/src/lua/system.rs | 238 ++++++++++++++++++++++++ lyra-scripting/src/lua/world.rs | 18 -- 4 files changed, 245 insertions(+), 267 deletions(-) create mode 100644 lyra-scripting/src/lua/system.rs diff --git a/lyra-scripting/src/lua/dynamic_iter.rs b/lyra-scripting/src/lua/dynamic_iter.rs index ad833b6..3834f39 100644 --- a/lyra-scripting/src/lua/dynamic_iter.rs +++ b/lyra-scripting/src/lua/dynamic_iter.rs @@ -120,7 +120,6 @@ impl Iterator for DynamicViewIter { continue; } - //let world = unsafe { self.world_ptr.as_ref() }; let world = self.world_ptr.as_ref(); self.fetchers = self.queries.iter() @@ -153,8 +152,6 @@ pub struct ReflectedIterator { } impl ReflectedIterator { - - #[cfg(feature = "lua")] pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option> { @@ -167,11 +164,6 @@ impl ReflectedIterator { .map(|r| NonNull::from(r.deref())); } - /* let mut row = ReflectedRow { - entity: row.entity, - row: row.item, - }; */ - let mut dynamic_row = vec![]; for d in row.item.iter() { let id = d.info.type_id.as_rust(); diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index add893c..cc2a366 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -1,11 +1,7 @@ pub mod dynamic_iter; -use anyhow::anyhow; pub use dynamic_iter::*; pub mod world; -use lyra_game::{game::GameStages, plugin::Plugin}; -use lyra_resource::ResourceManager; -use tracing::{debug, debug_span, error, trace}; pub use world::*; pub mod script; @@ -20,13 +16,15 @@ pub mod wrappers; pub mod proxy; pub use proxy::*; +pub mod system; +pub use system::*; + #[cfg(test)] mod test; -use std::{any::TypeId, ptr::NonNull, sync::Mutex}; +use std::{any::TypeId, sync::Mutex}; use lyra_ecs::{ - query::{Entities, ResMut, View}, DynamicBundle, World, }; use lyra_reflect::{FromType, Reflect, TypeRegistry}; @@ -38,13 +36,9 @@ pub type LuaContext = Mutex; pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; -use crate::{ - GameScriptExt, ScriptApiProviders, ScriptBorrow, ScriptContexts, ScriptData, - ScriptDynamicBundle, ScriptError, ScriptHost, ScriptList, ScriptWorldPtr, -}; - -use self::providers::{LyraEcsApiProvider, LyraMathApiProvider, UtilityApiProvider}; +use crate::{ScriptBorrow, ScriptDynamicBundle}; +/// A trait used for registering a Lua type with the world. pub trait RegisterLuaType { /// Register a type to lua that **is not wrapped**. fn register_lua_type<'a, T>(&mut self) @@ -113,232 +107,4 @@ impl mlua::UserData for ScriptDynamicBundle { Ok(()) }); } -} - -/// A system that creates the script contexts in the world as new scripts are found -pub fn lua_scripts_create_contexts( - mut host: ResMut, - mut contexts: ResMut>, - mut providers: ResMut>, - view: View<(Entities, &ScriptList)>, -) -> anyhow::Result<()> { - for (en, scripts) in view.into_iter() { - for script in scripts.iter() { - if !contexts.has_context(script.id()) { - let script_data = ScriptData { - name: script.name().to_string(), - script_id: script.id(), - entity: en, - }; - - 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() { - debug!("Loading script..."); - let mut script_ctx = - host.load_script(&script_res.bytes, &script_data, &mut providers)?; - trace!("Finished loading script"); - - debug!("Setting up script..."); - host.setup_script(&script_data, &mut script_ctx, &mut providers)?; - trace!("Finished setting up script"); - - contexts.add_context(script.id(), script_ctx); - } else { - trace!("Script is not loaded yet, skipping for now"); - } - } - } - } - - Ok(()) -} - -/// A system that triggers a reload of watched script resources. -/// -/// 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, - 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 watch_recv = resman.watcher_event_recv(&handle_path).unwrap(); - - match watch_recv.try_recv() { - Ok(ev) => { - let evs = - ev.map_err(|e| anyhow!("Script watcher ran into errors: {:?}", e))?; - - if evs.iter().any(|ev| ev.event.kind.is_modify()) { - debug!( - "Detected change of '{}' script, triggering reload", - handle_path - ); - - contexts.remove_context(script.id()).unwrap(); - resman.reload(handle)?; - } - } - Err(e) => match e { - lyra_resource::channel::TryRecvError::Empty => {} - lyra_resource::channel::TryRecvError::Disconnected => { - resman.stop_watching(&handle_path).unwrap(); - } - }, - } - } - } - } - - Ok(()) -} - -fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Result<()> { - let world_ptr = ScriptWorldPtr::from_ref(&world); - let mut host = world.get_resource_mut::(); - let mut contexts = world.get_resource_mut::>(); - let mut providers = world.get_resource_mut::>(); - - for (en, scripts) in world.view_iter::<(Entities, &ScriptList)>() { - for script in scripts.iter() { - let script_data = ScriptData { - name: script.name().to_string(), - script_id: script.id(), - entity: en, - }; - - if let Some(ctx) = contexts.get_context_mut(script.id()) { - trace!( - "Running '{}' function in script '{}'", - stage_name, - script.name() - ); - - match host.call_script( - world_ptr.clone(), - &script_data, - ctx, - &mut providers, - stage_name, - ) { - Ok(()) => {} - Err(e) => match e { - ScriptError::MluaError(m) => { - error!("Script '{}' ran into an error: {}", script.name(), m); - } - ScriptError::Other(_) => return Err(e.into()), - }, - } - } - } - } - - Ok(()) -} - -/// This system executes the 'on_update' function of lua scripts in the world. It is meant to run -/// during the 'GameStages::Update' stage. -pub fn lua_script_update_stage_system(world: &mut World) -> anyhow::Result<()> { - lua_call_script_function(world, "on_update") -} - -/// This system executes the 'on_pre_update' function of lua scripts in the world. It is meant to run -/// during the 'GameStages::PreUpdate' stage. -pub fn lua_script_pre_update_stage_system(world: &mut World) -> anyhow::Result<()> { - lua_call_script_function(world, "on_pre_update") -} - -/// This system executes the 'on_post_update' function of lua scripts in the world. It is meant to run -/// during the 'GameStages::PostUpdate' stage. -pub fn lua_script_post_update_stage_system(world: &mut World) -> anyhow::Result<()> { - lua_call_script_function(world, "on_post_update") -} - -/// This system executes the 'on_first' function of lua scripts in the world. It is meant to run -/// during the 'GameStages::First' stage. -pub fn lua_script_first_stage_system(world: &mut World) -> anyhow::Result<()> { - lua_call_script_function(world, "on_first") -} - -/// This system executes the 'on_last' function of lua scripts in the world. It is meant to run -/// during the 'GameStages::Last' stage. -pub fn lua_script_last_stage_system(world: &mut World) -> anyhow::Result<()> { - lua_call_script_function(world, "on_last") -} - -#[derive(Default)] -pub struct LuaScriptingPlugin; - -impl Plugin for LuaScriptingPlugin { - fn setup(&self, game: &mut lyra_game::game::Game) { - let world = game.world(); - - world.add_resource_default::(); - - world.add_resource_default::(); - world.add_resource_default::>(); - world.add_resource_default::>(); - - let mut loader = world - .try_get_resource_mut::() - .expect("Add 'ResourceManager' to the world before trying to add this plugin"); - loader.register_loader::(); - drop(loader); - - game.add_script_api_provider::(UtilityApiProvider); - game.add_script_api_provider::(LyraEcsApiProvider); - game.add_script_api_provider::(LyraMathApiProvider); - - game.add_system_to_stage( - GameStages::First, - "lua_create_contexts", - lua_scripts_create_contexts, - &[], - ) - .add_system_to_stage( - GameStages::First, - "lua_reload_scripts", - lua_scripts_reload_system, - &["lua_create_contexts"], - ) - .add_system_to_stage( - GameStages::First, - "lua_first_stage", - lua_script_first_stage_system, - &["lua_reload_scripts"], - ) - // cannot depend on 'lua_create_contexts' since it will cause a panic. - // the staged executor separates the executor of a single stage so this system - // cannot depend on the other one. - .add_system_to_stage( - GameStages::PreUpdate, - "lua_pre_update", - lua_script_pre_update_stage_system, - &[], - ) - .add_system_to_stage( - GameStages::Update, - "lua_update", - lua_script_update_stage_system, - &[], - ) - .add_system_to_stage( - GameStages::PostUpdate, - "lua_post_update", - lua_script_post_update_stage_system, - &[], - ) - .add_system_to_stage( - GameStages::Last, - "lua_last_stage", - lua_script_last_stage_system, - &[], - ); - } -} +} \ No newline at end of file diff --git a/lyra-scripting/src/lua/system.rs b/lyra-scripting/src/lua/system.rs new file mode 100644 index 0000000..071e69e --- /dev/null +++ b/lyra-scripting/src/lua/system.rs @@ -0,0 +1,238 @@ +use anyhow::anyhow; +use lyra_ecs::{query::{Entities, ResMut, View}, World}; +use lyra_game::{game::GameStages, plugin::Plugin}; +use lyra_reflect::TypeRegistry; +use lyra_resource::ResourceManager; +use tracing::{debug, debug_span, error, trace}; + +use crate::{GameScriptExt, ScriptApiProviders, ScriptContexts, ScriptData, ScriptError, ScriptHost, ScriptList, ScriptWorldPtr}; + +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( + mut host: ResMut, + mut contexts: ResMut>, + mut providers: ResMut>, + view: View<(Entities, &ScriptList)>, +) -> anyhow::Result<()> { + for (en, scripts) in view.into_iter() { + for script in scripts.iter() { + if !contexts.has_context(script.id()) { + let script_data = ScriptData { + name: script.name().to_string(), + script_id: script.id(), + entity: en, + }; + + 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() { + debug!("Loading script..."); + let mut script_ctx = + host.load_script(&script_res.bytes, &script_data, &mut providers)?; + trace!("Finished loading script"); + + debug!("Setting up script..."); + host.setup_script(&script_data, &mut script_ctx, &mut providers)?; + trace!("Finished setting up script"); + + contexts.add_context(script.id(), script_ctx); + } else { + trace!("Script is not loaded yet, skipping for now"); + } + } + } + } + + Ok(()) +} + +/// A system that triggers a reload of watched script resources. +/// +/// 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, + 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 watch_recv = resman.watcher_event_recv(&handle_path).unwrap(); + + match watch_recv.try_recv() { + Ok(ev) => { + let evs = + ev.map_err(|e| anyhow!("Script watcher ran into errors: {:?}", e))?; + + if evs.iter().any(|ev| ev.event.kind.is_modify()) { + debug!( + "Detected change of '{}' script, triggering reload", + handle_path + ); + + contexts.remove_context(script.id()).unwrap(); + resman.reload(handle)?; + } + } + Err(e) => match e { + lyra_resource::channel::TryRecvError::Empty => {} + lyra_resource::channel::TryRecvError::Disconnected => { + resman.stop_watching(&handle_path).unwrap(); + } + }, + } + } + } + } + + Ok(()) +} + +fn lua_call_script_function(world: &mut World, stage_name: &str) -> anyhow::Result<()> { + let world_ptr = ScriptWorldPtr::from_ref(&world); + let mut host = world.get_resource_mut::(); + let mut contexts = world.get_resource_mut::>(); + let mut providers = world.get_resource_mut::>(); + + for (en, scripts) in world.view_iter::<(Entities, &ScriptList)>() { + for script in scripts.iter() { + let script_data = ScriptData { + name: script.name().to_string(), + script_id: script.id(), + entity: en, + }; + + if let Some(ctx) = contexts.get_context_mut(script.id()) { + trace!( + "Running '{}' function in script '{}'", + stage_name, + script.name() + ); + + match host.call_script( + world_ptr.clone(), + &script_data, + ctx, + &mut providers, + stage_name, + ) { + Ok(()) => {} + Err(e) => match e { + ScriptError::MluaError(m) => { + error!("Script '{}' ran into an error: {}", script.name(), m); + } + ScriptError::Other(_) => return Err(e.into()), + }, + } + } + } + } + + Ok(()) +} + +/// This system executes the 'on_update' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::Update' stage. +pub fn lua_script_update_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_update") +} + +/// This system executes the 'on_pre_update' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::PreUpdate' stage. +pub fn lua_script_pre_update_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_pre_update") +} + +/// This system executes the 'on_post_update' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::PostUpdate' stage. +pub fn lua_script_post_update_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_post_update") +} + +/// This system executes the 'on_first' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::First' stage. +pub fn lua_script_first_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_first") +} + +/// This system executes the 'on_last' function of lua scripts in the world. It is meant to run +/// during the 'GameStages::Last' stage. +pub fn lua_script_last_stage_system(world: &mut World) -> anyhow::Result<()> { + lua_call_script_function(world, "on_last") +} + +#[derive(Default)] +pub struct LuaScriptingPlugin; + +impl Plugin for LuaScriptingPlugin { + fn setup(&self, game: &mut lyra_game::game::Game) { + let world = game.world(); + + world.add_resource_default::(); + + world.add_resource_default::(); + world.add_resource_default::>(); + world.add_resource_default::>(); + + let mut loader = world + .try_get_resource_mut::() + .expect("Add 'ResourceManager' to the world before trying to add this plugin"); + loader.register_loader::(); + drop(loader); + + game.add_script_api_provider::(UtilityApiProvider); + game.add_script_api_provider::(LyraEcsApiProvider); + game.add_script_api_provider::(LyraMathApiProvider); + + game.add_system_to_stage( + GameStages::First, + "lua_create_contexts", + lua_scripts_create_contexts, + &[], + ) + .add_system_to_stage( + GameStages::First, + "lua_reload_scripts", + lua_scripts_reload_system, + &["lua_create_contexts"], + ) + .add_system_to_stage( + GameStages::First, + "lua_first_stage", + lua_script_first_stage_system, + &["lua_reload_scripts"], + ) + // cannot depend on 'lua_create_contexts' since it will cause a panic. + // the staged executor separates the executor of a single stage so this system + // cannot depend on the other one. + .add_system_to_stage( + GameStages::PreUpdate, + "lua_pre_update", + lua_script_pre_update_stage_system, + &[], + ) + .add_system_to_stage( + GameStages::Update, + "lua_update", + lua_script_update_stage_system, + &[], + ) + .add_system_to_stage( + GameStages::PostUpdate, + "lua_post_update", + lua_script_post_update_stage_system, + &[], + ) + .add_system_to_stage( + GameStages::Last, + "lua_last_stage", + lua_script_last_stage_system, + &[], + ); + } +} diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index f1875e5..2efd36a 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -164,24 +164,6 @@ impl mlua::UserData for ScriptWorldPtr { let reflect = ty .call_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) .expect("Type does not implement 'reflect_type' properly"); - - //if let Some(data) = reflect.data { - /* let res = reflect.reflect_branch.as_resource_unchecked(); - if let Some(res_ptr) = res.reflect_arc(this.as_mut()) { - let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) - .unwrap(); - let proxy = reg_type.get_data::() - .expect("Type does not have ReflectLuaProxy as a TypeData"); - - //let res = Arc::new(RwLock::new(data.as_any_box())) - (proxy.fn_as_uservalue_ref)(lua, res_ptr) - .and_then(|ud| ud.into_lua(lua)) - - //Ok(mlua::Value::Nil) - } else { - Ok(mlua::Value::Nil) - } */ - let res = reflect.reflect_branch.as_resource_unchecked(); if let Some(res_ptr) = res.reflect_ptr(this.as_mut()) { -- 2.40.1 From f2f8248de736abf0f54acef18e077343a0b87faa Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Wed, 14 Feb 2024 20:33:07 -0500 Subject: [PATCH 18/41] scripting: add elua as a git submodule, remove mlua from the cargo.toml --- .gitmodules | 3 +++ Cargo.lock | 37 +++++++++++-------------------------- lyra-scripting/Cargo.toml | 5 +++-- lyra-scripting/elua | 1 + 4 files changed, 18 insertions(+), 28 deletions(-) create mode 100644 .gitmodules create mode 160000 lyra-scripting/elua diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c181ae1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lyra-scripting/elua"] + path = lyra-scripting/elua + url = git@git.seanomik.net:SeanOMik/elua.git diff --git a/Cargo.lock b/Cargo.lock index ad8a285..593b602 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,16 +432,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "bstr" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.14.0" @@ -742,6 +732,14 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "elua" +version = "0.1.0" +dependencies = [ + "mlua-sys", + "thiserror", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1608,13 +1606,13 @@ name = "lyra-scripting" version = "0.1.0" dependencies = [ "anyhow", + "elua", "itertools 0.12.0", "lyra-ecs", "lyra-game", "lyra-reflect", "lyra-resource", "lyra-scripting-derive", - "mlua", "thiserror", "tracing", "tracing-subscriber", @@ -1713,24 +1711,11 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "mlua" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c81f8ac20188feb5461a73eabb22a34dd09d6d58513535eb587e46bff6ba250" -dependencies = [ - "bstr", - "mlua-sys", - "num-traits", - "once_cell", - "rustc-hash", -] - [[package]] name = "mlua-sys" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc29228347d6bdc9e613dc95c69df2817f755434ee0f7f3b27b57755fe238b7f" +checksum = "2847b42764435201d8cbee1f517edb79c4cca4181877b90047587c89e1b7bce4" dependencies = [ "cc", "cfg-if", diff --git a/lyra-scripting/Cargo.toml b/lyra-scripting/Cargo.toml index 320ac06..2782c56 100644 --- a/lyra-scripting/Cargo.toml +++ b/lyra-scripting/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [features] default = ["lua"] -lua = ["dep:mlua"] +lua = ["dep:elua"] [dependencies] lyra-scripting-derive = { path = "lyra-scripting-derive" } @@ -20,7 +20,8 @@ anyhow = "1.0.77" tracing = "0.1.37" # enabled with lua feature -mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? +#mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? +elua = { path = "./elua", optional = true } itertools = "0.12.0" diff --git a/lyra-scripting/elua b/lyra-scripting/elua new file mode 160000 index 0000000..5694d39 --- /dev/null +++ b/lyra-scripting/elua @@ -0,0 +1 @@ +Subproject commit 5694d395abbbce340eda195c99104db3e9c9435a -- 2.40.1 From 4247c4f5c153e75012684a28760a7a599d95d8a7 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 17 Feb 2024 14:27:16 -0500 Subject: [PATCH 19/41] scripting: switch to elua, create LuaTableProxy for proxying rust types into and from lua tables --- examples/testbed/scripts/test.lua | 47 ++- examples/testbed/src/main.rs | 6 +- lyra-game/src/game.rs | 2 +- lyra-scripting/elua | 2 +- lyra-scripting/scripts/lua/math/quat.lua | 7 +- lyra-scripting/scripts/lua/math/transform.lua | 15 +- lyra-scripting/scripts/lua/math/vec3.lua | 64 ++-- lyra-scripting/src/host.rs | 6 +- lyra-scripting/src/lua/dynamic_iter.rs | 27 +- lyra-scripting/src/lua/mod.rs | 93 ++++-- lyra-scripting/src/lua/providers/ecs.rs | 2 +- lyra-scripting/src/lua/providers/math.rs | 119 ++++++- lyra-scripting/src/lua/providers/util.rs | 87 ++--- lyra-scripting/src/lua/proxy.rs | 136 ++++++-- lyra-scripting/src/lua/script.rs | 45 ++- lyra-scripting/src/lua/test.rs | 177 ---------- lyra-scripting/src/lua/world.rs | 316 ++++++++++-------- lyra-scripting/src/lua/wrappers/delta_time.rs | 35 +- lyra-scripting/src/lua/wrappers/math.rs | 24 +- 19 files changed, 677 insertions(+), 533 deletions(-) delete mode 100644 lyra-scripting/src/lua/test.rs diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index ffc46c5..2bbbf6d 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -12,18 +12,51 @@ function on_pre_update() print("Lua's pre-update function was called") end ]] +--- +---Recursively dumps a table as a string. +--- +---@param obj table +---@return string +---@nodiscard +function dump_table(obj) + if type(obj) == 'table' then + local s = '{ ' + for k,v in pairs(obj) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. dump_table(v) .. ',' + end + return s .. '} ' + else + return tostring(obj) + end +end + function on_update() --print("Lua's update function was called") - local dt = world:resource(DeltaTime) - --print("DeltaTime was " .. tostring(dt) .. "s") + --local v = Vec3:new(50, 10, 20) + --print("Vec3 = " .. tostring(v)) - world:view(function (t) - --print("Found entity at a really cool place: " .. tostring(t)) - t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) + world:view( + ---@param t Transform + function (t) + print("Found entity at a really cool place: " .. tostring(t)) + t.translation:move_by(0, 0.001, 0) - return t - end, Transform) + return t + end, + Transform + ) + +-- local dt = world:resource(DeltaTime) +-- --print("DeltaTime was " .. tostring(dt) .. "s") +-- +-- world:view(function (t) +-- --print("Found entity at a really cool place: " .. tostring(t)) +-- t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) +-- +-- return t +-- end, Transform) end --[[ function on_post_update() diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index 0dabc2f..f9fa6d3 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -84,16 +84,16 @@ async fn main() { let mut resman = world.get_resource_mut::(); //let diffuse_texture = resman.request::("assets/happy-tree.png").unwrap(); - let antique_camera_model = resman.request::("assets/AntiqueCamera.glb").unwrap(); + //let antique_camera_model = resman.request::("assets/AntiqueCamera.glb").unwrap(); //let cube_model = resman.request::("assets/cube-texture-bin.glb").unwrap(); let cube_model = resman.request::("assets/texture-sep/texture-sep.gltf").unwrap(); let crate_model = resman.request::("assets/crate/crate.gltf").unwrap(); drop(resman); - world.spawn(( + /* world.spawn(( ModelComponent(antique_camera_model), Transform::from_xyz(0.0, -5.0, -10.0), - )); + )); */ { let cube_tran = Transform::from_xyz(-3.5, 0.0, -8.0); diff --git a/lyra-game/src/game.rs b/lyra-game/src/game.rs index a3d8666..0a49e45 100755 --- a/lyra-game/src/game.rs +++ b/lyra-game/src/game.rs @@ -342,7 +342,7 @@ impl Game { .with(fmt::layer().with_writer(stdout_layer)) .with(filter::Targets::new() // done by prefix, so it includes all lyra subpackages - .with_target("lyra", Level::DEBUG) + .with_target("lyra", Level::TRACE) .with_target("wgpu", Level::WARN) .with_default(Level::INFO)) .init(); diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 5694d39..35bd889 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 5694d395abbbce340eda195c99104db3e9c9435a +Subproject commit 35bd889b4829446f972292440174e7c0e4fa40a2 diff --git a/lyra-scripting/scripts/lua/math/quat.lua b/lyra-scripting/scripts/lua/math/quat.lua index c6aca90..b268834 100644 --- a/lyra-scripting/scripts/lua/math/quat.lua +++ b/lyra-scripting/scripts/lua/math/quat.lua @@ -1,3 +1,8 @@ +---@class Quat +---@field x number +---@field y number +---@field z number +---@field w number Quat = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 } Quat.__index = Quat Quat.__name = "Quat" @@ -22,7 +27,7 @@ end Quat.IDENTITY = Quat:new(0, 0, 0, 1) -function Quat:copy() +function Quat:clone() return Quat:new(self.x, self.y, self.z, self.w) end diff --git a/lyra-scripting/scripts/lua/math/transform.lua b/lyra-scripting/scripts/lua/math/transform.lua index 76273e2..7f3eb14 100644 --- a/lyra-scripting/scripts/lua/math/transform.lua +++ b/lyra-scripting/scripts/lua/math/transform.lua @@ -1,6 +1,7 @@ ---require("math.quat") ---require("math.vec3") - +---@class Transform +---@field translation Vec3 +---@field rotation Quat +---@field Scale Vec3 Transform = { translation = Vec3.ZERO, rotation = Quat.IDENTITY, scale = Vec3.ONE } Transform.__index = Transform Transform.__name = "Transform" @@ -16,14 +17,14 @@ function Transform:new(translation, rotation, scale) return t end -function Transform:copy() - return Transform:new(self.translation:copy(), self.rotation:copy(), self.scale:copy()) +function Transform:clone() + return Transform:new(self.translation:clone(), self.rotation:clone(), self.scale:clone()) end --- Creates a new Transform with the translation at the vec3 --- @param pos Vec3 function Transform:from_vec3(pos) - local t = Transform:copy() -- copy of default transform + local t = Transform:clone() -- copy of default transform t.translation = pos return t end @@ -81,7 +82,7 @@ end --- @param alpha number --- @return Transform function Transform:lerp(rhs, alpha) - local res = self:copy() + local res = self:clone() res.translation = self.translation:lerp(rhs.translation, alpha) res.rotation = self.rotation:lerp(rhs.rotation, alpha) res.scale = self.scale:lerp(rhs.scale, alpha) diff --git a/lyra-scripting/scripts/lua/math/vec3.lua b/lyra-scripting/scripts/lua/math/vec3.lua index cf88af6..5b5ff44 100644 --- a/lyra-scripting/scripts/lua/math/vec3.lua +++ b/lyra-scripting/scripts/lua/math/vec3.lua @@ -1,3 +1,7 @@ +---@class Vec3 +---@field x number +---@field y number +---@field z number Vec3 = { x = 0.0, y = 0.0, z = 0.0 } Vec3.__index = Vec3 Vec3.__name = "Vec3" @@ -14,11 +18,13 @@ function Vec3:new(x, y, z) v.x = x v.y = y v.z = z - + return v end -function Vec3:copy() +---Creates a copy of self +---@return Vec3 +function Vec3:clone() return Vec3:new(self.x, self.y, self.z) end @@ -49,9 +55,10 @@ Vec3.ZERO = Vec3:new(0, 0, 0) Vec3.ONE = Vec3:new(1, 1, 1) --- Computes the absolute value of `self`. ----@return Vec3 function Vec3:abs() - return Vec3:new(math.abs(self.x), math.abs(self.y), math.abs(self.z)) + self.x = math.abs(self.x) + self.y = math.abs(self.y) + self.z = math.abs(self.z) end --- Computes the length of `self`. @@ -60,6 +67,16 @@ function Vec3:length() return math.sqrt(self:dot(self)) end +---Moves `self` by the provided coordinates +---@param x number +---@param y number +---@param z number +function Vec3:move_by(x, y, z) + self.x = self.x + x + self.y = self.y + y + self.z = self.z + z +end + --- Computes the dot product of `self` and `rhs`. ---@param rhs Vec3 ---@return number @@ -80,11 +97,12 @@ function Vec3:min(rhs) return Vec3:new(x, y, z) end ---- Returns `self` normalized to a length 1. ----@return unknown +--- Modifies `self` to be normalized to a length 1. function Vec3:normalize() local len_recip = 1.0 / self:length() - return self * len_recip + self.x = self.x * len_recip + self.y = self.y * len_recip + self.z = self.z * len_recip end --- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`. @@ -97,40 +115,48 @@ function Vec3:lerp(rhs, alpha) -- ensure alpha is [0, 1] local alpha = math.max(0, math.min(1, alpha)) - local res = self:copy() + local res = self:clone() res = res + ((rhs - res) * alpha) return res end function Vec3:__add(rhs) - return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) + if type(rhs) == "Vec3" then + return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) + else + return Vec3:new(self.x + rhs, self.y + rhs, self.z + rhs) + end end function Vec3:__sub(rhs) - return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) + if type(rhs) == "Vec3" then + return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) + else + return Vec3:new(self.x - rhs, self.y - rhs, self.z - rhs) + end end function Vec3:__mul(rhs) - if type(rhs) == "number" then - return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs) - else + if type(rhs) == "Vec3" then return Vec3:new(self.x * rhs.x, self.y * rhs.y, self.z * rhs.z) + else + return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs) end end function Vec3:__div(rhs) - if type(rhs) == "number" then - return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs) - else + if type(rhs) == "Vec3" then return Vec3:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z) + else + return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs) end end function Vec3:__idiv(rhs) - if type(rhs) == "number" then - return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs) - else + if type(rhs) == "Vec3" then return Vec3:new(self.x // rhs.x, self.y // rhs.y, self.z // rhs.z) + else + return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs) end end diff --git a/lyra-scripting/src/host.rs b/lyra-scripting/src/host.rs index 288a5f2..bcca5ba 100644 --- a/lyra-scripting/src/host.rs +++ b/lyra-scripting/src/host.rs @@ -8,15 +8,15 @@ use crate::ScriptWorldPtr; pub enum ScriptError { #[error("{0}")] #[cfg(feature = "lua")] - MluaError(mlua::Error), + MluaError(elua::Error), #[error("{0}")] Other(anyhow::Error), } #[cfg(feature = "lua")] -impl From for ScriptError { - fn from(value: mlua::Error) -> Self { +impl From for ScriptError { + fn from(value: elua::Error) -> Self { ScriptError::MluaError(value) } } diff --git a/lyra-scripting/src/lua/dynamic_iter.rs b/lyra-scripting/src/lua/dynamic_iter.rs index 3834f39..7b8f250 100644 --- a/lyra-scripting/src/lua/dynamic_iter.rs +++ b/lyra-scripting/src/lua/dynamic_iter.rs @@ -134,9 +134,9 @@ impl Iterator for DynamicViewIter { #[cfg(feature = "lua")] pub struct ReflectedItem<'a> { - pub proxy: &'a ReflectLuaProxy, + //pub proxy: &'a ReflectLuaProxy, pub comp_ptr: NonNull, - pub comp_ud: mlua::AnyUserData<'a>, + pub comp_val: elua::Value<'a>, } #[cfg(feature = "lua")] @@ -153,7 +153,11 @@ pub struct ReflectedIterator { impl ReflectedIterator { #[cfg(feature = "lua")] - pub fn next_lua<'a>(&mut self, lua: &'a mlua::Lua) -> Option> { + pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option> { + use elua::AsLua; + + use super::ReflectedLuaTableProxy; + let n = self.dyn_view.next(); @@ -172,17 +176,20 @@ impl ReflectedIterator { let reg_type = reflected_components.get_type(id) .expect("Requested type was not found in TypeRegistry"); - let proxy = reg_type.get_data::() - .expect("Type does not have ReflectLuaProxy as a TypeData"); - - let userdata = (proxy.fn_as_uservalue)(lua, d.ptr).unwrap(); + let value = if let Some(proxy) = reg_type.get_data::() { + (proxy.fn_as_uservalue)(lua, d.ptr).unwrap() + .as_lua(lua).unwrap() + } else if let Some(proxy) = reg_type.get_data::() { + (proxy.fn_as_table)(lua, d.ptr.cast()).unwrap() + .as_lua(lua).unwrap() + } else { + panic!("Type does not have ReflectLuaProxy or ReflectedLuaTableProxy as a TypeData"); + }; dynamic_row.push(ReflectedItem { - proxy, comp_ptr: d.ptr, - comp_ud: userdata + comp_val: value }); - //dynamic_row.push(( (proxy, d.ptr), userdata)); } let row = ReflectedRow { diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index cc2a366..cf11dc6 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -2,6 +2,7 @@ pub mod dynamic_iter; pub use dynamic_iter::*; pub mod world; +use elua::FromLua; pub use world::*; pub mod script; @@ -19,19 +20,14 @@ pub use proxy::*; pub mod system; pub use system::*; -#[cfg(test)] -mod test; - use std::{any::TypeId, sync::Mutex}; use lyra_ecs::{ - DynamicBundle, World, + Component, ComponentInfo, DynamicBundle, World }; use lyra_reflect::{FromType, Reflect, TypeRegistry}; -use mlua::{AnyUserDataExt, Lua}; - -pub type LuaContext = Mutex; +pub type LuaContext = Mutex; pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; @@ -40,21 +36,28 @@ use crate::{ScriptBorrow, ScriptDynamicBundle}; /// A trait used for registering a Lua type with the world. pub trait RegisterLuaType { - /// Register a type to lua that **is not wrapped**. + /// Register a type to Lua that **is not wrapped**. fn register_lua_type<'a, T>(&mut self) where - T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData; + T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata; - /// Registers a wrapped lua type. + /// Registers a type to Lua that is wrapped another type. + /// This would be used for something like `UserdataRef`. fn register_lua_wrapper<'a, W>(&mut self) where - W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData; + W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata; + + /// Registers a type to Lua that implements [`elua::TableProxy`] + fn register_lua_table_proxy<'a, T, W>(&mut self) + where + T: elua::TableProxy + 'static, + W: Component; } impl RegisterLuaType for World { fn register_lua_type<'a, T>(&mut self) where - T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData + T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata { let mut registry = self.get_resource_mut::(); @@ -67,44 +70,64 @@ impl RegisterLuaType for World { fn register_lua_wrapper<'a, W>(&mut self) where - W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua<'a> + mlua::UserData + W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata { let mut registry = self.get_resource_mut::(); let reg_type = registry.get_type_or_default(W::wrapped_type_id()); reg_type.add_data(>::from_type()); } + + fn register_lua_table_proxy<'a, T, C>(&mut self) + where + T: elua::TableProxy + 'static, + C: Component + { + let mut registry = self.get_resource_mut::(); + + let type_id = TypeId::of::(); + let reg_type = registry.get_type_or_default(TypeId::of::()); + reg_type.add_data(>::from_type()); + drop(registry); + + let mut lookup = self.get_resource_or_else::(LuaTableProxyLookup::default); + lookup.typeid_from_name.insert(T::table_name(), TypeId::of::()); + + let mut info = ComponentInfo::new::(); + lookup.comp_info_from_name.insert(T::table_name(), info); + } } -impl<'lua> mlua::FromLua<'lua> for ScriptBorrow { - fn from_lua(value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { +impl<'lua> elua::FromLua<'lua> for ScriptBorrow { + fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), _ => unreachable!(), } } } -impl mlua::UserData for ScriptBorrow {} - -pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow { - ud.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) - .expect("Type does not implement '__internal_reflect' properly") +impl<'lua> elua::FromLuaVec<'lua> for ScriptBorrow { + fn from_lua_value_vec(state: &'lua elua::State, mut values: elua::ValueVec<'lua>) -> elua::Result { + if let Some(v) = values.pop_front() { + ScriptBorrow::from_lua(state, v) + } else { + Err(elua::Error::Nil) + } + } } -impl mlua::UserData for ScriptDynamicBundle { - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new()))); - - methods.add_method_mut("push", |_, this, (comp,): (mlua::AnyUserData,)| { - let script_brw = comp.call_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?; - 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(()) - }); +impl elua::Userdata for ScriptBorrow { + fn name() -> String { + "ScriptBorrow".to_string() } + + fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + Ok(()) + } +} + +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") } \ 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 446688c..7b0a0be 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -13,7 +13,7 @@ impl ScriptApiProvider for LyraEcsApiProvider { fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); - let globals = ctx.globals(); + let globals = ctx.globals()?; globals.set("World", ctx.create_proxy::()?)?; globals.set("DynamicBundle", ctx.create_proxy::()?)?; diff --git a/lyra-scripting/src/lua/providers/math.rs b/lyra-scripting/src/lua/providers/math.rs index f95d745..c8118c9 100644 --- a/lyra-scripting/src/lua/providers/math.rs +++ b/lyra-scripting/src/lua/providers/math.rs @@ -1,8 +1,8 @@ +use elua::{TableProxy, Userdata}; use lyra_ecs::World; -use lyra_game::math; +use lyra_game::math::{self, Quat, Transform, Vec3}; use crate::ScriptData; use crate::lua::RegisterLuaType; -use crate::lua::wrappers::{LuaVec3, LuaTransform}; use crate::{ScriptApiProvider, lua::LuaContext}; @@ -13,26 +13,31 @@ impl ScriptApiProvider for LyraMathApiProvider { type ScriptContext = LuaContext; fn prepare_world(&mut self, world: &mut World) { - world.register_lua_wrapper::(); - world.register_lua_wrapper::(); + // TODO + /* world.register_lua_wrapper::(); + world.register_lua_wrapper::(); */ + world.register_lua_table_proxy::(); + world.register_lua_table_proxy::(); + world.register_lua_table_proxy::(); } - fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn expose_api(&mut self, _data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); - /* let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua"); - ctx.load(bytes.to_vec()).exec()?; + let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua"); + ctx.load("lyra/math/vec2.lua", bytes.as_slice())?.execute(())?; let bytes = include_bytes!("../../../scripts/lua/math/quat.lua"); - ctx.load(bytes.to_vec()).exec()?; + ctx.load("lyra/math/quat.lua", bytes.as_slice())?.execute(())?; let bytes = include_bytes!("../../../scripts/lua/math/transform.lua"); - ctx.load(bytes.to_vec()).exec()?; */ + ctx.load("lyra/math/transform.lua", bytes.as_slice())?.execute(())?; - let globals = ctx.globals(); - globals.set("Vec3", ctx.create_proxy::()?)?; - globals.set("Transform", ctx.create_proxy::()?)?; - //globals.set("Vec3", LuaVec3(math::Vec3::ZERO).into_lua(&ctx)?)?; + // TODO + //let globals = ctx.globals()?; + //globals.set("Vec3", elua::Proxy::::from(LuaVec3(Vec3::ZERO)))?; + //globals.set("Vec3", ctx.create_proxy::()?)?; + //globals.set("Transform", ctx.create_proxy::()?)?; Ok(()) } @@ -46,6 +51,94 @@ impl ScriptApiProvider for LyraMathApiProvider { } } +#[derive(Clone, Copy)] +struct LuaVec3(Vec3); + +impl TableProxy for LuaVec3 { + fn from_table<'a>(_: &'a elua::State, table: elua::Table<'a>) -> elua::Result { + let x: f32 = table.get("x")?; + let y: f32 = table.get("y")?; + let z: f32 = table.get("z")?; + + Ok(LuaVec3(Vec3 { + x, + y, + z, + })) + } + + fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { + let globals = state.globals()?; + let vec3: elua::Table = globals.get("Vec3")?; + let new_fn: elua::Function = vec3.get("new")?; + new_fn.exec((vec3, self.0.x, self.0.y, self.0.z)) + } + + fn table_name() -> String { + "Vec3".to_string() + } +} + +#[derive(Clone, Copy)] +struct LuaQuat(Quat); + +impl TableProxy for LuaQuat { + fn from_table<'a>(_: &'a elua::State, table: elua::Table<'a>) -> elua::Result { + let x: f32 = table.get("x")?; + let y: f32 = table.get("y")?; + let z: f32 = table.get("z")?; + let w: f32 = table.get("w")?; + + Ok(LuaQuat(Quat::from_xyzw(x, y, z, w))) + } + + fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { + let globals = state.globals()?; + let quat: elua::Table = globals.get("Quat")?; + let new_fn: elua::Function = quat.get("new")?; + new_fn.exec((quat, self.0.x, self.0.y, self.0.z, self.0.w)) + } + + fn table_name() -> String { + "Quat".to_string() + } +} + +#[derive(Clone, Copy)] +struct LuaTransform(Transform); + +impl TableProxy for LuaTransform { + fn from_table<'a>(lua: &'a elua::State, table: elua::Table<'a>) -> elua::Result { + let translation: elua::Table = table.get("translation")?; + let rotation: elua::Table = table.get("rotation")?; + let scale: elua::Table = table.get("scale")?; + + let translation = LuaVec3::from_table(lua, translation)?; + let rotation = LuaQuat::from_table(lua, rotation)?; + let scale = LuaVec3::from_table(lua, scale)?; + + Ok(LuaTransform(Transform::new(translation.0, rotation.0, scale.0))) + } + + fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { + let globals = state.globals()?; + let transform: elua::Table = globals.get("Transform")?; + let new_fn: elua::Function = transform.get("new")?; + + let translation = LuaVec3(self.0.translation).as_table(state)?; + let rotation = LuaQuat(self.0.rotation).as_table(state)?; + let scale = LuaVec3(self.0.scale).as_table(state)?; + + new_fn.exec((transform, translation, rotation, scale)) + } + + fn table_name() -> String { + "Transform".to_string() + } + + +} + /* #[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)] pub struct LuaVec3(#[reflect(skip)] math::Vec3); diff --git a/lyra-scripting/src/lua/providers/util.rs b/lyra-scripting/src/lua/providers/util.rs index e1277ee..905f123 100644 --- a/lyra-scripting/src/lua/providers/util.rs +++ b/lyra-scripting/src/lua/providers/util.rs @@ -17,47 +17,46 @@ use crate::{ScriptApiProvider, ScriptData}; pub struct UtilityApiProvider; impl ScriptApiProvider for UtilityApiProvider { - type ScriptContext = Mutex; + type ScriptContext = Mutex; fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); - fn printf(lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic)) -> mlua::Result<()> { + //fn printf(lua: &elua::State, (mut text, formats): (String, elua::Variadic)) -> elua::Result<()> { + let printf = |lua: &elua::State, (mut text, formats): (String, elua::Variadic)| { let mut formatted = String::new(); let mut arg_num = 0; while let Some(start) = text.find("{}") { let val_str = match formats.get(arg_num) { Some(v) => match v { - mlua::Value::Nil => "nil".to_string(), - mlua::Value::Boolean(b) => b.to_string(), - mlua::Value::LightUserData(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of LightUserData"))); + elua::Value::Nil => "nil".to_string(), + elua::Value::Boolean(b) => b.to_string(), + elua::Value::Number(n) => n.to_string(), + elua::Value::String(s) => s.clone(), + elua::Value::Table(_) => { + return Err(elua::Error::runtime("unable to get string representation of Table")); }, - mlua::Value::Integer(i) => i.to_string(), - mlua::Value::Number(n) => n.to_string(), - mlua::Value::String(s) => s.to_str().unwrap().to_string(), - mlua::Value::Table(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Table"))); + elua::Value::Function(_) => { + return Err(elua::Error::runtime("unable to get string representation of Function")); }, - mlua::Value::Function(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Function"))); + elua::Value::Thread(_) => { + return Err(elua::Error::runtime("unable to get string representation of Thread")); }, - mlua::Value::Thread(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Thread"))); - }, - mlua::Value::UserData(ud) => { - let metatable = ud.get_metatable()?; - if let Ok(tos) = metatable.get::(mlua::MetaMethod::ToString) { - tos.call::<_, String>((ud.clone(),))? + elua::Value::Userdata(ud) => { + if let Ok(tos) = ud.get::<_, elua::Function>(elua::MetaMethod::ToString) { + tos.exec::<_, String>(())? } else { - return Err(mlua::Error::RuntimeError(format!("UserData does not implement MetaMethod '__tostring'"))); + return Err(elua::Error::runtime("UserData does not implement MetaMethod '__tostring'")); } }, - mlua::Value::Error(e) => e.to_string(), + elua::Value::None => "None".to_string(), + elua::Value::Multi(v) => { + return Err(elua::Error::runtime("unable to get string representation of ValueVec")); + }, }, None => { - let got_args = arg_num; + let got_args = arg_num;// - 1; // continue searching for {} to get the number of format spots for the error message. while let Some(start) = text.find("{}") { @@ -65,13 +64,15 @@ impl ScriptApiProvider for UtilityApiProvider { arg_num += 1; } - return Err(mlua::Error::BadArgument { - to: Some("printf".to_string()), - pos: 2, - name: Some("...".to_string()), - cause: Arc::new(mlua::Error::RuntimeError(format!("not enough args \ + return Err(elua::Error::BadArgument { + func: Some("printf".to_string()), + arg_index: 2, + arg_name: Some("fmt...".to_string()), + error: Arc::new(elua::Error::Runtime(format!( + "not enough args \ given for the amount of format areas in the string. Expected {}, \ - got {}.", arg_num, got_args))) + got {}.", arg_num, got_args + ))) }) }, }; @@ -84,29 +85,31 @@ impl ScriptApiProvider for UtilityApiProvider { } if arg_num < formats.len() { - return Err(mlua::Error::BadArgument { - to: Some("printf".to_string()), - pos: 2, - name: Some("...".to_string()), - cause: Arc::new(mlua::Error::RuntimeError(format!("got more args \ - than format areas in the string. Expected {}, got {}.", formats.len(), arg_num))) + return Err(elua::Error::BadArgument { + func: Some("printf".to_string()), + arg_index: 2, + arg_name: Some("fmt...".to_string()), + error: Arc::new(elua::Error::Runtime(format!( + "got more args \ + than format areas in the string. Expected {}, got {}.", formats.len(), arg_num + ))) }) } formatted = format!("{}{}", formatted, text); - lua.globals() - .get::<_, mlua::Function>("print")? - .call::<_, ()>(formatted)?; + lua.globals()? + .get::<_, elua::Function>("print")? + .exec::<_, ()>(formatted)?; Ok(()) - } + }; - let script_name_reg = ctx.create_registry_value(data.name.clone())?; + let script_name_reg = ctx.registry_insert(data.name.clone())?; let printf_func = ctx.create_function(printf)?; let print_func = ctx.create_function(move |lua, text: String| { - let name = lua.registry_value::(&script_name_reg)?; + let name = lua.registry_get::(script_name_reg)?; let _span = debug_span!("lua", script = &name).entered(); debug!(target: "lyra_scripting::lua", "{}", text); @@ -114,7 +117,7 @@ impl ScriptApiProvider for UtilityApiProvider { Ok(()) })?; - let globals = ctx.globals(); + let globals = ctx.globals()?; globals.set("printf", printf_func)?; globals.set("print", print_func)?; diff --git a/lyra-scripting/src/lua/proxy.rs b/lyra-scripting/src/lua/proxy.rs index e3a79de..5980814 100644 --- a/lyra-scripting/src/lua/proxy.rs +++ b/lyra-scripting/src/lua/proxy.rs @@ -1,46 +1,51 @@ -use std::{any::TypeId, ptr::NonNull}; +use std::{any::TypeId, collections::HashMap, ptr::NonNull}; +use elua::{FromLua, TableProxy}; +use lyra_ecs::{ComponentInfo, DynamicBundle}; use lyra_reflect::{Reflect, FromType}; -use crate::ScriptDynamicBundle; +use crate::{ScriptBorrow, ScriptDynamicBundle}; + +use super::FN_NAME_INTERNAL_REFLECT; pub trait LuaWrapper { /// The type id of the wrapped type. fn wrapped_type_id() -> TypeId; } +/// A trait that used to convert something into lua, or to set something to a value from lua. pub trait LuaProxy { fn as_lua_value<'lua>( - lua: &'lua mlua::Lua, + lua: &'lua elua::State, this: &dyn Reflect, - ) -> mlua::Result>; + ) -> elua::Result>; fn apply( - lua: &mlua::Lua, + lua: &elua::State, this: &mut dyn Reflect, - apply: &mlua::AnyUserData, - ) -> mlua::Result<()>; + apply: &elua::AnyUserdata, + ) -> elua::Result<()>; } impl<'a, T> LuaProxy for T where - T: Reflect + Clone + mlua::FromLua<'a> + mlua::UserData + T: Reflect + Clone + elua::FromLua<'a> + elua::Userdata { fn as_lua_value<'lua>( - lua: &'lua mlua::Lua, + lua: &'lua elua::State, this: &dyn Reflect, - ) -> mlua::Result> { + ) -> elua::Result> { let this = this.as_any().downcast_ref::().unwrap(); lua.create_userdata(this.clone()) } fn apply( - _lua: &mlua::Lua, + _: &elua::State, this: &mut dyn Reflect, - apply: &mlua::AnyUserData, - ) -> mlua::Result<()> { + apply: &elua::AnyUserdata, + ) -> elua::Result<()> { let this = this.as_any_mut().downcast_mut::().unwrap(); - let apply = apply.borrow::()?; + let apply = apply.as_ref::()?; *this = apply.clone(); @@ -48,24 +53,66 @@ where } } +/// A struct that is used for retrieving rust type ids of types that implement `TableProxy`. +#[derive(Default)] +pub struct LuaTableProxyLookup { + pub(crate) typeid_from_name: HashMap, + pub(crate) comp_info_from_name: HashMap, +} + +/// A struct used for converting types that implement `TableProxy` to and from Lua. +#[derive(Clone)] +pub struct ReflectedLuaTableProxy { + pub table_name: String, + pub fn_as_table: + for<'a> fn(lua: &'a elua::State, this_ptr: NonNull<()>) -> elua::Result>, + pub fn_apply: for<'a> fn( + lua: &'a elua::State, + this_ptr: NonNull<()>, + table: &'a elua::Table<'a>, + ) -> elua::Result<()>, +} + +impl<'a, T> FromType for ReflectedLuaTableProxy +where + T: TableProxy +{ + fn from_type() -> Self { + Self { + table_name: T::table_name(), + fn_as_table: |lua, this| -> elua::Result { + let this = unsafe { this.cast::().as_ref() }; + this.as_table(lua) + }, + fn_apply: |lua, ptr, table| { + let this = unsafe { ptr.cast::().as_mut() }; + let tbl = T::from_table(lua, table.clone())?; + *this = tbl; + Ok(()) + }, + } + } +} + +/// A struct used for converting types that implement `LuaProxy` to and from Lua. #[derive(Clone)] pub struct ReflectLuaProxy { pub fn_as_uservalue: - for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull) -> mlua::Result>, + for<'a> fn(lua: &'a elua::State, this_ptr: NonNull) -> elua::Result>, pub fn_apply: for<'a> fn( - lua: &'a mlua::Lua, + lua: &'a elua::State, this_ptr: NonNull, - apply: &'a mlua::AnyUserData<'a>, - ) -> mlua::Result<()>, + apply: &'a elua::AnyUserdata<'a>, + ) -> elua::Result<()>, } impl<'a, T> FromType for ReflectLuaProxy where - T: Reflect + LuaProxy + Clone + mlua::FromLua<'a> + mlua::UserData + T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata { fn from_type() -> Self { Self { - fn_as_uservalue: |lua, this| -> mlua::Result { + fn_as_uservalue: |lua, this| -> elua::Result { let this = unsafe { this.cast::().as_ref() }; ::as_lua_value(lua, this) }, @@ -77,16 +124,45 @@ where } } -impl<'lua> mlua::FromLua<'lua> for ScriptDynamicBundle { - fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { - match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), - mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { - from: "Nil", - to: "DynamicBundle", - message: Some("Value was nil".to_string()), - }), - _ => panic!(), +impl<'lua> elua::FromLua<'lua> for ScriptDynamicBundle { + fn from_lua(_: &'lua elua::State, val: elua::Value<'lua>) -> elua::Result { + match val { + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), + elua::Value::Nil => Err(elua::Error::Nil), + _ => unreachable!(), } } +} + +impl<'lua> elua::FromLuaVec<'lua> for ScriptDynamicBundle { + fn from_lua_value_vec(state: &'lua elua::State, mut values: elua::ValueVec<'lua>) -> elua::Result { + if let Some(v) = values.pop_front() { + Ok(ScriptDynamicBundle::from_lua(state, v)?) + } else { + Err(elua::Error::Nil) + } + } +} + +impl elua::Userdata for ScriptDynamicBundle { + fn name() -> String { + "Bundle".to_string() + } + + fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + builder + .function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new()))) + .method_mut("push", |_, this, comp: elua::AnyUserdata| { + let script_brw = comp.execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())?; + 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(()) + }); + + Ok(()) + } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index c7a00ab..b0d1cc6 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -1,6 +1,6 @@ use std::sync::Mutex; -use mlua::IntoLua; +use elua::{AsLua, StdLibraries, StdLibrary}; use tracing::{debug, trace}; use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; @@ -8,40 +8,37 @@ use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; #[derive(Default)] pub struct LuaHost; -fn try_call_lua_function(lua: &mlua::Lua, fn_name: &str) -> Result<(), ScriptError> { - let globals = lua.globals(); - - match globals.get::<_, mlua::Function>(fn_name) { - Ok(init_fn) => { - init_fn.call(()) - .map_err(ScriptError::MluaError)?; - }, - Err(mlua::Error::FromLuaConversionError { from: "nil", to: "function", message: None }) => { - trace!("Function '{}' was not found, ignoring...", fn_name) - // ignore - }, - Err(e) => { - return Err(ScriptError::MluaError(e)); - }, +fn try_call_lua_function(lua: &elua::State, fn_name: &str) -> Result<(), ScriptError> { + let globals = lua.globals()?; + + if globals.has_key(fn_name)? { + let lua_fn = globals.get::<_, elua::Function>(fn_name)?; + lua_fn.exec(()) + .map_err(ScriptError::MluaError)?; } Ok(()) } impl ScriptHost for LuaHost { - type ScriptContext = Mutex; + type ScriptContext = Mutex; fn load_script(&mut self, script: &[u8], script_data: &crate::ScriptData, providers: &mut crate::ScriptApiProviders) -> Result { - let mut ctx = Mutex::new(mlua::Lua::new()); + let mut ctx = Mutex::new({ + let s = elua::State::new(); + s.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]); + s + }); + + //Mutex::new(elua::State::new_with_libraries(StdLibraries::all())); for provider in providers.apis.iter_mut() { provider.expose_api(script_data, &mut ctx)?; } let lua = ctx.lock().unwrap(); - lua.load(script) - .set_name(&script_data.name) - .exec() + lua.load(&script_data.name, script)? + .execute(()) .map_err(|e| ScriptError::MluaError(e))?; drop(lua); @@ -71,9 +68,9 @@ impl ScriptHost for LuaHost { let ctx = ctx.lock().expect("Failure to get Lua ScriptContext"); - let globals = ctx.globals(); - globals.set("world", world.into_lua(&ctx)?)?; - globals.set("entity", ScriptEntity(script_data.entity).into_lua(&ctx)?)?; + let globals = ctx.globals()?; + globals.set("world", world.as_lua(&ctx)?)?; + globals.set("entity", ScriptEntity(script_data.entity).as_lua(&ctx)?)?; try_call_lua_function(&ctx, function_name)?; diff --git a/lyra-scripting/src/lua/test.rs b/lyra-scripting/src/lua/test.rs deleted file mode 100644 index b30880d..0000000 --- a/lyra-scripting/src/lua/test.rs +++ /dev/null @@ -1,177 +0,0 @@ -use std::{sync::{Mutex, Arc}, ptr::NonNull}; - -use lyra_ecs::{World, system::{GraphExecutor, IntoSystem}}; -use lyra_resource::ResourceManager; -use mlua::{IntoLua, AnyUserDataExt}; -use tracing_subscriber::{layer::SubscriberExt, fmt, util::SubscriberInitExt}; - -use crate::{ScriptData, ScriptApiProvider, ScriptApiProviders, ScriptError, ScriptList, Script, ScriptContexts}; - -use super::{LuaHost, LuaLoader, LuaScript, lua_script_update_stage_system, lua_scripts_create_contexts, LuaContext}; - -fn enable_tracing() { - tracing_subscriber::registry() - .with(fmt::layer().with_writer(std::io::stdout)) - .init(); -} - -#[derive(Default)] -struct PrintfProvider; - -impl ScriptApiProvider for PrintfProvider { - type ScriptContext = Mutex; - - fn expose_api(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { - let ctx = ctx.lock().unwrap(); - - fn printf(lua: &mlua::Lua, (mut text, formats): (String, mlua::Variadic)) -> mlua::Result<()> { - let mut formatted = String::new(); - let mut arg_num = 0; - - while let Some(start) = text.find("{}") { - let val_str = match formats.get(arg_num) { - Some(v) => match v { - mlua::Value::Nil => "nil".to_string(), - mlua::Value::Boolean(b) => b.to_string(), - mlua::Value::LightUserData(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of LightUserData"))); - }, - mlua::Value::Integer(i) => i.to_string(), - mlua::Value::Number(n) => n.to_string(), - mlua::Value::String(s) => s.to_str().unwrap().to_string(), - mlua::Value::Table(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Table"))); - }, - mlua::Value::Function(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Function"))); - }, - mlua::Value::Thread(_) => { - return Err(mlua::Error::RuntimeError(format!("unable to get string representation of Thread"))); - }, - mlua::Value::UserData(ud) => { - if let Ok(tos) = ud.get::<_, mlua::Function>(mlua::MetaMethod::ToString.to_string()) { - tos.call::<_, String>(())? - } else { - return Err(mlua::Error::RuntimeError(format!("UserData does not implement MetaMethod '__tostring'"))); - } - }, - mlua::Value::Error(e) => e.to_string(), - }, - None => { - let got_args = arg_num;// - 1; - - // continue searching for {} to get the number of format spots for the error message. - while let Some(start) = text.find("{}") { - text = text[start + 2..].to_string(); - arg_num += 1; - } - - return Err(mlua::Error::BadArgument { - to: Some("printf".to_string()), - pos: 2, - name: Some("...".to_string()), - cause: Arc::new(mlua::Error::RuntimeError(format!("not enough args \ - given for the amount of format areas in the string. Expected {}, \ - got {}.", arg_num, got_args))) - }) - }, - }; - - formatted = format!("{}{}{}", formatted, &text[0..start], val_str); - - text = text[start + 2..].to_string(); - - arg_num += 1; - } - - if arg_num < formats.len() { - return Err(mlua::Error::BadArgument { - to: Some("printf".to_string()), - pos: 2, - name: Some("...".to_string()), - cause: Arc::new(mlua::Error::RuntimeError(format!("got more args \ - than format areas in the string. Expected {}, got {}.", formats.len(), arg_num))) - }) - } - - formatted = format!("{}{}", formatted, text); - - lua.globals() - .get::<_, mlua::Function>("print") - .unwrap() - .call::<_, ()>(formatted) - .unwrap(); - - Ok(()) - } - - let printf_func = ctx.create_function(printf).unwrap(); - - let globals = ctx.globals(); - globals.set("printf", printf_func).unwrap(); - - Ok(()) - } - - fn setup_script(&mut self, _data: &ScriptData, _ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { - Ok(()) - } - - fn update_script_environment(&mut self, world: crate::ScriptWorldPtr, _data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { - let ctx = ctx.lock().unwrap(); - let globals = ctx.globals(); - - let world_lua = world.into_lua(&ctx) - .map_err(ScriptError::MluaError)?; - globals.set("world", world_lua) - .map_err(ScriptError::MluaError)?; - - Ok(()) - } -} - -/// Tests a simple lua script that just prints some test -#[test] -pub fn lua_print() { - enable_tracing(); - - let mut world = World::new(); - - let test_provider = PrintfProvider::default(); - let mut providers = ScriptApiProviders::::default(); - providers.add_provider(test_provider); - - let host = LuaHost::default(); - - world.add_resource(host); - world.add_resource(providers); - world.add_resource(ScriptContexts::::default()); - - let mut res_loader = ResourceManager::new(); - res_loader.register_loader::(); - - let script = - r#" - print("Hello World") - - function update() - print("updated") - printf("I love to eat formatted {}!", "food") - --printf("World is {}", world) - end - "#; - let script = script.as_bytes(); - - let script = res_loader.load_bytes::("test_script.lua", - "text/lua", script.to_vec(), 0, script.len()).unwrap(); - let script = Script::new("text_script.lua", script); - - let scripts = ScriptList::new(vec![script]); - - world.spawn((scripts,)); - - let mut exec = GraphExecutor::new(); - exec.insert_system("lua_create_contexts", lua_scripts_create_contexts.into_system(), &[]); - exec.insert_system("lua_update_scripts", lua_script_update_stage_system.into_system(), &["lua_create_contexts"]); - exec.execute(NonNull::from(&world), true).unwrap(); -} \ No newline at end of file diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 2efd36a..df9f4ee 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,28 +1,33 @@ -use std::{sync::{Arc, RwLock}, ptr::NonNull, any::Any, ops::Deref}; +use std::{any::Any, ptr::NonNull, sync::Arc}; +use elua::AsLua; use lyra_ecs::query::dynamic::QueryDynamicType; use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType}; -use mlua::{AnyUserDataExt, IntoLua, IntoLuaMulti}; - use crate::{ScriptWorldPtr, ScriptEntity, ScriptDynamicBundle, ScriptBorrow}; -use super::{ReflectedIterator, DynamicViewIter, FN_NAME_INTERNAL_REFLECT_TYPE, reflect_user_data, ReflectLuaProxy, FN_NAME_INTERNAL_REFLECT}; +use super::{reflect_user_data, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, ReflectedLuaTableProxy, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}; -impl<'lua> mlua::FromLua<'lua> for ScriptEntity { - fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { +impl<'lua> elua::FromLua<'lua> for ScriptEntity { + fn from_lua(_: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), - mlua::Value::Nil => Err(mlua::Error::FromLuaConversionError { from: "Nil", to: "ScriptEntity", message: Some("Value was nil".to_string()) }), + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), + elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptEntity", "Nil")), _ => panic!(), } } } -impl mlua::UserData for ScriptEntity { - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { +impl elua::Userdata for ScriptEntity { + fn name() -> String { + "Entity".to_string() + } + + fn build<'a>(state: &elua::State, builder: &mut elua::userdata::UserdataBuilder<'a, Self>) -> elua::Result<()> { + builder.meta_method(elua::MetaMethod::ToString, |_, this, ()| { Ok(format!("{:?}", this.0)) - }) + }); + + Ok(()) } } @@ -32,153 +37,182 @@ pub enum WorldError { LuaInvalidUsage(String), } -impl mlua::UserData for ScriptWorldPtr { - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_method_mut("spawn", |_, this, (bundle,): (ScriptDynamicBundle,)| { - let world = unsafe { this.inner.as_mut() }; - - Ok(ScriptEntity(world.spawn(bundle.0))) - }); +impl<'a> elua::FromLua<'a> for ScriptWorldPtr { + fn from_lua(lua: &'a elua::State, val: elua::Value<'a>) -> elua::Result { + match val { + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), + elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptWorldPtr", "Nil")), + _ => panic!(), + } + } +} - methods.add_method("view_iter", |lua, this, queries: mlua::Variadic| { - let world = unsafe { this.inner.as_ref() }; - let mut view = world.dynamic_view(); +impl elua::Userdata for ScriptWorldPtr { + fn name() -> String { + "World".to_string() + } - for comp in queries.into_iter() { - let script_brw = comp.call_function::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) - .expect("Type does not implement '__internal_reflect_type' properly"); - let refl_comp = script_brw.reflect_branch.as_component_unchecked(); + fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + builder + .method("test", |_, this, ()| { + println!("hgello world"); + Ok(()) + }) + .method_mut("spawn", |_, this, bundle: ScriptDynamicBundle| { + let world = this.as_mut(); - let dyn_type = QueryDynamicType::from_info(refl_comp.info); - view.push(dyn_type); - } - - let iter = view.into_iter(); - let mut reflected_iter = ReflectedIterator { - world: this.clone(), - dyn_view: DynamicViewIter::from(iter), - reflected_components: None, - }; - - let f = lua.create_function_mut(move |lua, ()| { - if let Some(row) = reflected_iter.next_lua(lua) { - let row = row.row.into_iter().map(|i| i.comp_ud.into_lua(lua)) - .collect::>>()?; - Ok(mlua::MultiValue::from_vec(row)) - } else { - Ok(mlua::Value::Nil.into_lua_multi(lua)?) + 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(); - Ok(f) - }); + for (idx, comp) in queries.into_iter().enumerate() { + match comp { + elua::Value::Table(t) => { + let name: String = t.get("__name")?; - methods.add_method_mut("view", |lua, this, (system, queries): (mlua::Function, mlua::Variadic)| { - if queries.is_empty() { - return Err(mlua::Error::BadArgument { to: Some("world:view".to_string()), pos: 2, name: Some("...".to_string()), cause: - Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage("no component types provided".to_string()))) - }); - } - - let world = unsafe { this.inner.as_ref() }; - let mut view = world.dynamic_view(); + let lookup = world.get_resource::(); + 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)) + ) + } + )?; + + 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(); - for comp in queries.into_iter() { - let reflect = comp.call_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); - } - - 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.proxy, r.comp_ud, r.comp_ptr)) - .collect::>(); - let (reflects, values, ptrs): - (Vec<&ReflectLuaProxy>, Vec, Vec>) - = itertools::multiunzip(r); - let value_row: Vec<_> = values.into_iter().map(|ud| ud.into_lua(lua)).collect::>>()?; - let mult_val = mlua::MultiValue::from_vec(value_row); - let res: mlua::MultiValue = system.call(mult_val)?; - - // if values were returned, find the type in the type registry, and apply the new values - if res.len() <= reflects.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 (i, comp) in res.into_iter().enumerate() { - let ptr = ptrs[i]; + let iter = view.into_iter(); + let mut reflected_iter = ReflectedIterator { + world: this.clone(), + dyn_view: DynamicViewIter::from(iter), + reflected_components: None, + }; - match comp.as_userdata() { - Some(ud) => { - let lua_comp = reflect_user_data(ud); - let refl_comp = lua_comp.reflect_branch.as_component_unchecked(); - let lua_typeid = refl_comp.info.type_id.as_rust(); + let mut current = world.current_tick(); + let mut has_ticked = false; - // 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(refl_comp.type_id.into()).unwrap(); - c.entity_ticks[idx.0 as usize] = current; + while let Some(row) = reflected_iter.next_lua(lua) { + let r = row.row.into_iter() + .map(|r| (r.comp_val, r.comp_ptr)) + .collect::>(); + let (values, ptrs) = itertools::multiunzip::<(Vec, Vec>), _>(r); + let mult_val = elua::ValueVec::from(values); + let res: elua::ValueVec = system.exec(mult_val)?; - // 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::().unwrap(); - (proxy.fn_apply)(lua, ptr, ud)?; - } - None => { - panic!("A userdata value was not returned!"); + // 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 (i, comp) in res.into_iter().enumerate() { + let ptr = ptrs[i]; + + match comp { + elua::Value::Userdata(ud) => { + let lua_comp = reflect_user_data(&ud); + let refl_comp = lua_comp.reflect_branch.as_component_unchecked(); + let lua_typeid = refl_comp.info.type_id.as_rust(); + + // 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(refl_comp.type_id.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::() + .expect("Type does not have ReflectLuaProxy as a TypeData"); + (proxy.fn_apply)(lua, ptr, &ud)?; + }, + elua::Value::Table(tbl) => { + let name: String = tbl.get("__name")?; + + let lookup = world.get_resource::(); + let tyid = lookup.typeid_from_name.get(&name).unwrap(); + + // 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(tyid.clone().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(*tyid).unwrap(); + + let proxy = reg_type.get_data::() + .expect("Type does not have ReflectLuaProxy as a TypeData"); + (proxy.fn_apply)(lua, ptr.cast(), &tbl)?; + }, + _ => { + panic!("A userdata or table value was not returned!"); + } } } + } 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)); } - } else { - let msg = format!("Too many arguments were returned from the World view! - At most, the expected number of results is {}.", reflects.len()); - return Err(mlua::Error::external(WorldError::LuaInvalidUsage(msg))); } - } - Ok(()) - }); + Ok(()) + }) + .method_mut("resource", |lua, this, (ty,): (elua::AnyUserdata,)| { + let reflect = ty + .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()) { + //.expect("Failed to find resource pointer in world!"); + let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) + .unwrap(); + let proxy = reg_type.get_data::() + .expect("Type does not have ReflectLuaProxy as a TypeData"); - methods.add_method_mut("resource", |lua, this, (ty,): (mlua::AnyUserData,)| { - let reflect = ty - .call_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()) { - //.expect("Failed to find resource pointer in world!"); - let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) - .unwrap(); - let proxy = reg_type.get_data::() - .expect("Type does not have ReflectLuaProxy as a TypeData"); + (proxy.fn_as_uservalue)(lua, res_ptr) + .and_then(|ud| ud.as_lua(lua)) + } else { + // if the resource is not found in the world, return nil + Ok(elua::Value::Nil) + } + }); - (proxy.fn_as_uservalue)(lua, res_ptr) - .and_then(|ud| ud.into_lua(lua)) - } else { - // if the resource is not found in the world, return nil - Ok(mlua::Value::Nil) - } - }); + Ok(()) } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/wrappers/delta_time.rs b/lyra-scripting/src/lua/wrappers/delta_time.rs index a4fd4f6..c02ea4a 100644 --- a/lyra-scripting/src/lua/wrappers/delta_time.rs +++ b/lyra-scripting/src/lua/wrappers/delta_time.rs @@ -1,4 +1,4 @@ -use std::{ops::Deref, any::TypeId}; +use std::any::TypeId; use lyra_game::DeltaTime; use crate::{lyra_engine, lua::{FN_NAME_INTERNAL_REFLECT_TYPE, LuaWrapper}, ScriptBorrow}; @@ -20,28 +20,33 @@ impl std::ops::DerefMut for LuaDeltaTime { } } -impl<'lua> mlua::FromLua<'lua> for LuaDeltaTime { - fn from_lua(value: mlua::prelude::LuaValue<'lua>, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult { +impl<'lua> elua::FromLua<'lua> for LuaDeltaTime { + fn from_lua(lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { match value { - mlua::Value::UserData(ud) => Ok(ud.borrow::()?.clone()), + elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), _ => unreachable!(), } } } -impl mlua::UserData for LuaDeltaTime { - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_method("get", |_, this, ()| { - Ok(*this.0.deref()) - }); +impl elua::Userdata for LuaDeltaTime { + fn name() -> String { + "DeltaTime".to_string() + } - methods.add_meta_method(mlua::MetaMethod::ToString, |_, this, ()| { - Ok(format!("{}", this.0.deref())) - }); + fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + builder + .method("get", |_, this, ()| { + Ok(*this.0) + }) + .meta_method(elua::MetaMethod::ToString, |_, this, ()| { + Ok(format!("{}", *this.0)) + }) + .function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { + Ok(ScriptBorrow::from_resource::(None)) + }); - methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { - Ok(ScriptBorrow::from_resource::(None)) - }); + Ok(()) } } diff --git a/lyra-scripting/src/lua/wrappers/math.rs b/lyra-scripting/src/lua/wrappers/math.rs index 58d8f4a..05a38e7 100644 --- a/lyra-scripting/src/lua/wrappers/math.rs +++ b/lyra-scripting/src/lua/wrappers/math.rs @@ -7,7 +7,7 @@ use lyra_scripting_derive::wrap_math_vec_copy; use crate as lyra_scripting; // f32 types -wrap_math_vec_copy!( +/* wrap_math_vec_copy!( math::Vec2, derives(PartialEq), fields(x, y), @@ -32,7 +32,11 @@ wrap_math_vec_copy!( Mod(LuaVec3, f32), Eq, Unm, ToString ) -); +); */ + + +// ================================================= + /* wrap_math_vec_copy!( math::Vec3A, @@ -379,7 +383,20 @@ wrap_math_vec_copy!( ) ); */ -wrap_math_vec_copy!( + + + + + +// ============================================================ + + + + + + + +/* wrap_math_vec_copy!( math::Quat, derives(PartialEq), no_new, @@ -559,3 +576,4 @@ wrap_math_vec_copy!( }); } ); + */ \ No newline at end of file -- 2.40.1 From 6cd38376e05373d2e7ff8b8b7db432c5db55d666 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 17 Feb 2024 14:30:27 -0500 Subject: [PATCH 20/41] scripting: update elua submodule --- lyra-scripting/elua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 35bd889..8b88612 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 35bd889b4829446f972292440174e7c0e4fa40a2 +Subproject commit 8b88612890f7d67472b56cd97711bb037198ebf9 -- 2.40.1 From 20a36795dc90d76a913e72d02e3549ddf9c0d22b Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 17 Feb 2024 19:08:11 -0500 Subject: [PATCH 21/41] scripting: update elua, change test lua script --- examples/testbed/scripts/test.lua | 45 +++++++++---------------------- lyra-scripting/elua | 2 +- lyra-scripting/src/lua/world.rs | 5 ---- 3 files changed, 13 insertions(+), 39 deletions(-) diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 2bbbf6d..5751366 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -12,32 +12,10 @@ function on_pre_update() print("Lua's pre-update function was called") end ]] ---- ----Recursively dumps a table as a string. ---- ----@param obj table ----@return string ----@nodiscard -function dump_table(obj) - if type(obj) == 'table' then - local s = '{ ' - for k,v in pairs(obj) do - if type(k) ~= 'number' then k = '"'..k..'"' end - s = s .. '['..k..'] = ' .. dump_table(v) .. ',' - end - return s .. '} ' - else - return tostring(obj) - end -end - function on_update() --print("Lua's update function was called") - --local v = Vec3:new(50, 10, 20) - --print("Vec3 = " .. tostring(v)) - - world:view( + --[[ world:view( ---@param t Transform function (t) print("Found entity at a really cool place: " .. tostring(t)) @@ -46,17 +24,18 @@ function on_update() return t end, Transform - ) + ) ]] --- local dt = world:resource(DeltaTime) --- --print("DeltaTime was " .. tostring(dt) .. "s") --- --- world:view(function (t) --- --print("Found entity at a really cool place: " .. tostring(t)) --- t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) --- --- return t --- end, Transform) + local dt = world:resource(DeltaTime) + --print("DeltaTime was " .. tostring(dt) .. "s") + + world:view(function (t) + --print("Found entity at a really cool place: " .. tostring(t)) + --t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) + t.translation:move_by(0, 0.5 * dt:get(), 0) + + return t + end, Transform) end --[[ function on_post_update() diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 8b88612..2fe5dca 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 8b88612890f7d67472b56cd97711bb037198ebf9 +Subproject commit 2fe5dca6c4337f1528e4671d1f79464481739c61 diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index df9f4ee..dff4f95 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -54,10 +54,6 @@ impl elua::Userdata for ScriptWorldPtr { fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { builder - .method("test", |_, this, ()| { - println!("hgello world"); - Ok(()) - }) .method_mut("spawn", |_, this, bundle: ScriptDynamicBundle| { let world = this.as_mut(); @@ -199,7 +195,6 @@ impl elua::Userdata for ScriptWorldPtr { let res = reflect.reflect_branch.as_resource_unchecked(); if let Some(res_ptr) = res.reflect_ptr(this.as_mut()) { - //.expect("Failed to find resource pointer in world!"); let reg_type = this.as_ref().get_type::(reflect.reflect_branch.reflect_type_id()) .unwrap(); let proxy = reg_type.get_data::() -- 2.40.1 From e6c9e0725c321b5de6f26a93713c7967ef7ede49 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 18 Feb 2024 23:28:29 -0500 Subject: [PATCH 22/41] scripting: remove ReflectedLuaTableProxy and slightly modify ReflectLuaProxy to replace it --- lyra-scripting/elua | 2 +- lyra-scripting/src/lua/dynamic_iter.rs | 17 ++--- lyra-scripting/src/lua/mod.rs | 20 ++--- lyra-scripting/src/lua/providers/ecs.rs | 6 +- lyra-scripting/src/lua/providers/math.rs | 8 +- lyra-scripting/src/lua/providers/util.rs | 4 +- lyra-scripting/src/lua/proxy.rs | 97 +++++++++++------------- lyra-scripting/src/lua/script.rs | 3 +- lyra-scripting/src/lua/world.rs | 83 +++++++++----------- 9 files changed, 105 insertions(+), 135 deletions(-) diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 2fe5dca..9b542fb 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 2fe5dca6c4337f1528e4671d1f79464481739c61 +Subproject commit 9b542fb523a3dc5d2eb77b246136111ad8bc4dd2 diff --git a/lyra-scripting/src/lua/dynamic_iter.rs b/lyra-scripting/src/lua/dynamic_iter.rs index 7b8f250..c504470 100644 --- a/lyra-scripting/src/lua/dynamic_iter.rs +++ b/lyra-scripting/src/lua/dynamic_iter.rs @@ -156,9 +156,6 @@ impl ReflectedIterator { pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option> { use elua::AsLua; - use super::ReflectedLuaTableProxy; - - let n = self.dyn_view.next(); if let Some(row) = n { @@ -176,15 +173,11 @@ impl ReflectedIterator { let reg_type = reflected_components.get_type(id) .expect("Requested type was not found in TypeRegistry"); - let value = if let Some(proxy) = reg_type.get_data::() { - (proxy.fn_as_uservalue)(lua, d.ptr).unwrap() - .as_lua(lua).unwrap() - } else if let Some(proxy) = reg_type.get_data::() { - (proxy.fn_as_table)(lua, d.ptr.cast()).unwrap() - .as_lua(lua).unwrap() - } else { - panic!("Type does not have ReflectLuaProxy or ReflectedLuaTableProxy as a TypeData"); - }; + let proxy = reg_type.get_data::() + // TODO: properly handle this error + .expect("Type does not have ReflectLuaProxy as a TypeData"); + let value = (proxy.fn_as_lua)(lua, d.ptr.cast()).unwrap() + .as_lua(lua).unwrap(); dynamic_row.push(ReflectedItem { comp_ptr: d.ptr, diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index cf11dc6..5ef88a8 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -23,16 +23,16 @@ pub use system::*; use std::{any::TypeId, sync::Mutex}; use lyra_ecs::{ - Component, ComponentInfo, DynamicBundle, World + Component, ComponentInfo, World }; -use lyra_reflect::{FromType, Reflect, TypeRegistry}; +use lyra_reflect::{Reflect, TypeRegistry}; pub type LuaContext = Mutex; pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; -use crate::{ScriptBorrow, ScriptDynamicBundle}; +use crate::ScriptBorrow; /// A trait used for registering a Lua type with the world. pub trait RegisterLuaType { @@ -64,8 +64,7 @@ impl RegisterLuaType for World { let type_id = TypeId::of::(); let reg_type = registry.get_type_or_default(type_id); - reg_type.add_data(>::from_type()); - //reg_type.add_data(>::from_type()); + reg_type.add_data(ReflectLuaProxy::from_lua_proxy::()); } fn register_lua_wrapper<'a, W>(&mut self) @@ -75,7 +74,7 @@ impl RegisterLuaType for World { let mut registry = self.get_resource_mut::(); let reg_type = registry.get_type_or_default(W::wrapped_type_id()); - reg_type.add_data(>::from_type()); + reg_type.add_data(ReflectLuaProxy::from_lua_proxy::()); } fn register_lua_table_proxy<'a, T, C>(&mut self) @@ -85,15 +84,16 @@ impl RegisterLuaType for World { { let mut registry = self.get_resource_mut::(); - let type_id = TypeId::of::(); let reg_type = registry.get_type_or_default(TypeId::of::()); - reg_type.add_data(>::from_type()); + reg_type.add_data(ReflectLuaProxy::from_table_proxy::()); + /* let reg_type = registry.get_type_or_default(TypeId::of::()); + reg_type.add_data(>::from_type()); */ drop(registry); let mut lookup = self.get_resource_or_else::(LuaTableProxyLookup::default); lookup.typeid_from_name.insert(T::table_name(), TypeId::of::()); - let mut info = ComponentInfo::new::(); + let info = ComponentInfo::new::(); lookup.comp_info_from_name.insert(T::table_name(), info); } } @@ -122,7 +122,7 @@ impl elua::Userdata for ScriptBorrow { "ScriptBorrow".to_string() } - fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + fn build<'a>(_: &elua::State, _: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { Ok(()) } } diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index 7b0a0be..43c26a8 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -10,7 +10,7 @@ impl ScriptApiProvider for LyraEcsApiProvider { world.register_lua_wrapper::(); } - fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); let globals = ctx.globals()?; @@ -22,11 +22,11 @@ impl ScriptApiProvider for LyraEcsApiProvider { Ok(()) } - fn setup_script(&mut self, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn setup_script(&mut self, _: &crate::ScriptData, _: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { Ok(()) } - fn update_script_environment(&mut self, world: crate::ScriptWorldPtr, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn update_script_environment(&mut self, _: crate::ScriptWorldPtr, _: &crate::ScriptData, _: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { Ok(()) } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/providers/math.rs b/lyra-scripting/src/lua/providers/math.rs index c8118c9..ac767ee 100644 --- a/lyra-scripting/src/lua/providers/math.rs +++ b/lyra-scripting/src/lua/providers/math.rs @@ -1,6 +1,6 @@ -use elua::{TableProxy, Userdata}; +use elua::TableProxy; use lyra_ecs::World; -use lyra_game::math::{self, Quat, Transform, Vec3}; +use lyra_game::math::{Quat, Transform, Vec3}; use crate::ScriptData; use crate::lua::RegisterLuaType; @@ -42,11 +42,11 @@ impl ScriptApiProvider for LyraMathApiProvider { Ok(()) } - fn setup_script(&mut self, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn setup_script(&mut self, _: &crate::ScriptData, _: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { Ok(()) } - fn update_script_environment(&mut self, world: crate::ScriptWorldPtr, data: &crate::ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { + fn update_script_environment(&mut self, _: crate::ScriptWorldPtr, _: &crate::ScriptData, _: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { Ok(()) } } diff --git a/lyra-scripting/src/lua/providers/util.rs b/lyra-scripting/src/lua/providers/util.rs index 905f123..2ef6bc9 100644 --- a/lyra-scripting/src/lua/providers/util.rs +++ b/lyra-scripting/src/lua/providers/util.rs @@ -1,6 +1,6 @@ use std::sync::{Mutex, Arc}; -use tracing::{debug_span, debug, span, Level}; +use tracing::{debug_span, debug}; use crate::{ScriptApiProvider, ScriptData}; @@ -51,7 +51,7 @@ impl ScriptApiProvider for UtilityApiProvider { } }, elua::Value::None => "None".to_string(), - elua::Value::Multi(v) => { + elua::Value::Multi(_) => { return Err(elua::Error::runtime("unable to get string representation of ValueVec")); }, }, diff --git a/lyra-scripting/src/lua/proxy.rs b/lyra-scripting/src/lua/proxy.rs index 5980814..d60efba 100644 --- a/lyra-scripting/src/lua/proxy.rs +++ b/lyra-scripting/src/lua/proxy.rs @@ -1,8 +1,8 @@ use std::{any::TypeId, collections::HashMap, ptr::NonNull}; -use elua::{FromLua, TableProxy}; +use elua::{FromLua, TableProxy, AsLua}; use lyra_ecs::{ComponentInfo, DynamicBundle}; -use lyra_reflect::{Reflect, FromType}; +use lyra_reflect::Reflect; use crate::{ScriptBorrow, ScriptDynamicBundle}; @@ -18,12 +18,12 @@ pub trait LuaProxy { fn as_lua_value<'lua>( lua: &'lua elua::State, this: &dyn Reflect, - ) -> elua::Result>; + ) -> elua::Result>; fn apply( lua: &elua::State, this: &mut dyn Reflect, - apply: &elua::AnyUserdata, + value: &elua::Value, ) -> elua::Result<()>; } @@ -34,18 +34,21 @@ where fn as_lua_value<'lua>( lua: &'lua elua::State, this: &dyn Reflect, - ) -> elua::Result> { + ) -> elua::Result> { let this = this.as_any().downcast_ref::().unwrap(); lua.create_userdata(this.clone()) + .and_then(|ud| ud.as_lua(lua)) } fn apply( _: &elua::State, this: &mut dyn Reflect, - apply: &elua::AnyUserdata, + apply: &elua::Value, ) -> elua::Result<()> { let this = this.as_any_mut().downcast_mut::().unwrap(); - let apply = apply.as_ref::()?; + let apply = apply.as_userdata() + .expect("Somehow a non-userdata Lua Value was provided to a LuaProxy") + .as_ref::()?; *this = apply.clone(); @@ -60,59 +63,26 @@ pub struct LuaTableProxyLookup { pub(crate) comp_info_from_name: HashMap, } -/// A struct used for converting types that implement `TableProxy` to and from Lua. +/// A struct used for Proxying types to and from Lua. #[derive(Clone)] -pub struct ReflectedLuaTableProxy { - pub table_name: String, - pub fn_as_table: - for<'a> fn(lua: &'a elua::State, this_ptr: NonNull<()>) -> elua::Result>, +pub struct ReflectLuaProxy { + pub fn_as_lua: + for<'a> fn(lua: &'a elua::State, this_ptr: NonNull<()>) -> elua::Result>, pub fn_apply: for<'a> fn( lua: &'a elua::State, this_ptr: NonNull<()>, - table: &'a elua::Table<'a>, + value: &'a elua::Value<'a>, ) -> elua::Result<()>, } -impl<'a, T> FromType for ReflectedLuaTableProxy -where - T: TableProxy -{ - fn from_type() -> Self { +impl ReflectLuaProxy { + /// Create from a type that implements LuaProxy (among some other required traits) + pub fn from_lua_proxy<'a, T>() -> Self + where + T: Reflect + LuaProxy + { Self { - table_name: T::table_name(), - fn_as_table: |lua, this| -> elua::Result { - let this = unsafe { this.cast::().as_ref() }; - this.as_table(lua) - }, - fn_apply: |lua, ptr, table| { - let this = unsafe { ptr.cast::().as_mut() }; - let tbl = T::from_table(lua, table.clone())?; - *this = tbl; - Ok(()) - }, - } - } -} - -/// A struct used for converting types that implement `LuaProxy` to and from Lua. -#[derive(Clone)] -pub struct ReflectLuaProxy { - pub fn_as_uservalue: - for<'a> fn(lua: &'a elua::State, this_ptr: NonNull) -> elua::Result>, - pub fn_apply: for<'a> fn( - lua: &'a elua::State, - this_ptr: NonNull, - apply: &'a elua::AnyUserdata<'a>, - ) -> elua::Result<()>, -} - -impl<'a, T> FromType for ReflectLuaProxy -where - T: Reflect + LuaProxy + Clone + elua::FromLua<'a> + elua::Userdata -{ - fn from_type() -> Self { - Self { - fn_as_uservalue: |lua, this| -> elua::Result { + fn_as_lua: |lua, this| -> elua::Result { let this = unsafe { this.cast::().as_ref() }; ::as_lua_value(lua, this) }, @@ -122,6 +92,29 @@ where }, } } + + pub fn from_table_proxy() -> Self + where + T: TableProxy + { + Self { + fn_as_lua: |lua, this| -> elua::Result { + let this = unsafe { this.cast::().as_ref() }; + this.as_table(lua) + .and_then(|t| t.as_lua(lua)) + }, + fn_apply: |lua, ptr, value| { + let this = unsafe { ptr.cast::().as_mut() }; + let table = value.as_table() + .expect("Somehow a non-Table Lua Value was provided to a TableProxy"); + let new_val = T::from_table(lua, table.clone())?; + + *this = new_val; + + Ok(()) + }, + } + } } impl<'lua> elua::FromLua<'lua> for ScriptDynamicBundle { diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index b0d1cc6..3dccd70 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -1,7 +1,6 @@ use std::sync::Mutex; -use elua::{AsLua, StdLibraries, StdLibrary}; -use tracing::{debug, trace}; +use elua::{AsLua, StdLibrary}; use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index dff4f95..0f874a8 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,11 +1,11 @@ -use std::{any::Any, ptr::NonNull, sync::Arc}; +use std::{ptr::NonNull, sync::Arc}; use elua::AsLua; use lyra_ecs::query::dynamic::QueryDynamicType; use lyra_reflect::{TypeRegistry, ReflectWorldExt, RegisteredType}; use crate::{ScriptWorldPtr, ScriptEntity, ScriptDynamicBundle, ScriptBorrow}; -use super::{reflect_user_data, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, ReflectedLuaTableProxy, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}; +use super::{reflect_user_data, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, 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 +22,7 @@ impl elua::Userdata for ScriptEntity { "Entity".to_string() } - fn build<'a>(state: &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)) }); @@ -38,7 +38,7 @@ pub enum WorldError { } impl<'a> elua::FromLua<'a> for ScriptWorldPtr { - fn from_lua(lua: &'a elua::State, val: elua::Value<'a>) -> elua::Result { + fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result { match val { elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), elua::Value::Nil => Err(elua::Error::type_mismatch("ScriptWorldPtr", "Nil")), @@ -52,7 +52,7 @@ impl elua::Userdata for ScriptWorldPtr { "World".to_string() } - fn build<'a>(state: &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(); @@ -73,7 +73,7 @@ impl elua::Userdata for ScriptWorldPtr { for (idx, comp) in queries.into_iter().enumerate() { match comp { elua::Value::Table(t) => { - let name: String = t.get("__name")?; + let name: String = t.get(elua::MetaMethod::Name)?; let lookup = world.get_resource::(); let info = lookup.comp_info_from_name.get(&name) @@ -115,9 +115,9 @@ impl elua::Userdata for ScriptWorldPtr { while let Some(row) = reflected_iter.next_lua(lua) { let r = row.row.into_iter() - .map(|r| (r.comp_val, r.comp_ptr)) + .map(|r| (r.comp_val, r.comp_ptr.cast::<()>())) .collect::>(); - let (values, ptrs) = itertools::multiunzip::<(Vec, Vec>), _>(r); + let (values, ptrs) = itertools::multiunzip::<(Vec, Vec>), _>(r); let mult_val = elua::ValueVec::from(values); let res: elua::ValueVec = system.exec(mult_val)?; @@ -129,55 +129,40 @@ impl elua::Userdata for ScriptWorldPtr { has_ticked = true; } - for (i, comp) in res.into_iter().enumerate() { - let ptr = ptrs[i]; - - match comp { + 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 lua_comp = reflect_user_data(ud); let refl_comp = lua_comp.reflect_branch.as_component_unchecked(); - let lua_typeid = refl_comp.info.type_id.as_rust(); - - // 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(refl_comp.type_id.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::() - .expect("Type does not have ReflectLuaProxy as a TypeData"); - (proxy.fn_apply)(lua, ptr, &ud)?; + refl_comp.info.type_id.as_rust() }, elua::Value::Table(tbl) => { - let name: String = tbl.get("__name")?; + let name: String = tbl.get(elua::MetaMethod::Name)?; let lookup = world.get_resource::(); - let tyid = lookup.typeid_from_name.get(&name).unwrap(); - - // 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(tyid.clone().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(*tyid).unwrap(); - - let proxy = reg_type.get_data::() - .expect("Type does not have ReflectLuaProxy as a TypeData"); - (proxy.fn_apply)(lua, ptr.cast(), &tbl)?; + *lookup.typeid_from_name.get(&name).unwrap() }, _ => { - panic!("A userdata or table value was not returned!"); + 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! @@ -200,7 +185,7 @@ impl elua::Userdata for ScriptWorldPtr { let proxy = reg_type.get_data::() .expect("Type does not have ReflectLuaProxy as a TypeData"); - (proxy.fn_as_uservalue)(lua, res_ptr) + (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 -- 2.40.1 From 639ec0ee425b130390e90f3c3e6f6b359a63fa8d Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 18 Feb 2024 23:41:53 -0500 Subject: [PATCH 23/41] resource: fix resource manager tests A test was failing since 'watch_image' was deleting the file and recreating it while the other test was running --- lyra-resource/src/resource_manager.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lyra-resource/src/resource_manager.rs b/lyra-resource/src/resource_manager.rs index dcf7e8b..6b84686 100644 --- a/lyra-resource/src/resource_manager.rs +++ b/lyra-resource/src/resource_manager.rs @@ -237,6 +237,7 @@ impl ResourceManager { let path = resource.path(); if let Some(loader) = self.loaders.iter() .find(|l| l.does_support_file(&path)) { + println!("got loader"); let loader = Arc::clone(loader); // stop borrowing from self let loaded = loader.load(self, &path)?; let loaded = loaded.as_arc_any(); @@ -325,6 +326,7 @@ mod tests { let img = res.try_data_ref(); img.unwrap(); + println!("Path = {}", res.path()); man.reload(res.clone()).unwrap(); assert_eq!(res.version(), 1); @@ -334,8 +336,9 @@ mod tests { #[test] fn watch_image() { - let image_path = get_image("squiggles.png"); - let image_bytes = std::fs::read(&image_path).unwrap(); + let orig_path = get_image("squiggles.png"); + let image_path = get_image("squiggles_test.png"); + std::fs::copy(orig_path, &image_path).unwrap(); let mut man = ResourceManager::new(); let res = man.request::(&image_path).unwrap(); @@ -350,8 +353,6 @@ mod tests { let event = recv.recv().unwrap(); let event = event.unwrap(); - std::fs::write(image_path, image_bytes).unwrap(); - assert!(event.iter().any(|ev| ev.kind.is_remove() || ev.kind.is_modify())); } } \ No newline at end of file -- 2.40.1 From ea958f9f18511653a4a17d7daf00ce79acc68610 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 19 Feb 2024 11:27:49 -0500 Subject: [PATCH 24/41] scripting: convert LuaDeltaTime into a Lua Number instead of using it as userdata --- examples/testbed/scripts/test.lua | 3 +- lyra-scripting/src/lua/mod.rs | 17 +++++++-- lyra-scripting/src/lua/providers/ecs.rs | 19 ++++++++-- lyra-scripting/src/lua/proxy.rs | 21 +++++++++++ lyra-scripting/src/lua/world.rs | 20 +++++++++-- lyra-scripting/src/lua/wrappers/delta_time.rs | 35 +++++-------------- 6 files changed, 80 insertions(+), 35 deletions(-) diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 5751366..4c0bec0 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -26,13 +26,14 @@ function on_update() Transform ) ]] + ---@type number local dt = world:resource(DeltaTime) --print("DeltaTime was " .. tostring(dt) .. "s") world:view(function (t) --print("Found entity at a really cool place: " .. tostring(t)) --t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) - t.translation:move_by(0, 0.5 * dt:get(), 0) + t.translation:move_by(0, 0.5 * dt, 0) return t end, Transform) diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index 5ef88a8..ed8e36e 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -47,6 +47,11 @@ pub trait RegisterLuaType { where W: Reflect + LuaProxy + LuaWrapper + Clone + elua::FromLua<'a> + elua::Userdata; + /// Registers a type to Lua that can be converted into and from Lua types. + fn register_lua_convert(&mut self) + where + T: Clone + for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + LuaWrapper + 'static; + /// Registers a type to Lua that implements [`elua::TableProxy`] fn register_lua_table_proxy<'a, T, W>(&mut self) where @@ -77,6 +82,16 @@ impl RegisterLuaType for World { reg_type.add_data(ReflectLuaProxy::from_lua_proxy::()); } + fn register_lua_convert(&mut self) + where + T: Clone + for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + LuaWrapper + 'static, + { + let mut registry = self.get_resource_mut::(); + + let reg_type = registry.get_type_or_default(T::wrapped_type_id()); + reg_type.add_data(ReflectLuaProxy::from_as_and_from_lua::()); + } + fn register_lua_table_proxy<'a, T, C>(&mut self) where T: elua::TableProxy + 'static, @@ -86,8 +101,6 @@ impl RegisterLuaType for World { let reg_type = registry.get_type_or_default(TypeId::of::()); reg_type.add_data(ReflectLuaProxy::from_table_proxy::()); - /* let reg_type = registry.get_type_or_default(TypeId::of::()); - reg_type.add_data(>::from_type()); */ drop(registry); let mut lookup = self.get_resource_or_else::(LuaTableProxyLookup::default); diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index 43c26a8..7b3cb2b 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -1,4 +1,7 @@ -use crate::{lua::{LuaContext, wrappers::LuaDeltaTime, RegisterLuaType}, ScriptApiProvider, ScriptWorldPtr, ScriptDynamicBundle, ScriptData}; +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}; #[derive(Default)] pub struct LyraEcsApiProvider; @@ -7,7 +10,7 @@ impl ScriptApiProvider for LyraEcsApiProvider { type ScriptContext = LuaContext; fn prepare_world(&mut self, world: &mut lyra_ecs::World) { - world.register_lua_wrapper::(); + world.register_lua_convert::(); } fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { @@ -17,7 +20,8 @@ impl ScriptApiProvider for LyraEcsApiProvider { globals.set("World", ctx.create_proxy::()?)?; globals.set("DynamicBundle", ctx.create_proxy::()?)?; - globals.set("DeltaTime", ctx.create_proxy::()?)?; + let dt_table = create_reflect_table::(&ctx)?; + globals.set("DeltaTime", dt_table)?; Ok(()) } @@ -29,4 +33,13 @@ impl ScriptApiProvider for LyraEcsApiProvider { fn update_script_environment(&mut self, _: crate::ScriptWorldPtr, _: &crate::ScriptData, _: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { Ok(()) } +} + +fn create_reflect_table(lua: &elua::State) -> elua::Result { + let table = lua.create_table()?; + table.set(FN_NAME_INTERNAL_REFLECT_TYPE, lua.create_function(|_, ()| { + Ok(ScriptBorrow::from_resource::(None)) + })?)?; + + Ok(table) } \ No newline at end of file diff --git a/lyra-scripting/src/lua/proxy.rs b/lyra-scripting/src/lua/proxy.rs index d60efba..8372cb5 100644 --- a/lyra-scripting/src/lua/proxy.rs +++ b/lyra-scripting/src/lua/proxy.rs @@ -115,6 +115,27 @@ impl ReflectLuaProxy { }, } } + + /// Create from a type that implements FromLua and AsLua + pub fn from_as_and_from_lua() -> Self + where + T: for<'a> elua::FromLua<'a> + for<'a> elua::AsLua<'a> + Clone + { + Self { + fn_as_lua: |lua, this| -> elua::Result { + let this = unsafe { this.cast::().as_ref() }; + this.clone().as_lua(lua) + }, + fn_apply: |lua, ptr, value| { + let this = unsafe { ptr.cast::().as_mut() }; + let new_val = T::from_lua(lua, value.clone())?; + + *this = new_val; + + Ok(()) + }, + } + } } impl<'lua> elua::FromLua<'lua> for ScriptDynamicBundle { diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 0f874a8..8f0778f 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -173,10 +173,24 @@ impl elua::Userdata for ScriptWorldPtr { Ok(()) }) - .method_mut("resource", |lua, this, (ty,): (elua::AnyUserdata,)| { - let reflect = ty + .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::Table(t) => { + let f: elua::Function = t.get(FN_NAME_INTERNAL_REFLECT_TYPE)?; + f.exec::<_, ScriptBorrow>(()) + .expect("Type does not implement 'reflect_type' properly") + } + _ => { + panic!("how"); + } + }; + /* let reflect = ty .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 res = reflect.reflect_branch.as_resource_unchecked(); if let Some(res_ptr) = res.reflect_ptr(this.as_mut()) { diff --git a/lyra-scripting/src/lua/wrappers/delta_time.rs b/lyra-scripting/src/lua/wrappers/delta_time.rs index c02ea4a..da6e850 100644 --- a/lyra-scripting/src/lua/wrappers/delta_time.rs +++ b/lyra-scripting/src/lua/wrappers/delta_time.rs @@ -1,10 +1,11 @@ + use std::any::TypeId; use lyra_game::DeltaTime; -use crate::{lyra_engine, lua::{FN_NAME_INTERNAL_REFLECT_TYPE, LuaWrapper}, ScriptBorrow}; +use crate::lua::LuaWrapper; -#[derive(Clone, lyra_reflect::Reflect, Default)] -pub struct LuaDeltaTime(#[reflect(skip)] pub(crate) DeltaTime); +#[derive(Clone, Default)] +pub struct LuaDeltaTime(pub(crate) DeltaTime); impl std::ops::Deref for LuaDeltaTime { type Target = DeltaTime; @@ -22,31 +23,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 { - match value { - elua::Value::Userdata(ud) => Ok(ud.as_ref::()?.clone()), - _ => unreachable!(), - } + todo!() } } -impl elua::Userdata for LuaDeltaTime { - fn name() -> String { - "DeltaTime".to_string() - } - - fn build<'a>(state: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { - builder - .method("get", |_, this, ()| { - Ok(*this.0) - }) - .meta_method(elua::MetaMethod::ToString, |_, this, ()| { - Ok(format!("{}", *this.0)) - }) - .function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { - Ok(ScriptBorrow::from_resource::(None)) - }); - - Ok(()) +impl<'lua> elua::AsLua<'lua> for LuaDeltaTime { + fn as_lua(self, lua: &'lua elua::State) -> elua::Result> { + Ok(elua::Value::Number(*self.0 as f64)) } } @@ -54,4 +37,4 @@ impl LuaWrapper for LuaDeltaTime { fn wrapped_type_id() -> std::any::TypeId { TypeId::of::() } -} \ No newline at end of file +} \ No newline at end of file -- 2.40.1 From e6b4e83dee45018940f43b9023dbc1ecc69c3a6f Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 19 Feb 2024 17:57:48 -0500 Subject: [PATCH 25/41] scripting: fix math type wrapper macro for lua. Expose math types as userdata through LuaProxy trait --- examples/testbed/scripts/test.lua | 20 +- lyra-game/src/game.rs | 2 +- lyra-math/src/transform.rs | 7 + lyra-scripting/elua | 2 +- .../lyra-scripting-derive/src/lib.rs | 117 ++++++----- lyra-scripting/src/lua/providers/math.rs | 185 ++---------------- lyra-scripting/src/lua/script.rs | 6 +- lyra-scripting/src/lua/world.rs | 3 +- lyra-scripting/src/lua/wrappers/math.rs | 90 +++++---- shell.nix | 4 +- 10 files changed, 145 insertions(+), 291 deletions(-) diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 4c0bec0..0294bac 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -1,5 +1,3 @@ -print("Hello World") - --[[ function on_init() print("Lua script was initialized!") end @@ -13,27 +11,11 @@ function on_pre_update() end ]] function on_update() - --print("Lua's update function was called") - - --[[ world:view( - ---@param t Transform - function (t) - print("Found entity at a really cool place: " .. tostring(t)) - t.translation:move_by(0, 0.001, 0) - - return t - end, - Transform - ) ]] - ---@type number local dt = world:resource(DeltaTime) - --print("DeltaTime was " .. tostring(dt) .. "s") world:view(function (t) - --print("Found entity at a really cool place: " .. tostring(t)) - --t.translation = t.translation + (Vec3.new(0, 0.5, 0) * dt:get()) - t.translation:move_by(0, 0.5 * dt, 0) + t:translate(0, 0.5 * dt, 0) return t end, Transform) diff --git a/lyra-game/src/game.rs b/lyra-game/src/game.rs index 0a49e45..a3d8666 100755 --- a/lyra-game/src/game.rs +++ b/lyra-game/src/game.rs @@ -342,7 +342,7 @@ impl Game { .with(fmt::layer().with_writer(stdout_layer)) .with(filter::Targets::new() // done by prefix, so it includes all lyra subpackages - .with_target("lyra", Level::TRACE) + .with_target("lyra", Level::DEBUG) .with_target("wgpu", Level::WARN) .with_default(Level::INFO)) .init(); diff --git a/lyra-math/src/transform.rs b/lyra-math/src/transform.rs index f154423..cf9600c 100755 --- a/lyra-math/src/transform.rs +++ b/lyra-math/src/transform.rs @@ -85,6 +85,13 @@ impl Transform { pub fn rotate_z(&mut self, angle: Angle) { self.rotate(Quat::from_rotation_z(angle.to_radians())) } + + pub fn translate(&mut self, x: f32, y: f32, z: f32) { + let trans = &mut self.translation; + trans.x += x; + trans.y += y; + trans.z += z; + } /// Performs a linear interpolation between `self` and `rhs` based on the value `alpha`. /// diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 9b542fb..d32c138 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 9b542fb523a3dc5d2eb77b246136111ad8bc4dd2 +Subproject commit d32c138e996ec2fe17b7a5f7c0d3ff1562e446f0 diff --git a/lyra-scripting/lyra-scripting-derive/src/lib.rs b/lyra-scripting/lyra-scripting-derive/src/lib.rs index beda599..21367b1 100644 --- a/lyra-scripting/lyra-scripting-derive/src/lib.rs +++ b/lyra-scripting/lyra-scripting-derive/src/lib.rs @@ -140,7 +140,7 @@ impl MetaMethod { let body = Self::get_method_body(&self.ident, other); quote! { - methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| { #body }); } @@ -149,12 +149,12 @@ impl MetaMethod { let body = Self::get_body_for_arg(&self.ident, first, quote!(v)); quote! { - methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#first,)| { #body }); } } else { - // an optional match arm that matches mlua::Value:Number + // an optional match arm that matches elua::Value:Number let number_arm = { let num_ident = self.mods.iter().find(|i| { let is = i.to_string(); @@ -171,7 +171,7 @@ impl MetaMethod { let body = Self::get_body_for_arg(&self.ident, num_ident, quote!(n as #num_ident)); quote! { - mlua::Value::Number(n) => { + elua::Value::Number(n) => { #body }, } @@ -187,7 +187,7 @@ impl MetaMethod { let body = Self::get_method_body(&self.ident, quote!(other.0)); quote! { - if let Ok(other) = ud.borrow::<#i>() { + if let Ok(other) = ud.as_ref::<#i>() { #body } } @@ -195,30 +195,30 @@ impl MetaMethod { }); quote! { - mlua::Value::UserData(ud) => { + elua::Value::Userdata(ud) => { #(#if_statements else)* // this is the body of the else statement { // try to get the name of the userdata for the error message if let Ok(mt) = ud.get_metatable() { - if let Ok(name) = mt.get::("__name") { - return Err(mlua::Error::BadArgument { - to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), - pos: 2, - name: Some("rhs".to_string()), - cause: Arc::new(mlua::Error::RuntimeError( + if let Ok(name) = mt.get::<_, String>("__name") { + return Err(elua::Error::BadArgument { + func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + arg_index: 2, + arg_name: Some("rhs".to_string()), + error: Arc::new(elua::Error::Runtime( format!("cannot multiply with unknown userdata named {}", name) )) }); } } - Err(mlua::Error::BadArgument { - to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), - pos: 2, - name: Some("rhs".to_string()), - cause: Arc::new( - mlua::Error::runtime("cannot multiply with unknown userdata") + Err(elua::Error::BadArgument { + func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + arg_index: 2, + arg_name: Some("rhs".to_string()), + error: Arc::new( + elua::Error::runtime("cannot multiply with unknown userdata") ) }) } @@ -227,16 +227,16 @@ impl MetaMethod { }; quote! { - methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (mlua::Value,)| { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (elua::Value,)| { match v { #number_arm #userdata_arm - _ => Err(mlua::Error::BadArgument { - to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), - pos: 2, - name: Some("rhs".to_string()), - cause: Arc::new( - mlua::Error::RuntimeError(format!("cannot multiply with {}", v.type_name())) + _ => Err(elua::Error::BadArgument { + func: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)), + arg_index: 2, + arg_name: Some("rhs".to_string()), + error: Arc::new( + elua::Error::Runtime(format!("cannot multiply with {}", v.type_name())) ) }) } @@ -293,8 +293,8 @@ impl VecWrapper { if axis_type_name.contains("b") { return quote! { - fields.add_field("FALSE", #wrapper_ident(#wrapped_path::FALSE)); - fields.add_field("TRUE", #wrapper_ident(#wrapped_path::TRUE)); + builder.field("FALSE", #wrapper_ident(#wrapped_path::FALSE)); + builder.field("TRUE", #wrapper_ident(#wrapped_path::TRUE)); }; } @@ -331,7 +331,7 @@ impl VecWrapper { let const_name = cnst.to_string(); quote! { - fields.add_field(#const_name, #wrapper_ident(#wrapped_path::#cnst)); + builder.field(#const_name, #wrapper_ident(#wrapped_path::#cnst)); } }); @@ -356,17 +356,17 @@ impl VecWrapper { optional_methods.push( quote! { - methods.add_method("clamp_length", + builder.method("clamp_length", |_, this, (min, max): (#type_id, #type_id)| { Ok(#wrapper_ident(this.clamp_length(min, max))) }); - methods.add_method("abs_diff_eq", + builder.method("abs_diff_eq", |_, this, (rhs, max_abs_diff): (#wrapper_ident, #type_id)| { Ok(this.abs_diff_eq(rhs.0, max_abs_diff)) }); - methods.add_method("ceil", + builder.method("ceil", |_, this, (): ()| { Ok(#wrapper_ident(this.ceil())) }); @@ -376,7 +376,7 @@ impl VecWrapper { if vec_size != 4 { optional_methods.push( quote! { - methods.add_method("angle_between", + builder.method("angle_between", |_, this, (rhs,): (#wrapper_ident,)| { Ok(this.angle_between(rhs.0)) }); @@ -388,7 +388,7 @@ impl VecWrapper { if !axis_type_name.contains("u") { optional_methods.push( quote! { - methods.add_method("abs", + builder.method("abs", |_, this, (): ()| { Ok(#wrapper_ident(this.abs())) }); @@ -400,19 +400,19 @@ impl VecWrapper { quote! { - methods.add_method("clamp", + builder.method("clamp", |_, this, (min, max): (#wrapper_ident, #wrapper_ident)| { Ok(#wrapper_ident(this.clamp(min.0, max.0))) }); // TODO: Not all Vecs have this - /* methods.add_method("clamp_length", + /* builder.method("clamp_length", |_, this, (min, max): (f32, f32)| { Ok(#wrapper_ident(this.clamp_length(min, max))) }); */ - methods.add_method("to_array", + builder.method("to_array", |_, this, (): ()| { Ok(this.to_array()) }); @@ -570,10 +570,10 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt let field_get_set_pairs = input.field_idents.iter().map(|i| { let is = i.to_string(); quote! { - fields.add_field_method_get(#is, |_, this| { + builder.field_getter(#is, |_, this| { Ok(this.#i) }); - fields.add_field_method_set(#is, |_, this, #i| { + builder.field_setter(#is, |_, this, #i| { this.#i = #i; Ok(()) }); @@ -591,7 +591,7 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt if !input.skip_new_fn { quote! { - methods.add_function("new", |_, ( #(#idents_c),* )| { + builder.function("new", |_, ( #(#idents_c),* )| { Ok(#wrapper_typename(#path::new( #(#idents),* ))) }); } @@ -631,33 +631,35 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt } } - impl<'lua> mlua::FromLua<'lua> for #wrapper_typename { - fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result { + impl<'lua> elua::FromLua<'lua> for #wrapper_typename { + fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result { match value { - mlua::Value::UserData(ud) => Ok(*ud.borrow::()?), + elua::Value::Userdata(ud) => Ok(*ud.as_ref::()?), _ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()), } } } - impl mlua::UserData for #wrapper_typename { - fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) { + impl elua::Userdata for #wrapper_typename { + fn name() -> String { + stringify!(#type_name).to_string() + } + + fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { #(#field_get_set_pairs)* #matrix_wrapper_fields #vec_wrapper_fields #custom_fields - } - - fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + #new_fn_idents - methods.add_method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { + builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| { Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone()))) }); - methods.add_function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { + builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { Ok(crate::ScriptBorrow::from_component::<#path>(None)) }); @@ -667,6 +669,8 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt #vec_wrapper_methods #custom_methods + + Ok(()) } } @@ -677,5 +681,20 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt 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/lua/providers/math.rs b/lyra-scripting/src/lua/providers/math.rs index ac767ee..f0a33b7 100644 --- a/lyra-scripting/src/lua/providers/math.rs +++ b/lyra-scripting/src/lua/providers/math.rs @@ -1,6 +1,5 @@ -use elua::TableProxy; use lyra_ecs::World; -use lyra_game::math::{Quat, Transform, Vec3}; +use crate::lua::wrappers::{LuaQuat, LuaTransform, LuaVec3}; use crate::ScriptData; use crate::lua::RegisterLuaType; @@ -13,31 +12,21 @@ impl ScriptApiProvider for LyraMathApiProvider { type ScriptContext = LuaContext; fn prepare_world(&mut self, world: &mut World) { - // TODO - /* world.register_lua_wrapper::(); - world.register_lua_wrapper::(); */ - world.register_lua_table_proxy::(); - world.register_lua_table_proxy::(); - world.register_lua_table_proxy::(); + world.register_lua_wrapper::(); + world.register_lua_wrapper::(); + world.register_lua_wrapper::(); } fn expose_api(&mut self, _data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { let ctx = ctx.lock().unwrap(); - let bytes = include_bytes!("../../../scripts/lua/math/vec3.lua"); - ctx.load("lyra/math/vec2.lua", bytes.as_slice())?.execute(())?; + /* let bytes = include_bytes!("../../../scripts/lua/math/transform.lua"); + ctx.load("lyra/math/transform.lua", bytes.as_slice())?.execute(())?; */ - let bytes = include_bytes!("../../../scripts/lua/math/quat.lua"); - ctx.load("lyra/math/quat.lua", bytes.as_slice())?.execute(())?; - - let bytes = include_bytes!("../../../scripts/lua/math/transform.lua"); - ctx.load("lyra/math/transform.lua", bytes.as_slice())?.execute(())?; - - // TODO - //let globals = ctx.globals()?; - //globals.set("Vec3", elua::Proxy::::from(LuaVec3(Vec3::ZERO)))?; - //globals.set("Vec3", ctx.create_proxy::()?)?; - //globals.set("Transform", ctx.create_proxy::()?)?; + let globals = ctx.globals()?; + globals.set("Vec3", ctx.create_proxy::()?)?; + globals.set("Quat", ctx.create_proxy::()?)?; + globals.set("Transform", ctx.create_proxy::()?)?; Ok(()) } @@ -49,156 +38,4 @@ impl ScriptApiProvider for LyraMathApiProvider { fn update_script_environment(&mut self, _: crate::ScriptWorldPtr, _: &crate::ScriptData, _: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { Ok(()) } -} - -#[derive(Clone, Copy)] -struct LuaVec3(Vec3); - -impl TableProxy for LuaVec3 { - fn from_table<'a>(_: &'a elua::State, table: elua::Table<'a>) -> elua::Result { - let x: f32 = table.get("x")?; - let y: f32 = table.get("y")?; - let z: f32 = table.get("z")?; - - Ok(LuaVec3(Vec3 { - x, - y, - z, - })) - } - - fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { - let globals = state.globals()?; - let vec3: elua::Table = globals.get("Vec3")?; - let new_fn: elua::Function = vec3.get("new")?; - new_fn.exec((vec3, self.0.x, self.0.y, self.0.z)) - } - - fn table_name() -> String { - "Vec3".to_string() - } -} - -#[derive(Clone, Copy)] -struct LuaQuat(Quat); - -impl TableProxy for LuaQuat { - fn from_table<'a>(_: &'a elua::State, table: elua::Table<'a>) -> elua::Result { - let x: f32 = table.get("x")?; - let y: f32 = table.get("y")?; - let z: f32 = table.get("z")?; - let w: f32 = table.get("w")?; - - Ok(LuaQuat(Quat::from_xyzw(x, y, z, w))) - } - - fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { - let globals = state.globals()?; - let quat: elua::Table = globals.get("Quat")?; - let new_fn: elua::Function = quat.get("new")?; - new_fn.exec((quat, self.0.x, self.0.y, self.0.z, self.0.w)) - } - - fn table_name() -> String { - "Quat".to_string() - } -} - -#[derive(Clone, Copy)] -struct LuaTransform(Transform); - -impl TableProxy for LuaTransform { - fn from_table<'a>(lua: &'a elua::State, table: elua::Table<'a>) -> elua::Result { - let translation: elua::Table = table.get("translation")?; - let rotation: elua::Table = table.get("rotation")?; - let scale: elua::Table = table.get("scale")?; - - let translation = LuaVec3::from_table(lua, translation)?; - let rotation = LuaQuat::from_table(lua, rotation)?; - let scale = LuaVec3::from_table(lua, scale)?; - - Ok(LuaTransform(Transform::new(translation.0, rotation.0, scale.0))) - } - - fn as_table<'a>(&self, state: &'a elua::State) -> elua::Result> { - let globals = state.globals()?; - let transform: elua::Table = globals.get("Transform")?; - let new_fn: elua::Function = transform.get("new")?; - - let translation = LuaVec3(self.0.translation).as_table(state)?; - let rotation = LuaQuat(self.0.rotation).as_table(state)?; - let scale = LuaVec3(self.0.scale).as_table(state)?; - - new_fn.exec((transform, translation, rotation, scale)) - } - - fn table_name() -> String { - "Transform".to_string() - } - - -} - -/* #[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)] -pub struct LuaVec3(#[reflect(skip)] math::Vec3); - -impl From for LuaVec3 { - fn from(value: math::Vec3) -> Self { - Self(value) - } -} - -impl<'lua> mlua::IntoLua<'lua> for LuaVec3 { - fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult> { - let globals = lua.globals(); - - let v3 = globals.get::<_, mlua::Table>("Vec3")?; - let v3_new = v3.get::<_, mlua::Function>("new")?; - v3_new.call::<_, mlua::Table>((v3, self.0.x, self.0.y, self.0.z)) - .and_then(|t| t.into_lua(lua)) - } -} - -#[derive(Clone, Copy, PartialEq, Debug, lyra_reflect::Reflect)] -pub struct LuaQuat(#[reflect(skip)] math::Quat); - -impl From for LuaQuat { - fn from(value: math::Quat) -> Self { - Self(value) - } -} - -impl<'lua> mlua::IntoLua<'lua> for LuaQuat { - fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult> { - let globals = lua.globals(); - - let q = globals.get::<_, mlua::Table>("Quat")?; - let q_new = q.get::<_, mlua::Function>("new")?; - q_new.call::<_, mlua::Table>((q, self.0.x, self.0.y, self.0.z, self.0.w)) - .and_then(|t| t.into_lua(lua)) - } -} - -#[derive(Clone, Copy, Debug, lyra_reflect::Reflect)] -pub struct LuaTransform(#[reflect(skip)] math::Transform); - -impl From for LuaTransform { - fn from(value: math::Transform) -> Self { - Self(value) - } -} - -impl<'lua> mlua::IntoLua<'lua> for LuaTransform { - fn into_lua(self, lua: &'lua mlua::prelude::Lua) -> mlua::prelude::LuaResult> { - let globals = lua.globals(); - - let translation = LuaVec3(self.0.translation).into_lua(lua)?; - let rot = LuaQuat(self.0.rotation).into_lua(lua)?; - let scale = LuaVec3(self.0.scale).into_lua(lua)?; - - let transf = globals.get::<_, mlua::Table>("Transform")?; - let transf_new = transf.get::<_, mlua::Function>("new")?; - transf_new.call::<_, mlua::Table>((transf, translation, rot, scale)) - .and_then(|t| t.into_lua(lua)) - } -} */ \ No newline at end of file +} \ No newline at end of file diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index 3dccd70..3e4bca7 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -1,6 +1,6 @@ use std::sync::Mutex; -use elua::{AsLua, StdLibrary}; +use elua::{AsLua, StdLibraries}; use crate::{ScriptHost, ScriptError, ScriptWorldPtr, ScriptEntity}; @@ -25,12 +25,10 @@ impl ScriptHost for LuaHost { fn load_script(&mut self, script: &[u8], script_data: &crate::ScriptData, providers: &mut crate::ScriptApiProviders) -> Result { let mut ctx = Mutex::new({ let s = elua::State::new(); - s.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]); + s.expose_libraries(StdLibraries::all()); s }); - //Mutex::new(elua::State::new_with_libraries(StdLibraries::all())); - for provider in providers.apis.iter_mut() { provider.expose_api(script_data, &mut ctx)?; } diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 8f0778f..00c101c 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -75,7 +75,8 @@ impl elua::Userdata for ScriptWorldPtr { elua::Value::Table(t) => { let name: String = t.get(elua::MetaMethod::Name)?; - let lookup = world.get_resource::(); + 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 { diff --git a/lyra-scripting/src/lua/wrappers/math.rs b/lyra-scripting/src/lua/wrappers/math.rs index 05a38e7..7030619 100644 --- a/lyra-scripting/src/lua/wrappers/math.rs +++ b/lyra-scripting/src/lua/wrappers/math.rs @@ -19,7 +19,7 @@ use crate as lyra_scripting; Mod(LuaVec2, f32), Eq, Unm, ToString ) -); +); */ wrap_math_vec_copy!( math::Vec3, derives(PartialEq), @@ -32,7 +32,7 @@ wrap_math_vec_copy!( Mod(LuaVec3, f32), Eq, Unm, ToString ) -); */ +); // ================================================= @@ -396,7 +396,7 @@ wrap_math_vec_copy!( -/* wrap_math_vec_copy!( +wrap_math_vec_copy!( math::Quat, derives(PartialEq), no_new, @@ -409,55 +409,55 @@ wrap_math_vec_copy!( Div(f32), ), custom_methods { - methods.add_function("new", |_, (x, y, z, w)| { + builder.function("new", |_, (x, y, z, w)| { Ok(Self(math::Quat::from_xyzw(x, y, z, w))) }); - methods.add_function("from_rotation_x", |_, (rad,)| { + builder.function("from_rotation_x", |_, (rad,)| { let q = math::Quat::from_rotation_x(rad); Ok(Self(q)) }); - methods.add_function("from_rotation_y", |_, (rad,)| { + builder.function("from_rotation_y", |_, (rad,)| { let q = math::Quat::from_rotation_y(rad); Ok(Self(q)) }); - methods.add_function("from_rotation_z", |_, (rad,)| { + builder.function("from_rotation_z", |_, (rad,)| { let q = math::Quat::from_rotation_z(rad); Ok(Self(q)) }); - methods.add_method("dot", |_, this, (rhs,): (Self,)| { + builder.method("dot", |_, this, (rhs,): (Self,)| { Ok(this.dot(rhs.0)) }); - methods.add_method("length", |_, this, ()| { + builder.method("length", |_, this, ()| { Ok(this.length()) }); - methods.add_method("length_squared", |_, this, ()| { + builder.method("length_squared", |_, this, ()| { Ok(this.length_squared()) }); - methods.add_method("normalize", |_, this, ()| { + builder.method("normalize", |_, this, ()| { Ok(Self(this.normalize())) }); - methods.add_method("mult_quat", |_, this, (rhs,): (Self,)| { + builder.method("mult_quat", |_, this, (rhs,): (Self,)| { Ok(Self(this.0 * rhs.0)) }); - methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| { + builder.method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| { Ok(LuaVec3(this.0 * rhs.0)) }); // manually implemented here since it doesn't return `Self` - methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (rhs,): (LuaVec3,)| { + builder.meta_method(elua::MetaMethod::Mul, |_, this, (rhs,): (LuaVec3,)| { Ok(LuaVec3(this.0 * rhs.0)) }); - methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { + builder.method("lerp", |_, this, (rhs, alpha): (Self, f32)| { Ok(Self(this.lerp(*rhs, alpha))) }); } @@ -469,111 +469,119 @@ wrap_math_vec_copy!( no_new, metamethods(ToString, Eq), custom_fields { - fields.add_field_method_get("translation", |_, this| { + builder.field_getter("translation", |_, this| { Ok(LuaVec3(this.translation)) }); - fields.add_field_method_set("translation", |_, this, v: LuaVec3| { + builder.field_setter("translation", |_, this, v: LuaVec3| { this.translation = *v; Ok(()) }); - fields.add_field_method_get("rotation", |_, this| { + builder.field_getter("rotation", |_, this| { Ok(LuaQuat(this.rotation)) }); - fields.add_field_method_set("rotation", |_, this, v: LuaQuat| { + builder.field_setter("rotation", |_, this, v: LuaQuat| { this.rotation = *v; Ok(()) }); - fields.add_field_method_get("scale", |_, this| { + builder.field_getter("scale", |_, this| { Ok(LuaVec3(this.scale)) }); - fields.add_field_method_set("scale", |_, this, v: LuaVec3| { + builder.field_setter("scale", |_, this, v: LuaVec3| { this.scale = *v; Ok(()) }); }, custom_methods { - methods.add_function("default", |_, ()| { + builder.function("default", |_, ()| { Ok(Self(math::Transform::default())) }); - methods.add_function("new", |_, (pos, rot, scale): (LuaVec3, LuaQuat, LuaVec3)| { + builder.function("new", |_, (pos, rot, scale): (LuaVec3, LuaQuat, LuaVec3)| { Ok(Self(math::Transform::new(*pos, *rot, *scale))) }); - - methods.add_function("from_translation", |_, (pos,): (LuaVec3,)| { + + builder.function("from_translation", |_, (pos,): (LuaVec3,)| { Ok(Self(math::Transform::from_translation(*pos))) }); - - methods.add_function("from_xyz", |_, (x, y, z)| { + + builder.function("from_xyz", |_, (x, y, z)| { Ok(Self(math::Transform::from_xyz(x, y, z))) }); - methods.add_method("forward", |_, this, ()| { + builder.method("clone", |_, this, ()| { + Ok(this.clone()) + }); + + builder.method("forward", |_, this, ()| { Ok(LuaVec3(this.forward())) }); - methods.add_method("left", |_, this, ()| { + builder.method("left", |_, this, ()| { Ok(LuaVec3(this.left())) }); - methods.add_method("up", |_, this, ()| { + builder.method("up", |_, this, ()| { Ok(LuaVec3(this.up())) }); - methods.add_method_mut("rotate", |_, this, (quat,): (LuaQuat,)| { + builder.method_mut("rotate", |_, this, (quat,): (LuaQuat,)| { this.rotate(*quat); Ok(()) }); - methods.add_method_mut("rotate_x", |_, this, (deg,): (f32,)| { + builder.method_mut("rotate_x", |_, this, (deg,): (f32,)| { this.rotate_x(math::Angle::Degrees(deg)); Ok(()) }); - methods.add_method_mut("rotate_y", |_, this, (deg,): (f32,)| { + builder.method_mut("rotate_y", |_, this, (deg,): (f32,)| { this.rotate_y(math::Angle::Degrees(deg)); Ok(()) }); - methods.add_method_mut("rotate_z", |_, this, (deg,): (f32,)| { + builder.method_mut("rotate_z", |_, this, (deg,): (f32,)| { this.rotate_z(math::Angle::Degrees(deg)); Ok(()) }); - methods.add_method_mut("rotate_x_rad", |_, this, (rad,): (f32,)| { + builder.method_mut("rotate_x_rad", |_, this, (rad,): (f32,)| { this.rotate_x(math::Angle::Radians(rad)); Ok(()) }); - methods.add_method_mut("rotate_y_rad", |_, this, (rad,): (f32,)| { + builder.method_mut("rotate_y_rad", |_, this, (rad,): (f32,)| { this.rotate_y(math::Angle::Radians(rad)); Ok(()) }); - methods.add_method_mut("rotate_z_rad", |_, this, (rad,): (f32,)| { + builder.method_mut("rotate_z_rad", |_, this, (rad,): (f32,)| { this.rotate_z(math::Angle::Radians(rad)); Ok(()) }); - methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| { + builder.method_mut("translate", |_, this, (x, y, z): (f32, f32, f32)| { + this.translate(x, y, z); + Ok(()) + }); + + builder.method("lerp", |_, this, (rhs, alpha): (Self, f32)| { Ok(Self(this.lerp(*rhs, alpha))) }); // rotate a transform - methods.add_meta_method(mlua::MetaMethod::Mul, |_, this, (quat,): (LuaQuat,)| { + builder.meta_method(elua::MetaMethod::Mul, |_, this, (quat,): (LuaQuat,)| { let mut t = *this; t.rotation *= *quat; Ok(t) }); // move a transform - methods.add_meta_method(mlua::MetaMethod::Add, |_, this, (pos,): (LuaVec3,)| { + builder.meta_method(elua::MetaMethod::Add, |_, this, (pos,): (LuaVec3,)| { let mut t = *this; t.translation += *pos; Ok(t) }); } ); - */ \ No newline at end of file diff --git a/shell.nix b/shell.nix index 7039c42..551dc4c 100755 --- a/shell.nix +++ b/shell.nix @@ -12,9 +12,11 @@ mkShell rec { heaptrack mold udev + lua5_4_compat ]; buildInputs = [ - udev alsa-lib vulkan-loader + udev alsa-lib libGL gcc + vulkan-loader vulkan-headers vulkan-tools xorg.libX11 xorg.libXcursor xorg.libXi xorg.libXrandr # To use the x11 feature libxkbcommon wayland # To use the wayland feature ]; -- 2.40.1 From e354528942b5863645f4ef67180abd5638b26200 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 19 Feb 2024 23:14:38 -0500 Subject: [PATCH 26/41] scripting: Implement LuaVec3:move_by, and add more methods for Lua on math types --- examples/testbed/scripts/test.lua | 5 + lyra-scripting/elua | 2 +- .../lyra-scripting-derive/src/lib.rs | 33 +++- .../lyra-scripting-derive/src/mat_wrapper.rs | 34 ++-- lyra-scripting/src/lua/wrappers/math.rs | 174 ++++++++++++------ 5 files changed, 170 insertions(+), 78 deletions(-) diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 0294bac..7c2af70 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -14,6 +14,11 @@ 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 d32c138..22b6d21 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit d32c138e996ec2fe17b7a5f7c0d3ff1562e446f0 +Subproject commit 22b6d218bdb6a5aafc0efd4fb80a67d2c796d0e7 diff --git a/lyra-scripting/lyra-scripting-derive/src/lib.rs b/lyra-scripting/lyra-scripting-derive/src/lib.rs index 21367b1..5f90c92 100644 --- a/lyra-scripting/lyra-scripting-derive/src/lib.rs +++ b/lyra-scripting/lyra-scripting-derive/src/lib.rs @@ -38,6 +38,22 @@ impl MetaMethod { s.starts_with("Lua") } + /// Returns a boolean indiciating if the metamethod has takes in any arguments + fn does_metamethod_have_arg(metamethod: &Ident) -> bool { + let mm_str = metamethod.to_string(); + let mm_str = mm_str.as_str(); + match mm_str { + "Add" | "Sub" | "Div" | "Mul" | "Mod" | "Eq" | "Shl" | "Shr" | "BAnd" | "BOr" + | "BXor" => { + true + }, + "Unm" | "BNot" | "ToString" => { + false + }, + _ => todo!(), + } + } + /// returns the tokens of the body of the metamethod /// /// Parameters @@ -139,11 +155,20 @@ impl MetaMethod { }; let body = Self::get_method_body(&self.ident, other); - quote! { - builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| { - #body - }); + if Self::does_metamethod_have_arg(&self.ident) { + quote! { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, (v,): (#wrapper_ident,)| { + #body + }); + } + } else { + quote! { + builder.meta_method(elua::MetaMethod::#mt_ident, |_, this, ()| { + #body + }); + } } + } else if self.mods.len() == 1 { let first = self.mods.iter().next().unwrap(); let body = Self::get_body_for_arg(&self.ident, first, quote!(v)); diff --git a/lyra-scripting/lyra-scripting-derive/src/mat_wrapper.rs b/lyra-scripting/lyra-scripting-derive/src/mat_wrapper.rs index fdb0e39..770451f 100644 --- a/lyra-scripting/lyra-scripting-derive/src/mat_wrapper.rs +++ b/lyra-scripting/lyra-scripting-derive/src/mat_wrapper.rs @@ -9,9 +9,9 @@ pub(crate) struct MatWrapper { impl MatWrapper { pub fn to_field_tokens(&self, wrapped_path: &Path, wrapper_ident: &Ident) -> proc_macro2::TokenStream { quote! { - fields.add_field("ZERO", #wrapper_ident(#wrapped_path::ZERO)); - fields.add_field("IDENTITY", #wrapper_ident(#wrapped_path::IDENTITY)); - fields.add_field("NAN", #wrapper_ident(#wrapped_path::NAN)); + builder.field("ZERO", #wrapper_ident(#wrapped_path::ZERO)); + builder.field("IDENTITY", #wrapper_ident(#wrapped_path::IDENTITY)); + builder.field("NAN", #wrapper_ident(#wrapped_path::NAN)); } } @@ -28,37 +28,37 @@ impl MatWrapper { let element_ty = quote!(f32); quote! { - methods.add_function("from_cols", + builder.function("from_cols", |_, (x_axis, y_axis): (#column_type, #column_type)| { Ok(#wrapper_ident(#wrapped_path::from_cols(x_axis.0, y_axis.0))) }); - methods.add_function("from_cols_array", + builder.function("from_cols_array", |_, (arr,): ([#element_ty; #column_size_xtwo],)| { Ok(#wrapper_ident(#wrapped_path::from_cols_array(&arr))) }); - methods.add_function("from_cols_array_2d", + builder.function("from_cols_array_2d", |_, (arr,): ([[#element_ty; #column_size]; #column_size],)| { Ok(#wrapper_ident(#wrapped_path::from_cols_array_2d(&arr))) }); - methods.add_function("from_diagonal", + builder.function("from_diagonal", |_, (diag,): (#column_type,)| { Ok(#wrapper_ident(#wrapped_path::from_diagonal(diag.0))) }); - methods.add_method("col", + builder.method("col", |_, this, (idx,): (usize,)| { Ok(#column_type(this.col(idx))) }); - methods.add_method("row", + builder.method("row", |_, this, (idx,): (usize,)| { Ok(#column_type(this.row(idx))) }); - methods.add_method_mut("set_col", + builder.method_mut("set_col", |_, this, (idx, newc): (usize, #column_type)| { let col = this.col_mut(idx); *col = newc.0; @@ -66,38 +66,38 @@ impl MatWrapper { Ok(()) }); - methods.add_method("is_finite", + builder.method("is_finite", |_, this, (): ()| { Ok(this.is_finite()) }); - methods.add_method("is_nan", + builder.method("is_nan", |_, this, (): ()| { Ok(this.is_nan()) }); - methods.add_method("transpose", + builder.method("transpose", |_, this, (): ()| { Ok(#wrapper_ident(this.0.transpose())) }); - methods.add_method("determinant", + builder.method("determinant", |_, this, (): ()| { Ok(this.determinant()) }); - methods.add_method("inverse", + builder.method("inverse", |_, this, (): ()| { Ok(#wrapper_ident(this.inverse())) }); - methods.add_method("abs_diff_eq", + builder.method("abs_diff_eq", |_, this, (rhs, max_abs_diff): (#wrapper_ident, f32)| { Ok(this.abs_diff_eq(rhs.0, max_abs_diff)) }); // TODO: After all DMat's are implemented - /* methods.add_method("as_dmat", + /* builder.method("as_dmat", |_, this, (rhs, max_abs_diff): (#wrapper_ident, f32)| { Ok(D#wrapper_ident(this.as_dmat)) }); */ diff --git a/lyra-scripting/src/lua/wrappers/math.rs b/lyra-scripting/src/lua/wrappers/math.rs index 7030619..38cd41a 100644 --- a/lyra-scripting/src/lua/wrappers/math.rs +++ b/lyra-scripting/src/lua/wrappers/math.rs @@ -7,7 +7,7 @@ use lyra_scripting_derive::wrap_math_vec_copy; use crate as lyra_scripting; // f32 types -/* wrap_math_vec_copy!( +wrap_math_vec_copy!( math::Vec2, derives(PartialEq), fields(x, y), @@ -18,8 +18,21 @@ use crate as lyra_scripting; Mul(LuaVec2, f32), Mod(LuaVec2, f32), Eq, Unm, ToString - ) -); */ + ), + custom_methods { + builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| { + let vals_clone = vals.clone(); + if let Some((x, y)) = vals.try_into_vals::<(f32, f32)>(lua)? { + this.x += x; + this.y += y; + } else if let Some(v) = vals_clone.try_into_vals::(lua)? { + this.0 += v.0; + } + + Ok(()) + }); + } +); wrap_math_vec_copy!( math::Vec3, derives(PartialEq), @@ -31,9 +44,36 @@ wrap_math_vec_copy!( Mul(LuaVec3, f32), Mod(LuaVec3, f32), Eq, Unm, ToString - ) + ), + custom_methods { + builder.method_mut("move_by", |lua, this, vals: elua::ValueVec| { + let vals_clone = vals.clone(); + if let Some((x, y, z)) = vals.try_into_vals::<(f32, f32, f32)>(lua)? { + this.x += x; + this.y += y; + this.z += z; + } else if let Some(v) = vals_clone.try_into_vals::(lua)? { + this.0 += v.0; + } + + Ok(()) + }); + } ); +wrap_math_vec_copy!( + math::Vec4, + derives(PartialEq), + fields(w, x, y, z), + metamethods( + Add(LuaVec4, f32), + Sub(LuaVec4, f32), + Div(LuaVec4, f32), + Mul(LuaVec4, f32), + Mod(LuaVec4, f32), + Eq, Unm + ) +); // ================================================= @@ -51,19 +91,7 @@ wrap_math_vec_copy!( Eq, Unm ) ); -wrap_math_vec_copy!( - math::Vec4, - derives(PartialEq), - fields(w, x, y, z), - metamethods( - Add(LuaVec4, f32), - Sub(LuaVec4, f32), - Div(LuaVec4, f32), - Mul(LuaVec4, f32), - Mod(LuaVec4, f32), - Eq, Unm - ) -); + // f64 types wrap_math_vec_copy!( @@ -328,30 +356,30 @@ wrap_math_vec_copy!( BXor(LuaU64Vec4, u64), Eq, BNot ) -); +);*/ // bool types -wrap_math_vec_copy!( +/* wrap_math_vec_copy!( math::BVec2, derives(PartialEq, Eq, Hash), fields(x, y), - metamethods(Eq, BAnd, BOr, BXor, BNot) + metamethods(Eq, BAnd, BOr, BXOr, BNot) ); wrap_math_vec_copy!( math::BVec3, derives(PartialEq, Eq, Hash), fields(x, y, z), - metamethods(Eq, BAnd, BOr, BXor, BNot) + metamethods(Eq, BAnd, BOr, BXOr, BNot) ); wrap_math_vec_copy!( math::BVec4, derives(PartialEq, Eq, Hash), fields(w, x, y, z), - metamethods(Eq, BAnd, BOr, BXor, BNot) -); + metamethods(Eq, BAnd, BOr, BXOr, BNot) +); */ // mat2 -wrap_math_vec_copy!( +/* wrap_math_vec_copy!( math::Mat2, derives(PartialEq), no_new, @@ -367,6 +395,7 @@ wrap_math_vec_copy!( ) ); */ +// TODO /* wrap_math_vec_copy!( math::Mat4, derives(PartialEq), @@ -392,8 +421,31 @@ wrap_math_vec_copy!( +/// A macro that generates field getters and setters for lua wrapped types. +macro_rules! wrapped_field_getsetters { + ($builder: ident, $name: literal, $field: ident, $type: ident) => { + $builder.field_getter($name, |_, this| { + Ok($type(this.$field)) + }); + $builder.field_setter($name, |_, this, v: $type| { + this.$field = *v; + Ok(()) + }); + }; +} - +/// A macro that generates field getters and setters for types that already implement As/FromLua. +macro_rules! field_getsetters { + ($builder: ident, $name: literal, $field: ident, $type: ty) => { + $builder.field_getter($name, |_, this| { + Ok(this.$field) + }); + $builder.field_setter($name, |_, this, v: $type| { + this.$field = v; + Ok(()) + }); + }; +} wrap_math_vec_copy!( @@ -403,12 +455,19 @@ wrap_math_vec_copy!( metamethods( Eq, // __mul for LuaVec3 is manually implemented below since it doesn't return Self - Mul(LuaQuat, f32), + //Mul(LuaQuat, f32), Add, Sub, Div(f32), ), + custom_fields { + field_getsetters!(builder, "x", x, f32); + field_getsetters!(builder, "y", y, f32); + field_getsetters!(builder, "z", z, f32); + field_getsetters!(builder, "w", w, f32); + }, custom_methods { + // manually implemented since Quat doesn't have a `new` function builder.function("new", |_, (x, y, z, w)| { Ok(Self(math::Quat::from_xyzw(x, y, z, w))) }); @@ -440,21 +499,44 @@ wrap_math_vec_copy!( Ok(this.length_squared()) }); - builder.method("normalize", |_, this, ()| { - Ok(Self(this.normalize())) + builder.method_mut("normalize", |_, this, ()| { + this.0 = this.normalize(); + Ok(()) }); - builder.method("mult_quat", |_, this, (rhs,): (Self,)| { - Ok(Self(this.0 * rhs.0)) + builder.method_mut("mult_quat", |_, this, (rhs,): (Self,)| { + this.0 *= rhs.0; + Ok(()) }); builder.method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| { Ok(LuaVec3(this.0 * rhs.0)) }); - // manually implemented here since it doesn't return `Self` - builder.meta_method(elua::MetaMethod::Mul, |_, this, (rhs,): (LuaVec3,)| { - Ok(LuaVec3(this.0 * rhs.0)) + // manually implemented here since multiplying may not return `Self`. + builder.meta_method(elua::MetaMethod::Mul, |lua, this, (val,): (elua::Value,)| { + use elua::AsLua; + + match val { + elua::Value::Userdata(ud) => { + if ud.is::()? { + let v3 = ud.as_ref::()?; + LuaVec3(this.0 * v3.0) + .as_lua(lua) + } else { + let quat = ud.as_ref::()?; + LuaQuat(this.0 * quat.0) + .as_lua(lua) + } + }, + elua::Value::Number(n) => { + LuaQuat(this.0 * (n as f32)) + .as_lua(lua) + }, + _ => { + todo!() + } + } }); builder.method("lerp", |_, this, (rhs, alpha): (Self, f32)| { @@ -469,29 +551,9 @@ wrap_math_vec_copy!( no_new, metamethods(ToString, Eq), custom_fields { - builder.field_getter("translation", |_, this| { - Ok(LuaVec3(this.translation)) - }); - builder.field_setter("translation", |_, this, v: LuaVec3| { - this.translation = *v; - Ok(()) - }); - - builder.field_getter("rotation", |_, this| { - Ok(LuaQuat(this.rotation)) - }); - builder.field_setter("rotation", |_, this, v: LuaQuat| { - this.rotation = *v; - Ok(()) - }); - - builder.field_getter("scale", |_, this| { - Ok(LuaVec3(this.scale)) - }); - builder.field_setter("scale", |_, this, v: LuaVec3| { - this.scale = *v; - Ok(()) - }); + wrapped_field_getsetters!(builder, "translation", translation, LuaVec3); + wrapped_field_getsetters!(builder, "rotation", rotation, LuaQuat); + wrapped_field_getsetters!(builder, "scale", scale, LuaVec3); }, custom_methods { builder.function("default", |_, ()| { -- 2.40.1 From 380c8df7408734d0e630cc457168cf2b94bb686c Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 23 Feb 2024 16:34:21 -0500 Subject: [PATCH 27/41] ecs: implement deferred system commands, change the way system fn arguments are implemented --- lyra-ecs/Cargo.toml | 2 + lyra-ecs/src/archetype.rs | 4 +- lyra-ecs/src/command.rs | 114 +++++++++++++++++++ lyra-ecs/src/entity.rs | 58 ++++++++++ lyra-ecs/src/lib.rs | 22 ++++ lyra-ecs/src/query/borrow.rs | 2 +- lyra-ecs/src/query/entities.rs | 2 +- lyra-ecs/src/query/view.rs | 2 +- lyra-ecs/src/system/batched.rs | 4 + lyra-ecs/src/system/fn_sys.rs | 202 ++++++++++++++++++++------------- lyra-ecs/src/system/graph.rs | 19 +++- lyra-ecs/src/system/mod.rs | 2 + lyra-ecs/src/world.rs | 103 ++++++++--------- 13 files changed, 396 insertions(+), 140 deletions(-) create mode 100644 lyra-ecs/src/command.rs create mode 100644 lyra-ecs/src/entity.rs diff --git a/lyra-ecs/Cargo.toml b/lyra-ecs/Cargo.toml index 9e49722..fe1e97f 100644 --- a/lyra-ecs/Cargo.toml +++ b/lyra-ecs/Cargo.toml @@ -13,6 +13,8 @@ lyra-ecs-derive = { path = "./lyra-ecs-derive" } lyra-math = { path = "../lyra-math", optional = true } anyhow = "1.0.75" thiserror = "1.0.50" +unique = "0.9.1" +paste = "1.0.14" [dev-dependencies] rand = "0.8.5" # used for tests diff --git a/lyra-ecs/src/archetype.rs b/lyra-ecs/src/archetype.rs index f91f512..d00f607 100644 --- a/lyra-ecs/src/archetype.rs +++ b/lyra-ecs/src/archetype.rs @@ -1,6 +1,6 @@ use std::{ptr::{NonNull, self}, alloc::{self, Layout, alloc, dealloc}, mem, collections::HashMap, cell::{RefCell, Ref, RefMut, BorrowError, BorrowMutError}, ops::{DerefMut, Deref}}; -use crate::{world::{Entity, ArchetypeEntityId}, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick}; +use crate::{world::ArchetypeEntityId, bundle::Bundle, component_info::ComponentInfo, DynTypeId, Tick, Entity}; #[derive(Clone)] pub struct ComponentColumn { @@ -419,7 +419,7 @@ mod tests { use rand::Rng; - use crate::{tests::{Vec2, Vec3}, world::{Entity, EntityId}, bundle::Bundle, ComponentInfo, MemoryLayout, DynTypeId, DynamicBundle, Tick}; + use crate::{bundle::Bundle, tests::{Vec2, Vec3}, ComponentInfo, DynTypeId, DynamicBundle, Entity, EntityId, MemoryLayout, Tick}; use super::Archetype; diff --git a/lyra-ecs/src/command.rs b/lyra-ecs/src/command.rs new file mode 100644 index 0000000..d487d7c --- /dev/null +++ b/lyra-ecs/src/command.rs @@ -0,0 +1,114 @@ +use std::{cell::RefMut, collections::VecDeque, mem, ptr::{self, NonNull}}; + +use unique::Unique; + +use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World}; + +pub trait Command { + fn run(self, world: &mut World) -> anyhow::Result<()>; +} + +impl Command for F +where + F: FnOnce(&mut World) -> anyhow::Result<()> +{ + fn run(self, world: &mut World) -> anyhow::Result<()> { + self(world) + } +} + +type RunCommand = unsafe fn(cmd: Unique<()>, world: &mut World) -> anyhow::Result<()>; + +#[derive(Default)] +pub struct CommandQueue(VecDeque<(RunCommand, Unique<()>)>); + +pub struct Commands<'a, 'b> { + queue: &'b mut CommandQueue, + entities: &'a mut Entities, +} + +impl<'a, 'b> Commands<'a, 'b> { + pub fn new(queue: &'b mut CommandQueue, world: &'a mut World) -> Self { + Self { + queue, + entities: &mut world.entities, + } + } + + /// Add a command to the end of the command queue + pub fn add(&mut self, mut cmd: C) { + // get an owned pointer to the command, then forget it to ensure its destructor isn't ran + let ptr = Unique::from(&mut cmd).cast::<()>(); + mem::forget(cmd); + + let run_fn = |cmd_ptr: Unique<()>, world: &mut World| unsafe { + let cmd = cmd_ptr.cast::(); + let cmd = ptr::read(cmd.as_ptr()); + cmd.run(world)?; + + Ok(()) + }; + + self.queue.0.push_back((run_fn, ptr)); + } + + pub fn spawn(&mut self, mut bundle: B) -> Entity { + let e = self.entities.reserve(); + + let bundle_ptr = Unique::from(&mut bundle); + mem::forget(bundle); + //let bundle_box = Box::new(bundle); + + self.add(move |world: &mut World| { + let bundle = unsafe { ptr::read(bundle_ptr.as_ptr()) }; + world.spawn_into(e, bundle); + Ok(()) + }); + + e + } + + /// Execute all commands in the queue, in order of insertion + pub fn execute(&mut self, world: &mut World) -> anyhow::Result<()> { + while let Some((cmd_fn, cmd_ptr)) = self.queue.0.pop_front() { + unsafe { + cmd_fn(cmd_ptr, world)?; + } + } + + Ok(()) + } +} + +impl FnArgFetcher for Commands<'_, '_> { + type State = CommandQueue; + type Arg<'a, 'state> = Commands<'a, 'state>; + + fn world_access(&self) -> Access { + Access::Write + } + + unsafe fn get<'a, 'state>(state: &'state mut Self::State, mut world_ptr: ptr::NonNull) -> Self::Arg<'a, 'state> { + let world = world_ptr.as_mut(); + Commands::new(state, world) + } + + fn create_state(_: NonNull) -> Self::State { + CommandQueue::default() + } + + fn apply_deferred<'a>(mut state: Self::State, mut world_ptr: NonNull) { + let world = unsafe { world_ptr.as_mut() }; + + let mut cmds = Commands::new(&mut state, world); + // safety: Commands has a mut borrow to entities in the world + let world = unsafe { world_ptr.as_mut() }; + cmds.execute(world).unwrap() + } +} + +pub fn execute_deferred_commands(world: &mut World, mut commands: RefMut) -> anyhow::Result<()> { + commands.execute(world)?; + + Ok(()) +} \ No newline at end of file diff --git a/lyra-ecs/src/entity.rs b/lyra-ecs/src/entity.rs new file mode 100644 index 0000000..7fefa67 --- /dev/null +++ b/lyra-ecs/src/entity.rs @@ -0,0 +1,58 @@ +use std::collections::{HashMap, VecDeque}; + +use crate::Record; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct EntityId(pub u64); + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Entity { + pub(crate) id: EntityId, + pub(crate) generation: u64, +} + +pub struct Entities { + pub(crate) arch_index: HashMap, + dead: VecDeque, + next_id: EntityId, +} + +impl Default for Entities { + fn default() -> Self { + Self { + arch_index: Default::default(), + dead: Default::default(), + next_id: EntityId(0), + } + } +} + +impl Entities { + pub fn reserve(&mut self) -> Entity { + match self.dead.pop_front() { + Some(mut e) => { + e.generation += 1; + e + } + None => { + println!("id is {}", self.next_id.0); + let new_id = self.next_id; + self.next_id.0 += 1; + + Entity { + id: new_id, + generation: 0, + } + } + } + } + + /// Retrieves the Archetype Record for the entity + pub(crate) fn entity_record(&self, entity: Entity) -> Option { + self.arch_index.get(&entity.id).cloned() + } + + pub(crate) fn insert_entity_record(&mut self, entity: Entity, record: Record) { + self.arch_index.insert(entity.id, record); + } +} diff --git a/lyra-ecs/src/lib.rs b/lyra-ecs/src/lib.rs index e22f7f8..a66af95 100644 --- a/lyra-ecs/src/lib.rs +++ b/lyra-ecs/src/lib.rs @@ -8,11 +8,19 @@ pub(crate) mod lyra_engine { } pub mod archetype; +use std::ops::BitOr; + pub use archetype::*; +pub mod entity; +pub use entity::*; + pub mod world; pub use world::*; +pub mod command; +pub use command::*; + pub mod bundle; pub use bundle::*; @@ -48,4 +56,18 @@ pub enum Access { None, Read, Write, +} + +impl BitOr for Access { + type Output = Access; + + fn bitor(self, rhs: Self) -> Self::Output { + if self == Access::Write || rhs == Access::Write { + Access::Write + } else if self == Access::Read || rhs == Access::Read { + Access::Read + } else { + Access::None + } + } } \ No newline at end of file diff --git a/lyra-ecs/src/query/borrow.rs b/lyra-ecs/src/query/borrow.rs index 4ee6739..ce13ac2 100644 --- a/lyra-ecs/src/query/borrow.rs +++ b/lyra-ecs/src/query/borrow.rs @@ -221,7 +221,7 @@ impl AsQuery for &mut T { mod tests { use std::{mem::size_of, marker::PhantomData, ptr::NonNull}; - use crate::{world::{World, Entity, EntityId}, archetype::{Archetype, ArchetypeId}, query::{View, Fetch}, tests::Vec2, bundle::Bundle, DynTypeId, Tick}; + use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{Fetch, View}, tests::Vec2, world::World, DynTypeId, Entity, EntityId, Tick}; use super::{QueryBorrow, QueryBorrowMut, FetchBorrowMut}; diff --git a/lyra-ecs/src/query/entities.rs b/lyra-ecs/src/query/entities.rs index b1c28df..a75c33c 100644 --- a/lyra-ecs/src/query/entities.rs +++ b/lyra-ecs/src/query/entities.rs @@ -1,4 +1,4 @@ -use crate::{world::{Entity, World}, archetype::Archetype}; +use crate::{archetype::Archetype, world::World, Entity}; use super::{Fetch, Query, AsQuery}; diff --git a/lyra-ecs/src/query/view.rs b/lyra-ecs/src/query/view.rs index 43442d6..504c658 100644 --- a/lyra-ecs/src/query/view.rs +++ b/lyra-ecs/src/query/view.rs @@ -131,7 +131,7 @@ impl<'a, Q: Query> ViewOne<'a, Q> { } pub fn get(&self) -> Option> { - if let Some(record) = self.world.entity_index.get(&self.entity) { + if let Some(record) = self.world.entities.arch_index.get(&self.entity) { let arch = self.world.archetypes.get(&record.id) .expect("An invalid record was specified for an entity"); diff --git a/lyra-ecs/src/system/batched.rs b/lyra-ecs/src/system/batched.rs index a9ed48e..d924d0f 100644 --- a/lyra-ecs/src/system/batched.rs +++ b/lyra-ecs/src/system/batched.rs @@ -79,6 +79,10 @@ impl System for BatchedSystem { Ok(()) } + + fn execute_deferred(&mut self, _: std::ptr::NonNull) -> anyhow::Result<()> { + todo!() + } } impl IntoSystem<()> for BatchedSystem { diff --git a/lyra-ecs/src/system/fn_sys.rs b/lyra-ecs/src/system/fn_sys.rs index 08a6b9d..4252db7 100644 --- a/lyra-ecs/src/system/fn_sys.rs +++ b/lyra-ecs/src/system/fn_sys.rs @@ -1,13 +1,19 @@ use std::{ptr::NonNull, marker::PhantomData}; +use unique::Unique; +use paste::paste; use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}}; use super::{System, IntoSystem}; pub trait FnArgFetcher { - type Arg<'a>: FnArg; + /// stores data that persists after an execution of a system + type State: 'static; + + type Arg<'a, 'state>: FnArgFetcher; - fn new() -> Self; + //fn new() -> Self; + fn create_state(world: NonNull) -> Self::State; /// Return the appropriate world access if this fetcher gets the world directly. /// Return [`Access::None`] if you're only fetching components, or resources. @@ -20,7 +26,10 @@ pub trait FnArgFetcher { /// # Safety /// The system executor must ensure that on execution of the system, it will be safe to /// borrow the world. - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a>; + unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: NonNull) -> Self::Arg<'a, 'state>; + + /// Apply some action after the system was ran. + fn apply_deferred(state: Self::State, world: NonNull); } pub trait FnArg { @@ -29,8 +38,10 @@ pub trait FnArg { pub struct FnSystem { inner: F, - #[allow(dead_code)] - args: Args, + //#[allow(dead_code)] + //args: Args, + arg_state: Option>>, + _marker: PhantomData, } macro_rules! impl_fn_system_tuple { @@ -38,47 +49,87 @@ macro_rules! impl_fn_system_tuple { #[allow(non_snake_case)] impl System for FnSystem where - F: for<'a> FnMut($($name::Arg<'a>,)+) -> anyhow::Result<()>, + F: for<'a> FnMut($($name::Arg<'a, '_>,)+) -> anyhow::Result<()>, { fn world_access(&self) -> Access { todo!() } fn execute(&mut self, world: NonNull) -> anyhow::Result<()> { - $(let $name = unsafe { $name::new().get(world) };)+ + unsafe { + paste! { + $( + // get the arg fetcher, create its state, and get the arg + + let mut []: $name::State = $name::create_state(world); + let [<$name:lower>] = $name::get(&mut [], world); + + )+ + + (self.inner)($( [<$name:lower>] ),+)?; + + let mut state = Vec::new(); + $( + // type erase the now modified state, and store it + let [] = Unique::from(&mut []); + std::mem::forget([]); + state.push([].cast::<()>()); + )+ + + self.arg_state = Some(state); + } + + Ok(()) + } + } + + fn execute_deferred(&mut self, world: NonNull) -> anyhow::Result<()> { + $( + let s = self.arg_state.as_mut().expect("Somehow there was no state").pop().unwrap(); + let s = unsafe { std::ptr::read(s.cast::<$name::State>().as_ptr()) }; + $name::apply_deferred(s, world); + )+ - (self.inner)($($name,)+)?; - Ok(()) } } - /* impl IntoSystem for F + /* impl IntoSystem<($($name,)+)> for F where + /* for <'a> &'a mut F: + FnMut($($name,)+) -> anyhow::Result<()>, + FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, */ F: FnMut($($name,)+) -> anyhow::Result<()>, - F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>, + F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, { type System = FnSystem; fn into_system(self) -> Self::System { FnSystem { - args: ($($name::Fetcher::new(),)+), - inner: self + //args: ($($name::Fetcher::new(),)+), + inner: self, + arg_state: None, + _marker: PhantomData::<($($name::Fetcher,)+)>::default(), } } } */ - impl IntoSystem<($($name,)+)> for F + impl IntoSystem<($($name,)+)> for F where + /* for <'a> &'a mut F: + FnMut($($name,)+) -> anyhow::Result<()>, + FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, */ F: FnMut($($name,)+) -> anyhow::Result<()>, - F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>, + F: for<'a> FnMut($($name::Arg<'a, '_>,)+) -> anyhow::Result<()>, { - type System = FnSystem; + type System = FnSystem; fn into_system(self) -> Self::System { FnSystem { - args: ($($name::Fetcher::new(),)+), - inner: self + //args: ($($name::Fetcher::new(),)+), + inner: self, + arg_state: None, + _marker: PhantomData::<($($name,)+)>::default(), } } } @@ -103,127 +154,116 @@ impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O } impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P } /// An ArgFetcher implementation for query [`View`]s -pub struct ViewArgFetcher { +/* pub struct ViewArgFetcher { query: Q::Query } impl<'a, Q: AsQuery> FnArg for View<'a, Q> { type Fetcher = ViewArgFetcher; -} +} */ -impl FnArgFetcher for ViewArgFetcher { - type Arg<'a> = View<'a, Q>; - - fn new() -> Self { - ViewArgFetcher { - query: ::new(), - } - } +impl<'c, Q> FnArgFetcher for View<'c, Q> +where + Q: AsQuery, + ::Query: 'static +{ + type State = Q::Query; + type Arg<'a, 'state> = View<'a, Q>; fn world_access(&self) -> Access { todo!() } - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: NonNull) -> Self::Arg<'a, 'state> { let world = &*world.as_ptr(); let arch = world.archetypes.values().collect(); - let v = View::new(world, self.query, arch); + let v = View::new(world, state.clone(), arch); v } + + fn apply_deferred(_: Self::State, _: NonNull) { } + + fn create_state(_: NonNull) -> Self::State { + ::new() + } } /// An ArgFetcher implementation for borrowing the [`World`]. -pub struct WorldArgFetcher; +/* pub struct WorldArgFetcher; impl<'a> FnArg for &'a World { type Fetcher = WorldArgFetcher; -} +} */ -impl FnArgFetcher for WorldArgFetcher { - type Arg<'a> = &'a World; - - fn new() -> Self { - WorldArgFetcher - } +impl FnArgFetcher for &'_ World { + type State = (); + type Arg<'a, 'state> = &'a World; fn world_access(&self) -> Access { Access::Read } - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull) -> Self::Arg<'a, 'state> { &*world.as_ptr() } + fn apply_deferred(_: Self::State, _: NonNull) { } + + fn create_state(_: NonNull) -> Self::State { () } } -/// An ArgFetcher implementation for mutably borrowing the [`World`]. -pub struct WorldMutArgFetcher; - -impl<'a> FnArg for &'a mut World { - type Fetcher = WorldMutArgFetcher; -} - -impl FnArgFetcher for WorldMutArgFetcher { - type Arg<'a> = &'a mut World; - - fn new() -> Self { - WorldMutArgFetcher - } +impl FnArgFetcher for &'_ mut World { + type State = (); + type Arg<'a, 'state> = &'a mut World; fn world_access(&self) -> Access { Access::Write } - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull) -> Self::Arg<'a, 'state> { &mut *world.as_ptr() } + + fn apply_deferred(_: Self::State, _: NonNull) { } + + fn create_state(_: NonNull) -> Self::State { () } } -pub struct ResourceArgFetcher { +/* pub struct ResourceArgFetcher { phantom: PhantomData R> } impl<'a, R: ResourceObject> FnArg for Res<'a, R> { type Fetcher = ResourceArgFetcher; -} +} */ -impl FnArgFetcher for ResourceArgFetcher { - type Arg<'a> = Res<'a, R>; +impl FnArgFetcher for Res<'_, R> { + type State = (); + type Arg<'a, 'state> = Res<'a, R>; - fn new() -> Self { - ResourceArgFetcher { - phantom: PhantomData - } - } - - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull) -> Self::Arg<'a, 'state> { let world = world.as_ref(); Res(world.get_resource::()) } + + fn apply_deferred(_: Self::State, _: NonNull) { } + + fn create_state(_: NonNull) -> Self::State { () } } -pub struct ResourceMutArgFetcher { - phantom: PhantomData R> -} +impl FnArgFetcher for ResMut<'_, R> { + type State = (); + type Arg<'a, 'state> = ResMut<'a, R>; -impl<'a, R: ResourceObject> FnArg for ResMut<'a, R> { - type Fetcher = ResourceMutArgFetcher; -} - -impl FnArgFetcher for ResourceMutArgFetcher { - type Arg<'a> = ResMut<'a, R>; - - fn new() -> Self { - ResourceMutArgFetcher { - phantom: PhantomData - } - } - - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: NonNull) -> Self::Arg<'a, 'state> { let world = world.as_ref(); ResMut(world.get_resource_mut::()) } + + fn apply_deferred(_: Self::State, _: NonNull) { } + + fn create_state(_: NonNull) -> Self::State { () } } #[cfg(test)] diff --git a/lyra-ecs/src/system/graph.rs b/lyra-ecs/src/system/graph.rs index 4612fba..18ef23a 100644 --- a/lyra-ecs/src/system/graph.rs +++ b/lyra-ecs/src/system/graph.rs @@ -9,7 +9,9 @@ pub enum GraphExecutorError { #[error("could not find a system's dependency named `{0}`")] MissingSystem(String), #[error("system `{0}` returned with an error: `{1}`")] - SystemError(String, anyhow::Error) + SystemError(String, anyhow::Error), + #[error("a command returned with an error: `{0}`")] + Command(anyhow::Error) } /// A single system in the graph. @@ -56,7 +58,7 @@ impl GraphExecutor { } /// Executes the systems in the graph - pub fn execute(&mut self, world: NonNull, stop_on_error: bool) -> Result, GraphExecutorError> { + pub fn execute(&mut self, world_ptr: NonNull, stop_on_error: bool) -> Result, GraphExecutorError> { let mut stack = VecDeque::new(); let mut visited = HashSet::new(); @@ -69,7 +71,7 @@ impl GraphExecutor { while let Some(node) = stack.pop_front() { let system = self.systems.get_mut(node.as_str()).unwrap(); - if let Err(e) = system.system.execute(world) + if let Err(e) = system.system.execute(world_ptr) .map_err(|e| GraphExecutorError::SystemError(node, e)) { if stop_on_error { return Err(e); @@ -78,6 +80,17 @@ impl GraphExecutor { possible_errors.push(e); unimplemented!("Cannot resume topological execution from error"); // TODO: resume topological execution from error } + + if let Err(e) = system.system.execute_deferred(world_ptr) + .map_err(|e| GraphExecutorError::Command(e)) { + + if stop_on_error { + return Err(e); + } + + possible_errors.push(e); + unimplemented!("Cannot resume topological execution from error"); // TODO: resume topological execution from error + } } Ok(possible_errors) diff --git a/lyra-ecs/src/system/mod.rs b/lyra-ecs/src/system/mod.rs index ab87811..5cc4395 100644 --- a/lyra-ecs/src/system/mod.rs +++ b/lyra-ecs/src/system/mod.rs @@ -27,6 +27,8 @@ pub trait System { let _ = world; Ok(()) } + + fn execute_deferred(&mut self, world: NonNull) -> anyhow::Result<()>; } pub trait IntoSystem { diff --git a/lyra-ecs/src/world.rs b/lyra-ecs/src/world.rs index cbc1695..9e56991 100644 --- a/lyra-ecs/src/world.rs +++ b/lyra-ecs/src/world.rs @@ -1,21 +1,12 @@ -use std::{collections::{HashMap, VecDeque}, any::TypeId, cell::{Ref, RefMut}, ptr::NonNull}; +use std::{any::TypeId, cell::{Ref, RefMut}, collections::HashMap, ptr::NonNull}; -use crate::{archetype::{ArchetypeId, Archetype}, bundle::Bundle, query::{Query, ViewIter, View, AsQuery}, resource::ResourceData, query::{dynamic::DynamicView, ViewOne}, ComponentInfo, DynTypeId, TickTracker, Tick, ResourceObject}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct EntityId(pub u64); +use crate::{archetype::{Archetype, ArchetypeId}, bundle::Bundle, query::{dynamic::DynamicView, AsQuery, Query, View, ViewIter, ViewOne}, resource::ResourceData, ComponentInfo, DynTypeId, Entities, Entity, ResourceObject, Tick, TickTracker}; /// The id of the entity for the Archetype. /// The Archetype struct uses this as the index in the component columns #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct ArchetypeEntityId(pub u64); -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct Entity { - pub(crate) id: EntityId, - pub(crate) generation: u64, -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Record { pub id: ArchetypeId, @@ -25,11 +16,9 @@ pub struct Record { pub struct World { pub(crate) archetypes: HashMap, next_archetype_id: ArchetypeId, - pub(crate) entity_index: HashMap, - dead_entities: VecDeque, - next_entity_id: EntityId, resources: HashMap, tracker: TickTracker, + pub(crate) entities: Entities, } impl Default for World { @@ -37,11 +26,9 @@ impl Default for World { Self { archetypes: HashMap::new(), next_archetype_id: ArchetypeId(0), - entity_index: HashMap::new(), - dead_entities: VecDeque::new(), - next_entity_id: EntityId(0), resources: HashMap::new(), tracker: TickTracker::new(), + entities: Entities::default(), } } } @@ -51,32 +38,30 @@ impl World { Self::default() } - /// Gets a new Entity, will recycle dead entities and increment their generation. - fn get_new_entity(&mut self) -> Entity { - match self.dead_entities.pop_front() { - Some(mut e) => { - e.generation += 1; - e - }, - None => { - let new_id = self.next_entity_id; - self.next_entity_id.0 += 1; - - Entity { - id: new_id, - generation: 0, - } - } - } + /// Reserves an entity in the world + pub fn reserve_entity(&mut self) -> Entity { + self.entities.reserve() } - /// Spawns a new entity and inserts the component `bundle` into it. pub fn spawn(&mut self, bundle: B) -> Entity + where + B: Bundle + { + let new_entity = self.reserve_entity(); + self.spawn_into(new_entity, bundle); + new_entity + } + + /// Spawn the components into a reserved entity. Only do this with entities that + /// were 'reserved' with [`World::reserve`] + /// + /// # Safety + /// Do not use this method with an entity that is currently alive, it WILL cause undefined behavior. + pub fn spawn_into(&mut self, entity: Entity, bundle: B) where B: Bundle { let bundle_types = bundle.type_ids(); - let new_entity = self.get_new_entity(); let tick = self.tick(); @@ -86,7 +71,11 @@ impl World { .find(|a| a.is_archetype_for(&bundle_types)); if let Some(archetype) = archetype { - let arche_idx = archetype.add_entity(new_entity, bundle, &tick); + // make at just one check to ensure you're not spawning twice + debug_assert!(!archetype.entities.contains_key(&entity), + "You attempted to spawn components into an entity that already exists!"); + + let arche_idx = archetype.add_entity(entity, bundle, &tick); // Create entity record and store it let record = Record { @@ -94,16 +83,17 @@ impl World { index: arche_idx, }; - self.entity_index.insert(new_entity.id, record); + self.entities.insert_entity_record(entity, record); } // create a new archetype if one isn't found else { // create archetype let new_arch_id = self.next_archetype_id.increment(); let mut archetype = Archetype::from_bundle_info(new_arch_id, bundle.info()); - let entity_arch_id = archetype.add_entity(new_entity, bundle, &tick); + let entity_arch_id = archetype.add_entity(entity, bundle, &tick); // store archetype + println!("About to store arch, cap is {}, len is {}", self.archetypes.capacity(), self.archetypes.len()); self.archetypes.insert(new_arch_id, archetype); // Create entity record and store it @@ -113,27 +103,25 @@ impl World { index: entity_arch_id, }; - self.entity_index.insert(new_entity.id, record); + self.entities.insert_entity_record(entity, record); } - - new_entity } /// Despawn an entity from the World pub fn despawn(&mut self, entity: Entity) { // Tick the tracker if the entity is spawned. This is done here instead of the `if let` // below due to the borrow checker complaining about multiple mutable borrows to self. - let tick = if self.entity_index.contains_key(&entity.id) { + let tick = if self.entities.arch_index.contains_key(&entity.id) { Some(self.tick()) } else { None }; - if let Some(record) = self.entity_index.get_mut(&entity.id) { + if let Some(record) = self.entities.arch_index.get_mut(&entity.id) { let tick = tick.unwrap(); let arch = self.archetypes.get_mut(&record.id).unwrap(); if let Some((moved, new_index)) = arch.remove_entity(entity, &tick) { // replace the archetype index of the moved index with its new index. - self.entity_index.get_mut(&moved.id).unwrap().index = new_index; + self.entities.arch_index.get_mut(&moved.id).unwrap().index = new_index; } } } @@ -152,7 +140,7 @@ impl World { let tick = self.tick(); - let record = *self.entity_index.get(&entity.id).unwrap(); + let record = self.entities.entity_record(entity).unwrap(); let current_arch = self.archetypes.get(&record.id).unwrap(); let mut col_types: Vec = current_arch.columns.iter().map(|c| c.info.type_id).collect(); @@ -188,7 +176,7 @@ impl World { id: arch.id, index: res_index, }; - self.entity_index.insert(entity.id, new_record); + self.entities.insert_entity_record(entity, new_record); } else { let new_arch_id = self.next_archetype_id.increment(); let mut archetype = Archetype::from_bundle_info(new_arch_id, col_infos); @@ -202,7 +190,7 @@ impl World { index: entity_arch_id, }; - self.entity_index.insert(entity.id, record); + self.entities.insert_entity_record(entity, record); } let current_arch = self.archetypes.get_mut(&record.id).unwrap(); @@ -210,12 +198,12 @@ impl World { } pub fn entity_archetype(&self, entity: Entity) -> Option<&Archetype> { - self.entity_index.get(&entity.id) + self.entities.entity_record(entity) .and_then(|record| self.archetypes.get(&record.id)) } pub fn entity_archetype_mut(&mut self, entity: Entity) -> Option<&mut Archetype> { - self.entity_index.get_mut(&entity.id) + self.entities.entity_record(entity) .and_then(|record| self.archetypes.get_mut(&record.id)) } @@ -255,6 +243,14 @@ impl World { .get_mut() } + /// Get a resource from the world, or insert it into the world as its default. + pub fn get_resource_or_default(&mut self) -> RefMut + { + self.resources.entry(TypeId::of::()) + .or_insert_with(|| ResourceData::new(T::default())) + .get_mut() + } + /// Gets a resource from the World. /// /// Will panic if the resource is not in the world. See [`try_get_resource`] for @@ -264,6 +260,11 @@ impl World { .get() } + /// Returns boolean indicating if the World contains a resource of type `T`. + pub fn has_resource(&self) -> bool { + self.resources.contains_key(&TypeId::of::()) + } + /// Attempts to get a resource from the World. /// /// Returns `None` if the resource was not found. @@ -364,7 +365,7 @@ mod tests { world.despawn(middle_en); - let record = world.entity_index.get(&last_en.id).unwrap(); + let record = world.entities.entity_record(last_en).unwrap(); assert_eq!(record.index.0, 1); } -- 2.40.1 From 0373f68cc39c4c640ba14db5799a1a83f6cb311e Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 23 Feb 2024 16:38:38 -0500 Subject: [PATCH 28/41] resource: create the ability to keep resources as type erased data --- Cargo.lock | 85 +++++---------------------- lyra-game/src/delta_time.rs | 2 +- lyra-game/src/events.rs | 2 +- lyra-game/src/game.rs | 8 ++- lyra-game/src/input/system.rs | 4 ++ lyra-game/src/plugin.rs | 3 +- lyra-game/src/render/window.rs | 2 +- lyra-game/src/scene/model.rs | 5 +- lyra-game/src/stage.rs | 5 +- lyra-resource/Cargo.toml | 2 +- lyra-resource/src/lib.rs | 12 +++- lyra-resource/src/model.rs | 3 +- lyra-resource/src/resource.rs | 83 ++++++++++++++++---------- lyra-resource/src/resource_manager.rs | 65 ++++++++++++-------- lyra-resource/src/world_ext.rs | 75 +++++++++++++++++++++++ 15 files changed, 220 insertions(+), 136 deletions(-) create mode 100644 lyra-resource/src/world_ext.rs diff --git a/Cargo.lock b/Cargo.lock index 593b602..448879d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,12 +319,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "atomicell" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157342dd84c64f16899b4b16c1fb2cce54b887990362aac3c590b3d13810890f" - [[package]] name = "autocfg" version = "1.1.0" @@ -690,42 +684,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" -[[package]] -name = "edict" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85bf7cde5687ce04b093bfe183453fa12996b6bdfd2567a0262ebd621761d77" -dependencies = [ - "atomicell", - "edict-proc", - "hashbrown 0.13.2", - "parking_lot", - "smallvec", - "tiny-fn", -] - -[[package]] -name = "edict-proc" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94d80dc0f05250a9082bb9455bbf3d6c6c51db388b060df914aebcfb4a9b9f1" -dependencies = [ - "edict-proc-lib", - "syn 2.0.48", -] - -[[package]] -name = "edict-proc-lib" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d98f9931a4f71c7eb7d85cf4ef1271b27014625c85a65376a52c10ac4ffaea" -dependencies = [ - "proc-easy", - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "either" version = "1.9.0" @@ -1149,15 +1107,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash", -] - [[package]] name = "hashbrown" version = "0.14.3" @@ -1504,8 +1453,10 @@ dependencies = [ "anyhow", "lyra-ecs-derive", "lyra-math", + "paste", "rand", "thiserror", + "unique", ] [[package]] @@ -1569,6 +1520,7 @@ dependencies = [ "lyra-ecs", "lyra-math", "lyra-reflect-derive", + "lyra-resource", ] [[package]] @@ -1587,11 +1539,11 @@ dependencies = [ "anyhow", "base64 0.21.5", "crossbeam", - "edict", "glam", "gltf", "image", "infer", + "lyra-ecs", "mime", "notify", "notify-debouncer-full", @@ -2035,6 +1987,12 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2125,17 +2083,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "proc-easy" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea59c637cd0e6b71ae18e589854e9de9b7cb17fefdbf2047e42bd38e24285b19" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -2595,12 +2542,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-fn" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7b2c33e09916c65a15c92c1e583946052527e06102689ed11c6125f64fa8ba" - [[package]] name = "tiny-skia" version = "0.8.4" @@ -2747,6 +2688,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unique" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d360722e1f3884f5b14d332185f02ff111f771f0c76a313268fe6af1409aba96" + [[package]] name = "urlencoding" version = "2.1.3" diff --git a/lyra-game/src/delta_time.rs b/lyra-game/src/delta_time.rs index f64735a..d074677 100644 --- a/lyra-game/src/delta_time.rs +++ b/lyra-game/src/delta_time.rs @@ -37,7 +37,7 @@ pub struct DeltaTimePlugin; impl Plugin for DeltaTimePlugin { fn setup(&self, game: &mut crate::game::Game) { - game.world().add_resource(DeltaTime(0.0, None)); + game.world_mut().add_resource(DeltaTime(0.0, None)); game.add_system_to_stage(GameStages::First, "delta_time", delta_time_system, &[]); } } \ No newline at end of file diff --git a/lyra-game/src/events.rs b/lyra-game/src/events.rs index aee0bb0..11f538a 100644 --- a/lyra-game/src/events.rs +++ b/lyra-game/src/events.rs @@ -78,6 +78,6 @@ pub struct EventsPlugin; impl Plugin for EventsPlugin { fn setup(&self, game: &mut crate::game::Game) { - game.world().add_resource(EventQueue::new()); + game.world_mut().add_resource(EventQueue::new()); } } \ No newline at end of file diff --git a/lyra-game/src/game.rs b/lyra-game/src/game.rs index a3d8666..953b462 100755 --- a/lyra-game/src/game.rs +++ b/lyra-game/src/game.rs @@ -246,11 +246,17 @@ impl Game { } /// Get the world of this game - pub fn world(&mut self) -> &mut World { + pub fn world_mut(&mut self) -> &mut World { // world is always `Some`, so unwrapping is safe self.world.as_mut().unwrap() } + /// Get the world of this game + pub fn world(&self) -> &World { + // world is always `Some`, so unwrapping is safe + self.world.as_ref().unwrap() + } + /// Add a system to the ecs world pub fn with_system(&mut self, name: &str, system: S, depends: &[&str]) -> &mut Self where diff --git a/lyra-game/src/input/system.rs b/lyra-game/src/input/system.rs index 1a5171e..419d079 100755 --- a/lyra-game/src/input/system.rs +++ b/lyra-game/src/input/system.rs @@ -120,6 +120,10 @@ impl crate::ecs::system::System for InputSystem { fn world_access(&self) -> lyra_ecs::Access { lyra_ecs::Access::Write } + + fn execute_deferred(&mut self, _: NonNull) -> anyhow::Result<()> { + Ok(()) + } } impl IntoSystem<()> for InputSystem { diff --git a/lyra-game/src/plugin.rs b/lyra-game/src/plugin.rs index c7adca2..e014e61 100644 --- a/lyra-game/src/plugin.rs +++ b/lyra-game/src/plugin.rs @@ -1,3 +1,4 @@ +use lyra_ecs::Commands; use lyra_resource::ResourceManager; use crate::EventsPlugin; @@ -98,7 +99,7 @@ pub struct ResourceManagerPlugin; impl Plugin for ResourceManagerPlugin { fn setup(&self, game: &mut Game) { - game.world().add_resource(ResourceManager::new()); + game.world_mut().add_resource(ResourceManager::new()); } } diff --git a/lyra-game/src/render/window.rs b/lyra-game/src/render/window.rs index a974544..d577996 100644 --- a/lyra-game/src/render/window.rs +++ b/lyra-game/src/render/window.rs @@ -373,7 +373,7 @@ impl Plugin for WindowPlugin { fn setup(&self, game: &mut crate::game::Game) { let window_options = WindowOptions::default(); - game.world().add_resource(Ct::new(window_options)); + game.world_mut().add_resource(Ct::new(window_options)); game.with_system("window_updater", window_updater_system, &[]); } } diff --git a/lyra-game/src/scene/model.rs b/lyra-game/src/scene/model.rs index 8e62541..2c5a6b3 100644 --- a/lyra-game/src/scene/model.rs +++ b/lyra-game/src/scene/model.rs @@ -1,10 +1,11 @@ use lyra_ecs::Component; +use lyra_reflect::Reflect; use lyra_resource::ResHandle; use crate::assets::Model; -#[derive(Clone, Component)] -pub struct ModelComponent(pub ResHandle); +#[derive(Clone, Component, Reflect)] +pub struct ModelComponent(#[reflect(skip)] pub ResHandle); impl From> for ModelComponent { fn from(value: ResHandle) -> Self { diff --git a/lyra-game/src/stage.rs b/lyra-game/src/stage.rs index e06e8aa..15d79ff 100644 --- a/lyra-game/src/stage.rs +++ b/lyra-game/src/stage.rs @@ -9,7 +9,9 @@ pub enum StagedExecutorError { #[error("[stage={0}] could not find a system's dependency named `{1}`")] MissingSystem(String, String), #[error("[stage={0}] system `{1}` returned with an error: `{2}`")] - SystemError(String, String, anyhow::Error) + SystemError(String, String, anyhow::Error), + #[error("[stage={0}] a command returned with an error: `{1}`")] + CommandError(String, anyhow::Error), } impl StagedExecutorError { @@ -17,6 +19,7 @@ impl StagedExecutorError { match value { GraphExecutorError::MissingSystem(s) => Self::MissingSystem(stage, s), GraphExecutorError::SystemError(s, e) => Self::SystemError(stage, s, e), + GraphExecutorError::Command(e) => Self::CommandError(stage, e) } } } diff --git a/lyra-resource/Cargo.toml b/lyra-resource/Cargo.toml index 0f6adf3..753caff 100644 --- a/lyra-resource/Cargo.toml +++ b/lyra-resource/Cargo.toml @@ -6,10 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lyra-ecs = { path = "../lyra-ecs" } anyhow = "1.0.75" base64 = "0.21.4" crossbeam = { version = "0.8.4", features = [ "crossbeam-channel" ] } -edict = "0.5.0" glam = "0.24.1" gltf = { version = "1.3.0", features = ["KHR_materials_pbrSpecularGlossiness", "KHR_materials_specular"] } image = "0.24.7" diff --git a/lyra-resource/src/lib.rs b/lyra-resource/src/lib.rs index 65fe735..0427c2f 100644 --- a/lyra-resource/src/lib.rs +++ b/lyra-resource/src/lib.rs @@ -16,7 +16,17 @@ pub use model::*; pub mod material; pub use material::*; +pub mod world_ext; +pub use world_ext::*; + pub(crate) mod util; pub use crossbeam::channel as channel; -pub use notify; \ No newline at end of file +pub use notify; + +#[allow(unused_imports)] +pub(crate) mod lyra_engine { + pub(crate) mod ecs { + pub use lyra_ecs::*; + } +} diff --git a/lyra-resource/src/model.rs b/lyra-resource/src/model.rs index f1c24c2..acc8c2e 100644 --- a/lyra-resource/src/model.rs +++ b/lyra-resource/src/model.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::Material; +use crate::lyra_engine; #[repr(C)] #[derive(Clone, Debug, PartialEq)] @@ -81,7 +82,7 @@ pub enum MeshVertexAttribute { Other(String), } -#[derive(Clone, edict::Component)] +#[derive(Clone, lyra_ecs::Component)] pub struct Mesh { pub uuid: uuid::Uuid, pub attributes: HashMap, diff --git a/lyra-resource/src/resource.rs b/lyra-resource/src/resource.rs index ef85222..1423de2 100644 --- a/lyra-resource/src/resource.rs +++ b/lyra-resource/src/resource.rs @@ -1,7 +1,9 @@ -use std::sync::{Arc, RwLock}; +use std::{any::Any, sync::{Arc, RwLock}}; use uuid::Uuid; +use crate::ResourceStorage; + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum ResourceState { Loading, @@ -21,7 +23,7 @@ impl<'a, T> std::ops::Deref for ResourceDataRef<'a, T> { } } -pub(crate) struct Resource { +pub struct Resource { path: String, pub(crate) data: Option, pub(crate) version: usize, @@ -122,34 +124,51 @@ impl ResHandle { None } } - - /* /// Get a reference to the data in the resource - /// - /// # Panics - /// Panics if the resource was not loaded yet. - pub fn data_ref(&self) -> &T { - self.data.as_ref() - .expect("Resource is not loaded yet (use try_data_ref, or wait until its loaded)!") - } - - /// If the resource is loaded, returns `Some` reference to the data in the resource, - /// else it will return `None` - pub fn try_data_ref(&self) -> Option<&T> { - self.data.as_ref() - } - - /// Get a **mutable** reference to the data in the resource - /// - /// # Panics - /// Panics if the resource was not loaded yet. - pub fn data_mut(&mut self) -> &mut T { - self.data.as_mut() - .expect("Resource is not loaded yet (use try_data_ref, or wait until its loaded)!") - } - - /// If the resource is loaded, returns `Some` **mutable** reference to the data in the resource, - /// else it will return `None` - pub fn try_data_mut(&mut self) -> Option<&mut T> { - self.data.as_mut() - } */ } + +impl ResourceStorage for ResHandle { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn as_arc_any(self: Arc) -> Arc { + self.clone() + } + + fn as_box_any(self: Box) -> Box { + self + } + + fn set_watched(&self, watched: bool) { + let mut w = self.data.write().unwrap(); + w.is_watched = watched; + } + + fn path(&self) -> String { + self.path() + } + + fn version(&self) -> usize { + self.version() + } + + fn state(&self) -> ResourceState { + self.state() + } + + fn uuid(&self) -> Uuid { + self.uuid() + } + + fn is_watched(&self) -> bool { + self.is_watched() + } + + fn is_loaded(&self) -> bool { + self.is_loaded() + } +} \ No newline at end of file diff --git a/lyra-resource/src/resource_manager.rs b/lyra-resource/src/resource_manager.rs index 6b84686..dc24d33 100644 --- a/lyra-resource/src/resource_manager.rs +++ b/lyra-resource/src/resource_manager.rs @@ -4,39 +4,27 @@ use crossbeam::channel::Receiver; use notify::{Watcher, RecommendedWatcher}; use notify_debouncer_full::{DebouncedEvent, FileIdMap}; use thiserror::Error; +use uuid::Uuid; -use crate::{resource::ResHandle, loader::{ResourceLoader, LoaderError, image::ImageLoader, model::ModelLoader}}; +use crate::{loader::{image::ImageLoader, model::ModelLoader, LoaderError, ResourceLoader}, resource::ResHandle, ResourceState}; +/// A trait for type erased storage of a resource. +/// Implemented for [`ResHandle`] pub trait ResourceStorage: Send + Sync + Any + 'static { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; fn as_arc_any(self: Arc) -> Arc; fn as_box_any(self: Box) -> Box; + /// Do not set a resource to watched if it is not actually watched. + /// This is used internally. fn set_watched(&self, watched: bool); -} -/// Implements this trait for anything that fits the type bounds -impl ResourceStorage for ResHandle { - fn as_any(&self) -> &dyn Any { - self - } - - fn as_any_mut(&mut self) -> &mut dyn Any { - self - } - - fn as_arc_any(self: Arc) -> Arc { - self.clone() - } - - fn as_box_any(self: Box) -> Box { - self - } - - fn set_watched(&self, watched: bool) { - let mut w = self.data.write().unwrap(); - w.is_watched = watched; - } + fn path(&self) -> String; + fn version(&self) -> usize; + fn state(&self) -> ResourceState; + fn uuid(&self) -> Uuid; + fn is_watched(&self) -> bool; + fn is_loaded(&self) -> bool; } #[derive(Error, Debug)] @@ -131,6 +119,35 @@ impl ResourceManager { } } + /// Request a resource without downcasting to a ResHandle. + /// Whenever you're ready to downcast, you can do so like this: + /// ```compile_fail + /// let arc_any = res_arc.as_arc_any(); + /// let res: Arc> = res.downcast::>().expect("Failure to downcast resource"); + /// ``` + pub fn request_raw(&mut self, path: &str) -> Result, RequestError> { + match self.resources.get(&path.to_string()) { + Some(res) => { + Ok(res.clone()) + }, + None => { + if let Some(loader) = self.loaders.iter() + .find(|l| l.does_support_file(path)) { + + // Load the resource and store it + let loader = Arc::clone(loader); // stop borrowing from self + let res = loader.load(self, path)?; + let res: Arc = Arc::from(res); + self.resources.insert(path.to_string(), res.clone()); + + Ok(res) + } else { + Err(RequestError::UnsupportedFileExtension(path.to_string())) + } + } + } + } + /// Store bytes in the manager. If there is already an entry with the same identifier it will be updated. /// /// Panics: If there is already an entry with the same `ident`, and the entry is not bytes, this function will panic. diff --git a/lyra-resource/src/world_ext.rs b/lyra-resource/src/world_ext.rs new file mode 100644 index 0000000..2db2b04 --- /dev/null +++ b/lyra-resource/src/world_ext.rs @@ -0,0 +1,75 @@ +use std::any::Any; + +use crossbeam::channel::Receiver; +use lyra_ecs::World; +use notify_debouncer_full::DebouncedEvent; + +use crate::{RequestError, ResHandle, ResourceLoader, ResourceManager}; + +pub trait WorldAssetExt { + /// Register a resource loader with the resource manager. + fn register_res_loader(&mut self) + where + L: ResourceLoader + Default + 'static; + + /// Request a resource from the resource manager. + fn request_res(&mut self, path: &str) -> Result, RequestError> + where + T: Send + Sync + Any + 'static; + + /// Start watching a resource for changes. Returns a crossbeam channel that can be used to listen for events. + fn watch_res(&mut self, path: &str, recursive: bool) -> notify::Result, Vec>>>; + + /// Stop watching a resource for changes. + fn stop_watching_res(&mut self, path: &str) -> notify::Result<()>; + + /// Try to retrieve a crossbeam channel for a path that is currently watched. Returns None if the path is not watched + fn res_watcher_recv(&self, path: &str) -> Option, Vec>>>; + + /// Reload a resource. The data will be updated in the handle. This is not + /// automatically triggered if the resource is being watched. + fn reload_res(&mut self, resource: ResHandle) -> Result<(), RequestError> + where + T: Send + Sync + Any + 'static; +} + +impl WorldAssetExt for World { + fn register_res_loader(&mut self) + where + L: ResourceLoader + Default + 'static + { + let mut man = self.get_resource_or_default::(); + man.register_loader::(); + } + + fn request_res(&mut self, path: &str) -> Result, RequestError> + where + T: Send + Sync + Any + 'static + { + let mut man = self.get_resource_or_default::(); + man.request(path) + } + + fn watch_res(&mut self, path: &str, recursive: bool) -> notify::Result, Vec>>> { + let mut man = self.get_resource_or_default::(); + man.watch(path, recursive) + } + + fn stop_watching_res(&mut self, path: &str) -> notify::Result<()> { + let mut man = self.get_resource_or_default::(); + man.stop_watching(path) + } + + fn res_watcher_recv(&self, path: &str) -> Option, Vec>>> { + let man = self.get_resource::(); + man.watcher_event_recv(path) + } + + fn reload_res(&mut self, resource: ResHandle) -> Result<(), RequestError> + where + T: Send + Sync + Any + 'static + { + let mut man = self.get_resource_or_default::(); + man.reload(resource) + } +} \ No newline at end of file -- 2.40.1 From f2d302c6d4a27a19eab42d502b956fce85a1ef14 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 23 Feb 2024 16:39:51 -0500 Subject: [PATCH 29/41] reflect: ReflectedComponent takes ownership over reflected component when inserting into entity --- lyra-reflect/src/component.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lyra-reflect/src/component.rs b/lyra-reflect/src/component.rs index ca160a5..a6fb54d 100644 --- a/lyra-reflect/src/component.rs +++ b/lyra-reflect/src/component.rs @@ -1,4 +1,4 @@ -use std::{any::TypeId, cell::{Ref, RefMut}}; +use std::{any::{Any, TypeId}, cell::{Ref, RefMut}}; use lyra_ecs::{Component, ComponentInfo, World, Entity, DynamicBundle}; @@ -13,21 +13,21 @@ pub struct ReflectedComponent { //from_world: for<'a> fn (world: &'a mut World) -> Box, /// Inserts component into entity in the world - fn_insert: for<'a> fn (world: &'a mut World, entity: Entity, component: &dyn Reflect), + fn_insert: for<'a> fn (world: &'a mut World, entity: Entity, component: Box), /// Inserts component into a bundle - fn_bundle_insert: for<'a> fn (dynamic_bundle: &'a mut DynamicBundle, component: &dyn Reflect), + fn_bundle_insert: for<'a> fn (dynamic_bundle: &'a mut DynamicBundle, component: Box), fn_reflect: for<'a> fn (world: &'a World, entity: Entity) -> Option>, fn_reflect_mut: for<'a> fn (world: &'a mut World, entity: Entity) -> Option>, } impl ReflectedComponent { /// Insert the reflected component into an entity. - pub fn insert(&self, world: &mut World, entity: Entity, component: &dyn Reflect) { + pub fn insert(&self, world: &mut World, entity: Entity, component: Box) { (self.fn_insert)(world, entity, component); } /// Insert this component into a DynamicBundle - pub fn bundle_insert(&self, dynamic_bundle: &mut DynamicBundle, component: &dyn Reflect) { + pub fn bundle_insert(&self, dynamic_bundle: &mut DynamicBundle, component: Box) { (self.fn_bundle_insert)(dynamic_bundle, component) } @@ -42,19 +42,23 @@ impl ReflectedComponent { } } -impl FromType for ReflectedComponent { +impl FromType for ReflectedComponent { fn from_type() -> Self { ReflectedComponent { type_id: TypeId::of::(), info: ComponentInfo::new::(), - fn_insert: |world: &mut World, entity: Entity, component: &dyn Reflect| { - let mut c = C::default(); - c.apply(component); + fn_insert: |world: &mut World, entity: Entity, component: Box| { + let c = component as Box; + let c = c.downcast::() + .expect("Provided a non-matching type to ReflectedComponent insert method!"); + let c = *c; world.insert(entity, (c,)); }, - fn_bundle_insert: |bundle: &mut DynamicBundle, component: &dyn Reflect| { - let mut c = C::default(); - c.apply(component); + fn_bundle_insert: |bundle: &mut DynamicBundle, component: Box| { + let c = component as Box; + let c = c.downcast::() + .expect("Provided a non-matching type to ReflectedComponent insert method!"); + let c = *c; bundle.push(c); }, fn_reflect: |world: &World, entity: Entity| { -- 2.40.1 From 1f43a9d4da29e6f579a8ffd7768f3fc11ea8c8a4 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Fri, 23 Feb 2024 16:43:23 -0500 Subject: [PATCH 30/41] fix compiler errors after some changes to game crate --- examples/testbed/src/main.rs | 8 ++++---- lyra-scripting/src/lib.rs | 2 +- lyra-scripting/src/lua/system.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index f9fa6d3..d1b9a99 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -206,7 +206,7 @@ async fn main() { Ok(()) }; let fps_plugin = move |game: &mut Game| { - let world = game.world(); + let world = game.world_mut(); world.add_resource(fps_counter::FPSCounter::new()); }; @@ -228,7 +228,7 @@ async fn main() { }; let jiggle_plugin = move |game: &mut Game| { - game.world().add_resource(TpsAccumulator(0.0)); + game.world_mut().add_resource(TpsAccumulator(0.0)); let mut sys = BatchedSystem::new(); sys.with_criteria(FixedTimestep::new(45)); @@ -293,7 +293,7 @@ async fn main() { Ok(()) }; */ - let world = game.world(); + let world = game.world_mut(); world.add_resource(action_handler); world.spawn((Vec3::new(0.5, 0.1, 3.0),)); game.with_plugin(InputActionPlugin); @@ -303,7 +303,7 @@ async fn main() { let script_test_plugin = |game: &mut Game| { game.with_plugin(LuaScriptingPlugin); - let world = game.world(); + let world = game.world_mut(); let mut res_man = world.get_resource_mut::(); let script = res_man.request::("scripts/test.lua").unwrap(); res_man.watch("scripts/test.lua", false).unwrap(); diff --git a/lyra-scripting/src/lib.rs b/lyra-scripting/src/lib.rs index 640d227..4b5b42f 100644 --- a/lyra-scripting/src/lib.rs +++ b/lyra-scripting/src/lib.rs @@ -134,7 +134,7 @@ impl GameScriptExt for Game { T: ScriptHost, P: ScriptApiProvider + 'static { - let world = self.world(); + let world = self.world_mut(); provider.prepare_world(world); let mut providers = world.get_resource_mut::>(); providers.add_provider(provider); diff --git a/lyra-scripting/src/lua/system.rs b/lyra-scripting/src/lua/system.rs index 071e69e..4a5f335 100644 --- a/lyra-scripting/src/lua/system.rs +++ b/lyra-scripting/src/lua/system.rs @@ -171,7 +171,7 @@ pub struct LuaScriptingPlugin; impl Plugin for LuaScriptingPlugin { fn setup(&self, game: &mut lyra_game::game::Game) { - let world = game.world(); + let world = game.world_mut(); world.add_resource_default::(); -- 2.40.1 From 5dfc073db59e8aa7cf7c7746ec52475392ae95cd Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 24 Feb 2024 11:09:26 -0500 Subject: [PATCH 31/41] ecs: add commands test and fix Commands --- .vscode/launch.json | 2 +- Cargo.lock | 8 ---- lyra-ecs/Cargo.toml | 1 - lyra-ecs/src/command.rs | 80 +++++++++++++++++++++++++---------- lyra-ecs/src/entity.rs | 6 ++- lyra-ecs/src/system/fn_sys.rs | 47 +++++--------------- lyra-ecs/src/world.rs | 1 - 7 files changed, 75 insertions(+), 70 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 238c8c0..a05d1f5 100755 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -72,7 +72,7 @@ "--no-run", "--lib", "--package=lyra-ecs", - "world::tests::view_change_tracking", + "command::tests::deferred_commands", "--", "--exact --nocapture" ], diff --git a/Cargo.lock b/Cargo.lock index 448879d..4f54a87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1456,7 +1456,6 @@ dependencies = [ "paste", "rand", "thiserror", - "unique", ] [[package]] @@ -1520,7 +1519,6 @@ dependencies = [ "lyra-ecs", "lyra-math", "lyra-reflect-derive", - "lyra-resource", ] [[package]] @@ -2688,12 +2686,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "unique" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d360722e1f3884f5b14d332185f02ff111f771f0c76a313268fe6af1409aba96" - [[package]] name = "urlencoding" version = "2.1.3" diff --git a/lyra-ecs/Cargo.toml b/lyra-ecs/Cargo.toml index fe1e97f..47643c8 100644 --- a/lyra-ecs/Cargo.toml +++ b/lyra-ecs/Cargo.toml @@ -13,7 +13,6 @@ lyra-ecs-derive = { path = "./lyra-ecs-derive" } lyra-math = { path = "../lyra-math", optional = true } anyhow = "1.0.75" thiserror = "1.0.50" -unique = "0.9.1" paste = "1.0.14" [dev-dependencies] diff --git a/lyra-ecs/src/command.rs b/lyra-ecs/src/command.rs index d487d7c..137f8f1 100644 --- a/lyra-ecs/src/command.rs +++ b/lyra-ecs/src/command.rs @@ -1,26 +1,30 @@ -use std::{cell::RefMut, collections::VecDeque, mem, ptr::{self, NonNull}}; - -use unique::Unique; +use std::{any::Any, cell::RefMut, collections::VecDeque, ptr::{self, NonNull}}; use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World}; -pub trait Command { +pub trait Command: Any { + fn as_any_boxed(self: Box) -> Box; + fn run(self, world: &mut World) -> anyhow::Result<()>; } impl Command for F where - F: FnOnce(&mut World) -> anyhow::Result<()> + F: FnOnce(&mut World) -> anyhow::Result<()> + 'static { + fn as_any_boxed(self: Box) -> Box { + self + } + fn run(self, world: &mut World) -> anyhow::Result<()> { self(world) } } -type RunCommand = unsafe fn(cmd: Unique<()>, world: &mut World) -> anyhow::Result<()>; +type RunCommand = unsafe fn(cmd: Box, world: &mut World) -> anyhow::Result<()>; #[derive(Default)] -pub struct CommandQueue(VecDeque<(RunCommand, Unique<()>)>); +pub struct CommandQueue(VecDeque<(RunCommand, Box)>); pub struct Commands<'a, 'b> { queue: &'b mut CommandQueue, @@ -36,31 +40,26 @@ impl<'a, 'b> Commands<'a, 'b> { } /// Add a command to the end of the command queue - pub fn add(&mut self, mut cmd: C) { - // get an owned pointer to the command, then forget it to ensure its destructor isn't ran - let ptr = Unique::from(&mut cmd).cast::<()>(); - mem::forget(cmd); + pub fn add(&mut self, cmd: C) { + let cmd = Box::new(cmd); - let run_fn = |cmd_ptr: Unique<()>, world: &mut World| unsafe { - let cmd = cmd_ptr.cast::(); - let cmd = ptr::read(cmd.as_ptr()); + let run_fn = |cmd: Box, world: &mut World| { + let cmd = cmd.as_any_boxed() + .downcast::() + .unwrap(); + cmd.run(world)?; Ok(()) }; - self.queue.0.push_back((run_fn, ptr)); + self.queue.0.push_back((run_fn, cmd)); } - pub fn spawn(&mut self, mut bundle: B) -> Entity { + pub fn spawn(&mut self, bundle: B) -> Entity { let e = self.entities.reserve(); - let bundle_ptr = Unique::from(&mut bundle); - mem::forget(bundle); - //let bundle_box = Box::new(bundle); - self.add(move |world: &mut World| { - let bundle = unsafe { ptr::read(bundle_ptr.as_ptr()) }; world.spawn_into(e, bundle); Ok(()) }); @@ -101,7 +100,7 @@ impl FnArgFetcher for Commands<'_, '_> { let world = unsafe { world_ptr.as_mut() }; let mut cmds = Commands::new(&mut state, world); - // safety: Commands has a mut borrow to entities in the world + // safety: Commands has a mut borrow only to entities in the world let world = unsafe { world_ptr.as_mut() }; cmds.execute(world).unwrap() } @@ -111,4 +110,41 @@ pub fn execute_deferred_commands(world: &mut World, mut commands: RefMut anyhow::Result<()> { + commands.spawn((spawned_vec_cl.clone(),)); + + Ok(()) + }; + + let mut graph_exec = GraphExecutor::new(); + graph_exec.insert_system("test", test_sys.into_system(), &[]); + graph_exec.execute(NonNull::from(&world), true).unwrap(); + + assert_eq!(world.entities.len(), 4); + + // there's only one archetype + let arch = world.archetypes.values().next().unwrap(); + let col = arch.get_column(DynTypeId::of::()).unwrap(); + let vec2: &Vec2 = unsafe { col.get(3) }; + assert_eq!(vec2.clone(), spawned_vec); + } } \ No newline at end of file diff --git a/lyra-ecs/src/entity.rs b/lyra-ecs/src/entity.rs index 7fefa67..b12e04f 100644 --- a/lyra-ecs/src/entity.rs +++ b/lyra-ecs/src/entity.rs @@ -35,7 +35,6 @@ impl Entities { e } None => { - println!("id is {}", self.next_id.0); let new_id = self.next_id; self.next_id.0 += 1; @@ -47,6 +46,11 @@ impl Entities { } } + /// Returns the number of spawned entities + pub fn len(&self) -> usize { + self.next_id.0 as usize - self.dead.len() + } + /// Retrieves the Archetype Record for the entity pub(crate) fn entity_record(&self, entity: Entity) -> Option { self.arch_index.get(&entity.id).cloned() diff --git a/lyra-ecs/src/system/fn_sys.rs b/lyra-ecs/src/system/fn_sys.rs index 4252db7..856ce16 100644 --- a/lyra-ecs/src/system/fn_sys.rs +++ b/lyra-ecs/src/system/fn_sys.rs @@ -1,6 +1,5 @@ -use std::{ptr::NonNull, marker::PhantomData}; +use std::{any::Any, marker::PhantomData, ptr::NonNull}; -use unique::Unique; use paste::paste; use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}}; @@ -12,7 +11,6 @@ pub trait FnArgFetcher { type Arg<'a, 'state>: FnArgFetcher; - //fn new() -> Self; fn create_state(world: NonNull) -> Self::State; /// Return the appropriate world access if this fetcher gets the world directly. @@ -40,7 +38,7 @@ pub struct FnSystem { inner: F, //#[allow(dead_code)] //args: Args, - arg_state: Option>>, + arg_state: Option>>, _marker: PhantomData, } @@ -60,10 +58,8 @@ macro_rules! impl_fn_system_tuple { paste! { $( // get the arg fetcher, create its state, and get the arg - let mut []: $name::State = $name::create_state(world); let [<$name:lower>] = $name::get(&mut [], world); - )+ (self.inner)($( [<$name:lower>] ),+)?; @@ -71,9 +67,8 @@ macro_rules! impl_fn_system_tuple { let mut state = Vec::new(); $( // type erase the now modified state, and store it - let [] = Unique::from(&mut []); - std::mem::forget([]); - state.push([].cast::<()>()); + let boxed = Box::new([]) as Box; + state.push(boxed); )+ self.arg_state = Some(state); @@ -84,41 +79,22 @@ macro_rules! impl_fn_system_tuple { } fn execute_deferred(&mut self, world: NonNull) -> anyhow::Result<()> { + let state = self.arg_state.as_mut().expect("Somehow there was no state"); + $( - let s = self.arg_state.as_mut().expect("Somehow there was no state").pop().unwrap(); - let s = unsafe { std::ptr::read(s.cast::<$name::State>().as_ptr()) }; - $name::apply_deferred(s, world); + let arg_state_box = state.pop() + .expect("Missing expected arg state"); + let arg_state = *arg_state_box.downcast::<$name::State>() + .unwrap(); + $name::apply_deferred(arg_state, world); )+ Ok(()) } } - /* impl IntoSystem<($($name,)+)> for F - where - /* for <'a> &'a mut F: - FnMut($($name,)+) -> anyhow::Result<()>, - FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, */ - F: FnMut($($name,)+) -> anyhow::Result<()>, - F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, - { - type System = FnSystem; - - fn into_system(self) -> Self::System { - FnSystem { - //args: ($($name::Fetcher::new(),)+), - inner: self, - arg_state: None, - _marker: PhantomData::<($($name::Fetcher,)+)>::default(), - } - } - } */ - impl IntoSystem<($($name,)+)> for F where - /* for <'a> &'a mut F: - FnMut($($name,)+) -> anyhow::Result<()>, - FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, */ F: FnMut($($name,)+) -> anyhow::Result<()>, F: for<'a> FnMut($($name::Arg<'a, '_>,)+) -> anyhow::Result<()>, { @@ -126,7 +102,6 @@ macro_rules! impl_fn_system_tuple { fn into_system(self) -> Self::System { FnSystem { - //args: ($($name::Fetcher::new(),)+), inner: self, arg_state: None, _marker: PhantomData::<($($name,)+)>::default(), diff --git a/lyra-ecs/src/world.rs b/lyra-ecs/src/world.rs index 9e56991..1c89301 100644 --- a/lyra-ecs/src/world.rs +++ b/lyra-ecs/src/world.rs @@ -93,7 +93,6 @@ impl World { let entity_arch_id = archetype.add_entity(entity, bundle, &tick); // store archetype - println!("About to store arch, cap is {}, len is {}", self.archetypes.capacity(), self.archetypes.len()); self.archetypes.insert(new_arch_id, archetype); // Create entity record and store it -- 2.40.1 From 90b821f95cd07ba8d510c39d35fc8aa1476ec5f0 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 24 Feb 2024 11:16:09 -0500 Subject: [PATCH 32/41] scripting: implement loading assets in lua --- examples/testbed/scripts/test.lua | 16 +- lyra-scripting/elua | 2 +- lyra-scripting/src/host.rs | 4 +- lyra-scripting/src/lib.rs | 8 +- lyra-scripting/src/lua/providers/ecs.rs | 4 +- lyra-scripting/src/lua/proxy.rs | 1 - lyra-scripting/src/lua/script.rs | 2 +- lyra-scripting/src/lua/system.rs | 18 +- lyra-scripting/src/lua/world.rs | 285 ++++++++++-------- lyra-scripting/src/lua/wrappers/mod.rs | 8 +- lyra-scripting/src/lua/wrappers/model_comp.rs | 70 +++++ lyra-scripting/src/lua/wrappers/res_handle.rs | 62 ++++ 12 files changed, 337 insertions(+), 143 deletions(-) create mode 100644 lyra-scripting/src/lua/wrappers/model_comp.rs create mode 100644 lyra-scripting/src/lua/wrappers/res_handle.rs 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 -- 2.40.1 From 4a7cdfab80d9df76a995853e40a30f994010b64b Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 24 Feb 2024 14:30:09 -0500 Subject: [PATCH 33/41] ecs: fix executing deferred fn system arguments --- lyra-ecs/src/system/fn_sys.rs | 3 ++- lyra-ecs/src/system/graph.rs | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lyra-ecs/src/system/fn_sys.rs b/lyra-ecs/src/system/fn_sys.rs index 856ce16..fd7ac5c 100644 --- a/lyra-ecs/src/system/fn_sys.rs +++ b/lyra-ecs/src/system/fn_sys.rs @@ -80,12 +80,13 @@ macro_rules! impl_fn_system_tuple { fn execute_deferred(&mut self, world: NonNull) -> anyhow::Result<()> { let state = self.arg_state.as_mut().expect("Somehow there was no state"); + state.reverse(); $( let arg_state_box = state.pop() .expect("Missing expected arg state"); let arg_state = *arg_state_box.downcast::<$name::State>() - .unwrap(); + .expect("Somehow the state cannot be downcasted from boxed Any"); $name::apply_deferred(arg_state, world); )+ diff --git a/lyra-ecs/src/system/graph.rs b/lyra-ecs/src/system/graph.rs index 18ef23a..40ac0cc 100644 --- a/lyra-ecs/src/system/graph.rs +++ b/lyra-ecs/src/system/graph.rs @@ -2,7 +2,7 @@ use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull}; use super::System; -use crate::world::World; +use crate::{world::World, CommandQueue, Commands}; #[derive(thiserror::Error, Debug)] pub enum GraphExecutorError { @@ -58,7 +58,7 @@ impl GraphExecutor { } /// Executes the systems in the graph - pub fn execute(&mut self, world_ptr: NonNull, stop_on_error: bool) -> Result, GraphExecutorError> { + pub fn execute(&mut self, mut world_ptr: NonNull, stop_on_error: bool) -> Result, GraphExecutorError> { let mut stack = VecDeque::new(); let mut visited = HashSet::new(); @@ -91,6 +91,25 @@ impl GraphExecutor { possible_errors.push(e); unimplemented!("Cannot resume topological execution from error"); // TODO: resume topological execution from error } + + let world = unsafe { world_ptr.as_mut() }; + if let Some(mut queue) = world.try_get_resource_mut::() { + // Safety: Commands only borrows world.entities when adding commands + let world = unsafe { world_ptr.as_mut() }; + let mut commands = Commands::new(&mut queue, world); + + let world = unsafe { world_ptr.as_mut() }; + if let Err(e) = commands.execute(world) + .map_err(|e| GraphExecutorError::Command(e)) { + + if stop_on_error { + return Err(e); + } + + possible_errors.push(e); + unimplemented!("Cannot resume topological execution from error"); // TODO: resume topological execution from error + } + } } Ok(possible_errors) -- 2.40.1 From 6731fcd7f225ee39547376219b877cf9ef1b213a Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 24 Feb 2024 14:32:06 -0500 Subject: [PATCH 34/41] scripting: implement spawning entity with loaded 3d models --- examples/testbed/scripts/test.lua | 10 +++++---- lyra-game/src/plugin.rs | 14 +++++++++++- lyra-scripting/src/lua/script.rs | 3 --- lyra-scripting/src/lua/system.rs | 34 +++++++++++++++++------------ lyra-scripting/src/lua/world.rs | 36 +++++++++++++++++++++++++++---- 5 files changed, 71 insertions(+), 26 deletions(-) diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 75e8dd5..1e67ad3 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -1,10 +1,12 @@ ---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") + + local pos = Transform.from_translation(Vec3.new(0, 0, -8.0)) + local cube_comp = ModelComponent.new(cube) + + local e = world:spawn(pos, cube_comp) + print("spawned entity " .. tostring(e)) end --[[ function on_first() diff --git a/lyra-game/src/plugin.rs b/lyra-game/src/plugin.rs index e014e61..f1b8db7 100644 --- a/lyra-game/src/plugin.rs +++ b/lyra-game/src/plugin.rs @@ -1,4 +1,4 @@ -use lyra_ecs::Commands; +use lyra_ecs::CommandQueue; use lyra_resource::ResourceManager; use crate::EventsPlugin; @@ -109,10 +109,22 @@ pub struct DefaultPlugins; impl Plugin for DefaultPlugins { fn setup(&self, game: &mut Game) { + CommandQueuePlugin.setup(game); EventsPlugin.setup(game); InputPlugin.setup(game); ResourceManagerPlugin.setup(game); WindowPlugin::default().setup(game); DeltaTimePlugin.setup(game); } +} + +/// A plugin that creates a CommandQueue, and inserts it into the world as a Resource. +/// The queue is processed at the end of every system execution in the GraphExecutor. +#[derive(Default)] +pub struct CommandQueuePlugin; + +impl Plugin for CommandQueuePlugin { + fn setup(&self, game: &mut Game) { + game.world_mut().add_resource(CommandQueue::default()); + } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/script.rs b/lyra-scripting/src/lua/script.rs index e344a59..cbf7258 100644 --- a/lyra-scripting/src/lua/script.rs +++ b/lyra-scripting/src/lua/script.rs @@ -47,9 +47,6 @@ impl ScriptHost for LuaHost { provider.setup_script(script_data, ctx)?; } - let ctx = ctx.lock().expect("Failure to get Lua ScriptContext"); - try_call_lua_function(&ctx, "on_init")?; - Ok(()) } diff --git a/lyra-scripting/src/lua/system.rs b/lyra-scripting/src/lua/system.rs index ea4fbfa..7b0effc 100644 --- a/lyra-scripting/src/lua/system.rs +++ b/lyra-scripting/src/lua/system.rs @@ -1,7 +1,5 @@ -use std::ptr::NonNull; - use anyhow::anyhow; -use lyra_ecs::{query::{Entities, ResMut, View}, CommandQueue, Commands, World}; +use lyra_ecs::{query::{Entities, ResMut, View}, World}; use lyra_game::{game::GameStages, plugin::Plugin}; use lyra_reflect::TypeRegistry; use lyra_resource::ResourceManager; @@ -12,15 +10,13 @@ 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<'a>( +pub fn lua_scripts_create_contexts( 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()) { @@ -43,6 +39,24 @@ pub fn lua_scripts_create_contexts<'a>( host.setup_script(&script_data, &mut script_ctx, &mut providers)?; trace!("Finished setting up script"); + // call on_init, handle the error + let world_ptr = ScriptWorldPtr::from_ref(&world); + match host.call_script( + world_ptr, + &script_data, + &mut script_ctx, + &mut providers, + "on_init", + ) { + Ok(()) => {} + Err(e) => match e { + ScriptError::MluaError(m) => { + error!("Script '{}' ran into an error: {}", script.name(), m); + } + ScriptError::Other(_) => return Err(e.into()), + }, + } + contexts.add_context(script.id(), script_ctx); break; } else { @@ -52,14 +66,6 @@ pub fn lua_scripts_create_contexts<'a>( } } - 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 20e8c52..615812d 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,8 +1,8 @@ use std::{ptr::NonNull, sync::Arc}; -use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptEntity, ScriptWorldPtr}; +use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr}; use elua::AsLua; -use lyra_ecs::{query::dynamic::QueryDynamicType, Commands, DynamicBundle}; +use lyra_ecs::{query::dynamic::QueryDynamicType, CommandQueue, Commands, DynamicBundle, World}; use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry}; use lyra_resource::ResourceManager; @@ -64,10 +64,38 @@ impl elua::Userdata for ScriptWorldPtr { builder: &mut elua::UserdataBuilder<'a, Self>, ) -> elua::Result<()> { builder - .method_mut("spawn", |_, this, bundle: ScriptDynamicBundle| { + .method_mut("spawn", |_, this, vals: elua::ValueVec| { let world = this.as_mut(); - Ok(ScriptEntity(world.spawn(bundle.0))) + let mut bundle = DynamicBundle::new(); + + //while let Some(val) = vals.pop_front() { + for (i, val) in vals.into_iter().enumerate() { + let ud = val.as_userdata().ok_or( + elua::Error::bad_arg( + Some("World:spawn"), + 2 + i as i32, // i starts at 0 + Some("components..."), + 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 refl_data = script_brw.data.unwrap(); + //let refl_data = refl_data.as_ref(); + reflect.bundle_insert(&mut bundle, refl_data); + } + + // defer the entity spawn + // safety: Commands borrows Entities from World, the resource borrows from the world resouces, + // they are borrowing different parts of World. + let world_ptr: *mut World = world; + let mut commands_queue = world.get_resource_mut::(); + let mut commands = Commands::new(&mut commands_queue, unsafe { &mut *world_ptr }); + let entity = commands.spawn(bundle); + + Ok(ScriptEntity(entity)) }) .method_mut( "view", -- 2.40.1 From 388f686917812b215a9fe8896d3e8e70a599ce00 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 24 Feb 2024 15:27:01 -0500 Subject: [PATCH 35/41] scripting: create `FN_NAME_INTERNAL_AS_COMPONENT` for implicitly converting some types as components --- examples/testbed/scripts/test.lua | 3 +- lyra-scripting/src/lua/mod.rs | 44 ++++++++++++++++++- lyra-scripting/src/lua/world.rs | 22 +++++++--- lyra-scripting/src/lua/wrappers/delta_time.rs | 4 +- lyra-scripting/src/lua/wrappers/model_comp.rs | 7 +-- lyra-scripting/src/lua/wrappers/res_handle.rs | 21 ++++++++- 6 files changed, 84 insertions(+), 17 deletions(-) diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 1e67ad3..29f39ff 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -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 diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index ed8e36e..c068582 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -26,13 +26,52 @@ use lyra_ecs::{ Component, ComponentInfo, World }; use lyra_reflect::{Reflect, TypeRegistry}; +use crate::ScriptBorrow; pub type LuaContext = Mutex; +/// 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") } \ No newline at end of file diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 615812d..3cb081f 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -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); } diff --git a/lyra-scripting/src/lua/wrappers/delta_time.rs b/lyra-scripting/src/lua/wrappers/delta_time.rs index da6e850..027985c 100644 --- a/lyra-scripting/src/lua/wrappers/delta_time.rs +++ b/lyra-scripting/src/lua/wrappers/delta_time.rs @@ -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 { + fn from_lua(_: &'lua elua::State, _: elua::Value<'lua>) -> elua::Result { todo!() } } impl<'lua> elua::AsLua<'lua> for LuaDeltaTime { - fn as_lua(self, lua: &'lua elua::State) -> elua::Result> { + fn as_lua(self, _: &'lua elua::State) -> elua::Result> { Ok(elua::Value::Number(*self.0 as f64)) } } diff --git a/lyra-scripting/src/lua/wrappers/model_comp.rs b/lyra-scripting/src/lua/wrappers/model_comp.rs index d9445d9..442b049 100644 --- a/lyra-scripting/src/lua/wrappers/model_comp.rs +++ b/lyra-scripting/src/lua/wrappers/model_comp.rs @@ -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::(None)) - }).method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { + }) + .method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { Ok(ScriptBorrow::from_component(Some(this.0.clone()))) }); diff --git a/lyra-scripting/src/lua/wrappers/res_handle.rs b/lyra-scripting/src/lua/wrappers/res_handle.rs index c3f6d0e..884cddf 100644 --- a/lyra-scripting/src/lua/wrappers/res_handle.rs +++ b/lyra-scripting/src/lua/wrappers/res_handle.rs @@ -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); @@ -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::>() { + Some(model) => { + LuaModelComponent(ModelComponent(model.clone())).as_lua(lua) + }, + None => { + Ok(elua::Value::Nil) + } + } + }); + Ok(()) } } -- 2.40.1 From 5a7ac6ad118dab8daa7112b9097a0f907558ae88 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 24 Feb 2024 17:57:05 -0500 Subject: [PATCH 36/41] scripting: fix some doc comments --- lyra-scripting/src/lua/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index c068582..09c22d4 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -62,14 +62,14 @@ pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect"; /// 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) +/// world:spawn(pos, cube_comp) /// ``` /// /// With this function: -/// /// ```lua +/// ```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) +/// world:spawn(pos, cube) /// ``` pub const FN_NAME_INTERNAL_AS_COMPONENT: &str = "__lyra_internal_refl_as_component"; -- 2.40.1 From 5521d4a65931fc65fc6c6e2e60b73a007cf2e404 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 25 Feb 2024 17:06:53 -0500 Subject: [PATCH 37/41] scripting: start work on exposing InputActions to lua, implement inserting reflected resource --- Cargo.lock | 40 ++++ examples/testbed/scripts/test.lua | 45 +++++ examples/testbed/src/main.rs | 21 +- lyra-ecs/src/world.rs | 6 +- lyra-game/src/input/action.rs | 123 +++++++++--- lyra-game/src/input/mod.rs | 180 +++++++++++++++++- lyra-game/src/input/system.rs | 2 - lyra-reflect/src/resource.rs | 15 +- lyra-scripting/Cargo.toml | 2 + lyra-scripting/elua | 2 +- lyra-scripting/src/lib.rs | 8 + lyra-scripting/src/lua/providers/ecs.rs | 3 +- lyra-scripting/src/lua/world.rs | 28 ++- .../src/lua/wrappers/input_actions.rs | 147 ++++++++++++++ lyra-scripting/src/lua/wrappers/mod.rs | 5 +- 15 files changed, 570 insertions(+), 57 deletions(-) create mode 100644 lyra-scripting/src/lua/wrappers/input_actions.rs diff --git a/Cargo.lock b/Cargo.lock index 4f54a87..23f32fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -1558,11 +1567,13 @@ dependencies = [ "anyhow", "elua", "itertools 0.12.0", + "lazy_static", "lyra-ecs", "lyra-game", "lyra-reflect", "lyra-resource", "lyra-scripting-derive", + "regex", "thiserror", "tracing", "tracing-subscriber", @@ -2204,6 +2215,35 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "renderdoc-sys" version = "0.7.1" diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index 29f39ff..d7b91f7 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -6,6 +6,48 @@ function on_init() local e = world:spawn(pos, cube) print("spawned entity " .. tostring(e)) + + local action_handler_tbl = { + layouts = { 0 }, + actions = { + MoveForwardBackward = "Axis", + MoveLeftRight = "Axis", + MoveUpDown = "Axis", + LookLeftRight = "Axis", + LookUpDown = "Axis", + LookRoll = "Axis", + }, + mappings = { + { + layout = 0, + binds = { + MoveForwardBackward = { + "key:w=1.0", "key:s=-1.0" + }, + MoveLeftRight = { + "key:a=-1.0", "key:d=1.0" + }, + MoveUpDown = { + "key:c=1.0", "key:z=-1.0" + }, + LookLeftRight = { + "key:left=-1.0", "key:right=1.0", + "mouse:axis:x" + }, + LookUpDown = { + "key:up=-1.0", "key:down=1.0", + "mouse:axis:y", + }, + LookRoll = { + "key:e=-1.0", "key:q=1.0", + } + } + } + } + } + + local handler = ActionHandler.new(action_handler_tbl) + world:add_resource(handler) end --[[ function on_first() @@ -25,6 +67,9 @@ function on_update() return t end, Transform) + + --local input = world:resource(Input) + --input. end --[[ function on_post_update() diff --git a/examples/testbed/src/main.rs b/examples/testbed/src/main.rs index d1b9a99..d9d840a 100644 --- a/examples/testbed/src/main.rs +++ b/examples/testbed/src/main.rs @@ -240,7 +240,7 @@ async fn main() { }; let action_handler_plugin = |game: &mut Game| { - let action_handler = ActionHandler::new() + /* let action_handler = ActionHandler::builder() .add_layout(LayoutId::from(0)) .add_action(CommonActionLabel::MoveForwardBackward, Action::new(ActionKind::Axis)) @@ -250,7 +250,7 @@ async fn main() { .add_action(CommonActionLabel::LookUpDown, Action::new(ActionKind::Axis)) .add_action(CommonActionLabel::LookRoll, Action::new(ActionKind::Axis)) - .add_mapping(ActionMapping::new(LayoutId::from(0), ActionMappingId::from(0)) + .add_mapping(ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0)) .bind(CommonActionLabel::MoveForwardBackward, &[ ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0), ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0) @@ -282,22 +282,9 @@ async fn main() { .finish() ); - /* #[allow(unused_variables)] - let test_system = |world: &mut World| -> anyhow::Result<()> { - let handler = world.get_resource::(); - - if let Some(alpha) = handler.get_axis_modifier("look_rotate") { - debug!("'look_rotate': {alpha}"); - } - - Ok(()) - }; */ - let world = game.world_mut(); world.add_resource(action_handler); - world.spawn((Vec3::new(0.5, 0.1, 3.0),)); - game.with_plugin(InputActionPlugin); - //game.with_system("input_test", test_system, &[]); + game.with_plugin(InputActionPlugin); */ }; let script_test_plugin = |game: &mut Game| { @@ -322,6 +309,6 @@ async fn main() { .with_plugin(script_test_plugin) //.with_plugin(fps_plugin) .with_plugin(jiggle_plugin) - .with_plugin(FreeFlyCameraPlugin) + //.with_plugin(FreeFlyCameraPlugin) .run().await; } diff --git a/lyra-ecs/src/world.rs b/lyra-ecs/src/world.rs index 1c89301..1e06f71 100644 --- a/lyra-ecs/src/world.rs +++ b/lyra-ecs/src/world.rs @@ -255,7 +255,8 @@ impl World { /// Will panic if the resource is not in the world. See [`try_get_resource`] for /// a function that returns an option. pub fn get_resource(&self) -> Ref { - self.resources.get(&TypeId::of::()).unwrap() + self.resources.get(&TypeId::of::()) + .expect(&format!("World is missing resource of type '{}'", std::any::type_name::())) .get() } @@ -277,7 +278,8 @@ impl World { /// Will panic if the resource is not in the world. See [`try_get_resource_mut`] for /// a function that returns an option. pub fn get_resource_mut(&self) -> RefMut { - self.resources.get(&TypeId::of::()).unwrap() + self.resources.get(&TypeId::of::()) + .expect(&format!("World is missing resource of type '{}'", std::any::type_name::())) .get_mut() } diff --git a/lyra-game/src/input/action.rs b/lyra-game/src/input/action.rs index db60930..aa756bf 100644 --- a/lyra-game/src/input/action.rs +++ b/lyra-game/src/input/action.rs @@ -2,6 +2,7 @@ use std::{collections::HashMap, ops::Deref, hash::{Hash, DefaultHasher, Hasher}, use glam::Vec2; use lyra_ecs::world::World; +use lyra_reflect::Reflect; use crate::{plugin::Plugin, game::GameStages, EventQueue}; @@ -213,7 +214,7 @@ impl Action { } #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct LayoutId(u32); +pub struct LayoutId(pub u32); impl From for LayoutId { fn from(value: u32) -> Self { @@ -240,7 +241,7 @@ impl Layout { } #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct ActionMappingId(u32); +pub struct ActionMappingId(pub u32); impl From for ActionMappingId { fn from(value: u32) -> Self { @@ -264,6 +265,10 @@ impl ActionMapping { } } + pub fn builder(layout: LayoutId, id: ActionMappingId) -> ActionMappingBuilder { + ActionMappingBuilder::new(ActionMapping::new(layout, id)) + } + /// Creates a binding for the action. /// /// If the action is not in this layout, this will panic! @@ -271,7 +276,7 @@ impl ActionMapping { /// Parameters: /// * `action` - The label corresponding to the action in this Layout. /// * `bind` - The Binding to add to the Action. - pub fn bind(mut self, action: L, bindings: &[Binding]) -> Self + pub fn bind(&mut self, action: L, bindings: &[Binding]) -> &mut Self where L: ActionLabel { @@ -283,32 +288,48 @@ impl ActionMapping { self } - /// Creates multiple binding for the action. - /// - /// If the action is not in this layout, this will panic! - /// - /// Parameters: - /// * `action_label` - The label corresponding to the action in this Layout. - /// * `bindings` - The list of Bindings to add to the Action. - /* pub fn add_bindings(&mut self, action_label: String, bindings: &[Binding]) -> &mut Self { - let mut bindings = bindings.to_vec(); - let action_binds = self.action_binds.entry(action_label) - .or_insert_with(Vec::new); - action_binds.append(&mut bindings); - - self - } */ - pub fn finish(self) -> Self { self } } -#[derive(Clone, Default)] +pub struct ActionMappingBuilder { + mapping: ActionMapping, +} + +impl ActionMappingBuilder { + fn new(mapping: ActionMapping) -> Self { + Self { + mapping, + } + } + + pub fn bind(mut self, action: L, bindings: &[Binding]) -> Self + where + L: ActionLabel + { + let mut bindings = bindings.to_vec(); + + let action_binds = self.mapping.action_binds.entry(action.label_hash()).or_default(); + action_binds.append(&mut bindings); + + self + } + + pub fn finish(self) -> ActionMapping { + self.mapping + } +} + +#[derive(Clone, Default, Reflect)] pub struct ActionHandler { + #[reflect(skip)] // TODO: dont just skip all these pub actions: HashMap, + #[reflect(skip)] pub layouts: HashMap, + #[reflect(skip)] pub current_layout: LayoutId, + #[reflect(skip)] pub current_mapping: ActionMappingId, } @@ -317,26 +338,31 @@ impl ActionHandler { Self::default() } - pub fn add_layout(mut self, id: LayoutId) -> Self { - self.layouts.insert(id, Layout::new()); - - self + pub fn builder() -> ActionHandlerBuilder { + ActionHandlerBuilder::default() } - pub fn add_action(mut self, label: L, action: Action) -> Self + pub fn add_layout(&mut self, id: LayoutId) { + self.layouts.insert(id, Layout::new()); + } + + pub fn action(&self, label: L) -> Option<&Action> + where + L: ActionLabel + { + self.actions.get(&label.label_hash()) + } + + pub fn add_action(&mut self, label: L, action: Action) where L: ActionLabel { self.actions.insert(label.label_hash(), action); - - self } - pub fn add_mapping(mut self, mapping: ActionMapping) -> Self { + pub fn add_mapping(&mut self, mapping: ActionMapping) { let layout = self.layouts.get_mut(&mapping.layout).unwrap(); layout.add_mapping(mapping); - - self } /// Returns true if the action is pressed (or was just pressed). @@ -443,6 +469,43 @@ impl ActionHandler { } } +#[derive(Default)] +pub struct ActionHandlerBuilder { + handler: ActionHandler, +} + +impl ActionHandlerBuilder { + pub fn new() -> Self { + Self::default() + } + + pub fn add_layout(mut self, id: LayoutId) -> Self { + self.handler.layouts.insert(id, Layout::new()); + + self + } + + pub fn add_action(mut self, label: L, action: Action) -> Self + where + L: ActionLabel + { + self.handler.actions.insert(label.label_hash(), action); + + self + } + + pub fn add_mapping(mut self, mapping: ActionMapping) -> Self { + let layout = self.handler.layouts.get_mut(&mapping.layout).unwrap(); + layout.add_mapping(mapping); + + self + } + + pub fn finish(self) -> ActionHandler { + self.handler + } +} + fn actions_system(world: &mut World) -> anyhow::Result<()> { let keys = world.try_get_resource::>() .map(|r| r.deref().clone()); diff --git a/lyra-game/src/input/mod.rs b/lyra-game/src/input/mod.rs index f03bd5f..bfa8175 100644 --- a/lyra-game/src/input/mod.rs +++ b/lyra-game/src/input/mod.rs @@ -11,4 +11,182 @@ pub mod buttons; pub use buttons::*; pub mod action; -pub use action::*; \ No newline at end of file +pub use action::*; + +pub type KeyCode = winit::event::VirtualKeyCode; + +/// Parses a [`KeyCode`] from a [`&str`]. +/// +/// There are some changes to a few keycodes. All the number keys `Key1`, `Key2`, etc., have +/// the `Key` prefix removed; so they are expected to be `1`, `2`, etc. +pub fn keycode_from_str(s: &str) -> Option { + let s = s.to_lowercase(); + let s = s.as_str(); + + match s { + "1" => Some(KeyCode::Key1), + "2" => Some(KeyCode::Key2), + "3" => Some(KeyCode::Key3), + "4" => Some(KeyCode::Key4), + "5" => Some(KeyCode::Key5), + "6" => Some(KeyCode::Key6), + "7" => Some(KeyCode::Key7), + "8" => Some(KeyCode::Key8), + "9" => Some(KeyCode::Key9), + "0" => Some(KeyCode::Key0), + "a" => Some(KeyCode::A), + "b" => Some(KeyCode::B), + "c" => Some(KeyCode::C), + "d" => Some(KeyCode::D), + "e" => Some(KeyCode::E), + "f" => Some(KeyCode::F), + "g" => Some(KeyCode::G), + "h" => Some(KeyCode::H), + "i" => Some(KeyCode::I), + "j" => Some(KeyCode::J), + "k" => Some(KeyCode::K), + "l" => Some(KeyCode::L), + "m" => Some(KeyCode::M), + "n" => Some(KeyCode::N), + "o" => Some(KeyCode::O), + "p" => Some(KeyCode::P), + "q" => Some(KeyCode::Q), + "r" => Some(KeyCode::R), + "s" => Some(KeyCode::S), + "t" => Some(KeyCode::T), + "u" => Some(KeyCode::U), + "v" => Some(KeyCode::V), + "w" => Some(KeyCode::W), + "x" => Some(KeyCode::X), + "y" => Some(KeyCode::Y), + "z" => Some(KeyCode::Z), + "escape" => Some(KeyCode::Escape), + "f1" => Some(KeyCode::F1), + "f2" => Some(KeyCode::F2), + "f3" => Some(KeyCode::F3), + "f4" => Some(KeyCode::F4), + "f5" => Some(KeyCode::F5), + "f6" => Some(KeyCode::F6), + "f7" => Some(KeyCode::F7), + "f8" => Some(KeyCode::F8), + "f9" => Some(KeyCode::F9), + "f10" => Some(KeyCode::F10), + "f11" => Some(KeyCode::F11), + "f12" => Some(KeyCode::F12), + "f13" => Some(KeyCode::F13), + "f14" => Some(KeyCode::F14), + "f15" => Some(KeyCode::F15), + "f16" => Some(KeyCode::F16), + "f17" => Some(KeyCode::F17), + "f18" => Some(KeyCode::F18), + "f19" => Some(KeyCode::F19), + "f20" => Some(KeyCode::F20), + "f21" => Some(KeyCode::F21), + "f22" => Some(KeyCode::F22), + "f23" => Some(KeyCode::F23), + "f24" => Some(KeyCode::F24), + "snapshot" => Some(KeyCode::Snapshot), + "scroll" => Some(KeyCode::Scroll), + "pause" => Some(KeyCode::Pause), + "insert" => Some(KeyCode::Insert), + "home" => Some(KeyCode::Home), + "delete" => Some(KeyCode::Delete), + "end" => Some(KeyCode::End), + "pagedown" => Some(KeyCode::PageDown), + "pageup" => Some(KeyCode::PageUp), + "left" => Some(KeyCode::Left), + "up" => Some(KeyCode::Up), + "right" => Some(KeyCode::Right), + "down" => Some(KeyCode::Down), + "back" => Some(KeyCode::Back), + "return" => Some(KeyCode::Return), + "space" => Some(KeyCode::Space), + "compose" => Some(KeyCode::Compose), + "caret" => Some(KeyCode::Caret), + "numlock" => Some(KeyCode::Numlock), + "numpad0" => Some(KeyCode::Numpad0), + "numpad1" => Some(KeyCode::Numpad1), + "numpad2" => Some(KeyCode::Numpad2), + "numpad3" => Some(KeyCode::Numpad3), + "numpad4" => Some(KeyCode::Numpad4), + "numpad5" => Some(KeyCode::Numpad5), + "numpad6" => Some(KeyCode::Numpad6), + "numpad7" => Some(KeyCode::Numpad7), + "numpad8" => Some(KeyCode::Numpad8), + "numpad9" => Some(KeyCode::Numpad9), + "numpadadd" => Some(KeyCode::NumpadAdd), + "numpaddivide" => Some(KeyCode::NumpadDivide), + "numpaddecimal" => Some(KeyCode::NumpadDecimal), + "numpadcomma" => Some(KeyCode::NumpadComma), + "numpadenter" => Some(KeyCode::NumpadEnter), + "numpadequals" => Some(KeyCode::NumpadEquals), + "numpadmultiply" => Some(KeyCode::NumpadMultiply), + "numpadsubtract" => Some(KeyCode::NumpadSubtract), + "abntc1" => Some(KeyCode::AbntC1), + "abntc2" => Some(KeyCode::AbntC2), + "apostrophe" => Some(KeyCode::Apostrophe), + "apps" => Some(KeyCode::Apps), + "asterisk" => Some(KeyCode::Asterisk), + "at" => Some(KeyCode::At), + "ax" => Some(KeyCode::Ax), + "backslash" => Some(KeyCode::Backslash), + "calculator" => Some(KeyCode::Calculator), + "capital" => Some(KeyCode::Capital), + "colon" => Some(KeyCode::Colon), + "comma" => Some(KeyCode::Comma), + "convert" => Some(KeyCode::Convert), + "equals" => Some(KeyCode::Equals), + "grave" => Some(KeyCode::Grave), + "kana" => Some(KeyCode::Kana), + "kanji" => Some(KeyCode::Kanji), + "lalt" => Some(KeyCode::LAlt), + "lbracket" => Some(KeyCode::LBracket), + "lcontrol" => Some(KeyCode::LControl), + "lshift" => Some(KeyCode::LShift), + "lwin" => Some(KeyCode::LWin), + "mail" => Some(KeyCode::Mail), + "mediaselect" => Some(KeyCode::MediaSelect), + "mediastop" => Some(KeyCode::MediaStop), + "minus" => Some(KeyCode::Minus), + "mute" => Some(KeyCode::Mute), + "mycomputer" => Some(KeyCode::MyComputer), + "navigateforward" => Some(KeyCode::NavigateForward), + "navigatebackward" => Some(KeyCode::NavigateBackward), + "nexttrack" => Some(KeyCode::NextTrack), + "noconvert" => Some(KeyCode::NoConvert), + "oem102" => Some(KeyCode::OEM102), + "period" => Some(KeyCode::Period), + "playpause" => Some(KeyCode::PlayPause), + "plus" => Some(KeyCode::Plus), + "power" => Some(KeyCode::Power), + "prevtrack" => Some(KeyCode::PrevTrack), + "ralt" => Some(KeyCode::RAlt), + "rbracket" => Some(KeyCode::RBracket), + "rcontrol" => Some(KeyCode::RControl), + "rshift" => Some(KeyCode::RShift), + "rwin" => Some(KeyCode::RWin), + "semicolon" => Some(KeyCode::Semicolon), + "slash" => Some(KeyCode::Slash), + "sleep" => Some(KeyCode::Sleep), + "stop" => Some(KeyCode::Stop), + "sysrq" => Some(KeyCode::Sysrq), + "tab" => Some(KeyCode::Tab), + "underline" => Some(KeyCode::Underline), + "unlabeled" => Some(KeyCode::Unlabeled), + "volumedown" => Some(KeyCode::VolumeDown), + "volumeup" => Some(KeyCode::VolumeUp), + "wake" => Some(KeyCode::Wake), + "webback" => Some(KeyCode::WebBack), + "webfavorites" => Some(KeyCode::WebFavorites), + "webforward" => Some(KeyCode::WebForward), + "webhome" => Some(KeyCode::WebHome), + "webrefresh" => Some(KeyCode::WebRefresh), + "websearch" => Some(KeyCode::WebSearch), + "webstop" => Some(KeyCode::WebStop), + "yen" => Some(KeyCode::Yen), + "copy" => Some(KeyCode::Copy), + "paste" => Some(KeyCode::Paste), + "cut" => Some(KeyCode::Cut), + _ => None + } +} \ No newline at end of file diff --git a/lyra-game/src/input/system.rs b/lyra-game/src/input/system.rs index 419d079..ec6b20f 100755 --- a/lyra-game/src/input/system.rs +++ b/lyra-game/src/input/system.rs @@ -8,8 +8,6 @@ use crate::{EventQueue, plugin::Plugin, game::GameStages}; use super::{events::*, InputButtons, InputEvent}; -pub type KeyCode = winit::event::VirtualKeyCode; - #[derive(Default)] pub struct InputSystem; diff --git a/lyra-reflect/src/resource.rs b/lyra-reflect/src/resource.rs index aa95293..ab7445c 100644 --- a/lyra-reflect/src/resource.rs +++ b/lyra-reflect/src/resource.rs @@ -1,4 +1,4 @@ -use std::{any::TypeId, cell::{Ref, RefMut}, ptr::NonNull}; +use std::{any::{Any, TypeId}, cell::{Ref, RefMut}, ptr::NonNull}; use lyra_ecs::{World, ResourceObject}; @@ -11,6 +11,7 @@ pub struct ReflectedResource { fn_reflect: for<'a> fn (world: &'a World) -> Option>, fn_reflect_mut: for<'a> fn (world: &'a mut World) -> Option>, fn_reflect_ptr: fn (world: &mut World) -> Option>, + fn_refl_insert: fn (world: &mut World, this: Box), } impl ReflectedResource { @@ -27,6 +28,11 @@ impl ReflectedResource { pub fn reflect_ptr(&self, world: &mut World) -> Option> { (self.fn_reflect_ptr)(world) } + + /// Insert the resource into the world. + pub fn insert(&self, world: &mut World, this: Box) { + (self.fn_refl_insert)(world, this) + } } impl FromType for ReflectedResource { @@ -45,6 +51,13 @@ impl FromType for ReflectedResource { world.try_get_resource_ptr::() .map(|ptr| ptr.cast::()) }, + fn_refl_insert: |world: &mut World, this: Box| { + let res = this as Box; + let res = res.downcast::() + .expect("Provided a non-matching type to ReflectedResource insert method!"); + let res = *res; + world.add_resource(res); + } } } } \ No newline at end of file diff --git a/lyra-scripting/Cargo.toml b/lyra-scripting/Cargo.toml index 2782c56..3582afd 100644 --- a/lyra-scripting/Cargo.toml +++ b/lyra-scripting/Cargo.toml @@ -23,6 +23,8 @@ tracing = "0.1.37" #mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? elua = { path = "./elua", optional = true } itertools = "0.12.0" +regex = "1.10.3" +lazy_static = "1.4.0" [dev-dependencies] diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 70e2985..beea6c3 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 70e2985cc44fdb30cdf2157c50d2f0e3385e08fa +Subproject commit beea6c33fcb2f5bd16f40e1919b61a89b4aaecb6 diff --git a/lyra-scripting/src/lib.rs b/lyra-scripting/src/lib.rs index 4ba4a98..6d6a77a 100644 --- a/lyra-scripting/src/lib.rs +++ b/lyra-scripting/src/lib.rs @@ -61,6 +61,14 @@ impl ReflectBranch { } } + /// Gets self as a [`ReflectedResource`], returning `None` if self is not an instance of it. + pub fn as_resource(&self) -> Option<&ReflectedResource> { + match self { + ReflectBranch::Resource(v) => Some(v), + _ => None + } + } + /// Returns a boolean indicating if `self` is a reflection of a Resource. pub fn is_resource(&self) -> bool { matches!(self, ReflectBranch::Resource(_)) diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index dd72a0e..edcb4ae 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, LuaModelComponent}, LuaContext, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; +use crate::{lua::{wrappers::{LuaActionHandler, LuaDeltaTime, LuaModelComponent}, LuaContext, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; #[derive(Default)] pub struct LyraEcsApiProvider; @@ -21,6 +21,7 @@ impl ScriptApiProvider for LyraEcsApiProvider { globals.set("World", ctx.create_proxy::()?)?; globals.set("DynamicBundle", ctx.create_proxy::()?)?; globals.set("ModelComponent", ctx.create_proxy::()?)?; + globals.set("ActionHandler", ctx.create_proxy::()?)?; let dt_table = create_reflect_table::(&ctx)?; globals.set("DeltaTime", dt_table)?; diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 3cb081f..1e1a03a 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,7 +1,7 @@ use std::{ptr::NonNull, sync::Arc}; use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr}; -use elua::AsLua; +use elua::{AnyUserdata, AsLua, Table}; use lyra_ecs::{query::dynamic::QueryDynamicType, CommandQueue, Commands, DynamicBundle, World}; use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry}; use lyra_resource::ResourceManager; @@ -279,6 +279,32 @@ impl elua::Userdata for ScriptWorldPtr { Ok(elua::Value::Nil) } }) + .method_mut("add_resource", |_, this, res: elua::Value| { + let reflect = match res { + elua::Value::Userdata(ud) => ud + .execute_method::<_, ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ()) + .expect("Type does not implement 'reflect_type' properly"), + elua::Value::Table(t) => { + let f: elua::Function = t.get(FN_NAME_INTERNAL_REFLECT)?; + f.exec::<_, ScriptBorrow>(()) + .expect("Type does not implement 'reflect_type' properly") + } + _ => { + panic!("how"); + } + }; + + let data = reflect.data + .expect("Its expected that 'FN_NAME_INTERNAL_REFLECT' returns data in World:add_resource"); + + let res = reflect.reflect_branch.as_resource() + .ok_or(elua::Error::runtime("Provided type is not a resource!"))?; + + let world = this.as_mut(); + res.insert(world, data); + + Ok(()) + }) .method_mut("request_res", |_, this, path: String| { let world = this.as_mut(); let mut man = world.get_resource_mut::(); diff --git a/lyra-scripting/src/lua/wrappers/input_actions.rs b/lyra-scripting/src/lua/wrappers/input_actions.rs new file mode 100644 index 0000000..9325280 --- /dev/null +++ b/lyra-scripting/src/lua/wrappers/input_actions.rs @@ -0,0 +1,147 @@ +use lyra_game::input::{keycode_from_str, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, LayoutId, MouseAxis, MouseInput}; + +use lazy_static::lazy_static; +use regex::Regex; + +use crate::{lua::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow}; + +lazy_static! { + static ref BINDING_INPUT_RE: Regex = Regex::new(r"(\w+):(\w+)(?:=(-?\d+(?:.\d+)?))?").unwrap(); +} + +#[derive(Clone)] +pub struct LuaActionHandler { + handler: ActionHandler +} + +impl elua::Userdata for LuaActionHandler { + fn name() -> String { + "ActionHandler".to_string() + } + + fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + builder.function("new", |_, table: elua::Table| { + let mut handler = ActionHandler::new(); + + // create the layouts + let layouts = table.get::<_, elua::Table>("layouts") + .map_err(|_| elua::Error::runtime("missing 'layouts' in ActionHandler table"))?; + for layout_id in layouts.sequence_iter::() { + let layout_id = layout_id?; + + handler.add_layout(LayoutId(layout_id)); + } + + let actions = table.get::<_, elua::Table>("actions") + .map_err(|_| elua::Error::runtime("missing 'actions' in ActionHandler table"))?; + for pair in actions.pairs::() { + let (action_lbl, action_type) = pair?; + let action_type = action_type.to_lowercase(); + + let action_type = match action_type.as_str() { + "axis" => ActionKind::Axis, + "button" => ActionKind::Button, + _ => todo!("Handle invalid axis type"), + }; + + handler.add_action(action_lbl, Action::new(action_type)); + } + + let mappings= table.get::<_, elua::Table>("mappings") + .map_err(|_| elua::Error::runtime("missing 'mappings' in ActionHandler table"))?; + for (map_id, tbl) in mappings.sequence_iter::().enumerate() { + let tbl = tbl?; + + let layout_id = tbl.get::<_, u32>("layout")?; + let mut mapping = ActionMapping::new(LayoutId(layout_id), ActionMappingId(map_id as u32)); + + let binds_tbl = tbl.get::<_, elua::Table>("binds") + .map_err(|_| elua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?; + for pair in binds_tbl.pairs::() { + let (action_lbl, input_binds) = pair?; + + for input in input_binds.sequence_iter::() { + let input = input?.to_lowercase(); + + let action = handler.action(&action_lbl) + .ok_or(elua::Error::Runtime(format!("Unknown action specified in mapping binds: {}", action_lbl)))?; + + let mut binds = Vec::new(); + + let input_split: Vec<&str> = input.split(":").collect(); + let input_name = input_split[0]; + let button = input_split[1]; + + if action.kind == ActionKind::Axis { + if button == "axis" { + let axis_name = input_split[2]; + + let src = process_axis_string(input_name, axis_name) + .ok_or(elua::Error::Runtime(format!("invalid bind '{input_name}', unable to find device or axis for device")))?; + binds.push(src.into_binding()); + } else { + // splits 'down=1' into 'down' and '1' + let (button, val_str) = button.split_once("=") + .ok_or(elua::Error::Runtime(format!("invalid bind string for Axis Action: '{input}' (expected '=' with float)")))?; + + let val = val_str.parse::() + .map_err(|e| elua::Error::Runtime(format!("invalid bind string for Axis Action: '{input}' ({e})")))?; + + let src = process_keyboard_string(button) + .ok_or(elua::Error::Runtime(format!("invalid key in bind: '{button}'")))?; + binds.push(src.into_binding_modifier(val)); + } + } else { + todo!("Process bindings for Button Actions"); + } + + mapping.bind(action_lbl.clone(), &binds); + } + } + + handler.add_mapping(mapping); + } + + Ok(LuaActionHandler { + handler, + }) + }) + .method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { + Ok(ScriptBorrow::from_resource::(Some(this.handler.clone()))) + }) + .method(FN_NAME_INTERNAL_REFLECT_TYPE, |_, _, ()| { + Ok(ScriptBorrow::from_resource::(None)) + }); + + Ok(()) + } +} + +impl<'a> elua::FromLua<'a> for LuaActionHandler { + 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("ActionHandler", &tyname))?; + let handle = ud.as_ref::()?; + + Ok(handle.clone()) + } +} + +fn process_keyboard_string(key_name: &str) -> Option { + let key = keycode_from_str(key_name)?; + + Some(ActionSource::Keyboard(key)) +} + +fn process_axis_string(device: &str, axis_name: &str) -> Option { + match device { + "mouse" => match axis_name { + "y" => Some(ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y))), + "x" => Some(ActionSource::Mouse(MouseInput::Axis(MouseAxis::X))), + "scroll" | "scrollwheel" => Some(ActionSource::Mouse(MouseInput::Axis(MouseAxis::ScrollWheel))), + _ => None, + }, + _ => None + } +} diff --git a/lyra-scripting/src/lua/wrappers/mod.rs b/lyra-scripting/src/lua/wrappers/mod.rs index 244419d..81b3255 100644 --- a/lyra-scripting/src/lua/wrappers/mod.rs +++ b/lyra-scripting/src/lua/wrappers/mod.rs @@ -8,4 +8,7 @@ pub mod res_handle; pub use res_handle::*; pub mod model_comp; -pub use model_comp::*; \ No newline at end of file +pub use model_comp::*; + +pub mod input_actions; +pub use input_actions::*; \ No newline at end of file -- 2.40.1 From d0179cda697037bd9a7eebf148a047663db5cb3a Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sun, 25 Feb 2024 17:42:14 -0500 Subject: [PATCH 38/41] scripting: get FreeFlyCamera working with ActionHandler created by Lua --- examples/testbed/src/free_fly_camera.rs | 12 ++++++------ examples/testbed/src/main.rs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/testbed/src/free_fly_camera.rs b/examples/testbed/src/free_fly_camera.rs index 63f8cc3..f4d7411 100644 --- a/examples/testbed/src/free_fly_camera.rs +++ b/examples/testbed/src/free_fly_camera.rs @@ -55,9 +55,9 @@ pub fn free_fly_camera_controller(delta_time: Res, handler: Res, handler: Res Date: Sun, 25 Feb 2024 20:53:20 -0500 Subject: [PATCH 39/41] scripting: expose all other ActionHandler methods to Lua --- Cargo.lock | 40 ------------ examples/testbed/scripts/test.lua | 20 +++--- lyra-scripting/Cargo.toml | 2 - lyra-scripting/elua | 2 +- lyra-scripting/src/lua/providers/ecs.rs | 1 + lyra-scripting/src/lua/world.rs | 4 +- .../src/lua/wrappers/input_actions.rs | 61 +++++++++++++++---- 7 files changed, 66 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23f32fe..4f54a87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,15 +45,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - [[package]] name = "allocator-api2" version = "0.2.16" @@ -1567,13 +1558,11 @@ dependencies = [ "anyhow", "elua", "itertools 0.12.0", - "lazy_static", "lyra-ecs", "lyra-game", "lyra-reflect", "lyra-resource", "lyra-scripting-derive", - "regex", "thiserror", "tracing", "tracing-subscriber", @@ -2215,35 +2204,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "regex" -version = "1.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - [[package]] name = "renderdoc-sys" version = "0.7.1" diff --git a/examples/testbed/scripts/test.lua b/examples/testbed/scripts/test.lua index d7b91f7..65a8460 100644 --- a/examples/testbed/scripts/test.lua +++ b/examples/testbed/scripts/test.lua @@ -7,7 +7,7 @@ function on_init() local e = world:spawn(pos, cube) print("spawned entity " .. tostring(e)) - local action_handler_tbl = { + local handler = ActionHandler.new { layouts = { 0 }, actions = { MoveForwardBackward = "Axis", @@ -16,6 +16,7 @@ function on_init() LookLeftRight = "Axis", LookUpDown = "Axis", LookRoll = "Axis", + ObjectsMoveUpDown = "Axis" }, mappings = { { @@ -40,13 +41,15 @@ function on_init() }, LookRoll = { "key:e=-1.0", "key:q=1.0", + }, + ObjectsMoveUpDown = { + "key:u=1.0", "key:j=-1.0" } } } } } - local handler = ActionHandler.new(action_handler_tbl) world:add_resource(handler) end @@ -61,15 +64,16 @@ 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) - t:translate(0, 0.5 * dt, 0) - - return t + if move_objs ~= nil then + t:translate(0, move_objs * 0.35 * dt, 0) + return t + end end, Transform) - - --local input = world:resource(Input) - --input. end --[[ function on_post_update() diff --git a/lyra-scripting/Cargo.toml b/lyra-scripting/Cargo.toml index 3582afd..2782c56 100644 --- a/lyra-scripting/Cargo.toml +++ b/lyra-scripting/Cargo.toml @@ -23,8 +23,6 @@ tracing = "0.1.37" #mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? elua = { path = "./elua", optional = true } itertools = "0.12.0" -regex = "1.10.3" -lazy_static = "1.4.0" [dev-dependencies] diff --git a/lyra-scripting/elua b/lyra-scripting/elua index beea6c3..936ea60 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit beea6c33fcb2f5bd16f40e1919b61a89b4aaecb6 +Subproject commit 936ea606343dbcf5321b4b988de65daf8769ff3f diff --git a/lyra-scripting/src/lua/providers/ecs.rs b/lyra-scripting/src/lua/providers/ecs.rs index edcb4ae..7982271 100644 --- a/lyra-scripting/src/lua/providers/ecs.rs +++ b/lyra-scripting/src/lua/providers/ecs.rs @@ -12,6 +12,7 @@ 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::(); } fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 1e1a03a..1cec468 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -1,7 +1,7 @@ use std::{ptr::NonNull, sync::Arc}; use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr}; -use elua::{AnyUserdata, AsLua, Table}; +use elua::AsLua; use lyra_ecs::{query::dynamic::QueryDynamicType, CommandQueue, Commands, DynamicBundle, World}; use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry}; use lyra_resource::ResourceManager; @@ -268,7 +268,7 @@ impl elua::Userdata for ScriptWorldPtr { let reg_type = this .as_ref() .get_type::(reflect.reflect_branch.reflect_type_id()) - .unwrap(); + .expect("Resource is not type registered!"); let proxy = reg_type .get_data::() .expect("Type does not have ReflectLuaProxy as a TypeData"); diff --git a/lyra-scripting/src/lua/wrappers/input_actions.rs b/lyra-scripting/src/lua/wrappers/input_actions.rs index 9325280..ee393ad 100644 --- a/lyra-scripting/src/lua/wrappers/input_actions.rs +++ b/lyra-scripting/src/lua/wrappers/input_actions.rs @@ -1,15 +1,11 @@ -use lyra_game::input::{keycode_from_str, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, LayoutId, MouseAxis, MouseInput}; +use lyra_game::input::{keycode_from_str, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, ActionState, LayoutId, MouseAxis, MouseInput}; +use crate::lyra_engine; -use lazy_static::lazy_static; -use regex::Regex; +use lyra_reflect::Reflect; -use crate::{lua::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow}; +use crate::{lua::{LuaWrapper, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow}; -lazy_static! { - static ref BINDING_INPUT_RE: Regex = Regex::new(r"(\w+):(\w+)(?:=(-?\d+(?:.\d+)?))?").unwrap(); -} - -#[derive(Clone)] +#[derive(Clone, Reflect)] pub struct LuaActionHandler { handler: ActionHandler } @@ -23,7 +19,7 @@ impl elua::Userdata for LuaActionHandler { builder.function("new", |_, table: elua::Table| { let mut handler = ActionHandler::new(); - // create the layouts + // create the layouts and add them to the handler let layouts = table.get::<_, elua::Table>("layouts") .map_err(|_| elua::Error::runtime("missing 'layouts' in ActionHandler table"))?; for layout_id in layouts.sequence_iter::() { @@ -32,6 +28,7 @@ impl elua::Userdata for LuaActionHandler { handler.add_layout(LayoutId(layout_id)); } + // add the actions to the handler let actions = table.get::<_, elua::Table>("actions") .map_err(|_| elua::Error::runtime("missing 'actions' in ActionHandler table"))?; for pair in actions.pairs::() { @@ -47,6 +44,7 @@ impl elua::Userdata for LuaActionHandler { handler.add_action(action_lbl, Action::new(action_type)); } + // find the mappings and start processing them let mappings= table.get::<_, elua::Table>("mappings") .map_err(|_| elua::Error::runtime("missing 'mappings' in ActionHandler table"))?; for (map_id, tbl) in mappings.sequence_iter::().enumerate() { @@ -55,6 +53,8 @@ impl elua::Userdata for LuaActionHandler { let layout_id = tbl.get::<_, u32>("layout")?; let mut mapping = ActionMapping::new(LayoutId(layout_id), ActionMappingId(map_id as u32)); + // find the binds and start processing them + // the keys are used as the action names, and then the value is an array (lua table) let binds_tbl = tbl.get::<_, elua::Table>("binds") .map_err(|_| elua::Error::runtime("missing 'binds' in ActionHandler 'mappings' table"))?; for pair in binds_tbl.pairs::() { @@ -106,10 +106,43 @@ impl elua::Userdata for LuaActionHandler { handler, }) }) + .method("get_axis", |_, this, action: String| { + Ok(this.handler.get_axis_modifier(action)) + }) + .method("is_pressed", |_, this, action: String| { + Ok(this.handler.is_action_pressed(action)) + }) + .method("was_just_pressed", |_, this, action: String| { + Ok(this.handler.was_action_just_pressed(action)) + }) + .method("was_just_released", |_, this, action: String| { + Ok(this.handler.was_action_just_released(action)) + }) + .method("get_just_pressed", |_, this, action: String| { + Ok(this.handler.get_just_pressed_modifier(action)) + }) + .method("get_action_state", |lua, this, action: String| { + let state = this.handler.get_action_state(action); + + let (name, val) = match state { + ActionState::Idle => ("Idle", None), + ActionState::Pressed(v) => ("Pressed", Some(v)), + ActionState::JustPressed(v) => ("JustPressed", Some(v)), + ActionState::JustReleased => ("JustReleased", None), + ActionState::Axis(v) => ("Axis", Some(v)), + ActionState::Other(v) => ("Other", Some(v)), + }; + + let mut multi = elua::ValueVec::new(); + multi.push_val(lua, name)?; + multi.push_val(lua, val)?; + + Ok(elua::Value::Multi(multi)) + }) .method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { Ok(ScriptBorrow::from_resource::(Some(this.handler.clone()))) }) - .method(FN_NAME_INTERNAL_REFLECT_TYPE, |_, _, ()| { + .function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| { Ok(ScriptBorrow::from_resource::(None)) }); @@ -128,6 +161,12 @@ impl<'a> elua::FromLua<'a> for LuaActionHandler { } } +impl LuaWrapper for LuaActionHandler { + fn wrapped_type_id() -> std::any::TypeId { + std::any::TypeId::of::() + } +} + fn process_keyboard_string(key_name: &str) -> Option { let key = keycode_from_str(key_name)?; -- 2.40.1 From de4fb76f3140b973c394b611adfb538ba87f1b86 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Wed, 28 Feb 2024 21:10:52 -0500 Subject: [PATCH 40/41] scripting: update for elua and new wip support for teal --- Cargo.lock | 846 +++++++++++++++++- examples/testbed/Cargo.toml | 1 + lyra-scripting/Cargo.toml | 1 + lyra-scripting/elua | 2 +- .../lyra-scripting-derive/src/lib.rs | 4 +- lyra-scripting/src/lua/mod.rs | 4 +- lyra-scripting/src/lua/proxy.rs | 4 +- lyra-scripting/src/lua/world.rs | 14 +- .../src/lua/wrappers/input_actions.rs | 3 +- lyra-scripting/src/lua/wrappers/model_comp.rs | 4 +- lyra-scripting/src/lua/wrappers/res_handle.rs | 4 +- 11 files changed, 834 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f54a87..53d3f80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,6 +33,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.8.7" @@ -190,7 +201,7 @@ dependencies = [ "polling 2.8.0", "rustix 0.37.27", "slab", - "socket2", + "socket2 0.4.10", "waker-fn", ] @@ -310,7 +321,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -352,6 +363,12 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bit-set" version = "0.5.3" @@ -391,6 +408,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block-sys" version = "0.1.0-beta.1" @@ -449,7 +475,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -458,6 +484,33 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "calloop" version = "0.10.6" @@ -494,6 +547,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -525,6 +588,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "core-foundation" version = "0.9.4" @@ -565,6 +634,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -643,6 +721,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "d3d12" version = "0.6.0" @@ -663,6 +751,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -694,10 +793,33 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" name = "elua" version = "0.1.0" dependencies = [ + "elua-derive", "mlua-sys", "thiserror", ] +[[package]] +name = "elua-derive" +version = "0.1.0" +dependencies = [ + "bytes", + "proc-macro2", + "quote", + "reqwest", + "syn 2.0.51", + "tempdir", + "zip", +] + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -832,6 +954,12 @@ dependencies = [ "spin", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foreign-types" version = "0.3.2" @@ -847,6 +975,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fps_counter" version = "2.0.0" @@ -862,6 +999,12 @@ dependencies = [ "libc", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "futures-channel" version = "0.3.30" @@ -911,6 +1054,33 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "fxhash" version = "0.2.1" @@ -920,6 +1090,16 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.11" @@ -1025,7 +1205,7 @@ dependencies = [ "inflections", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1092,6 +1272,25 @@ dependencies = [ "bitflags 2.4.1", ] +[[package]] +name = "h2" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.2.1" @@ -1144,6 +1343,96 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.6", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" version = "0.24.7" @@ -1226,6 +1515,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -1259,6 +1557,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itertools" version = "0.11.0" @@ -1454,7 +1758,7 @@ dependencies = [ "lyra-ecs-derive", "lyra-math", "paste", - "rand", + "rand 0.8.5", "thiserror", ] @@ -1464,7 +1768,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1494,7 +1798,7 @@ dependencies = [ "lyra-reflect", "lyra-resource", "quote", - "syn 2.0.48", + "syn 2.0.51", "thiserror", "tracing", "tracing-appender", @@ -1527,7 +1831,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1574,7 +1878,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1692,6 +1996,24 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.7.0" @@ -1830,6 +2152,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.5.11" @@ -1869,7 +2201,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1932,6 +2264,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.51", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "orbclient" version = "0.3.47" @@ -1985,12 +2361,35 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -2093,9 +2492,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.75" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -2124,6 +2523,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + [[package]] name = "rand" version = "0.8.5" @@ -2132,7 +2544,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -2142,9 +2554,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.6.4" @@ -2186,6 +2613,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "redox_syscall" version = "0.3.5" @@ -2204,12 +2640,61 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "renderdoc-sys" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" +[[package]] +name = "reqwest" +version = "0.11.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" +dependencies = [ + "base64 0.21.5", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -2249,6 +2734,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.5", +] + [[package]] name = "ryu" version = "1.0.16" @@ -2264,6 +2758,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -2289,6 +2792,29 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.194" @@ -2306,7 +2832,7 @@ checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2320,6 +2846,40 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -2397,6 +2957,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -2428,6 +2998,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -2441,15 +3017,65 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand 0.4.6", + "remove_dir_all", +] + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand 2.0.1", + "redox_syscall 0.4.1", + "rustix 0.38.28", + "windows-sys 0.52.0", +] + [[package]] name = "termcolor" version = "1.4.0" @@ -2467,6 +3093,7 @@ dependencies = [ "async-std", "fps_counter", "lyra-engine", + "lyra-scripting", "tracing", ] @@ -2487,7 +3114,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2565,6 +3192,61 @@ dependencies = [ "strict-num", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2 0.5.6", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml_datetime" version = "0.6.5" @@ -2582,6 +3264,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" @@ -2613,7 +3301,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2662,18 +3350,45 @@ dependencies = [ "tracing-log 0.2.0", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "ttf-parser" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.11" @@ -2686,6 +3401,17 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "urlencoding" version = "2.1.3" @@ -2699,7 +3425,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom", - "rand", + "rand 0.8.5", ] [[package]] @@ -2714,6 +3440,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62ce5bb364b23e66b528d03168df78b38c0f7b6fe17386928f29d5ab2e7cb2f7" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" @@ -2742,6 +3474,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2769,7 +3510,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", "wasm-bindgen-shared", ] @@ -2803,7 +3544,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3310,6 +4051,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -3350,7 +4101,56 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2", + "sha1", + "time", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.9+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +dependencies = [ + "cc", + "pkg-config", ] [[package]] diff --git a/examples/testbed/Cargo.toml b/examples/testbed/Cargo.toml index 8498541..877d0b4 100644 --- a/examples/testbed/Cargo.toml +++ b/examples/testbed/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] lyra-engine = { path = "../../", version = "0.0.1", features = ["lua_scripting"] } +lyra-scripting = { path = "../../lyra-scripting", features = ["lua", "teal"] } #lyra-ecs = { path = "../../lyra-ecs"} anyhow = "1.0.75" async-std = "1.12.0" diff --git a/lyra-scripting/Cargo.toml b/lyra-scripting/Cargo.toml index 2782c56..22d2bd0 100644 --- a/lyra-scripting/Cargo.toml +++ b/lyra-scripting/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [features] default = ["lua"] lua = ["dep:elua"] +teal = ["lua", "elua/teal"] [dependencies] lyra-scripting-derive = { path = "lyra-scripting-derive" } diff --git a/lyra-scripting/elua b/lyra-scripting/elua index 936ea60..feb93f2 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit 936ea606343dbcf5321b4b988de65daf8769ff3f +Subproject commit feb93f2b4ed7063e4fba366377b039ba4f9026ef diff --git a/lyra-scripting/lyra-scripting-derive/src/lib.rs b/lyra-scripting/lyra-scripting-derive/src/lib.rs index 5f90c92..cb7d832 100644 --- a/lyra-scripting/lyra-scripting-derive/src/lib.rs +++ b/lyra-scripting/lyra-scripting-derive/src/lib.rs @@ -670,7 +670,7 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt stringify!(#type_name).to_string() } - fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { #(#field_get_set_pairs)* #matrix_wrapper_fields @@ -694,8 +694,6 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt #vec_wrapper_methods #custom_methods - - Ok(()) } } diff --git a/lyra-scripting/src/lua/mod.rs b/lyra-scripting/src/lua/mod.rs index 09c22d4..ce37ef5 100644 --- a/lyra-scripting/src/lua/mod.rs +++ b/lyra-scripting/src/lua/mod.rs @@ -174,9 +174,7 @@ impl elua::Userdata for ScriptBorrow { "ScriptBorrow".to_string() } - fn build<'a>(_: &elua::State, _: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { - Ok(()) - } + fn build<'a>(_: &mut elua::UserdataBuilder<'a, Self>) { } } /// Helper function used for reflecting userdata as a ScriptBorrow diff --git a/lyra-scripting/src/lua/proxy.rs b/lyra-scripting/src/lua/proxy.rs index 1362f4e..f43faf2 100644 --- a/lyra-scripting/src/lua/proxy.rs +++ b/lyra-scripting/src/lua/proxy.rs @@ -163,7 +163,7 @@ impl elua::Userdata for ScriptDynamicBundle { "Bundle".to_string() } - fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { builder .function("new", |_, ()| Ok(ScriptDynamicBundle(DynamicBundle::new()))) .method_mut("push", |_, this, comp: elua::AnyUserdata| { @@ -175,7 +175,5 @@ impl elua::Userdata for ScriptDynamicBundle { Ok(()) }); - - Ok(()) } } \ No newline at end of file diff --git a/lyra-scripting/src/lua/world.rs b/lyra-scripting/src/lua/world.rs index 1cec468..848bd86 100644 --- a/lyra-scripting/src/lua/world.rs +++ b/lyra-scripting/src/lua/world.rs @@ -25,15 +25,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>(builder: &mut elua::userdata::UserdataBuilder<'a, Self>) { builder.meta_method(elua::MetaMethod::ToString, |_, this, ()| { Ok(format!("{:?}", this.0)) }); - - Ok(()) } } @@ -58,10 +53,7 @@ impl elua::Userdata for ScriptWorldPtr { "World".to_string() } - fn build<'a>( - _: &elua::State, - builder: &mut elua::UserdataBuilder<'a, Self>, - ) -> elua::Result<()> { + fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { builder .method_mut("spawn", |_, this, vals: elua::ValueVec| { let world = this.as_mut(); @@ -312,7 +304,5 @@ impl elua::Userdata for ScriptWorldPtr { Ok(LuaResHandle::from(handle)) }); - - Ok(()) } } diff --git a/lyra-scripting/src/lua/wrappers/input_actions.rs b/lyra-scripting/src/lua/wrappers/input_actions.rs index ee393ad..410b83c 100644 --- a/lyra-scripting/src/lua/wrappers/input_actions.rs +++ b/lyra-scripting/src/lua/wrappers/input_actions.rs @@ -15,7 +15,7 @@ impl elua::Userdata for LuaActionHandler { "ActionHandler".to_string() } - fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { builder.function("new", |_, table: elua::Table| { let mut handler = ActionHandler::new(); @@ -146,7 +146,6 @@ impl elua::Userdata for LuaActionHandler { Ok(ScriptBorrow::from_resource::(None)) }); - Ok(()) } } diff --git a/lyra-scripting/src/lua/wrappers/model_comp.rs b/lyra-scripting/src/lua/wrappers/model_comp.rs index 442b049..5f32198 100644 --- a/lyra-scripting/src/lua/wrappers/model_comp.rs +++ b/lyra-scripting/src/lua/wrappers/model_comp.rs @@ -20,7 +20,7 @@ impl elua::Userdata for LuaModelComponent { "ModelComponent".to_string() } - fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) { builder .function("new", |_, model: Ref| { let res = model.0.clone(); @@ -48,8 +48,6 @@ impl elua::Userdata for LuaModelComponent { .method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| { Ok(ScriptBorrow::from_component(Some(this.0.clone()))) }); - - Ok(()) } } diff --git a/lyra-scripting/src/lua/wrappers/res_handle.rs b/lyra-scripting/src/lua/wrappers/res_handle.rs index 884cddf..c17df96 100644 --- a/lyra-scripting/src/lua/wrappers/res_handle.rs +++ b/lyra-scripting/src/lua/wrappers/res_handle.rs @@ -30,7 +30,7 @@ impl elua::Userdata for LuaResHandle { "Handle".to_string() } - fn build<'a>(_: &elua::State, builder: &mut elua::UserdataBuilder<'a, Self>) -> elua::Result<()> { + 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())); @@ -62,8 +62,6 @@ impl elua::Userdata for LuaResHandle { } } }); - - Ok(()) } } -- 2.40.1 From 31398d5446325a400c309f06db362f4d7e5a11d3 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Thu, 29 Feb 2024 19:31:30 -0500 Subject: [PATCH 41/41] scripting: update elua submodule --- lyra-scripting/elua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lyra-scripting/elua b/lyra-scripting/elua index feb93f2..54c9926 160000 --- a/lyra-scripting/elua +++ b/lyra-scripting/elua @@ -1 +1 @@ -Subproject commit feb93f2b4ed7063e4fba366377b039ba4f9026ef +Subproject commit 54c9926a04cdef657289fd67730c0b85d1bdda3e -- 2.40.1