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()) {