diff --git a/server/src/game.rs b/server/src/game.rs new file mode 100644 index 0000000..bae14e5 --- /dev/null +++ b/server/src/game.rs @@ -0,0 +1,183 @@ +use crate::AppState; +use crate::User; +use rand::prelude::IteratorRandom; +use rand::thread_rng; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; +use uuid::Uuid; + +/// Internal manifest for making a new game +#[derive(Debug)] +pub struct NewGameManifest { + /// Game name + pub name: String, + /// Game host + pub host: Arc>, + /// Selected game packs + pub packs: Vec, +} + +/// A processed white card for use server-side +#[derive(Debug)] +pub struct CardWhiteWithID { + /// Unique identifier + pub uuid: Uuid, + /// Card text + pub text: String, +} + +/// A processed black card for use server-side +#[derive(Debug)] +pub struct CardBlackWithID { + /// Unique identifier + pub uuid: Uuid, + /// Card text + pub text: String, + /// Amount of cards to submit for judging + pub pick: u8, +} + +/// A player +#[derive(Debug)] +pub struct Player { + /// Pointer to user + pub user: Arc>, + /// The player's hand + pub white: Vec>, + /// The player's wins + pub black: Vec>, +} + +/// The game object +#[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>, + /// Current card czar + pub czar: Arc>, + /// Packs selected for this game + pub packs: Vec, + /// White draw pile + pub white: Vec>, + /// Black draw pile + pub black: Vec>, + pub players: HashMap, + /// Black card for the current round + pub current_black: Arc, + /// Judge pool + pub judge_pool: HashMap, String>, +} + +impl Game { + /// Returns a new game object + pub fn new(state: Arc, 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(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) { + if let Some(card) = &pack.white { + white.extend(card.clone()) + } + if let Some(card) = &pack.black { + black.extend(card.clone()) + } + } + } + + // Draw first black card + let current_black = black.swap_remove( + (0..black.len()) + .choose(&mut thread_rng()) + .expect("No black cards to draw from!"), + ); + + // These are at the largest size they should ever be + white.shrink_to_fit(); + black.shrink_to_fit(); + + // Return game object + Game { + uuid: Uuid::now_v7(), + name: request.name, + host: request.host.clone(), + czar: request.host.clone(), + white, + black, + players: HashMap::new(), + current_black, + packs: request.packs.clone(), + judge_pool: HashMap::new(), + } + } + + /// Tick forward + pub fn finish_round(&mut self, winner_id: String) { + self.players + .get_mut(&winner_id) + .unwrap() + .black + .push(self.current_black.clone()); + + self.current_black = self.draw_one_black().unwrap(); + } + + /// Draw one black card at random from play deck. + fn draw_one_black(&mut self) -> Option> { + let deck = &mut self.black; + + if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) { + Some(deck.swap_remove(index)) + } else { + tracing::error!("Tried to draw white card that doesn't exist!"); + None + } + } + + /// Draw one white card at random from play deck. + fn draw_one_white(&mut self) -> Option> { + let deck = &mut self.white; + + if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) { + Some(deck.swap_remove(index)) + } else { + tracing::error!("Tried to draw white card that doesn't exist!"); + None + } + } + + /// Create a new player and add them to the game. + pub fn create_player(&mut self, user: Arc>) { + // Build hand of 10 white cards + let mut white = vec![]; + for _ in 0..10 { + if let Some(card) = self.draw_one_white() { + white.push(card); + } + } + + // New player object + let new_player = Player { + user: user.clone(), + white, + black: vec![], + }; + + // Add player to game + self.players + .insert(user.read().unwrap().uuid.clone(), new_player); + } +} diff --git a/server/src/game_handler.rs b/server/src/game_handler.rs index fb73bab..e86eb8b 100644 --- a/server/src/game_handler.rs +++ b/server/src/game_handler.rs @@ -1,22 +1,15 @@ +use crate::game::*; use crate::user_handler::*; use crate::AppState; use crate::DmUserMethod::*; use crate::GameHandlerMessage::*; use crate::SendUserMessage::*; -use crate::User; use crate::UserHandlerMessage::*; -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; /// For interacting with the game handler pub enum GameHandlerMessage { @@ -372,332 +365,3 @@ impl GameHandler { .unwrap(); } } - -/// Card Set -#[derive(Debug)] -struct CardSet { - white: Option>>, - black: 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 processed white card for use server-side -#[derive(Debug)] -pub struct CardWhiteWithID { - /// Unique identifier - pub uuid: Uuid, - /// Card text - pub text: String, -} - -/// A processed black card for use server-side -#[derive(Debug)] -struct CardBlackWithID { - /// Unique identifier - uuid: Uuid, - /// Card text - text: String, - /// Amount of cards to submit for judging - pick: 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>, -} - -/// Internal manifest for making a new game -#[derive(Debug)] -struct NewGameManifest { - /// Game name - name: String, - /// Game host - host: Arc>, - /// Selected game packs - packs: Vec, -} - -/// 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 object -#[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>, - /// Current card czar - pub czar: Arc>, - /// Packs selected for this game - pub packs: Vec, - /// White draw pile - white: Vec>, - /// Black draw pile - black: Vec>, - pub players: HashMap, - /// Black card for the current round - current_black: Arc, - /// Judge pool - judge_pool: HashMap, String>, -} - -impl Game { - /// Returns a new game object - fn new(state: Arc, 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(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) { - if let Some(card) = &pack.white { - white.extend(card.clone()) - } - if let Some(card) = &pack.black { - black.extend(card.clone()) - } - } - } - - // Draw first black card - let current_black = black.swap_remove( - (0..black.len()) - .choose(&mut thread_rng()) - .expect("No black cards to draw from!"), - ); - - // These are at the largest size they should ever be - white.shrink_to_fit(); - black.shrink_to_fit(); - - // Return game object - Game { - uuid: Uuid::now_v7(), - name: request.name, - host: request.host.clone(), - czar: request.host.clone(), - white, - black, - players: HashMap::new(), - current_black, - packs: request.packs.clone(), - judge_pool: HashMap::new(), - } - } - - /// Tick forward - pub fn finish_round(&mut self, winner_id: String) { - self.players - .get_mut(&winner_id) - .unwrap() - .black - .push(self.current_black.clone()); - - self.current_black = self.draw_one_black().unwrap(); - } - - /// Draw one black card at random from play deck. - fn draw_one_black(&mut self) -> Option> { - let deck = &mut self.black; - - if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) { - Some(deck.swap_remove(index)) - } else { - tracing::error!("Tried to draw white card that doesn't exist!"); - None - } - } - - /// Draw one white card at random from play deck. - fn draw_one_white(&mut self) -> Option> { - let deck = &mut self.white; - - if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) { - Some(deck.swap_remove(index)) - } else { - tracing::error!("Tried to draw white card that doesn't exist!"); - None - } - } - - /// Create a new player and add them to the game. - pub fn create_player(&mut self, user: Arc>) { - // Build hand of 10 white cards - let mut white = vec![]; - for _ in 0..10 { - if let Some(card) = self.draw_one_white() { - white.push(card); - } - } - - // New player object - let new_player = Player { - user: user.clone(), - white, - black: vec![], - }; - - // Add player to game - self.players - .insert(user.read().unwrap().uuid.clone(), new_player); - } -} - -/// 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 unofficial: HashMap = HashMap::new(); - let mut official_meta: Vec = vec![]; - 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!"), - num_white, - num_black, - }; - - 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/lib.rs b/server/src/lib.rs index 1a3352e..938f508 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,13 +1,14 @@ #![feature(if_let_guard)] +use crate::game::*; use crate::game_handler::*; use anyhow::{Context, Result}; use axum::extract::ws::Message; -use game_handler::*; use lib::*; +use serde::Deserialize; use std::{ collections::{HashMap, HashSet}, - fs::File, + fs::{read_to_string, File}, io::{BufRead, BufReader}, net::SocketAddr, sync::{Arc, RwLock}, @@ -16,6 +17,7 @@ use tokio::sync::mpsc::Sender; use tokio::sync::{broadcast, mpsc}; use user_handler::*; use uuid::Uuid; +pub mod game; pub mod game_handler; pub mod message_handler; pub mod user_handler; @@ -75,3 +77,158 @@ pub struct AppState { pub packs_meta: CardPacksMeta, pub games: RwLock>>>, } + +/// Card Set +#[derive(Debug)] +struct CardSet { + white: Option>>, + black: 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 unofficial: HashMap = HashMap::new(); + let mut official_meta: Vec = vec![]; + 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!"), + num_white, + num_black, + }; + + 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)) +}