diff --git a/Server.toml b/Server.toml index d290581..14ae320 100644 --- a/Server.toml +++ b/Server.toml @@ -1,3 +1,4 @@ [server] address = "0.0.0.0:12345" max_clients = 254 +timeout_ms = 10000 diff --git a/kubi-server/src/config.rs b/kubi-server/src/config.rs index a16dbf2..4593221 100644 --- a/kubi-server/src/config.rs +++ b/kubi-server/src/config.rs @@ -6,6 +6,7 @@ use std::{fs, net::SocketAddr}; pub struct ConfigTableServer { pub address: SocketAddr, pub max_clients: usize, + pub timeout_ms: u64, } #[derive(Unique, Serialize, Deserialize)] diff --git a/kubi-server/src/server.rs b/kubi-server/src/server.rs index 589b160..e69d710 100644 --- a/kubi-server/src/server.rs +++ b/kubi-server/src/server.rs @@ -1,6 +1,7 @@ use shipyard::{AllStoragesView, Unique, UniqueView, UniqueViewMut}; use kubi_udp::server::{Server, ServerConfig}; use kubi_shared::networking::messages::{ClientToServerMessage, ServerToClientMessage}; +use std::time::Duration; use crate::config::ConfigTable; #[derive(Unique)] @@ -14,7 +15,11 @@ pub fn bind_server( let config = storages.borrow::>().unwrap(); let server: Server = Server::bind( config.server.address, - ServerConfig { max_clients: config.server.max_clients } + ServerConfig { + max_clients: config.server.max_clients, + client_timeout: Duration::from_millis(config.server.timeout_ms), + ..Default::default() + } ).unwrap(); storages.add_unique(UdpServer(server)); } diff --git a/kubi-shared/src/networking/messages.rs b/kubi-shared/src/networking/messages.rs index 9a84abf..5578781 100644 --- a/kubi-shared/src/networking/messages.rs +++ b/kubi-shared/src/networking/messages.rs @@ -5,7 +5,9 @@ type IVec3Arr = [i32; 3]; type Vec3Arr = [f32; 3]; type QuatArr = [f32; 3]; -#[derive(Encode, Decode)] +pub const PROTOCOL_ID: u16 = 1; + +#[derive(Encode, Decode, Clone)] pub enum ClientToServerMessage { ClientHello { username: String, @@ -21,7 +23,7 @@ pub enum ClientToServerMessage { }, } -#[derive(Encode, Decode)] +#[derive(Encode, Decode, Clone)] pub enum ServerToClientMessage { ServerHello, ServerFuckOff { diff --git a/kubi-udp/src/client.rs b/kubi-udp/src/client.rs index 50d02de..cb411b1 100644 --- a/kubi-udp/src/client.rs +++ b/kubi-udp/src/client.rs @@ -6,11 +6,10 @@ use std::{ collections::{VecDeque, vec_deque::Drain as DrainDeque}, io::ErrorKind, }; -use bincode::{Encode, Decode}; use crate::{ BINCODE_CONFIG, - packet::{ClientPacket, IdClientPacket, IdServerPacket, ServerPacket}, - common::ClientId + packet::{ClientPacket, IdClientPacket, IdServerPacket, ServerPacket, Message}, + common::{ClientId, PROTOCOL_ID, DEFAULT_USER_PROTOCOL_ID} }; #[derive(Default, Clone, Debug)] @@ -22,6 +21,7 @@ pub enum DisconnectReason { KickedByServer(Option), Timeout, ConnectionReset, + InvalidProtocolId, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -33,25 +33,27 @@ pub enum ClientStatus { #[derive(Clone, Copy, Debug)] pub struct ClientConfig { + pub protocol_id: u16, pub timeout: Duration, pub heartbeat_interval: Duration, } impl Default for ClientConfig { fn default() -> Self { Self { + protocol_id: DEFAULT_USER_PROTOCOL_ID, timeout: Duration::from_secs(5), heartbeat_interval: Duration::from_secs(3), } } } -pub enum ClientEvent where T: Encode + Decode { +pub enum ClientEvent where T: Message { Connected(ClientId), Disconnected(DisconnectReason), MessageReceived(T) } -pub struct Client where S: Encode + Decode, R: Encode + Decode { +pub struct Client where S: Message, R: Message { config: ClientConfig, addr: SocketAddr, socket: UdpSocket, @@ -63,9 +65,15 @@ pub struct Client where S: Encode + Decode, R: Encode + Decode { event_queue: VecDeque>, _s: PhantomData, } -impl Client where S: Encode + Decode, R: Encode + Decode { +impl Client where S: Message, R: Message { #[inline] pub fn new(addr: SocketAddr, config: ClientConfig) -> Result { + if config.protocol_id == 0 { + log::warn!("Warning: using 0 as protocol_id is not recommended"); + } + if config.protocol_id == DEFAULT_USER_PROTOCOL_ID { + log::warn!("Warning: using default protocol_id is not recommended"); + } let bind_addr: SocketAddr = "0.0.0.0:0".parse().unwrap(); let socket = UdpSocket::bind(bind_addr)?; socket.set_nonblocking(true)?; @@ -107,7 +115,7 @@ impl Client where S: Encode + Decode, R: Encode + Decode { #[inline] pub fn connect(&mut self) -> Result<()> { - log::info!("client connect called"); + log::info!("Client connecting.."); if self.status != ClientStatus::Disconnected { bail!("Not Disconnected"); } @@ -115,7 +123,10 @@ impl Client where S: Encode + Decode, R: Encode + Decode { self.last_heartbeat = Instant::now(); self.reset_timeout(); self.socket.connect(self.addr)?; - self.send_raw_packet(ClientPacket::Connect)?; + self.send_raw_packet(ClientPacket::Connect{ + user_protocol: self.config.protocol_id, + inner_protocol: PROTOCOL_ID, + })?; Ok(()) } @@ -216,6 +227,15 @@ impl Client where S: Encode + Decode, R: Encode + Decode { }, ServerPacket::Data(message) => { self.event_queue.push_back(ClientEvent::MessageReceived(message)); + self.timeout = Instant::now(); + }, + ServerPacket::Heartbeat => { + self.timeout = Instant::now(); + }, + ServerPacket::ProtoDisconnect => { + let reason = DisconnectReason::InvalidProtocolId; + self.disconnect_inner(reason, true)?; //this should never fail but we're handling the error anyway + return Ok(()); } } }, diff --git a/kubi-udp/src/common.rs b/kubi-udp/src/common.rs index 76e92b2..84b2822 100644 --- a/kubi-udp/src/common.rs +++ b/kubi-udp/src/common.rs @@ -3,3 +3,6 @@ use std::num::NonZeroU8; pub type ClientId = NonZeroU8; pub type ClientIdRepr = u8; pub const MAX_CLIENTS: usize = u8::MAX as _; + +pub const PROTOCOL_ID: u16 = 1; +pub const DEFAULT_USER_PROTOCOL_ID: u16 = 0xcafe; diff --git a/kubi-udp/src/packet.rs b/kubi-udp/src/packet.rs index 8f13403..b5eff11 100644 --- a/kubi-udp/src/packet.rs +++ b/kubi-udp/src/packet.rs @@ -1,25 +1,33 @@ use bincode::{Encode, Decode}; use crate::common::ClientId; +pub trait Message: Encode + Decode + Clone {} +impl Message for T {} + #[repr(u8)] #[derive(Encode, Decode)] -pub enum ClientPacket where T: Encode + Decode { +pub enum ClientPacket where T: Message { + Connect { + inner_protocol: u16, + user_protocol: u16, + }, //should always stay 0! Data(T), - Connect, Disconnect, Heartbeat, } #[derive(Encode, Decode)] -pub struct IdClientPacket(pub Option, pub ClientPacket); +pub struct IdClientPacket(pub Option, pub ClientPacket); #[repr(u8)] #[derive(Encode, Decode)] -pub enum ServerPacket where T: Encode + Decode { +pub enum ServerPacket where T: Message { + ProtoDisconnect = 0, Data(T), - Connected(ClientId), Disconnected(String), + Connected(ClientId), + Heartbeat, } #[derive(Encode, Decode)] -pub struct IdServerPacket(pub Option, pub ServerPacket); +pub struct IdServerPacket(pub Option, pub ServerPacket); diff --git a/kubi-udp/src/server.rs b/kubi-udp/src/server.rs index b15a185..18fa508 100644 --- a/kubi-udp/src/server.rs +++ b/kubi-udp/src/server.rs @@ -1,18 +1,17 @@ use std::{ net::{UdpSocket, SocketAddr}, - time::Instant, + time::{Instant, Duration}, marker::PhantomData, collections::{VecDeque, vec_deque::Drain as DrainDeque}, io::ErrorKind }; use anyhow::{Result, bail}; -use bincode::{Encode, Decode}; use hashbrown::HashMap; use nohash_hasher::BuildNoHashHasher; use crate::{ BINCODE_CONFIG, - common::{ClientId, ClientIdRepr, MAX_CLIENTS}, - packet::{IdClientPacket, ClientPacket, ServerPacket, IdServerPacket} + common::{ClientId, ClientIdRepr, MAX_CLIENTS, PROTOCOL_ID, DEFAULT_USER_PROTOCOL_ID}, + packet::{IdClientPacket, ClientPacket, ServerPacket, IdServerPacket, Message} }; //i was feeling a bit sick while writing most of this please excuse me for my terrible code :3 @@ -26,16 +25,20 @@ pub struct ConnectedClient { #[derive(Clone, Copy, Debug)] pub struct ServerConfig { pub max_clients: usize, + pub client_timeout: Duration, + pub protocol_id: u16, } impl Default for ServerConfig { fn default() -> Self { Self { max_clients: MAX_CLIENTS, + client_timeout: Duration::from_secs(5), + protocol_id: DEFAULT_USER_PROTOCOL_ID, } } } -pub enum ServerEvent where T: Encode + Decode { +pub enum ServerEvent where T: Message { Connected(ClientId), Disconnected(ClientId), MessageReceived { @@ -44,16 +47,22 @@ pub enum ServerEvent where T: Encode + Decode { } } -pub struct Server where S: Encode + Decode, R: Encode + Decode { +pub struct Server where S: Message, R: Message { socket: UdpSocket, clients: HashMap>, config: ServerConfig, event_queue: VecDeque>, _s: PhantomData, } -impl Server where S: Encode + Decode, R: Encode + Decode { +impl Server where S: Message, R: Message { pub fn bind(addr: SocketAddr, config: ServerConfig) -> anyhow::Result { - assert!(config.max_clients <= MAX_CLIENTS); + assert!(config.max_clients <= MAX_CLIENTS, "max_clients value exceeds the maximum allowed amount of clients"); + if config.protocol_id == 0 { + log::warn!("Warning: using 0 as protocol_id is not recommended"); + } + if config.protocol_id == DEFAULT_USER_PROTOCOL_ID { + log::warn!("Warning: using default protocol_id is not recommended"); + } let socket = UdpSocket::bind(addr)?; socket.set_nonblocking(true)?; Ok(Self { @@ -127,6 +136,12 @@ impl Server where S: Encode + Decode, R: Encode + Decode { self.send_packet(IdServerPacket(Some(id), ServerPacket::Data(message)))?; Ok(()) } + pub fn multicast_message(&mut self, _clients: impl IntoIterator, _message: S) { + todo!() + } + pub fn broadcast_message(&mut self, _message: S) -> anyhow::Result<()> { + todo!() + } pub fn update(&mut self) -> Result<()> { //TODO client timeout @@ -141,7 +156,10 @@ impl Server where S: Encode + Decode, R: Encode + Decode { Some(id) => { if !self.clients.contains_key(&id) { bail!("Client with id {id} doesn't exist"); - }; + } + if self.clients.get(&id).unwrap().addr != addr { + bail!("Client addr doesn't match"); + } match packet { ClientPacket::Data(data) => { self.event_queue.push_back(ServerEvent::MessageReceived { @@ -155,21 +173,30 @@ impl Server where S: Encode + Decode, R: Encode + Decode { self.disconnect_client_inner(id, "Disconnected".into())?; }, ClientPacket::Heartbeat => { - self.clients.get_mut(&id).unwrap().timeout = Instant::now() + self.clients.get_mut(&id).unwrap().timeout = Instant::now(); + self.send_packet(IdServerPacket(Some(id), ServerPacket::Heartbeat))?; }, - ClientPacket::Connect => bail!("Client already connected"), + ClientPacket::Connect{..} => bail!("Client already connected"), } }, None => { match packet { - ClientPacket::Connect => { + ClientPacket::Connect { user_protocol, inner_protocol } => { + if (inner_protocol != PROTOCOL_ID) || (user_protocol != self.config.protocol_id ) { + log::error!("Client conenction refused: Invalid protocol id"); + self.send_to_addr(addr, + IdServerPacket(None, ServerPacket::ProtoDisconnect) + )?; + continue; + } + match self.add_client(addr) { Ok(id) => { log::info!("Client with id {id} connected"); self.event_queue.push_back(ServerEvent::Connected(id)); self.send_to_addr(addr, - IdServerPacket(None, ServerPacket::Connected(id) - ))?; + IdServerPacket(None, ServerPacket::Connected(id)) + )?; }, Err(error) => { let reason = error.to_string(); diff --git a/kubi/src/gui.rs b/kubi/src/gui.rs index 73ac0d9..e45af3a 100644 --- a/kubi/src/gui.rs +++ b/kubi/src/gui.rs @@ -1,11 +1,11 @@ -use shipyard::{Component, Unique, Workload, IntoWorkload, AllStoragesView, AllStoragesViewMut, View, UniqueView, NonSendSync, UniqueViewMut, IntoIter}; -use glam::{Vec4, Mat3, vec2, Mat4}; -use crate::{color::color_hex, transform::Transform2d, events::WindowResizedEvent, rendering::Renderer}; +use shipyard::{Component, Unique, Workload, IntoWorkload, AllStoragesView, View, UniqueViewMut, IntoIter}; +use glam::{Vec4, Mat4}; +use crate::{color::color_hex, events::WindowResizedEvent}; pub mod text_widget; pub mod progressbar; -use progressbar::{render_progressbars, ProgressbarComponent}; +use progressbar::render_progressbars; //TODO compute gui scale on window resize #[derive(Unique, Clone, Copy, Debug, Default)] diff --git a/kubi/src/init.rs b/kubi/src/init.rs index 24536c6..23500e9 100644 --- a/kubi/src/init.rs +++ b/kubi/src/init.rs @@ -1,5 +1,5 @@ use shipyard::{AllStoragesViewMut, UniqueViewMut}; -use std::{env, net::SocketAddr, borrow::BorrowMut}; +use std::{env, net::SocketAddr}; use crate::{ networking::{GameType, ServerAddress}, state::{GameState, NextState} diff --git a/kubi/src/loading_screen.rs b/kubi/src/loading_screen.rs index 02242aa..75873ff 100644 --- a/kubi/src/loading_screen.rs +++ b/kubi/src/loading_screen.rs @@ -47,7 +47,7 @@ fn update_progress_bar_progress ( ) { let bar = (&mut bar).get(eid.0).unwrap(); let loaded = world.chunks.iter().fold(0, |acc, (&_, chunk)| { - acc + chunk.desired_state.matches(chunk.current_state) as usize + acc + chunk.desired_state.matches_current(chunk.current_state) as usize }); let total = world.chunks.len(); let progress = loaded as f32 / total as f32; @@ -62,7 +62,7 @@ fn switch_to_ingame_if_loaded( return } if world.chunks.iter().all(|(_, chunk)| { - chunk.desired_state.matches(chunk.current_state) + chunk.desired_state.matches_current(chunk.current_state) }) { log::info!("Finished loading chunks"); state.0 = Some(GameState::InGame); diff --git a/kubi/src/world/chunk.rs b/kubi/src/world/chunk.rs index cd8f77e..f22801f 100644 --- a/kubi/src/world/chunk.rs +++ b/kubi/src/world/chunk.rs @@ -30,11 +30,6 @@ pub enum CurrentChunkState { RecalculatingMesh, Unloading, } -impl CurrentChunkState { - pub fn matches(self, desired: DesiredChunkState) -> bool { - desired.matches(self) - } -} #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum DesiredChunkState { @@ -45,7 +40,7 @@ pub enum DesiredChunkState { ToUnload, } impl DesiredChunkState { - pub fn matches(self, current: CurrentChunkState) -> bool { + pub fn matches_current(self, current: CurrentChunkState) -> bool { (matches!(self, DesiredChunkState::Nothing) && matches!(current, CurrentChunkState::Nothing)) || (matches!(self, DesiredChunkState::Loaded) && matches!(current, CurrentChunkState::Loaded)) || (matches!(self, DesiredChunkState::Rendered) && matches!(current, CurrentChunkState::Rendered))