123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- 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 rand::thread_rng;
- use rand::seq::SliceRandom;
- use crate::game;
- use crate::websocket;
- use crate::websocket::*;
- const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
- const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
- #[derive(Message)]
- #[rtype(result = "Answer")]
- struct JoinRequest{ lobby_id: String, nick: String, p: Addr<GameConnection> }
- #[derive(Message)]
- #[rtype(result = "Answer")]
- struct CreateLobbyRequest{ lobby_id: String, p: Addr<GameConnection> }
- #[derive(Message)]
- #[rtype(result = "()")]
- struct SubmitWordMsg{ word: String, nick: String }
- #[derive(Message)]
- #[rtype(result = "Result<game::Game, ()>")]
- struct GetGame;
- #[derive(Message)]
- #[rtype(result = "()")]
- struct LobbyJoined(Addr<GameLobby>);
- #[derive(Message)]
- #[rtype(result = "()")]
- struct GameUpdate{ game_data: GameData }
- enum Answer {
- LobbyJoined(Addr<GameLobby>),
- LobbyCreated(Addr<GameLobby>),
- LobbyAlreadyExists,
- NoSuchLobby,
- }
- impl<A, M> MessageResponse<A, M> for Answer
- where
- A: Actor,
- M: Message<Result = Answer>,
- {
- fn handle<R: ResponseChannel<M>>(self, _: &mut A::Context, tx: Option<R>) {
- if let Some(tx) = tx {
- tx.send(self);
- }
- }
- }
- impl<A, M> MessageResponse<A, M> for game::Game
- where
- A: Actor,
- M: Message<Result = game::Game>,
- {
- fn handle<R: ResponseChannel<M>>(self, _: &mut A::Context, tx: Option<R>) {
- if let Some(tx) = tx {
- tx.send(self);
- }
- }
- }
- pub struct Server {
- pub lobbies: BTreeMap<String, Addr<GameLobby>>,
- }
- impl Actor for Server {
- type Context = Context<Self>;
- fn started(&mut self, ctx: &mut Self::Context) {
- }
- }
- impl Handler<JoinRequest> for Server {
- type Result = Answer;
- 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);
- Answer::LobbyJoined(lobby.clone())
- },
- None => Answer::NoSuchLobby
- }
- }
- }
- impl Handler<CreateLobbyRequest> for Server {
- type Result = Answer;
- 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(_) => Answer::LobbyAlreadyExists,
- None => {
- let lobby = GameLobby::new(clr.lobby_id.clone());
- let lobby_addr = lobby.start();
- self.lobbies.insert(clr.lobby_id.clone(), lobby_addr.clone());
- Answer::LobbyCreated(lobby_addr)
- }
- }
- }
- }
- pub struct GameLobby {
- connected_players: BTreeMap<String, Addr<GameConnection>>,
- game_id: String,
- game: game::Game,
- }
- impl Actor for GameLobby {
- type Context = Context<Self>;
- fn started(&mut self, ctx: &mut Self::Context) {
- self.game.new_round();
- }
- }
- impl Handler<JoinRequest> for GameLobby {
- type Result = Answer;
- fn handle(&mut self, jr: JoinRequest, ctx: &mut Self::Context) -> Self::Result {
- jr.p.do_send(LobbyJoined(ctx.address()));
- self.connected_players.insert(jr.nick.clone(), jr.p);
- self.game.player_join(jr.nick);
- self.send_game_to_all();
- Answer::LobbyJoined(ctx.address())
- }
- }
- impl Handler<GetGame> for GameLobby {
- type Result = Result<game::Game, ()>;
- fn handle(&mut self, gg: GetGame, ctx: &mut Self::Context) -> Self::Result {
- Ok(self.game.clone())
- }
- }
- impl Handler<SubmitWordMsg> for GameLobby {
- type Result = ();
- fn handle(&mut self, swm: SubmitWordMsg, ctx: &mut Self::Context) -> Self::Result {
- self.game.submit_creation(&swm.nick, swm.word);
- if self.game.all_submitted() {
- self.game.next_state();
- self.send_game_to_all();
- }
- }
- }
- impl GameLobby {
- pub fn new(gi: String) -> Self {
- GameLobby {
- connected_players: BTreeMap::new(),
- game_id: gi,
- game: game::Game::new()
- }
- }
- pub fn send_game_to_all(&self) {
- for (nick, player) in &self.connected_players {
- let game_state = self.create_opaque_message(nick);
- player.do_send(GameUpdate{ game_data: game_state });
- }
- }
- pub fn create_opaque_message(&self, nick: &str) -> GameData {
- GameData {
- players: self.game.players.iter().map(|p| websocket::PlayerData{ nick: p.nick.clone(), points: 0 }).collect::<Vec<_>>(),
- state_data: match self.game.state {
- game::GameState::Creating => {
- GameStateData::Creating{
- question: "Ein Gerät um Elche zu häuten".to_owned(),
- available_chars: vec!['a', 'b', 'c']
- }
- },
- game::GameState::Guessing => {
- self.create_guessing()
- },
- }
- }
- }
- pub fn create_guessing(&self) -> GameStateData {
- let chars = self.game.round.exercises.iter()
- .map(|x| x.letters.clone())
- .collect::<Vec<_>>();
-
- let words_with_chars = self.game.players.iter()
- .map(|p| p.submitted_word.clone().unwrap_or("".to_owned()))
- .zip(chars)
- .collect::<Vec<_>>();
- let mut questions = self.game.round.exercises.iter()
- .map(|x| x.question.clone())
- .collect::<Vec<_>>();
- questions.shuffle(&mut thread_rng());
- GameStateData::Guessing {
- submitted_words: words_with_chars,
- questions: questions
- }
- }
- }
- ///
- /// 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.0);
- }
- }
- 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 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)) => {
- println!("hmmm: {:?}", text);
- self.received_message(&text, ctx);
- },
- Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
- Ok(ws::Message::Close(reason)) => {
- 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 {
- //println!("Websocket Client heartbeat failed, disconnecting!");
- ctx.stop();
- 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();
- println!("{:?}", 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} => {
- self.game_id = Some(game_id.clone());
- self.nick = Some(nick);
- self.server.do_send(CreateLobbyRequest{ lobby_id: game_id.clone(), p: ctx.address() });
- },
- ClientMessage::Join{game_id, 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::SubmitWord{ word } => {
- match &self.game_lobby {
- Some(lobby) => {
- if let Some(nick) = &self.nick {
- lobby.do_send(SubmitWordMsg{ word: word, nick: nick.clone() });
- }
- },
- None => {
-
- }
- }
- },
- ClientMessage::SubmitGuess{ guesses } => {
-
- }
- }
- }
- else {
- println!("error parsing json");
- }
- }
- }
|