From dd966dd00e8f15ce747d62e6910cf154578374c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Fri, 16 Oct 2020 21:37:28 +0200 Subject: [PATCH 01/10] remote all streams from `Client` and put it directly in the ecs system. This commit does not run compile as the message sys now would requiere 30 imput parameters which is over the max of 26 :/ --- server/src/client.rs | 126 +++---------- server/src/cmd.rs | 16 +- server/src/connection_handler.rs | 39 +++-- server/src/events/entity_manipulation.rs | 8 +- server/src/events/group_manip.rs | 187 +++++++++++--------- server/src/events/interaction.rs | 214 +++++++++++++++-------- server/src/events/inventory_manip.rs | 15 +- server/src/events/player.rs | 53 +++++- server/src/lib.rs | 93 +++++++++- server/src/state_ext.rs | 97 ++++++---- server/src/sys/entity_sync.rs | 113 +++++++----- server/src/sys/invite_timeout.rs | 17 +- server/src/sys/message.rs | 98 ++++++----- server/src/sys/subscription.rs | 35 ++-- server/src/sys/terrain.rs | 18 +- server/src/sys/terrain_sync.rs | 15 +- server/src/sys/waypoint.rs | 24 ++- 17 files changed, 707 insertions(+), 461 deletions(-) diff --git a/server/src/client.rs b/server/src/client.rs index e706689d80..eec6385676 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -1,24 +1,39 @@ -use crate::error::Error; -use common::msg::{ClientInGame, ClientType, ServerGeneral, ServerMsg}; +use common::msg::{ClientInGame, ClientType}; use hashbrown::HashSet; use network::{Participant, Stream}; -use serde::{de::DeserializeOwned, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; -use tracing::debug; use vek::*; +// Streams +// we ignore errors on send, and do unified error handling in recv +pub struct GeneralStream(pub Stream); +pub struct PingStream(pub Stream); +pub struct RegisterStream(pub Stream); +pub struct CharacterScreenStream(pub Stream); +pub struct InGameStream(pub Stream); + +impl Component for GeneralStream { + type Storage = FlaggedStorage>; +} +impl Component for PingStream { + type Storage = FlaggedStorage>; +} +impl Component for RegisterStream { + type Storage = FlaggedStorage>; +} +impl Component for CharacterScreenStream { + type Storage = FlaggedStorage>; +} +impl Component for InGameStream { + type Storage = FlaggedStorage>; +} + pub struct Client { pub registered: bool, pub client_type: ClientType, pub in_game: Option, pub participant: Option, - pub general_stream: Stream, - pub ping_stream: Stream, - pub register_stream: Stream, - pub character_screen_stream: Stream, - pub in_game_stream: Stream, - pub network_error: bool, pub last_ping: f64, pub login_msg_sent: bool, } @@ -27,97 +42,6 @@ impl Component for Client { type Storage = FlaggedStorage>; } -impl Client { - fn internal_send(err: &mut bool, s: &mut Stream, msg: M) { - if !*err { - if let Err(e) = s.send(msg) { - debug!(?e, "got a network error with client"); - *err = true; - } - } - } - - /* - fn internal_send_raw(b: &AtomicBool, s: &mut Stream, msg: Arc) { - if !b.load(Ordering::Relaxed) { - if let Err(e) = s.send_raw(msg) { - debug!(?e, "got a network error with client"); - b.store(true, Ordering::Relaxed); - } - } - } - */ - - pub fn send_msg(&mut self, msg: S) - where - S: Into, - { - const ERR: &str = - "Don't do that. Sending these messages is only done ONCE at connect and not by this fn"; - match msg.into() { - ServerMsg::Info(_) => panic!(ERR), - ServerMsg::Init(_) => panic!(ERR), - ServerMsg::RegisterAnswer(msg) => { - Self::internal_send(&mut self.network_error, &mut self.register_stream, &msg) - }, - ServerMsg::General(msg) => { - let stream = match &msg { - //Character Screen related - ServerGeneral::CharacterDataLoadError(_) - | ServerGeneral::CharacterListUpdate(_) - | ServerGeneral::CharacterActionError(_) - | ServerGeneral::CharacterSuccess => &mut self.character_screen_stream, - //Ingame related - ServerGeneral::GroupUpdate(_) - | ServerGeneral::GroupInvite { .. } - | ServerGeneral::InvitePending(_) - | ServerGeneral::InviteComplete { .. } - | ServerGeneral::ExitInGameSuccess - | ServerGeneral::InventoryUpdate(_, _) - | ServerGeneral::TerrainChunkUpdate { .. } - | ServerGeneral::TerrainBlockUpdates(_) - | ServerGeneral::SetViewDistance(_) - | ServerGeneral::Outcomes(_) - | ServerGeneral::Knockback(_) => &mut self.in_game_stream, - // Always possible - ServerGeneral::PlayerListUpdate(_) - | ServerGeneral::ChatMsg(_) - | ServerGeneral::SetPlayerEntity(_) - | ServerGeneral::TimeOfDay(_) - | ServerGeneral::EntitySync(_) - | ServerGeneral::CompSync(_) - | ServerGeneral::CreateEntity(_) - | ServerGeneral::DeleteEntity(_) - | ServerGeneral::Disconnect(_) - | ServerGeneral::Notification(_) => &mut self.general_stream, - }; - Self::internal_send(&mut self.network_error, stream, &msg) - }, - ServerMsg::Ping(msg) => { - Self::internal_send(&mut self.network_error, &mut self.ping_stream, &msg) - }, - }; - } - - pub async fn internal_recv( - err: &mut bool, - s: &mut Stream, - ) -> Result { - if !*err { - match s.recv().await { - Ok(r) => Ok(r), - Err(e) => { - debug!(?e, "got a network error with client while recv"); - *err = true; - Err(Error::StreamErr(e)) - }, - } - } else { - Err(Error::StreamErr(network::StreamError::StreamClosed)) - } - } -} - // Distance from fuzzy_chunk before snapping to current chunk pub const CHUNK_FUZZ: u32 = 2; // Distance out of the range of a region before removing it from subscriptions diff --git a/server/src/cmd.rs b/server/src/cmd.rs index e747e725ad..462d6c3e05 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -3,7 +3,6 @@ //! `CHAT_COMMANDS` and provide a handler function. use crate::{ - client::Client, settings::{BanRecord, EditableSetting}, Server, StateExt, }; @@ -27,7 +26,7 @@ use std::convert::TryFrom; use vek::*; use world::util::Sampler; -use crate::login_provider::LoginProvider; +use crate::{client::InGameStream, login_provider::LoginProvider}; use scan_fmt::{scan_fmt, scan_fmt_some}; use tracing::error; @@ -650,7 +649,8 @@ fn handle_spawn( // Add to group system if a pet if matches!(alignment, comp::Alignment::Owned { .. }) { let state = server.state(); - let mut clients = state.ecs().write_storage::(); + let mut in_game_streams = + state.ecs().write_storage::(); let uids = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); @@ -662,15 +662,15 @@ fn handle_spawn( &state.ecs().read_storage(), &uids, &mut |entity, group_change| { - clients + in_game_streams .get_mut(entity) - .and_then(|c| { + .and_then(|s| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, c)) + .map(|g| (g, s)) }) - .map(|(g, c)| { - c.send_msg(ServerGeneral::GroupUpdate(g)) + .map(|(g, s)| { + let _ = s.0.send(ServerGeneral::GroupUpdate(g)); }); }, ); diff --git a/server/src/connection_handler.rs b/server/src/connection_handler.rs index 2f79f1795d..82c4f191f9 100644 --- a/server/src/connection_handler.rs +++ b/server/src/connection_handler.rs @@ -1,4 +1,7 @@ -use crate::{Client, ClientType, ServerInfo}; +use crate::{ + CharacterScreenStream, Client, ClientType, GeneralStream, InGameStream, PingStream, + RegisterStream, ServerInfo, +}; use crossbeam::{bounded, unbounded, Receiver, Sender}; use futures_channel::oneshot; use futures_executor::block_on; @@ -13,10 +16,19 @@ pub(crate) struct ServerInfoPacket { pub time: f64, } +pub(crate) struct ClientPackage { + pub client: Client, + pub general: GeneralStream, + pub ping: PingStream, + pub register: RegisterStream, + pub character: CharacterScreenStream, + pub in_game: InGameStream, +} + pub(crate) struct ConnectionHandler { _network: Arc, thread_handle: Option>, - pub client_receiver: Receiver, + pub client_receiver: Receiver, pub info_requester_receiver: Receiver>, stop_sender: Option>, } @@ -31,7 +43,7 @@ impl ConnectionHandler { let network_clone = Arc::clone(&network); let (stop_sender, stop_receiver) = oneshot::channel(); - let (client_sender, client_receiver) = unbounded::(); + let (client_sender, client_receiver) = unbounded::(); let (info_requester_sender, info_requester_receiver) = bounded::>(1); @@ -55,7 +67,7 @@ impl ConnectionHandler { async fn work( network: Arc, - client_sender: Sender, + client_sender: Sender, info_requester_sender: Sender>, stop_receiver: oneshot::Receiver<()>, ) { @@ -92,7 +104,7 @@ impl ConnectionHandler { async fn init_participant( participant: Participant, - client_sender: Sender, + client_sender: Sender, info_requester_sender: Sender>, ) -> Result<(), Box> { debug!("New Participant connected to the server"); @@ -129,17 +141,20 @@ impl ConnectionHandler { client_type, in_game: None, participant: Some(participant), - general_stream, - ping_stream, - register_stream, - in_game_stream, - character_screen_stream, - network_error: false, last_ping: server_data.time, login_msg_sent: false, }; - client_sender.send(client)?; + let package = ClientPackage { + client, + general: GeneralStream(general_stream), + ping: PingStream(ping_stream), + register: RegisterStream(register_stream), + character: CharacterScreenStream(character_screen_stream), + in_game: InGameStream(in_game_stream), + }; + + client_sender.send(package)?; Ok(()) } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index cb175c4c20..1e3dd4af62 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,5 +1,5 @@ use crate::{ - client::Client, + client::{Client, InGameStream}, comp::{biped_large, quadruped_medium, quadruped_small}, Server, SpawnPoint, StateExt, }; @@ -42,9 +42,9 @@ pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3) if let Some(vel) = velocities.get_mut(entity) { vel.0 = impulse; } - let mut clients = state.ecs().write_storage::(); - if let Some(client) = clients.get_mut(entity) { - client.send_msg(ServerGeneral::Knockback(impulse)); + let mut in_game_streams = state.ecs().write_storage::(); + if let Some(in_game_stream) = in_game_streams.get_mut(entity) { + let _ = in_game_stream.0.send(ServerGeneral::Knockback(impulse)); } } diff --git a/server/src/events/group_manip.rs b/server/src/events/group_manip.rs index 78c0df462b..8d8e00d150 100644 --- a/server/src/events/group_manip.rs +++ b/server/src/events/group_manip.rs @@ -1,4 +1,7 @@ -use crate::{client::Client, Server}; +use crate::{ + client::{GeneralStream, InGameStream}, + Server, +}; use common::{ comp::{ self, @@ -25,20 +28,21 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani match manip { GroupManip::Invite(uid) => { - let mut clients = state.ecs().write_storage::(); - let invitee = match state.ecs().entity_from_uid(uid.into()) { - Some(t) => t, - None => { - // Inform of failure - if let Some(client) = clients.get_mut(entity) { - client.send_msg( - ChatType::Meta - .server_msg("Invite failed, target does not exist.".to_owned()), - ); - } - return; - }, - }; + let mut general_streams = state.ecs().write_storage::(); + let invitee = + match state.ecs().entity_from_uid(uid.into()) { + Some(t) => t, + None => { + // Inform of failure + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = + general_stream.0.send(ChatType::Meta.server_msg( + "Invite failed, target does not exist.".to_owned(), + )); + } + return; + }, + }; let uids = state.ecs().read_storage::(); @@ -62,8 +66,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }); if already_in_same_group { // Inform of failure - if let Some(client) = clients.get_mut(entity) { - client.send_msg(ChatType::Meta.server_msg( + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send(ChatType::Meta.server_msg( "Invite failed, can't invite someone already in your group".to_owned(), )); } @@ -92,8 +96,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani >= max_group_size as usize; if group_size_limit_reached { // Inform inviter that they have reached the group size limit - if let Some(client) = clients.get_mut(entity) { - client.send_msg( + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send( ChatType::Meta.server_msg( "Invite failed, pending invites plus current group size have reached \ the group size limit" @@ -109,11 +113,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if invites.contains(invitee) { // Inform inviter that there is already an invite - if let Some(client) = clients.get_mut(entity) { - client.send_msg( - ChatType::Meta - .server_msg("This player already has a pending invite.".to_owned()), - ); + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = + general_stream + .0 + .send(ChatType::Meta.server_msg( + "This player already has a pending invite.".to_owned(), + )); } return; } @@ -150,33 +156,35 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani } }; + let mut in_game_streams = state.ecs().write_storage::(); + // If client comp - if let (Some(client), Some(inviter)) = - (clients.get_mut(invitee), uids.get(entity).copied()) + if let (Some(in_game_stream), Some(inviter)) = + (in_game_streams.get_mut(invitee), uids.get(entity).copied()) { if send_invite() { - client.send_msg(ServerGeneral::GroupInvite { + let _ = in_game_stream.0.send(ServerGeneral::GroupInvite { inviter, timeout: PRESENTED_INVITE_TIMEOUT_DUR, }); } } else if agents.contains(invitee) { send_invite(); - } else if let Some(client) = clients.get_mut(entity) { - client.send_msg( + } else if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send( ChatType::Meta.server_msg("Can't invite, not a player or npc".to_owned()), ); } // Notify inviter that the invite is pending if invite_sent { - if let Some(client) = clients.get_mut(entity) { - client.send_msg(ServerGeneral::InvitePending(uid)); + if let Some(in_game_stream) = in_game_streams.get_mut(entity) { + let _ = in_game_stream.0.send(ServerGeneral::InvitePending(uid)); } } }, GroupManip::Accept => { - let mut clients = state.ecs().write_storage::(); + let mut in_game_streams = state.ecs().write_storage::(); let uids = state.ecs().read_storage::(); let mut invites = state.ecs().write_storage::(); if let Some(inviter) = invites.remove(entity).and_then(|invite| { @@ -193,13 +201,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani Some(inviter) }) { - if let (Some(client), Some(target)) = - (clients.get_mut(inviter), uids.get(entity).copied()) + if let (Some(in_game_stream), Some(target)) = + (in_game_streams.get_mut(inviter), uids.get(entity).copied()) { - client.send_msg(ServerGeneral::InviteComplete { + let _ = in_game_stream.0.send(ServerGeneral::InviteComplete { target, answer: InviteAnswer::Accepted, - }) + }); } let mut group_manager = state.ecs().write_resource::(); group_manager.add_group_member( @@ -210,20 +218,20 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani &state.ecs().read_storage(), &uids, |entity, group_change| { - clients + in_game_streams .get_mut(entity) - .and_then(|c| { + .and_then(|s| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, c)) + .map(|g| (g, s)) }) - .map(|(g, c)| c.send_msg(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); }, ); } }, GroupManip::Decline => { - let mut clients = state.ecs().write_storage::(); + let mut in_game_streams = state.ecs().write_storage::(); let uids = state.ecs().read_storage::(); let mut invites = state.ecs().write_storage::(); if let Some(inviter) = invites.remove(entity).and_then(|invite| { @@ -241,18 +249,18 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani Some(inviter) }) { // Inform inviter of rejection - if let (Some(client), Some(target)) = - (clients.get_mut(inviter), uids.get(entity).copied()) + if let (Some(in_game_stream), Some(target)) = + (in_game_streams.get_mut(inviter), uids.get(entity).copied()) { - client.send_msg(ServerGeneral::InviteComplete { + let _ = in_game_stream.0.send(ServerGeneral::InviteComplete { target, answer: InviteAnswer::Declined, - }) + }); } } }, GroupManip::Leave => { - let mut clients = state.ecs().write_storage::(); + let mut in_game_streams = state.ecs().write_storage::(); let uids = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); group_manager.leave_group( @@ -262,19 +270,19 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani &uids, &state.ecs().entities(), &mut |entity, group_change| { - clients + in_game_streams .get_mut(entity) - .and_then(|c| { + .and_then(|s| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, c)) + .map(|g| (g, s)) }) - .map(|(g, c)| c.send_msg(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); }, ); }, GroupManip::Kick(uid) => { - let mut clients = state.ecs().write_storage::(); + let mut general_streams = state.ecs().write_storage::(); let uids = state.ecs().read_storage::(); let alignments = state.ecs().read_storage::(); @@ -282,8 +290,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani Some(t) => t, None => { // Inform of failure - if let Some(client) = clients.get_mut(entity) { - client.send_msg( + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send( ChatType::Meta .server_msg("Kick failed, target does not exist.".to_owned()), ); @@ -295,8 +303,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Can't kick pet if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target).map_or(true, |u| u != owner)) { - if let Some(client) = clients.get_mut(entity) { - client.send_msg( + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send( ChatType::Meta.server_msg("Kick failed, you can't kick pets.".to_owned()), ); } @@ -304,8 +312,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani } // Can't kick yourself if uids.get(entity).map_or(false, |u| *u == uid) { - if let Some(client) = clients.get_mut(entity) { - client.send_msg( + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send( ChatType::Meta .server_msg("Kick failed, you can't kick yourself.".to_owned()), ); @@ -315,6 +323,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani let mut groups = state.ecs().write_storage::(); let mut group_manager = state.ecs().write_resource::(); + let mut in_game_streams = state.ecs().write_storage::(); // Make sure kicker is the group leader match groups .get(target) @@ -329,58 +338,59 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani &uids, &state.ecs().entities(), &mut |entity, group_change| { - clients + in_game_streams .get_mut(entity) - .and_then(|c| { + .and_then(|s| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, c)) + .map(|g| (g, s)) }) - .map(|(g, c)| c.send_msg(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); }, ); // Tell them the have been kicked - if let Some(client) = clients.get_mut(target) { - client.send_msg( + if let Some(general_stream) = general_streams.get_mut(target) { + let _ = general_stream.0.send( ChatType::Meta .server_msg("You were removed from the group.".to_owned()), ); } // Tell kicker that they were succesful - if let Some(client) = clients.get_mut(entity) { - client.send_msg(ChatType::Meta.server_msg("Player kicked.".to_owned())); + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream + .0 + .send(ChatType::Meta.server_msg("Player kicked.".to_owned())); } }, Some(_) => { // Inform kicker that they are not the leader - if let Some(client) = clients.get_mut(entity) { - client.send_msg(ChatType::Meta.server_msg( + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send(ChatType::Meta.server_msg( "Kick failed: You are not the leader of the target's group.".to_owned(), )); } }, None => { // Inform kicker that the target is not in a group - if let Some(client) = clients.get_mut(entity) { - client.send_msg( - ChatType::Meta.server_msg( + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = + general_stream.0.send(ChatType::Meta.server_msg( "Kick failed: Your target is not in a group.".to_owned(), - ), - ); + )); } }, } }, GroupManip::AssignLeader(uid) => { - let mut clients = state.ecs().write_storage::(); + let mut general_streams = state.ecs().write_storage::(); let uids = state.ecs().read_storage::(); let target = match state.ecs().entity_from_uid(uid.into()) { Some(t) => t, None => { // Inform of failure - if let Some(client) = clients.get_mut(entity) { - client.send_msg(ChatType::Meta.server_msg( + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send(ChatType::Meta.server_msg( "Leadership transfer failed, target does not exist".to_owned(), )); } @@ -389,6 +399,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }; let groups = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); + let mut in_game_streams = state.ecs().write_storage::(); // Make sure assigner is the group leader match groups .get(target) @@ -403,25 +414,25 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani &state.ecs().read_storage(), &uids, |entity, group_change| { - clients + in_game_streams .get_mut(entity) - .and_then(|c| { + .and_then(|s| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, c)) + .map(|g| (g, s)) }) - .map(|(g, c)| c.send_msg(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); }, ); // Tell them they are the leader - if let Some(client) = clients.get_mut(target) { - client.send_msg( + if let Some(general_stream) = general_streams.get_mut(target) { + let _ = general_stream.0.send( ChatType::Meta.server_msg("You are the group leader now.".to_owned()), ); } // Tell the old leader that the transfer was succesful - if let Some(client) = clients.get_mut(target) { - client.send_msg( + if let Some(general_stream) = general_streams.get_mut(target) { + let _ = general_stream.0.send( ChatType::Meta .server_msg("You are no longer the group leader.".to_owned()), ); @@ -429,8 +440,9 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }, Some(_) => { // Inform transferer that they are not the leader - if let Some(client) = clients.get_mut(entity) { - client.send_msg( + let mut general_streams = state.ecs().write_storage::(); + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send( ChatType::Meta.server_msg( "Transfer failed: You are not the leader of the target's group." .to_owned(), @@ -440,8 +452,9 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }, None => { // Inform transferer that the target is not in a group - if let Some(client) = clients.get_mut(entity) { - client.send_msg(ChatType::Meta.server_msg( + let mut general_streams = state.ecs().write_storage::(); + if let Some(general_stream) = general_streams.get_mut(entity) { + let _ = general_stream.0.send(ChatType::Meta.server_msg( "Transfer failed: Your target is not in a group.".to_owned(), )); } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index ff9f2e467a..21a27b01e2 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -1,5 +1,8 @@ use crate::{ - client::{Client, RegionSubscription}, + client::{ + CharacterScreenStream, Client, GeneralStream, InGameStream, PingStream, RegionSubscription, + RegisterStream, + }, Server, }; use common::{ @@ -120,80 +123,153 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { // You can't possess other players let mut clients = ecs.write_storage::(); + let mut general_streams = ecs.write_storage::(); + let mut ping_streams = ecs.write_storage::(); + let mut register_streams = ecs.write_storage::(); + let mut character_screen_streams = ecs.write_storage::(); + let mut in_game_streams = ecs.write_storage::(); if clients.get_mut(possesse).is_none() { - if let Some(mut client) = clients.remove(possessor) { - client.send_msg(ServerGeneral::SetPlayerEntity(possesse_uid)); - clients - .insert(possesse, client) - .err() - .map(|e| error!(?e, "Error inserting client component during possession")); - // Put possess item into loadout - let mut loadouts = ecs.write_storage::(); - let loadout = loadouts - .entry(possesse) - .expect("Could not read loadouts component while possessing") - .or_insert(comp::Loadout::default()); + let client = match clients.remove(possessor) { + Some(c) => c, + None => return, + }; + let mut general_stream = match general_streams.remove(possessor) { + Some(c) => c, + None => return, + }; + let ping_stream = match ping_streams.remove(possessor) { + Some(c) => c, + None => return, + }; + let register_stream = match register_streams.remove(possessor) { + Some(c) => c, + None => return, + }; + let character_screen_stream = match character_screen_streams.remove(possessor) { + Some(c) => c, + None => return, + }; + let in_game_stream = match in_game_streams.remove(possessor) { + Some(c) => c, + None => return, + }; + let _ = general_stream + .0 + .send(ServerGeneral::SetPlayerEntity(possesse_uid)); + clients + .insert(possesse, client) + .err() + .map(|e| error!(?e, "Error inserting client component during possession")); + general_streams + .insert(possesse, general_stream) + .err() + .map(|e| { + error!( + ?e, + "Error inserting general_streams component during possession" + ) + }); + ping_streams.insert(possesse, ping_stream).err().map(|e| { + error!( + ?e, + "Error inserting ping_streams component during possession" + ) + }); + register_streams + .insert(possesse, register_stream) + .err() + .map(|e| { + error!( + ?e, + "Error inserting register_streams component during possession" + ) + }); + character_screen_streams + .insert(possesse, character_screen_stream) + .err() + .map(|e| { + error!( + ?e, + "Error inserting character_screen_streams component during possession" + ) + }); + in_game_streams + .insert(possesse, in_game_stream) + .err() + .map(|e| { + error!( + ?e, + "Error inserting in_game_streams component during possession" + ) + }); + // Put possess item into loadout + let mut loadouts = ecs.write_storage::(); + let loadout = loadouts + .entry(possesse) + .expect("Could not read loadouts component while possessing") + .or_insert(comp::Loadout::default()); - let item = comp::Item::new_from_asset_expect("common.items.debug.possess"); - if let item::ItemKind::Tool(tool) = item.kind() { - let mut abilities = tool.get_abilities(); - let mut ability_drain = abilities.drain(..); - let debug_item = comp::ItemConfig { - item, - ability1: ability_drain.next(), - ability2: ability_drain.next(), - ability3: ability_drain.next(), - block_ability: None, - dodge_ability: None, - }; - std::mem::swap(&mut loadout.active_item, &mut loadout.second_item); - loadout.active_item = Some(debug_item); - } + let item = comp::Item::new_from_asset_expect("common.items.debug.possess"); + if let item::ItemKind::Tool(tool) = item.kind() { + let mut abilities = tool.get_abilities(); + let mut ability_drain = abilities.drain(..); + let debug_item = comp::ItemConfig { + item, + ability1: ability_drain.next(), + ability2: ability_drain.next(), + ability3: ability_drain.next(), + block_ability: None, + dodge_ability: None, + }; + std::mem::swap(&mut loadout.active_item, &mut loadout.second_item); + loadout.active_item = Some(debug_item); + } - // Move player component - { - let mut players = ecs.write_storage::(); - if let Some(player) = players.remove(possessor) { - players.insert(possesse, player).err().map(|e| { - error!(?e, "Error inserting player component during possession") - }); - } + // Move player component + { + let mut players = ecs.write_storage::(); + if let Some(player) = players.remove(possessor) { + players + .insert(possesse, player) + .err() + .map(|e| error!(?e, "Error inserting player component during possession")); } - // Transfer region subscription - { - let mut subscriptions = ecs.write_storage::(); - if let Some(s) = subscriptions.remove(possessor) { - subscriptions.insert(possesse, s).err().map(|e| { - error!( - ?e, - "Error inserting subscription component during possession" - ) - }); - } + } + // Transfer region subscription + { + let mut subscriptions = ecs.write_storage::(); + if let Some(s) = subscriptions.remove(possessor) { + subscriptions.insert(possesse, s).err().map(|e| { + error!( + ?e, + "Error inserting subscription component during possession" + ) + }); } - // Remove will of the entity - ecs.write_storage::().remove(possesse); - // Reset controller of former shell - ecs.write_storage::() - .get_mut(possessor) - .map(|c| c.reset()); - // Transfer admin powers - { - let mut admins = ecs.write_storage::(); - if let Some(admin) = admins.remove(possessor) { - admins.insert(possesse, admin).err().map(|e| { - error!(?e, "Error inserting admin component during possession") - }); - } + } + // Remove will of the entity + ecs.write_storage::().remove(possesse); + // Reset controller of former shell + ecs.write_storage::() + .get_mut(possessor) + .map(|c| c.reset()); + // Transfer admin powers + { + let mut admins = ecs.write_storage::(); + if let Some(admin) = admins.remove(possessor) { + admins + .insert(possesse, admin) + .err() + .map(|e| error!(?e, "Error inserting admin component during possession")); } - // Transfer waypoint - { - let mut waypoints = ecs.write_storage::(); - if let Some(waypoint) = waypoints.remove(possessor) { - waypoints.insert(possesse, waypoint).err().map(|e| { - error!(?e, "Error inserting waypoint component during possession",) - }); - } + } + // Transfer waypoint + { + let mut waypoints = ecs.write_storage::(); + if let Some(waypoint) = waypoints.remove(possessor) { + waypoints.insert(possesse, waypoint).err().map(|e| { + error!(?e, "Error inserting waypoint component during possession",) + }); } } } diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index e82f292385..3bc648aea1 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -1,4 +1,4 @@ -use crate::{client::Client, Server, StateExt}; +use crate::{client::InGameStream, Server, StateExt}; use common::{ comp::{ self, item, @@ -279,7 +279,8 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .insert(tameable_entity, comp::Alignment::Owned(uid)); // Add to group system - let mut clients = state.ecs().write_storage::(); + let mut in_game_streams = + state.ecs().write_storage::(); let uids = state.ecs().read_storage::(); let mut group_manager = state .ecs() @@ -293,15 +294,15 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv &state.ecs().read_storage(), &uids, &mut |entity, group_change| { - clients + in_game_streams .get_mut(entity) - .and_then(|c| { + .and_then(|s| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, c)) + .map(|g| (g, s)) }) - .map(|(g, c)| { - c.send_msg(ServerGeneral::GroupUpdate(g)) + .map(|(g, s)| { + s.0.send(ServerGeneral::GroupUpdate(g)) }); }, ); diff --git a/server/src/events/player.rs b/server/src/events/player.rs index e466a5b116..383de143bf 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -1,6 +1,12 @@ use super::Event; use crate::{ - client::Client, login_provider::LoginProvider, persistence, state_ext::StateExt, Server, + client::{ + CharacterScreenStream, Client, GeneralStream, InGameStream, PingStream, RegisterStream, + }, + login_provider::LoginProvider, + persistence, + state_ext::StateExt, + Server, }; use common::{ comp, @@ -17,26 +23,61 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { span!(_guard, "handle_exit_ingame"); let state = server.state_mut(); - // Create new entity with just `Client`, `Uid`, and `Player` components - // Easier than checking and removing all other known components + // Create new entity with just `Client`, `Uid`, `Player`, and `...Stream` + // components Easier than checking and removing all other known components // Note: If other `ServerEvent`s are referring to this entity they will be // disrupted let maybe_client = state.ecs().write_storage::().remove(entity); let maybe_uid = state.read_component_copied::(entity); let maybe_player = state.ecs().write_storage::().remove(entity); let maybe_admin = state.ecs().write_storage::().remove(entity); + let maybe_general_stream = state.ecs().write_storage::().remove(entity); + let maybe_ping_stream = state.ecs().write_storage::().remove(entity); + let maybe_register_stream = state.ecs().write_storage::().remove(entity); + let maybe_character_screen_stream = state + .ecs() + .write_storage::() + .remove(entity); + let maybe_in_game_stream = state.ecs().write_storage::().remove(entity); let maybe_group = state .ecs() .write_storage::() .get(entity) .cloned(); - if let (Some(mut client), Some(uid), Some(player)) = (maybe_client, maybe_uid, maybe_player) { + if let ( + Some(mut client), + Some(uid), + Some(player), + Some(mut general_stream), + Some(ping_stream), + Some(register_stream), + Some(character_screen_stream), + Some(in_game_stream), + ) = ( + maybe_client, + maybe_uid, + maybe_player, + maybe_general_stream, + maybe_ping_stream, + maybe_register_stream, + maybe_character_screen_stream, + maybe_in_game_stream, + ) { // Tell client its request was successful client.in_game = None; - client.send_msg(ServerGeneral::ExitInGameSuccess); + let _ = general_stream.0.send(ServerGeneral::ExitInGameSuccess); - let entity_builder = state.ecs_mut().create_entity().with(client).with(player); + let entity_builder = state + .ecs_mut() + .create_entity() + .with(client) + .with(player) + .with(general_stream) + .with(ping_stream) + .with(register_stream) + .with(character_screen_stream) + .with(in_game_stream); // Preserve group component if present let entity_builder = match maybe_group { diff --git a/server/src/lib.rs b/server/src/lib.rs index e3acfa2f02..976c9f4d9c 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -34,7 +34,10 @@ pub use crate::{ use crate::{ alias_validator::AliasValidator, chunk_generator::ChunkGenerator, - client::{Client, RegionSubscription}, + client::{ + CharacterScreenStream, Client, GeneralStream, InGameStream, PingStream, RegionSubscription, + RegisterStream, + }, cmd::ChatCommandExt, connection_handler::ConnectionHandler, data_dir::DataDir, @@ -180,6 +183,11 @@ impl Server { // Server-only components state.ecs_mut().register::(); state.ecs_mut().register::(); + state.ecs_mut().register::(); + state.ecs_mut().register::(); + state.ecs_mut().register::(); + state.ecs_mut().register::(); + state.ecs_mut().register::(); //Alias validator let banned_words_paths = &settings.banned_words_files; @@ -804,8 +812,8 @@ impl Server { }); } - while let Ok(data) = self.connection_handler.client_receiver.try_recv() { - let mut client = data; + while let Ok(mut package) = self.connection_handler.client_receiver.try_recv() { + let client = package.client; if self.settings().max_players <= self.state.ecs().read_storage::().join().count() @@ -814,7 +822,7 @@ impl Server { ?client.participant, "to many players, wont allow participant to connect" ); - client.register_stream.send(ServerInit::TooManyPlayers)?; + package.register.0.send(ServerInit::TooManyPlayers)?; continue; } @@ -823,6 +831,11 @@ impl Server { .ecs_mut() .create_entity_synced() .with(client) + .with(package.general) + .with(package.ping) + .with(package.register) + .with(package.character) + .with(package.in_game) .build(); self.state .ecs() @@ -834,10 +847,10 @@ impl Server { debug!("Starting initial sync with client."); self.state .ecs() - .write_storage::() + .write_storage::() .get_mut(entity) .unwrap() - .register_stream + .0 .send(ServerInit::GameSync { // Send client their entity entity_package: TrackedComps::fetch(&self.state.ecs()) @@ -859,8 +872,72 @@ impl Server { where S: Into, { - if let Some(client) = self.state.ecs().write_storage::().get_mut(entity) { - client.send_msg(msg.into()) + const ERR: &str = + "Don't do that. Sending these messages is only done ONCE at connect and not by this fn"; + match msg.into() { + ServerMsg::Info(_) => panic!(ERR), + ServerMsg::Init(_) => panic!(ERR), + ServerMsg::RegisterAnswer(msg) => { + self.state + .ecs() + .write_storage::() + .get_mut(entity) + .map(|s| s.0.send(msg)); + }, + ServerMsg::General(msg) => { + match &msg { + //Character Screen related + ServerGeneral::CharacterDataLoadError(_) + | ServerGeneral::CharacterListUpdate(_) + | ServerGeneral::CharacterActionError(_) + | ServerGeneral::CharacterSuccess => self + .state + .ecs() + .write_storage::() + .get_mut(entity) + .map(|s| s.0.send(msg)), + //Ingame related + ServerGeneral::GroupUpdate(_) + | ServerGeneral::GroupInvite { .. } + | ServerGeneral::InvitePending(_) + | ServerGeneral::InviteComplete { .. } + | ServerGeneral::ExitInGameSuccess + | ServerGeneral::InventoryUpdate(_, _) + | ServerGeneral::TerrainChunkUpdate { .. } + | ServerGeneral::TerrainBlockUpdates(_) + | ServerGeneral::SetViewDistance(_) + | ServerGeneral::Outcomes(_) + | ServerGeneral::Knockback(_) => self + .state + .ecs() + .write_storage::() + .get_mut(entity) + .map(|s| s.0.send(msg)), + // Always possible + ServerGeneral::PlayerListUpdate(_) + | ServerGeneral::ChatMsg(_) + | ServerGeneral::SetPlayerEntity(_) + | ServerGeneral::TimeOfDay(_) + | ServerGeneral::EntitySync(_) + | ServerGeneral::CompSync(_) + | ServerGeneral::CreateEntity(_) + | ServerGeneral::DeleteEntity(_) + | ServerGeneral::Disconnect(_) + | ServerGeneral::Notification(_) => self + .state + .ecs() + .write_storage::() + .get_mut(entity) + .map(|s| s.0.send(msg)), + }; + }, + ServerMsg::Ping(msg) => { + self.state + .ecs() + .write_storage::() + .get_mut(entity) + .map(|s| s.0.send(msg)); + }, } } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 06d7c523e3..57d714838b 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -1,11 +1,14 @@ use crate::{ - client::Client, persistence::PersistedComponents, sys::sentinel::DeletedEntities, SpawnPoint, + client::{CharacterScreenStream, Client, GeneralStream}, + persistence::PersistedComponents, + sys::sentinel::DeletedEntities, + SpawnPoint, }; use common::{ character::CharacterId, comp, effect::Effect, - msg::{CharacterInfo, ClientInGame, PlayerListUpdate, ServerGeneral, ServerMsg}, + msg::{CharacterInfo, ClientInGame, PlayerListUpdate, ServerGeneral}, state::State, sync::{Uid, UidAllocator, WorldSyncExt}, util::Dir, @@ -222,8 +225,16 @@ impl StateExt for State { // Tell the client its request was successful. if let Some(client) = self.ecs().write_storage::().get_mut(entity) { - client.in_game = Some(ClientInGame::Character); - client.send_msg(ServerGeneral::CharacterSuccess) + if let Some(character_screen_stream) = self + .ecs() + .write_storage::() + .get_mut(entity) + { + client.in_game = Some(ClientInGame::Character); + let _ = character_screen_stream + .0 + .send(ServerGeneral::CharacterSuccess); + } } } @@ -293,14 +304,16 @@ impl StateExt for State { } }, comp::ChatType::Tell(u, t) => { - for (client, uid) in ( - &mut ecs.write_storage::(), + for (general_stream, uid) in ( + &mut ecs.write_storage::(), &ecs.read_storage::(), ) .join() { if uid == u || uid == t { - client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone())); + let _ = general_stream + .0 + .send(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -310,9 +323,13 @@ impl StateExt for State { (*ecs.read_resource::()).retrieve_entity_internal(uid.0); let positions = ecs.read_storage::(); if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) { - for (client, pos) in (&mut ecs.write_storage::(), &positions).join() { + for (general_stream, pos) in + (&mut ecs.write_storage::(), &positions).join() + { if is_within(comp::ChatMsg::SAY_DISTANCE, pos, speaker_pos) { - client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone())); + let _ = general_stream + .0 + .send(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -322,9 +339,13 @@ impl StateExt for State { (*ecs.read_resource::()).retrieve_entity_internal(uid.0); let positions = ecs.read_storage::(); if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) { - for (client, pos) in (&mut ecs.write_storage::(), &positions).join() { + for (general_stream, pos) in + (&mut ecs.write_storage::(), &positions).join() + { if is_within(comp::ChatMsg::REGION_DISTANCE, pos, speaker_pos) { - client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone())); + let _ = general_stream + .0 + .send(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -334,35 +355,43 @@ impl StateExt for State { (*ecs.read_resource::()).retrieve_entity_internal(uid.0); let positions = ecs.read_storage::(); if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) { - for (client, pos) in (&mut ecs.write_storage::(), &positions).join() { + for (general_stream, pos) in + (&mut ecs.write_storage::(), &positions).join() + { if is_within(comp::ChatMsg::NPC_DISTANCE, pos, speaker_pos) { - client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone())); + let _ = general_stream + .0 + .send(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } }, comp::ChatType::FactionMeta(s) | comp::ChatType::Faction(_, s) => { - for (client, faction) in ( - &mut ecs.write_storage::(), + for (general_stream, faction) in ( + &mut ecs.write_storage::(), &ecs.read_storage::(), ) .join() { if s == &faction.0 { - client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone())); + let _ = general_stream + .0 + .send(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, comp::ChatType::GroupMeta(g) | comp::ChatType::Group(_, g) => { - for (client, group) in ( - &mut ecs.write_storage::(), + for (general_stream, group) in ( + &mut ecs.write_storage::(), &ecs.read_storage::(), ) .join() { if g == group { - client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone())); + let _ = general_stream + .0 + .send(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -371,23 +400,27 @@ impl StateExt for State { /// Sends the message to all connected clients fn notify_registered_clients(&self, msg: ServerGeneral) { - let msg: ServerMsg = msg.into(); - for client in (&mut self.ecs().write_storage::()) + for (general_stream, _) in ( + &mut self.ecs().write_storage::(), + &self.ecs().read_storage::(), + ) .join() - .filter(|c| c.registered) + .filter(|(_, c)| c.registered) { - client.send_msg(msg.clone()); + let _ = general_stream.0.send(msg.clone()); } } /// Sends the message to all clients playing in game fn notify_in_game_clients(&self, msg: ServerGeneral) { - let msg: ServerMsg = msg.into(); - for client in (&mut self.ecs().write_storage::()) + for (general_stream, _) in ( + &mut self.ecs().write_storage::(), + &self.ecs().read_storage::(), + ) .join() - .filter(|c| c.in_game.is_some()) + .filter(|(_, c)| c.in_game.is_some()) { - client.send_msg(msg.clone()); + let _ = general_stream.0.send(msg.clone()); } } @@ -397,7 +430,7 @@ impl StateExt for State { ) -> Result<(), specs::error::WrongGeneration> { // Remove entity from a group if they are in one { - let mut clients = self.ecs().write_storage::(); + let mut general_streams = self.ecs().write_storage::(); let uids = self.ecs().read_storage::(); let mut group_manager = self.ecs().write_resource::(); group_manager.entity_deleted( @@ -407,14 +440,14 @@ impl StateExt for State { &uids, &self.ecs().entities(), &mut |entity, group_change| { - clients + general_streams .get_mut(entity) - .and_then(|c| { + .and_then(|s| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, c)) + .map(|g| (g, s)) }) - .map(|(g, c)| c.send_msg(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); }, ); } diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index 0442a04e8e..465a77d728 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -3,7 +3,7 @@ use super::{ SysTimer, }; use crate::{ - client::{Client, RegionSubscription}, + client::{Client, GeneralStream, InGameStream, RegionSubscription}, Tick, }; use common::{ @@ -43,6 +43,8 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Last>, WriteStorage<'a, Last>, WriteStorage<'a, Client>, + WriteStorage<'a, InGameStream>, + WriteStorage<'a, GeneralStream>, WriteStorage<'a, ForceUpdate>, WriteStorage<'a, InventoryUpdate>, Write<'a, DeletedEntities>, @@ -70,6 +72,8 @@ impl<'a> System<'a> for Sys { mut last_vel, mut last_ori, mut clients, + mut in_game_streams, + mut general_streams, mut force_updates, mut inventory_updates, mut deleted_entities, @@ -104,15 +108,31 @@ impl<'a> System<'a> for Sys { for (key, region) in region_map.iter() { // Assemble subscriber list for this region by iterating through clients and // checking if they are subscribed to this region - let mut subscribers = (&mut clients, &entities, &subscriptions, &positions) + let mut subscribers = ( + &mut clients, + &entities, + &subscriptions, + &positions, + &mut in_game_streams, + &mut general_streams, + ) .join() - .filter_map(|(client, entity, subscription, pos)| { - if client.in_game.is_some() && subscription.regions.contains(&key) { - Some((client, &subscription.regions, entity, *pos)) - } else { - None - } - }) + .filter_map( + |(client, entity, subscription, pos, in_game_stream, general_stream)| { + if client.in_game.is_some() && subscription.regions.contains(&key) { + Some(( + client, + &subscription.regions, + entity, + *pos, + in_game_stream, + general_stream, + )) + } else { + None + } + }, + ) .collect::>(); for event in region.events() { @@ -135,7 +155,9 @@ impl<'a> System<'a> for Sys { vel.copied(), ori.copied(), )); - for (client, regions, client_entity, _) in &mut subscribers { + for (_, regions, client_entity, _, _, general_stream) in + &mut subscribers + { if maybe_key .as_ref() .map(|key| !regions.contains(key)) @@ -143,7 +165,7 @@ impl<'a> System<'a> for Sys { // Client doesn't need to know about itself && *client_entity != entity { - client.send_msg(create_msg.clone()); + let _ = general_stream.0.send(create_msg.clone()); } } } @@ -151,13 +173,13 @@ impl<'a> System<'a> for Sys { RegionEvent::Left(id, maybe_key) => { // Lookup UID for entity if let Some(&uid) = uids.get(entities.entity(*id)) { - for (client, regions, _, _) in &mut subscribers { + for (_, regions, _, _, _, general_stream) in &mut subscribers { if maybe_key .as_ref() .map(|key| !regions.contains(key)) .unwrap_or(true) { - client.send_msg(ServerGeneral::DeleteEntity(uid)); + let _ = general_stream.0.send(ServerGeneral::DeleteEntity(uid)); } } } @@ -176,17 +198,19 @@ impl<'a> System<'a> for Sys { ); let entity_sync_msg = ServerGeneral::EntitySync(entity_sync_package); let comp_sync_msg = ServerGeneral::CompSync(comp_sync_package); - subscribers.iter_mut().for_each(move |(client, _, _, _)| { - client.send_msg(entity_sync_msg.clone()); - client.send_msg(comp_sync_msg.clone()); - }); + subscribers + .iter_mut() + .for_each(move |(_, _, _, _, _, general_stream)| { + let _ = general_stream.0.send(entity_sync_msg.clone()); + let _ = general_stream.0.send(comp_sync_msg.clone()); + }); - let mut send_msg = |msg: ServerGeneral, - entity: EcsEntity, - pos: Pos, - force_update: Option<&ForceUpdate>, - throttle: bool| { - for (client, _, client_entity, client_pos) in &mut subscribers { + let mut send_general = |msg: ServerGeneral, + entity: EcsEntity, + pos: Pos, + force_update: Option<&ForceUpdate>, + throttle: bool| { + for (_, _, client_entity, client_pos, _, general_stream) in &mut subscribers { if if client_entity == &entity { // Don't send client physics updates about itself unless force update is set force_update.is_some() @@ -212,7 +236,7 @@ impl<'a> System<'a> for Sys { true // Closer than 100 blocks } } { - client.send_msg(msg.clone()); + let _ = general_stream.0.send(msg.clone()); } } }; @@ -286,7 +310,7 @@ impl<'a> System<'a> for Sys { comp_sync_package.comp_removed::(uid); } - send_msg( + send_general( ServerGeneral::CompSync(comp_sync_package), entity, pos, @@ -299,19 +323,20 @@ impl<'a> System<'a> for Sys { // Handle entity deletion in regions that don't exist in RegionMap // (theoretically none) for (region_key, deleted) in deleted_entities.take_remaining_deleted() { - for client in - (&mut clients, &subscriptions) - .join() - .filter_map(|(client, subscription)| { - if client.in_game.is_some() && subscription.regions.contains(®ion_key) { - Some(client) - } else { - None - } - }) + for general_stream in (&mut clients, &subscriptions, &mut general_streams) + .join() + .filter_map(|(client, subscription, general_stream)| { + if client.in_game.is_some() && subscription.regions.contains(®ion_key) { + Some(general_stream) + } else { + None + } + }) { for uid in &deleted { - client.send_msg(ServerGeneral::DeleteEntity(Uid(*uid))); + let _ = general_stream + .0 + .send(ServerGeneral::DeleteEntity(Uid(*uid))); } } } @@ -319,15 +344,19 @@ impl<'a> System<'a> for Sys { // TODO: Sync clients that don't have a position? // Sync inventories - for (client, inventory, update) in (&mut clients, &inventories, &inventory_updates).join() { - client.send_msg(ServerGeneral::InventoryUpdate( + for (inventory, update, in_game_stream) in + (&inventories, &inventory_updates, &mut in_game_streams).join() + { + let _ = in_game_stream.0.send(ServerGeneral::InventoryUpdate( inventory.clone(), update.event(), )); } // Sync outcomes - for (client, player, pos) in (&mut clients, &players, positions.maybe()).join() { + for (player, pos, in_game_stream) in + (&players, positions.maybe(), &mut in_game_streams).join() + { let is_near = |o_pos: Vec3| { pos.zip_with(player.view_distance, |pos, vd| { pos.0.xy().distance_squared(o_pos.xy()) @@ -341,7 +370,7 @@ impl<'a> System<'a> for Sys { .cloned() .collect::>(); if !outcomes.is_empty() { - client.send_msg(ServerGeneral::Outcomes(outcomes)); + let _ = in_game_stream.0.send(ServerGeneral::Outcomes(outcomes)); } } outcomes.clear(); @@ -354,8 +383,8 @@ impl<'a> System<'a> for Sys { // TODO: doesn't really belong in this system (rename system or create another // system?) let tof_msg = ServerGeneral::TimeOfDay(*time_of_day); - for client in (&mut clients).join() { - client.send_msg(tof_msg.clone()); + for general_stream in (&mut general_streams).join() { + let _ = general_stream.0.send(tof_msg.clone()); } timer.end(); diff --git a/server/src/sys/invite_timeout.rs b/server/src/sys/invite_timeout.rs index 42f6603fe8..87fa70b25c 100644 --- a/server/src/sys/invite_timeout.rs +++ b/server/src/sys/invite_timeout.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::client::Client; +use crate::client::InGameStream; use common::{ comp::group::{Invite, PendingInvites}, msg::{InviteAnswer, ServerGeneral}, @@ -16,14 +16,14 @@ impl<'a> System<'a> for Sys { Entities<'a>, WriteStorage<'a, Invite>, WriteStorage<'a, PendingInvites>, - WriteStorage<'a, Client>, + WriteStorage<'a, InGameStream>, ReadStorage<'a, Uid>, Write<'a, SysTimer>, ); fn run( &mut self, - (entities, mut invites, mut pending_invites, mut clients, uids, mut timer): Self::SystemData, + (entities, mut invites, mut pending_invites, mut in_game_streams, uids, mut timer): Self::SystemData, ) { span!(_guard, "run", "invite_timeout::Sys::run"); timer.start(); @@ -51,13 +51,14 @@ impl<'a> System<'a> for Sys { } // Inform inviter of timeout - if let (Some(client), Some(target)) = - (clients.get_mut(*inviter), uids.get(invitee).copied()) - { - client.send_msg(ServerGeneral::InviteComplete { + if let (Some(in_game_stream), Some(target)) = ( + in_game_streams.get_mut(*inviter), + uids.get(invitee).copied(), + ) { + let _ = in_game_stream.0.send(ServerGeneral::InviteComplete { target, answer: InviteAnswer::TimedOut, - }) + }); } Some(invitee) diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 9a9dfc92d3..78183af705 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -2,7 +2,9 @@ use super::SysTimer; use crate::{ alias_validator::AliasValidator, character_creator, - client::Client, + client::{ + CharacterScreenStream, Client, GeneralStream, InGameStream, PingStream, RegisterStream, + }, login_provider::LoginProvider, metrics::{NetworkRequestMetrics, PlayerMetrics}, persistence::character_loader::CharacterLoader, @@ -41,6 +43,7 @@ impl Sys { new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, entity: specs::Entity, client: &mut Client, + general_stream: &mut GeneralStream, player_metrics: &ReadExpect<'_, PlayerMetrics>, uids: &ReadStorage<'_, Uid>, chat_modes: &ReadStorage<'_, ChatMode>, @@ -68,7 +71,9 @@ impl Sys { } }, ClientGeneral::Disconnect => { - client.send_msg(ServerGeneral::Disconnect(DisconnectReason::Requested)); + general_stream + .0 + .send(ServerGeneral::Disconnect(DisconnectReason::Requested))?; }, ClientGeneral::Terminate => { debug!(?entity, "Client send message to termitate session"); @@ -88,6 +93,7 @@ impl Sys { server_emitter: &mut common::event::Emitter<'_, ServerEvent>, entity: specs::Entity, client: &mut Client, + in_game_stream: &mut InGameStream, terrain: &ReadExpect<'_, TerrainGrid>, network_metrics: &ReadExpect<'_, NetworkRequestMetrics>, can_build: &ReadStorage<'_, CanBuild>, @@ -115,7 +121,7 @@ impl Sys { ClientGeneral::ExitInGame => { client.in_game = None; server_emitter.emit(ServerEvent::ExitIngame { entity }); - client.send_msg(ServerGeneral::ExitInGameSuccess); + in_game_stream.0.send(ServerGeneral::ExitInGameSuccess)?; }, ClientGeneral::SetViewDistance(view_distance) => { players.get_mut(entity).map(|player| { @@ -133,9 +139,9 @@ impl Sys { .map(|max| view_distance > max) .unwrap_or(false) { - client.send_msg(ServerGeneral::SetViewDistance( + in_game_stream.0.send(ServerGeneral::SetViewDistance( settings.max_view_distance.unwrap_or(0), - )); + ))?; } }, ClientGeneral::ControllerInputs(inputs) => { @@ -203,10 +209,10 @@ impl Sys { match terrain.get_key(key) { Some(chunk) => { network_metrics.chunks_served_from_memory.inc(); - client.send_msg(ServerGeneral::TerrainChunkUpdate { + in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { key, chunk: Ok(Box::new(chunk.clone())), - }) + })? }, None => { network_metrics.chunks_generation_triggered.inc(); @@ -243,6 +249,7 @@ impl Sys { new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, entity: specs::Entity, client: &mut Client, + character_screen_stream: &mut CharacterScreenStream, character_loader: &ReadExpect<'_, CharacterLoader>, uids: &ReadStorage<'_, Uid>, players: &mut WriteStorage<'_, Player>, @@ -278,10 +285,11 @@ impl Sys { // Give the player a welcome message if !editable_settings.server_description.is_empty() { - client.send_msg( - ChatType::CommandInfo - .server_msg(String::from(&*editable_settings.server_description)), - ); + character_screen_stream + .0 + .send(ChatType::CommandInfo.server_msg(String::from( + &*editable_settings.server_description, + )))?; } if !client.login_msg_sent { @@ -295,9 +303,11 @@ impl Sys { } } } else { - client.send_msg(ServerGeneral::CharacterDataLoadError(String::from( - "Failed to fetch player entity", - ))) + character_screen_stream + .0 + .send(ServerGeneral::CharacterDataLoadError(String::from( + "Failed to fetch player entity", + )))? } } ClientGeneral::Character(_) => { @@ -313,7 +323,9 @@ impl Sys { ClientGeneral::CreateCharacter { alias, tool, body } => { if let Err(error) = alias_validator.validate(&alias) { debug!(?error, ?alias, "denied alias as it contained a banned word"); - client.send_msg(ServerGeneral::CharacterActionError(error.to_string())); + character_screen_stream + .0 + .send(ServerGeneral::CharacterActionError(error.to_string()))?; } else if let Some(player) = players.get(entity) { character_creator::create_character( entity, @@ -340,9 +352,13 @@ impl Sys { } #[allow(clippy::too_many_arguments)] - fn handle_ping_msg(client: &mut Client, msg: PingMsg) -> Result<(), crate::error::Error> { + fn handle_ping_msg( + client: &mut Client, + ping_stream: &mut PingStream, + msg: PingMsg, + ) -> Result<(), crate::error::Error> { match msg { - PingMsg::Ping => client.send_msg(PingMsg::Pong), + PingMsg::Ping => ping_stream.0.send(PingMsg::Pong)?, PingMsg::Pong => {}, } Ok(()) @@ -354,6 +370,7 @@ impl Sys { new_players: &mut Vec, entity: specs::Entity, client: &mut Client, + register_stream: &mut RegisterStream, player_metrics: &ReadExpect<'_, PlayerMetrics>, login_provider: &mut WriteExpect<'_, LoginProvider>, admins: &mut WriteStorage<'_, Admin>, @@ -368,9 +385,7 @@ impl Sys { &*editable_settings.banlist, ) { Err(err) => { - client - .register_stream - .send(ServerRegisterAnswer::Err(err))?; + register_stream.0.send(ServerRegisterAnswer::Err(err))?; return Ok(()); }, Ok((username, uuid)) => (username, uuid), @@ -382,8 +397,8 @@ impl Sys { if !player.is_valid() { // Invalid player - client - .register_stream + register_stream + .0 .send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?; return Ok(()); } @@ -401,12 +416,14 @@ impl Sys { // Tell the client its request was successful. client.registered = true; - client.register_stream.send(ServerRegisterAnswer::Ok(()))?; + register_stream.0.send(ServerRegisterAnswer::Ok(()))?; // Send initial player list - client.send_msg(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init( - player_list.clone(), - ))); + register_stream + .0 + .send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init( + player_list.clone(), + )))?; // Add to list to notify all clients of the new player new_players.push(entity); @@ -446,19 +463,8 @@ impl Sys { editable_settings: &ReadExpect<'_, EditableSettings>, alias_validator: &ReadExpect<'_, AliasValidator>, ) -> Result<(), crate::error::Error> { - let (mut b1, mut b2, mut b3, mut b4, mut b5) = ( - client.network_error, - client.network_error, - client.network_error, - client.network_error, - client.network_error, - ); loop { /* - waiting for 1 of the 5 streams to return a massage asynchronous. - If so, handle that msg type. This code will be refactored soon - */ - let q1 = Client::internal_recv(&mut b1, &mut client.general_stream); let q2 = Client::internal_recv(&mut b2, &mut client.in_game_stream); let q3 = Client::internal_recv(&mut b3, &mut client.character_screen_stream); @@ -540,7 +546,7 @@ impl Sys { editable_settings, msg?, )?; - } + }*/ } } } @@ -571,6 +577,11 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Ori>, WriteStorage<'a, Player>, WriteStorage<'a, Client>, + WriteStorage<'a, GeneralStream>, + //WriteStorage<'a, PingStream>, + //WriteStorage<'a, RegisterStream>, + //WriteStorage<'a, CharacterScreenStream>, + //WriteStorage<'a, InGameStream>, WriteStorage<'a, Controller>, Read<'a, Settings>, ReadExpect<'a, EditableSettings>, @@ -604,6 +615,11 @@ impl<'a> System<'a> for Sys { mut orientations, mut players, mut clients, + mut general_streams, + //mut ping_streams, + //mut register_streams, + //mut character_screen_streams, + //mut in_game_streams, mut controllers, settings, editable_settings, @@ -697,7 +713,8 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::ClientDisconnect(entity)); } else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 * 0.5 { // Try pinging the client if the timeout is nearing. - client.send_msg(PingMsg::Ping); + //FIXME + //client.send_msg(PingMsg::Ping); } } @@ -713,7 +730,8 @@ impl<'a> System<'a> for Sys { character: None, // new players will be on character select. })); for client in (&mut clients).join().filter(|c| c.registered) { - client.send_msg(msg.clone()) + //FIXME + //client.send_msg(msg.clone()) } } } diff --git a/server/src/sys/subscription.rs b/server/src/sys/subscription.rs index 2eac60f2a6..5fd1611ae0 100644 --- a/server/src/sys/subscription.rs +++ b/server/src/sys/subscription.rs @@ -2,7 +2,7 @@ use super::{ sentinel::{DeletedEntities, TrackedComps}, SysTimer, }; -use crate::client::{self, Client, RegionSubscription}; +use crate::client::{self, Client, InGameStream, RegionSubscription}; use common::{ comp::{Ori, Player, Pos, Vel}, msg::ServerGeneral, @@ -32,7 +32,8 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Vel>, ReadStorage<'a, Ori>, ReadStorage<'a, Player>, - WriteStorage<'a, Client>, + ReadStorage<'a, Client>, + WriteStorage<'a, InGameStream>, WriteStorage<'a, RegionSubscription>, Write<'a, DeletedEntities>, TrackedComps<'a>, @@ -50,7 +51,8 @@ impl<'a> System<'a> for Sys { velocities, orientations, players, - mut clients, + clients, + mut in_game_streams, mut subscriptions, mut deleted_entities, tracked_comps, @@ -71,17 +73,18 @@ impl<'a> System<'a> for Sys { // 7. Determine list of regions that are in range and iterate through it // - check if in hashset (hash calc) if not add it let mut regions_to_remove = Vec::new(); - for (client, subscription, pos, vd, client_entity) in ( - &mut clients, + for (_, subscription, pos, vd, client_entity, in_game_stream) in ( + &clients, &mut subscriptions, &positions, &players, &entities, + &mut in_game_streams, ) .join() - .filter_map(|(client, s, pos, player, e)| { + .filter_map(|(client, s, pos, player, e, stream)| { if client.in_game.is_some() { - player.view_distance.map(|v| (client, s, pos, v, e)) + player.view_distance.map(|v| (client, s, pos, v, e, stream)) } else { None } @@ -153,7 +156,9 @@ impl<'a> System<'a> for Sys { .map(|key| subscription.regions.contains(key)) .unwrap_or(false) { - client.send_msg(ServerGeneral::DeleteEntity(uid)); + let _ = in_game_stream + .0 + .send(ServerGeneral::DeleteEntity(uid)); } } }, @@ -161,7 +166,7 @@ impl<'a> System<'a> for Sys { } // Tell client to delete entities in the region for (&uid, _) in (&uids, region.entities()).join() { - client.send_msg(ServerGeneral::DeleteEntity(uid)); + let _ = in_game_stream.0.send(ServerGeneral::DeleteEntity(uid)); } } // Send deleted entities since they won't be processed for this client in entity @@ -171,7 +176,9 @@ impl<'a> System<'a> for Sys { .iter() .flat_map(|v| v.iter()) { - client.send_msg(ServerGeneral::DeleteEntity(Uid(*uid))); + let _ = in_game_stream + .0 + .send(ServerGeneral::DeleteEntity(Uid(*uid))); } } @@ -196,7 +203,7 @@ impl<'a> System<'a> for Sys { { // Send message to create entity and tracked components and physics // components - client.send_msg(ServerGeneral::CreateEntity( + let _ = in_game_stream.0.send(ServerGeneral::CreateEntity( tracked_comps.create_entity_package( entity, Some(*pos), @@ -217,14 +224,14 @@ impl<'a> System<'a> for Sys { /// Initialize region subscription pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { - if let (Some(client_pos), Some(client_vd), Some(client)) = ( + if let (Some(client_pos), Some(client_vd), Some(in_game_stream)) = ( world.read_storage::().get(entity), world .read_storage::() .get(entity) .map(|pl| pl.view_distance) .and_then(|v| v), - world.write_storage::().get_mut(entity), + world.write_storage::().get_mut(entity), ) { let fuzzy_chunk = (Vec2::::from(client_pos.0)) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); @@ -249,7 +256,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { .join() { // Send message to create entity and tracked components and physics components - client.send_msg(ServerGeneral::CreateEntity( + let _ = in_game_stream.0.send(ServerGeneral::CreateEntity( tracked_comps.create_entity_package( entity, Some(*pos), diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index eafa24b0ff..8a359e2423 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::{chunk_generator::ChunkGenerator, client::Client, Tick}; +use crate::{chunk_generator::ChunkGenerator, client::InGameStream, Tick}; use common::{ comp::{self, bird_medium, Alignment, Player, Pos}, event::{EventBus, ServerEvent}, @@ -34,7 +34,7 @@ impl<'a> System<'a> for Sys { Write<'a, TerrainChanges>, ReadStorage<'a, Pos>, ReadStorage<'a, Player>, - WriteStorage<'a, Client>, + WriteStorage<'a, InGameStream>, ); fn run( @@ -48,7 +48,7 @@ impl<'a> System<'a> for Sys { mut terrain_changes, positions, players, - mut clients, + mut in_game_streams, ): Self::SystemData, ) { span!(_guard, "run", "terrain::Sys::run"); @@ -62,8 +62,8 @@ impl<'a> System<'a> for Sys { let (chunk, supplement) = match res { Ok((chunk, supplement)) => (chunk, supplement), Err(Some(entity)) => { - if let Some(client) = clients.get_mut(entity) { - client.send_msg(ServerGeneral::TerrainChunkUpdate { + if let Some(in_game_stream) = in_game_streams.get_mut(entity) { + let _ = in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { key, chunk: Err(()), }); @@ -75,10 +75,10 @@ impl<'a> System<'a> for Sys { }, }; // Send the chunk to all nearby players. - for (view_distance, pos, client) in (&players, &positions, &mut clients) + for (view_distance, pos, in_game_stream) in (&players, &positions, &mut in_game_streams) .join() - .filter_map(|(player, pos, client)| { - player.view_distance.map(|vd| (vd, pos, client)) + .filter_map(|(player, pos, in_game_stream)| { + player.view_distance.map(|vd| (vd, pos, in_game_stream)) }) { let chunk_pos = terrain.pos_key(pos.0.map(|e| e as i32)); @@ -90,7 +90,7 @@ impl<'a> System<'a> for Sys { .magnitude_squared(); if adjusted_dist_sqr <= view_distance.pow(2) { - client.send_msg(ServerGeneral::TerrainChunkUpdate { + let _ = in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { key, chunk: Ok(Box::new(chunk.clone())), }); diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index a2e21f42a9..e547f4a8e3 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::client::Client; +use crate::client::InGameStream; use common::{ comp::{Player, Pos}, msg::ServerGeneral, @@ -20,25 +20,26 @@ impl<'a> System<'a> for Sys { Write<'a, SysTimer>, ReadStorage<'a, Pos>, ReadStorage<'a, Player>, - WriteStorage<'a, Client>, + WriteStorage<'a, InGameStream>, ); fn run( &mut self, - (terrain, terrain_changes, mut timer, positions, players, mut clients): Self::SystemData, + (terrain, terrain_changes, mut timer, positions, players, mut in_game_streams): Self::SystemData, ) { span!(_guard, "run", "terrain_sync::Sys::run"); timer.start(); // Sync changed chunks 'chunk: for chunk_key in &terrain_changes.modified_chunks { - for (player, pos, client) in (&players, &positions, &mut clients).join() { + for (player, pos, in_game_stream) in (&players, &positions, &mut in_game_streams).join() + { if player .view_distance .map(|vd| super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, vd)) .unwrap_or(false) { - client.send_msg(ServerGeneral::TerrainChunkUpdate { + let _ = in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { key: *chunk_key, chunk: Ok(Box::new(match terrain.get_key(*chunk_key) { Some(chunk) => chunk.clone(), @@ -52,9 +53,9 @@ impl<'a> System<'a> for Sys { // TODO: Don't send all changed blocks to all clients // Sync changed blocks let msg = ServerGeneral::TerrainBlockUpdates(terrain_changes.modified_blocks.clone()); - for (player, client) in (&players, &mut clients).join() { + for (player, in_game_stream) in (&players, &mut in_game_streams).join() { if player.view_distance.is_some() { - client.send_msg(msg.clone()); + let _ = in_game_stream.0.send(msg.clone()); } } diff --git a/server/src/sys/waypoint.rs b/server/src/sys/waypoint.rs index bf8034baa0..3af23149b4 100644 --- a/server/src/sys/waypoint.rs +++ b/server/src/sys/waypoint.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::client::Client; +use crate::client::GeneralStream; use common::{ comp::{Player, Pos, Waypoint, WaypointArea}, msg::{Notification, ServerGeneral}, @@ -22,28 +22,38 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Player>, ReadStorage<'a, WaypointArea>, WriteStorage<'a, Waypoint>, - WriteStorage<'a, Client>, + WriteStorage<'a, GeneralStream>, Read<'a, Time>, Write<'a, SysTimer>, ); fn run( &mut self, - (entities, positions, players, waypoint_areas, mut waypoints, mut clients, time, mut timer): Self::SystemData, + ( + entities, + positions, + players, + waypoint_areas, + mut waypoints, + mut general_streams, + time, + mut timer, + ): Self::SystemData, ) { span!(_guard, "run", "waypoint::Sys::run"); timer.start(); - for (entity, player_pos, _, client) in - (&entities, &positions, &players, &mut clients).join() + for (entity, player_pos, _, general_stream) in + (&entities, &positions, &players, &mut general_streams).join() { for (waypoint_pos, waypoint_area) in (&positions, &waypoint_areas).join() { if player_pos.0.distance_squared(waypoint_pos.0) < waypoint_area.radius().powi(2) { if let Ok(wp_old) = waypoints.insert(entity, Waypoint::new(player_pos.0, *time)) { if wp_old.map_or(true, |w| w.elapsed(*time) > NOTIFY_TIME) { - client - .send_msg(ServerGeneral::Notification(Notification::WaypointSaved)); + let _ = general_stream + .0 + .send(ServerGeneral::Notification(Notification::WaypointSaved)); } } } From 9459ccf61b490b5cd3db6f57cf008209e42d27e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Sat, 17 Oct 2020 01:42:19 +0200 Subject: [PATCH 02/10] working on implementing seperated msg system. Does compile, but only reach connection till character screen. Full play not yet possible --- server/src/sys/message.rs | 151 +++++++++-------- server/src/sys/mod.rs | 1 + server/src/sys/msg/in_game.rs | 304 ++++++++++++++++++++++++++++++++++ server/src/sys/msg/mod.rs | 1 + 4 files changed, 386 insertions(+), 71 deletions(-) create mode 100644 server/src/sys/msg/in_game.rs create mode 100644 server/src/sys/msg/mod.rs diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 78183af705..39ef2537c2 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -88,6 +88,7 @@ impl Sys { Ok(()) } + /* #[allow(clippy::too_many_arguments)] fn handle_client_in_game_msg( server_emitter: &mut common::event::Emitter<'_, ServerEvent>, @@ -242,6 +243,7 @@ impl Sys { } Ok(()) } + */ #[allow(clippy::too_many_arguments)] fn handle_client_character_screen_msg( @@ -454,44 +456,81 @@ impl Sys { login_provider: &mut WriteExpect<'_, LoginProvider>, block_changes: &mut Write<'_, BlockChange>, admins: &mut WriteStorage<'_, Admin>, - positions: &mut WriteStorage<'_, Pos>, - velocities: &mut WriteStorage<'_, Vel>, - orientations: &mut WriteStorage<'_, Ori>, + //positions: &mut WriteStorage<'_, Pos>, + //velocities: &mut WriteStorage<'_, Vel>, + //orientations: &mut WriteStorage<'_, Ori>, players: &mut WriteStorage<'_, Player>, + general_stream: &mut GeneralStream, + ping_stream: &mut PingStream, + register_stream: &mut RegisterStream, + character_screen_stream: &mut CharacterScreenStream, + //in_game_stream: &mut InGameStream, controllers: &mut WriteStorage<'_, Controller>, settings: &Read<'_, Settings>, editable_settings: &ReadExpect<'_, EditableSettings>, alias_validator: &ReadExpect<'_, AliasValidator>, ) -> Result<(), crate::error::Error> { loop { - /* - let q1 = Client::internal_recv(&mut b1, &mut client.general_stream); - let q2 = Client::internal_recv(&mut b2, &mut client.in_game_stream); - let q3 = Client::internal_recv(&mut b3, &mut client.character_screen_stream); - let q4 = Client::internal_recv(&mut b4, &mut client.ping_stream); - let q5 = Client::internal_recv(&mut b5, &mut client.register_stream); - - let (m1, m2, m3, m4, m5) = select!( - msg = q1.fuse() => (Some(msg), None, None, None, None), - msg = q2.fuse() => (None, Some(msg), None, None, None), - msg = q3.fuse() => (None, None, Some(msg), None, None), - msg = q4.fuse() => (None, None, None, Some(msg), None), - msg = q5.fuse() => (None, None, None, None,Some(msg)), - ); - *cnt += 1; - if let Some(msg) = m1 { - client.network_error |= b1; + if let Some(msg) = general_stream.0.try_recv()? { Self::handle_client_msg( server_emitter, new_chat_msgs, entity, client, + general_stream, player_metrics, uids, chat_modes, - msg?, + msg, )?; + *cnt += 1; + continue; } + + if let Some(msg) = ping_stream.0.try_recv()? { + Self::handle_ping_msg(client, ping_stream, msg)?; + *cnt += 1; + continue; + } + + if let Some(msg) = register_stream.0.try_recv()? { + Self::handle_register_msg( + player_list, + new_players, + entity, + client, + register_stream, + player_metrics, + login_provider, + admins, + players, + editable_settings, + msg, + )?; + *cnt += 1; + continue; + } + + if let Some(msg) = character_screen_stream.0.try_recv()? { + Self::handle_client_character_screen_msg( + server_emitter, + new_chat_msgs, + entity, + client, + character_screen_stream, + character_loader, + uids, + players, + editable_settings, + alias_validator, + msg, + )?; + *cnt += 1; + continue; + } + + break Ok(()) + /* if let Some(msg) = m2 { client.network_error |= b2; Self::handle_client_in_game_msg( @@ -512,40 +551,6 @@ impl Sys { settings, msg?, )?; - } - if let Some(msg) = m3 { - client.network_error |= b3; - Self::handle_client_character_screen_msg( - server_emitter, - new_chat_msgs, - entity, - client, - character_loader, - uids, - players, - editable_settings, - alias_validator, - msg?, - )?; - } - if let Some(msg) = m4 { - client.network_error |= b4; - Self::handle_ping_msg(client, msg?)?; - } - if let Some(msg) = m5 { - client.network_error |= b5; - Self::handle_register_msg( - player_list, - new_players, - entity, - client, - player_metrics, - login_provider, - admins, - players, - editable_settings, - msg?, - )?; }*/ } } @@ -572,15 +577,15 @@ impl<'a> System<'a> for Sys { WriteExpect<'a, LoginProvider>, Write<'a, BlockChange>, WriteStorage<'a, Admin>, - WriteStorage<'a, Pos>, - WriteStorage<'a, Vel>, - WriteStorage<'a, Ori>, + //WriteStorage<'a, Pos>, + //WriteStorage<'a, Vel>, + //WriteStorage<'a, Ori>, WriteStorage<'a, Player>, WriteStorage<'a, Client>, WriteStorage<'a, GeneralStream>, - //WriteStorage<'a, PingStream>, - //WriteStorage<'a, RegisterStream>, - //WriteStorage<'a, CharacterScreenStream>, + WriteStorage<'a, PingStream>, + WriteStorage<'a, RegisterStream>, + WriteStorage<'a, CharacterScreenStream>, //WriteStorage<'a, InGameStream>, WriteStorage<'a, Controller>, Read<'a, Settings>, @@ -610,15 +615,15 @@ impl<'a> System<'a> for Sys { mut accounts, mut block_changes, mut admins, - mut positions, - mut velocities, - mut orientations, + //mut positions, + //mut velocities, + //mut orientations, mut players, mut clients, mut general_streams, - //mut ping_streams, - //mut register_streams, - //mut character_screen_streams, + mut ping_streams, + mut register_streams, + mut character_screen_streams, //mut in_game_streams, mut controllers, settings, @@ -651,7 +656,7 @@ impl<'a> System<'a> for Sys { // List of new players to update player lists of all clients. let mut new_players = Vec::new(); - for (entity, client) in (&entities, &mut clients).join() { + for (entity, client, general_stream, ping_stream, register_stream, character_screen_stream) in (&entities, &mut clients, &mut general_streams, &mut ping_streams, &mut register_streams, &mut character_screen_streams).join() { let mut cnt = 0; let network_err: Result<(), crate::error::Error> = block_on(async { @@ -676,10 +681,14 @@ impl<'a> System<'a> for Sys { &mut accounts, &mut block_changes, &mut admins, - &mut positions, - &mut velocities, - &mut orientations, + //&mut positions, + //&mut velocities, + //&mut orientations, &mut players, + general_stream, + ping_stream, + register_stream, + character_screen_stream, &mut controllers, &settings, &editable_settings, diff --git a/server/src/sys/mod.rs b/server/src/sys/mod.rs index 6b98fc9edb..6dba8a0f3c 100644 --- a/server/src/sys/mod.rs +++ b/server/src/sys/mod.rs @@ -1,5 +1,6 @@ pub mod entity_sync; pub mod invite_timeout; +pub mod msg; pub mod message; pub mod object; pub mod persistence; diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs new file mode 100644 index 0000000000..0090f7603b --- /dev/null +++ b/server/src/sys/msg/in_game.rs @@ -0,0 +1,304 @@ +use super::super::SysTimer; +use crate::{ + alias_validator::AliasValidator, + client::{ + Client, InGameStream, + }, + login_provider::LoginProvider, + metrics::{NetworkRequestMetrics, PlayerMetrics}, + persistence::character_loader::CharacterLoader, + EditableSettings, Settings, +}; +use common::{ + comp::{ + Admin, CanBuild, ChatMode, ControlEvent, Controller, ForceUpdate, Ori, Player, + Pos, Stats, Vel, + }, + event::{EventBus, ServerEvent}, + msg::{ + ClientGeneral, ClientInGame, + ServerGeneral, + }, + span, + state::{BlockChange, Time}, + sync::Uid, + terrain::{TerrainChunkSize, TerrainGrid}, + vol::{ReadVol, RectVolSize}, +}; +use specs::{ + Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, +}; +use tracing::{debug, trace}; + +impl Sys { + #[allow(clippy::too_many_arguments)] + fn handle_client_in_game_msg( + server_emitter: &mut common::event::Emitter<'_, ServerEvent>, + entity: specs::Entity, + client: &mut Client, + in_game_stream: &mut InGameStream, + terrain: &ReadExpect<'_, TerrainGrid>, + network_metrics: &ReadExpect<'_, NetworkRequestMetrics>, + can_build: &ReadStorage<'_, CanBuild>, + force_updates: &ReadStorage<'_, ForceUpdate>, + stats: &mut WriteStorage<'_, Stats>, + block_changes: &mut Write<'_, BlockChange>, + positions: &mut WriteStorage<'_, Pos>, + velocities: &mut WriteStorage<'_, Vel>, + orientations: &mut WriteStorage<'_, Ori>, + players: &mut WriteStorage<'_, Player>, + controllers: &mut WriteStorage<'_, Controller>, + settings: &Read<'_, Settings>, + msg: ClientGeneral, + ) -> Result<(), crate::error::Error> { + if client.in_game.is_none() { + debug!(?entity, "client is not in_game, ignoring msg"); + trace!(?msg, "ignored msg content"); + if matches!(msg, ClientGeneral::TerrainChunkRequest{ .. }) { + network_metrics.chunks_request_dropped.inc(); + } + return Ok(()); + } + match msg { + // Go back to registered state (char selection screen) + ClientGeneral::ExitInGame => { + client.in_game = None; + server_emitter.emit(ServerEvent::ExitIngame { entity }); + in_game_stream.0.send(ServerGeneral::ExitInGameSuccess)?; + }, + ClientGeneral::SetViewDistance(view_distance) => { + players.get_mut(entity).map(|player| { + player.view_distance = Some( + settings + .max_view_distance + .map(|max| view_distance.min(max)) + .unwrap_or(view_distance), + ) + }); + + //correct client if its VD is to high + if settings + .max_view_distance + .map(|max| view_distance > max) + .unwrap_or(false) + { + in_game_stream.0.send(ServerGeneral::SetViewDistance( + settings.max_view_distance.unwrap_or(0), + ))?; + } + }, + ClientGeneral::ControllerInputs(inputs) => { + if let Some(ClientInGame::Character) = client.in_game { + if let Some(controller) = controllers.get_mut(entity) { + controller.inputs.update_with_new(inputs); + } + } + }, + ClientGeneral::ControlEvent(event) => { + if let Some(ClientInGame::Character) = client.in_game { + // Skip respawn if client entity is alive + if let ControlEvent::Respawn = event { + if stats.get(entity).map_or(true, |s| !s.is_dead) { + //Todo: comment why return! + return Ok(()); + } + } + if let Some(controller) = controllers.get_mut(entity) { + controller.events.push(event); + } + } + }, + ClientGeneral::ControlAction(event) => { + if let Some(ClientInGame::Character) = client.in_game { + if let Some(controller) = controllers.get_mut(entity) { + controller.actions.push(event); + } + } + }, + ClientGeneral::PlayerPhysics { pos, vel, ori } => { + if let Some(ClientInGame::Character) = client.in_game { + if force_updates.get(entity).is_none() + && stats.get(entity).map_or(true, |s| !s.is_dead) + { + let _ = positions.insert(entity, pos); + let _ = velocities.insert(entity, vel); + let _ = orientations.insert(entity, ori); + } + } + }, + ClientGeneral::BreakBlock(pos) => { + if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) { + block_changes.set(pos, block.into_vacant()); + } + }, + ClientGeneral::PlaceBlock(pos, block) => { + if can_build.get(entity).is_some() { + block_changes.try_set(pos, block); + } + }, + ClientGeneral::TerrainChunkRequest { key } => { + let in_vd = if let (Some(view_distance), Some(pos)) = ( + players.get(entity).and_then(|p| p.view_distance), + positions.get(entity), + ) { + pos.0.xy().map(|e| e as f64).distance( + key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64), + ) < (view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt()) + * TerrainChunkSize::RECT_SIZE.x as f64 + } else { + true + }; + if in_vd { + match terrain.get_key(key) { + Some(chunk) => { + network_metrics.chunks_served_from_memory.inc(); + in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { + key, + chunk: Ok(Box::new(chunk.clone())), + })? + }, + None => { + network_metrics.chunks_generation_triggered.inc(); + server_emitter.emit(ServerEvent::ChunkRequest(entity, key)) + }, + } + } else { + network_metrics.chunks_request_dropped.inc(); + } + }, + ClientGeneral::UnlockSkill(skill) => { + stats + .get_mut(entity) + .map(|s| s.skill_set.unlock_skill(skill)); + }, + ClientGeneral::RefundSkill(skill) => { + stats + .get_mut(entity) + .map(|s| s.skill_set.refund_skill(skill)); + }, + ClientGeneral::UnlockSkillGroup(skill_group_type) => { + stats + .get_mut(entity) + .map(|s| s.skill_set.unlock_skill_group(skill_group_type)); + }, + _ => unreachable!("not a client_in_game msg"), + } + Ok(()) + } +} + +/// This system will handle new messages from clients +pub struct Sys; +impl<'a> System<'a> for Sys { + #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + type SystemData = ( + Entities<'a>, + Read<'a, EventBus>, + Read<'a, Time>, + ReadExpect<'a, CharacterLoader>, + ReadExpect<'a, TerrainGrid>, + ReadExpect<'a, NetworkRequestMetrics>, + ReadExpect<'a, PlayerMetrics>, + Write<'a, SysTimer>, + ReadStorage<'a, Uid>, + ReadStorage<'a, CanBuild>, + ReadStorage<'a, ForceUpdate>, + WriteStorage<'a, Stats>, + ReadStorage<'a, ChatMode>, + WriteExpect<'a, LoginProvider>, + Write<'a, BlockChange>, + WriteStorage<'a, Admin>, + WriteStorage<'a, Pos>, + WriteStorage<'a, Vel>, + WriteStorage<'a, Ori>, + WriteStorage<'a, Player>, + WriteStorage<'a, Client>, + WriteStorage<'a, InGameStream>, + WriteStorage<'a, Controller>, + Read<'a, Settings>, + ReadExpect<'a, EditableSettings>, + ReadExpect<'a, AliasValidator>, + ); + + #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 + #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 + #[allow(clippy::single_match)] // TODO: Pending review in #587 + fn run( + &mut self, + ( + entities, + server_event_bus, + time, + character_loader, + terrain, + network_metrics, + player_metrics, + mut timer, + uids, + can_build, + force_updates, + mut stats, + chat_modes, + mut accounts, + mut block_changes, + mut admins, + mut positions, + mut velocities, + mut orientations, + mut players, + mut clients, + mut in_game_streams, + mut controllers, + settings, + editable_settings, + alias_validator, + ): Self::SystemData, + ) { + span!(_guard, "run", "msg::in_game::Sys::run"); + timer.start(); + + let mut server_emitter = server_event_bus.emitter(); + + for (entity, client, mut in_game_stream) in (&entities, &mut clients, &mut in_game_streams).join() { + let mut cnt = 0; + + let network_err: Result<(), crate::error::Error> = { + loop { + let msg = match in_game_stream.0.try_recv() { + Ok(Some(msg)) => msg, + Ok(None) => break Ok(()), + Err(e) => break Err(e.into()), + }; + if let Err(e) = Self::handle_client_in_game_msg( + &mut server_emitter, + entity, + client, + &mut in_game_stream, + &terrain, + &network_metrics, + &can_build, + &force_updates, + &mut stats, + &mut block_changes, + &mut positions, + &mut velocities, + &mut orientations, + &mut players, + &mut controllers, + &settings, + msg, + ) { + break Err(e); + } + cnt += 1; + } + }; + + if cnt > 0 { // Update client ping. + client.last_ping = time.0 + } + } + + timer.end() + } +} diff --git a/server/src/sys/msg/mod.rs b/server/src/sys/msg/mod.rs new file mode 100644 index 0000000000..ee8449ae96 --- /dev/null +++ b/server/src/sys/msg/mod.rs @@ -0,0 +1 @@ +mod in_game; \ No newline at end of file From e9be36c9933b410721d0e6d44122efe0c6dbe13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Sat, 17 Oct 2020 11:36:44 +0200 Subject: [PATCH 03/10] replace the single `message` system with 5 message systems. one per stream to handle less ECS systems PER msg system. As the MAIN message system was already on 25 of the max of 26 possible Ressources --- server/src/client.rs | 26 +- server/src/cmd.rs | 7 +- server/src/connection_handler.rs | 4 +- server/src/events/entity_manipulation.rs | 5 +- server/src/events/group_manip.rs | 93 ++- server/src/events/interaction.rs | 104 ++- server/src/events/inventory_manip.rs | 7 +- server/src/events/player.rs | 13 +- server/src/lib.rs | 43 +- server/src/state_ext.rs | 50 +- server/src/streams.rs | 118 ++++ server/src/sys/entity_sync.rs | 23 +- server/src/sys/invite_timeout.rs | 4 +- server/src/sys/message.rs | 764 ----------------------- server/src/sys/mod.rs | 7 +- server/src/sys/msg/character_screen.rs | 224 +++++++ server/src/sys/msg/general.rs | 153 +++++ server/src/sys/msg/in_game.rs | 115 ++-- server/src/sys/msg/mod.rs | 29 +- server/src/sys/msg/ping.rs | 106 ++++ server/src/sys/msg/register.rs | 208 ++++++ server/src/sys/subscription.rs | 32 +- server/src/sys/terrain.rs | 10 +- server/src/sys/terrain_sync.rs | 6 +- server/src/sys/waypoint.rs | 8 +- 25 files changed, 1079 insertions(+), 1080 deletions(-) create mode 100644 server/src/streams.rs delete mode 100644 server/src/sys/message.rs create mode 100644 server/src/sys/msg/character_screen.rs create mode 100644 server/src/sys/msg/general.rs create mode 100644 server/src/sys/msg/ping.rs create mode 100644 server/src/sys/msg/register.rs diff --git a/server/src/client.rs b/server/src/client.rs index eec6385676..9462713c78 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -1,34 +1,10 @@ use common::msg::{ClientInGame, ClientType}; use hashbrown::HashSet; -use network::{Participant, Stream}; +use network::Participant; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; use vek::*; -// Streams -// we ignore errors on send, and do unified error handling in recv -pub struct GeneralStream(pub Stream); -pub struct PingStream(pub Stream); -pub struct RegisterStream(pub Stream); -pub struct CharacterScreenStream(pub Stream); -pub struct InGameStream(pub Stream); - -impl Component for GeneralStream { - type Storage = FlaggedStorage>; -} -impl Component for PingStream { - type Storage = FlaggedStorage>; -} -impl Component for RegisterStream { - type Storage = FlaggedStorage>; -} -impl Component for CharacterScreenStream { - type Storage = FlaggedStorage>; -} -impl Component for InGameStream { - type Storage = FlaggedStorage>; -} - pub struct Client { pub registered: bool, pub client_type: ClientType, diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 462d6c3e05..dfa5c9b064 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -26,7 +26,10 @@ use std::convert::TryFrom; use vek::*; use world::util::Sampler; -use crate::{client::InGameStream, login_provider::LoginProvider}; +use crate::{ + login_provider::LoginProvider, + streams::{GetStream, InGameStream}, +}; use scan_fmt::{scan_fmt, scan_fmt_some}; use tracing::error; @@ -670,7 +673,7 @@ fn handle_spawn( .map(|g| (g, s)) }) .map(|(g, s)| { - let _ = s.0.send(ServerGeneral::GroupUpdate(g)); + s.send_unchecked(ServerGeneral::GroupUpdate(g)); }); }, ); diff --git a/server/src/connection_handler.rs b/server/src/connection_handler.rs index 82c4f191f9..6a790f367a 100644 --- a/server/src/connection_handler.rs +++ b/server/src/connection_handler.rs @@ -1,6 +1,6 @@ use crate::{ - CharacterScreenStream, Client, ClientType, GeneralStream, InGameStream, PingStream, - RegisterStream, ServerInfo, + streams::{CharacterScreenStream, GeneralStream, InGameStream, PingStream, RegisterStream}, + Client, ClientType, ServerInfo, }; use crossbeam::{bounded, unbounded, Receiver, Sender}; use futures_channel::oneshot; diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 1e3dd4af62..39bb1c39e9 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,6 +1,7 @@ use crate::{ - client::{Client, InGameStream}, + client::Client, comp::{biped_large, quadruped_medium, quadruped_small}, + streams::{GetStream, InGameStream}, Server, SpawnPoint, StateExt, }; use common::{ @@ -44,7 +45,7 @@ pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3) } let mut in_game_streams = state.ecs().write_storage::(); if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - let _ = in_game_stream.0.send(ServerGeneral::Knockback(impulse)); + in_game_stream.send_unchecked(ServerGeneral::Knockback(impulse)); } } diff --git a/server/src/events/group_manip.rs b/server/src/events/group_manip.rs index 8d8e00d150..17729c436b 100644 --- a/server/src/events/group_manip.rs +++ b/server/src/events/group_manip.rs @@ -1,5 +1,5 @@ use crate::{ - client::{GeneralStream, InGameStream}, + streams::{GeneralStream, GetStream, InGameStream}, Server, }; use common::{ @@ -29,20 +29,19 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani match manip { GroupManip::Invite(uid) => { let mut general_streams = state.ecs().write_storage::(); - let invitee = - match state.ecs().entity_from_uid(uid.into()) { - Some(t) => t, - None => { - // Inform of failure - if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = - general_stream.0.send(ChatType::Meta.server_msg( - "Invite failed, target does not exist.".to_owned(), - )); - } - return; - }, - }; + let invitee = match state.ecs().entity_from_uid(uid.into()) { + Some(t) => t, + None => { + // Inform of failure + if let Some(general_stream) = general_streams.get_mut(entity) { + general_stream.send_unchecked( + ChatType::Meta + .server_msg("Invite failed, target does not exist.".to_owned()), + ); + } + return; + }, + }; let uids = state.ecs().read_storage::(); @@ -67,7 +66,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if already_in_same_group { // Inform of failure if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send(ChatType::Meta.server_msg( + general_stream.send_unchecked(ChatType::Meta.server_msg( "Invite failed, can't invite someone already in your group".to_owned(), )); } @@ -97,7 +96,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if group_size_limit_reached { // Inform inviter that they have reached the group size limit if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta.server_msg( "Invite failed, pending invites plus current group size have reached \ the group size limit" @@ -114,12 +113,10 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if invites.contains(invitee) { // Inform inviter that there is already an invite if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = - general_stream - .0 - .send(ChatType::Meta.server_msg( - "This player already has a pending invite.".to_owned(), - )); + general_stream.send_unchecked( + ChatType::Meta + .server_msg("This player already has a pending invite.".to_owned()), + ); } return; } @@ -163,7 +160,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani (in_game_streams.get_mut(invitee), uids.get(entity).copied()) { if send_invite() { - let _ = in_game_stream.0.send(ServerGeneral::GroupInvite { + in_game_stream.send_unchecked(ServerGeneral::GroupInvite { inviter, timeout: PRESENTED_INVITE_TIMEOUT_DUR, }); @@ -171,7 +168,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani } else if agents.contains(invitee) { send_invite(); } else if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta.server_msg("Can't invite, not a player or npc".to_owned()), ); } @@ -179,7 +176,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Notify inviter that the invite is pending if invite_sent { if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - let _ = in_game_stream.0.send(ServerGeneral::InvitePending(uid)); + in_game_stream.send_unchecked(ServerGeneral::InvitePending(uid)); } } }, @@ -204,7 +201,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if let (Some(in_game_stream), Some(target)) = (in_game_streams.get_mut(inviter), uids.get(entity).copied()) { - let _ = in_game_stream.0.send(ServerGeneral::InviteComplete { + in_game_stream.send_unchecked(ServerGeneral::InviteComplete { target, answer: InviteAnswer::Accepted, }); @@ -225,7 +222,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani .try_map(|e| uids.get(e).copied()) .map(|g| (g, s)) }) - .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); }, ); } @@ -252,7 +249,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if let (Some(in_game_stream), Some(target)) = (in_game_streams.get_mut(inviter), uids.get(entity).copied()) { - let _ = in_game_stream.0.send(ServerGeneral::InviteComplete { + in_game_stream.send_unchecked(ServerGeneral::InviteComplete { target, answer: InviteAnswer::Declined, }); @@ -277,7 +274,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani .try_map(|e| uids.get(e).copied()) .map(|g| (g, s)) }) - .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); }, ); }, @@ -291,7 +288,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani None => { // Inform of failure if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta .server_msg("Kick failed, target does not exist.".to_owned()), ); @@ -304,7 +301,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target).map_or(true, |u| u != owner)) { if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta.server_msg("Kick failed, you can't kick pets.".to_owned()), ); } @@ -313,7 +310,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Can't kick yourself if uids.get(entity).map_or(false, |u| *u == uid) { if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta .server_msg("Kick failed, you can't kick yourself.".to_owned()), ); @@ -345,28 +342,27 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani .try_map(|e| uids.get(e).copied()) .map(|g| (g, s)) }) - .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); }, ); // Tell them the have been kicked if let Some(general_stream) = general_streams.get_mut(target) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta .server_msg("You were removed from the group.".to_owned()), ); } // Tell kicker that they were succesful if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream - .0 - .send(ChatType::Meta.server_msg("Player kicked.".to_owned())); + general_stream + .send_unchecked(ChatType::Meta.server_msg("Player kicked.".to_owned())); } }, Some(_) => { // Inform kicker that they are not the leader if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send(ChatType::Meta.server_msg( + general_stream.send_unchecked(ChatType::Meta.server_msg( "Kick failed: You are not the leader of the target's group.".to_owned(), )); } @@ -374,10 +370,11 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani None => { // Inform kicker that the target is not in a group if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = - general_stream.0.send(ChatType::Meta.server_msg( + general_stream.send_unchecked( + ChatType::Meta.server_msg( "Kick failed: Your target is not in a group.".to_owned(), - )); + ), + ); } }, } @@ -390,7 +387,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani None => { // Inform of failure if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send(ChatType::Meta.server_msg( + general_stream.send_unchecked(ChatType::Meta.server_msg( "Leadership transfer failed, target does not exist".to_owned(), )); } @@ -421,18 +418,18 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani .try_map(|e| uids.get(e).copied()) .map(|g| (g, s)) }) - .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); }, ); // Tell them they are the leader if let Some(general_stream) = general_streams.get_mut(target) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta.server_msg("You are the group leader now.".to_owned()), ); } // Tell the old leader that the transfer was succesful if let Some(general_stream) = general_streams.get_mut(target) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta .server_msg("You are no longer the group leader.".to_owned()), ); @@ -442,7 +439,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Inform transferer that they are not the leader let mut general_streams = state.ecs().write_storage::(); if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send( + general_stream.send_unchecked( ChatType::Meta.server_msg( "Transfer failed: You are not the leader of the target's group." .to_owned(), @@ -454,7 +451,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Inform transferer that the target is not in a group let mut general_streams = state.ecs().write_storage::(); if let Some(general_stream) = general_streams.get_mut(entity) { - let _ = general_stream.0.send(ChatType::Meta.server_msg( + general_stream.send_unchecked(ChatType::Meta.server_msg( "Transfer failed: Your target is not in a group.".to_owned(), )); } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 21a27b01e2..cd1da4e31b 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -1,7 +1,7 @@ use crate::{ - client::{ - CharacterScreenStream, Client, GeneralStream, InGameStream, PingStream, RegionSubscription, - RegisterStream, + client::{Client, RegionSubscription}, + streams::{ + CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, }, Server, }; @@ -153,55 +153,34 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { Some(c) => c, None => return, }; - let _ = general_stream - .0 - .send(ServerGeneral::SetPlayerEntity(possesse_uid)); - clients - .insert(possesse, client) - .err() - .map(|e| error!(?e, "Error inserting client component during possession")); - general_streams - .insert(possesse, general_stream) - .err() - .map(|e| { - error!( - ?e, - "Error inserting general_streams component during possession" - ) - }); - ping_streams.insert(possesse, ping_stream).err().map(|e| { - error!( - ?e, - "Error inserting ping_streams component during possession" - ) - }); - register_streams - .insert(possesse, register_stream) - .err() - .map(|e| { - error!( - ?e, - "Error inserting register_streams component during possession" - ) - }); - character_screen_streams - .insert(possesse, character_screen_stream) - .err() - .map(|e| { - error!( - ?e, - "Error inserting character_screen_streams component during possession" - ) - }); - in_game_streams - .insert(possesse, in_game_stream) - .err() - .map(|e| { - error!( - ?e, - "Error inserting in_game_streams component during possession" - ) - }); + general_stream.send_unchecked(ServerGeneral::SetPlayerEntity(possesse_uid)); + let err_fn = |c, e: Option| { + e.map(|e| error!(?e, "Error inserting {} component during possession", c)); + }; + + err_fn("client", clients.insert(possesse, client).err()); + err_fn( + "general_streams", + general_streams.insert(possesse, general_stream).err(), + ); + err_fn( + "ping_streams", + ping_streams.insert(possesse, ping_stream).err(), + ); + err_fn( + "register_streams", + register_streams.insert(possesse, register_stream).err(), + ); + err_fn( + "character_screen_streams", + character_screen_streams + .insert(possesse, character_screen_stream) + .err(), + ); + err_fn( + "in_game_streams", + in_game_streams.insert(possesse, in_game_stream).err(), + ); // Put possess item into loadout let mut loadouts = ecs.write_storage::(); let loadout = loadouts @@ -229,22 +208,14 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { { let mut players = ecs.write_storage::(); if let Some(player) = players.remove(possessor) { - players - .insert(possesse, player) - .err() - .map(|e| error!(?e, "Error inserting player component during possession")); + err_fn("player", players.insert(possesse, player).err()); } } // Transfer region subscription { let mut subscriptions = ecs.write_storage::(); if let Some(s) = subscriptions.remove(possessor) { - subscriptions.insert(possesse, s).err().map(|e| { - error!( - ?e, - "Error inserting subscription component during possession" - ) - }); + err_fn("subscription", subscriptions.insert(possesse, s).err()); } } // Remove will of the entity @@ -257,19 +228,14 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { { let mut admins = ecs.write_storage::(); if let Some(admin) = admins.remove(possessor) { - admins - .insert(possesse, admin) - .err() - .map(|e| error!(?e, "Error inserting admin component during possession")); + err_fn("admin", admins.insert(possesse, admin).err()); } } // Transfer waypoint { let mut waypoints = ecs.write_storage::(); if let Some(waypoint) = waypoints.remove(possessor) { - waypoints.insert(possesse, waypoint).err().map(|e| { - error!(?e, "Error inserting waypoint component during possession",) - }); + err_fn("waypoints", waypoints.insert(possesse, waypoint).err()); } } } diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 3bc648aea1..76dd82f638 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -1,4 +1,7 @@ -use crate::{client::InGameStream, Server, StateExt}; +use crate::{ + streams::{GetStream, InGameStream}, + Server, StateExt, +}; use common::{ comp::{ self, item, @@ -302,7 +305,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .map(|g| (g, s)) }) .map(|(g, s)| { - s.0.send(ServerGeneral::GroupUpdate(g)) + s.send(ServerGeneral::GroupUpdate(g)) }); }, ); diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 383de143bf..0d35b28d07 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -1,11 +1,12 @@ use super::Event; use crate::{ - client::{ - CharacterScreenStream, Client, GeneralStream, InGameStream, PingStream, RegisterStream, - }, + client::Client, login_provider::LoginProvider, persistence, state_ext::StateExt, + streams::{ + CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, + }, Server, }; use common::{ @@ -49,11 +50,11 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { Some(mut client), Some(uid), Some(player), - Some(mut general_stream), + Some(general_stream), Some(ping_stream), Some(register_stream), Some(character_screen_stream), - Some(in_game_stream), + Some(mut in_game_stream), ) = ( maybe_client, maybe_uid, @@ -66,7 +67,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { ) { // Tell client its request was successful client.in_game = None; - let _ = general_stream.0.send(ServerGeneral::ExitInGameSuccess); + in_game_stream.send_unchecked(ServerGeneral::ExitInGameSuccess); let entity_builder = state .ecs_mut() diff --git a/server/src/lib.rs b/server/src/lib.rs index 976c9f4d9c..9f5eced842 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -19,6 +19,7 @@ pub mod metrics; pub mod persistence; pub mod settings; pub mod state_ext; +pub mod streams; pub mod sys; #[cfg(not(feature = "worldgen"))] mod test_world; @@ -34,15 +35,15 @@ pub use crate::{ use crate::{ alias_validator::AliasValidator, chunk_generator::ChunkGenerator, - client::{ - CharacterScreenStream, Client, GeneralStream, InGameStream, PingStream, RegionSubscription, - RegisterStream, - }, + client::{Client, RegionSubscription}, cmd::ChatCommandExt, connection_handler::ConnectionHandler, data_dir::DataDir, login_provider::LoginProvider, state_ext::StateExt, + streams::{ + CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, + }, sys::sentinel::{DeletedEntities, TrackedComps}, }; use common::{ @@ -166,7 +167,13 @@ impl Server { // System timers for performance monitoring state.ecs_mut().insert(sys::EntitySyncTimer::default()); - state.ecs_mut().insert(sys::MessageTimer::default()); + state.ecs_mut().insert(sys::GeneralMsgTimer::default()); + state.ecs_mut().insert(sys::PingMsgTimer::default()); + state.ecs_mut().insert(sys::RegisterMsgTimer::default()); + state + .ecs_mut() + .insert(sys::CharacterScreenMsgTimer::default()); + state.ecs_mut().insert(sys::InGameMsgTimer::default()); state.ecs_mut().insert(sys::SentinelTimer::default()); state.ecs_mut().insert(sys::SubscriptionTimer::default()); state.ecs_mut().insert(sys::TerrainSyncTimer::default()); @@ -466,7 +473,12 @@ impl Server { // Run message receiving sys before the systems in common for decreased latency // (e.g. run before controller system) - sys::message::Sys.run_now(&self.state.ecs()); + //TODO: run in parallel + sys::msg::general::Sys.run_now(&self.state.ecs()); + sys::msg::register::Sys.run_now(&self.state.ecs()); + sys::msg::character_screen::Sys.run_now(&self.state.ecs()); + sys::msg::in_game::Sys.run_now(&self.state.ecs()); + sys::msg::ping::Sys.run_now(&self.state.ecs()); let before_state_tick = Instant::now(); @@ -615,7 +627,14 @@ impl Server { .ecs() .read_resource::() .nanos as i64; - let message_nanos = self.state.ecs().read_resource::().nanos as i64; + let message_nanos = { + let state = self.state.ecs(); + (state.read_resource::().nanos + + state.read_resource::().nanos + + state.read_resource::().nanos + + state.read_resource::().nanos + + state.read_resource::().nanos) as i64 + }; let sentinel_nanos = self.state.ecs().read_resource::().nanos as i64; let subscription_nanos = self .state @@ -882,7 +901,7 @@ impl Server { .ecs() .write_storage::() .get_mut(entity) - .map(|s| s.0.send(msg)); + .map(|s| s.send(msg)); }, ServerMsg::General(msg) => { match &msg { @@ -895,7 +914,7 @@ impl Server { .ecs() .write_storage::() .get_mut(entity) - .map(|s| s.0.send(msg)), + .map(|s| s.send(msg)), //Ingame related ServerGeneral::GroupUpdate(_) | ServerGeneral::GroupInvite { .. } @@ -912,7 +931,7 @@ impl Server { .ecs() .write_storage::() .get_mut(entity) - .map(|s| s.0.send(msg)), + .map(|s| s.send(msg)), // Always possible ServerGeneral::PlayerListUpdate(_) | ServerGeneral::ChatMsg(_) @@ -928,7 +947,7 @@ impl Server { .ecs() .write_storage::() .get_mut(entity) - .map(|s| s.0.send(msg)), + .map(|s| s.send(msg)), }; }, ServerMsg::Ping(msg) => { @@ -936,7 +955,7 @@ impl Server { .ecs() .write_storage::() .get_mut(entity) - .map(|s| s.0.send(msg)); + .map(|s| s.send(msg)); }, } } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 57d714838b..d7cb4a3a57 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -1,6 +1,7 @@ use crate::{ - client::{CharacterScreenStream, Client, GeneralStream}, + client::Client, persistence::PersistedComponents, + streams::{CharacterScreenStream, GeneralStream, GetStream, InGameStream}, sys::sentinel::DeletedEntities, SpawnPoint, }; @@ -231,9 +232,7 @@ impl StateExt for State { .get_mut(entity) { client.in_game = Some(ClientInGame::Character); - let _ = character_screen_stream - .0 - .send(ServerGeneral::CharacterSuccess); + character_screen_stream.send_unchecked(ServerGeneral::CharacterSuccess); } } } @@ -292,14 +291,14 @@ impl StateExt for State { self.notify_registered_clients(ServerGeneral::ChatMsg(resolved_msg)) }, comp::ChatType::Online(u) => { - for (client, uid) in ( - &mut ecs.write_storage::(), + for (general_stream, uid) in ( + &mut ecs.write_storage::(), &ecs.read_storage::(), ) .join() { if uid != u { - client.send_msg(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream.send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -311,9 +310,7 @@ impl StateExt for State { .join() { if uid == u || uid == t { - let _ = general_stream - .0 - .send(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream.send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -327,9 +324,8 @@ impl StateExt for State { (&mut ecs.write_storage::(), &positions).join() { if is_within(comp::ChatMsg::SAY_DISTANCE, pos, speaker_pos) { - let _ = general_stream - .0 - .send(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream + .send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -343,9 +339,8 @@ impl StateExt for State { (&mut ecs.write_storage::(), &positions).join() { if is_within(comp::ChatMsg::REGION_DISTANCE, pos, speaker_pos) { - let _ = general_stream - .0 - .send(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream + .send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -359,9 +354,8 @@ impl StateExt for State { (&mut ecs.write_storage::(), &positions).join() { if is_within(comp::ChatMsg::NPC_DISTANCE, pos, speaker_pos) { - let _ = general_stream - .0 - .send(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream + .send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -375,9 +369,7 @@ impl StateExt for State { .join() { if s == &faction.0 { - let _ = general_stream - .0 - .send(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream.send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -389,9 +381,7 @@ impl StateExt for State { .join() { if g == group { - let _ = general_stream - .0 - .send(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream.send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -407,7 +397,7 @@ impl StateExt for State { .join() .filter(|(_, c)| c.registered) { - let _ = general_stream.0.send(msg.clone()); + general_stream.send_unchecked(msg.clone()); } } @@ -420,7 +410,7 @@ impl StateExt for State { .join() .filter(|(_, c)| c.in_game.is_some()) { - let _ = general_stream.0.send(msg.clone()); + general_stream.send_unchecked(msg.clone()); } } @@ -430,7 +420,7 @@ impl StateExt for State { ) -> Result<(), specs::error::WrongGeneration> { // Remove entity from a group if they are in one { - let mut general_streams = self.ecs().write_storage::(); + let mut in_game_streams = self.ecs().write_storage::(); let uids = self.ecs().read_storage::(); let mut group_manager = self.ecs().write_resource::(); group_manager.entity_deleted( @@ -440,14 +430,14 @@ impl StateExt for State { &uids, &self.ecs().entities(), &mut |entity, group_change| { - general_streams + in_game_streams .get_mut(entity) .and_then(|s| { group_change .try_map(|e| uids.get(e).copied()) .map(|g| (g, s)) }) - .map(|(g, s)| s.0.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); }, ); } diff --git a/server/src/streams.rs b/server/src/streams.rs new file mode 100644 index 0000000000..4fa4bfd4ad --- /dev/null +++ b/server/src/streams.rs @@ -0,0 +1,118 @@ +use common::msg::{ClientGeneral, ClientRegister, PingMsg, ServerGeneral, ServerRegisterAnswer}; + +use network::{Stream, StreamError}; +use serde::{de::DeserializeOwned, Serialize}; + +use specs::Component; +use specs_idvs::IdvStorage; + +/// helped to reduce code duplication +pub(crate) trait GetStream { + type RecvMsg: DeserializeOwned; + type SendMsg: Serialize + core::fmt::Debug; + fn get_mut(&mut self) -> &mut Stream; + fn verify(msg: &Self::SendMsg) -> bool; + + fn send(&mut self, msg: Self::SendMsg) -> Result<(), StreamError> { + if Self::verify(&msg) { + self.get_mut().send(msg) + } else { + unreachable!("sending this msg isn't allowed! got: {:?}", msg) + } + } + + fn send_unchecked(&mut self, msg: Self::SendMsg) { let _ = self.send(msg); } +} + +// Streams +// we ignore errors on send, and do unified error handling in recv +pub struct GeneralStream(pub(crate) Stream); +pub struct PingStream(pub(crate) Stream); +pub struct RegisterStream(pub(crate) Stream); +pub struct CharacterScreenStream(pub(crate) Stream); +pub struct InGameStream(pub(crate) Stream); + +impl Component for GeneralStream { + type Storage = IdvStorage; +} +impl Component for PingStream { + type Storage = IdvStorage; +} +impl Component for RegisterStream { + type Storage = IdvStorage; +} +impl Component for CharacterScreenStream { + type Storage = IdvStorage; +} +impl Component for InGameStream { + type Storage = IdvStorage; +} + +impl GetStream for GeneralStream { + type RecvMsg = ClientGeneral; + type SendMsg = ServerGeneral; + + fn get_mut(&mut self) -> &mut Stream { &mut self.0 } + + fn verify(msg: &Self::SendMsg) -> bool { + matches!(&msg, ServerGeneral::PlayerListUpdate(_) + | ServerGeneral::ChatMsg(_) + | ServerGeneral::SetPlayerEntity(_) + | ServerGeneral::TimeOfDay(_) + | ServerGeneral::EntitySync(_) + | ServerGeneral::CompSync(_) + | ServerGeneral::CreateEntity(_) + | ServerGeneral::DeleteEntity(_) + | ServerGeneral::Disconnect(_) + | ServerGeneral::Notification(_)) + } +} +impl GetStream for PingStream { + type RecvMsg = PingMsg; + type SendMsg = PingMsg; + + fn get_mut(&mut self) -> &mut Stream { &mut self.0 } + + fn verify(_: &Self::SendMsg) -> bool { true } +} +impl GetStream for RegisterStream { + type RecvMsg = ClientRegister; + type SendMsg = ServerRegisterAnswer; + + fn get_mut(&mut self) -> &mut Stream { &mut self.0 } + + fn verify(_: &Self::SendMsg) -> bool { true } +} +impl GetStream for CharacterScreenStream { + type RecvMsg = ClientGeneral; + type SendMsg = ServerGeneral; + + fn get_mut(&mut self) -> &mut Stream { &mut self.0 } + + fn verify(msg: &Self::SendMsg) -> bool { + matches!(&msg, ServerGeneral::CharacterDataLoadError(_) + | ServerGeneral::CharacterListUpdate(_) + | ServerGeneral::CharacterActionError(_) + | ServerGeneral::CharacterSuccess) + } +} +impl GetStream for InGameStream { + type RecvMsg = ClientGeneral; + type SendMsg = ServerGeneral; + + fn get_mut(&mut self) -> &mut Stream { &mut self.0 } + + fn verify(msg: &Self::SendMsg) -> bool { + matches!(&msg, ServerGeneral::GroupUpdate(_) + | ServerGeneral::GroupInvite { .. } + | ServerGeneral::InvitePending(_) + | ServerGeneral::InviteComplete { .. } + | ServerGeneral::ExitInGameSuccess + | ServerGeneral::InventoryUpdate(_, _) + | ServerGeneral::TerrainChunkUpdate { .. } + | ServerGeneral::TerrainBlockUpdates(_) + | ServerGeneral::SetViewDistance(_) + | ServerGeneral::Outcomes(_) + | ServerGeneral::Knockback(_)) + } +} diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index 465a77d728..96c61d0ba4 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -3,7 +3,8 @@ use super::{ SysTimer, }; use crate::{ - client::{Client, GeneralStream, InGameStream, RegionSubscription}, + client::{Client, RegionSubscription}, + streams::{GeneralStream, GetStream, InGameStream}, Tick, }; use common::{ @@ -165,7 +166,7 @@ impl<'a> System<'a> for Sys { // Client doesn't need to know about itself && *client_entity != entity { - let _ = general_stream.0.send(create_msg.clone()); + general_stream.send_unchecked(create_msg.clone()); } } } @@ -179,7 +180,7 @@ impl<'a> System<'a> for Sys { .map(|key| !regions.contains(key)) .unwrap_or(true) { - let _ = general_stream.0.send(ServerGeneral::DeleteEntity(uid)); + general_stream.send_unchecked(ServerGeneral::DeleteEntity(uid)); } } } @@ -201,8 +202,8 @@ impl<'a> System<'a> for Sys { subscribers .iter_mut() .for_each(move |(_, _, _, _, _, general_stream)| { - let _ = general_stream.0.send(entity_sync_msg.clone()); - let _ = general_stream.0.send(comp_sync_msg.clone()); + general_stream.send_unchecked(entity_sync_msg.clone()); + general_stream.send_unchecked(comp_sync_msg.clone()); }); let mut send_general = |msg: ServerGeneral, @@ -236,7 +237,7 @@ impl<'a> System<'a> for Sys { true // Closer than 100 blocks } } { - let _ = general_stream.0.send(msg.clone()); + general_stream.send_unchecked(msg.clone()); } } }; @@ -334,9 +335,7 @@ impl<'a> System<'a> for Sys { }) { for uid in &deleted { - let _ = general_stream - .0 - .send(ServerGeneral::DeleteEntity(Uid(*uid))); + general_stream.send_unchecked(ServerGeneral::DeleteEntity(Uid(*uid))); } } } @@ -347,7 +346,7 @@ impl<'a> System<'a> for Sys { for (inventory, update, in_game_stream) in (&inventories, &inventory_updates, &mut in_game_streams).join() { - let _ = in_game_stream.0.send(ServerGeneral::InventoryUpdate( + in_game_stream.send_unchecked(ServerGeneral::InventoryUpdate( inventory.clone(), update.event(), )); @@ -370,7 +369,7 @@ impl<'a> System<'a> for Sys { .cloned() .collect::>(); if !outcomes.is_empty() { - let _ = in_game_stream.0.send(ServerGeneral::Outcomes(outcomes)); + in_game_stream.send_unchecked(ServerGeneral::Outcomes(outcomes)); } } outcomes.clear(); @@ -384,7 +383,7 @@ impl<'a> System<'a> for Sys { // system?) let tof_msg = ServerGeneral::TimeOfDay(*time_of_day); for general_stream in (&mut general_streams).join() { - let _ = general_stream.0.send(tof_msg.clone()); + general_stream.send_unchecked(tof_msg.clone()); } timer.end(); diff --git a/server/src/sys/invite_timeout.rs b/server/src/sys/invite_timeout.rs index 87fa70b25c..fdb1d392f9 100644 --- a/server/src/sys/invite_timeout.rs +++ b/server/src/sys/invite_timeout.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::client::InGameStream; +use crate::streams::{GetStream, InGameStream}; use common::{ comp::group::{Invite, PendingInvites}, msg::{InviteAnswer, ServerGeneral}, @@ -55,7 +55,7 @@ impl<'a> System<'a> for Sys { in_game_streams.get_mut(*inviter), uids.get(invitee).copied(), ) { - let _ = in_game_stream.0.send(ServerGeneral::InviteComplete { + in_game_stream.send_unchecked(ServerGeneral::InviteComplete { target, answer: InviteAnswer::TimedOut, }); diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs deleted file mode 100644 index 39ef2537c2..0000000000 --- a/server/src/sys/message.rs +++ /dev/null @@ -1,764 +0,0 @@ -use super::SysTimer; -use crate::{ - alias_validator::AliasValidator, - character_creator, - client::{ - CharacterScreenStream, Client, GeneralStream, InGameStream, PingStream, RegisterStream, - }, - login_provider::LoginProvider, - metrics::{NetworkRequestMetrics, PlayerMetrics}, - persistence::character_loader::CharacterLoader, - EditableSettings, Settings, -}; -use common::{ - comp::{ - Admin, CanBuild, ChatMode, ChatType, ControlEvent, Controller, ForceUpdate, Ori, Player, - Pos, Stats, UnresolvedChatMsg, Vel, - }, - event::{EventBus, ServerEvent}, - msg::{ - validate_chat_msg, CharacterInfo, ChatMsgValidationError, ClientGeneral, ClientInGame, - ClientRegister, DisconnectReason, PingMsg, PlayerInfo, PlayerListUpdate, RegisterError, - ServerGeneral, ServerRegisterAnswer, MAX_BYTES_CHAT_MSG, - }, - span, - state::{BlockChange, Time}, - sync::Uid, - terrain::{TerrainChunkSize, TerrainGrid}, - vol::{ReadVol, RectVolSize}, -}; -use futures_executor::block_on; -use futures_timer::Delay; -use futures_util::{select, FutureExt}; -use hashbrown::HashMap; -use specs::{ - Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, -}; -use tracing::{debug, error, info, trace, warn}; - -impl Sys { - #[allow(clippy::too_many_arguments)] - fn handle_client_msg( - server_emitter: &mut common::event::Emitter<'_, ServerEvent>, - new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, - entity: specs::Entity, - client: &mut Client, - general_stream: &mut GeneralStream, - player_metrics: &ReadExpect<'_, PlayerMetrics>, - uids: &ReadStorage<'_, Uid>, - chat_modes: &ReadStorage<'_, ChatMode>, - msg: ClientGeneral, - ) -> Result<(), crate::error::Error> { - match msg { - ClientGeneral::ChatMsg(message) => { - if client.registered { - match validate_chat_msg(&message) { - Ok(()) => { - if let Some(from) = uids.get(entity) { - let mode = chat_modes.get(entity).cloned().unwrap_or_default(); - let msg = mode.new_message(*from, message); - new_chat_msgs.push((Some(entity), msg)); - } else { - error!("Could not send message. Missing player uid"); - } - }, - Err(ChatMsgValidationError::TooLong) => { - let max = MAX_BYTES_CHAT_MSG; - let len = message.len(); - warn!(?len, ?max, "Received a chat message that's too long") - }, - } - } - }, - ClientGeneral::Disconnect => { - general_stream - .0 - .send(ServerGeneral::Disconnect(DisconnectReason::Requested))?; - }, - ClientGeneral::Terminate => { - debug!(?entity, "Client send message to termitate session"); - player_metrics - .clients_disconnected - .with_label_values(&["gracefully"]) - .inc(); - server_emitter.emit(ServerEvent::ClientDisconnect(entity)); - }, - _ => unreachable!("not a client_general msg"), - } - Ok(()) - } - - /* - #[allow(clippy::too_many_arguments)] - fn handle_client_in_game_msg( - server_emitter: &mut common::event::Emitter<'_, ServerEvent>, - entity: specs::Entity, - client: &mut Client, - in_game_stream: &mut InGameStream, - terrain: &ReadExpect<'_, TerrainGrid>, - network_metrics: &ReadExpect<'_, NetworkRequestMetrics>, - can_build: &ReadStorage<'_, CanBuild>, - force_updates: &ReadStorage<'_, ForceUpdate>, - stats: &mut WriteStorage<'_, Stats>, - block_changes: &mut Write<'_, BlockChange>, - positions: &mut WriteStorage<'_, Pos>, - velocities: &mut WriteStorage<'_, Vel>, - orientations: &mut WriteStorage<'_, Ori>, - players: &mut WriteStorage<'_, Player>, - controllers: &mut WriteStorage<'_, Controller>, - settings: &Read<'_, Settings>, - msg: ClientGeneral, - ) -> Result<(), crate::error::Error> { - if client.in_game.is_none() { - debug!(?entity, "client is not in_game, ignoring msg"); - trace!(?msg, "ignored msg content"); - if matches!(msg, ClientGeneral::TerrainChunkRequest{ .. }) { - network_metrics.chunks_request_dropped.inc(); - } - return Ok(()); - } - match msg { - // Go back to registered state (char selection screen) - ClientGeneral::ExitInGame => { - client.in_game = None; - server_emitter.emit(ServerEvent::ExitIngame { entity }); - in_game_stream.0.send(ServerGeneral::ExitInGameSuccess)?; - }, - ClientGeneral::SetViewDistance(view_distance) => { - players.get_mut(entity).map(|player| { - player.view_distance = Some( - settings - .max_view_distance - .map(|max| view_distance.min(max)) - .unwrap_or(view_distance), - ) - }); - - //correct client if its VD is to high - if settings - .max_view_distance - .map(|max| view_distance > max) - .unwrap_or(false) - { - in_game_stream.0.send(ServerGeneral::SetViewDistance( - settings.max_view_distance.unwrap_or(0), - ))?; - } - }, - ClientGeneral::ControllerInputs(inputs) => { - if let Some(ClientInGame::Character) = client.in_game { - if let Some(controller) = controllers.get_mut(entity) { - controller.inputs.update_with_new(inputs); - } - } - }, - ClientGeneral::ControlEvent(event) => { - if let Some(ClientInGame::Character) = client.in_game { - // Skip respawn if client entity is alive - if let ControlEvent::Respawn = event { - if stats.get(entity).map_or(true, |s| !s.is_dead) { - //Todo: comment why return! - return Ok(()); - } - } - if let Some(controller) = controllers.get_mut(entity) { - controller.events.push(event); - } - } - }, - ClientGeneral::ControlAction(event) => { - if let Some(ClientInGame::Character) = client.in_game { - if let Some(controller) = controllers.get_mut(entity) { - controller.actions.push(event); - } - } - }, - ClientGeneral::PlayerPhysics { pos, vel, ori } => { - if let Some(ClientInGame::Character) = client.in_game { - if force_updates.get(entity).is_none() - && stats.get(entity).map_or(true, |s| !s.is_dead) - { - let _ = positions.insert(entity, pos); - let _ = velocities.insert(entity, vel); - let _ = orientations.insert(entity, ori); - } - } - }, - ClientGeneral::BreakBlock(pos) => { - if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) { - block_changes.set(pos, block.into_vacant()); - } - }, - ClientGeneral::PlaceBlock(pos, block) => { - if can_build.get(entity).is_some() { - block_changes.try_set(pos, block); - } - }, - ClientGeneral::TerrainChunkRequest { key } => { - let in_vd = if let (Some(view_distance), Some(pos)) = ( - players.get(entity).and_then(|p| p.view_distance), - positions.get(entity), - ) { - pos.0.xy().map(|e| e as f64).distance( - key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64), - ) < (view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt()) - * TerrainChunkSize::RECT_SIZE.x as f64 - } else { - true - }; - if in_vd { - match terrain.get_key(key) { - Some(chunk) => { - network_metrics.chunks_served_from_memory.inc(); - in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { - key, - chunk: Ok(Box::new(chunk.clone())), - })? - }, - None => { - network_metrics.chunks_generation_triggered.inc(); - server_emitter.emit(ServerEvent::ChunkRequest(entity, key)) - }, - } - } else { - network_metrics.chunks_request_dropped.inc(); - } - }, - ClientGeneral::UnlockSkill(skill) => { - stats - .get_mut(entity) - .map(|s| s.skill_set.unlock_skill(skill)); - }, - ClientGeneral::RefundSkill(skill) => { - stats - .get_mut(entity) - .map(|s| s.skill_set.refund_skill(skill)); - }, - ClientGeneral::UnlockSkillGroup(skill_group_type) => { - stats - .get_mut(entity) - .map(|s| s.skill_set.unlock_skill_group(skill_group_type)); - }, - _ => unreachable!("not a client_in_game msg"), - } - Ok(()) - } - */ - - #[allow(clippy::too_many_arguments)] - fn handle_client_character_screen_msg( - server_emitter: &mut common::event::Emitter<'_, ServerEvent>, - new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, - entity: specs::Entity, - client: &mut Client, - character_screen_stream: &mut CharacterScreenStream, - character_loader: &ReadExpect<'_, CharacterLoader>, - uids: &ReadStorage<'_, Uid>, - players: &mut WriteStorage<'_, Player>, - editable_settings: &ReadExpect<'_, EditableSettings>, - alias_validator: &ReadExpect<'_, AliasValidator>, - msg: ClientGeneral, - ) -> Result<(), crate::error::Error> { - match msg { - // Request spectator state - ClientGeneral::Spectate if client.registered => { - client.in_game = Some(ClientInGame::Spectator) - }, - ClientGeneral::Spectate => debug!("dropped Spectate msg from unregistered client"), - ClientGeneral::Character(character_id) - if client.registered && client.in_game.is_none() => - { - if let Some(player) = players.get(entity) { - // Send a request to load the character's component data from the - // DB. Once loaded, persisted components such as stats and inventory - // will be inserted for the entity - character_loader.load_character_data( - entity, - player.uuid().to_string(), - character_id, - ); - - // Start inserting non-persisted/default components for the entity - // while we load the DB data - server_emitter.emit(ServerEvent::InitCharacterData { - entity, - character_id, - }); - - // Give the player a welcome message - if !editable_settings.server_description.is_empty() { - character_screen_stream - .0 - .send(ChatType::CommandInfo.server_msg(String::from( - &*editable_settings.server_description, - )))?; - } - - if !client.login_msg_sent { - if let Some(player_uid) = uids.get(entity) { - new_chat_msgs.push((None, UnresolvedChatMsg { - chat_type: ChatType::Online(*player_uid), - message: "".to_string(), - })); - - client.login_msg_sent = true; - } - } - } else { - character_screen_stream - .0 - .send(ServerGeneral::CharacterDataLoadError(String::from( - "Failed to fetch player entity", - )))? - } - } - ClientGeneral::Character(_) => { - let registered = client.registered; - let in_game = client.in_game; - debug!(?registered, ?in_game, "dropped Character msg from client"); - }, - ClientGeneral::RequestCharacterList => { - if let Some(player) = players.get(entity) { - character_loader.load_character_list(entity, player.uuid().to_string()) - } - }, - ClientGeneral::CreateCharacter { alias, tool, body } => { - if let Err(error) = alias_validator.validate(&alias) { - debug!(?error, ?alias, "denied alias as it contained a banned word"); - character_screen_stream - .0 - .send(ServerGeneral::CharacterActionError(error.to_string()))?; - } else if let Some(player) = players.get(entity) { - character_creator::create_character( - entity, - player.uuid().to_string(), - alias, - tool, - body, - character_loader, - ); - } - }, - ClientGeneral::DeleteCharacter(character_id) => { - if let Some(player) = players.get(entity) { - character_loader.delete_character( - entity, - player.uuid().to_string(), - character_id, - ); - } - }, - _ => unreachable!("not a client_character_screen msg"), - } - Ok(()) - } - - #[allow(clippy::too_many_arguments)] - fn handle_ping_msg( - client: &mut Client, - ping_stream: &mut PingStream, - msg: PingMsg, - ) -> Result<(), crate::error::Error> { - match msg { - PingMsg::Ping => ping_stream.0.send(PingMsg::Pong)?, - PingMsg::Pong => {}, - } - Ok(()) - } - - #[allow(clippy::too_many_arguments)] - fn handle_register_msg( - player_list: &HashMap, - new_players: &mut Vec, - entity: specs::Entity, - client: &mut Client, - register_stream: &mut RegisterStream, - player_metrics: &ReadExpect<'_, PlayerMetrics>, - login_provider: &mut WriteExpect<'_, LoginProvider>, - admins: &mut WriteStorage<'_, Admin>, - players: &mut WriteStorage<'_, Player>, - editable_settings: &ReadExpect<'_, EditableSettings>, - msg: ClientRegister, - ) -> Result<(), crate::error::Error> { - let (username, uuid) = match login_provider.try_login( - &msg.token_or_username, - &*editable_settings.admins, - &*editable_settings.whitelist, - &*editable_settings.banlist, - ) { - Err(err) => { - register_stream.0.send(ServerRegisterAnswer::Err(err))?; - return Ok(()); - }, - Ok((username, uuid)) => (username, uuid), - }; - - const INITIAL_VD: Option = Some(5); //will be changed after login - let player = Player::new(username, None, INITIAL_VD, uuid); - let is_admin = editable_settings.admins.contains(&uuid); - - if !player.is_valid() { - // Invalid player - register_stream - .0 - .send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?; - return Ok(()); - } - - if !client.registered && client.in_game.is_none() { - // Add Player component to this client - let _ = players.insert(entity, player); - player_metrics.players_connected.inc(); - - // Give the Admin component to the player if their name exists in - // admin list - if is_admin { - let _ = admins.insert(entity, Admin); - } - - // Tell the client its request was successful. - client.registered = true; - register_stream.0.send(ServerRegisterAnswer::Ok(()))?; - - // Send initial player list - register_stream - .0 - .send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init( - player_list.clone(), - )))?; - - // Add to list to notify all clients of the new player - new_players.push(entity); - } - Ok(()) - } - - ///We needed to move this to a async fn, if we would use a async closures - /// the compiler generates to much recursion and fails to compile this - #[allow(clippy::too_many_arguments)] - async fn handle_messages( - server_emitter: &mut common::event::Emitter<'_, ServerEvent>, - new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, - player_list: &HashMap, - new_players: &mut Vec, - entity: specs::Entity, - client: &mut Client, - cnt: &mut u64, - character_loader: &ReadExpect<'_, CharacterLoader>, - terrain: &ReadExpect<'_, TerrainGrid>, - network_metrics: &ReadExpect<'_, NetworkRequestMetrics>, - player_metrics: &ReadExpect<'_, PlayerMetrics>, - uids: &ReadStorage<'_, Uid>, - can_build: &ReadStorage<'_, CanBuild>, - force_updates: &ReadStorage<'_, ForceUpdate>, - stats: &mut WriteStorage<'_, Stats>, - chat_modes: &ReadStorage<'_, ChatMode>, - login_provider: &mut WriteExpect<'_, LoginProvider>, - block_changes: &mut Write<'_, BlockChange>, - admins: &mut WriteStorage<'_, Admin>, - //positions: &mut WriteStorage<'_, Pos>, - //velocities: &mut WriteStorage<'_, Vel>, - //orientations: &mut WriteStorage<'_, Ori>, - players: &mut WriteStorage<'_, Player>, - general_stream: &mut GeneralStream, - ping_stream: &mut PingStream, - register_stream: &mut RegisterStream, - character_screen_stream: &mut CharacterScreenStream, - //in_game_stream: &mut InGameStream, - controllers: &mut WriteStorage<'_, Controller>, - settings: &Read<'_, Settings>, - editable_settings: &ReadExpect<'_, EditableSettings>, - alias_validator: &ReadExpect<'_, AliasValidator>, - ) -> Result<(), crate::error::Error> { - loop { - if let Some(msg) = general_stream.0.try_recv()? { - Self::handle_client_msg( - server_emitter, - new_chat_msgs, - entity, - client, - general_stream, - player_metrics, - uids, - chat_modes, - msg, - )?; - *cnt += 1; - continue; - } - - if let Some(msg) = ping_stream.0.try_recv()? { - Self::handle_ping_msg(client, ping_stream, msg)?; - *cnt += 1; - continue; - } - - if let Some(msg) = register_stream.0.try_recv()? { - Self::handle_register_msg( - player_list, - new_players, - entity, - client, - register_stream, - player_metrics, - login_provider, - admins, - players, - editable_settings, - msg, - )?; - *cnt += 1; - continue; - } - - if let Some(msg) = character_screen_stream.0.try_recv()? { - Self::handle_client_character_screen_msg( - server_emitter, - new_chat_msgs, - entity, - client, - character_screen_stream, - character_loader, - uids, - players, - editable_settings, - alias_validator, - msg, - )?; - *cnt += 1; - continue; - } - - break Ok(()) - /* - if let Some(msg) = m2 { - client.network_error |= b2; - Self::handle_client_in_game_msg( - server_emitter, - entity, - client, - terrain, - network_metrics, - can_build, - force_updates, - stats, - block_changes, - positions, - velocities, - orientations, - players, - controllers, - settings, - msg?, - )?; - }*/ - } - } -} - -/// This system will handle new messages from clients -pub struct Sys; -impl<'a> System<'a> for Sys { - #[allow(clippy::type_complexity)] // TODO: Pending review in #587 - type SystemData = ( - Entities<'a>, - Read<'a, EventBus>, - Read<'a, Time>, - ReadExpect<'a, CharacterLoader>, - ReadExpect<'a, TerrainGrid>, - ReadExpect<'a, NetworkRequestMetrics>, - ReadExpect<'a, PlayerMetrics>, - Write<'a, SysTimer>, - ReadStorage<'a, Uid>, - ReadStorage<'a, CanBuild>, - ReadStorage<'a, ForceUpdate>, - WriteStorage<'a, Stats>, - ReadStorage<'a, ChatMode>, - WriteExpect<'a, LoginProvider>, - Write<'a, BlockChange>, - WriteStorage<'a, Admin>, - //WriteStorage<'a, Pos>, - //WriteStorage<'a, Vel>, - //WriteStorage<'a, Ori>, - WriteStorage<'a, Player>, - WriteStorage<'a, Client>, - WriteStorage<'a, GeneralStream>, - WriteStorage<'a, PingStream>, - WriteStorage<'a, RegisterStream>, - WriteStorage<'a, CharacterScreenStream>, - //WriteStorage<'a, InGameStream>, - WriteStorage<'a, Controller>, - Read<'a, Settings>, - ReadExpect<'a, EditableSettings>, - ReadExpect<'a, AliasValidator>, - ); - - #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 - #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 - #[allow(clippy::single_match)] // TODO: Pending review in #587 - fn run( - &mut self, - ( - entities, - server_event_bus, - time, - character_loader, - terrain, - network_metrics, - player_metrics, - mut timer, - uids, - can_build, - force_updates, - mut stats, - chat_modes, - mut accounts, - mut block_changes, - mut admins, - //mut positions, - //mut velocities, - //mut orientations, - mut players, - mut clients, - mut general_streams, - mut ping_streams, - mut register_streams, - mut character_screen_streams, - //mut in_game_streams, - mut controllers, - settings, - editable_settings, - alias_validator, - ): Self::SystemData, - ) { - span!(_guard, "run", "message::Sys::run"); - timer.start(); - - let mut server_emitter = server_event_bus.emitter(); - - let mut new_chat_msgs = Vec::new(); - - // Player list to send new players. - let player_list = (&uids, &players, stats.maybe(), admins.maybe()) - .join() - .map(|(uid, player, stats, admin)| { - (*uid, PlayerInfo { - is_online: true, - is_admin: admin.is_some(), - player_alias: player.alias.clone(), - character: stats.map(|stats| CharacterInfo { - name: stats.name.clone(), - level: stats.level.level(), - }), - }) - }) - .collect::>(); - // List of new players to update player lists of all clients. - let mut new_players = Vec::new(); - - for (entity, client, general_stream, ping_stream, register_stream, character_screen_stream) in (&entities, &mut clients, &mut general_streams, &mut ping_streams, &mut register_streams, &mut character_screen_streams).join() { - let mut cnt = 0; - - let network_err: Result<(), crate::error::Error> = block_on(async { - //TIMEOUT 0.02 ms for msg handling - let work_future = Self::handle_messages( - &mut server_emitter, - &mut new_chat_msgs, - &player_list, - &mut new_players, - entity, - client, - &mut cnt, - &character_loader, - &terrain, - &network_metrics, - &player_metrics, - &uids, - &can_build, - &force_updates, - &mut stats, - &chat_modes, - &mut accounts, - &mut block_changes, - &mut admins, - //&mut positions, - //&mut velocities, - //&mut orientations, - &mut players, - general_stream, - ping_stream, - register_stream, - character_screen_stream, - &mut controllers, - &settings, - &editable_settings, - &alias_validator, - ); - select!( - _ = Delay::new(std::time::Duration::from_micros(20)).fuse() => Ok(()), - err = work_future.fuse() => err, - ) - }); - - // Network error - if network_err.is_err() { - debug!(?entity, "postbox error with client, disconnecting"); - player_metrics - .clients_disconnected - .with_label_values(&["network_error"]) - .inc(); - server_emitter.emit(ServerEvent::ClientDisconnect(entity)); - } else if cnt > 0 { - // Update client ping. - client.last_ping = time.0 - } else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 - // Timeout - { - info!(?entity, "timeout error with client, disconnecting"); - player_metrics - .clients_disconnected - .with_label_values(&["timeout"]) - .inc(); - server_emitter.emit(ServerEvent::ClientDisconnect(entity)); - } else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 * 0.5 { - // Try pinging the client if the timeout is nearing. - //FIXME - //client.send_msg(PingMsg::Ping); - } - } - - // Handle new players. - // Tell all clients to add them to the player list. - for entity in new_players { - if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) { - let msg = - ServerGeneral::PlayerListUpdate(PlayerListUpdate::Add(*uid, PlayerInfo { - player_alias: player.alias.clone(), - is_online: true, - is_admin: admins.get(entity).is_some(), - character: None, // new players will be on character select. - })); - for client in (&mut clients).join().filter(|c| c.registered) { - //FIXME - //client.send_msg(msg.clone()) - } - } - } - - // Handle new chat messages. - for (entity, msg) in new_chat_msgs { - // Handle chat commands. - if msg.message.starts_with("/") { - if let (Some(entity), true) = (entity, msg.message.len() > 1) { - let argv = String::from(&msg.message[1..]); - server_emitter.emit(ServerEvent::ChatCmd(entity, argv)); - } - } else { - // Send chat message - server_emitter.emit(ServerEvent::Chat(msg)); - } - } - - timer.end() - } -} diff --git a/server/src/sys/mod.rs b/server/src/sys/mod.rs index 6dba8a0f3c..7e34ea933b 100644 --- a/server/src/sys/mod.rs +++ b/server/src/sys/mod.rs @@ -1,7 +1,6 @@ pub mod entity_sync; pub mod invite_timeout; pub mod msg; -pub mod message; pub mod object; pub mod persistence; pub mod sentinel; @@ -17,7 +16,11 @@ use std::{ }; pub type EntitySyncTimer = SysTimer; -pub type MessageTimer = SysTimer; +pub type GeneralMsgTimer = SysTimer; +pub type PingMsgTimer = SysTimer; +pub type RegisterMsgTimer = SysTimer; +pub type CharacterScreenMsgTimer = SysTimer; +pub type InGameMsgTimer = SysTimer; pub type SentinelTimer = SysTimer; pub type SubscriptionTimer = SysTimer; pub type TerrainTimer = SysTimer; diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs new file mode 100644 index 0000000000..8444886a62 --- /dev/null +++ b/server/src/sys/msg/character_screen.rs @@ -0,0 +1,224 @@ +use super::super::SysTimer; +use crate::{ + alias_validator::AliasValidator, + character_creator, + client::Client, + persistence::character_loader::CharacterLoader, + streams::{CharacterScreenStream, GeneralStream, GetStream}, + EditableSettings, +}; +use common::{ + comp::{ChatType, Player, UnresolvedChatMsg}, + event::{EventBus, ServerEvent}, + msg::{ClientGeneral, ClientInGame, ServerGeneral}, + span, + state::Time, + sync::Uid, +}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; +use tracing::debug; + +impl Sys { + #[allow(clippy::too_many_arguments)] + fn handle_client_character_screen_msg( + server_emitter: &mut common::event::Emitter<'_, ServerEvent>, + new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, + entity: specs::Entity, + client: &mut Client, + character_screen_stream: &mut CharacterScreenStream, + general_stream: &mut GeneralStream, + character_loader: &ReadExpect<'_, CharacterLoader>, + uids: &ReadStorage<'_, Uid>, + players: &ReadStorage<'_, Player>, + editable_settings: &ReadExpect<'_, EditableSettings>, + alias_validator: &ReadExpect<'_, AliasValidator>, + msg: ClientGeneral, + ) -> Result<(), crate::error::Error> { + match msg { + // Request spectator state + ClientGeneral::Spectate if client.registered => { + client.in_game = Some(ClientInGame::Spectator) + }, + ClientGeneral::Spectate => debug!("dropped Spectate msg from unregistered client"), + ClientGeneral::Character(character_id) + if client.registered && client.in_game.is_none() => + { + if let Some(player) = players.get(entity) { + // Send a request to load the character's component data from the + // DB. Once loaded, persisted components such as stats and inventory + // will be inserted for the entity + character_loader.load_character_data( + entity, + player.uuid().to_string(), + character_id, + ); + + // Start inserting non-persisted/default components for the entity + // while we load the DB data + server_emitter.emit(ServerEvent::InitCharacterData { + entity, + character_id, + }); + + // Give the player a welcome message + if !editable_settings.server_description.is_empty() { + general_stream + .send(ChatType::CommandInfo.server_msg(String::from( + &*editable_settings.server_description, + )))?; + } + + if !client.login_msg_sent { + if let Some(player_uid) = uids.get(entity) { + new_chat_msgs.push((None, UnresolvedChatMsg { + chat_type: ChatType::Online(*player_uid), + message: "".to_string(), + })); + + client.login_msg_sent = true; + } + } + } else { + character_screen_stream.send(ServerGeneral::CharacterDataLoadError( + String::from("Failed to fetch player entity"), + ))? + } + } + ClientGeneral::Character(_) => { + let registered = client.registered; + let in_game = client.in_game; + debug!(?registered, ?in_game, "dropped Character msg from client"); + }, + ClientGeneral::RequestCharacterList => { + if let Some(player) = players.get(entity) { + character_loader.load_character_list(entity, player.uuid().to_string()) + } + }, + ClientGeneral::CreateCharacter { alias, tool, body } => { + if let Err(error) = alias_validator.validate(&alias) { + debug!(?error, ?alias, "denied alias as it contained a banned word"); + character_screen_stream + .send(ServerGeneral::CharacterActionError(error.to_string()))?; + } else if let Some(player) = players.get(entity) { + character_creator::create_character( + entity, + player.uuid().to_string(), + alias, + tool, + body, + character_loader, + ); + } + }, + ClientGeneral::DeleteCharacter(character_id) => { + if let Some(player) = players.get(entity) { + character_loader.delete_character( + entity, + player.uuid().to_string(), + character_id, + ); + } + }, + _ => unreachable!("not a client_character_screen msg"), + } + Ok(()) + } +} + +/// This system will handle new messages from clients +pub struct Sys; +impl<'a> System<'a> for Sys { + #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + type SystemData = ( + Entities<'a>, + Read<'a, EventBus>, + Read<'a, Time>, + ReadExpect<'a, CharacterLoader>, + Write<'a, SysTimer>, + ReadStorage<'a, Uid>, + WriteStorage<'a, Client>, + ReadStorage<'a, Player>, + WriteStorage<'a, CharacterScreenStream>, + WriteStorage<'a, GeneralStream>, + ReadExpect<'a, EditableSettings>, + ReadExpect<'a, AliasValidator>, + ); + + #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 + #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 + #[allow(clippy::single_match)] // TODO: Pending review in #587 + fn run( + &mut self, + ( + entities, + server_event_bus, + time, + character_loader, + mut timer, + uids, + mut clients, + players, + mut character_screen_streams, + mut general_streams, + editable_settings, + alias_validator, + ): Self::SystemData, + ) { + span!(_guard, "run", "msg::character_screen::Sys::run"); + timer.start(); + + let mut server_emitter = server_event_bus.emitter(); + let mut new_chat_msgs = Vec::new(); + + for (entity, client, character_screen_stream, general_stream) in ( + &entities, + &mut clients, + &mut character_screen_streams, + &mut general_streams, + ) + .join() + { + let res = + super::try_recv_all(character_screen_stream, |character_screen_stream, msg| { + Self::handle_client_character_screen_msg( + &mut server_emitter, + &mut new_chat_msgs, + entity, + client, + character_screen_stream, + general_stream, + &character_loader, + &uids, + &players, + &editable_settings, + &alias_validator, + msg, + ) + }); + + match res { + Ok(1_u64..=u64::MAX) => { + // Update client ping. + client.last_ping = time.0 + }, + _ => (/*handled by ping*/), + } + } + + // Handle new chat messages. + for (entity, msg) in new_chat_msgs { + // Handle chat commands. + if msg.message.starts_with("/") { + if let (Some(entity), true) = (entity, msg.message.len() > 1) { + let argv = String::from(&msg.message[1..]); + server_emitter.emit(ServerEvent::ChatCmd(entity, argv)); + } + } else { + // Send chat message + server_emitter.emit(ServerEvent::Chat(msg)); + } + } + + timer.end() + } +} diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs new file mode 100644 index 0000000000..19a2fb2dd6 --- /dev/null +++ b/server/src/sys/msg/general.rs @@ -0,0 +1,153 @@ +use super::super::SysTimer; +use crate::{ + client::Client, + metrics::PlayerMetrics, + streams::{GeneralStream, GetStream}, +}; +use common::{ + comp::{ChatMode, UnresolvedChatMsg}, + event::{EventBus, ServerEvent}, + msg::{ + validate_chat_msg, ChatMsgValidationError, ClientGeneral, DisconnectReason, ServerGeneral, + MAX_BYTES_CHAT_MSG, + }, + span, + state::Time, + sync::Uid, +}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; +use tracing::{debug, error, warn}; + +impl Sys { + #[allow(clippy::too_many_arguments)] + fn handle_general_msg( + server_emitter: &mut common::event::Emitter<'_, ServerEvent>, + new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, + entity: specs::Entity, + client: &mut Client, + general_stream: &mut GeneralStream, + player_metrics: &ReadExpect<'_, PlayerMetrics>, + uids: &ReadStorage<'_, Uid>, + chat_modes: &ReadStorage<'_, ChatMode>, + msg: ClientGeneral, + ) -> Result<(), crate::error::Error> { + match msg { + ClientGeneral::ChatMsg(message) => { + if client.registered { + match validate_chat_msg(&message) { + Ok(()) => { + if let Some(from) = uids.get(entity) { + let mode = chat_modes.get(entity).cloned().unwrap_or_default(); + let msg = mode.new_message(*from, message); + new_chat_msgs.push((Some(entity), msg)); + } else { + error!("Could not send message. Missing player uid"); + } + }, + Err(ChatMsgValidationError::TooLong) => { + let max = MAX_BYTES_CHAT_MSG; + let len = message.len(); + warn!(?len, ?max, "Received a chat message that's too long") + }, + } + } + }, + ClientGeneral::Disconnect => { + general_stream.send(ServerGeneral::Disconnect(DisconnectReason::Requested))?; + }, + ClientGeneral::Terminate => { + debug!(?entity, "Client send message to termitate session"); + player_metrics + .clients_disconnected + .with_label_values(&["gracefully"]) + .inc(); + server_emitter.emit(ServerEvent::ClientDisconnect(entity)); + }, + _ => unreachable!("not a client_general msg"), + } + Ok(()) + } +} + +/// This system will handle new messages from clients +pub struct Sys; +impl<'a> System<'a> for Sys { + #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + type SystemData = ( + Entities<'a>, + Read<'a, EventBus>, + Read<'a, Time>, + ReadExpect<'a, PlayerMetrics>, + Write<'a, SysTimer>, + ReadStorage<'a, Uid>, + ReadStorage<'a, ChatMode>, + WriteStorage<'a, Client>, + WriteStorage<'a, GeneralStream>, + ); + + #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 + #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 + #[allow(clippy::single_match)] // TODO: Pending review in #587 + fn run( + &mut self, + ( + entities, + server_event_bus, + time, + player_metrics, + mut timer, + uids, + chat_modes, + mut clients, + mut general_streams, + ): Self::SystemData, + ) { + span!(_guard, "run", "msg::general::Sys::run"); + timer.start(); + + let mut server_emitter = server_event_bus.emitter(); + let mut new_chat_msgs = Vec::new(); + + for (entity, client, general_stream) in + (&entities, &mut clients, &mut general_streams).join() + { + let res = super::try_recv_all(general_stream, |general_stream, msg| { + Self::handle_general_msg( + &mut server_emitter, + &mut new_chat_msgs, + entity, + client, + general_stream, + &player_metrics, + &uids, + &chat_modes, + msg, + ) + }); + + match res { + Ok(1_u64..=u64::MAX) => { + // Update client ping. + client.last_ping = time.0 + }, + _ => (/*handled by ping*/), + } + } + + // Handle new chat messages. + for (entity, msg) in new_chat_msgs { + // Handle chat commands. + if msg.message.starts_with("/") { + if let (Some(entity), true) = (entity, msg.message.len() > 1) { + let argv = String::from(&msg.message[1..]); + server_emitter.emit(ServerEvent::ChatCmd(entity, argv)); + } + } else { + // Send chat message + server_emitter.emit(ServerEvent::Chat(msg)); + } + } + + timer.end() + } +} diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index 0090f7603b..ed3356d38a 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -1,33 +1,20 @@ use super::super::SysTimer; use crate::{ - alias_validator::AliasValidator, - client::{ - Client, InGameStream, - }, - login_provider::LoginProvider, - metrics::{NetworkRequestMetrics, PlayerMetrics}, - persistence::character_loader::CharacterLoader, - EditableSettings, Settings, + client::Client, + metrics::NetworkRequestMetrics, + streams::{GetStream, InGameStream}, + Settings, }; use common::{ - comp::{ - Admin, CanBuild, ChatMode, ControlEvent, Controller, ForceUpdate, Ori, Player, - Pos, Stats, Vel, - }, + comp::{CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, Stats, Vel}, event::{EventBus, ServerEvent}, - msg::{ - ClientGeneral, ClientInGame, - ServerGeneral, - }, + msg::{ClientGeneral, ClientInGame, ServerGeneral}, span, state::{BlockChange, Time}, - sync::Uid, terrain::{TerrainChunkSize, TerrainGrid}, vol::{ReadVol, RectVolSize}, }; -use specs::{ - Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, -}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; use tracing::{debug, trace}; impl Sys { @@ -64,7 +51,7 @@ impl Sys { ClientGeneral::ExitInGame => { client.in_game = None; server_emitter.emit(ServerEvent::ExitIngame { entity }); - in_game_stream.0.send(ServerGeneral::ExitInGameSuccess)?; + in_game_stream.send(ServerGeneral::ExitInGameSuccess)?; }, ClientGeneral::SetViewDistance(view_distance) => { players.get_mut(entity).map(|player| { @@ -82,7 +69,7 @@ impl Sys { .map(|max| view_distance > max) .unwrap_or(false) { - in_game_stream.0.send(ServerGeneral::SetViewDistance( + in_game_stream.send(ServerGeneral::SetViewDistance( settings.max_view_distance.unwrap_or(0), ))?; } @@ -152,7 +139,7 @@ impl Sys { match terrain.get_key(key) { Some(chunk) => { network_metrics.chunks_served_from_memory.inc(); - in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { + in_game_stream.send(ServerGeneral::TerrainChunkUpdate { key, chunk: Ok(Box::new(chunk.clone())), })? @@ -195,19 +182,13 @@ impl<'a> System<'a> for Sys { Entities<'a>, Read<'a, EventBus>, Read<'a, Time>, - ReadExpect<'a, CharacterLoader>, ReadExpect<'a, TerrainGrid>, ReadExpect<'a, NetworkRequestMetrics>, - ReadExpect<'a, PlayerMetrics>, Write<'a, SysTimer>, - ReadStorage<'a, Uid>, ReadStorage<'a, CanBuild>, ReadStorage<'a, ForceUpdate>, WriteStorage<'a, Stats>, - ReadStorage<'a, ChatMode>, - WriteExpect<'a, LoginProvider>, Write<'a, BlockChange>, - WriteStorage<'a, Admin>, WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, @@ -216,8 +197,6 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, InGameStream>, WriteStorage<'a, Controller>, Read<'a, Settings>, - ReadExpect<'a, EditableSettings>, - ReadExpect<'a, AliasValidator>, ); #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 @@ -229,19 +208,13 @@ impl<'a> System<'a> for Sys { entities, server_event_bus, time, - character_loader, terrain, network_metrics, - player_metrics, mut timer, - uids, can_build, force_updates, mut stats, - chat_modes, - mut accounts, mut block_changes, - mut admins, mut positions, mut velocities, mut orientations, @@ -250,8 +223,6 @@ impl<'a> System<'a> for Sys { mut in_game_streams, mut controllers, settings, - editable_settings, - alias_validator, ): Self::SystemData, ) { span!(_guard, "run", "msg::in_game::Sys::run"); @@ -259,43 +230,37 @@ impl<'a> System<'a> for Sys { let mut server_emitter = server_event_bus.emitter(); - for (entity, client, mut in_game_stream) in (&entities, &mut clients, &mut in_game_streams).join() { - let mut cnt = 0; + for (entity, client, in_game_stream) in + (&entities, &mut clients, &mut in_game_streams).join() + { + let res = super::try_recv_all(in_game_stream, |in_game_stream, msg| { + Self::handle_client_in_game_msg( + &mut server_emitter, + entity, + client, + in_game_stream, + &terrain, + &network_metrics, + &can_build, + &force_updates, + &mut stats, + &mut block_changes, + &mut positions, + &mut velocities, + &mut orientations, + &mut players, + &mut controllers, + &settings, + msg, + ) + }); - let network_err: Result<(), crate::error::Error> = { - loop { - let msg = match in_game_stream.0.try_recv() { - Ok(Some(msg)) => msg, - Ok(None) => break Ok(()), - Err(e) => break Err(e.into()), - }; - if let Err(e) = Self::handle_client_in_game_msg( - &mut server_emitter, - entity, - client, - &mut in_game_stream, - &terrain, - &network_metrics, - &can_build, - &force_updates, - &mut stats, - &mut block_changes, - &mut positions, - &mut velocities, - &mut orientations, - &mut players, - &mut controllers, - &settings, - msg, - ) { - break Err(e); - } - cnt += 1; - } - }; - - if cnt > 0 { // Update client ping. - client.last_ping = time.0 + match res { + Ok(1_u64..=u64::MAX) => { + // Update client ping. + client.last_ping = time.0 + }, + _ => (/*handled by ping*/), } } diff --git a/server/src/sys/msg/mod.rs b/server/src/sys/msg/mod.rs index ee8449ae96..a95ef02a94 100644 --- a/server/src/sys/msg/mod.rs +++ b/server/src/sys/msg/mod.rs @@ -1 +1,28 @@ -mod in_game; \ No newline at end of file +pub mod character_screen; +pub mod general; +pub mod in_game; +pub mod ping; +pub mod register; + +use crate::streams::GetStream; + +/// handles all send msg and calls a handle fn +/// Aborts when a error occurred returns cnt of successful msg otherwise +pub(crate) fn try_recv_all(stream: &mut T, mut f: F) -> Result +where + T: GetStream, + F: FnMut(&mut T, T::RecvMsg) -> Result<(), crate::error::Error>, +{ + let mut cnt = 0u64; + loop { + let msg = match stream.get_mut().try_recv() { + Ok(Some(msg)) => msg, + Ok(None) => break Ok(cnt), + Err(e) => break Err(e.into()), + }; + if let Err(e) = f(stream, msg) { + break Err(e); + } + cnt += 1; + } +} diff --git a/server/src/sys/msg/ping.rs b/server/src/sys/msg/ping.rs new file mode 100644 index 0000000000..b5b93bdc66 --- /dev/null +++ b/server/src/sys/msg/ping.rs @@ -0,0 +1,106 @@ +use super::super::SysTimer; +use crate::{ + client::Client, + metrics::PlayerMetrics, + streams::{GetStream, PingStream}, + Settings, +}; +use common::{ + event::{EventBus, ServerEvent}, + msg::PingMsg, + span, + state::Time, +}; +use specs::{Entities, Join, Read, ReadExpect, System, Write, WriteStorage}; +use tracing::{debug, info}; + +impl Sys { + fn handle_ping_msg( + ping_stream: &mut PingStream, + msg: PingMsg, + ) -> Result<(), crate::error::Error> { + match msg { + PingMsg::Ping => ping_stream.send(PingMsg::Pong)?, + PingMsg::Pong => {}, + } + Ok(()) + } +} + +/// This system will handle new messages from clients +pub struct Sys; +impl<'a> System<'a> for Sys { + #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + type SystemData = ( + Entities<'a>, + Read<'a, EventBus>, + Read<'a, Time>, + ReadExpect<'a, PlayerMetrics>, + Write<'a, SysTimer>, + WriteStorage<'a, Client>, + WriteStorage<'a, PingStream>, + Read<'a, Settings>, + ); + + #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 + #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 + #[allow(clippy::single_match)] // TODO: Pending review in #587 + fn run( + &mut self, + ( + entities, + server_event_bus, + time, + player_metrics, + mut timer, + mut clients, + mut ping_streams, + settings, + ): Self::SystemData, + ) { + span!(_guard, "run", "msg::ping::Sys::run"); + timer.start(); + + let mut server_emitter = server_event_bus.emitter(); + + for (entity, client, ping_stream) in (&entities, &mut clients, &mut ping_streams).join() { + let res = super::try_recv_all(ping_stream, |ping_stream, msg| { + Self::handle_ping_msg(ping_stream, msg) + }); + + match res { + Err(e) => { + debug!(?entity, ?e, "network error with client, disconnecting"); + player_metrics + .clients_disconnected + .with_label_values(&["network_error"]) + .inc(); + server_emitter.emit(ServerEvent::ClientDisconnect(entity)); + }, + Ok(1_u64..=u64::MAX) => { + // Update client ping. + client.last_ping = time.0 + }, + Ok(0) => { + if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 + // Timeout + { + info!(?entity, "timeout error with client, disconnecting"); + player_metrics + .clients_disconnected + .with_label_values(&["timeout"]) + .inc(); + server_emitter.emit(ServerEvent::ClientDisconnect(entity)); + } else if time.0 - client.last_ping + > settings.client_timeout.as_secs() as f64 * 0.5 + { + // Try pinging the client if the timeout is nearing. + ping_stream.send_unchecked(PingMsg::Ping); + } + }, + } + } + + timer.end() + } +} diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs new file mode 100644 index 0000000000..437243ab52 --- /dev/null +++ b/server/src/sys/msg/register.rs @@ -0,0 +1,208 @@ +use super::super::SysTimer; +use crate::{ + client::Client, + login_provider::LoginProvider, + metrics::PlayerMetrics, + streams::{GeneralStream, GetStream, RegisterStream}, + EditableSettings, +}; +use common::{ + comp::{Admin, Player, Stats}, + msg::{ + CharacterInfo, ClientRegister, PlayerInfo, PlayerListUpdate, RegisterError, ServerGeneral, + ServerRegisterAnswer, + }, + span, + state::Time, + sync::Uid, +}; +use hashbrown::HashMap; +use specs::{ + Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, +}; + +impl Sys { + #[allow(clippy::too_many_arguments)] + fn handle_register_msg( + player_list: &HashMap, + new_players: &mut Vec, + entity: specs::Entity, + client: &mut Client, + register_stream: &mut RegisterStream, + general_stream: &mut GeneralStream, + player_metrics: &ReadExpect<'_, PlayerMetrics>, + login_provider: &mut WriteExpect<'_, LoginProvider>, + admins: &mut WriteStorage<'_, Admin>, + players: &mut WriteStorage<'_, Player>, + editable_settings: &ReadExpect<'_, EditableSettings>, + msg: ClientRegister, + ) -> Result<(), crate::error::Error> { + let (username, uuid) = match login_provider.try_login( + &msg.token_or_username, + &*editable_settings.admins, + &*editable_settings.whitelist, + &*editable_settings.banlist, + ) { + Err(err) => { + register_stream.send(ServerRegisterAnswer::Err(err))?; + return Ok(()); + }, + Ok((username, uuid)) => (username, uuid), + }; + + const INITIAL_VD: Option = Some(5); //will be changed after login + let player = Player::new(username, None, INITIAL_VD, uuid); + let is_admin = editable_settings.admins.contains(&uuid); + + if !player.is_valid() { + // Invalid player + register_stream.send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?; + return Ok(()); + } + + if !client.registered && client.in_game.is_none() { + // Add Player component to this client + let _ = players.insert(entity, player); + player_metrics.players_connected.inc(); + + // Give the Admin component to the player if their name exists in + // admin list + if is_admin { + let _ = admins.insert(entity, Admin); + } + + // Tell the client its request was successful. + client.registered = true; + register_stream.send(ServerRegisterAnswer::Ok(()))?; + + // Send initial player list + general_stream.send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init( + player_list.clone(), + )))?; + + // Add to list to notify all clients of the new player + new_players.push(entity); + } + Ok(()) + } +} + +/// This system will handle new messages from clients +pub struct Sys; +impl<'a> System<'a> for Sys { + #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + type SystemData = ( + Entities<'a>, + Read<'a, Time>, + ReadExpect<'a, PlayerMetrics>, + Write<'a, SysTimer>, + ReadStorage<'a, Uid>, + WriteStorage<'a, Client>, + WriteStorage<'a, Player>, + ReadStorage<'a, Stats>, + WriteExpect<'a, LoginProvider>, + WriteStorage<'a, Admin>, + WriteStorage<'a, RegisterStream>, + WriteStorage<'a, GeneralStream>, + ReadExpect<'a, EditableSettings>, + ); + + #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 + #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 + #[allow(clippy::single_match)] // TODO: Pending review in #587 + fn run( + &mut self, + ( + entities, + time, + player_metrics, + mut timer, + uids, + mut clients, + mut players, + stats, + mut login_provider, + mut admins, + mut register_streams, + mut general_streams, + editable_settings, + ): Self::SystemData, + ) { + span!(_guard, "run", "msg::register::Sys::run"); + timer.start(); + + // Player list to send new players. + let player_list = (&uids, &players, stats.maybe(), admins.maybe()) + .join() + .map(|(uid, player, stats, admin)| { + (*uid, PlayerInfo { + is_online: true, + is_admin: admin.is_some(), + player_alias: player.alias.clone(), + character: stats.map(|stats| CharacterInfo { + name: stats.name.clone(), + level: stats.level.level(), + }), + }) + }) + .collect::>(); + // List of new players to update player lists of all clients. + let mut new_players = Vec::new(); + + for (entity, client, register_stream, general_stream) in ( + &entities, + &mut clients, + &mut register_streams, + &mut general_streams, + ) + .join() + { + let res = super::try_recv_all(register_stream, |register_stream, msg| { + Self::handle_register_msg( + &player_list, + &mut new_players, + entity, + client, + register_stream, + general_stream, + &player_metrics, + &mut login_provider, + &mut admins, + &mut players, + &editable_settings, + msg, + ) + }); + + match res { + Ok(1_u64..=u64::MAX) => { + // Update client ping. + client.last_ping = time.0 + }, + _ => (/*handled by ping*/), + } + } + + // Handle new players. + // Tell all clients to add them to the player list. + for entity in new_players { + if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) { + let msg = + ServerGeneral::PlayerListUpdate(PlayerListUpdate::Add(*uid, PlayerInfo { + player_alias: player.alias.clone(), + is_online: true, + is_admin: admins.get(entity).is_some(), + character: None, // new players will be on character select. + })); + for (_, general_stream) in (&mut clients, &mut general_streams) + .join() + .filter(|(c, _)| c.registered) + { + let _ = general_stream.send(msg.clone()); + } + } + } + + timer.end() + } +} diff --git a/server/src/sys/subscription.rs b/server/src/sys/subscription.rs index 5fd1611ae0..ce7c3567b1 100644 --- a/server/src/sys/subscription.rs +++ b/server/src/sys/subscription.rs @@ -2,7 +2,10 @@ use super::{ sentinel::{DeletedEntities, TrackedComps}, SysTimer, }; -use crate::client::{self, Client, InGameStream, RegionSubscription}; +use crate::{ + client::{self, Client, RegionSubscription}, + streams::{GeneralStream, GetStream}, +}; use common::{ comp::{Ori, Player, Pos, Vel}, msg::ServerGeneral, @@ -33,7 +36,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Ori>, ReadStorage<'a, Player>, ReadStorage<'a, Client>, - WriteStorage<'a, InGameStream>, + WriteStorage<'a, GeneralStream>, WriteStorage<'a, RegionSubscription>, Write<'a, DeletedEntities>, TrackedComps<'a>, @@ -52,7 +55,7 @@ impl<'a> System<'a> for Sys { orientations, players, clients, - mut in_game_streams, + mut general_streams, mut subscriptions, mut deleted_entities, tracked_comps, @@ -73,13 +76,13 @@ impl<'a> System<'a> for Sys { // 7. Determine list of regions that are in range and iterate through it // - check if in hashset (hash calc) if not add it let mut regions_to_remove = Vec::new(); - for (_, subscription, pos, vd, client_entity, in_game_stream) in ( + for (_, subscription, pos, vd, client_entity, general_stream) in ( &clients, &mut subscriptions, &positions, &players, &entities, - &mut in_game_streams, + &mut general_streams, ) .join() .filter_map(|(client, s, pos, player, e, stream)| { @@ -156,9 +159,8 @@ impl<'a> System<'a> for Sys { .map(|key| subscription.regions.contains(key)) .unwrap_or(false) { - let _ = in_game_stream - .0 - .send(ServerGeneral::DeleteEntity(uid)); + general_stream + .send_unchecked(ServerGeneral::DeleteEntity(uid)); } } }, @@ -166,7 +168,7 @@ impl<'a> System<'a> for Sys { } // Tell client to delete entities in the region for (&uid, _) in (&uids, region.entities()).join() { - let _ = in_game_stream.0.send(ServerGeneral::DeleteEntity(uid)); + let _ = general_stream.send(ServerGeneral::DeleteEntity(uid)); } } // Send deleted entities since they won't be processed for this client in entity @@ -176,9 +178,7 @@ impl<'a> System<'a> for Sys { .iter() .flat_map(|v| v.iter()) { - let _ = in_game_stream - .0 - .send(ServerGeneral::DeleteEntity(Uid(*uid))); + general_stream.send_unchecked(ServerGeneral::DeleteEntity(Uid(*uid))); } } @@ -203,7 +203,7 @@ impl<'a> System<'a> for Sys { { // Send message to create entity and tracked components and physics // components - let _ = in_game_stream.0.send(ServerGeneral::CreateEntity( + general_stream.send_unchecked(ServerGeneral::CreateEntity( tracked_comps.create_entity_package( entity, Some(*pos), @@ -224,14 +224,14 @@ impl<'a> System<'a> for Sys { /// Initialize region subscription pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { - if let (Some(client_pos), Some(client_vd), Some(in_game_stream)) = ( + if let (Some(client_pos), Some(client_vd), Some(general_stream)) = ( world.read_storage::().get(entity), world .read_storage::() .get(entity) .map(|pl| pl.view_distance) .and_then(|v| v), - world.write_storage::().get_mut(entity), + world.write_storage::().get_mut(entity), ) { let fuzzy_chunk = (Vec2::::from(client_pos.0)) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); @@ -256,7 +256,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { .join() { // Send message to create entity and tracked components and physics components - let _ = in_game_stream.0.send(ServerGeneral::CreateEntity( + general_stream.send_unchecked(ServerGeneral::CreateEntity( tracked_comps.create_entity_package( entity, Some(*pos), diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 8a359e2423..07f11a19db 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,5 +1,9 @@ use super::SysTimer; -use crate::{chunk_generator::ChunkGenerator, client::InGameStream, Tick}; +use crate::{ + chunk_generator::ChunkGenerator, + streams::{GetStream, InGameStream}, + Tick, +}; use common::{ comp::{self, bird_medium, Alignment, Player, Pos}, event::{EventBus, ServerEvent}, @@ -63,7 +67,7 @@ impl<'a> System<'a> for Sys { Ok((chunk, supplement)) => (chunk, supplement), Err(Some(entity)) => { if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - let _ = in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { + in_game_stream.send_unchecked(ServerGeneral::TerrainChunkUpdate { key, chunk: Err(()), }); @@ -90,7 +94,7 @@ impl<'a> System<'a> for Sys { .magnitude_squared(); if adjusted_dist_sqr <= view_distance.pow(2) { - let _ = in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { + in_game_stream.send_unchecked(ServerGeneral::TerrainChunkUpdate { key, chunk: Ok(Box::new(chunk.clone())), }); diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index e547f4a8e3..fc2f657518 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::client::InGameStream; +use crate::streams::{GetStream, InGameStream}; use common::{ comp::{Player, Pos}, msg::ServerGeneral, @@ -39,7 +39,7 @@ impl<'a> System<'a> for Sys { .map(|vd| super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, vd)) .unwrap_or(false) { - let _ = in_game_stream.0.send(ServerGeneral::TerrainChunkUpdate { + let _ = in_game_stream.send(ServerGeneral::TerrainChunkUpdate { key: *chunk_key, chunk: Ok(Box::new(match terrain.get_key(*chunk_key) { Some(chunk) => chunk.clone(), @@ -55,7 +55,7 @@ impl<'a> System<'a> for Sys { let msg = ServerGeneral::TerrainBlockUpdates(terrain_changes.modified_blocks.clone()); for (player, in_game_stream) in (&players, &mut in_game_streams).join() { if player.view_distance.is_some() { - let _ = in_game_stream.0.send(msg.clone()); + in_game_stream.send_unchecked(msg.clone()); } } diff --git a/server/src/sys/waypoint.rs b/server/src/sys/waypoint.rs index 3af23149b4..0d2f7426dd 100644 --- a/server/src/sys/waypoint.rs +++ b/server/src/sys/waypoint.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::client::GeneralStream; +use crate::streams::{GeneralStream, GetStream}; use common::{ comp::{Player, Pos, Waypoint, WaypointArea}, msg::{Notification, ServerGeneral}, @@ -51,9 +51,9 @@ impl<'a> System<'a> for Sys { if let Ok(wp_old) = waypoints.insert(entity, Waypoint::new(player_pos.0, *time)) { if wp_old.map_or(true, |w| w.elapsed(*time) > NOTIFY_TIME) { - let _ = general_stream - .0 - .send(ServerGeneral::Notification(Notification::WaypointSaved)); + general_stream.send_unchecked(ServerGeneral::Notification( + Notification::WaypointSaved, + )); } } } From 2290efd21979ab8ef962690d256780ad561ad98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 19 Oct 2020 00:57:43 +0200 Subject: [PATCH 04/10] remove clippy warnings no longer needed --- server/src/sys/msg/character_screen.rs | 16 +++++----------- server/src/sys/msg/general.rs | 16 +++++----------- server/src/sys/msg/in_game.rs | 14 ++++---------- server/src/sys/msg/ping.rs | 5 +---- server/src/sys/msg/register.rs | 14 ++++---------- 5 files changed, 19 insertions(+), 46 deletions(-) diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index 8444886a62..4c344a5007 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -128,7 +128,7 @@ impl Sys { /// This system will handle new messages from clients pub struct Sys; impl<'a> System<'a> for Sys { - #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, Read<'a, EventBus>, @@ -144,9 +144,6 @@ impl<'a> System<'a> for Sys { ReadExpect<'a, AliasValidator>, ); - #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 - #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 - #[allow(clippy::single_match)] // TODO: Pending review in #587 fn run( &mut self, ( @@ -196,19 +193,16 @@ impl<'a> System<'a> for Sys { ) }); - match res { - Ok(1_u64..=u64::MAX) => { - // Update client ping. - client.last_ping = time.0 - }, - _ => (/*handled by ping*/), + if let Ok(1_u64..=u64::MAX) = res { + // Update client ping. + client.last_ping = time.0 } } // Handle new chat messages. for (entity, msg) in new_chat_msgs { // Handle chat commands. - if msg.message.starts_with("/") { + if msg.message.starts_with('/') { if let (Some(entity), true) = (entity, msg.message.len() > 1) { let argv = String::from(&msg.message[1..]); server_emitter.emit(ServerEvent::ChatCmd(entity, argv)); diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs index 19a2fb2dd6..a583a76805 100644 --- a/server/src/sys/msg/general.rs +++ b/server/src/sys/msg/general.rs @@ -72,7 +72,7 @@ impl Sys { /// This system will handle new messages from clients pub struct Sys; impl<'a> System<'a> for Sys { - #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, Read<'a, EventBus>, @@ -85,9 +85,6 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, GeneralStream>, ); - #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 - #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 - #[allow(clippy::single_match)] // TODO: Pending review in #587 fn run( &mut self, ( @@ -125,19 +122,16 @@ impl<'a> System<'a> for Sys { ) }); - match res { - Ok(1_u64..=u64::MAX) => { - // Update client ping. - client.last_ping = time.0 - }, - _ => (/*handled by ping*/), + if let Ok(1_u64..=u64::MAX) = res { + // Update client ping. + client.last_ping = time.0 } } // Handle new chat messages. for (entity, msg) in new_chat_msgs { // Handle chat commands. - if msg.message.starts_with("/") { + if msg.message.starts_with('/') { if let (Some(entity), true) = (entity, msg.message.len() > 1) { let argv = String::from(&msg.message[1..]); server_emitter.emit(ServerEvent::ChatCmd(entity, argv)); diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index ed3356d38a..0b8ffa8e46 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -177,7 +177,7 @@ impl Sys { /// This system will handle new messages from clients pub struct Sys; impl<'a> System<'a> for Sys { - #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, Read<'a, EventBus>, @@ -199,9 +199,6 @@ impl<'a> System<'a> for Sys { Read<'a, Settings>, ); - #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 - #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 - #[allow(clippy::single_match)] // TODO: Pending review in #587 fn run( &mut self, ( @@ -255,12 +252,9 @@ impl<'a> System<'a> for Sys { ) }); - match res { - Ok(1_u64..=u64::MAX) => { - // Update client ping. - client.last_ping = time.0 - }, - _ => (/*handled by ping*/), + if let Ok(1_u64..=u64::MAX) = res { + // Update client ping. + client.last_ping = time.0 } } diff --git a/server/src/sys/msg/ping.rs b/server/src/sys/msg/ping.rs index b5b93bdc66..68be90ffc0 100644 --- a/server/src/sys/msg/ping.rs +++ b/server/src/sys/msg/ping.rs @@ -30,7 +30,7 @@ impl Sys { /// This system will handle new messages from clients pub struct Sys; impl<'a> System<'a> for Sys { - #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, Read<'a, EventBus>, @@ -42,9 +42,6 @@ impl<'a> System<'a> for Sys { Read<'a, Settings>, ); - #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 - #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 - #[allow(clippy::single_match)] // TODO: Pending review in #587 fn run( &mut self, ( diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs index 437243ab52..f08541990d 100644 --- a/server/src/sys/msg/register.rs +++ b/server/src/sys/msg/register.rs @@ -90,7 +90,7 @@ impl Sys { /// This system will handle new messages from clients pub struct Sys; impl<'a> System<'a> for Sys { - #[allow(clippy::type_complexity)] // TODO: Pending review in #587 + #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, Read<'a, Time>, @@ -107,9 +107,6 @@ impl<'a> System<'a> for Sys { ReadExpect<'a, EditableSettings>, ); - #[allow(clippy::match_ref_pats)] // TODO: Pending review in #587 - #[allow(clippy::single_char_pattern)] // TODO: Pending review in #587 - #[allow(clippy::single_match)] // TODO: Pending review in #587 fn run( &mut self, ( @@ -174,12 +171,9 @@ impl<'a> System<'a> for Sys { ) }); - match res { - Ok(1_u64..=u64::MAX) => { - // Update client ping. - client.last_ping = time.0 - }, - _ => (/*handled by ping*/), + if let Ok(1_u64..=u64::MAX) = res { + // Update client ping. + client.last_ping = time.0 } } From 9ba19a1cd95d2294b25a8b705883f844a98e7baa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 19 Oct 2020 01:53:19 +0200 Subject: [PATCH 05/10] implement lazy_msg which only serialize + compress AT MAX ONCE if the same msg is send to multiple participants --- server/src/state_ext.rs | 16 ++++++++++++++-- server/src/streams.rs | 10 +++++++++- server/src/sys/entity_sync.rs | 32 ++++++++++++++++++++++++++------ server/src/sys/msg/register.rs | 22 ++++++++++++++-------- server/src/sys/terrain_sync.rs | 33 ++++++++++++++++++++++++--------- 5 files changed, 87 insertions(+), 26 deletions(-) diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index d7cb4a3a57..afc8a88f0c 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -390,6 +390,7 @@ impl StateExt for State { /// Sends the message to all connected clients fn notify_registered_clients(&self, msg: ServerGeneral) { + let mut lazy_msg = None; for (general_stream, _) in ( &mut self.ecs().write_storage::(), &self.ecs().read_storage::(), @@ -397,12 +398,18 @@ impl StateExt for State { .join() .filter(|(_, c)| c.registered) { - general_stream.send_unchecked(msg.clone()); + if lazy_msg.is_none() { + lazy_msg = Some(general_stream.prepare(&msg)); + } + lazy_msg + .as_ref() + .map(|ref msg| general_stream.0.send_raw(&msg)); } } /// Sends the message to all clients playing in game fn notify_in_game_clients(&self, msg: ServerGeneral) { + let mut lazy_msg = None; for (general_stream, _) in ( &mut self.ecs().write_storage::(), &self.ecs().read_storage::(), @@ -410,7 +417,12 @@ impl StateExt for State { .join() .filter(|(_, c)| c.in_game.is_some()) { - general_stream.send_unchecked(msg.clone()); + if lazy_msg.is_none() { + lazy_msg = Some(general_stream.prepare(&msg)); + } + lazy_msg + .as_ref() + .map(|ref msg| general_stream.0.send_raw(&msg)); } } diff --git a/server/src/streams.rs b/server/src/streams.rs index 4fa4bfd4ad..7c279902d9 100644 --- a/server/src/streams.rs +++ b/server/src/streams.rs @@ -1,6 +1,6 @@ use common::msg::{ClientGeneral, ClientRegister, PingMsg, ServerGeneral, ServerRegisterAnswer}; -use network::{Stream, StreamError}; +use network::{Message, Stream, StreamError}; use serde::{de::DeserializeOwned, Serialize}; use specs::Component; @@ -22,6 +22,14 @@ pub(crate) trait GetStream { } fn send_unchecked(&mut self, msg: Self::SendMsg) { let _ = self.send(msg); } + + fn prepare(&mut self, msg: &Self::SendMsg) -> Message { + if Self::verify(&msg) { + Message::serialize(&msg, &self.get_mut()) + } else { + unreachable!("sending this msg isn't allowed! got: {:?}", msg) + } + } } // Streams diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index 96c61d0ba4..ec1e6aa63a 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -197,13 +197,28 @@ impl<'a> System<'a> for Sys { .take_deleted_in_region(key) .unwrap_or_default(), ); - let entity_sync_msg = ServerGeneral::EntitySync(entity_sync_package); - let comp_sync_msg = ServerGeneral::CompSync(comp_sync_package); + let mut entity_sync_package = Some(entity_sync_package); + let mut comp_sync_package = Some(comp_sync_package); + let mut entity_sync_lazymsg = None; + let mut comp_sync_lazymsg = None; subscribers .iter_mut() .for_each(move |(_, _, _, _, _, general_stream)| { - general_stream.send_unchecked(entity_sync_msg.clone()); - general_stream.send_unchecked(comp_sync_msg.clone()); + if entity_sync_lazymsg.is_none() { + entity_sync_lazymsg = Some(general_stream.prepare( + &ServerGeneral::EntitySync(entity_sync_package.take().unwrap()), + )); + comp_sync_lazymsg = + Some(general_stream.prepare(&ServerGeneral::CompSync( + comp_sync_package.take().unwrap(), + ))); + } + entity_sync_lazymsg + .as_ref() + .map(|msg| general_stream.0.send_raw(&msg)); + comp_sync_lazymsg + .as_ref() + .map(|msg| general_stream.0.send_raw(&msg)); }); let mut send_general = |msg: ServerGeneral, @@ -381,9 +396,14 @@ impl<'a> System<'a> for Sys { // Sync resources // TODO: doesn't really belong in this system (rename system or create another // system?) - let tof_msg = ServerGeneral::TimeOfDay(*time_of_day); + let mut tof_lazymsg = None; for general_stream in (&mut general_streams).join() { - general_stream.send_unchecked(tof_msg.clone()); + if tof_lazymsg.is_none() { + tof_lazymsg = Some(general_stream.prepare(&ServerGeneral::TimeOfDay(*time_of_day))); + } + tof_lazymsg + .as_ref() + .map(|msg| general_stream.0.send_raw(&msg)); } timer.end(); diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs index f08541990d..6f3fc31641 100644 --- a/server/src/sys/msg/register.rs +++ b/server/src/sys/msg/register.rs @@ -181,18 +181,24 @@ impl<'a> System<'a> for Sys { // Tell all clients to add them to the player list. for entity in new_players { if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) { - let msg = - ServerGeneral::PlayerListUpdate(PlayerListUpdate::Add(*uid, PlayerInfo { - player_alias: player.alias.clone(), - is_online: true, - is_admin: admins.get(entity).is_some(), - character: None, // new players will be on character select. - })); + let mut lazy_msg = None; for (_, general_stream) in (&mut clients, &mut general_streams) .join() .filter(|(c, _)| c.registered) { - let _ = general_stream.send(msg.clone()); + if lazy_msg.is_none() { + lazy_msg = Some(general_stream.prepare(&ServerGeneral::PlayerListUpdate( + PlayerListUpdate::Add(*uid, PlayerInfo { + player_alias: player.alias.clone(), + is_online: true, + is_admin: admins.get(entity).is_some(), + character: None, // new players will be on character select. + }), + ))); + } + lazy_msg + .as_ref() + .map(|ref msg| general_stream.0.send_raw(&msg)); } } } diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index fc2f657518..0942261f5a 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -32,6 +32,8 @@ impl<'a> System<'a> for Sys { // Sync changed chunks 'chunk: for chunk_key in &terrain_changes.modified_chunks { + let mut lazy_msg = None; + for (player, pos, in_game_stream) in (&players, &positions, &mut in_game_streams).join() { if player @@ -39,23 +41,36 @@ impl<'a> System<'a> for Sys { .map(|vd| super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, vd)) .unwrap_or(false) { - let _ = in_game_stream.send(ServerGeneral::TerrainChunkUpdate { - key: *chunk_key, - chunk: Ok(Box::new(match terrain.get_key(*chunk_key) { - Some(chunk) => chunk.clone(), - None => break 'chunk, - })), - }); + if lazy_msg.is_none() { + lazy_msg = + Some(in_game_stream.prepare(&ServerGeneral::TerrainChunkUpdate { + key: *chunk_key, + chunk: Ok(Box::new(match terrain.get_key(*chunk_key) { + Some(chunk) => chunk.clone(), + None => break 'chunk, + })), + })); + } + lazy_msg + .as_ref() + .map(|ref msg| in_game_stream.0.send_raw(&msg)); } } } // TODO: Don't send all changed blocks to all clients // Sync changed blocks - let msg = ServerGeneral::TerrainBlockUpdates(terrain_changes.modified_blocks.clone()); + let mut lazy_msg = None; for (player, in_game_stream) in (&players, &mut in_game_streams).join() { + if lazy_msg.is_none() { + lazy_msg = Some(in_game_stream.prepare(&ServerGeneral::TerrainBlockUpdates( + terrain_changes.modified_blocks.clone(), + ))); + } if player.view_distance.is_some() { - in_game_stream.send_unchecked(msg.clone()); + lazy_msg + .as_ref() + .map(|ref msg| in_game_stream.0.send_raw(&msg)); } } From 37d08e93ca9c6f872e1ce0552a77b232718fbb42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Wed, 21 Oct 2020 12:23:34 +0200 Subject: [PATCH 06/10] review: - fix wording in error msg - find better name for structs - unify errors and cleanup code with `(|| {foo?; Some(())})()` pattern - fix the negative PlayersOnline, it was caused by having a gracefull shutdown AND a timeout error. we now unregister the client when he issues TERMINATE --- server/src/cmd.rs | 2 +- server/src/connection_handler.rs | 14 +- server/src/events/entity_manipulation.rs | 2 +- server/src/events/group_manip.rs | 42 ++--- server/src/events/interaction.rs | 200 ++++++++++------------- server/src/events/player.rs | 58 +++---- server/src/state_ext.rs | 16 +- server/src/streams.rs | 2 +- server/src/sys/entity_sync.rs | 12 +- server/src/sys/invite_timeout.rs | 2 +- server/src/sys/msg/general.rs | 2 + server/src/sys/msg/mod.rs | 5 +- server/src/sys/msg/ping.rs | 33 ++-- server/src/sys/subscription.rs | 8 +- server/src/sys/terrain.rs | 4 +- server/src/sys/waypoint.rs | 2 +- 16 files changed, 193 insertions(+), 211 deletions(-) diff --git a/server/src/cmd.rs b/server/src/cmd.rs index dfa5c9b064..204d4f399d 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -673,7 +673,7 @@ fn handle_spawn( .map(|g| (g, s)) }) .map(|(g, s)| { - s.send_unchecked(ServerGeneral::GroupUpdate(g)); + s.send_fallible(ServerGeneral::GroupUpdate(g)); }); }, ); diff --git a/server/src/connection_handler.rs b/server/src/connection_handler.rs index 6a790f367a..847d6d8693 100644 --- a/server/src/connection_handler.rs +++ b/server/src/connection_handler.rs @@ -16,7 +16,7 @@ pub(crate) struct ServerInfoPacket { pub time: f64, } -pub(crate) struct ClientPackage { +pub(crate) struct IncomingClient { pub client: Client, pub general: GeneralStream, pub ping: PingStream, @@ -28,7 +28,7 @@ pub(crate) struct ClientPackage { pub(crate) struct ConnectionHandler { _network: Arc, thread_handle: Option>, - pub client_receiver: Receiver, + pub client_receiver: Receiver, pub info_requester_receiver: Receiver>, stop_sender: Option>, } @@ -43,7 +43,7 @@ impl ConnectionHandler { let network_clone = Arc::clone(&network); let (stop_sender, stop_receiver) = oneshot::channel(); - let (client_sender, client_receiver) = unbounded::(); + let (client_sender, client_receiver) = unbounded::(); let (info_requester_sender, info_requester_receiver) = bounded::>(1); @@ -67,7 +67,7 @@ impl ConnectionHandler { async fn work( network: Arc, - client_sender: Sender, + client_sender: Sender, info_requester_sender: Sender>, stop_receiver: oneshot::Receiver<()>, ) { @@ -104,7 +104,7 @@ impl ConnectionHandler { async fn init_participant( participant: Participant, - client_sender: Sender, + client_sender: Sender, info_requester_sender: Sender>, ) -> Result<(), Box> { debug!("New Participant connected to the server"); @@ -130,7 +130,7 @@ impl ConnectionHandler { t = register_stream.recv::().fuse() => Some(t), ) { None => { - debug!("slow client connection detected, dropping it"); + debug!("Timeout for incoming client elapsed, aborting connection"); return Ok(()); }, Some(client_type) => client_type?, @@ -145,7 +145,7 @@ impl ConnectionHandler { login_msg_sent: false, }; - let package = ClientPackage { + let package = IncomingClient { client, general: GeneralStream(general_stream), ping: PingStream(ping_stream), diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 39bb1c39e9..c8896e1f8f 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -45,7 +45,7 @@ pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3) } let mut in_game_streams = state.ecs().write_storage::(); if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - in_game_stream.send_unchecked(ServerGeneral::Knockback(impulse)); + in_game_stream.send_fallible(ServerGeneral::Knockback(impulse)); } } diff --git a/server/src/events/group_manip.rs b/server/src/events/group_manip.rs index 17729c436b..2c750f86bb 100644 --- a/server/src/events/group_manip.rs +++ b/server/src/events/group_manip.rs @@ -34,7 +34,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani None => { // Inform of failure if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta .server_msg("Invite failed, target does not exist.".to_owned()), ); @@ -66,7 +66,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if already_in_same_group { // Inform of failure if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked(ChatType::Meta.server_msg( + general_stream.send_fallible(ChatType::Meta.server_msg( "Invite failed, can't invite someone already in your group".to_owned(), )); } @@ -96,7 +96,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if group_size_limit_reached { // Inform inviter that they have reached the group size limit if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta.server_msg( "Invite failed, pending invites plus current group size have reached \ the group size limit" @@ -113,7 +113,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if invites.contains(invitee) { // Inform inviter that there is already an invite if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta .server_msg("This player already has a pending invite.".to_owned()), ); @@ -160,7 +160,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani (in_game_streams.get_mut(invitee), uids.get(entity).copied()) { if send_invite() { - in_game_stream.send_unchecked(ServerGeneral::GroupInvite { + in_game_stream.send_fallible(ServerGeneral::GroupInvite { inviter, timeout: PRESENTED_INVITE_TIMEOUT_DUR, }); @@ -168,7 +168,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani } else if agents.contains(invitee) { send_invite(); } else if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta.server_msg("Can't invite, not a player or npc".to_owned()), ); } @@ -176,7 +176,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Notify inviter that the invite is pending if invite_sent { if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - in_game_stream.send_unchecked(ServerGeneral::InvitePending(uid)); + in_game_stream.send_fallible(ServerGeneral::InvitePending(uid)); } } }, @@ -201,7 +201,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if let (Some(in_game_stream), Some(target)) = (in_game_streams.get_mut(inviter), uids.get(entity).copied()) { - in_game_stream.send_unchecked(ServerGeneral::InviteComplete { + in_game_stream.send_fallible(ServerGeneral::InviteComplete { target, answer: InviteAnswer::Accepted, }); @@ -249,7 +249,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if let (Some(in_game_stream), Some(target)) = (in_game_streams.get_mut(inviter), uids.get(entity).copied()) { - in_game_stream.send_unchecked(ServerGeneral::InviteComplete { + in_game_stream.send_fallible(ServerGeneral::InviteComplete { target, answer: InviteAnswer::Declined, }); @@ -288,7 +288,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani None => { // Inform of failure if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta .server_msg("Kick failed, target does not exist.".to_owned()), ); @@ -301,7 +301,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target).map_or(true, |u| u != owner)) { if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta.server_msg("Kick failed, you can't kick pets.".to_owned()), ); } @@ -310,7 +310,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Can't kick yourself if uids.get(entity).map_or(false, |u| *u == uid) { if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta .server_msg("Kick failed, you can't kick yourself.".to_owned()), ); @@ -348,7 +348,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Tell them the have been kicked if let Some(general_stream) = general_streams.get_mut(target) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta .server_msg("You were removed from the group.".to_owned()), ); @@ -356,13 +356,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Tell kicker that they were succesful if let Some(general_stream) = general_streams.get_mut(entity) { general_stream - .send_unchecked(ChatType::Meta.server_msg("Player kicked.".to_owned())); + .send_fallible(ChatType::Meta.server_msg("Player kicked.".to_owned())); } }, Some(_) => { // Inform kicker that they are not the leader if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked(ChatType::Meta.server_msg( + general_stream.send_fallible(ChatType::Meta.server_msg( "Kick failed: You are not the leader of the target's group.".to_owned(), )); } @@ -370,7 +370,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani None => { // Inform kicker that the target is not in a group if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta.server_msg( "Kick failed: Your target is not in a group.".to_owned(), ), @@ -387,7 +387,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani None => { // Inform of failure if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked(ChatType::Meta.server_msg( + general_stream.send_fallible(ChatType::Meta.server_msg( "Leadership transfer failed, target does not exist".to_owned(), )); } @@ -423,13 +423,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani ); // Tell them they are the leader if let Some(general_stream) = general_streams.get_mut(target) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta.server_msg("You are the group leader now.".to_owned()), ); } // Tell the old leader that the transfer was succesful if let Some(general_stream) = general_streams.get_mut(target) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta .server_msg("You are no longer the group leader.".to_owned()), ); @@ -439,7 +439,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Inform transferer that they are not the leader let mut general_streams = state.ecs().write_storage::(); if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked( + general_stream.send_fallible( ChatType::Meta.server_msg( "Transfer failed: You are not the leader of the target's group." .to_owned(), @@ -451,7 +451,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Inform transferer that the target is not in a group let mut general_streams = state.ecs().write_storage::(); if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_unchecked(ChatType::Meta.server_msg( + general_stream.send_fallible(ChatType::Meta.server_msg( "Transfer failed: Your target is not in a group.".to_owned(), )); } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index cd1da4e31b..79a9073309 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -121,124 +121,96 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { return; } - // You can't possess other players let mut clients = ecs.write_storage::(); let mut general_streams = ecs.write_storage::(); - let mut ping_streams = ecs.write_storage::(); - let mut register_streams = ecs.write_storage::(); - let mut character_screen_streams = ecs.write_storage::(); - let mut in_game_streams = ecs.write_storage::(); - if clients.get_mut(possesse).is_none() { - let client = match clients.remove(possessor) { - Some(c) => c, - None => return, - }; - let mut general_stream = match general_streams.remove(possessor) { - Some(c) => c, - None => return, - }; - let ping_stream = match ping_streams.remove(possessor) { - Some(c) => c, - None => return, - }; - let register_stream = match register_streams.remove(possessor) { - Some(c) => c, - None => return, - }; - let character_screen_stream = match character_screen_streams.remove(possessor) { - Some(c) => c, - None => return, - }; - let in_game_stream = match in_game_streams.remove(possessor) { - Some(c) => c, - None => return, - }; - general_stream.send_unchecked(ServerGeneral::SetPlayerEntity(possesse_uid)); - let err_fn = |c, e: Option| { - e.map(|e| error!(?e, "Error inserting {} component during possession", c)); - }; - err_fn("client", clients.insert(possesse, client).err()); - err_fn( - "general_streams", - general_streams.insert(possesse, general_stream).err(), - ); - err_fn( - "ping_streams", - ping_streams.insert(possesse, ping_stream).err(), - ); - err_fn( - "register_streams", - register_streams.insert(possesse, register_stream).err(), - ); - err_fn( - "character_screen_streams", - character_screen_streams - .insert(possesse, character_screen_stream) - .err(), - ); - err_fn( - "in_game_streams", - in_game_streams.insert(possesse, in_game_stream).err(), - ); - // Put possess item into loadout - let mut loadouts = ecs.write_storage::(); - let loadout = loadouts - .entry(possesse) - .expect("Could not read loadouts component while possessing") - .or_insert(comp::Loadout::default()); - - let item = comp::Item::new_from_asset_expect("common.items.debug.possess"); - if let item::ItemKind::Tool(tool) = item.kind() { - let mut abilities = tool.get_abilities(); - let mut ability_drain = abilities.drain(..); - let debug_item = comp::ItemConfig { - item, - ability1: ability_drain.next(), - ability2: ability_drain.next(), - ability3: ability_drain.next(), - block_ability: None, - dodge_ability: None, - }; - std::mem::swap(&mut loadout.active_item, &mut loadout.second_item); - loadout.active_item = Some(debug_item); - } - - // Move player component - { - let mut players = ecs.write_storage::(); - if let Some(player) = players.remove(possessor) { - err_fn("player", players.insert(possesse, player).err()); - } - } - // Transfer region subscription - { - let mut subscriptions = ecs.write_storage::(); - if let Some(s) = subscriptions.remove(possessor) { - err_fn("subscription", subscriptions.insert(possesse, s).err()); - } - } - // Remove will of the entity - ecs.write_storage::().remove(possesse); - // Reset controller of former shell - ecs.write_storage::() - .get_mut(possessor) - .map(|c| c.reset()); - // Transfer admin powers - { - let mut admins = ecs.write_storage::(); - if let Some(admin) = admins.remove(possessor) { - err_fn("admin", admins.insert(possesse, admin).err()); - } - } - // Transfer waypoint - { - let mut waypoints = ecs.write_storage::(); - if let Some(waypoint) = waypoints.remove(possessor) { - err_fn("waypoints", waypoints.insert(possesse, waypoint).err()); - } - } + if clients.get_mut(possesse).is_some() { + error!("can't possess other players"); + return; } + + match (|| -> Option> { + let mut ping_streams = ecs.write_storage::(); + let mut register_streams = ecs.write_storage::(); + let mut character_screen_streams = ecs.write_storage::(); + let mut in_game_streams = ecs.write_storage::(); + + let c = clients.remove(possessor)?; + clients.insert(possesse, c).ok()?; + let s = general_streams.remove(possessor)?; + general_streams.insert(possesse, s).ok()?; + let s = ping_streams.remove(possessor)?; + ping_streams.insert(possesse, s).ok()?; + let s = register_streams.remove(possessor)?; + register_streams.insert(possesse, s).ok()?; + let s = character_screen_streams.remove(possessor)?; + character_screen_streams.insert(possesse, s).ok()?; + let s = in_game_streams.remove(possessor)?; + in_game_streams.insert(possesse, s).ok()?; + //optional entities + let mut players = ecs.write_storage::(); + let mut subscriptions = ecs.write_storage::(); + let mut admins = ecs.write_storage::(); + let mut waypoints = ecs.write_storage::(); + players + .remove(possessor) + .map(|p| players.insert(possesse, p).ok()?); + subscriptions + .remove(possessor) + .map(|s| subscriptions.insert(possesse, s).ok()?); + admins + .remove(possessor) + .map(|a| admins.insert(possesse, a).ok()?); + waypoints + .remove(possessor) + .map(|w| waypoints.insert(possesse, w).ok()?); + + Some(Ok(())) + })() { + Some(Ok(())) => (), + Some(Err(e)) => { + error!(?e, ?possesse, "Error inserting component during possession"); + return; + }, + None => { + error!(?possessor, "Error removing component during possession"); + return; + }, + } + + general_streams + .get_mut(possesse) + .map(|s| s.send_fallible(ServerGeneral::SetPlayerEntity(possesse_uid))); + + // Put possess item into loadout + let mut loadouts = ecs.write_storage::(); + let loadout = loadouts + .entry(possesse) + .expect("Could not read loadouts component while possessing") + .or_insert(comp::Loadout::default()); + + let item = comp::Item::new_from_asset_expect("common.items.debug.possess"); + if let item::ItemKind::Tool(tool) = item.kind() { + let mut abilities = tool.get_abilities(); + let mut ability_drain = abilities.drain(..); + let debug_item = comp::ItemConfig { + item, + ability1: ability_drain.next(), + ability2: ability_drain.next(), + ability3: ability_drain.next(), + block_ability: None, + dodge_ability: None, + }; + std::mem::swap(&mut loadout.active_item, &mut loadout.second_item); + loadout.active_item = Some(debug_item); + } + + // Remove will of the entity + ecs.write_storage::().remove(possesse); + // Reset controller of former shell + ecs.write_storage::() + .get_mut(possessor) + .map(|c| c.reset()); } } diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 0d35b28d07..0e79497073 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -28,46 +28,40 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { // components Easier than checking and removing all other known components // Note: If other `ServerEvent`s are referring to this entity they will be // disrupted - let maybe_client = state.ecs().write_storage::().remove(entity); - let maybe_uid = state.read_component_copied::(entity); - let maybe_player = state.ecs().write_storage::().remove(entity); - let maybe_admin = state.ecs().write_storage::().remove(entity); - let maybe_general_stream = state.ecs().write_storage::().remove(entity); - let maybe_ping_stream = state.ecs().write_storage::().remove(entity); - let maybe_register_stream = state.ecs().write_storage::().remove(entity); - let maybe_character_screen_stream = state - .ecs() - .write_storage::() - .remove(entity); - let maybe_in_game_stream = state.ecs().write_storage::().remove(entity); + let maybe_admin = state.ecs().write_storage::().remove(entity); let maybe_group = state .ecs() .write_storage::() .get(entity) .cloned(); - if let ( - Some(mut client), - Some(uid), - Some(player), - Some(general_stream), - Some(ping_stream), - Some(register_stream), - Some(character_screen_stream), - Some(mut in_game_stream), - ) = ( - maybe_client, - maybe_uid, - maybe_player, - maybe_general_stream, - maybe_ping_stream, - maybe_register_stream, - maybe_character_screen_stream, - maybe_in_game_stream, - ) { + + if let Some(( + mut client, + uid, + player, + general_stream, + ping_stream, + register_stream, + character_screen_stream, + mut in_game_stream, + )) = (|| { + let ecs = state.ecs(); + Some(( + ecs.write_storage::().remove(entity)?, + ecs.write_storage::().remove(entity)?, + ecs.write_storage::().remove(entity)?, + ecs.write_storage::().remove(entity)?, + ecs.write_storage::().remove(entity)?, + ecs.write_storage::().remove(entity)?, + ecs.write_storage::() + .remove(entity)?, + ecs.write_storage::().remove(entity)?, + )) + })() { // Tell client its request was successful client.in_game = None; - in_game_stream.send_unchecked(ServerGeneral::ExitInGameSuccess); + in_game_stream.send_fallible(ServerGeneral::ExitInGameSuccess); let entity_builder = state .ecs_mut() diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index afc8a88f0c..7cd3082955 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -232,7 +232,7 @@ impl StateExt for State { .get_mut(entity) { client.in_game = Some(ClientInGame::Character); - character_screen_stream.send_unchecked(ServerGeneral::CharacterSuccess); + character_screen_stream.send_fallible(ServerGeneral::CharacterSuccess); } } } @@ -298,7 +298,7 @@ impl StateExt for State { .join() { if uid != u { - general_stream.send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -310,7 +310,7 @@ impl StateExt for State { .join() { if uid == u || uid == t { - general_stream.send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -325,7 +325,7 @@ impl StateExt for State { { if is_within(comp::ChatMsg::SAY_DISTANCE, pos, speaker_pos) { general_stream - .send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); + .send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -340,7 +340,7 @@ impl StateExt for State { { if is_within(comp::ChatMsg::REGION_DISTANCE, pos, speaker_pos) { general_stream - .send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); + .send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -355,7 +355,7 @@ impl StateExt for State { { if is_within(comp::ChatMsg::NPC_DISTANCE, pos, speaker_pos) { general_stream - .send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); + .send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -369,7 +369,7 @@ impl StateExt for State { .join() { if s == &faction.0 { - general_stream.send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -381,7 +381,7 @@ impl StateExt for State { .join() { if g == group { - general_stream.send_unchecked(ServerGeneral::ChatMsg(resolved_msg.clone())); + general_stream.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, diff --git a/server/src/streams.rs b/server/src/streams.rs index 7c279902d9..d1dfebfd86 100644 --- a/server/src/streams.rs +++ b/server/src/streams.rs @@ -21,7 +21,7 @@ pub(crate) trait GetStream { } } - fn send_unchecked(&mut self, msg: Self::SendMsg) { let _ = self.send(msg); } + fn send_fallible(&mut self, msg: Self::SendMsg) { let _ = self.send(msg); } fn prepare(&mut self, msg: &Self::SendMsg) -> Message { if Self::verify(&msg) { diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index ec1e6aa63a..7431f979fe 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -166,7 +166,7 @@ impl<'a> System<'a> for Sys { // Client doesn't need to know about itself && *client_entity != entity { - general_stream.send_unchecked(create_msg.clone()); + general_stream.send_fallible(create_msg.clone()); } } } @@ -180,7 +180,7 @@ impl<'a> System<'a> for Sys { .map(|key| !regions.contains(key)) .unwrap_or(true) { - general_stream.send_unchecked(ServerGeneral::DeleteEntity(uid)); + general_stream.send_fallible(ServerGeneral::DeleteEntity(uid)); } } } @@ -252,7 +252,7 @@ impl<'a> System<'a> for Sys { true // Closer than 100 blocks } } { - general_stream.send_unchecked(msg.clone()); + general_stream.send_fallible(msg.clone()); } } }; @@ -350,7 +350,7 @@ impl<'a> System<'a> for Sys { }) { for uid in &deleted { - general_stream.send_unchecked(ServerGeneral::DeleteEntity(Uid(*uid))); + general_stream.send_fallible(ServerGeneral::DeleteEntity(Uid(*uid))); } } } @@ -361,7 +361,7 @@ impl<'a> System<'a> for Sys { for (inventory, update, in_game_stream) in (&inventories, &inventory_updates, &mut in_game_streams).join() { - in_game_stream.send_unchecked(ServerGeneral::InventoryUpdate( + in_game_stream.send_fallible(ServerGeneral::InventoryUpdate( inventory.clone(), update.event(), )); @@ -384,7 +384,7 @@ impl<'a> System<'a> for Sys { .cloned() .collect::>(); if !outcomes.is_empty() { - in_game_stream.send_unchecked(ServerGeneral::Outcomes(outcomes)); + in_game_stream.send_fallible(ServerGeneral::Outcomes(outcomes)); } } outcomes.clear(); diff --git a/server/src/sys/invite_timeout.rs b/server/src/sys/invite_timeout.rs index fdb1d392f9..ea913a1a1c 100644 --- a/server/src/sys/invite_timeout.rs +++ b/server/src/sys/invite_timeout.rs @@ -55,7 +55,7 @@ impl<'a> System<'a> for Sys { in_game_streams.get_mut(*inviter), uids.get(invitee).copied(), ) { - in_game_stream.send_unchecked(ServerGeneral::InviteComplete { + in_game_stream.send_fallible(ServerGeneral::InviteComplete { target, answer: InviteAnswer::TimedOut, }); diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs index a583a76805..9038dac57f 100644 --- a/server/src/sys/msg/general.rs +++ b/server/src/sys/msg/general.rs @@ -61,6 +61,8 @@ impl Sys { .clients_disconnected .with_label_values(&["gracefully"]) .inc(); + client.registered = false; + client.in_game = None; server_emitter.emit(ServerEvent::ClientDisconnect(entity)); }, _ => unreachable!("not a client_general msg"), diff --git a/server/src/sys/msg/mod.rs b/server/src/sys/msg/mod.rs index a95ef02a94..54aa60a9d4 100644 --- a/server/src/sys/msg/mod.rs +++ b/server/src/sys/msg/mod.rs @@ -8,7 +8,10 @@ use crate::streams::GetStream; /// handles all send msg and calls a handle fn /// Aborts when a error occurred returns cnt of successful msg otherwise -pub(crate) fn try_recv_all(stream: &mut T, mut f: F) -> Result +pub(in crate::sys::msg) fn try_recv_all( + stream: &mut T, + mut f: F, +) -> Result where T: GetStream, F: FnMut(&mut T, T::RecvMsg) -> Result<(), crate::error::Error>, diff --git a/server/src/sys/msg/ping.rs b/server/src/sys/msg/ping.rs index 68be90ffc0..db0308c20a 100644 --- a/server/src/sys/msg/ping.rs +++ b/server/src/sys/msg/ping.rs @@ -67,11 +67,19 @@ impl<'a> System<'a> for Sys { match res { Err(e) => { - debug!(?entity, ?e, "network error with client, disconnecting"); - player_metrics - .clients_disconnected - .with_label_values(&["network_error"]) - .inc(); + let reg = client.registered; + debug!( + ?entity, + ?e, + ?reg, + "network error with client, disconnecting" + ); + if reg { + player_metrics + .clients_disconnected + .with_label_values(&["network_error"]) + .inc(); + } server_emitter.emit(ServerEvent::ClientDisconnect(entity)); }, Ok(1_u64..=u64::MAX) => { @@ -82,17 +90,20 @@ impl<'a> System<'a> for Sys { if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 // Timeout { - info!(?entity, "timeout error with client, disconnecting"); - player_metrics - .clients_disconnected - .with_label_values(&["timeout"]) - .inc(); + let reg = client.registered; + info!(?entity, ?reg, "timeout error with client, disconnecting"); + if reg { + player_metrics + .clients_disconnected + .with_label_values(&["timeout"]) + .inc(); + } server_emitter.emit(ServerEvent::ClientDisconnect(entity)); } else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 * 0.5 { // Try pinging the client if the timeout is nearing. - ping_stream.send_unchecked(PingMsg::Ping); + ping_stream.send_fallible(PingMsg::Ping); } }, } diff --git a/server/src/sys/subscription.rs b/server/src/sys/subscription.rs index ce7c3567b1..45327314b5 100644 --- a/server/src/sys/subscription.rs +++ b/server/src/sys/subscription.rs @@ -160,7 +160,7 @@ impl<'a> System<'a> for Sys { .unwrap_or(false) { general_stream - .send_unchecked(ServerGeneral::DeleteEntity(uid)); + .send_fallible(ServerGeneral::DeleteEntity(uid)); } } }, @@ -178,7 +178,7 @@ impl<'a> System<'a> for Sys { .iter() .flat_map(|v| v.iter()) { - general_stream.send_unchecked(ServerGeneral::DeleteEntity(Uid(*uid))); + general_stream.send_fallible(ServerGeneral::DeleteEntity(Uid(*uid))); } } @@ -203,7 +203,7 @@ impl<'a> System<'a> for Sys { { // Send message to create entity and tracked components and physics // components - general_stream.send_unchecked(ServerGeneral::CreateEntity( + general_stream.send_fallible(ServerGeneral::CreateEntity( tracked_comps.create_entity_package( entity, Some(*pos), @@ -256,7 +256,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { .join() { // Send message to create entity and tracked components and physics components - general_stream.send_unchecked(ServerGeneral::CreateEntity( + general_stream.send_fallible(ServerGeneral::CreateEntity( tracked_comps.create_entity_package( entity, Some(*pos), diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 07f11a19db..cd2826541c 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -67,7 +67,7 @@ impl<'a> System<'a> for Sys { Ok((chunk, supplement)) => (chunk, supplement), Err(Some(entity)) => { if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - in_game_stream.send_unchecked(ServerGeneral::TerrainChunkUpdate { + in_game_stream.send_fallible(ServerGeneral::TerrainChunkUpdate { key, chunk: Err(()), }); @@ -94,7 +94,7 @@ impl<'a> System<'a> for Sys { .magnitude_squared(); if adjusted_dist_sqr <= view_distance.pow(2) { - in_game_stream.send_unchecked(ServerGeneral::TerrainChunkUpdate { + in_game_stream.send_fallible(ServerGeneral::TerrainChunkUpdate { key, chunk: Ok(Box::new(chunk.clone())), }); diff --git a/server/src/sys/waypoint.rs b/server/src/sys/waypoint.rs index 0d2f7426dd..237391b964 100644 --- a/server/src/sys/waypoint.rs +++ b/server/src/sys/waypoint.rs @@ -51,7 +51,7 @@ impl<'a> System<'a> for Sys { if let Ok(wp_old) = waypoints.insert(entity, Waypoint::new(player_pos.0, *time)) { if wp_old.map_or(true, |w| w.elapsed(*time) > NOTIFY_TIME) { - general_stream.send_unchecked(ServerGeneral::Notification( + general_stream.send_fallible(ServerGeneral::Notification( Notification::WaypointSaved, )); } From 6bb74c9c6f0871d99a6eefc4ef14fcaf5b274142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Fri, 23 Oct 2020 01:45:19 +0200 Subject: [PATCH 07/10] Fix clients are disconnecting GRACEFULLY by removing the Disconnect Request from a client, a client now sends a TERMINATE message directly --- client/src/lib.rs | 21 +++++++++------------ common/src/msg/client.rs | 5 +---- common/src/msg/server.rs | 2 -- server/src/client.rs | 1 + server/src/connection_handler.rs | 1 + server/src/sys/msg/general.rs | 21 ++++----------------- server/src/sys/msg/ping.rs | 19 ++++++------------- voxygen/src/session.rs | 5 ++++- 8 files changed, 26 insertions(+), 49 deletions(-) diff --git a/client/src/lib.rs b/client/src/lib.rs index 85c77c3e6b..d01636e650 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -500,9 +500,9 @@ impl Client { | ClientGeneral::RefundSkill(_) | ClientGeneral::UnlockSkillGroup(_) => &mut self.in_game_stream, //Always possible - ClientGeneral::ChatMsg(_) - | ClientGeneral::Disconnect - | ClientGeneral::Terminate => &mut self.general_stream, + ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => { + &mut self.general_stream + }, }; stream.send(msg) }, @@ -552,9 +552,11 @@ impl Client { } /// Send disconnect message to the server - pub fn request_logout(&mut self) { - debug!("Requesting logout from server"); - self.send_msg(ClientGeneral::Disconnect); + pub fn logout(&mut self) { + debug!("Sending logout from server"); + self.send_msg(ClientGeneral::Terminate); + self.registered = false; + self.in_game = None; } /// Request a state transition to `ClientState::Registered` from an ingame @@ -1133,11 +1135,6 @@ impl Client { match msg { ServerGeneral::Disconnect(reason) => match reason { DisconnectReason::Shutdown => return Err(Error::ServerShutdown), - DisconnectReason::Requested => { - debug!("finally sending ClientMsg::Terminate"); - frontend_events.push(Event::Disconnect); - self.send_msg_err(ClientGeneral::Terminate)?; - }, DisconnectReason::Kicked(reason) => { debug!("sending ClientMsg::Terminate because we got kicked"); frontend_events.push(Event::Kicked(reason)); @@ -1840,7 +1837,7 @@ impl Drop for Client { fn drop(&mut self) { trace!("Dropping client"); if self.registered { - if let Err(e) = self.send_msg_err(ClientGeneral::Disconnect) { + if let Err(e) = self.send_msg_err(ClientGeneral::Terminate) { warn!( ?e, "Error during drop of client, couldn't send disconnect package, is the \ diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index e8b8be3b02..fc68afe140 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -78,7 +78,6 @@ pub enum ClientGeneral { UnlockSkillGroup(SkillGroupType), //Always possible ChatMsg(String), - Disconnect, Terminate, } @@ -119,9 +118,7 @@ impl ClientMsg { c_type == ClientType::Game && in_game.is_some() }, //Always possible - ClientGeneral::ChatMsg(_) - | ClientGeneral::Disconnect - | ClientGeneral::Terminate => true, + ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => true, } }, ClientMsg::Ping(_) => true, diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index eda77ac4ab..3f7fe9beb1 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -163,8 +163,6 @@ pub enum Notification { pub enum DisconnectReason { /// Server shut down Shutdown, - /// Client sent disconnect message - Requested, /// Client was kicked Kicked(String), } diff --git a/server/src/client.rs b/server/src/client.rs index 9462713c78..680a826a3b 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -12,6 +12,7 @@ pub struct Client { pub participant: Option, pub last_ping: f64, pub login_msg_sent: bool, + pub terminate_msg_recv: bool, } impl Component for Client { diff --git a/server/src/connection_handler.rs b/server/src/connection_handler.rs index 847d6d8693..e0d2214388 100644 --- a/server/src/connection_handler.rs +++ b/server/src/connection_handler.rs @@ -143,6 +143,7 @@ impl ConnectionHandler { participant: Some(participant), last_ping: server_data.time, login_msg_sent: false, + terminate_msg_recv: false, }; let package = IncomingClient { diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs index 9038dac57f..6900a63c19 100644 --- a/server/src/sys/msg/general.rs +++ b/server/src/sys/msg/general.rs @@ -1,16 +1,9 @@ use super::super::SysTimer; -use crate::{ - client::Client, - metrics::PlayerMetrics, - streams::{GeneralStream, GetStream}, -}; +use crate::{client::Client, metrics::PlayerMetrics, streams::GeneralStream}; use common::{ comp::{ChatMode, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, - msg::{ - validate_chat_msg, ChatMsgValidationError, ClientGeneral, DisconnectReason, ServerGeneral, - MAX_BYTES_CHAT_MSG, - }, + msg::{validate_chat_msg, ChatMsgValidationError, ClientGeneral, MAX_BYTES_CHAT_MSG}, span, state::Time, sync::Uid, @@ -25,7 +18,6 @@ impl Sys { new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, entity: specs::Entity, client: &mut Client, - general_stream: &mut GeneralStream, player_metrics: &ReadExpect<'_, PlayerMetrics>, uids: &ReadStorage<'_, Uid>, chat_modes: &ReadStorage<'_, ChatMode>, @@ -52,17 +44,13 @@ impl Sys { } } }, - ClientGeneral::Disconnect => { - general_stream.send(ServerGeneral::Disconnect(DisconnectReason::Requested))?; - }, ClientGeneral::Terminate => { debug!(?entity, "Client send message to termitate session"); player_metrics .clients_disconnected .with_label_values(&["gracefully"]) .inc(); - client.registered = false; - client.in_game = None; + client.terminate_msg_recv = true; server_emitter.emit(ServerEvent::ClientDisconnect(entity)); }, _ => unreachable!("not a client_general msg"), @@ -110,13 +98,12 @@ impl<'a> System<'a> for Sys { for (entity, client, general_stream) in (&entities, &mut clients, &mut general_streams).join() { - let res = super::try_recv_all(general_stream, |general_stream, msg| { + let res = super::try_recv_all(general_stream, |_, msg| { Self::handle_general_msg( &mut server_emitter, &mut new_chat_msgs, entity, client, - general_stream, &player_metrics, &uids, &chat_modes, diff --git a/server/src/sys/msg/ping.rs b/server/src/sys/msg/ping.rs index db0308c20a..c2fc8ea915 100644 --- a/server/src/sys/msg/ping.rs +++ b/server/src/sys/msg/ping.rs @@ -67,20 +67,14 @@ impl<'a> System<'a> for Sys { match res { Err(e) => { - let reg = client.registered; - debug!( - ?entity, - ?e, - ?reg, - "network error with client, disconnecting" - ); - if reg { + if !client.terminate_msg_recv { + debug!(?entity, ?e, "network error with client, disconnecting"); player_metrics .clients_disconnected .with_label_values(&["network_error"]) .inc(); + server_emitter.emit(ServerEvent::ClientDisconnect(entity)); } - server_emitter.emit(ServerEvent::ClientDisconnect(entity)); }, Ok(1_u64..=u64::MAX) => { // Update client ping. @@ -90,15 +84,14 @@ impl<'a> System<'a> for Sys { if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 // Timeout { - let reg = client.registered; - info!(?entity, ?reg, "timeout error with client, disconnecting"); - if reg { + if !client.terminate_msg_recv { + info!(?entity, "timeout error with client, disconnecting"); player_metrics .clients_disconnected .with_label_values(&["timeout"]) .inc(); + server_emitter.emit(ServerEvent::ClientDisconnect(entity)); } - server_emitter.emit(ServerEvent::ClientDisconnect(entity)); } else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 * 0.5 { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index f99d8890c4..052f0e5127 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -763,7 +763,10 @@ impl PlayState for SessionState { HudEvent::CharacterSelection => { self.client.borrow_mut().request_remove_character() }, - HudEvent::Logout => self.client.borrow_mut().request_logout(), + HudEvent::Logout => { + self.client.borrow_mut().logout(); + return PlayStateResult::Pop; + }, HudEvent::Quit => { return PlayStateResult::Shutdown; }, From 084b60d7ec84d84ecc9f0aaf30027cc997717697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Sun, 25 Oct 2020 19:05:11 +0100 Subject: [PATCH 08/10] Fix an error that a failed initialization of a client lead to a Kill of the gameserver. Instead we just log a msg now. --- server/src/lib.rs | 119 +++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 54 deletions(-) diff --git a/server/src/lib.rs b/server/src/lib.rs index 9f5eced842..f87a821566 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -467,7 +467,7 @@ impl Server { let before_new_connections = Instant::now(); // 3) Handle inputs from clients - self.handle_new_connections(&mut frontend_events)?; + self.handle_new_connections(&mut frontend_events); let before_message_system = Instant::now(); @@ -820,8 +820,61 @@ impl Server { self.state.cleanup(); } + fn initialize_client( + &mut self, + mut incoming: crate::connection_handler::IncomingClient, + ) -> Result, Error> { + let client = incoming.client; + + if self.settings().max_players <= self.state.ecs().read_storage::().join().count() { + trace!( + ?client.participant, + "to many players, wont allow participant to connect" + ); + incoming.register.0.send(ServerInit::TooManyPlayers)?; + return Ok(None); + } + + let entity = self + .state + .ecs_mut() + .create_entity_synced() + .with(client) + .with(incoming.general) + .with(incoming.ping) + .with(incoming.register) + .with(incoming.character) + .with(incoming.in_game) + .build(); + self.state + .ecs() + .read_resource::() + .clients_connected + .inc(); + // Send client all the tracked components currently attached to its entity as + // well as synced resources (currently only `TimeOfDay`) + debug!("Starting initial sync with client."); + self.state + .ecs() + .write_storage::() + .get_mut(entity) + .unwrap() + .0 + .send(ServerInit::GameSync { + // Send client their entity + entity_package: TrackedComps::fetch(&self.state.ecs()) + .create_entity_package(entity, None, None, None), + time_of_day: *self.state.ecs().read_resource(), + max_group_size: self.settings().max_player_group_size, + client_timeout: self.settings().client_timeout, + world_map: self.map.clone(), + recipe_book: (&*default_recipe_book()).clone(), + })?; + Ok(Some(entity)) + } + /// Handle new client connections. - fn handle_new_connections(&mut self, frontend_events: &mut Vec) -> Result<(), Error> { + fn handle_new_connections(&mut self, frontend_events: &mut Vec) { while let Ok(sender) = self.connection_handler.info_requester_receiver.try_recv() { // can fail, e.g. due to timeout or network prob. trace!("sending info to connection_handler"); @@ -831,60 +884,18 @@ impl Server { }); } - while let Ok(mut package) = self.connection_handler.client_receiver.try_recv() { - let client = package.client; - - if self.settings().max_players - <= self.state.ecs().read_storage::().join().count() - { - trace!( - ?client.participant, - "to many players, wont allow participant to connect" - ); - package.register.0.send(ServerInit::TooManyPlayers)?; - continue; + while let Ok(incoming) = self.connection_handler.client_receiver.try_recv() { + match self.initialize_client(incoming) { + Ok(None) => (), + Ok(Some(entity)) => { + frontend_events.push(Event::ClientConnected { entity }); + debug!("Done initial sync with client."); + }, + Err(e) => { + debug!(?e, "failed initializing a new client"); + }, } - - let entity = self - .state - .ecs_mut() - .create_entity_synced() - .with(client) - .with(package.general) - .with(package.ping) - .with(package.register) - .with(package.character) - .with(package.in_game) - .build(); - self.state - .ecs() - .read_resource::() - .clients_connected - .inc(); - // Send client all the tracked components currently attached to its entity as - // well as synced resources (currently only `TimeOfDay`) - debug!("Starting initial sync with client."); - self.state - .ecs() - .write_storage::() - .get_mut(entity) - .unwrap() - .0 - .send(ServerInit::GameSync { - // Send client their entity - entity_package: TrackedComps::fetch(&self.state.ecs()) - .create_entity_package(entity, None, None, None), - time_of_day: *self.state.ecs().read_resource(), - max_group_size: self.settings().max_player_group_size, - client_timeout: self.settings().client_timeout, - world_map: self.map.clone(), - recipe_book: (&*default_recipe_book()).clone(), - })?; - - frontend_events.push(Event::ClientConnected { entity }); - debug!("Done initial sync with client."); } - Ok(()) } pub fn notify_client(&self, entity: EcsEntity, msg: S) From 00456c8373677fc731ace1e287531f655f7104cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Fri, 30 Oct 2020 17:39:53 +0100 Subject: [PATCH 09/10] extract a Presence Component, which is `server` only and has state of `Player` and `Client`. Presence is only valid for Clients that are in game --- client/src/lib.rs | 36 +++++----- common/src/comp/player.rs | 17 +---- common/src/msg/client.rs | 10 +-- common/src/msg/mod.rs | 5 +- common/src/msg/server.rs | 10 +-- server-cli/src/shutdown_coordinator.rs | 2 +- server/src/client.rs | 31 +++------ server/src/cmd.rs | 14 ++-- server/src/connection_handler.rs | 2 - server/src/events/entity_manipulation.rs | 13 ++-- server/src/events/interaction.rs | 3 +- server/src/events/player.rs | 16 ++--- server/src/lib.rs | 11 +-- server/src/presence.rs | 40 +++++++++++ server/src/state_ext.rs | 51 ++++++-------- server/src/sys/entity_sync.rs | 37 +++++++---- server/src/sys/msg/character_screen.rs | 83 ++++++++++++----------- server/src/sys/msg/general.rs | 17 +++-- server/src/sys/msg/in_game.rs | 85 ++++++++++++------------ server/src/sys/msg/register.rs | 14 ++-- server/src/sys/persistence.rs | 21 +++--- server/src/sys/subscription.rs | 44 +++++------- server/src/sys/terrain.rs | 24 +++---- server/src/sys/terrain_sync.rs | 33 ++++----- voxygen/src/hud/mod.rs | 8 ++- voxygen/src/menu/char_selection/mod.rs | 6 +- voxygen/src/session.rs | 16 +++-- 27 files changed, 328 insertions(+), 321 deletions(-) create mode 100644 server/src/presence.rs diff --git a/client/src/lib.rs b/client/src/lib.rs index d01636e650..46fefef2d2 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -25,9 +25,9 @@ use common::{ }, event::{EventBus, LocalEvent}, msg::{ - validate_chat_msg, ChatMsgValidationError, ClientGeneral, ClientInGame, ClientMsg, - ClientRegister, ClientType, DisconnectReason, InviteAnswer, Notification, PingMsg, - PlayerInfo, PlayerListUpdate, RegisterError, ServerGeneral, ServerInfo, ServerInit, + validate_chat_msg, ChatMsgValidationError, ClientGeneral, ClientMsg, ClientRegister, + ClientType, DisconnectReason, InviteAnswer, Notification, PingMsg, PlayerInfo, + PlayerListUpdate, PresenceKind, RegisterError, ServerGeneral, ServerInfo, ServerInit, ServerRegisterAnswer, MAX_BYTES_CHAT_MSG, }, outcome::Outcome, @@ -71,7 +71,7 @@ pub enum Event { pub struct Client { registered: bool, - in_game: Option, + presence: Option, thread_pool: ThreadPool, pub server_info: ServerInfo, /// Just the "base" layer for LOD; currently includes colors and nothing @@ -98,7 +98,6 @@ pub struct Client { pub world_map: (Arc, Vec2, Vec2), pub player_list: HashMap, pub character_list: CharacterList, - pub active_character_id: Option, recipe_book: RecipeBook, available_recipes: HashSet, @@ -376,7 +375,7 @@ impl Client { Ok(Self { registered: false, - in_game: None, + presence: None, thread_pool, server_info, world_map, @@ -385,7 +384,6 @@ impl Client { lod_horizon, player_list: HashMap::new(), character_list: CharacterList::default(), - active_character_id: None, recipe_book, available_recipes: HashSet::default(), @@ -467,12 +465,12 @@ impl Client { #[cfg(debug_assertions)] { const C_TYPE: ClientType = ClientType::Game; - let verified = msg.verify(C_TYPE, self.registered, self.in_game); + let verified = msg.verify(C_TYPE, self.registered, self.presence); assert!( verified, format!( - "c_type: {:?}, registered: {}, in_game: {:?}, msg: {:?}", - C_TYPE, self.registered, self.in_game, msg + "c_type: {:?}, registered: {}, presence: {:?}, msg: {:?}", + C_TYPE, self.registered, self.presence, msg ) ); } @@ -528,9 +526,7 @@ impl Client { self.send_msg(ClientGeneral::Character(character_id)); //Assume we are in_game unless server tells us otherwise - self.in_game = Some(ClientInGame::Character); - - self.active_character_id = Some(character_id); + self.presence = Some(PresenceKind::Character(character_id)); } /// Load the current players character list @@ -556,7 +552,7 @@ impl Client { debug!("Sending logout from server"); self.send_msg(ClientGeneral::Terminate); self.registered = false; - self.in_game = None; + self.presence = None; } /// Request a state transition to `ClientState::Registered` from an ingame @@ -922,7 +918,7 @@ impl Client { // 1) Handle input from frontend. // Pass character actions from frontend input to the player's entity. - if self.in_game.is_some() { + if self.presence.is_some() { if let Err(e) = self .state .ecs() @@ -1093,7 +1089,7 @@ impl Client { } // 6) Update the server about the player's physics attributes. - if self.in_game.is_some() { + if self.presence.is_some() { if let (Some(pos), Some(vel), Some(ori)) = ( self.state.read_storage().get(self.entity).cloned(), self.state.read_storage().get(self.entity).cloned(), @@ -1375,9 +1371,9 @@ impl Client { }; frontend_events.push(Event::Chat(comp::ChatType::Meta.chat_msg(msg))); }, - // Cleanup for when the client goes back to the `in_game = None` + // Cleanup for when the client goes back to the `presence = None` ServerGeneral::ExitInGameSuccess => { - self.in_game = None; + self.presence = None; self.clean_state(); }, ServerGeneral::InventoryUpdate(mut inventory, event) => { @@ -1438,7 +1434,7 @@ impl Client { }, ServerGeneral::CharacterDataLoadError(error) => { trace!("Handling join error by server"); - self.in_game = None; + self.presence = None; self.clean_state(); self.character_list.error = Some(error); }, @@ -1548,7 +1544,7 @@ impl Client { pub fn uid(&self) -> Option { self.state.read_component_copied(self.entity) } - pub fn in_game(&self) -> Option { self.in_game } + pub fn presence(&self) -> Option { self.presence } pub fn registered(&self) -> bool { self.registered } diff --git a/common/src/comp/player.rs b/common/src/comp/player.rs index 7b12b8741a..d8a5a90307 100644 --- a/common/src/comp/player.rs +++ b/common/src/comp/player.rs @@ -1,4 +1,3 @@ -use crate::character::CharacterId; use authc::Uuid; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage, NullStorage}; @@ -9,25 +8,11 @@ const MAX_ALIAS_LEN: usize = 32; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Player { pub alias: String, - pub character_id: Option, - pub view_distance: Option, uuid: Uuid, } impl Player { - pub fn new( - alias: String, - character_id: Option, - view_distance: Option, - uuid: Uuid, - ) -> Self { - Self { - alias, - character_id, - view_distance, - uuid, - } - } + pub fn new(alias: String, uuid: Uuid) -> Self { Self { alias, uuid } } pub fn is_valid(&self) -> bool { Self::alias_is_valid(&self.alias) } diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index fc68afe140..c9c5bb1fba 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -86,21 +86,21 @@ impl ClientMsg { &self, c_type: ClientType, registered: bool, - in_game: Option, + presence: Option, ) -> bool { match self { ClientMsg::Type(t) => c_type == *t, - ClientMsg::Register(_) => !registered && in_game.is_none(), + ClientMsg::Register(_) => !registered && presence.is_none(), ClientMsg::General(g) => { registered && match g { ClientGeneral::RequestCharacterList | ClientGeneral::CreateCharacter { .. } | ClientGeneral::DeleteCharacter(_) => { - c_type != ClientType::ChatOnly && in_game.is_none() + c_type != ClientType::ChatOnly && presence.is_none() }, ClientGeneral::Character(_) | ClientGeneral::Spectate => { - c_type == ClientType::Game && in_game.is_none() + c_type == ClientType::Game && presence.is_none() }, //Only in game ClientGeneral::ControllerInputs(_) @@ -115,7 +115,7 @@ impl ClientMsg { | ClientGeneral::UnlockSkill(_) | ClientGeneral::RefundSkill(_) | ClientGeneral::UnlockSkillGroup(_) => { - c_type == ClientType::Game && in_game.is_some() + c_type == ClientType::Game && presence.is_some() }, //Always possible ClientGeneral::ChatMsg(_) | ClientGeneral::Terminate => true, diff --git a/common/src/msg/mod.rs b/common/src/msg/mod.rs index c25cb8a8a1..4580088795 100644 --- a/common/src/msg/mod.rs +++ b/common/src/msg/mod.rs @@ -13,12 +13,13 @@ pub use self::{ }, world_msg::WorldMapMsg, }; +use crate::character::CharacterId; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] -pub enum ClientInGame { +pub enum PresenceKind { Spectator, - Character, + Character(CharacterId), } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 3f7fe9beb1..625d07b0e1 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -182,11 +182,11 @@ impl ServerMsg { &self, c_type: ClientType, registered: bool, - in_game: Option, + presence: Option, ) -> bool { match self { ServerMsg::Info(_) | ServerMsg::Init(_) | ServerMsg::RegisterAnswer(_) => { - !registered && in_game.is_none() + !registered && presence.is_none() }, ServerMsg::General(g) => { registered @@ -195,10 +195,10 @@ impl ServerMsg { ServerGeneral::CharacterDataLoadError(_) | ServerGeneral::CharacterListUpdate(_) | ServerGeneral::CharacterActionError(_) => { - c_type != ClientType::ChatOnly && in_game.is_none() + c_type != ClientType::ChatOnly && presence.is_none() }, ServerGeneral::CharacterSuccess => { - c_type == ClientType::Game && in_game.is_none() + c_type == ClientType::Game && presence.is_none() }, //Ingame related ServerGeneral::GroupUpdate(_) @@ -212,7 +212,7 @@ impl ServerMsg { | ServerGeneral::SetViewDistance(_) | ServerGeneral::Outcomes(_) | ServerGeneral::Knockback(_) => { - c_type == ClientType::Game && in_game.is_some() + c_type == ClientType::Game && presence.is_some() }, // Always possible ServerGeneral::PlayerListUpdate(_) diff --git a/server-cli/src/shutdown_coordinator.rs b/server-cli/src/shutdown_coordinator.rs index 59705c2003..fa9be90b25 100644 --- a/server-cli/src/shutdown_coordinator.rs +++ b/server-cli/src/shutdown_coordinator.rs @@ -155,7 +155,7 @@ impl ShutdownCoordinator { /// Logs and sends a message to all connected clients fn send_msg(server: &mut Server, msg: String) { info!("{}", &msg); - server.notify_registered_clients(ChatType::CommandError.server_msg(msg)); + server.notify_players(ChatType::CommandError.server_msg(msg)); } /// Converts a `Duration` into text in the format XsXm for example 1 minute diff --git a/server/src/client.rs b/server/src/client.rs index 680a826a3b..a968194fad 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -1,14 +1,16 @@ -use common::msg::{ClientInGame, ClientType}; -use hashbrown::HashSet; +use common::msg::ClientType; use network::Participant; -use specs::{Component, FlaggedStorage}; +use specs::Component; use specs_idvs::IdvStorage; -use vek::*; +/// Client handles ALL network related information of everything that connects +/// to the server Client DOES NOT handle game states +/// Client DOES NOT handle network information that is only relevant to some +/// "things" connecting to the server (there is currently no such case). First a +/// Client connects to the game, when it registers, it gets the `Player` +/// component, when he enters the game he gets the `InGame` component. pub struct Client { - pub registered: bool, pub client_type: ClientType, - pub in_game: Option, pub participant: Option, pub last_ping: f64, pub login_msg_sent: bool, @@ -16,20 +18,5 @@ pub struct Client { } impl Component for Client { - type Storage = FlaggedStorage>; -} - -// Distance from fuzzy_chunk before snapping to current chunk -pub const CHUNK_FUZZ: u32 = 2; -// Distance out of the range of a region before removing it from subscriptions -pub const REGION_FUZZ: u32 = 16; - -#[derive(Clone, Debug)] -pub struct RegionSubscription { - pub fuzzy_chunk: Vec2, - pub regions: HashSet>, -} - -impl Component for RegionSubscription { - type Storage = FlaggedStorage>; + type Storage = IdvStorage; } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 204d4f399d..edb1cc2fc0 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -510,11 +510,11 @@ fn handle_alias( *uid, player.alias.clone(), )); - server.state.notify_registered_clients(msg); + server.state.notify_players(msg); // Announce alias change if target has a Body. if ecs.read_storage::().get(target).is_some() { - server.state.notify_registered_clients( + server.state.notify_players( ChatType::CommandInfo .server_msg(format!("{} is now known as {}.", old_alias, player.alias)), ); @@ -1214,7 +1214,7 @@ fn handle_adminify( .expect("Player should have uid"), is_admin, )); - server.state.notify_registered_clients(msg); + server.state.notify_players(msg); }, None => { server.notify_client( @@ -1671,11 +1671,9 @@ fn handle_set_level( .read_storage::() .get(player) .expect("Failed to get uid for player"); - server - .state - .notify_registered_clients(ServerGeneral::PlayerListUpdate( - PlayerListUpdate::LevelChange(uid, lvl), - )); + server.state.notify_players(ServerGeneral::PlayerListUpdate( + PlayerListUpdate::LevelChange(uid, lvl), + )); if let Some(stats) = server .state diff --git a/server/src/connection_handler.rs b/server/src/connection_handler.rs index e0d2214388..dfe64a971d 100644 --- a/server/src/connection_handler.rs +++ b/server/src/connection_handler.rs @@ -137,9 +137,7 @@ impl ConnectionHandler { }; let client = Client { - registered: false, client_type, - in_game: None, participant: Some(participant), last_ping: server_data.time, login_msg_sent: false, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index c8896e1f8f..dbbd361f8a 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -200,9 +200,8 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc | HealthSource::Healing { by: _ } | HealthSource::Unknown => KillSource::Other, }; - state.notify_registered_clients( - comp::ChatType::Kill(kill_source, *uid).server_msg("".to_string()), - ); + state + .notify_players(comp::ChatType::Kill(kill_source, *uid).server_msg("".to_string())); } } @@ -668,11 +667,9 @@ pub fn handle_level_up(server: &mut Server, entity: EcsEntity, new_level: u32) { .get(entity) .expect("Failed to fetch uid component for entity."); - server - .state - .notify_registered_clients(ServerGeneral::PlayerListUpdate( - PlayerListUpdate::LevelChange(*uid, new_level), - )); + server.state.notify_players(ServerGeneral::PlayerListUpdate( + PlayerListUpdate::LevelChange(*uid, new_level), + )); } pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::BuffChange) { diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 79a9073309..9d868692d2 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -1,5 +1,6 @@ use crate::{ - client::{Client, RegionSubscription}, + client::Client, + presence::RegionSubscription, streams::{ CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, }, diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 0e79497073..65297e3f60 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -3,6 +3,7 @@ use crate::{ client::Client, login_provider::LoginProvider, persistence, + presence::Presence, state_ext::StateExt, streams::{ CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, @@ -12,7 +13,7 @@ use crate::{ use common::{ comp, comp::{group, Player}, - msg::{PlayerListUpdate, ServerGeneral}, + msg::{PlayerListUpdate, PresenceKind, ServerGeneral}, span, sync::{Uid, UidAllocator}, }; @@ -37,7 +38,7 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { .cloned(); if let Some(( - mut client, + client, uid, player, general_stream, @@ -60,7 +61,6 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { )) })() { // Tell client its request was successful - client.in_game = None; in_game_stream.send_fallible(ServerGeneral::ExitInGameSuccess); let entity_builder = state @@ -163,9 +163,9 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event state.read_storage::().get(entity), state.read_storage::().get(entity), ) { - state.notify_registered_clients(comp::ChatType::Offline(*uid).server_msg("")); + state.notify_players(comp::ChatType::Offline(*uid).server_msg("")); - state.notify_registered_clients(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Remove( + state.notify_players(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Remove( *uid, ))); } @@ -177,8 +177,8 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event } // Sync the player's character data to the database - if let (Some(player), Some(stats), Some(inventory), Some(loadout), updater) = ( - state.read_storage::().get(entity), + if let (Some(presences), Some(stats), Some(inventory), Some(loadout), updater) = ( + state.read_storage::().get(entity), state.read_storage::().get(entity), state.read_storage::().get(entity), state.read_storage::().get(entity), @@ -186,7 +186,7 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event .ecs() .read_resource::(), ) { - if let Some(character_id) = player.character_id { + if let PresenceKind::Character(character_id) = presences.kind { updater.update(character_id, stats, inventory, loadout); } } diff --git a/server/src/lib.rs b/server/src/lib.rs index f87a821566..256f12c0f0 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -17,6 +17,7 @@ pub mod input; pub mod login_provider; pub mod metrics; pub mod persistence; +pub mod presence; pub mod settings; pub mod state_ext; pub mod streams; @@ -35,11 +36,12 @@ pub use crate::{ use crate::{ alias_validator::AliasValidator, chunk_generator::ChunkGenerator, - client::{Client, RegionSubscription}, + client::Client, cmd::ChatCommandExt, connection_handler::ConnectionHandler, data_dir::DataDir, login_provider::LoginProvider, + presence::{Presence, RegionSubscription}, state_ext::StateExt, streams::{ CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, @@ -190,6 +192,7 @@ impl Server { // Server-only components state.ecs_mut().register::(); state.ecs_mut().register::(); + state.ecs_mut().register::(); state.ecs_mut().register::(); state.ecs_mut().register::(); state.ecs_mut().register::(); @@ -971,9 +974,7 @@ impl Server { } } - pub fn notify_registered_clients(&mut self, msg: ServerGeneral) { - self.state.notify_registered_clients(msg); - } + pub fn notify_players(&mut self, msg: ServerGeneral) { self.state.notify_players(msg); } pub fn generate_chunk(&mut self, entity: EcsEntity, key: Vec2) { self.state @@ -1051,7 +1052,7 @@ impl Server { impl Drop for Server { fn drop(&mut self) { self.state - .notify_registered_clients(ServerGeneral::Disconnect(DisconnectReason::Shutdown)); + .notify_players(ServerGeneral::Disconnect(DisconnectReason::Shutdown)); } } diff --git a/server/src/presence.rs b/server/src/presence.rs new file mode 100644 index 0000000000..5cba90dd72 --- /dev/null +++ b/server/src/presence.rs @@ -0,0 +1,40 @@ +use common::msg::PresenceKind; +use hashbrown::HashSet; +use serde::{Deserialize, Serialize}; +use specs::{Component, FlaggedStorage}; +use specs_idvs::IdvStorage; +use vek::*; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Presence { + pub view_distance: u32, + pub kind: PresenceKind, +} + +impl Presence { + pub fn new(view_distance: u32, kind: PresenceKind) -> Self { + Self { + view_distance, + kind, + } + } +} + +impl Component for Presence { + type Storage = FlaggedStorage>; +} + +// Distance from fuzzy_chunk before snapping to current chunk +pub const CHUNK_FUZZ: u32 = 2; +// Distance out of the range of a region before removing it from subscriptions +pub const REGION_FUZZ: u32 = 16; + +#[derive(Clone, Debug)] +pub struct RegionSubscription { + pub fuzzy_chunk: Vec2, + pub regions: HashSet>, +} + +impl Component for RegionSubscription { + type Storage = FlaggedStorage>; +} diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 7cd3082955..71548b2dd8 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -1,6 +1,6 @@ use crate::{ - client::Client, persistence::PersistedComponents, + presence::Presence, streams::{CharacterScreenStream, GeneralStream, GetStream, InGameStream}, sys::sentinel::DeletedEntities, SpawnPoint, @@ -9,7 +9,7 @@ use common::{ character::CharacterId, comp, effect::Effect, - msg::{CharacterInfo, ClientInGame, PlayerListUpdate, ServerGeneral}, + msg::{CharacterInfo, PlayerListUpdate, PresenceKind, ServerGeneral}, state::State, sync::{Uid, UidAllocator, WorldSyncExt}, util::Dir, @@ -63,7 +63,7 @@ pub trait StateExt { fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents); /// Iterates over registered clients and send each `ServerMsg` fn send_chat(&self, msg: comp::UnresolvedChatMsg); - fn notify_registered_clients(&self, msg: ServerGeneral); + fn notify_players(&self, msg: ServerGeneral); fn notify_in_game_clients(&self, msg: ServerGeneral); /// Delete an entity, recording the deletion in [`DeletedEntities`] fn delete_entity_recorded( @@ -212,28 +212,19 @@ impl StateExt for State { // Make sure physics components are updated self.write_component(entity, comp::ForceUpdate); - // Set the character id for the player - // TODO this results in a warning in the console: "Error modifying synced - // component, it doesn't seem to exist" - // It appears to be caused by the player not yet existing on the client at this - // point, despite being able to write the data on the server - self.ecs() - .write_storage::() - .get_mut(entity) - .map(|player| { - player.character_id = Some(character_id); - }); + const INITIAL_VD: u32 = 5; //will be changed after login + self.write_component( + entity, + Presence::new(INITIAL_VD, PresenceKind::Character(character_id)), + ); // Tell the client its request was successful. - if let Some(client) = self.ecs().write_storage::().get_mut(entity) { - if let Some(character_screen_stream) = self - .ecs() - .write_storage::() - .get_mut(entity) - { - client.in_game = Some(ClientInGame::Character); - character_screen_stream.send_fallible(ServerGeneral::CharacterSuccess); - } + if let Some(character_screen_stream) = self + .ecs() + .write_storage::() + .get_mut(entity) + { + character_screen_stream.send_fallible(ServerGeneral::CharacterSuccess); } } @@ -242,7 +233,7 @@ impl StateExt for State { if let Some(player_uid) = self.read_component_copied::(entity) { // Notify clients of a player list update - self.notify_registered_clients(ServerGeneral::PlayerListUpdate( + self.notify_players(ServerGeneral::PlayerListUpdate( PlayerListUpdate::SelectedCharacter(player_uid, CharacterInfo { name: String::from(&stats.name), level: stats.level.level(), @@ -287,9 +278,7 @@ impl StateExt for State { | comp::ChatType::Loot | comp::ChatType::Kill(_, _) | comp::ChatType::Meta - | comp::ChatType::World(_) => { - self.notify_registered_clients(ServerGeneral::ChatMsg(resolved_msg)) - }, + | comp::ChatType::World(_) => self.notify_players(ServerGeneral::ChatMsg(resolved_msg)), comp::ChatType::Online(u) => { for (general_stream, uid) in ( &mut ecs.write_storage::(), @@ -389,14 +378,13 @@ impl StateExt for State { } /// Sends the message to all connected clients - fn notify_registered_clients(&self, msg: ServerGeneral) { + fn notify_players(&self, msg: ServerGeneral) { let mut lazy_msg = None; for (general_stream, _) in ( &mut self.ecs().write_storage::(), - &self.ecs().read_storage::(), + &self.ecs().read_storage::(), ) .join() - .filter(|(_, c)| c.registered) { if lazy_msg.is_none() { lazy_msg = Some(general_stream.prepare(&msg)); @@ -412,10 +400,9 @@ impl StateExt for State { let mut lazy_msg = None; for (general_stream, _) in ( &mut self.ecs().write_storage::(), - &self.ecs().read_storage::(), + &self.ecs().read_storage::(), ) .join() - .filter(|(_, c)| c.in_game.is_some()) { if lazy_msg.is_none() { lazy_msg = Some(general_stream.prepare(&msg)); diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index 7431f979fe..811af3b723 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -3,12 +3,13 @@ use super::{ SysTimer, }; use crate::{ - client::{Client, RegionSubscription}, + client::Client, + presence::{Presence, RegionSubscription}, streams::{GeneralStream, GetStream, InGameStream}, Tick, }; use common::{ - comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Player, Pos, Vel}, + comp::{ForceUpdate, Inventory, InventoryUpdate, Last, Ori, Pos, Vel}, msg::ServerGeneral, outcome::Outcome, region::{Event as RegionEvent, RegionMap}, @@ -39,7 +40,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Ori>, ReadStorage<'a, Inventory>, ReadStorage<'a, RegionSubscription>, - ReadStorage<'a, Player>, + ReadStorage<'a, Presence>, WriteStorage<'a, Last>, WriteStorage<'a, Last>, WriteStorage<'a, Last>, @@ -68,7 +69,7 @@ impl<'a> System<'a> for Sys { orientations, inventories, subscriptions, - players, + presences, mut last_pos, mut last_vel, mut last_ori, @@ -112,6 +113,7 @@ impl<'a> System<'a> for Sys { let mut subscribers = ( &mut clients, &entities, + presences.maybe(), &subscriptions, &positions, &mut in_game_streams, @@ -119,8 +121,16 @@ impl<'a> System<'a> for Sys { ) .join() .filter_map( - |(client, entity, subscription, pos, in_game_stream, general_stream)| { - if client.in_game.is_some() && subscription.regions.contains(&key) { + |( + client, + entity, + presence, + subscription, + pos, + in_game_stream, + general_stream, + )| { + if presence.is_some() && subscription.regions.contains(&key) { Some(( client, &subscription.regions, @@ -339,10 +349,10 @@ impl<'a> System<'a> for Sys { // Handle entity deletion in regions that don't exist in RegionMap // (theoretically none) for (region_key, deleted) in deleted_entities.take_remaining_deleted() { - for general_stream in (&mut clients, &subscriptions, &mut general_streams) + for general_stream in (presences.maybe(), &subscriptions, &mut general_streams) .join() - .filter_map(|(client, subscription, general_stream)| { - if client.in_game.is_some() && subscription.regions.contains(®ion_key) { + .filter_map(|(presence, subscription, general_stream)| { + if presence.is_some() && subscription.regions.contains(®ion_key) { Some(general_stream) } else { None @@ -368,13 +378,14 @@ impl<'a> System<'a> for Sys { } // Sync outcomes - for (player, pos, in_game_stream) in - (&players, positions.maybe(), &mut in_game_streams).join() + for (presence, pos, in_game_stream) in + (presences.maybe(), positions.maybe(), &mut in_game_streams).join() { let is_near = |o_pos: Vec3| { - pos.zip_with(player.view_distance, |pos, vd| { + pos.zip_with(presence, |pos, presence| { pos.0.xy().distance_squared(o_pos.xy()) - < (vd as f32 * TerrainChunkSize::RECT_SIZE.x as f32).powf(2.0) + < (presence.view_distance as f32 * TerrainChunkSize::RECT_SIZE.x as f32) + .powf(2.0) }) }; diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index 4c344a5007..2fb8b8a49c 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -4,19 +4,20 @@ use crate::{ character_creator, client::Client, persistence::character_loader::CharacterLoader, + presence::Presence, streams::{CharacterScreenStream, GeneralStream, GetStream}, EditableSettings, }; use common::{ comp::{ChatType, Player, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, - msg::{ClientGeneral, ClientInGame, ServerGeneral}, + msg::{ClientGeneral, ServerGeneral}, span, state::Time, sync::Uid, }; use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; -use tracing::debug; +use tracing::{debug, warn}; impl Sys { #[allow(clippy::too_many_arguments)] @@ -30,64 +31,65 @@ impl Sys { character_loader: &ReadExpect<'_, CharacterLoader>, uids: &ReadStorage<'_, Uid>, players: &ReadStorage<'_, Player>, + presences: &ReadStorage<'_, Presence>, editable_settings: &ReadExpect<'_, EditableSettings>, alias_validator: &ReadExpect<'_, AliasValidator>, msg: ClientGeneral, ) -> Result<(), crate::error::Error> { match msg { // Request spectator state - ClientGeneral::Spectate if client.registered => { - client.in_game = Some(ClientInGame::Spectator) + ClientGeneral::Spectate => { + if players.contains(entity) { + warn!("Spectator mode not yet implemented on server"); + } else { + debug!("dropped Spectate msg from unregistered client") + } }, - ClientGeneral::Spectate => debug!("dropped Spectate msg from unregistered client"), - ClientGeneral::Character(character_id) - if client.registered && client.in_game.is_none() => - { + ClientGeneral::Character(character_id) => { if let Some(player) = players.get(entity) { - // Send a request to load the character's component data from the - // DB. Once loaded, persisted components such as stats and inventory - // will be inserted for the entity - character_loader.load_character_data( - entity, - player.uuid().to_string(), - character_id, - ); + if presences.contains(entity) { + debug!("player already ingame, aborting"); + } else { + // Send a request to load the character's component data from the + // DB. Once loaded, persisted components such as stats and inventory + // will be inserted for the entity + character_loader.load_character_data( + entity, + player.uuid().to_string(), + character_id, + ); - // Start inserting non-persisted/default components for the entity - // while we load the DB data - server_emitter.emit(ServerEvent::InitCharacterData { - entity, - character_id, - }); + // Start inserting non-persisted/default components for the entity + // while we load the DB data + server_emitter.emit(ServerEvent::InitCharacterData { + entity, + character_id, + }); - // Give the player a welcome message - if !editable_settings.server_description.is_empty() { - general_stream - .send(ChatType::CommandInfo.server_msg(String::from( + // Give the player a welcome message + if !editable_settings.server_description.is_empty() { + general_stream.send(ChatType::CommandInfo.server_msg(String::from( &*editable_settings.server_description, )))?; - } + } - if !client.login_msg_sent { - if let Some(player_uid) = uids.get(entity) { - new_chat_msgs.push((None, UnresolvedChatMsg { - chat_type: ChatType::Online(*player_uid), - message: "".to_string(), - })); + if !client.login_msg_sent { + if let Some(player_uid) = uids.get(entity) { + new_chat_msgs.push((None, UnresolvedChatMsg { + chat_type: ChatType::Online(*player_uid), + message: "".to_string(), + })); - client.login_msg_sent = true; + client.login_msg_sent = true; + } } } } else { + debug!("Client is not yet registered"); character_screen_stream.send(ServerGeneral::CharacterDataLoadError( String::from("Failed to fetch player entity"), ))? } - } - ClientGeneral::Character(_) => { - let registered = client.registered; - let in_game = client.in_game; - debug!(?registered, ?in_game, "dropped Character msg from client"); }, ClientGeneral::RequestCharacterList => { if let Some(player) = players.get(entity) { @@ -138,6 +140,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Uid>, WriteStorage<'a, Client>, ReadStorage<'a, Player>, + ReadStorage<'a, Presence>, WriteStorage<'a, CharacterScreenStream>, WriteStorage<'a, GeneralStream>, ReadExpect<'a, EditableSettings>, @@ -155,6 +158,7 @@ impl<'a> System<'a> for Sys { uids, mut clients, players, + presences, mut character_screen_streams, mut general_streams, editable_settings, @@ -187,6 +191,7 @@ impl<'a> System<'a> for Sys { &character_loader, &uids, &players, + &presences, &editable_settings, &alias_validator, msg, diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs index 6900a63c19..dc58478cdb 100644 --- a/server/src/sys/msg/general.rs +++ b/server/src/sys/msg/general.rs @@ -1,7 +1,7 @@ use super::super::SysTimer; use crate::{client::Client, metrics::PlayerMetrics, streams::GeneralStream}; use common::{ - comp::{ChatMode, UnresolvedChatMsg}, + comp::{ChatMode, Player, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, msg::{validate_chat_msg, ChatMsgValidationError, ClientGeneral, MAX_BYTES_CHAT_MSG}, span, @@ -18,6 +18,7 @@ impl Sys { new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, entity: specs::Entity, client: &mut Client, + player: Option<&Player>, player_metrics: &ReadExpect<'_, PlayerMetrics>, uids: &ReadStorage<'_, Uid>, chat_modes: &ReadStorage<'_, ChatMode>, @@ -25,7 +26,7 @@ impl Sys { ) -> Result<(), crate::error::Error> { match msg { ClientGeneral::ChatMsg(message) => { - if client.registered { + if player.is_some() { match validate_chat_msg(&message) { Ok(()) => { if let Some(from) = uids.get(entity) { @@ -71,6 +72,7 @@ impl<'a> System<'a> for Sys { Write<'a, SysTimer>, ReadStorage<'a, Uid>, ReadStorage<'a, ChatMode>, + ReadStorage<'a, Player>, WriteStorage<'a, Client>, WriteStorage<'a, GeneralStream>, ); @@ -85,6 +87,7 @@ impl<'a> System<'a> for Sys { mut timer, uids, chat_modes, + players, mut clients, mut general_streams, ): Self::SystemData, @@ -95,8 +98,13 @@ impl<'a> System<'a> for Sys { let mut server_emitter = server_event_bus.emitter(); let mut new_chat_msgs = Vec::new(); - for (entity, client, general_stream) in - (&entities, &mut clients, &mut general_streams).join() + for (entity, client, player, general_stream) in ( + &entities, + &mut clients, + (&players).maybe(), + &mut general_streams, + ) + .join() { let res = super::try_recv_all(general_stream, |_, msg| { Self::handle_general_msg( @@ -104,6 +112,7 @@ impl<'a> System<'a> for Sys { &mut new_chat_msgs, entity, client, + player, &player_metrics, &uids, &chat_modes, diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index 0b8ffa8e46..8d3ddd0248 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -2,13 +2,14 @@ use super::super::SysTimer; use crate::{ client::Client, metrics::NetworkRequestMetrics, + presence::Presence, streams::{GetStream, InGameStream}, Settings, }; use common::{ - comp::{CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, Stats, Vel}, + comp::{CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Pos, Stats, Vel}, event::{EventBus, ServerEvent}, - msg::{ClientGeneral, ClientInGame, ServerGeneral}, + msg::{ClientGeneral, PresenceKind, ServerGeneral}, span, state::{BlockChange, Time}, terrain::{TerrainChunkSize, TerrainGrid}, @@ -22,7 +23,8 @@ impl Sys { fn handle_client_in_game_msg( server_emitter: &mut common::event::Emitter<'_, ServerEvent>, entity: specs::Entity, - client: &mut Client, + _client: &Client, + maybe_presence: &mut Option<&mut Presence>, in_game_stream: &mut InGameStream, terrain: &ReadExpect<'_, TerrainGrid>, network_metrics: &ReadExpect<'_, NetworkRequestMetrics>, @@ -33,35 +35,33 @@ impl Sys { positions: &mut WriteStorage<'_, Pos>, velocities: &mut WriteStorage<'_, Vel>, orientations: &mut WriteStorage<'_, Ori>, - players: &mut WriteStorage<'_, Player>, controllers: &mut WriteStorage<'_, Controller>, settings: &Read<'_, Settings>, msg: ClientGeneral, ) -> Result<(), crate::error::Error> { - if client.in_game.is_none() { - debug!(?entity, "client is not in_game, ignoring msg"); - trace!(?msg, "ignored msg content"); - if matches!(msg, ClientGeneral::TerrainChunkRequest{ .. }) { - network_metrics.chunks_request_dropped.inc(); - } - return Ok(()); - } + let presence = match maybe_presence { + Some(g) => g, + None => { + debug!(?entity, "client is not in_game, ignoring msg"); + trace!(?msg, "ignored msg content"); + if matches!(msg, ClientGeneral::TerrainChunkRequest{ .. }) { + network_metrics.chunks_request_dropped.inc(); + } + return Ok(()); + }, + }; match msg { // Go back to registered state (char selection screen) ClientGeneral::ExitInGame => { - client.in_game = None; server_emitter.emit(ServerEvent::ExitIngame { entity }); in_game_stream.send(ServerGeneral::ExitInGameSuccess)?; + *maybe_presence = None; }, ClientGeneral::SetViewDistance(view_distance) => { - players.get_mut(entity).map(|player| { - player.view_distance = Some( - settings - .max_view_distance - .map(|max| view_distance.min(max)) - .unwrap_or(view_distance), - ) - }); + presence.view_distance = settings + .max_view_distance + .map(|max| view_distance.min(max)) + .unwrap_or(view_distance); //correct client if its VD is to high if settings @@ -75,14 +75,14 @@ impl Sys { } }, ClientGeneral::ControllerInputs(inputs) => { - if let Some(ClientInGame::Character) = client.in_game { + if matches!(presence.kind, PresenceKind::Character(_)) { if let Some(controller) = controllers.get_mut(entity) { controller.inputs.update_with_new(inputs); } } }, ClientGeneral::ControlEvent(event) => { - if let Some(ClientInGame::Character) = client.in_game { + if matches!(presence.kind, PresenceKind::Character(_)) { // Skip respawn if client entity is alive if let ControlEvent::Respawn = event { if stats.get(entity).map_or(true, |s| !s.is_dead) { @@ -96,21 +96,20 @@ impl Sys { } }, ClientGeneral::ControlAction(event) => { - if let Some(ClientInGame::Character) = client.in_game { + if matches!(presence.kind, PresenceKind::Character(_)) { if let Some(controller) = controllers.get_mut(entity) { controller.actions.push(event); } } }, ClientGeneral::PlayerPhysics { pos, vel, ori } => { - if let Some(ClientInGame::Character) = client.in_game { - if force_updates.get(entity).is_none() - && stats.get(entity).map_or(true, |s| !s.is_dead) - { - let _ = positions.insert(entity, pos); - let _ = velocities.insert(entity, vel); - let _ = orientations.insert(entity, ori); - } + if matches!(presence.kind, PresenceKind::Character(_)) + && force_updates.get(entity).is_none() + && stats.get(entity).map_or(true, |s| !s.is_dead) + { + let _ = positions.insert(entity, pos); + let _ = velocities.insert(entity, vel); + let _ = orientations.insert(entity, ori); } }, ClientGeneral::BreakBlock(pos) => { @@ -124,13 +123,10 @@ impl Sys { } }, ClientGeneral::TerrainChunkRequest { key } => { - let in_vd = if let (Some(view_distance), Some(pos)) = ( - players.get(entity).and_then(|p| p.view_distance), - positions.get(entity), - ) { + let in_vd = if let Some(pos) = positions.get(entity) { pos.0.xy().map(|e| e as f64).distance( key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64), - ) < (view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt()) + ) < (presence.view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt()) * TerrainChunkSize::RECT_SIZE.x as f64 } else { true @@ -192,7 +188,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, - WriteStorage<'a, Player>, + WriteStorage<'a, Presence>, WriteStorage<'a, Client>, WriteStorage<'a, InGameStream>, WriteStorage<'a, Controller>, @@ -215,7 +211,7 @@ impl<'a> System<'a> for Sys { mut positions, mut velocities, mut orientations, - mut players, + mut presences, mut clients, mut in_game_streams, mut controllers, @@ -227,14 +223,20 @@ impl<'a> System<'a> for Sys { let mut server_emitter = server_event_bus.emitter(); - for (entity, client, in_game_stream) in - (&entities, &mut clients, &mut in_game_streams).join() + for (entity, client, mut presence, in_game_stream) in ( + &entities, + &mut clients, + (&mut presences).maybe(), + &mut in_game_streams, + ) + .join() { let res = super::try_recv_all(in_game_stream, |in_game_stream, msg| { Self::handle_client_in_game_msg( &mut server_emitter, entity, client, + &mut presence, in_game_stream, &terrain, &network_metrics, @@ -245,7 +247,6 @@ impl<'a> System<'a> for Sys { &mut positions, &mut velocities, &mut orientations, - &mut players, &mut controllers, &settings, msg, diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs index 6f3fc31641..219b3b81e9 100644 --- a/server/src/sys/msg/register.rs +++ b/server/src/sys/msg/register.rs @@ -27,7 +27,7 @@ impl Sys { player_list: &HashMap, new_players: &mut Vec, entity: specs::Entity, - client: &mut Client, + _client: &mut Client, register_stream: &mut RegisterStream, general_stream: &mut GeneralStream, player_metrics: &ReadExpect<'_, PlayerMetrics>, @@ -50,8 +50,7 @@ impl Sys { Ok((username, uuid)) => (username, uuid), }; - const INITIAL_VD: Option = Some(5); //will be changed after login - let player = Player::new(username, None, INITIAL_VD, uuid); + let player = Player::new(username, uuid); let is_admin = editable_settings.admins.contains(&uuid); if !player.is_valid() { @@ -60,7 +59,7 @@ impl Sys { return Ok(()); } - if !client.registered && client.in_game.is_none() { + if !players.contains(entity) { // Add Player component to this client let _ = players.insert(entity, player); player_metrics.players_connected.inc(); @@ -72,7 +71,6 @@ impl Sys { } // Tell the client its request was successful. - client.registered = true; register_stream.send(ServerRegisterAnswer::Ok(()))?; // Send initial player list @@ -83,6 +81,7 @@ impl Sys { // Add to list to notify all clients of the new player new_players.push(entity); } + Ok(()) } } @@ -182,10 +181,7 @@ impl<'a> System<'a> for Sys { for entity in new_players { if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) { let mut lazy_msg = None; - for (_, general_stream) in (&mut clients, &mut general_streams) - .join() - .filter(|(c, _)| c.registered) - { + for (_, general_stream) in (&players, &mut general_streams).join() { if lazy_msg.is_none() { lazy_msg = Some(general_stream.prepare(&ServerGeneral::PlayerListUpdate( PlayerListUpdate::Add(*uid, PlayerInfo { diff --git a/server/src/sys/persistence.rs b/server/src/sys/persistence.rs index 91bc9c4fe4..60c578d7d8 100644 --- a/server/src/sys/persistence.rs +++ b/server/src/sys/persistence.rs @@ -1,9 +1,11 @@ use crate::{ persistence::character_updater, + presence::Presence, sys::{SysScheduler, SysTimer}, }; use common::{ - comp::{Inventory, Loadout, Player, Stats}, + comp::{Inventory, Loadout, Stats}, + msg::PresenceKind, span, }; use specs::{Join, ReadExpect, ReadStorage, System, Write}; @@ -13,7 +15,7 @@ pub struct Sys; impl<'a> System<'a> for Sys { #[allow(clippy::type_complexity)] // TODO: Pending review in #587 type SystemData = ( - ReadStorage<'a, Player>, + ReadStorage<'a, Presence>, ReadStorage<'a, Stats>, ReadStorage<'a, Inventory>, ReadStorage<'a, Loadout>, @@ -25,7 +27,7 @@ impl<'a> System<'a> for Sys { fn run( &mut self, ( - players, + presences, player_stats, player_inventories, player_loadouts, @@ -39,17 +41,18 @@ impl<'a> System<'a> for Sys { timer.start(); updater.batch_update( ( - &players, + &presences, &player_stats, &player_inventories, &player_loadouts, ) .join() - .filter_map(|(player, stats, inventory, loadout)| { - player - .character_id - .map(|id| (id, stats, inventory, loadout)) - }), + .filter_map( + |(presence, stats, inventory, loadout)| match presence.kind { + PresenceKind::Character(id) => Some((id, stats, inventory, loadout)), + PresenceKind::Spectator => None, + }, + ), ); timer.end(); } diff --git a/server/src/sys/subscription.rs b/server/src/sys/subscription.rs index 45327314b5..66d9992e03 100644 --- a/server/src/sys/subscription.rs +++ b/server/src/sys/subscription.rs @@ -3,11 +3,12 @@ use super::{ SysTimer, }; use crate::{ - client::{self, Client, RegionSubscription}, + client::Client, + presence::{self, Presence, RegionSubscription}, streams::{GeneralStream, GetStream}, }; use common::{ - comp::{Ori, Player, Pos, Vel}, + comp::{Ori, Pos, Vel}, msg::ServerGeneral, region::{region_in_vd, regions_in_vd, Event as RegionEvent, RegionMap}, span, @@ -34,7 +35,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Pos>, ReadStorage<'a, Vel>, ReadStorage<'a, Ori>, - ReadStorage<'a, Player>, + ReadStorage<'a, Presence>, ReadStorage<'a, Client>, WriteStorage<'a, GeneralStream>, WriteStorage<'a, RegionSubscription>, @@ -53,8 +54,8 @@ impl<'a> System<'a> for Sys { positions, velocities, orientations, - players, - clients, + presences, + _clients, mut general_streams, mut subscriptions, mut deleted_entities, @@ -76,23 +77,16 @@ impl<'a> System<'a> for Sys { // 7. Determine list of regions that are in range and iterate through it // - check if in hashset (hash calc) if not add it let mut regions_to_remove = Vec::new(); - for (_, subscription, pos, vd, client_entity, general_stream) in ( - &clients, + for (subscription, pos, presence, client_entity, general_stream) in ( &mut subscriptions, &positions, - &players, + &presences, &entities, &mut general_streams, ) .join() - .filter_map(|(client, s, pos, player, e, stream)| { - if client.in_game.is_some() { - player.view_distance.map(|v| (client, s, pos, v, e, stream)) - } else { - None - } - }) { + let vd = presence.view_distance; // Calculate current chunk let chunk = (Vec2::::from(pos.0)) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); @@ -107,7 +101,7 @@ impl<'a> System<'a> for Sys { }) - Vec2::from(pos.0)) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| { - e.abs() > (sz / 2 + client::CHUNK_FUZZ) as f32 + e.abs() > (sz / 2 + presence::CHUNK_FUZZ) as f32 }) .reduce_or() { @@ -123,7 +117,9 @@ impl<'a> System<'a> for Sys { *key, pos.0, (vd as f32 * chunk_size) - + (client::CHUNK_FUZZ as f32 + client::REGION_FUZZ as f32 + chunk_size) + + (presence::CHUNK_FUZZ as f32 + + presence::REGION_FUZZ as f32 + + chunk_size) * 2.0f32.sqrt(), ) { // Add to the list of regions to remove @@ -185,7 +181,7 @@ impl<'a> System<'a> for Sys { for key in regions_in_vd( pos.0, (vd as f32 * chunk_size) - + (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(), + + (presence::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(), ) { // Send client initial info about the entities in this region if it was not // already within the set of subscribed regions @@ -224,13 +220,9 @@ impl<'a> System<'a> for Sys { /// Initialize region subscription pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { - if let (Some(client_pos), Some(client_vd), Some(general_stream)) = ( + if let (Some(client_pos), Some(presence), Some(general_stream)) = ( world.read_storage::().get(entity), - world - .read_storage::() - .get(entity) - .map(|pl| pl.view_distance) - .and_then(|v| v), + world.read_storage::().get(entity), world.write_storage::().get_mut(entity), ) { let fuzzy_chunk = (Vec2::::from(client_pos.0)) @@ -238,8 +230,8 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { let chunk_size = TerrainChunkSize::RECT_SIZE.reduce_max() as f32; let regions = common::region::regions_in_vd( client_pos.0, - (client_vd as f32 * chunk_size) as f32 - + (client::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(), + (presence.view_distance as f32 * chunk_size) as f32 + + (presence::CHUNK_FUZZ as f32 + chunk_size) * 2.0f32.sqrt(), ); let region_map = world.read_resource::(); diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index cd2826541c..5b169ab18a 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,11 +1,12 @@ use super::SysTimer; use crate::{ chunk_generator::ChunkGenerator, + presence::Presence, streams::{GetStream, InGameStream}, Tick, }; use common::{ - comp::{self, bird_medium, Alignment, Player, Pos}, + comp::{self, bird_medium, Alignment, Pos}, event::{EventBus, ServerEvent}, generation::get_npc_name, msg::ServerGeneral, @@ -37,7 +38,7 @@ impl<'a> System<'a> for Sys { WriteExpect<'a, TerrainGrid>, Write<'a, TerrainChanges>, ReadStorage<'a, Pos>, - ReadStorage<'a, Player>, + ReadStorage<'a, Presence>, WriteStorage<'a, InGameStream>, ); @@ -51,7 +52,7 @@ impl<'a> System<'a> for Sys { mut terrain, mut terrain_changes, positions, - players, + presences, mut in_game_streams, ): Self::SystemData, ) { @@ -79,11 +80,8 @@ impl<'a> System<'a> for Sys { }, }; // Send the chunk to all nearby players. - for (view_distance, pos, in_game_stream) in (&players, &positions, &mut in_game_streams) - .join() - .filter_map(|(player, pos, in_game_stream)| { - player.view_distance.map(|vd| (vd, pos, in_game_stream)) - }) + for (presence, pos, in_game_stream) in + (&presences, &positions, &mut in_game_streams).join() { let chunk_pos = terrain.pos_key(pos.0.map(|e| e as i32)); // Subtract 2 from the offset before computing squared magnitude @@ -93,7 +91,7 @@ impl<'a> System<'a> for Sys { .map(|e: i32| (e.abs() as u32).saturating_sub(2)) .magnitude_squared(); - if adjusted_dist_sqr <= view_distance.pow(2) { + if adjusted_dist_sqr <= presence.view_distance.pow(2) { in_game_stream.send_fallible(ServerGeneral::TerrainChunkUpdate { key, chunk: Ok(Box::new(chunk.clone())), @@ -210,12 +208,8 @@ impl<'a> System<'a> for Sys { let mut should_drop = true; // For each player with a position, calculate the distance. - for (player, pos) in (&players, &positions).join() { - if player - .view_distance - .map(|vd| chunk_in_vd(pos.0, chunk_key, &terrain, vd)) - .unwrap_or(false) - { + for (presence, pos) in (&presences, &positions).join() { + if chunk_in_vd(pos.0, chunk_key, &terrain, presence.view_distance) { should_drop = false; break; } diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index 0942261f5a..a04c55fd25 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -1,12 +1,9 @@ use super::SysTimer; -use crate::streams::{GetStream, InGameStream}; -use common::{ - comp::{Player, Pos}, - msg::ServerGeneral, - span, - state::TerrainChanges, - terrain::TerrainGrid, +use crate::{ + presence::Presence, + streams::{GetStream, InGameStream}, }; +use common::{comp::Pos, msg::ServerGeneral, span, state::TerrainChanges, terrain::TerrainGrid}; use specs::{Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; /// This systems sends new chunks to clients as well as changes to existing @@ -19,13 +16,13 @@ impl<'a> System<'a> for Sys { Read<'a, TerrainChanges>, Write<'a, SysTimer>, ReadStorage<'a, Pos>, - ReadStorage<'a, Player>, + ReadStorage<'a, Presence>, WriteStorage<'a, InGameStream>, ); fn run( &mut self, - (terrain, terrain_changes, mut timer, positions, players, mut in_game_streams): Self::SystemData, + (terrain, terrain_changes, mut timer, positions, presences, mut in_game_streams): Self::SystemData, ) { span!(_guard, "run", "terrain_sync::Sys::run"); timer.start(); @@ -34,12 +31,10 @@ impl<'a> System<'a> for Sys { 'chunk: for chunk_key in &terrain_changes.modified_chunks { let mut lazy_msg = None; - for (player, pos, in_game_stream) in (&players, &positions, &mut in_game_streams).join() + for (presence, pos, in_game_stream) in + (&presences, &positions, &mut in_game_streams).join() { - if player - .view_distance - .map(|vd| super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, vd)) - .unwrap_or(false) + if super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, presence.view_distance) { if lazy_msg.is_none() { lazy_msg = @@ -61,17 +56,15 @@ impl<'a> System<'a> for Sys { // TODO: Don't send all changed blocks to all clients // Sync changed blocks let mut lazy_msg = None; - for (player, in_game_stream) in (&players, &mut in_game_streams).join() { + for (_, in_game_stream) in (&presences, &mut in_game_streams).join() { if lazy_msg.is_none() { lazy_msg = Some(in_game_stream.prepare(&ServerGeneral::TerrainBlockUpdates( terrain_changes.modified_blocks.clone(), ))); } - if player.view_distance.is_some() { - lazy_msg - .as_ref() - .map(|ref msg| in_game_stream.0.send_raw(&msg)); - } + lazy_msg + .as_ref() + .map(|ref msg| in_game_stream.0.send_raw(&msg)); } timer.end(); diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a0496cadf5..1022a935dd 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -64,6 +64,7 @@ use common::{ item::{ItemDesc, Quality}, BuffKind, }, + msg::PresenceKind, span, sync::Uid, terrain::TerrainChunk, @@ -658,7 +659,12 @@ impl Hud { let server = &client.server_info.name; // Get the id, unwrap is safe because this CANNOT be None at this // point. - let character_id = client.active_character_id.unwrap(); + + let character_id = match client.presence().unwrap() { + PresenceKind::Character(id) => id, + PresenceKind::Spectator => unreachable!("HUD creation in Spectator mode!"), + }; + // Create a new HotbarState from the persisted slots. let hotbar_state = HotbarState::new(global_state.profile.get_hotbar_slots(server, character_id)); diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index fe80710b07..cd3adc0229 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -61,11 +61,11 @@ impl PlayState for CharSelectionState { fn tick(&mut self, global_state: &mut GlobalState, events: Vec) -> PlayStateResult { span!(_guard, "tick", "::tick"); - let (client_in_game, client_registered) = { + let (client_presence, client_registered) = { let client = self.client.borrow(); - (client.in_game(), client.registered()) + (client.presence(), client.registered()) }; - if client_in_game.is_none() && client_registered { + if client_presence.is_none() && client_registered { // Handle window events for event in events { if self.char_selection_ui.handle_event(event.clone()) { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 052f0e5127..b51a178346 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -18,6 +18,7 @@ use common::{ comp::{ChatMsg, ChatType, InventoryUpdateEvent, Pos, Vel}, consts::{MAX_MOUNT_RANGE, MAX_PICKUP_RANGE}, event::EventBus, + msg::PresenceKind, outcome::Outcome, span, terrain::{Block, BlockKind}, @@ -211,11 +212,11 @@ impl PlayState for SessionState { )); // TODO: can this be a method on the session or are there borrowcheck issues? - let (client_in_game, client_registered) = { + let (client_presence, client_registered) = { let client = self.client.borrow(); - (client.in_game(), client.registered()) + (client.presence(), client.registered()) }; - if client_in_game.is_some() { + if client_presence.is_some() { // Update MyEntity // Note: Alternatively, the client could emit an event when the entity changes // which may or may not be more elegant @@ -927,7 +928,12 @@ impl PlayState for SessionState { let server = &client.server_info.name; // If we are changing the hotbar state this CANNOT be None. - let character_id = client.active_character_id.unwrap(); + let character_id = match client.presence().unwrap() { + PresenceKind::Character(id) => id, + PresenceKind::Spectator => { + unreachable!("HUD adaption in Spectator mode!") + }, + }; // Get or update the ServerProfile. global_state @@ -1080,7 +1086,7 @@ impl PlayState for SessionState { self.cleanup(); PlayStateResult::Continue - } else if client_registered && client_in_game.is_none() { + } else if client_registered && client_presence.is_none() { PlayStateResult::Switch(Box::new(CharSelectionState::new( global_state, Rc::clone(&self.client), From 3d9c3e481eded02c0c2cf19821cf92af8edc79f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=A4rtens?= Date: Mon, 2 Nov 2020 19:30:56 +0100 Subject: [PATCH 10/10] Undo one Componenet per `Stream` and instead use `Client`. In order to keep the performance we made it Internal Mutability and use a `Mutex` per Stream, till `Stream.send` is no longer `&mut self`. The old solution didn't rely on this, but needed multiple Components instead which zest didn't liked --- server/src/client.rs | 196 ++++++++++++++++++++++- server/src/cmd.rs | 20 +-- server/src/connection_handler.rs | 40 ++--- server/src/events/entity_manipulation.rs | 7 +- server/src/events/group_manip.rs | 146 ++++++++--------- server/src/events/interaction.rs | 29 +--- server/src/events/inventory_manip.rs | 20 +-- server/src/events/player.rs | 41 +---- server/src/lib.rs | 97 ++--------- server/src/state_ext.rs | 100 +++++------- server/src/streams.rs | 126 --------------- server/src/sys/entity_sync.rs | 121 +++++--------- server/src/sys/invite_timeout.rs | 15 +- server/src/sys/msg/character_screen.rs | 89 ++++------ server/src/sys/msg/general.rs | 28 ++-- server/src/sys/msg/in_game.rs | 42 ++--- server/src/sys/msg/mod.rs | 16 +- server/src/sys/msg/ping.rs | 44 ++--- server/src/sys/msg/register.rs | 59 ++----- server/src/sys/subscription.rs | 24 ++- server/src/sys/terrain.rs | 23 +-- server/src/sys/terrain_sync.rs | 42 ++--- server/src/sys/waypoint.rs | 12 +- 23 files changed, 523 insertions(+), 814 deletions(-) delete mode 100644 server/src/streams.rs diff --git a/server/src/client.rs b/server/src/client.rs index a968194fad..8c4b2655de 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -1,7 +1,9 @@ -use common::msg::ClientType; -use network::Participant; +use common::msg::{ClientType, ServerGeneral, ServerMsg}; +use network::{Message, Participant, Stream, StreamError}; +use serde::{de::DeserializeOwned, Serialize}; use specs::Component; use specs_idvs::IdvStorage; +use std::sync::{atomic::AtomicBool, Mutex}; /// Client handles ALL network related information of everything that connects /// to the server Client DOES NOT handle game states @@ -12,11 +14,195 @@ use specs_idvs::IdvStorage; pub struct Client { pub client_type: ClientType, pub participant: Option, - pub last_ping: f64, - pub login_msg_sent: bool, - pub terminate_msg_recv: bool, + pub last_ping: Mutex, + pub login_msg_sent: AtomicBool, + pub terminate_msg_recv: AtomicBool, + + //TODO: improve network crate so that `send` is no longer `&mut self` and we can get rid of + // this Mutex. This Mutex is just to please the compiler as we do not get into contention + general_stream: Mutex, + ping_stream: Mutex, + register_stream: Mutex, + character_screen_stream: Mutex, + in_game_stream: Mutex, +} + +pub struct PreparedMsg { + stream_id: u8, + message: Message, } impl Component for Client { type Storage = IdvStorage; } + +impl Client { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new( + client_type: ClientType, + participant: Participant, + last_ping: f64, + general_stream: Stream, + ping_stream: Stream, + register_stream: Stream, + character_screen_stream: Stream, + in_game_stream: Stream, + ) -> Self { + Client { + client_type, + participant: Some(participant), + last_ping: Mutex::new(last_ping), + login_msg_sent: AtomicBool::new(false), + terminate_msg_recv: AtomicBool::new(false), + general_stream: Mutex::new(general_stream), + ping_stream: Mutex::new(ping_stream), + register_stream: Mutex::new(register_stream), + character_screen_stream: Mutex::new(character_screen_stream), + in_game_stream: Mutex::new(in_game_stream), + } + } + + pub(crate) fn send>(&self, msg: M) -> Result<(), StreamError> { + match msg.into() { + ServerMsg::Info(m) => self.register_stream.try_lock().unwrap().send(m), + ServerMsg::Init(m) => self.register_stream.try_lock().unwrap().send(m), + ServerMsg::RegisterAnswer(m) => self.register_stream.try_lock().unwrap().send(m), + ServerMsg::General(g) => { + match g { + //Character Screen related + ServerGeneral::CharacterDataLoadError(_) + | ServerGeneral::CharacterListUpdate(_) + | ServerGeneral::CharacterActionError(_) + | ServerGeneral::CharacterSuccess => { + self.character_screen_stream.try_lock().unwrap().send(g) + }, + //Ingame related + ServerGeneral::GroupUpdate(_) + | ServerGeneral::GroupInvite { .. } + | ServerGeneral::InvitePending(_) + | ServerGeneral::InviteComplete { .. } + | ServerGeneral::ExitInGameSuccess + | ServerGeneral::InventoryUpdate(_, _) + | ServerGeneral::TerrainChunkUpdate { .. } + | ServerGeneral::TerrainBlockUpdates(_) + | ServerGeneral::SetViewDistance(_) + | ServerGeneral::Outcomes(_) + | ServerGeneral::Knockback(_) => { + self.in_game_stream.try_lock().unwrap().send(g) + }, + // Always possible + ServerGeneral::PlayerListUpdate(_) + | ServerGeneral::ChatMsg(_) + | ServerGeneral::SetPlayerEntity(_) + | ServerGeneral::TimeOfDay(_) + | ServerGeneral::EntitySync(_) + | ServerGeneral::CompSync(_) + | ServerGeneral::CreateEntity(_) + | ServerGeneral::DeleteEntity(_) + | ServerGeneral::Disconnect(_) + | ServerGeneral::Notification(_) => { + self.general_stream.try_lock().unwrap().send(g) + }, + } + }, + ServerMsg::Ping(m) => self.ping_stream.try_lock().unwrap().send(m), + } + } + + pub(crate) fn send_fallible>(&self, msg: M) { let _ = self.send(msg); } + + pub(crate) fn send_prepared(&self, msg: &PreparedMsg) -> Result<(), StreamError> { + match msg.stream_id { + 0 => self + .register_stream + .try_lock() + .unwrap() + .send_raw(&msg.message), + 1 => self + .character_screen_stream + .try_lock() + .unwrap() + .send_raw(&msg.message), + 2 => self + .in_game_stream + .try_lock() + .unwrap() + .send_raw(&msg.message), + 3 => self + .general_stream + .try_lock() + .unwrap() + .send_raw(&msg.message), + 4 => self.ping_stream.try_lock().unwrap().send_raw(&msg.message), + _ => unreachable!("invalid stream id"), + } + } + + pub(crate) fn prepare>(&self, msg: M) -> PreparedMsg { + match msg.into() { + ServerMsg::Info(m) => PreparedMsg::new(0, &m, &self.register_stream), + ServerMsg::Init(m) => PreparedMsg::new(0, &m, &self.register_stream), + ServerMsg::RegisterAnswer(m) => PreparedMsg::new(0, &m, &self.register_stream), + ServerMsg::General(g) => { + match g { + //Character Screen related + ServerGeneral::CharacterDataLoadError(_) + | ServerGeneral::CharacterListUpdate(_) + | ServerGeneral::CharacterActionError(_) + | ServerGeneral::CharacterSuccess => { + PreparedMsg::new(1, &g, &self.character_screen_stream) + }, + //Ingame related + ServerGeneral::GroupUpdate(_) + | ServerGeneral::GroupInvite { .. } + | ServerGeneral::InvitePending(_) + | ServerGeneral::InviteComplete { .. } + | ServerGeneral::ExitInGameSuccess + | ServerGeneral::InventoryUpdate(_, _) + | ServerGeneral::TerrainChunkUpdate { .. } + | ServerGeneral::TerrainBlockUpdates(_) + | ServerGeneral::SetViewDistance(_) + | ServerGeneral::Outcomes(_) + | ServerGeneral::Knockback(_) => PreparedMsg::new(2, &g, &self.in_game_stream), + // Always possible + ServerGeneral::PlayerListUpdate(_) + | ServerGeneral::ChatMsg(_) + | ServerGeneral::SetPlayerEntity(_) + | ServerGeneral::TimeOfDay(_) + | ServerGeneral::EntitySync(_) + | ServerGeneral::CompSync(_) + | ServerGeneral::CreateEntity(_) + | ServerGeneral::DeleteEntity(_) + | ServerGeneral::Disconnect(_) + | ServerGeneral::Notification(_) => { + PreparedMsg::new(3, &g, &self.general_stream) + }, + } + }, + ServerMsg::Ping(m) => PreparedMsg::new(4, &m, &self.ping_stream), + } + } + + pub(crate) fn recv( + &self, + stream_id: u8, + ) -> Result, StreamError> { + match stream_id { + 0 => self.register_stream.try_lock().unwrap().try_recv(), + 1 => self.character_screen_stream.try_lock().unwrap().try_recv(), + 2 => self.in_game_stream.try_lock().unwrap().try_recv(), + 3 => self.general_stream.try_lock().unwrap().try_recv(), + 4 => self.ping_stream.try_lock().unwrap().try_recv(), + _ => unreachable!("invalid stream id"), + } + } +} + +impl PreparedMsg { + fn new(id: u8, msg: &M, stream: &Mutex) -> PreparedMsg { + Self { + stream_id: id, + message: Message::serialize(&msg, &stream.try_lock().unwrap()), + } + } +} diff --git a/server/src/cmd.rs b/server/src/cmd.rs index edb1cc2fc0..a573c94878 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -26,10 +26,7 @@ use std::convert::TryFrom; use vek::*; use world::util::Sampler; -use crate::{ - login_provider::LoginProvider, - streams::{GetStream, InGameStream}, -}; +use crate::{client::Client, login_provider::LoginProvider}; use scan_fmt::{scan_fmt, scan_fmt_some}; use tracing::error; @@ -652,8 +649,7 @@ fn handle_spawn( // Add to group system if a pet if matches!(alignment, comp::Alignment::Owned { .. }) { let state = server.state(); - let mut in_game_streams = - state.ecs().write_storage::(); + let clients = state.ecs().read_storage::(); let uids = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); @@ -665,15 +661,15 @@ fn handle_spawn( &state.ecs().read_storage(), &uids, &mut |entity, group_change| { - in_game_streams - .get_mut(entity) - .and_then(|s| { + clients + .get(entity) + .and_then(|c| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, s)) + .map(|g| (g, c)) }) - .map(|(g, s)| { - s.send_fallible(ServerGeneral::GroupUpdate(g)); + .map(|(g, c)| { + c.send_fallible(ServerGeneral::GroupUpdate(g)); }); }, ); diff --git a/server/src/connection_handler.rs b/server/src/connection_handler.rs index dfe64a971d..bbc04605cd 100644 --- a/server/src/connection_handler.rs +++ b/server/src/connection_handler.rs @@ -1,7 +1,4 @@ -use crate::{ - streams::{CharacterScreenStream, GeneralStream, InGameStream, PingStream, RegisterStream}, - Client, ClientType, ServerInfo, -}; +use crate::{Client, ClientType, ServerInfo}; use crossbeam::{bounded, unbounded, Receiver, Sender}; use futures_channel::oneshot; use futures_executor::block_on; @@ -16,14 +13,7 @@ pub(crate) struct ServerInfoPacket { pub time: f64, } -pub(crate) struct IncomingClient { - pub client: Client, - pub general: GeneralStream, - pub ping: PingStream, - pub register: RegisterStream, - pub character: CharacterScreenStream, - pub in_game: InGameStream, -} +pub(crate) type IncomingClient = Client; pub(crate) struct ConnectionHandler { _network: Arc, @@ -136,24 +126,18 @@ impl ConnectionHandler { Some(client_type) => client_type?, }; - let client = Client { + let client = Client::new( client_type, - participant: Some(participant), - last_ping: server_data.time, - login_msg_sent: false, - terminate_msg_recv: false, - }; + participant, + server_data.time, + general_stream, + ping_stream, + register_stream, + character_screen_stream, + in_game_stream, + ); - let package = IncomingClient { - client, - general: GeneralStream(general_stream), - ping: PingStream(ping_stream), - register: RegisterStream(register_stream), - character: CharacterScreenStream(character_screen_stream), - in_game: InGameStream(in_game_stream), - }; - - client_sender.send(package)?; + client_sender.send(client)?; Ok(()) } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index dbbd361f8a..48ae6b7563 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,7 +1,6 @@ use crate::{ client::Client, comp::{biped_large, quadruped_medium, quadruped_small}, - streams::{GetStream, InGameStream}, Server, SpawnPoint, StateExt, }; use common::{ @@ -43,9 +42,9 @@ pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3) if let Some(vel) = velocities.get_mut(entity) { vel.0 = impulse; } - let mut in_game_streams = state.ecs().write_storage::(); - if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - in_game_stream.send_fallible(ServerGeneral::Knockback(impulse)); + let clients = state.ecs().read_storage::(); + if let Some(client) = clients.get(entity) { + client.send_fallible(ServerGeneral::Knockback(impulse)); } } diff --git a/server/src/events/group_manip.rs b/server/src/events/group_manip.rs index 2c750f86bb..65598019c9 100644 --- a/server/src/events/group_manip.rs +++ b/server/src/events/group_manip.rs @@ -1,7 +1,4 @@ -use crate::{ - streams::{GeneralStream, GetStream, InGameStream}, - Server, -}; +use crate::{client::Client, Server}; use common::{ comp::{ self, @@ -28,13 +25,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani match manip { GroupManip::Invite(uid) => { - let mut general_streams = state.ecs().write_storage::(); + let clients = state.ecs().read_storage::(); let invitee = match state.ecs().entity_from_uid(uid.into()) { Some(t) => t, None => { // Inform of failure - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible( + if let Some(client) = clients.get(entity) { + client.send_fallible( ChatType::Meta .server_msg("Invite failed, target does not exist.".to_owned()), ); @@ -65,7 +62,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }); if already_in_same_group { // Inform of failure - if let Some(general_stream) = general_streams.get_mut(entity) { + if let Some(general_stream) = clients.get(entity) { general_stream.send_fallible(ChatType::Meta.server_msg( "Invite failed, can't invite someone already in your group".to_owned(), )); @@ -95,7 +92,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani >= max_group_size as usize; if group_size_limit_reached { // Inform inviter that they have reached the group size limit - if let Some(general_stream) = general_streams.get_mut(entity) { + if let Some(general_stream) = clients.get(entity) { general_stream.send_fallible( ChatType::Meta.server_msg( "Invite failed, pending invites plus current group size have reached \ @@ -112,8 +109,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani if invites.contains(invitee) { // Inform inviter that there is already an invite - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible( + if let Some(client) = clients.get(entity) { + client.send_fallible( ChatType::Meta .server_msg("This player already has a pending invite.".to_owned()), ); @@ -153,35 +150,32 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani } }; - let mut in_game_streams = state.ecs().write_storage::(); - // If client comp - if let (Some(in_game_stream), Some(inviter)) = - (in_game_streams.get_mut(invitee), uids.get(entity).copied()) + if let (Some(client), Some(inviter)) = (clients.get(invitee), uids.get(entity).copied()) { if send_invite() { - in_game_stream.send_fallible(ServerGeneral::GroupInvite { + client.send_fallible(ServerGeneral::GroupInvite { inviter, timeout: PRESENTED_INVITE_TIMEOUT_DUR, }); } } else if agents.contains(invitee) { send_invite(); - } else if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible( + } else if let Some(client) = clients.get(entity) { + client.send_fallible( ChatType::Meta.server_msg("Can't invite, not a player or npc".to_owned()), ); } // Notify inviter that the invite is pending if invite_sent { - if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - in_game_stream.send_fallible(ServerGeneral::InvitePending(uid)); + if let Some(client) = clients.get(entity) { + client.send_fallible(ServerGeneral::InvitePending(uid)); } } }, GroupManip::Accept => { - let mut in_game_streams = state.ecs().write_storage::(); + let clients = state.ecs().read_storage::(); let uids = state.ecs().read_storage::(); let mut invites = state.ecs().write_storage::(); if let Some(inviter) = invites.remove(entity).and_then(|invite| { @@ -198,10 +192,10 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani Some(inviter) }) { - if let (Some(in_game_stream), Some(target)) = - (in_game_streams.get_mut(inviter), uids.get(entity).copied()) + if let (Some(client), Some(target)) = + (clients.get(inviter), uids.get(entity).copied()) { - in_game_stream.send_fallible(ServerGeneral::InviteComplete { + client.send_fallible(ServerGeneral::InviteComplete { target, answer: InviteAnswer::Accepted, }); @@ -215,20 +209,20 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani &state.ecs().read_storage(), &uids, |entity, group_change| { - in_game_streams - .get_mut(entity) - .and_then(|s| { + clients + .get(entity) + .and_then(|c| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, s)) + .map(|g| (g, c)) }) - .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); }, ); } }, GroupManip::Decline => { - let mut in_game_streams = state.ecs().write_storage::(); + let clients = state.ecs().read_storage::(); let uids = state.ecs().read_storage::(); let mut invites = state.ecs().write_storage::(); if let Some(inviter) = invites.remove(entity).and_then(|invite| { @@ -246,10 +240,10 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani Some(inviter) }) { // Inform inviter of rejection - if let (Some(in_game_stream), Some(target)) = - (in_game_streams.get_mut(inviter), uids.get(entity).copied()) + if let (Some(client), Some(target)) = + (clients.get(inviter), uids.get(entity).copied()) { - in_game_stream.send_fallible(ServerGeneral::InviteComplete { + client.send_fallible(ServerGeneral::InviteComplete { target, answer: InviteAnswer::Declined, }); @@ -257,7 +251,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani } }, GroupManip::Leave => { - let mut in_game_streams = state.ecs().write_storage::(); + let clients = state.ecs().read_storage::(); let uids = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); group_manager.leave_group( @@ -267,19 +261,19 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani &uids, &state.ecs().entities(), &mut |entity, group_change| { - in_game_streams - .get_mut(entity) - .and_then(|s| { + clients + .get(entity) + .and_then(|c| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, s)) + .map(|g| (g, c)) }) - .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); }, ); }, GroupManip::Kick(uid) => { - let mut general_streams = state.ecs().write_storage::(); + let clients = state.ecs().read_storage::(); let uids = state.ecs().read_storage::(); let alignments = state.ecs().read_storage::(); @@ -287,8 +281,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani Some(t) => t, None => { // Inform of failure - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible( + if let Some(client) = clients.get(entity) { + client.send_fallible( ChatType::Meta .server_msg("Kick failed, target does not exist.".to_owned()), ); @@ -300,7 +294,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani // Can't kick pet if matches!(alignments.get(target), Some(comp::Alignment::Owned(owner)) if uids.get(target).map_or(true, |u| u != owner)) { - if let Some(general_stream) = general_streams.get_mut(entity) { + if let Some(general_stream) = clients.get(entity) { general_stream.send_fallible( ChatType::Meta.server_msg("Kick failed, you can't kick pets.".to_owned()), ); @@ -309,8 +303,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani } // Can't kick yourself if uids.get(entity).map_or(false, |u| *u == uid) { - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible( + if let Some(client) = clients.get(entity) { + client.send_fallible( ChatType::Meta .server_msg("Kick failed, you can't kick yourself.".to_owned()), ); @@ -320,7 +314,6 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani let mut groups = state.ecs().write_storage::(); let mut group_manager = state.ecs().write_resource::(); - let mut in_game_streams = state.ecs().write_storage::(); // Make sure kicker is the group leader match groups .get(target) @@ -335,42 +328,42 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani &uids, &state.ecs().entities(), &mut |entity, group_change| { - in_game_streams - .get_mut(entity) - .and_then(|s| { + clients + .get(entity) + .and_then(|c| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, s)) + .map(|g| (g, c)) }) - .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); }, ); // Tell them the have been kicked - if let Some(general_stream) = general_streams.get_mut(target) { - general_stream.send_fallible( + if let Some(client) = clients.get(target) { + client.send_fallible( ChatType::Meta .server_msg("You were removed from the group.".to_owned()), ); } // Tell kicker that they were succesful - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream + if let Some(client) = clients.get(entity) { + client .send_fallible(ChatType::Meta.server_msg("Player kicked.".to_owned())); } }, Some(_) => { // Inform kicker that they are not the leader - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible(ChatType::Meta.server_msg( + if let Some(client) = clients.get(entity) { + client.send_fallible(ChatType::Meta.server_msg( "Kick failed: You are not the leader of the target's group.".to_owned(), )); } }, None => { // Inform kicker that the target is not in a group - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible( + if let Some(client) = clients.get(entity) { + client.send_fallible( ChatType::Meta.server_msg( "Kick failed: Your target is not in a group.".to_owned(), ), @@ -380,14 +373,14 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani } }, GroupManip::AssignLeader(uid) => { - let mut general_streams = state.ecs().write_storage::(); + let clients = state.ecs().read_storage::(); let uids = state.ecs().read_storage::(); let target = match state.ecs().entity_from_uid(uid.into()) { Some(t) => t, None => { // Inform of failure - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible(ChatType::Meta.server_msg( + if let Some(client) = clients.get(entity) { + client.send_fallible(ChatType::Meta.server_msg( "Leadership transfer failed, target does not exist".to_owned(), )); } @@ -396,7 +389,6 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }; let groups = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); - let mut in_game_streams = state.ecs().write_storage::(); // Make sure assigner is the group leader match groups .get(target) @@ -411,25 +403,25 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani &state.ecs().read_storage(), &uids, |entity, group_change| { - in_game_streams - .get_mut(entity) - .and_then(|s| { + clients + .get(entity) + .and_then(|c| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, s)) + .map(|g| (g, c)) }) - .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); }, ); // Tell them they are the leader - if let Some(general_stream) = general_streams.get_mut(target) { - general_stream.send_fallible( + if let Some(client) = clients.get(target) { + client.send_fallible( ChatType::Meta.server_msg("You are the group leader now.".to_owned()), ); } // Tell the old leader that the transfer was succesful - if let Some(general_stream) = general_streams.get_mut(target) { - general_stream.send_fallible( + if let Some(client) = clients.get(target) { + client.send_fallible( ChatType::Meta .server_msg("You are no longer the group leader.".to_owned()), ); @@ -437,9 +429,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }, Some(_) => { // Inform transferer that they are not the leader - let mut general_streams = state.ecs().write_storage::(); - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible( + if let Some(client) = clients.get(entity) { + client.send_fallible( ChatType::Meta.server_msg( "Transfer failed: You are not the leader of the target's group." .to_owned(), @@ -449,9 +440,8 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }, None => { // Inform transferer that the target is not in a group - let mut general_streams = state.ecs().write_storage::(); - if let Some(general_stream) = general_streams.get_mut(entity) { - general_stream.send_fallible(ChatType::Meta.server_msg( + if let Some(client) = clients.get(entity) { + client.send_fallible(ChatType::Meta.server_msg( "Transfer failed: Your target is not in a group.".to_owned(), )); } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 9d868692d2..9a0cfcb32f 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -1,11 +1,4 @@ -use crate::{ - client::Client, - presence::RegionSubscription, - streams::{ - CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, - }, - Server, -}; +use crate::{client::Client, presence::RegionSubscription, Server}; use common::{ comp::{self, item, Pos}, consts::MAX_MOUNT_RANGE, @@ -123,7 +116,6 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { } let mut clients = ecs.write_storage::(); - let mut general_streams = ecs.write_storage::(); if clients.get_mut(possesse).is_some() { error!("can't possess other players"); @@ -131,23 +123,8 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { } match (|| -> Option> { - let mut ping_streams = ecs.write_storage::(); - let mut register_streams = ecs.write_storage::(); - let mut character_screen_streams = ecs.write_storage::(); - let mut in_game_streams = ecs.write_storage::(); - let c = clients.remove(possessor)?; clients.insert(possesse, c).ok()?; - let s = general_streams.remove(possessor)?; - general_streams.insert(possesse, s).ok()?; - let s = ping_streams.remove(possessor)?; - ping_streams.insert(possesse, s).ok()?; - let s = register_streams.remove(possessor)?; - register_streams.insert(possesse, s).ok()?; - let s = character_screen_streams.remove(possessor)?; - character_screen_streams.insert(possesse, s).ok()?; - let s = in_game_streams.remove(possessor)?; - in_game_streams.insert(possesse, s).ok()?; //optional entities let mut players = ecs.write_storage::(); let mut subscriptions = ecs.write_storage::(); @@ -179,9 +156,9 @@ pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) { }, } - general_streams + clients .get_mut(possesse) - .map(|s| s.send_fallible(ServerGeneral::SetPlayerEntity(possesse_uid))); + .map(|c| c.send_fallible(ServerGeneral::SetPlayerEntity(possesse_uid))); // Put possess item into loadout let mut loadouts = ecs.write_storage::(); diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 76dd82f638..a9b936da70 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -1,7 +1,4 @@ -use crate::{ - streams::{GetStream, InGameStream}, - Server, StateExt, -}; +use crate::{client::Client, Server, StateExt}; use common::{ comp::{ self, item, @@ -282,8 +279,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv .insert(tameable_entity, comp::Alignment::Owned(uid)); // Add to group system - let mut in_game_streams = - state.ecs().write_storage::(); + let clients = state.ecs().read_storage::(); let uids = state.ecs().read_storage::(); let mut group_manager = state .ecs() @@ -297,15 +293,15 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv &state.ecs().read_storage(), &uids, &mut |entity, group_change| { - in_game_streams - .get_mut(entity) - .and_then(|s| { + clients + .get(entity) + .and_then(|c| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, s)) + .map(|g| (g, c)) }) - .map(|(g, s)| { - s.send(ServerGeneral::GroupUpdate(g)) + .map(|(g, c)| { + c.send(ServerGeneral::GroupUpdate(g)) }); }, ); diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 65297e3f60..e522fe28f0 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -1,14 +1,7 @@ use super::Event; use crate::{ - client::Client, - login_provider::LoginProvider, - persistence, - presence::Presence, - state_ext::StateExt, - streams::{ - CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, - }, - Server, + client::Client, login_provider::LoginProvider, persistence, presence::Presence, + state_ext::StateExt, Server, }; use common::{ comp, @@ -37,42 +30,18 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) { .get(entity) .cloned(); - if let Some(( - client, - uid, - player, - general_stream, - ping_stream, - register_stream, - character_screen_stream, - mut in_game_stream, - )) = (|| { + if let Some((client, uid, player)) = (|| { let ecs = state.ecs(); Some(( ecs.write_storage::().remove(entity)?, ecs.write_storage::().remove(entity)?, ecs.write_storage::().remove(entity)?, - ecs.write_storage::().remove(entity)?, - ecs.write_storage::().remove(entity)?, - ecs.write_storage::().remove(entity)?, - ecs.write_storage::() - .remove(entity)?, - ecs.write_storage::().remove(entity)?, )) })() { // Tell client its request was successful - in_game_stream.send_fallible(ServerGeneral::ExitInGameSuccess); + client.send_fallible(ServerGeneral::ExitInGameSuccess); - let entity_builder = state - .ecs_mut() - .create_entity() - .with(client) - .with(player) - .with(general_stream) - .with(ping_stream) - .with(register_stream) - .with(character_screen_stream) - .with(in_game_stream); + let entity_builder = state.ecs_mut().create_entity().with(client).with(player); // Preserve group component if present let entity_builder = match maybe_group { diff --git a/server/src/lib.rs b/server/src/lib.rs index 256f12c0f0..8db6c0532f 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -20,7 +20,6 @@ pub mod persistence; pub mod presence; pub mod settings; pub mod state_ext; -pub mod streams; pub mod sys; #[cfg(not(feature = "worldgen"))] mod test_world; @@ -43,9 +42,6 @@ use crate::{ login_provider::LoginProvider, presence::{Presence, RegionSubscription}, state_ext::StateExt, - streams::{ - CharacterScreenStream, GeneralStream, GetStream, InGameStream, PingStream, RegisterStream, - }, sys::sentinel::{DeletedEntities, TrackedComps}, }; use common::{ @@ -193,11 +189,6 @@ impl Server { state.ecs_mut().register::(); state.ecs_mut().register::(); state.ecs_mut().register::(); - state.ecs_mut().register::(); - state.ecs_mut().register::(); - state.ecs_mut().register::(); - state.ecs_mut().register::(); - state.ecs_mut().register::(); //Alias validator let banned_words_paths = &settings.banned_words_files; @@ -825,16 +816,14 @@ impl Server { fn initialize_client( &mut self, - mut incoming: crate::connection_handler::IncomingClient, + client: crate::connection_handler::IncomingClient, ) -> Result, Error> { - let client = incoming.client; - if self.settings().max_players <= self.state.ecs().read_storage::().join().count() { trace!( ?client.participant, "to many players, wont allow participant to connect" ); - incoming.register.0.send(ServerInit::TooManyPlayers)?; + client.send(ServerInit::TooManyPlayers)?; return Ok(None); } @@ -843,11 +832,6 @@ impl Server { .ecs_mut() .create_entity_synced() .with(client) - .with(incoming.general) - .with(incoming.ping) - .with(incoming.register) - .with(incoming.character) - .with(incoming.in_game) .build(); self.state .ecs() @@ -859,10 +843,9 @@ impl Server { debug!("Starting initial sync with client."); self.state .ecs() - .write_storage::() - .get_mut(entity) + .read_storage::() + .get(entity) .unwrap() - .0 .send(ServerInit::GameSync { // Send client their entity entity_package: TrackedComps::fetch(&self.state.ecs()) @@ -905,73 +888,11 @@ impl Server { where S: Into, { - const ERR: &str = - "Don't do that. Sending these messages is only done ONCE at connect and not by this fn"; - match msg.into() { - ServerMsg::Info(_) => panic!(ERR), - ServerMsg::Init(_) => panic!(ERR), - ServerMsg::RegisterAnswer(msg) => { - self.state - .ecs() - .write_storage::() - .get_mut(entity) - .map(|s| s.send(msg)); - }, - ServerMsg::General(msg) => { - match &msg { - //Character Screen related - ServerGeneral::CharacterDataLoadError(_) - | ServerGeneral::CharacterListUpdate(_) - | ServerGeneral::CharacterActionError(_) - | ServerGeneral::CharacterSuccess => self - .state - .ecs() - .write_storage::() - .get_mut(entity) - .map(|s| s.send(msg)), - //Ingame related - ServerGeneral::GroupUpdate(_) - | ServerGeneral::GroupInvite { .. } - | ServerGeneral::InvitePending(_) - | ServerGeneral::InviteComplete { .. } - | ServerGeneral::ExitInGameSuccess - | ServerGeneral::InventoryUpdate(_, _) - | ServerGeneral::TerrainChunkUpdate { .. } - | ServerGeneral::TerrainBlockUpdates(_) - | ServerGeneral::SetViewDistance(_) - | ServerGeneral::Outcomes(_) - | ServerGeneral::Knockback(_) => self - .state - .ecs() - .write_storage::() - .get_mut(entity) - .map(|s| s.send(msg)), - // Always possible - ServerGeneral::PlayerListUpdate(_) - | ServerGeneral::ChatMsg(_) - | ServerGeneral::SetPlayerEntity(_) - | ServerGeneral::TimeOfDay(_) - | ServerGeneral::EntitySync(_) - | ServerGeneral::CompSync(_) - | ServerGeneral::CreateEntity(_) - | ServerGeneral::DeleteEntity(_) - | ServerGeneral::Disconnect(_) - | ServerGeneral::Notification(_) => self - .state - .ecs() - .write_storage::() - .get_mut(entity) - .map(|s| s.send(msg)), - }; - }, - ServerMsg::Ping(msg) => { - self.state - .ecs() - .write_storage::() - .get_mut(entity) - .map(|s| s.send(msg)); - }, - } + self.state + .ecs() + .read_storage::() + .get(entity) + .map(|c| c.send(msg)); } pub fn notify_players(&mut self, msg: ServerGeneral) { self.state.notify_players(msg); } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 71548b2dd8..46fe38fb12 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -1,9 +1,6 @@ use crate::{ - persistence::PersistedComponents, - presence::Presence, - streams::{CharacterScreenStream, GeneralStream, GetStream, InGameStream}, - sys::sentinel::DeletedEntities, - SpawnPoint, + client::Client, persistence::PersistedComponents, presence::Presence, + sys::sentinel::DeletedEntities, SpawnPoint, }; use common::{ character::CharacterId, @@ -219,12 +216,8 @@ impl StateExt for State { ); // Tell the client its request was successful. - if let Some(character_screen_stream) = self - .ecs() - .write_storage::() - .get_mut(entity) - { - character_screen_stream.send_fallible(ServerGeneral::CharacterSuccess); + if let Some(client) = self.ecs().read_storage::().get(entity) { + client.send_fallible(ServerGeneral::CharacterSuccess); } } @@ -280,26 +273,20 @@ impl StateExt for State { | comp::ChatType::Meta | comp::ChatType::World(_) => self.notify_players(ServerGeneral::ChatMsg(resolved_msg)), comp::ChatType::Online(u) => { - for (general_stream, uid) in ( - &mut ecs.write_storage::(), - &ecs.read_storage::(), - ) - .join() + for (client, uid) in + (&ecs.read_storage::(), &ecs.read_storage::()).join() { if uid != u { - general_stream.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); + client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, comp::ChatType::Tell(u, t) => { - for (general_stream, uid) in ( - &mut ecs.write_storage::(), - &ecs.read_storage::(), - ) - .join() + for (client, uid) in + (&ecs.read_storage::(), &ecs.read_storage::()).join() { if uid == u || uid == t { - general_stream.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); + client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -309,12 +296,9 @@ impl StateExt for State { (*ecs.read_resource::()).retrieve_entity_internal(uid.0); let positions = ecs.read_storage::(); if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) { - for (general_stream, pos) in - (&mut ecs.write_storage::(), &positions).join() - { + for (client, pos) in (&ecs.read_storage::(), &positions).join() { if is_within(comp::ChatMsg::SAY_DISTANCE, pos, speaker_pos) { - general_stream - .send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); + client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -324,12 +308,9 @@ impl StateExt for State { (*ecs.read_resource::()).retrieve_entity_internal(uid.0); let positions = ecs.read_storage::(); if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) { - for (general_stream, pos) in - (&mut ecs.write_storage::(), &positions).join() - { + for (client, pos) in (&ecs.read_storage::(), &positions).join() { if is_within(comp::ChatMsg::REGION_DISTANCE, pos, speaker_pos) { - general_stream - .send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); + client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } @@ -339,38 +320,35 @@ impl StateExt for State { (*ecs.read_resource::()).retrieve_entity_internal(uid.0); let positions = ecs.read_storage::(); if let Some(speaker_pos) = entity_opt.and_then(|e| positions.get(e)) { - for (general_stream, pos) in - (&mut ecs.write_storage::(), &positions).join() - { + for (client, pos) in (&ecs.read_storage::(), &positions).join() { if is_within(comp::ChatMsg::NPC_DISTANCE, pos, speaker_pos) { - general_stream - .send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); + client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } } }, comp::ChatType::FactionMeta(s) | comp::ChatType::Faction(_, s) => { - for (general_stream, faction) in ( - &mut ecs.write_storage::(), + for (client, faction) in ( + &ecs.read_storage::(), &ecs.read_storage::(), ) .join() { if s == &faction.0 { - general_stream.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); + client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, comp::ChatType::GroupMeta(g) | comp::ChatType::Group(_, g) => { - for (general_stream, group) in ( - &mut ecs.write_storage::(), + for (client, group) in ( + &ecs.read_storage::(), &ecs.read_storage::(), ) .join() { if g == group { - general_stream.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); + client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); } } }, @@ -379,37 +357,35 @@ impl StateExt for State { /// Sends the message to all connected clients fn notify_players(&self, msg: ServerGeneral) { + let mut msg = Some(msg); let mut lazy_msg = None; - for (general_stream, _) in ( - &mut self.ecs().write_storage::(), + for (client, _) in ( + &self.ecs().read_storage::(), &self.ecs().read_storage::(), ) .join() { if lazy_msg.is_none() { - lazy_msg = Some(general_stream.prepare(&msg)); + lazy_msg = Some(client.prepare(msg.take().unwrap())); } - lazy_msg - .as_ref() - .map(|ref msg| general_stream.0.send_raw(&msg)); + lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg)); } } /// Sends the message to all clients playing in game fn notify_in_game_clients(&self, msg: ServerGeneral) { + let mut msg = Some(msg); let mut lazy_msg = None; - for (general_stream, _) in ( - &mut self.ecs().write_storage::(), + for (client, _) in ( + &mut self.ecs().write_storage::(), &self.ecs().read_storage::(), ) .join() { if lazy_msg.is_none() { - lazy_msg = Some(general_stream.prepare(&msg)); + lazy_msg = Some(client.prepare(msg.take().unwrap())); } - lazy_msg - .as_ref() - .map(|ref msg| general_stream.0.send_raw(&msg)); + lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg)); } } @@ -419,7 +395,7 @@ impl StateExt for State { ) -> Result<(), specs::error::WrongGeneration> { // Remove entity from a group if they are in one { - let mut in_game_streams = self.ecs().write_storage::(); + let clients = self.ecs().read_storage::(); let uids = self.ecs().read_storage::(); let mut group_manager = self.ecs().write_resource::(); group_manager.entity_deleted( @@ -429,14 +405,14 @@ impl StateExt for State { &uids, &self.ecs().entities(), &mut |entity, group_change| { - in_game_streams - .get_mut(entity) - .and_then(|s| { + clients + .get(entity) + .and_then(|c| { group_change .try_map(|e| uids.get(e).copied()) - .map(|g| (g, s)) + .map(|g| (g, c)) }) - .map(|(g, s)| s.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); }, ); } diff --git a/server/src/streams.rs b/server/src/streams.rs deleted file mode 100644 index d1dfebfd86..0000000000 --- a/server/src/streams.rs +++ /dev/null @@ -1,126 +0,0 @@ -use common::msg::{ClientGeneral, ClientRegister, PingMsg, ServerGeneral, ServerRegisterAnswer}; - -use network::{Message, Stream, StreamError}; -use serde::{de::DeserializeOwned, Serialize}; - -use specs::Component; -use specs_idvs::IdvStorage; - -/// helped to reduce code duplication -pub(crate) trait GetStream { - type RecvMsg: DeserializeOwned; - type SendMsg: Serialize + core::fmt::Debug; - fn get_mut(&mut self) -> &mut Stream; - fn verify(msg: &Self::SendMsg) -> bool; - - fn send(&mut self, msg: Self::SendMsg) -> Result<(), StreamError> { - if Self::verify(&msg) { - self.get_mut().send(msg) - } else { - unreachable!("sending this msg isn't allowed! got: {:?}", msg) - } - } - - fn send_fallible(&mut self, msg: Self::SendMsg) { let _ = self.send(msg); } - - fn prepare(&mut self, msg: &Self::SendMsg) -> Message { - if Self::verify(&msg) { - Message::serialize(&msg, &self.get_mut()) - } else { - unreachable!("sending this msg isn't allowed! got: {:?}", msg) - } - } -} - -// Streams -// we ignore errors on send, and do unified error handling in recv -pub struct GeneralStream(pub(crate) Stream); -pub struct PingStream(pub(crate) Stream); -pub struct RegisterStream(pub(crate) Stream); -pub struct CharacterScreenStream(pub(crate) Stream); -pub struct InGameStream(pub(crate) Stream); - -impl Component for GeneralStream { - type Storage = IdvStorage; -} -impl Component for PingStream { - type Storage = IdvStorage; -} -impl Component for RegisterStream { - type Storage = IdvStorage; -} -impl Component for CharacterScreenStream { - type Storage = IdvStorage; -} -impl Component for InGameStream { - type Storage = IdvStorage; -} - -impl GetStream for GeneralStream { - type RecvMsg = ClientGeneral; - type SendMsg = ServerGeneral; - - fn get_mut(&mut self) -> &mut Stream { &mut self.0 } - - fn verify(msg: &Self::SendMsg) -> bool { - matches!(&msg, ServerGeneral::PlayerListUpdate(_) - | ServerGeneral::ChatMsg(_) - | ServerGeneral::SetPlayerEntity(_) - | ServerGeneral::TimeOfDay(_) - | ServerGeneral::EntitySync(_) - | ServerGeneral::CompSync(_) - | ServerGeneral::CreateEntity(_) - | ServerGeneral::DeleteEntity(_) - | ServerGeneral::Disconnect(_) - | ServerGeneral::Notification(_)) - } -} -impl GetStream for PingStream { - type RecvMsg = PingMsg; - type SendMsg = PingMsg; - - fn get_mut(&mut self) -> &mut Stream { &mut self.0 } - - fn verify(_: &Self::SendMsg) -> bool { true } -} -impl GetStream for RegisterStream { - type RecvMsg = ClientRegister; - type SendMsg = ServerRegisterAnswer; - - fn get_mut(&mut self) -> &mut Stream { &mut self.0 } - - fn verify(_: &Self::SendMsg) -> bool { true } -} -impl GetStream for CharacterScreenStream { - type RecvMsg = ClientGeneral; - type SendMsg = ServerGeneral; - - fn get_mut(&mut self) -> &mut Stream { &mut self.0 } - - fn verify(msg: &Self::SendMsg) -> bool { - matches!(&msg, ServerGeneral::CharacterDataLoadError(_) - | ServerGeneral::CharacterListUpdate(_) - | ServerGeneral::CharacterActionError(_) - | ServerGeneral::CharacterSuccess) - } -} -impl GetStream for InGameStream { - type RecvMsg = ClientGeneral; - type SendMsg = ServerGeneral; - - fn get_mut(&mut self) -> &mut Stream { &mut self.0 } - - fn verify(msg: &Self::SendMsg) -> bool { - matches!(&msg, ServerGeneral::GroupUpdate(_) - | ServerGeneral::GroupInvite { .. } - | ServerGeneral::InvitePending(_) - | ServerGeneral::InviteComplete { .. } - | ServerGeneral::ExitInGameSuccess - | ServerGeneral::InventoryUpdate(_, _) - | ServerGeneral::TerrainChunkUpdate { .. } - | ServerGeneral::TerrainBlockUpdates(_) - | ServerGeneral::SetViewDistance(_) - | ServerGeneral::Outcomes(_) - | ServerGeneral::Knockback(_)) - } -} diff --git a/server/src/sys/entity_sync.rs b/server/src/sys/entity_sync.rs index 811af3b723..fffa97871e 100644 --- a/server/src/sys/entity_sync.rs +++ b/server/src/sys/entity_sync.rs @@ -5,7 +5,6 @@ use super::{ use crate::{ client::Client, presence::{Presence, RegionSubscription}, - streams::{GeneralStream, GetStream, InGameStream}, Tick, }; use common::{ @@ -44,9 +43,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Last>, WriteStorage<'a, Last>, WriteStorage<'a, Last>, - WriteStorage<'a, Client>, - WriteStorage<'a, InGameStream>, - WriteStorage<'a, GeneralStream>, + ReadStorage<'a, Client>, WriteStorage<'a, ForceUpdate>, WriteStorage<'a, InventoryUpdate>, Write<'a, DeletedEntities>, @@ -73,9 +70,7 @@ impl<'a> System<'a> for Sys { mut last_pos, mut last_vel, mut last_ori, - mut clients, - mut in_game_streams, - mut general_streams, + clients, mut force_updates, mut inventory_updates, mut deleted_entities, @@ -111,39 +106,20 @@ impl<'a> System<'a> for Sys { // Assemble subscriber list for this region by iterating through clients and // checking if they are subscribed to this region let mut subscribers = ( - &mut clients, + &clients, &entities, presences.maybe(), &subscriptions, &positions, - &mut in_game_streams, - &mut general_streams, ) .join() - .filter_map( - |( - client, - entity, - presence, - subscription, - pos, - in_game_stream, - general_stream, - )| { - if presence.is_some() && subscription.regions.contains(&key) { - Some(( - client, - &subscription.regions, - entity, - *pos, - in_game_stream, - general_stream, - )) - } else { - None - } - }, - ) + .filter_map(|(client, entity, presence, subscription, pos)| { + if presence.is_some() && subscription.regions.contains(&key) { + Some((client, &subscription.regions, entity, *pos)) + } else { + None + } + }) .collect::>(); for event in region.events() { @@ -166,9 +142,7 @@ impl<'a> System<'a> for Sys { vel.copied(), ori.copied(), )); - for (_, regions, client_entity, _, _, general_stream) in - &mut subscribers - { + for (client, regions, client_entity, _) in &mut subscribers { if maybe_key .as_ref() .map(|key| !regions.contains(key)) @@ -176,7 +150,7 @@ impl<'a> System<'a> for Sys { // Client doesn't need to know about itself && *client_entity != entity { - general_stream.send_fallible(create_msg.clone()); + client.send_fallible(create_msg.clone()); } } } @@ -184,13 +158,13 @@ impl<'a> System<'a> for Sys { RegionEvent::Left(id, maybe_key) => { // Lookup UID for entity if let Some(&uid) = uids.get(entities.entity(*id)) { - for (_, regions, _, _, _, general_stream) in &mut subscribers { + for (client, regions, _, _) in &mut subscribers { if maybe_key .as_ref() .map(|key| !regions.contains(key)) .unwrap_or(true) { - general_stream.send_fallible(ServerGeneral::DeleteEntity(uid)); + client.send_fallible(ServerGeneral::DeleteEntity(uid)); } } } @@ -211,32 +185,29 @@ impl<'a> System<'a> for Sys { let mut comp_sync_package = Some(comp_sync_package); let mut entity_sync_lazymsg = None; let mut comp_sync_lazymsg = None; - subscribers - .iter_mut() - .for_each(move |(_, _, _, _, _, general_stream)| { - if entity_sync_lazymsg.is_none() { - entity_sync_lazymsg = Some(general_stream.prepare( - &ServerGeneral::EntitySync(entity_sync_package.take().unwrap()), - )); - comp_sync_lazymsg = - Some(general_stream.prepare(&ServerGeneral::CompSync( - comp_sync_package.take().unwrap(), - ))); - } - entity_sync_lazymsg - .as_ref() - .map(|msg| general_stream.0.send_raw(&msg)); - comp_sync_lazymsg - .as_ref() - .map(|msg| general_stream.0.send_raw(&msg)); - }); + subscribers.iter_mut().for_each(move |(client, _, _, _)| { + if entity_sync_lazymsg.is_none() { + entity_sync_lazymsg = Some(client.prepare(ServerGeneral::EntitySync( + entity_sync_package.take().unwrap(), + ))); + comp_sync_lazymsg = Some( + client.prepare(ServerGeneral::CompSync(comp_sync_package.take().unwrap())), + ); + } + entity_sync_lazymsg + .as_ref() + .map(|msg| client.send_prepared(&msg)); + comp_sync_lazymsg + .as_ref() + .map(|msg| client.send_prepared(&msg)); + }); let mut send_general = |msg: ServerGeneral, entity: EcsEntity, pos: Pos, force_update: Option<&ForceUpdate>, throttle: bool| { - for (_, _, client_entity, client_pos, _, general_stream) in &mut subscribers { + for (client, _, client_entity, client_pos) in &mut subscribers { if if client_entity == &entity { // Don't send client physics updates about itself unless force update is set force_update.is_some() @@ -262,7 +233,7 @@ impl<'a> System<'a> for Sys { true // Closer than 100 blocks } } { - general_stream.send_fallible(msg.clone()); + client.send_fallible(msg.clone()); } } }; @@ -349,18 +320,18 @@ impl<'a> System<'a> for Sys { // Handle entity deletion in regions that don't exist in RegionMap // (theoretically none) for (region_key, deleted) in deleted_entities.take_remaining_deleted() { - for general_stream in (presences.maybe(), &subscriptions, &mut general_streams) + for client in (presences.maybe(), &subscriptions, &clients) .join() - .filter_map(|(presence, subscription, general_stream)| { + .filter_map(|(presence, subscription, client)| { if presence.is_some() && subscription.regions.contains(®ion_key) { - Some(general_stream) + Some(client) } else { None } }) { for uid in &deleted { - general_stream.send_fallible(ServerGeneral::DeleteEntity(Uid(*uid))); + client.send_fallible(ServerGeneral::DeleteEntity(Uid(*uid))); } } } @@ -368,19 +339,15 @@ impl<'a> System<'a> for Sys { // TODO: Sync clients that don't have a position? // Sync inventories - for (inventory, update, in_game_stream) in - (&inventories, &inventory_updates, &mut in_game_streams).join() - { - in_game_stream.send_fallible(ServerGeneral::InventoryUpdate( + for (inventory, update, client) in (&inventories, &inventory_updates, &clients).join() { + client.send_fallible(ServerGeneral::InventoryUpdate( inventory.clone(), update.event(), )); } // Sync outcomes - for (presence, pos, in_game_stream) in - (presences.maybe(), positions.maybe(), &mut in_game_streams).join() - { + for (presence, pos, client) in (presences.maybe(), positions.maybe(), &clients).join() { let is_near = |o_pos: Vec3| { pos.zip_with(presence, |pos, presence| { pos.0.xy().distance_squared(o_pos.xy()) @@ -395,7 +362,7 @@ impl<'a> System<'a> for Sys { .cloned() .collect::>(); if !outcomes.is_empty() { - in_game_stream.send_fallible(ServerGeneral::Outcomes(outcomes)); + client.send_fallible(ServerGeneral::Outcomes(outcomes)); } } outcomes.clear(); @@ -408,13 +375,11 @@ impl<'a> System<'a> for Sys { // TODO: doesn't really belong in this system (rename system or create another // system?) let mut tof_lazymsg = None; - for general_stream in (&mut general_streams).join() { + for client in (&clients).join() { if tof_lazymsg.is_none() { - tof_lazymsg = Some(general_stream.prepare(&ServerGeneral::TimeOfDay(*time_of_day))); + tof_lazymsg = Some(client.prepare(ServerGeneral::TimeOfDay(*time_of_day))); } - tof_lazymsg - .as_ref() - .map(|msg| general_stream.0.send_raw(&msg)); + tof_lazymsg.as_ref().map(|msg| client.send_prepared(&msg)); } timer.end(); diff --git a/server/src/sys/invite_timeout.rs b/server/src/sys/invite_timeout.rs index ea913a1a1c..27819c317f 100644 --- a/server/src/sys/invite_timeout.rs +++ b/server/src/sys/invite_timeout.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::streams::{GetStream, InGameStream}; +use crate::client::Client; use common::{ comp::group::{Invite, PendingInvites}, msg::{InviteAnswer, ServerGeneral}, @@ -16,14 +16,14 @@ impl<'a> System<'a> for Sys { Entities<'a>, WriteStorage<'a, Invite>, WriteStorage<'a, PendingInvites>, - WriteStorage<'a, InGameStream>, + ReadStorage<'a, Client>, ReadStorage<'a, Uid>, Write<'a, SysTimer>, ); fn run( &mut self, - (entities, mut invites, mut pending_invites, mut in_game_streams, uids, mut timer): Self::SystemData, + (entities, mut invites, mut pending_invites, clients, uids, mut timer): Self::SystemData, ) { span!(_guard, "run", "invite_timeout::Sys::run"); timer.start(); @@ -51,11 +51,10 @@ impl<'a> System<'a> for Sys { } // Inform inviter of timeout - if let (Some(in_game_stream), Some(target)) = ( - in_game_streams.get_mut(*inviter), - uids.get(invitee).copied(), - ) { - in_game_stream.send_fallible(ServerGeneral::InviteComplete { + if let (Some(client), Some(target)) = + (clients.get(*inviter), uids.get(invitee).copied()) + { + client.send_fallible(ServerGeneral::InviteComplete { target, answer: InviteAnswer::TimedOut, }); diff --git a/server/src/sys/msg/character_screen.rs b/server/src/sys/msg/character_screen.rs index 2fb8b8a49c..6dae2482fb 100644 --- a/server/src/sys/msg/character_screen.rs +++ b/server/src/sys/msg/character_screen.rs @@ -1,22 +1,17 @@ use super::super::SysTimer; use crate::{ - alias_validator::AliasValidator, - character_creator, - client::Client, - persistence::character_loader::CharacterLoader, - presence::Presence, - streams::{CharacterScreenStream, GeneralStream, GetStream}, - EditableSettings, + alias_validator::AliasValidator, character_creator, client::Client, + persistence::character_loader::CharacterLoader, presence::Presence, EditableSettings, }; use common::{ comp::{ChatType, Player, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, msg::{ClientGeneral, ServerGeneral}, span, - state::Time, sync::Uid, }; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write}; +use std::sync::atomic::Ordering; use tracing::{debug, warn}; impl Sys { @@ -25,9 +20,7 @@ impl Sys { server_emitter: &mut common::event::Emitter<'_, ServerEvent>, new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, entity: specs::Entity, - client: &mut Client, - character_screen_stream: &mut CharacterScreenStream, - general_stream: &mut GeneralStream, + client: &Client, character_loader: &ReadExpect<'_, CharacterLoader>, uids: &ReadStorage<'_, Uid>, players: &ReadStorage<'_, Player>, @@ -68,27 +61,27 @@ impl Sys { // Give the player a welcome message if !editable_settings.server_description.is_empty() { - general_stream.send(ChatType::CommandInfo.server_msg(String::from( + client.send(ChatType::CommandInfo.server_msg(String::from( &*editable_settings.server_description, )))?; } - if !client.login_msg_sent { + if !client.login_msg_sent.load(Ordering::Relaxed) { if let Some(player_uid) = uids.get(entity) { new_chat_msgs.push((None, UnresolvedChatMsg { chat_type: ChatType::Online(*player_uid), message: "".to_string(), })); - client.login_msg_sent = true; + client.login_msg_sent.store(true, Ordering::Relaxed); } } } } else { debug!("Client is not yet registered"); - character_screen_stream.send(ServerGeneral::CharacterDataLoadError( - String::from("Failed to fetch player entity"), - ))? + client.send(ServerGeneral::CharacterDataLoadError(String::from( + "Failed to fetch player entity", + )))? } }, ClientGeneral::RequestCharacterList => { @@ -99,8 +92,7 @@ impl Sys { ClientGeneral::CreateCharacter { alias, tool, body } => { if let Err(error) = alias_validator.validate(&alias) { debug!(?error, ?alias, "denied alias as it contained a banned word"); - character_screen_stream - .send(ServerGeneral::CharacterActionError(error.to_string()))?; + client.send(ServerGeneral::CharacterActionError(error.to_string()))?; } else if let Some(player) = players.get(entity) { character_creator::create_character( entity, @@ -134,15 +126,12 @@ impl<'a> System<'a> for Sys { type SystemData = ( Entities<'a>, Read<'a, EventBus>, - Read<'a, Time>, ReadExpect<'a, CharacterLoader>, Write<'a, SysTimer>, ReadStorage<'a, Uid>, - WriteStorage<'a, Client>, + ReadStorage<'a, Client>, ReadStorage<'a, Player>, ReadStorage<'a, Presence>, - WriteStorage<'a, CharacterScreenStream>, - WriteStorage<'a, GeneralStream>, ReadExpect<'a, EditableSettings>, ReadExpect<'a, AliasValidator>, ); @@ -152,15 +141,12 @@ impl<'a> System<'a> for Sys { ( entities, server_event_bus, - time, character_loader, mut timer, uids, - mut clients, + clients, players, presences, - mut character_screen_streams, - mut general_streams, editable_settings, alias_validator, ): Self::SystemData, @@ -171,37 +157,22 @@ impl<'a> System<'a> for Sys { let mut server_emitter = server_event_bus.emitter(); let mut new_chat_msgs = Vec::new(); - for (entity, client, character_screen_stream, general_stream) in ( - &entities, - &mut clients, - &mut character_screen_streams, - &mut general_streams, - ) - .join() - { - let res = - super::try_recv_all(character_screen_stream, |character_screen_stream, msg| { - Self::handle_client_character_screen_msg( - &mut server_emitter, - &mut new_chat_msgs, - entity, - client, - character_screen_stream, - general_stream, - &character_loader, - &uids, - &players, - &presences, - &editable_settings, - &alias_validator, - msg, - ) - }); - - if let Ok(1_u64..=u64::MAX) = res { - // Update client ping. - client.last_ping = time.0 - } + for (entity, client) in (&entities, &clients).join() { + let _ = super::try_recv_all(client, 1, |client, msg| { + Self::handle_client_character_screen_msg( + &mut server_emitter, + &mut new_chat_msgs, + entity, + client, + &character_loader, + &uids, + &players, + &presences, + &editable_settings, + &alias_validator, + msg, + ) + }); } // Handle new chat messages. diff --git a/server/src/sys/msg/general.rs b/server/src/sys/msg/general.rs index dc58478cdb..8051da65b5 100644 --- a/server/src/sys/msg/general.rs +++ b/server/src/sys/msg/general.rs @@ -1,5 +1,5 @@ use super::super::SysTimer; -use crate::{client::Client, metrics::PlayerMetrics, streams::GeneralStream}; +use crate::{client::Client, metrics::PlayerMetrics}; use common::{ comp::{ChatMode, Player, UnresolvedChatMsg}, event::{EventBus, ServerEvent}, @@ -8,7 +8,8 @@ use common::{ state::Time, sync::Uid, }; -use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write}; +use std::sync::atomic::Ordering; use tracing::{debug, error, warn}; impl Sys { @@ -17,7 +18,7 @@ impl Sys { server_emitter: &mut common::event::Emitter<'_, ServerEvent>, new_chat_msgs: &mut Vec<(Option, UnresolvedChatMsg)>, entity: specs::Entity, - client: &mut Client, + client: &Client, player: Option<&Player>, player_metrics: &ReadExpect<'_, PlayerMetrics>, uids: &ReadStorage<'_, Uid>, @@ -51,7 +52,7 @@ impl Sys { .clients_disconnected .with_label_values(&["gracefully"]) .inc(); - client.terminate_msg_recv = true; + client.terminate_msg_recv.store(true, Ordering::Relaxed); server_emitter.emit(ServerEvent::ClientDisconnect(entity)); }, _ => unreachable!("not a client_general msg"), @@ -73,8 +74,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Uid>, ReadStorage<'a, ChatMode>, ReadStorage<'a, Player>, - WriteStorage<'a, Client>, - WriteStorage<'a, GeneralStream>, + ReadStorage<'a, Client>, ); fn run( @@ -88,8 +88,7 @@ impl<'a> System<'a> for Sys { uids, chat_modes, players, - mut clients, - mut general_streams, + clients, ): Self::SystemData, ) { span!(_guard, "run", "msg::general::Sys::run"); @@ -98,15 +97,8 @@ impl<'a> System<'a> for Sys { let mut server_emitter = server_event_bus.emitter(); let mut new_chat_msgs = Vec::new(); - for (entity, client, player, general_stream) in ( - &entities, - &mut clients, - (&players).maybe(), - &mut general_streams, - ) - .join() - { - let res = super::try_recv_all(general_stream, |_, msg| { + for (entity, client, player) in (&entities, &clients, (&players).maybe()).join() { + let res = super::try_recv_all(client, 3, |client, msg| { Self::handle_general_msg( &mut server_emitter, &mut new_chat_msgs, @@ -122,7 +114,7 @@ impl<'a> System<'a> for Sys { if let Ok(1_u64..=u64::MAX) = res { // Update client ping. - client.last_ping = time.0 + *client.last_ping.lock().unwrap() = time.0 } } diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index 8d3ddd0248..ae49d6d8e2 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -1,17 +1,11 @@ use super::super::SysTimer; -use crate::{ - client::Client, - metrics::NetworkRequestMetrics, - presence::Presence, - streams::{GetStream, InGameStream}, - Settings, -}; +use crate::{client::Client, metrics::NetworkRequestMetrics, presence::Presence, Settings}; use common::{ comp::{CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Pos, Stats, Vel}, event::{EventBus, ServerEvent}, msg::{ClientGeneral, PresenceKind, ServerGeneral}, span, - state::{BlockChange, Time}, + state::BlockChange, terrain::{TerrainChunkSize, TerrainGrid}, vol::{ReadVol, RectVolSize}, }; @@ -23,9 +17,8 @@ impl Sys { fn handle_client_in_game_msg( server_emitter: &mut common::event::Emitter<'_, ServerEvent>, entity: specs::Entity, - _client: &Client, + client: &Client, maybe_presence: &mut Option<&mut Presence>, - in_game_stream: &mut InGameStream, terrain: &ReadExpect<'_, TerrainGrid>, network_metrics: &ReadExpect<'_, NetworkRequestMetrics>, can_build: &ReadStorage<'_, CanBuild>, @@ -54,7 +47,7 @@ impl Sys { // Go back to registered state (char selection screen) ClientGeneral::ExitInGame => { server_emitter.emit(ServerEvent::ExitIngame { entity }); - in_game_stream.send(ServerGeneral::ExitInGameSuccess)?; + client.send(ServerGeneral::ExitInGameSuccess)?; *maybe_presence = None; }, ClientGeneral::SetViewDistance(view_distance) => { @@ -69,7 +62,7 @@ impl Sys { .map(|max| view_distance > max) .unwrap_or(false) { - in_game_stream.send(ServerGeneral::SetViewDistance( + client.send(ServerGeneral::SetViewDistance( settings.max_view_distance.unwrap_or(0), ))?; } @@ -135,7 +128,7 @@ impl Sys { match terrain.get_key(key) { Some(chunk) => { network_metrics.chunks_served_from_memory.inc(); - in_game_stream.send(ServerGeneral::TerrainChunkUpdate { + client.send(ServerGeneral::TerrainChunkUpdate { key, chunk: Ok(Box::new(chunk.clone())), })? @@ -177,7 +170,6 @@ impl<'a> System<'a> for Sys { type SystemData = ( Entities<'a>, Read<'a, EventBus>, - Read<'a, Time>, ReadExpect<'a, TerrainGrid>, ReadExpect<'a, NetworkRequestMetrics>, Write<'a, SysTimer>, @@ -190,7 +182,6 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Ori>, WriteStorage<'a, Presence>, WriteStorage<'a, Client>, - WriteStorage<'a, InGameStream>, WriteStorage<'a, Controller>, Read<'a, Settings>, ); @@ -200,7 +191,6 @@ impl<'a> System<'a> for Sys { ( entities, server_event_bus, - time, terrain, network_metrics, mut timer, @@ -213,7 +203,6 @@ impl<'a> System<'a> for Sys { mut orientations, mut presences, mut clients, - mut in_game_streams, mut controllers, settings, ): Self::SystemData, @@ -223,21 +212,15 @@ impl<'a> System<'a> for Sys { let mut server_emitter = server_event_bus.emitter(); - for (entity, client, mut presence, in_game_stream) in ( - &entities, - &mut clients, - (&mut presences).maybe(), - &mut in_game_streams, - ) - .join() + for (entity, client, mut maybe_presence) in + (&entities, &mut clients, (&mut presences).maybe()).join() { - let res = super::try_recv_all(in_game_stream, |in_game_stream, msg| { + let _ = super::try_recv_all(client, 2, |client, msg| { Self::handle_client_in_game_msg( &mut server_emitter, entity, client, - &mut presence, - in_game_stream, + &mut maybe_presence, &terrain, &network_metrics, &can_build, @@ -252,11 +235,6 @@ impl<'a> System<'a> for Sys { msg, ) }); - - if let Ok(1_u64..=u64::MAX) = res { - // Update client ping. - client.last_ping = time.0 - } } timer.end() diff --git a/server/src/sys/msg/mod.rs b/server/src/sys/msg/mod.rs index 54aa60a9d4..0242d83794 100644 --- a/server/src/sys/msg/mod.rs +++ b/server/src/sys/msg/mod.rs @@ -4,26 +4,28 @@ pub mod in_game; pub mod ping; pub mod register; -use crate::streams::GetStream; +use crate::client::Client; +use serde::de::DeserializeOwned; /// handles all send msg and calls a handle fn /// Aborts when a error occurred returns cnt of successful msg otherwise -pub(in crate::sys::msg) fn try_recv_all( - stream: &mut T, +pub(in crate::sys::msg) fn try_recv_all( + client: &Client, + stream_id: u8, mut f: F, ) -> Result where - T: GetStream, - F: FnMut(&mut T, T::RecvMsg) -> Result<(), crate::error::Error>, + M: DeserializeOwned, + F: FnMut(&Client, M) -> Result<(), crate::error::Error>, { let mut cnt = 0u64; loop { - let msg = match stream.get_mut().try_recv() { + let msg = match client.recv(stream_id) { Ok(Some(msg)) => msg, Ok(None) => break Ok(cnt), Err(e) => break Err(e.into()), }; - if let Err(e) = f(stream, msg) { + if let Err(e) = f(client, msg) { break Err(e); } cnt += 1; diff --git a/server/src/sys/msg/ping.rs b/server/src/sys/msg/ping.rs index c2fc8ea915..aa476ac2ae 100644 --- a/server/src/sys/msg/ping.rs +++ b/server/src/sys/msg/ping.rs @@ -1,26 +1,19 @@ use super::super::SysTimer; -use crate::{ - client::Client, - metrics::PlayerMetrics, - streams::{GetStream, PingStream}, - Settings, -}; +use crate::{client::Client, metrics::PlayerMetrics, Settings}; use common::{ event::{EventBus, ServerEvent}, msg::PingMsg, span, state::Time, }; -use specs::{Entities, Join, Read, ReadExpect, System, Write, WriteStorage}; +use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write}; +use std::sync::atomic::Ordering; use tracing::{debug, info}; impl Sys { - fn handle_ping_msg( - ping_stream: &mut PingStream, - msg: PingMsg, - ) -> Result<(), crate::error::Error> { + fn handle_ping_msg(client: &Client, msg: PingMsg) -> Result<(), crate::error::Error> { match msg { - PingMsg::Ping => ping_stream.send(PingMsg::Pong)?, + PingMsg::Ping => client.send(PingMsg::Pong)?, PingMsg::Pong => {}, } Ok(()) @@ -37,8 +30,7 @@ impl<'a> System<'a> for Sys { Read<'a, Time>, ReadExpect<'a, PlayerMetrics>, Write<'a, SysTimer>, - WriteStorage<'a, Client>, - WriteStorage<'a, PingStream>, + ReadStorage<'a, Client>, Read<'a, Settings>, ); @@ -50,8 +42,7 @@ impl<'a> System<'a> for Sys { time, player_metrics, mut timer, - mut clients, - mut ping_streams, + clients, settings, ): Self::SystemData, ) { @@ -60,14 +51,12 @@ impl<'a> System<'a> for Sys { let mut server_emitter = server_event_bus.emitter(); - for (entity, client, ping_stream) in (&entities, &mut clients, &mut ping_streams).join() { - let res = super::try_recv_all(ping_stream, |ping_stream, msg| { - Self::handle_ping_msg(ping_stream, msg) - }); + for (entity, client) in (&entities, &clients).join() { + let res = super::try_recv_all(client, 4, Self::handle_ping_msg); match res { Err(e) => { - if !client.terminate_msg_recv { + if !client.terminate_msg_recv.load(Ordering::Relaxed) { debug!(?entity, ?e, "network error with client, disconnecting"); player_metrics .clients_disconnected @@ -78,13 +67,14 @@ impl<'a> System<'a> for Sys { }, Ok(1_u64..=u64::MAX) => { // Update client ping. - client.last_ping = time.0 + *client.last_ping.lock().unwrap() = time.0 }, Ok(0) => { - if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 + let last_ping: f64 = *client.last_ping.lock().unwrap(); + if time.0 - last_ping > settings.client_timeout.as_secs() as f64 // Timeout { - if !client.terminate_msg_recv { + if !client.terminate_msg_recv.load(Ordering::Relaxed) { info!(?entity, "timeout error with client, disconnecting"); player_metrics .clients_disconnected @@ -92,11 +82,9 @@ impl<'a> System<'a> for Sys { .inc(); server_emitter.emit(ServerEvent::ClientDisconnect(entity)); } - } else if time.0 - client.last_ping - > settings.client_timeout.as_secs() as f64 * 0.5 - { + } else if time.0 - last_ping > settings.client_timeout.as_secs() as f64 * 0.5 { // Try pinging the client if the timeout is nearing. - ping_stream.send_fallible(PingMsg::Ping); + client.send_fallible(PingMsg::Ping); } }, } diff --git a/server/src/sys/msg/register.rs b/server/src/sys/msg/register.rs index 219b3b81e9..d072ada6c8 100644 --- a/server/src/sys/msg/register.rs +++ b/server/src/sys/msg/register.rs @@ -1,10 +1,6 @@ use super::super::SysTimer; use crate::{ - client::Client, - login_provider::LoginProvider, - metrics::PlayerMetrics, - streams::{GeneralStream, GetStream, RegisterStream}, - EditableSettings, + client::Client, login_provider::LoginProvider, metrics::PlayerMetrics, EditableSettings, }; use common::{ comp::{Admin, Player, Stats}, @@ -13,13 +9,10 @@ use common::{ ServerRegisterAnswer, }, span, - state::Time, sync::Uid, }; use hashbrown::HashMap; -use specs::{ - Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, -}; +use specs::{Entities, Join, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage}; impl Sys { #[allow(clippy::too_many_arguments)] @@ -27,9 +20,7 @@ impl Sys { player_list: &HashMap, new_players: &mut Vec, entity: specs::Entity, - _client: &mut Client, - register_stream: &mut RegisterStream, - general_stream: &mut GeneralStream, + client: &Client, player_metrics: &ReadExpect<'_, PlayerMetrics>, login_provider: &mut WriteExpect<'_, LoginProvider>, admins: &mut WriteStorage<'_, Admin>, @@ -44,7 +35,7 @@ impl Sys { &*editable_settings.banlist, ) { Err(err) => { - register_stream.send(ServerRegisterAnswer::Err(err))?; + client.send(ServerRegisterAnswer::Err(err))?; return Ok(()); }, Ok((username, uuid)) => (username, uuid), @@ -55,7 +46,7 @@ impl Sys { if !player.is_valid() { // Invalid player - register_stream.send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?; + client.send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?; return Ok(()); } @@ -71,10 +62,10 @@ impl Sys { } // Tell the client its request was successful. - register_stream.send(ServerRegisterAnswer::Ok(()))?; + client.send(ServerRegisterAnswer::Ok(()))?; // Send initial player list - general_stream.send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init( + client.send(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init( player_list.clone(), )))?; @@ -92,17 +83,14 @@ impl<'a> System<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, - Read<'a, Time>, ReadExpect<'a, PlayerMetrics>, Write<'a, SysTimer>, ReadStorage<'a, Uid>, - WriteStorage<'a, Client>, + ReadStorage<'a, Client>, WriteStorage<'a, Player>, ReadStorage<'a, Stats>, WriteExpect<'a, LoginProvider>, WriteStorage<'a, Admin>, - WriteStorage<'a, RegisterStream>, - WriteStorage<'a, GeneralStream>, ReadExpect<'a, EditableSettings>, ); @@ -110,17 +98,14 @@ impl<'a> System<'a> for Sys { &mut self, ( entities, - time, player_metrics, mut timer, uids, - mut clients, + clients, mut players, stats, mut login_provider, mut admins, - mut register_streams, - mut general_streams, editable_settings, ): Self::SystemData, ) { @@ -145,22 +130,13 @@ impl<'a> System<'a> for Sys { // List of new players to update player lists of all clients. let mut new_players = Vec::new(); - for (entity, client, register_stream, general_stream) in ( - &entities, - &mut clients, - &mut register_streams, - &mut general_streams, - ) - .join() - { - let res = super::try_recv_all(register_stream, |register_stream, msg| { + for (entity, client) in (&entities, &clients).join() { + let _ = super::try_recv_all(client, 0, |client, msg| { Self::handle_register_msg( &player_list, &mut new_players, entity, client, - register_stream, - general_stream, &player_metrics, &mut login_provider, &mut admins, @@ -169,11 +145,6 @@ impl<'a> System<'a> for Sys { msg, ) }); - - if let Ok(1_u64..=u64::MAX) = res { - // Update client ping. - client.last_ping = time.0 - } } // Handle new players. @@ -181,9 +152,9 @@ impl<'a> System<'a> for Sys { for entity in new_players { if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) { let mut lazy_msg = None; - for (_, general_stream) in (&players, &mut general_streams).join() { + for (_, client) in (&players, &clients).join() { if lazy_msg.is_none() { - lazy_msg = Some(general_stream.prepare(&ServerGeneral::PlayerListUpdate( + lazy_msg = Some(client.prepare(ServerGeneral::PlayerListUpdate( PlayerListUpdate::Add(*uid, PlayerInfo { player_alias: player.alias.clone(), is_online: true, @@ -192,9 +163,7 @@ impl<'a> System<'a> for Sys { }), ))); } - lazy_msg - .as_ref() - .map(|ref msg| general_stream.0.send_raw(&msg)); + lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg)); } } } diff --git a/server/src/sys/subscription.rs b/server/src/sys/subscription.rs index 66d9992e03..b1fa4e4d71 100644 --- a/server/src/sys/subscription.rs +++ b/server/src/sys/subscription.rs @@ -5,7 +5,6 @@ use super::{ use crate::{ client::Client, presence::{self, Presence, RegionSubscription}, - streams::{GeneralStream, GetStream}, }; use common::{ comp::{Ori, Pos, Vel}, @@ -37,7 +36,6 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Ori>, ReadStorage<'a, Presence>, ReadStorage<'a, Client>, - WriteStorage<'a, GeneralStream>, WriteStorage<'a, RegionSubscription>, Write<'a, DeletedEntities>, TrackedComps<'a>, @@ -55,8 +53,7 @@ impl<'a> System<'a> for Sys { velocities, orientations, presences, - _clients, - mut general_streams, + clients, mut subscriptions, mut deleted_entities, tracked_comps, @@ -77,12 +74,12 @@ impl<'a> System<'a> for Sys { // 7. Determine list of regions that are in range and iterate through it // - check if in hashset (hash calc) if not add it let mut regions_to_remove = Vec::new(); - for (subscription, pos, presence, client_entity, general_stream) in ( + for (subscription, pos, presence, client_entity, client) in ( &mut subscriptions, &positions, &presences, &entities, - &mut general_streams, + &clients, ) .join() { @@ -155,8 +152,7 @@ impl<'a> System<'a> for Sys { .map(|key| subscription.regions.contains(key)) .unwrap_or(false) { - general_stream - .send_fallible(ServerGeneral::DeleteEntity(uid)); + client.send_fallible(ServerGeneral::DeleteEntity(uid)); } } }, @@ -164,7 +160,7 @@ impl<'a> System<'a> for Sys { } // Tell client to delete entities in the region for (&uid, _) in (&uids, region.entities()).join() { - let _ = general_stream.send(ServerGeneral::DeleteEntity(uid)); + client.send_fallible(ServerGeneral::DeleteEntity(uid)); } } // Send deleted entities since they won't be processed for this client in entity @@ -174,7 +170,7 @@ impl<'a> System<'a> for Sys { .iter() .flat_map(|v| v.iter()) { - general_stream.send_fallible(ServerGeneral::DeleteEntity(Uid(*uid))); + client.send_fallible(ServerGeneral::DeleteEntity(Uid(*uid))); } } @@ -199,7 +195,7 @@ impl<'a> System<'a> for Sys { { // Send message to create entity and tracked components and physics // components - general_stream.send_fallible(ServerGeneral::CreateEntity( + client.send_fallible(ServerGeneral::CreateEntity( tracked_comps.create_entity_package( entity, Some(*pos), @@ -220,10 +216,10 @@ impl<'a> System<'a> for Sys { /// Initialize region subscription pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { - if let (Some(client_pos), Some(presence), Some(general_stream)) = ( + if let (Some(client_pos), Some(presence), Some(client)) = ( world.read_storage::().get(entity), world.read_storage::().get(entity), - world.write_storage::().get_mut(entity), + world.write_storage::().get(entity), ) { let fuzzy_chunk = (Vec2::::from(client_pos.0)) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as i32 / sz as i32); @@ -248,7 +244,7 @@ pub fn initialize_region_subscription(world: &World, entity: specs::Entity) { .join() { // Send message to create entity and tracked components and physics components - general_stream.send_fallible(ServerGeneral::CreateEntity( + client.send_fallible(ServerGeneral::CreateEntity( tracked_comps.create_entity_package( entity, Some(*pos), diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 5b169ab18a..b2da237a3c 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -1,10 +1,5 @@ use super::SysTimer; -use crate::{ - chunk_generator::ChunkGenerator, - presence::Presence, - streams::{GetStream, InGameStream}, - Tick, -}; +use crate::{chunk_generator::ChunkGenerator, client::Client, presence::Presence, Tick}; use common::{ comp::{self, bird_medium, Alignment, Pos}, event::{EventBus, ServerEvent}, @@ -17,7 +12,7 @@ use common::{ LoadoutBuilder, }; use rand::Rng; -use specs::{Join, Read, ReadStorage, System, Write, WriteExpect, WriteStorage}; +use specs::{Join, Read, ReadStorage, System, Write, WriteExpect}; use std::sync::Arc; use vek::*; @@ -39,7 +34,7 @@ impl<'a> System<'a> for Sys { Write<'a, TerrainChanges>, ReadStorage<'a, Pos>, ReadStorage<'a, Presence>, - WriteStorage<'a, InGameStream>, + ReadStorage<'a, Client>, ); fn run( @@ -53,7 +48,7 @@ impl<'a> System<'a> for Sys { mut terrain_changes, positions, presences, - mut in_game_streams, + clients, ): Self::SystemData, ) { span!(_guard, "run", "terrain::Sys::run"); @@ -67,8 +62,8 @@ impl<'a> System<'a> for Sys { let (chunk, supplement) = match res { Ok((chunk, supplement)) => (chunk, supplement), Err(Some(entity)) => { - if let Some(in_game_stream) = in_game_streams.get_mut(entity) { - in_game_stream.send_fallible(ServerGeneral::TerrainChunkUpdate { + if let Some(client) = clients.get(entity) { + client.send_fallible(ServerGeneral::TerrainChunkUpdate { key, chunk: Err(()), }); @@ -80,9 +75,7 @@ impl<'a> System<'a> for Sys { }, }; // Send the chunk to all nearby players. - for (presence, pos, in_game_stream) in - (&presences, &positions, &mut in_game_streams).join() - { + for (presence, pos, client) in (&presences, &positions, &clients).join() { let chunk_pos = terrain.pos_key(pos.0.map(|e| e as i32)); // Subtract 2 from the offset before computing squared magnitude // 1 since chunks need neighbors to be meshed @@ -92,7 +85,7 @@ impl<'a> System<'a> for Sys { .magnitude_squared(); if adjusted_dist_sqr <= presence.view_distance.pow(2) { - in_game_stream.send_fallible(ServerGeneral::TerrainChunkUpdate { + client.send_fallible(ServerGeneral::TerrainChunkUpdate { key, chunk: Ok(Box::new(chunk.clone())), }); diff --git a/server/src/sys/terrain_sync.rs b/server/src/sys/terrain_sync.rs index a04c55fd25..900248b6f1 100644 --- a/server/src/sys/terrain_sync.rs +++ b/server/src/sys/terrain_sync.rs @@ -1,10 +1,7 @@ use super::SysTimer; -use crate::{ - presence::Presence, - streams::{GetStream, InGameStream}, -}; +use crate::{client::Client, presence::Presence}; use common::{comp::Pos, msg::ServerGeneral, span, state::TerrainChanges, terrain::TerrainGrid}; -use specs::{Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage}; +use specs::{Join, Read, ReadExpect, ReadStorage, System, Write}; /// This systems sends new chunks to clients as well as changes to existing /// chunks @@ -17,12 +14,12 @@ impl<'a> System<'a> for Sys { Write<'a, SysTimer>, ReadStorage<'a, Pos>, ReadStorage<'a, Presence>, - WriteStorage<'a, InGameStream>, + ReadStorage<'a, Client>, ); fn run( &mut self, - (terrain, terrain_changes, mut timer, positions, presences, mut in_game_streams): Self::SystemData, + (terrain, terrain_changes, mut timer, positions, presences, clients): Self::SystemData, ) { span!(_guard, "run", "terrain_sync::Sys::run"); timer.start(); @@ -31,24 +28,19 @@ impl<'a> System<'a> for Sys { 'chunk: for chunk_key in &terrain_changes.modified_chunks { let mut lazy_msg = None; - for (presence, pos, in_game_stream) in - (&presences, &positions, &mut in_game_streams).join() - { + for (presence, pos, client) in (&presences, &positions, &clients).join() { if super::terrain::chunk_in_vd(pos.0, *chunk_key, &terrain, presence.view_distance) { if lazy_msg.is_none() { - lazy_msg = - Some(in_game_stream.prepare(&ServerGeneral::TerrainChunkUpdate { - key: *chunk_key, - chunk: Ok(Box::new(match terrain.get_key(*chunk_key) { - Some(chunk) => chunk.clone(), - None => break 'chunk, - })), - })); + lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate { + key: *chunk_key, + chunk: Ok(Box::new(match terrain.get_key(*chunk_key) { + Some(chunk) => chunk.clone(), + None => break 'chunk, + })), + })); } - lazy_msg - .as_ref() - .map(|ref msg| in_game_stream.0.send_raw(&msg)); + lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg)); } } } @@ -56,15 +48,13 @@ impl<'a> System<'a> for Sys { // TODO: Don't send all changed blocks to all clients // Sync changed blocks let mut lazy_msg = None; - for (_, in_game_stream) in (&presences, &mut in_game_streams).join() { + for (_, client) in (&presences, &clients).join() { if lazy_msg.is_none() { - lazy_msg = Some(in_game_stream.prepare(&ServerGeneral::TerrainBlockUpdates( + lazy_msg = Some(client.prepare(ServerGeneral::TerrainBlockUpdates( terrain_changes.modified_blocks.clone(), ))); } - lazy_msg - .as_ref() - .map(|ref msg| in_game_stream.0.send_raw(&msg)); + lazy_msg.as_ref().map(|ref msg| client.send_prepared(&msg)); } timer.end(); diff --git a/server/src/sys/waypoint.rs b/server/src/sys/waypoint.rs index 237391b964..89e16be7c5 100644 --- a/server/src/sys/waypoint.rs +++ b/server/src/sys/waypoint.rs @@ -1,5 +1,5 @@ use super::SysTimer; -use crate::streams::{GeneralStream, GetStream}; +use crate::client::Client; use common::{ comp::{Player, Pos, Waypoint, WaypointArea}, msg::{Notification, ServerGeneral}, @@ -22,7 +22,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Player>, ReadStorage<'a, WaypointArea>, WriteStorage<'a, Waypoint>, - WriteStorage<'a, GeneralStream>, + ReadStorage<'a, Client>, Read<'a, Time>, Write<'a, SysTimer>, ); @@ -35,7 +35,7 @@ impl<'a> System<'a> for Sys { players, waypoint_areas, mut waypoints, - mut general_streams, + clients, time, mut timer, ): Self::SystemData, @@ -43,15 +43,13 @@ impl<'a> System<'a> for Sys { span!(_guard, "run", "waypoint::Sys::run"); timer.start(); - for (entity, player_pos, _, general_stream) in - (&entities, &positions, &players, &mut general_streams).join() - { + for (entity, player_pos, _, client) in (&entities, &positions, &players, &clients).join() { for (waypoint_pos, waypoint_area) in (&positions, &waypoint_areas).join() { if player_pos.0.distance_squared(waypoint_pos.0) < waypoint_area.radius().powi(2) { if let Ok(wp_old) = waypoints.insert(entity, Waypoint::new(player_pos.0, *time)) { if wp_old.map_or(true, |w| w.elapsed(*time) > NOTIFY_TIME) { - general_stream.send_fallible(ServerGeneral::Notification( + client.send_fallible(ServerGeneral::Notification( Notification::WaypointSaved, )); }