scripting: update lua scripts on 'update' stage

This commit is contained in:
SeanOMik 2024-01-06 23:06:00 -05:00
parent 8f58096643
commit 13ad671a55
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
10 changed files with 95 additions and 58 deletions

View File

@ -14,6 +14,7 @@ members = [
[features]
scripting = ["dep:lyra-scripting"]
lua_scripting = ["scripting", "lyra-scripting/lua"]
[dependencies]
lyra-game = { path = "lyra-game" }

View File

@ -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"

View File

@ -0,0 +1,7 @@
print("Hello World")
function update()
print("updated")
--printf("I love to eat formatted {}!", "food")
--printf("World is {}", world)
end

View File

@ -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::<ResourceManager>();
let script = res_man.request::<LuaScript>("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)

View File

@ -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<P>(&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

View File

@ -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;

View File

@ -30,7 +30,7 @@ impl From<io::Error> 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];

View File

@ -13,7 +13,7 @@ pub struct LuaLoader;
impl ResourceLoader for LuaLoader {
fn extensions(&self) -> &[&str] {
&[".lua"]
&["lua"]
}
fn mime_types(&self) -> &[&str] {

View File

@ -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<Self> {
@ -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::<LuaHost>();
let mut contexts = world.get_resource_mut::<ScriptContexts<Mutex<mlua::Lua>>>();
let mut providers = world.get_resource_mut::<ScriptApiProviders<LuaHost>>();
for scripts in world.view_iter::<&ScriptList<LuaScript>>() {
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::<ScriptApiProviders<LuaHost>>();
world.add_resource_default::<ScriptContexts<Mutex<mlua::Lua>>>();
let mut loader = world.get_resource_or_else(ResourceManager::default);
let mut loader = world.try_get_resource_mut::<ResourceManager>()
.expect("Add 'ResourceManager' to the world before trying to add this plugin");
loader.register_loader::<LuaLoader>();
drop(loader);
game.with_system("lua_update", lua_update_scripts, &[]);
}
}

View File

@ -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::<LuaHost>();
let mut contexts = world.get_resource_mut::<ScriptContexts<Mutex<mlua::Lua>>>();
let mut providers = world.get_resource_mut::<ScriptApiProviders<LuaHost>>();
for scripts in world.view_iter::<&ScriptList<LuaScript>>() {
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(())
}