From b63fc4d1b181530909690f4aef2d953e707d889d Mon Sep 17 00:00:00 2001 From: Tekhnae Raav Date: Fri, 8 May 2026 09:32:30 -0500 Subject: [PATCH 01/36] feat: move TileLayer grid methods into separate type & add resizing methods --- .../bones_framework/src/render/tilemap.rs | 175 +++++++++++++++--- 1 file changed, 145 insertions(+), 30 deletions(-) diff --git a/framework_crates/bones_framework/src/render/tilemap.rs b/framework_crates/bones_framework/src/render/tilemap.rs index 2da156303b..a3a28df04d 100644 --- a/framework_crates/bones_framework/src/render/tilemap.rs +++ b/framework_crates/bones_framework/src/render/tilemap.rs @@ -2,18 +2,36 @@ use crate::prelude::*; -/// A tilemap layer component. +/// Used for mapping entities to a sized grid. This is used in the [`TileLayer`] +/// component for mapping entities with [`Tile`] components to it. #[derive(Clone, Debug, HasSchema, Default)] -pub struct TileLayer { +pub struct EntityGridMap { /// The vector of tile slots in this layer. pub tiles: Vec>, - /// The size of the layer in tiles. + /// The size of the map in tiles (or columns and rows). pub grid_size: UVec2, /// The size of each tile in the layer. pub tile_size: Vec2, +} + +/// A tilemap layer component. +#[derive(Clone, Debug, HasSchema, Default, Deref, DerefMut)] +pub struct TileLayer { + #[deref] + /// The grid map for this [`TileLayer`]'s [`Tile`]s. + pub map: EntityGridMap, /// The texture atlas to use for the layer pub atlas: Handle, } +impl TileLayer { + /// Create a new [`TileLayer`]. + pub fn new(grid_size: UVec2, tile_size: Vec2, atlas: Handle) -> Self { + Self { + map: EntityGridMap::new(grid_size, tile_size), + atlas, + } + } +} /// A tilemap tile component. #[derive(Clone, Debug, HasSchema, Default)] @@ -27,60 +45,157 @@ pub struct Tile { pub flip_y: bool, } -impl TileLayer { +impl EntityGridMap { /// Create a new tile layer - pub fn new(grid_size: UVec2, tile_size: Vec2, atlas: Handle) -> Self { + pub fn new(grid_size: UVec2, tile_size: Vec2) -> Self { let mut out = Self { tiles: Vec::new(), grid_size, tile_size, - atlas, }; out.ensure_space(); out } - /// Makes sure the tiles vector has space for all of our tiles. - fn ensure_space(&mut self) { - let tile_count = (self.grid_size.x * self.grid_size.y) as usize; + /// Get the overall world size + pub fn size(&self) -> Vec2 { + self.grid_size.as_vec2() * self.tile_size + } - if unlikely(self.tiles.len() < tile_count) { - self.tiles - .extend((0..(tile_count - self.tiles.len())).map(|_| None)); - } + /// Get's the entity at the given position in the grid if set, indexed with + /// the bottom-left of the grid being (0, 0). + /// + /// Returns `None` is no `Entity` assigned to that position. + /// + /// Panics if the position is out of the grid bounds. + pub fn get(&self, pos: UVec2) -> Option { + let idx = self.index(pos); + self.tiles.get(idx as usize).cloned().flatten() } - /// Get the index of the tile at the given position. - #[inline] - pub fn idx(&self, pos: UVec2) -> u32 { - self.grid_size.x * pos.y + pos.x + /// Get's the entity at the given position in the grid if set, indexed with + /// the bottom-left of the grid being (0, 0). + /// + /// Returns `None` if the position is out of the grid bounds or there is no + /// `Entity` assigned to that position. + pub fn try_get(&self, pos: UVec2) -> Option { + self.try_index(pos).and_then(|idx| self.tiles[idx as usize]) } - /// Get the position of the tile at the given index. + /// Get the position on the grid with the given entity index. + /// + /// Panics if the index is out of the grid bounds. pub fn pos(&self, idx: u32) -> UVec2 { + if idx as usize >= self.tiles.len() { + panic!("index out of grid bounds") + } let y = idx / self.grid_size.x; let x = idx - (y * self.grid_size.x); UVec2::new(x, y) } + /// Try to get the position on the grid with the given entity index. + /// + /// Returns `None` if the index is out of the grid bounds. + pub fn try_pos(&self, idx: u32) -> Option { + if idx as usize >= self.tiles.len() { + None + } else { + let y = idx / self.grid_size.x; + let x = idx - (y * self.grid_size.x); - /// Get's the tile at the given position in the layer, indexed with the bottom-left of the layer - /// being (0, 0). - pub fn get(&self, pos: UVec2) -> Option { - let idx = self.idx(pos); - self.tiles.get(idx as usize).cloned().flatten() + UVec2::new(x, y).into() + } + } + + /// Get the index of the tile at the given position. + #[inline] + #[track_caller] + pub fn index(&self, pos: UVec2) -> u32 { + if pos.x + 1 > self.grid_size.x || pos.y + 1 > self.grid_size.y { + panic!("grid postion out of bounds") + } + self.grid_size.x * pos.y + pos.x + } + /// Try to get the index of the tile at the given position. + #[inline] + pub fn try_index(&self, pos: UVec2) -> Option { + if pos.x + 1 > self.grid_size.x || pos.y + 1 > self.grid_size.y { + None + } else { + Some(self.grid_size.x * pos.y + pos.x) + } } /// Set the tile at the given position, to a certain entity. pub fn set(&mut self, pos: UVec2, entity: Option) { self.ensure_space(); - let idx = self.idx(pos); - *self.tiles.get_mut(idx as usize).unwrap_or_else(|| { - panic!( - "Tile pos out of range of tile size: pos {:?} size {:?}", - pos, self.grid_size - ) - }) = entity; + let idx = self.index(pos); + self.tiles[idx as usize] = entity; + } + + /// Makes sure the tiles vector has space for all of our tiles. + fn ensure_space(&mut self) { + let tile_count = (self.grid_size.x * self.grid_size.y) as usize; + + if unlikely(self.tiles.len() < tile_count) { + self.tiles + .extend((0..(tile_count - self.tiles.len())).map(|_| None)); + } + } + + /// Set a tile position, resizing the map to accommodate the new tile if the map grid is not + /// already large enough. + pub fn set_with_resize(&mut self, pos: UVec2, entity: Option) { + // Calculate whatever size is larger, the grid size, or the size needed to store the tile + // position. + let required_grid_size = self.grid_size.max(pos + UVec2::ONE); + + // If the larger size is not the grid size we have currently + if required_grid_size != self.grid_size { + // Resize the grid to fit the position + self.resize_grid(required_grid_size); + } + + // Set the tile position + self.set(pos, entity) + } + + /// Copy all the tiles from another tileset into this one. + /// + /// This can be useful for resizing a map, for example. + /// + /// The tiles from the other layer are copied to the corresponding positions in this layer, if + /// they fit into this layer's size. + pub fn copy_from(&mut self, other: &EntityGridMap) { + // Get the smallest size so that we know we won't go out of bounds while iterating over the + // tiles. + let visit_size = other.grid_size.min(self.grid_size); + + // Iterate over every position + for x in 0..visit_size.x { + for y in 0..visit_size.y { + let pos = uvec2(x, y); + // Set the position in this layer from the position in the other layer + self.set(pos, other.get(pos)) + } + } + } + + /// Resize the layer, setting a new grid size. + /// + /// If shrinking the layer, tiles that are out-of-bounds will be removed. + pub fn resize_grid(&mut self, grid_size: UVec2) { + // Create a new layer with the desired grid size + let mut new_layer = EntityGridMap::new(grid_size, self.tile_size); + + // Copy the tiles from this layer into the new one + new_layer.copy_from(self); + + // Swap out the new layer and the current one + std::mem::swap(self, &mut new_layer); } } + +// TODO: add tests for EntityGridMap From 336cea4f2ad13354d620249e2f93e264fd9c5674 Mon Sep 17 00:00:00 2001 From: Tekhnae Raav Date: Sun, 10 May 2026 15:42:16 -0500 Subject: [PATCH 02/36] refactor: move startup systems to `SystemStage` out of `SystemStages`. Maintains the rollback & reset compatibility. Deprecates the `add_startup_system` method. --- demos/features/src/main.rs | 10 +- demos/scripting/assets/plugin1.plugin.lua | 2 +- demos/scripting/src/main.rs | 5 +- .../bones_ecs/examples/pos_vel.rs | 2 +- framework_crates/bones_ecs/src/stage.rs | 176 ++++++++---------- .../bones_framework/tests/reset.rs | 2 +- framework_crates/bones_lib/src/lib.rs | 9 +- framework_crates/bones_lib/src/reset.rs | 3 - framework_crates/bones_scripting/src/lua.rs | 16 +- .../bones_scripting/src/lua/asset.rs | 20 -- .../bones_scripting/src/lua/bindings.rs | 1 + .../src/lua/bindings/schema.rs | 7 + 12 files changed, 106 insertions(+), 147 deletions(-) diff --git a/demos/features/src/main.rs b/demos/features/src/main.rs index 51e4380b9d..85a0968b24 100644 --- a/demos/features/src/main.rs +++ b/demos/features/src/main.rs @@ -150,7 +150,7 @@ pub fn menu_plugin(session: &mut SessionBuilder) { // And add our systems. session .add_system_to_stage(Update, menu_system) - .add_startup_system(menu_startup); + .add_system_to_stage(CoreStage::Startup, menu_startup); } /// Setup the main menu. @@ -283,7 +283,7 @@ fn menu_system( fn sprite_demo_plugin(session: &mut SessionBuilder) { session .install_plugin(DefaultSessionPlugin) - .add_startup_system(sprite_demo_startup) + .add_system_to_stage(CoreStage::Startup, sprite_demo_startup) .add_system_to_stage(Update, back_to_menu_ui) .add_system_to_stage(Update, move_sprite); } @@ -347,7 +347,7 @@ fn move_sprite( fn tilemap_demo_plugin(session: &mut SessionBuilder) { session .install_plugin(DefaultSessionPlugin) - .add_startup_system(tilemap_startup_system) + .add_system_to_stage(CoreStage::Startup, tilemap_startup_system) .add_system_to_stage(Update, back_to_menu_ui); } @@ -393,7 +393,7 @@ fn tilemap_startup_system( fn atlas_demo_plugin(session: &mut SessionBuilder) { session .install_plugin(DefaultSessionPlugin) - .add_startup_system(atlas_demo_startup) + .add_system_to_stage(CoreStage::Startup, atlas_demo_startup) .add_system_to_stage(Update, back_to_menu_ui); } @@ -497,7 +497,7 @@ fn storage_demo_ui( fn path2d_demo_plugin(session: &mut SessionBuilder) { session .install_plugin(DefaultSessionPlugin) - .add_startup_system(path2d_demo_startup) + .add_system_to_stage(CoreStage::Startup, path2d_demo_startup) .add_system_to_stage(Update, back_to_menu_ui); } diff --git a/demos/scripting/assets/plugin1.plugin.lua b/demos/scripting/assets/plugin1.plugin.lua index 91f34e8229..ff0273b1ea 100644 --- a/demos/scripting/assets/plugin1.plugin.lua +++ b/demos/scripting/assets/plugin1.plugin.lua @@ -30,5 +30,5 @@ local function update() end end -session:add_startup_system(startup) +session:add_system_to_stage(CoreStage.Startup, startup) session:add_system_to_stage(CoreStage.Update, update) diff --git a/demos/scripting/src/main.rs b/demos/scripting/src/main.rs index 8ada4088c8..cfcad810f1 100644 --- a/demos/scripting/src/main.rs +++ b/demos/scripting/src/main.rs @@ -19,11 +19,12 @@ fn main() { game.install_plugin(DefaultGamePlugin); game.shared_resource_mut::() .register_default_assets(); + Transform::register_schema(); GameMeta::register_schema(); game.sessions .create_with("launch", |builder: &mut SessionBuilder| { - builder.add_startup_system(launch_game_session); + builder.add_system_to_stage(CoreStage::Startup, launch_game_session); }); let mut renderer = BonesBevyRenderer::new(game); @@ -50,7 +51,7 @@ fn launch_game_session( // Tell it to install the lua plugins specified in our game meta Arc::new(meta.plugins.iter().copied().collect()), )) - .add_startup_system(game_startup); + .add_system_to_stage(CoreStage::Startup, game_startup); }); } diff --git a/framework_crates/bones_ecs/examples/pos_vel.rs b/framework_crates/bones_ecs/examples/pos_vel.rs index 6d4f52ea2d..31dbbcddf1 100644 --- a/framework_crates/bones_ecs/examples/pos_vel.rs +++ b/framework_crates/bones_ecs/examples/pos_vel.rs @@ -27,7 +27,7 @@ fn main() { // Add our systems to the system stages stages_builder - .add_startup_system(startup_system) + .add_system_to_stage(CoreStage::Startup, startup_system) .add_system_to_stage(CoreStage::Update, pos_vel_system) .add_system_to_stage(CoreStage::PostUpdate, print_system); diff --git a/framework_crates/bones_ecs/src/stage.rs b/framework_crates/bones_ecs/src/stage.rs index f5a4db2c86..2e13cb92c1 100644 --- a/framework_crates/bones_ecs/src/stage.rs +++ b/framework_crates/bones_ecs/src/stage.rs @@ -15,9 +15,6 @@ pub struct CurrentSystemStage(pub Ulid); pub struct SystemStagesBuilder { /// The stages in the collection, in the order that they will be run. stages: Vec>, - /// The systems that should run at startup. - /// They will be executed next step based on if [`SessionStarted`] resource in world says session has not started, or if resource does not exist. - startup_systems: Vec>, /// Resources installed during session plugin installs. Copied to world as first step on startup of stages' execution. startup_resources: UntypedResourceSet, @@ -37,6 +34,7 @@ impl SystemStagesBuilder { pub fn with_core_stages() -> Self { Self { stages: vec![ + Box::new(StartupSystemStage::new(CoreStage::Startup)), Box::new(SimpleSystemStage::new(CoreStage::First)), Box::new(SimpleSystemStage::new(CoreStage::PreUpdate)), Box::new(SimpleSystemStage::new(CoreStage::Update)), @@ -44,7 +42,6 @@ impl SystemStagesBuilder { Box::new(SimpleSystemStage::new(CoreStage::Last)), ], startup_resources: default(), - startup_systems: default(), single_success_systems: Vec::new(), } } @@ -53,7 +50,6 @@ impl SystemStagesBuilder { pub fn finish(self) -> SystemStages { SystemStages { stages: self.stages, - startup_systems: self.startup_systems, startup_resources: self.startup_resources, single_success_systems: self.single_success_systems, } @@ -63,19 +59,20 @@ impl SystemStagesBuilder { pub fn from_stages(stages: SystemStages) -> Self { Self { stages: stages.stages, - startup_systems: stages.startup_systems, startup_resources: stages.startup_resources, single_success_systems: stages.single_success_systems, } } - /// Add a system that will run only once, before all of the other non-startup systems. - /// If wish to reset session and run again, can modify [`SessionStarted`] resource in world. + /// Add a system to the [`CoreStage::Startup`] stage which specially runs + /// only once, before all other core stages. Inserting [`StartupSystemsRan`] + /// into the world prevents systems in the startup stage from running. + #[deprecated = "use `.add_system_to_stage(CoreStage::Startup, your_system)` instead"] pub fn add_startup_system(&mut self, system: S) -> &mut Self where S: IntoSystem>, { - self.startup_systems.push(system.system()); + self.add_system_to_stage(CoreStage::Startup, system.system()); self } @@ -171,10 +168,6 @@ pub struct SystemStages { /// The stages in the collection, in the order that they will be run. stages: Vec>, - /// The systems that should run at startup. - /// They will be executed next step based on if [`SessionStarted`] resource in world says session has not started, or if resource does not exist. - startup_systems: Vec>, - /// Resources installed during session plugin installs. Copied to world as first step on startup of stages' execution. startup_resources: UntypedResourceSet, @@ -206,8 +199,8 @@ impl SystemStages { /// Execute the systems on the given `world`. pub fn run(&mut self, world: &mut World) { - // If we haven't run startup systems and setup resources yet, do so - self.handle_startup(world); + // If we haven't setup resources yet, do so + self.handle_startup_resources(world); // Run single success systems for (index, system) in self.single_success_systems.iter_mut().enumerate() { @@ -234,87 +227,18 @@ impl SystemStages { world.resources.remove::(); } - /// If [`SessionStarted`] resource indicates have not yet started, - /// perform startup tasks (insert startup resources, run startup systems). - /// - /// For advanced use cases in which want to only insert startup resources, or run startup systems and split this - /// behavior, see [`SystemStages::handle_startup_systems`] and `[SystemStages::handle_startup_resources`]. - /// - /// - /// While this is used internally by [`SystemStages::run`], this is also used - /// for resetting world. This allows world to immediately startup and re-initialize after reset. - /// - /// # Panics - /// - /// May panic if resources are borrowed, should not borrow resources when calling. - pub fn handle_startup(&mut self, world: &mut World) { - self.handle_startup_resources(world); - self.handle_startup_systems(world); - } - - /// If [`SessionStarted`] resource indicates startup resources have not yet been inserted, will do so and update `SessionStarted`. - /// - /// This function contains only half of stage's startup behavior, see [`SystemStages::handle_startup`] if not intending to split - /// resource insertion from startup systems (Splitting these is more for advanced special cases). + /// If [`StartupResourcesInserted`] resource has not yet been inserted, will do so. /// /// # Panics /// /// May panic if resources are borrowed, should not borrow resources when calling. pub fn handle_startup_resources(&mut self, world: &mut World) { - let (mut resources_inserted, systems_run) = match world.get_resource_mut::() - { - Some(session_started) => ( - session_started.startup_resources_inserted, - session_started.startup_systems_executed, - ), - None => (false, false), - }; + let inserted = world.get_resource::().is_some(); - if !resources_inserted { + if !inserted { self.insert_startup_resources(world); - resources_inserted = true; - world.insert_resource(SessionStarted { - startup_systems_executed: systems_run, - startup_resources_inserted: resources_inserted, - }); - } - } - - /// If [`SessionStarted`] resource indicates startup systems have not yet been executed, will do so and update `SessionStarted`. - /// - /// This function contains only half of stage's startup behavior, see [`SystemStages::handle_startup`] if not intending to split - /// startup system execution from startup resource insertion (Splitting these is more for advanced special cases). - /// - /// # Panics - /// - /// May panic if resources are borrowed, should not borrow resources when calling. - pub fn handle_startup_systems(&mut self, world: &mut World) { - let (resources_inserted, mut systems_run) = match world.get_resource_mut::() - { - Some(session_started) => ( - session_started.startup_resources_inserted, - session_started.startup_systems_executed, - ), - None => (false, false), - }; - - if !systems_run { - // Set the current stage resource - world.insert_resource(CurrentSystemStage(Ulid(0))); - - // For each startup system - for system in &mut self.startup_systems { - // Run the system - system.run(world, ()); - } - systems_run = true; - - world.resources.remove::(); - world.insert_resource(SessionStarted { - startup_systems_executed: systems_run, - startup_resources_inserted: resources_inserted, - }); + world.insert_resource(StartupResourcesInserted); } } @@ -431,6 +355,68 @@ impl SystemStage for SimpleSystemStage { } } +/// Resource for tracking whether or not the game has run the startup systems in +/// the [`StartupSystemStage`]. +#[derive(HasSchema, Clone, Default, Debug)] +pub struct StartupSystemsRan; + +/// [`SystemStage`] for running systems once on startup. +pub struct StartupSystemStage { + /// The unique identifier for the stage. + pub id: Ulid, + /// The human-readable name for the stage, used for error messages when something goes wrong. + pub name: String, + /// The list of systems in the stage. + /// + /// Each system will be run in the order that they are in in this list. + pub systems: Vec>, +} +impl StartupSystemStage { + /// Create a new, empty stage, for the given label. + pub fn new(label: L) -> Self { + Self { + id: label.id(), + name: label.name(), + systems: Default::default(), + } + } +} +impl SystemStage for StartupSystemStage { + fn id(&self) -> Ulid { + self.id + } + + fn name(&self) -> String { + self.name.clone() + } + + fn run(&mut self, world: &World) { + if world.get_resource_mut::().is_none() { + // Run the systems + for system in &mut self.systems { + system.run(world, ()); + } + world.insert_resource(StartupSystemsRan); + } + + // Drain the command queue + let queue = world.resources.get_mut::(); + if let Some(mut command_queue) = queue { + for mut system in command_queue.queue.drain(..) { + system.run(world, ()); + } + } + } + + fn add_system(&mut self, system: StaticSystem<(), ()>) { + self.systems.push(system); + } + + fn remove_all_systems(&mut self) { + self.systems.clear(); + } +} + /// Trait for things that may be used to identify a system stage. pub trait StageLabel { /// Returns the human-readable name of the label, used in error messages. @@ -442,6 +428,8 @@ pub trait StageLabel { /// A [`StageLabel`] for the 5 core stages. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum CoreStage { + /// The startup stage + Startup, /// The first stage First, /// The second stage @@ -461,6 +449,7 @@ impl StageLabel for CoreStage { fn id(&self) -> Ulid { match self { + CoreStage::Startup => Ulid(2149923225027836290977818907354065512), CoreStage::First => Ulid(2021715391084198804812356024998495966), CoreStage::PreUpdate => Ulid(2021715401330719559452824437611089988), CoreStage::Update => Ulid(2021715410160177201728645950400543948), @@ -526,17 +515,12 @@ impl<'a> SystemParam for Commands<'a> { } } -/// Resource tracking if Session has started (startup systems executed and resources inserted). +/// Have startup resources beeen inserted into [`World`]? +/// /// If field is set to false, that operation needs to be handled on next stage run. /// If resource is not present, assumed to have not started (and will be initialized upon next stage execution). #[derive(Copy, Clone, HasSchema, Default)] -pub struct SessionStarted { - /// Have startup systems executed? - pub startup_systems_executed: bool, - - /// Have startup resources beeen inserted into [`World`]? - pub startup_resources_inserted: bool, -} +pub struct StartupResourcesInserted; /// Resource tracking which of single success systems in `Session`'s [`SystemStages`] have completed. /// Success is tracked to diff --git a/framework_crates/bones_framework/tests/reset.rs b/framework_crates/bones_framework/tests/reset.rs index 698730c398..7a0031a1f5 100644 --- a/framework_crates/bones_framework/tests/reset.rs +++ b/framework_crates/bones_framework/tests/reset.rs @@ -13,7 +13,7 @@ pub fn startup_system_reset() { // Session startup increments counter by 1 game.sessions .create_with("game", |builder: &mut SessionBuilder| { - builder.add_startup_system(|mut counter: ResMut| { + builder.add_system_to_stage(Startup, |mut counter: ResMut| { // Increment to 1 counter.0 += 1; }); diff --git a/framework_crates/bones_lib/src/lib.rs b/framework_crates/bones_lib/src/lib.rs index 8f28f5e915..407ce6cc0f 100644 --- a/framework_crates/bones_lib/src/lib.rs +++ b/framework_crates/bones_lib/src/lib.rs @@ -136,13 +136,16 @@ impl SessionBuilder { self.stages.startup_resource_mut::() } - /// Add a system that will run only once, before all of the other non-startup systems. - /// If wish to reset startup systems during gameplay and run again, can modify [`SessionStarted`] resource in world. + /// Add a system to the [`CoreStage::Startup`] stage which specially runs + /// only once, before all other core stages. Inserting [`StartupSystemsRan`] + /// into the world prevents systems in the startup stage from running. + #[deprecated = "use `add_system_to_stage(CoreStage::Startup, your_system)` instead"] pub fn add_startup_system(&mut self, system: S) -> &mut Self where S: IntoSystem>, { - self.stages.add_startup_system(system.system()); + self.stages + .add_system_to_stage(CoreStage::Startup, system.system()); self } diff --git a/framework_crates/bones_lib/src/reset.rs b/framework_crates/bones_lib/src/reset.rs index 2a9ddb6a14..e4cf1ed831 100644 --- a/framework_crates/bones_lib/src/reset.rs +++ b/framework_crates/bones_lib/src/reset.rs @@ -110,8 +110,5 @@ impl WorldExt for World { let remove_empty = true; resources.insert_on_world(self, remove_empty); } - - // Now run the startup systems after updating resources. - stages.handle_startup_systems(self); } } diff --git a/framework_crates/bones_scripting/src/lua.rs b/framework_crates/bones_scripting/src/lua.rs index 414eb27423..b306120982 100644 --- a/framework_crates/bones_scripting/src/lua.rs +++ b/framework_crates/bones_scripting/src/lua.rs @@ -51,6 +51,7 @@ impl SessionPlugin for LuaPluginLoaderSessionPlugin { session.insert_resource(LuaPlugins(self.0)); for lua_stage in [ + CoreStage::Startup, CoreStage::First, CoreStage::PreUpdate, CoreStage::Update, @@ -87,21 +88,6 @@ impl SessionPlugin for LuaPluginLoaderSessionPlugin { let mut systems = plugin.systems.borrow_mut(); let systems = systems.as_loaded_mut(); - for (has_run, closure) in &mut systems.startup { - if !*has_run { - let executor = lua.enter(|ctx| { - let closure = ctx.registry().fetch(closure); - let ex = Executor::start(ctx, closure.into(), ()); - ctx.registry().stash(&ctx, ex) - }); - if let Err(e) = lua.execute::<()>(&executor) { - tracing::error!("Error running lua plugin system: {e}"); - } - - *has_run = true; - } - } - for (stage, closure) in &systems.core_stages { if stage == &lua_stage { let executor = lua.enter(|ctx| { diff --git a/framework_crates/bones_scripting/src/lua/asset.rs b/framework_crates/bones_scripting/src/lua/asset.rs index ac3b4b661a..5e99dc0c36 100644 --- a/framework_crates/bones_scripting/src/lua/asset.rs +++ b/framework_crates/bones_scripting/src/lua/asset.rs @@ -131,21 +131,6 @@ fn session_metatable(ctx: Context) -> Table { ) .unwrap(); - let add_startup_system_callback = ctx.registry().stash( - &ctx, - Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| { - let (this, closure): (UserData, Closure) = stack.consume(ctx)?; - let this = this.downcast_static::()?; - - let mut systems = this.borrow_mut(); - systems - .as_loaded_mut() - .startup - .push((false, ctx.registry().stash(&ctx, closure))); - - Ok(CallbackReturn::Return) - }), - ); let add_system_to_stage_callback = ctx.registry().stash( &ctx, Callback::from_fn(&ctx, move |ctx, _fuel, mut stack| { @@ -176,9 +161,6 @@ fn session_metatable(ctx: Context) -> Table { stack .push_front(ctx.registry().fetch(&add_system_to_stage_callback).into()); } - b"add_startup_system" => { - stack.push_front(ctx.registry().fetch(&add_startup_system_callback).into()); - } _ => (), } @@ -234,8 +216,6 @@ pub type SystemStageId = Ulid; /// The systems that have been registered by a lua plugin. #[derive(Default)] pub struct LuaPluginSystems { - /// Startup systems. The bool indicates whether the system has been run yet. - pub startup: Vec<(bool, StashedClosure)>, /// Systems that run in the core stages. pub core_stages: Vec<(CoreStage, StashedClosure)>, } diff --git a/framework_crates/bones_scripting/src/lua/bindings.rs b/framework_crates/bones_scripting/src/lua/bindings.rs index 9a4a5c9d93..a3bad98f03 100644 --- a/framework_crates/bones_scripting/src/lua/bindings.rs +++ b/framework_crates/bones_scripting/src/lua/bindings.rs @@ -49,6 +49,7 @@ pub fn env(ctx: Context) -> Table { // Set the `CoreStage` enum global let core_stage_table = Table::new(&ctx); for (name, stage) in [ + ("Startup", CoreStage::Startup), ("First", CoreStage::First), ("PreUpdate", CoreStage::PreUpdate), ("Update", CoreStage::Update), diff --git a/framework_crates/bones_scripting/src/lua/bindings/schema.rs b/framework_crates/bones_scripting/src/lua/bindings/schema.rs index 91ddc718af..c25d9fc7db 100644 --- a/framework_crates/bones_scripting/src/lua/bindings/schema.rs +++ b/framework_crates/bones_scripting/src/lua/bindings/schema.rs @@ -28,7 +28,14 @@ pub fn schema_fn(ctx: Context) -> Callback { schema.set_metatable(&ctx, Some(schema_metatable)); stack.push_front(schema.into()); } else { + #[cfg(not(debug_assertions))] // script info only, not a developer return Err(anyhow::format_err!("Schema not found: {schema_name}").into()); + + #[cfg(debug_assertions)] // add note about registry, probably a developer + return Err(anyhow::format_err!( + "Schema not found: {schema_name}, you may need to register the schema" + ) + .into()); } Ok(CallbackReturn::Return) From ab9dcc3996ea8382c63a2da8a396f654ac5d8f97 Mon Sep 17 00:00:00 2001 From: Tekhnae Raav Date: Mon, 11 May 2026 08:41:48 -0500 Subject: [PATCH 03/36] chore: remove mutable world borrows from `World::init_resource` --- framework_crates/bones_ecs/src/world.rs | 4 ++-- framework_crates/bones_framework/tests/system_param.rs | 2 +- framework_crates/bones_lib/src/lib.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework_crates/bones_ecs/src/world.rs b/framework_crates/bones_ecs/src/world.rs index 867d3686d4..bad906b94b 100644 --- a/framework_crates/bones_ecs/src/world.rs +++ b/framework_crates/bones_ecs/src/world.rs @@ -130,7 +130,7 @@ impl World { } /// Initialize a resource of type `T` by inserting it's default value. - pub fn init_resource(&mut self) -> RefMut<'_, R> { + pub fn init_resource(&self) -> RefMut<'_, R> { if unlikely(!self.resources.contains::()) { let value = R::from_world(self); self.resources.insert(value); @@ -394,7 +394,7 @@ mod tests { #[test] fn init_resource_does_not_overwrite() { - let mut w = World::default(); + let w = World::default(); w.insert_resource(TestResource(0)); w.init_resource::(); w.insert_resource(TestResource(1)); diff --git a/framework_crates/bones_framework/tests/system_param.rs b/framework_crates/bones_framework/tests/system_param.rs index f61ce6784b..e2f4be8462 100644 --- a/framework_crates/bones_framework/tests/system_param.rs +++ b/framework_crates/bones_framework/tests/system_param.rs @@ -9,7 +9,7 @@ use futures_lite::{ }; fn create_world() -> World { - let mut world = World::default(); + let world = World::default(); let core_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests") diff --git a/framework_crates/bones_lib/src/lib.rs b/framework_crates/bones_lib/src/lib.rs index 407ce6cc0f..c23ae739ca 100644 --- a/framework_crates/bones_lib/src/lib.rs +++ b/framework_crates/bones_lib/src/lib.rs @@ -205,7 +205,7 @@ impl SessionBuilder { pub fn finish_and_add(mut self, sessions: &mut Sessions) -> &mut Session { let mut session = Session { world: { - let mut w = World::default(); + let w = World::default(); w.init_resource::