Skip to content

Component Removal Actions for Required Component Enforcement #18514

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

Closed
6 tasks
ElliottjPierce opened this issue Mar 24, 2025 · 3 comments
Closed
6 tasks

Component Removal Actions for Required Component Enforcement #18514

ElliottjPierce opened this issue Mar 24, 2025 · 3 comments
Labels
A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible X-Controversial There is active debate or serious implications around merging this PR

Comments

@ElliottjPierce
Copy link
Contributor

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

Currently, required components are not enforced. If A requires B, and A is spawned on an entity, the entity has both A and B, but a user could just remove B, leaving A without its requirement. This is intended behavior, but in cases where A can not function without B, it may be useful to make the requirement an invariant. As I understand it, this is the case for lots of rendering components, and this invariant has to be manually checked. If we make the ECS enforce this, the extra work to check can be removed, leading to rendering performance improvements. Other performance improvements may be possible. For example Transform would no longer need to make sure it has GlobalTransform, etc. In the long run, we may even be able to use this to improve Query performance.

What solution would you like?

I have done some experimenting and have created a functional prototype of the feature. (See here.)

However, after working on it, I think it would be better to generalize, and open up these "removal actions" to be applicable to more than just requirements.

#[derive(Component, Default)]
#[removal(also(B), when(C), without(D), stops(E)]
struct A;

In this example, also states that when A is removed, B is also removed, when states that when C is removed, A is removed, without states that A is only removed when D is not present, and stops states that when A is present, E can not be removed.
Note that also and when are inverses, and without and stops are inverses. We could also include other options, for example, panics_with, etc.

These removal actions can be registered similarly to but separate from required components. In my prototype, I tied these directly to required components, but that is both limiting and problematic. For example, if A requires B which requires C, and A wants to enforce an invariant with its indirect requirement of C without overwriting the B's constructor for C , this is impossible without separating required components from removal actions. This separation could result in other uses for removal actions, but the primary use would still be to enforce invariants.

When creating BundleInfo, we collect these removal actions, and during component removal, we just iterate a new removed_components instead of explicit_components.

What alternative(s) have you considered?

This could sort of be done via hooks, but that would be slower than adding direct support for it, and it would make reflexive actions more difficult. In the above example, also would be easy, but when would require accessing C's hooks, which could have conflicts, etc.

We could make such a thing as a RemovalGroup, where Bundle deals with what is inserted/spawned, but RemovalGroup deals with what is removed/taken. This could be a nice generalization, but I think this refactor is kinda tangential to this.

Roadmap

  • Refactor to use a BundleRemover this has been a todo item in the ECS for a while, and the refactor will make this feature easier to implement.
  • Implement removal actions also and when.
  • Provide removal action registration to the World for plugins.
  • Implement removal actions derive macro.
  • Implement without and stops.
  • Maybe implement panics_with and causes_panic_of if desired.
@ElliottjPierce ElliottjPierce added C-Feature A new feature, making something new possible S-Needs-Triage This issue needs to be labelled labels Mar 24, 2025
@ItsDoot
Copy link
Contributor

ItsDoot commented Mar 24, 2025

Related: #1481

@brianreavis
Copy link
Contributor

#17976 would benefit from components like GlobalTransform and ViewVisibility being unremovable. Having certain components flagged with #[removal(disallowed)] would be useful (i.e. not relying on some other component to drive the behavior)

@alice-i-cecile
Copy link
Member

I think that #18566 describes my preferred flavor of solution better: I'm going to close this out to collect discussion into a single spot.

@alice-i-cecile alice-i-cecile added A-ECS Entities, components, systems, and events X-Controversial There is active debate or serious implications around merging this PR and removed S-Needs-Triage This issue needs to be labelled labels Mar 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

No branches or pull requests

4 participants