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

Replaced EntityMap with HashMap #9461

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
92 changes: 7 additions & 85 deletions crates/bevy_ecs/src/entity/map_entities.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{entity::Entity, world::World};
use bevy_utils::{Entry, HashMap};
use bevy_utils::HashMap;

/// Operation to map all contained [`Entity`] fields in a type to new values.
///
Expand Down Expand Up @@ -41,93 +41,15 @@ pub trait MapEntities {

/// A mapping from one set of entities to another.
///
/// The API generally follows [`HashMap`], but each [`Entity`] is returned by value, as they are [`Copy`].
///
/// This is typically used to coordinate data transfer between sets of entities, such as between a scene and the world
/// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse
/// identifiers directly.
///
/// On its own, an `EntityMap` is not capable of allocating new entity identifiers, which is needed to map references
/// to entities that lie outside the source entity set. To do this, an `EntityMap` can be wrapped in an
/// [`EntityMapper`] which scopes it to a particular destination [`World`] and allows new identifiers to be allocated.
/// This functionality can be accessed through [`Self::world_scope()`].
#[derive(Default, Debug)]
pub struct EntityMap {
map: HashMap<Entity, Entity>,
}

impl EntityMap {
/// Inserts an entities pair into the map.
///
/// If the map did not have `from` present, [`None`] is returned.
///
/// If the map did have `from` present, the value is updated, and the old value is returned.
pub fn insert(&mut self, from: Entity, to: Entity) -> Option<Entity> {
self.map.insert(from, to)
}

/// Removes an `entity` from the map, returning the mapped value of it if the `entity` was previously in the map.
pub fn remove(&mut self, entity: Entity) -> Option<Entity> {
self.map.remove(&entity)
}

/// Gets the given entity's corresponding entry in the map for in-place manipulation.
pub fn entry(&mut self, entity: Entity) -> Entry<'_, Entity, Entity> {
self.map.entry(entity)
}

/// Returns the corresponding mapped entity.
pub fn get(&self, entity: Entity) -> Option<Entity> {
self.map.get(&entity).copied()
}

/// An iterator visiting all keys in arbitrary order.
pub fn keys(&self) -> impl Iterator<Item = Entity> + '_ {
self.map.keys().cloned()
}

/// An iterator visiting all values in arbitrary order.
pub fn values(&self) -> impl Iterator<Item = Entity> + '_ {
self.map.values().cloned()
}

/// Returns the number of elements in the map.
pub fn len(&self) -> usize {
self.map.len()
}

/// Returns true if the map contains no elements.
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}

/// An iterator visiting all (key, value) pairs in arbitrary order.
pub fn iter(&self) -> impl Iterator<Item = (Entity, Entity)> + '_ {
self.map.iter().map(|(from, to)| (*from, *to))
}

/// Clears the map, removing all entity pairs. Keeps the allocated memory for reuse.
pub fn clear(&mut self) {
self.map.clear();
}

/// Creates an [`EntityMapper`] from this [`EntityMap`] and scoped to the provided [`World`], then calls the
/// provided function with it. This allows one to allocate new entity references in the provided `World` that are
/// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely
/// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called
/// within the scope of the passed world. Its return value is then returned from `world_scope` as the generic type
/// parameter `R`.
pub fn world_scope<R>(
&mut self,
world: &mut World,
f: impl FnOnce(&mut World, &mut EntityMapper) -> R,
) -> R {
let mut mapper = EntityMapper::new(self, world);
let result = f(world, &mut mapper);
mapper.finish(world);
result
}
}
/// This functionality can be accessed through [`World::world_scope()`].
pub type EntityMap = HashMap<Entity, Entity>;
bushrat011899 marked this conversation as resolved.
Show resolved Hide resolved

/// A wrapper for [`EntityMap`], augmenting it with the ability to allocate new [`Entity`] references in a destination
/// world. These newly allocated references are guaranteed to never point to any living entity in that world.
Expand All @@ -147,7 +69,7 @@ pub struct EntityMapper<'m> {
impl<'m> EntityMapper<'m> {
/// Returns the corresponding mapped entity or reserves a new dead entity ID if it is absent.
pub fn get_or_reserve(&mut self, entity: Entity) -> Entity {
if let Some(mapped) = self.map.get(entity) {
if let Some(&mapped) = self.map.get(&entity) {
return mapped;
}

Expand All @@ -174,7 +96,7 @@ impl<'m> EntityMapper<'m> {
}

/// Creates a new [`EntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]
fn new(map: &'m mut EntityMap, world: &mut World) -> Self {
pub(crate) fn new(map: &'m mut EntityMap, world: &mut World) -> Self {
Self {
map,
// SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope`
Expand All @@ -187,7 +109,7 @@ impl<'m> EntityMapper<'m> {
/// [`Entity`] while reserving extra generations via [`crate::entity::Entities::reserve_generations`]. Because this
/// renders the [`EntityMapper`] unable to safely allocate any more references, this method takes ownership of
/// `self` in order to render it unusable.
fn finish(self, world: &mut World) {
pub(crate) fn finish(self, world: &mut World) {
// SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope`
let entities = unsafe { world.entities_mut() };
assert!(entities.free(self.dead_start).is_some());
Expand Down Expand Up @@ -235,7 +157,7 @@ mod tests {
let mut map = EntityMap::default();
let mut world = World::new();

let dead_ref = map.world_scope(&mut world, |_, mapper| {
let dead_ref = world.world_scope(&mut map, |_, mapper| {
mapper.get_or_reserve(Entity::new(0, 0))
});

Expand Down
10 changes: 7 additions & 3 deletions crates/bevy_ecs/src/reflect/map_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl ReflectMapEntities {
/// 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(&self, world: &mut World, entity_map: &mut EntityMap) {
entity_map.world_scope(world, self.map_all_entities);
world.world_scope(entity_map, self.map_all_entities);
}

/// A general method for applying [`MapEntities`] behavior to elements in an [`EntityMap`]. Unlike
Expand All @@ -37,7 +37,7 @@ 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(&self, world: &mut World, entity_map: &mut EntityMap, entities: &[Entity]) {
entity_map.world_scope(world, |world, mapper| {
world.world_scope(entity_map, |world, mapper| {
(self.map_entities)(world, mapper, entities);
});
}
Expand All @@ -54,7 +54,11 @@ impl<C: Component + MapEntities> FromType<C> for ReflectMapEntities {
}
},
map_all_entities: |world, entity_mapper| {
let entities = entity_mapper.get_map().values().collect::<Vec<Entity>>();
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);
Expand Down
21 changes: 20 additions & 1 deletion crates/bevy_ecs/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use crate::{
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::{MutUntyped, TicksMut},
component::{Component, ComponentDescriptor, ComponentId, ComponentInfo, Components, Tick},
entity::{AllocAtWithoutReplacement, Entities, Entity, EntityLocation},
entity::{
AllocAtWithoutReplacement, Entities, Entity, EntityLocation, EntityMap, EntityMapper,
},
event::{Event, Events},
query::{DebugCheckedUnwrap, QueryState, ReadOnlyWorldQuery, WorldQuery},
removal_detection::RemovedComponentEvents,
Expand Down Expand Up @@ -1864,6 +1866,23 @@ impl World {
pub fn run_schedule(&mut self, label: impl AsRef<dyn ScheduleLabel>) {
self.schedule_scope(label, |world, sched| sched.run(world));
}

/// Creates an [`EntityMapper`] from this [`World`] and scoped to the provided [`EntityMap`], then calls the
/// provided function with it. This allows one to allocate new entity references in this `World` that are
/// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely
/// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called
/// within the scope of this world. Its return value is then returned from `world_scope` as the generic type
/// parameter `R`.
pub fn world_scope<R>(
bushrat011899 marked this conversation as resolved.
Show resolved Hide resolved
&mut self,
entity_map: &mut EntityMap,
f: impl FnOnce(&mut World, &mut EntityMapper) -> R,
) -> R {
let mut mapper = EntityMapper::new(entity_map, self);
let result = f(self, &mut mapper);
mapper.finish(self);
result
}
}

impl fmt::Debug for World {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_scene/src/dynamic_scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ mod tests {
let mut entity_map = EntityMap::default();
scene.write_to_world(&mut world, &mut entity_map).unwrap();

let from_scene_parent_entity = entity_map.get(original_parent_entity).unwrap();
let from_scene_child_entity = entity_map.get(original_child_entity).unwrap();
let &from_scene_parent_entity = entity_map.get(&original_parent_entity).unwrap();
let &from_scene_child_entity = entity_map.get(&original_child_entity).unwrap();

// We then add the parent from the scene as a child of the original child
// Hierarchy should look like:
Expand Down
5 changes: 3 additions & 2 deletions crates/bevy_scene/src/scene_spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl SceneSpawner {

pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) {
if let Some(instance) = self.spawned_instances.remove(instance_id) {
for entity in instance.entity_map.values() {
for &entity in instance.entity_map.values() {
let _ = world.despawn(entity);
}
}
Expand Down Expand Up @@ -277,7 +277,7 @@ impl SceneSpawner {

for (instance_id, parent) in scenes_with_parent {
if let Some(instance) = self.spawned_instances.get(&instance_id) {
for entity in instance.entity_map.values() {
for &entity in instance.entity_map.values() {
// Add the `Parent` component to the scene root, and update the `Children` component of
// the scene parent
if !world
Expand Down Expand Up @@ -322,6 +322,7 @@ impl SceneSpawner {
.map(|instance| instance.entity_map.values())
.into_iter()
.flatten()
.copied()
}
}

Expand Down