-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Split EntityClonerBuilder
in OptOut
and OptIn
variants
#19649
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?
Conversation
release-content/migration-guides/entity_cloner_builder_split.md
Outdated
Show resolved
Hide resolved
release-content/migration-guides/entity_cloner_builder_split.md
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that giving up on handling required components is a very reasonable choice.
I quite like this PR: this is well-designed after quite a bit of back-and-forth, presents a clear API, and has excellent docs, tests and benchmarks. The builder pattern with a generic for the type state is a bit fancy, but clear enough, and this isn't a high enough traffic beginner API that I'm going to complain.
One last minute idea, while it makes no sense to include required components for
... what about denying components required by this component? When you don't want I could also add an equivalent to It would fit the "symmetry" here but it also sounds like a footgun to indirectly not clone "higher" components that, because of technicality, might be unknown to the API user. But then again, if that can be the case the user should not deny a required component in the first place. 🤔 I currently lean into implementing this idea with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The duplication of various APIs due to the split is a bit unfortunate, but I think it should be fine overall.
One last minute idea
I think this does make sense since components that require other components expect them to be present on the entity to function. That being said, I'm not sure how useful that would actually be in practice.
Co-authored-by: eugineerd <[email protected]>
Co-authored-by: eugineerd <[email protected]>
Co-authored-by: eugineerd <[email protected]>
Co-authored-by: eugineerd <[email protected]>
Co-authored-by: eugineerd <[email protected]>
…red-by behavior for OptOut filter
I fixed the bug, added missing benches and introduced the new "backward" behavior of required components for the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think everything should be good now, only needs some minor code consolidation changes.
As an aside, not related to this PR, just something I though about while reviewing the code and will probably experiment with later:
EntityClonerBuilder
uses&mut self
style build since I wanted the closure configuration style api forCommands
andwithout_required_components
, however now I feel likeself
-based builder would've actually been better.clone_entity
on the builder was never intended to be called multiple times and is implemented this way just to allow chaining style api. Usingself
-based builder would allow us to remove the*_opt_in
/*_opt_out
split at theCommands
level and just require the user to pass in their configuredEntityCloner
. Not sure if this will be more ergonomic, but maybe?*_by_ids
/*_by_type_ids
/*_by_bundle_id
resulted in a combinatorial explosion due to the addition of*_if_new
. I think it makes sense to introduce a trait similar toWorldEntityFetch
for ids that will be implemented forComponentId
,TypeId
andBundleId
as well as theirIntoIter
versions.
I agree, for reusing the cloner the builder should be
That is a good idea as well. I agree both fit more into a follow-up though. This PR is already not very concrete in it's task. Thank you for helping me improving these changes. ❤️ |
…680/bevy into EntityClonerBuilder-split
Objective
Further tests after #19326 showed that configuring
EntityCloner
with required components is bug prone and the current design has several weaknesses in it's API:EntityClonerBuilder::allow
andEntityClonerBuilder::deny
requires extra care how to support that which has an impact on surrounding code that has to keep edge cases in mind. This is especially true for attempts to fix the following issues. There is no use-case known (to me) why someone would mix those.EntityClonerBuilder::allow_all
configuration tries to support required components likeEntityClonerBuilder::deny_all
does, but the meaning of that is conflicting with how you'd expect things to work:A
, do you also want to exclude required components ofA
too? Or are these also valid withoutA
at the target entity?EntityClonerBuilder::allow_all
should ignore required components and not add them to be filtered away, which purpose hasEntityClonerBuilder::without_required_components
for this cloner?A
also denies required components ofA
even whenA
does not exist at the source entityA
also allows required components ofA
even whenA
does not exist at the source entityallow_if_new
filters to the cloner faces the same issues and require a common solution to dealing with source-archetype sensitive cloningAlternative to #19632 and #19635.
Solution
EntityClonerBuilder
is made generic and split intoEntityClonerBuilder<OptOut>
andEntityClonerBuilder<OptIn>
For an overview of the changes, see the migration guide. It is generally a good idea to start a review of that.
Algorithm
The generic of
EntityClonerBuilder
contains the filter data that is needed to build and clone the entity components.As the filter needs to be borrowed mutably for the duration of the clone, the borrow checker forced me to separate the filter value and all other fields in
EntityCloner
. The latter are now in theEntityClonerConfig
struct. This caused many changed LOC, sorry.To make reviewing easier:
EntityCloner
now just call identitcalEntityClonerConfig
methods with a mutable borrow of the filterEntityClonerConfig::clone_entity_internal
which changed a bit regarding the filter usage that is now trait powered (CloneByFilter
) to supportOptOut
,OptIn
andEntityClonerFilter
(an enum combining the first two)OptOut
type that no longer tracks required components but has ainsert_mode
fieldOptIn
type that has the most logic changesTesting
I added a bunch of tests that cover the new logic parts and the fixed issues.
Benchmarks are in a comment a bit below which shows ~4% to 9% regressions, but it varied wildly for me. For example at one run the reflection-based clonings were on-par with main while the other are not, and redoing that swapped the situation for both.
It would be really cool if I could get some hints how to get better benchmark results or if you could run them on your machine too.
Just be aware this is not a Performance PR but a Bugfix PR, even if I smuggled in some more functionalities. So doing changes to
EntityClonerBuilder
is kind of required here which might make us bite the bullet.