diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index f99b5948c9..a76caf897e 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -896,6 +896,22 @@ impl<'w, 's> Commands<'w, 's> { self.queue(command::insert_resource(resource)); } + /// Inserts a [`Resource`] into the [`World`] with a specific value + /// if the resource is different or missing. + #[track_caller] + pub fn insert_resource_if_neq(&mut self, resource: R) { + let caller = MaybeLocation::caller(); + + self.queue(move |world: &mut World| { + if world + .get_resource::() + .is_none_or(|old_resource| *old_resource != resource) + { + world.insert_resource_with_caller(resource, caller); + } + }); + } + /// Removes a [`Resource`] from the [`World`]. /// /// # Example @@ -2912,6 +2928,111 @@ mod tests { assert!(world.contains_resource::>()); } + #[test] + fn insert_resource_if_not_equal() { + #[derive(Resource, PartialEq)] + struct P(u8); + + let mut world = World::default(); + let mut queue = CommandQueue::default(); + + { + let mut commands = Commands::new(&mut queue, &world); + commands.insert_resource_if_neq(P(41)); + } + + queue.apply(&mut world); + assert!(world.is_resource_added::

()); + assert_eq!(world.get_resource::

().unwrap().0, 41); + + world.clear_trackers(); + + { + let mut commands = Commands::new(&mut queue, &world); + commands.insert_resource_if_neq(P(42)); + } + + queue.apply(&mut world); + assert!(world.is_resource_changed::

()); + assert_eq!(world.get_resource::

().unwrap().0, 42); + + world.clear_trackers(); + + { + let mut commands = Commands::new(&mut queue, &world); + commands.insert_resource_if_neq(P(42)); + } + + queue.apply(&mut world); + assert!(!world.is_resource_changed::

()); + assert_eq!(world.get_resource::

().unwrap().0, 42); + } + + #[cfg(feature = "track_location")] + #[test] + fn insert_resource_if_not_equal_tracks_caller() { + use crate::change_detection::DetectChanges; + use core::panic::Location; + + #[derive(Resource, PartialEq)] + struct P(u8); + + let mut world = World::default(); + let mut queue = CommandQueue::default(); + + macro_rules! insert_resource_if_neq_with_expected_caller { + ($commands:expr, $resource:expr) => {{ + $commands.insert_resource_if_neq($resource); + Location::caller() + }}; + } + let expected1 = + insert_resource_if_neq_with_expected_caller!(Commands::new(&mut queue, &world), P(41)); + + queue.apply(&mut world); + + assert_eq!( + world + .get_resource_ref::

() + .unwrap() + .changed_by() + .into_option(), + Some(expected1) + ); + + world.clear_trackers(); + + let expected2 = + insert_resource_if_neq_with_expected_caller!(Commands::new(&mut queue, &world), P(42)); + + queue.apply(&mut world); + + assert_eq!( + world + .get_resource_ref::

() + .unwrap() + .changed_by() + .into_option(), + Some(expected2) + ); + + world.clear_trackers(); + + let expected3 = + insert_resource_if_neq_with_expected_caller!(Commands::new(&mut queue, &world), P(42)); + + queue.apply(&mut world); + + assert_ne!( + world + .get_resource_ref::

() + .unwrap() + .changed_by() + .into_option(), + Some(expected3) + ); + } + #[test] fn remove_component_with_required_components() { #[derive(Component)]