1#![deny(unsafe_code)]
2#![expect(incomplete_features)]
3#![expect(
4 clippy::identity_op,
5 clippy::option_map_unit_fn,
6 clippy::needless_pass_by_ref_mut )]
8#![deny(clippy::clone_on_ref_ptr)]
9#![feature(extract_if, slice_as_chunks, let_chains, generic_const_exprs)]
10#![recursion_limit = "2048"]
11
12#[macro_use]
13pub mod ui;
14pub mod audio;
15pub mod cli;
16pub mod cmd;
17pub mod controller;
18mod credits;
19#[cfg(feature = "discord")] pub mod discord;
20mod ecs;
21pub mod error;
22pub mod game_input;
23pub mod hud;
24pub mod key_state;
25pub mod menu;
26pub mod mesh;
27pub mod panic_handler;
28pub mod profile;
29pub mod render;
30pub mod run;
31pub mod scene;
32pub mod session;
33pub mod settings;
34#[cfg(feature = "singleplayer")]
35pub mod singleplayer;
36pub mod window;
37
38#[cfg(feature = "singleplayer")]
39use crate::singleplayer::Singleplayer;
40#[cfg(feature = "singleplayer")]
41use crate::singleplayer::SingleplayerState;
42#[cfg(feature = "egui-ui")]
43use crate::ui::egui::EguiState;
44use crate::{
45 audio::AudioFrontend,
46 profile::Profile,
47 render::{Drawer, GlobalsBindGroup},
48 settings::Settings,
49 window::{Event, Window},
50};
51use common::clock::Clock;
52use common_base::span;
53use i18n::LocalizationHandle;
54use std::path::PathBuf;
55
56use std::sync::Arc;
57use tokio::runtime::Runtime;
58
59pub struct GlobalState {
61 pub userdata_dir: PathBuf,
62 pub config_dir: PathBuf,
63 pub settings: Settings,
64 pub profile: Profile,
65 pub window: Window,
66 pub tokio_runtime: Arc<Runtime>,
67 #[cfg(feature = "egui-ui")]
68 pub egui_state: EguiState,
69 pub lazy_init: scene::terrain::SpriteRenderContextLazy,
70 pub audio: AudioFrontend,
71 pub info_message: Option<String>,
72 pub clock: Clock,
73 #[cfg(feature = "singleplayer")]
74 pub singleplayer: SingleplayerState,
75 pub i18n: LocalizationHandle,
77 pub clipboard: iced_winit::Clipboard,
78 pub clear_shadows_next_frame: bool,
80 pub args: crate::cli::Args,
82 #[cfg(feature = "discord")]
84 pub discord: crate::discord::Discord,
85}
86
87impl GlobalState {
88 pub fn on_play_state_changed(&mut self) {
91 self.window.grab_cursor(false);
92 self.window.needs_refresh_resize();
93 }
94
95 pub fn maintain(&mut self) {
96 span!(_guard, "maintain", "GlobalState::maintain");
97 self.audio.maintain();
98 self.window.renderer().maintain()
99 }
100
101 #[cfg(feature = "singleplayer")]
102 pub fn paused(&self) -> bool {
103 self.singleplayer
104 .as_running()
105 .is_some_and(Singleplayer::is_paused)
106 }
107
108 #[cfg(not(feature = "singleplayer"))]
109 pub fn paused(&self) -> bool { false }
110
111 #[cfg(feature = "singleplayer")]
112 pub fn unpause(&self) { self.singleplayer.as_running().map(|s| s.pause(false)); }
113
114 #[cfg(feature = "singleplayer")]
115 pub fn pause(&self) { self.singleplayer.as_running().map(|s| s.pause(true)); }
116}
117
118pub enum Direction {
120 Forwards,
121 Backwards,
122}
123
124pub enum PlayStateResult {
127 Continue,
129 Shutdown,
131 Pop,
133 Push(Box<dyn PlayState>),
135 Switch(Box<dyn PlayState>),
137}
138
139pub trait PlayState {
142 fn enter(&mut self, global_state: &mut GlobalState, direction: Direction);
144
145 fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult;
147
148 fn name(&self) -> &'static str;
150
151 fn capped_fps(&self) -> bool;
153
154 fn globals_bind_group(&self) -> &GlobalsBindGroup;
155
156 fn render(&self, drawer: &mut Drawer<'_>, settings: &Settings);
158
159 fn egui_enabled(&self) -> bool;
161}