use std::{iter::FromIterator, ops::AddAssign};
use crate::{
join::RepeatableLendGet,
prelude::*,
storage::{SharedGetMutOnly, UnprotectedStorage},
world::Index,
};
pub struct ChangeSet<T> {
mask: BitSet,
inner: DenseVecStorage<T>,
}
impl<T> Default for ChangeSet<T> {
fn default() -> Self {
Self {
mask: Default::default(),
inner: Default::default(),
}
}
}
impl<T> ChangeSet<T> {
pub fn new() -> Self {
Default::default()
}
pub fn add(&mut self, entity: Entity, value: T)
where
T: AddAssign,
{
if self.mask.contains(entity.id()) {
unsafe { *self.inner.get_mut(entity.id()) += value };
} else {
unsafe { self.inner.insert(entity.id(), value) };
self.mask.add(entity.id());
}
}
pub fn clear(&mut self) {
let mut mask_temp = core::mem::take(&mut self.mask);
unsafe { self.inner.clean(&mask_temp) };
mask_temp.clear();
self.mask = mask_temp;
}
}
impl<T> FromIterator<(Entity, T)> for ChangeSet<T>
where
T: AddAssign,
{
fn from_iter<I: IntoIterator<Item = (Entity, T)>>(iter: I) -> Self {
let mut changeset = Self::new();
for (entity, d) in iter {
changeset.add(entity, d);
}
changeset
}
}
impl<T> Extend<(Entity, T)> for ChangeSet<T>
where
T: AddAssign,
{
fn extend<I: IntoIterator<Item = (Entity, T)>>(&mut self, iter: I) {
for (entity, d) in iter {
self.add(entity, d);
}
}
}
#[nougat::gat]
unsafe impl<'a, T> LendJoin for &'a mut ChangeSet<T> {
type Mask = &'a BitSet;
type Type<'next> = &'next mut T;
type Value = &'a mut DenseVecStorage<T>;
unsafe fn open(self) -> (Self::Mask, Self::Value) {
(&self.mask, &mut self.inner)
}
unsafe fn get<'next>(value: &'next mut Self::Value, id: Index) -> Self::Type<'next> {
unsafe { value.get_mut(id) }
}
}
unsafe impl<'a, T> RepeatableLendGet for &'a mut ChangeSet<T> {}
unsafe impl<'a, T> Join for &'a mut ChangeSet<T> {
type Mask = &'a BitSet;
type Type = &'a mut T;
type Value = SharedGetMutOnly<'a, T, DenseVecStorage<T>>;
unsafe fn open(self) -> (Self::Mask, Self::Value) {
(&self.mask, SharedGetMutOnly::new(&mut self.inner))
}
unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type {
unsafe { SharedGetMutOnly::get_mut(value, id) }
}
}
#[nougat::gat]
unsafe impl<'a, T> LendJoin for &'a ChangeSet<T> {
type Mask = &'a BitSet;
type Type<'next> = &'a T;
type Value = &'a DenseVecStorage<T>;
unsafe fn open(self) -> (Self::Mask, Self::Value) {
(&self.mask, &self.inner)
}
unsafe fn get<'next>(value: &'next mut Self::Value, id: Index) -> Self::Type<'next> {
unsafe { value.get(id) }
}
}
unsafe impl<'a, T> RepeatableLendGet for &'a ChangeSet<T> {}
unsafe impl<'a, T> Join for &'a ChangeSet<T> {
type Mask = &'a BitSet;
type Type = &'a T;
type Value = &'a DenseVecStorage<T>;
unsafe fn open(self) -> (Self::Mask, Self::Value) {
(&self.mask, &self.inner)
}
unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type {
unsafe { value.get(id) }
}
}
#[nougat::gat]
#[allow(clippy::missing_safety_doc)]
unsafe impl<T> LendJoin for ChangeSet<T> {
type Mask = BitSet;
type Type<'next> = T;
type Value = DenseVecStorage<T>;
unsafe fn open(self) -> (Self::Mask, Self::Value) {
(self.mask, self.inner)
}
unsafe fn get<'next>(value: &'next mut Self::Value, id: Index) -> Self::Type<'next> {
unsafe { value.remove(id) }
}
}
unsafe impl<T> Join for ChangeSet<T> {
type Mask = BitSet;
type Type = T;
type Value = DenseVecStorage<T>;
unsafe fn open(self) -> (Self::Mask, Self::Value) {
(self.mask, self.inner)
}
unsafe fn get(value: &mut Self::Value, id: Index) -> Self::Type {
unsafe { value.remove(id) }
}
}
#[cfg(test)]
mod tests {
use super::ChangeSet;
use crate::{
join::Join,
storage::DenseVecStorage,
world::{Builder, Component, WorldExt},
};
use shred::World;
pub struct Health(i32);
impl Component for Health {
type Storage = DenseVecStorage<Self>;
}
#[test]
fn test() {
let mut world = World::new();
world.register::<Health>();
let a = world.create_entity().with(Health(100)).build();
let b = world.create_entity().with(Health(200)).build();
let c = world.create_entity().with(Health(300)).build();
let changeset = [(a, 32), (b, 12), (b, 13)]
.iter()
.cloned()
.collect::<ChangeSet<i32>>();
for (health, modifier) in (&mut world.write_storage::<Health>(), &changeset).join() {
health.0 -= modifier;
}
let healths = world.read_storage::<Health>();
assert_eq!(68, healths.get(a).unwrap().0);
assert_eq!(175, healths.get(b).unwrap().0);
assert_eq!(300, healths.get(c).unwrap().0);
}
}