@@ -307,6 +307,15 @@ impl SparseSetIndex for BundleId {
307
307
}
308
308
}
309
309
310
+ // What to do on insertion if component already exists
311
+ #[ derive( Clone , Copy , Eq , PartialEq ) ]
312
+ pub ( crate ) enum InsertMode {
313
+ /// Any existing components of a matching type will be overwritten.
314
+ Replace ,
315
+ /// Any existing components of a matching type will kept unchanged.
316
+ Keep ,
317
+ }
318
+
310
319
/// Stores metadata associated with a specific type of [`Bundle`] for a given [`World`].
311
320
///
312
321
/// [`World`]: crate::world::World
@@ -410,6 +419,7 @@ impl BundleInfo {
410
419
table_row : TableRow ,
411
420
change_tick : Tick ,
412
421
bundle : T ,
422
+ insert_mode : InsertMode ,
413
423
#[ cfg( feature = "track_change_detection" ) ] caller : & ' static Location < ' static > ,
414
424
) {
415
425
// NOTE: get_components calls this closure on each component in "bundle order".
@@ -425,8 +435,8 @@ impl BundleInfo {
425
435
unsafe { table. get_column_mut ( component_id) . debug_checked_unwrap ( ) } ;
426
436
// SAFETY: bundle_component is a valid index for this bundle
427
437
let status = unsafe { bundle_component_status. get_status ( bundle_component) } ;
428
- match status {
429
- ComponentStatus :: Added => {
438
+ match ( status, insert_mode ) {
439
+ ( ComponentStatus :: Added , _ ) => {
430
440
column. initialize (
431
441
table_row,
432
442
component_ptr,
@@ -435,7 +445,7 @@ impl BundleInfo {
435
445
caller,
436
446
) ;
437
447
}
438
- ComponentStatus :: Mutated => {
448
+ ( ComponentStatus :: Existing , InsertMode :: Replace ) => {
439
449
column. replace (
440
450
table_row,
441
451
component_ptr,
@@ -444,6 +454,9 @@ impl BundleInfo {
444
454
caller,
445
455
) ;
446
456
}
457
+ ( ComponentStatus :: Existing , InsertMode :: Keep ) => {
458
+ column. drop ( component_ptr) ;
459
+ }
447
460
}
448
461
}
449
462
StorageType :: SparseSet => {
@@ -489,7 +502,7 @@ impl BundleInfo {
489
502
let current_archetype = & mut archetypes[ archetype_id] ;
490
503
for component_id in self . component_ids . iter ( ) . cloned ( ) {
491
504
if current_archetype. contains ( component_id) {
492
- bundle_status. push ( ComponentStatus :: Mutated ) ;
505
+ bundle_status. push ( ComponentStatus :: Existing ) ;
493
506
mutated. push ( component_id) ;
494
507
} else {
495
508
bundle_status. push ( ComponentStatus :: Added ) ;
@@ -692,6 +705,7 @@ impl<'w> BundleInserter<'w> {
692
705
entity : Entity ,
693
706
location : EntityLocation ,
694
707
bundle : T ,
708
+ insert_mode : InsertMode ,
695
709
#[ cfg( feature = "track_change_detection" ) ] caller : & ' static core:: panic:: Location < ' static > ,
696
710
) -> EntityLocation {
697
711
let bundle_info = self . bundle_info . as_ref ( ) ;
@@ -705,13 +719,15 @@ impl<'w> BundleInserter<'w> {
705
719
// SAFETY: Mutable references do not alias and will be dropped after this block
706
720
let mut deferred_world = self . world . into_deferred ( ) ;
707
721
708
- deferred_world. trigger_on_replace (
709
- archetype,
710
- entity,
711
- add_bundle. mutated . iter ( ) . copied ( ) ,
712
- ) ;
713
- if archetype. has_replace_observer ( ) {
714
- deferred_world. trigger_observers ( ON_REPLACE , entity, & add_bundle. mutated ) ;
722
+ if insert_mode == InsertMode :: Replace {
723
+ deferred_world. trigger_on_replace (
724
+ archetype,
725
+ entity,
726
+ add_bundle. existing . iter ( ) . copied ( ) ,
727
+ ) ;
728
+ if archetype. has_replace_observer ( ) {
729
+ deferred_world. trigger_observers ( ON_REPLACE , entity, & add_bundle. existing ) ;
730
+ }
715
731
}
716
732
}
717
733
@@ -735,6 +751,7 @@ impl<'w> BundleInserter<'w> {
735
751
location. table_row ,
736
752
self . change_tick ,
737
753
bundle,
754
+ insert_mode,
738
755
#[ cfg( feature = "track_change_detection" ) ]
739
756
caller,
740
757
) ;
@@ -775,6 +792,7 @@ impl<'w> BundleInserter<'w> {
775
792
result. table_row ,
776
793
self . change_tick ,
777
794
bundle,
795
+ insert_mode,
778
796
#[ cfg( feature = "track_change_detection" ) ]
779
797
caller,
780
798
) ;
@@ -856,6 +874,7 @@ impl<'w> BundleInserter<'w> {
856
874
move_result. new_row ,
857
875
self . change_tick ,
858
876
bundle,
877
+ insert_mode,
859
878
#[ cfg( feature = "track_change_detection" ) ]
860
879
caller,
861
880
) ;
@@ -875,9 +894,34 @@ impl<'w> BundleInserter<'w> {
875
894
if new_archetype. has_add_observer ( ) {
876
895
deferred_world. trigger_observers ( ON_ADD , entity, & add_bundle. added ) ;
877
896
}
878
- deferred_world. trigger_on_insert ( new_archetype, entity, bundle_info. iter_components ( ) ) ;
879
- if new_archetype. has_insert_observer ( ) {
880
- deferred_world. trigger_observers ( ON_INSERT , entity, bundle_info. components ( ) ) ;
897
+ match insert_mode {
898
+ InsertMode :: Replace => {
899
+ // insert triggers for both new and existing components if we're replacing them
900
+ deferred_world. trigger_on_insert (
901
+ new_archetype,
902
+ entity,
903
+ bundle_info. iter_components ( ) ,
904
+ ) ;
905
+ if new_archetype. has_insert_observer ( ) {
906
+ deferred_world. trigger_observers (
907
+ ON_INSERT ,
908
+ entity,
909
+ bundle_info. components ( ) ,
910
+ ) ;
911
+ }
912
+ }
913
+ InsertMode :: Keep => {
914
+ // insert triggers only for new components if we're not replacing them (since
915
+ // nothing is actually inserted).
916
+ deferred_world. trigger_on_insert (
917
+ new_archetype,
918
+ entity,
919
+ add_bundle. added . iter ( ) . cloned ( ) ,
920
+ ) ;
921
+ if new_archetype. has_insert_observer ( ) {
922
+ deferred_world. trigger_observers ( ON_INSERT , entity, & add_bundle. added ) ;
923
+ }
924
+ }
881
925
}
882
926
}
883
927
@@ -977,6 +1021,7 @@ impl<'w> BundleSpawner<'w> {
977
1021
table_row,
978
1022
self . change_tick ,
979
1023
bundle,
1024
+ InsertMode :: Replace ,
980
1025
#[ cfg( feature = "track_change_detection" ) ]
981
1026
caller,
982
1027
) ;
@@ -1230,6 +1275,9 @@ mod tests {
1230
1275
#[ derive( Component ) ]
1231
1276
struct D ;
1232
1277
1278
+ #[ derive( Component , Eq , PartialEq , Debug ) ]
1279
+ struct V ( & ' static str ) ; // component with a value
1280
+
1233
1281
#[ derive( Resource , Default ) ]
1234
1282
struct R ( usize ) ;
1235
1283
@@ -1302,6 +1350,7 @@ mod tests {
1302
1350
world. init_resource :: < R > ( ) ;
1303
1351
let mut entity = world. entity_mut ( entity) ;
1304
1352
entity. insert ( A ) ;
1353
+ entity. insert_if_new ( A ) ; // this will not trigger on_replace or on_insert
1305
1354
entity. flush ( ) ;
1306
1355
assert_eq ! ( 2 , world. resource:: <R >( ) . 0 ) ;
1307
1356
}
@@ -1371,4 +1420,18 @@ mod tests {
1371
1420
world. spawn ( A ) . flush ( ) ;
1372
1421
assert_eq ! ( 4 , world. resource:: <R >( ) . 0 ) ;
1373
1422
}
1423
+
1424
+ #[ test]
1425
+ fn insert_if_new ( ) {
1426
+ let mut world = World :: new ( ) ;
1427
+ let id = world. spawn ( V ( "one" ) ) . id ( ) ;
1428
+ let mut entity = world. entity_mut ( id) ;
1429
+ entity. insert_if_new ( V ( "two" ) ) ;
1430
+ entity. insert_if_new ( ( A , V ( "three" ) ) ) ;
1431
+ entity. flush ( ) ;
1432
+ // should still contain "one"
1433
+ let entity = world. entity ( id) ;
1434
+ assert ! ( entity. contains:: <A >( ) ) ;
1435
+ assert_eq ! ( entity. get( ) , Some ( & V ( "one" ) ) ) ;
1436
+ }
1374
1437
}
0 commit comments