use std::{ collections::HashMap, net::SocketAddr, time::{Duration, Instant}, }; use godot::{prelude::*, tools::get_autoload_by_name}; use serde::{Deserialize, Serialize}; use teanet::{Peer, PeerConfig, PeerMessage}; use crate::{ game_manager::{Game, GameManager, GameOption, GameOptionValue, NetPlayer}, net::{net_stats::Stats, util::NetVector3}, player::{DamageSource, NetTransform}, ui::cli::{CliColor, CommandLinePanel}, weapon::WeaponType, }; #[derive(GodotClass)] #[class(base=Node)] pub struct NetworkManager { #[export] lobby_scene: Option>, 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 { lobby_scene: None, 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(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() { self.handle_message(message); } } } } impl NetworkManager { 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); let peer = Peer::listen(Some(port), PeerConfig::default()); 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); if let Some(game) = &mut Game::singleton() { let id = game.bind_mut().new_player( SocketAddr::from(([0, 0, 0, 0], 0)), Some("Host".to_owned()), 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); } } } } pub fn join(&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); let peer = Peer::listen(None, PeerConfig::default()); 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(&self, is_host: bool) { if let Some(scene) = &self.lobby_scene { self.base().get_tree().change_scene_to_packed(scene); GameManager::singleton().bind_mut().init_game(is_host); } } 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)); } } } } } #[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), GameOptionChanged(GameOption, GameOptionValue), // 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), TakeDamage(DamageSource, u16, i32), Kill(DamageSource, u16), SpawnBall(NetTransform, NetVector3, Option), DespawnBall, SwapWeapon(u16, WeaponType), Goal(Vec), } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SyncPackage { pub movement_direction: NetVector3, pub look_up: f32, pub transform: NetTransform, } #[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, } } }