1use bevy::{
2 app::{App, Plugin, Update},
3 ecs::{resource::Resource, world::World},
4};
5use moirai::{
6 jobs::{Jobs, JobsMeta},
7 queue::JobQueue,
8 third_party::intuicio_data::managed::ManagedLazy,
9};
10
11const WORLD_META: &str = "~world~";
12const NEXT_FRAME_META: &str = "~next-frame~";
13
14pub struct MoiraiPlugin;
15
16impl Plugin for MoiraiPlugin {
17 fn build(&self, app: &mut App) {
18 app.insert_resource(Coroutines::default());
19 app.add_systems(Update, coroutine_system);
20 }
21}
22
23#[derive(Resource)]
24pub struct Coroutines {
25 pub jobs: Jobs,
26 next_frame_queue: JobQueue,
27}
28
29impl Default for Coroutines {
30 fn default() -> Self {
31 Self::new(Jobs::local_only())
32 }
33}
34
35impl Coroutines {
36 pub fn new(jobs: Jobs) -> Self {
37 Self {
38 jobs,
39 next_frame_queue: Default::default(),
40 }
41 }
42}
43
44fn coroutine_system(world: &mut World) {
45 let mut next_frame = world.resource::<Coroutines>().next_frame_queue.clone();
46
47 let (world_lazy, _world_lifetime) = ManagedLazy::make(world);
48 let (next_frame_lazy, _next_frame_lifetime) = ManagedLazy::make(&mut next_frame);
49 let meta = JobsMeta::default()
50 .with(WORLD_META, world_lazy.into_dynamic())
51 .with(NEXT_FRAME_META, next_frame_lazy.into_dynamic());
52
53 {
54 let coroutines = world.resource::<Coroutines>();
55 coroutines.jobs.submit_queue(&coroutines.next_frame_queue);
56 while !coroutines.jobs.queue().is_empty() {
57 coroutines.jobs.run_local_with_meta(meta.clone());
58 }
59 }
60}
61
62pub mod coroutine {
63 use super::*;
64 use bevy::time::Time;
65 use moirai::{
66 coroutine::{meta, move_to},
67 job::JobLocation,
68 };
69
70 pub async fn world() -> ManagedLazy<World> {
71 meta::<World>(WORLD_META).await.unwrap()
72 }
73
74 pub async fn next_frame() {
75 let next_frame_queue = meta::<JobQueue>(NEXT_FRAME_META)
76 .await
77 .unwrap()
78 .read()
79 .unwrap()
80 .clone();
81
82 move_to(JobLocation::Queue(next_frame_queue)).await;
83 }
84
85 pub async fn wait_secs(seconds: f32) {
86 let start = world()
87 .await
88 .read()
89 .unwrap()
90 .resource::<Time>()
91 .elapsed_secs();
92
93 loop {
94 let now = world()
95 .await
96 .read()
97 .unwrap()
98 .resource::<Time>()
99 .elapsed_secs();
100
101 if now - start >= seconds {
102 break;
103 }
104
105 next_frame().await;
106 }
107 }
108}