@@ -2136,6 +2136,209 @@ impl World {
21362136 }
21372137 }
21382138
2139+ /// Iterates over all resources in the world.
2140+ ///
2141+ /// The returned iterator provides lifetimed, but type-unsafe pointers. Actually reading the contents
2142+ /// of each resource will require the use of unsafe code.
2143+ ///
2144+ /// # Examples
2145+ ///
2146+ /// ## Printing the size of all resources
2147+ ///
2148+ /// ```
2149+ /// # use bevy_ecs::prelude::*;
2150+ /// # #[derive(Resource)]
2151+ /// # struct A(u32);
2152+ /// # #[derive(Resource)]
2153+ /// # struct B(u32);
2154+ /// #
2155+ /// # let mut world = World::new();
2156+ /// # world.insert_resource(A(1));
2157+ /// # world.insert_resource(B(2));
2158+ /// let mut total = 0;
2159+ /// for (info, _) in world.iter_resources() {
2160+ /// println!("Resource: {}", info.name());
2161+ /// println!("Size: {} bytes", info.layout().size());
2162+ /// total += info.layout().size();
2163+ /// }
2164+ /// println!("Total size: {} bytes", total);
2165+ /// # assert_eq!(total, std::mem::size_of::<A>() + std::mem::size_of::<B>());
2166+ /// ```
2167+ ///
2168+ /// ## Dynamically running closures for resources matching specific `TypeId`s
2169+ ///
2170+ /// ```
2171+ /// # use bevy_ecs::prelude::*;
2172+ /// # use std::collections::HashMap;
2173+ /// # use std::any::TypeId;
2174+ /// # use bevy_ptr::Ptr;
2175+ /// # #[derive(Resource)]
2176+ /// # struct A(u32);
2177+ /// # #[derive(Resource)]
2178+ /// # struct B(u32);
2179+ /// #
2180+ /// # let mut world = World::new();
2181+ /// # world.insert_resource(A(1));
2182+ /// # world.insert_resource(B(2));
2183+ /// #
2184+ /// // In this example, `A` and `B` are resources. We deliberately do not use the
2185+ /// // `bevy_reflect` crate here to showcase the low-level [`Ptr`] usage. You should
2186+ /// // probably use something like `ReflectFromPtr` in a real-world scenario.
2187+ ///
2188+ /// // Create the hash map that will store the closures for each resource type
2189+ /// let mut closures: HashMap<TypeId, Box<dyn Fn(&Ptr<'_>)>> = HashMap::new();
2190+ ///
2191+ /// // Add closure for `A`
2192+ /// closures.insert(TypeId::of::<A>(), Box::new(|ptr| {
2193+ /// // SAFETY: We assert ptr is the same type of A with TypeId of A
2194+ /// let a = unsafe { &ptr.deref::<A>() };
2195+ /// # assert_eq!(a.0, 1);
2196+ /// // ... do something with `a` here
2197+ /// }));
2198+ ///
2199+ /// // Add closure for `B`
2200+ /// closures.insert(TypeId::of::<B>(), Box::new(|ptr| {
2201+ /// // SAFETY: We assert ptr is the same type of B with TypeId of B
2202+ /// let b = unsafe { &ptr.deref::<B>() };
2203+ /// # assert_eq!(b.0, 2);
2204+ /// // ... do something with `b` here
2205+ /// }));
2206+ ///
2207+ /// // Iterate all resources, in order to run the closures for each matching resource type
2208+ /// for (info, ptr) in world.iter_resources() {
2209+ /// let Some(type_id) = info.type_id() else {
2210+ /// // It's possible for resources to not have a `TypeId` (e.g. non-Rust resources
2211+ /// // dynamically inserted via a scripting language) in which case we can't match them.
2212+ /// continue;
2213+ /// };
2214+ ///
2215+ /// let Some(closure) = closures.get(&type_id) else {
2216+ /// // No closure for this resource type, skip it.
2217+ /// continue;
2218+ /// };
2219+ ///
2220+ /// // Run the closure for the resource
2221+ /// closure(&ptr);
2222+ /// }
2223+ /// ```
2224+ #[ inline]
2225+ pub fn iter_resources ( & self ) -> impl Iterator < Item = ( & ComponentInfo , Ptr < ' _ > ) > {
2226+ self . storages
2227+ . resources
2228+ . iter ( )
2229+ . filter_map ( |( component_id, data) | {
2230+ // SAFETY: If a resource has been initialized, a corresponding ComponentInfo must exist with it's ID.
2231+ let component_info = unsafe {
2232+ self . components
2233+ . get_info ( component_id)
2234+ . debug_checked_unwrap ( )
2235+ } ;
2236+ Some ( ( component_info, data. get_data ( ) ?) )
2237+ } )
2238+ }
2239+
2240+ /// Mutably iterates over all resources in the world.
2241+ ///
2242+ /// The returned iterator provides lifetimed, but type-unsafe pointers. Actually reading from or writing
2243+ /// to the contents of each resource will require the use of unsafe code.
2244+ ///
2245+ /// # Example
2246+ ///
2247+ /// ```
2248+ /// # use bevy_ecs::prelude::*;
2249+ /// # use bevy_ecs::change_detection::MutUntyped;
2250+ /// # use std::collections::HashMap;
2251+ /// # use std::any::TypeId;
2252+ /// # #[derive(Resource)]
2253+ /// # struct A(u32);
2254+ /// # #[derive(Resource)]
2255+ /// # struct B(u32);
2256+ /// #
2257+ /// # let mut world = World::new();
2258+ /// # world.insert_resource(A(1));
2259+ /// # world.insert_resource(B(2));
2260+ /// #
2261+ /// // In this example, `A` and `B` are resources. We deliberately do not use the
2262+ /// // `bevy_reflect` crate here to showcase the low-level `MutUntyped` usage. You should
2263+ /// // probably use something like `ReflectFromPtr` in a real-world scenario.
2264+ ///
2265+ /// // Create the hash map that will store the mutator closures for each resource type
2266+ /// let mut mutators: HashMap<TypeId, Box<dyn Fn(&mut MutUntyped<'_>)>> = HashMap::new();
2267+ ///
2268+ /// // Add mutator closure for `A`
2269+ /// mutators.insert(TypeId::of::<A>(), Box::new(|mut_untyped| {
2270+ /// // Note: `MutUntyped::as_mut()` automatically marks the resource as changed
2271+ /// // for ECS change detection, and gives us a `PtrMut` we can use to mutate the resource.
2272+ /// // SAFETY: We assert ptr is the same type of A with TypeId of A
2273+ /// let a = unsafe { &mut mut_untyped.as_mut().deref_mut::<A>() };
2274+ /// # a.0 += 1;
2275+ /// // ... mutate `a` here
2276+ /// }));
2277+ ///
2278+ /// // Add mutator closure for `B`
2279+ /// mutators.insert(TypeId::of::<B>(), Box::new(|mut_untyped| {
2280+ /// // SAFETY: We assert ptr is the same type of B with TypeId of B
2281+ /// let b = unsafe { &mut mut_untyped.as_mut().deref_mut::<B>() };
2282+ /// # b.0 += 1;
2283+ /// // ... mutate `b` here
2284+ /// }));
2285+ ///
2286+ /// // Iterate all resources, in order to run the mutator closures for each matching resource type
2287+ /// for (info, mut mut_untyped) in world.iter_resources_mut() {
2288+ /// let Some(type_id) = info.type_id() else {
2289+ /// // It's possible for resources to not have a `TypeId` (e.g. non-Rust resources
2290+ /// // dynamically inserted via a scripting language) in which case we can't match them.
2291+ /// continue;
2292+ /// };
2293+ ///
2294+ /// let Some(mutator) = mutators.get(&type_id) else {
2295+ /// // No mutator closure for this resource type, skip it.
2296+ /// continue;
2297+ /// };
2298+ ///
2299+ /// // Run the mutator closure for the resource
2300+ /// mutator(&mut mut_untyped);
2301+ /// }
2302+ /// # assert_eq!(world.resource::<A>().0, 2);
2303+ /// # assert_eq!(world.resource::<B>().0, 3);
2304+ /// ```
2305+ #[ inline]
2306+ pub fn iter_resources_mut ( & mut self ) -> impl Iterator < Item = ( & ComponentInfo , MutUntyped < ' _ > ) > {
2307+ self . storages
2308+ . resources
2309+ . iter ( )
2310+ . filter_map ( |( component_id, data) | {
2311+ // SAFETY: If a resource has been initialized, a corresponding ComponentInfo must exist with it's ID.
2312+ let component_info = unsafe {
2313+ self . components
2314+ . get_info ( component_id)
2315+ . debug_checked_unwrap ( )
2316+ } ;
2317+ let ( ptr, ticks) = data. get_with_ticks ( ) ?;
2318+
2319+ // SAFETY:
2320+ // - We have exclusive access to the world, so no other code can be aliasing the `TickCells`
2321+ // - We only hold one `TicksMut` at a time, and we let go of it before getting the next one
2322+ let ticks = unsafe {
2323+ TicksMut :: from_tick_cells (
2324+ ticks,
2325+ self . last_change_tick ( ) ,
2326+ self . read_change_tick ( ) ,
2327+ )
2328+ } ;
2329+
2330+ let mut_untyped = MutUntyped {
2331+ // SAFETY:
2332+ // - We have exclusive access to the world, so no other code can be aliasing the `Ptr`
2333+ // - We iterate one resource at a time, and we let go of each `PtrMut` before getting the next one
2334+ value : unsafe { ptr. assert_unique ( ) } ,
2335+ ticks,
2336+ } ;
2337+
2338+ Some ( ( component_info, mut_untyped) )
2339+ } )
2340+ }
2341+
21392342 /// Gets a `!Send` resource to the resource with the id [`ComponentId`] if it exists.
21402343 /// The returned pointer must not be used to modify the resource, and must not be
21412344 /// dereferenced after the immutable borrow of the [`World`] ends.
@@ -2554,6 +2757,12 @@ mod tests {
25542757 #[ derive( Resource ) ]
25552758 struct TestResource ( u32 ) ;
25562759
2760+ #[ derive( Resource ) ]
2761+ struct TestResource2 ( String ) ;
2762+
2763+ #[ derive( Resource ) ]
2764+ struct TestResource3 ;
2765+
25572766 #[ test]
25582767 fn get_resource_by_id ( ) {
25592768 let mut world = World :: new ( ) ;
@@ -2594,6 +2803,66 @@ mod tests {
25942803 assert_eq ! ( resource. 0 , 43 ) ;
25952804 }
25962805
2806+ #[ test]
2807+ fn iter_resources ( ) {
2808+ let mut world = World :: new ( ) ;
2809+ world. insert_resource ( TestResource ( 42 ) ) ;
2810+ world. insert_resource ( TestResource2 ( "Hello, world!" . to_string ( ) ) ) ;
2811+ world. insert_resource ( TestResource3 ) ;
2812+ world. remove_resource :: < TestResource3 > ( ) ;
2813+
2814+ let mut iter = world. iter_resources ( ) ;
2815+
2816+ let ( info, ptr) = iter. next ( ) . unwrap ( ) ;
2817+ assert_eq ! ( info. name( ) , std:: any:: type_name:: <TestResource >( ) ) ;
2818+ // SAFETY: We know that the resource is of type `TestResource`
2819+ assert_eq ! ( unsafe { ptr. deref:: <TestResource >( ) . 0 } , 42 ) ;
2820+
2821+ let ( info, ptr) = iter. next ( ) . unwrap ( ) ;
2822+ assert_eq ! ( info. name( ) , std:: any:: type_name:: <TestResource2 >( ) ) ;
2823+ assert_eq ! (
2824+ // SAFETY: We know that the resource is of type `TestResource2`
2825+ unsafe { & ptr. deref:: <TestResource2 >( ) . 0 } ,
2826+ & "Hello, world!" . to_string( )
2827+ ) ;
2828+
2829+ assert ! ( iter. next( ) . is_none( ) ) ;
2830+ }
2831+
2832+ #[ test]
2833+ fn iter_resources_mut ( ) {
2834+ let mut world = World :: new ( ) ;
2835+ world. insert_resource ( TestResource ( 42 ) ) ;
2836+ world. insert_resource ( TestResource2 ( "Hello, world!" . to_string ( ) ) ) ;
2837+ world. insert_resource ( TestResource3 ) ;
2838+ world. remove_resource :: < TestResource3 > ( ) ;
2839+
2840+ let mut iter = world. iter_resources_mut ( ) ;
2841+
2842+ let ( info, mut mut_untyped) = iter. next ( ) . unwrap ( ) ;
2843+ assert_eq ! ( info. name( ) , std:: any:: type_name:: <TestResource >( ) ) ;
2844+ // SAFETY: We know that the resource is of type `TestResource`
2845+ unsafe {
2846+ mut_untyped. as_mut ( ) . deref_mut :: < TestResource > ( ) . 0 = 43 ;
2847+ } ;
2848+
2849+ let ( info, mut mut_untyped) = iter. next ( ) . unwrap ( ) ;
2850+ assert_eq ! ( info. name( ) , std:: any:: type_name:: <TestResource2 >( ) ) ;
2851+ // SAFETY: We know that the resource is of type `TestResource2`
2852+ unsafe {
2853+ mut_untyped. as_mut ( ) . deref_mut :: < TestResource2 > ( ) . 0 = "Hello, world?" . to_string ( ) ;
2854+ } ;
2855+
2856+ assert ! ( iter. next( ) . is_none( ) ) ;
2857+ std:: mem:: drop ( iter) ;
2858+
2859+ assert_eq ! ( world. resource:: <TestResource >( ) . 0 , 43 ) ;
2860+ assert_eq ! (
2861+ world. resource:: <TestResource2 >( ) . 0 ,
2862+ "Hello, world?" . to_string( )
2863+ ) ;
2864+ }
2865+
25972866 #[ test]
25982867 fn custom_resource_with_layout ( ) {
25992868 static DROP_COUNT : AtomicU32 = AtomicU32 :: new ( 0 ) ;
0 commit comments