From 13ad671a5596b067e3039a9654202378262a80d9 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 6 Jan 2024 23:06:00 -0500 Subject: [PATCH] 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