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 {
|
impl RelationshipSourceCollection for Entity {
|
||||||
type SourceIter<'a> = core::iter::Once<Entity>;
|
type SourceIter<'a> = core::option::IntoIter<Entity>;
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Entity::PLACEHOLDER
|
Entity::PLACEHOLDER
|
||||||
@ -339,6 +339,12 @@ impl RelationshipSourceCollection for Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add(&mut self, entity: Entity) -> bool {
|
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;
|
*self = entity;
|
||||||
|
|
||||||
true
|
true
|
||||||
@ -355,7 +361,11 @@ impl RelationshipSourceCollection for Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn iter(&self) -> Self::SourceIter<'_> {
|
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 {
|
fn len(&self) -> usize {
|
||||||
@ -372,7 +382,13 @@ impl RelationshipSourceCollection for Entity {
|
|||||||
fn shrink_to_fit(&mut self) {}
|
fn shrink_to_fit(&mut self) {}
|
||||||
|
|
||||||
fn extend_from_iter(&mut self, entities: impl IntoIterator<Item = Entity>) {
|
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;
|
*self = entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -530,4 +546,42 @@ mod tests {
|
|||||||
assert!(world.get::<Below>(b).is_none());
|
assert!(world.get::<Below>(b).is_none());
|
||||||
assert_eq!(a, world.get::<Below>(c).unwrap().0);
|
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