use crossbeam_queue::SegQueue;
use crate::{prelude::*, world::EntitiesRes};
use std::sync::Arc;
struct Queue<T>(SegQueue<T>);
impl<T> Default for Queue<T> {
fn default() -> Self {
Self(SegQueue::default())
}
}
#[cfg(feature = "parallel")]
pub trait LazyUpdateInternal: Send + Sync {
fn update(self: Box<Self>, world: &mut World);
}
#[cfg(not(feature = "parallel"))]
pub trait LazyUpdateInternal {
fn update(self: Box<Self>, world: &mut World);
}
macro_rules! parallel_feature {
(
$(
$(#[$attrs:meta])* $($words:ident)+<$($ty_params:ident),+> $args:tt $(-> $return_ty:ty)?
where
$($ty_param:ident:
$bound_first:ident $(< $ty_type:ident = $ty_bound:tt >)? $(($($fn_bound:tt)*))?
$(+ $bound:tt $($bound_2:ident)?)*,)+
$body:block
)+
) =>
{
$(
$(#[$attrs])*
#[cfg(feature = "parallel")]
$($words)+<$($ty_params),+> $args $(-> $return_ty)?
where
$($ty_param:
Send + Sync +
$bound_first $(< $ty_type = $ty_bound >)? $(($($fn_bound)*))?
$(+ $bound $($bound_2)?)*,)+
$body
$(#[$attrs])*
#[cfg(not(feature = "parallel"))]
$($words)+<$($ty_params),+> $args $(-> $return_ty)?
where
$($ty_param:
$bound_first $(< $ty_type = $ty_bound >)? $(($($fn_bound)*))?
$(+ $bound $($bound_2)?)*,)+
$body
)+
};
}
#[must_use = "Please call .build() on this to finish building it."]
pub struct LazyBuilder<'a> {
pub entity: Entity,
pub lazy: &'a LazyUpdate,
}
impl<'a> Builder for LazyBuilder<'a> {
parallel_feature! {
fn with<C>(self, component: C) -> Self
where
C: Component,
{
let entity = self.entity;
self.lazy.exec(move |world| {
let mut storage: WriteStorage<C> = SystemData::fetch(world);
if storage.insert(entity, component).is_err() {
log::warn!(
"Lazy insert of component failed because {:?} was dead.",
entity
);
}
});
self
}
}
fn build(self) -> Entity {
self.entity
}
}
#[cfg(feature = "parallel")]
impl<F> LazyUpdateInternal for F
where
F: FnOnce(&mut World) + Send + Sync + 'static,
{
fn update(self: Box<Self>, world: &mut World) {
self(world);
}
}
#[cfg(not(feature = "parallel"))]
impl<F> LazyUpdateInternal for F
where
F: FnOnce(&mut World) + 'static,
{
fn update(self: Box<Self>, world: &mut World) {
self(world);
}
}
#[derive(Default)]
pub struct LazyUpdate {
queue: Arc<Queue<Box<dyn LazyUpdateInternal>>>,
}
impl LazyUpdate {
parallel_feature! {
pub fn insert<C>(&self, e: Entity, c: C)
where
C: Component,
{
self.exec(move |world| {
let mut storage: WriteStorage<C> = SystemData::fetch(world);
if storage.insert(e, c).is_err() {
log::warn!("Lazy insert of component failed because {:?} was dead.", e);
}
});
}
pub fn insert_all<C, I>(&self, iter: I)
where
C: Component,
I: IntoIterator<Item = (Entity, C)> + 'static,
{
self.exec(move |world| {
let mut storage: WriteStorage<C> = SystemData::fetch(world);
for (e, c) in iter {
if storage.insert(e, c).is_err() {
log::warn!("Lazy insert of component failed because {:?} was dead.", e);
}
}
});
}
pub fn remove<C>(&self, e: Entity)
where
C: Component,
{
self.exec(move |world| {
let mut storage: WriteStorage<C> = SystemData::fetch(world);
storage.remove(e);
});
}
pub fn exec<F>(&self, f: F)
where
F: FnOnce(&mut World) + 'static,
{
self.queue
.0
.push(Box::new(f));
}
pub fn exec_mut<F>(&self, f: F)
where
F: FnOnce(&mut World) + 'static,
{
self.queue.0.push(Box::new(f));
}
}
pub fn create_entity(&self, ent: &EntitiesRes) -> LazyBuilder {
let entity = ent.create();
LazyBuilder { entity, lazy: self }
}
pub(super) fn clone(&self) -> Self {
Self {
queue: self.queue.clone(),
}
}
pub(super) fn maintain(&self, world: &mut World) {
while let Some(l) = self.queue.0.pop() {
l.update(world);
}
}
}
impl Drop for LazyUpdate {
fn drop(&mut self) {
while self.queue.0.pop().is_some() {}
}
}