use std::error::Error; use std::net::SocketAddr; use futures::stream::{SplitSink, SplitStream}; use futures::{FutureExt, SinkExt, StreamExt}; use hyper::upgrade::Upgraded; use log::{error, info}; use tokio::sync::mpsc::Receiver; use tokio_tungstenite::WebSocketStream; use tungstenite::Message; use protocol::{GoodbyeReason, MessageC2S, MessageS2C, PROTOCOL_VERSION, ps2c, recv, send, State}; use crate::handler::{ClientHandlerMessage, ClientManager}; pub async fn handle_client(mgr: ClientManager, remote_addr: SocketAddr, mut rx: Receiver, mut client_tx: SplitSink, Message>, mut client_rx: SplitStream>) -> Result<(), Box> { let mut state = State::Handshake; loop { if let Some(msg) = rx.recv().await { match msg { ClientHandlerMessage::Tick => {} // this intentionally does nothing } } else { info!("channel closed, shutting down"); break; } if let Some(pkt) = recv!(client_rx)? { match state { State::Handshake => { match pkt { MessageC2S::Hello { version, requested_username, next_state } => { if !matches!(next_state, State::Play) { error!("client sent unexpected state {:?} (expected: Play)", next_state); send!(client_tx, &MessageS2C::Goodbye { reason: GoodbyeReason::UnexpectedNextState, }).await?; break; } // check version if version != PROTOCOL_VERSION { error!("client sent incompatible version {} (expected: {})", version, PROTOCOL_VERSION); send!(client_tx, &MessageS2C::Goodbye { reason: GoodbyeReason::UnsupportedProtocol { supported: PROTOCOL_VERSION, got: version, }, }).await?; break; } // determine if we can give them that username { if mgr.usernames.read().await.values().any(|u| *u == requested_username) { error!("client requested username {} but it is in use", requested_username); send!(client_tx, &MessageS2C::Goodbye { reason: GoodbyeReason::UsernameTaken, }).await?; break; } } // username is fine { mgr.usernames.write().await.insert(remote_addr, requested_username.clone()); } send!(client_tx, &MessageS2C::Hello { version, given_username: requested_username, next_state, }).await?; }, MessageC2S::Goodbye { reason } => { info!("client sent goodbye: {:?}", reason); break; } } } State::Play => { match pkt { MessageC2S::Hello { .. } => { error!("client sent unexpected packet {:?} for state {:?}", pkt, state); send!(client_tx, &MessageS2C::Goodbye { reason: GoodbyeReason::UnexpectedPacket, }).await?; break; } MessageC2S::Goodbye { reason } => { info!("client sent goodbye: {:?}", reason); break; } } } } } } Ok(()) }