diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 5813cb3918604..5ffbf61549ffd 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -314,7 +314,7 @@ impl Plugin for AssetPlugin { { let mut sources = app .world_mut() - .get_resource_or_insert_with::(Default::default); + .get_resource_or_init::(); sources.init_default_source( &self.file_path, (!matches!(self.mode, AssetMode::Unprocessed)) @@ -519,7 +519,7 @@ impl AssetApp for App { { let mut sources = self .world_mut() - .get_resource_or_insert_with(AssetSourceBuilders::default); + .get_resource_or_init::(); sources.insert(id, source); } diff --git a/crates/bevy_ecs/src/event/registry.rs b/crates/bevy_ecs/src/event/registry.rs index 67061f221208b..ccc7b9fb92a3f 100644 --- a/crates/bevy_ecs/src/event/registry.rs +++ b/crates/bevy_ecs/src/event/registry.rs @@ -49,7 +49,7 @@ impl EventRegistry { // By initializing the resource here, we can be sure that it is present, // and receive the correct, up-to-date `ComponentId` even if it was previously removed. let component_id = world.init_resource::>(); - let mut registry = world.get_resource_or_insert_with(Self::default); + let mut registry = world.get_resource_or_init::(); registry.event_updates.push(RegisteredEvent { component_id, previously_updated: false, @@ -84,7 +84,7 @@ impl EventRegistry { /// Removes an event from the world and it's associated [`EventRegistry`]. pub fn deregister_events(world: &mut World) { let component_id = world.init_resource::>(); - let mut registry = world.get_resource_or_insert_with(Self::default); + let mut registry = world.get_resource_or_init::(); registry .event_updates .retain(|e| e.component_id != component_id); diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index fd2837adb0dc7..d139a1af1361c 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -405,7 +405,7 @@ impl Schedule { if self.graph.changed { self.graph.initialize(world); let ignored_ambiguities = world - .get_resource_or_insert_with::(Schedules::default) + .get_resource_or_init::() .ignored_scheduling_ambiguities .clone(); self.graph.update_schedule( diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 6b194a0df5733..00e175cd77e17 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -2101,6 +2101,19 @@ impl World { /// Gets a mutable reference to the resource of type `T` if it exists, /// otherwise inserts the resource using the result of calling `func`. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// #[derive(Resource)] + /// struct MyResource(i32); + /// + /// # let mut world = World::new(); + /// let my_res = world.get_resource_or_insert_with(|| MyResource(10)); + /// assert_eq!(my_res.0, 10); + /// ``` #[inline] #[track_caller] pub fn get_resource_or_insert_with( @@ -2137,6 +2150,82 @@ impl World { unsafe { data.with_type::() } } + /// Gets a mutable reference to the resource of type `T` if it exists, + /// otherwise initializes the resource by calling its [`FromWorld`] + /// implementation. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::*; + /// # + /// #[derive(Resource)] + /// struct Foo(i32); + /// + /// impl Default for Foo { + /// fn default() -> Self { + /// Self(15) + /// } + /// } + /// + /// #[derive(Resource)] + /// struct MyResource(i32); + /// + /// impl FromWorld for MyResource { + /// fn from_world(world: &mut World) -> Self { + /// let foo = world.get_resource_or_init::(); + /// Self(foo.0 * 2) + /// } + /// } + /// + /// # let mut world = World::new(); + /// let my_res = world.get_resource_or_init::(); + /// assert_eq!(my_res.0, 30); + /// ``` + #[track_caller] + pub fn get_resource_or_init(&mut self) -> Mut<'_, R> { + #[cfg(feature = "track_change_detection")] + let caller = Location::caller(); + let change_tick = self.change_tick(); + let last_change_tick = self.last_change_tick(); + + let component_id = self.components.register_resource::(); + if self + .storages + .resources + .get(component_id) + .map_or(true, |data| !data.is_present()) + { + let value = R::from_world(self); + OwningPtr::make(value, |ptr| { + // SAFETY: component_id was just initialized and corresponds to resource of type R. + unsafe { + self.insert_resource_by_id( + component_id, + ptr, + #[cfg(feature = "track_change_detection")] + caller, + ); + } + }); + } + + // SAFETY: The resource was just initialized if it was empty. + let data = unsafe { + self.storages + .resources + .get_mut(component_id) + .debug_checked_unwrap() + }; + // SAFETY: The resource must be present, as we would have inserted it if it was empty. + let data = unsafe { + data.get_mut(last_change_tick, change_tick) + .debug_checked_unwrap() + }; + // SAFETY: The underlying type of the resource is `R`. + unsafe { data.with_type::() } + } + /// Gets an immutable reference to the non-send resource of the given type, if it exists. /// /// # Panics @@ -3230,7 +3319,7 @@ impl World { /// /// The `Schedules` resource will be initialized if it does not already exist. pub fn add_schedule(&mut self, schedule: Schedule) { - let mut schedules = self.get_resource_or_insert_with(Schedules::default); + let mut schedules = self.get_resource_or_init::(); schedules.insert(schedule); } diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index cb3c446a1ad7a..a6f45fbfea85f 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -241,12 +241,10 @@ impl AppGizmoBuilder for App { } self.world_mut() - .get_resource_or_insert_with::(Default::default) + .get_resource_or_init::() .register::(); - let mut handles = self - .world_mut() - .get_resource_or_insert_with::(Default::default); + let mut handles = self.world_mut().get_resource_or_init::(); handles.list.insert(TypeId::of::(), None); handles.strip.insert(TypeId::of::(), None); @@ -288,7 +286,7 @@ impl AppGizmoBuilder for App { self.init_gizmo_group::(); self.world_mut() - .get_resource_or_insert_with::(Default::default) + .get_resource_or_init::() .insert(config, group); self diff --git a/crates/bevy_state/src/state/transitions.rs b/crates/bevy_state/src/state/transitions.rs index 6902a791c5d20..bf2712e04cd6e 100644 --- a/crates/bevy_state/src/state/transitions.rs +++ b/crates/bevy_state/src/state/transitions.rs @@ -181,7 +181,7 @@ pub(crate) fn internal_apply_state_transition( /// Runs automatically when using `App` to insert states, but needs to /// be added manually in other situations. pub fn setup_state_transitions_in_world(world: &mut World) { - let mut schedules = world.get_resource_or_insert_with(Schedules::default); + let mut schedules = world.get_resource_or_init::(); if schedules.contains(StateTransition) { return; }