Skip to main content

moirai_bevy/
lib.rs

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}