Compare commits

..

2 Commits

7 changed files with 94 additions and 29 deletions

View File

@ -1,10 +1,12 @@
--local cube: resource = world:request_res("loader", "assets/cube-texture-embedded.gltf")
--cube = nil
function on_init() function on_init()
local cube = world:request_res("assets/cube-texture-embedded.gltf") local cube = world:request_res("assets/cube-texture-embedded.gltf")
print("Loaded textured cube") 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 end
--[[ function on_first() --[[ function on_first()

View File

@ -80,12 +80,13 @@ macro_rules! impl_fn_system_tuple {
fn execute_deferred(&mut self, world: NonNull<World>) -> anyhow::Result<()> { fn execute_deferred(&mut self, world: NonNull<World>) -> anyhow::Result<()> {
let state = self.arg_state.as_mut().expect("Somehow there was no state"); let state = self.arg_state.as_mut().expect("Somehow there was no state");
state.reverse();
$( $(
let arg_state_box = state.pop() let arg_state_box = state.pop()
.expect("Missing expected arg state"); .expect("Missing expected arg state");
let arg_state = *arg_state_box.downcast::<$name::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); $name::apply_deferred(arg_state, world);
)+ )+

View File

@ -2,7 +2,7 @@ use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull};
use super::System; use super::System;
use crate::world::World; use crate::{world::World, CommandQueue, Commands};
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum GraphExecutorError { pub enum GraphExecutorError {
@ -58,7 +58,7 @@ impl GraphExecutor {
} }
/// Executes the systems in the graph /// Executes the systems in the graph
pub fn execute(&mut self, world_ptr: NonNull<World>, stop_on_error: bool) -> Result<Vec<GraphExecutorError>, GraphExecutorError> { pub fn execute(&mut self, mut world_ptr: NonNull<World>, stop_on_error: bool) -> Result<Vec<GraphExecutorError>, GraphExecutorError> {
let mut stack = VecDeque::new(); let mut stack = VecDeque::new();
let mut visited = HashSet::new(); let mut visited = HashSet::new();
@ -91,6 +91,25 @@ impl GraphExecutor {
possible_errors.push(e); possible_errors.push(e);
unimplemented!("Cannot resume topological execution from error"); // TODO: resume topological execution from error 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::<CommandQueue>() {
// 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) Ok(possible_errors)

View File

@ -1,4 +1,4 @@
use lyra_ecs::Commands; use lyra_ecs::CommandQueue;
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
use crate::EventsPlugin; use crate::EventsPlugin;
@ -109,6 +109,7 @@ pub struct DefaultPlugins;
impl Plugin for DefaultPlugins { impl Plugin for DefaultPlugins {
fn setup(&self, game: &mut Game) { fn setup(&self, game: &mut Game) {
CommandQueuePlugin.setup(game);
EventsPlugin.setup(game); EventsPlugin.setup(game);
InputPlugin.setup(game); InputPlugin.setup(game);
ResourceManagerPlugin.setup(game); ResourceManagerPlugin.setup(game);
@ -116,3 +117,14 @@ impl Plugin for DefaultPlugins {
DeltaTimePlugin.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());
}
}

View File

@ -47,9 +47,6 @@ impl ScriptHost for LuaHost {
provider.setup_script(script_data, ctx)?; provider.setup_script(script_data, ctx)?;
} }
let ctx = ctx.lock().expect("Failure to get Lua ScriptContext");
try_call_lua_function(&ctx, "on_init")?;
Ok(()) Ok(())
} }

View File

@ -1,7 +1,5 @@
use std::ptr::NonNull;
use anyhow::anyhow; 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_game::{game::GameStages, plugin::Plugin};
use lyra_reflect::TypeRegistry; use lyra_reflect::TypeRegistry;
use lyra_resource::ResourceManager; 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}; 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 /// 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, world: &mut World,
mut host: ResMut<LuaHost>, mut host: ResMut<LuaHost>,
mut contexts: ResMut<ScriptContexts<LuaContext>>, mut contexts: ResMut<ScriptContexts<LuaContext>>,
mut providers: ResMut<ScriptApiProviders<LuaHost>>, mut providers: ResMut<ScriptApiProviders<LuaHost>>,
view: View<(Entities, &ScriptList<LuaScript>)>, view: View<(Entities, &ScriptList<LuaScript>)>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
world.add_resource_default::<CommandQueue>();
for (en, scripts) in view.into_iter() { for (en, scripts) in view.into_iter() {
for script in scripts.iter() { for script in scripts.iter() {
if !contexts.has_context(script.id()) { 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)?; host.setup_script(&script_data, &mut script_ctx, &mut providers)?;
trace!("Finished setting up script"); 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); contexts.add_context(script.id(), script_ctx);
break; break;
} else { } 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::<CommandQueue>();
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(()) Ok(())
} }

View File

@ -1,8 +1,8 @@
use std::{ptr::NonNull, sync::Arc}; use std::{ptr::NonNull, sync::Arc};
use crate::{ScriptBorrow, ScriptDynamicBundle, ScriptEntity, ScriptWorldPtr}; use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr};
use elua::AsLua; 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_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
use lyra_resource::ResourceManager; use lyra_resource::ResourceManager;
@ -64,10 +64,38 @@ impl elua::Userdata for ScriptWorldPtr {
builder: &mut elua::UserdataBuilder<'a, Self>, builder: &mut elua::UserdataBuilder<'a, Self>,
) -> elua::Result<()> { ) -> elua::Result<()> {
builder builder
.method_mut("spawn", |_, this, bundle: ScriptDynamicBundle| { .method_mut("spawn", |_, this, vals: elua::ValueVec| {
let world = this.as_mut(); 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::<CommandQueue>();
let mut commands = Commands::new(&mut commands_queue, unsafe { &mut *world_ptr });
let entity = commands.spawn(bundle);
Ok(ScriptEntity(entity))
}) })
.method_mut( .method_mut(
"view", "view",