crude card pack selection

This commit is contained in:
Adam 2024-08-04 03:13:34 -04:00
parent e0e02bcf83
commit 44ff6adb8a
9 changed files with 192 additions and 34 deletions

View file

@ -41,6 +41,7 @@ pub fn Auth() -> impl IntoView {
placeholder=move || username.get() placeholder=move || username.get()
node_ref=username_input_ref node_ref=username_input_ref
/> />
handle empty
<br/> <br/>
<input <input
class="py-2 px-4 pl-4 font-bold text-white rounded border-b-4 bg-slate-600 border-slate-800 hover:bg-slate-700 hover:border-slate-500" class="py-2 px-4 pl-4 font-bold text-white rounded border-b-4 bg-slate-600 border-slate-800 hover:bg-slate-700 hover:border-slate-500"

View file

@ -1,7 +1,10 @@
use std::collections::BTreeSet;
use crate::components::websocket::WebSocketContext; use crate::components::websocket::WebSocketContext;
use leptos::html::Input; use leptos::html::Input;
use leptos::*; use leptos::*;
use leptos_use::core::ConnectionReadyState; use leptos_use::core::ConnectionReadyState;
use leptos_use::signal_throttled_with_options;
use lib::models::*; use lib::models::*;
use serde_json::to_string; use serde_json::to_string;
@ -11,19 +14,28 @@ pub fn Browser() -> impl IntoView {
let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open; let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open;
let game_update_context = expect_context::<ReadSignal<Option<GamesUpdate>>>(); let game_update_context = expect_context::<ReadSignal<Option<GamesUpdate>>>();
let card_packs = expect_context::<ReadSignal<CardPacksMeta>>();
let (active_games, set_active_games) = create_signal::<Vec<String>>(vec![]); let (active_games, set_active_games) = create_signal::<Vec<String>>(vec![]);
let new_game_input_ref = create_node_ref::<Input>(); let new_game_name_ref = create_node_ref::<Input>();
let (selected_packs, set_selected_packs) = create_signal::<BTreeSet<u8>>(BTreeSet::new());
// create_effect(move |_| {
// logging::log!("{:#?}", selected_packs().iter().collect::<Vec<_>>());
// });
// Game stuff // Game stuff
let new_game = move |_| { let new_game = move |_| {
(websocket.send)( (websocket.send)(
&to_string(&NewGameRequest { &to_string(&NewGameRequest {
name: new_game_input_ref.get().unwrap().value(), name: new_game_name_ref.get().unwrap().value(),
packs: selected_packs()
.into_iter()
.map(|n| n.clone()) // hax
.collect::<Vec<_>>(),
}) })
.unwrap(), .unwrap(),
); );
new_game_input_ref.get().unwrap().set_value(""); new_game_name_ref.get().unwrap().set_value("");
}; };
create_effect(move |_| { create_effect(move |_| {
@ -40,20 +52,128 @@ pub fn Browser() -> impl IntoView {
set_active_games(vec![]); set_active_games(vec![]);
} }
}); });
let (show_packs, set_show_packs) = create_signal(false);
let show_packs_button = move |_| set_show_packs(!show_packs());
view! { view! {
<div class="p-1"> <div class="p-1">
<h2 class="text-2xl">Game Browser</h2>
<ul> <ul>
<h2 class="text-2xl">Game Browser</h2>
{move || active_games().into_iter().map(|n| view! { <li>{n}</li> }).collect_view()} {move || active_games().into_iter().map(|n| view! { <li>{n}</li> }).collect_view()}
</ul> </ul>
</div>
<hr/>
<div class="flex p-1">
<form onsubmit="return false" on:submit=new_game> <form onsubmit="return false" on:submit=new_game>
<h2 class="text-2xl">Create Game</h2>
<input <input
class="w-80 h-11 font-mono rounded-sm bg-slate-900 text-slate-200" class="w-80 h-11 font-mono rounded-sm bg-slate-900 text-slate-200"
placeholder="Name" placeholder="Name"
disabled=move || !connected() disabled=move || !connected()
node_ref=new_game_input_ref node_ref=new_game_name_ref
/> />
handle empty
<h2 class="text-2xl">Packs</h2>
<div class="p-2">
<button type="button" class="bg-slate-600">
<input type="checkbox" id="all"/>
<label for="all">All</label>
</button>
<button type="button" class="bg-slate-600">
<input type="checkbox" id="official" checked/>
<label for="official">Official</label>
</button>
<button type="button" class="bg-slate-600" on:click=show_packs_button>
<label>Custom</label>
</button>
finish this
</div>
<Show when=move || show_packs()>
<span class="flex">
<div>
<h2 class="text-xl">Official</h2>
{move || {
card_packs()
.official_meta
.into_iter()
.map(|n| {
view! {
<input
type="checkbox"
value=n.pack
id=n.pack
checked
on:change=move |e| {
if event_target_checked(&e) {
logging::log!("insert");
set_selected_packs
.update(|packs| {
packs.insert(n.pack);
})
} else {
logging::log!("remove");
set_selected_packs
.update(|packs| {
packs.remove(&n.pack);
})
}
}
/>
<label for=n.pack>{n.name}</label>
<br/>
{set_selected_packs
.update(|packs| {
packs.insert(n.pack);
})}
}
})
.collect_view()
}}
</div>
<div>
<h2 class="text-xl">Unofficial</h2>
{move || {
card_packs()
.unofficial_meta
.into_iter()
.map(|n| {
view! {
<input
type="checkbox"
value=n.pack
id=n.pack
on:change=move |e| {
if event_target_checked(&e) {
logging::log!("insert");
set_selected_packs
.update(|packs| {
packs.insert(n.pack);
})
} else {
logging::log!("remove");
set_selected_packs
.update(|packs| {
packs.remove(&n.pack);
})
}
}
/>
<label for=n.pack>{n.name}</label>
<br/>
}
})
.collect_view()
}}
</div>
</span>
</Show>
<input <input
class="py-2 px-4 pl-4 font-bold text-white rounded border-b-4 bg-slate-600 border-slate-800 hover:bg-slate-700 hover:border-slate-500" class="py-2 px-4 pl-4 font-bold text-white rounded border-b-4 bg-slate-600 border-slate-800 hover:bg-slate-700 hover:border-slate-500"
type="submit" type="submit"

View file

@ -124,6 +124,7 @@ pub fn Chat() -> impl IntoView {
type="submit" type="submit"
value="Send" value="Send"
/> />
handle empty
</form> </form>
</span> </span>
<br/> <br/>

View file

@ -1,9 +1,8 @@
use std::rc::Rc;
use leptos::*; use leptos::*;
use leptos_use::{core::ConnectionReadyState, use_websocket, UseWebsocketReturn}; use leptos_use::{core::ConnectionReadyState, use_websocket, UseWebsocketReturn};
use lib::models::*; use lib::models::*;
use serde_json::from_str; use serde_json::from_str;
use std::rc::Rc;
#[derive(Clone)] #[derive(Clone)]
pub struct WebSocketContext { pub struct WebSocketContext {
@ -74,12 +73,17 @@ pub fn Websocket() -> impl IntoView {
let (chat_update, set_chat_update) = create_signal::<Option<ChatUpdate>>(Option::None); let (chat_update, set_chat_update) = create_signal::<Option<ChatUpdate>>(Option::None);
let (chat_message, set_chat_message) = create_signal::<Option<ChatMessage>>(Option::None); let (chat_message, set_chat_message) = create_signal::<Option<ChatMessage>>(Option::None);
let (active_games, set_active_games) = create_signal::<Option<GamesUpdate>>(Option::None); let (active_games, set_active_games) = create_signal::<Option<GamesUpdate>>(Option::None);
let (card_packs_meta, set_card_packs_meta) = create_signal::<CardPacksMeta>(CardPacksMeta {
official_meta: vec![],
unofficial_meta: vec![],
});
// provide_context::<ReadSignal<Option<Game>>>(game_object); // provide_context::<ReadSignal<Option<Game>>>(game_object);
provide_context::<ReadSignal<Option<UserUpdate>>>(user_update); provide_context::<ReadSignal<Option<UserUpdate>>>(user_update);
provide_context::<ReadSignal<Option<ChatUpdate>>>(chat_update); provide_context::<ReadSignal<Option<ChatUpdate>>>(chat_update);
provide_context::<ReadSignal<Option<ChatMessage>>>(chat_message); provide_context::<ReadSignal<Option<ChatMessage>>>(chat_message);
provide_context::<ReadSignal<Option<GamesUpdate>>>(active_games); provide_context::<ReadSignal<Option<GamesUpdate>>>(active_games);
provide_context::<ReadSignal<CardPacksMeta>>(card_packs_meta);
provide_context::<ReadSignal<Option<ServerStateSummary>>>(state_summary); provide_context::<ReadSignal<Option<ServerStateSummary>>>(state_summary);
// Message handler // Message handler
@ -101,6 +105,8 @@ pub fn Websocket() -> impl IntoView {
set_chat_update(Some(chat_update)); set_chat_update(Some(chat_update));
} else if let Ok(games_update) = from_str::<GamesUpdate>(message) { } else if let Ok(games_update) = from_str::<GamesUpdate>(message) {
set_active_games(Some(games_update)); set_active_games(Some(games_update));
} else if let Ok(packs_meta_update) = from_str::<CardPacksMeta>(message) {
set_card_packs_meta(packs_meta_update);
} else { } else {
logging::log!("Unhandled message: {}", message); logging::log!("Unhandled message: {}", message);
} }

View file

@ -8,7 +8,7 @@ use crate::models::*;
impl Game { impl Game {
/// Build game decks from input data for game start. /// Build game decks from input data for game start.
/// This should only run once and at startup. /// This should only run once and at startup.
fn _build_decks(&mut self, cards_json: Vec<CardSet>) -> Result<()> { fn _build_decks(&mut self, cards_json: Vec<CardPack>) -> Result<()> {
for pack in cards_json { for pack in cards_json {
if let Some(white) = pack.white { if let Some(white) = pack.white {
self.white.extend(white) self.white.extend(white)

View file

@ -1,6 +1,27 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
/// Card Pack Meta
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CardPackMeta {
pub name: String,
pub pack: u8,
pub num_white: usize,
pub num_black: usize,
}
/// Card Packs Meta
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CardPacksMeta {
pub official_meta: Vec<CardPackMeta>,
pub unofficial_meta: Vec<CardPackMeta>,
}
/// Card Packs Meta
#[derive(Debug, Serialize, Deserialize)]
pub struct CardPacks {
pub official: Vec<CardPack>,
pub unofficial: Vec<CardPack>,
}
/// Games update /// Games update
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct GamesUpdate { pub struct GamesUpdate {
@ -56,6 +77,7 @@ impl User {
pub struct NewGameRequest { pub struct NewGameRequest {
/// Game name /// Game name
pub name: String, pub name: String,
pub packs: Vec<u8>,
} }
/// New game request structure /// New game request structure
@ -97,13 +119,11 @@ pub struct CardBlack {
pub pack: u8, pub pack: u8,
} }
/// A card set /// A card pack
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct CardSet { pub struct CardPack {
/// Name of the pack /// Name of the pack
pub name: String, pub name: String,
/// Pack Description
pub description: Option<String>,
/// Whether or not this is an official card pack /// Whether or not this is an official card pack
pub official: bool, pub official: bool,
/// White card data /// White card data

View file

@ -93,6 +93,7 @@ async fn handle_new_user(
.send(Message::Text(server_summary_update(state))) .send(Message::Text(server_summary_update(state)))
.await?; .await?;
sender.send(Message::Text(games_update(state))).await?; sender.send(Message::Text(games_update(state))).await?;
sender.send(Message::Text(cards_meta_update(state))).await?;
// Broadcast new user's existence // Broadcast new user's existence
// this should probably be combined and sent as one // this should probably be combined and sent as one
@ -138,6 +139,12 @@ fn chat_meta_update(state: &Arc<AppState>) -> String {
.unwrap() .unwrap()
} }
/// Generage cards meta message
fn cards_meta_update(state: &Arc<AppState>) -> String {
tracing::debug!("sending cards meta");
to_string::<CardPacksMeta>(&state.packs_meta).unwrap()
}
/// Generate message-of-the-day server greeting /// Generate message-of-the-day server greeting
fn motd() -> String { fn motd() -> String {
to_string::<ChatMessage>(&ChatMessage { to_string::<ChatMessage>(&ChatMessage {

View file

@ -64,6 +64,7 @@ fn handle_new_game(
.unwrap() .unwrap()
.clone(), .clone(),
}; };
tracing::debug!("Game Packs {:?}", new_game.packs);
// create game // create game
if let Ok(new_game_object) = Game::new(manifest) { if let Ok(new_game_object) = Game::new(manifest) {

View file

@ -16,27 +16,17 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
pub mod api; pub mod api;
use crate::api::*; use crate::api::*;
/// Card Pack Meta
#[derive(Debug)]
pub struct CardPackMeta {
name: String,
description: Option<String>,
pack: u8,
num_white: usize,
num_black: usize,
}
/// Parse json for card data /// Parse json for card data
fn load_cards_from_json(path: &str) -> Result<Vec<CardSet>> { fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> {
let data: String = let data: String =
read_to_string(path).with_context(|| format!("Invalid JSON path: \"{}\"", path))?; read_to_string(path).with_context(|| format!("Invalid JSON path: \"{}\"", path))?;
let jayson: Vec<CardSet> = let jayson: Vec<CardPack> = serde_json::from_str(&data)
serde_json::from_str(&data).with_context(|| format!("\"{path}\" is invalid json"))?; .with_context(|| format!("The contents of \"{path}\" is not valid JSON."))?;
let mut official: Vec<CardPack> = vec![];
let mut unofficial: Vec<CardPack> = vec![];
let mut official: Vec<CardSet> = vec![];
let mut official_meta: Vec<CardPackMeta> = vec![]; let mut official_meta: Vec<CardPackMeta> = vec![];
let mut unofficial: Vec<CardSet> = vec![];
let mut unofficial_meta: Vec<CardPackMeta> = vec![]; let mut unofficial_meta: Vec<CardPackMeta> = vec![];
for set in jayson { for set in jayson {
@ -59,7 +49,6 @@ fn load_cards_from_json(path: &str) -> Result<Vec<CardSet>> {
let meta = CardPackMeta { let meta = CardPackMeta {
name: set.name, name: set.name,
description: set.description,
pack: pack.expect("No card pack number!"), pack: pack.expect("No card pack number!"),
num_white, num_white,
num_black, num_black,
@ -80,11 +69,22 @@ fn load_cards_from_json(path: &str) -> Result<Vec<CardSet>> {
tracing::debug!("{:#?}", unofficial_meta[0]); tracing::debug!("{:#?}", unofficial_meta[0]);
official.shrink_to_fit(); official.shrink_to_fit();
official_meta.shrink_to_fit();
unofficial.shrink_to_fit(); unofficial.shrink_to_fit();
let packs = CardPacks {
official,
unofficial,
};
official_meta.shrink_to_fit();
unofficial_meta.shrink_to_fit(); unofficial_meta.shrink_to_fit();
Ok(official) let packs_meta = CardPacksMeta {
official_meta,
unofficial_meta,
};
Ok((packs, packs_meta))
} }
/// Parse name list /// Parse name list
@ -109,7 +109,8 @@ pub struct AppState {
// Channel used to send messages to all connected clients. // Channel used to send messages to all connected clients.
tx: broadcast::Sender<String>, tx: broadcast::Sender<String>,
// Master card decks // Master card decks
all_cards: Vec<CardSet>, packs: CardPacks,
packs_meta: CardPacksMeta,
// Games list // Games list
games: RwLock<Vec<Game>>, games: RwLock<Vec<Game>>,
// chatrooms: Mutex<HashMap<String, ChatRoom<'user>>>, // chatrooms: Mutex<HashMap<String, ChatRoom<'user>>>,
@ -133,7 +134,7 @@ async fn main() -> Result<()> {
let (tx, _rx) = broadcast::channel(100); let (tx, _rx) = broadcast::channel(100);
let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new()); let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new());
let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new()); let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new());
let all_cards = load_cards_from_json("data/cah-cards-full.json")?; let (packs, packs_meta) = load_cards_from_json("data/cah-cards-full.json")?;
let games = RwLock::new(vec![]); let games = RwLock::new(vec![]);
let first_names = load_names("data/first.txt")?; let first_names = load_names("data/first.txt")?;
let last_names = load_names("data/last.txt")?; let last_names = load_names("data/last.txt")?;
@ -142,7 +143,8 @@ async fn main() -> Result<()> {
online_users, online_users,
offline_users, offline_users,
tx, tx,
all_cards, packs,
packs_meta,
games, games,
first_names, first_names,
last_names, last_names,