macroquad/experimental/
coroutines.rs1use std::any::{Any, TypeId};
7use std::future::Future;
8use std::marker::PhantomData;
9use std::pin::Pin;
10use std::task::{Context, Poll};
11
12use crate::exec::resume;
13use crate::get_context;
14
15mod generational_storage;
16
17use generational_storage::{GenerationalId, GenerationalStorage};
18
19struct CoroutineInternal {
20 future: Pin<Box<dyn Future<Output = Box<dyn Any>>>>,
21 manual_poll: bool,
22 manual_time: Option<f64>,
23 has_value: bool,
27}
28
29enum CoroutineState {
30 Running(CoroutineInternal),
31 Value(Box<dyn Any>),
32 Nothing,
33}
34
35impl CoroutineState {
36 pub const fn is_value(&self) -> bool {
37 matches!(self, CoroutineState::Value(_))
38 }
39
40 pub const fn is_nothing(&self) -> bool {
41 matches!(self, CoroutineState::Nothing)
42 }
43
44 pub fn take_value(&mut self) -> Option<Box<dyn Any>> {
45 if self.is_value() {
46 let state = std::mem::replace(self, CoroutineState::Nothing);
47 if let CoroutineState::Value(v) = state {
48 return Some(v);
49 }
50 }
51
52 None
53 }
54}
55
56pub(crate) struct CoroutinesContext {
57 coroutines: GenerationalStorage<CoroutineState>,
58 active_coroutine_now: Option<f64>,
59 active_coroutine_delta: Option<f64>,
60}
61
62impl CoroutinesContext {
63 pub fn new() -> CoroutinesContext {
64 CoroutinesContext {
65 coroutines: GenerationalStorage::new(),
66 active_coroutine_now: None,
67 active_coroutine_delta: None,
68 }
69 }
70
71 pub fn update(&mut self) {
72 self.coroutines.retain(|coroutine| {
73 if let CoroutineState::Running(ref mut f) = coroutine {
74 if f.manual_poll == false {
75 if let Some(v) = resume(&mut f.future) {
76 if f.has_value {
77 *coroutine = CoroutineState::Value(v);
78 } else {
79 return false;
80 }
81 }
82 }
83 }
84
85 true
86 });
87 }
88
89 pub(crate) fn allocated_memory(&self) -> usize {
90 self.coroutines.allocated_memory()
91 }
92
93 pub(crate) fn active_coroutines_count(&self) -> usize {
94 self.coroutines.count()
95 }
96}
97#[derive(Clone, Copy, Debug)]
98pub struct Coroutine<T = ()> {
99 id: GenerationalId,
100 _phantom: PhantomData<T>,
101}
102
103impl<T: 'static + Any> Coroutine<T> {
104 pub fn is_done(&self) -> bool {
106 let context = &get_context().coroutines_context;
107
108 let coroutine = context.coroutines.get(self.id);
109
110 if let Some(coroutine) = coroutine {
111 return coroutine.is_value() || coroutine.is_nothing();
112 }
113
114 return true;
115 }
116
117 pub fn retrieve(&self) -> Option<T> {
118 let context = &mut get_context().coroutines_context;
119
120 if self.is_done() && TypeId::of::<()>() == TypeId::of::<T>() {
123 let res = Box::new(()) as Box<dyn Any>;
125 return Some(*res.downcast().unwrap());
126 }
127
128 let coroutine = context.coroutines.get_mut(self.id);
129 if let Some(v) = coroutine.and_then(|c| c.take_value()) {
130 let res = Some(*v.downcast().unwrap());
131 context.coroutines.free(self.id);
132 return res;
133 }
134
135 None
136 }
137
138 pub fn set_manual_poll(&mut self) {
163 let context = &mut get_context().coroutines_context;
164
165 if let Some(CoroutineState::Running(coroutine)) = context.coroutines.get_mut(self.id) {
166 coroutine.manual_time = Some(0.);
167 coroutine.manual_poll = true;
168 }
169 }
170
171 pub fn poll(&mut self, delta_time: f64) {
175 let context = &mut get_context().coroutines_context;
176
177 let coroutine = context.coroutines.get_mut(self.id);
178
179 if coroutine.is_none() {
181 return;
182 }
183
184 let coroutine = coroutine.unwrap();
185 if let CoroutineState::Running(f) = coroutine {
186 context.active_coroutine_now = f.manual_time;
187 context.active_coroutine_delta = Some(delta_time);
188 *f.manual_time.as_mut().unwrap() += delta_time;
189 if let Some(v) = resume(&mut f.future) {
190 if f.has_value {
191 *coroutine = CoroutineState::Value(v);
192 } else {
193 context.coroutines.free(self.id);
194 }
195 }
196 context.active_coroutine_now = None;
197 context.active_coroutine_delta = None;
198 }
199 }
200}
201
202pub fn start_coroutine<T: 'static + Any>(
203 future: impl Future<Output = T> + 'static + Send,
204) -> Coroutine<T> {
205 let context = &mut get_context().coroutines_context;
206
207 let has_value = TypeId::of::<()>() != TypeId::of::<T>();
208
209 let id = context
210 .coroutines
211 .push(CoroutineState::Running(CoroutineInternal {
212 future: Box::pin(async { Box::new(future.await) as _ }),
213 has_value,
214 manual_poll: false,
215 manual_time: None,
216 }));
217
218 Coroutine {
219 id,
220 _phantom: PhantomData,
221 }
222}
223
224pub fn stop_all_coroutines() {
225 let context = &mut get_context().coroutines_context;
226
227 context.coroutines.clear();
228}
229
230pub fn stop_coroutine(coroutine: Coroutine) {
231 let context = &mut get_context().coroutines_context;
232
233 context.coroutines.free(coroutine.id);
234}
235
236pub struct TimerDelayFuture {
237 pub(crate) remaining_time: f32,
238}
239
240impl Future for TimerDelayFuture {
241 type Output = Option<()>;
242
243 fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
244 let delta = get_context()
245 .coroutines_context
246 .active_coroutine_delta
247 .unwrap_or(crate::time::get_frame_time() as _);
248
249 self.remaining_time -= delta as f32;
250
251 if self.remaining_time <= 0.0 {
252 Poll::Ready(Some(()))
253 } else {
254 Poll::Pending
255 }
256 }
257}
258
259pub const fn wait_seconds(time: f32) -> TimerDelayFuture {
260 TimerDelayFuture {
261 remaining_time: time,
262 }
263}
264
265pub mod tweens {
267 use crate::experimental::scene::{Handle, Lens, Node};
268 use std::future::Future;
269 use std::pin::Pin;
270 use std::{
271 ops::{Add, Mul, Sub},
272 task::{Context, Poll},
273 };
274
275 pub struct LinearTweenFuture<T>
276 where
277 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T>,
278 {
279 from: T,
280 to: T,
281 lens: Lens<T>,
282 start_time: f64,
283 time: f32,
284 }
285 impl<T> Unpin for LinearTweenFuture<T> where
286 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T>
287 {
288 }
289
290 impl<T> Future for LinearTweenFuture<T>
291 where
292 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T>,
293 {
294 type Output = ();
295
296 fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll<Self::Output> {
297 let t = (miniquad::date::now() - self.start_time) / self.time as f64;
298 let this = self.get_mut();
299 let var = this.lens.get();
300
301 if var.is_none() {
303 return Poll::Ready(());
304 }
305 let var = var.unwrap();
306
307 if t <= 1. {
308 *var = this.from + (this.to - this.from) * t as f32;
309
310 Poll::Pending
311 } else {
312 *var = this.to;
313
314 Poll::Ready(())
315 }
316 }
317 }
318
319 pub fn linear<T, T1, F>(
320 handle: Handle<T1>,
321 lens: F,
322 from: T,
323 to: T,
324 time: f32,
325 ) -> LinearTweenFuture<T>
326 where
327 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T>,
328 T1: Node,
329 F: for<'r> FnMut(&'r mut T1) -> &'r mut T,
330 {
331 LinearTweenFuture {
332 to,
333 from,
334 lens: handle.lens(lens),
335 time,
336 start_time: miniquad::date::now(),
337 }
338 }
339
340 pub async fn follow_path<T, T1, F>(handle: Handle<T1>, mut lens: F, path: Vec<T>, time: f32)
341 where
342 T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T>,
343 T1: Node,
344 F: for<'r> FnMut(&'r mut T1) -> &'r mut T,
345 {
346 for point in path.windows(2) {
347 linear(
348 handle,
349 &mut lens,
350 point[0],
351 point[1],
352 time / path.len() as f32,
353 )
354 .await
355 }
356 }
357}