use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct NetworkManager {
pub player_id: String,
pub is_host: bool,
pub connected_players: HashMap<String, PlayerInfo>,
pub messages: Vec<NetworkMessage>,
pub lobby: LobbyInfo,
last_sync: Instant,
sync_interval: Duration,
prediction_enabled: bool,
server_tick: u64,
pub stats: NetworkStats,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlayerInfo {
pub id: String,
pub name: String,
pub position: (f32, f32, f32), pub rotation: (f32, f32, f32), pub velocity: (f32, f32, f32),
pub score: u32,
pub ping: u32,
pub team: Option<String>,
pub is_ready: bool,
#[serde(skip)]
last_position: (f32, f32, f32),
#[serde(skip, default = "Instant::now")]
last_update: Instant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LobbyInfo {
pub name: String,
pub max_players: usize,
pub is_public: bool,
pub game_mode: String,
pub map: String,
pub password: Option<String>,
}
#[derive(Debug, Clone)]
pub struct NetworkStats {
pub bytes_sent: u64,
pub bytes_received: u64,
pub packets_sent: u64,
pub packets_received: u64,
pub packet_loss: f32,
pub average_ping: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NetworkMessage {
PlayerJoined { id: String, name: String },
PlayerLeft { id: String },
PlayerMove { id: String, x: f32, y: f32, z: f32, tick: u64 },
PlayerRotate { id: String, pitch: f32, yaw: f32, roll: f32 },
PlayerVelocity { id: String, vx: f32, vy: f32, vz: f32 },
PlayerAction { id: String, action: String, data: Vec<u8> },
PlayerShoot { id: String, origin: (f32, f32, f32), direction: (f32, f32, f32) },
PlayerInteract { id: String, target_id: String },
GameState { tick: u64, data: Vec<u8> },
GameEvent { event_type: String, data: Vec<u8> },
LobbyUpdate { lobby: LobbyInfo },
PlayerReady { id: String, ready: bool },
TeamChange { id: String, team: String },
Chat { from: String, message: String, channel: ChatChannel },
Ping { timestamp: u64 },
Pong { timestamp: u64 },
SyncRequest { tick: u64 },
SyncResponse { tick: u64, state: Vec<u8> },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ChatChannel {
Global,
Team,
Whisper(String),
}
impl NetworkManager {
pub fn new(player_id: &str) -> Self {
Self {
player_id: player_id.to_string(),
is_host: false,
connected_players: HashMap::new(),
messages: Vec::new(),
lobby: LobbyInfo::default(),
last_sync: Instant::now(),
sync_interval: Duration::from_millis(50), prediction_enabled: true,
server_tick: 0,
stats: NetworkStats::default(),
}
}
pub fn create_host(player_id: &str, player_name: &str) -> Self {
let mut manager = Self::new(player_id);
manager.is_host = true;
manager.lobby.name = format!("{}'s Lobby", player_name);
manager.add_player(player_id, player_name);
println!("🌐 Host criado: {} ({})", player_name, player_id);
manager
}
pub fn connect_to_host(player_id: &str, player_name: &str, _host_address: &str) -> Self {
let mut manager = Self::new(player_id);
manager.add_player(player_id, player_name);
println!("🌐 Conectando ao host...");
manager
}
fn add_player(&mut self, id: &str, name: &str) {
self.connected_players.insert(
id.to_string(),
PlayerInfo {
id: id.to_string(),
name: name.to_string(),
position: (0.0, 0.0, 0.0),
rotation: (0.0, 0.0, 0.0),
velocity: (0.0, 0.0, 0.0),
score: 0,
ping: 0,
team: None,
is_ready: false,
last_position: (0.0, 0.0, 0.0),
last_update: Instant::now(),
},
);
}
pub fn update_player_position(&mut self, x: f32, y: f32, z: f32) {
if let Some(player) = self.connected_players.get_mut(&self.player_id) {
player.last_position = player.position;
player.position = (x, y, z);
player.last_update = Instant::now();
}
self.send_message(NetworkMessage::PlayerMove {
id: self.player_id.clone(),
x,
y,
z,
tick: self.server_tick,
});
}
pub fn update_player_rotation(&mut self, pitch: f32, yaw: f32, roll: f32) {
if let Some(player) = self.connected_players.get_mut(&self.player_id) {
player.rotation = (pitch, yaw, roll);
}
self.send_message(NetworkMessage::PlayerRotate {
id: self.player_id.clone(),
pitch,
yaw,
roll,
});
}
pub fn update_player_velocity(&mut self, vx: f32, vy: f32, vz: f32) {
if let Some(player) = self.connected_players.get_mut(&self.player_id) {
player.velocity = (vx, vy, vz);
}
self.send_message(NetworkMessage::PlayerVelocity {
id: self.player_id.clone(),
vx,
vy,
vz,
});
}
pub fn send_player_action(&mut self, action: &str, data: Vec<u8>) {
self.send_message(NetworkMessage::PlayerAction {
id: self.player_id.clone(),
action: action.to_string(),
data,
});
}
pub fn send_shoot(&mut self, origin: (f32, f32, f32), direction: (f32, f32, f32)) {
self.send_message(NetworkMessage::PlayerShoot {
id: self.player_id.clone(),
origin,
direction,
});
}
pub fn send_interact(&mut self, target_id: &str) {
self.send_message(NetworkMessage::PlayerInteract {
id: self.player_id.clone(),
target_id: target_id.to_string(),
});
}
pub fn send_chat(&mut self, message: &str, channel: ChatChannel) {
self.send_message(NetworkMessage::Chat {
from: self.player_id.clone(),
message: message.to_string(),
channel,
});
}
pub fn set_ready(&mut self, ready: bool) {
if let Some(player) = self.connected_players.get_mut(&self.player_id) {
player.is_ready = ready;
}
self.send_message(NetworkMessage::PlayerReady {
id: self.player_id.clone(),
ready,
});
}
pub fn change_team(&mut self, team: &str) {
if let Some(player) = self.connected_players.get_mut(&self.player_id) {
player.team = Some(team.to_string());
}
self.send_message(NetworkMessage::TeamChange {
id: self.player_id.clone(),
team: team.to_string(),
});
}
pub fn send_message(&mut self, message: NetworkMessage) {
self.messages.push(message);
self.stats.packets_sent += 1;
}
pub fn tick(&mut self, dt: f32) {
self.server_tick += 1;
if self.last_sync.elapsed() >= self.sync_interval {
self.sync_state();
self.last_sync = Instant::now();
}
self.interpolate_players(dt);
self.process_messages();
}
fn sync_state(&mut self) {
if self.is_host {
let state_data = bincode::serialize(&self.connected_players).unwrap_or_default();
self.send_message(NetworkMessage::GameState {
tick: self.server_tick,
data: state_data,
});
}
}
fn interpolate_players(&mut self, dt: f32) {
if !self.prediction_enabled {
return;
}
for player in self.connected_players.values_mut() {
if player.id == self.player_id {
continue; }
let elapsed = player.last_update.elapsed().as_secs_f32();
if elapsed < 0.1 {
let t = (elapsed / 0.1).min(1.0);
player.position.0 = lerp(player.last_position.0, player.position.0, t);
player.position.1 = lerp(player.last_position.1, player.position.1, t);
player.position.2 = lerp(player.last_position.2, player.position.2, t);
}
if player.velocity != (0.0, 0.0, 0.0) {
player.position.0 += player.velocity.0 * dt;
player.position.1 += player.velocity.1 * dt;
player.position.2 += player.velocity.2 * dt;
}
}
}
pub fn process_messages(&mut self) {
let messages: Vec<_> = self.messages.drain(..).collect();
for message in messages {
self.stats.packets_received += 1;
match message {
NetworkMessage::PlayerJoined { id, name } => {
println!("👤 Jogador entrou: {} ({})", name, id);
self.add_player(&id, &name);
}
NetworkMessage::PlayerLeft { id } => {
println!("👋 Jogador saiu: {}", id);
self.connected_players.remove(&id);
}
NetworkMessage::PlayerMove { id, x, y, z, .. } => {
if let Some(player) = self.connected_players.get_mut(&id) {
player.last_position = player.position;
player.position = (x, y, z);
player.last_update = Instant::now();
}
}
NetworkMessage::PlayerRotate { id, pitch, yaw, roll } => {
if let Some(player) = self.connected_players.get_mut(&id) {
player.rotation = (pitch, yaw, roll);
}
}
NetworkMessage::PlayerVelocity { id, vx, vy, vz } => {
if let Some(player) = self.connected_players.get_mut(&id) {
player.velocity = (vx, vy, vz);
}
}
NetworkMessage::PlayerReady { id, ready } => {
if let Some(player) = self.connected_players.get_mut(&id) {
player.is_ready = ready;
}
}
NetworkMessage::TeamChange { id, team } => {
if let Some(player) = self.connected_players.get_mut(&id) {
player.team = Some(team);
}
}
NetworkMessage::Chat { from, message, channel } => {
let prefix = match channel {
ChatChannel::Global => "💬",
ChatChannel::Team => "👥",
ChatChannel::Whisper(_) => "🔒",
};
println!("{} {}: {}", prefix, from, message);
}
NetworkMessage::GameState { tick, data } => {
if !self.is_host {
if let Ok(players) = bincode::deserialize::<HashMap<String, PlayerInfo>>(&data) {
for (id, player_info) in players {
if id != self.player_id {
self.connected_players.insert(id, player_info);
}
}
}
self.server_tick = tick;
}
}
_ => {}
}
}
}
pub fn player_count(&self) -> usize {
self.connected_players.len()
}
pub fn is_connected(&self) -> bool {
!self.connected_players.is_empty()
}
pub fn get_player(&self, id: &str) -> Option<&PlayerInfo> {
self.connected_players.get(id)
}
pub fn get_all_players(&self) -> Vec<&PlayerInfo> {
self.connected_players.values().collect()
}
pub fn get_team_players(&self, team: &str) -> Vec<&PlayerInfo> {
self.connected_players
.values()
.filter(|p| p.team.as_deref() == Some(team))
.collect()
}
pub fn all_players_ready(&self) -> bool {
!self.connected_players.is_empty()
&& self.connected_players.values().all(|p| p.is_ready)
}
pub fn update_lobby(&mut self, lobby: LobbyInfo) {
self.lobby = lobby.clone();
self.send_message(NetworkMessage::LobbyUpdate { lobby });
}
pub fn set_prediction(&mut self, enabled: bool) {
self.prediction_enabled = enabled;
}
pub fn set_sync_rate(&mut self, hz: u32) {
self.sync_interval = Duration::from_millis(1000 / hz as u64);
}
}
impl Default for NetworkManager {
fn default() -> Self {
Self::new("player_0")
}
}
impl Default for LobbyInfo {
fn default() -> Self {
Self {
name: "Lobby".to_string(),
max_players: 8,
is_public: true,
game_mode: "Deathmatch".to_string(),
map: "default".to_string(),
password: None,
}
}
}
impl Default for NetworkStats {
fn default() -> Self {
Self {
bytes_sent: 0,
bytes_received: 0,
packets_sent: 0,
packets_received: 0,
packet_loss: 0.0,
average_ping: 0,
}
}
}
fn lerp(a: f32, b: f32, t: f32) -> f32 {
a + (b - a) * t
}
#[cfg(feature = "multiplayer")]
pub mod real_network {
use super::*;
use quinn::{Endpoint, ServerConfig, ClientConfig};
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::sync::mpsc;
pub struct NetworkClient {
endpoint: Endpoint,
connection: Option<quinn::Connection>,
rx: mpsc::UnboundedReceiver<NetworkMessage>,
tx: mpsc::UnboundedSender<NetworkMessage>,
}
pub struct NetworkServer {
endpoint: Endpoint,
connections: HashMap<String, quinn::Connection>,
rx: mpsc::UnboundedReceiver<(String, NetworkMessage)>,
tx: mpsc::UnboundedSender<(String, NetworkMessage)>,
}
impl NetworkClient {
pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
let mut endpoint = Endpoint::client("0.0.0.0:0".parse()?)?;
endpoint.set_default_client_config(configure_client());
let (tx, rx) = mpsc::unbounded_channel();
Ok(Self {
endpoint,
connection: None,
rx,
tx,
})
}
pub async fn connect(&mut self, addr: SocketAddr) -> Result<(), Box<dyn std::error::Error>> {
let connection = self.endpoint.connect(addr, "localhost")?.await?;
self.connection = Some(connection);
println!("✅ Conectado ao servidor: {}", addr);
Ok(())
}
pub async fn send(&self, message: NetworkMessage) -> Result<(), Box<dyn std::error::Error>> {
if let Some(conn) = &self.connection {
let data = bincode::serialize(&message)?;
let mut send = conn.open_uni().await?;
send.write_all(&data).await?;
send.finish()?;
}
Ok(())
}
pub fn try_recv(&mut self) -> Option<NetworkMessage> {
self.rx.try_recv().ok()
}
}
impl NetworkServer {
pub async fn new(addr: SocketAddr) -> Result<Self, Box<dyn std::error::Error>> {
let server_config = configure_server()?;
let endpoint = Endpoint::server(server_config, addr)?;
let (tx, rx) = mpsc::unbounded_channel();
println!("🌐 Servidor iniciado em: {}", addr);
Ok(Self {
endpoint,
connections: HashMap::new(),
rx,
tx,
})
}
pub async fn accept(&mut self) -> Result<(), Box<dyn std::error::Error>> {
if let Some(conn) = self.endpoint.accept().await {
let connection = conn.await?;
let remote = connection.remote_address();
let id = format!("{}", remote);
self.connections.insert(id.clone(), connection);
println!("👤 Cliente conectado: {}", id);
}
Ok(())
}
pub async fn send_to(&self, client_id: &str, message: NetworkMessage) -> Result<(), Box<dyn std::error::Error>> {
if let Some(conn) = self.connections.get(client_id) {
let data = bincode::serialize(&message)?;
let mut send = conn.open_uni().await?;
send.write_all(&data).await?;
send.finish()?;
}
Ok(())
}
pub async fn broadcast(&self, message: NetworkMessage) {
for (id, _) in &self.connections {
let _ = self.send_to(id, message.clone()).await;
}
}
}
fn configure_client() -> ClientConfig {
let crypto = rustls::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(SkipServerVerification::new())
.with_no_client_auth();
ClientConfig::new(Arc::new(
quinn::crypto::rustls::QuicClientConfig::try_from(crypto).unwrap()
))
}
fn configure_server() -> Result<ServerConfig, Box<dyn std::error::Error>> {
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()])?;
let cert_der = cert.cert.der().to_vec();
let priv_key = cert.key_pair.serialize_der();
let cert_chain = vec![rustls::pki_types::CertificateDer::from(cert_der.clone())];
let key_der = rustls::pki_types::PrivateKeyDer::try_from(priv_key)?;
let server_crypto = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, key_der)?;
let mut server_config = ServerConfig::with_crypto(Arc::new(
quinn::crypto::rustls::QuicServerConfig::try_from(server_crypto)?
));
let transport_config = Arc::get_mut(&mut server_config.transport).unwrap();
transport_config.max_concurrent_uni_streams(0_u8.into());
Ok(server_config)
}
#[derive(Debug)]
struct SkipServerVerification(Arc<rustls::crypto::CryptoProvider>);
impl SkipServerVerification {
fn new() -> Arc<Self> {
Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider())))
}
}
impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
fn verify_server_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_server_name: &rustls::pki_types::ServerName<'_>,
_ocsp: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls12_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls13_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.0.signature_verification_algorithms.supported_schemes()
}
}
}
#[cfg(feature = "multiplayer")]
pub use real_network::{NetworkClient, NetworkServer};