From 4160c9f8a47cb8e89870449df6316369add850ca Mon Sep 17 00:00:00 2001 From: Max Whitehead Date: Thu, 4 Apr 2024 22:47:49 -0700 Subject: [PATCH 1/2] feat: Add Session commands to modify sessions from game code --- framework_crates/bones_lib/src/lib.rs | 30 ++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/framework_crates/bones_lib/src/lib.rs b/framework_crates/bones_lib/src/lib.rs index 108684e08c..dc2244b059 100644 --- a/framework_crates/bones_lib/src/lib.rs +++ b/framework_crates/bones_lib/src/lib.rs @@ -11,18 +11,30 @@ pub use bones_ecs as ecs; /// Bones lib prelude pub mod prelude { pub use crate::{ - ecs::prelude::*, instant::Instant, time::*, Game, GamePlugin, Session, SessionOptions, - SessionPlugin, SessionRunner, Sessions, + ecs::prelude::*, instant::Instant, time::*, Game, GamePlugin, Session, SessionCommands, + SessionOptions, SessionPlugin, SessionRunner, Sessions, }; } pub use instant; pub mod time; -use std::{fmt::Debug, sync::Arc}; +use std::{collections::VecDeque, fmt::Debug, sync::Arc}; use crate::prelude::*; +/// Commands that operate on [`Sessions`], called after all sessions update. +/// These may be used to add/delete/modify sessions. +/// +/// `SessionCommands` must be installed as a shared resource to take effect. +/// This may be achieved while installing a [`GamePlugin`]. +/// +/// `SessionCommands` is useful in a situation where you want to remove / recreate +/// a session from within it's own system. You cannot do this while the `Session` is running +#[derive(HasSchema, Default, Deref, DerefMut)] +#[schema(no_clone)] +pub struct SessionCommands(VecDeque>); + /// A bones game. This includes all of the game worlds, and systems. #[derive(Deref, DerefMut)] pub struct Session { @@ -400,6 +412,18 @@ impl Game { self.sessions.map.insert(session_name, current_session); } + // Extract `SessionCommands` from optional shared resource + // (Swap to avoid double borrow of self) + let mut session_commands = SessionCommands::default(); + if let Some(mut new_commands) = self.shared_resource_mut::() { + std::mem::swap(&mut session_commands.0, &mut new_commands.0); + } + + // Execute Session Commands + for command in session_commands.0.drain(..) { + command(&mut self.sessions); + } + // Run any after session game systems if let Some(systems) = game_systems.after_session.get_mut(&session_name) { for system in systems { From e8fd8cb6f0cdf30e918886e49e3e669c644767eb Mon Sep 17 00:00:00 2001 From: Max Whitehead Date: Sat, 13 Apr 2024 21:54:56 -0700 Subject: [PATCH 2/2] fix: Move session commands into Sessions. --- framework_crates/bones_lib/src/lib.rs | 55 ++++++++++++++------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/framework_crates/bones_lib/src/lib.rs b/framework_crates/bones_lib/src/lib.rs index dc2244b059..870429293e 100644 --- a/framework_crates/bones_lib/src/lib.rs +++ b/framework_crates/bones_lib/src/lib.rs @@ -11,7 +11,7 @@ pub use bones_ecs as ecs; /// Bones lib prelude pub mod prelude { pub use crate::{ - ecs::prelude::*, instant::Instant, time::*, Game, GamePlugin, Session, SessionCommands, + ecs::prelude::*, instant::Instant, time::*, Game, GamePlugin, Session, SessionCommand, SessionOptions, SessionPlugin, SessionRunner, Sessions, }; } @@ -22,19 +22,6 @@ pub mod time; use std::{collections::VecDeque, fmt::Debug, sync::Arc}; use crate::prelude::*; - -/// Commands that operate on [`Sessions`], called after all sessions update. -/// These may be used to add/delete/modify sessions. -/// -/// `SessionCommands` must be installed as a shared resource to take effect. -/// This may be achieved while installing a [`GamePlugin`]. -/// -/// `SessionCommands` is useful in a situation where you want to remove / recreate -/// a session from within it's own system. You cannot do this while the `Session` is running -#[derive(HasSchema, Default, Deref, DerefMut)] -#[schema(no_clone)] -pub struct SessionCommands(VecDeque>); - /// A bones game. This includes all of the game worlds, and systems. #[derive(Deref, DerefMut)] pub struct Session { @@ -412,18 +399,6 @@ impl Game { self.sessions.map.insert(session_name, current_session); } - // Extract `SessionCommands` from optional shared resource - // (Swap to avoid double borrow of self) - let mut session_commands = SessionCommands::default(); - if let Some(mut new_commands) = self.shared_resource_mut::() { - std::mem::swap(&mut session_commands.0, &mut new_commands.0); - } - - // Execute Session Commands - for command in session_commands.0.drain(..) { - command(&mut self.sessions); - } - // Run any after session game systems if let Some(systems) = game_systems.after_session.get_mut(&session_name) { for system in systems { @@ -432,6 +407,15 @@ impl Game { } } + // Execute Session Commands + { + let mut session_commands: VecDeque> = default(); + std::mem::swap(&mut session_commands, &mut self.sessions.commands); + for command in session_commands.drain(..) { + command(&mut self.sessions); + } + } + // Run after systems for system in &mut game_systems.after { system(self) @@ -520,12 +504,24 @@ impl GameSystems { } } +/// Type of session command +pub type SessionCommand = dyn FnOnce(&mut Sessions) + Sync + Send; + /// Container for multiple game sessions. /// /// Each session shares the same [`Entities`]. -#[derive(HasSchema, Default, Debug)] +#[derive(HasSchema, Default)] pub struct Sessions { map: UstrMap, + + /// Commands that operate on [`Sessions`], called after all sessions update. + /// These may be used to add/delete/modify sessions. + /// + /// Commands are useful in a situation where you want to remove / recreate + /// a session from within it's own system. You cannot do this while the `Session` is running. + /// + /// Commands added inside a session command will not be executed until next frame. + commands: VecDeque>, } /// Resource that allows you to configure the current session. @@ -597,6 +593,11 @@ impl Sessions { pub fn iter(&self) -> std::collections::hash_map::Iter { self.map.iter() } + + /// Add a [`SessionCommand`] to queue. + pub fn add_command(&mut self, command: Box) { + self.commands.push_back(command); + } } // We implement `Clone` so that the world can still be snapshot with this resouce in it, but we