protocol id

This commit is contained in:
griffi-gh 2023-02-13 01:53:55 +01:00
parent a5b35ffa7d
commit 6b9243a556
12 changed files with 106 additions and 44 deletions

View file

@ -1,3 +1,4 @@
[server]
address = "0.0.0.0:12345"
max_clients = 254
timeout_ms = 10000

View file

@ -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)]

View file

@ -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::<UniqueView<ConfigTable>>().unwrap();
let server: Server<ServerToClientMessage, ClientToServerMessage> = 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));
}

View file

@ -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 {

View file

@ -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<String>),
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<T> where T: Encode + Decode {
pub enum ClientEvent<T> where T: Message {
Connected(ClientId),
Disconnected(DisconnectReason),
MessageReceived(T)
}
pub struct Client<S, R> where S: Encode + Decode, R: Encode + Decode {
pub struct Client<S, R> where S: Message, R: Message {
config: ClientConfig,
addr: SocketAddr,
socket: UdpSocket,
@ -63,9 +65,15 @@ pub struct Client<S, R> where S: Encode + Decode, R: Encode + Decode {
event_queue: VecDeque<ClientEvent<R>>,
_s: PhantomData<S>,
}
impl<S, R> Client<S, R> where S: Encode + Decode, R: Encode + Decode {
impl<S, R> Client<S, R> where S: Message, R: Message {
#[inline]
pub fn new(addr: SocketAddr, config: ClientConfig) -> Result<Self> {
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<S, R> Client<S, R> 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<S, R> Client<S, R> 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<S, R> Client<S, R> 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(());
}
}
},

View file

@ -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;

View file

@ -1,25 +1,33 @@
use bincode::{Encode, Decode};
use crate::common::ClientId;
pub trait Message: Encode + Decode + Clone {}
impl<T: Encode + Decode + Clone> Message for T {}
#[repr(u8)]
#[derive(Encode, Decode)]
pub enum ClientPacket<T> where T: Encode + Decode {
pub enum ClientPacket<T> 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<T: Encode + Decode>(pub Option<ClientId>, pub ClientPacket<T>);
pub struct IdClientPacket<T: Message>(pub Option<ClientId>, pub ClientPacket<T>);
#[repr(u8)]
#[derive(Encode, Decode)]
pub enum ServerPacket<T> where T: Encode + Decode {
pub enum ServerPacket<T> where T: Message {
ProtoDisconnect = 0,
Data(T),
Connected(ClientId),
Disconnected(String),
Connected(ClientId),
Heartbeat,
}
#[derive(Encode, Decode)]
pub struct IdServerPacket<T: Encode + Decode>(pub Option<ClientId>, pub ServerPacket<T>);
pub struct IdServerPacket<T: Message>(pub Option<ClientId>, pub ServerPacket<T>);

View file

@ -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<T> where T: Encode + Decode {
pub enum ServerEvent<T> where T: Message {
Connected(ClientId),
Disconnected(ClientId),
MessageReceived {
@ -44,16 +47,22 @@ pub enum ServerEvent<T> where T: Encode + Decode {
}
}
pub struct Server<S, R> where S: Encode + Decode, R: Encode + Decode {
pub struct Server<S, R> where S: Message, R: Message {
socket: UdpSocket,
clients: HashMap<ClientId, ConnectedClient, BuildNoHashHasher<ClientIdRepr>>,
config: ServerConfig,
event_queue: VecDeque<ServerEvent<R>>,
_s: PhantomData<S>,
}
impl<S, R> Server<S, R> where S: Encode + Decode, R: Encode + Decode {
impl<S, R> Server<S, R> where S: Message, R: Message {
pub fn bind(addr: SocketAddr, config: ServerConfig) -> anyhow::Result<Self> {
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<S, R> Server<S, R> 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<Item = ClientId>, _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<S, R> Server<S, R> 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<S, R> Server<S, R> 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();

View file

@ -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)]

View file

@ -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}

View file

@ -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);

View file

@ -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))