|
@@ -25,12 +25,27 @@ struct CreateLobbyRequest{ lobby_id: String, p: Addr<GameConnection> }
|
|
|
|
|
|
#[derive(Message)]
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
#[rtype(result = "()")]
|
|
|
|
+struct ReadyMsg(String);
|
|
|
|
+
|
|
|
|
+#[derive(Message)]
|
|
|
|
+#[rtype(result = "()")]
|
|
struct SubmitWordMsg{ word: String, nick: String }
|
|
struct SubmitWordMsg{ word: String, nick: String }
|
|
|
|
|
|
#[derive(Message)]
|
|
#[derive(Message)]
|
|
|
|
+#[rtype(result = "()")]
|
|
|
|
+struct SubmitGuessMsg{ guesses: Vec<(String, String)>, nick: String }
|
|
|
|
+
|
|
|
|
+#[derive(Message)]
|
|
|
|
+#[rtype(result = "()")]
|
|
|
|
+struct ResultMsg{ results: RoundResultData }
|
|
|
|
+
|
|
|
|
+#[derive(Message)]
|
|
#[rtype(result = "Result<game::Game, ()>")]
|
|
#[rtype(result = "Result<game::Game, ()>")]
|
|
struct GetGame;
|
|
struct GetGame;
|
|
|
|
|
|
|
|
+#[derive(Message)]
|
|
|
|
+#[rtype(result = "()")]
|
|
|
|
+struct NoSuchLobby(String);
|
|
|
|
|
|
#[derive(Message)]
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
#[rtype(result = "()")]
|
|
@@ -92,7 +107,10 @@ impl Handler<JoinRequest> for Server {
|
|
let _sent = lobby.do_send(jr);
|
|
let _sent = lobby.do_send(jr);
|
|
Answer::LobbyJoined(lobby.clone())
|
|
Answer::LobbyJoined(lobby.clone())
|
|
},
|
|
},
|
|
- None => Answer::NoSuchLobby
|
|
|
|
|
|
+ None => {
|
|
|
|
+ jr.p.do_send(NoSuchLobby(jr.lobby_id));
|
|
|
|
+ Answer::NoSuchLobby
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -113,27 +131,57 @@ impl Handler<CreateLobbyRequest> for Server {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#[derive(PartialEq)]
|
|
|
|
+enum LobbyState {
|
|
|
|
+ Starting,
|
|
|
|
+ Creating,
|
|
|
|
+ Guessing,
|
|
|
|
+ Revealing
|
|
|
|
+}
|
|
|
|
+
|
|
pub struct GameLobby {
|
|
pub struct GameLobby {
|
|
connected_players: BTreeMap<String, Addr<GameConnection>>,
|
|
connected_players: BTreeMap<String, Addr<GameConnection>>,
|
|
game_id: String,
|
|
game_id: String,
|
|
game: game::Game,
|
|
game: game::Game,
|
|
|
|
+ waiting_players: BTreeMap<String, Addr<GameConnection>>,
|
|
|
|
+ ready_players: Vec<String>,
|
|
|
|
+ lobby_state: LobbyState
|
|
}
|
|
}
|
|
|
|
|
|
impl Actor for GameLobby {
|
|
impl Actor for GameLobby {
|
|
type Context = Context<Self>;
|
|
type Context = Context<Self>;
|
|
fn started(&mut self, ctx: &mut Self::Context) {
|
|
fn started(&mut self, ctx: &mut Self::Context) {
|
|
- self.game.new_round();
|
|
|
|
|
|
+ self.game.start_round();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
impl Handler<JoinRequest> for GameLobby {
|
|
impl Handler<JoinRequest> for GameLobby {
|
|
type Result = Answer;
|
|
type Result = Answer;
|
|
fn handle(&mut self, jr: JoinRequest, ctx: &mut Self::Context) -> Self::Result {
|
|
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())
|
|
|
|
|
|
+ if self.lobby_state == LobbyState::Starting {
|
|
|
|
+ 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())
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ self.waiting_players.insert(jr.nick.clone(), jr.p);
|
|
|
|
+ Answer::NoSuchLobby
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl Handler<ReadyMsg> 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);
|
|
|
|
+ }
|
|
|
|
+ if self.ready_players.len() >= self.connected_players.len() {
|
|
|
|
+ self.set_state(LobbyState::Creating);
|
|
|
|
+ self.send_game_to_all();
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -148,7 +196,23 @@ impl Handler<SubmitWordMsg> for GameLobby {
|
|
type Result = ();
|
|
type Result = ();
|
|
fn handle(&mut self, swm: SubmitWordMsg, ctx: &mut Self::Context) -> Self::Result {
|
|
fn handle(&mut self, swm: SubmitWordMsg, ctx: &mut Self::Context) -> Self::Result {
|
|
self.game.submit_creation(&swm.nick, swm.word);
|
|
self.game.submit_creation(&swm.nick, swm.word);
|
|
- if self.game.all_submitted() {
|
|
|
|
|
|
+ if self.game.all_words_submitted() {
|
|
|
|
+ self.set_state(LobbyState::Guessing);
|
|
|
|
+ self.game.next_state();
|
|
|
|
+ self.send_game_to_all();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl Handler<SubmitGuessMsg> 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();
|
|
|
|
+ self.broadcast_results(results);
|
|
|
|
+
|
|
self.game.next_state();
|
|
self.game.next_state();
|
|
self.send_game_to_all();
|
|
self.send_game_to_all();
|
|
}
|
|
}
|
|
@@ -160,10 +224,30 @@ impl GameLobby {
|
|
GameLobby {
|
|
GameLobby {
|
|
connected_players: BTreeMap::new(),
|
|
connected_players: BTreeMap::new(),
|
|
game_id: gi,
|
|
game_id: gi,
|
|
- game: game::Game::new()
|
|
|
|
|
|
+ game: game::Game::new(),
|
|
|
|
+ waiting_players: BTreeMap::new(),
|
|
|
|
+ ready_players: Vec::new(),
|
|
|
|
+ lobby_state: LobbyState::Starting,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ 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);
|
|
|
|
+ self.ready_players.clear();
|
|
|
|
+ },
|
|
|
|
+ LobbyState::Creating => {
|
|
|
|
+ self.game.start_round();
|
|
|
|
+ },
|
|
|
|
+ _ => {}
|
|
|
|
+ }
|
|
|
|
+ self.lobby_state = new_state;
|
|
|
|
+ }
|
|
|
|
+
|
|
pub fn send_game_to_all(&self) {
|
|
pub fn send_game_to_all(&self) {
|
|
for (nick, player) in &self.connected_players {
|
|
for (nick, player) in &self.connected_players {
|
|
let game_state = self.create_opaque_message(nick);
|
|
let game_state = self.create_opaque_message(nick);
|
|
@@ -171,36 +255,74 @@ impl GameLobby {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ pub fn create_result_data(&self) -> RoundResultData {
|
|
|
|
+ let results_table = self.game.create_results();
|
|
|
|
+
|
|
|
|
+ let words = self.game.players.iter()
|
|
|
|
+ .map(|p| p.submitted_word.clone().unwrap())
|
|
|
|
+ .collect::<Vec<_>>();
|
|
|
|
+ let questions = self.game.players.iter()
|
|
|
|
+ .map(|x| x.creating_exercise.clone().unwrap().question)
|
|
|
|
+ .collect::<Vec<_>>();
|
|
|
|
+ let solutions = words.iter()
|
|
|
|
+ .map(|x| x.clone())
|
|
|
|
+ .zip(questions.iter().map(|x| x.clone()))
|
|
|
|
+ .collect::<Vec<_>>();
|
|
|
|
+
|
|
|
|
+ let guesses = self.game.create_results();
|
|
|
|
+
|
|
|
|
+ RoundResultData {
|
|
|
|
+ words,
|
|
|
|
+ questions,
|
|
|
|
+ solutions,
|
|
|
|
+ guesses
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pub fn broadcast_results(&self, results: RoundResultData) {
|
|
|
|
+ for (_nick, player) in &self.connected_players {
|
|
|
|
+ player.do_send(ResultMsg{ results: results.clone() });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
pub fn create_opaque_message(&self, nick: &str) -> GameData {
|
|
pub fn create_opaque_message(&self, nick: &str) -> GameData {
|
|
|
|
+
|
|
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()
|
|
|
|
- },
|
|
|
|
- }
|
|
|
|
|
|
+ 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::Starting => {
|
|
|
|
+ GameStateData::Starting
|
|
|
|
+ },
|
|
|
|
+ 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 {
|
|
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()
|
|
let words_with_chars = self.game.players.iter()
|
|
- .map(|p| p.submitted_word.clone().unwrap_or("".to_owned()))
|
|
|
|
- .zip(chars)
|
|
|
|
|
|
+ .map(|p|
|
|
|
|
+ (p.submitted_word.clone().unwrap(),
|
|
|
|
+ p.creating_exercise.clone().unwrap().letters))
|
|
.collect::<Vec<_>>();
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
- let mut questions = self.game.round.exercises.iter()
|
|
|
|
- .map(|x| x.question.clone())
|
|
|
|
|
|
+ let mut questions = self.game.players.iter()
|
|
|
|
+ .map(|p| p.creating_exercise.clone().unwrap().question)
|
|
.collect::<Vec<_>>();
|
|
.collect::<Vec<_>>();
|
|
|
|
+ questions.append(&mut self.game.additional_questions.iter()
|
|
|
|
+ .map(|x| x.question.clone())
|
|
|
|
+ .collect::<Vec<_>>()
|
|
|
|
+ );
|
|
|
|
|
|
questions.shuffle(&mut thread_rng());
|
|
questions.shuffle(&mut thread_rng());
|
|
|
|
|
|
@@ -237,6 +359,13 @@ impl Handler<LobbyJoined> for GameConnection {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+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<GameUpdate> for GameConnection {
|
|
impl Handler<GameUpdate> for GameConnection {
|
|
type Result = ();
|
|
type Result = ();
|
|
fn handle(&mut self, gu: GameUpdate, ctx: &mut Self::Context) -> Self::Result {
|
|
fn handle(&mut self, gu: GameUpdate, ctx: &mut Self::Context) -> Self::Result {
|
|
@@ -244,6 +373,13 @@ impl Handler<GameUpdate> for GameConnection {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+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 StreamHandler<Result<ws::Message, ws::ProtocolError>> for GameConnection {
|
|
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for GameConnection {
|
|
fn handle(
|
|
fn handle(
|
|
&mut self,
|
|
&mut self,
|
|
@@ -314,20 +450,26 @@ impl GameConnection {
|
|
self.game_id = Some(game_id.clone());
|
|
self.game_id = Some(game_id.clone());
|
|
self.nick = Some(nick);
|
|
self.nick = Some(nick);
|
|
},
|
|
},
|
|
|
|
+ ClientMessage::Ready => {
|
|
|
|
+ if let Some(lobby) = &self.game_lobby {
|
|
|
|
+ if let Some(nick) = &self.nick {
|
|
|
|
+ lobby.do_send(ReadyMsg(nick.clone()));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
ClientMessage::SubmitWord{ word } => {
|
|
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 => {
|
|
|
|
-
|
|
|
|
|
|
+ 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 } => {
|
|
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() });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|