Path: blob/main/crates/bevy_ecs/src/entity/map_entities.rs
6849 views
pub use bevy_ecs_macros::MapEntities;1use indexmap::{IndexMap, IndexSet};23use crate::{4entity::{hash_map::EntityHashMap, Entity},5world::World,6};78use alloc::{9collections::{BTreeMap, BTreeSet, VecDeque},10vec::Vec,11};12use bevy_platform::collections::{HashMap, HashSet};13use core::{14hash::{BuildHasher, Hash},15mem,16};17use smallvec::SmallVec;1819use super::EntityIndexSet;2021/// Operation to map all contained [`Entity`] fields in a type to new values.22///23/// As entity IDs are valid only for the [`World`] they're sourced from, using [`Entity`]24/// as references in components copied from another world will be invalid. This trait25/// allows defining custom mappings for these references via [`EntityMappers`](EntityMapper), which26/// inject the entity mapping strategy between your `MapEntities` type and the current world27/// (usually by using an [`EntityHashMap<Entity>`] between source entities and entities in the28/// current world).29///30/// Components use [`Component::map_entities`](crate::component::Component::map_entities) to map31/// entities in the context of scenes and entity cloning, which generally uses [`MapEntities`] internally32/// to map each field (see those docs for usage).33///34/// [`HashSet<Entity>`]: bevy_platform::collections::HashSet35///36/// ## Example37///38/// ```39/// use bevy_ecs::prelude::*;40/// use bevy_ecs::entity::MapEntities;41///42/// #[derive(Component)]43/// struct Spring {44/// a: Entity,45/// b: Entity,46/// }47///48/// impl MapEntities for Spring {49/// fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {50/// self.a = entity_mapper.get_mapped(self.a);51/// self.b = entity_mapper.get_mapped(self.b);52/// }53/// }54/// ```55pub trait MapEntities {56/// Updates all [`Entity`] references stored inside using `entity_mapper`.57///58/// Implementors should look up any and all [`Entity`] values stored within `self` and59/// update them to the mapped values via `entity_mapper`.60fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E);61}6263impl MapEntities for Entity {64fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {65*self = entity_mapper.get_mapped(*self);66}67}6869impl<T: MapEntities> MapEntities for Option<T> {70fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {71if let Some(entities) = self {72entities.map_entities(entity_mapper);73}74}75}7677impl<K: MapEntities + Eq + Hash, V: MapEntities, S: BuildHasher + Default> MapEntities78for HashMap<K, V, S>79{80fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {81*self = self82.drain()83.map(|(mut key_entities, mut value_entities)| {84key_entities.map_entities(entity_mapper);85value_entities.map_entities(entity_mapper);86(key_entities, value_entities)87})88.collect();89}90}9192impl<T: MapEntities + Eq + Hash, S: BuildHasher + Default> MapEntities for HashSet<T, S> {93fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {94*self = self95.drain()96.map(|mut entities| {97entities.map_entities(entity_mapper);98entities99})100.collect();101}102}103104impl<K: MapEntities + Eq + Hash, V: MapEntities, S: BuildHasher + Default> MapEntities105for IndexMap<K, V, S>106{107fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {108*self = self109.drain(..)110.map(|(mut key_entities, mut value_entities)| {111key_entities.map_entities(entity_mapper);112value_entities.map_entities(entity_mapper);113(key_entities, value_entities)114})115.collect();116}117}118119impl<T: MapEntities + Eq + Hash, S: BuildHasher + Default> MapEntities for IndexSet<T, S> {120fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {121*self = self122.drain(..)123.map(|mut entities| {124entities.map_entities(entity_mapper);125entities126})127.collect();128}129}130131impl MapEntities for EntityIndexSet {132fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {133*self = self134.drain(..)135.map(|e| entity_mapper.get_mapped(e))136.collect();137}138}139140impl<K: MapEntities + Ord, V: MapEntities> MapEntities for BTreeMap<K, V> {141fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {142*self = mem::take(self)143.into_iter()144.map(|(mut key_entities, mut value_entities)| {145key_entities.map_entities(entity_mapper);146value_entities.map_entities(entity_mapper);147(key_entities, value_entities)148})149.collect();150}151}152153impl<T: MapEntities + Ord> MapEntities for BTreeSet<T> {154fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {155*self = mem::take(self)156.into_iter()157.map(|mut entities| {158entities.map_entities(entity_mapper);159entities160})161.collect();162}163}164165impl<T: MapEntities, const N: usize> MapEntities for [T; N] {166fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {167for entities in self.iter_mut() {168entities.map_entities(entity_mapper);169}170}171}172173impl<T: MapEntities> MapEntities for Vec<T> {174fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {175for entities in self.iter_mut() {176entities.map_entities(entity_mapper);177}178}179}180181impl<T: MapEntities> MapEntities for VecDeque<T> {182fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {183for entities in self.iter_mut() {184entities.map_entities(entity_mapper);185}186}187}188189impl<T: MapEntities, A: smallvec::Array<Item = T>> MapEntities for SmallVec<A> {190fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {191for entities in self.iter_mut() {192entities.map_entities(entity_mapper);193}194}195}196197/// An implementor of this trait knows how to map an [`Entity`] into another [`Entity`].198///199/// Usually this is done by using an [`EntityHashMap<Entity>`] to map source entities200/// (mapper inputs) to the current world's entities (mapper outputs).201///202/// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World).203///204/// This is used by [`MapEntities`] implementors.205///206/// ## Example207///208/// ```209/// # use bevy_ecs::entity::{Entity, EntityMapper};210/// # use bevy_ecs::entity::EntityHashMap;211/// #212/// pub struct SimpleEntityMapper {213/// map: EntityHashMap<Entity>,214/// }215///216/// // Example implementation of EntityMapper where we map an entity to another entity if it exists217/// // in the underlying `EntityHashMap`, otherwise we just return the original entity.218/// impl EntityMapper for SimpleEntityMapper {219/// fn get_mapped(&mut self, entity: Entity) -> Entity {220/// self.map.get(&entity).copied().unwrap_or(entity)221/// }222///223/// fn set_mapped(&mut self, source: Entity, target: Entity) {224/// self.map.insert(source, target);225/// }226/// }227/// ```228pub trait EntityMapper {229/// Returns the "target" entity that maps to the given `source`.230fn get_mapped(&mut self, source: Entity) -> Entity;231232/// Maps the `target` entity to the given `source`. For some implementations this might not actually determine the result233/// of [`EntityMapper::get_mapped`].234fn set_mapped(&mut self, source: Entity, target: Entity);235}236237impl EntityMapper for () {238#[inline]239fn get_mapped(&mut self, source: Entity) -> Entity {240source241}242243#[inline]244fn set_mapped(&mut self, _source: Entity, _target: Entity) {}245}246247impl EntityMapper for (Entity, Entity) {248#[inline]249fn get_mapped(&mut self, source: Entity) -> Entity {250if source == self.0 {251self.1252} else {253source254}255}256257fn set_mapped(&mut self, _source: Entity, _target: Entity) {}258}259260impl EntityMapper for &mut dyn EntityMapper {261fn get_mapped(&mut self, source: Entity) -> Entity {262(*self).get_mapped(source)263}264265fn set_mapped(&mut self, source: Entity, target: Entity) {266(*self).set_mapped(source, target);267}268}269270impl EntityMapper for SceneEntityMapper<'_> {271/// Returns the corresponding mapped entity or reserves a new dead entity ID in the current world if it is absent.272fn get_mapped(&mut self, source: Entity) -> Entity {273if let Some(&mapped) = self.map.get(&source) {274return mapped;275}276277// this new entity reference is specifically designed to never represent any living entity278let new = Entity::from_raw_and_generation(279self.dead_start.row(),280self.dead_start.generation.after_versions(self.generations),281);282self.generations = self.generations.wrapping_add(1);283284self.map.insert(source, new);285286new287}288289fn set_mapped(&mut self, source: Entity, target: Entity) {290self.map.insert(source, target);291}292}293294impl EntityMapper for EntityHashMap<Entity> {295/// Returns the corresponding mapped entity or returns `entity` if there is no mapped entity296fn get_mapped(&mut self, source: Entity) -> Entity {297self.get(&source).cloned().unwrap_or(source)298}299300fn set_mapped(&mut self, source: Entity, target: Entity) {301self.insert(source, target);302}303}304305/// A wrapper for [`EntityHashMap<Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination306/// world. These newly allocated references are guaranteed to never point to any living entity in that world.307///308/// References are allocated by returning increasing generations starting from an internally initialized base309/// [`Entity`]. After it is finished being used, this entity is despawned and the requisite number of generations reserved.310pub struct SceneEntityMapper<'m> {311/// A mapping from one set of entities to another.312///313/// This is typically used to coordinate data transfer between sets of entities, such as between a scene and the world314/// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse315/// identifiers directly.316///317/// On its own, a [`EntityHashMap<Entity>`] is not capable of allocating new entity identifiers, which is needed to map references318/// to entities that lie outside the source entity set. This functionality can be accessed through [`SceneEntityMapper::world_scope()`].319map: &'m mut EntityHashMap<Entity>,320/// A base [`Entity`] used to allocate new references.321dead_start: Entity,322/// The number of generations this mapper has allocated thus far.323generations: u32,324}325326impl<'m> SceneEntityMapper<'m> {327/// Gets a reference to the underlying [`EntityHashMap<Entity>`].328pub fn get_map(&'m self) -> &'m EntityHashMap<Entity> {329self.map330}331332/// Gets a mutable reference to the underlying [`EntityHashMap<Entity>`].333pub fn get_map_mut(&'m mut self) -> &'m mut EntityHashMap<Entity> {334self.map335}336337/// Creates a new [`SceneEntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]338pub fn new(map: &'m mut EntityHashMap<Entity>, world: &mut World) -> Self {339// We're going to be calling methods on `Entities` that require advance340// flushing, such as `alloc` and `free`.341world.flush_entities();342Self {343map,344// SAFETY: Entities data is kept in a valid state via `EntityMapper::world_scope`345dead_start: unsafe { world.entities_mut().alloc() },346generations: 0,347}348}349350/// Reserves the allocated references to dead entities within the world. This frees the temporary base351/// [`Entity`] while reserving extra generations. Because this makes the [`SceneEntityMapper`] unable to352/// safely allocate any more references, this method takes ownership of `self` in order to render it unusable.353pub fn finish(self, world: &mut World) {354// SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope`355let entities = unsafe { world.entities_mut() };356assert!(entities.free(self.dead_start).is_some());357assert!(entities.reserve_generations(self.dead_start.index(), self.generations));358}359360/// Creates an [`SceneEntityMapper`] from a provided [`World`] and [`EntityHashMap<Entity>`], then calls the361/// provided function with it. This allows one to allocate new entity references in this [`World`] that are362/// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely363/// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called364/// within the scope of this world. Its return value is then returned from `world_scope` as the generic type365/// parameter `R`.366pub fn world_scope<R>(367entity_map: &'m mut EntityHashMap<Entity>,368world: &mut World,369f: impl FnOnce(&mut World, &mut Self) -> R,370) -> R {371let mut mapper = Self::new(entity_map, world);372let result = f(world, &mut mapper);373mapper.finish(world);374result375}376}377378#[cfg(test)]379mod tests {380381use crate::{382entity::{Entity, EntityHashMap, EntityMapper, SceneEntityMapper},383world::World,384};385386#[test]387fn entity_mapper() {388let mut map = EntityHashMap::default();389let mut world = World::new();390let mut mapper = SceneEntityMapper::new(&mut map, &mut world);391392let mapped_ent = Entity::from_raw_u32(1).unwrap();393let dead_ref = mapper.get_mapped(mapped_ent);394395assert_eq!(396dead_ref,397mapper.get_mapped(mapped_ent),398"should persist the allocated mapping from the previous line"399);400assert_eq!(401mapper.get_mapped(Entity::from_raw_u32(2).unwrap()).index(),402dead_ref.index(),403"should re-use the same index for further dead refs"404);405406mapper.finish(&mut world);407// Next allocated entity should be a further generation on the same index408let entity = world.spawn_empty().id();409assert_eq!(entity.index(), dead_ref.index());410assert!(entity411.generation()412.cmp_approx(&dead_ref.generation())413.is_gt());414}415416#[test]417fn world_scope_reserves_generations() {418let mut map = EntityHashMap::default();419let mut world = World::new();420421let dead_ref = SceneEntityMapper::world_scope(&mut map, &mut world, |_, mapper| {422mapper.get_mapped(Entity::from_raw_u32(0).unwrap())423});424425// Next allocated entity should be a further generation on the same index426let entity = world.spawn_empty().id();427assert_eq!(entity.index(), dead_ref.index());428assert!(entity429.generation()430.cmp_approx(&dead_ref.generation())431.is_gt());432}433434#[test]435fn entity_mapper_no_panic() {436let mut world = World::new();437// "Dirty" the `Entities`, requiring a flush afterward.438world.entities.reserve_entity();439assert!(world.entities.needs_flush());440441// Create and exercise a SceneEntityMapper - should not panic because it flushes442// `Entities` first.443SceneEntityMapper::world_scope(&mut Default::default(), &mut world, |_, m| {444m.get_mapped(Entity::PLACEHOLDER);445});446447// The SceneEntityMapper should leave `Entities` in a flushed state.448assert!(!world.entities.needs_flush());449}450}451452453