use std::{
any::{Any, TypeId},
sync::atomic::AtomicU64,
sync::Arc,
};
use crate::{
entity::Entities,
facade::{Facade, FacadeSchedule, Request},
storage::Components,
Edges, Graph, GraphError, ViewMut,
};
static SYSTEM_ITERATION: AtomicU64 = AtomicU64::new(0);
#[inline]
pub fn current_iteration() -> u64 {
SYSTEM_ITERATION.load(std::sync::atomic::Ordering::Relaxed)
}
#[inline]
fn increment_current_iteration() -> u64 {
SYSTEM_ITERATION.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}
pub struct LazyOp {
pub(crate) op: Box<
dyn FnOnce(&mut World) -> Result<Arc<dyn Any + Send + Sync>, GraphError>
+ Send
+ Sync
+ 'static,
>,
pub(crate) tx: async_channel::Sender<Arc<dyn Any + Send + Sync>>,
}
pub enum Parallelism {
Automatic,
Explicit(u32),
}
#[derive(Edges)]
#[apecs(crate = crate)]
struct EntityUpkeepSystem {
entities: ViewMut<Entities>,
components: ViewMut<Components>,
}
impl EntityUpkeepSystem {
fn tick(mut self) -> Result<(), GraphError> {
let dead_ids: Vec<usize> = {
let mut dead_ids = vec![];
while let Ok(id) = self.entities.delete_rx.try_recv() {
dead_ids.push(id);
}
dead_ids
};
if !dead_ids.is_empty() {
let ids_and_types: Vec<(usize, smallvec::SmallVec<[TypeId; 4]>)> =
{ self.components.upkeep(&dead_ids) };
self.entities
.recycle
.extend(ids_and_types.iter().map(|(id, _)| *id));
self.entities
.deleted
.push_front((crate::world::current_iteration(), ids_and_types));
while self.entities.deleted.len() > 3 {
let _ = self.entities.deleted.pop_back();
}
}
Ok(())
}
}
pub struct World {
pub(crate) graph: Graph,
pub(crate) facade: Facade,
pub(crate) facade_requests: async_channel::Receiver<Request>,
pub(crate) facade_graph: Graph,
pub(crate) lazy_ops: (
async_channel::Sender<LazyOp>,
async_channel::Receiver<LazyOp>,
),
}
impl Default for World {
fn default() -> Self {
let lazy_ops = async_channel::unbounded();
let entities = Entities::new(lazy_ops.0.clone());
let (tx, rx) = async_channel::unbounded();
let facade = Facade {
request_tx: tx,
lazy_tx: lazy_ops.0.clone(),
};
let mut world = Self {
graph: Graph::default(),
facade: facade.clone(),
facade_requests: rx,
facade_graph: Graph::default(),
lazy_ops,
};
world
.add_resource(entities)
.add_resource(Components::default())
.add_resource(facade);
world
}
}
impl World {
pub fn facade(&self) -> Facade {
self.facade.clone()
}
pub fn facade_count(&self) -> usize {
self.facade.count()
}
pub fn add_subgraph(&mut self, graph: Graph) -> &mut Self {
self.graph.add_subgraph(graph);
let _ = self.graph.reschedule_if_necessary();
self
}
pub fn remove_node(&mut self, name: impl AsRef<str>) {
let _ = self.graph.remove_node(name);
}
pub fn remove_nodes<T: AsRef<str>>(&mut self, names: impl IntoIterator<Item = T>) {
for name in names.into_iter() {
self.remove_node(name);
}
}
pub fn interleave_subgraph(&mut self, graph: Graph) -> &mut Self {
self.graph.interleave_subgraph(graph);
let _ = self.graph.reschedule_if_necessary();
self
}
pub fn contains_resource<T: Any + Send + Sync>(&self) -> bool {
self.graph.contains_resource::<T>()
}
pub fn add_resource<T: Any + Send + Sync>(&mut self, t: T) -> &mut Self {
self.graph.add_resource(t);
self
}
pub fn get_resource<T: Any + Send + Sync>(&self) -> Option<&T> {
self.graph.get_resource::<T>().unwrap()
}
pub fn get_resource_mut<T: Any + Send + Sync>(&mut self) -> Option<&mut T> {
self.graph.get_resource_mut::<T>().unwrap()
}
pub fn visit<T: Edges, S>(&mut self, f: impl FnOnce(T) -> S) -> Result<S, GraphError> {
self.graph.visit(f)
}
pub fn with_parallelism(&mut self, parallelism: Parallelism) -> &mut Self {
match parallelism {
Parallelism::Automatic => {
#[cfg(target_arch = "wasm32")]
{
1
}
#[cfg(not(target_arch = "wasm32"))]
{
rayon::current_num_threads() as u32
}
}
Parallelism::Explicit(n) => {
if n > 1 {
log::info!("building a rayon thread pool with {} threads", n);
rayon::ThreadPoolBuilder::new()
.num_threads(n as usize)
.build()
.unwrap();
n
} else {
1
}
}
};
self
}
pub fn tick(&mut self) -> Result<(), GraphError> {
self.visit(EntityUpkeepSystem::tick)??;
self.tick_sync()?;
self.tick_lazy()?;
Ok(())
}
pub fn tock(&mut self) {
self.tick().unwrap()
}
pub(crate) fn tick_sync(&mut self) -> Result<(), GraphError> {
log::trace!("tick sync");
self.graph.reschedule_if_necessary()?;
let mut local: Option<fn(_) -> Result<_, _>> = None;
let mut got_trimmed = false;
let mut batches = self.graph.batches();
while let Some(batch) = batches.next_batch() {
let batch_result = batch.run(&mut local)?;
let did_trim_batch = batch_result.save(true, true)?;
got_trimmed = got_trimmed || did_trim_batch;
let _ = increment_current_iteration();
}
if got_trimmed {
self.graph.reschedule()?;
}
Ok(())
}
pub(crate) fn tick_lazy(&mut self) -> Result<(), GraphError> {
log::trace!("tick lazy");
while let Ok(LazyOp { op, tx }) = self.lazy_ops.1.try_recv() {
let t = (op)(self)?;
let _ = tx.try_send(t);
}
Ok(())
}
pub fn has_facade_requests(&self) -> bool {
self.facade_graph.node_len() > 0 || !self.facade_requests.is_empty()
}
pub fn get_facade_schedule(&mut self) -> Result<FacadeSchedule, GraphError> {
let current_iteration = crate::current_iteration();
let mut i = 0;
while let Ok(request) = self.facade_requests.try_recv() {
let node = moongraph::Node::from(request)
.with_name(format!("request-{}-{}", current_iteration, i));
self.facade_graph.add_node(node);
i += 1;
}
self.facade_graph.reschedule()?;
let mut batches = self.facade_graph.batches();
batches.set_resources(self.graph._resources_mut());
Ok(FacadeSchedule { batches })
}
pub fn run(&mut self) -> Result<&mut Self, GraphError> {
loop {
self.tick()?;
if self.has_facade_requests() || self.graph.node_len() == 0 {
break;
}
}
Ok(self)
}
pub fn get_components(&self) -> &Components {
self.get_resource::<Components>().unwrap()
}
pub fn get_components_mut(&mut self) -> &mut Components {
self.get_resource_mut::<Components>().unwrap()
}
pub fn get_entities(&self) -> &Entities {
self.get_resource::<Entities>().unwrap()
}
pub fn get_entities_mut(&mut self) -> &mut Entities {
self.get_resource_mut::<Entities>().unwrap()
}
pub fn get_schedule_names(&mut self) -> Vec<Vec<&str>> {
self.graph.get_schedule()
}
}