#![feature(if_let_guard)] use anyhow::{Context, Result}; use axum::extract::ws::Message; use lib::*; use rand::prelude::IteratorRandom; use rand::thread_rng; use serde::Deserialize; use std::{ collections::{HashMap, HashSet}, fs::{read_to_string, File}, io::{BufRead, BufReader}, net::SocketAddr, sync::{Arc, RwLock}, }; use tokio::sync::mpsc::Sender; use tokio::sync::{broadcast, mpsc}; use user_handler::UserHandlerMessage; pub mod message_handler; pub mod user_handler; pub mod websocket; /// User #[derive(Debug)] pub struct User { pub name: String, pub tx: Sender, } impl User { /// Create a new user object from incoming data pub fn new(name: String, tx: Sender) -> User { User { name, tx } } pub fn change_name(&mut self, new_name: String) { self.name = new_name; } } /// Card Set #[derive(Debug)] pub struct CardSet { pub white: Option>, pub black: Option>, } /// Card Packs #[derive(Debug)] pub struct CardPacks { pub official: HashMap, pub unofficial: HashMap, } /// A white card #[derive(Debug, Deserialize)] pub struct CardWhite { /// Card text pub text: String, /// ID of the pack it came from pub pack: u8, } /// A black card #[derive(Debug, Deserialize)] pub struct CardBlack { /// Card text pub text: String, /// Amount of cards to submit for judging pub pick: u8, /// ID of the pack it came from pub pack: u8, } /// A card pack #[derive(Debug, Deserialize)] pub struct CardPack { /// Name of the pack pub name: String, /// Whether or not this is an official card pack pub official: bool, /// White card data pub white: Option>, /// Black card data pub black: Option>, } /// New game request structure #[derive(Debug)] pub struct NewGameManifest { /// Game name pub name: String, /// Game host pub host: Arc>, } /// A struct that represents a player #[derive(Debug)] pub struct Player { /// Player's username pub user: Arc>, /// The player's hand pub white: Vec, /// The player's wins pub black: Vec, } /// The game master #[derive(Debug)] pub struct Game { /// The name of the game pub name: String, /// The host user of the game pub host: Arc>, /// White draw pile pub white: Vec, /// Black draw pile pub black: Vec, /// White discard pile pub white_discard: Vec, /// Black discard pile pub black_discard: Vec, /// Indicates game active/game over pub game_active: bool, /// List of current players pub players: Vec, // /// Reference to current card czar // czar: &Player, /// Black card for the current round pub current_black: Option, } impl Game { /// Build game decks from input data for game start. /// This should only run once and at startup. fn _build_decks(&mut self, cards_json: Vec) -> Result<()> { for pack in cards_json { if let Some(white) = pack.white { self.white.extend(white) } if let Some(black) = pack.black { self.black.extend(black) } } Ok(()) } pub fn new(request: NewGameManifest) -> Result { let mut game = Game { name: request.host.read().unwrap().name.clone(), host: request.host.clone(), white: vec![], black: vec![], white_discard: vec![], black_discard: vec![], game_active: false, players: vec![], current_black: Option::None, }; 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(request.packs)?; game.create_player(request.host)?; game.deal_black()?; Ok(game) } // pub fn join(request:GameJoinRequest) /// Log counts of current drawable cards /// For testing pub fn deck_counts(&self) { tracing::debug!( "Deck Counts:\n {} White cards\n {} Black cards", self.white.len(), self.black.len() ); } /// Draw one white card at random from play deck. fn draw_one_white(&mut self) -> Result { let deck = &mut self.white; // 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; // 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.push(new_player); Ok(()) } pub fn game_start(&mut self) -> Result<()> { self.game_active = true; tracing::debug!("Game Active!"); if let Some(black) = &self.current_black { tracing::debug!("{}", black.text); } else { tracing::debug!("YOU DONE FUCKED UP (no current black card)"); } Ok(()) } } /// Parse json for card data pub fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> { 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)) } /// Parse name list pub fn load_names(path: &str) -> Result> { let f = File::open(path).with_context(|| format!("Invalid names path: \"{}\"", path))?; let f = BufReader::new(f); let mut buf = vec![]; for line in f.lines() { buf.push(line?) } Ok(buf) } // Our shared state pub struct AppState { pub broadcast_tx: broadcast::Sender, pub users_tx: mpsc::Sender, pub messages_tx: mpsc::Sender<(SocketAddr, Message)>, pub first_names: Vec, pub last_names: Vec, pub reserved_names: RwLock>, pub online_users: RwLock>>>, pub offline_users: RwLock>>>, pub packs: CardPacks, pub packs_meta: CardPacksMeta, pub games: RwLock>>, }