Create an early scripting engine #2
|
@ -14,6 +14,7 @@ members = [
|
|||
|
||||
[features]
|
||||
scripting = ["dep:lyra-scripting"]
|
||||
lua_scripting = ["scripting", "lyra-scripting/lua"]
|
||||
|
||||
[dependencies]
|
||||
lyra-game = { path = "lyra-game" }
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
print("Hello World")
|
||||
|
||||
function update()
|
||||
print("updated")
|
||||
--printf("I love to eat formatted {}!", "food")
|
||||
--printf("World is {}", world)
|
||||
end
|
|
@ -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;
|
||||
|
@ -298,10 +298,25 @@ async fn main() {
|
|||
//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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
|
@ -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];
|
||||
|
|
|
@ -13,7 +13,7 @@ pub struct LuaLoader;
|
|||
|
||||
impl ResourceLoader for LuaLoader {
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&[".lua"]
|
||||
&["lua"]
|
||||
}
|
||||
|
||||
fn mime_types(&self) -> &[&str] {
|
||||
|
|
|
@ -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, &[]);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
@ -177,43 +177,3 @@ pub fn lua_print() {
|
|||
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(())
|
||||
}
|
Loading…
Reference in New Issue