From: Geoffrey Allott Date: Fri, 5 Feb 2021 19:33:21 +0000 (+0000) Subject: add new JoinLobby command to subscribe to game list X-Git-Url: https://git.pointlesshacks.com/?a=commitdiff_plain;h=75120b4ebf1804aa77082432d3ee8f409ef6b44b;p=pokerwave.git add new JoinLobby command to subscribe to game list --- diff --git a/src/api.rs b/src/api.rs index 8daac46..c4f464c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -19,11 +19,12 @@ pub enum ClientMessage { ChangeNickname { nickname: String }, Logout, CreateGame { settings: GameSettings }, - GetGameList { tags: Vec }, + JoinLobby { filter: String }, JoinGame { id: u32 }, TakeAction { action: Action }, SendMessage { scope: Scope, message: String }, LeaveGame, + LeaveLobby, GetHandHistory { scope: Scope }, } @@ -42,8 +43,9 @@ pub enum ServerMessage { LogoutSuccess, CreateGameSuccess { id: u32 }, CreateGameFailure { reason: String }, - GameList { games: Vec }, - GameListFailure { reason: String }, + JoinLobbySuccess { games: Vec }, + JoinLobbyFailure { reason: String }, + NewGame { game: GameSummary }, JoinGameSuccess { game: Game }, JoinGameFailure { reason: String }, NewAction { action: UserAction }, @@ -52,6 +54,8 @@ pub enum ServerMessage { NewMessage { username: String, message: String }, LeaveGameSuccess, LeaveGameFailure { reason: String }, + LeaveLobbySuccess, + LeaveLobbyFailure { reason: String }, HandHistory { games: Vec }, ProtocolError { reason: String }, } diff --git a/src/client.rs b/src/client.rs index 2bb2e39..d50873a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use futures::stream::{Stream, StreamExt, empty, iter, once}; use crate::api::{ClientMessage, ServerMessage}; -use crate::game::{Game, UserAction}; +use crate::game::{Game, GameList, UserAction}; use crate::server::{ActionStatus, ServerState}; pub struct ConnectionState { @@ -30,7 +30,8 @@ pub enum ClientInterest { #[derive(Debug, Clone)] pub enum LoggedInState { - InLobby, + Idle, + InLobby { game_list: GameList }, InGame { game: Game }, } @@ -44,7 +45,16 @@ impl ConnectionState { pub async fn retrieve_updates(&mut self, update: ClientInterest) -> impl Stream { match update { - ClientInterest::GameList => empty().boxed(), // TODO + ClientInterest::GameList => match &mut self.client { + ClientState::LoggedIn{state: LoggedInState::InLobby{ref mut game_list}, ..} => { + let from = game_list.games_len(); + match self.server.update_game_list(game_list).await { + Ok(()) => iter(game_list.update(from)).map(|game| ServerMessage::NewGame{game}).boxed(), + Err(err) => once(async move { ServerMessage::ProtocolError{reason: err.to_string()} }).boxed(), + } + } + _ => empty().boxed(), + } ClientInterest::Game{id} => match &mut self.client { ClientState::LoggedIn{ref username, state: LoggedInState::InGame{ref mut game}} if game.id() == id => { let from = game.actions_len(); @@ -64,12 +74,17 @@ impl ConnectionState { if let ClientState::LoggedIn{ref username, ref state} = &self.client { let username = username.to_string(); ret.insert(ClientInterest::User{username}); - ret.insert(ClientInterest::GameList); - if let LoggedInState::InGame{ref game} = state { - ret.insert(ClientInterest::Game{id: game.id()}); - for username in game.players() { - let username = username.to_string(); - ret.insert(ClientInterest::User{username}); + match state { + LoggedInState::Idle => {}, + LoggedInState::InLobby{..} => { + ret.insert(ClientInterest::GameList); + } + LoggedInState::InGame{ref game} => { + ret.insert(ClientInterest::Game{id: game.id()}); + for username in game.players() { + let username = username.to_string(); + ret.insert(ClientInterest::User{username}); + } } } } @@ -91,7 +106,7 @@ impl ConnectionState { } (ClientState::LoginAuthIssued{username, challenge}, ClientMessage::LoginAuthResponse{signature}) => { if self.server.verify(&username, &challenge, &signature).await { - self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InLobby}; + self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::Idle}; self.server.register_interests(self.interests()).await; ServerMessage::LoginSuccess } else { @@ -99,10 +114,15 @@ impl ConnectionState { ServerMessage::LoginFailure{reason: "Invalid username or password".to_string()} } } - (ClientState::LoggedIn{..}, ClientMessage::GetGameList{..}) => { - match self.server.get_game_list().await { - Ok(games) => ServerMessage::GameList{games}, - Err(err) => ServerMessage::GameListFailure{reason: err.to_string()}, + (ClientState::LoggedIn{username, state: LoggedInState::Idle}, ClientMessage::JoinLobby{filter}) => { + let mut game_list = GameList::new(filter); + match self.server.update_game_list(&mut game_list).await { + Ok(()) => { + let games = game_list.update(0); + self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InLobby{game_list}}; + ServerMessage::JoinLobbySuccess{games} + } + Err(err) => ServerMessage::JoinLobbyFailure{reason: err.to_string()}, } } (ClientState::LoggedIn{..}, ClientMessage::CreateGame{settings}) => { @@ -111,7 +131,7 @@ impl ConnectionState { Err(err) => ServerMessage::CreateGameFailure{reason: err.to_string()}, } } - (ClientState::LoggedIn{username, state: LoggedInState::InLobby}, ClientMessage::JoinGame{id}) => { + (ClientState::LoggedIn{username, ..}, ClientMessage::JoinGame{id}) => { match self.server.get_game(id).await { Ok(game) => { let game_view = game.view_for(&username); @@ -144,8 +164,13 @@ impl ConnectionState { } } } + (ClientState::LoggedIn{username, state: LoggedInState::InLobby{..}}, ClientMessage::LeaveLobby) => { + self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::Idle}; + self.server.register_interests(self.interests()).await; + ServerMessage::LeaveGameSuccess + } (ClientState::LoggedIn{username, state: LoggedInState::InGame{..}}, ClientMessage::LeaveGame) => { - self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::InLobby}; + self.client = ClientState::LoggedIn{username: username.clone(), state: LoggedInState::Idle}; self.server.register_interests(self.interests()).await; ServerMessage::LeaveGameSuccess } diff --git a/src/game.rs b/src/game.rs index b4d8f07..39e87e5 100644 --- a/src/game.rs +++ b/src/game.rs @@ -13,6 +13,34 @@ pub enum GameSettings { }, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "format")] +pub struct GameList { + filter: String, + games: Vec, +} + +impl GameList { + pub fn new(filter: String) -> Self { + Self { + filter, + games: Vec::new(), + } + } + + pub fn update(&self, from: usize) -> Vec { + self.games.iter().skip(from).cloned().collect() + } + + pub fn games_len(&self) -> usize { + self.games.len() + } + + pub fn push(&mut self, game: GameSummary) { + self.games.push(game); + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "action")] pub enum Action { diff --git a/src/main.rs b/src/main.rs index 793244c..77da1d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use std::collections::HashSet; use std::convert::TryFrom; use std::mem::swap; -use futures::{channel::mpsc::{channel, Receiver}, future::{Either, select}, select, FutureExt, SinkExt, StreamExt, pin_mut, stream::{self, FuturesUnordered, pending}}; +use futures::{channel::mpsc::{channel, Receiver}, future::{Either, select}, select, FutureExt, SinkExt, StreamExt, pin_mut, stream::{self, FuturesUnordered}}; use redis::{aio::PubSub, Client, Msg}; use signal_hook::consts::signal::*; use signal_hook_async_std::Signals; @@ -52,7 +52,7 @@ pub async fn handle_client_interest(mut connection: PubSub, mut new_clients: Rec next_client_interest.push(sender.register_interest.next().map(move |interest| (index, interest))); } let mut next_client_interest = next_client_interest.select_next_some(); - let mut action = Action::ConnectionClosed; + let action; select! { interest = next_interest => { if let Some(interest) = interest { diff --git a/src/server.rs b/src/server.rs index 0e32637..5886448 100644 --- a/src/server.rs +++ b/src/server.rs @@ -7,7 +7,7 @@ use serde::{Serialize, Deserialize}; use crate::auth::Auth; use crate::client::ClientInterest; -use crate::game::{Game, GameSettings, GameSummary, UserAction}; +use crate::game::{Game, GameList, GameSettings, GameSummary, UserAction}; #[derive(Clone)] pub struct Server { @@ -149,17 +149,19 @@ impl ServerState { self.redis.rpush("games", AsJson(settings)).await.map(|i: u32| i - 1) } - pub async fn get_game_list(&mut self) -> RedisResult> { + pub async fn update_game_list(&mut self, game_list: &mut GameList) -> RedisResult<()> { const GAME_LIST_BLOCK_SIZE: isize = 1024; - let mut ret = Vec::new(); - for i in (0..).step_by(GAME_LIST_BLOCK_SIZE as usize) { + debug!("update_game_list: from: {}", game_list.games_len()); + for i in (game_list.games_len() as isize..).step_by(GAME_LIST_BLOCK_SIZE as usize) { + debug!("update_game_list: LRANGE games {} {}", i, i + GAME_LIST_BLOCK_SIZE); let games: Vec> = self.redis.lrange("games", i, i + GAME_LIST_BLOCK_SIZE).await?; + debug!("update_game_list: received games: {:#?}", games); if games.is_empty() { break; } for (j, AsJson(settings)) in games.into_iter().enumerate() { - ret.push(GameSummary::new(i as u32 + j as u32, settings)); + game_list.push(GameSummary::new(i as u32 + j as u32, settings)); } } - Ok(ret) + Ok(()) } pub async fn update_game_state(&mut self, game: &mut Game) -> RedisResult<()> { @@ -210,6 +212,7 @@ impl FromRedisValue for ActionStatus { } } +#[derive(Debug, Clone)] struct AsJson(T); impl AsJson {