use crate::user_handler::*; use crate::AppState; use crate::User; use anyhow::{Context, Result}; use lib::*; use rand::prelude::IteratorRandom; use rand::thread_rng; use serde::Deserialize; use std::{ collections::HashMap, fs::read_to_string, net::SocketAddr, sync::{Arc, RwLock}, }; use uuid::Uuid; /// Parse json for card data pub fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> { // TODO: Repack these cards so every card is stored once and pointers are passed around instead of // cloning stuff let data: String = read_to_string(path).with_context(|| format!("Invalid JSON path: \"{}\"", path))?; let jayson: Vec = serde_json::from_str(&data) .with_context(|| format!("The contents of \"{path}\" is not valid JSON."))?; let mut official: HashMap = HashMap::new(); let mut unofficial: HashMap = HashMap::new(); let mut official_meta: Vec = vec![]; let mut unofficial_meta: Vec = vec![]; for set in jayson { let mut num_white = 0; let mut num_black = 0; let mut newset = CardSet { white: Option::None, black: Option::None, }; // No safe default for this so make it an Option let mut pack: Option = Option::None; if let Some(ref white) = set.white { num_white = white.len(); if num_white > 0 { pack = Some(white[0].pack); newset.white = Some(set.white.unwrap()); } } if let Some(ref black) = set.black { num_black = black.len(); if num_black > 0 { pack = Some(black[0].pack); newset.black = Some(set.black.unwrap()); } } let meta = CardPackMeta { name: set.name, pack: pack.expect("No card pack number!"), num_white, num_black, }; if set.official { official_meta.push(meta); official.insert(pack.unwrap(), newset); } else { unofficial_meta.push(meta); unofficial.insert(pack.unwrap(), newset); } } official.shrink_to_fit(); unofficial.shrink_to_fit(); official_meta.shrink_to_fit(); unofficial_meta.shrink_to_fit(); tracing::debug!("{} official", official.len()); tracing::debug!("{} official meta", official_meta.len()); tracing::debug!("{} unofficial", unofficial.len()); tracing::debug!("{} unofficial meta", unofficial_meta.len()); tracing::debug!("{:#?}", official_meta[0]); tracing::debug!("{:#?}", unofficial_meta[0]); let packs = CardPacks { official, unofficial, }; let packs_meta = CardPacksMeta { official_meta, unofficial_meta, }; Ok((packs, packs_meta)) } /// Card Set #[derive(Debug)] struct CardSet { white: Option>, black: Option>, } /// Card Packs #[derive(Debug)] pub struct CardPacks { official: HashMap, unofficial: HashMap, } /// A white card // TODO: Remove this clone! #[derive(Debug, Deserialize, Clone)] struct CardWhite { /// Card text text: String, /// ID of the pack it came from pack: u8, } /// A black card // TODO: Remove this clone! #[derive(Debug, Deserialize, Clone)] struct CardBlack { /// Card text text: String, /// Amount of cards to submit for judging pick: u8, /// ID of the pack it came from pack: u8, } /// A card pack #[derive(Debug, Deserialize)] struct CardPack { /// Name of the pack name: String, /// Whether or not this is an official card pack official: bool, /// White card data white: Option>, /// Black card data black: Option>, } /// New game request structure #[derive(Debug)] struct NewGameManifest { /// Game name name: String, /// Game host host: Arc>, /// Selected game packs packs: Vec, } /// A struct that represents a player #[derive(Debug)] pub struct Player { /// Pointer to user user: Arc>, /// The player's hand white: Vec, /// The player's wins black: Vec, } /// The game master #[derive(Debug)] pub struct Game { /// Game's UUID pub uuid: Uuid, /// The name of the game pub name: String, /// The host user of the game pub host: Arc>, /// White draw pile white: Vec, /// Black draw pile black: Vec, pub players: HashMap, /// Black card for the current round current_black: Option, pub packs: Vec, } impl Game { fn new(state: Arc, request: NewGameManifest) -> Result { tracing::debug!("{:#?}", request.packs); tracing::debug!("{:#?}", request.packs.len()); let mut game = Game { uuid: Uuid::now_v7(), name: request.host.read().unwrap().name.clone(), host: request.host.clone(), white: vec![], black: vec![], players: HashMap::new(), current_black: Option::None, packs: request.packs.clone(), }; tracing::debug!( "Creating game {} with {} as host", &request.name, request.host.read().unwrap().name ); game.name = request.name; game.host = request.host.clone(); game.build_decks(state, request.packs)?; game.create_player(request.host)?; game.deal_black()?; Ok(game) } /// Build game decks from input data for game start. /// This should only run once and at startup. fn build_decks(&mut self, state: Arc, selected_packs: Vec) -> Result<()> { // TODO: Make this right -- remove the clones, use references to single cards for pack_num in selected_packs { if let Some(pack) = state.packs.official.get(&pack_num) { if let Some(white) = &pack.white { self.white.extend(white.clone()) } if let Some(black) = &pack.black { self.black.extend(black.clone()) } } else if let Some(pack) = state.packs.unofficial.get(&pack_num) { if let Some(white) = &pack.white { self.white.extend(white.clone()) } if let Some(black) = &pack.black { self.black.extend(black.clone()) } } } Ok(()) } /// Draw one white card at random from play deck. fn draw_one_white(&mut self) -> Result { let deck = &mut self.white; // TODO: this feels sloppy if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) { Ok(deck.swap_remove(index)) } else { Ok(CardWhite { text: "Error.\n\nbtw if you see this tell me I'm lazy :)".to_string(), pack: 0, }) } } /// Draw one black card at random from play deck. fn draw_one_black(&mut self) -> Result { let deck = &mut self.black; // TODO: this feels sloppy if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) { Ok(deck.swap_remove(index)) } else { Ok(CardBlack { text: "Error.\n\nbtw if you see this tell me I'm lazy :)".to_string(), pick: 0, pack: 0, }) } } /// Deal a black card and use it for the current round fn deal_black(&mut self) -> Result<()> { self.current_black = Some(self.draw_one_black()?); Ok(()) } /// Create a new player and add them to the game. pub fn create_player(&mut self, user: Arc>) -> Result<()> { let mut new_player = Player { user: user.clone(), white: vec![], black: vec![], }; let new_player_name = user.read().unwrap().name.clone(); tracing::debug!("Creating player for {}", &new_player_name); let mut hand_buf = vec![]; for _ in 0..10 { hand_buf.push(self.draw_one_white()?); } tracing::debug!("Dealing hand to {}", &new_player_name); new_player.white.extend(hand_buf); self.players .insert(user.read().unwrap().uuid.clone(), new_player); Ok(()) } } pub enum GameHandlerMessage { NewGame { addr: SocketAddr, new_game: NewGameRequest, }, JoinGame { addr: SocketAddr, id: String, }, } pub struct GameHandler { state: Arc, } impl GameHandler { pub fn new(state: Arc) -> Self { GameHandler { state } } pub async fn handle(&self, message: GameHandlerMessage) { match message { GameHandlerMessage::NewGame { addr, new_game } => self.new_game(addr, new_game).await, GameHandlerMessage::JoinGame { addr, id } => self.join_game(addr, id).await, _ => { tracing::debug!("Unhandled at game handler"); } } } async fn join_game(&self, addr: SocketAddr, id: String) { // Get pointers let this_game = self.state.games.read().unwrap().get(&id).unwrap().clone(); let this_user = self .state .online_users .read() .unwrap() .get(&addr) .unwrap() .clone(); // Create player this_game.write().unwrap().create_player(this_user).unwrap(); // Send updates for all players for player in this_game.read().unwrap().players.values() { // Create update for user's game view let mut black_card = ("Error".to_string(), 0u8); if let Some(ref current_black) = this_game.read().unwrap().current_black { black_card = (current_black.text.to_owned(), current_black.pick) } let meta = GameStateMeta { uuid: id.clone(), name: this_game.read().unwrap().name.clone(), host: this_game.read().unwrap().host.read().unwrap().name.clone(), players: this_game .read() .unwrap() .players .values() .map(|player| player.user.read().unwrap().name.clone()) .collect(), czar: this_game.read().unwrap().host.read().unwrap().name.clone(), black: black_card, white: player.white.iter().map(|card| card.text.clone()).collect(), packs: this_game.read().unwrap().packs.clone(), }; // Send user's update let msg = serde_json::to_string(&meta).unwrap(); let user_tx = player.user.read().unwrap().tx.clone(); tokio::spawn(async move { user_tx.send(msg).await }); } // Broadcast game browser update self.state .broadcast_tx .send(meta_games_browser_update(&self.state)) .unwrap(); // Broadcast server meta update self.state .broadcast_tx .send(meta_server_summary_update(&self.state)) .unwrap(); } async fn new_game(&self, addr: SocketAddr, new_game: NewGameRequest) { if new_game.packs.is_empty() { tracing::debug!("Cards are empty"); return; } else if new_game.name.is_empty() { tracing::debug!("Name are empty"); return; } let manifest = NewGameManifest { name: new_game.name, host: self .state .online_users .read() .unwrap() .get(&addr) .unwrap() .clone(), packs: new_game.packs, }; // Create game if let Ok(new_game_object) = Game::new(self.state.clone(), manifest) { let tx = self .state .online_users .read() .unwrap() .get(&addr) .unwrap() .read() .unwrap() .tx .clone(); // Create update for user's game view let mut black_card = ("Error".to_string(), 0u8); if let Some(ref current_black) = new_game_object.current_black { black_card = (current_black.text.to_owned(), current_black.pick) } let meta = GameStateMeta { uuid: new_game_object.uuid.to_string(), name: new_game_object.name.clone(), host: new_game_object.host.read().unwrap().name.clone(), players: new_game_object .players .iter() .map(|player| { self.state .user_uuid .read() .unwrap() .get(player.0) .unwrap() .read() .unwrap() .name .clone() }) .collect(), czar: new_game_object.host.read().unwrap().name.clone(), black: black_card, white: new_game_object .players .get(&new_game_object.host.read().unwrap().uuid) .unwrap() .white .iter() .map(|card| card.text.clone()) .collect(), packs: new_game_object.packs.clone(), }; // Send user's update tx.send(serde_json::to_string(&meta).unwrap()) .await .unwrap(); // Add game to active list self.state.games.write().unwrap().insert( new_game_object.uuid.to_string(), Arc::new(RwLock::new(new_game_object)), ); // Broadcast game browser update self.state .broadcast_tx .send(meta_games_browser_update(&self.state)) .unwrap(); // Broadcast server meta update self.state .broadcast_tx .send(meta_server_summary_update(&self.state)) .unwrap(); } } }