use alloc::sync::Arc;
use amity::{flip_queue::FlipQueue, ring_buffer::RingBuffer};
use core::{
any::TypeId,
iter::FusedIterator,
sync::atomic::{AtomicBool, Ordering},
};
use crate::{
bundle::{Bundle, ComponentBundle, DynamicBundle, DynamicComponentBundle},
component::Component,
entity::EntityId,
relation::Relation,
type_id,
world::{iter_reserve_hint, World},
};
use super::ActionFn;
struct Shared {
queue: FlipQueue<ActionFn<'static>>,
non_empty: AtomicBool,
connected: AtomicBool,
}
pub(crate) struct ActionChannel {
shared: Arc<Shared>,
spare_buffer: RingBuffer<ActionFn<'static>>,
}
impl Drop for ActionChannel {
fn drop(&mut self) {
self.shared.connected.store(false, Ordering::Relaxed);
}
}
impl ActionChannel {
#[inline(always)]
pub fn new() -> Self {
ActionChannel {
shared: Arc::new(Shared {
queue: FlipQueue::new(),
non_empty: AtomicBool::new(false),
connected: AtomicBool::new(true),
}),
spare_buffer: RingBuffer::new(),
}
}
#[inline(always)]
pub fn sender(&self) -> ActionSender {
ActionSender {
shared: self.shared.clone(),
}
}
#[inline(always)]
pub fn fetch(&mut self) {
debug_assert!(self.spare_buffer.is_empty());
if self.shared.non_empty.swap(false, Ordering::Relaxed) {
self.shared.queue.swap_buffer(&mut self.spare_buffer);
}
}
#[inline(always)]
pub(crate) fn pop(&mut self) -> Option<ActionFn<'static>> {
self.spare_buffer.pop()
}
}
#[derive(Clone)]
pub struct ActionSender {
shared: Arc<Shared>,
}
impl ActionSender {
#[inline(always)]
pub fn spawn<B>(&self, bundle: B)
where
B: DynamicComponentBundle + Send + 'static,
{
self.push_fn(move |world| {
let _ = world.spawn(bundle);
});
}
#[inline(always)]
pub fn spawn_external<B>(&self, bundle: B)
where
B: DynamicBundle + Send + 'static,
{
self.push_fn(move |world| {
let _ = world.spawn_external(bundle);
});
}
#[inline(always)]
pub fn spawn_batch<I>(&self, bundles: I) -> SpawnBatchChannel<I>
where
I: IntoIterator,
I::Item: ComponentBundle + Send + 'static,
{
self.push_fn(|world| {
world.ensure_bundle_registered::<I::Item>();
});
SpawnBatchChannel {
bundles,
sender: self,
}
}
#[inline(always)]
pub fn spawn_external_batch<I>(&self, bundles: I) -> SpawnBatchChannel<I>
where
I: IntoIterator,
I::Item: Bundle + Send + 'static,
{
SpawnBatchChannel {
bundles,
sender: self,
}
}
#[inline(always)]
pub fn despawn(&self, id: EntityId) {
self.push_fn(move |world| {
let _ = world.despawn(id);
})
}
#[inline(always)]
pub fn insert<T>(&self, id: EntityId, component: T)
where
T: Component + Send,
{
self.push_fn(move |world| {
let _ = world.insert(id, component);
});
}
#[inline(always)]
pub fn insert_external<T>(&self, id: EntityId, component: T)
where
T: Send + 'static,
{
self.push_fn(move |world| {
let _ = world.insert_external(id, component);
});
}
#[inline(always)]
pub fn insert_bundle<B>(&self, id: EntityId, bundle: B)
where
B: DynamicComponentBundle + Send + 'static,
{
self.push_fn(move |world| {
let _ = world.insert_bundle(id, bundle);
});
}
#[inline(always)]
pub fn insert_external_bundle<B>(&self, id: EntityId, bundle: B)
where
B: DynamicBundle + Send + 'static,
{
self.push_fn(move |world| {
let _ = world.insert_external_bundle(id, bundle);
});
}
#[inline(always)]
pub fn drop<T>(&self, id: EntityId)
where
T: 'static,
{
self.drop_erased(id, type_id::<T>())
}
#[inline(always)]
pub fn drop_erased(&self, id: EntityId, ty: TypeId) {
self.push_fn(move |world| {
let _ = world.drop_erased(id, ty);
})
}
#[inline(always)]
pub fn drop_bundle<B>(&self, id: EntityId)
where
B: Bundle,
{
self.push_fn(move |world| {
let _ = world.drop_bundle::<B>(id);
});
}
#[inline(always)]
pub fn add_relation<R>(&self, origin: EntityId, relation: R, target: EntityId)
where
R: Relation + Send,
{
self.push_fn(move |world| {
let _ = world.add_relation(origin, relation, target);
});
}
#[inline(always)]
pub fn drop_relation<R>(&self, origin: EntityId, target: EntityId)
where
R: Relation,
{
self.push_fn(move |world| {
let _ = world.remove_relation::<R>(origin, target);
});
}
pub fn insert_resource<T>(&self, resource: T)
where
T: Send + 'static,
{
self.push_fn(move |world| {
world.insert_resource(resource);
});
}
pub fn drop_resource<T: 'static>(&self) {
self.push_fn(move |world| {
world.remove_resource::<T>();
});
}
#[inline(always)]
pub fn closure(&self, fun: impl FnOnce(&mut World) + Send + 'static) {
self.push_fn(fun)
}
#[inline(always)]
fn push_fn(&self, fun: impl FnOnce(&mut World) + Send + 'static) {
let action = ActionFn::new(fun);
self.shared.queue.push(action);
self.shared.non_empty.store(true, Ordering::Relaxed);
}
#[inline(always)]
pub fn is_connected(&self) -> bool {
self.shared.connected.load(Ordering::Relaxed)
}
}
pub struct SpawnBatchChannel<'a, I> {
bundles: I,
sender: &'a ActionSender,
}
impl<B, I> SpawnBatchChannel<'_, I>
where
I: Iterator<Item = B>,
B: Bundle + Send + 'static,
{
#[inline(always)]
pub fn spawn_all(self) {
self.for_each(|_| {});
}
}
impl<B, I> Iterator for SpawnBatchChannel<'_, I>
where
I: Iterator<Item = B>,
B: Bundle + Send + 'static,
{
type Item = ();
#[inline(always)]
fn next(&mut self) -> Option<()> {
let bundle = self.bundles.next()?;
Some(self.sender.spawn_external(bundle))
}
#[inline(always)]
fn nth(&mut self, n: usize) -> Option<()> {
let bundle = self.bundles.nth(n)?;
Some(self.sender.spawn_external(bundle))
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.bundles.size_hint()
}
#[inline(always)]
fn fold<T, F>(self, init: T, mut f: F) -> T
where
F: FnMut(T, ()) -> T,
{
let additional = iter_reserve_hint(&self.bundles);
self.sender.push_fn(move |world| {
world.spawn_reserve::<B>(additional);
});
self.bundles.fold(init, |acc, bundle| {
f(acc, self.sender.spawn_external(bundle))
})
}
#[inline(always)]
fn collect<T>(self) -> T
where
T: FromIterator<()>,
{
let additional = iter_reserve_hint(&self.bundles);
self.sender.push_fn(move |world| {
world.spawn_reserve::<B>(additional);
});
FromIterator::from_iter(self)
}
}
impl<B, I> ExactSizeIterator for SpawnBatchChannel<'_, I>
where
I: ExactSizeIterator<Item = B>,
B: Bundle + Send + 'static,
{
#[inline(always)]
fn len(&self) -> usize {
self.bundles.len()
}
}
impl<B, I> DoubleEndedIterator for SpawnBatchChannel<'_, I>
where
I: DoubleEndedIterator<Item = B>,
B: Bundle + Send + 'static,
{
#[inline(always)]
fn next_back(&mut self) -> Option<()> {
let bundle = self.bundles.next_back()?;
Some(self.sender.spawn_external(bundle))
}
#[inline(always)]
fn nth_back(&mut self, n: usize) -> Option<()> {
let bundle = self.bundles.nth_back(n)?;
Some(self.sender.spawn_external(bundle))
}
#[inline(always)]
fn rfold<T, F>(self, init: T, mut f: F) -> T
where
Self: Sized,
F: FnMut(T, ()) -> T,
{
let additional = iter_reserve_hint(&self.bundles);
self.sender.push_fn(move |world| {
world.spawn_reserve::<B>(additional);
});
self.bundles.rfold(init, |acc, bundle| {
f(acc, self.sender.spawn_external(bundle))
})
}
}
impl<B, I> FusedIterator for SpawnBatchChannel<'_, I>
where
I: FusedIterator<Item = B>,
B: Bundle + Send + 'static,
{
}