let game handler own card data
This commit is contained in:
parent
11c37f9f6d
commit
53b6d63f3d
6 changed files with 191 additions and 182 deletions
159
server/src/card_loader.rs
Normal file
159
server/src/card_loader.rs
Normal 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))
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Reference in a new issue