-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Entity scope v1: component scope #21430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Entity scope v1: component scope #21430
Conversation
It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note. Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes. |
617f6f4
to
609de31
Compare
Marked as X-Controversial due to the architectural implications, but I quite like the core strategy and really want this functionality. |
I have not looked very closely at the implementation yet, but here's a few thoughts. This is probably the best way to implement component scopes. In my own ecs engine, I've implemented something similar to the chunked tables, and it was very much not worth it. Removing, scoping, and re-adding a component is probably also too slow, would create useless or maybe even invalid (with archetype invariants) archetypes, and maybe cause hook issues. Either hooks will run, making the previous sate unrecoverable, or the scope will have an invalid world state–this is general form of the relationship problem you mentioned. Even in terms of the objective, I'm not convinced this is the right way to go. This is definitely not my decision, but what I've noticed is that these At a conceptual level, I also think this might be unsound. As an example, what if you scope access to a component value and then clear the world? Now the old copy has been dropped, but you still have a mutable reference to a copy of a now dropped component! That's a big deal if the component has a I know I said this is probably the best way to do component scopes, and I stand by that (unless you come up with something better 👀 ). All other ways of doing this that I can think of come with massive performance issues and correctness concerns. I think this is the most useful way of doing it; it just needs to be I'll try to find time to review the code later if possible. This is a really good idea to explore, but it is very complicated. |
Objective
related: #13128
With Resources-as-Entities, and possibly more *-as-Entities on the horizon, simultaneous mutable access to an entity and the world has and will come up more often:
resource_scope
, for example, needs mutable access to the resource component on the resource entity, and doesn't want the entity to be available for it's scope.Solution
I propose the following solution:
Entities can be 'hard' disabled by swapping them into a region at the beginning of their archetype/table and their meta is updated to contain no location.
These regions are skipped by queries and not dropped when the table is removed.
A
component_scope
ptr::read
s a component onto the stack, then disables the entity and calls the closure with a mutable reference to the stack copy of the component.After the closure finishes, the stack component is
ptr::write
n back into storage and the entity is enabled: during the scope the component in storage must not be dropped because the scope closure may decide to drop it itself.A
component_scope
limited to a single component is the simplest and easiest functionality to build here, because we cannot ensure that the table doesn't move the disabled components around or that the table even remains live for the duration of the scope, so we can't give out references to the ECS storage, and can't useWorldQuery
and related traits to easily retrieve multiple components: this will require some more trait magic ontop ofWorldQuery
, but I think it shouldn't be excessively complicated or unsound.Remaining Questions
The
component_scope
closure could be passedCommands
of some shape onto which to record operations related to the disabled entity.component_scopes
should not require a swap when re-enabling the entity, but different functionality build with disabling/enabling might.Testing
there are still lots of TODOs in this PR:
Alternatives
Tables might be made up of chunks in which rows are stable across table growth; an entity could be moved to a dedicated chunk, which can be owned by the
entity_scope
to be moved back into the table afterwards. This would probably have a significant performance impact, not to mention the architectural impact.Showcase