mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-11-25 08:18:43 -06:00
protocol id
This commit is contained in:
parent
a5b35ffa7d
commit
6b9243a556
|
@ -1,3 +1,4 @@
|
|||
[server]
|
||||
address = "0.0.0.0:12345"
|
||||
max_clients = 254
|
||||
timeout_ms = 10000
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue