Skip to main content

quaso/
game.rs

1#[cfg(feature = "editor")]
2use crate::editor::EditorInput;
3use crate::{
4    assets::{
5        anim_texture::AnimTextureAssetSubsystem, font::FontAssetSubsystem,
6        gltf::GltfAssetSubsystem, shader::ShaderAssetSubsystem, sound::SoundAssetSubsystem,
7        texture::TextureAssetSubsystem,
8    },
9    audio::Audio,
10    context::{GameContext, GameSubsystems},
11    gc::{DynGc, Gc},
12    multiplayer::{GameMultiplayer, GameMultiplayerChange, GameNetwork, local::LocalMultiplayer},
13    third_party::{
14        time::{Duration, Instant},
15        windowing::{
16            event::{Event, WindowEvent},
17            window::Window,
18        },
19    },
20};
21use gilrs::Gilrs;
22use intuicio_data::managed::DynamicManagedLazy;
23use keket::database::AssetDatabase;
24use moirai::{
25    job::{JobHandle, JobLocation, JobOptions},
26    jobs::Jobs,
27    queue::JobQueue,
28};
29use spitfire_draw::{
30    context::DrawContext,
31    utils::{ShaderRef, Vertex},
32};
33use spitfire_glow::{
34    app::{AppControl, AppState},
35    graphics::Graphics,
36    renderer::GlowBlending,
37};
38use spitfire_gui::context::GuiContext;
39use spitfire_input::InputContext;
40use std::{
41    any::{Any, TypeId},
42    borrow::Cow,
43    cell::LazyCell,
44    collections::BTreeMap,
45    pin::Pin,
46};
47use tehuti::peer::{Peer, PeerId};
48#[cfg(feature = "editor")]
49use vek::{Rect, Vec2};
50
51pub(crate) const CONTEXT_META: &str = "game_context";
52pub(crate) const DELTA_TIME_META: &str = "delta_time";
53pub(crate) const NEXT_FRAME_QUEUE_META: &str = "game_next_frame_queue";
54
55pub trait GameObject {
56    #[allow(unused_variables)]
57    fn activate(&mut self, context: &mut GameContext) {}
58
59    #[allow(unused_variables)]
60    fn deactivate(&mut self, context: &mut GameContext) {}
61
62    #[allow(unused_variables)]
63    fn process(&mut self, context: &mut GameContext, delta_time: f32) {}
64
65    #[allow(unused_variables)]
66    fn draw(&mut self, context: &mut GameContext) {}
67}
68
69#[derive(Default)]
70pub enum GameStateChange {
71    #[default]
72    Continue,
73    Swap(Box<dyn GameState>),
74    Push(Box<dyn GameState>),
75    Pop,
76}
77
78impl GameStateChange {
79    pub fn is_change(&self) -> bool {
80        !matches!(self, GameStateChange::Continue)
81    }
82}
83
84#[allow(unused_variables)]
85pub trait GameState {
86    fn enter(&mut self, context: GameContext) {}
87
88    fn exit(&mut self, context: GameContext) {}
89
90    fn update(&mut self, context: GameContext, delta_time: f32) {}
91
92    fn fixed_update(&mut self, context: GameContext, delta_time: f32) {}
93
94    fn draw(&mut self, context: GameContext) {}
95
96    fn draw_gui(&mut self, context: GameContext) {}
97
98    fn event(&mut self, globals: &mut GameGlobals, event: &Event<()>) {}
99
100    fn timeline(
101        &mut self,
102        context: GameContext,
103    ) -> Pin<Box<dyn Future<Output = ()> + Send + Sync>> {
104        Box::pin(async {})
105    }
106
107    fn can_reentry_from_background(&self) -> bool {
108        false
109    }
110
111    fn multiplayer_peer_added(&mut self, context: GameContext, peer: Peer) {}
112
113    fn multiplayer_peer_removed(&mut self, context: GameContext, peer_id: PeerId) {}
114
115    fn custom_event(&mut self, context: GameContext, payload: &mut dyn Any) {}
116}
117
118#[macro_export]
119macro_rules! game_state_custom_event {
120    (@item ($self:expr, $context:expr, $payload:expr) => trait($trait:ident)) => {
121        if <Self as $trait>::is($payload) {
122            <Self as $trait>::custom_event($self, $context, $payload);
123            return;
124        }
125    };
126    (@item ($self:expr, $context:expr, $payload:expr) => fn($method:ident, $payload_type:ident)) => {
127        if let Some(payload) = $payload.downcast_mut::<$payload_type>() {
128            $self.$method($context, payload);
129            return;
130        }
131    };
132    ($(
133        $kind:ident ( $( $arg:tt ),* )
134    ),*) => {
135        fn custom_event(&mut self, context: GameContext, payload: &mut dyn std::any::Any) {
136            $(
137                $crate::game_state_custom_event!(
138                    @item (self, context, payload) => $kind ( $( $arg ),* )
139                );
140            )*
141        }
142    };
143}
144
145#[allow(unused_variables)]
146pub trait GameSubsystem {
147    fn update(&mut self, context: GameContext, delta_time: f32) {}
148
149    fn fixed_update(&mut self, context: GameContext, delta_time: f32) {}
150
151    fn draw(&mut self, context: GameContext) {}
152
153    fn draw_gui(&mut self, context: GameContext) {}
154
155    fn event(&mut self, globals: &mut GameGlobals, event: &Event<()>) {}
156
157    fn as_any(&self) -> &dyn Any;
158
159    fn as_any_mut(&mut self) -> &mut dyn Any;
160}
161
162#[cfg(feature = "editor")]
163#[derive(Default)]
164pub struct EditorGlobals {
165    pub(crate) is_editing: bool,
166    pub(crate) viewport_rectangle: Rect<f32, f32>,
167    pub(crate) input: EditorInput,
168}
169
170#[cfg(feature = "editor")]
171impl EditorGlobals {
172    pub fn is_editing(&self) -> bool {
173        self.is_editing
174    }
175
176    pub fn viewport_rectangle(&self) -> Rect<f32, f32> {
177        self.viewport_rectangle
178    }
179
180    pub fn window_screen_to_viewport_screen(
181        &self,
182        graphics: &Graphics<Vertex>,
183        point: Vec2<f32>,
184    ) -> Vec2<f32> {
185        let view = (point - self.viewport_rectangle.position()) / self.viewport_rectangle.extent();
186        graphics.state.main_camera.screen_size * view
187    }
188
189    pub fn input(&self) -> &EditorInput {
190        &self.input
191    }
192}
193
194pub struct GameGlobals {
195    globals: BTreeMap<TypeId, DynGc>,
196    is_touch_device: LazyCell<bool>,
197    #[cfg(feature = "editor")]
198    pub editor: EditorGlobals,
199}
200
201impl Default for GameGlobals {
202    fn default() -> Self {
203        Self {
204            globals: Default::default(),
205            is_touch_device: LazyCell::new(|| {
206                #[cfg(target_arch = "wasm32")]
207                {
208                    use wasm_bindgen::prelude::*;
209                    use web_sys::window;
210
211                    let Some(window) = window() else {
212                        return false;
213                    };
214                    if js_sys::Reflect::has(&window, &JsValue::from_str("ontouchstart"))
215                        .unwrap_or(false)
216                    {
217                        return true;
218                    }
219                    if window.navigator().max_touch_points() > 0 {
220                        return true;
221                    }
222                    false
223                }
224                #[cfg(not(target_arch = "wasm32"))]
225                {
226                    cfg!(target_os = "android") || cfg!(target_os = "ios")
227                }
228            }),
229            #[cfg(feature = "editor")]
230            editor: Default::default(),
231        }
232    }
233}
234
235impl GameGlobals {
236    pub fn set<T: 'static>(&mut self, value: T) {
237        self.globals.insert(TypeId::of::<T>(), DynGc::new(value));
238    }
239
240    pub fn unset<T: 'static>(&mut self) {
241        self.globals.remove(&TypeId::of::<T>());
242    }
243
244    pub fn access<T: 'static>(&'_ self) -> Option<Gc<T>> {
245        self.globals
246            .get(&TypeId::of::<T>())
247            .map(|v| v.reference().into_typed())
248    }
249
250    pub fn ensure<T: Default + 'static>(&mut self) -> Gc<T> {
251        self.globals
252            .entry(TypeId::of::<T>())
253            .or_insert_with(|| DynGc::new(T::default()))
254            .reference()
255            .into_typed()
256    }
257
258    pub fn is_touch_device(&self) -> bool {
259        *self.is_touch_device
260    }
261}
262
263#[derive(Default)]
264pub struct GameJobs {
265    jobs: Jobs,
266}
267
268impl GameJobs {
269    pub fn coroutine<T: Send>(
270        &self,
271        job: impl Future<Output = T> + Send + Sync + 'static,
272    ) -> JobHandle<T> {
273        self.spawn(JobLocation::Local, job)
274    }
275
276    pub fn coroutine_with_meta<T: Send>(
277        &self,
278        meta: impl IntoIterator<Item = (Cow<'static, str>, DynGc)>,
279        job: impl Future<Output = T> + Send + Sync + 'static,
280    ) -> JobHandle<T> {
281        self.spawn(
282            JobOptions::default()
283                .location(JobLocation::Local)
284                .meta_many(meta.into_iter().map(|(id, gc)| (id, gc.0.into()))),
285            job,
286        )
287    }
288
289    pub fn spawn<T: Send>(
290        &self,
291        options: impl Into<JobOptions>,
292        job: impl Future<Output = T> + Send + Sync + 'static,
293    ) -> JobHandle<T> {
294        self.jobs.spawn(options, job)
295    }
296
297    pub fn jobs(&self) -> &Jobs {
298        &self.jobs
299    }
300}
301
302pub struct GameInstance {
303    pub fixed_delta_time: f32,
304    pub unfocused_fixed_delta_time_scale: f32,
305    pub color_shader: &'static str,
306    pub image_shader: &'static str,
307    pub text_shader: &'static str,
308    pub input_maintain_on_fixed_step: bool,
309    draw: DrawContext,
310    gui: GuiContext,
311    input: InputContext,
312    assets: AssetDatabase,
313    audio: Audio,
314    timer: Instant,
315    fixed_timer: Instant,
316    total_timer: Instant,
317    frame: usize,
318    #[allow(clippy::type_complexity)]
319    states: Vec<(Box<dyn GameState>, JobHandle<()>, Gc<()>)>,
320    state_change: GameStateChange,
321    subsystems: Vec<Box<dyn GameSubsystem>>,
322    globals: GameGlobals,
323    jobs: GameJobs,
324    network: GameNetwork,
325    multiplayer: Box<dyn GameMultiplayer>,
326    multiplayer_change: GameMultiplayerChange,
327    next_frame_queue: JobQueue,
328    update_queue: JobQueue,
329    next_update_queue: JobQueue,
330    fixed_update_queue: JobQueue,
331    next_fixed_update_queue: JobQueue,
332    draw_queue: JobQueue,
333    next_draw_queue: JobQueue,
334    draw_gui_queue: JobQueue,
335    next_draw_gui_queue: JobQueue,
336    focused: bool,
337    #[cfg(feature = "editor")]
338    editor: crate::editor::Editor,
339}
340
341impl Default for GameInstance {
342    fn default() -> Self {
343        Self {
344            fixed_delta_time: 1.0 / 60.0,
345            unfocused_fixed_delta_time_scale: 1.0,
346            color_shader: "color",
347            image_shader: "image",
348            text_shader: "text",
349            input_maintain_on_fixed_step: true,
350            draw: Default::default(),
351            gui: Default::default(),
352            input: Default::default(),
353            assets: Default::default(),
354            audio: Default::default(),
355            timer: Instant::now(),
356            fixed_timer: Instant::now(),
357            total_timer: Instant::now(),
358            frame: 0,
359            states: Default::default(),
360            state_change: Default::default(),
361            subsystems: vec![
362                Box::new(ShaderAssetSubsystem),
363                Box::new(TextureAssetSubsystem),
364                Box::new(AnimTextureAssetSubsystem),
365                Box::new(FontAssetSubsystem),
366                Box::new(SoundAssetSubsystem),
367                Box::new(GltfAssetSubsystem),
368            ],
369            globals: Default::default(),
370            jobs: Default::default(),
371            network: Default::default(),
372            multiplayer: Box::new(LocalMultiplayer::default()),
373            multiplayer_change: Default::default(),
374            next_frame_queue: Default::default(),
375            update_queue: Default::default(),
376            next_update_queue: Default::default(),
377            fixed_update_queue: Default::default(),
378            next_fixed_update_queue: Default::default(),
379            draw_queue: Default::default(),
380            next_draw_queue: Default::default(),
381            draw_gui_queue: Default::default(),
382            next_draw_gui_queue: Default::default(),
383            focused: true,
384            #[cfg(feature = "editor")]
385            editor: Default::default(),
386        }
387    }
388}
389
390impl GameInstance {
391    pub fn new(state: impl GameState + 'static) -> Self {
392        Self {
393            state_change: GameStateChange::Push(Box::new(state)),
394            ..Default::default()
395        }
396    }
397
398    #[cfg(feature = "editor")]
399    pub fn with_editor(mut self, editor: crate::editor::Editor) -> Self {
400        self.editor = editor;
401        self
402    }
403
404    pub fn with_fixed_time_step(mut self, value: f32) -> Self {
405        self.fixed_delta_time = value;
406        self
407    }
408
409    pub fn with_fps(mut self, frames_per_second: usize) -> Self {
410        self.set_fps(frames_per_second);
411        self
412    }
413
414    pub fn with_unfocused_fixed_time_step_scale(mut self, value: f32) -> Self {
415        self.unfocused_fixed_delta_time_scale = value;
416        self
417    }
418
419    pub fn with_color_shader(mut self, name: &'static str) -> Self {
420        self.color_shader = name;
421        self
422    }
423
424    pub fn with_image_shader(mut self, name: &'static str) -> Self {
425        self.image_shader = name;
426        self
427    }
428
429    pub fn with_text_shader(mut self, name: &'static str) -> Self {
430        self.text_shader = name;
431        self
432    }
433
434    pub fn with_input_maintain_on_fixed_step(mut self, value: bool) -> Self {
435        self.input_maintain_on_fixed_step = value;
436        self
437    }
438
439    pub fn with_subsystem(mut self, subsystem: impl GameSubsystem + 'static) -> Self {
440        self.subsystems.push(Box::new(subsystem));
441        self
442    }
443
444    pub fn with_globals<T: 'static>(mut self, value: T) -> Self {
445        self.globals.set(value);
446        self
447    }
448
449    pub fn with_jobs(mut self, jobs: Jobs) -> Self {
450        self.jobs.jobs = jobs;
451        self
452    }
453
454    pub fn with_jobs_unnamed_worker(mut self, iteration_timeout: Duration) -> Self {
455        self.jobs.jobs.add_unnamed_worker(iteration_timeout);
456        self
457    }
458
459    pub fn with_jobs_named_worker(
460        mut self,
461        iteration_timeout: Duration,
462        name: impl ToString,
463    ) -> Self {
464        self.jobs.jobs.add_named_worker(iteration_timeout, name);
465        self
466    }
467
468    pub fn with_network(mut self, network: GameNetwork) -> Self {
469        self.network = network;
470        self
471    }
472
473    pub fn with_gamepads(mut self) -> Self {
474        self.input = self.input.with_gamepads();
475        self
476    }
477
478    pub fn with_gamepads_custom(mut self, gamepads: Gilrs) -> Self {
479        self.input = self.input.with_gamepads_custom(gamepads);
480        self
481    }
482
483    pub fn setup_assets(mut self, f: impl FnOnce(&mut AssetDatabase)) -> Self {
484        f(&mut self.assets);
485        self
486    }
487
488    pub fn setup(self, f: impl FnOnce(Self) -> Self) -> Self {
489        f(self)
490    }
491
492    pub fn fps(&self) -> usize {
493        (1.0 / self.fixed_delta_time).ceil() as usize
494    }
495
496    pub fn set_fps(&mut self, frames_per_second: usize) {
497        self.fixed_delta_time = 1.0 / frames_per_second as f32;
498    }
499
500    pub fn process_frame(&mut self, graphics: &mut Graphics<Vertex>) {
501        let total_time = self.total_timer.elapsed().as_secs_f32();
502
503        loop {
504            match std::mem::take(&mut self.state_change) {
505                GameStateChange::Continue => {}
506                GameStateChange::Swap(mut state) => {
507                    if let Some((mut state, job, state_value)) = self.states.pop() {
508                        job.cancel();
509                        let state_heartbeat = state_value.heartbeat();
510                        state.exit(GameContext {
511                            graphics,
512                            draw: &mut self.draw,
513                            gui: &mut self.gui,
514                            input: &mut self.input,
515                            state_change: &mut self.state_change,
516                            multiplayer_change: &mut self.multiplayer_change,
517                            assets: &mut self.assets,
518                            audio: &mut self.audio,
519                            globals: &mut self.globals,
520                            jobs: Some(&self.jobs),
521                            network: &mut self.network,
522                            multiplayer: Some(&mut *self.multiplayer),
523                            update_queue: &self.next_update_queue,
524                            fixed_update_queue: &self.next_fixed_update_queue,
525                            draw_queue: &self.next_draw_queue,
526                            draw_gui_queue: &self.next_draw_gui_queue,
527                            state_heartbeat: &state_heartbeat,
528                            subsystems: GameSubsystems {
529                                subsystems: &mut self.subsystems,
530                            },
531                            time: total_time,
532                            frame: self.frame,
533                        });
534                        if self.state_change.is_change() {
535                            continue;
536                        }
537                    }
538                    let state_value = Gc::new(());
539                    let state_heartbeat = state_value.heartbeat();
540                    state.enter(GameContext {
541                        graphics,
542                        draw: &mut self.draw,
543                        gui: &mut self.gui,
544                        input: &mut self.input,
545                        state_change: &mut self.state_change,
546                        multiplayer_change: &mut self.multiplayer_change,
547                        assets: &mut self.assets,
548                        audio: &mut self.audio,
549                        globals: &mut self.globals,
550                        jobs: Some(&self.jobs),
551                        network: &mut self.network,
552                        multiplayer: Some(&mut *self.multiplayer),
553                        update_queue: &self.next_update_queue,
554                        fixed_update_queue: &self.next_fixed_update_queue,
555                        draw_queue: &self.next_draw_queue,
556                        draw_gui_queue: &self.next_draw_gui_queue,
557                        state_heartbeat: &state_heartbeat,
558                        subsystems: GameSubsystems {
559                            subsystems: &mut self.subsystems,
560                        },
561                        time: total_time,
562                        frame: self.frame,
563                    });
564                    if self.state_change.is_change() {
565                        continue;
566                    }
567                    let future = state.timeline(GameContext {
568                        graphics,
569                        draw: &mut self.draw,
570                        gui: &mut self.gui,
571                        input: &mut self.input,
572                        state_change: &mut self.state_change,
573                        multiplayer_change: &mut self.multiplayer_change,
574                        assets: &mut self.assets,
575                        audio: &mut self.audio,
576                        globals: &mut self.globals,
577                        jobs: Some(&self.jobs),
578                        network: &mut self.network,
579                        multiplayer: Some(&mut *self.multiplayer),
580                        update_queue: &self.next_update_queue,
581                        fixed_update_queue: &self.next_fixed_update_queue,
582                        draw_queue: &self.next_draw_queue,
583                        draw_gui_queue: &self.next_draw_gui_queue,
584                        state_heartbeat: &state_heartbeat,
585                        subsystems: GameSubsystems {
586                            subsystems: &mut self.subsystems,
587                        },
588                        time: total_time,
589                        frame: self.frame,
590                    });
591                    if self.state_change.is_change() {
592                        continue;
593                    }
594                    let job = self.jobs.coroutine(future);
595                    self.states.push((state, job, state_value));
596                    self.timer = Instant::now();
597                }
598                GameStateChange::Push(mut state) => {
599                    if let Some((state, _, state_value)) = self.states.last_mut()
600                        && state.can_reentry_from_background()
601                    {
602                        let state_heartbeat = state_value.heartbeat();
603                        state.exit(GameContext {
604                            graphics,
605                            draw: &mut self.draw,
606                            gui: &mut self.gui,
607                            input: &mut self.input,
608                            state_change: &mut self.state_change,
609                            multiplayer_change: &mut self.multiplayer_change,
610                            assets: &mut self.assets,
611                            audio: &mut self.audio,
612                            globals: &mut self.globals,
613                            jobs: Some(&self.jobs),
614                            network: &mut self.network,
615                            multiplayer: Some(&mut *self.multiplayer),
616                            update_queue: &self.next_update_queue,
617                            fixed_update_queue: &self.next_fixed_update_queue,
618                            draw_queue: &self.next_draw_queue,
619                            draw_gui_queue: &self.next_draw_gui_queue,
620                            state_heartbeat: &state_heartbeat,
621                            subsystems: GameSubsystems {
622                                subsystems: &mut self.subsystems,
623                            },
624                            time: total_time,
625                            frame: self.frame,
626                        });
627                    }
628                    let state_value = Gc::new(());
629                    let state_heartbeat = state_value.heartbeat();
630                    state.enter(GameContext {
631                        graphics,
632                        draw: &mut self.draw,
633                        gui: &mut self.gui,
634                        input: &mut self.input,
635                        state_change: &mut self.state_change,
636                        multiplayer_change: &mut self.multiplayer_change,
637                        assets: &mut self.assets,
638                        audio: &mut self.audio,
639                        globals: &mut self.globals,
640                        jobs: Some(&self.jobs),
641                        network: &mut self.network,
642                        multiplayer: Some(&mut *self.multiplayer),
643                        update_queue: &self.next_update_queue,
644                        fixed_update_queue: &self.next_fixed_update_queue,
645                        draw_queue: &self.next_draw_queue,
646                        draw_gui_queue: &self.next_draw_gui_queue,
647                        state_heartbeat: &state_heartbeat,
648                        subsystems: GameSubsystems {
649                            subsystems: &mut self.subsystems,
650                        },
651                        time: total_time,
652                        frame: self.frame,
653                    });
654                    if self.state_change.is_change() {
655                        continue;
656                    }
657                    let future = state.timeline(GameContext {
658                        graphics,
659                        draw: &mut self.draw,
660                        gui: &mut self.gui,
661                        input: &mut self.input,
662                        state_change: &mut self.state_change,
663                        multiplayer_change: &mut self.multiplayer_change,
664                        assets: &mut self.assets,
665                        audio: &mut self.audio,
666                        globals: &mut self.globals,
667                        jobs: Some(&self.jobs),
668                        network: &mut self.network,
669                        multiplayer: Some(&mut *self.multiplayer),
670                        update_queue: &self.next_update_queue,
671                        fixed_update_queue: &self.next_fixed_update_queue,
672                        draw_queue: &self.next_draw_queue,
673                        draw_gui_queue: &self.next_draw_gui_queue,
674                        state_heartbeat: &state_heartbeat,
675                        subsystems: GameSubsystems {
676                            subsystems: &mut self.subsystems,
677                        },
678                        time: total_time,
679                        frame: self.frame,
680                    });
681                    if self.state_change.is_change() {
682                        continue;
683                    }
684                    let job = self.jobs.coroutine(future);
685                    self.states.push((state, job, state_value));
686                    self.timer = Instant::now();
687                }
688                GameStateChange::Pop => {
689                    if let Some((mut state, job, state_value)) = self.states.pop() {
690                        let state_heartbeat = state_value.heartbeat();
691                        job.cancel();
692                        state.exit(GameContext {
693                            graphics,
694                            draw: &mut self.draw,
695                            gui: &mut self.gui,
696                            input: &mut self.input,
697                            state_change: &mut self.state_change,
698                            multiplayer_change: &mut self.multiplayer_change,
699                            assets: &mut self.assets,
700                            audio: &mut self.audio,
701                            globals: &mut self.globals,
702                            jobs: Some(&self.jobs),
703                            network: &mut self.network,
704                            multiplayer: Some(&mut *self.multiplayer),
705                            update_queue: &self.next_update_queue,
706                            fixed_update_queue: &self.next_fixed_update_queue,
707                            draw_queue: &self.next_draw_queue,
708                            draw_gui_queue: &self.next_draw_gui_queue,
709                            state_heartbeat: &state_heartbeat,
710                            subsystems: GameSubsystems {
711                                subsystems: &mut self.subsystems,
712                            },
713                            time: total_time,
714                            frame: self.frame,
715                        });
716                    }
717                    if self.state_change.is_change() {
718                        continue;
719                    }
720                    if let Some((state, _, state_value)) = self.states.last_mut()
721                        && state.can_reentry_from_background()
722                    {
723                        let state_heartbeat = state_value.heartbeat();
724                        state.enter(GameContext {
725                            graphics,
726                            draw: &mut self.draw,
727                            gui: &mut self.gui,
728                            input: &mut self.input,
729                            state_change: &mut self.state_change,
730                            multiplayer_change: &mut self.multiplayer_change,
731                            assets: &mut self.assets,
732                            audio: &mut self.audio,
733                            globals: &mut self.globals,
734                            jobs: Some(&self.jobs),
735                            network: &mut self.network,
736                            multiplayer: Some(&mut *self.multiplayer),
737                            update_queue: &self.next_update_queue,
738                            fixed_update_queue: &self.next_fixed_update_queue,
739                            draw_queue: &self.next_draw_queue,
740                            draw_gui_queue: &self.next_draw_gui_queue,
741                            state_heartbeat: &state_heartbeat,
742                            subsystems: GameSubsystems {
743                                subsystems: &mut self.subsystems,
744                            },
745                            time: total_time,
746                            frame: self.frame,
747                        });
748                    }
749                    self.timer = Instant::now();
750                }
751            }
752            break;
753        }
754
755        self.frame += 1;
756        #[cfg(feature = "editor")]
757        let is_editing = self.globals.editor.is_editing();
758        let mut delta_time = self.timer.elapsed().as_secs_f32();
759        let jobs_timer = self.timer;
760        self.timer = Instant::now();
761        let frame_budget = Duration::from_secs_f32(self.fixed_delta_time);
762        let Some(state_value) = self.states.last().map(|(_, _, value)| value) else {
763            return;
764        };
765        let state_heartbeat = state_value.heartbeat();
766
767        self.network.maintain();
768        if let Some((state, _, _)) = self.states.last_mut() {
769            let change = match std::mem::take(&mut self.multiplayer_change) {
770                GameMultiplayerChange::None => None,
771                GameMultiplayerChange::Set(multiplayer) => Some(multiplayer),
772                GameMultiplayerChange::Reset => {
773                    Some(Box::new(LocalMultiplayer::default()) as Box<dyn GameMultiplayer>)
774                }
775            };
776            if let Some(multiplayer) = change {
777                self.multiplayer.on_cleanup(
778                    &mut **state,
779                    GameContext {
780                        graphics,
781                        draw: &mut self.draw,
782                        gui: &mut self.gui,
783                        input: &mut self.input,
784                        state_change: &mut self.state_change,
785                        multiplayer_change: &mut self.multiplayer_change,
786                        assets: &mut self.assets,
787                        audio: &mut self.audio,
788                        globals: &mut self.globals,
789                        jobs: Some(&self.jobs),
790                        network: &mut self.network,
791                        multiplayer: None,
792                        update_queue: &self.next_update_queue,
793                        fixed_update_queue: &self.next_fixed_update_queue,
794                        draw_queue: &self.next_draw_queue,
795                        draw_gui_queue: &self.next_draw_gui_queue,
796                        state_heartbeat: &state_heartbeat,
797                        subsystems: GameSubsystems {
798                            subsystems: &mut self.subsystems,
799                        },
800                        time: total_time,
801                        frame: self.frame,
802                    },
803                );
804                self.multiplayer = multiplayer;
805                self.multiplayer.on_startup(
806                    &mut **state,
807                    GameContext {
808                        graphics,
809                        draw: &mut self.draw,
810                        gui: &mut self.gui,
811                        input: &mut self.input,
812                        state_change: &mut self.state_change,
813                        multiplayer_change: &mut self.multiplayer_change,
814                        assets: &mut self.assets,
815                        audio: &mut self.audio,
816                        globals: &mut self.globals,
817                        jobs: Some(&self.jobs),
818                        network: &mut self.network,
819                        multiplayer: None,
820                        update_queue: &self.next_update_queue,
821                        fixed_update_queue: &self.next_fixed_update_queue,
822                        draw_queue: &self.next_draw_queue,
823                        draw_gui_queue: &self.next_draw_gui_queue,
824                        state_heartbeat: &state_heartbeat,
825                        subsystems: GameSubsystems {
826                            subsystems: &mut self.subsystems,
827                        },
828                        time: total_time,
829                        frame: self.frame,
830                    },
831                );
832            }
833            self.multiplayer.maintain(
834                &mut **state,
835                GameContext {
836                    graphics,
837                    draw: &mut self.draw,
838                    gui: &mut self.gui,
839                    input: &mut self.input,
840                    state_change: &mut self.state_change,
841                    multiplayer_change: &mut self.multiplayer_change,
842                    assets: &mut self.assets,
843                    audio: &mut self.audio,
844                    globals: &mut self.globals,
845                    jobs: Some(&self.jobs),
846                    network: &mut self.network,
847                    multiplayer: None,
848                    update_queue: &self.next_update_queue,
849                    fixed_update_queue: &self.next_fixed_update_queue,
850                    draw_queue: &self.next_draw_queue,
851                    draw_gui_queue: &self.next_draw_gui_queue,
852                    state_heartbeat: &state_heartbeat,
853                    subsystems: GameSubsystems {
854                        subsystems: &mut self.subsystems,
855                    },
856                    time: total_time,
857                    frame: self.frame,
858                },
859                delta_time,
860            );
861        }
862
863        let mut update_phase = || {
864            if let Some((state, _, _)) = self.states.last_mut() {
865                state.update(
866                    GameContext {
867                        graphics,
868                        draw: &mut self.draw,
869                        gui: &mut self.gui,
870                        input: &mut self.input,
871                        state_change: &mut self.state_change,
872                        multiplayer_change: &mut self.multiplayer_change,
873                        assets: &mut self.assets,
874                        audio: &mut self.audio,
875                        globals: &mut self.globals,
876                        jobs: Some(&self.jobs),
877                        network: &mut self.network,
878                        multiplayer: Some(&mut *self.multiplayer),
879                        update_queue: &self.next_update_queue,
880                        fixed_update_queue: &self.next_fixed_update_queue,
881                        draw_queue: &self.next_draw_queue,
882                        draw_gui_queue: &self.next_draw_gui_queue,
883                        state_heartbeat: &state_heartbeat,
884                        subsystems: GameSubsystems {
885                            subsystems: &mut self.subsystems,
886                        },
887                        time: total_time,
888                        frame: self.frame,
889                    },
890                    delta_time,
891                );
892            }
893            for subsystem in &mut self.subsystems {
894                subsystem.update(
895                    GameContext {
896                        graphics,
897                        draw: &mut self.draw,
898                        gui: &mut self.gui,
899                        input: &mut self.input,
900                        state_change: &mut self.state_change,
901                        multiplayer_change: &mut self.multiplayer_change,
902                        assets: &mut self.assets,
903                        audio: &mut self.audio,
904                        globals: &mut self.globals,
905                        jobs: Some(&self.jobs),
906                        network: &mut self.network,
907                        multiplayer: Some(&mut *self.multiplayer),
908                        update_queue: &self.next_update_queue,
909                        fixed_update_queue: &self.next_fixed_update_queue,
910                        draw_queue: &self.next_draw_queue,
911                        draw_gui_queue: &self.next_draw_gui_queue,
912                        state_heartbeat: &state_heartbeat,
913                        subsystems: GameSubsystems {
914                            subsystems: &mut [],
915                        },
916                        time: total_time,
917                        frame: self.frame,
918                    },
919                    delta_time,
920                );
921            }
922            self.update_queue.append(&self.next_update_queue);
923            while !self.update_queue.is_empty() {
924                let mut async_context = GameContext {
925                    graphics,
926                    draw: &mut self.draw,
927                    gui: &mut self.gui,
928                    input: &mut self.input,
929                    state_change: &mut self.state_change,
930                    multiplayer_change: &mut self.multiplayer_change,
931                    assets: &mut self.assets,
932                    audio: &mut self.audio,
933                    globals: &mut self.globals,
934                    jobs: None,
935                    network: &mut self.network,
936                    multiplayer: Some(&mut *self.multiplayer),
937                    update_queue: &self.next_update_queue,
938                    fixed_update_queue: &self.next_fixed_update_queue,
939                    draw_queue: &self.next_draw_queue,
940                    draw_gui_queue: &self.next_draw_gui_queue,
941                    state_heartbeat: &state_heartbeat,
942                    subsystems: GameSubsystems {
943                        subsystems: &mut self.subsystems,
944                    },
945                    time: total_time,
946                    frame: self.frame,
947                };
948                let (async_context_lazy, _async_context_lifetime) =
949                    DynamicManagedLazy::make(&mut async_context);
950                let (delta_time_lazy, _delta_time_lifetime) =
951                    DynamicManagedLazy::make(&mut delta_time);
952                let (next_frame_queue_lazy, _next_frame_queue_lifetime) =
953                    DynamicManagedLazy::make(&mut self.next_update_queue);
954                self.jobs.jobs.run_queue_with_meta(
955                    &self.update_queue,
956                    [
957                        (CONTEXT_META.into(), async_context_lazy.into()),
958                        (DELTA_TIME_META.into(), delta_time_lazy.into()),
959                        (NEXT_FRAME_QUEUE_META.into(), next_frame_queue_lazy.into()),
960                    ]
961                    .into_iter()
962                    .collect(),
963                );
964            }
965            #[cfg(feature = "editor")]
966            for subsystem in &mut self.editor.subsystems {
967                subsystem.update(
968                    GameContext {
969                        graphics,
970                        draw: &mut self.draw,
971                        gui: &mut self.gui,
972                        input: &mut self.input,
973                        state_change: &mut self.state_change,
974                        multiplayer_change: &mut self.multiplayer_change,
975                        assets: &mut self.assets,
976                        audio: &mut self.audio,
977                        globals: &mut self.globals,
978                        jobs: Some(&self.jobs),
979                        network: &mut self.network,
980                        multiplayer: Some(&mut *self.multiplayer),
981                        update_queue: &self.next_update_queue,
982                        fixed_update_queue: &self.next_fixed_update_queue,
983                        draw_queue: &self.next_draw_queue,
984                        draw_gui_queue: &self.next_draw_gui_queue,
985                        state_heartbeat: &state_heartbeat,
986                        subsystems: GameSubsystems {
987                            subsystems: &mut self.subsystems,
988                        },
989                        time: total_time,
990                        frame: self.frame,
991                    },
992                    delta_time,
993                );
994            }
995
996            let mut fixed_delta_time = self.fixed_timer.elapsed().as_secs_f32();
997            let fixed_delta_time_limit = if self.focused {
998                self.fixed_delta_time
999            } else {
1000                self.fixed_delta_time * self.unfocused_fixed_delta_time_scale
1001            };
1002
1003            if fixed_delta_time > fixed_delta_time_limit {
1004                self.fixed_timer = Instant::now();
1005                if let Some((state, _, _)) = self.states.last_mut() {
1006                    state.fixed_update(
1007                        GameContext {
1008                            graphics,
1009                            draw: &mut self.draw,
1010                            gui: &mut self.gui,
1011                            input: &mut self.input,
1012                            state_change: &mut self.state_change,
1013                            multiplayer_change: &mut self.multiplayer_change,
1014                            assets: &mut self.assets,
1015                            audio: &mut self.audio,
1016                            globals: &mut self.globals,
1017                            jobs: Some(&self.jobs),
1018                            network: &mut self.network,
1019                            multiplayer: Some(&mut *self.multiplayer),
1020                            update_queue: &self.next_update_queue,
1021                            fixed_update_queue: &self.next_fixed_update_queue,
1022                            draw_queue: &self.next_draw_queue,
1023                            draw_gui_queue: &self.next_draw_gui_queue,
1024                            state_heartbeat: &state_heartbeat,
1025                            subsystems: GameSubsystems {
1026                                subsystems: &mut self.subsystems,
1027                            },
1028                            time: total_time,
1029                            frame: self.frame,
1030                        },
1031                        fixed_delta_time,
1032                    );
1033                }
1034                for subsystem in &mut self.subsystems {
1035                    subsystem.fixed_update(
1036                        GameContext {
1037                            graphics,
1038                            draw: &mut self.draw,
1039                            gui: &mut self.gui,
1040                            input: &mut self.input,
1041                            state_change: &mut self.state_change,
1042                            multiplayer_change: &mut self.multiplayer_change,
1043                            assets: &mut self.assets,
1044                            audio: &mut self.audio,
1045                            globals: &mut self.globals,
1046                            jobs: Some(&self.jobs),
1047                            network: &mut self.network,
1048                            multiplayer: Some(&mut *self.multiplayer),
1049                            update_queue: &self.next_update_queue,
1050                            fixed_update_queue: &self.next_fixed_update_queue,
1051                            draw_queue: &self.next_draw_queue,
1052                            draw_gui_queue: &self.next_draw_gui_queue,
1053                            state_heartbeat: &state_heartbeat,
1054                            subsystems: GameSubsystems {
1055                                subsystems: &mut [],
1056                            },
1057                            time: total_time,
1058                            frame: self.frame,
1059                        },
1060                        fixed_delta_time,
1061                    );
1062                }
1063                self.fixed_update_queue
1064                    .append(&self.next_fixed_update_queue);
1065                while !self.fixed_update_queue.is_empty() {
1066                    let mut async_context = GameContext {
1067                        graphics,
1068                        draw: &mut self.draw,
1069                        gui: &mut self.gui,
1070                        input: &mut self.input,
1071                        state_change: &mut self.state_change,
1072                        multiplayer_change: &mut self.multiplayer_change,
1073                        assets: &mut self.assets,
1074                        audio: &mut self.audio,
1075                        globals: &mut self.globals,
1076                        jobs: None,
1077                        network: &mut self.network,
1078                        multiplayer: Some(&mut *self.multiplayer),
1079                        update_queue: &self.next_update_queue,
1080                        fixed_update_queue: &self.next_fixed_update_queue,
1081                        draw_queue: &self.next_draw_queue,
1082                        draw_gui_queue: &self.next_draw_gui_queue,
1083                        state_heartbeat: &state_heartbeat,
1084                        subsystems: GameSubsystems {
1085                            subsystems: &mut self.subsystems,
1086                        },
1087                        time: total_time,
1088                        frame: self.frame,
1089                    };
1090                    let (async_context_lazy, _async_context_lifetime) =
1091                        DynamicManagedLazy::make(&mut async_context);
1092                    let (delta_time_lazy, _delta_time_lifetime) =
1093                        DynamicManagedLazy::make(&mut fixed_delta_time);
1094                    let (next_frame_queue_lazy, _next_frame_queue_lifetime) =
1095                        DynamicManagedLazy::make(&mut self.next_fixed_update_queue);
1096                    self.jobs.jobs.run_queue_with_meta(
1097                        &self.fixed_update_queue,
1098                        [
1099                            (CONTEXT_META.into(), async_context_lazy.into()),
1100                            (DELTA_TIME_META.into(), delta_time_lazy.into()),
1101                            (NEXT_FRAME_QUEUE_META.into(), next_frame_queue_lazy.into()),
1102                        ]
1103                        .into_iter()
1104                        .collect(),
1105                    );
1106                }
1107                #[cfg(feature = "editor")]
1108                for subsystem in &mut self.editor.subsystems {
1109                    subsystem.fixed_update(
1110                        GameContext {
1111                            graphics,
1112                            draw: &mut self.draw,
1113                            gui: &mut self.gui,
1114                            input: &mut self.input,
1115                            state_change: &mut self.state_change,
1116                            multiplayer_change: &mut self.multiplayer_change,
1117                            assets: &mut self.assets,
1118                            audio: &mut self.audio,
1119                            globals: &mut self.globals,
1120                            jobs: Some(&self.jobs),
1121                            network: &mut self.network,
1122                            multiplayer: Some(&mut *self.multiplayer),
1123                            update_queue: &self.next_update_queue,
1124                            fixed_update_queue: &self.next_fixed_update_queue,
1125                            draw_queue: &self.next_draw_queue,
1126                            draw_gui_queue: &self.next_draw_gui_queue,
1127                            state_heartbeat: &state_heartbeat,
1128                            subsystems: GameSubsystems {
1129                                subsystems: &mut self.subsystems,
1130                            },
1131                            time: total_time,
1132                            frame: self.frame,
1133                        },
1134                        fixed_delta_time,
1135                    );
1136                }
1137                true
1138            } else {
1139                false
1140            }
1141        };
1142        #[cfg(feature = "editor")]
1143        let fixed_step = if is_editing { false } else { update_phase() };
1144        #[cfg(not(feature = "editor"))]
1145        let fixed_step = update_phase();
1146        self.assets.maintain().unwrap();
1147
1148        self.draw.begin_frame(graphics);
1149        #[cfg(feature = "editor")]
1150        self.editor.begin_frame_capture(graphics, &mut self.draw);
1151        self.draw.push_shader(&ShaderRef::name(self.image_shader));
1152        self.draw.push_blending(GlowBlending::Alpha);
1153        if let Some((state, _, _)) = self.states.last_mut() {
1154            state.draw(GameContext {
1155                graphics,
1156                draw: &mut self.draw,
1157                gui: &mut self.gui,
1158                input: &mut self.input,
1159                state_change: &mut self.state_change,
1160                multiplayer_change: &mut self.multiplayer_change,
1161                assets: &mut self.assets,
1162                audio: &mut self.audio,
1163                globals: &mut self.globals,
1164                jobs: Some(&self.jobs),
1165                network: &mut self.network,
1166                multiplayer: Some(&mut *self.multiplayer),
1167                update_queue: &self.next_update_queue,
1168                fixed_update_queue: &self.next_fixed_update_queue,
1169                draw_queue: &self.next_draw_queue,
1170                draw_gui_queue: &self.next_draw_gui_queue,
1171                state_heartbeat: &state_heartbeat,
1172                subsystems: GameSubsystems {
1173                    subsystems: &mut self.subsystems,
1174                },
1175                time: total_time,
1176                frame: self.frame,
1177            });
1178        }
1179        for subsystem in &mut self.subsystems {
1180            subsystem.draw(GameContext {
1181                graphics,
1182                draw: &mut self.draw,
1183                gui: &mut self.gui,
1184                input: &mut self.input,
1185                state_change: &mut self.state_change,
1186                multiplayer_change: &mut self.multiplayer_change,
1187                assets: &mut self.assets,
1188                audio: &mut self.audio,
1189                globals: &mut self.globals,
1190                jobs: Some(&self.jobs),
1191                network: &mut self.network,
1192                multiplayer: Some(&mut *self.multiplayer),
1193                update_queue: &self.next_update_queue,
1194                fixed_update_queue: &self.next_fixed_update_queue,
1195                draw_queue: &self.next_draw_queue,
1196                draw_gui_queue: &self.next_draw_gui_queue,
1197                state_heartbeat: &state_heartbeat,
1198                subsystems: GameSubsystems {
1199                    subsystems: &mut [],
1200                },
1201                time: total_time,
1202                frame: self.frame,
1203            });
1204        }
1205        self.draw_queue.append(&self.next_draw_queue);
1206        while !self.draw_queue.is_empty() {
1207            let mut async_context = GameContext {
1208                graphics,
1209                draw: &mut self.draw,
1210                gui: &mut self.gui,
1211                input: &mut self.input,
1212                state_change: &mut self.state_change,
1213                multiplayer_change: &mut self.multiplayer_change,
1214                assets: &mut self.assets,
1215                audio: &mut self.audio,
1216                globals: &mut self.globals,
1217                jobs: None,
1218                network: &mut self.network,
1219                multiplayer: Some(&mut *self.multiplayer),
1220                update_queue: &self.next_update_queue,
1221                fixed_update_queue: &self.next_fixed_update_queue,
1222                draw_queue: &self.next_draw_queue,
1223                draw_gui_queue: &self.next_draw_gui_queue,
1224                state_heartbeat: &state_heartbeat,
1225                subsystems: GameSubsystems {
1226                    subsystems: &mut self.subsystems,
1227                },
1228                time: total_time,
1229                frame: self.frame,
1230            };
1231            let (async_context_lazy, _async_context_lifetime) =
1232                DynamicManagedLazy::make(&mut async_context);
1233            let (delta_time_lazy, _delta_time_lifetime) = DynamicManagedLazy::make(&mut delta_time);
1234            let (next_frame_queue_lazy, _next_frame_queue_lifetime) =
1235                DynamicManagedLazy::make(&mut self.next_draw_queue);
1236            self.jobs.jobs.run_queue_with_meta(
1237                &self.draw_queue,
1238                [
1239                    (CONTEXT_META.into(), async_context_lazy.into()),
1240                    (DELTA_TIME_META.into(), delta_time_lazy.into()),
1241                    (NEXT_FRAME_QUEUE_META.into(), next_frame_queue_lazy.into()),
1242                ]
1243                .into_iter()
1244                .collect(),
1245            );
1246        }
1247        #[cfg(feature = "editor")]
1248        {
1249            for subsystem in &mut self.editor.subsystems {
1250                subsystem.draw(GameContext {
1251                    graphics,
1252                    draw: &mut self.draw,
1253                    gui: &mut self.gui,
1254                    input: &mut self.input,
1255                    state_change: &mut self.state_change,
1256                    multiplayer_change: &mut self.multiplayer_change,
1257                    assets: &mut self.assets,
1258                    audio: &mut self.audio,
1259                    globals: &mut self.globals,
1260                    jobs: Some(&self.jobs),
1261                    network: &mut self.network,
1262                    multiplayer: Some(&mut *self.multiplayer),
1263                    update_queue: &self.next_update_queue,
1264                    fixed_update_queue: &self.next_fixed_update_queue,
1265                    draw_queue: &self.next_draw_queue,
1266                    draw_gui_queue: &self.next_draw_gui_queue,
1267                    state_heartbeat: &state_heartbeat,
1268                    subsystems: GameSubsystems {
1269                        subsystems: &mut self.subsystems,
1270                    },
1271                    time: total_time,
1272                    frame: self.frame,
1273                });
1274            }
1275            self.editor.end_frame_capture(graphics, &mut self.draw);
1276            self.draw.push_shader(&ShaderRef::name(self.image_shader));
1277            self.draw.push_blending(GlowBlending::Alpha);
1278        }
1279        self.gui.begin_frame();
1280        #[cfg(feature = "editor")]
1281        self.editor.begin_gui_capture();
1282        if let Some((state, _, _)) = self.states.last_mut() {
1283            state.draw_gui(GameContext {
1284                graphics,
1285                draw: &mut self.draw,
1286                gui: &mut self.gui,
1287                input: &mut self.input,
1288                state_change: &mut self.state_change,
1289                multiplayer_change: &mut self.multiplayer_change,
1290                assets: &mut self.assets,
1291                audio: &mut self.audio,
1292                globals: &mut self.globals,
1293                jobs: Some(&self.jobs),
1294                network: &mut self.network,
1295                multiplayer: Some(&mut *self.multiplayer),
1296                update_queue: &self.next_update_queue,
1297                fixed_update_queue: &self.next_fixed_update_queue,
1298                draw_queue: &self.next_draw_queue,
1299                draw_gui_queue: &self.next_draw_gui_queue,
1300                state_heartbeat: &state_heartbeat,
1301                subsystems: GameSubsystems {
1302                    subsystems: &mut self.subsystems,
1303                },
1304                time: total_time,
1305                frame: self.frame,
1306            });
1307        }
1308        for subsystem in &mut self.subsystems {
1309            subsystem.draw_gui(GameContext {
1310                graphics,
1311                draw: &mut self.draw,
1312                gui: &mut self.gui,
1313                input: &mut self.input,
1314                state_change: &mut self.state_change,
1315                multiplayer_change: &mut self.multiplayer_change,
1316                assets: &mut self.assets,
1317                audio: &mut self.audio,
1318                globals: &mut self.globals,
1319                jobs: Some(&self.jobs),
1320                network: &mut self.network,
1321                multiplayer: Some(&mut *self.multiplayer),
1322                update_queue: &self.next_update_queue,
1323                fixed_update_queue: &self.next_fixed_update_queue,
1324                draw_queue: &self.next_draw_queue,
1325                draw_gui_queue: &self.next_draw_gui_queue,
1326                state_heartbeat: &state_heartbeat,
1327                subsystems: GameSubsystems {
1328                    subsystems: &mut [],
1329                },
1330                time: total_time,
1331                frame: self.frame,
1332            });
1333        }
1334        self.draw_gui_queue.append(&self.next_draw_gui_queue);
1335        while !self.draw_gui_queue.is_empty() {
1336            let mut async_context = GameContext {
1337                graphics,
1338                draw: &mut self.draw,
1339                gui: &mut self.gui,
1340                input: &mut self.input,
1341                state_change: &mut self.state_change,
1342                multiplayer_change: &mut self.multiplayer_change,
1343                assets: &mut self.assets,
1344                audio: &mut self.audio,
1345                globals: &mut self.globals,
1346                jobs: None,
1347                network: &mut self.network,
1348                multiplayer: Some(&mut *self.multiplayer),
1349                update_queue: &self.next_update_queue,
1350                fixed_update_queue: &self.next_fixed_update_queue,
1351                draw_queue: &self.next_draw_queue,
1352                draw_gui_queue: &self.next_draw_gui_queue,
1353                state_heartbeat: &state_heartbeat,
1354                subsystems: GameSubsystems {
1355                    subsystems: &mut self.subsystems,
1356                },
1357                time: total_time,
1358                frame: self.frame,
1359            };
1360            let (async_context_lazy, _async_context_lifetime) =
1361                DynamicManagedLazy::make(&mut async_context);
1362            let (delta_time_lazy, _delta_time_lifetime) = DynamicManagedLazy::make(&mut delta_time);
1363            let (next_frame_queue_lazy, _next_frame_queue_lifetime) =
1364                DynamicManagedLazy::make(&mut self.next_draw_gui_queue);
1365            self.jobs.jobs.run_queue_with_meta(
1366                &self.draw_gui_queue,
1367                [
1368                    (CONTEXT_META.into(), async_context_lazy.into()),
1369                    (DELTA_TIME_META.into(), delta_time_lazy.into()),
1370                    (NEXT_FRAME_QUEUE_META.into(), next_frame_queue_lazy.into()),
1371                ]
1372                .into_iter()
1373                .collect(),
1374            );
1375        }
1376        #[cfg(feature = "editor")]
1377        {
1378            for subsystem in &mut self.editor.subsystems {
1379                subsystem.draw_gui(GameContext {
1380                    graphics,
1381                    draw: &mut self.draw,
1382                    gui: &mut self.gui,
1383                    input: &mut self.input,
1384                    state_change: &mut self.state_change,
1385                    multiplayer_change: &mut self.multiplayer_change,
1386                    assets: &mut self.assets,
1387                    audio: &mut self.audio,
1388                    globals: &mut self.globals,
1389                    jobs: Some(&self.jobs),
1390                    network: &mut self.network,
1391                    multiplayer: Some(&mut *self.multiplayer),
1392                    update_queue: &self.next_update_queue,
1393                    fixed_update_queue: &self.next_fixed_update_queue,
1394                    draw_queue: &self.next_draw_queue,
1395                    draw_gui_queue: &self.next_draw_gui_queue,
1396                    state_heartbeat: &state_heartbeat,
1397                    subsystems: GameSubsystems {
1398                        subsystems: &mut self.subsystems,
1399                    },
1400                    time: total_time,
1401                    frame: self.frame,
1402                });
1403            }
1404            self.editor.end_gui_capture();
1405            self.editor.draw_gui(GameContext {
1406                graphics,
1407                draw: &mut self.draw,
1408                gui: &mut self.gui,
1409                input: &mut self.input,
1410                state_change: &mut self.state_change,
1411                multiplayer_change: &mut self.multiplayer_change,
1412                assets: &mut self.assets,
1413                audio: &mut self.audio,
1414                globals: &mut self.globals,
1415                jobs: Some(&self.jobs),
1416                network: &mut self.network,
1417                multiplayer: Some(&mut *self.multiplayer),
1418                update_queue: &self.next_update_queue,
1419                fixed_update_queue: &self.next_fixed_update_queue,
1420                draw_queue: &self.next_draw_queue,
1421                draw_gui_queue: &self.next_draw_gui_queue,
1422                state_heartbeat: &state_heartbeat,
1423                subsystems: GameSubsystems {
1424                    subsystems: &mut self.subsystems,
1425                },
1426                time: total_time,
1427                frame: self.frame,
1428            });
1429        }
1430        self.gui.end_frame(
1431            &mut self.draw,
1432            graphics,
1433            &ShaderRef::name(self.color_shader),
1434            &ShaderRef::name(self.image_shader),
1435            &ShaderRef::name(self.text_shader),
1436        );
1437        self.draw.end_frame();
1438        #[cfg(feature = "editor")]
1439        self.editor.update(graphics, &self.gui, &mut self.globals);
1440        if !self.input_maintain_on_fixed_step || fixed_step {
1441            self.input.maintain();
1442        }
1443
1444        if !self.next_frame_queue.is_empty() {
1445            self.jobs.jobs.submit_queue(&self.next_frame_queue);
1446        }
1447        while !self
1448            .jobs
1449            .jobs
1450            .queue()
1451            .filter_count(|object| object.location() == &JobLocation::Local)
1452            > 0
1453        {
1454            let mut async_context = GameContext {
1455                graphics,
1456                draw: &mut self.draw,
1457                gui: &mut self.gui,
1458                input: &mut self.input,
1459                state_change: &mut self.state_change,
1460                multiplayer_change: &mut self.multiplayer_change,
1461                assets: &mut self.assets,
1462                audio: &mut self.audio,
1463                globals: &mut self.globals,
1464                jobs: None,
1465                network: &mut self.network,
1466                multiplayer: Some(&mut *self.multiplayer),
1467                update_queue: &self.next_update_queue,
1468                fixed_update_queue: &self.next_fixed_update_queue,
1469                draw_queue: &self.next_draw_queue,
1470                draw_gui_queue: &self.next_draw_gui_queue,
1471                state_heartbeat: &state_heartbeat,
1472                subsystems: GameSubsystems {
1473                    subsystems: &mut self.subsystems,
1474                },
1475                time: total_time,
1476                frame: self.frame,
1477            };
1478            let (async_context_lazy, _async_context_lifetime) =
1479                DynamicManagedLazy::make(&mut async_context);
1480            let (delta_time_lazy, _delta_time_lifetime) = DynamicManagedLazy::make(&mut delta_time);
1481            let (next_frame_queue_lazy, _next_frame_queue_lifetime) =
1482                DynamicManagedLazy::make(&mut self.next_frame_queue);
1483            self.jobs.jobs.run_local_timeout_with_meta(
1484                frame_budget,
1485                [
1486                    (CONTEXT_META.into(), async_context_lazy.into()),
1487                    (DELTA_TIME_META.into(), delta_time_lazy.into()),
1488                    (NEXT_FRAME_QUEUE_META.into(), next_frame_queue_lazy.into()),
1489                ]
1490                .into_iter()
1491                .collect(),
1492            );
1493            if jobs_timer.elapsed() >= frame_budget {
1494                break;
1495            }
1496        }
1497    }
1498
1499    pub fn process_event(&mut self, event: &Event<()>) -> bool {
1500        if let Event::WindowEvent { event, .. } = event {
1501            if let WindowEvent::Focused(focused) = &event {
1502                self.focused = *focused;
1503            }
1504            #[cfg(feature = "editor")]
1505            {
1506                self.editor.event(event, &mut self.gui, &mut self.globals);
1507                self.globals.editor.input.context.on_event(event);
1508            }
1509            self.input.on_event(event);
1510        }
1511        if let Some((state, _, _)) = self.states.last_mut() {
1512            state.event(&mut self.globals, event);
1513        }
1514        for subsystem in &mut self.subsystems {
1515            subsystem.event(&mut self.globals, event);
1516        }
1517        #[cfg(feature = "editor")]
1518        for subsystem in &mut self.editor.subsystems {
1519            subsystem.event(&mut self.globals, event);
1520        }
1521        !self.states.is_empty() || !matches!(self.state_change, GameStateChange::Continue)
1522    }
1523}
1524
1525impl AppState<Vertex> for GameInstance {
1526    fn on_init(&mut self, _graphics: &mut Graphics<Vertex>, _: &mut AppControl) {
1527        #[cfg(feature = "editor")]
1528        {
1529            let temp = Gc::new(());
1530            let state_heartbeat = temp.heartbeat();
1531            self.editor.initialize(GameContext {
1532                graphics: _graphics,
1533                draw: &mut self.draw,
1534                gui: &mut self.gui,
1535                input: &mut self.input,
1536                state_change: &mut self.state_change,
1537                multiplayer_change: &mut self.multiplayer_change,
1538                assets: &mut self.assets,
1539                audio: &mut self.audio,
1540                globals: &mut self.globals,
1541                jobs: Some(&self.jobs),
1542                network: &mut self.network,
1543                multiplayer: Some(&mut *self.multiplayer),
1544                update_queue: &self.next_update_queue,
1545                fixed_update_queue: &self.next_fixed_update_queue,
1546                draw_queue: &self.next_draw_queue,
1547                draw_gui_queue: &self.next_draw_gui_queue,
1548                state_heartbeat: &state_heartbeat,
1549                subsystems: GameSubsystems {
1550                    subsystems: &mut self.subsystems,
1551                },
1552                time: 0.0,
1553                frame: self.frame,
1554            });
1555        }
1556    }
1557
1558    fn on_redraw(&mut self, graphics: &mut Graphics<Vertex>, _: &mut AppControl) {
1559        self.process_frame(graphics);
1560    }
1561
1562    fn on_event(&mut self, event: Event<()>, _: &mut Window) -> bool {
1563        self.process_event(&event)
1564    }
1565}