Panic on overlapping one-to-one relationships (#18833)
# Objective One to one relationships (added in https://github.com/bevyengine/bevy/pull/18087) can currently easily be invalidated by having two entities relate to the same target. Alternative to #18817 (removing one-to-one relationships) ## Solution Panic if a RelationshipTarget is already targeted. Thanks @urben1680 for the idea! --------- Co-authored-by: François Mockers <mockersf@gmail.com>
This commit is contained in:
parent
fc7705c0a6
commit
15ac36f6d5
@ -326,7 +326,7 @@ impl<const N: usize> RelationshipSourceCollection for SmallVec<[Entity; N]> {
|
||||
}
|
||||
|
||||
impl RelationshipSourceCollection for Entity {
|
||||
type SourceIter<'a> = core::iter::Once<Entity>;
|
||||
type SourceIter<'a> = core::option::IntoIter<Entity>;
|
||||
|
||||
fn new() -> Self {
|
||||
Entity::PLACEHOLDER
|
||||
@ -339,6 +339,12 @@ impl RelationshipSourceCollection for Entity {
|
||||
}
|
||||
|
||||
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
|
||||
@ -355,7 +361,11 @@ impl RelationshipSourceCollection for Entity {
|
||||
}
|
||||
|
||||
fn iter(&self) -> Self::SourceIter<'_> {
|
||||
core::iter::once(*self)
|
||||
if *self == Entity::PLACEHOLDER {
|
||||
None.into_iter()
|
||||
} else {
|
||||
Some(*self).into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
@ -372,7 +382,13 @@ impl RelationshipSourceCollection for Entity {
|
||||
fn shrink_to_fit(&mut self) {}
|
||||
|
||||
fn extend_from_iter(&mut self, entities: impl IntoIterator<Item = Entity>) {
|
||||
if let Some(entity) = entities.into_iter().last() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -530,4 +546,42 @@ mod tests {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user