From 163298fe38e07c928a60de103a772728283cee35 Mon Sep 17 00:00:00 2001 From: griffi-gh Date: Fri, 19 May 2023 07:02:20 +0200 Subject: [PATCH] Big refactor + Early wip position sync /connect events in multiplayer --- kubi-server/src/auth.rs | 69 +++++++++++++++++--- kubi-server/src/client.rs | 46 +++++++++++++- kubi-server/src/world.rs | 3 +- kubi-shared/src/entity.rs | 19 +++++- kubi-shared/src/networking/channels.rs | 1 + kubi-shared/src/networking/client.rs | 8 +++ kubi-shared/src/networking/messages.rs | 16 +++-- kubi-shared/src/player.rs | 5 ++ kubi-shared/src/transform.rs | 2 + kubi/src/block_placement.rs | 17 ++--- kubi/src/networking.rs | 14 ++++- kubi/src/networking/handshake.rs | 86 ++++++++++++++++++------- kubi/src/networking/player.rs | 87 +++++++++++++++++++++----- kubi/src/player.rs | 5 +- 14 files changed, 309 insertions(+), 69 deletions(-) diff --git a/kubi-server/src/auth.rs b/kubi-server/src/auth.rs index 514dc27..f41a5a0 100644 --- a/kubi-server/src/auth.rs +++ b/kubi-server/src/auth.rs @@ -1,4 +1,5 @@ -use shipyard::{UniqueView, NonSendSync, EntitiesViewMut, ViewMut, UniqueViewMut, AllStoragesView}; +use glam::{Vec3, Mat4, vec3}; +use shipyard::{UniqueView, NonSendSync, EntitiesViewMut, ViewMut, UniqueViewMut, AllStoragesView, IntoIter}; use uflow::{server::Event as ServerEvent, SendMode}; use kubi_shared::{ networking::{ @@ -6,9 +7,11 @@ use kubi_shared::{ ClientToServerMessage, ServerToClientMessage, InitData, - C_CLIENT_HELLO + ClientInitData, + C_CLIENT_HELLO, }, - client::{Client, ClientId}, channels::CHANNEL_AUTH + client::{Client, ClientId, Username}, + channels::{CHANNEL_AUTH, CHANNEL_SYS_EVT}, }, player::{Player, PLAYER_HEALTH}, transform::Transform, entity::{Entity, Health} @@ -99,13 +102,15 @@ pub fn authenticate_players( &mut storages.borrow::>().unwrap(), &mut storages.borrow::>().unwrap(), &mut storages.borrow::>().unwrap(), + &mut storages.borrow::>().unwrap(), ), ( Entity, Player, Health::new(PLAYER_HEALTH), Client(client_id), ClientAddress(*client_addr), - Transform::default(), + Transform(Mat4::from_translation(vec3(0., 60., 0.))), + Username(username.clone()), )) }; @@ -113,12 +118,60 @@ pub fn authenticate_players( client_entity_map.0.insert(client_id, entity_id); client_addr_map.0.insert(*client_addr, entity_id); - //Approve the user + //Create init data + let init_data = { + let mut user = None; + let mut users = Vec::with_capacity(client_entity_map.0.len() - 1); + for (client, username, transform, &health) in ( + &storages.borrow::>().unwrap(), + &storages.borrow::>().unwrap(), + &storages.borrow::>().unwrap(), + &storages.borrow::>().unwrap(), + ).iter() { + let (_, direction, position) = transform.0.to_scale_rotation_translation(); + let idata = ClientInitData { + client_id: client.0, + username: username.0.clone(), + position, + velocity: Vec3::ZERO, + direction, + health, + }; + if client_id == client.0 { + user = Some(idata); + } else { + users.push(idata); + } + } + InitData { + user: user.unwrap(), + users + } + }; + + //Announce new player to other clients + { + let message = &ServerToClientMessage::PlayerConnected { + init: init_data.user.clone() + }; + for (other_client_addr, _) in client_addr_map.0.iter() { + //TODO: ONLY JOINED CLIENTS HERE! + let Some(other_client) = server.0.client(other_client_addr) else { + log::error!("Other client doesn't exist"); + continue + }; + other_client.borrow_mut().send( + postcard::to_allocvec(&message).unwrap().into_boxed_slice(), + CHANNEL_SYS_EVT, + SendMode::Reliable + ); + } + } + + //Approve the user and send init data client.borrow_mut().send( postcard::to_allocvec(&ServerToClientMessage::ServerHello { - init: InitData { - users: vec![] //TODO create init data - } + init: init_data }).unwrap().into_boxed_slice(), CHANNEL_AUTH, SendMode::Reliable diff --git a/kubi-server/src/client.rs b/kubi-server/src/client.rs index 6e385d4..238a1f5 100644 --- a/kubi-server/src/client.rs +++ b/kubi-server/src/client.rs @@ -1,7 +1,12 @@ -use shipyard::{Component, EntityId, Unique, AllStoragesView}; +use shipyard::{Component, EntityId, Unique, AllStoragesView, UniqueView, NonSendSync, View, Get}; use hashbrown::HashMap; use std::net::SocketAddr; -pub use kubi_shared::networking::client::ClientIdMap; +use uflow::server::Event as ServerEvent; +use kubi_shared::networking::{ + client::{ClientIdMap, Client}, + messages::{ClientToServerMessage, C_POSITION_CHANGED} +}; +use crate::server::{ServerEvents, IsMessageOfType, UdpServer}; #[derive(Component, Clone, Copy)] pub struct ClientAddress(pub SocketAddr); @@ -18,3 +23,40 @@ pub fn init_client_maps( storages.add_unique(ClientIdMap::new()); storages.add_unique(ClientAddressMap::new()); } + +pub fn sync_client_positions( + server: NonSendSync>, + events: UniqueView, + addr_map: UniqueView, + clients: View, +) { + for event in &events.0 { + let ServerEvent::Receive(client_addr, data) = event else{ + continue + }; + if !event.is_message_of_type::() { + continue + } + let Some(client) = server.0.client(client_addr) else { + log::error!("Client doesn't exist"); + continue + }; + let Some(&entity_id) = addr_map.0.get(client_addr) else { + log::error!("Client not authenticated"); + continue + }; + let Ok(&Client(client_id)) = (&clients).get(entity_id) else { + log::error!("Entity ID is invalid"); + continue + }; + let Ok(parsed_message) = postcard::from_bytes(data) else { + log::error!("Malformed message"); + continue + }; + let ClientToServerMessage::PositionChanged { position, velocity, direction } = parsed_message else { + unreachable!() + }; + + + } +} diff --git a/kubi-server/src/world.rs b/kubi-server/src/world.rs index fa34ac2..da9f5c8 100644 --- a/kubi-server/src/world.rs +++ b/kubi-server/src/world.rs @@ -13,10 +13,11 @@ use uflow::{ use lz4_flex::compress_prepend_size as lz4_compress; use anyhow::Result; use std::{rc::Rc, cell::RefCell}; +use kubi_shared::networking::client::ClientIdMap; use crate::{ server::{UdpServer, ServerEvents, IsMessageOfType}, config::ConfigTable, - client::{ClientAddress, ClientIdMap, ClientAddressMap}, + client::{ClientAddress, ClientAddressMap}, }; pub mod chunk; diff --git a/kubi-shared/src/entity.rs b/kubi-shared/src/entity.rs index 9c5401d..37c973a 100644 --- a/kubi-shared/src/entity.rs +++ b/kubi-shared/src/entity.rs @@ -1,9 +1,10 @@ use shipyard::Component; +use serde::{Serialize, Deserialize}; #[derive(Component)] pub struct Entity; -#[derive(Component)] +#[derive(Component, Serialize, Deserialize, Clone, Copy, Debug)] pub struct Health { pub current: u8, pub max: u8, @@ -16,3 +17,19 @@ impl Health { } } } +impl PartialEq for Health { + fn eq(&self, other: &Self) -> bool { + self.current == other.current + } +} +impl Eq for Health {} +impl PartialOrd for Health { + fn partial_cmp(&self, other: &Self) -> Option { + self.current.partial_cmp(&other.current) + } +} +impl Ord for Health { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.current.cmp(&other.current) + } +} diff --git a/kubi-shared/src/networking/channels.rs b/kubi-shared/src/networking/channels.rs index 88ddfc1..57de639 100644 --- a/kubi-shared/src/networking/channels.rs +++ b/kubi-shared/src/networking/channels.rs @@ -3,3 +3,4 @@ pub const CHANNEL_AUTH: usize = 1; pub const CHANNEL_WORLD: usize = 2; pub const CHANNEL_BLOCK: usize = 3; pub const CHANNEL_MOVE: usize = 4; +pub const CHANNEL_SYS_EVT: usize = 5; diff --git a/kubi-shared/src/networking/client.rs b/kubi-shared/src/networking/client.rs index c4baa5c..58c5970 100644 --- a/kubi-shared/src/networking/client.rs +++ b/kubi-shared/src/networking/client.rs @@ -4,16 +4,24 @@ use nohash_hasher::BuildNoHashHasher; pub type ClientId = u16; +#[derive(Component, Clone, Debug)] +#[repr(transparent)] +pub struct Username(pub String); + #[derive(Component, Clone, Copy, Debug)] +#[repr(transparent)] pub struct Client(pub ClientId); #[derive(Unique)] +#[repr(transparent)] pub struct ClientIdMap(pub HashMap>); + impl ClientIdMap { pub fn new() -> Self { Self(HashMap::with_capacity_and_hasher(16, BuildNoHashHasher::default())) } } + impl Default for ClientIdMap { fn default() -> Self { Self::new() diff --git a/kubi-shared/src/networking/messages.rs b/kubi-shared/src/networking/messages.rs index 39ef2ec..8dac1ba 100644 --- a/kubi-shared/src/networking/messages.rs +++ b/kubi-shared/src/networking/messages.rs @@ -1,6 +1,6 @@ use glam::{Vec3, IVec3, Quat}; use serde::{Serialize, Deserialize}; -use crate::{chunk::BlockData, queue::QueuedBlock}; +use crate::{chunk::BlockData, queue::QueuedBlock, entity::Health}; use super::client::ClientId; //protocol id not used yet @@ -41,6 +41,7 @@ pub const S_PLAYER_POSITION_CHANGED: u8 = 2; pub const S_CHUNK_RESPONSE: u8 = 3; pub const S_QUEUE_BLOCK: u8 = 4; pub const S_PLAYER_CONNECTED: u8 = 5; +pub const S_PLAYER_DISCONNECTED: u8 = 6; #[derive(Serialize, Deserialize, Clone)] #[repr(u8)] @@ -52,7 +53,7 @@ pub enum ServerToClientMessage { reason: String, } = S_SERVER_FUCK_OFF, PlayerPositionChanged { - client_id: u8, + client_id: ClientId, position: Vec3, direction: Quat, } = S_PLAYER_POSITION_CHANGED, @@ -69,22 +70,27 @@ pub enum ServerToClientMessage { item: QueuedBlock } = S_QUEUE_BLOCK, PlayerConnected { - init: UserInitData + init: ClientInitData } = S_PLAYER_CONNECTED, + PlayerDisconnected { + id: ClientId + } = S_PLAYER_DISCONNECTED, } //--- #[derive(Serialize, Deserialize, Clone)] -pub struct UserInitData { +pub struct ClientInitData { pub client_id: ClientId, pub username: String, pub position: Vec3, pub velocity: Vec3, pub direction: Quat, + pub health: Health, } #[derive(Serialize, Deserialize, Clone)] pub struct InitData { - pub users: Vec + pub user: ClientInitData, + pub users: Vec } diff --git a/kubi-shared/src/player.rs b/kubi-shared/src/player.rs index ea96706..3ef9b5c 100644 --- a/kubi-shared/src/player.rs +++ b/kubi-shared/src/player.rs @@ -1,6 +1,11 @@ use shipyard::Component; +use crate::block::Block; pub const PLAYER_HEALTH: u8 = 20; #[derive(Component)] pub struct Player; + +#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Eq)] +#[repr(transparent)] +pub struct PlayerHolding(pub Option); diff --git a/kubi-shared/src/transform.rs b/kubi-shared/src/transform.rs index c918deb..c2b911f 100644 --- a/kubi-shared/src/transform.rs +++ b/kubi-shared/src/transform.rs @@ -2,7 +2,9 @@ use shipyard::Component; use glam::{Mat4, Mat3}; #[derive(Component, Clone, Copy, Debug, Default)] +#[repr(transparent)] pub struct Transform(pub Mat4); #[derive(Component, Clone, Copy, Debug, Default)] +#[repr(transparent)] pub struct Transform2d(pub Mat3); diff --git a/kubi/src/block_placement.rs b/kubi/src/block_placement.rs index 800bbdb..110d7a8 100644 --- a/kubi/src/block_placement.rs +++ b/kubi/src/block_placement.rs @@ -2,7 +2,8 @@ use shipyard::{UniqueViewMut, UniqueView, View, IntoIter, ViewMut, EntitiesViewM use glium::glutin::event::VirtualKeyCode; use kubi_shared::{ block::Block, - queue::QueuedBlock, + queue::QueuedBlock, + player::PlayerHolding, }; use crate::{ player::MainPlayer, @@ -11,14 +12,6 @@ use crate::{ events::{EventComponent, player_actions::PlayerActionEvent}, }; -#[derive(Component)] -pub struct PlayerHolding(pub Block); -impl Default for PlayerHolding { - fn default() -> Self { - Self(Block::Cobblestone) - } -} - const BLOCK_KEY_MAP: &[(VirtualKeyCode, Block)] = &[ (VirtualKeyCode::Key1, Block::Cobblestone), (VirtualKeyCode::Key2, Block::Planks), @@ -38,7 +31,7 @@ fn pick_block_with_number_keys( let Some((_, mut holding)) = (&main_player, &mut holding).iter().next() else { return }; for &(key, block) in BLOCK_KEY_MAP { if input.keyboard_state.contains(&key) { - holding.0 = block; + holding.0 = Some(block); return } } @@ -63,9 +56,9 @@ fn block_placement_system( let Some(ray) = ray.0 else { return }; //get coord and block type let (place_position, place_block) = if action_place { - if block.0 == Block::Air { return } + if block.0.is_none() { return } let position = (ray.position - ray.direction * (RAYCAST_STEP + 0.001)).floor().as_ivec3(); - (position, block.0) + (position, block.0.unwrap()) } else { (ray.block_position, Block::Air) }; diff --git a/kubi/src/networking.rs b/kubi/src/networking.rs index 618bb5e..216779e 100644 --- a/kubi/src/networking.rs +++ b/kubi/src/networking.rs @@ -29,6 +29,8 @@ use world::{ }; use player::send_player_movement_events; +use self::player::{receive_player_movement_events, receive_player_connect_events}; + #[derive(Unique, Clone, Copy, PartialEq, Eq)] pub enum GameType { Singleplayer, @@ -120,8 +122,14 @@ pub fn update_networking() -> Workload { handle_disconnect, ).into_sequential_workload().run_if(is_join_state::<{ClientJoinState::Connected as u8}>), ( - recv_block_place_events - ).run_if(is_join_state::<{ClientJoinState::Joined as u8}>).run_if(is_ingame_or_loading), + ( + receive_player_connect_events + ), + ( + recv_block_place_events, + receive_player_movement_events, + ).into_workload() + ).into_sequential_workload().run_if(is_join_state::<{ClientJoinState::Joined as u8}>).run_if(is_ingame_or_loading), inject_network_responses_into_manager_queue.run_if(is_ingame_or_loading).skip_if_missing_unique::(), ).into_sequential_workload() } @@ -131,7 +139,7 @@ pub fn update_networking_late() -> Workload { ( send_block_place_events, send_player_movement_events, - ).into_sequential_workload().run_if(is_join_state::<{ClientJoinState::Joined as u8}>), + ).into_workload().run_if(is_join_state::<{ClientJoinState::Joined as u8}>), flush_client, ).into_sequential_workload() } diff --git a/kubi/src/networking/handshake.rs b/kubi/src/networking/handshake.rs index 9066947..f0bc4fe 100644 --- a/kubi/src/networking/handshake.rs +++ b/kubi/src/networking/handshake.rs @@ -1,11 +1,17 @@ -use shipyard::{UniqueViewMut, View, IntoIter}; +use glam::Mat4; +use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesViewMut, ViewMut, IntoWithId}; use uflow::{client::Event as ClientEvent, SendMode}; -use kubi_shared::networking::{ - messages::{ClientToServerMessage, ServerToClientMessage, S_SERVER_HELLO}, - state::ClientJoinState, - channels::CHANNEL_AUTH, +use kubi_shared::{ + networking::{ + messages::{ClientToServerMessage, ServerToClientMessage, S_SERVER_HELLO}, + state::ClientJoinState, + channels::CHANNEL_AUTH, + client::{Username, Client}, + }, + transform::Transform, entity::Health }; -use super::{UdpClient, NetworkEvent}; +use crate::player::MainPlayer; +use super::{UdpClient, NetworkEvent, player::add_net_player}; pub fn set_client_join_state_to_connected( mut join_state: UniqueViewMut @@ -16,14 +22,15 @@ pub fn set_client_join_state_to_connected( pub fn say_hello( mut client: UniqueViewMut, + main_player: View, + username: View ) { + let username = (&main_player, &username).iter().next().unwrap().1.0.clone(); + let password = None; log::info!("Authenticating"); client.0.send( postcard::to_allocvec( - &ClientToServerMessage::ClientHello { - username: "Sbeve".into(), - password: None - } + &ClientToServerMessage::ClientHello { username, password } ).unwrap().into_boxed_slice(), CHANNEL_AUTH, SendMode::Reliable @@ -31,26 +38,63 @@ pub fn say_hello( } pub fn check_server_hello_response( - network_events: View, - mut join_state: UniqueViewMut + mut storages: AllStoragesViewMut, ) { - for event in network_events.iter() { + //Check if we got the message and extract the init data from it + let Some(init) = storages.borrow::>().unwrap().iter().find_map(|event| { let ClientEvent::Receive(data) = &event.0 else { - continue + return None }; if !event.is_message_of_type::() { - continue + return None } let Ok(parsed_message) = postcard::from_bytes(data) else { log::error!("Malformed message"); - continue + return None }; - let ServerToClientMessage::ServerHello { init: _ } = parsed_message else { + let ServerToClientMessage::ServerHello { init } = parsed_message else { unreachable!() }; - //TODO handle init data - *join_state = ClientJoinState::Joined; - log::info!("Joined the server!"); - return; + Some(init) + }) else { return }; + + // struct ClientInitData { + // client_id: ClientId, + // username: String, + // position: Vec3, + // velocity: Vec3, + // direction: Quat, + // health: Health, + // } + + //Add components to main player + { + let entity = (&storages.borrow::>().unwrap()).iter().with_id().next().unwrap().0; + storages.add_component(entity, Client(init.user.client_id)); } + + //Modify main player + { + for (entity, (_, mut username, mut transform, mut health)) in ( + &storages.borrow::>().unwrap(), + &mut storages.borrow::>().unwrap(), + &mut storages.borrow::>().unwrap(), + &mut storages.borrow::>().unwrap(), + ).iter().with_id() { + username.0 = init.user.username.clone(); + transform.0 = Mat4::from_rotation_translation(init.user.direction, init.user.position); + *health = init.user.health; + } + } + + //Init players + for init_data in init.users { + add_net_player(&mut storages, init_data); + } + + // Set state to connected + let mut join_state = storages.borrow::>().unwrap(); + *join_state = ClientJoinState::Joined; + + log::info!("Joined the server!"); } diff --git a/kubi/src/networking/player.rs b/kubi/src/networking/player.rs index 48d7e5e..c2a0155 100644 --- a/kubi/src/networking/player.rs +++ b/kubi/src/networking/player.rs @@ -1,12 +1,15 @@ -use glam::Vec3; -use hashbrown::HashMap; -use nohash_hasher::BuildNoHashHasher; -use shipyard::{UniqueViewMut, View, IntoIter, Unique, EntityId, AllStoragesView}; +use glam::{Vec3, Mat4}; +use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesView, AllStoragesViewMut, UniqueView, ViewMut, Get}; use uflow::{SendMode, client::Event as ClientEvent}; -use kubi_shared::networking::{ - messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED}, - channels::CHANNEL_MOVE, - client::{ClientId, ClientIdMap}, +use kubi_shared::{ + player::{Player, PlayerHolding}, + entity::Entity, + transform::Transform, + networking::{ + messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED, ClientInitData}, + channels::CHANNEL_MOVE, + client::{ClientIdMap, Username, Client}, + }, }; use crate::events::player_actions::PlayerActionEvent; use super::{UdpClient, NetworkEvent}; @@ -17,8 +20,33 @@ pub fn init_client_map( storages.add_unique(ClientIdMap::new()); } -pub fn add_net_player() { - //TODO +pub fn add_net_player( + storages: &mut AllStoragesViewMut, + init: ClientInitData +) { + // struct ClientInitData { + // client_id: ClientId, + // username: String, + // position: Vec3, + // velocity: Vec3, + // direction: Quat, + // health: Health, + // } + + //Spawn player locally + let entity_id = storages.add_entity(( + Username(init.username), + Client(init.client_id), + Player, + Entity, + init.health, + Transform(Mat4::from_rotation_translation(init.direction, init.position)), + PlayerHolding::default(), + )); + + //Add it to the client id map + let mut client_id_map = storages.borrow::>().unwrap(); + client_id_map.0.insert(init.client_id, entity_id); } pub fn send_player_movement_events( @@ -42,29 +70,60 @@ pub fn send_player_movement_events( } pub fn receive_player_movement_events( + mut transforms: ViewMut, network_events: View, + id_map: UniqueView ) { for event in network_events.iter() { let ClientEvent::Receive(data) = &event.0 else { continue }; + if !event.is_message_of_type::() { continue } + let Ok(parsed_message) = postcard::from_bytes(data) else { log::error!("Malformed message"); continue }; + let ServerToClientMessage::PlayerPositionChanged { client_id, position, direction } = parsed_message else { unreachable!() }; - //TODO apply position to local player + + let Some(&ent_id) = id_map.0.get(&client_id) else { + log::error!("Not in client-id map"); + continue + }; + + let mut transform = (&mut transforms).get(ent_id) + .expect("invalid player entity id"); + + transform.0 = Mat4::from_rotation_translation(direction, position); } } - -pub fn receive_connected_players( - network_events: View, +pub fn receive_player_connect_events( + mut storages: AllStoragesViewMut, ) { + let messages: Vec = storages.borrow::>().unwrap().iter().filter_map(|event| { + let ClientEvent::Receive(data) = &event.0 else { + return None + }; + if !event.is_message_of_type::() { + return None + }; + let Ok(parsed_message) = postcard::from_bytes(data) else { + log::error!("Malformed message"); + return None + }; + Some(parsed_message) + }).collect(); + for message in messages { + let ServerToClientMessage::PlayerConnected { init } = message else { unreachable!() }; + log::info!("player connected: {} (id {})", init.username, init.client_id); + add_net_player(&mut storages, init); + } } diff --git a/kubi/src/player.rs b/kubi/src/player.rs index 67d2cae..d6129ea 100644 --- a/kubi/src/player.rs +++ b/kubi/src/player.rs @@ -1,14 +1,14 @@ use shipyard::{Component, AllStoragesViewMut}; use kubi_shared::{ entity::{Entity, Health}, - player::PLAYER_HEALTH + player::{PLAYER_HEALTH, PlayerHolding}, + networking::client::Username }; use crate::{ transform::Transform, camera::Camera, fly_controller::FlyController, world::raycast::LookingAtBlock, - block_placement::PlayerHolding, }; pub use kubi_shared::player::Player; @@ -29,5 +29,6 @@ pub fn spawn_player ( FlyController, LookingAtBlock::default(), PlayerHolding::default(), + Username("Sbeve".into()) )); }