use std::{ collections::HashMap, net::{SocketAddr, UdpSocket}, sync::mpsc::{self, Receiver, TryRecvError}, thread, time::{Duration, Instant, SystemTime}, }; use renet::{ transport::{ ClientAuthentication, NetcodeClientTransport, NetcodeServerTransport, ServerAuthentication, ServerConfig, NETCODE_USER_DATA_BYTES, }, ClientId, ConnectionConfig, DefaultChannel, RenetClient, RenetServer, ServerEvent, }; // Helper struct to pass an username in the user data struct Username(String); impl Username { fn to_netcode_user_data(&self) -> [u8; NETCODE_USER_DATA_BYTES] { let mut user_data = [0u8; NETCODE_USER_DATA_BYTES]; if self.0.len() > NETCODE_USER_DATA_BYTES - 8 { panic!("Username is too big"); } user_data[0..8].copy_from_slice(&(self.0.len() as u64).to_le_bytes()); user_data[8..self.0.len() + 8].copy_from_slice(self.0.as_bytes()); user_data } fn from_user_data(user_data: &[u8; NETCODE_USER_DATA_BYTES]) -> Self { let mut buffer = [0u8; 8]; buffer.copy_from_slice(&user_data[0..8]); let mut len = u64::from_le_bytes(buffer) as usize; len = len.min(NETCODE_USER_DATA_BYTES - 8); let data = user_data[8..len + 8].to_vec(); let username = String::from_utf8(data).unwrap(); Self(username) } } fn main() { env_logger::init(); println!("Usage: server [SERVER_PORT] or client [SERVER_ADDR] [USER_NAME]"); let args: Vec = std::env::args().collect(); let exec_type = &args[1]; match exec_type.as_str() { "client" => { let server_addr: SocketAddr = args[2].parse().unwrap(); let username = Username(args[3].clone()); client(server_addr, username); } "server" => { let server_addr: SocketAddr = format!("0.0.0.0:{}", args[2]).parse().unwrap(); server(server_addr); } _ => { println!("Invalid argument, first one must be \"client\" or \"server\"."); } } } const PROTOCOL_ID: u64 = 7; fn server(public_addr: SocketAddr) { let connection_config = ConnectionConfig::default(); let mut server: RenetServer = RenetServer::new(connection_config); let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let server_config = ServerConfig { current_time, max_clients: 64, protocol_id: PROTOCOL_ID, public_addresses: vec![public_addr], authentication: ServerAuthentication::Unsecure, }; let socket: UdpSocket = UdpSocket::bind(public_addr).unwrap(); let mut transport = NetcodeServerTransport::new(server_config, socket).unwrap(); let mut usernames: HashMap = HashMap::new(); let mut received_messages = vec![]; let mut last_updated = Instant::now(); loop { let now = Instant::now(); let duration = now - last_updated; last_updated = now; server.update(duration); transport.update(duration, &mut server).unwrap(); received_messages.clear(); while let Some(event) = server.get_event() { match event { ServerEvent::ClientConnected { client_id } => { let user_data = transport.user_data(client_id).unwrap(); let username = Username::from_user_data(&user_data); server.broadcast_message_except( client_id, DefaultChannel::ReliableOrdered, format!("User \"{}\" connected", username.0), ); usernames.insert(client_id, username.0); println!("Client {} connected.", client_id) } ServerEvent::ClientDisconnected { client_id, reason } => { println!("Client {} disconnected: {}", client_id, reason); if let Some(username) = usernames.remove(&client_id) { server.broadcast_message_except( client_id, DefaultChannel::ReliableOrdered, format!("User \"{}\" disconnected", username), ); } } } } for client_id in server.clients_id() { while let Some(message) = server.receive_message(client_id, DefaultChannel::ReliableOrdered) { let text = String::from_utf8(message.into()).unwrap(); let username = usernames.get(&client_id).unwrap(); println!("Client {} ({}) sent text: {}", username, client_id, text); let text = format!("{}: {}", username, text); received_messages.push(text); } } for text in received_messages.iter() { server.broadcast_message(DefaultChannel::ReliableOrdered, text.as_bytes().to_vec()); } transport.send_packets(&mut server); thread::sleep(Duration::from_millis(50)); } } fn client(server_addr: SocketAddr, username: Username) { let connection_config = ConnectionConfig::default(); let mut client = RenetClient::new(connection_config); let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let client_id = current_time.as_millis() as u64; let authentication = ClientAuthentication::Unsecure { server_addr, client_id, user_data: Some(username.to_netcode_user_data()), protocol_id: PROTOCOL_ID, }; let mut transport = NetcodeClientTransport::new(current_time, authentication, socket).unwrap(); let stdin_channel: Receiver = spawn_stdin_channel(); let mut last_updated = Instant::now(); loop { let now = Instant::now(); let duration = now - last_updated; last_updated = now; client.update(duration); transport.update(duration, &mut client).unwrap(); if client.is_connected() { match stdin_channel.try_recv() { Ok(text) => client.send_message(DefaultChannel::ReliableOrdered, text.as_bytes().to_vec()), Err(TryRecvError::Empty) => {} Err(TryRecvError::Disconnected) => panic!("Channel disconnected"), } while let Some(text) = client.receive_message(DefaultChannel::ReliableOrdered) { let text = String::from_utf8(text.into()).unwrap(); println!("{}", text); } } transport.send_packets(&mut client).unwrap(); thread::sleep(Duration::from_millis(50)); } } fn spawn_stdin_channel() -> Receiver { let (tx, rx) = mpsc::channel::(); thread::spawn(move || loop { let mut buffer = String::new(); std::io::stdin().read_line(&mut buffer).unwrap(); tx.send(buffer.trim_end().to_string()).unwrap(); }); rx }