use std::net::SocketAddr;
use std::time::Duration;
use anyhow::Result;
use bevy::prelude::{Resource, Time, Virtual, World};
use tracing::{debug, trace};
use crate::channel::builder::Channel;
use crate::connection::events::ConnectionEvents;
use crate::inputs::input_buffer::InputBuffer;
use crate::netcode::Client as NetcodeClient;
use crate::netcode::{ConnectToken, Key};
use crate::packet::message::Message;
use crate::protocol::channel::ChannelKind;
use crate::protocol::Protocol;
use crate::shared::ping::message::SyncMessage;
use crate::shared::replication::manager::ReplicationManager;
use crate::shared::tick_manager::TickManager;
use crate::shared::tick_manager::{Tick, TickManaged};
use crate::shared::time_manager::TimeManager;
use crate::transport::io::Io;
use crate::transport::{PacketReceiver, PacketSender, Transport};
use super::config::ClientConfig;
use super::connection::Connection;
#[derive(Resource)]
pub struct Client<P: Protocol> {
io: Io,
config: ClientConfig,
netcode: crate::netcode::Client,
connection: Connection<P>,
protocol: P,
events: ConnectionEvents<P>,
pub(crate) time_manager: TimeManager,
tick_manager: TickManager,
}
#[allow(clippy::large_enum_variant)]
pub enum Authentication {
Token(ConnectToken),
Manual {
server_addr: SocketAddr,
client_id: u64,
private_key: Key,
protocol_id: u64,
},
}
impl Authentication {
fn get_token(self, client_timeout_secs: i32) -> Option<ConnectToken> {
match self {
Authentication::Token(token) => Some(token),
Authentication::Manual {
server_addr,
client_id,
private_key,
protocol_id,
} => ConnectToken::build(server_addr, protocol_id, client_id, private_key)
.timeout_seconds(client_timeout_secs)
.generate()
.ok(),
}
}
}
impl<P: Protocol> Client<P> {
pub fn new(config: ClientConfig, io: Io, auth: Authentication, protocol: P) -> Self {
let config_clone = config.clone();
let token = auth
.get_token(config.netcode.client_timeout_secs)
.expect("could not generate token");
let token_bytes = token.try_into_bytes().unwrap();
let netcode = NetcodeClient::with_config(&token_bytes, config.netcode.build())
.expect("could not create netcode client");
let connection = Connection::new(protocol.channel_registry(), config.sync, &config.ping);
Self {
io,
config: config_clone,
protocol,
netcode,
connection,
events: ConnectionEvents::new(),
time_manager: TimeManager::new(config.shared.client_send_interval),
tick_manager: TickManager::from_config(config.shared.tick),
}
}
pub fn config(&self) -> &ClientConfig {
&self.config
}
pub fn local_addr(&self) -> SocketAddr {
self.io.local_addr()
}
pub fn connect(&mut self) {
self.netcode.connect();
}
pub fn is_connected(&self) -> bool {
self.netcode.is_connected()
}
pub fn is_synced(&self) -> bool {
self.connection.sync_manager.is_synced()
}
pub fn add_input(&mut self, input: P::Input) {
self.connection
.add_input(input, self.tick_manager.current_tick());
}
pub fn get_input_buffer(&self) -> &InputBuffer<P::Input> {
&self.connection.input_buffer
}
pub fn get_mut_input_buffer(&mut self) -> &mut InputBuffer<P::Input> {
&mut self.connection.input_buffer
}
pub fn get_input(&mut self, tick: Tick) -> Option<P::Input> {
self.connection.input_buffer.get(tick).cloned()
}
pub fn is_ready_to_send(&self) -> bool {
self.time_manager.is_ready_to_send()
}
pub fn set_base_relative_speed(&mut self, relative_speed: f32) {
self.time_manager.base_relative_speed = relative_speed;
}
pub(crate) fn update_relative_speed(&mut self, time: &mut Time<Virtual>) {
if self.connection.sync_manager.is_synced() {
self.connection.sync_manager.update_prediction_time(
&mut self.time_manager,
&self.tick_manager,
&self.connection.base.ping_manager,
);
self.time_manager.update_relative_speed(time);
}
}
pub fn tick(&self) -> Tick {
self.tick_manager.current_tick()
}
pub fn latest_received_server_tick(&self) -> Tick {
self.connection.sync_manager.latest_received_server_tick
}
pub fn received_new_server_tick(&self) -> bool {
self.connection
.sync_manager
.duration_since_latest_received_server_tick
== Duration::default()
}
pub(crate) fn replication_manager(&self) -> &ReplicationManager<P> {
&self.connection.base.replication_manager
}
pub(crate) fn update(&mut self, delta: Duration, overstep: Duration) -> Result<()> {
self.time_manager.update(delta, overstep);
self.netcode.try_update(delta.as_secs_f64(), &mut self.io)?;
if self.netcode.is_connected() {
self.connection
.update(&self.time_manager, &self.tick_manager);
}
Ok(())
}
pub(crate) fn sync_update(&mut self) {
if self.netcode.is_connected() {
self.connection.sync_manager.update(
&mut self.time_manager,
&mut self.tick_manager,
&self.connection.base.ping_manager,
&self.config.interpolation.delay,
self.config.shared.server_send_interval,
);
}
}
pub fn buffer_send<C: Channel, M: Message>(&mut self, message: M) -> Result<()>
where
P::Message: From<M>,
{
let channel = ChannelKind::of::<C>();
self.connection.base.buffer_message(message.into(), channel)
}
pub(crate) fn receive(&mut self, world: &mut World) -> ConnectionEvents<P> {
trace!("Receive server packets");
self.connection.base.receive(world, &self.time_manager)
}
pub(crate) fn send_packets(&mut self) -> Result<()> {
let packet_bytes = self
.connection
.base
.send_packets(&self.time_manager, &self.tick_manager)?;
for packet_byte in packet_bytes {
self.netcode.send(packet_byte.as_slice(), &mut self.io)?;
}
Ok(())
}
pub(crate) fn recv_packets(&mut self) -> Result<()> {
while let Some(mut reader) = self.netcode.recv() {
self.connection
.recv_packet(&mut reader, &self.time_manager, &self.tick_manager)?;
}
Ok(())
}
}
impl<P: Protocol> TickManaged for Client<P> {
fn increment_tick(&mut self) {
self.tick_manager.increment_tick();
}
}
#[cfg(test)]
impl<P: Protocol> Client<P> {
pub fn set_latest_received_server_tick(&mut self, tick: Tick) {
self.connection.sync_manager.latest_received_server_tick = tick;
self.connection
.sync_manager
.duration_since_latest_received_server_tick = Duration::default();
}
pub fn connection(&self) -> &Connection<P> {
&self.connection
}
pub fn set_synced(&mut self) {
self.connection.sync_manager.synced = true;
}
}
impl<P: Protocol> Client<P> {
pub(crate) fn interpolation_tick(&self) -> Tick {
self.connection
.sync_manager
.interpolation_tick(&self.tick_manager)
}
}