let game handler own card data

This commit is contained in:
Adam 2024-11-21 12:41:17 -05:00
parent 11c37f9f6d
commit 53b6d63f3d
6 changed files with 191 additions and 182 deletions

159
server/src/card_loader.rs Normal file
View file

@ -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<Vec<Arc<CardBlackWithID>>>,
pub white: Option<Vec<Arc<CardWhiteWithID>>>,
}
/// Card Packs
pub struct CardPacks {
pub official: HashMap<u8, CardSet>,
pub unofficial: HashMap<u8, CardSet>,
}
/// 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<Vec<CardWhiteFromJSON>>,
/// Black card data
black: Option<Vec<CardBlackFromJSON>>,
}
/// Parse json for card data
pub fn card_loader(
path: &str,
) -> Result<(
CardPacks,
CardPacksMeta,
// HashMap<String, Arc<CardWhiteWithID>>,
)> {
// Load in json
let data: String =
read_to_string(path).with_context(|| format!("Invalid JSON path: \"{}\"", path))?;
let jayson: Vec<CardPack> = serde_json::from_str(&data)
.with_context(|| format!("The contents of \"{path}\" is not valid JSON."))?;
// For global state
let mut official: HashMap<u8, CardSet> = HashMap::new();
let mut official_meta: Vec<CardPackMeta> = vec![];
let mut unofficial: HashMap<u8, CardSet> = HashMap::new();
let mut unofficial_meta: Vec<CardPackMeta> = vec![];
// let mut white_cards_by_id = HashMap::<String, Arc<CardWhiteWithID>>::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<u8> = 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))
}

View file

@ -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<AppState>, 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())
}

View file

@ -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<String>),
SendGameMetaUpdate(Vec<String>),
BroadcastGamesUpdate(),
SendCardPacks(Sender<Message>),
}
/// Handles game stuff
@ -28,13 +29,21 @@ pub struct GameHandler {
/// Global state pointer
state: Arc<AppState>,
games: HashMap<String, Game>,
packs: CardPacks,
packs_meta: CardPacksMeta,
}
impl GameHandler {
/// Returns a new game handler
pub fn new(state: Arc<AppState>) -> 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<Message>) {
let msg = Message::Text(to_string::<CardPacksMeta>(&self.packs_meta).unwrap());
if let Err(e) = tx.send(msg).await {
tracing::error!("Error sending card packs: {}", e)
}
}
}

View file

@ -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<HashMap<String, Vec<String>>>,
pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>,
pub online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>,
pub packs: CardPacks,
pub packs_meta: CardPacksMeta,
pub tx_broadcast: broadcast::Sender<Message>,
pub tx_game_handler: mpsc::Sender<GameHandlerMessage>,
pub tx_incoming_message_handler: mpsc::Sender<(SocketAddr, Message)>,
pub tx_user_handler: mpsc::Sender<UserHandlerMessage>,
pub white_cards_by_id: HashMap<String, Arc<CardWhiteWithID>>,
}
/// Card Set
#[derive(Debug)]
struct CardSet {
black: Option<Vec<Arc<CardBlackWithID>>>,
white: Option<Vec<Arc<CardWhiteWithID>>>,
}
/// Card Packs
#[derive(Debug)]
pub struct CardPacks {
official: HashMap<u8, CardSet>,
unofficial: HashMap<u8, CardSet>,
}
/// 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<Vec<CardWhiteFromJSON>>,
/// Black card data
black: Option<Vec<CardBlackFromJSON>>,
}
/// Parse json for card data
pub fn load_cards_from_json(
path: &str,
) -> Result<(
CardPacks,
CardPacksMeta,
HashMap<String, Arc<CardWhiteWithID>>,
)> {
// Load in json
let data: String =
read_to_string(path).with_context(|| format!("Invalid JSON path: \"{}\"", path))?;
let jayson: Vec<CardPack> = serde_json::from_str(&data)
.with_context(|| format!("The contents of \"{path}\" is not valid JSON."))?;
// For global state
let mut official: HashMap<u8, CardSet> = HashMap::new();
let mut official_meta: Vec<CardPackMeta> = vec![];
let mut unofficial: HashMap<u8, CardSet> = HashMap::new();
let mut unofficial_meta: Vec<CardPackMeta> = vec![];
let mut white_cards_by_id = HashMap::<String, Arc<CardWhiteWithID>>::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<u8> = 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))
}

View file

@ -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

View file

@ -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<AppState>, addr: &SocketAddr) -> Mess
Message::Text(to_string::<ChatMessage>(&ChatMessage { text: msg }).unwrap())
}
/// Generage cards meta message
pub fn meta_new_game_card_packs(state: &Arc<AppState>) -> Message {
Message::Text(to_string::<CardPacksMeta>(&state.packs_meta).unwrap())
}
/// Generate message-of-the-day server greeting
pub fn meta_motd() -> Message {
Message::Text(