diff --git a/kubi-server/src/auth.rs b/kubi-server/src/auth.rs index a907cf3..9673619 100644 --- a/kubi-server/src/auth.rs +++ b/kubi-server/src/auth.rs @@ -33,6 +33,7 @@ pub fn authenticate_players( let config = storages.borrow::>().unwrap(); for event in &events.0 { + // NOT using `check_message_auth` here because the user is not authed yet! let ServerEvent::Receive(client_addr, data) = event else{ continue }; diff --git a/kubi-server/src/client.rs b/kubi-server/src/client.rs index f29f2d4..3c1d849 100644 --- a/kubi-server/src/client.rs +++ b/kubi-server/src/client.rs @@ -1,12 +1,18 @@ -use shipyard::{Component, EntityId, Unique, AllStoragesView, UniqueView, NonSendSync, View, Get}; +use glam::Mat4; +use shipyard::{Component, EntityId, Unique, AllStoragesView, UniqueView, NonSendSync, View, ViewMut, Get}; use hashbrown::HashMap; use std::net::SocketAddr; -use uflow::server::Event as ServerEvent; -use kubi_shared::networking::{ - client::{ClientIdMap, Client}, - messages::{ClientToServerMessage, C_POSITION_CHANGED} +use kubi_shared::{ + networking::{ + client::{ClientIdMap, Client}, + messages::{ClientToServerMessage, C_POSITION_CHANGED} + }, + transform::Transform +}; +use crate::{ + server::{ServerEvents, UdpServer}, + util::check_message_auth }; -use crate::server::{ServerEvents, IsMessageOfType, UdpServer}; #[derive(Component, Clone, Copy)] pub struct ClientAddress(pub SocketAddr); @@ -29,34 +35,18 @@ pub fn sync_client_positions( events: UniqueView, addr_map: UniqueView, clients: View, + mut transforms: ViewMut ) { for event in &events.0 { - let ServerEvent::Receive(client_addr, data) = event else{ - continue + let Some(message) = check_message_auth::(&server, event, &clients, &addr_map) 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 { + let ClientToServerMessage::PositionChanged { position, velocity: _, direction } = message.message else { unreachable!() }; + //Apply position to client + let mut trans = (&mut transforms).get(message.entity_id).unwrap(); + trans.0 = Mat4::from_rotation_translation(direction, position); - //TODO: sync positions on server } } diff --git a/kubi-server/src/main.rs b/kubi-server/src/main.rs index a5f7e02..3878e8e 100644 --- a/kubi-server/src/main.rs +++ b/kubi-server/src/main.rs @@ -1,6 +1,7 @@ use shipyard::{World, Workload, IntoWorkload}; use std::{thread, time::Duration}; +mod util; mod config; mod server; mod client; diff --git a/kubi-server/src/util.rs b/kubi-server/src/util.rs new file mode 100644 index 0000000..74f2f98 --- /dev/null +++ b/kubi-server/src/util.rs @@ -0,0 +1,72 @@ +use std::{net::SocketAddr, rc::Rc, cell::RefCell}; +use shipyard::{View, Get, EntityId}; +use uflow::server::{Event as ServerEvent, RemoteClient}; +use kubi_shared::networking::{ + messages::ClientToServerMessage, + client::{Client, ClientId} +}; +use crate::{ + server::{IsMessageOfType, UdpServer}, + client::ClientAddressMap +}; + +#[derive(Clone)] +pub struct CtsMessageMetadata<'a> { + pub message: ClientToServerMessage, + pub client_id: ClientId, + pub entity_id: EntityId, + pub client_addr: SocketAddr, + pub client: &'a Rc>, +} +impl From> for ClientToServerMessage { + fn from(value: CtsMessageMetadata) -> Self { value.message } +} +impl From> for ClientId { + fn from(value: CtsMessageMetadata) -> Self { value.client_id } +} +impl From> for EntityId { + fn from(value: CtsMessageMetadata) -> Self { value.entity_id } +} +impl From> for SocketAddr { + fn from(value: CtsMessageMetadata) -> Self { value.client_addr } +} +impl<'a> From> for &'a Rc> { + fn from(value: CtsMessageMetadata<'a>) -> Self { value.client } +} + +pub fn check_message_auth<'a, const C_MSG: u8>( + server: &'a UdpServer, + event: &ServerEvent, + clients: &View, + addr_map: &ClientAddressMap +) -> Option> { + let ServerEvent::Receive(client_addr, data) = event else{ + return None + }; + if !event.is_message_of_type::() { + return None + } + let Some(client) = server.0.client(client_addr) else { + log::error!("Client doesn't exist"); + return None + }; + let Some(&entity_id) = addr_map.0.get(client_addr) else { + log::error!("Client not authenticated"); + return None + }; + let Ok(&Client(client_id)) = (&clients).get(entity_id) else { + log::error!("Entity ID is invalid"); + return None + }; + let Ok(message) = postcard::from_bytes(data) else { + log::error!("Malformed message"); + return None + }; + Some(CtsMessageMetadata { + message, + client_id, + entity_id, + client_addr: *client_addr, + client + }) +} diff --git a/kubi-server/src/world.rs b/kubi-server/src/world.rs index da9f5c8..00d9109 100644 --- a/kubi-server/src/world.rs +++ b/kubi-server/src/world.rs @@ -18,6 +18,7 @@ use crate::{ server::{UdpServer, ServerEvents, IsMessageOfType}, config::ConfigTable, client::{ClientAddress, ClientAddressMap}, + util::check_message_auth, }; pub mod chunk; @@ -61,38 +62,19 @@ fn process_chunk_requests( clients: View ) { for event in &events.0 { - let ServerEvent::Receive(client_addr, data) = event else{ - continue + let Some(message) = check_message_auth::(&server, event, &clients, &addr_map) 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::ChunkSubRequest { chunk: chunk_position } = parsed_message else { + let ClientToServerMessage::ChunkSubRequest { chunk: chunk_position } = message.message else { unreachable!() }; if let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) { - chunk.subscriptions.insert(client_id); + chunk.subscriptions.insert(message.client_id); //TODO Start task here if status is "Nothing" if let Some(blocks) = &chunk.blocks { send_chunk_compressed( - &client, + &message.client, &ServerToClientMessage::ChunkResponse { chunk: chunk_position, data: blocks.clone(), @@ -103,7 +85,7 @@ fn process_chunk_requests( } else { let mut chunk = Chunk::new(chunk_position); chunk.state = ChunkState::Loading; - chunk.subscriptions.insert(client_id); + chunk.subscriptions.insert(message.client_id); chunk_manager.chunks.insert(chunk_position, chunk); task_manager.spawn_task(ChunkTask::LoadChunk { position: chunk_position, @@ -172,32 +154,15 @@ fn process_block_queue_messages( addrs: View, ) { for event in &events.0 { - let ServerEvent::Receive(client_addr, data) = event else{ - continue - }; - if !event.is_message_of_type::() { - 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::QueueBlock { item } = parsed_message else { - unreachable!() + let Some(message) = check_message_auth::(&server, event, &clients, &addr_map) else { + continue; }; + let ClientToServerMessage::QueueBlock { item } = message.message else { unreachable!() }; //TODO place in our own queue, for now just send to other clients log::info!("Placed block {:?} at {}", item.block_type, item.position); for (other_client, other_client_address) in (&clients, &addrs).iter() { //No need to send the event back - if client_id == other_client.0 { + if message.client_id == other_client.0 { continue } //Get client diff --git a/kubi/shaders/world.frag b/kubi/shaders/world.frag index f6054c2..edc8178 100644 --- a/kubi/shaders/world.frag +++ b/kubi/shaders/world.frag @@ -9,9 +9,26 @@ flat in uint v_tex_index; out vec4 color; uniform sampler2DArray tex; +vec4 alpha_drop(vec4 b, vec4 a) { + if ((a.w < 1.) || (b.w < 1.)) { + return vec4(b.xyz, 0.); + } + return a; +} + void main() { // base color from texture color = texture(tex, vec3(v_uv, v_tex_index)); + // HACKY texture "antialiasing" + color += ( + alpha_drop(color, texture(tex, vec3(v_uv + vec2(.000, .001), v_tex_index))) + + alpha_drop(color, texture(tex, vec3(v_uv + vec2(.001, .000), v_tex_index))) + + alpha_drop(color, texture(tex, vec3(v_uv + vec2(.001, .001), v_tex_index))) + + alpha_drop(color, texture(tex, vec3(v_uv - vec2(.000, .001), v_tex_index))) + + alpha_drop(color, texture(tex, vec3(v_uv - vec2(.001, .000), v_tex_index))) + + alpha_drop(color, texture(tex, vec3(v_uv - vec2(.001, .001), v_tex_index))) + ) / 6.; + color /= 2.; // discard fully transparent pixels if (color.w <= 0.0) { discard; diff --git a/kubi/src/main.rs b/kubi/src/main.rs index 1667b6d..b15f056 100644 --- a/kubi/src/main.rs +++ b/kubi/src/main.rs @@ -78,7 +78,7 @@ use delta_time::{DeltaTime, init_delta_time}; use cursor_lock::{insert_lock_state, update_cursor_lock_state, lock_cursor_now}; use control_flow::{exit_on_esc, insert_control_flow_unique, SetControlFlow}; use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting}; -use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit}; +use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer}; use init::initialize_from_args; use gui::{render_gui, init_gui, update_gui}; use loading_screen::update_loading_screen; @@ -114,7 +114,9 @@ fn update() -> Workload { process_inputs, ( init_game_world.run_if_missing_unique::(), - spawn_player.run_if_storage_empty::(), + ( + spawn_player.run_if_storage_empty::(), + ).into_sequential_workload().run_if(is_singleplayer), ).into_sequential_workload().run_if(is_ingame_or_loading), update_networking.run_if(is_multiplayer), ( diff --git a/kubi/src/networking.rs b/kubi/src/networking.rs index 216779e..f2626fc 100644 --- a/kubi/src/networking.rs +++ b/kubi/src/networking.rs @@ -4,7 +4,8 @@ use std::net::SocketAddr; use uflow::{client::{Client, Config as ClientConfig, Event as ClientEvent}, EndpointConfig}; use kubi_shared::networking::{ messages::ServerToClientMessage, - state::ClientJoinState, + state::ClientJoinState, + client::ClientIdMap, }; use crate::{ events::EventComponent, @@ -27,7 +28,10 @@ use world::{ send_block_place_events, recv_block_place_events, }; -use player::send_player_movement_events; +use player::{ + init_client_map, + send_player_movement_events, +}; use self::player::{receive_player_movement_events, receive_player_connect_events}; @@ -111,6 +115,7 @@ fn handle_disconnect( pub fn update_networking() -> Workload { ( + init_client_map.run_if_missing_unique::(), connect_client.run_if_missing_unique::(), poll_client, ( diff --git a/kubi/src/networking/handshake.rs b/kubi/src/networking/handshake.rs index f0bc4fe..4af3689 100644 --- a/kubi/src/networking/handshake.rs +++ b/kubi/src/networking/handshake.rs @@ -1,17 +1,15 @@ -use glam::Mat4; -use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesViewMut, ViewMut, IntoWithId}; +use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesViewMut}; use uflow::{client::Event as ClientEvent, SendMode}; use kubi_shared::{ networking::{ messages::{ClientToServerMessage, ServerToClientMessage, S_SERVER_HELLO}, state::ClientJoinState, channels::CHANNEL_AUTH, - client::{Username, Client}, + client::Username, }, - transform::Transform, entity::Health }; -use crate::player::MainPlayer; -use super::{UdpClient, NetworkEvent, player::add_net_player}; +use crate::player::{MainPlayer, spawn_local_player_multiplayer, spawn_remote_player_multiplayer}; +use super::{UdpClient, NetworkEvent}; pub fn set_client_join_state_to_connected( mut join_state: UniqueViewMut @@ -25,7 +23,7 @@ pub fn say_hello( main_player: View, username: View ) { - let username = (&main_player, &username).iter().next().unwrap().1.0.clone(); + let username = "XxX-FishFucker-69420-XxX".into(); //(&main_player, &username).iter().next().unwrap().1.0.clone(); let password = None; log::info!("Authenticating"); client.0.send( @@ -68,28 +66,11 @@ pub fn check_server_hello_response( // } //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; - } - } + spawn_local_player_multiplayer(&mut storages, init.user); //Init players for init_data in init.users { - add_net_player(&mut storages, init_data); + spawn_remote_player_multiplayer(&mut storages, init_data); } // Set state to connected diff --git a/kubi/src/networking/player.rs b/kubi/src/networking/player.rs index c2a0155..e2b3abf 100644 --- a/kubi/src/networking/player.rs +++ b/kubi/src/networking/player.rs @@ -2,16 +2,17 @@ use glam::{Vec3, Mat4}; use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesView, AllStoragesViewMut, UniqueView, ViewMut, Get}; use uflow::{SendMode, client::Event as ClientEvent}; use kubi_shared::{ - player::{Player, PlayerHolding}, - entity::Entity, transform::Transform, networking::{ - messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED, ClientInitData}, + messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED}, channels::CHANNEL_MOVE, - client::{ClientIdMap, Username, Client}, + client::ClientIdMap, }, }; -use crate::events::player_actions::PlayerActionEvent; +use crate::{ + events::player_actions::PlayerActionEvent, + player::spawn_remote_player_multiplayer, +}; use super::{UdpClient, NetworkEvent}; pub fn init_client_map( @@ -20,35 +21,6 @@ pub fn init_client_map( storages.add_unique(ClientIdMap::new()); } -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( actions: View, mut client: UniqueViewMut, @@ -124,6 +96,6 @@ pub fn receive_player_connect_events( 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); + spawn_remote_player_multiplayer(&mut storages, init); } } diff --git a/kubi/src/player.rs b/kubi/src/player.rs index d6129ea..d42ae7b 100644 --- a/kubi/src/player.rs +++ b/kubi/src/player.rs @@ -1,8 +1,12 @@ -use shipyard::{Component, AllStoragesViewMut}; +use glam::Mat4; +use shipyard::{Component, AllStoragesViewMut, UniqueViewMut}; use kubi_shared::{ entity::{Entity, Health}, player::{PLAYER_HEALTH, PlayerHolding}, - networking::client::Username + networking::{ + client::{Username, Client, ClientIdMap}, + messages::ClientInitData + } }; use crate::{ transform::Transform, @@ -16,7 +20,7 @@ pub use kubi_shared::player::Player; pub struct MainPlayer; pub fn spawn_player ( - mut storages: AllStoragesViewMut + mut storages: AllStoragesViewMut, ) { log::info!("spawning player"); storages.add_entity(( @@ -29,6 +33,55 @@ pub fn spawn_player ( FlyController, LookingAtBlock::default(), PlayerHolding::default(), - Username("Sbeve".into()) + Username("LocalPlayer".into()) )); } + +pub fn spawn_local_player_multiplayer ( + storages: &mut AllStoragesViewMut, + init: ClientInitData +) { + log::info!("spawning local multiplayer player"); + let entity_id = storages.add_entity(( + ( + Player, + Client(init.client_id), + MainPlayer, + Entity, + init.health, + Transform(Mat4::from_rotation_translation(init.direction, init.position)), + Camera::default(), + FlyController, + LookingAtBlock::default(), + PlayerHolding::default(), + ),( + Username(init.username) + ) + )); + + //Add ourself 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 spawn_remote_player_multiplayer( + storages: &mut AllStoragesViewMut, + init: ClientInitData +) { + log::info!("spawning remote multiplayer player"); + + //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); +}