Skip to content
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

Make the MapEntities trait generic over Mappers, and add a simpler EntityMapper #11428

Merged
merged 22 commits into from
Jan 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 35 additions & 5 deletions crates/bevy_ecs/src/entity/map_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use bevy_utils::EntityHashMap;
///
/// ```
/// use bevy_ecs::prelude::*;
/// use bevy_ecs::entity::{EntityMapper, MapEntities};
/// use bevy_ecs::entity::{EntityMapper, MapEntities, SimpleEntityMapper};
///
/// #[derive(Component)]
/// struct Spring {
Expand All @@ -27,9 +27,13 @@ use bevy_utils::EntityHashMap;
/// }
///
/// impl MapEntities for Spring {
/// fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
/// self.a = entity_mapper.get_or_reserve(self.a);
/// self.b = entity_mapper.get_or_reserve(self.b);
/// fn map_or_gen_entities(&mut self, entity_mapper: &mut EntityMapper) {
/// self.a.map_or_gen_entities(entity_mapper);
/// self.b.map_or_gen_entities(entity_mapper);
/// }
/// fn map_entities(&mut self, entity_mapper: &mut SimpleEntityMapper) {
/// self.a.map_entities(entity_mapper);
/// self.b.map_entities(entity_mapper)
/// }
/// }
/// ```
Expand All @@ -39,7 +43,33 @@ pub trait MapEntities {
///
/// Implementors should look up any and all [`Entity`] values stored within and
cBournhonesque marked this conversation as resolved.
Show resolved Hide resolved
/// update them to the mapped values via `entity_mapper`.
fn map_entities(&mut self, entity_mapper: &mut EntityMapper);
fn map_or_gen_entities(&mut self, entity_mapper: &mut EntityMapper);

/// Updates all [`Entity`] references stored inside using `entity_mapper`.
///
/// Only updates the references for which there is a mapping.
fn map_entities(&mut self, entity_mapper: &mut SimpleEntityMapper);
cBournhonesque marked this conversation as resolved.
Show resolved Hide resolved
}

/// Similar to `EntityMapper`, but does not allocate new [`Entity`] references in case we couldn't map the entity.
cBournhonesque marked this conversation as resolved.
Show resolved Hide resolved
pub struct SimpleEntityMapper<'m> {
Copy link
Contributor

@Shatur Shatur Jan 24, 2024

Choose a reason for hiding this comment

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

It's never used inside Bevy, I would remove it and left its implementation to users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hm maybe

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I'm mostly not sure on why you would want this behavior. If that can be clearly demonstrated with docs (and tested) I'd be fine to ship it.

Copy link
Contributor Author

@cBournhonesque cBournhonesque Jan 26, 2024

Choose a reason for hiding this comment

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

That's the behaviour I'd prefer to have for networking, as I don't want/need the complexities associated with the existing EntityMapper. (Having to use EntityMapper::world_scope() and needing access to &mut World)
I will remove this from the PR as users can implement it themselves

Copy link
Member

Choose a reason for hiding this comment

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

Sounds good. I'd happily accept a PR to add it with docs, but I agree, it can be split out from this.

cBournhonesque marked this conversation as resolved.
Show resolved Hide resolved
map: &'m EntityHashMap<Entity, Entity>,
}

impl<'m> SimpleEntityMapper<'m> {
pub fn new(map: &'m EntityHashMap<Entity, Entity>) -> Self {
Self { map }
}

/// Returns the corresponding mapped entity or None if it is absent.
cBournhonesque marked this conversation as resolved.
Show resolved Hide resolved
pub fn get(&mut self, entity: Entity) -> Option<Entity> {
self.map.get(&entity).copied()
}
cBournhonesque marked this conversation as resolved.
Show resolved Hide resolved

/// Gets a reference to the underlying [`EntityHashMap<Entity, Entity>`].
pub fn get_map(&'m self) -> &'m EntityHashMap<Entity, Entity> {
self.map
}
}

/// A wrapper for [`EntityHashMap<Entity, Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination
Expand Down
12 changes: 12 additions & 0 deletions crates/bevy_ecs/src/entity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ pub struct Entity {
index: u32,
}

impl MapEntities for Entity {
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
fn map_or_gen_entities(&mut self, entity_mapper: &mut EntityMapper) {
*self = entity_mapper.get_or_reserve(*self);
}

fn map_entities(&mut self, entity_mapper: &mut SimpleEntityMapper) {
if let Some(mapped) = entity_mapper.get(*self) {
*self = mapped;
}
}
}

// By not short-circuiting in comparisons, we get better codegen.
// See <https://github.com/rust-lang/rust/issues/117800>
impl PartialEq for Entity {
Expand Down
20 changes: 10 additions & 10 deletions crates/bevy_ecs/src/reflect/map_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use bevy_utils::EntityHashMap;
/// See [`MapEntities`] for more information.
cBournhonesque marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Clone)]
pub struct ReflectMapEntities {
map_all_entities: fn(&mut World, &mut EntityMapper),
map_entities: fn(&mut World, &mut EntityMapper, &[Entity]),
map_or_gen_all_entities: fn(&mut World, &mut EntityMapper),
map_or_gen_entities: fn(&mut World, &mut EntityMapper, &[Entity]),
}

impl ReflectMapEntities {
Expand All @@ -27,12 +27,12 @@ impl ReflectMapEntities {
/// An example of this: A scene can be loaded with `Parent` components, but then a `Parent` component can be added
/// to these entities after they have been loaded. If you reload the scene using [`map_all_entities`](Self::map_all_entities), those `Parent`
/// components with already valid entity references could be updated to point at something else entirely.
pub fn map_all_entities(
pub fn map_or_gen_all_entities(
&self,
world: &mut World,
entity_map: &mut EntityHashMap<Entity, Entity>,
) {
EntityMapper::world_scope(entity_map, world, self.map_all_entities);
EntityMapper::world_scope(entity_map, world, self.map_or_gen_all_entities);
}

/// A general method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap<Entity, Entity>`]. Unlike
Expand All @@ -41,37 +41,37 @@ impl ReflectMapEntities {
///
/// This is useful mostly for when you need to be careful not to update components that already contain valid entity
/// values. See [`map_all_entities`](Self::map_all_entities) for more details.
pub fn map_entities(
pub fn map_or_gen_entities(
&self,
world: &mut World,
entity_map: &mut EntityHashMap<Entity, Entity>,
entities: &[Entity],
) {
EntityMapper::world_scope(entity_map, world, |world, mapper| {
(self.map_entities)(world, mapper, entities);
(self.map_or_gen_entities)(world, mapper, entities);
});
}
}

impl<C: Component + MapEntities> FromType<C> for ReflectMapEntities {
fn from_type() -> Self {
ReflectMapEntities {
map_entities: |world, entity_mapper, entities| {
map_or_gen_entities: |world, entity_mapper, entities| {
for &entity in entities {
if let Some(mut component) = world.get_mut::<C>(entity) {
component.map_entities(entity_mapper);
component.map_or_gen_entities(entity_mapper);
}
}
},
map_all_entities: |world, entity_mapper| {
map_or_gen_all_entities: |world, entity_mapper| {
let entities = entity_mapper
.get_map()
.values()
.copied()
.collect::<Vec<Entity>>();
for entity in &entities {
if let Some(mut component) = world.get_mut::<C>(*entity) {
component.map_entities(entity_mapper);
component.map_or_gen_entities(entity_mapper);
}
}
},
Expand Down
11 changes: 9 additions & 2 deletions crates/bevy_hierarchy/src/components/children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use bevy_ecs::{
use bevy_utils::smallvec::SmallVec;
use core::slice;
use std::ops::Deref;
use bevy_ecs::entity::SimpleEntityMapper;

/// Contains references to the child entities of this entity.
///
Expand All @@ -29,9 +30,15 @@ use std::ops::Deref;
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);

impl MapEntities for Children {
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
fn map_or_gen_entities(&mut self, entity_mapper: &mut EntityMapper) {
for entity in &mut self.0 {
*entity = entity_mapper.get_or_reserve(*entity);
entity.map_or_gen_entities(entity_mapper);
}
}

fn map_entities(&mut self, entity_mapper: &mut SimpleEntityMapper) {
for entity in &mut self.0 {
entity.map_entities(entity_mapper);
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions crates/bevy_hierarchy/src/components/parent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use bevy_ecs::{
world::{FromWorld, World},
};
use std::ops::Deref;
use bevy_ecs::entity::SimpleEntityMapper;

/// Holds a reference to the parent entity of this entity.
/// This component should only be present on entities that actually have a parent entity.
Expand Down Expand Up @@ -56,8 +57,12 @@ impl FromWorld for Parent {
}

impl MapEntities for Parent {
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
self.0 = entity_mapper.get_or_reserve(self.0);
fn map_or_gen_entities(&mut self, entity_mapper: &mut EntityMapper) {
self.0.map_or_gen_entities(entity_mapper);
}

fn map_entities(&mut self, entity_mapper: &mut SimpleEntityMapper) {
self.0.map_entities(entity_mapper);
}
}

Expand Down
11 changes: 9 additions & 2 deletions crates/bevy_render/src/mesh/mesh/skinning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use bevy_ecs::{
use bevy_math::Mat4;
use bevy_reflect::{Reflect, TypePath};
use std::ops::Deref;
use bevy_ecs::entity::SimpleEntityMapper;

#[derive(Component, Debug, Default, Clone, Reflect)]
#[reflect(Component, MapEntities)]
Expand All @@ -17,9 +18,15 @@ pub struct SkinnedMesh {
}

impl MapEntities for SkinnedMesh {
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
fn map_or_gen_entities(&mut self, entity_mapper: &mut EntityMapper) {
for joint in &mut self.joints {
*joint = entity_mapper.get_or_reserve(*joint);
*joint.map_or_gen_entities(entity_mapper);
}
}

fn map_entities(&mut self, entity_mapper: &mut SimpleEntityMapper) {
for joint in &mut self.joints {
*joint.map_entities(entity_mapper);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_scene/src/dynamic_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl DynamicScene {
"we should be getting TypeId from this TypeRegistration in the first place",
);
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
map_entities_reflect.map_entities(world, entity_map, &entities);
map_entities_reflect.map_or_gen_entities(world, entity_map, &entities);
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_scene/src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl Scene {

for registration in type_registry.iter() {
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
map_entities_reflect.map_all_entities(world, &mut instance_info.entity_map);
map_entities_reflect.map_or_gen_all_entities(world, &mut instance_info.entity_map);
}
}

Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_scene/src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ mod tests {
use crate::ron;
use crate::serde::{SceneDeserializer, SceneSerializer};
use crate::{DynamicScene, DynamicSceneBuilder};
use bevy_ecs::entity::{Entity, EntityMapper, MapEntities};
use bevy_ecs::entity::{Entity, EntityMapper, MapEntities, SimpleEntityMapper};
use bevy_ecs::prelude::{Component, ReflectComponent, ReflectResource, Resource, World};
use bevy_ecs::query::{With, Without};
use bevy_ecs::reflect::{AppTypeRegistry, ReflectMapEntities};
Expand Down Expand Up @@ -550,8 +550,12 @@ mod tests {
struct MyEntityRef(Entity);

impl MapEntities for MyEntityRef {
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
self.0 = entity_mapper.get_or_reserve(self.0);
fn map_or_gen_entities(&mut self, entity_mapper: &mut EntityMapper) {
self.0.map_or_gen_entities(entity_mapper);
}

fn map_entities(&mut self, entity_mapper: &mut SimpleEntityMapper) {
self.0.map_entities(entity_mapper);
}
}

Expand Down
14 changes: 12 additions & 2 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use bevy_ecs::{
entity::{Entity, EntityMapper, MapEntities},
prelude::{Component, ReflectComponent},
};
use bevy_ecs::entity::SimpleEntityMapper;
use bevy_math::{DVec2, IVec2, Vec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};

Expand Down Expand Up @@ -59,10 +60,19 @@ impl WindowRef {
}

impl MapEntities for WindowRef {
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
fn map_or_gen_entities(&mut self, entity_mapper: &mut EntityMapper) {
match self {
Self::Entity(entity) => {
*entity = entity_mapper.get_or_reserve(*entity);
*entity.map_entities(entity_mapper);
}
Self::Primary => {}
};
}

fn map_entities(&mut self, entity_mapper: &mut SimpleEntityMapper) {
match self {
Self::Entity(entity) => {
*entity.map_or_gen_entities(entity_mapper);
}
Self::Primary => {}
};
Expand Down
Loading