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}; use crate::{ game_manager::{Game, GameManager, NetPlayer}, net::{net_stats::Stats, util::NetVector3}, player::{DamageSource, NetPlayerTransform}, ui::cli::{CliColor, CommandLinePanel}, }; #[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 process(&mut self, _delta: f64) { if let Some(peer) = &mut self.peer { let mut cli = CommandLinePanel::singleton(); 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); } } } match peer { PeerKind::Client(peer, _) => match peer.poll() { Ok(message) => { if let Some(message) = message { match message { teanet::PeerMessage::NewConnection(connection) => { cli.bind_mut().publish_message( format!("Connected to: {}", connection.address), CliColor::Info, ); self.swap_to_lobby(false); } teanet::PeerMessage::Disconnected(connection, connection_error) => { cli.bind_mut().publish_message( format!("Disconnected from: {}", connection.address), CliColor::Info, ); if let Some(err) = &connection_error { cli.bind_mut().publish_message( format!("Error with connection: {}", err), CliColor::Error, ); } peer.close(); } teanet::PeerMessage::Closed => { self.peer = None; GameManager::singleton().bind_mut().game = None; } teanet::PeerMessage::Message(_, msg) => match msg { Package::Hello => { cli.bind_mut().publish_message( format!("Got \"Hello\"!"), CliColor::Info, ); } Package::Players(players, self_id) => { cli.bind_mut().publish_message( format!("Got players"), CliColor::Info, ); if let Some(game) = &mut Game::singleton() { game.bind_mut().set_players( players .into_iter() .map(|p| p.clone().into()) .collect(), ); game.bind_mut().set_self(self_id); } } Package::SetNick(id, nick) => { if let Some(game) = &mut Game::singleton() { if game.bind().self_id != Some(id) { game.bind_mut().update_player_nick(id, nick); } } } Package::UpdatePings(pings) => { if let Some(game) = &mut Game::singleton() { for (player_id, ping) in pings { game.bind_mut().update_player_ping(player_id, ping); } } } Package::NewPlayer(player) => { if let Some(game) = &mut Game::singleton() { if player.id != game.bind().self_id.unwrap_or(u16::MAX) { game.bind_mut().new_player( SocketAddr::from(([0, 0, 0, 0], 0)), player.name, Some(player.id), ); } } } Package::PlayerLeft(player) => { if let Some(game) = &mut Game::singleton() { if player.id != game.bind().self_id.unwrap_or(u16::MAX) { game.bind_mut().remove_player(player.id); } } } Package::SetReady(player_id, is_ready) => { if let Some(game) = &mut Game::singleton() { game.bind_mut() .update_player_ready(player_id, is_ready); } } Package::SelectMap(map_id) => { if let Some(game) = &mut Game::singleton() { game.bind_mut().change_map_selection(map_id, true); } } Package::GameStarted => { if let Some(game) = &mut Game::singleton() { game.bind_mut().start_game(); } } Package::SpawnPlayer(id, transform) => { if let Some(game) = &mut Game::singleton() { game.bind_mut().spawn_player_client(id, transform); } } Package::Sync(sync_data) => { if let Some(game) = &mut Game::singleton() { game.bind_mut().handle_sync(sync_data); } } Package::Jump(player_id) => { if let Some(game) = &mut Game::singleton() { game.bind_mut().handle_jump(player_id); } } Package::Shoot(player_id, to, transform, look_up) => { if let Some(game) = &mut Game::singleton() { game.bind_mut().handle_shoot( player_id, to.into(), transform, look_up, false, ); } } Package::TakeDamage( source_player_id, target_player_id, damage, ) => { if let Some(game) = &mut Game::singleton() { game.bind_mut().handle_take_damage( source_player_id, target_player_id, damage, ); } } Package::Kill(source_player_id, target_player_id) => { if let Some(game) = &mut Game::singleton() { game.bind_mut() .handle_kill(source_player_id, target_player_id); } } _ => {} }, } } } Err(err) => { cli.bind_mut() .publish_message(format!("Error: {}", err), CliColor::Info); } }, PeerKind::Server(peer, _) => match peer.poll() { Ok(message) => { if let Some(message) = message { match message { teanet::PeerMessage::NewConnection(connection) => { cli.bind_mut().publish_message( format!("Client connected from: {}", connection.address), CliColor::Info, ); if let Some(game) = &mut Game::singleton() { let self_id = game.bind_mut().new_player( connection.address, String::new(), None, ); peer.send_reliable( &connection.address, Package::Players( game.bind() .players .clone() .into_iter() .map(|p| p.into()) .collect(), self_id, ), ); peer.broadcast_reliable(Package::NewPlayer( game.bind() .find_player(self_id) .unwrap() .clone() .into(), )); peer.send_reliable( &connection.address, Package::SelectMap(game.bind().selected_map_idx), ); } } teanet::PeerMessage::Disconnected(connection, connection_error) => { cli.bind_mut().publish_message( format!("Client disconnected: {}", connection.address), CliColor::Info, ); if let Some(err) = &connection_error { cli.bind_mut().publish_message( format!("Error with connection: {}", err), CliColor::Error, ); } if let Some(game) = &mut Game::singleton() { let player_id = game .bind() .find_player_by_addr(&connection.address) .map(|p| p.id); if let Some(player_id) = player_id { peer.broadcast_reliable(Package::PlayerLeft( game.bind() .find_player(player_id) .unwrap() .clone() .into(), )); game.bind_mut().remove_player(player_id); } } } teanet::PeerMessage::Closed => { cli.bind_mut() .publish_message(format!("Server closed"), CliColor::Info); self.peer = None; GameManager::singleton().bind_mut().game = None; } teanet::PeerMessage::Message(conn, msg) => match msg { Package::SetSelfNick(nick) => { if let Some(game) = &mut Game::singleton() { let player = game .bind() .find_player_by_addr(&conn.address) .cloned(); if let Some(player) = player { peer.broadcast_reliable(Package::SetNick( player.id, nick.clone(), )); game.bind_mut().update_player_nick(player.id, nick); } } } Package::SetReady(_, is_ready) => { if let Some(game) = &mut Game::singleton() { let player = game .bind() .find_player_by_addr(&conn.address) .cloned(); if let Some(player) = player { game.bind_mut() .update_player_ready(player.id, is_ready); peer.broadcast_reliable(Package::SetReady( player.id, is_ready, )); } } } Package::MapLoaded => { if let Some(game) = Game::singleton() { for player in &game.bind().players { if let Some(transform) = player.get_transform() { peer.send_reliable( &conn.address, Package::SpawnPlayer(player.id, transform), ); } } } } Package::SelfSync(sync_data) => { if let Some(game) = &mut Game::singleton() { if let Some(player) = &mut game .bind_mut() .find_player_by_addr_mut(&conn.address) { player.apply_self_sync(sync_data); } } } Package::Jump(_) => { if let Some(game) = &mut Game::singleton() { let player = game .bind() .find_player_by_addr(&conn.address) .cloned(); if let Some(player) = player { game.bind_mut().handle_jump(player.id); peer.broadcast_reliable(Package::Jump(player.id)); } } } Package::Shoot(_, to, transform, look_up) => { if let Some(game) = &mut Game::singleton() { let player = game .bind() .find_player_by_addr(&conn.address) .cloned(); if let Some(player) = player { if game.bind_mut().handle_shoot( player.id, to.into(), transform, look_up, true, ) { peer.broadcast_reliable(Package::Shoot( player.id, to, transform, look_up, )); } } } } _ => {} }, } } } Err(err) => { cli.bind_mut() .publish_message(format!("Error: {}", err), CliColor::Error); } }, } } } } impl NetworkManager { 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)), "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(), } } } 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), // Lobby packages SetReady(u16, bool), SelectMap(u8), GameStarted, MapLoaded, // Game packages SpawnPlayer(u16, NetPlayerTransform), Sync(HashMap), SelfSync(SyncPackage), Jump(u16), Shoot(u16, NetVector3, NetPlayerTransform, f32), TakeDamage(DamageSource, u16, i32), Kill(DamageSource, u16), } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SyncPackage { pub movement_direction: NetVector3, pub look_up: f32, pub transform: NetPlayerTransform, } 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, } } }