use crate::queue::QueryQueue;
use crate::reactors::Reactors;
use async_executor::{LocalExecutor, Task};
use bevy::asset::AssetServer;
use bevy::ecs::world::World;
use bevy::log::error;
use std::fmt::Display;
use std::future::Future;
use std::rc::Rc;
scoped_tls_hkt::scoped_thread_local!(pub(crate) static mut WORLD: World);
scoped_tls_hkt::scoped_thread_local!(pub(crate) static WORLD_READ_ONLY: World);
pub(crate) const USED_OUTSIDE: &str =
"Should not be called outside of a `bevy_defer` future or inside a access function.";
#[track_caller]
pub(crate) fn with_world_ref<T>(f: impl FnOnce(&World) -> T) -> T {
if WORLD_READ_ONLY.is_set() {
WORLD_READ_ONLY.with(f)
} else if WORLD.is_set() {
WORLD.with(|w| WORLD_READ_ONLY.set(w, || f(w)))
} else {
panic!("{}", USED_OUTSIDE)
}
}
#[track_caller]
pub(crate) fn with_world_mut<T>(f: impl FnOnce(&mut World) -> T) -> T {
if !WORLD.is_set() {
panic!("{}", USED_OUTSIDE)
}
WORLD.with(f)
}
scoped_tls_hkt::scoped_thread_local!(pub(crate) static ASSET_SERVER: AssetServer);
scoped_tls_hkt::scoped_thread_local!(pub(crate) static QUERY_QUEUE: QueryQueue);
scoped_tls_hkt::scoped_thread_local!(pub(crate) static SPAWNER: LocalExecutor<'static>);
scoped_tls_hkt::scoped_thread_local!(pub(crate) static REACTORS: Reactors);
pub fn in_async_context() -> bool {
QUERY_QUEUE.is_set()
}
#[derive(Debug, Default, Clone)]
pub struct AsyncExecutor(pub(crate) Rc<async_executor::LocalExecutor<'static>>);
impl AsyncExecutor {
pub fn spawn_any<T: 'static>(&self, future: impl Future<Output = T> + 'static) {
self.0.spawn(future).detach();
}
pub fn spawn_task<T: 'static>(&self, future: impl Future<Output = T> + 'static) -> Task<T> {
self.0.spawn(future)
}
pub fn spawn<T: 'static, E: Display>(
&self,
future: impl Future<Output = Result<T, E>> + 'static,
) {
self.0
.spawn(async {
if let Err(e) = future.await {
error!("{e}")
}
})
.detach();
}
}
pub fn run_async_executor(world: &mut World) {
let reactors = world.resource::<Reactors>().clone();
let queue = world.non_send_resource::<QueryQueue>().clone();
let executor = world.non_send_resource::<AsyncExecutor>().clone();
let assets = world.get_resource::<AssetServer>().cloned();
let mut f = || {
SPAWNER.set(&executor.0.clone(), || {
QUERY_QUEUE.set(&queue, || {
REACTORS.set(&reactors, || {
WORLD.set(world, || while executor.0.try_tick() {});
})
})
})
};
if let Some(assets) = assets {
ASSET_SERVER.set(&assets, f)
} else {
f()
}
}