Open alice-i-cecile opened 6 months ago
Returning an actual &mut World
would be unsound even with any form of entity disabling, as it still allows you to do structural changes that might affect the disabled entity. And there's also the extreme case where the World
is replaced (i.e. by doing *world = World::new();
)
Returning an actual
&mut World
would be unsound even with any form of entity disabling
how about instead of having access to the whole entity (EntityMut
) - you just get access to a set of components?
let _ = world.entity_mut(entity).component_scope::<(Position, Health), _>(|world, (p, h)| {
// blabla
}).unwrap(); // panics if either Position or Health doesn't exist
this can almost be done already with EntityWorldMut::take
+ EntityWorldMut::insert
but it triggers Added
(bad) and it's surprisingly easy to gorget to insert the component back afterwards.
An approach similar to EntityWorldMut::take
+ EntityWorldMut::insert
could work, but I don't see a way to optimize it to avoid the archetype move, which is pretty costly.
Returning an actual
&mut World
would be unsound even with any form of entity disabling, as it still allows you to do structural changes that might affect the disabled entity. And there's also the extreme case where theWorld
is replaced (i.e. by doing*world = World::new();
)
What if we return a &mut DeferredWorld
, since it doesn't allow structural changes?
What if we return a
&mut DeferredWorld
, since it doesn't allow structural changes?
I think that might work, but it will likely still be pretty tricky to do correctly, especially if you want to recursively call entity_scope
on another entity (since it seems the main ideas for how to implement entity_scope
involve doing structural changes)
What problem does this solve or what need does it fill?
When working with avatar-centric games (like platformers or FPS games), one object (the player avatar) is typically wildly more complex and important than others. Writing exclusive systems that fetch "the player" and operate on it in a game-object style can be an effective programming pattern.
In order to make this work, we often want to access that highly important entity, and relate it to "everything else around it". The current most obvious pattern is to fetch that data, clone or remove it out of the world, and then operate on it.
What solution would you like?
Add
World::entity_scope
, which works likeresource_scope
, but returns anEntityMut
instead of a resource, in addition to a reference to the rest of the world.This could be done by removing and then re-adding the entity but:
Entity
id of the object should remain unchanged.A strong form of entity disabling (#11090), which makes it fully impossible to access the data of the object would be a clean way to do this, but would have more significant architectural implications. This would also come with less caveats: being able to add children to the requested entity without immediately panicking would be great.
What alternative(s) have you considered?
An
EntityWorldMut
would be strictly more powerful, and makes more conceptual sense: being able to freely add and remove components would be really useful. However, actually providing that access is fundamentally unsound with the archetypal ECS that Bevy uses: changing this data requires changes to the metadata of the component storage itself, and requires a true hard lock.Users could manually implement this pattern, by despawning and then spawning the player object, but without a way to clone entities (#1515) or otherwise extract all of their component, actually doing so is tedious.
Furthermore, any existing references to that entity (such as
Parent
components!) will break, since theEntity
ID will be changed. Change detection will also be triggered on every component, effectively rendering it useless for their game object style entity.Additional context
This is a conceptual sister to https://github.com/bevyengine/bevy/issues/13127: both are in the service of making the "get and work with a single entity in a loosely typed way" workflow easier.
Performance is always nice, but is a secondary concern for this style of API.
An equivalent
hierarchy_scope
, which extracts a whole tree of entities, is probably useful down the line as well: player objects are often conceptually composed of complete trees in order to support complex animated models.