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}