ambient_core 0.2.1

Ambient core functionality. Host-only.
Documentation
use ambient_ecs::{components, Entity, FnSystem, Resource, SystemGroup, World};
use ambient_sys::time::Instant;

components!("ecs", {
    @[Resource]
    async_run: AsyncRun,
});

pub type AsyncEcsAction = Box<dyn FnOnce(&mut World) + Sync + Send>;

#[derive(Clone)]
#[allow(clippy::type_complexity)]
pub struct AsyncRun(flume::Sender<AsyncEcsAction>, Option<flume::Receiver<AsyncEcsAction>>);
impl AsyncRun {
    fn new() -> Self {
        let (tx, rx) = flume::unbounded();
        Self(tx, Some(rx))
    }
    pub fn run<F: FnOnce(&mut World) + Sync + Send + 'static>(&self, action: F) {
        self.0.send(Box::new(action)).ok();
    }
}
impl std::fmt::Debug for AsyncRun {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("AsyncRun").finish()
    }
}

pub fn async_ecs_resources() -> Entity {
    Entity::new().with(async_run(), AsyncRun::new())
}

pub fn async_ecs_systems() -> SystemGroup {
    SystemGroup::new(
        "async_ecs_systems",
        vec![Box::new(FnSystem::new(move |world, _| {
            let start = Instant::now();
            let rx = world.resource_mut(async_run()).1.take().unwrap();
            for action in rx.try_iter() {
                action(world);
                if Instant::now().duration_since(start).as_millis() > 50 {
                    let remaining = rx.len();
                    if remaining > 0 {
                        tracing::warn!("async ecs timeout. Remaining actions: {remaining}");
                    }
                    break;
                }
            }
            world.resource_mut(async_run()).1 = Some(rx);

            let dur = Instant::now().duration_since(start).as_millis();
            if dur > 100 {
                tracing::warn!("Async run ran for {dur}ms");
            }
        }))],
    )
}