use crossbeam_queue::SegQueue;
use crate::{prelude::*, world::EntitiesRes};
struct Queue<T>(SegQueue<T>);
impl<T> Default for Queue<T> {
fn default() -> Self {
Self(SegQueue::new())
}
}
#[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);
}
}
pub struct LazyUpdate {
queue: Option<Queue<Box<dyn LazyUpdateInternal>>>,
}
impl Default for LazyUpdate {
fn default() -> Self {
Self {
queue: Some(Default::default()),
}
}
}
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
.as_ref()
.unwrap()
.0
.push(Box::new(|w: &mut World| f(w)));
}
pub fn exec_mut<F>(&self, f: F)
where
F: FnOnce(&mut World) + 'static,
{
self.queue.as_ref().unwrap().0.push(Box::new(f));
}
}
pub fn create_entity(&self, ent: &EntitiesRes) -> LazyBuilder {
let entity = ent.create();
LazyBuilder { entity, lazy: self }
}
pub(super) fn take(&mut self) -> Self {
Self {
queue: self.queue.take(),
}
}
pub(super) fn restore(&mut self, mut maintained: Self) {
use std::mem::swap;
swap(&mut self.queue, &mut maintained.queue);
}
pub(super) fn maintain(&mut self, world: &mut World) {
let lazy = &mut self.queue.as_mut().unwrap().0;
while let Ok(l) = lazy.pop() {
l.update(world);
}
}
}
impl Drop for LazyUpdate {
fn drop(&mut self) {
if let Some(queue) = self.queue.as_mut() {
while queue.0.pop().is_ok() {}
}
}
}