use std::{ net::SocketAddr, time::{Duration, SystemTime, UNIX_EPOCH}, }; use godot::{ classes::{ AudioServer, FileAccess, InputEvent, InputEventKey, InputEventMouse, InputEventMouseButton, InputMap, file_access::ModeFlags, }, global::{Key, MouseButton}, prelude::*, tools::get_autoload_by_name, }; use serde::{Deserialize, Serialize}; pub const NETWORK_SINGLETON_NAME: &str = "GameSettingsGlobal"; pub const MASTER_BUS: &str = "Master"; pub const SFX_BUS: &str = "SFX"; pub const MUSIC_BUS: &str = "Music"; pub const ANNOUNCER_BUS: &str = "Announcer"; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct GameSettings { pub fov: f64, pub default_name: String, pub recent_servers: Vec, pub volume: VolumeSettings, pub keybinds: KeybindSettings, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct VolumeSettings { pub master_volume: f64, pub sfx_volume: f64, pub music_volume: f64, pub announcer_volume: f64, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct KeybindSettings { pub move_left: Keybind, pub move_right: Keybind, pub move_forward: Keybind, pub move_backward: Keybind, pub select_raygun: Keybind, pub select_shotgun: Keybind, pub shoot: Keybind, pub melee: Keybind, pub jump: Keybind, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, PartialOrd)] pub enum Keybind { Key(i32), MouseButton(i32), } impl ToString for Keybind { fn to_string(&self) -> String { match self { Keybind::Key(value) => format!("{:?}", Key::from_godot(*value)), Keybind::MouseButton(value) => format!("{:?}", MouseButton::from_godot(*value)), } } } impl Keybind { pub fn from_event(event: Gd) -> Option { if let Ok(key) = event.clone().try_cast::() { Some(Keybind::Key(key.get_keycode().to_godot())) } else if let Ok(mouse) = event.clone().try_cast::() { Some(Keybind::MouseButton(mouse.get_button_index().to_godot())) } else { None } } pub fn into_event(&self) -> Gd { match self { Keybind::Key(key) => { let mut event = InputEventKey::new_gd(); event.set_keycode(Key::from_godot(*key)); event.upcast() } Keybind::MouseButton(mb) => { let mut event = InputEventMouseButton::new_gd(); event.set_button_index(MouseButton::from_godot(*mb)); event.upcast() } } } } impl VolumeSettings { pub fn master(&self) -> f32 { (self.master_volume / 100.) as f32 } pub fn sfx(&self) -> f32 { (self.sfx_volume / 100.) as f32 } pub fn music(&self) -> f32 { (self.music_volume / 10_000.) as f32 } pub fn announcer(&self) -> f32 { (self.announcer_volume / 100.) as f32 } } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RecentServer { pub addr: SocketAddr, pub visited: u128, } impl Default for GameSettings { fn default() -> Self { Self { fov: 90., default_name: "Default McGee".to_owned(), recent_servers: Vec::new(), volume: VolumeSettings { master_volume: 100., sfx_volume: 100., music_volume: 100., announcer_volume: 100., }, keybinds: KeybindSettings { move_left: Keybind::Key(Key::A.to_godot()), move_right: Keybind::Key(Key::D.to_godot()), move_forward: Keybind::Key(Key::W.to_godot()), move_backward: Keybind::Key(Key::S.to_godot()), select_raygun: Keybind::Key(Key::KEY_1.to_godot()), select_shotgun: Keybind::Key(Key::KEY_2.to_godot()), shoot: Keybind::MouseButton(MouseButton::LEFT.to_godot()), melee: Keybind::Key(Key::F.to_godot()), jump: Keybind::Key(Key::SPACE.to_godot()), }, } } } #[derive(GodotClass)] #[class(base=Node)] pub struct GameSettingsManager { pub settings: GameSettings, base: Base, } #[godot_api] impl INode for GameSettingsManager { fn init(base: Base) -> GameSettingsManager { let mut settings = GameSettings::default(); if let Some(file) = FileAccess::open("user://config.toml", ModeFlags::READ) { if let Ok(deserialized) = toml::from_str(&file.get_as_text().to_string()) { settings = deserialized; } } GameSettingsManager { settings, base } } } #[godot_api] impl GameSettingsManager { #[signal] pub fn on_settings_changed(); pub fn singleton() -> Gd { get_autoload_by_name::(NETWORK_SINGLETON_NAME) } pub fn update_settings(&mut self, settings: GameSettings) { set_bus_volume(MASTER_BUS, settings.volume.master()); set_bus_volume(SFX_BUS, settings.volume.sfx()); set_bus_volume(MUSIC_BUS, settings.volume.music()); set_bus_volume(ANNOUNCER_BUS, settings.volume.announcer()); set_keybind("right", settings.keybinds.move_right); set_keybind("left", settings.keybinds.move_left); set_keybind("forward", settings.keybinds.move_forward); set_keybind("backward", settings.keybinds.move_backward); set_keybind("weapon_1", settings.keybinds.select_raygun); set_keybind("weapon_2", settings.keybinds.select_shotgun); set_keybind("shoot", settings.keybinds.shoot); set_keybind("punch", settings.keybinds.melee); set_keybind("jump", settings.keybinds.jump); self.settings = settings; if let Some(mut file) = FileAccess::open("user://config.toml", ModeFlags::WRITE) { if let Ok(serialized) = toml::to_string(&self.settings) { file.store_string(&serialized); } } self.run_deferred(|s| s.signals().on_settings_changed().emit()); } pub fn visit_server(&mut self, server: SocketAddr) { if let Some(server) = self .settings .recent_servers .iter_mut() .find(|s| s.addr == server) { server.visited = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or(Duration::from_millis(0)) .as_nanos(); } else { self.settings.recent_servers.push(RecentServer { addr: server, visited: SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or(Duration::from_millis(0)) .as_nanos(), }); } let mut servers = self.settings.recent_servers.clone(); servers.sort_by_key(|s| s.visited); servers = servers.into_iter().rev().collect(); while servers.len() > 10 { servers.remove(0); } let settings = GameSettings { recent_servers: servers, ..self.settings.clone() }; self.update_settings(settings); } } fn set_bus_volume(bus: &str, volume: f32) { let idx = AudioServer::singleton().get_bus_index(bus); AudioServer::singleton().set_bus_volume_linear(idx, volume); } fn set_keybind(action: &str, keybind: Keybind) { InputMap::singleton().action_erase_events(action); InputMap::singleton().action_add_event(action, &keybind.into_event()); }