From 455f420bd3b32063ff17415ce589a5822c90c3d1 Mon Sep 17 00:00:00 2001 From: MrGVSV Date: Tue, 14 Dec 2021 11:38:23 -0800 Subject: [PATCH 1/7] Added method to restart the current state --- crates/bevy_ecs/src/schedule/state.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index 7aba070dabf63..d2d0d46ae3ba0 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -369,6 +369,32 @@ where Ok(()) } + /// Schedule a state change that restarts the active state. + /// This will fail if there is a scheduled operation + pub fn restart(&mut self) -> Result<(), StateError> { + if self.scheduled.is_some() { + return Err(StateError::StateAlreadyQueued); + } + + if let Some(state) = self.stack.last() { + self.scheduled = Some(ScheduledOperation::Set(state.clone())); + Ok(()) + } else { + return Err(StateError::StackEmpty); + } + } + + /// Same as [Self::restart], but if there is already a scheduled state operation, + /// it will be overwritten instead of failing + pub fn overwrite_restart(&mut self) -> Result<(), StateError> { + if let Some(state) = self.stack.last() { + self.scheduled = Some(ScheduledOperation::Set(state.clone())); + Ok(()) + } else { + return Err(StateError::StackEmpty); + } + } + pub fn current(&self) -> &T { self.stack.last().unwrap() } From 82b3201cdf50bf62e32dbde42373910bb92bb163 Mon Sep 17 00:00:00 2001 From: MrGVSV Date: Tue, 14 Dec 2021 11:52:02 -0800 Subject: [PATCH 2/7] Fixed Clippy warnings --- crates/bevy_ecs/src/schedule/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index d2d0d46ae3ba0..62752bca71aee 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -380,7 +380,7 @@ where self.scheduled = Some(ScheduledOperation::Set(state.clone())); Ok(()) } else { - return Err(StateError::StackEmpty); + Err(StateError::StackEmpty) } } @@ -391,7 +391,7 @@ where self.scheduled = Some(ScheduledOperation::Set(state.clone())); Ok(()) } else { - return Err(StateError::StackEmpty); + Err(StateError::StackEmpty) } } From 561e8de788abf58fc917b16dcecd5332203ced80 Mon Sep 17 00:00:00 2001 From: MrGVSV Date: Tue, 14 Dec 2021 14:25:39 -0800 Subject: [PATCH 3/7] Added tests for restarting state --- crates/bevy_ecs/src/schedule/state.rs | 86 +++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index 62752bca71aee..9be634c267533 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -681,4 +681,90 @@ mod test { stage.run(&mut world); assert!(*world.get_resource::().unwrap(), "after test"); } + + #[test] + fn restart_state_tests() { + #[derive(Clone, PartialEq, Eq, Debug, Hash)] + enum LoadState { + Load, + Finish, + } + + #[derive(PartialEq, Eq, Debug)] + enum LoadStatus { + EnterLoad, + ExitLoad, + EnterFinish, + } + + let mut world = World::new(); + world.insert_resource(Vec::::new()); + world.insert_resource(State::new(LoadState::Load)); + + let mut stage = SystemStage::parallel(); + stage.add_system_set(State::::get_driver()); + + // Systems to track loading status + stage + .add_system_set( + State::on_enter_set(LoadState::Load) + .with_system(|mut r: ResMut>| r.push(LoadStatus::EnterLoad)), + ) + .add_system_set( + State::on_exit_set(LoadState::Load) + .with_system(|mut r: ResMut>| r.push(LoadStatus::ExitLoad)), + ) + .add_system_set( + State::on_enter_set(LoadState::Finish) + .with_system(|mut r: ResMut>| r.push(LoadStatus::EnterFinish)), + ); + + stage.run(&mut world); + + // A. Restart state + let mut state = world.get_resource_mut::>().unwrap(); + let result = state.restart(); + assert!(matches!(result, Ok(()))); + stage.run(&mut world); + + // B. Restart state (overwrite schedule) + let mut state = world.get_resource_mut::>().unwrap(); + state.set(LoadState::Finish).unwrap(); + let result = state.overwrite_restart(); + assert!(matches!(result, Ok(()))); + stage.run(&mut world); + + // C. Fail restart state (transition already scheduled) + let mut state = world.get_resource_mut::>().unwrap(); + state.set(LoadState::Finish).unwrap(); + let result = state.restart(); + assert!(matches!(result, Err(StateError::StateAlreadyQueued))); + stage.run(&mut world); + + const EXPECTED: &[LoadStatus] = &[ + LoadStatus::EnterLoad, + // A + LoadStatus::ExitLoad, + LoadStatus::EnterLoad, + // B + LoadStatus::ExitLoad, + LoadStatus::EnterLoad, + // C + LoadStatus::ExitLoad, + LoadStatus::EnterFinish, + ]; + + let mut collected = world.get_resource_mut::>().unwrap(); + let mut count = 0; + for (found, expected) in collected.drain(..).zip(EXPECTED) { + assert_eq!(found, *expected); + count += 1; + } + // If not equal, some elements weren't executed + assert_eq!(EXPECTED.len(), count); + assert_eq!( + world.get_resource::>().unwrap().current(), + &LoadState::Finish + ); + } } From 06c6888b8f02dfb0522030c3fa7a54245178140b Mon Sep 17 00:00:00 2001 From: MrGVSV Date: Wed, 15 Dec 2021 10:49:36 -0800 Subject: [PATCH 4/7] Replaced if-let with unwrap --- crates/bevy_ecs/src/schedule/state.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index 9be634c267533..513b81db070f2 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -376,23 +376,17 @@ where return Err(StateError::StateAlreadyQueued); } - if let Some(state) = self.stack.last() { - self.scheduled = Some(ScheduledOperation::Set(state.clone())); - Ok(()) - } else { - Err(StateError::StackEmpty) - } + let state = self.stack.last().unwrap(); + self.scheduled = Some(ScheduledOperation::Set(state.clone())); + Ok(()) } /// Same as [Self::restart], but if there is already a scheduled state operation, /// it will be overwritten instead of failing pub fn overwrite_restart(&mut self) -> Result<(), StateError> { - if let Some(state) = self.stack.last() { - self.scheduled = Some(ScheduledOperation::Set(state.clone())); - Ok(()) - } else { - Err(StateError::StackEmpty) - } + let state = self.stack.last().unwrap(); + self.scheduled = Some(ScheduledOperation::Set(state.clone())); + Ok(()) } pub fn current(&self) -> &T { From 91bd018a1aa1d83bb6a5c95b43ec579430e1c91a Mon Sep 17 00:00:00 2001 From: MrGVSV Date: Wed, 15 Dec 2021 10:53:53 -0800 Subject: [PATCH 5/7] Added clarifying documentation to stack field --- crates/bevy_ecs/src/schedule/state.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index 513b81db070f2..ff990c1587fa4 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -21,6 +21,9 @@ impl StateData for T where T: Send + Sync + Clone + Eq + Debug + Hash + 'stat #[derive(Debug)] pub struct State { transition: Option>, + /// The current states in the stack. + /// + /// There is always guaranteed to be at least one. stack: Vec, scheduled: Option>, end_next_loop: bool, From f2869d12009e0c9daaf611e8b479b355047b242a Mon Sep 17 00:00:00 2001 From: MrGVSV Date: Thu, 23 Dec 2021 10:29:02 -0800 Subject: [PATCH 6/7] Removed unnecessary Result --- crates/bevy_ecs/src/schedule/state.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index ff990c1587fa4..ffce80839db84 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -386,10 +386,9 @@ where /// Same as [Self::restart], but if there is already a scheduled state operation, /// it will be overwritten instead of failing - pub fn overwrite_restart(&mut self) -> Result<(), StateError> { + pub fn overwrite_restart(&mut self) { let state = self.stack.last().unwrap(); self.scheduled = Some(ScheduledOperation::Set(state.clone())); - Ok(()) } pub fn current(&self) -> &T { @@ -727,8 +726,7 @@ mod test { // B. Restart state (overwrite schedule) let mut state = world.get_resource_mut::>().unwrap(); state.set(LoadState::Finish).unwrap(); - let result = state.overwrite_restart(); - assert!(matches!(result, Ok(()))); + state.overwrite_restart(); stage.run(&mut world); // C. Fail restart state (transition already scheduled) From 421b7260c7660a0631f29336ad7f7f1d002caaf7 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Thu, 3 Feb 2022 18:45:50 -0800 Subject: [PATCH 7/7] clippy --- crates/bevy_ecs/src/schedule/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index ffce80839db84..130eff9e8dcb0 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -384,7 +384,7 @@ where Ok(()) } - /// Same as [Self::restart], but if there is already a scheduled state operation, + /// Same as [`Self::restart`], but if there is already a scheduled state operation, /// it will be overwritten instead of failing pub fn overwrite_restart(&mut self) { let state = self.stack.last().unwrap();