use crate::{
error::{DisconnectionReason, RenetError},
network_info::{ClientPacketInfo, NetworkInfo, PacketInfo},
RenetConnectionConfig, 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::time::Duration;
use std::{io, net::SocketAddr};
#[allow(clippy::large_enum_variant)]
pub enum ClientAuthentication {
Secure { connect_token: ConnectToken },
Unsecure {
protocol_id: u64,
client_id: u64,
server_addr: SocketAddr,
user_data: Option<[u8; NETCODE_USER_DATA_BYTES]>,
},
}
#[cfg_attr(feature = "bevy", derive(bevy_ecs::system::Resource))]
pub struct RenetClient {
current_time: Duration,
netcode_client: NetcodeClient,
socket: UdpSocket,
reliable_connection: RemoteConnection,
buffer: [u8; NETCODE_MAX_PACKET_BYTES],
client_packet_info: ClientPacketInfo,
}
impl RenetClient {
pub fn new(
current_time: Duration,
socket: UdpSocket,
config: RenetConnectionConfig,
authentication: ClientAuthentication,
) -> Result<Self, RenetError> {
socket.set_nonblocking(true)?;
let reliable_connection = RemoteConnection::new(current_time, config.to_connection_config());
let connect_token: ConnectToken = match authentication {
ClientAuthentication::Unsecure {
server_addr,
protocol_id,
client_id,
user_data,
} => ConnectToken::generate(
current_time,
protocol_id,
300,
client_id,
15,
vec![server_addr],
user_data.as_ref(),
&[0; NETCODE_KEY_BYTES],
)?,
ClientAuthentication::Secure { connect_token } => connect_token,
};
let netcode_client = NetcodeClient::new(current_time, connect_token);
let client_packet_info = ClientPacketInfo::new(config.bandwidth_smoothing_factor);
Ok(Self {
current_time,
buffer: [0u8; NETCODE_MAX_PACKET_BYTES],
socket,
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()
}
pub fn is_connected(&self) -> bool {
self.netcode_client.connected()
}
pub fn disconnected(&self) -> Option<DisconnectionReason> {
if let Some(reason) = self.reliable_connection.disconnected() {
return Some(reason.into());
}
if let Some(reason) = self.netcode_client.disconnected() {
return Some(reason.into());
}
None
}
pub fn disconnect(&mut self) {
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) {
log::error!("failed to send disconnect packet to server: {}", e);
}
}
}
Err(e) => log::error!("failed to generate disconnect packet: {}", e),
}
}
pub fn receive_message<I: Into<u8>>(&mut self, channel_id: I) -> Option<Vec<u8>> {
self.reliable_connection.receive_message(channel_id)
}
pub fn send_message<I: Into<u8>, B: Into<Bytes>>(&mut self, channel_id: I, message: B) {
self.reliable_connection.send_message(channel_id, message);
}
pub fn can_send_message<I: Into<u8>>(&self, channel_id: I) -> bool {
self.reliable_connection.can_send_message(channel_id)
}
pub fn network_info(&self) -> NetworkInfo {
NetworkInfo {
sent_kbps: self.client_packet_info.sent_kbps,
received_kbps: self.client_packet_info.received_kbps,
rtt: self.reliable_connection.rtt(),
packet_loss: self.reliable_connection.packet_loss(),
}
}
pub fn send_packets(&mut self) -> Result<(), RenetError> {
if self.netcode_client.connected() {
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)?;
}
}
Ok(())
}
pub fn update(&mut self, duration: Duration) -> Result<(), RenetError> {
self.current_time += duration;
self.reliable_connection.advance_time(duration);
if let Some(reason) = self.netcode_client.disconnected() {
return Err(NetcodeError::Disconnected(reason).into());
}
if let Some(reason) = self.reliable_connection.disconnected() {
self.disconnect();
return Err(RechannelError::ClientDisconnected(reason).into());
}
loop {
let packet = match self.socket.recv_from(&mut self.buffer) {
Ok((len, addr)) => {
if addr != self.netcode_client.server_addr() {
debug!("Discarded packet from unknown server {:?}", addr);
continue;
}
&mut self.buffer[..len]
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break,
Err(e) => return Err(RenetError::IO(e)),
};
let packet_info = PacketInfo::new(self.current_time, packet.len());
self.client_packet_info.add_packet_received(packet_info);
if let Some(payload) = self.netcode_client.process_packet(packet) {
self.reliable_connection.process_packet(payload)?;
}
}
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)?;
}
self.client_packet_info.update_metrics();
Ok(())
}
}
fn send_to(
current_time: Duration,
socket: &UdpSocket,
client_packet_info: &mut ClientPacketInfo,
packet: &[u8],
address: SocketAddr,
) -> Result<usize, std::io::Error> {
let packet_info = PacketInfo::new(current_time, packet.len());
client_packet_info.add_packet_sent(packet_info);
socket.send_to(packet, address)
}