This commit is contained in:
Adam 2024-08-12 17:14:27 -04:00
parent dcc427dc55
commit 032b031d6e
8 changed files with 168 additions and 206 deletions

View file

@ -11,7 +11,7 @@ pub fn Game() -> impl IntoView {
let (game_host, set_game_host) = create_signal("".to_string()); let (game_host, set_game_host) = create_signal("".to_string());
let (game_players, set_game_players) = create_signal(vec![]); let (game_players, set_game_players) = create_signal(vec![]);
let (game_czar, set_game_czar) = create_signal("".to_string()); let (game_czar, set_game_czar) = create_signal("".to_string());
let (game_black, set_game_black) = create_signal("".to_string()); let (game_black, set_game_black) = create_signal(("".to_string(), 0u8));
let (game_white, set_game_white) = create_signal(vec![]); let (game_white, set_game_white) = create_signal(vec![]);
create_effect(move |_| { create_effect(move |_| {
@ -28,12 +28,12 @@ pub fn Game() -> impl IntoView {
view! { view! {
<div class="p-1"> <div class="p-1">
<h2 class="text-2xl">Game</h2> <h2 class="text-2xl">Game</h2>
<p>Name: {move || game_name}</p> <p>Name: {move || game_name()}</p>
<p>Host: {move || game_host}</p> <p>Host: {move || game_host()}</p>
<p>Players: {move || game_players}</p> <p>Players: {move || game_players()}</p>
<p>Czar: {move || game_czar}</p> <p>Czar: {move || game_czar()}</p>
<p>Black Card: {move || game_black}</p> <p>Black Card: {move || game_black().0} Pick: {move || game_black().1}</p>
<p>Your Cards: {move || game_white}</p> <p>Your Cards: {move || game_white()}</p>
</div> </div>
} }
} }

View file

@ -7,7 +7,7 @@ pub struct GameMeta {
pub host: String, pub host: String,
pub players: Vec<String>, pub players: Vec<String>,
pub czar: String, pub czar: String,
pub black: String, pub black: (String, u8),
pub white: Vec<String>, pub white: Vec<String>,
} }

View file

@ -1,87 +1,171 @@
use crate::user_handler::*; use crate::user_handler::*;
use crate::AppState; use crate::AppState;
use crate::User; use crate::User;
use anyhow::Result; use anyhow::{Context, Result};
use lib::*; use lib::*;
use rand::prelude::IteratorRandom; use rand::prelude::IteratorRandom;
use rand::thread_rng; use rand::thread_rng;
use serde::Deserialize; use serde::Deserialize;
use std::{ use std::{
collections::HashMap, collections::HashMap,
fs::read_to_string,
net::SocketAddr, net::SocketAddr,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use uuid::Uuid; use uuid::Uuid;
// This file is disgusting, don't look at it /// Parse json for card data
pub fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> {
// TODO: Repack these cards so every card is stored once and pointers are passed around instead of
// cloning stuff
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."))?;
let mut official: HashMap<u8, CardSet> = HashMap::new();
let mut unofficial: HashMap<u8, CardSet> = HashMap::new();
let mut official_meta: Vec<CardPackMeta> = vec![];
let mut unofficial_meta: Vec<CardPackMeta> = 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<u8> = 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))
}
/// Card Set /// Card Set
#[derive(Debug)] #[derive(Debug)]
pub struct CardSet { struct CardSet {
pub white: Option<Vec<CardWhite>>, white: Option<Vec<CardWhite>>,
pub black: Option<Vec<CardBlack>>, black: Option<Vec<CardBlack>>,
} }
/// Card Packs /// Card Packs
#[derive(Debug)] #[derive(Debug)]
pub struct CardPacks { pub struct CardPacks {
pub official: HashMap<u8, CardSet>, official: HashMap<u8, CardSet>,
pub unofficial: HashMap<u8, CardSet>, unofficial: HashMap<u8, CardSet>,
} }
/// A white card /// A white card
#[derive(Debug, Deserialize)] // TODO: Remove this clone!
pub struct CardWhite { #[derive(Debug, Deserialize, Clone)]
struct CardWhite {
/// Card text /// Card text
pub text: String, text: String,
/// ID of the pack it came from /// ID of the pack it came from
pub pack: u8, pack: u8,
} }
/// A black card /// A black card
#[derive(Debug, Deserialize)] // TODO: Remove this clone!
pub struct CardBlack { #[derive(Debug, Deserialize, Clone)]
struct CardBlack {
/// Card text /// Card text
pub text: String, text: String,
/// Amount of cards to submit for judging /// Amount of cards to submit for judging
pub pick: u8, pick: u8,
/// ID of the pack it came from /// ID of the pack it came from
pub pack: u8, pack: u8,
} }
/// A card pack /// A card pack
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct CardPack { struct CardPack {
/// Name of the pack /// Name of the pack
pub name: String, name: String,
/// Whether or not this is an official card pack /// Whether or not this is an official card pack
pub official: bool, official: bool,
/// White card data /// White card data
pub white: Option<Vec<CardWhite>>, white: Option<Vec<CardWhite>>,
/// Black card data /// Black card data
pub black: Option<Vec<CardBlack>>, black: Option<Vec<CardBlack>>,
} }
/// New game request structure /// New game request structure
#[derive(Debug)] #[derive(Debug)]
pub struct NewGameManifest { struct NewGameManifest {
/// Game name /// Game name
pub name: String, name: String,
/// Game host /// Game host
pub host: Arc<RwLock<User>>, host: Arc<RwLock<User>>,
/// Selected game packs /// Selected game packs
pub packs: Vec<u8>, packs: Vec<u8>,
} }
/// A struct that represents a player /// A struct that represents a player
#[derive(Debug)] #[derive(Debug)]
pub struct Player { struct Player {
/// Player's user's uuid
pub user_uuid: Uuid,
/// The player's hand /// The player's hand
pub white: Vec<CardWhite>, white: Vec<CardWhite>,
/// The player's wins /// The player's wins
pub black: Vec<CardBlack>, black: Vec<CardBlack>,
} }
/// The game master /// The game master
@ -92,47 +176,21 @@ pub struct Game {
/// The host user of the game /// The host user of the game
pub host: Arc<RwLock<User>>, pub host: Arc<RwLock<User>>,
/// White draw pile /// White draw pile
pub white: Vec<CardWhite>, white: Vec<CardWhite>,
/// Black draw pile /// Black draw pile
pub black: Vec<CardBlack>, black: Vec<CardBlack>,
/// White discard pile players: HashMap<Uuid, Player>,
pub white_discard: Vec<CardWhite>,
/// Black discard pile
pub black_discard: Vec<CardBlack>,
/// List of current players
pub players: HashMap<Uuid, Player>,
// /// Reference to current card czar
// czar: &Player,
/// Black card for the current round /// Black card for the current round
pub current_black: Option<CardBlack>, current_black: Option<CardBlack>,
} }
impl Game { impl Game {
/// Build game decks from input data for game start. fn new(state: Arc<AppState>, request: NewGameManifest) -> Result<Self> {
/// This should only run once and at startup.
fn build_decks(&mut self, selected_packs: Vec<u8>) -> Result<()> {
// for pack in selected_packs {
// for pack in card_packs {
// 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<Self> {
let mut game = Game { let mut game = Game {
name: request.host.read().unwrap().name.clone(), name: request.host.read().unwrap().name.clone(),
host: request.host.clone(), host: request.host.clone(),
white: vec![], white: vec![],
black: vec![], black: vec![],
white_discard: vec![],
black_discard: vec![],
players: HashMap::new(), players: HashMap::new(),
current_black: Option::None, current_black: Option::None,
}; };
@ -144,30 +202,43 @@ impl Game {
game.name = request.name; game.name = request.name;
game.host = request.host.clone(); game.host = request.host.clone();
game.build_decks(request.packs)?; game.build_decks(state, request.packs)?;
game.create_player(request.host)?; game.create_player(request.host)?;
game.deal_black()?; game.deal_black()?;
Ok(game) Ok(game)
} }
// pub fn join(request:GameJoinRequest) /// Build game decks from input data for game start.
/// This should only run once and at startup.
fn build_decks(&mut self, state: Arc<AppState>, selected_packs: Vec<u8>) -> Result<()> {
// TODO: Make this right -- remove the clones, use references to single cards
for pack_num in selected_packs {
if let Some(pack) = state.packs.official.get(&pack_num) {
if let Some(white) = &pack.white {
self.white.extend(white.clone())
}
if let Some(black) = &pack.black {
self.black.extend(black.clone())
}
} else if let Some(pack) = state.packs.unofficial.get(&pack_num) {
if let Some(white) = &pack.white {
self.white.extend(white.clone())
}
if let Some(black) = &pack.black {
self.black.extend(black.clone())
}
}
}
/// Log counts of current drawable cards Ok(())
/// 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. /// Draw one white card at random from play deck.
fn draw_one_white(&mut self) -> Result<CardWhite> { fn draw_one_white(&mut self) -> Result<CardWhite> {
let deck = &mut self.white; let deck = &mut self.white;
// this feels sloppy // TODO: this feels sloppy
if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) { if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) {
Ok(deck.swap_remove(index)) Ok(deck.swap_remove(index))
} else { } else {
@ -182,7 +253,7 @@ impl Game {
fn draw_one_black(&mut self) -> Result<CardBlack> { fn draw_one_black(&mut self) -> Result<CardBlack> {
let deck = &mut self.black; let deck = &mut self.black;
// this feels sloppy // TODO: this feels sloppy
if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) { if let Some(index) = (0..deck.len()).choose(&mut thread_rng()) {
Ok(deck.swap_remove(index)) Ok(deck.swap_remove(index))
} else { } else {
@ -202,9 +273,8 @@ impl Game {
} }
/// Create a new player and add them to the game. /// Create a new player and add them to the game.
pub fn create_player(&mut self, user: Arc<RwLock<User>>) -> Result<()> { fn create_player(&mut self, user: Arc<RwLock<User>>) -> Result<()> {
let mut new_player = Player { let mut new_player = Player {
user_uuid: user.read().unwrap().uuid.clone(),
white: vec![], white: vec![],
black: vec![], black: vec![],
}; };
@ -225,16 +295,6 @@ impl Game {
Ok(()) Ok(())
} }
pub fn game_start(&mut self) -> Result<()> {
if let Some(black) = &self.current_black {
tracing::debug!("{}", black.text);
} else {
tracing::debug!("YOU DONE FUCKED UP (no current black card)");
}
Ok(())
}
} }
pub enum GameHandlerMessage { pub enum GameHandlerMessage {
@ -288,7 +348,7 @@ impl GameHandler {
}; };
// create game // create game
if let Ok(new_game_object) = Game::new(manifest) { if let Ok(new_game_object) = Game::new(self.state.clone(), manifest) {
let tx = self let tx = self
.state .state
.online_users .online_users
@ -301,9 +361,9 @@ impl GameHandler {
.tx .tx
.clone(); .clone();
let mut black_text = "Error".to_string(); let mut black_card = ("Error".to_string(), 0u8);
if let Some(ref current_black) = new_game_object.current_black { if let Some(ref current_black) = new_game_object.current_black {
black_text = current_black.text.to_owned() black_card = (current_black.text.to_owned(), current_black.pick)
} }
let meta = GameMeta { let meta = GameMeta {
@ -326,7 +386,7 @@ impl GameHandler {
}) })
.collect(), .collect(),
czar: new_game_object.host.read().unwrap().name.clone(), czar: new_game_object.host.read().unwrap().name.clone(),
black: black_text, black: black_card,
white: new_game_object white: new_game_object
.players .players
.get(&new_game_object.host.read().unwrap().uuid) .get(&new_game_object.host.read().unwrap().uuid)

View file

@ -7,7 +7,7 @@ use game_handler::GameHandlerMessage;
use lib::*; use lib::*;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fs::{read_to_string, File}, fs::File,
io::{BufRead, BufReader}, io::{BufRead, BufReader},
net::SocketAddr, net::SocketAddr,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
@ -44,89 +44,6 @@ impl User {
} }
} }
/// 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<CardPack> = serde_json::from_str(&data)
.with_context(|| format!("The contents of \"{path}\" is not valid JSON."))?;
let mut official: HashMap<u8, CardSet> = HashMap::new();
let mut unofficial: HashMap<u8, CardSet> = HashMap::new();
let mut official_meta: Vec<CardPackMeta> = vec![];
let mut unofficial_meta: Vec<CardPackMeta> = 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<u8> = 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 /// Parse name list
pub fn load_names(path: &str) -> Result<Vec<String>> { pub fn load_names(path: &str) -> Result<Vec<String>> {
let f = File::open(path).with_context(|| format!("Invalid names path: \"{}\"", path))?; let f = File::open(path).with_context(|| format!("Invalid names path: \"{}\"", path))?;

View file

@ -64,16 +64,7 @@ async fn main() -> Result<()> {
} }
}); });
// Make an outgoing message handler handler // TODO: Make an outgoing message handler handler
// DO it
// DO it
// DO it
// DO it
// DO it
// DO it
// DO it
// DO it
// DO it
// Spawn task to handle User things // Spawn task to handle User things
let user_handler = UserHandler::new(app_state.clone()); let user_handler = UserHandler::new(app_state.clone());

View file

@ -7,9 +7,6 @@ use serde_json::{from_str, to_string};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
// Handle incoming messages over the WebSocket, and probably do more than we should.
// Also with lots of unwrapping
pub struct MessageHandler { pub struct MessageHandler {
state: Arc<AppState>, state: Arc<AppState>,
} }
@ -23,7 +20,7 @@ impl MessageHandler {
match message { match message {
Message::Text(text) => match text { Message::Text(text) => match text {
_chat_message if let Ok(chat_message) = from_str::<ChatMessage>(&text) => { _chat_message if let Ok(chat_message) = from_str::<ChatMessage>(&text) => {
// This should be delegated to user handler and an outgoing message and/or chat handler // TODO: This should be delegated to user handler and an outgoing message and/or chat handler
let msg = format! {"{0}: {1}", self.state.online_users.read().unwrap().get(&addr).unwrap().read().unwrap().name, chat_message.text}; let msg = format! {"{0}: {1}", self.state.online_users.read().unwrap().get(&addr).unwrap().read().unwrap().name, chat_message.text};
tracing::debug!("{msg}"); tracing::debug!("{msg}");
self.state self.state

View file

@ -5,8 +5,6 @@ use serde_json::to_string;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
// This file is a mess, don't read it
pub enum UserHandlerMessage { pub enum UserHandlerMessage {
NewUser { user: User, addr: SocketAddr }, NewUser { user: User, addr: SocketAddr },
UserLogIn { username: String, addr: SocketAddr }, UserLogIn { username: String, addr: SocketAddr },
@ -24,7 +22,7 @@ impl UserHandler {
pub async fn handle(&self, message: UserHandlerMessage) { pub async fn handle(&self, message: UserHandlerMessage) {
match message { match message {
UserHandlerMessage::NewUser { user, addr } => { UserHandlerMessage::NewUser { user, addr } => {
// make this not async // TODO: make this not async
self.set_up_new_user(user, addr).await self.set_up_new_user(user, addr).await
} }
UserHandlerMessage::UserLogIn { username, addr } => self.login(username, addr).await, UserHandlerMessage::UserLogIn { username, addr } => self.login(username, addr).await,
@ -65,7 +63,7 @@ impl UserHandler {
.unwrap(); .unwrap();
// Broadcast new user's existence // Broadcast new user's existence
// this should probably be combined and sent as one // TODO: this should probably be combined and sent as one
let _ = &self let _ = &self
.state .state
.broadcast_tx .broadcast_tx
@ -82,8 +80,7 @@ impl UserHandler {
.send(meta_chat_update(&self.state)) .send(meta_chat_update(&self.state))
.unwrap(); .unwrap();
// this races the broadcasts but if it's done last it'll probably show up // TODO: this races the broadcasts but if it's done last it'll probably show up last
// last
tx.send(meta_motd()).await.unwrap(); tx.send(meta_motd()).await.unwrap();
} }
@ -245,7 +242,7 @@ pub fn user_client_self_update(new_user: &Arc<RwLock<User>>) -> String {
/// Generate chatroom metadata update /// Generate chatroom metadata update
pub fn meta_chat_update(state: &Arc<AppState>) -> String { pub fn meta_chat_update(state: &Arc<AppState>) -> String {
// this may get expensive if there are many users // TODO: this may get expensive if there are many users
let mut names = vec![]; let mut names = vec![];
for user in state.online_users.read().unwrap().iter() { for user in state.online_users.read().unwrap().iter() {
@ -285,7 +282,7 @@ pub fn meta_server_summary_update(state: &Arc<AppState>) -> String {
/// Generate games list update /// Generate games list update
pub fn meta_games_browser_update(state: &Arc<AppState>) -> String { pub fn meta_games_browser_update(state: &Arc<AppState>) -> String {
// this may get expensive if there are many games // TODO: this may get expensive if there are many games
let mut names = vec![]; let mut names = vec![];
for game in state.games.read().unwrap().values() { for game in state.games.read().unwrap().values() {

View file

@ -37,9 +37,8 @@ pub async fn websocket_on_connection(stream: WebSocket, state: Arc<AppState>, ad
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert(addr, dm_tx.clone()); map.insert(addr, dm_tx.clone());
let _ = state state
.users_tx .users_tx
// add tx
.send(UserHandlerMessage::NewUser { .send(UserHandlerMessage::NewUser {
user: User::new( user: User::new(
format!( format!(
@ -51,7 +50,8 @@ pub async fn websocket_on_connection(stream: WebSocket, state: Arc<AppState>, ad
), ),
addr, addr,
}) })
.await; .await
.unwrap();
// Subscribe to receive from global broadcast channel // Subscribe to receive from global broadcast channel
let mut rx = state.broadcast_tx.subscribe(); let mut rx = state.broadcast_tx.subscribe();