veloren/server/src/client.rs
2023-03-11 16:45:10 -05:00

263 lines
11 KiB
Rust

use common_net::msg::{ClientType, ServerGeneral, ServerMsg};
use network::{Message, Participant, Stream, StreamError, StreamParams};
use serde::{de::DeserializeOwned, Serialize};
use specs::Component;
use std::sync::atomic::AtomicBool;
/// 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 client_type: ClientType,
pub participant: Option<Participant>,
pub last_ping: f64,
pub login_msg_sent: AtomicBool,
//TODO: Consider splitting each of these out into their own components so all the message
//processing systems can run in parallel with each other (though it may turn out not to
//matter that much).
general_stream: Stream,
ping_stream: Stream,
register_stream: Stream,
character_screen_stream: Stream,
in_game_stream: Stream,
terrain_stream: Stream,
general_stream_params: StreamParams,
ping_stream_params: StreamParams,
register_stream_params: StreamParams,
character_screen_stream_params: StreamParams,
in_game_stream_params: StreamParams,
terrain_stream_params: StreamParams,
}
pub struct PreparedMsg {
stream_id: u8,
message: Message,
}
impl Component for Client {
type Storage = specs::DenseVecStorage<Self>;
}
impl Client {
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,
terrain_stream: Stream,
) -> Self {
let general_stream_params = general_stream.params();
let ping_stream_params = ping_stream.params();
let register_stream_params = register_stream.params();
let character_screen_stream_params = character_screen_stream.params();
let in_game_stream_params = in_game_stream.params();
let terrain_stream_params = terrain_stream.params();
Client {
client_type,
participant: Some(participant),
last_ping,
login_msg_sent: AtomicBool::new(false),
general_stream,
ping_stream,
register_stream,
character_screen_stream,
in_game_stream,
terrain_stream,
general_stream_params,
ping_stream_params,
register_stream_params,
character_screen_stream_params,
in_game_stream_params,
terrain_stream_params,
}
}
pub(crate) fn send<M: Into<ServerMsg>>(&self, msg: M) -> Result<(), StreamError> {
// TODO: hack to avoid locking stream mutex while serializing the message,
// remove this when the mutexes on the Streams are removed
let prepared = self.prepare(msg);
self.send_prepared(&prepared)
/*match msg.into() {
ServerMsg::Info(m) => self.register_stream.lock().unwrap().send(m),
ServerMsg::Init(m) => self.register_stream.lock().unwrap().send(m),
ServerMsg::RegisterAnswer(m) => self.register_stream.lock().unwrap().send(m),
ServerMsg::General(g) => {
match g {
//Character Screen related
ServerGeneral::CharacterDataLoadResult(_)
| ServerGeneral::CharacterListUpdate(_)
| ServerGeneral::CharacterActionError(_)
| ServerGeneral::CharacterCreated(_)
| ServerGeneral::CharacterEdited(_)
| ServerGeneral::CharacterSuccess => {
self.character_screen_stream.lock().unwrap().send(g)
},
//In-game related
ServerGeneral::GroupUpdate(_)
| ServerGeneral::Invite { .. }
| ServerGeneral::InvitePending(_)
| ServerGeneral::InviteComplete { .. }
| ServerGeneral::ExitInGameSuccess
| ServerGeneral::InventoryUpdate(_, _)
| ServerGeneral::SetViewDistance(_)
| ServerGeneral::SiteEconomy(_)
| ServerGeneral::Outcomes(_)
| ServerGeneral::Knockback(_)
| ServerGeneral::UpdatePendingTrade(_, _, _)
| ServerGeneral::FinishedTrade(_)
| ServerGeneral::WeatherUpdate(_) => {
self.in_game_stream.lock().unwrap().send(g)
},
//Ingame related, terrain
ServerGeneral::TerrainChunkUpdate { .. }
| ServerGeneral::LodZoneUpdate { .. }
| ServerGeneral::TerrainBlockUpdates(_) => {
self.terrain_stream.lock().unwrap().send(g)
},
// Always possible
ServerGeneral::PlayerListUpdate(_)
| ServerGeneral::ChatMsg(_)
| ServerGeneral::ChatMode(_)
| ServerGeneral::SetPlayerEntity(_)
| ServerGeneral::TimeOfDay(_, _)
| ServerGeneral::EntitySync(_)
| ServerGeneral::CompSync(_)
| ServerGeneral::CreateEntity(_)
| ServerGeneral::DeleteEntity(_)
| ServerGeneral::Disconnect(_)
| ServerGeneral::Notification(_) => self.general_stream.lock().unwrap().send(g),
}
},
ServerMsg::Ping(m) => self.ping_stream.lock().unwrap().send(m),
}*/
}
/// Like `send` but any errors are explicitly ignored.
pub(crate) fn send_fallible<M: Into<ServerMsg>>(&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.send_raw(&msg.message),
1 => self.character_screen_stream.send_raw(&msg.message),
2 => self.in_game_stream.send_raw(&msg.message),
3 => self.general_stream.send_raw(&msg.message),
4 => self.ping_stream.send_raw(&msg.message),
5 => self.terrain_stream.send_raw(&msg.message),
_ => unreachable!("invalid stream id"),
}
}
pub(crate) fn prepare<M: Into<ServerMsg>>(&self, msg: M) -> PreparedMsg {
match msg.into() {
ServerMsg::Info(m) => PreparedMsg::new(0, &m, &self.register_stream_params),
ServerMsg::Init(m) => PreparedMsg::new(0, &m, &self.register_stream_params),
ServerMsg::RegisterAnswer(m) => PreparedMsg::new(0, &m, &self.register_stream_params),
ServerMsg::General(g) => {
match g {
//Character Screen related
ServerGeneral::CharacterDataLoadResult(_)
| ServerGeneral::CharacterListUpdate(_)
| ServerGeneral::CharacterActionError(_)
| ServerGeneral::CharacterCreated(_)
| ServerGeneral::CharacterEdited(_)
| ServerGeneral::CharacterSuccess
| ServerGeneral::SpectatorSuccess(_) => {
PreparedMsg::new(1, &g, &self.character_screen_stream_params)
},
//In-game related
ServerGeneral::GroupUpdate(_)
| ServerGeneral::Invite { .. }
| ServerGeneral::InvitePending(_)
| ServerGeneral::InviteComplete { .. }
| ServerGeneral::ExitInGameSuccess
| ServerGeneral::InventoryUpdate(_, _)
| ServerGeneral::GroupInventoryUpdate(_, _, _)
| ServerGeneral::SetViewDistance(_)
| ServerGeneral::Outcomes(_)
| ServerGeneral::Knockback(_)
| ServerGeneral::SiteEconomy(_)
| ServerGeneral::UpdatePendingTrade(_, _, _)
| ServerGeneral::FinishedTrade(_)
| ServerGeneral::MapMarker(_)
| ServerGeneral::WeatherUpdate(_)
| ServerGeneral::SpectatePosition(_) => {
PreparedMsg::new(2, &g, &self.in_game_stream_params)
},
//In-game related, terrain
ServerGeneral::TerrainChunkUpdate { .. }
| ServerGeneral::LodZoneUpdate { .. }
| ServerGeneral::TerrainBlockUpdates(_) => {
PreparedMsg::new(5, &g, &self.terrain_stream_params)
},
// Always possible
ServerGeneral::PlayerListUpdate(_)
| ServerGeneral::ChatMsg(_)
| ServerGeneral::ChatMode(_)
| ServerGeneral::SetPlayerEntity(_)
| ServerGeneral::TimeOfDay(_, _, _)
| ServerGeneral::EntitySync(_)
| ServerGeneral::CompSync(_, _)
| ServerGeneral::CreateEntity(_)
| ServerGeneral::DeleteEntity(_)
| ServerGeneral::Disconnect(_)
| ServerGeneral::Notification(_) => {
PreparedMsg::new(3, &g, &self.general_stream_params)
},
}
},
ServerMsg::Ping(m) => PreparedMsg::new(4, &m, &self.ping_stream_params),
}
}
pub(crate) fn terrain_params(&self) -> StreamParams { self.terrain_stream_params.clone() }
/// Only used for Serialize Chunks in a SlowJob.
/// TODO: find a more elegant version for this invariant
pub(crate) fn prepare_chunk_update_msg(
terrain_chunk_update: ServerGeneral,
params: &StreamParams,
) -> PreparedMsg {
if !matches!(
terrain_chunk_update,
ServerGeneral::TerrainChunkUpdate { .. }
) {
unreachable!("You must not call this function without a terrain chunk update!")
}
PreparedMsg::new(5, &terrain_chunk_update, params)
}
pub(crate) fn recv<M: DeserializeOwned>(
&mut self,
stream_id: u8,
) -> Result<Option<M>, StreamError> {
// TODO: are two systems using the same stream?? why is there contention here?
match stream_id {
0 => self.register_stream.try_recv(),
1 => self.character_screen_stream.try_recv(),
2 => self.in_game_stream.try_recv(),
3 => self.general_stream.try_recv(),
4 => self.ping_stream.try_recv(),
5 => self.terrain_stream.try_recv(),
_ => unreachable!("invalid stream id"),
}
}
}
impl PreparedMsg {
fn new<M: Serialize + ?Sized>(id: u8, msg: &M, stream_params: &StreamParams) -> PreparedMsg {
Self {
stream_id: id,
message: Message::serialize(&msg, stream_params.clone()),
}
}
}