use std::{collections::HashMap, net::SocketAddr}; use godot::{classes::CharacterBody3D, prelude::*, tools::get_autoload_by_name}; use serde::{Deserialize, Serialize}; use crate::{ announcer::{ Announcement::{self, FirstBlood}, Announcer, }, bgm::{Music, MusicManager}, chat::{Chat, ChatMessage}, map::Map, map_resource::MapResource, net::{ network_manager::{ BallSync, NetworkManager, Package::{ self, Kill, SelfSync, SetTeam, SpawnPlayer, TakeDamage, TelegrenadeTeleport, }, PeerKind, SyncPackage, TelegrenadeSync, }, util::NetVector3, }, pickup::PickupKind, player::{ DamageSource, IPlayer, NetTransform, ball::Ball, local_player::LocalPlayer, remote_player::RemotePlayer, telegrenade::Telegrenade, weapon::WeaponType, }, replay::{ReplayEvent, ReplayPlayback, ReplayRecorder}, team_resource::TeamResource, ui::cli::{Command, CommandLinePanel}, }; pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal"; #[derive(GodotClass)] #[class(base=Node, init)] pub struct GameManager { #[export] pub respawn_timer: f64, #[export] pub lobby_screen: Option>, #[export] pub game_finished_screen: Option>, #[export] pub main_menu: Option>, #[export] pub maps: Array>, #[export] #[export_group(name = "Prefabs")] pub local_player: Option>, #[export] #[export_group(name = "Prefabs")] pub remote_player: Option>, #[export] #[export_group(name = "Prefabs")] pub replay_player: Option>, #[export] #[export_group(name = "Prefabs")] pub ball: Option>, #[export] #[export_group(name = "Prefabs")] pub replay_ball: Option>, #[export] #[export_group(name = "Prefabs")] pub telegrenade: Option>, pub game: Option>, base: Base, exiting: bool, } #[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) { self.exiting = false; 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)); game.signals().on_game_finished().connect_other(s, |s| { if !s.exiting { s.go_to_finish_screen() } }); } }); self.base_mut().add_child(&game); self.game = Some(game); } pub fn exit_game(&mut self) { self.exiting = true; self.run_deferred(|s| { if let Some(mut game) = s.game.take() { game.bind_mut().finish_game(); game.queue_free(); s.go_to_main_menu(); } }); } pub fn go_to_finish_screen(&self) { if let Some(scene) = &self.game_finished_screen { self.base().get_tree().change_scene_to_packed(scene); } } pub fn go_to_lobby(&self) { if let Some(lobby) = &self.lobby_screen { self.base().get_tree().change_scene_to_packed(lobby); } } pub fn go_to_main_menu(&self) { if let Some(scene) = &self.main_menu { self.base().get_tree().change_scene_to_packed(scene); } } } #[derive(Debug)] 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)); opts.insert(GameOption::FriendlyFire, GameOptionValue::Boolean(true)); opts.insert(GameOption::GoalsNeededToWin, GameOptionValue::Number(10.)); opts.insert( GameOption::TelegrenadesAllowed, GameOptionValue::Boolean(true), ); 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, /// Allow anyone to shoot anyone, not just other team's members FriendlyFire, /// The number of goals needed to win a match GoalsNeededToWin, /// Whether telegrenades are allowed or not. TelegrenadesAllowed, } 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", GameOption::FriendlyFire => "Friendly fire", GameOption::GoalsNeededToWin => "Goals needed to win", GameOption::TelegrenadesAllowed => "Telegrenades allowed", } .to_owned() } } #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum GameOptionValue { Boolean(bool), Number(f64), } pub struct RespawnBallCommand; impl Command for RespawnBallCommand { fn execute(&self, _: &mut CommandLinePanel, _: &[&str]) { if let Some(game) = &mut Game::singleton() { game.bind_mut().respawn_ball(); } } fn help(&self) -> &str { "respawns the ball" } } #[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, pub game_started: bool, pub current_map: Option>, pub world_ball: Option>, pub ball_holder: Option, pub goals: HashMap, pub replay_recorder: Option>, pub current_replay_frame_start: usize, pub current_replay_playback: Option>, pub last_replay_clone: f64, base: Base, } #[godot_api] impl INode for Game { fn ready(&mut self) { let chat = Chat::new_alloc(); self.base_mut().add_child(&chat); } fn physics_process(&mut self, delta: f64) { 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(), ) { let telegrenade_sync = if let Some(character) = &player.character && let Some(telegrenade) = character.dyn_bind().get_telegrenade() { Some(TelegrenadeSync { transform: telegrenade.bind().get_transform(), velocity: telegrenade.bind().get_velocity(), }) } else { None }; sync_data.insert( player.id(), SyncPackage { movement_direction: dir, look_up, transform, telegrenade: telegrenade_sync, }, ); } } 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(), )); } } } } } } for player in &mut self.players { player.killing_spree_cd -= delta; if player.killing_spree_cd <= 0. { player.killing_spree = 0; } } if let Some(playback) = &mut self.current_replay_playback && let Some(recorder) = &self.replay_recorder { self.last_replay_clone += delta; if self.last_replay_clone >= 1. { playback.bind_mut().replay = recorder .bind() .replay .clone_since(self.current_replay_frame_start); self.last_replay_clone = 0. } } } } #[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(); #[signal] pub fn on_goal(); #[signal] pub fn on_game_finished(); #[signal] pub fn on_player_killed(player: u16); #[signal] pub fn on_map_loaded(map: Gd); pub fn singleton() -> Option> { if GameManager::singleton().get_children().len() > 0 { GameManager::singleton() .get_child(0) .map(|g| g.cast::()) } else { None } } pub fn start_playback(&mut self, time_delta: f64, spectated_id: u16) { if let Some(recorder) = &self.replay_recorder { let mut playback = ReplayPlayback::new_alloc(); let frame = recorder .bind() .replay .find_frame(recorder.bind().time_elapsed + time_delta); self.current_replay_frame_start = frame; playback.bind_mut().replay = recorder.bind().replay.clone_since(frame); playback.bind_mut().current_map = self.current_map.clone(); playback.bind_mut().current_time = (recorder.bind().time_elapsed + time_delta).max(0.); playback.bind_mut().fast_forwarding = true; playback.bind_mut().spectated_player_id = spectated_id; self.current_replay_playback = Some(playback.clone()); self.base_mut().add_child(&playback); } for player in &mut self.players { if let Some(character) = &mut player.character { character.set_visible(false); } } if let Some(ball) = &mut self.world_ball { ball.set_visible(false); } } pub fn stop_playback(&mut self) { if let Some(mut playback) = self.current_replay_playback.take() { playback.queue_free(); } for player in &mut self.players { if let Some(character) = &mut player.character { character.set_visible(true); } } if let Some(ball) = &mut self.world_ball { ball.set_visible(true); } } 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); } } MusicManager::singleton() .bind_mut() .play(Some(Music::Map(self.selected_map_idx)), false); 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); let mut cli = CommandLinePanel::singleton(); cli.bind_mut() .register_command("respawn_ball".to_string(), Box::new(RespawnBallCommand)); } } }); let recorder = ReplayRecorder::new_alloc(); self.replay_recorder = Some(recorder.clone()); self.base_mut().add_child(&recorder); } pub fn reset_game(&mut self) { if let Some(mut recorder) = self.replay_recorder.take() { recorder.queue_free(); } if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer && let Some(player) = self.find_player_mut(self.self_id.unwrap_or(0)) { player.data.ready = true; peer.broadcast_reliable(Package::SetReady(player.id(), true)); } for player in &mut self.players { player.data.kills = 0; player.data.deaths = 0; } self.goals.clear(); } pub fn finish_game(&mut self) { if let Some(recorder) = &mut self.replay_recorder { recorder.bind_mut().stop_recording(); } self.game_started = false; for player in &mut self.players { if let Some(mut character) = player.character.take() { character.queue_free(); } player.data.ready = false; } self.run_deferred(|s| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { for player in &s.players { peer.broadcast_reliable(Package::SetReady(player.id(), false)); } } }); if let Some(mut ball) = self.world_ball.take() { ball.queue_free(); } self.current_map.take(); self.run_deferred(|s| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { peer.set_accepting_connections(true); peer.broadcast_reliable(Package::FinishGame); } s.signals().on_game_finished().emit() }); let mut cli = CommandLinePanel::singleton(); cli.bind_mut() .unregister_command("respawn_ball".to_string()); } 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 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, kills: 0, deaths: 0, }, killing_spree: 0, killing_spree_cd: 0., character: None, last_held_weapon: WeaponType::Raygun, }); 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) { if let Some(player) = self.find_player_mut(id) && let Some(mut character) = player.character.take() { character.dyn_bind_mut().drop_ball(false); self.run_deferred(move |_| { character.queue_free(); }); } 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.data.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.clone()); 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); }); } } self.signals().on_map_loaded().emit(&map); } pub fn spawn_ball( &mut self, transform: Option, velocity: Option, player_id: Option, ) { if let Some(map) = &mut self.current_map { let mut ball = map.bind_mut().spawn_ball(transform, velocity, player_id); if let Some(ball) = &mut ball { self.world_ball = Some(ball.clone()); ball.signals() .on_ball_pickup() .connect_other(self, |s, player_id| s.handle_ball_pickup(player_id)); if self.current_replay_playback.is_some() { ball.set_visible(false); } } 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(ball_holder) = self.ball_holder.take() && let Some(player) = self.find_player_mut(ball_holder).cloned() { self.handle_swap_weapon(ball_holder, player.last_held_weapon, false, false); } 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_goal(&mut self, teams: Vec) { for team in &teams { self.goals .insert(*team, self.goals.get(&team).copied().unwrap_or(0) + 1); } self.run_deferred(move |s| { s.signals().on_goal().emit(); if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer { if let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Package::Goal(teams)); } } if NetworkManager::singleton().bind().is_host() { let max_goals = s.goals.values().max().copied().unwrap_or(0); let needed_goals = match s .opts .values .get(&GameOption::GoalsNeededToWin) .unwrap_or(&&GameOptionValue::Number(10.)) { GameOptionValue::Boolean(_) => 10, GameOptionValue::Number(value) => *value as u16, }; if max_goals >= needed_goals { s.finish_game(); } } }); } 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, false) }); self.ball_holder = Some(player_id); } } // Handle a swap-weapon command package pub fn handle_swap_weapon( &mut self, player_id: u16, weapon_type: WeaponType, allow_ball: bool, drop_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 { if weapon_type != WeaponType::Ball { player.last_held_weapon = weapon_type; } character.dyn_bind_mut().swap_weapon(weapon_type, drop_ball); 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 id == self.self_id.unwrap_or(0) { self.stop_playback(); } if let Some(player) = self.find_player(id) && let Some(mut character) = player.character.clone() { // This is a respawn, remove old character if let Some(mut recorder) = self.replay_recorder.clone() && let Some(state) = player.get_state(player.clone()) { recorder .bind_mut() .record_event(ReplayEvent::Respawn(id, state)); } character.queue_free(); } if let Some(mut map) = self.current_map.clone() { let team = self.find_player(id).map(|p| p.data.team).unwrap_or(0); let mut character = map.bind_mut() .spawn_player(id, team, id == self.self_id.unwrap_or(0), None, true); if let Some(character) = &mut character { if self.current_replay_playback.is_some() { character.set_visible(false); } if let Some(player) = self.find_player(id) { character .dyn_bind_mut() .swap_weapon(player.last_held_weapon, true); } if let Ok(mut local_player) = character.clone().try_cast::() { local_player.bind_mut().signals().on_die().connect_other( self, move |s, signal, spid, kind| { s.on_death(DamageSource::from_signal(signal, spid, kind), id) }, ); local_player .bind_mut() .signals() .on_take_damage() .connect_other(self, move |s, signal, spid, kind, damage| { s.on_take_damage( DamageSource::from_signal(signal, spid, kind), 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, kind| { s.on_death(DamageSource::from_signal(signal, spid, kind), id) }, ); remote_player .bind_mut() .signals() .on_take_damage() .connect_other(self, move |s, signal, spid, kind, damage| { s.on_take_damage( DamageSource::from_signal(signal, spid, kind), 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 id == self.self_id.unwrap_or(0) { self.stop_playback(); } if let Some(map) = &mut self.current_map { if let Some(player) = self.players.iter_mut().find(|p| p.id() == id) { if let Some(mut character) = player.character.clone() { // This is a respawn, delete old character if let Some(mut recorder) = self.replay_recorder.clone() && let Some(state) = player.get_state(player.clone()) { recorder .bind_mut() .record_event(ReplayEvent::Respawn(id, state)); } character.queue_free(); } let mut character = map.bind_mut().spawn_player( player.id(), player.data.team, player.id() == self.self_id.unwrap_or(0), Some(transform), true, ); if let Some(character) = &mut character { if self.current_replay_playback.is_some() { character.set_visible(false); } character .dyn_bind_mut() .swap_weapon(player.last_held_weapon, true); } 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, telegrenade, }, ) in sync_data { if let Some(player) = self.players.iter_mut().find(|p| p.id() == player_id) { if let Some(telegrenade_sync) = telegrenade && let Some(character) = &mut player.character && let Some(mut telegrenade) = character.dyn_bind().get_telegrenade() { telegrenade.set_global_position(telegrenade_sync.transform.location.into()); telegrenade.set_global_rotation(telegrenade_sync.transform.rotation.into()); telegrenade.set_linear_velocity(telegrenade_sync.velocity.into()); } 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(recorder) = &mut self.replay_recorder { recorder .bind_mut() .record_event(ReplayEvent::Jump(player_id)); } 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 player_id != self_id { if let Some(recorder) = &mut self.replay_recorder { recorder .bind_mut() .record_event(ReplayEvent::Shoot(player_id, to.into())); } if let Some(player) = self.find_player_mut(player_id) { player.try_shoot(to, transform, look_up, deal_damage) } else { false } } else { false } } // Handle shooting, either from the server of from a client pub fn handle_punch( &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 player_id != self_id { if let Some(recorder) = &mut self.replay_recorder { recorder .bind_mut() .record_event(ReplayEvent::Punch(player_id, to.into())); } if let Some(player) = self.find_player_mut(player_id) { player.try_punch(to, transform, look_up, deal_damage) } else { false } } else { false } } // Handle for when a player is killed, run both on client and server pub fn on_kill(&mut self, source: DamageSource, player_id: u16) { if let Some(recorder) = &mut self.replay_recorder { recorder .bind_mut() .record_event(ReplayEvent::Death(source, player_id)); } if let DamageSource::Player(source_player_id) = source && let Some(player) = self.find_player(source_player_id) && let Some(character) = player.character.clone() && let Ok(mut local_player) = character.try_cast::() { local_player.bind_mut().play_local_hit_sound(); } let total_prev_kills: u16 = self.players.iter().map(|p| p.data.kills).sum(); if let Some(chat) = &mut Chat::singleton() { chat.bind_mut() .new_message(ChatMessage::Death(source, player_id)); } let killing_spree = if let DamageSource::Player(source_id) = source && let Some(source_player) = self.find_player_mut(source_id) { source_player.data.kills += 1; source_player.killing_spree += 1; source_player.killing_spree_cd = 10.; source_player.killing_spree } else { 0 }; if let Some(player) = self.find_player_mut(player_id) { player.killing_spree = 0; player.killing_spree_cd = 0.; player.data.deaths += 1; } self.run_deferred(move |s| { s.signals().on_player_killed().emit(player_id); if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { if total_prev_kills == 0 { if let DamageSource::Player(source_id) = source { s.on_announcement(source_id, Announcement::FirstBlood); peer.broadcast_reliable(Package::Announcement(source_id, FirstBlood)); } } let announcement = match killing_spree { 2 => Some(Announcement::DoubleKill), 3 => Some(Announcement::TripleKill), 4 => Some(Announcement::MegaKill), _ => None, }; if let Some(announcement) = announcement { if let DamageSource::Player(source_id) = source { s.on_announcement(source_id, announcement); peer.broadcast_reliable(Package::Announcement(source_id, announcement)); } } } }); } pub fn on_chat_message(&mut self, player_id: u16, message: String) { if let Some(chat) = &mut Chat::singleton() { if let Some(player) = self.find_player(player_id) && let Some(team) = self.get_team(player.data.team) { chat.bind_mut().new_message(ChatMessage::ChatMessage( player.data.name.clone(), team.bind().color, message.clone(), )); } } self.run_deferred(move |_| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Package::ChatMessage(player_id, message.clone())); } }); } pub fn on_announcement(&mut self, player_id: u16, announcement: Announcement) { if let Some(chat) = &mut Chat::singleton() { chat.bind_mut() .new_message(ChatMessage::Announcement(player_id, announcement)); } Announcer::singleton().bind_mut().announce(announcement); } // Handle taking damage, from server to client pub fn handle_take_damage(&mut self, source: DamageSource, player_id: u16, damage: i32) { if let Some(recorder) = &mut self.replay_recorder { recorder .bind_mut() .record_event(ReplayEvent::TakeDamage(player_id, source, damage)); } 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.clone()); self.on_kill(source, player_id); } } } // 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(recorder) = &mut self.replay_recorder { recorder.bind_mut().record_event(ReplayEvent::TakeDamage( target_player_id, source, damage, )); } 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.clone(), target_player_id)); self.on_kill(source, target_player_id); } } } pub fn recorder(&mut self) -> &mut Option> { &mut self.replay_recorder } pub fn on_pickup(&mut self, id: u8, player_id: u16) { self.run_deferred(move |s| { let kind = if let Some(map) = &mut s.current_map && let Some(pickup) = map.bind_mut().pickups.get_mut(&id) { pickup.dyn_bind_mut().on_pickup(); pickup.dyn_bind().kind() } else { PickupKind::Health }; if let Some(player) = s.find_player_mut(player_id) && let Some(character) = &mut player.character { character.dyn_bind_mut().on_pickup(kind); } if let Some(recorder) = &mut s.replay_recorder { recorder .bind_mut() .record_event(ReplayEvent::Pickup(kind, player_id)); } if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(Package::Pickup(id, player_id)); } }); } pub fn spawn_telegrenade( &mut self, location: NetVector3, velocity: NetVector3, ) -> Option> { if let Some(map) = &mut self.current_map { map.bind_mut().spawn_telegrenade(location, velocity) } else { None } } pub fn on_throw_telegrenade(&mut self, player_id: u16, to: NetVector3) { if let Some(player) = self.find_player_mut(player_id) && let Some(character) = &mut player.character { character.dyn_bind_mut().try_throw_telegrenade(to.into()); } } // Handle telegrenade activate from server to client pub fn handle_telegrenade_teleport(&mut self, player_id: u16, to: NetVector3) { if let Some(player) = self.find_player_mut(player_id) && let Some(character) = &mut player.character { character.dyn_bind_mut().telegrenade_teleport(to.into()); } } // Handle telegrenade activation from client to server pub fn handle_telegrenade_activate(&mut self, player_id: u16) { if let Some(player) = self.find_player_mut(player_id) && let Some(character) = &mut player.character { let pos = if let Some(telegrenade) = character.dyn_bind().get_telegrenade() { telegrenade.get_global_position() } else { Vector3::ZERO }; character.dyn_bind_mut().telegrenade_teleport(pos); self.run_deferred(move |_| { if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer && let PeerKind::Server(peer, _) = peer { peer.broadcast_reliable(TelegrenadeTeleport(player_id, pos.into())); } }); } } } #[derive(Clone, Debug)] pub struct Player { pub connection_addr: SocketAddr, pub data: NetPlayer, pub character: Option>, pub killing_spree: u16, pub killing_spree_cd: f64, pub last_held_weapon: WeaponType, } 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(), telegrenade: None, }) } else { None } } pub fn apply_self_sync(&mut self, data: SyncPackage) { if let Some(character) = &mut self.character { let server_pos = character.get_global_position(); let client_pos: Vector3 = data.transform.location.into(); let max_distance = (self.data.ping as f32 / 1000.) * (character.dyn_bind().get_move_speed() + character.dyn_bind().get_y_speed()); if client_pos.distance_to(server_pos) <= max_distance { character.dyn_bind_mut().apply_position(client_pos); } 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 try_punch( &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_punch(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 ready: bool, pub name: String, pub ping: u128, pub team: u8, pub kills: u16, pub deaths: u16, } impl From for Player { fn from(value: NetPlayer) -> Self { Player { connection_addr: SocketAddr::from(([0, 0, 0, 0], 0)), data: value, character: None, killing_spree: 0, killing_spree_cd: 0., last_held_weapon: WeaponType::Raygun, } } } impl From for NetPlayer { fn from(value: Player) -> Self { value.data } }