Skip to content

Conversation

ElliottjPierce
Copy link
Contributor

Objective

This is a followup to #18704 . There's lots more followup work, but this is the minimum to unblock #18670, etc.

This direction has been given the green light by Alice here.

Solution

I could have split this over multiple PRs, but I figured skipping straight here would be easiest for everyone and would unblock things the quickest.

This removes the now no longer needed identifier module and makes Entity::generation go from NonZeroU32 to struct EntityGeneration(u32).

Testing

CI

@ElliottjPierce ElliottjPierce added A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 7, 2025
Copy link
Contributor

@bushrat011899 bushrat011899 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly just some nitpicking. The changes look good and I love that this PR demonstrates the rolling updates we can do to migration guides.


/// Returns the [`EntityGeneration`] that would result from this many more `versions` of the corresponding [`EntityRow`] from passing.
#[inline]
pub const fn after_versions(self, versions: u32) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future PR: might be nice to implement Iterator<Item = Self> for EntityGeneration. That gives an idiomatic next method.

@atlv24 atlv24 added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 7, 2025
Copy link
Contributor

@manokara manokara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noticed some typos in the migration guide

ElliottjPierce and others added 2 commits May 7, 2025 22:11
@ElliottjPierce
Copy link
Contributor Author

Benchmarks looks good, sparse set change detection was the only thing that was slowed down by anything notable. But I don't think that's a big deal. Other than that, I think this is generally an improvement in perf. (Less checks when changing the generation.)

Also want to point out that we loose a niche on EntityMeta here, but it can be added back shortly (via TableRow and ArchetypeRow). And I wanted to call out the big stability warning on Entity which gives me more confidence that this breaking change should be ok.

@alice-i-cecile alice-i-cecile added this pull request to the merge queue May 8, 2025
Merged via the queue into bevyengine:main with commit 12aba64 May 8, 2025
32 checks passed
andrewzhurov pushed a commit to andrewzhurov/bevy that referenced this pull request May 17, 2025
…9121)

# Objective

This is a followup to bevyengine#18704 . There's lots more followup work, but this
is the minimum to unblock bevyengine#18670, etc.

This direction has been given the green light by Alice
[here](bevyengine#18704 (comment)).

## Solution

I could have split this over multiple PRs, but I figured skipping
straight here would be easiest for everyone and would unblock things the
quickest.

This removes the now no longer needed `identifier` module and makes
`Entity::generation` go from `NonZeroU32` to `struct
EntityGeneration(u32)`.

## Testing

CI

---------

Co-authored-by: Mark Nokalt <[email protected]>
github-merge-queue bot pushed a commit that referenced this pull request May 29, 2025
# Objective

Recently the `u32` `Entity::generation` was replaced with the new
`EntityGeneration` in #19121.
This made meanings a lot more clear, and prevented accidental misuse.

One common misuse was assuming that `u32`s that were greater than others
came after those others.
Wrapping makes this assumption false.
When `EntityGeneration` was created, it retained the `u32` ordering,
which was useless at best and wrong at worst.
This pr fixes the ordering implementation, so new generations are
greater than older generations.

Some users were already accounting for this ordering issue (which was
still present in 0.16 and before) by manually accessing the `u32`
representation. This made migrating difficult for avian physics; see
[here](https://discord.com/channels/691052431525675048/749335865876021248/1377431569228103780).

I am generally of the opinion that this type should be kept opaque to
prevent accidental misuse.
As we find issues like this, the functionality should be added to
`EntityGeneration` directly.

## Solution

Fix the ordering implementation through `Ord`.

Alternatively, we could keep `Ord` the same and make a `cmp_age` method,
but I think this is better, even though sorting entity ids may be
*marginally* slower now (but more correct). This is a tradeoff.

## Testing

I improved documentation for aliasing and ordering, adding some doc
tests.
Shatur added a commit to simgine/bevy that referenced this pull request Sep 27, 2025
Otherwise creation with index = 1 and generation = 1 looks like this:
```rust
let expected_entity = Entity::from_bits((1 ^ u32::MAX) as u64 | (1 << 32));
```

I also removed outdated comment which left untouched after the
generation rework (bevyengine#19121)
Shatur added a commit to simgine/bevy_replicon that referenced this pull request Sep 28, 2025
For details see bevyengine/bevy#19121
bevyengine/bevy#18704

The niche is now in the index, which makes the compression logic even
simpler.

The index now represented by NonMaxU32, which internally represented as
NonZeroU32 with all bits reversed, so we have to xor the bits.

I opened a PR to make it more ergonomic and avoid us relying on the
internal layout: bevyengine/bevy#21246
github-merge-queue bot pushed a commit that referenced this pull request Sep 28, 2025
# Objective

Entity serialization is necessary for networking. Entities can exist
inside components and events. After deserialization, we simply map
remote entities to local entities.

To serialize entities efficiently, we split them into index and
generation, which benefits from varint serialization. #19121 and #18704
changed the entity layout, and I like the new layout a lot. We can now
use the extra bit from the index to store whether the generation is zero
or not, avoiding the need to serialize the generation entirely.

However, constructing new entities requires relying on the internal
layout, which is not very ergonomic. For example, here is how an entity
with index = 1 and generation = 1 can be created:

```rust
let expected_entity = Entity::from_bits((1 ^ u32::MAX) as u64 | (1 << 32));
```

## Solution

- Make `Entity::from_raw_and_generation` public. While at it, I also
removed outdated comment.
- Add `EntityRow::from_raw_u32` to make the initialization nicer.

## Testing

- It's a trivial change, but I re-used `EntityRow::from_raw_u32` in unit
tests to simplify them.

## Notes

I'd probably rename `Entity::from_raw_and_generation` into
`Entity::from_row_and_generation` or
`Entity::from_index_and_generation`.
github-merge-queue bot pushed a commit that referenced this pull request Sep 28, 2025
# Objective

I think "raw" is a leftover from old Entity methods where a raw u32
value was accepted. Since we pass a _row_ now, I think it worth
renaming.

## Solution

- Adjusted a single letter in 2 methods 😅 
- While at it, adjusted the comment about generation since it can be
zero since #19121.
mockersf pushed a commit that referenced this pull request Sep 28, 2025
# Objective

Entity serialization is necessary for networking. Entities can exist
inside components and events. After deserialization, we simply map
remote entities to local entities.

To serialize entities efficiently, we split them into index and
generation, which benefits from varint serialization. #19121 and #18704
changed the entity layout, and I like the new layout a lot. We can now
use the extra bit from the index to store whether the generation is zero
or not, avoiding the need to serialize the generation entirely.

However, constructing new entities requires relying on the internal
layout, which is not very ergonomic. For example, here is how an entity
with index = 1 and generation = 1 can be created:

```rust
let expected_entity = Entity::from_bits((1 ^ u32::MAX) as u64 | (1 << 32));
```

## Solution

- Make `Entity::from_raw_and_generation` public. While at it, I also
removed outdated comment.
- Add `EntityRow::from_raw_u32` to make the initialization nicer.

## Testing

- It's a trivial change, but I re-used `EntityRow::from_raw_u32` in unit
tests to simplify them.

## Notes

I'd probably rename `Entity::from_raw_and_generation` into
`Entity::from_row_and_generation` or
`Entity::from_index_and_generation`.
mockersf pushed a commit that referenced this pull request Sep 28, 2025
# Objective

I think "raw" is a leftover from old Entity methods where a raw u32
value was accepted. Since we pass a _row_ now, I think it worth
renaming.

## Solution

- Adjusted a single letter in 2 methods 😅 
- While at it, adjusted the comment about generation since it can be
zero since #19121.
mockersf pushed a commit that referenced this pull request Sep 28, 2025
# Objective

Entity serialization is necessary for networking. Entities can exist
inside components and events. After deserialization, we simply map
remote entities to local entities.

To serialize entities efficiently, we split them into index and
generation, which benefits from varint serialization. #19121 and #18704
changed the entity layout, and I like the new layout a lot. We can now
use the extra bit from the index to store whether the generation is zero
or not, avoiding the need to serialize the generation entirely.

However, constructing new entities requires relying on the internal
layout, which is not very ergonomic. For example, here is how an entity
with index = 1 and generation = 1 can be created:

```rust
let expected_entity = Entity::from_bits((1 ^ u32::MAX) as u64 | (1 << 32));
```

## Solution

- Make `Entity::from_raw_and_generation` public. While at it, I also
removed outdated comment.
- Add `EntityRow::from_raw_u32` to make the initialization nicer.

## Testing

- It's a trivial change, but I re-used `EntityRow::from_raw_u32` in unit
tests to simplify them.

## Notes

I'd probably rename `Entity::from_raw_and_generation` into
`Entity::from_row_and_generation` or
`Entity::from_index_and_generation`.
mockersf pushed a commit that referenced this pull request Sep 28, 2025
# Objective

I think "raw" is a leftover from old Entity methods where a raw u32
value was accepted. Since we pass a _row_ now, I think it worth
renaming.

## Solution

- Adjusted a single letter in 2 methods 😅 
- While at it, adjusted the comment about generation since it can be
zero since #19121.
Shatur added a commit to simgine/bevy_replicon that referenced this pull request Sep 30, 2025
For details see bevyengine/bevy#19121
bevyengine/bevy#18704

The niche is now in the index, which makes the compression logic even
simpler.

The index now represented by NonMaxU32, which internally represented as
NonZeroU32 with all bits reversed, so we have to xor the bits.

I opened a PR to make it more ergonomic and avoid us relying on the
internal layout: bevyengine/bevy#21246
Shatur added a commit to simgine/bevy_replicon that referenced this pull request Oct 1, 2025
For details see bevyengine/bevy#19121
bevyengine/bevy#18704

The niche is now in the index, which makes the compression logic even
simpler.
Shatur added a commit to simgine/bevy_replicon that referenced this pull request Oct 1, 2025
For details see bevyengine/bevy#19121
bevyengine/bevy#18704

The niche is now in the index, which makes the compression logic even
simpler.
Shatur added a commit to simgine/bevy_replicon that referenced this pull request Oct 2, 2025
For details see bevyengine/bevy#19121
bevyengine/bevy#18704

The niche is now in the index, which makes the compression logic even
simpler.
Shatur added a commit to simgine/bevy_replicon that referenced this pull request Oct 3, 2025
* Bump Bevy version
* Migrate to the new Entity layout
For details see bevyengine/bevy#19121
bevyengine/bevy#18704
The niche is now in the index, which makes the compression logic even
simpler.
* Migrate to the new `SystemParam` changes
For details see bevyengine/bevy#16885
bevyengine/bevy#19143
* Remove `*_trigger_targets`
* Simplify fns logic
We no longer need custom sed/de to additionally serialize targets. This
allows us to express things a bit nicer using conversion traits.
* Rename all "event" into "message".
* Rename all "trigger" into "event".
* Rename "resend locally" into just "send locally"
Fits better.
* Split channel methods

---------

Co-authored-by: UkoeHB <[email protected]>
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-Code-Quality A section of code that is hard to understand or change S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants