use crate::cli_args::cli_args; use crate::server; use futures::{SinkExt, StreamExt}; use sea_battle_backend::data::GameRules; use sea_battle_backend::human_player_ws::{ClientMessage, ServerMessage}; use sea_battle_backend::server::BotPlayQuery; use sea_battle_backend::utils::{boxed_error, Res}; use tokio::net::TcpStream; use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; /// Connection client /// /// This structure acts as a wrapper around websocket connection that handles automatically parsing /// of incoming messages and encoding of outgoing messages pub struct Client { socket: WebSocketStream>, } impl Client { /// Start to play against a bot /// /// When playing against a bot, local server is always used pub async fn start_bot_play(rules: &GameRules) -> Res { server::start_server_if_missing().await; Self::connect_url( &cli_args().local_server_address(), &format!( "/play/bot?{}", serde_urlencoded::to_string(&BotPlayQuery { rules: rules.clone(), player_name: "Human".to_string() }) .unwrap() ), ) .await } /// Do connect to a server, returning async fn connect_url(server: &str, uri: &str) -> Res { let mut url = server.replace("http", "ws"); url.push_str(uri); log::debug!("Connecting to {}", url); let (socket, _) = tokio_tungstenite::connect_async(url).await?; Ok(Self { socket }) } /// Receive next message from stream async fn recv_next_msg(&mut self) -> Res { loop { let chunk = match self.socket.next().await { None => return Err(boxed_error("No more message in queue!")), Some(d) => d, }; match chunk? { Message::Text(t) => { log::debug!("TEXT Got a text message from server!"); let msg: ServerMessage = serde_json::from_str(&t)?; return Ok(msg); } Message::Binary(_) => { log::debug!("BINARY Got an unexpected binary message"); return Err(boxed_error("Received an unexpected binary message!")); } Message::Ping(_) => { log::debug!("PING Got a ping message from server"); } Message::Pong(_) => { log::debug!("PONG Got a pong message"); } Message::Close(_) => { log::debug!("CLOSE Got a close websocket message"); return Err(boxed_error("Server requested to close connection!")); } Message::Frame(_) => { log::debug!("FRAME Got an unexpected frame from server!"); return Err(boxed_error("Got an unexpected frame!")); } } } } /// Send a message through the stream pub async fn send_message(&mut self, msg: &ClientMessage) -> Res { self.socket .send(Message::Text(serde_json::to_string(&msg)?)) .await?; Ok(()) } }