|
|
|
@ -2,8 +2,12 @@ use crate::games::{CardId, CardIdx, Pile, PileKind, RunningGame}; |
|
|
|
use crate::{db, games::Game}; |
|
|
|
|
|
|
|
use super::{ |
|
|
|
client_id, |
|
|
|
grpc::game::{game_server, message_status::Piles, CardKind, Image, MessageStatus}, |
|
|
|
client_id, |
|
|
|
grpc::game::{ |
|
|
|
game_server, |
|
|
|
message_status::Piles, |
|
|
|
CardKind, Image, MessageStatus, |
|
|
|
}, |
|
|
|
}; |
|
|
|
pub use game_server::GameServer; |
|
|
|
use tonic::{Response, Status}; |
|
|
|
@ -14,164 +18,190 @@ use std::sync::Arc; |
|
|
|
use tokio::sync::RwLock; |
|
|
|
|
|
|
|
pub struct GameService { |
|
|
|
conn: RwLock<db::DbClient>, |
|
|
|
games: Arc<Vec<Game>>, |
|
|
|
running_games: Arc<RwLock<HashMap<u32, RwLock<(u32, RunningGame)>>>>, |
|
|
|
conn: RwLock<db::DbClient>, |
|
|
|
games: Arc<Vec<Game>>, |
|
|
|
running_games: Arc<RwLock<HashMap<u32, RwLock<(u32, RunningGame)>>>>, |
|
|
|
} |
|
|
|
|
|
|
|
impl GameService { |
|
|
|
pub fn new( |
|
|
|
conn: db::DbClient, |
|
|
|
games: Arc<Vec<Game>>, |
|
|
|
running_games: Arc<RwLock<HashMap<u32, RwLock<(u32, RunningGame)>>>>, |
|
|
|
) -> Self { |
|
|
|
Self { |
|
|
|
conn: RwLock::new(conn), |
|
|
|
running_games, |
|
|
|
games, |
|
|
|
} |
|
|
|
} |
|
|
|
pub fn new( |
|
|
|
conn: db::DbClient, |
|
|
|
games: Arc<Vec<Game>>, |
|
|
|
running_games: Arc<RwLock<HashMap<u32, RwLock<(u32, RunningGame)>>>>, |
|
|
|
) -> Self { |
|
|
|
Self { |
|
|
|
conn: RwLock::new(conn), |
|
|
|
running_games, |
|
|
|
games, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[tonic::async_trait] |
|
|
|
impl game_server::Game for GameService { |
|
|
|
async fn get_card_image( |
|
|
|
&self, |
|
|
|
request: tonic::Request<CardKind>, |
|
|
|
) -> Result<tonic::Response<Image>, Status> { |
|
|
|
let uuid = client_id::get(request.metadata()).map_err(|x| match x { |
|
|
|
client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), |
|
|
|
client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), |
|
|
|
})?; |
|
|
|
let mut conn = self.conn.write().await; |
|
|
|
let lobby: u32 = match conn.get_lobby_for_user(uuid).await { |
|
|
|
Some(l) => l, |
|
|
|
None => return Err(Status::failed_precondition("User isn't in a lobby")), |
|
|
|
}; |
|
|
|
let game_id = match self.running_games.read().await.get(&lobby) { |
|
|
|
Some(x) => x.read().await.0, |
|
|
|
None => { |
|
|
|
return Err(Status::failed_precondition( |
|
|
|
"User isn't in a lobby with a running game", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
let game = &self.games[game_id as usize]; |
|
|
|
let (face, back) = game.get_card_paths(&request.into_inner().kind); |
|
|
|
let mut face_buf = Vec::new(); |
|
|
|
image::open(&face) |
|
|
|
.expect(&format!("Error loading the image in {:?}", face)) |
|
|
|
.write_to(&mut face_buf, image::ImageOutputFormat::Png) |
|
|
|
.unwrap(); |
|
|
|
let mut back_buf = Vec::new(); |
|
|
|
image::open(&back) |
|
|
|
.expect(&format!("Error loading the image in {:?}", back)) |
|
|
|
.write_to(&mut back_buf, image::ImageOutputFormat::Png) |
|
|
|
.unwrap(); |
|
|
|
Ok(Response::new(Image { |
|
|
|
face: face_buf, |
|
|
|
back: back_buf, |
|
|
|
})) |
|
|
|
} |
|
|
|
async fn get_card_image( |
|
|
|
&self, |
|
|
|
request: tonic::Request<CardKind>, |
|
|
|
) -> Result<tonic::Response<Image>, Status> { |
|
|
|
let uuid = client_id::get(request.metadata()).map_err(|x| match x { |
|
|
|
client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), |
|
|
|
client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), |
|
|
|
})?; |
|
|
|
let mut conn = self.conn.write().await; |
|
|
|
let lobby: u32 = match conn.get_lobby_for_user(uuid).await { |
|
|
|
Some(l) => l, |
|
|
|
None => return Err(Status::failed_precondition("User isn't in a lobby")), |
|
|
|
}; |
|
|
|
let game_id = match self.running_games.read().await.get(&lobby) { |
|
|
|
Some(x) => x.read().await.0, |
|
|
|
None => { |
|
|
|
return Err(Status::failed_precondition( |
|
|
|
"User isn't in a lobby with a running game", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
let game = &self.games[game_id as usize]; |
|
|
|
let (face, back) = game.get_card_paths(&request.into_inner().kind); |
|
|
|
let mut face_buf = Vec::new(); |
|
|
|
image::open(&face) |
|
|
|
.expect(&format!("Error loading the image in {:?}", face)) |
|
|
|
.write_to(&mut face_buf, image::ImageOutputFormat::Png) |
|
|
|
.unwrap(); |
|
|
|
let mut back_buf = Vec::new(); |
|
|
|
image::open(&back) |
|
|
|
.expect(&format!("Error loading the image in {:?}", back)) |
|
|
|
.write_to(&mut back_buf, image::ImageOutputFormat::Png) |
|
|
|
.unwrap(); |
|
|
|
Ok(Response::new(Image { |
|
|
|
face: face_buf, |
|
|
|
back: back_buf, |
|
|
|
})) |
|
|
|
} |
|
|
|
|
|
|
|
async fn on_click( |
|
|
|
&self, |
|
|
|
request: tonic::Request<super::grpc::game::CardId>, |
|
|
|
) -> Result<tonic::Response<()>, Status> { |
|
|
|
let uuid = client_id::get(request.metadata()).map_err(|x| match x { |
|
|
|
client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), |
|
|
|
client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), |
|
|
|
})?; |
|
|
|
let mut conn = self.conn.write().await; |
|
|
|
let lobby: u32 = match conn.get_lobby_for_user(uuid).await { |
|
|
|
Some(l) => l, |
|
|
|
None => return Err(Status::failed_precondition("User isn't in a lobby")), |
|
|
|
}; |
|
|
|
let games_lock = self.running_games.read().await; |
|
|
|
let mut game_lock = match games_lock.get(&lobby) { |
|
|
|
Some(x) => x.write().await, |
|
|
|
None => { |
|
|
|
return Err(Status::failed_precondition( |
|
|
|
"User isn't in a lobby with a running game", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
let game = &mut game_lock.1; |
|
|
|
let card = request.into_inner(); |
|
|
|
let idx = match card.card_index.unwrap().pos.unwrap() { |
|
|
|
super::grpc::game::card_index::Pos::Bottom(()) => CardIdx::Bottom, |
|
|
|
super::grpc::game::card_index::Pos::Top(()) => CardIdx::Top, |
|
|
|
super::grpc::game::card_index::Pos::Index(x) => CardIdx::Indexed(x as usize), |
|
|
|
}; |
|
|
|
let pile_kind = match card.pile_kind.unwrap().kind.unwrap() { |
|
|
|
super::grpc::game::pile_kind::Kind::Common(()) => PileKind::Common, |
|
|
|
super::grpc::game::pile_kind::Kind::Owned(p) => PileKind::Owned(p), |
|
|
|
}; |
|
|
|
let card: CardId = CardId { |
|
|
|
idx, |
|
|
|
pile_kind, |
|
|
|
pile_name: card.pile_name, |
|
|
|
}; |
|
|
|
game.on_click(card, game.get_player_for_uuid(&uuid).unwrap()); |
|
|
|
Ok(Response::new(())) |
|
|
|
} |
|
|
|
async fn on_click( |
|
|
|
&self, |
|
|
|
request: tonic::Request<super::grpc::game::CardId>, |
|
|
|
) -> Result<tonic::Response<()>, Status> { |
|
|
|
let uuid = client_id::get(request.metadata()).map_err(|x| match x { |
|
|
|
client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), |
|
|
|
client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), |
|
|
|
})?; |
|
|
|
let mut conn = self.conn.write().await; |
|
|
|
let lobby: u32 = match conn.get_lobby_for_user(uuid).await { |
|
|
|
Some(l) => l, |
|
|
|
None => return Err(Status::failed_precondition("User isn't in a lobby")), |
|
|
|
}; |
|
|
|
let games_lock = self.running_games.read().await; |
|
|
|
let mut game_lock = match games_lock.get(&lobby) { |
|
|
|
Some(x) => x.write().await, |
|
|
|
None => { |
|
|
|
return Err(Status::failed_precondition( |
|
|
|
"User isn't in a lobby with a running game", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
let game = &mut game_lock.1; |
|
|
|
let card = request.into_inner(); |
|
|
|
let idx = match card.card_index.unwrap().pos.unwrap() { |
|
|
|
super::grpc::game::card_index::Pos::Bottom(()) => CardIdx::Bottom, |
|
|
|
super::grpc::game::card_index::Pos::Top(()) => CardIdx::Top, |
|
|
|
super::grpc::game::card_index::Pos::Index(x) => CardIdx::Indexed(x as usize), |
|
|
|
}; |
|
|
|
let pile_kind = match card.pile_kind.unwrap().kind.unwrap() { |
|
|
|
super::grpc::game::pile_kind::Kind::Common(()) => PileKind::Common, |
|
|
|
super::grpc::game::pile_kind::Kind::Owned(p) => PileKind::Owned(p), |
|
|
|
}; |
|
|
|
let card: CardId = CardId { |
|
|
|
idx, |
|
|
|
pile_kind, |
|
|
|
pile_name: card.pile_name, |
|
|
|
}; |
|
|
|
game.on_click(card, game.get_player_for_uuid(&uuid).unwrap()); |
|
|
|
Ok(Response::new(())) |
|
|
|
} |
|
|
|
|
|
|
|
async fn status( |
|
|
|
&self, |
|
|
|
request: tonic::Request<()>, |
|
|
|
) -> Result<Response<super::grpc::game::MessageStatus>, Status> { |
|
|
|
let uuid = client_id::get(request.metadata()).map_err(|x| match x { |
|
|
|
client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), |
|
|
|
client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), |
|
|
|
})?; |
|
|
|
let mut conn = self.conn.write().await; |
|
|
|
let lobby: u32 = match conn.get_lobby_for_user(uuid).await { |
|
|
|
Some(l) => l, |
|
|
|
None => return Err(Status::failed_precondition("User isn't in a lobby")), |
|
|
|
}; |
|
|
|
let games_lock = self.running_games.read().await; |
|
|
|
let game_lock = match games_lock.get(&lobby) { |
|
|
|
Some(x) => x.read().await, |
|
|
|
None => { |
|
|
|
return Err(Status::failed_precondition( |
|
|
|
"User isn't in a lobby with a running game", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
let game = &game_lock.1; |
|
|
|
let mut names = vec![]; |
|
|
|
for (uuid, id) in &game.players { |
|
|
|
names.push((conn.get_name_for_uuid(uuid.clone()).await, *id)) |
|
|
|
} |
|
|
|
names.sort_by(|(_, a), (_, b)| a.cmp(b)); |
|
|
|
let names = names.into_iter().map(|(x, _)| super::grpc::common::Name {name: x}).collect(); |
|
|
|
let status = MessageStatus { |
|
|
|
current_turn: game.get_current_player(), |
|
|
|
common_piles: Some(Piles { |
|
|
|
piles: game |
|
|
|
.piles |
|
|
|
.iter() |
|
|
|
.map(|(k, v)| (k.clone(), v.clone())) |
|
|
|
.map(|(k, v): (String, Pile)| { |
|
|
|
( |
|
|
|
k, |
|
|
|
super::grpc::game::message_status::Pile { |
|
|
|
cards: v |
|
|
|
.cards |
|
|
|
.into_iter() |
|
|
|
.map(|c| super::grpc::game::message_status::Card { |
|
|
|
kind: Some(CardKind { kind: c }), |
|
|
|
visible: true, |
|
|
|
}) |
|
|
|
.collect(), |
|
|
|
}, |
|
|
|
) |
|
|
|
}) |
|
|
|
.collect(), |
|
|
|
}), |
|
|
|
player_piles: vec![], |
|
|
|
names, |
|
|
|
}; |
|
|
|
Ok(Response::new(status)) |
|
|
|
} |
|
|
|
async fn status( |
|
|
|
&self, |
|
|
|
request: tonic::Request<()>, |
|
|
|
) -> Result<Response<super::grpc::game::MessageStatus>, Status> { |
|
|
|
let uuid = client_id::get(request.metadata()).map_err(|x| match x { |
|
|
|
client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), |
|
|
|
client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), |
|
|
|
})?; |
|
|
|
let mut conn = self.conn.write().await; |
|
|
|
let lobby: u32 = match conn.get_lobby_for_user(uuid).await { |
|
|
|
Some(l) => l, |
|
|
|
None => return Err(Status::failed_precondition("User isn't in a lobby")), |
|
|
|
}; |
|
|
|
let games_lock = self.running_games.read().await; |
|
|
|
let game_lock = match games_lock.get(&lobby) { |
|
|
|
Some(x) => x.read().await, |
|
|
|
None => { |
|
|
|
return Err(Status::failed_precondition( |
|
|
|
"User isn't in a lobby with a running game", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
let game = &game_lock.1; |
|
|
|
let mut names = vec![]; |
|
|
|
for (uuid, id) in &game.players { |
|
|
|
names.push((conn.get_name_for_uuid(uuid.clone()).await, *id)) |
|
|
|
} |
|
|
|
names.sort_by(|(_, a), (_, b)| a.cmp(b)); |
|
|
|
let names = names |
|
|
|
.into_iter() |
|
|
|
.map(|(x, _)| super::grpc::common::Name { name: x }) |
|
|
|
.collect(); |
|
|
|
let status = MessageStatus { |
|
|
|
current_turn: game.get_current_player(), |
|
|
|
common_piles: Some(Piles { |
|
|
|
piles: game |
|
|
|
.piles |
|
|
|
.iter() |
|
|
|
.map(|(k, v)| (k.clone(), v.clone())) |
|
|
|
.map(|(k, v): (String, Pile)| { |
|
|
|
( |
|
|
|
k, |
|
|
|
super::grpc::game::message_status::Pile { |
|
|
|
cards: v |
|
|
|
.cards |
|
|
|
.into_iter() |
|
|
|
.map(|c| super::grpc::game::message_status::Card { |
|
|
|
kind: Some(CardKind { kind: c }), |
|
|
|
visible: true, |
|
|
|
}) |
|
|
|
.collect(), |
|
|
|
}, |
|
|
|
) |
|
|
|
}) |
|
|
|
.collect(), |
|
|
|
}), |
|
|
|
player_piles: game |
|
|
|
.player_piles.clone() |
|
|
|
.into_iter() |
|
|
|
.map(|piles| Piles { |
|
|
|
piles: piles |
|
|
|
.into_iter() |
|
|
|
.map(|(k, v)| { |
|
|
|
( |
|
|
|
k, |
|
|
|
super::grpc::game::message_status::Pile { |
|
|
|
cards: v |
|
|
|
.cards |
|
|
|
.into_iter() |
|
|
|
.map(|x| super::grpc::game::message_status::Card { |
|
|
|
kind: Some(CardKind {kind: x}), |
|
|
|
visible: true, |
|
|
|
}) |
|
|
|
.collect(), |
|
|
|
}, |
|
|
|
) |
|
|
|
}) |
|
|
|
.collect(), |
|
|
|
}) |
|
|
|
.collect(), |
|
|
|
names, |
|
|
|
}; |
|
|
|
Ok(Response::new(status)) |
|
|
|
} |
|
|
|
} |
|
|
|
|