bevy/crates/bevy_ecs/src/relationship/relationship_source_collection.rs
databasedav 289c51b547 fix .insert_related index bound (#19134)
# Objective

resolves #19092

## Solution

- remove the `.saturating_sub` from the index transformation
- add `.saturating_add` to the internal offset calculation

## Testing

- added regression test, confirming 0 index order + testing max bound
2025-05-30 22:59:09 +02:00

587 lines
17 KiB
Rust

use crate::entity::{hash_set::EntityHashSet, Entity};
use alloc::vec::Vec;
use smallvec::SmallVec;
/// The internal [`Entity`] collection used by a [`RelationshipTarget`](crate::relationship::RelationshipTarget) component.
/// This is not intended to be modified directly by users, as it could invalidate the correctness of relationships.
pub trait RelationshipSourceCollection {
/// The type of iterator returned by the `iter` method.
///
/// This is an associated type (rather than using a method that returns an opaque return-position impl trait)
/// to ensure that all methods and traits (like [`DoubleEndedIterator`]) of the underlying collection's iterator
/// are available to the user when implemented without unduly restricting the possible collections.
///
/// The [`SourceIter`](super::SourceIter) type alias can be helpful to reduce confusion when working with this associated type.
type SourceIter<'a>: Iterator<Item = Entity>
where
Self: 'a;
/// Creates a new empty instance.
fn new() -> Self;
/// Returns an instance with the given pre-allocated entity `capacity`.
///
/// Some collections will ignore the provided `capacity` and return a default instance.
fn with_capacity(capacity: usize) -> Self;
/// Reserves capacity for at least `additional` more entities to be inserted.
///
/// Not all collections support this operation, in which case it is a no-op.
fn reserve(&mut self, additional: usize);
/// Adds the given `entity` to the collection.
///
/// Returns whether the entity was added to the collection.
/// Mainly useful when dealing with collections that don't allow
/// multiple instances of the same entity ([`EntityHashSet`]).
fn add(&mut self, entity: Entity) -> bool;
/// Removes the given `entity` from the collection.
///
/// Returns whether the collection actually contained
/// the entity.
fn remove(&mut self, entity: Entity) -> bool;
/// Iterates all entities in the collection.
fn iter(&self) -> Self::SourceIter<'_>;
/// Returns the current length of the collection.
fn len(&self) -> usize;
/// Clears the collection.
fn clear(&mut self);
/// Attempts to save memory by shrinking the capacity to fit the current length.
///
/// This operation is a no-op for collections that do not support it.
fn shrink_to_fit(&mut self);
/// Returns true if the collection contains no entities.
#[inline]
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Add multiple entities to collection at once.
///
/// May be faster than repeatedly calling [`Self::add`].
fn extend_from_iter(&mut self, entities: impl IntoIterator<Item = Entity>) {
// The method name shouldn't conflict with `Extend::extend` as it's in the rust prelude and
// would always conflict with it.
for entity in entities {
self.add(entity);
}
}
}
/// This trait signals that a [`RelationshipSourceCollection`] is ordered.
pub trait OrderedRelationshipSourceCollection: RelationshipSourceCollection {
/// Inserts the entity at a specific index.
/// If the index is too large, the entity will be added to the end of the collection.
fn insert(&mut self, index: usize, entity: Entity);
/// Removes the entity at the specified idnex if it exists.
fn remove_at(&mut self, index: usize) -> Option<Entity>;
/// Inserts the entity at a specific index.
/// This will never reorder other entities.
/// If the index is too large, the entity will be added to the end of the collection.
fn insert_stable(&mut self, index: usize, entity: Entity);
/// Removes the entity at the specified idnex if it exists.
/// This will never reorder other entities.
fn remove_at_stable(&mut self, index: usize) -> Option<Entity>;
/// Sorts the source collection.
fn sort(&mut self);
/// Inserts the entity at the proper place to maintain sorting.
fn insert_sorted(&mut self, entity: Entity);
/// This places the most recently added entity at the particular index.
fn place_most_recent(&mut self, index: usize);
/// This places the given entity at the particular index.
/// This will do nothing if the entity is not in the collection.
/// If the index is out of bounds, this will put the entity at the end.
fn place(&mut self, entity: Entity, index: usize);
/// Adds the entity at index 0.
fn push_front(&mut self, entity: Entity) {
self.insert(0, entity);
}
/// Adds the entity to the back of the collection.
fn push_back(&mut self, entity: Entity) {
self.insert(usize::MAX, entity);
}
/// Removes the first entity.
fn pop_front(&mut self) -> Option<Entity> {
self.remove_at(0)
}
/// Removes the last entity.
fn pop_back(&mut self) -> Option<Entity> {
if self.is_empty() {
None
} else {
self.remove_at(self.len() - 1)
}
}
}
impl RelationshipSourceCollection for Vec<Entity> {
type SourceIter<'a> = core::iter::Copied<core::slice::Iter<'a, Entity>>;
fn new() -> Self {
Vec::new()
}
fn reserve(&mut self, additional: usize) {
Vec::reserve(self, additional);
}
fn with_capacity(capacity: usize) -> Self {
Vec::with_capacity(capacity)
}
fn add(&mut self, entity: Entity) -> bool {
Vec::push(self, entity);
true
}
fn remove(&mut self, entity: Entity) -> bool {
if let Some(index) = <[Entity]>::iter(self).position(|e| *e == entity) {
Vec::remove(self, index);
return true;
}
false
}
fn iter(&self) -> Self::SourceIter<'_> {
<[Entity]>::iter(self).copied()
}
fn len(&self) -> usize {
Vec::len(self)
}
fn clear(&mut self) {
self.clear();
}
fn shrink_to_fit(&mut self) {
Vec::shrink_to_fit(self);
}
fn extend_from_iter(&mut self, entities: impl IntoIterator<Item = Entity>) {
self.extend(entities);
}
}
impl OrderedRelationshipSourceCollection for Vec<Entity> {
fn insert(&mut self, index: usize, entity: Entity) {
self.push(entity);
let len = self.len();
if index < len {
self.swap(index, len - 1);
}
}
fn remove_at(&mut self, index: usize) -> Option<Entity> {
(index < self.len()).then(|| self.swap_remove(index))
}
fn insert_stable(&mut self, index: usize, entity: Entity) {
if index < self.len() {
Vec::insert(self, index, entity);
} else {
self.push(entity);
}
}
fn remove_at_stable(&mut self, index: usize) -> Option<Entity> {
(index < self.len()).then(|| self.remove(index))
}
fn sort(&mut self) {
self.sort_unstable();
}
fn insert_sorted(&mut self, entity: Entity) {
let index = self.partition_point(|e| e <= &entity);
self.insert_stable(index, entity);
}
fn place_most_recent(&mut self, index: usize) {
if let Some(entity) = self.pop() {
let index = index.min(self.len());
self.insert(index, entity);
}
}
fn place(&mut self, entity: Entity, index: usize) {
if let Some(current) = <[Entity]>::iter(self).position(|e| *e == entity) {
let index = index.min(self.len());
Vec::remove(self, current);
self.insert(index, entity);
};
}
}
impl RelationshipSourceCollection for EntityHashSet {
type SourceIter<'a> = core::iter::Copied<crate::entity::hash_set::Iter<'a>>;
fn new() -> Self {
EntityHashSet::new()
}
fn reserve(&mut self, additional: usize) {
self.0.reserve(additional);
}
fn with_capacity(capacity: usize) -> Self {
EntityHashSet::with_capacity(capacity)
}
fn add(&mut self, entity: Entity) -> bool {
self.insert(entity)
}
fn remove(&mut self, entity: Entity) -> bool {
// We need to call the remove method on the underlying hash set,
// which takes its argument by reference
self.0.remove(&entity)
}
fn iter(&self) -> Self::SourceIter<'_> {
self.iter().copied()
}
fn len(&self) -> usize {
self.len()
}
fn clear(&mut self) {
self.0.clear();
}
fn shrink_to_fit(&mut self) {
self.0.shrink_to_fit();
}
fn extend_from_iter(&mut self, entities: impl IntoIterator<Item = Entity>) {
self.extend(entities);
}
}
impl<const N: usize> RelationshipSourceCollection for SmallVec<[Entity; N]> {
type SourceIter<'a> = core::iter::Copied<core::slice::Iter<'a, Entity>>;
fn new() -> Self {
SmallVec::new()
}
fn reserve(&mut self, additional: usize) {
SmallVec::reserve(self, additional);
}
fn with_capacity(capacity: usize) -> Self {
SmallVec::with_capacity(capacity)
}
fn add(&mut self, entity: Entity) -> bool {
SmallVec::push(self, entity);
true
}
fn remove(&mut self, entity: Entity) -> bool {
if let Some(index) = <[Entity]>::iter(self).position(|e| *e == entity) {
SmallVec::remove(self, index);
return true;
}
false
}
fn iter(&self) -> Self::SourceIter<'_> {
<[Entity]>::iter(self).copied()
}
fn len(&self) -> usize {
SmallVec::len(self)
}
fn clear(&mut self) {
self.clear();
}
fn shrink_to_fit(&mut self) {
SmallVec::shrink_to_fit(self);
}
fn extend_from_iter(&mut self, entities: impl IntoIterator<Item = Entity>) {
self.extend(entities);
}
}
impl RelationshipSourceCollection for Entity {
type SourceIter<'a> = core::option::IntoIter<Entity>;
fn new() -> Self {
Entity::PLACEHOLDER
}
fn reserve(&mut self, _: usize) {}
fn with_capacity(_capacity: usize) -> Self {
Self::new()
}
fn add(&mut self, entity: Entity) -> bool {
assert_eq!(
*self,
Entity::PLACEHOLDER,
"Entity {entity} attempted to target an entity with a one-to-one relationship, but it is already targeted by {}. You must remove the original relationship first.",
*self
);
*self = entity;
true
}
fn remove(&mut self, entity: Entity) -> bool {
if *self == entity {
*self = Entity::PLACEHOLDER;
return true;
}
false
}
fn iter(&self) -> Self::SourceIter<'_> {
if *self == Entity::PLACEHOLDER {
None.into_iter()
} else {
Some(*self).into_iter()
}
}
fn len(&self) -> usize {
if *self == Entity::PLACEHOLDER {
return 0;
}
1
}
fn clear(&mut self) {
*self = Entity::PLACEHOLDER;
}
fn shrink_to_fit(&mut self) {}
fn extend_from_iter(&mut self, entities: impl IntoIterator<Item = Entity>) {
for entity in entities {
assert_eq!(
*self,
Entity::PLACEHOLDER,
"Entity {entity} attempted to target an entity with a one-to-one relationship, but it is already targeted by {}. You must remove the original relationship first.",
*self
);
*self = entity;
}
}
}
impl<const N: usize> OrderedRelationshipSourceCollection for SmallVec<[Entity; N]> {
fn insert(&mut self, index: usize, entity: Entity) {
self.push(entity);
let len = self.len();
if index < len {
self.swap(index, len - 1);
}
}
fn remove_at(&mut self, index: usize) -> Option<Entity> {
(index < self.len()).then(|| self.swap_remove(index))
}
fn insert_stable(&mut self, index: usize, entity: Entity) {
if index < self.len() {
SmallVec::<[Entity; N]>::insert(self, index, entity);
} else {
self.push(entity);
}
}
fn remove_at_stable(&mut self, index: usize) -> Option<Entity> {
(index < self.len()).then(|| self.remove(index))
}
fn sort(&mut self) {
self.sort_unstable();
}
fn insert_sorted(&mut self, entity: Entity) {
let index = self.partition_point(|e| e <= &entity);
self.insert_stable(index, entity);
}
fn place_most_recent(&mut self, index: usize) {
if let Some(entity) = self.pop() {
let index = index.min(self.len() - 1);
self.insert(index, entity);
}
}
fn place(&mut self, entity: Entity, index: usize) {
if let Some(current) = <[Entity]>::iter(self).position(|e| *e == entity) {
// The len is at least 1, so the subtraction is safe.
let index = index.min(self.len() - 1);
SmallVec::<[Entity; N]>::remove(self, current);
self.insert(index, entity);
};
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::{Component, World};
use crate::relationship::RelationshipTarget;
#[test]
fn vec_relationship_source_collection() {
#[derive(Component)]
#[relationship(relationship_target = RelTarget)]
struct Rel(Entity);
#[derive(Component)]
#[relationship_target(relationship = Rel, linked_spawn)]
struct RelTarget(Vec<Entity>);
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
world.entity_mut(a).insert(Rel(b));
let rel_target = world.get::<RelTarget>(b).unwrap();
let collection = rel_target.collection();
assert_eq!(collection, &alloc::vec!(a));
}
#[test]
fn smallvec_relationship_source_collection() {
#[derive(Component)]
#[relationship(relationship_target = RelTarget)]
struct Rel(Entity);
#[derive(Component)]
#[relationship_target(relationship = Rel, linked_spawn)]
struct RelTarget(SmallVec<[Entity; 4]>);
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
world.entity_mut(a).insert(Rel(b));
let rel_target = world.get::<RelTarget>(b).unwrap();
let collection = rel_target.collection();
assert_eq!(collection, &SmallVec::from_buf([a]));
}
#[test]
fn entity_relationship_source_collection() {
#[derive(Component)]
#[relationship(relationship_target = RelTarget)]
struct Rel(Entity);
#[derive(Component)]
#[relationship_target(relationship = Rel)]
struct RelTarget(Entity);
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
world.entity_mut(a).insert(Rel(b));
let rel_target = world.get::<RelTarget>(b).unwrap();
let collection = rel_target.collection();
assert_eq!(collection, &a);
}
#[test]
fn one_to_one_relationships() {
#[derive(Component)]
#[relationship(relationship_target = Below)]
struct Above(Entity);
#[derive(Component)]
#[relationship_target(relationship = Above)]
struct Below(Entity);
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
world.entity_mut(a).insert(Above(b));
assert_eq!(a, world.get::<Below>(b).unwrap().0);
// Verify removing target removes relationship
world.entity_mut(b).remove::<Below>();
assert!(world.get::<Above>(a).is_none());
// Verify removing relationship removes target
world.entity_mut(a).insert(Above(b));
world.entity_mut(a).remove::<Above>();
assert!(world.get::<Below>(b).is_none());
// Actually - a is above c now! Verify relationship was updated correctly
let c = world.spawn_empty().id();
world.entity_mut(a).insert(Above(c));
assert!(world.get::<Below>(b).is_none());
assert_eq!(a, world.get::<Below>(c).unwrap().0);
}
#[test]
#[should_panic]
fn one_to_one_relationship_shared_target() {
#[derive(Component)]
#[relationship(relationship_target = Below)]
struct Above(Entity);
#[derive(Component)]
#[relationship_target(relationship = Above)]
struct Below(Entity);
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
let c = world.spawn_empty().id();
world.entity_mut(a).insert(Above(c));
world.entity_mut(b).insert(Above(c));
}
#[test]
fn one_to_one_relationship_reinsert() {
#[derive(Component)]
#[relationship(relationship_target = Below)]
struct Above(Entity);
#[derive(Component)]
#[relationship_target(relationship = Above)]
struct Below(Entity);
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
world.entity_mut(a).insert(Above(b));
world.entity_mut(a).insert(Above(b));
}
}