bevyengine / bevyengine/bevy

One to one relations not enforced

Open
#18,941 4 comments 2 reactions 0 assignees View on GitHub
A-ECS C-Bug D-Straightforward S-Needs-Investigation
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.