bevyengine / bevyengine/bevy

EntityRef::get_ref should use the change ticks of the System (if it is called from within one)

Open
#13,735 8 comments 0 reactions 0 assignees View on GitHub
A-ECS C-Bug C-Usability D-Complex S-Ready-For-Implementation X-Uncontroversial
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

## What problem does this solve or what need does it fill?

1) When you query a `Ref`, the `last_run` tick inside `Ref` is populated from the last time the system has run. So `Ref::is_changed()` tells you if the component has changed since the last time the system has run.
2) This is also the case when you call `EntityRef::get_ref::` from within an exclusive system.
(probably because of https://docs.rs/bevy/latest/bevy/ecs/world/struct.World.html#method.last_change_tick

> When called from within an exclusive system (a [System](https://docs.rs/bevy/latest/bevy/ecs/system/trait.System.html) that takes &mut World as its first parameter), this method returns the [Tick](https://docs.rs/bevy/latest/bevy/ecs/component/struct.Tick.html) indicating the last time the exclusive system was run.
Otherwise, this returns the Tick indicating the last time that [World::clear_trackers](https://docs.rs/bevy/latest/bevy/ecs/world/struct.World.html#method.clear_trackers) was called.

3) But when you call `EntityRef::get_ref::` outside of an exclusive system, the `last_run` tick is from the last time `World::clear_trackers` was called (which is called in every `Last` schedule).

I think this is fairly misleading, as users are taught to expect that `Ref::is_added()` and `Ref::is_changed()` will return true if the component was added/changed since **the last time the system ran**.

(in my use case, I was calling `EntityRef::get_ref` from a system that uses the `&World` SystemParam, so I was in case 3) )

Some code that shows the current behaviour:
```rust
#[test]
fn test_ref() {
let mut world = World::new();
world.init_component::();

let entity = world.spawn(A).id();
let update_a = world.register_system(move |world: &mut World | {
world.entity_mut(entity).get_mut::
().unwrap().set_changed();
});

let check_change_detection_via_entity_ref = world.register_system(move |world: &World| {
let a_ref = world.entity(entity).get_ref::
().unwrap();
// dbg!(&a_ref.ticks);
dbg!("via entity_ref: ", a_ref.is_changed());
});
let check_change_detection_via_entity_ref_in_exclusive_system = world.register_system(move |world: &mut World| {
let a_ref = world.entity(entity).get_ref::
().unwrap();
// dbg!(&a_ref.ticks);
dbg!("via entity_ref in exclusive system: ", a_ref.is_changed());
});
let check_change_detection_via_query = world.register_system(|query: Query>| {
let a_ref = query.single();
dbg!("via query: ", a_ref.is_changed());
});

// all are true
world.run_system(check_change_detection_via_query).unwrap();
world.run_system(check_change_detection_via_entity_ref).unwrap();
world.run_system(check_change_detection_via_entity_ref_in_exclusive_system).unwrap();

// we update A
world.run_system(update_a).unwrap();
// we clear the world trackers
world.clear_trackers();

// is_changed = true (Ref uses the ticks since the last time the system ran)
world.run_system(check_change_detection_via_query).unwrap();
// is_changed = true (entity_ref.get_ref uses the ticks since the last time the system ran, when inside
// an exclusive system)
world.run_system(check_change_detection_via_entity_ref_in_exclusive_system).unwrap();
// is_changed = true (entity_ref.get_ref uses the ticks since the last world.clear_trackers(), when not
// inside an exclusive system)
world.run_system(check_change_detection_via_entity_ref).unwrap();
}
```

## What solution would you like?

When calling `entity_ref::get_ref()` from within a system, the `last_run` `Tick` of `Ref` is populated from the last time the system has run.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.