@@ -825,38 +825,39 @@ impl<'w> EntityWorldMut<'w> {
825
825
entities. set ( entity. index ( ) , new_location) ;
826
826
}
827
827
828
- /// Removes any components in the [`Bundle`] from the entity.
829
- // TODO: BundleRemover?
830
- pub fn remove < T : Bundle > ( & mut self ) -> & mut Self {
831
- let archetypes = & mut self . world . archetypes ;
832
- let storages = & mut self . world . storages ;
833
- let components = & mut self . world . components ;
834
- let entities = & mut self . world . entities ;
835
- let removed_components = & mut self . world . removed_components ;
836
-
837
- let bundle_info = self . world . bundles . init_info :: < T > ( components, storages) ;
838
- let old_location = self . location ;
839
-
840
- // SAFETY: `archetype_id` exists because it is referenced in the old `EntityLocation` which is valid,
841
- // components exist in `bundle_info` because `Bundles::init_info` initializes a `BundleInfo` containing all components of the bundle type `T`
842
- let new_archetype_id = unsafe {
843
- remove_bundle_from_archetype (
844
- archetypes,
845
- storages,
846
- components,
847
- old_location. archetype_id ,
848
- bundle_info,
849
- true ,
850
- )
851
- . expect ( "intersections should always return a result" )
852
- } ;
828
+ /// Remove the components of `bundle_info` from `entity`, where `self_location` and `old_location`
829
+ /// are the location of this entity, and `self_location` is updated to the new location.
830
+ ///
831
+ /// SAFETY: `old_location` must be valid and the components in `bundle_info` must exist.
832
+ #[ allow( clippy:: too_many_arguments) ]
833
+ unsafe fn remove_bundle_info (
834
+ entity : Entity ,
835
+ self_location : & mut EntityLocation ,
836
+ old_location : EntityLocation ,
837
+ bundle_info : & BundleInfo ,
838
+ archetypes : & mut Archetypes ,
839
+ storages : & mut Storages ,
840
+ components : & Components ,
841
+ entities : & mut Entities ,
842
+ removed_components : & mut RemovedComponentEvents ,
843
+ ) {
844
+ // SAFETY: `archetype_id` exists because it is referenced in `old_location` which is valid
845
+ // and components in `bundle_info` must exist due to this functions safety invariants.
846
+ let new_archetype_id = remove_bundle_from_archetype (
847
+ archetypes,
848
+ storages,
849
+ components,
850
+ old_location. archetype_id ,
851
+ bundle_info,
852
+ true ,
853
+ )
854
+ . expect ( "intersections should always return a result" ) ;
853
855
854
856
if new_archetype_id == old_location. archetype_id {
855
- return self ;
857
+ return ;
856
858
}
857
859
858
860
let old_archetype = & mut archetypes[ old_location. archetype_id ] ;
859
- let entity = self . entity ;
860
861
for component_id in bundle_info. components ( ) . iter ( ) . cloned ( ) {
861
862
if old_archetype. contains ( component_id) {
862
863
removed_components. send ( component_id, entity) ;
@@ -873,17 +874,86 @@ impl<'w> EntityWorldMut<'w> {
873
874
}
874
875
}
875
876
876
- #[ allow( clippy:: undocumented_unsafe_blocks) ] // TODO: document why this is safe
877
+ // SAFETY: `new_archetype_id` is a subset of the components in `old_location.archetype_id`
878
+ // because it is created by removing a bundle from these components.
879
+ Self :: move_entity_from_remove :: < true > (
880
+ entity,
881
+ self_location,
882
+ old_location. archetype_id ,
883
+ old_location,
884
+ entities,
885
+ archetypes,
886
+ storages,
887
+ new_archetype_id,
888
+ ) ;
889
+ }
890
+
891
+ /// Removes any components in the [`Bundle`] from the entity.
892
+ // TODO: BundleRemover?
893
+ pub fn remove < T : Bundle > ( & mut self ) -> & mut Self {
894
+ let archetypes = & mut self . world . archetypes ;
895
+ let storages = & mut self . world . storages ;
896
+ let components = & mut self . world . components ;
897
+ let entities = & mut self . world . entities ;
898
+ let removed_components = & mut self . world . removed_components ;
899
+
900
+ let bundle_info = self . world . bundles . init_info :: < T > ( components, storages) ;
901
+ let old_location = self . location ;
902
+
903
+ // SAFETY: Components exist in `bundle_info` because `Bundles::init_info`
904
+ // initializes a `BundleInfo` containing all components of the bundle type `T`.
877
905
unsafe {
878
- Self :: move_entity_from_remove :: < true > (
879
- entity,
906
+ Self :: remove_bundle_info (
907
+ self . entity ,
880
908
& mut self . location ,
881
- old_location. archetype_id ,
882
909
old_location,
910
+ bundle_info,
911
+ archetypes,
912
+ storages,
913
+ components,
883
914
entities,
915
+ removed_components,
916
+ ) ;
917
+ }
918
+
919
+ self
920
+ }
921
+
922
+ /// Removes any components except those in the [`Bundle`] from the entity.
923
+ pub fn retain < T : Bundle > ( & mut self ) -> & mut Self {
924
+ let archetypes = & mut self . world . archetypes ;
925
+ let storages = & mut self . world . storages ;
926
+ let components = & mut self . world . components ;
927
+ let entities = & mut self . world . entities ;
928
+ let removed_components = & mut self . world . removed_components ;
929
+
930
+ let retained_bundle_info = self . world . bundles . init_info :: < T > ( components, storages) ;
931
+ let old_location = self . location ;
932
+ let old_archetype = & mut archetypes[ old_location. archetype_id ] ;
933
+
934
+ let to_remove = & old_archetype
935
+ . components ( )
936
+ . filter ( |c| !retained_bundle_info. components ( ) . contains ( c) )
937
+ . collect :: < Vec < _ > > ( ) ;
938
+ let remove_bundle_info = self
939
+ . world
940
+ . bundles
941
+ . init_dynamic_info ( components, to_remove)
942
+ . 0 ;
943
+
944
+ // SAFETY: Components exist in `remove_bundle_info` because `Bundles::init_dynamic_info`
945
+ // initializes a `BundleInfo` containing all components in the to_remove Bundle.
946
+ unsafe {
947
+ Self :: remove_bundle_info (
948
+ self . entity ,
949
+ & mut self . location ,
950
+ old_location,
951
+ remove_bundle_info,
884
952
archetypes,
885
953
storages,
886
- new_archetype_id,
954
+ components,
955
+ entities,
956
+ removed_components,
887
957
) ;
888
958
}
889
959
@@ -1775,6 +1845,43 @@ mod tests {
1775
1845
assert_eq ! ( world. entity( e2) . get:: <Dense >( ) . unwrap( ) , & Dense ( 1 ) ) ;
1776
1846
}
1777
1847
1848
+ // Test that calling retain with `()` removes all components.
1849
+ #[ test]
1850
+ fn retain_nothing ( ) {
1851
+ #[ derive( Component ) ]
1852
+ struct Marker < const N : usize > ;
1853
+
1854
+ let mut world = World :: new ( ) ;
1855
+ let ent = world. spawn ( ( Marker :: < 1 > , Marker :: < 2 > , Marker :: < 3 > ) ) . id ( ) ;
1856
+
1857
+ world. entity_mut ( ent) . retain :: < ( ) > ( ) ;
1858
+ assert_eq ! ( world. entity( ent) . archetype( ) . components( ) . next( ) , None ) ;
1859
+ }
1860
+
1861
+ // Test removing some components with `retain`, including components not on the entity.
1862
+ #[ test]
1863
+ fn retain_some_components ( ) {
1864
+ #[ derive( Component ) ]
1865
+ struct Marker < const N : usize > ;
1866
+
1867
+ let mut world = World :: new ( ) ;
1868
+ let ent = world. spawn ( ( Marker :: < 1 > , Marker :: < 2 > , Marker :: < 3 > ) ) . id ( ) ;
1869
+
1870
+ world. entity_mut ( ent) . retain :: < ( Marker < 2 > , Marker < 4 > ) > ( ) ;
1871
+ // Check that marker 2 was retained.
1872
+ assert ! ( world. entity( ent) . get:: <Marker <2 >>( ) . is_some( ) ) ;
1873
+ // Check that only marker 2 was retained.
1874
+ assert_eq ! (
1875
+ world
1876
+ . entity( ent)
1877
+ . archetype( )
1878
+ . components( )
1879
+ . collect:: <Vec <_>>( )
1880
+ . len( ) ,
1881
+ 1
1882
+ ) ;
1883
+ }
1884
+
1778
1885
// regression test for https://github.com/bevyengine/bevy/pull/7805
1779
1886
#[ test]
1780
1887
fn inserting_sparse_updates_archetype_row ( ) {
0 commit comments