use std::collections::BTreeMap; use std::collections::BTreeSet; use std::time::{Duration, Instant}; use std::sync::{Mutex, RwLock, Arc}; use actix::dev::{MessageResponse, ResponseChannel}; use actix::prelude::*; use actix_web::{web, Error, HttpRequest, HttpResponse}; use actix_web_actors::ws; use crate::game; use crate::websocket; use crate::websocket::*; use crate::datasource; use crate::server::gamelobby::{GameLobby}; use crate::server::messages::*; const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); const CLIENT_TIMEOUT: Duration = Duration::from_secs(10); pub async fn ws_initiate(server: web::Data>, r: HttpRequest, stream: web::Payload) -> Result { println!("{:?}", r); let res = ws::start(GameConnection::new(server.as_ref().clone()), &r, stream)?; println!("{:?}", res); Ok(res) } pub struct Server { pub lobbies: BTreeMap>, pub default_data: Arc> } impl Actor for Server { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { } } impl Handler for Server { type Result = (); fn handle(&mut self, jr: JoinRequest, ctx: &mut Self::Context) -> Self::Result { let mb_lobby = self.lobbies.get(&jr.lobby_id); match mb_lobby { Some(lobby) => { let _sent = lobby.do_send(jr); }, None => { jr.p.do_send(NoSuchLobby(jr.lobby_id)); } } } } impl Handler for Server { type Result = (); fn handle(&mut self, clr: CreateLobbyRequest, ctx: &mut Self::Context) -> Self::Result { let existing_lobby = self.lobbies.get(&clr.lobby_id); match existing_lobby { Some(_) => {}, None => { let lobby = GameLobby::new(clr.lobby_id.clone(), self.default_data.clone(), ctx.address()); let lobby_addr = lobby.start(); self.lobbies.insert(clr.lobby_id.clone(), lobby_addr.clone()); self.handle(JoinRequest { lobby_id: clr.lobby_id, nick: clr.nick, p: clr.p }, ctx); } } } } impl Handler for Server { type Result = (); fn handle(&mut self, lf: LobbyFinished, ctx: &mut Self::Context) -> Self::Result { self.lobbies.remove(&lf.0); } } /// /// connection to one single client /// pub struct GameConnection { heartbeat: Instant, nick: Option, game_id: Option, server: Addr, game_lobby: Option> } impl Actor for GameConnection { type Context = ws::WebsocketContext; fn started(&mut self, ctx: &mut Self::Context) { self.initiate_heartbeat(ctx); } } impl Handler for GameConnection { type Result = (); fn handle(&mut self, gu: LobbyJoined, ctx: &mut Self::Context) -> Self::Result { self.game_lobby = Some(gu.lobby); self.game_id = Some(gu.game_id); self.nick = Some(gu.nick); } } impl Handler for GameConnection { type Result = (); fn handle(&mut self, nsl: NoSuchLobby, ctx: &mut Self::Context) -> Self::Result { self.send_message(&UpdateMessage::GameNotFound{ game_id: nsl.0 }, ctx); } } impl Handler for GameConnection { type Result = (); fn handle(&mut self, gu: GameUpdate, ctx: &mut Self::Context) -> Self::Result { self.send_message(&UpdateMessage::GameState(gu.game_data), ctx); } } impl Handler for GameConnection { type Result = (); fn handle(&mut self, rm: ResultMsg, ctx: &mut Self::Context) -> Self::Result { self.send_message(&UpdateMessage::RoundResult(rm.results), ctx); } } impl Handler for GameConnection { type Result = (); fn handle(&mut self, _sm: StopMsg, ctx: &mut Self::Context) -> Self::Result { self.leave_lobby(ctx); ctx.stop(); } } impl StreamHandler> for GameConnection { fn handle( &mut self, msg: Result, ctx: &mut Self::Context, ) { match msg { Ok(ws::Message::Ping(msg)) => { self.heartbeat = Instant::now(); ctx.pong(&msg); } Ok(ws::Message::Pong(_)) => { self.heartbeat = Instant::now(); } Ok(ws::Message::Text(text)) => { println!("hmmm: {:?}", text); self.received_message(&text, ctx); }, Ok(ws::Message::Binary(bin)) => ctx.binary(bin), Ok(ws::Message::Close(reason)) => { self.leave_lobby(ctx); ctx.close(reason); ctx.stop(); } _ => ctx.stop(), } } } impl GameConnection { pub fn new(server_addr: Addr) -> Self { GameConnection { heartbeat: Instant::now(), nick: None, game_id: None, server: server_addr, game_lobby: None } } pub fn initiate_heartbeat(&self, ctx: &mut ::Context) { ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| { if Instant::now().duration_since(act.heartbeat) > CLIENT_TIMEOUT { ctx.address().do_send(StopMsg); return; } ctx.ping(b""); }); } pub fn send_message(&self, m: &UpdateMessage, ctx: &mut ::Context) { let txt = serde_json::to_string(m).unwrap(); println!("{:?}", txt); ctx.text(txt); } pub fn received_message(&mut self, text: &str, ctx: &mut ::Context) { let parsed: Result = serde_json::from_str(text); if let Ok(msg) = parsed { match msg { ClientMessage::CreateGame{game_id, nick} => { if nick != "" && game_id != "" { self.game_id = Some(game_id.clone()); self.nick = Some(nick.clone()); self.server.do_send(CreateLobbyRequest { lobby_id: game_id.clone(), nick: nick.clone(), p: ctx.address() }); } }, ClientMessage::Join{game_id, nick} => { if nick != "" { self.server.do_send(JoinRequest{ lobby_id: game_id.clone(), nick: nick.clone(), p: ctx.address() }); self.game_id = Some(game_id.clone()); self.nick = Some(nick); } }, ClientMessage::LeaveLobby => { self.leave_lobby(ctx); }, ClientMessage::Ready => { if let Some(lobby) = &self.game_lobby { if let Some(nick) = &self.nick { lobby.do_send(ReadyMsg(nick.clone())); } } }, ClientMessage::SubmitWord{ word } => { if let Some(lobby) = &self.game_lobby { if let Some(nick) = &self.nick { lobby.do_send(SubmitWordMsg{ word: word, nick: nick.clone() }); } } }, ClientMessage::SubmitGuess{ guesses } => { if let Some(lobby) = &self.game_lobby { if let Some(nick) = &self.nick { lobby.do_send(SubmitGuessMsg{ guesses: guesses, nick: nick.clone() }); } } } } } else { println!("error parsing json"); } } pub fn leave_lobby(&mut self, ctx: &mut ::Context) { if let Some(lobby) = &self.game_lobby { if let Some(nick) = &self.nick { lobby.do_send(LeaveMsg(nick.clone())); self.send_message(&UpdateMessage::LeftLobby{ nick: nick.clone() }, ctx); } } } }