use std::collections::BTreeMap; use std::sync::Arc; use rand::thread_rng; use rand::seq::SliceRandom; use actix::prelude::*; use log::info; use crate::server::server::{GameConnection, Server}; use crate::datasource; use crate::game; use crate::server::protocol::*; use crate::server::messages::*; #[derive(PartialEq)] enum LobbyState { Starting, Creating, Guessing, Revealing } pub struct GameLobby { server: Addr, connected_players: BTreeMap>, game_id: String, game: game::Game, player_points: BTreeMap, waiting_players: BTreeMap>, ready_players: Vec, revealed_players: Vec, round_result: Option, lobby_state: LobbyState, shuffler: datasource::Shuffler, letter_distribution: datasource::LetterDistribution } impl Actor for GameLobby { type Context = Context; fn started(&mut self, _ctx: &mut Self::Context) { self.lobby_state = LobbyState::Starting; } } impl Handler for GameLobby { type Result = (); fn handle(&mut self, jr: JoinRequest, ctx: &mut Self::Context) -> Self::Result { if self.lobby_state == LobbyState::Starting { if self.connected_players.contains_key(&jr.nick) { jr.p.do_send(NickAlreadyExistsMsg { game_id: self.game_id.clone(), nick: jr.nick }); } else { self.connected_players.insert(jr.nick.clone(), jr.p.clone()); self.game.player_join(jr.nick.clone()); jr.p.do_send( LobbyJoined { lobby: ctx.address(), game_id: self.game_id.clone(), nick: jr.nick.clone() } ); self.send_game_to_all(); info!("Player {} joined lobby {}", jr.nick, self.game_id); } } else { //self.waiting_players.insert(jr.nick.clone(), jr.p); jr.p.do_send(GameAlreadyStartedMsg(jr.lobby_id)); } } } impl Handler for GameLobby { type Result = (); fn handle(&mut self, lm: LeaveMsg, _ctx: &mut Self::Context) -> Self::Result { self.waiting_players.remove(&lm.0); self.connected_players.remove(&lm.0); self.ready_players.retain(|p| p != &lm.0); self.game.remove_player(&lm.0); if self.connected_players.is_empty() { self.server.do_send(LobbyFinished(self.game_id.clone())); } } } impl Handler for GameLobby { type Result = (); fn handle(&mut self, rm: ReadyMsg, _ctx: &mut Self::Context) -> Self::Result { if !self.ready_players.contains(&rm.0) { self.ready_players.push(rm.0); } self.send_playerlist_to_all(); if self.ready_players.len() >= self.connected_players.len() { self.set_state(LobbyState::Creating); self.send_game_to_all(); } } } impl Handler for GameLobby { type Result = (); fn handle(&mut self, urm: UnreadyMsg, _ctx: &mut Self::Context) -> Self::Result { self.ready_players.retain(|p| p != &urm.0); self.send_playerlist_to_all(); } } impl Handler for GameLobby { type Result = (); fn handle(&mut self, rm: RevealMsg, _ctx: &mut Self::Context) -> Self::Result { self.revealed_players.retain(|p| p != &rm.0); self.revealed_players.push(rm.0); self.send_game_to_all(); if self.all_revealed() { self.set_state(LobbyState::Starting); self.send_game_to_all(); } } } impl Handler for GameLobby { type Result = (); fn handle(&mut self, swm: SubmitWordMsg, _ctx: &mut Self::Context) -> Self::Result { let _correct = self.game.submit_creation(&swm.nick, swm.word); if self.game.all_words_submitted() { self.set_state(LobbyState::Guessing); self.game.next_state(); self.send_game_to_all(); } else { self.send_playerlist_to_all(); } } } impl Handler for GameLobby { type Result = (); fn handle(&mut self, sgm: SubmitGuessMsg, _ctx: &mut Self::Context) -> Self::Result { self.game.submit_guess(&sgm.nick, sgm.guesses); if self.game.all_guesses_submitted() { self.set_state(LobbyState::Revealing); let results = self.create_result_data(); for (nick, pts) in &results.point_list { self.add_player_points(nick, *pts); } //self.broadcast_results(results.clone()); self.round_result = Some(results); self.game.next_state(); self.send_game_to_all(); } else { self.send_playerlist_to_all(); } } } impl GameLobby { pub fn new(gi: String, data_source: Arc>, server: Addr) -> Self { GameLobby { server: server, connected_players: BTreeMap::new(), game_id: gi, game: game::Game::new(), player_points: BTreeMap::new(), waiting_players: BTreeMap::new(), round_result: None, ready_players: Vec::new(), revealed_players: Vec::new(), lobby_state: LobbyState::Starting, shuffler: datasource::Shuffler::create(data_source), letter_distribution: datasource::LetterDistribution::create() } } fn set_state(&mut self, new_state: LobbyState) { match new_state { LobbyState::Starting => { for (nick, _addr) in &self.waiting_players { self.game.player_join(nick.clone()); } self.connected_players.append(&mut self.waiting_players); }, LobbyState::Creating => { let s = &mut self.shuffler; let ld = &self.letter_distribution; self.game.start_round(|| s.get(), || ld.get(4, 6)); }, _ => {} } self.ready_players.clear(); self.revealed_players.clear(); self.round_result = None; self.lobby_state = new_state; } pub fn get_player_points(&self, nick: &str) -> i64 { *self.player_points.get(nick).unwrap_or(&0) } pub fn add_player_points(&mut self, nick: &str, points: i64) { let entry = self.player_points.get_mut(nick); match entry { Some(x) => *x += points, None => { self.player_points.insert(nick.to_owned(), points); }, } } 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 send_playerlist_to_all(&self) { let player_list = self.create_playerlist(); for (_nick, player) in &self.connected_players { player.do_send(PlayerListUpdate{ player_list: player_list.clone() }); } } pub fn create_result_data(&self) -> RoundResultData { let (guesses, point_list) = self.game.create_results(); let words = self.game.players.iter() .map(|p| ( p.nick.clone(), p.submitted_word.clone().unwrap(), self.revealed_players.iter().any(|x| x == &p.nick) ) ) .collect::>(); let questions = self.game.players.iter() .map(|x| x.creating_exercise.as_ref().unwrap().question.clone()) .chain(self.game.additional_questions.clone().into_iter()) .collect::>(); let solutions = words.iter() .map(|(_n, w, _r)| w.clone()) .zip(questions.iter().map(|x| x.clone())) .collect::>(); let mut permutation = (0..questions.len()).collect::>(); permutation.shuffle(&mut thread_rng()); let mut shuffled_questions: Vec = Vec::new(); let mut shuffled_guesses: Vec>> = Vec::new(); for pi in &permutation { shuffled_questions.push(questions[*pi].clone()); } for guessline in guesses { let mut shuffled_line: Vec> = Vec::new(); for pi in &permutation { if *pi < guessline.len() { shuffled_line.push(guessline[*pi].clone()); } else { shuffled_line.push(Vec::new()); } } shuffled_guesses.push(shuffled_line); } RoundResultData { words, questions: shuffled_questions, solutions, guesses: shuffled_guesses, point_list } } /*pub fn broadcast_results(&self, results: RoundResultData) { let has_revealed = self.revealed_players.iter() .map(|n| (n.clone(), self.revealed_players.iter().any(|x| x == n))) .collect::>(); for (_nick, player) in &self.connected_players { player.do_send(ResultMsg { results: results.clone(), //has_revealed: has_revealed.clone() }); } }*/ /// /// determines if a player is ready to progress into the next game state /// fn player_is_ready(&self, nick: &str) -> bool { match self.lobby_state { LobbyState::Starting => self.ready_players.iter().any(|p| p == nick), LobbyState::Creating => self.game.get_player(nick) .map(|p| p.has_submitted_word()) .unwrap_or(false), LobbyState::Guessing => self.game.get_player(nick) .map(|p| p.has_submitted_guess()) .unwrap_or(false), LobbyState::Revealing => self.ready_players.iter().any(|p| p == nick) } } fn create_playerlist(&self) -> Vec { self.game.players.iter() .map(|p| PlayerData { nick: p.nick.clone(), points: self.get_player_points(&p.nick), ready: self.player_is_ready(&p.nick), }) .collect::>() } pub fn create_opaque_message(&self, nick: &str) -> GameData { let player = self.game.players.iter() .find(|p| p.nick == nick); GameData { players: self.create_playerlist(), state_data: if self.lobby_state == LobbyState::Revealing { let mut round_result = self.round_result.as_ref().unwrap().clone(); for (nick, _word, revealed) in &mut round_result.words { *revealed = self.revealed_players.iter().any(|x| x == nick); } GameStateData::Revealing { round_result } } else { match self.game.state { game::GameState::Starting => { GameStateData::Starting }, game::GameState::Creating => { GameStateData::Creating{ question: player.unwrap().creating_exercise.as_ref().unwrap().question.clone(), available_chars: player.unwrap().creating_exercise.as_ref().unwrap().letters.clone() } }, game::GameState::Guessing => { self.create_guessing(nick) }, } } } } pub fn check_optionals(&self) { for p in &self.game.players { println!("{:?}", p); } } pub fn all_revealed(&self) -> bool { self.connected_players.iter().all(|(nick, _p)| self.revealed_players.iter().any(|x| x == nick)) } pub fn create_guessing(&self, nick: &str) -> GameStateData { self.check_optionals(); let words_with_chars = self.game.players.iter() .filter(|p| p.nick != nick) .map(|p| (p.submitted_word.clone().unwrap(), p.creating_exercise.clone().unwrap().letters)) .collect::>(); let mut questions = self.game.players.iter() .filter(|p| p.nick != nick) .map(|p| p.creating_exercise.clone().unwrap().question) .collect::>(); questions.append(&mut self.game.additional_questions.iter() .map(|x| x.clone()) .collect::>() ); questions.shuffle(&mut thread_rng()); GameStateData::Guessing { submitted_words: words_with_chars, questions: questions } } }