diff --git a/Cargo.lock b/Cargo.lock index 789a79c..15895aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,7 +101,7 @@ dependencies = [ "base64 0.21.7", "bytes", "futures-util", - "http 1.1.0", + "http", "http-body", "http-body-util", "hyper", @@ -136,7 +136,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", + "http", "http-body", "http-body-util", "mime", @@ -260,6 +260,7 @@ dependencies = [ name = "client" version = "0.1.0" dependencies = [ + "codee", "console_error_panic_hook", "console_log", "leptos", @@ -275,6 +276,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "codee" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af40247be877a1e3353fb406aa27ab3ef4bd3ff18cef91e75e667bfa3fde701d" +dependencies = [ + "thiserror", +] + [[package]] name = "collection_literals" version = "1.0.1" @@ -476,6 +486,17 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "drain_filter_polyfill" version = "0.1.3" @@ -627,27 +648,6 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" -[[package]] -name = "gloo-net" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http 0.2.12", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "gloo-net" version = "0.6.0" @@ -658,7 +658,7 @@ dependencies = [ "futures-core", "futures-sink", "gloo-utils", - "http 1.1.0", + "http", "js-sys", "pin-project", "serde", @@ -725,17 +725,6 @@ dependencies = [ "utf8-width", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.1.0" @@ -754,7 +743,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http", ] [[package]] @@ -765,7 +754,7 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http", "http-body", "pin-project-lite", ] @@ -797,7 +786,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http", "http-body", "httparse", "httpdate", @@ -815,7 +804,7 @@ checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http", "http-body", "hyper", "pin-project-lite", @@ -877,9 +866,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -892,9 +881,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leptos" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57727cd8f6d1e78aa9721270002037d7f63b5a7a2b60a7830239f6938cbca9b7" +checksum = "a15911b4e53bb6e1b033d717eadb39924418a4a288279128122e5a65c70ba3e6" dependencies = [ "cfg-if", "leptos_config", @@ -912,12 +901,13 @@ dependencies = [ [[package]] name = "leptos-use" -version = "0.10.10" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3272d90b77cdbb99e9060f90eb6f5738e56128b2f912db57a50efb006a26e262" +checksum = "268b9df23d8c68ed0518c39d6f0d3b99fcbe30190a6dce4a7e5f342027ab0033" dependencies = [ "async-trait", "cfg-if", + "codee", "cookie", "default-struct-builder", "futures-util", @@ -928,6 +918,7 @@ dependencies = [ "leptos", "paste", "thiserror", + "unic-langid", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -1019,9 +1010,9 @@ dependencies = [ [[package]] name = "leptos_meta" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206825db2cb802a9b06c1f33c08569086706a7fa4d8acb86e5ed6892a8dd2cec" +checksum = "779bc68f8c05b15dde6557f9eb7baef4fa606a5bc450c1d6ff8787b092d0ae93" dependencies = [ "cfg-if", "indexmap", @@ -1060,12 +1051,12 @@ dependencies = [ [[package]] name = "leptos_router" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d397f1c3217368aaf6009ea0bf6bdfa47325ab9c8fb8c124eda5ebc527d11445" +checksum = "e5006e35b7c768905286dbea0d3525396cd39d961cb7b9fb664aa00b0c984ae6" dependencies = [ "cfg-if", - "gloo-net 0.5.0", + "gloo-net", "itertools", "js-sys", "lazy_static", @@ -1202,6 +1193,16 @@ dependencies = [ "unicase", ] +[[package]] +name = "minicov" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1779,8 +1780,8 @@ dependencies = [ "const_format", "dashmap", "futures", - "gloo-net 0.6.0", - "http 1.1.0", + "gloo-net", + "http", "js-sys", "once_cell", "send_wrapper", @@ -1993,6 +1994,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -2121,7 +2131,7 @@ dependencies = [ "bitflags", "bytes", "futures-util", - "http 1.1.0", + "http", "http-body", "http-body-util", "http-range-header", @@ -2220,7 +2230,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http", "httparse", "log", "rand", @@ -2256,6 +2266,24 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unic-langid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" +dependencies = [ + "tinystr", +] + [[package]] name = "unicase" version = "2.7.0" @@ -2362,19 +2390,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", @@ -2387,9 +2416,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -2399,9 +2428,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2409,9 +2438,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", @@ -2422,18 +2451,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-bindgen-test" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" dependencies = [ "console_error_panic_hook", "js-sys", + "minicov", "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", @@ -2442,9 +2472,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", @@ -2466,9 +2496,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/client/Cargo.toml b/client/Cargo.toml index 5e2c4de..7eea2db 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -13,11 +13,12 @@ leptos_router = { version = "0.6", features = ["csr", "nightly"] } console_log = "1" log = "0.4" console_error_panic_hook = "0.1" -leptos-use = "0.10.10" +leptos-use = "0.12" serde-lite = "0.5.0" lib = { workspace = true } serde_json = "1.0.120" +codee = "0.1.2" # utils # strum = { version = "0.25", features = ["derive", "strum_macros"] } @@ -25,6 +26,6 @@ serde_json = "1.0.120" [dev-dependencies] -wasm-bindgen = "=0.2.92" +wasm-bindgen = "0.2.92" wasm-bindgen-test = "0.3" web-sys = { version = "0.3", features = ["Document", "Window"] } diff --git a/client/src/components/auth.rs b/client/src/components/auth.rs index 2320e16..a9dcaf3 100644 --- a/client/src/components/auth.rs +++ b/client/src/components/auth.rs @@ -26,7 +26,7 @@ pub fn Auth() -> impl IntoView { if input.value() != String::from("") { logging::log!("send"); websocket.send( - &to_string(&UserLogIn { + &to_string(&UserLogInRequest { username: input.value(), }) .unwrap(), diff --git a/client/src/components/websocket.rs b/client/src/components/websocket.rs index 330c7ce..cd43eed 100644 --- a/client/src/components/websocket.rs +++ b/client/src/components/websocket.rs @@ -1,5 +1,6 @@ +use codee::string::FromToStringCodec; use leptos::*; -use leptos_use::{core::ConnectionReadyState, use_websocket, UseWebsocketReturn}; +use leptos_use::{core::ConnectionReadyState, use_websocket, UseWebSocketReturn}; use lib::*; use serde_json::from_str; use std::rc::Rc; @@ -8,7 +9,7 @@ use std::rc::Rc; pub struct WebSocketContext { pub ready_state: Signal, // pub message: Signal>, - pub send: Rc, + pub send: Rc, pub open: Rc, pub close: Rc, } @@ -17,13 +18,12 @@ impl WebSocketContext { pub fn new( ready_state: Signal, // message: Signal>, - send: Rc, + send: Rc, open: Rc, close: Rc, ) -> Self { Self { ready_state, - // message, send, open, close, @@ -31,35 +31,24 @@ impl WebSocketContext { } #[inline(always)] - pub fn send(&self, message: &str) { + pub fn send(&self, message: &String) { (self.send)(message) } - - // #[inline(always)] - // pub fn open(&self) { - // (self.open)() - // } - // - // #[inline(always)] - // pub fn close(&self) { - // (self.close)() - // } } #[component] pub fn Websocket() -> impl IntoView { - let UseWebsocketReturn { + let UseWebSocketReturn { ready_state, message, send, open, close, .. - } = use_websocket("ws://0.0.0.0:3030/websocket"); + } = use_websocket::("ws://0.0.0.0:3030/websocket"); provide_context(WebSocketContext::new( ready_state, - // message, Rc::new(send.clone()), Rc::new(open.clone()), Rc::new(close.clone()), @@ -68,7 +57,6 @@ pub fn Websocket() -> impl IntoView { // Contexts for message handler let (state_summary, set_state_summary) = create_signal::>(Option::None); - // let (game_object, set_game_object) = create_signal::>(Option::None); let (user_update, set_user_update) = create_signal::>(Option::None); let (chat_update, set_chat_update) = create_signal::>(Option::None); let (chat_message, set_chat_message) = create_signal::>(Option::None); @@ -91,12 +79,7 @@ pub fn Websocket() -> impl IntoView { // Message handler create_effect(move |_| { message.with(move |message_raw| { - // Send all messages as strings into chat box - if let Some(message) = message_raw { - // if let Ok(game) = from_str::(message) { - // set_game_object(Some(game)); - // } if let Ok(state_summary) = from_str::(message) { set_state_summary(Some(state_summary)); } else if let Ok(chat_message) = from_str::(message) { @@ -115,6 +98,6 @@ pub fn Websocket() -> impl IntoView { logging::log!("Unhandled message: {}", message); } } - }) + }); }); } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 79fcdce..5e6f3dd 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -60,7 +60,7 @@ pub struct ChatUpdate { /// User login request (to change name) #[derive(Serialize, Deserialize, Debug)] -pub struct UserLogIn { +pub struct UserLogInRequest { pub username: String, } diff --git a/server/src/game_handler.rs b/server/src/game_handler.rs index b0519a9..968321a 100644 --- a/server/src/game_handler.rs +++ b/server/src/game_handler.rs @@ -1,5 +1,6 @@ use crate::user_handler::*; use crate::AppState; +use crate::GameHandlerMessage::*; use crate::User; use anyhow::{Context, Result}; use lib::*; @@ -14,89 +15,188 @@ use std::{ }; use uuid::Uuid; -/// 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 = serde_json::from_str(&data) - .with_context(|| format!("The contents of \"{path}\" is not valid JSON."))?; +pub enum GameHandlerMessage { + NewGame { + addr: SocketAddr, + new_game: NewGameRequest, + }, + JoinGame { + addr: SocketAddr, + id: String, + }, +} +pub struct GameHandler { + state: Arc, +} - let mut official: HashMap = HashMap::new(); - let mut unofficial: HashMap = HashMap::new(); +impl GameHandler { + pub fn new(state: Arc) -> Self { + GameHandler { state } + } - 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); + pub async fn handle(&self, message: GameHandlerMessage) { + match message { + NewGame { addr, new_game } => self.new_game(addr, new_game).await, + JoinGame { addr, id } => self.join_game(addr, id).await, } } - official.shrink_to_fit(); - unofficial.shrink_to_fit(); + async fn join_game(&self, addr: SocketAddr, id: String) { + // Get pointers + let this_game = self.state.games.read().unwrap().get(&id).unwrap().clone(); + let this_user = self + .state + .online_users + .read() + .unwrap() + .get(&addr) + .unwrap() + .clone(); - official_meta.shrink_to_fit(); - unofficial_meta.shrink_to_fit(); + // Create player + this_game.write().unwrap().create_player(this_user).unwrap(); - 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]); + // Send updates for all players + for player in this_game.read().unwrap().players.values() { + // Create update for user's game view + let mut black_card = ("Error".to_string(), 0u8); + if let Some(ref current_black) = this_game.read().unwrap().current_black { + black_card = (current_black.text.to_owned(), current_black.pick) + } + let meta = GameStateMeta { + uuid: id.clone(), + name: this_game.read().unwrap().name.clone(), + host: this_game.read().unwrap().host.read().unwrap().name.clone(), + players: this_game + .read() + .unwrap() + .players + .values() + .map(|player| player.user.read().unwrap().name.clone()) + .collect(), + czar: this_game.read().unwrap().host.read().unwrap().name.clone(), + black: black_card, + white: player.white.iter().map(|card| card.text.clone()).collect(), + packs: this_game.read().unwrap().packs.clone(), + }; - let packs = CardPacks { - official, - unofficial, - }; + // Send user's update + let msg = serde_json::to_string(&meta).unwrap(); + let user_tx = player.user.read().unwrap().tx.clone(); + tokio::spawn(async move { user_tx.send(msg).await }); + } - let packs_meta = CardPacksMeta { - official_meta, - unofficial_meta, - }; + // Broadcast game browser update + self.state + .broadcast_tx + .send(meta_games_browser_update(&self.state)) + .unwrap(); - Ok((packs, packs_meta)) + // Broadcast server meta update + self.state + .broadcast_tx + .send(meta_server_summary_update(&self.state)) + .unwrap(); + } + + async fn new_game(&self, addr: SocketAddr, new_game: NewGameRequest) { + if new_game.packs.is_empty() { + tracing::debug!("Cards are empty"); + return; + } else if new_game.name.is_empty() { + tracing::debug!("Name are empty"); + return; + } + + let manifest = NewGameManifest { + name: new_game.name, + host: self + .state + .online_users + .read() + .unwrap() + .get(&addr) + .unwrap() + .clone(), + packs: new_game.packs, + }; + + // Create game + if let Ok(new_game_object) = Game::new(self.state.clone(), manifest) { + let tx = self + .state + .online_users + .read() + .unwrap() + .get(&addr) + .unwrap() + .read() + .unwrap() + .tx + .clone(); + + // Create update for user's game view + let mut black_card = ("Error".to_string(), 0u8); + if let Some(ref current_black) = new_game_object.current_black { + black_card = (current_black.text.to_owned(), current_black.pick) + } + let meta = GameStateMeta { + uuid: new_game_object.uuid.to_string(), + name: new_game_object.name.clone(), + host: new_game_object.host.read().unwrap().name.clone(), + players: new_game_object + .players + .iter() + .map(|player| { + self.state + .user_uuid + .read() + .unwrap() + .get(player.0) + .unwrap() + .read() + .unwrap() + .name + .clone() + }) + .collect(), + czar: new_game_object.host.read().unwrap().name.clone(), + black: black_card, + white: new_game_object + .players + .get(&new_game_object.host.read().unwrap().uuid) + .unwrap() + .white + .iter() + .map(|card| card.text.clone()) + .collect(), + packs: new_game_object.packs.clone(), + }; + + // Send user's update + tx.send(serde_json::to_string(&meta).unwrap()) + .await + .unwrap(); + + // Add game to active list + self.state.games.write().unwrap().insert( + new_game_object.uuid.to_string(), + Arc::new(RwLock::new(new_game_object)), + ); + + // Broadcast game browser update + self.state + .broadcast_tx + .send(meta_games_browser_update(&self.state)) + .unwrap(); + + // Broadcast server meta update + self.state + .broadcast_tx + .send(meta_server_summary_update(&self.state)) + .unwrap(); + } + } } /// Card Set @@ -307,189 +407,87 @@ impl Game { } } -pub enum GameHandlerMessage { - NewGame { - addr: SocketAddr, - new_game: NewGameRequest, - }, - JoinGame { - addr: SocketAddr, - id: String, - }, -} -pub struct GameHandler { - state: Arc, -} +/// 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 = serde_json::from_str(&data) + .with_context(|| format!("The contents of \"{path}\" is not valid JSON."))?; -impl GameHandler { - pub fn new(state: Arc) -> Self { - GameHandler { state } - } + let mut official: HashMap = HashMap::new(); + let mut unofficial: HashMap = HashMap::new(); - pub async fn handle(&self, message: GameHandlerMessage) { - match message { - GameHandlerMessage::NewGame { addr, new_game } => self.new_game(addr, new_game).await, - GameHandlerMessage::JoinGame { addr, id } => self.join_game(addr, id).await, - _ => { - tracing::debug!("Unhandled at game handler"); - } - } - } + let mut official_meta: Vec = vec![]; + let mut unofficial_meta: Vec = vec![]; - async fn join_game(&self, addr: SocketAddr, id: String) { - // Get pointers - let this_game = self.state.games.read().unwrap().get(&id).unwrap().clone(); - let this_user = self - .state - .online_users - .read() - .unwrap() - .get(&addr) - .unwrap() - .clone(); + for set in jayson { + let mut num_white = 0; + let mut num_black = 0; - // Create player - this_game.write().unwrap().create_player(this_user).unwrap(); - - // Send updates for all players - for player in this_game.read().unwrap().players.values() { - // Create update for user's game view - let mut black_card = ("Error".to_string(), 0u8); - if let Some(ref current_black) = this_game.read().unwrap().current_black { - black_card = (current_black.text.to_owned(), current_black.pick) - } - let meta = GameStateMeta { - uuid: id.clone(), - name: this_game.read().unwrap().name.clone(), - host: this_game.read().unwrap().host.read().unwrap().name.clone(), - players: this_game - .read() - .unwrap() - .players - .values() - .map(|player| player.user.read().unwrap().name.clone()) - .collect(), - czar: this_game.read().unwrap().host.read().unwrap().name.clone(), - black: black_card, - white: player.white.iter().map(|card| card.text.clone()).collect(), - packs: this_game.read().unwrap().packs.clone(), - }; - - // Send user's update - let msg = serde_json::to_string(&meta).unwrap(); - let user_tx = player.user.read().unwrap().tx.clone(); - tokio::spawn(async move { user_tx.send(msg).await }); - } - - // Broadcast game browser update - self.state - .broadcast_tx - .send(meta_games_browser_update(&self.state)) - .unwrap(); - - // Broadcast server meta update - self.state - .broadcast_tx - .send(meta_server_summary_update(&self.state)) - .unwrap(); - } - - async fn new_game(&self, addr: SocketAddr, new_game: NewGameRequest) { - if new_game.packs.is_empty() { - tracing::debug!("Cards are empty"); - return; - } else if new_game.name.is_empty() { - tracing::debug!("Name are empty"); - return; - } - - let manifest = NewGameManifest { - name: new_game.name, - host: self - .state - .online_users - .read() - .unwrap() - .get(&addr) - .unwrap() - .clone(), - packs: new_game.packs, + let mut newset = CardSet { + white: Option::None, + black: Option::None, }; - // Create game - if let Ok(new_game_object) = Game::new(self.state.clone(), manifest) { - let tx = self - .state - .online_users - .read() - .unwrap() - .get(&addr) - .unwrap() - .read() - .unwrap() - .tx - .clone(); + // No safe default for this so make it an Option + let mut pack: Option = Option::None; - // Create update for user's game view - let mut black_card = ("Error".to_string(), 0u8); - if let Some(ref current_black) = new_game_object.current_black { - black_card = (current_black.text.to_owned(), current_black.pick) + 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()); } - let meta = GameStateMeta { - uuid: new_game_object.uuid.to_string(), - name: new_game_object.name.clone(), - host: new_game_object.host.read().unwrap().name.clone(), - players: new_game_object - .players - .iter() - .map(|player| { - self.state - .user_uuid - .read() - .unwrap() - .get(player.0) - .unwrap() - .read() - .unwrap() - .name - .clone() - }) - .collect(), - czar: new_game_object.host.read().unwrap().name.clone(), - black: black_card, - white: new_game_object - .players - .get(&new_game_object.host.read().unwrap().uuid) - .unwrap() - .white - .iter() - .map(|card| card.text.clone()) - .collect(), - packs: new_game_object.packs.clone(), - }; + } - // Send user's update - tx.send(serde_json::to_string(&meta).unwrap()) - .await - .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()); + } + } - // Add game to active list - self.state.games.write().unwrap().insert( - new_game_object.uuid.to_string(), - Arc::new(RwLock::new(new_game_object)), - ); + let meta = CardPackMeta { + name: set.name, + pack: pack.expect("No card pack number!"), + num_white, + num_black, + }; - // Broadcast game browser update - self.state - .broadcast_tx - .send(meta_games_browser_update(&self.state)) - .unwrap(); - - // Broadcast server meta update - self.state - .broadcast_tx - .send(meta_server_summary_update(&self.state)) - .unwrap(); + 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)) } diff --git a/server/src/message_handler.rs b/server/src/message_handler.rs index e501870..9784132 100644 --- a/server/src/message_handler.rs +++ b/server/src/message_handler.rs @@ -28,7 +28,7 @@ impl MessageHandler { .send(to_string::(&ChatMessage { text: msg }).unwrap()) .unwrap(); } - _user_log_in if let Ok(user_log_in) = from_str::(&text) => { + _user_log_in if let Ok(user_log_in) = from_str::(&text) => { self.state .users_tx .send(UserHandlerMessage::UserLogIn { diff --git a/server/src/user_handler.rs b/server/src/user_handler.rs index 37356c2..bf8491a 100644 --- a/server/src/user_handler.rs +++ b/server/src/user_handler.rs @@ -1,5 +1,6 @@ use crate::AppState; use crate::User; +use crate::UserHandlerMessage::*; use lib::*; use serde_json::to_string; use std::net::SocketAddr; @@ -22,14 +23,12 @@ impl UserHandler { pub async fn handle(&self, message: UserHandlerMessage) { match message { - UserHandlerMessage::NewUser { user, addr } => { + NewUser { user, addr } => { // TODO: make this not async self.set_up_new_user(user, addr).await } - UserHandlerMessage::UserLogIn { username, addr } => self.login(username, addr).await, - UserHandlerMessage::DmUserAddr { addr, message } => { - self.send_message_addr(addr, message).await - } + UserLogIn { username, addr } => self.login(username, addr).await, + DmUserAddr { addr, message } => self.send_message_addr(addr, message).await, } }