use std::sync::Arc; use actix::prelude::*; use actix::{Actor, Handler, StreamHandler}; use actix_web_actors::ws; use actix_web_actors::ws::{CloseCode, CloseReason, Message, ProtocolError, WebsocketContext}; use uuid::Uuid; use crate::data::{BoatsLayout, BotType, GameRules}; use crate::game::{AddPlayer, Game}; use crate::human_player::HumanPlayer; use crate::random_bot::RandomBot; #[derive(Default, Debug)] pub enum StartMode { Bot(GameRules), #[default] AgainstHuman, } #[derive(serde::Deserialize, serde::Serialize, Debug)] #[serde(tag = "type")] pub enum ClientMessage { StopGame, BoatsLayout { layout: BoatsLayout }, } #[derive(Message)] #[rtype(result = "()")] #[derive(serde::Serialize, serde::Deserialize, Debug)] #[serde(tag = "type")] pub enum ServerMessage { WaitingForAnotherPlayer, QueryBoatsLayout { rules: GameRules }, WaitingForOtherPlayerConfiguration, OtherPlayerReady, GameStarting, } #[derive(Default)] pub struct HumanPlayerWS { inner: Option>, pub start_mode: StartMode, // TODO : add heartbeat stuff } impl HumanPlayerWS { fn send_message(&self, msg: ServerMessage, ctx: &mut ::Context) { ctx.text(serde_json::to_string(&msg).unwrap()); } } impl Actor for HumanPlayerWS { type Context = WebsocketContext; fn started(&mut self, ctx: &mut Self::Context) { self.send_message(ServerMessage::WaitingForAnotherPlayer, ctx); // Start game, according to appropriate start mode match &self.start_mode { StartMode::Bot(rules) => { log::debug!("Start play with a bot"); let game = Game::new(rules.clone()).start(); match rules.bot_type { BotType::Random => { game.do_send(AddPlayer(Arc::new(RandomBot::new(game.clone())))); } }; let player = Arc::new(HumanPlayer { name: "Human".to_string(), game: game.clone(), player: ctx.address(), uuid: Uuid::new_v4(), }); self.inner = Some(player.clone()); game.do_send(AddPlayer(player)); } StartMode::AgainstHuman => { unimplemented!(); } } } fn stopped(&mut self, _ctx: &mut Self::Context) { if let Some(player) = &self.inner { player.handle_client_message(ClientMessage::StopGame); } } } impl StreamHandler> for HumanPlayerWS { fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { match msg { Ok(Message::Ping(msg)) => ctx.pong(&msg), Ok(Message::Binary(_bin)) => log::warn!("Got unsupported binary message!"), Ok(Message::Text(msg)) => match serde_json::from_str::(&msg) { Ok(msg) => match &self.inner { None => { log::error!("Client tried to send message without game!"); ctx.text("No game yet!"); } Some(p) => p.handle_client_message(msg), }, Err(e) => log::warn!("Got invalid message from client! {:?}", e), }, Ok(Message::Nop) => log::warn!("Got WS nop"), Ok(Message::Continuation(_)) => { log::warn!("Got unsupported continuation message!"); } Ok(Message::Pong(_)) => { log::info!("Got pong message"); // TODO : handle pong message } Ok(Message::Close(reason)) => { log::info!("Client asked to close this socket! reason={:?}", reason); ctx.close(Some(CloseReason::from(CloseCode::Away))); } Err(e) => log::warn!("Websocket protocol error! {:?}", e), } } } impl Handler for HumanPlayerWS { type Result = (); fn handle(&mut self, msg: ServerMessage, ctx: &mut Self::Context) -> Self::Result { ctx.text(serde_json::to_string(&msg).unwrap()); } }