Skip to content

Commit 19be538

Browse files
committed
Moved indexing into bevy_hierarchy
Ensures access to plugin system without modifying the ECS crate.
1 parent d6eed58 commit 19be538

File tree

4 files changed

+83
-53
lines changed

4 files changed

+83
-53
lines changed

crates/bevy_ecs/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ pub mod change_detection;
1010
pub mod component;
1111
pub mod entity;
1212
pub mod event;
13-
pub mod indexing;
1413
pub mod query;
1514
#[cfg(feature = "bevy_reflect")]
1615
pub mod reflect;

crates/bevy_ecs/src/indexing.rs renamed to crates/bevy_hierarchy/src/indexing.rs

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,34 @@
1-
//! Provides an [`Index`] system parameter, allowing a user to lookup an [`Entity`]
2-
//! based on the value of one of its [`Components`][`Component`].
1+
use std::{hash::Hash, marker::PhantomData};
2+
3+
use bevy_app::{App, Plugin, Update};
34

4-
use crate as bevy_ecs;
55
use bevy_ecs::{
66
component::{Component, Tick},
77
prelude::{Changed, Entity, Query, Ref, RemovedComponents, ResMut},
88
query::ReadOnlyWorldQuery,
9-
system::{SystemChangeTick, SystemParam},
9+
system::{Resource, SystemChangeTick, SystemParam},
1010
};
1111

12-
use bevy_ecs_macros::Resource;
13-
1412
use bevy_utils::{default, EntityHashMap, EntityHashSet, HashMap};
1513

16-
use std::{hash::Hash, marker::PhantomData};
17-
18-
/// Describes how to transform a [`Component`] `Input` into an `Index` suitable for an [`Index`].
14+
/// Describes how to transform an `Input` into an `Index` suitable for an [`Index`].
1915
pub trait Indexer {
20-
/// The input [`Component`] to index against.
21-
type Input: Component;
16+
/// The input to index against.
17+
type Input;
2218

23-
/// A type suitable for indexing the [`Component`] `Input`
24-
type Index: Hash + Eq + Clone + Sync + Send + 'static;
19+
/// A type suitable for indexing the `Input`
20+
type Index;
2521

2622
/// Generate an `Index` from the provided `Input`
2723
fn index(input: &Self::Input) -> Self::Index;
2824
}
2925

30-
/// A basic [`Indexer`] which directly uses the [`Component`] `T`'s value.
26+
/// A basic [`Indexer`] which directly uses `T`'s value.
3127
pub struct SimpleIndexer<T>(PhantomData<T>);
3228

3329
impl<T> Indexer for SimpleIndexer<T>
3430
where
35-
T: Component + Hash + Eq + Clone,
31+
T: Clone,
3632
{
3733
type Input = T;
3834

@@ -45,7 +41,7 @@ where
4541

4642
/// Stored data required for an [`Index`].
4743
#[derive(Resource)]
48-
pub struct IndexBacking<T, F = (), I = SimpleIndexer<T>>
44+
struct IndexBacking<T, F = (), I = SimpleIndexer<T>>
4945
where
5046
I: Indexer,
5147
{
@@ -75,10 +71,16 @@ where
7571
impl<T, F, I> IndexBacking<T, F, I>
7672
where
7773
I: Indexer<Input = T>,
74+
I::Index: Hash + Clone + Eq,
7875
{
7976
fn update(&mut self, entity: Entity, value: Option<&T>) -> Option<I::Index> {
8077
let value = value.map(|value| I::index(value));
8178

79+
if self.reverse.get(&entity) == value.as_ref() {
80+
// Return early since the value is already up-to-date
81+
return None;
82+
}
83+
8284
let old = if let Some(ref value) = value {
8385
self.reverse.insert(entity, value.clone())
8486
} else {
@@ -102,13 +104,33 @@ where
102104
old
103105
}
104106

107+
fn insert(&mut self, entity: Entity, value: &T) -> Option<I::Index> {
108+
self.update(entity, Some(value))
109+
}
110+
111+
fn remove_by_entity(&mut self, entity: Entity) -> Option<I::Index> {
112+
self.update(entity, None)
113+
}
114+
105115
fn get(&self, value: &T) -> impl Iterator<Item = Entity> + '_ {
116+
self.get_by_index(&I::index(value))
117+
}
118+
119+
fn get_by_index(&self, index: &I::Index) -> impl Iterator<Item = Entity> + '_ {
106120
self.forward
107-
.get(&I::index(value))
121+
.get(index)
108122
.unwrap_or(&self.empty)
109123
.iter()
110124
.copied()
111125
}
126+
127+
fn iter(
128+
&mut self,
129+
) -> impl Iterator<Item = (&I::Index, impl Iterator<Item = Entity> + '_)> + '_ {
130+
self.forward
131+
.iter()
132+
.map(|(index, entities)| (index, entities.iter().copied()))
133+
}
112134
}
113135

114136
/// Allows for lookup of an [`Entity`] based on the [`Component`] `T`'s value.
@@ -121,64 +143,92 @@ where
121143
T: Component,
122144
I: Indexer + 'static,
123145
F: ReadOnlyWorldQuery + 'static,
146+
I::Index: Send + Sync + 'static,
124147
{
125148
changed: Query<'w, 's, (Entity, Ref<'static, T>), (Changed<T>, F)>,
126149
removed: RemovedComponents<'w, 's, T>,
127150
index: ResMut<'w, IndexBacking<T, F, I>>,
128-
this_run: SystemChangeTick,
151+
change_tick: SystemChangeTick,
129152
}
130153

131154
impl<'w, 's, T, F, I> Index<'w, 's, T, F, I>
132155
where
133156
T: Component,
134157
I: Indexer<Input = T> + 'static,
135158
F: ReadOnlyWorldQuery + 'static,
159+
I::Index: Hash + Clone + Eq + Send + Sync + 'static,
136160
{
137161
fn update_index_internal(&mut self) {
138-
let this_run = self.this_run.this_run();
162+
let this_run = self.change_tick.this_run();
139163

140164
// Remove old entires
141165
for entity in self.removed.read() {
142-
self.index.update(entity, None);
166+
self.index.remove_by_entity(entity);
143167
}
144168

145169
// Update new and existing entries
146170
for (entity, component) in self.changed.iter() {
147-
self.index.update(entity, Some(component.as_ref()));
171+
self.index.insert(entity, component.as_ref());
148172
}
149173

150174
self.index.last_this_run = Some(this_run);
151175
}
152176

153177
/// System to keep [`Index`] coarsely updated every frame
154-
pub fn update_index(mut index: Index<T, F, I>) {
178+
fn update_index(mut index: Index<T, F, I>) {
155179
index.update_index_internal();
156180
}
157181

158182
fn ensure_updated(&mut self) {
159-
let this_run = self.this_run.this_run();
183+
let this_run = self.change_tick.this_run();
160184

161185
if self.index.last_this_run != Some(this_run) {
162186
self.update_index_internal();
163187
}
164188
}
165189

166-
/// Get
190+
/// Get all [entities](`Entity`) with a [`Component`] of `value`.
167191
pub fn get(&mut self, value: &T) -> impl Iterator<Item = Entity> + '_ {
168192
self.ensure_updated();
169193

170194
self.index.get(value)
171195
}
172196

197+
/// Get all [entities](`Entity`) with an `index`.
198+
pub fn get_by_index(&mut self, index: &I::Index) -> impl Iterator<Item = Entity> + '_ {
199+
self.ensure_updated();
200+
201+
self.index.get_by_index(index)
202+
}
203+
173204
/// Iterate over [entities](`Entity`) grouped by their [Index](`Indexer::Index`)
174205
pub fn iter(
175206
&mut self,
176207
) -> impl Iterator<Item = (&I::Index, impl Iterator<Item = Entity> + '_)> + '_ {
177208
self.ensure_updated();
178209

179-
self.index
180-
.forward
181-
.iter()
182-
.map(|(index, entities)| (index, entities.iter().copied()))
210+
self.index.iter()
211+
}
212+
}
213+
214+
/// Starts indexing the [`Component`] `T`. This provides access to the [`Index`] system parameter.
215+
pub struct IndexPlugin<T, F = (), I = SimpleIndexer<T>>(PhantomData<fn(T, F, I)>);
216+
217+
impl<T, F, I> Default for IndexPlugin<T, F, I> {
218+
fn default() -> Self {
219+
Self(PhantomData)
220+
}
221+
}
222+
223+
impl<T, I, F> Plugin for IndexPlugin<T, F, I>
224+
where
225+
T: Component,
226+
I: Indexer<Input = T> + 'static,
227+
F: ReadOnlyWorldQuery + 'static,
228+
I::Index: Hash + Clone + Eq + Send + Sync + 'static,
229+
{
230+
fn build(&self, app: &mut App) {
231+
app.init_resource::<IndexBacking<T, F, I>>()
232+
.add_systems(Update, Index::<T, F, I>::update_index);
183233
}
184234
}

crates/bevy_hierarchy/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ pub use components::*;
1010
mod hierarchy;
1111
pub use hierarchy::*;
1212

13+
mod indexing;
14+
pub use indexing::*;
15+
1316
mod child_builder;
1417
pub use child_builder::*;
1518

examples/ecs/indexing.rs

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,10 @@
22
#![allow(clippy::type_complexity)]
33

44
use bevy::{
5-
ecs::{indexing::*, query::ReadOnlyWorldQuery},
5+
hierarchy::{Index, IndexPlugin},
66
prelude::*,
77
};
8-
use std::{hash::Hash, marker::PhantomData};
9-
10-
pub struct IndexPlugin<T, F = (), I = SimpleIndexer<T>>(PhantomData<fn(T, F, I)>);
11-
12-
impl<T, F, I> Default for IndexPlugin<T, F, I> {
13-
fn default() -> Self {
14-
Self(PhantomData)
15-
}
16-
}
17-
18-
impl<T, I, F> Plugin for IndexPlugin<T, F, I>
19-
where
20-
T: Component,
21-
I: Indexer<Input = T> + 'static,
22-
F: ReadOnlyWorldQuery + 'static,
23-
{
24-
fn build(&self, app: &mut App) {
25-
app.init_resource::<IndexBacking<T, F, I>>()
26-
.add_systems(Update, Index::<T, F, I>::update_index);
27-
}
28-
}
29-
30-
// Usage
8+
use std::hash::Hash;
319

3210
#[derive(Component, Hash, Clone, PartialEq, Eq, Debug)]
3311
struct Player(usize);

0 commit comments

Comments
 (0)