Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ multi_threaded = ["bevy_tasks/multi_threaded", "dep:arrayvec"]
## Adds serialization support through `serde`.
serialize = ["dep:serde", "bevy_platform/serialize", "indexmap/serde"]

## Supports relationship collections through `wordvec`.
wordvec = ["dep:wordvec"]

## Adds runtime reflection support using `bevy_reflect`.
bevy_reflect = ["dep:bevy_reflect"]

Expand Down Expand Up @@ -115,6 +118,7 @@ smallvec = { version = "1", default-features = false, features = [
"union",
"const_generics",
] }
wordvec = { version = "0.1.0", optional = true }
indexmap = { version = "2.5.0", default-features = false }
variadics_please = { version = "1.1", default-features = false }
tracing = { version = "0.1", default-features = false, optional = true }
Expand Down
11 changes: 11 additions & 0 deletions crates/bevy_ecs/src/entity/map_entities.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub use bevy_ecs_macros::MapEntities;
use indexmap::{IndexMap, IndexSet};
#[cfg(feature = "wordvec")]
use wordvec::WordVec;

use crate::{
entity::{hash_map::EntityHashMap, Entity},
Expand Down Expand Up @@ -195,6 +197,15 @@ impl<T: MapEntities, A: smallvec::Array<Item = T>> MapEntities for SmallVec<A> {
}
}

#[cfg(feature = "wordvec")]
impl<T: MapEntities, const N: usize> MapEntities for WordVec<T, N> {
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E) {
for entities in self.iter_mut() {
entities.map_entities(entity_mapper);
}
}
}

/// An implementor of this trait knows how to map an [`Entity`] into another [`Entity`].
///
/// Usually this is done by using an [`EntityHashMap<Entity>`] to map source entities
Expand Down
128 changes: 128 additions & 0 deletions crates/bevy_ecs/src/relationship/relationship_source_collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::entity::{Entity, EntityHashSet, EntityIndexSet};
use alloc::vec::Vec;
use indexmap::IndexSet;
use smallvec::SmallVec;
#[cfg(feature = "wordvec")]
use wordvec::WordVec;

/// The internal [`Entity`] collection used by a [`RelationshipTarget`](crate::relationship::RelationshipTarget) component.
/// This is not intended to be modified directly by users, as it could invalidate the correctness of relationships.
Expand Down Expand Up @@ -331,6 +333,58 @@ impl<const N: usize> RelationshipSourceCollection for SmallVec<[Entity; N]> {
}
}

#[cfg(feature = "wordvec")]
impl<const N: usize> RelationshipSourceCollection for WordVec<Entity, N> {
type SourceIter<'a> = core::iter::Copied<core::slice::Iter<'a, Entity>>;

fn new() -> Self {
WordVec::new()
}

fn reserve(&mut self, additional: usize) {
WordVec::reserve(self, additional);
}

fn with_capacity(capacity: usize) -> Self {
WordVec::with_capacity(capacity)
}

fn add(&mut self, entity: Entity) -> bool {
WordVec::push(self, entity);

true
}

fn remove(&mut self, entity: Entity) -> bool {
if let Some(index) = <[Entity]>::iter(self).position(|e| *e == entity) {
WordVec::remove(self, index);
return true;
}

false
}

fn iter(&self) -> Self::SourceIter<'_> {
<[Entity]>::iter(self).copied()
}

fn len(&self) -> usize {
WordVec::len(self)
}

fn clear(&mut self) {
WordVec::clear(self);
}

fn shrink_to_fit(&mut self) {
WordVec::shrink_to_fit(self);
}

fn extend_from_iter(&mut self, entities: impl IntoIterator<Item = Entity>) {
self.extend(entities);
}
}

impl RelationshipSourceCollection for Entity {
type SourceIter<'a> = core::option::IntoIter<Entity>;

Expand Down Expand Up @@ -446,6 +500,58 @@ impl<const N: usize> OrderedRelationshipSourceCollection for SmallVec<[Entity; N
}
}

#[cfg(feature = "wordvec")]
impl<const N: usize> OrderedRelationshipSourceCollection for WordVec<Entity, N> {
fn insert(&mut self, index: usize, entity: Entity) {
self.push(entity);
let len = self.len();
if index < len {
self.swap(index, len - 1);
}
}

fn remove_at(&mut self, index: usize) -> Option<Entity> {
(index < self.len()).then(|| self.swap_remove(index))
}

fn insert_stable(&mut self, index: usize, entity: Entity) {
if index < self.len() {
WordVec::insert(self, index, entity);
} else {
self.push(entity);
}
}

fn remove_at_stable(&mut self, index: usize) -> Option<Entity> {
(index < self.len()).then(|| self.remove(index))
}

fn sort(&mut self) {
self.sort_unstable();
}

fn insert_sorted(&mut self, entity: Entity) {
let index = self.partition_point(|e| e <= &entity);
self.insert_stable(index, entity);
}

fn place_most_recent(&mut self, index: usize) {
if let Some(entity) = self.pop() {
let index = index.min(self.len() - 1);
self.insert(index, entity);
}
}

fn place(&mut self, entity: Entity, index: usize) {
if let Some(current) = <[Entity]>::iter(self).position(|e| *e == entity) {
// The len is at least 1, so the subtraction is safe.
let index = index.min(self.len() - 1);
WordVec::remove(self, current);
self.insert(index, entity);
};
}
}

impl<S: BuildHasher + Default> RelationshipSourceCollection for IndexSet<Entity, S> {
type SourceIter<'a>
= core::iter::Copied<indexmap::set::Iter<'a, Entity>>
Expand Down Expand Up @@ -630,6 +736,28 @@ mod tests {
assert_eq!(collection, &SmallVec::from_buf([a]));
}

#[cfg(feature = "wordvec")]
#[test]
fn wordvec_relationship_source_collection() {
#[derive(Component)]
#[relationship(relationship_target = RelTarget)]
struct Rel(Entity);

#[derive(Component)]
#[relationship_target(relationship = Rel, linked_spawn)]
struct RelTarget(WordVec<Entity, 4>);

let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();

world.entity_mut(a).insert(Rel(b));

let rel_target = world.get::<RelTarget>(b).unwrap();
let collection = rel_target.collection();
assert_eq!(collection.as_slice(), &[a]);
}

#[test]
fn entity_relationship_source_collection() {
#[derive(Component)]
Expand Down