use std::{
cell::RefCell,
future::Future,
rc::Rc,
task::Poll,
time::{Duration, Instant},
};
use crate::{
core::{app, messages::Frame},
entity,
global::{OkEmpty, ResultEmpty},
internal::executor::EXECUTOR,
message::Listener,
prelude::RuntimeMessage,
};
pub fn game_time() -> Duration {
entity::get_component(entity::resources(), app::components::game_time()).unwrap()
}
pub fn epoch_time() -> Duration {
entity::get_component(entity::resources(), app::components::epoch_time()).unwrap()
}
pub fn delta_time() -> f32 {
entity::get_component(entity::resources(), app::components::delta_time()).unwrap()
}
pub fn fixed_rate_tick(dt: Duration, mut callback: impl FnMut(Duration) + 'static) -> Listener {
let mut last_tick = game_time();
Frame::subscribe(move |_| {
let delta = game_time() - last_tick;
if delta < dt {
return;
}
callback(delta);
last_tick = game_time();
})
}
pub trait CallbackReturn {
#[doc(hidden)]
fn into_result(self) -> ResultEmpty;
}
impl CallbackReturn for ResultEmpty {
fn into_result(self) -> ResultEmpty {
self
}
}
impl CallbackReturn for () {
fn into_result(self) -> ResultEmpty {
OkEmpty
}
}
pub fn run_async<R: CallbackReturn>(future: impl Future<Output = R> + 'static) {
EXECUTOR.spawn(Box::pin(async move { future.await.into_result() }));
}
pub async fn block_until(condition: impl Fn() -> bool) {
std::future::poll_fn(move |_cx| {
if condition() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await
}
pub async fn sleep(seconds: f32) {
let target_time = Instant::now() + Duration::from_secs_f32(seconds);
block_until(|| Instant::now() > target_time).await
}
pub async fn wait_for_runtime_message<T: RuntimeMessage + Clone + 'static>(
is_relevant: impl Fn(&T) -> bool + 'static,
) -> T {
let result = Rc::new(RefCell::new(None));
let mut listener = Some(T::subscribe({
let result = result.clone();
move |response| {
if !is_relevant(&response) {
return;
}
*result.borrow_mut() = Some(response);
}
}));
std::future::poll_fn(move |_cx| match &*result.borrow() {
Some(r) => {
let r = (*r).clone();
if let Some(listener) = listener.take() {
listener.stop();
}
Poll::Ready(r)
}
_ => Poll::Pending,
})
.await
}
pub async fn wait_for_fallible_runtime_messages<
Success: RuntimeMessage + Clone + 'static,
Failure: RuntimeMessage + Clone + 'static,
>(
is_relevant_success: impl Fn(&Success) -> bool + 'static,
is_relevant_failure: impl Fn(&Failure) -> bool + 'static,
) -> Result<Success, Failure> {
let result = Rc::new(RefCell::new(None));
let mut success_listener = Some(Success::subscribe({
let result = result.clone();
move |response| {
if !is_relevant_success(&response) {
return;
}
*result.borrow_mut() = Some(Ok(response));
}
}));
let mut failure_listener = Some(Failure::subscribe({
let result = result.clone();
move |response| {
if !is_relevant_failure(&response) {
return;
}
*result.borrow_mut() = Some(Err(response));
}
}));
std::future::poll_fn(move |_cx| match &*result.borrow() {
Some(r) => {
let r = (*r).clone();
if let Some(listener) = success_listener.take() {
listener.stop();
}
if let Some(listener) = failure_listener.take() {
listener.stop();
}
Poll::Ready(r)
}
_ => Poll::Pending,
})
.await
}