123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- use std::collections::BTreeMap;
- use std::time::{Duration, Instant};
- use std::sync::{Arc};
- use actix::prelude::*;
- use actix_web::{web, Error, HttpRequest, HttpResponse};
- use actix_web_actors::ws;
- use log::{debug, error};
- use crate::datasource;
- use crate::server::protocol::*;
- 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<Addr<Server>>,
- r: HttpRequest,
- stream: web::Payload) -> Result<HttpResponse, Error> {
- let res = ws::start(GameConnection::new(server.as_ref().clone()), &r, stream)?;
- Ok(res)
- }
- pub struct Server {
- pub lobbies: BTreeMap<String, Addr<GameLobby>>,
- pub default_data: Arc<dyn datasource::DataSource<String>>
- }
- impl Actor for Server {
- type Context = Context<Self>;
- fn started(&mut self, _ctx: &mut Self::Context) {
- }
- }
- impl Handler<JoinRequest> 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<CreateLobbyRequest> 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<LobbyFinished> 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<String>,
- game_id: Option<String>,
- server: Addr<Server>,
- game_lobby: Option<Addr<GameLobby>>
- }
- impl Actor for GameConnection {
- type Context = ws::WebsocketContext<Self>;
- fn started(&mut self, ctx: &mut Self::Context) {
- self.initiate_heartbeat(ctx);
- }
- }
- impl Handler<LobbyJoined> 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<NoSuchLobby> 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<GameAlreadyStartedMsg> for GameConnection {
- type Result = ();
- fn handle(&mut self, gas: GameAlreadyStartedMsg, ctx: &mut Self::Context) -> Self::Result {
- self.send_message(&UpdateMessage::GameAlreadyStarted{ game_id: gas.0 }, ctx);
- }
- }
- impl Handler<NickAlreadyExistsMsg> for GameConnection {
- type Result = ();
- fn handle(&mut self, naem: NickAlreadyExistsMsg, ctx: &mut Self::Context) -> Self::Result {
- self.send_message(&UpdateMessage::NickAlreadyExists{ game_id: naem.game_id, nick: naem.nick }, ctx);
- }
- }
- impl Handler<GameUpdate> 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<PlayerListUpdate> for GameConnection {
- type Result = ();
- fn handle(&mut self, plu: PlayerListUpdate, ctx: &mut Self::Context) -> Self::Result {
- self.send_message(&UpdateMessage::PlayerList(plu.player_list), ctx);
- }
- }
- impl Handler<ResultMsg> 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<StopMsg> for GameConnection {
- type Result = ();
- fn handle(&mut self, _sm: StopMsg, ctx: &mut Self::Context) -> Self::Result {
- self.leave_lobby(ctx);
- ctx.stop();
- }
- }
- impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for GameConnection {
- fn handle(
- &mut self,
- msg: Result<ws::Message, ws::ProtocolError>,
- 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)) => {
- debug!("received client message: {}", 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<Server>) -> Self {
- GameConnection {
- heartbeat: Instant::now(),
- nick: None,
- game_id: None,
- server: server_addr,
- game_lobby: None
- }
- }
- pub fn initiate_heartbeat(&self, ctx: &mut <Self as Actor>::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 <Self as Actor>::Context) {
- let txt = serde_json::to_string(m).unwrap();
- debug!("sending message to client {}", txt);
- ctx.text(txt);
- }
- pub fn received_message(&mut self, text: &str, ctx: &mut <Self as Actor>::Context) {
- let parsed: Result<ClientMessage, _> = 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::Unready => {
- if let Some(lobby) = &self.game_lobby {
- if let Some(nick) = &self.nick {
- lobby.do_send(UnreadyMsg(nick.clone()));
- }
- }
- },
- ClientMessage::Reveal => {
- if let Some(lobby) = &self.game_lobby {
- if let Some(nick) = &self.nick {
- lobby.do_send(RevealMsg(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 {
- error!("error parsing json: {}", text);
- }
- }
- pub fn leave_lobby(&mut self, ctx: &mut <Self as Actor>::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);
- }
- }
- }
- }
|