One to one relations not enforced
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 171
Description
## Bevy version
0.16
## What you did
Seems like manually inserting a relationship target breaks the 1:1 invariant. Here's a minimum repro:
```rs
use bevy::prelude::*;
fn main() {
let mut world = World::new();
// spawn two cells
let a = world.spawn(()).id();
let b = world.spawn(()).id();
// spawn a monster in cell a
let monster = world.spawn(Occupying(a)).id();
// move monster to cell b
world.entity_mut(b).insert(OccupiedBy(monster));
// print cells and who occupies them
for (e, occupied_by) in world.query::<(Entity, &OccupiedBy)>().iter(&world) {
println!("Entity {:?} is occupied by {:?}", e, occupied_by.0);
}
// print monsters and where they are
for (e, occupying) in world.query::<(Entity, &Occupying)>().iter(&world) {
println!("Entity {:?} is occupying {:?}", e, occupying.0);
}
}
#[derive(Component)]
#[relationship(relationship_target = OccupiedBy)]
struct Occupying(Entity);
#[derive(Component)]
#[relationship_target(relationship = Occupying)]
struct OccupiedBy(Entity);
```
## What went wrong
I'd expect that inserting `OccupiedBy(monster)` onto `b` would remove `monster`'s old relation to `a` before inserting the new one, but this prints:
```
Entity 0v1#4294967296 is occupied by 2v1#4294967298
Entity 1v1#4294967297 is occupied by 2v1#4294967298
Entity 2v1#4294967298 is occupying 0v1#4294967296
```
0v1 and 1v1 are cells `a` and `b`, and 2v1 is `monster`. You can see two cells think they're occupied by `monster`, violating the invariant.
## Additional information
Mildly confused by this because I thought there was a change that would mean we panic before breaking the invariant, but I must've either misunderstood when we panic or else this is a different issue completely. I have not yet done any investigation into the reason for the unexpected behavior.
Contributor guide
Research direction
Start by running the Rust minimum reproduction against Bevy 0.16 and trace relationship insertion for Occupying and OccupiedBy. Identify why inserting the new target leaves the old target attached, then add a regression test showing that a one-to-one relationship has only one target after reassignment.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- game-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100