From 99fde8b780982fcfa81a476e08fde05eab191133 Mon Sep 17 00:00:00 2001 From: poffo Date: Tue, 9 Aug 2022 18:36:23 -0300 Subject: [PATCH 01/10] Renet: use Transport Trait for sending/receiving packets --- renet/examples/echo.rs | 9 ++--- renet/src/client.rs | 77 ++++++++++++++++++++-------------------- renet/src/error.rs | 10 +++--- renet/src/lib.rs | 2 ++ renet/src/server.rs | 80 ++++++++++++++++++++---------------------- renet/src/transport.rs | 44 +++++++++++++++++++++++ 6 files changed, 133 insertions(+), 89 deletions(-) create mode 100644 renet/src/transport.rs diff --git a/renet/examples/echo.rs b/renet/examples/echo.rs index 6e89b364..1914b85a 100644 --- a/renet/examples/echo.rs +++ b/renet/examples/echo.rs @@ -8,7 +8,7 @@ use std::{ use renet::{ ClientAuthentication, DefaultChannel, RenetClient, RenetConnectionConfig, RenetServer, ServerAuthentication, ServerConfig, ServerEvent, - NETCODE_USER_DATA_BYTES, + UdpTransport, NETCODE_USER_DATA_BYTES, }; // Helper struct to pass an username in the user data @@ -62,10 +62,11 @@ const PROTOCOL_ID: u64 = 7; fn server(addr: SocketAddr) { let socket = UdpSocket::bind(addr).unwrap(); + let transport = UdpTransport::with_socket(socket).unwrap(); let connection_config = RenetConnectionConfig::default(); let server_config = ServerConfig::new(64, PROTOCOL_ID, addr, ServerAuthentication::Unsecure); let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); - let mut server: RenetServer = RenetServer::new(current_time, server_config, connection_config, socket).unwrap(); + let mut server: RenetServer = RenetServer::new(current_time, server_config, connection_config, transport); let mut usernames: HashMap = HashMap::new(); let mut received_messages = vec![]; @@ -111,7 +112,7 @@ fn server(addr: SocketAddr) { } fn client(server_addr: SocketAddr, username: Username) { - let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); + let transport = UdpTransport::new().unwrap(); let connection_config = RenetConnectionConfig::default(); let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let client_id = current_time.as_millis() as u64; @@ -121,7 +122,7 @@ fn client(server_addr: SocketAddr, username: Username) { user_data: Some(username.to_netcode_user_data()), protocol_id: PROTOCOL_ID, }; - let mut client = RenetClient::new(current_time, socket, connection_config, authentication).unwrap(); + let mut client = RenetClient::new(current_time, connection_config, authentication, transport).unwrap(); let stdin_channel = spawn_stdin_channel(); let mut last_updated = Instant::now(); diff --git a/renet/src/client.rs b/renet/src/client.rs index fb21ef10..babf6c0e 100644 --- a/renet/src/client.rs +++ b/renet/src/client.rs @@ -1,16 +1,16 @@ use crate::{ error::{DisconnectionReason, RenetError}, network_info::{ClientPacketInfo, NetworkInfo, PacketInfo}, - RenetConnectionConfig, NUM_DISCONNECT_PACKETS_TO_SEND, + RenetConnectionConfig, Transport, UdpTransport, NUM_DISCONNECT_PACKETS_TO_SEND, }; use log::debug; use rechannel::{error::RechannelError, remote_connection::RemoteConnection, Bytes}; use renetcode::{ConnectToken, NetcodeClient, NetcodeError, NETCODE_KEY_BYTES, NETCODE_MAX_PACKET_BYTES, NETCODE_USER_DATA_BYTES}; -use std::net::UdpSocket; +use std::error::Error; +use std::net::SocketAddr; use std::time::Duration; -use std::{io, net::SocketAddr}; /// Configuration to establishe an secure ou unsecure connection with the server. #[allow(clippy::large_enum_variant)] @@ -32,23 +32,43 @@ pub enum ClientAuthentication { /// A client that establishes an authenticated connection with a server. /// Can send/receive encrypted messages from/to the server. -pub struct RenetClient { +pub struct RenetClient { current_time: Duration, netcode_client: NetcodeClient, - socket: UdpSocket, + transport: T, reliable_connection: RemoteConnection, buffer: [u8; NETCODE_MAX_PACKET_BYTES], client_packet_info: ClientPacketInfo, } -impl RenetClient { +impl RenetClient { + #[doc(hidden)] + pub fn __test() -> Self { + let server_addr = "127.0.0.1:5000".parse().unwrap(); + let transport = UdpTransport::new().unwrap(); + + Self::new( + Duration::ZERO, + Default::default(), + ClientAuthentication::Unsecure { + client_id: 0, + server_addr, + user_data: None, + protocol_id: 0, + }, + transport, + ) + .unwrap() + } +} + +impl RenetClient { pub fn new( current_time: Duration, - socket: UdpSocket, config: RenetConnectionConfig, authentication: ClientAuthentication, + transport: T, ) -> Result { - socket.set_nonblocking(true)?; let reliable_connection = RemoteConnection::new(current_time, config.to_connection_config()); let connect_token: ConnectToken = match authentication { ClientAuthentication::Unsecure { @@ -75,32 +95,13 @@ impl RenetClient { Ok(Self { current_time, buffer: [0u8; NETCODE_MAX_PACKET_BYTES], - socket, + transport, reliable_connection, netcode_client, client_packet_info, }) } - #[doc(hidden)] - pub fn __test() -> Self { - let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); - let server_addr = "127.0.0.1:5000".parse().unwrap(); - - Self::new( - Duration::ZERO, - socket, - Default::default(), - ClientAuthentication::Unsecure { - client_id: 0, - server_addr, - user_data: None, - protocol_id: 0, - }, - ) - .unwrap() - } - pub fn client_id(&self) -> u64 { self.netcode_client.client_id() } @@ -127,7 +128,7 @@ impl RenetClient { match self.netcode_client.disconnect() { Ok((addr, payload)) => { for _ in 0..NUM_DISCONNECT_PACKETS_TO_SEND { - if let Err(e) = send_to(self.current_time, &self.socket, &mut self.client_packet_info, payload, addr) { + if let Err(e) = send_to(self.current_time, &mut self.transport, &mut self.client_packet_info, payload, addr) { log::error!("failed to send disconnect packet to server: {}", e); } } @@ -166,7 +167,7 @@ impl RenetClient { let packets = self.reliable_connection.get_packets_to_send()?; for packet in packets.into_iter() { let (addr, payload) = self.netcode_client.generate_payload_packet(&packet)?; - send_to(self.current_time, &self.socket, &mut self.client_packet_info, payload, addr)?; + send_to(self.current_time, &mut self.transport, &mut self.client_packet_info, payload, addr)?; } } Ok(()) @@ -186,8 +187,8 @@ impl RenetClient { } loop { - let packet = match self.socket.recv_from(&mut self.buffer) { - Ok((len, addr)) => { + let packet = match self.transport.recv_from(&mut self.buffer) { + Ok(Some((len, addr))) => { if addr != self.netcode_client.server_addr() { debug!("Discarded packet from unknown server {:?}", addr); continue; @@ -195,8 +196,8 @@ impl RenetClient { &mut self.buffer[..len] } - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break, - Err(e) => return Err(RenetError::IO(e)), + Ok(None) => break, + Err(e) => return Err(RenetError::Transport(e)), }; let packet_info = PacketInfo::new(self.current_time, packet.len()); @@ -209,7 +210,7 @@ impl RenetClient { self.reliable_connection.update()?; if let Some((packet, addr)) = self.netcode_client.update(duration) { - send_to(self.current_time, &self.socket, &mut self.client_packet_info, packet, addr)?; + send_to(self.current_time, &mut self.transport, &mut self.client_packet_info, packet, addr)?; } self.client_packet_info.update_metrics(); @@ -220,12 +221,12 @@ impl RenetClient { fn send_to( current_time: Duration, - socket: &UdpSocket, + transport: &mut dyn Transport, client_packet_info: &mut ClientPacketInfo, packet: &[u8], address: SocketAddr, -) -> Result { +) -> Result<(), Box> { let packet_info = PacketInfo::new(current_time, packet.len()); client_packet_info.add_packet_sent(packet_info); - socket.send_to(packet, address) + transport.send_to(packet, address) } diff --git a/renet/src/error.rs b/renet/src/error.rs index b2d4b2fc..9a82d878 100644 --- a/renet/src/error.rs +++ b/renet/src/error.rs @@ -9,7 +9,7 @@ use renetcode::DisconnectReason as NetcodeDisconnectReason; pub enum RenetError { Netcode(renetcode::NetcodeError), Rechannel(rechannel::error::RechannelError), - IO(std::io::Error), + Transport(Box), } impl Error for RenetError {} @@ -19,7 +19,7 @@ impl fmt::Display for RenetError { match *self { RenetError::Netcode(ref err) => err.fmt(fmt), RenetError::Rechannel(ref err) => err.fmt(fmt), - RenetError::IO(ref err) => err.fmt(fmt), + RenetError::Transport(ref err) => err.fmt(fmt), } } } @@ -42,9 +42,9 @@ impl From for RenetError { } } -impl From for RenetError { - fn from(inner: std::io::Error) -> Self { - RenetError::IO(inner) +impl From> for RenetError { + fn from(inner: Box) -> Self { + RenetError::Transport(inner) } } diff --git a/renet/src/lib.rs b/renet/src/lib.rs index e3ae4038..a296c92a 100644 --- a/renet/src/lib.rs +++ b/renet/src/lib.rs @@ -4,6 +4,7 @@ mod config; mod error; mod network_info; mod server; +mod transport; pub use rechannel::channel::{BlockChannelConfig, ChannelConfig, DefaultChannel, ReliableChannelConfig, UnreliableChannelConfig}; pub use rechannel::error::{ChannelError, DisconnectionReason, RechannelError}; @@ -16,6 +17,7 @@ pub use config::RenetConnectionConfig; pub use error::RenetError; pub use network_info::NetworkInfo; pub use server::{RenetServer, ServerAuthentication, ServerConfig, ServerEvent}; +pub use transport::{Transport, UdpTransport}; // Reused in the renet_visualizer crate #[doc(hidden)] diff --git a/renet/src/server.rs b/renet/src/server.rs index 8e9ee8e7..686d1135 100644 --- a/renet/src/server.rs +++ b/renet/src/server.rs @@ -1,11 +1,11 @@ use crate::{ network_info::{ClientPacketInfo, NetworkInfo, PacketInfo}, - RenetConnectionConfig, NUM_DISCONNECT_PACKETS_TO_SEND, + RenetConnectionConfig, RenetError, Transport, UdpTransport, NUM_DISCONNECT_PACKETS_TO_SEND, }; use std::{ collections::{HashMap, VecDeque}, - io, + error::Error, net::{SocketAddr, UdpSocket}, time::Duration, }; @@ -17,8 +17,8 @@ use renetcode::{NetcodeServer, ServerResult, NETCODE_KEY_BYTES, NETCODE_USER_DAT /// A server that can establish authenticated connections with multiple clients. /// Can send/receive encrypted messages from/to them. #[derive(Debug)] -pub struct RenetServer { - socket: UdpSocket, +pub struct RenetServer { + transport: T, reliable_server: RechannelServer, netcode_server: NetcodeServer, bandwidth_smoothing_factor: f32, @@ -74,13 +74,18 @@ impl ServerConfig { } } -impl RenetServer { - pub fn new( - current_time: Duration, - server_config: ServerConfig, - connection_config: RenetConnectionConfig, - socket: UdpSocket, - ) -> Result { +impl RenetServer { + #[doc(hidden)] + pub fn __test() -> Self { + let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); + let server_config = ServerConfig::new(64, 0, socket.local_addr().unwrap(), ServerAuthentication::Unsecure); + let transport = UdpTransport::new().unwrap(); + Self::new(Duration::ZERO, server_config, RenetConnectionConfig::default(), transport) + } +} + +impl RenetServer { + pub fn new(current_time: Duration, server_config: ServerConfig, connection_config: RenetConnectionConfig, transport: T) -> Self { let buffer = vec![0u8; connection_config.max_packet_size as usize].into_boxed_slice(); let bandwidth_smoothing_factor = connection_config.bandwidth_smoothing_factor; let reliable_server = RechannelServer::new(current_time, connection_config.to_connection_config()); @@ -99,24 +104,15 @@ impl RenetServer { private_key, ); - socket.set_nonblocking(true)?; - - Ok(Self { - socket, + Self { + transport, netcode_server, reliable_server, bandwidth_smoothing_factor, buffer, clients_packet_info: HashMap::new(), events: VecDeque::new(), - }) - } - - #[doc(hidden)] - pub fn __test() -> Self { - let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); - let server_config = ServerConfig::new(64, 0, socket.local_addr().unwrap(), ServerAuthentication::Unsecure); - Self::new(Duration::ZERO, server_config, RenetConnectionConfig::default(), socket).unwrap() + } } pub fn addr(&self) -> SocketAddr { @@ -135,7 +131,7 @@ impl RenetServer { server_result, current_time, self.bandwidth_smoothing_factor, - &self.socket, + &mut self.transport, &mut self.reliable_server, &mut self.clients_packet_info, &mut self.events, @@ -174,15 +170,15 @@ impl RenetServer { } /// Advances the server by duration, and receive packets from the network. - pub fn update(&mut self, duration: Duration) -> Result<(), io::Error> { + pub fn update(&mut self, duration: Duration) -> Result<(), RenetError> { self.reliable_server.update_connections(duration); self.netcode_server.update(duration); let current_time = self.netcode_server.current_time(); loop { - match self.socket.recv_from(&mut self.buffer) { - Ok((len, addr)) => { + match self.transport.recv_from(&mut self.buffer) { + Ok(Some((len, addr))) => { if let Some(info) = self.clients_packet_info.get_mut(&addr) { let packet_info = PacketInfo::new(current_time, len); info.add_packet_received(packet_info); @@ -193,14 +189,14 @@ impl RenetServer { server_result, current_time, self.bandwidth_smoothing_factor, - &self.socket, + &mut self.transport, &mut self.reliable_server, &mut self.clients_packet_info, &mut self.events, )?; } - Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break, - Err(e) => return Err(e), + Ok(None) => break, + Err(e) => return Err(e)?, }; } @@ -210,7 +206,7 @@ impl RenetServer { server_result, current_time, self.bandwidth_smoothing_factor, - &self.socket, + &mut self.transport, &mut self.reliable_server, &mut self.clients_packet_info, &mut self.events, @@ -227,7 +223,7 @@ impl RenetServer { Err(e) => error!("Failed to encrypt disconnect packet: {}", e), Ok((addr, payload)) => { for _ in 0..NUM_DISCONNECT_PACKETS_TO_SEND { - self.socket.send_to(payload, addr)?; + self.transport.send_to(payload, addr)?; } } }, @@ -269,7 +265,7 @@ impl RenetServer { } /// Send packets to connected clients. - pub fn send_packets(&mut self) -> Result<(), io::Error> { + pub fn send_packets(&mut self) -> Result<(), RenetError> { for client_id in self.reliable_server.connections_id().into_iter() { let packets = match self.reliable_server.get_packets_to_send(&client_id) { Ok(p) => p, @@ -283,7 +279,7 @@ impl RenetServer { for packet in packets.iter() { match self.netcode_server.generate_payload_packet(client_id, packet) { Ok((addr, payload)) => { - send_to(current_time, &self.socket, &mut self.clients_packet_info, payload, addr)?; + send_to(current_time, &mut self.transport, &mut self.clients_packet_info, payload, addr)?; } Err(e) => error!("Failed to encrypt payload packet: {}", e), } @@ -327,15 +323,15 @@ fn handle_server_result( server_result: ServerResult, current_time: Duration, bandwidth_smoothing_factor: f32, - socket: &UdpSocket, + transport: &mut dyn Transport, reliable_server: &mut RechannelServer, packet_infos: &mut HashMap, events: &mut VecDeque, -) -> Result<(), io::Error> { +) -> Result<(), Box> { match server_result { ServerResult::None => {} ServerResult::PacketToSend { payload, addr } => { - send_to(current_time, socket, packet_infos, payload, addr)?; + send_to(current_time, transport, packet_infos, payload, addr)?; } ServerResult::Payload { client_id, payload } => { if !reliable_server.is_connected(&client_id) { @@ -355,7 +351,7 @@ fn handle_server_result( reliable_server.add_connection(&client_id); packet_infos.insert(addr, ClientPacketInfo::new(bandwidth_smoothing_factor)); events.push_back(ServerEvent::ClientConnected(client_id, user_data)); - send_to(current_time, socket, packet_infos, payload, addr)?; + send_to(current_time, transport, packet_infos, payload, addr)?; } ServerResult::ClientDisconnected { client_id, addr, payload } => { events.push_back(ServerEvent::ClientDisconnected(client_id)); @@ -363,7 +359,7 @@ fn handle_server_result( packet_infos.remove(&addr); if let Some(payload) = payload { for _ in 0..NUM_DISCONNECT_PACKETS_TO_SEND { - socket.send_to(payload, addr)?; + transport.send_to(payload, addr)?; } } } @@ -374,14 +370,14 @@ fn handle_server_result( fn send_to( current_time: Duration, - socket: &UdpSocket, + transport: &mut dyn Transport, packet_infos: &mut HashMap, packet: &[u8], addr: SocketAddr, -) -> Result { +) -> Result<(), Box> { if let Some(info) = packet_infos.get_mut(&addr) { let packet_info = PacketInfo::new(current_time, packet.len()); info.add_packet_sent(packet_info); } - socket.send_to(packet, addr) + transport.send_to(packet, addr) } diff --git a/renet/src/transport.rs b/renet/src/transport.rs new file mode 100644 index 00000000..bbe373d2 --- /dev/null +++ b/renet/src/transport.rs @@ -0,0 +1,44 @@ +use std::{ + error::Error, + io, + net::{SocketAddr, UdpSocket}, +}; + +pub trait Transport { + fn recv_from(&mut self, buffer: &mut [u8]) -> Result, Box>; + fn send_to(&mut self, buffer: &[u8], addr: SocketAddr) -> Result<(), Box>; +} + +pub struct UdpTransport(UdpSocket); + +impl UdpTransport { + pub fn new() -> Result { + let addr = SocketAddr::from(([127, 0, 0, 1], 0)); + let socket = UdpSocket::bind(addr)?; + socket.set_nonblocking(true)?; + + Ok(Self(socket)) + } + + pub fn with_socket(socket: UdpSocket) -> Result { + socket.set_nonblocking(true)?; + + Ok(Self(socket)) + } +} + +impl Transport for UdpTransport { + fn send_to(&mut self, buffer: &[u8], addr: SocketAddr) -> Result<(), Box> { + self.0.send_to(buffer, addr)?; + + Ok(()) + } + + fn recv_from(&mut self, buffer: &mut [u8]) -> Result, Box> { + match self.0.recv_from(buffer) { + Ok((len, addr)) => Ok(Some((len, addr))), + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None), + Err(e) => Err(Box::new(e)), + } + } +} From 28966f6df52ed69083a3f9e1ef422de80c3f6396 Mon Sep 17 00:00:00 2001 From: poffo Date: Tue, 9 Aug 2022 18:36:41 -0300 Subject: [PATCH 02/10] Renet: Box transport --- renet/examples/echo.rs | 4 ++-- renet/src/client.rs | 54 ++++++++++++++++++++---------------------- renet/src/server.rs | 40 ++++++++++++++++--------------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/renet/examples/echo.rs b/renet/examples/echo.rs index 1914b85a..f0fb742e 100644 --- a/renet/examples/echo.rs +++ b/renet/examples/echo.rs @@ -62,7 +62,7 @@ const PROTOCOL_ID: u64 = 7; fn server(addr: SocketAddr) { let socket = UdpSocket::bind(addr).unwrap(); - let transport = UdpTransport::with_socket(socket).unwrap(); + let transport = Box::new(UdpTransport::with_socket(socket).unwrap()); let connection_config = RenetConnectionConfig::default(); let server_config = ServerConfig::new(64, PROTOCOL_ID, addr, ServerAuthentication::Unsecure); let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); @@ -112,7 +112,7 @@ fn server(addr: SocketAddr) { } fn client(server_addr: SocketAddr, username: Username) { - let transport = UdpTransport::new().unwrap(); + let transport = Box::new(UdpTransport::new().unwrap()); let connection_config = RenetConnectionConfig::default(); let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let client_id = current_time.as_millis() as u64; diff --git a/renet/src/client.rs b/renet/src/client.rs index babf6c0e..f044e0c7 100644 --- a/renet/src/client.rs +++ b/renet/src/client.rs @@ -32,42 +32,21 @@ pub enum ClientAuthentication { /// A client that establishes an authenticated connection with a server. /// Can send/receive encrypted messages from/to the server. -pub struct RenetClient { +pub struct RenetClient { current_time: Duration, netcode_client: NetcodeClient, - transport: T, + transport: Box, reliable_connection: RemoteConnection, buffer: [u8; NETCODE_MAX_PACKET_BYTES], client_packet_info: ClientPacketInfo, } -impl RenetClient { - #[doc(hidden)] - pub fn __test() -> Self { - let server_addr = "127.0.0.1:5000".parse().unwrap(); - let transport = UdpTransport::new().unwrap(); - - Self::new( - Duration::ZERO, - Default::default(), - ClientAuthentication::Unsecure { - client_id: 0, - server_addr, - user_data: None, - protocol_id: 0, - }, - transport, - ) - .unwrap() - } -} - -impl RenetClient { +impl RenetClient { pub fn new( current_time: Duration, config: RenetConnectionConfig, authentication: ClientAuthentication, - transport: T, + transport: Box, ) -> Result { let reliable_connection = RemoteConnection::new(current_time, config.to_connection_config()); let connect_token: ConnectToken = match authentication { @@ -102,6 +81,25 @@ impl RenetClient { }) } + #[doc(hidden)] + pub fn __test() -> Self { + let server_addr = "127.0.0.1:5000".parse().unwrap(); + let transport = UdpTransport::new().unwrap(); + + Self::new( + Duration::ZERO, + Default::default(), + ClientAuthentication::Unsecure { + client_id: 0, + server_addr, + user_data: None, + protocol_id: 0, + }, + Box::new(transport), + ) + .unwrap() + } + pub fn client_id(&self) -> u64 { self.netcode_client.client_id() } @@ -128,7 +126,7 @@ impl RenetClient { match self.netcode_client.disconnect() { Ok((addr, payload)) => { for _ in 0..NUM_DISCONNECT_PACKETS_TO_SEND { - if let Err(e) = send_to(self.current_time, &mut self.transport, &mut self.client_packet_info, payload, addr) { + if let Err(e) = send_to(self.current_time, &mut *self.transport, &mut self.client_packet_info, payload, addr) { log::error!("failed to send disconnect packet to server: {}", e); } } @@ -167,7 +165,7 @@ impl RenetClient { let packets = self.reliable_connection.get_packets_to_send()?; for packet in packets.into_iter() { let (addr, payload) = self.netcode_client.generate_payload_packet(&packet)?; - send_to(self.current_time, &mut self.transport, &mut self.client_packet_info, payload, addr)?; + send_to(self.current_time, &mut *self.transport, &mut self.client_packet_info, payload, addr)?; } } Ok(()) @@ -210,7 +208,7 @@ impl RenetClient { self.reliable_connection.update()?; if let Some((packet, addr)) = self.netcode_client.update(duration) { - send_to(self.current_time, &mut self.transport, &mut self.client_packet_info, packet, addr)?; + send_to(self.current_time, &mut *self.transport, &mut self.client_packet_info, packet, addr)?; } self.client_packet_info.update_metrics(); diff --git a/renet/src/server.rs b/renet/src/server.rs index 686d1135..8d935eb2 100644 --- a/renet/src/server.rs +++ b/renet/src/server.rs @@ -16,9 +16,8 @@ use renetcode::{NetcodeServer, ServerResult, NETCODE_KEY_BYTES, NETCODE_USER_DAT /// A server that can establish authenticated connections with multiple clients. /// Can send/receive encrypted messages from/to them. -#[derive(Debug)] -pub struct RenetServer { - transport: T, +pub struct RenetServer { + transport: Box, reliable_server: RechannelServer, netcode_server: NetcodeServer, bandwidth_smoothing_factor: f32, @@ -74,18 +73,13 @@ impl ServerConfig { } } -impl RenetServer { - #[doc(hidden)] - pub fn __test() -> Self { - let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); - let server_config = ServerConfig::new(64, 0, socket.local_addr().unwrap(), ServerAuthentication::Unsecure); - let transport = UdpTransport::new().unwrap(); - Self::new(Duration::ZERO, server_config, RenetConnectionConfig::default(), transport) - } -} - -impl RenetServer { - pub fn new(current_time: Duration, server_config: ServerConfig, connection_config: RenetConnectionConfig, transport: T) -> Self { +impl RenetServer { + pub fn new( + current_time: Duration, + server_config: ServerConfig, + connection_config: RenetConnectionConfig, + transport: Box, + ) -> Self { let buffer = vec![0u8; connection_config.max_packet_size as usize].into_boxed_slice(); let bandwidth_smoothing_factor = connection_config.bandwidth_smoothing_factor; let reliable_server = RechannelServer::new(current_time, connection_config.to_connection_config()); @@ -115,6 +109,14 @@ impl RenetServer { } } + #[doc(hidden)] + pub fn __test() -> Self { + let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); + let server_config = ServerConfig::new(64, 0, socket.local_addr().unwrap(), ServerAuthentication::Unsecure); + let transport = UdpTransport::new().unwrap(); + Self::new(Duration::ZERO, server_config, RenetConnectionConfig::default(), Box::new(transport)) + } + pub fn addr(&self) -> SocketAddr { self.netcode_server.address() } @@ -131,7 +133,7 @@ impl RenetServer { server_result, current_time, self.bandwidth_smoothing_factor, - &mut self.transport, + &mut *self.transport, &mut self.reliable_server, &mut self.clients_packet_info, &mut self.events, @@ -189,7 +191,7 @@ impl RenetServer { server_result, current_time, self.bandwidth_smoothing_factor, - &mut self.transport, + &mut *self.transport, &mut self.reliable_server, &mut self.clients_packet_info, &mut self.events, @@ -206,7 +208,7 @@ impl RenetServer { server_result, current_time, self.bandwidth_smoothing_factor, - &mut self.transport, + &mut *self.transport, &mut self.reliable_server, &mut self.clients_packet_info, &mut self.events, @@ -279,7 +281,7 @@ impl RenetServer { for packet in packets.iter() { match self.netcode_server.generate_payload_packet(client_id, packet) { Ok((addr, payload)) => { - send_to(current_time, &mut self.transport, &mut self.clients_packet_info, payload, addr)?; + send_to(current_time, &mut *self.transport, &mut self.clients_packet_info, payload, addr)?; } Err(e) => error!("Failed to encrypt payload packet: {}", e), } From 2ff967cd674492e14d9a96698dc0d4bd667bf84e Mon Sep 17 00:00:00 2001 From: poffo Date: Thu, 8 Sep 2022 21:02:41 -0300 Subject: [PATCH 03/10] Steam: Add transport layer for steam --- Cargo.toml | 2 +- renet/Cargo.toml | 3 + renet_transport_steam/Cargo.toml | 11 ++++ renet_transport_steam/src/lib.rs | 102 +++++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 renet_transport_steam/Cargo.toml create mode 100644 renet_transport_steam/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 5784aafd..2073ae29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["renet", "rechannel", "demo_chat/matcher", "demo_chat/chat", "demo_bevy", "renetcode", "bevy_renet", "renet_visualizer"] +members = ["renet", "rechannel", "demo_chat/matcher", "demo_chat/chat", "demo_bevy", "renetcode", "bevy_renet", "renet_visualizer", "renet_transport_steam"] resolver = "2" diff --git a/renet/Cargo.toml b/renet/Cargo.toml index 3d8efa8c..fa7b813d 100644 --- a/renet/Cargo.toml +++ b/renet/Cargo.toml @@ -14,3 +14,6 @@ edition = "2018" rechannel = { path = "../rechannel", version = "0.0.5" } renetcode = { path = "../renetcode", version = "0.0.5" } log = "0.4.11" + +[dev-dependencies] +env_logger = "0.9.0" diff --git a/renet_transport_steam/Cargo.toml b/renet_transport_steam/Cargo.toml new file mode 100644 index 00000000..b77ac6fa --- /dev/null +++ b/renet_transport_steam/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "renet_transport_steam" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +renet = { path = "../renet", version = "0.0.9" } +steamworks = "0.9.0" +log = "0.4.11" diff --git a/renet_transport_steam/src/lib.rs b/renet_transport_steam/src/lib.rs new file mode 100644 index 00000000..cb49fc64 --- /dev/null +++ b/renet_transport_steam/src/lib.rs @@ -0,0 +1,102 @@ +use std::{ + error::Error, + fmt, io, + net::{Ipv6Addr, SocketAddr, SocketAddrV6}, +}; + +use renet::Transport; +use steamworks::{networking_messages::NetworkingMessages, Client, ClientManager, SteamId, networking_types::{NetworkingIdentity, SendFlags}}; + +pub struct SteamTransport { + networking_messages: NetworkingMessages, +} + +const CHANNEL_ID: u32 = 0; + +#[derive(Debug)] +pub enum SteamTransportError { + InvalidAddress, +} + +impl Error for SteamTransportError {} + +impl fmt::Display for SteamTransportError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + use SteamTransportError::*; + + match *self { + InvalidAddress => write!(fmt, "received invalid address to send to"), + } + } +} + +pub fn address_from_steam_id(steam_id: SteamId) -> SocketAddr { + let raw = steam_id.raw(); + let segments: [u8; 8] = raw.to_le_bytes(); + let segments: [u16; 8] = segments.map(u16::from); + + SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(segments), 0, 0, 0)) +} + +fn steam_id_from_address(address: SocketAddr) -> Result { + if let SocketAddr::V6(address) = address { + let ip = address.ip(); + let segments = ip.segments(); + let segments: [u8; 8] = segments.map(|x| x as u8); + let raw = u64::from_le_bytes(segments); + return Ok(SteamId::from_raw(raw)); + } + + Err(SteamTransportError::InvalidAddress) +} + +impl SteamTransport { + pub fn new(client: &Client) -> Self { + let networking_messages = client.networking_messages(); + + Self { + networking_messages + } + } +} + +impl Transport for SteamTransport { + fn recv_from(&mut self, buffer: &mut [u8]) -> Result, Box> { + let messages = self.networking_messages.receive_messages_on_channel(CHANNEL_ID, 1); + + if let Some(message) = messages.get(0) { + let network_id = message.identity_peer(); + let addr = match network_id.steam_id() { + Some(steam_id) => address_from_steam_id(steam_id), + None => { + log::warn!("Received message without steam id"); + return Ok(None); + } + }; + + let data = message.data(); + if data.len() > buffer.len() { + log::error!( + "Received message bigger than buffer, got {}, expected less than {}", + data.len(), + buffer.len() + ); + + return Ok(None); + } + + buffer[..data.len()].copy_from_slice(data); + return Ok(Some((data.len(), addr))); + } + + Ok(None) + } + + fn send_to(&mut self, buffer: &[u8], addr: SocketAddr) -> Result<(), Box> { + let steam_id = steam_id_from_address(addr)?; + let network_id = NetworkingIdentity::new_steam_id(steam_id); + self.networking_messages.send_message_to_user(network_id, SendFlags::UNRELIABLE_NO_NAGLE, buffer, CHANNEL_ID); + + Ok(()) + } +} From 4a156541455b0a0f3f4fe161f9a23d35fe9e370b Mon Sep 17 00:00:00 2001 From: poffo Date: Thu, 8 Sep 2022 21:03:44 -0300 Subject: [PATCH 04/10] DemoChat: use Steam transport Should be added feature flag for udp/steam when more testing is done using steam. --- demo_chat/chat/Cargo.toml | 2 ++ demo_chat/chat/src/client.rs | 21 ++++++++++++++++----- demo_chat/chat/src/server.rs | 12 ++++++++---- demo_chat/chat/src/ui.rs | 7 +++++-- demo_chat/chat/steam_appid.txt | 1 + 5 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 demo_chat/chat/steam_appid.txt diff --git a/demo_chat/chat/Cargo.toml b/demo_chat/chat/Cargo.toml index c4fcbc5f..858dfc62 100644 --- a/demo_chat/chat/Cargo.toml +++ b/demo_chat/chat/Cargo.toml @@ -11,6 +11,8 @@ edition = "2018" matcher = { path = "../matcher" } renet = { path = "../../renet" } renet_visualizer = { path = "../../renet_visualizer" } +renet_transport_steam = { path = "../../renet_transport_steam" } +steamworks = "0.9.0" eframe = "0.19" egui_extras = "0.19" serde = { version = "1.0", features = [ "derive" ] } diff --git a/demo_chat/chat/src/client.rs b/demo_chat/chat/src/client.rs index d962005e..081de48f 100644 --- a/demo_chat/chat/src/client.rs +++ b/demo_chat/chat/src/client.rs @@ -3,7 +3,9 @@ use eframe::egui; use log::error; use matcher::{LobbyListing, RequestConnection}; use renet::{ClientAuthentication, ConnectToken, DefaultChannel, RenetClient, RenetConnectionConfig}; +use renet_transport_steam::SteamTransport; use renet_visualizer::RenetClientVisualizer; +use steamworks::{Client, SingleClient, ClientManager}; use std::{ collections::HashMap, @@ -54,6 +56,8 @@ pub enum AppState { pub struct ChatApp { state: AppState, + client: Client, + single_client: SingleClient, ui_state: UiState, last_updated: Instant, } @@ -122,10 +126,14 @@ pub fn connect_token_request( impl Default for ChatApp { fn default() -> Self { + let (client, single_client) = Client::init().unwrap(); + Self { state: AppState::main_screen(), ui_state: UiState::default(), last_updated: Instant::now(), + client, + single_client } } } @@ -135,7 +143,7 @@ impl ChatApp { match &mut self.state { AppState::MainScreen { lobby_list, .. } => { let lobby_list = lobby_list.clone(); - draw_main_screen(&mut self.ui_state, &mut self.state, lobby_list, ctx); + draw_main_screen(&self.client, &mut self.ui_state, &mut self.state, lobby_list, ctx); } AppState::RequestingToken { .. } => draw_loader(ctx), AppState::ClientChat { client, .. } if !client.is_connected() => draw_loader(ctx), @@ -155,6 +163,8 @@ impl ChatApp { let duration = now - self.last_updated; self.last_updated = now; + self.single_client.run_callbacks(); + match &mut self.state { AppState::ClientChat { client, @@ -216,7 +226,7 @@ impl ChatApp { }, AppState::RequestingToken { token } => match token.try_recv() { Ok(Ok(token)) => { - let client = create_renet_client_from_token(token); + let client = create_renet_client_from_token(&self.client, token); self.state = AppState::ClientChat { visualizer: Box::new(RenetClientVisualizer::default()), @@ -239,13 +249,14 @@ impl ChatApp { } } -fn create_renet_client_from_token(connect_token: ConnectToken) -> RenetClient { +fn create_renet_client_from_token(client: &Client, connect_token: ConnectToken) -> RenetClient { let client_addr = SocketAddr::from(([127, 0, 0, 1], 0)); - let socket = UdpSocket::bind(client_addr).unwrap(); + // let socket = UdpSocket::bind(client_addr).unwrap(); + let transport = SteamTransport::new(client); let connection_config = RenetConnectionConfig::default(); let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let authentication = ClientAuthentication::Secure { connect_token }; - RenetClient::new(current_time, socket, connection_config, authentication).unwrap() + RenetClient::new(current_time, connection_config, authentication, Box::new(transport)).unwrap() } diff --git a/demo_chat/chat/src/server.rs b/demo_chat/chat/src/server.rs index b650af1d..24e551cd 100644 --- a/demo_chat/chat/src/server.rs +++ b/demo_chat/chat/src/server.rs @@ -13,6 +13,8 @@ use renet_visualizer::RenetServerVisualizer; use crate::{lobby_status::update_lobby_status, ClientMessages, Message, ServerMessages}; use bincode::Options; use log::info; +use steamworks::{Client, SingleClient, ClientManager}; +use renet_transport_steam::{address_from_steam_id, SteamTransport}; pub struct ChatServer { pub server: RenetServer, @@ -23,9 +25,11 @@ pub struct ChatServer { } impl ChatServer { - pub fn new(lobby_name: String, host_username: String, password: String) -> Self { - let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); - let server_addr = socket.local_addr().unwrap(); + pub fn new(client: &Client, lobby_name: String, host_username: String, password: String) -> Self { + // let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); + let transport = SteamTransport::new(client); + + let server_addr = address_from_steam_id(client.user().steam_id()); let connection_config = RenetConnectionConfig::default(); let private_key = generate_random_bytes(); let server_config = ServerConfig::new(64, PROTOCOL_ID, server_addr, ServerAuthentication::Secure { private_key }); @@ -41,7 +45,7 @@ impl ChatServer { current_clients: 0, }; let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); - let server = RenetServer::new(current_time, server_config, connection_config, socket).unwrap(); + let server = RenetServer::new(current_time, server_config, connection_config, Box::new(transport)); let mut usernames = HashMap::new(); usernames.insert(1, host_username); diff --git a/demo_chat/chat/src/ui.rs b/demo_chat/chat/src/ui.rs index bbfb7a1f..1c107766 100644 --- a/demo_chat/chat/src/ui.rs +++ b/demo_chat/chat/src/ui.rs @@ -8,6 +8,8 @@ use matcher::{LobbyListing, RequestConnection}; use renet::DefaultChannel; use std::{collections::HashMap, sync::mpsc}; +use steamworks::{Client, ClientManager}; + use crate::{client::connect_token_request, ClientMessages}; use crate::{ @@ -15,6 +17,7 @@ use crate::{ server::ChatServer, }; + pub fn draw_lobby_list(ui: &mut Ui, lobby_list: Vec) -> Option<(u64, bool)> { ui.separator(); ui.heading("Lobby list"); @@ -119,7 +122,7 @@ pub fn draw_host_commands(ui: &mut Ui, chat_server: &mut ChatServer) { }); } -pub fn draw_main_screen(ui_state: &mut UiState, state: &mut AppState, lobby_list: Vec, ctx: &egui::Context) { +pub fn draw_main_screen(client: &Client, ui_state: &mut UiState, state: &mut AppState, lobby_list: Vec, ctx: &egui::Context) { egui::CentralPanel::default().show(ctx, |ui| { egui::Area::new("buttons") .anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0)) @@ -148,7 +151,7 @@ pub fn draw_main_screen(ui_state: &mut UiState, state: &mut AppState, lobby_list ui_state.error = Some("Nick or Lobby name can't be empty".to_owned()); } else { let server = - ChatServer::new(ui_state.lobby_name.clone(), ui_state.username.clone(), ui_state.password.clone()); + ChatServer::new(client, ui_state.lobby_name.clone(), ui_state.username.clone(), ui_state.password.clone()); *state = AppState::HostChat { chat_server: Box::new(server), }; diff --git a/demo_chat/chat/steam_appid.txt b/demo_chat/chat/steam_appid.txt new file mode 100644 index 00000000..7ad80225 --- /dev/null +++ b/demo_chat/chat/steam_appid.txt @@ -0,0 +1 @@ +480 \ No newline at end of file From 850ef31a545eb233e05af4fe0fb1aba5d84dae9f Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 10 Sep 2022 01:14:02 -0300 Subject: [PATCH 05/10] Steam: add callback for accepting connection requests and for failures --- renet_transport_steam/src/lib.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/renet_transport_steam/src/lib.rs b/renet_transport_steam/src/lib.rs index cb49fc64..a93fffa3 100644 --- a/renet_transport_steam/src/lib.rs +++ b/renet_transport_steam/src/lib.rs @@ -54,6 +54,23 @@ impl SteamTransport { pub fn new(client: &Client) -> Self { let networking_messages = client.networking_messages(); + // Accept all connections + networking_messages.session_request_callback(|request| { + request.accept(); + }); + + networking_messages.session_failed_callback(|info| { + let reason = if let Some(end_reason) = info.end_reason() { + format!("{:?}", end_reason) + } else { + "Unknown".to_owned() + }; + + if let Some(identity) = info.identity_remote() { + log::error!("Session from user {:?} failed: {}", identity.steam_id(), reason); + } + }); + Self { networking_messages } @@ -95,7 +112,9 @@ impl Transport for SteamTransport { fn send_to(&mut self, buffer: &[u8], addr: SocketAddr) -> Result<(), Box> { let steam_id = steam_id_from_address(addr)?; let network_id = NetworkingIdentity::new_steam_id(steam_id); - self.networking_messages.send_message_to_user(network_id, SendFlags::UNRELIABLE_NO_NAGLE, buffer, CHANNEL_ID); + if let Err(e) = self.networking_messages.send_message_to_user(network_id, SendFlags::UNRELIABLE_NO_NAGLE, buffer, CHANNEL_ID) { + log::error!("Error while sending message to {:?}: {}", steam_id, e); + } Ok(()) } From 4adaa547b137393bd0d5bd4cf4fc300b9de83445 Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 10 Sep 2022 01:14:53 -0300 Subject: [PATCH 06/10] DemoChat: update menu with connect by IP with unsafe authentication --- demo_chat/chat/src/client.rs | 3 +- demo_chat/chat/src/ui.rs | 69 +++++++++++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/demo_chat/chat/src/client.rs b/demo_chat/chat/src/client.rs index 081de48f..3e87b9f5 100644 --- a/demo_chat/chat/src/client.rs +++ b/demo_chat/chat/src/client.rs @@ -32,6 +32,7 @@ pub struct UiState { pub lobby_name: String, pub error: Option, pub text_input: String, + pub server_addr: String, pub show_network_info: bool, } @@ -250,7 +251,7 @@ impl ChatApp { } fn create_renet_client_from_token(client: &Client, connect_token: ConnectToken) -> RenetClient { - let client_addr = SocketAddr::from(([127, 0, 0, 1], 0)); + // let client_addr = SocketAddr::from(([127, 0, 0, 1], 0)); // let socket = UdpSocket::bind(client_addr).unwrap(); let transport = SteamTransport::new(client); let connection_config = RenetConnectionConfig::default(); diff --git a/demo_chat/chat/src/ui.rs b/demo_chat/chat/src/ui.rs index 1c107766..2528dd5c 100644 --- a/demo_chat/chat/src/ui.rs +++ b/demo_chat/chat/src/ui.rs @@ -4,19 +4,20 @@ use eframe::{ epaint::PathShape, }; use egui_extras::{Size, TableBuilder}; -use matcher::{LobbyListing, RequestConnection}; -use renet::DefaultChannel; +use matcher::{LobbyListing, RequestConnection, Username}; +use renet::{ClientAuthentication, DefaultChannel, RenetClient, RenetConnectionConfig}; +use renet_visualizer::RenetClientVisualizer; -use std::{collections::HashMap, sync::mpsc}; +use std::{collections::HashMap, net::SocketAddr, sync::mpsc, time::SystemTime}; use steamworks::{Client, ClientManager}; - use crate::{client::connect_token_request, ClientMessages}; use crate::{ client::{AppState, UiState}, server::ChatServer, }; - +use matcher::PROTOCOL_ID; +use renet_transport_steam::SteamTransport; pub fn draw_lobby_list(ui: &mut Ui, lobby_list: Vec) -> Option<(u64, bool)> { ui.separator(); @@ -122,7 +123,13 @@ pub fn draw_host_commands(ui: &mut Ui, chat_server: &mut ChatServer) { }); } -pub fn draw_main_screen(client: &Client, ui_state: &mut UiState, state: &mut AppState, lobby_list: Vec, ctx: &egui::Context) { +pub fn draw_main_screen( + client: &Client, + ui_state: &mut UiState, + state: &mut AppState, + lobby_list: Vec, + ctx: &egui::Context, +) { egui::CentralPanel::default().show(ctx, |ui| { egui::Area::new("buttons") .anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0)) @@ -150,8 +157,12 @@ pub fn draw_main_screen(client: &Client, ui_state: &mut UiState, if ui_state.username.is_empty() || ui_state.lobby_name.is_empty() { ui_state.error = Some("Nick or Lobby name can't be empty".to_owned()); } else { - let server = - ChatServer::new(client, ui_state.lobby_name.clone(), ui_state.username.clone(), ui_state.password.clone()); + let server = ChatServer::new( + client, + ui_state.lobby_name.clone(), + ui_state.username.clone(), + ui_state.password.clone(), + ); *state = AppState::HostChat { chat_server: Box::new(server), }; @@ -159,6 +170,48 @@ pub fn draw_main_screen(client: &Client, ui_state: &mut UiState, } }); + ui.separator(); + + ui.horizontal(|ui| { + ui.label("Server addr:"); + ui.text_edit_singleline(&mut ui_state.server_addr) + }); + + ui.vertical_centered_justified(|ui| { + if ui.button("Connect").clicked() { + if ui_state.username.is_empty() { + ui_state.error = Some("Nick can't be empty".to_owned()); + } else { + match ui_state.server_addr.parse::() { + Err(e) => ui_state.error = Some(e.to_string()), + Ok(addr) => { + let transport = SteamTransport::new(client); + let connection_config = RenetConnectionConfig::default(); + let username = Username(ui_state.username.clone()); + + let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + let authentication = ClientAuthentication::Unsecure { + protocol_id: PROTOCOL_ID, + client_id: client.user().steam_id().raw(), + server_addr: addr, + user_data: Some(username.to_netcode_user_data()), + }; + + let client = + RenetClient::new(current_time, connection_config, authentication, Box::new(transport)).unwrap(); + + *state = AppState::ClientChat { + client: Box::new(client), + usernames: HashMap::new(), + messages: vec![], + visualizer: Box::new(RenetClientVisualizer::default()), + }; + } + } + } + } + }); + if let Some(error) = &ui_state.error { ui.separator(); ui.colored_label(Color32::RED, format!("Error: {}", error)); From 7adee619263ccd4f0f8907b40a32f68e4afed646 Mon Sep 17 00:00:00 2001 From: poffo Date: Sat, 10 Sep 2022 18:05:10 -0300 Subject: [PATCH 07/10] Steam: try to fix network messages API --- demo_chat/chat/Cargo.toml | 2 +- renet_transport_steam/Cargo.toml | 2 +- renet_transport_steam/src/lib.rs | 37 ++++++++++++++++++++------------ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/demo_chat/chat/Cargo.toml b/demo_chat/chat/Cargo.toml index 858dfc62..fe3c9413 100644 --- a/demo_chat/chat/Cargo.toml +++ b/demo_chat/chat/Cargo.toml @@ -12,7 +12,7 @@ matcher = { path = "../matcher" } renet = { path = "../../renet" } renet_visualizer = { path = "../../renet_visualizer" } renet_transport_steam = { path = "../../renet_transport_steam" } -steamworks = "0.9.0" +steamworks = { path = "../../../steamworks-rs", version = "0.9.0" } eframe = "0.19" egui_extras = "0.19" serde = { version = "1.0", features = [ "derive" ] } diff --git a/renet_transport_steam/Cargo.toml b/renet_transport_steam/Cargo.toml index b77ac6fa..b8e3ab45 100644 --- a/renet_transport_steam/Cargo.toml +++ b/renet_transport_steam/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" [dependencies] renet = { path = "../renet", version = "0.0.9" } -steamworks = "0.9.0" +steamworks = { path = "../../steamworks-rs", version = "0.9.0" } log = "0.4.11" diff --git a/renet_transport_steam/src/lib.rs b/renet_transport_steam/src/lib.rs index a93fffa3..499a7630 100644 --- a/renet_transport_steam/src/lib.rs +++ b/renet_transport_steam/src/lib.rs @@ -1,14 +1,20 @@ use std::{ error::Error, - fmt, io, + fmt, net::{Ipv6Addr, SocketAddr, SocketAddrV6}, }; use renet::Transport; -use steamworks::{networking_messages::NetworkingMessages, Client, ClientManager, SteamId, networking_types::{NetworkingIdentity, SendFlags}}; +use steamworks::{ + networking_messages::NetworkingMessages, + networking_types::{NetworkingIdentity, SendFlags}, + CallbackHandle, Client, ClientManager, SteamId, +}; pub struct SteamTransport { networking_messages: NetworkingMessages, + _session_request_callback: CallbackHandle, + _session_failed_callback: CallbackHandle, } const CHANNEL_ID: u32 = 0; @@ -55,24 +61,24 @@ impl SteamTransport { let networking_messages = client.networking_messages(); // Accept all connections - networking_messages.session_request_callback(|request| { + let _session_request_callback = networking_messages.session_request_callback(|request| { + println!("Receveid request from {:?}", request.remote().steam_id()); request.accept(); }); - networking_messages.session_failed_callback(|info| { - let reason = if let Some(end_reason) = info.end_reason() { - format!("{:?}", end_reason) - } else { - "Unknown".to_owned() - }; + let _session_failed_callback = networking_messages.session_failed_callback(|info| { + let reason = if let Some(end_reason) = info.end_reason() { format!("{:?}", end_reason) } else { "Unknown".to_owned() }; - if let Some(identity) = info.identity_remote() { - log::error!("Session from user {:?} failed: {}", identity.steam_id(), reason); - } + let user = + if let Some(identity) = info.identity_remote() { format!("{:?}", identity.steam_id()) } else { "unknown".to_owned() }; + + log::error!("Session from user {} failed: {}", user, reason); }); Self { - networking_messages + networking_messages, + _session_request_callback, + _session_failed_callback, } } } @@ -112,7 +118,10 @@ impl Transport for SteamTransport { fn send_to(&mut self, buffer: &[u8], addr: SocketAddr) -> Result<(), Box> { let steam_id = steam_id_from_address(addr)?; let network_id = NetworkingIdentity::new_steam_id(steam_id); - if let Err(e) = self.networking_messages.send_message_to_user(network_id, SendFlags::UNRELIABLE_NO_NAGLE, buffer, CHANNEL_ID) { + if let Err(e) = self + .networking_messages + .send_message_to_user(network_id, SendFlags::UNRELIABLE_NO_NAGLE, buffer, CHANNEL_ID) + { log::error!("Error while sending message to {:?}: {}", steam_id, e); } From 9f26cb6bd4d8420a353b50f61faea400914b2ea6 Mon Sep 17 00:00:00 2001 From: poffo Date: Sun, 11 Sep 2022 13:25:25 -0300 Subject: [PATCH 08/10] Steam: remove println --- demo_chat/chat/src/server.rs | 2 +- renet_transport_steam/src/lib.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/demo_chat/chat/src/server.rs b/demo_chat/chat/src/server.rs index 24e551cd..d7401f3e 100644 --- a/demo_chat/chat/src/server.rs +++ b/demo_chat/chat/src/server.rs @@ -32,7 +32,7 @@ impl ChatServer { let server_addr = address_from_steam_id(client.user().steam_id()); let connection_config = RenetConnectionConfig::default(); let private_key = generate_random_bytes(); - let server_config = ServerConfig::new(64, PROTOCOL_ID, server_addr, ServerAuthentication::Secure { private_key }); + let server_config = ServerConfig::new(64, PROTOCOL_ID, server_addr, ServerAuthentication::Unsecure); let password = if password.is_empty() { None } else { Some(password) }; diff --git a/renet_transport_steam/src/lib.rs b/renet_transport_steam/src/lib.rs index 499a7630..e0c3173e 100644 --- a/renet_transport_steam/src/lib.rs +++ b/renet_transport_steam/src/lib.rs @@ -62,7 +62,7 @@ impl SteamTransport { // Accept all connections let _session_request_callback = networking_messages.session_request_callback(|request| { - println!("Receveid request from {:?}", request.remote().steam_id()); + log::info!("Received session request from {:?}", request.remote().steam_id()); request.accept(); }); @@ -86,7 +86,6 @@ impl SteamTransport { impl Transport for SteamTransport { fn recv_from(&mut self, buffer: &mut [u8]) -> Result, Box> { let messages = self.networking_messages.receive_messages_on_channel(CHANNEL_ID, 1); - if let Some(message) = messages.get(0) { let network_id = message.identity_peer(); let addr = match network_id.steam_id() { @@ -120,7 +119,7 @@ impl Transport for SteamTransport { let network_id = NetworkingIdentity::new_steam_id(steam_id); if let Err(e) = self .networking_messages - .send_message_to_user(network_id, SendFlags::UNRELIABLE_NO_NAGLE, buffer, CHANNEL_ID) + .send_message_to_user(network_id, SendFlags::UNRELIABLE, buffer, CHANNEL_ID) { log::error!("Error while sending message to {:?}: {}", steam_id, e); } From 8406fa0029fbe9a6d22fdee6759d8392a4ba2bbd Mon Sep 17 00:00:00 2001 From: poffo Date: Thu, 15 Sep 2022 20:23:50 -0300 Subject: [PATCH 09/10] Steam: add functions to overwrite session request/failed callbacks --- renet_transport_steam/src/lib.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/renet_transport_steam/src/lib.rs b/renet_transport_steam/src/lib.rs index e0c3173e..42f75390 100644 --- a/renet_transport_steam/src/lib.rs +++ b/renet_transport_steam/src/lib.rs @@ -6,15 +6,15 @@ use std::{ use renet::Transport; use steamworks::{ - networking_messages::NetworkingMessages, - networking_types::{NetworkingIdentity, SendFlags}, + networking_messages::{NetworkingMessages, SessionRequest}, + networking_types::{NetConnectionInfo, NetworkingIdentity, SendFlags}, CallbackHandle, Client, ClientManager, SteamId, }; pub struct SteamTransport { networking_messages: NetworkingMessages, - _session_request_callback: CallbackHandle, - _session_failed_callback: CallbackHandle, + session_request_callback: CallbackHandle, + session_failed_callback: CallbackHandle, } const CHANNEL_ID: u32 = 0; @@ -61,12 +61,12 @@ impl SteamTransport { let networking_messages = client.networking_messages(); // Accept all connections - let _session_request_callback = networking_messages.session_request_callback(|request| { + let session_request_callback = networking_messages.session_request_callback(|request| { log::info!("Received session request from {:?}", request.remote().steam_id()); request.accept(); }); - let _session_failed_callback = networking_messages.session_failed_callback(|info| { + let session_failed_callback = networking_messages.session_failed_callback(|info| { let reason = if let Some(end_reason) = info.end_reason() { format!("{:?}", end_reason) } else { "Unknown".to_owned() }; let user = @@ -77,10 +77,18 @@ impl SteamTransport { Self { networking_messages, - _session_request_callback, - _session_failed_callback, + session_request_callback, + session_failed_callback, } } + + pub fn session_request_callback(&mut self, callback: impl FnMut(SessionRequest) + Send + 'static) { + self.session_request_callback = self.networking_messages.session_request_callback(callback); + } + + pub fn session_failed_callback(&mut self, callback: impl FnMut(NetConnectionInfo) + Send + 'static) { + self.session_failed_callback = self.networking_messages.session_failed_callback(callback); + } } impl Transport for SteamTransport { From 321095a7cbda63ace061ad5bc59dd80a50865133 Mon Sep 17 00:00:00 2001 From: poffo Date: Thu, 15 Sep 2022 20:24:30 -0300 Subject: [PATCH 10/10] DemoSteam: showcase echo demo using steam lobbies --- Cargo.toml | 2 +- demo_steam/Cargo.toml | 11 +++ demo_steam/src/bin/client.rs | 117 +++++++++++++++++++++++++++++ demo_steam/src/bin/server.rs | 142 +++++++++++++++++++++++++++++++++++ demo_steam/src/lib.rs | 2 + demo_steam/steam_appid.txt | 1 + 6 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 demo_steam/Cargo.toml create mode 100644 demo_steam/src/bin/client.rs create mode 100644 demo_steam/src/bin/server.rs create mode 100644 demo_steam/src/lib.rs create mode 100644 demo_steam/steam_appid.txt diff --git a/Cargo.toml b/Cargo.toml index 2073ae29..8ee46826 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["renet", "rechannel", "demo_chat/matcher", "demo_chat/chat", "demo_bevy", "renetcode", "bevy_renet", "renet_visualizer", "renet_transport_steam"] +members = ["renet", "rechannel", "demo_chat/matcher", "demo_chat/chat", "demo_bevy", "demo_steam", "renetcode", "bevy_renet", "renet_visualizer", "renet_transport_steam"] resolver = "2" diff --git a/demo_steam/Cargo.toml b/demo_steam/Cargo.toml new file mode 100644 index 00000000..8ab40a32 --- /dev/null +++ b/demo_steam/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "demo_steam" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +renet = { path = "../renet", version = "0.0.9" } +steamworks = { path = "../../steamworks-rs", version = "0.9.0" } +renet_transport_steam = { path = "../renet_transport_steam" } \ No newline at end of file diff --git a/demo_steam/src/bin/client.rs b/demo_steam/src/bin/client.rs new file mode 100644 index 00000000..e9c5082f --- /dev/null +++ b/demo_steam/src/bin/client.rs @@ -0,0 +1,117 @@ +use std::{ + sync::mpsc::{self, Receiver, TryRecvError}, + time::{Duration, Instant, SystemTime}, +}; + +use renet::{ClientAuthentication, ConnectToken, DefaultChannel, RenetClient, RenetConnectionConfig}; +use renet_transport_steam::SteamTransport; +use steamworks::{Client, LobbyId}; +use demo_steam::CONNECT_TOKEN_CHANNEL; + +fn main() { + println!("Usage: cargo run --bin client -- [LOBBY_ID]"); + let args: Vec = std::env::args().collect(); + + let lobby_id = args[1].parse::().unwrap(); + client(LobbyId::from_raw(lobby_id)); +} + +enum ClientState { + WaitingLobby, + WaitingConnectToken { lobby_id: LobbyId }, + Client { client: RenetClient }, +} + +fn client(lobby_id: LobbyId) { + let (client, single_client) = Client::init().unwrap(); + + let mut state = ClientState::WaitingLobby; + + let matchmaking = client.matchmaking(); + let networking_messages = client.networking_messages(); + + let (sender_join_lobby, receiver_join_lobby) = mpsc::channel(); + + matchmaking.join_lobby(lobby_id, move |result| match result { + Ok(lobby) => sender_join_lobby.send(lobby).unwrap(), + Err(_) => panic!("Failed to connect lobby"), + }); + + let stdin_channel = spawn_stdin_channel(); + + let mut last_updated = Instant::now(); + loop { + single_client.run_callbacks(); + let now = Instant::now(); + let delta = now - last_updated; + last_updated = now; + match state { + ClientState::WaitingLobby => { + if let Ok(lobby_id) = receiver_join_lobby.recv() { + state = ClientState::WaitingConnectToken { lobby_id }; + } + } + ClientState::WaitingConnectToken { lobby_id } => { + for message in networking_messages.receive_messages_on_channel(CONNECT_TOKEN_CHANNEL, 1).iter() { + let data = message.data().to_vec(); + if let Ok(connect_token) = ConnectToken::read(&mut data.as_slice()) { + let connection_config = RenetConnectionConfig::default(); + + let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + let authentication = ClientAuthentication::Secure { connect_token }; + + let mut transport = SteamTransport::new(&client); + + let client_clone = client.clone(); + let lobby_id_clone = lobby_id.clone(); + // Only accepts request from the lobby owner. + transport.session_request_callback(move |request| { + let matchmaking = client_clone.matchmaking(); + let lobby_owner = matchmaking.lobby_owner(lobby_id_clone); + if let Some(remote_user) = request.remote().steam_id() { + if remote_user == lobby_owner { + request.accept(); + return; + } + } + + request.reject(); + }); + + let client = RenetClient::new(current_time, connection_config, authentication, Box::new(transport)).unwrap(); + state = ClientState::Client { client }; + } + } + } + ClientState::Client { ref mut client } => { + client.update(delta).unwrap(); + if client.is_connected() { + match stdin_channel.try_recv() { + Ok(text) => client.send_message(DefaultChannel::Reliable, text.as_bytes().to_vec()), + Err(TryRecvError::Empty) => {} + Err(TryRecvError::Disconnected) => panic!("Channel disconnected"), + } + + while let Some(text) = client.receive_message(DefaultChannel::Reliable) { + let text = String::from_utf8(text).unwrap(); + println!("{}", text); + } + } + + client.send_packets().unwrap(); + } + } + + std::thread::sleep(Duration::from_millis(50)); + } +} + +fn spawn_stdin_channel() -> Receiver { + let (tx, rx) = mpsc::channel::(); + std::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 +} diff --git a/demo_steam/src/bin/server.rs b/demo_steam/src/bin/server.rs new file mode 100644 index 00000000..dcf482d4 --- /dev/null +++ b/demo_steam/src/bin/server.rs @@ -0,0 +1,142 @@ +use std::{ + sync::mpsc, + time::{Duration, Instant, SystemTime}, +}; + +use renet::{ + generate_random_bytes, ConnectToken, DefaultChannel, RenetConnectionConfig, RenetServer, ServerAuthentication, ServerConfig, + ServerEvent, +}; +use renet_transport_steam::{address_from_steam_id, SteamTransport}; +use steamworks::{networking_types::SendFlags, Client, LobbyType}; +use demo_steam::{PROTOCOL_ID, CONNECT_TOKEN_CHANNEL}; + +fn main() { + println!("Usage: cargo run --bin server"); + + server(); +} + +enum ServerState { + WaitingLobby, + Lobby { server: RenetServer }, +} + +fn server() { + let (client, single_client) = Client::init().unwrap(); + let matchmaking = client.matchmaking(); + + let (sender_create_lobby, receiver_create_lobby) = mpsc::channel(); + let mut state = ServerState::WaitingLobby; + + matchmaking.create_lobby(LobbyType::FriendsOnly, 4, move |lobby| match lobby { + Ok(lobby) => { + sender_create_lobby.send(lobby).unwrap(); + } + Err(e) => { + panic!("Failed to create lobby: {}", e); + } + }); + + let mut last_updated = Instant::now(); + + loop { + single_client.run_callbacks(); + let now = Instant::now(); + let delta = now - last_updated; + last_updated = now; + + match &mut state { + ServerState::WaitingLobby => { + if let Ok(lobby_id) = receiver_create_lobby.try_recv() { + println!("Created lobby: {:?}", lobby_id); + let private_key = generate_random_bytes(); + + let client_clone = client.clone(); + let mut transport = SteamTransport::new(&client); + transport.session_request_callback(move |request| { + let matchmaking = client_clone.matchmaking(); + let networking_messages = client_clone.networking_messages(); + + let lobby_members = matchmaking.lobby_members(lobby_id); + let network_identity = request.remote().clone(); + if let Some(remote_user) = request.remote().steam_id() { + if lobby_members.contains(&remote_user) { + request.accept(); + + let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + let fake_server_addr = address_from_steam_id(client_clone.user().steam_id()); + let token = ConnectToken::generate( + current_time, + PROTOCOL_ID, + 360, + remote_user.raw(), + 30, + vec![fake_server_addr], + None, + &private_key, + ) + .unwrap(); + + let mut data = vec![]; + token.write(&mut data).unwrap(); + + if let Err(e) = networking_messages.send_message_to_user( + network_identity, + SendFlags::RELIABLE, + &data, + CONNECT_TOKEN_CHANNEL, + ) { + println!("Failed to send ConnectToken: {e}"); + } + return; + } + } + request.reject(); + }); + + let server_addr = address_from_steam_id(client.user().steam_id()); + let connection_config = RenetConnectionConfig::default(); + let server_config = ServerConfig::new(64, PROTOCOL_ID, server_addr, ServerAuthentication::Secure { private_key }); + let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + + let server = RenetServer::new(current_time, server_config, connection_config, Box::new(transport)); + + state = ServerState::Lobby { server }; + } + } + ServerState::Lobby { ref mut server } => { + server.update(delta).unwrap(); + + let mut received_messages = vec![]; + while let Some(event) = server.get_event() { + match event { + ServerEvent::ClientConnected(id, _) => { + println!("Client {} connected.", id) + } + ServerEvent::ClientDisconnected(id) => { + println!("Client {} disconnected", id); + } + } + } + + for client_id in server.clients_id().into_iter() { + while let Some(message) = server.receive_message(client_id, DefaultChannel::Reliable) { + let text = String::from_utf8(message).unwrap(); + println!("Client {} sent text: {}", client_id, text); + let text = format!("{}: {}", client_id, text); + received_messages.push(text); + } + } + + for text in received_messages.iter() { + server.broadcast_message(DefaultChannel::Reliable, text.as_bytes().to_vec()); + } + + server.send_packets().unwrap(); + } + } + + std::thread::sleep(Duration::from_millis(50)); + } +} diff --git a/demo_steam/src/lib.rs b/demo_steam/src/lib.rs new file mode 100644 index 00000000..65065bf1 --- /dev/null +++ b/demo_steam/src/lib.rs @@ -0,0 +1,2 @@ +pub const PROTOCOL_ID: u64 = 7; +pub const CONNECT_TOKEN_CHANNEL: u32 = 1; \ No newline at end of file diff --git a/demo_steam/steam_appid.txt b/demo_steam/steam_appid.txt new file mode 100644 index 00000000..7ad80225 --- /dev/null +++ b/demo_steam/steam_appid.txt @@ -0,0 +1 @@ +480 \ No newline at end of file