use std::{collections::HashMap, net::SocketAddr}; use godot::{classes::CharacterBody3D, prelude::*, tools::get_autoload_by_name}; use serde::{Deserialize, Serialize}; use crate::{ ball::Ball, map::Map, map_resource::MapResource, net::{ network_manager::{ BallSync, NetworkManager, Package::{self, Kill, SelfSync, SetTeam, SpawnPlayer, TakeDamage}, PeerKind, SyncPackage, }, util::NetVector3, }, player::{DamageSource, IPlayer, LocalPlayer, NetTransform, RemotePlayer}, team_resource::TeamResource, weapon::WeaponType, }; pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal"; #[derive(GodotClass)] #[class(base=Node, init)] pub struct GameManager { #[export] pub respawn_timer: f64, pub game: Option>, base: Base, #[export] pub maps: Array>, } #[godot_api] impl GameManager { #[signal] pub fn on_players_updated(); #[signal] pub fn on_new_player(id: u16); #[signal] pub fn on_player_disconnected(id: u16); #[signal] pub fn on_map_changed(id: u8); pub fn singleton() -> Gd { get_autoload_by_name::(GAME_MANAGER_GLOBAL) } pub fn init_game(&mut self, is_host: bool) { if let Some(mut game) = self.game.take() { game.queue_free(); } let mut game = Game::new_alloc(); game.bind_mut().is_host = is_host; game.bind_mut().maps = self.maps.clone(); game.bind_mut().respawn_timer = self.respawn_timer; self.run_deferred(|s| { if let Some(game) = &s.game { game.signals() .on_new_player() .connect_other(s, |s, id| s.signals().on_new_player().emit(id)); game.signals() .on_player_disconnected() .connect_other(s, |s, id| s.signals().on_player_disconnected().emit(id)); game.signals() .on_players_updated() .connect_other(s, |s| s.signals().on_players_updated().emit()); game.signals() .on_map_changed() .connect_other(s, |s, id| s.signals().on_map_changed().emit(id)); } }); self.base_mut().add_child(&game); self.game = Some(game); } } pub struct GameOptions { pub values: HashMap, } impl Default for GameOptions { fn default() -> Self { let mut opts = HashMap::new(); opts.insert(GameOption::AllowAnyMap, GameOptionValue::Boolean(false)); opts.insert(GameOption::AllowAnyTeam, GameOptionValue::Boolean(false)); Self { values: opts } } } impl GameOptions { pub fn is_true(&self, opt: GameOption) -> bool { let opt = self .values .get(&opt) .copied() .unwrap_or(GameOptionValue::Boolean(false)); match opt { GameOptionValue::Boolean(value) => value, _ => false, } } } #[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Copy, Clone, Serialize, Deserialize)] pub enum GameOption { /// Allow anyone to select a map, not just the host AllowAnyMap, /// Allow anyone to select anyone's team, not just themselves and the host AllowAnyTeam, } impl ToString for GameOption { fn to_string(&self) -> String { match self { GameOption::AllowAnyMap => "Allow anyone to select the map", GameOption::AllowAnyTeam => "Allow anyone to select any teams", } .to_owned() } } #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum GameOptionValue { Boolean(bool), } #[derive(GodotClass)] #[class(base=Node, init)] pub struct Game { #[export] pub respawn_timer: f64, pub selected_map_idx: u8, pub selected_map: Option>, pub maps: Array>, pub opts: GameOptions, is_host: bool, player_counter: u16, pub self_id: Option, pub players: Vec, game_started: bool, pub current_map: Option>, pub world_ball: Option>, since_last_sync: f64, base: Base, } #[godot_api] impl INode for Game { fn physics_process(&mut self, delta: f64) { self.since_last_sync += delta; if self.since_last_sync > (1. / 60.) { self.since_last_sync = 0.; if let Some(_) = self.current_map { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { match peer { PeerKind::Client(peer, socket_addr) => { if let Some(player) = self .players .iter() .find(|p| p.id() == self.self_id.unwrap_or(0)) { if let Some(data) = &player.get_self_sync() { peer.send_unreliable(socket_addr, SelfSync(data.clone())); } } } PeerKind::Server(peer, _) => { let mut sync_data = HashMap::new(); for player in &self.players { if let Some(character) = &player.character { if character.dyn_bind().is_dead() { continue; } } if let (Some(dir), Some(look_up), Some(transform)) = ( player.get_move_dir(), player.get_look_up(), player.get_transform(), ) { sync_data.insert( player.id(), SyncPackage { movement_direction: dir, look_up, transform, }, ); } } peer.broadcast_unreliable(Package::Sync( sync_data, self.world_ball.as_ref().map(|b| b.bind().get_sync()), )); } } } } } if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { if let PeerKind::Server(peer, _) = peer { for player in self.players.clone() { let player_id = player.id(); if let Some(character) = player.character { if character.dyn_bind().dead_for() > self.respawn_timer { let character = self.spawn_player_server(player_id); if let Some(character) = character { peer.broadcast_reliable(SpawnPlayer( player_id, character.dyn_bind().get_transform(), )); } } } } } } } } #[godot_api] impl Game { #[signal] pub fn on_players_updated(); #[signal] pub fn on_new_player(id: u16); #[signal] pub fn on_player_disconnected(id: u16); #[signal] pub fn on_player_team_changed(id: u16, team: u8); #[signal] pub fn on_map_changed(id: u8); #[signal] pub fn options_changed(); pub fn singleton() -> Option> { GameManager::singleton() .get_child(0) .map(|g| g.cast::()) } pub fn change_option(&mut self, opt: GameOption, value: GameOptionValue) { self.opts.values.insert(opt, value); self.run_deferred(move |s| { s.signals().options_changed().emit(); if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { if let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Package::GameOptionChanged(opt, value)); } } }); } pub fn change_map_selection(&mut self, map_idx: u8, emit: bool) { self.selected_map_idx = map_idx; if let Some(map) = self.maps.get(map_idx as usize) { self.selected_map = Some(map); let teams_max = (self.get_teams().len() - 1) as u8; self.run_deferred(move |s| { if NetworkManager::singleton().bind().is_host() { for player in s.players.clone() { s.run_deferred(move |s| { s.try_set_player_team( player.id(), player.data.team.clamp(0, teams_max), true, ) }); } } }); if emit { self.run_deferred(move |s| s.signals().on_map_changed().emit(map_idx)); } } } pub fn get_teams(&self) -> Array> { if let Some(map) = &self.selected_map { map.bind().teams.clone() } else { Array::new() } } pub fn get_team(&self, team: u8) -> Option> { self.get_teams().get(team as usize) } pub fn try_set_player_team(&mut self, player_id: u16, team: u8, emit: bool) { if let Some(player) = self.find_player_mut(player_id) { player.data.team = team; self.run_deferred(move |_| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { match peer { PeerKind::Client(peer, socket_addr) => { peer.send_reliable(socket_addr, SetTeam(player_id, team)); } PeerKind::Server(peer, _) => { peer.broadcast_reliable(SetTeam(player_id, team)); } } } }); if emit { self.run_deferred(move |s| { s.signals().on_player_team_changed().emit(player_id, team) }); } } } pub fn handle_player_team_change(&mut self, player_id: u16, team: u8) { if let Some(player) = self.find_player_mut(player_id) { player.data.team = team; self.run_deferred(move |s| s.signals().on_player_team_changed().emit(player_id, team)); } } pub fn start_game(&mut self) { self.game_started = true; if let Some(map) = &self.selected_map { if let Some(scene) = &map.bind().scene { self.base().get_tree().change_scene_to_packed(scene); } } self.run_deferred(|_| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { if let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Package::GameStarted); peer.set_accepting_connections(false); } } }); } pub fn set_self(&mut self, id: u16) { self.self_id = Some(id); } pub fn new_player(&mut self, addr: SocketAddr, name: Option, id: Option) -> u16 { let id = if let Some(id) = id { id } else { let id = self.player_counter; self.player_counter += 1; id }; self.players.push(Player { connection_addr: addr, data: NetPlayer { id, name: name.unwrap_or(format!("Player {}", id)), ping: 0, team: 0, }, ready: false, character: None, }); self.run_deferred(move |s| s.signals().on_new_player().emit(id)); let team_with_least = self .get_teams() .iter_shared() .enumerate() .min_by(|(team_idx_a, _), (team_idx_b, _)| { let a = self .players .iter() .filter(|p| p.data.team == *team_idx_a as u8) .count(); let b = self .players .iter() .filter(|p| p.data.team == *team_idx_b as u8) .count(); a.cmp(&b) }) .map(|(team_idx, _)| team_idx) .unwrap_or(0); self.try_set_player_team(id, team_with_least as u8, true); id } pub fn set_players(&mut self, players: Vec) { self.players = players; for player in self.players.clone() { self.run_deferred(move |s| s.signals().on_new_player().emit(player.id())); } } pub fn find_player_by_addr(&self, addr: &SocketAddr) -> Option<&Player> { self.players.iter().find(|p| p.connection_addr == *addr) } pub fn find_player_by_addr_mut(&mut self, addr: &SocketAddr) -> Option<&mut Player> { self.players.iter_mut().find(|p| p.connection_addr == *addr) } pub fn find_player(&self, player_id: u16) -> Option<&Player> { self.players.iter().find(|p| p.id() == player_id) } pub fn find_player_mut(&mut self, player_id: u16) -> Option<&mut Player> { self.players.iter_mut().find(|p| p.id() == player_id) } pub fn remove_player(&mut self, id: u16) { self.players.retain(|p| p.id() != id); self.run_deferred(move |s| s.signals().on_player_disconnected().emit(id)); } pub fn update_player_nick(&mut self, player_id: u16, nick: String) { if let Some(player) = self.players.iter_mut().find(|p| p.id() == player_id) { player.data.name = nick; } self.run_deferred(|s| s.signals().on_players_updated().emit()); } pub fn update_player_ping(&mut self, player_id: u16, ping: u128) { if let Some(player) = self.players.iter_mut().find(|p| p.id() == player_id) { player.data.ping = ping; } self.run_deferred(|s| s.signals().on_players_updated().emit()); } pub fn update_player_ready(&mut self, player_id: u16, ready: bool) { if let Some(player) = self.players.iter_mut().find(|p| p.id() == player_id) { player.ready = ready; } self.run_deferred(|s| s.signals().on_players_updated().emit()); } pub fn on_map_loaded(&mut self, map: Gd) { self.current_map = Some(map); if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { if let PeerKind::Client(peer, to) = peer { peer.send_reliable(to, Package::MapLoaded); } else { self.run_deferred(|s| { for player in s.players.clone() { s.spawn_player_server(player.id()); } s.spawn_ball(None, None, None); }); } } } pub fn spawn_ball( &mut self, transform: Option, velocity: Option, player_id: Option, ) { if let Some(map) = &mut self.current_map { let ball = map.bind_mut().spawn_ball(transform, velocity, player_id); if let Some(ball) = &ball { self.world_ball = Some(ball.clone()); ball.signals() .on_ball_pickup() .connect_other(self, |s, player_id| s.handle_ball_pickup(player_id)); } self.run_deferred(move |_| { if let Some(ball) = ball && let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Package::SpawnBall( ball.bind().get_transform(), ball.bind().get_velocity(), player_id, )); } }); } } pub fn despawn_ball(&mut self) { if let Some(mut ball) = self.world_ball.take() { ball.queue_free(); self.run_deferred(|_| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Package::DespawnBall); } }); } } pub fn respawn_ball(&mut self) { self.despawn_ball(); self.spawn_ball(None, None, None); } pub fn handle_ball_pickup(&mut self, player_id: u16) { if NetworkManager::singleton().bind_mut().is_host() { self.despawn_ball(); self.run_deferred(move |s| s.handle_swap_weapon(player_id, WeaponType::Ball, true)); } } // Handle a swap-weapon command package pub fn handle_swap_weapon( &mut self, player_id: u16, weapon_type: WeaponType, allow_ball: bool, ) { if weapon_type == WeaponType::Ball && !allow_ball { return; } if let Some(player) = self.find_player_mut(player_id) && let Some(character) = &mut player.character { character.dyn_bind_mut().swap_weapon(weapon_type); let player_id = player.id(); self.run_deferred(move |_| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Package::SwapWeapon(player_id, weapon_type)); } }); } } pub fn spawn_player_server(&mut self, id: u16) -> Option> { if let Some(player) = self.find_player_mut(id) && let Some(character) = &mut player.character { // This is a respawn, remove old character character.queue_free(); } if let Some(mut map) = self.current_map.clone() { let mut character = map.bind_mut() .spawn_player(id, id == self.self_id.unwrap_or(0), None); if let Some(character) = &mut character { if let Ok(mut local_player) = character.clone().try_cast::() { local_player.bind_mut().signals().on_die().connect_other( self, move |s, signal, spid| { s.on_death(DamageSource::from_signal(signal, spid), id) }, ); local_player .bind_mut() .signals() .on_take_damage() .connect_other(self, move |s, signal, spid, damage| { s.on_take_damage(DamageSource::from_signal(signal, spid), id, damage) }); } else if let Ok(mut remote_player) = character.clone().try_cast::() { remote_player.bind_mut().signals().on_die().connect_other( self, move |s, signal, spid| { s.on_death(DamageSource::from_signal(signal, spid), id) }, ); remote_player .bind_mut() .signals() .on_take_damage() .connect_other(self, move |s, signal, spid, damage| { s.on_take_damage(DamageSource::from_signal(signal, spid), id, damage) }); } } if let Some(player) = self.find_player_mut(id) { player.character = character.clone(); } character } else { None } } // Method called on the client-side whenever a player has spawned pub fn spawn_player_client(&mut self, id: u16, transform: NetTransform) { if let Some(map) = &mut self.current_map { if let Some(player) = self.players.iter_mut().find(|p| p.id() == id) { if let Some(character) = &mut player.character { // This is a respawn, delete old character character.queue_free(); } let character = map.bind_mut().spawn_player( player.id(), player.id() == self.self_id.unwrap_or(0), Some(transform), ); player.character = character; } } } // Handle a sync package from the server pub fn handle_sync( &mut self, sync_data: HashMap, ball_sync: Option, ) { for ( player_id, SyncPackage { movement_direction, look_up, transform, }, ) in sync_data { if let Some(player) = self.players.iter_mut().find(|p| p.id() == player_id) { if player.id() != self.self_id.unwrap_or(0) { // Sync remote players always player.set_transform(transform); player.set_move_dir(movement_direction); player.apply_look_up(look_up); } else { // Sync local player only if it's far enough if let Some(local_net_transform) = player.get_transform() { let local_location: Vector3 = local_net_transform.location.into(); let remote_location: Vector3 = transform.location.into(); if local_location.distance_squared_to(remote_location) > 2. { player.set_transform(transform); } } } } } if let Some(ball_sync) = ball_sync && let Some(ball) = &mut self.world_ball { ball.bind_mut().apply_sync(ball_sync); } } // Handle a jump, either from the server or from a client pub fn handle_jump(&mut self, player_id: u16) { if player_id != self.self_id.unwrap_or(0) { if let Some(player) = self.find_player_mut(player_id) { player.apply_jump(); } } } // Handle shooting, either from the server of from a client pub fn handle_shoot( &mut self, player_id: u16, to: Vector3, transform: NetTransform, look_up: f32, deal_damage: bool, ) -> bool { let self_id = self.self_id.clone().unwrap_or(0); if let Some(player) = self.find_player_mut(player_id) { if player.id() != self_id { player.try_shoot(to, transform, look_up, deal_damage) } else { false } } else { false } } // Handle taking damage, from server to client pub fn handle_take_damage(&mut self, source: DamageSource, player_id: u16, damage: i32) { if let Some(player) = self.find_player_mut(player_id) { if let Some(character) = &mut player.character { character.dyn_bind_mut().take_damage(source, damage, false); } } } // Handle dying, from server to client pub fn handle_kill(&mut self, source: DamageSource, player_id: u16) { if let Some(player) = self.find_player_mut(player_id) { if let Some(character) = &mut player.character { character.dyn_bind_mut().kill(source); } } } // Called whenever a player takes damage on the server-side pub fn on_take_damage(&mut self, source: DamageSource, target_player_id: u16, damage: i32) { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { if let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(TakeDamage(source, target_player_id, damage)); } } } // Called whenever a player dies on the server-side pub fn on_death(&mut self, source: DamageSource, target_player_id: u16) { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { if let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Kill(source, target_player_id)); } } } } #[derive(Clone, Debug)] pub struct Player { pub connection_addr: SocketAddr, pub data: NetPlayer, pub ready: bool, pub character: Option>, } impl Player { pub fn get_transform(&self) -> Option { if let Some(character) = &self.character { Some(character.dyn_bind().get_transform()) } else { None } } pub fn set_transform(&mut self, transform: NetTransform) { if let Some(character) = &mut self.character { character.dyn_bind_mut().apply_transform(transform); } } pub fn get_move_dir(&self) -> Option { if let Some(character) = &self.character { Some(character.dyn_bind().get_movement_dir().into()) } else { None } } pub fn get_look_up(&self) -> Option { if let Some(character) = &self.character { Some(character.dyn_bind().get_look_up()) } else { None } } pub fn set_move_dir(&mut self, dir: NetVector3) { if let Some(character) = &mut self.character { character.dyn_bind_mut().set_movement_dir(dir.into()); } } pub fn get_self_sync(&self) -> Option { if let Some(character) = &self.character { Some(SyncPackage { movement_direction: character.dyn_bind().get_movement_dir().into(), look_up: character.dyn_bind().get_look_up(), transform: character.dyn_bind().get_transform(), }) } else { None } } pub fn apply_self_sync(&mut self, data: SyncPackage) { if let Some(character) = &mut self.character { character .dyn_bind_mut() .set_movement_dir(data.movement_direction.into()); character .dyn_bind_mut() .apply_rotation(data.transform.rotation.into()); character.dyn_bind_mut().apply_look_up(data.look_up); } } pub fn apply_jump(&mut self) { if let Some(character) = &mut self.character { character.dyn_bind_mut().jump(); } } pub fn apply_look_up(&mut self, rot: f32) { if let Some(character) = &mut self.character { character.dyn_bind_mut().apply_look_up(rot); } } pub fn try_shoot( &mut self, to: Vector3, transform: NetTransform, look_up: f32, deal_damage: bool, ) -> bool { if let Some(character) = &mut self.character { character .dyn_bind_mut() .apply_rotation(transform.rotation.into()); character.dyn_bind_mut().apply_look_up(look_up); character.dyn_bind_mut().try_shoot(to, deal_damage) } else { false } } pub fn id(&self) -> u16 { self.data.id } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct NetPlayer { pub id: u16, pub name: String, pub ping: u128, pub team: u8, } impl From for Player { fn from(value: NetPlayer) -> Self { Player { connection_addr: SocketAddr::from(([0, 0, 0, 0], 0)), data: value, ready: false, character: None, } } } impl From for NetPlayer { fn from(value: Player) -> Self { value.data } }