use std::{ collections::HashMap, net::{SocketAddr, ToSocketAddrs}, time::{Duration, Instant}, }; use godot::{prelude::*, tools::get_autoload_by_name}; use serde::{Deserialize, Serialize}; use teanet::{Peer, PeerConfig, PeerMessage, package::CloseReason}; use crate::{ announcer::Announcement, game::{ Game, GameManager, NetPlayer, opts::{GameOption, GameOptionValue, GameOptions}, }, game_settings::GameSettingsManager, net::{ net_stats::Stats, util::{NetTransform, NetVector3}, }, player::{DamageSource, weapon::WeaponType}, popup_queue::{Popup, PopupQueue}, ui::cli::{CliColor, CommandLinePanel}, }; #[derive(GodotClass)] #[class(base=Node)] pub struct NetworkManager { pub peer: Option, last_hello: Instant, last_netstat_update: Instant, last_player_update: Instant, base: Base, } pub const NETWORK_SINGLETON_NAME: &str = "NetworkManagerGlob"; #[godot_api] impl INode for NetworkManager { fn init(base: Base) -> Self { NetworkManager { peer: None, last_hello: Instant::now(), last_netstat_update: Instant::now(), last_player_update: Instant::now(), base, } } fn ready(&mut self) { let mut cli = CommandLinePanel::singleton(); cli.bind_mut() .signals() .on_host() .connect_other(self, |s, port| s.host(port)); cli.bind_mut() .signals() .on_join() .connect_other(self, |s, addr| s.join_directly(addr.parse().unwrap())); cli.bind_mut() .signals() .on_disconnect() .connect_other(self, |s| s.disconnect()); } fn physics_process(&mut self, _delta: f64) { if let Some(peer) = &mut self.peer { let now = Instant::now(); if now - self.last_netstat_update > Duration::from_millis(1000) { Stats::singleton().bind_mut().update_stats(match peer { PeerKind::Client(peer, _) => peer.statistics(), PeerKind::Server(peer, _) => peer.statistics(), }); self.last_netstat_update = now; } if now - self.last_player_update > Duration::from_millis(500) { self.last_player_update = now; if let PeerKind::Server(peer, _) = peer { let mut pings = HashMap::new(); for connection in peer.connections() { if let Some(mut game) = Game::singleton() { let player = game .bind() .find_player_by_addr(&connection.address) .cloned(); if let Some(player) = player { game.bind_mut() .update_player_ping(player.id(), connection.ping.as_millis()); pings.insert(player.id(), connection.ping.as_millis()); } } } peer.broadcast_reliable(Package::UpdatePings(pings)); } } match peer { PeerKind::Client(..) => {} PeerKind::Server(peer, _) => { if now - self.last_hello > Duration::from_millis(2000) { self.last_hello = now; peer.broadcast_reliable(Package::Hello); } } } while let Some(message) = self.poll() { match &message { PeerMessage::Disconnected(_, connection_error, reason) => { if !self.is_host() { let message = if let Some(err) = connection_error { format!("{}", err) } else { String::new() }; self.signals() .on_client_disconnected() .emit(&format!("{}", reason), &message); } } _ => {} } self.handle_message(message); } } } } #[godot_api] impl NetworkManager { #[signal] pub fn lobby_joined(); #[signal] pub fn on_client_disconnected(reason: GString, error: GString); #[signal] pub fn on_error_hosting_server(error: GString); fn poll(&mut self) -> Option> { let mut cli = CommandLinePanel::singleton(); if let Some(peer) = &mut self.peer { let peer = match peer { PeerKind::Client(peer, _) => peer, PeerKind::Server(peer, _) => peer, }; let result = peer.poll(); match result { Ok(msg) => msg, Err(err) => { cli.bind_mut() .publish_message(format!("Error: {}", err), CliColor::Error); None } } } else { None } } pub fn singleton() -> Gd { get_autoload_by_name::(NETWORK_SINGLETON_NAME) } pub fn is_host(&self) -> bool { if let Some(peer) = &self.peer { if let PeerKind::Server(_, _) = peer { true } else { false } } else { false } } pub fn host(&mut self, port: u16) { if self.peer.is_none() { let mut cli = CommandLinePanel::singleton(); cli.bind_mut() .publish_message(format!("Hosting server at {}", port), CliColor::Info); cli.bind_mut().publish_message( format!("Identifier: {}", NetworkManager::identifier()), CliColor::Info, ); let peer = Peer::listen( Some(port), PeerConfig::default().with_identifier(NetworkManager::identifier()), ); match peer { Ok(peer) => { self.peer = Some(PeerKind::Server(peer, port)); cli.bind_mut() .publish_message(format!("Server hosted"), CliColor::Info); self.swap_to_lobby(true); let default_name = GameSettingsManager::singleton() .bind() .settings .default_name .clone(); if let Some(game) = &mut Game::singleton() { let id = game.bind_mut().new_player( SocketAddr::from(([0, 0, 0, 0], 0)), Some(default_name), None, ); game.bind_mut().update_player_ready(id, true); game.bind_mut().set_self(id); } } Err(err) => { cli.bind_mut() .publish_message(format!("Error hosting server: {}", err), CliColor::Error); self.run_deferred(move |s| { s.signals() .on_error_hosting_server() .emit(&format!("{}", err)); PopupQueue::singleton().bind_mut().queue(Popup { title: "Error hosting server".to_owned(), message: err.to_string(), ok: true, }); }); } } } } pub fn join(&mut self, host: String, port: u16) -> Result<(), ()> { match format!("{}:{}", host, port).to_socket_addrs() { Ok(mut addrs) => { if let Some(addr) = addrs.next() { GameSettingsManager::singleton() .bind_mut() .visit_server(host, port); self.join_directly(addr); Ok(()) } else { Err(()) } } Err(_) => Err(()), } } pub fn join_directly(&mut self, addr: SocketAddr) { if self.peer.is_none() { let mut cli = CommandLinePanel::singleton(); cli.bind_mut() .publish_message(format!("Joining server at {}", addr), CliColor::Info); cli.bind_mut().publish_message( format!("Identifier: {}", NetworkManager::identifier()), CliColor::Info, ); let peer = Peer::listen( None, PeerConfig::default() .with_identifier(NetworkManager::identifier()) .with_additional_ping(Duration::from_millis(200)), ); match peer { Ok(mut peer) => { peer.connect_to(addr); self.peer = Some(PeerKind::Client(peer, addr)); } Err(err) => { cli.bind_mut() .publish_message(format!("Error joining server: {}", err), CliColor::Error); } } } } pub fn disconnect(&mut self) { if let Some(peer) = &mut self.peer { match peer { PeerKind::Client(peer, _) => peer.close(), PeerKind::Server(peer, _) => peer.close(), } } } pub fn swap_to_lobby(&mut self, is_host: bool) { GameManager::singleton().bind_mut().init_game(is_host); GameManager::singleton().bind().go_to_lobby(); self.run_deferred(|s| s.signals().lobby_joined().emit()); } pub fn update_nick(&mut self, nick: String) { if let Some(peer) = &mut self.peer { match peer { PeerKind::Client(peer, addr) => { peer.send_reliable(&addr, Package::SetSelfNick(nick)); } PeerKind::Server(peer, ..) => { let player_id = if let Some(game) = Game::singleton() { game.bind().self_id.unwrap_or(0) } else { 0 }; peer.broadcast_reliable(Package::SetNick(player_id, nick)); } } } } pub fn kick(&mut self, socket_addr: &SocketAddr) { if let Some(peer) = &mut self.peer { match peer { PeerKind::Client(peer, _) => { peer.close(); } PeerKind::Server(peer, _) => { peer.close_connection(socket_addr, CloseReason::Kicked); } } } } fn identifier() -> String { format!("{}@{}", env!("CARGO_PKG_NAME"), env!("GIT_HASH")) } } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Package { Hello, // Meta values Players(Vec, u16), UpdatePings(HashMap), SetNick(u16, String), SetSelfNick(String), NewPlayer(NetPlayer), PlayerLeft(NetPlayer), SetTeam(u16, u8), GameOptions(GameOptions), GameOptionChanged(GameOption, GameOptionValue), TeamGameOptionChanged(u8, GameOption, GameOptionValue), ChatMessage(u16, String), // Lobby packages SetReady(u16, bool), SelectMap(u8), GameStarted, MapLoaded, // Game packages SpawnPlayer(u16, NetTransform), Sync(HashMap, Option), SelfSync(SyncPackage), Jump(u16), Shoot(u16, NetVector3, NetTransform, f32), Punch(u16, NetVector3, NetTransform, f32), OnShot(u16, DamageSource, i32, NetVector3), TakeDamage(DamageSource, u16, i32), Kill(DamageSource, u16), SpawnBall(NetTransform, NetVector3, Option), DespawnBall, SwapWeapon(u16, WeaponType), Goal(u16, Vec), Announcement(u16, Announcement), Pickup(u8, u16), TriggerActivate(u8, u16), TriggerDeactivate(u8), ThrowTelegrenade(u16, NetVector3), TelegrenadeTeleport(u16, NetVector3), TelegrenadeActivate, // End-of-Game packages FinishGame, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SyncPackage { pub movement_direction: NetVector3, pub look_up: f32, pub transform: NetTransform, pub telegrenade: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TelegrenadeSync { pub transform: NetTransform, pub velocity: NetVector3, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BallSync { pub transform: NetTransform, pub velocity: NetVector3, } pub enum PeerKind { Client(Peer, SocketAddr), Server(Peer, u16), } impl PeerKind { pub fn is_host(&self) -> bool { match self { PeerKind::Client(_, _) => false, PeerKind::Server(_, _) => true, } } }