use crate::index::IndexInfo;
use crate::unique_multimap::UniqueMultiMap;
use bevy::ecs::change_detection::Tick;
use bevy::ecs::system::{StaticSystemParam, SystemChangeTick, SystemParam};
use bevy::prelude::*;
use std::marker::PhantomData;
#[cfg(feature = "reflect")]
use bevy::reflect::Reflect;
pub trait IndexStorage<I: IndexInfo>: Resource + Default {
type RefreshData<'w, 's>: SystemParam;
fn lookup<'w, 's>(
&mut self,
val: &I::Value,
data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>,
) -> impl Iterator<Item = Entity>;
fn refresh<'w, 's>(&mut self, data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>);
fn force_refresh<'w, 's>(&mut self, data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>);
fn insertion_observer() -> Option<Observer>;
fn removal_observer() -> Option<Observer>;
}
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "reflect", reflect(Resource))]
#[derive(Resource)]
pub struct HashmapStorage<I: IndexInfo> {
map: UniqueMultiMap<I::Value, Entity>,
last_refresh_tick: Tick,
removed_entities: Vec<Entity>,
}
impl<I: IndexInfo> Default for HashmapStorage<I> {
fn default() -> Self {
Self {
map: Default::default(),
last_refresh_tick: Tick::new(0),
removed_entities: Vec::with_capacity(16),
}
}
}
impl<I: IndexInfo> IndexStorage<I> for HashmapStorage<I> {
type RefreshData<'w, 's> = HashmapStorageRefreshData<'w, 's, I>;
fn lookup<'w, 's>(
&mut self,
val: &I::Value,
_data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>,
) -> impl Iterator<Item = Entity> {
self.map.get(val).copied()
}
fn refresh<'w, 's>(&mut self, data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>) {
if self.last_refresh_tick != data.ticks.this_run() {
self.force_refresh(data);
}
}
fn force_refresh<'w, 's>(&mut self, data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>) {
for entity in self.removed_entities.iter() {
self.map.remove(entity);
}
self.removed_entities.clear();
for (entity, component) in &data.components {
if component.last_changed().is_newer_than(
Tick::new(self.last_refresh_tick.get().wrapping_sub(1)),
data.ticks.this_run(),
) {
self.map.insert(&I::value(&component), entity);
}
}
self.last_refresh_tick = data.ticks.this_run();
}
fn insertion_observer() -> Option<Observer> {
if I::REFRESH_POLICY.is_when_inserted() {
Some(Observer::new(
|insertion: On<Insert, I::Component>,
mut storage: ResMut<HashmapStorage<I>>,
components: Query<&I::Component>| {
let target = insertion.entity;
let component = components
.get(target)
.expect("Component that was just inserted is missing!");
println!("INSERTION");
storage.map.insert(&I::value(component), target);
},
))
} else {
None
}
}
fn removal_observer() -> Option<Observer> {
Some(Observer::new(
|removal: On<Remove, I::Component>, mut storage: ResMut<HashmapStorage<I>>| {
if I::REFRESH_POLICY.is_when_inserted() {
storage.map.remove(&removal.entity);
} else {
storage.removed_entities.push(removal.entity);
}
},
))
}
}
type ComponentsQuery<'w, 's, T> =
Query<'w, 's, (Entity, Ref<'static, <T as IndexInfo>::Component>)>;
#[doc(hidden)]
#[derive(SystemParam)]
pub struct HashmapStorageRefreshData<'w, 's, I: IndexInfo> {
components: ComponentsQuery<'w, 's, I>,
ticks: SystemChangeTick,
}
#[derive(Resource)]
#[cfg_attr(feature = "reflect", derive(Reflect))]
#[cfg_attr(feature = "reflect", reflect(Resource))]
pub struct NoStorage<I: IndexInfo> {
#[cfg_attr(feature = "reflect", reflect(ignore))]
phantom: PhantomData<fn() -> I>,
}
impl<I: IndexInfo> Default for NoStorage<I> {
fn default() -> Self {
Self {
phantom: PhantomData,
}
}
}
impl<I: IndexInfo> IndexStorage<I> for NoStorage<I> {
type RefreshData<'w, 's> = Query<'w, 's, (Entity, &'static I::Component)>;
fn lookup<'w, 's>(
&mut self,
val: &I::Value,
data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>,
) -> impl Iterator<Item = Entity> {
data.iter()
.filter_map(|(e, c)| if I::value(c) == *val { Some(e) } else { None })
}
fn refresh<'w, 's>(&mut self, _data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>) {}
fn force_refresh<'w, 's>(&mut self, _data: &mut StaticSystemParam<Self::RefreshData<'w, 's>>) {}
fn insertion_observer() -> Option<Observer> {
None
}
fn removal_observer() -> Option<Observer> {
None
}
}