diff --git a/crates/bevy_ecs/hecs/src/archetype.rs b/crates/bevy_ecs/hecs/src/archetype.rs index 32889d301a..2a80ad3076 100644 --- a/crates/bevy_ecs/hecs/src/archetype.rs +++ b/crates/bevy_ecs/hecs/src/archetype.rs @@ -406,11 +406,15 @@ impl Archetype { size: usize, index: usize, added: bool, + mutated: bool, ) { let state = self.state.get_mut(&ty).unwrap(); if added { state.added_entities[index] = true; } + if mutated { + state.mutated_entities[index] = true; + } let ptr = (*self.data.get()) .as_ptr() .add(state.offset + size * index) diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index b192a1974d..0e66fe6982 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -900,24 +900,24 @@ mod tests { } } - fn get_changed_a(world: &mut World) -> Vec { + fn get_mutated_a(world: &mut World) -> Vec { world .query_mut::<(Mutated, Entity)>() .map(|(_a, e)| e) .collect::>() }; - assert_eq!(get_changed_a(&mut world), vec![e1, e3]); + assert_eq!(get_mutated_a(&mut world), vec![e1, e3]); // ensure changing an entity's archetypes also moves its mutated state world.insert(e1, (C,)).unwrap(); - assert_eq!(get_changed_a(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); + assert_eq!(get_mutated_a(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); // spawning a new A entity should not change existing mutated state world.insert(e1, (A(0), B)).unwrap(); assert_eq!( - get_changed_a(&mut world), + get_mutated_a(&mut world), vec![e3, e1], "changed entities list should not change" ); @@ -925,7 +925,7 @@ mod tests { // removing an unchanged entity should not change mutated state world.despawn(e2).unwrap(); assert_eq!( - get_changed_a(&mut world), + get_mutated_a(&mut world), vec![e3, e1], "changed entities list should not change" ); @@ -933,7 +933,7 @@ mod tests { // removing a changed entity should remove it from enumeration world.despawn(e1).unwrap(); assert_eq!( - get_changed_a(&mut world), + get_mutated_a(&mut world), vec![e3], "e1 should no longer be returned" ); @@ -943,8 +943,46 @@ mod tests { assert!(world .query_mut::<(Mutated, Entity)>() .map(|(_a, e)| e) - .collect::>() - .is_empty()); + .next() + .is_none()); + + let e4 = world.spawn(()); + + world.insert_one(e4, A(0)).unwrap(); + assert!(get_mutated_a(&mut world).is_empty()); + + world.insert_one(e4, A(1)).unwrap(); + assert_eq!(get_mutated_a(&mut world), vec![e4]); + + world.clear_trackers(); + + // ensure inserting multiple components set mutated state for + // already existing components and set added state for + // non existing components even when changing archetype. + world.insert(e4, (A(0), B(0))).unwrap(); + + let added_a = world + .query::<(Added, Entity)>() + .iter() + .map(|(_, e)| e) + .next(); + assert!(added_a.is_none()); + + assert_eq!(get_mutated_a(&mut world), vec![e4]); + + let added_b = world + .query::<(Added, Entity)>() + .iter() + .map(|(_, e)| e) + .next(); + assert!(added_b.is_some()); + + let mutated_b = world + .query_mut::<(Mutated, Entity)>() + .iter() + .map(|(_, e)| e) + .next(); + assert!(mutated_b.is_none()); } #[test] @@ -962,11 +1000,11 @@ mod tests { b.0 += 1; } - let a_b_changed = world + let a_b_mutated = world .query_mut::<(Mutated, Mutated, Entity)>() .map(|(_a, _b, e)| e) .collect::>(); - assert_eq!(a_b_changed, vec![e2]); + assert_eq!(a_b_mutated, vec![e2]); } #[test] @@ -986,12 +1024,12 @@ mod tests { b.0 += 1; } - let a_b_changed = world + let a_b_mutated = world .query_mut::<(Or<(Mutated, Mutated)>, Entity)>() .map(|((_a, _b), e)| e) .collect::>(); // e1 has mutated A, e3 has mutated B, e2 has mutated A and B, _e4 has no mutated component - assert_eq!(a_b_changed, vec![e1, e2, e3]); + assert_eq!(a_b_mutated, vec![e1, e2, e3]); } #[test] diff --git a/crates/bevy_ecs/hecs/src/world.rs b/crates/bevy_ecs/hecs/src/world.rs index 3b314d64ed..f2bed53072 100644 --- a/crates/bevy_ecs/hecs/src/world.rs +++ b/crates/bevy_ecs/hecs/src/world.rs @@ -102,7 +102,7 @@ impl World { unsafe { let index = archetype.allocate(entity); components.put(|ptr, ty, size| { - archetype.put_dynamic(ptr, ty, size, index, true); + archetype.put_dynamic(ptr, ty, size, index, true, false); true }); self.entities.meta[entity.id as usize].location = Location { @@ -530,7 +530,7 @@ impl World { // Update components in the current archetype let arch = &mut self.archetypes[loc.archetype as usize]; components.put(|ptr, ty, size| { - arch.put_dynamic(ptr, ty, size, loc.index, false); + arch.put_dynamic(ptr, ty, size, loc.index, false, true); true }); return Ok(()); @@ -547,7 +547,7 @@ impl World { let old_index = mem::replace(&mut loc.index, target_index); if let Some(moved) = source_arch.move_to(old_index, |ptr, ty, size, is_added, is_mutated| { - target_arch.put_dynamic(ptr, ty, size, target_index, false); + target_arch.put_dynamic(ptr, ty, size, target_index, false, false); let type_state = target_arch.get_type_state_mut(ty).unwrap(); *type_state.added().as_ptr().add(target_index) = is_added; *type_state.mutated().as_ptr().add(target_index) = is_mutated; @@ -557,7 +557,8 @@ impl World { } components.put(|ptr, ty, size| { - target_arch.put_dynamic(ptr, ty, size, target_index, true); + let had_component = source_arch.has_dynamic(ty); + target_arch.put_dynamic(ptr, ty, size, target_index, !had_component, had_component); true }); } @@ -1003,7 +1004,8 @@ where unsafe { let index = self.archetype.allocate(entity); components.put(|ptr, ty, size| { - self.archetype.put_dynamic(ptr, ty, size, index, true); + self.archetype + .put_dynamic(ptr, ty, size, index, true, false); true }); self.entities.meta[entity.id as usize].location = Location { diff --git a/crates/bevy_ecs/src/resource/resources.rs b/crates/bevy_ecs/src/resource/resources.rs index acec0d2f07..a912599e16 100644 --- a/crates/bevy_ecs/src/resource/resources.rs +++ b/crates/bevy_ecs/src/resource/resources.rs @@ -231,6 +231,7 @@ impl Resources { core::mem::size_of::(), index, added, + !added, ); std::mem::forget(resource); }