From 53b6d63f3de0b8c87b3e1e3d33eef9d8c2cce35e Mon Sep 17 00:00:00 2001 From: Adam Doyle Date: Thu, 21 Nov 2024 12:41:17 -0500 Subject: [PATCH] let game handler own card data --- server/src/card_loader.rs | 159 ++++++++++++++++++++++++++++++++++++ server/src/game.rs | 8 +- server/src/game_handler.rs | 28 +++++-- server/src/lib.rs | 162 +------------------------------------ server/src/main.rs | 4 - server/src/user_handler.rs | 12 +-- 6 files changed, 191 insertions(+), 182 deletions(-) create mode 100644 server/src/card_loader.rs diff --git a/server/src/card_loader.rs b/server/src/card_loader.rs new file mode 100644 index 0000000..bddac23 --- /dev/null +++ b/server/src/card_loader.rs @@ -0,0 +1,159 @@ +use crate::game::*; +use anyhow::{Context, Result}; +use lib::*; +use serde::Deserialize; +use std::{collections::HashMap, fs::read_to_string, sync::Arc}; +use uuid::Uuid; + +/// Card Set +pub struct CardSet { + pub black: Option>>, + pub white: Option>>, +} + +/// Card Packs +pub struct CardPacks { + pub official: HashMap, + pub unofficial: HashMap, +} + +/// A raw white card as it exists in the source json +#[derive(Deserialize)] +struct CardWhiteFromJSON { + /// Card text + text: String, + /// ID of the pack it came from + pack: u8, +} + +/// A raw black card as it exists in the source json +#[derive(Deserialize)] +struct CardBlackFromJSON { + /// 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(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>, +} + +/// Parse json for card data +pub fn card_loader( + path: &str, +) -> Result<( + CardPacks, + CardPacksMeta, + // HashMap>, +)> { + // Load in json + 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."))?; + + // For global state + let mut official: HashMap = HashMap::new(); + let mut official_meta: Vec = vec![]; + let mut unofficial: HashMap = HashMap::new(); + let mut unofficial_meta: Vec = vec![]; + // let mut white_cards_by_id = HashMap::>::new(); + + // Unpack the json + for sets 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; + + // Process white cards if there are any + if let Some(ref white) = sets.white { + num_white = white.len(); + if num_white > 0 { + pack = Some(white[0].pack); + let mut white_buf = vec![]; + for card in sets.white.unwrap() { + let uuid = Uuid::now_v7(); + + let new_card = Arc::new(CardWhiteWithID { + uuid, + text: card.text, + }); + + // white_cards_by_id.insert(uuid.to_string(), new_card.clone()); + white_buf.push(new_card); + } + newset.white = Some(white_buf); + } + } + + // Process black cards if there are any + if let Some(ref black) = sets.black { + num_black = black.len(); + if num_black > 0 { + pack = Some(black[0].pack); + let mut black_buf = vec![]; + for card in sets.black.unwrap() { + black_buf.push(Arc::new(CardBlackWithID { + uuid: Uuid::now_v7(), + text: card.text, + pick: card.pick, + })); + } + newset.black = Some(black_buf); + } + } + + // Start repackaging + let meta = CardPackMeta { + name: sets.name, + pack: pack.expect("No card pack number!").to_string(), + num_white: num_white.try_into().unwrap(), + num_black: num_black.try_into().unwrap(), + }; + + if sets.official { + official_meta.push(meta); + official.insert(pack.unwrap(), newset); + } else { + unofficial_meta.push(meta); + unofficial.insert(pack.unwrap(), newset); + } + } + + // These are now the largest size they should ever be + official.shrink_to_fit(); + unofficial.shrink_to_fit(); + official_meta.shrink_to_fit(); + unofficial_meta.shrink_to_fit(); + + // Package for export + let packs = CardPacks { + official, + unofficial, + }; + let packs_meta = CardPacksMeta { + official_meta, + unofficial_meta, + }; + + Ok((packs, packs_meta)) +} diff --git a/server/src/game.rs b/server/src/game.rs index e4e1926..31fdb91 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,4 +1,4 @@ -use crate::AppState; +use crate::card_loader::*; use crate::User; use lib::*; use rand::prelude::IteratorRandom; @@ -89,19 +89,19 @@ pub struct Game { impl Game { /// Returns a new game object - pub fn new(state: Arc, request: NewGameManifest) -> Self { + pub fn new(packs: &CardPacks, request: NewGameManifest) -> Self { // Build the decks let mut white = vec![]; let mut black = vec![]; for pack_num in &request.packs { - if let Some(pack) = state.packs.official.get(&pack_num) { + if let Some(pack) = packs.official.get(&pack_num) { if let Some(card) = &pack.white { white.extend(card.clone()) } if let Some(card) = &pack.black { black.extend(card.clone()) } - } else if let Some(pack) = state.packs.unofficial.get(&pack_num) { + } else if let Some(pack) = packs.unofficial.get(&pack_num) { if let Some(card) = &pack.white { white.extend(card.clone()) } diff --git a/server/src/game_handler.rs b/server/src/game_handler.rs index f881ebf..5304f29 100644 --- a/server/src/game_handler.rs +++ b/server/src/game_handler.rs @@ -1,15 +1,15 @@ +use crate::card_loader::*; use crate::game::*; - use crate::AppState; use crate::DmUserMethod::*; use crate::GameHandlerMessage::*; use crate::SendUserMessage::*; +use crate::Sender; use crate::UserHandlerMessage::*; use axum::extract::ws::Message; use lib::*; use serde_json::to_string; -use std::collections::HashMap; -use std::{net::SocketAddr, sync::Arc}; +use std::{collections::HashMap, net::SocketAddr, sync::Arc}; /// For interacting with the game handler pub enum GameHandlerMessage { @@ -21,6 +21,7 @@ pub enum GameHandlerMessage { SendGameStateUpdate(Vec), SendGameMetaUpdate(Vec), BroadcastGamesUpdate(), + SendCardPacks(Sender), } /// Handles game stuff @@ -28,13 +29,21 @@ pub struct GameHandler { /// Global state pointer state: Arc, games: HashMap, + packs: CardPacks, + packs_meta: CardPacksMeta, } impl GameHandler { /// Returns a new game handler pub fn new(state: Arc) -> Self { let games = HashMap::new(); - GameHandler { state, games } + let (packs, packs_meta) = card_loader("data/cah-cards-full.json").unwrap(); + GameHandler { + state, + games, + packs, + packs_meta, + } } /// Handles incoming messages @@ -48,6 +57,7 @@ impl GameHandler { SendGameStateUpdate(game_ids) => self.send_game_state_update_all(game_ids), SendGameMetaUpdate(game_ids) => self.send_game_meta_update(game_ids), BroadcastGamesUpdate() => self.broadcast_game_browser_update(), + SendCardPacks(tx) => self.send_card_packs(tx).await, } } @@ -340,7 +350,7 @@ impl GameHandler { }; // Create game using manifest - let mut new_game_object = Game::new(self.state.clone(), manifest); + let mut new_game_object = Game::new(&self.packs, manifest); // Don't forget to create the host player!!! new_game_object.create_player(host.clone()); @@ -399,4 +409,12 @@ impl GameHandler { } }); } + + /// Send available card packs to a user + async fn send_card_packs(&self, tx: Sender) { + let msg = Message::Text(to_string::(&self.packs_meta).unwrap()); + if let Err(e) = tx.send(msg).await { + tracing::error!("Error sending card packs: {}", e) + } + } } diff --git a/server/src/lib.rs b/server/src/lib.rs index db74312..ba8ba37 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,14 +1,11 @@ #![feature(if_let_guard)] -use crate::game::*; use crate::game_handler::*; use anyhow::{Context, Result}; use axum::extract::ws::Message; use lib::*; -use serde::Deserialize; use std::{ collections::{HashMap, HashSet}, - fs::read_to_string, net::SocketAddr, sync::{Arc, RwLock}, }; @@ -16,6 +13,7 @@ use tokio::sync::mpsc::Sender; use tokio::sync::{broadcast, mpsc}; use user_handler::*; use uuid::Uuid; +pub mod card_loader; pub mod game; pub mod game_handler; pub mod incoming_message_handler; @@ -50,166 +48,8 @@ pub struct AppState { pub games_by_user: RwLock>>, pub offline_users: RwLock>>>, pub online_users: RwLock>>>, - pub packs: CardPacks, - pub packs_meta: CardPacksMeta, pub tx_broadcast: broadcast::Sender, pub tx_game_handler: mpsc::Sender, pub tx_incoming_message_handler: mpsc::Sender<(SocketAddr, Message)>, pub tx_user_handler: mpsc::Sender, - pub white_cards_by_id: HashMap>, -} - -/// Card Set -#[derive(Debug)] -struct CardSet { - black: Option>>, - white: Option>>, -} - -/// Card Packs -#[derive(Debug)] -pub struct CardPacks { - official: HashMap, - unofficial: HashMap, -} - -/// A raw white card as it exists in the source json -#[derive(Debug, Deserialize)] -struct CardWhiteFromJSON { - /// Card text - text: String, - /// ID of the pack it came from - pack: u8, -} - -/// A raw black card as it exists in the source json -#[derive(Debug, Deserialize)] -struct CardBlackFromJSON { - /// 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>, -} - -/// Parse json for card data -pub fn load_cards_from_json( - path: &str, -) -> Result<( - CardPacks, - CardPacksMeta, - HashMap>, -)> { - // Load in json - 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."))?; - - // For global state - let mut official: HashMap = HashMap::new(); - let mut official_meta: Vec = vec![]; - let mut unofficial: HashMap = HashMap::new(); - let mut unofficial_meta: Vec = vec![]; - let mut white_cards_by_id = HashMap::>::new(); - - // Unpack the json - for sets 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; - - // Process white cards if there are any - if let Some(ref white) = sets.white { - num_white = white.len(); - if num_white > 0 { - pack = Some(white[0].pack); - let mut white_buf = vec![]; - for card in sets.white.unwrap() { - let uuid = Uuid::now_v7(); - - let new_card = Arc::new(CardWhiteWithID { - uuid, - text: card.text, - }); - - white_cards_by_id.insert(uuid.to_string(), new_card.clone()); - white_buf.push(new_card); - } - newset.white = Some(white_buf); - } - } - - // Process black cards if there are any - if let Some(ref black) = sets.black { - num_black = black.len(); - if num_black > 0 { - pack = Some(black[0].pack); - let mut black_buf = vec![]; - for card in sets.black.unwrap() { - black_buf.push(Arc::new(CardBlackWithID { - uuid: Uuid::now_v7(), - text: card.text, - pick: card.pick, - })); - } - newset.black = Some(black_buf); - } - } - - // Start repackaging - let meta = CardPackMeta { - name: sets.name, - pack: pack.expect("No card pack number!").to_string(), - num_white: num_white.try_into().unwrap(), - num_black: num_black.try_into().unwrap(), - }; - - if sets.official { - official_meta.push(meta); - official.insert(pack.unwrap(), newset); - } else { - unofficial_meta.push(meta); - unofficial.insert(pack.unwrap(), newset); - } - } - - // These are now the largest size they should ever be - official.shrink_to_fit(); - unofficial.shrink_to_fit(); - official_meta.shrink_to_fit(); - unofficial_meta.shrink_to_fit(); - - // Package for export - let packs = CardPacks { - official, - unofficial, - }; - let packs_meta = CardPacksMeta { - official_meta, - unofficial_meta, - }; - - Ok((packs, packs_meta, white_cards_by_id)) } diff --git a/server/src/main.rs b/server/src/main.rs index 22c5c32..52435d6 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -68,7 +68,6 @@ async fn main() -> Result<()> { }); // Set up state - let (packs, packs_meta, white_cards_by_id) = load_cards_from_json("data/cah-cards-full.json")?; let (tx_broadcast, _rx_broadcast) = broadcast::channel(32); let (tx_game_handler, mut rx_game_handler) = mpsc::channel(32); let (tx_incoming_message_handler, mut rx_incoming_message_handler) = mpsc::channel(32); @@ -81,13 +80,10 @@ async fn main() -> Result<()> { games_by_user, offline_users, online_users, - packs, - packs_meta, tx_broadcast, tx_game_handler, tx_incoming_message_handler, tx_user_handler, - white_cards_by_id, }); // Spawn task to handle incoming messages, also handles outging messages diff --git a/server/src/user_handler.rs b/server/src/user_handler.rs index 0211568..b7dc0d0 100644 --- a/server/src/user_handler.rs +++ b/server/src/user_handler.rs @@ -180,9 +180,10 @@ impl UserHandler { let tx = self.state.tx_game_handler.clone(); let msg = GameHandlerMessage::BroadcastGamesUpdate(); tokio::spawn(async move { tx.send(msg).await }); - let tx = dm_tx.clone(); - let msg = meta_new_game_card_packs(&self.state); - tokio::spawn(async move { tx.send(msg).await }); + let msg = GameHandlerMessage::SendCardPacks(dm_tx.clone()); + if let Err(e) = self.state.tx_game_handler.send(msg).await { + tracing::error!("Error contacing game handler {}", e) + } // Broadcast new user's existence // TODO: this should probably be combined and sent as one @@ -448,11 +449,6 @@ pub fn meta_announce_user_join(state: &Arc, addr: &SocketAddr) -> Mess Message::Text(to_string::(&ChatMessage { text: msg }).unwrap()) } -/// Generage cards meta message -pub fn meta_new_game_card_packs(state: &Arc) -> Message { - Message::Text(to_string::(&state.packs_meta).unwrap()) -} - /// Generate message-of-the-day server greeting pub fn meta_motd() -> Message { Message::Text(