diff --git a/server/schema/game-config.json b/server/schema/game-config.json index 35fecd8..5dda872 100644 --- a/server/schema/game-config.json +++ b/server/schema/game-config.json @@ -23,6 +23,12 @@ "$ref": "#/definitions/Card" } }, + "default_back": { + "type": [ + "string", + "null" + ] + }, "name": { "type": "string" }, @@ -53,6 +59,12 @@ "image" ], "properties": { + "back_image": { + "type": [ + "string", + "null" + ] + }, "image": { "type": "string" } diff --git a/server/src/games/config.rs b/server/src/games/config.rs index 2c74aa6..8a64650 100644 --- a/server/src/games/config.rs +++ b/server/src/games/config.rs @@ -12,6 +12,7 @@ pub struct Config { pub authors: Vec, pub script: String, pub available_cards: HashMap, + pub default_back: Option, pub piles: HashMap, pub player_piles: HashMap, } @@ -23,6 +24,7 @@ fn default_version() -> String { #[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)] pub struct Card { pub image: PathBuf, + pub back_image: Option, #[serde(flatten)] pub other: HashMap, } @@ -62,7 +64,7 @@ impl Pile { impl Config { pub fn load + std::fmt::Debug>(file: P) -> Self { - serde_json::from_reader(std::fs::File::open(&file).unwrap()) + let s: Config = serde_json::from_reader(std::fs::File::open(&file).unwrap()) .map_err(|e| { log::error!( "Malformed game defintion file @ {}", @@ -71,12 +73,21 @@ impl Config { log::error!("JSON Error: {}", e); panic!() }) - .unwrap() + .unwrap(); + if s.default_back.is_none() { + for (name, card) in &s.available_cards { + if card.back_image.is_none() { + panic!("Card {} from game {} can not have a default back if there's not default back", name, s.name) + } + } + } + s } } pub fn setup() { - if cfg!(debug_assertions) { + #[cfg(debug_assertions)] + { if let Ok(e) = std::env::var("CARGO_MANIFEST_DIR") { std::fs::write( AsRef::::as_ref(&e) diff --git a/server/src/games/mod.rs b/server/src/games/mod.rs index f4daf9e..c66b6e6 100644 --- a/server/src/games/mod.rs +++ b/server/src/games/mod.rs @@ -8,98 +8,104 @@ use uuid::Uuid; #[derive(Clone)] pub struct Game { - pub name: String, - pub version: String, - pub authors: Vec, - conf: config::Config, - folder: std::path::PathBuf, - ast: rhai::AST, - fns: Vec, + pub name: String, + pub version: String, + pub authors: Vec, + conf: config::Config, + folder: std::path::PathBuf, + ast: rhai::AST, + fns: Vec, } impl Game { - pub fn load + std::fmt::Debug>(folder: P) -> Self { - // config::setup(); + pub fn load + std::fmt::Debug>(folder: P) -> Self { + // config::setup(); - let conf = config::Config::load(folder.as_ref().join("game.json")); - let (ast, fns) = - RunningGame::compile(folder.as_ref().join(&conf.script)).expect("Compile error"); - Self { - conf: conf.clone(), - name: conf.name, - version: conf.version, - authors: conf.authors, - folder: folder.as_ref().to_path_buf(), - ast, - fns, - } - } + let conf = config::Config::load(folder.as_ref().join("game.json")); + let (ast, fns) = + RunningGame::compile(folder.as_ref().join(&conf.script)).expect("Compile error"); + Self { + conf: conf.clone(), + name: conf.name, + version: conf.version, + authors: conf.authors, + folder: folder.as_ref().to_path_buf(), + ast, + fns, + } + } - // pub fn name(&self) -> String { - // self.name.clone() - // } - // pub fn version(&self) -> String { - // self.version.clone() - // } - // pub fn authors(&self) -> Vec { - // self.authors.clone() - // } + // pub fn name(&self) -> String { + // self.name.clone() + // } + // pub fn version(&self) -> String { + // self.version.clone() + // } + // pub fn authors(&self) -> Vec { + // self.authors.clone() + // } - pub fn run(&self, players: &[Uuid]) -> run::RunningGame { - // let ast = rhai::Engine::new().compile_file(self.folder.join(&self.conf.script)).unwrap(); - run::RunningGame::new( - self.name.clone(), - self.ast.clone(), - &self.fns, - &self.conf, - players, - ) - } + pub fn run(&self, players: &[Uuid]) -> run::RunningGame { + // let ast = rhai::Engine::new().compile_file(self.folder.join(&self.conf.script)).unwrap(); + run::RunningGame::new( + self.name.clone(), + self.ast.clone(), + &self.fns, + &self.conf, + players, + ) + } - pub fn get_card_path(&self, image: &str) -> std::path::PathBuf { - self.folder - .join(&self.conf.available_cards[image].image) - .to_path_buf() - } + pub fn get_card_paths(&self, image: &str) -> (std::path::PathBuf, std::path::PathBuf) { + let card = &self.conf.available_cards[image]; + let front = self.folder.join(&card.image).to_path_buf(); + let back = self.folder.join( + &card + .back_image + .as_ref() + .unwrap_or(self.conf.default_back.as_ref().unwrap()), + ); + (front, back) + } } impl std::fmt::Display for Game { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{} [{}]{}", - self.name, - self.version, - if self.authors.is_empty() { - String::new() - } else { - format!(" by {}", self.authors.join(", ")) - } - ) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} [{}]{}", + self.name, + self.version, + if self.authors.is_empty() { + String::new() + } else { + format!(" by {}", self.authors.join(", ")) + } + ) + } } impl std::fmt::Debug for Game { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self) + } } pub fn load_games() -> Vec { - config::setup(); - let mut games = Vec::new(); - for file in read_dir("games").unwrap() { - if let Ok(folder) = file { - if folder.path().is_dir() { - for file in read_dir(folder.path()).unwrap() { - if let Ok(file) = file { - if file.file_name().to_str().unwrap() == "game.json" { - games.push(Game::load(folder.path())) - } - } - } - } - } - } - games + config::setup(); + let mut games = Vec::new(); + for file in read_dir("games").unwrap() { + if let Ok(folder) = file { + if folder.path().is_dir() { + for file in read_dir(folder.path()).unwrap() { + if let Ok(file) = file { + if file.file_name().to_str().unwrap() == "game.json" { + games.push(Game::load(folder.path())) + } + } + } + } + } + } + games } diff --git a/server/src/games/run.rs b/server/src/games/run.rs index 4dd8982..25e7e94 100644 --- a/server/src/games/run.rs +++ b/server/src/games/run.rs @@ -85,6 +85,10 @@ impl RunningGame { // Self {setup: Box::new(Func::<(Vec, Vec>), ()>::create_from_ast(engine, ast, "setup"))} } + pub fn get_player_for_uuid(&self, user: &Uuid) -> Option { + self.players.get(user).copied() + } + fn data_as_dynamic(&self) -> RhaiResult { to_dynamic(Data { piles: self.piles.clone(), diff --git a/server/src/server.rs b/server/src/server.rs index 1237951..4d5b234 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -32,8 +32,8 @@ pub async fn start( voting: voting.clone(), running_games: running_games.clone(), }; - let lobby = LobbyService::new(pool.clone().await, voting, games, running_games.clone()); - let game = GameService::new(pool, running_games); + let lobby = LobbyService::new(pool.clone().await, voting, games.clone(), running_games.clone()); + let game = GameService::new(pool, games, running_games); Server::builder() .add_service(ConnectionServer::new(connection)) diff --git a/server/src/server/game.rs b/server/src/server/game.rs index 501c2d7..202d0a4 100644 --- a/server/src/server/game.rs +++ b/server/src/server/game.rs @@ -1,8 +1,9 @@ -use crate::db; use crate::games::RunningGame; +use crate::{db, games::Game}; -use super::grpc::game::game_server; +use super::{client_id, grpc::game::{CardKind, Image, game_server}}; pub use game_server::GameServer; +use tonic::{Response, Status}; use std::collections::HashMap; use std::sync::Arc; @@ -11,17 +12,20 @@ use tokio::sync::RwLock; pub struct GameService { conn: RwLock, + games: Arc>, running_games: Arc>>>, } impl GameService { pub fn new( conn: db::DbClient, + games: Arc>, running_games: Arc>>>, ) -> Self { Self { conn: RwLock::new(conn), running_games, + games, } } } @@ -30,15 +34,44 @@ impl GameService { impl game_server::Game for GameService { async fn get_card_image( &self, - request: tonic::Request, - ) -> Result, tonic::Status> { - todo!() + request: tonic::Request, + ) -> Result, 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, - ) -> Result, tonic::Status> { + ) -> Result, Status> { todo!() } }