add submission and judging
This commit is contained in:
parent
9418451af1
commit
5d1ca4a96d
8 changed files with 314 additions and 54 deletions
|
@ -3,12 +3,13 @@ use crate::components::websocket::WebSocketContext;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use lib::*;
|
use lib::*;
|
||||||
use serde_json::to_string;
|
use serde_json::to_string;
|
||||||
use std::collections::{BTreeSet, HashMap};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Game() -> impl IntoView {
|
pub fn Game() -> impl IntoView {
|
||||||
let websocket = expect_context::<WebSocketContext>();
|
let websocket = expect_context::<WebSocketContext>();
|
||||||
let game_meta = expect_context::<ReadSignal<Option<GameStateMeta>>>();
|
let game_meta = expect_context::<ReadSignal<Option<GameStateMeta>>>();
|
||||||
|
let judge_round = expect_context::<ReadSignal<Option<JudgeRound>>>();
|
||||||
|
|
||||||
let (game_id, set_game_id) = create_signal("".to_string());
|
let (game_id, set_game_id) = create_signal("".to_string());
|
||||||
let (game_name, set_game_name) = create_signal("".to_string());
|
let (game_name, set_game_name) = create_signal("".to_string());
|
||||||
|
@ -16,14 +17,47 @@ pub fn Game() -> impl IntoView {
|
||||||
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(), 0u8));
|
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![]);
|
||||||
let (player_white, set_player_white) =
|
|
||||||
create_signal::<HashMap<String, WhiteCardMeta>>(HashMap::new());
|
|
||||||
let (selected_cards_ordered, set_selected_cards_ordered) = create_signal::<Vec<String>>(vec![]);
|
let (selected_cards_ordered, set_selected_cards_ordered) = create_signal::<Vec<String>>(vec![]);
|
||||||
let (player_hand, set_player_hand) = create_signal::<Vec<String>>(vec![]);
|
let (player_hand, set_player_hand) = create_signal::<Vec<String>>(vec![]);
|
||||||
|
let (player_white, set_player_white) =
|
||||||
|
create_signal::<HashMap<String, WhiteCardMeta>>(HashMap::new());
|
||||||
let (card_clicked, set_card_clicked) = create_signal::<String>(String::new());
|
let (card_clicked, set_card_clicked) = create_signal::<String>(String::new());
|
||||||
provide_context::<WriteSignal<String>>(set_card_clicked);
|
provide_context::<WriteSignal<String>>(set_card_clicked);
|
||||||
|
|
||||||
|
// Handle incoming judge message
|
||||||
|
create_effect(move |_| {
|
||||||
|
judge_round.with(move |judge_round| {
|
||||||
|
set_player_hand.update(|list| {
|
||||||
|
list.clear();
|
||||||
|
});
|
||||||
|
set_player_white.update(|list| {
|
||||||
|
list.clear();
|
||||||
|
});
|
||||||
|
set_card_clicked.update(|list| {
|
||||||
|
list.clear();
|
||||||
|
});
|
||||||
|
set_selected_cards_ordered.update(|list| {
|
||||||
|
list.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load hand
|
||||||
|
if let Some(judge) = judge_round {
|
||||||
|
for cards in judge.cards_to_judge.clone() {
|
||||||
|
for card in cards {
|
||||||
|
set_player_white.update(|hand| {
|
||||||
|
hand.insert(card.uuid.clone(), card.clone());
|
||||||
|
});
|
||||||
|
set_player_hand.update(|hand| {
|
||||||
|
hand.push(card.uuid.clone());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle incoming state update
|
||||||
create_effect(move |_| {
|
create_effect(move |_| {
|
||||||
if let Some(game) = game_meta() {
|
if let Some(game) = game_meta() {
|
||||||
// Clear in case of (re-)joining game
|
// Clear in case of (re-)joining game
|
||||||
|
@ -61,7 +95,7 @@ pub fn Game() -> impl IntoView {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Move cards back and forth from hand to selected when clicked
|
// Move cards back and forth between hand and selected when clicked
|
||||||
create_effect(move |_| {
|
create_effect(move |_| {
|
||||||
if let Some(card_index) = player_hand()
|
if let Some(card_index) = player_hand()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -88,16 +122,34 @@ pub fn Game() -> impl IntoView {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// create_effect(move |_| {
|
let submit_cards = move |_| {
|
||||||
// logging::log!("{:#?}", selected_cards());
|
let tx = websocket.clone();
|
||||||
// websocket.send(
|
judge_round.with(move |judge| {
|
||||||
// &to_string(&PlayerMoveRequest {
|
let mut _message: Option<String> = None;
|
||||||
// game_id: game_id(),
|
|
||||||
// card_ids: selected_cards(),
|
if judge.is_some() {
|
||||||
// })
|
_message = Some(
|
||||||
// .unwrap(),
|
to_string(&JudgeDecisionRequest {
|
||||||
// )
|
game_id: game_id(),
|
||||||
// });
|
winning_cards: selected_cards_ordered(),
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_message = Some(
|
||||||
|
to_string(&PlayerMoveRequest {
|
||||||
|
game_id: game_id(),
|
||||||
|
card_ids: selected_cards_ordered(),
|
||||||
|
})
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(msg) = _message {
|
||||||
|
tx.send(&msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="p-1">
|
<div class="p-1">
|
||||||
|
@ -136,6 +188,11 @@ pub fn Game() -> impl IntoView {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full inline-flex flex-wrap justify-center">
|
||||||
|
<button type="button" on:click=submit_cards>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
// Player cards
|
// Player cards
|
||||||
<div class="inline-flex flex-wrap justify-center">
|
<div class="inline-flex flex-wrap justify-center">
|
||||||
|
|
|
@ -54,26 +54,29 @@ pub fn Websocket() -> impl IntoView {
|
||||||
Rc::new(close.clone()),
|
Rc::new(close.clone()),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// TODO: This context stuff can probably be done better
|
||||||
|
|
||||||
// Contexts for message handler
|
// Contexts for message handler
|
||||||
let (state_summary, set_state_summary) =
|
let (state_summary, set_state_summary) =
|
||||||
create_signal::<Option<ServerStateSummary>>(Option::None);
|
create_signal::<Option<ServerStateSummary>>(Option::None);
|
||||||
|
let (active_games, set_active_games) = create_signal::<Vec<GameBrowserMeta>>(vec![]);
|
||||||
let (user_update, set_user_update) = create_signal::<Option<UserUpdate>>(Option::None);
|
let (user_update, set_user_update) = create_signal::<Option<UserUpdate>>(Option::None);
|
||||||
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 (judge_round, set_judge_round) = create_signal::<Option<JudgeRound>>(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::<Vec<GameBrowserMeta>>(vec![]);
|
|
||||||
let (current_game, set_current_game) = create_signal::<Option<GameStateMeta>>(Option::None);
|
let (current_game, set_current_game) = create_signal::<Option<GameStateMeta>>(Option::None);
|
||||||
let (card_packs_meta, set_card_packs_meta) = create_signal::<CardPacksMeta>(CardPacksMeta {
|
let (card_packs_meta, set_card_packs_meta) = create_signal::<CardPacksMeta>(CardPacksMeta {
|
||||||
official_meta: vec![],
|
official_meta: vec![],
|
||||||
unofficial_meta: vec![],
|
unofficial_meta: vec![],
|
||||||
});
|
});
|
||||||
|
|
||||||
// provide_context::<ReadSignal<Option<Game>>>(game_object);
|
provide_context::<ReadSignal<CardPacksMeta>>(card_packs_meta);
|
||||||
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<JudgeRound>>>(judge_round);
|
||||||
provide_context::<ReadSignal<Option<ChatMessage>>>(chat_message);
|
provide_context::<ReadSignal<Option<ChatMessage>>>(chat_message);
|
||||||
provide_context::<ReadSignal<Vec<GameBrowserMeta>>>(active_games);
|
provide_context::<ReadSignal<Vec<GameBrowserMeta>>>(active_games);
|
||||||
provide_context::<ReadSignal<Option<GameStateMeta>>>(current_game);
|
provide_context::<ReadSignal<Option<GameStateMeta>>>(current_game);
|
||||||
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
|
||||||
|
@ -94,6 +97,8 @@ pub fn Websocket() -> impl IntoView {
|
||||||
set_current_game(Some(game_update));
|
set_current_game(Some(game_update));
|
||||||
} else if let Ok(packs_meta_update) = from_str::<CardPacksMeta>(message) {
|
} else if let Ok(packs_meta_update) = from_str::<CardPacksMeta>(message) {
|
||||||
set_card_packs_meta(packs_meta_update);
|
set_card_packs_meta(packs_meta_update);
|
||||||
|
} else if let Ok(judge_update) = from_str::<JudgeRound>(message) {
|
||||||
|
set_judge_round(Some(judge_update));
|
||||||
} else {
|
} else {
|
||||||
logging::log!("Unhandled message: {}", message);
|
logging::log!("Unhandled message: {}", message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
use std::collections::BTreeSet;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Judge decision
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct JudgeDecisionRequest {
|
||||||
|
pub game_id: String,
|
||||||
|
pub winning_cards: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Judge round
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct JudgeRound {
|
||||||
|
pub cards_to_judge: Vec<Vec<WhiteCardMeta>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete game request
|
/// Delete game request
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct GameDeleteRequest {
|
pub struct GameDeleteRequest {
|
||||||
|
@ -18,7 +29,7 @@ pub struct GameJoinRequest {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct PlayerMoveRequest {
|
pub struct PlayerMoveRequest {
|
||||||
pub game_id: String,
|
pub game_id: String,
|
||||||
pub card_ids: BTreeSet<String>,
|
pub card_ids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// White Card Meta
|
/// White Card Meta
|
||||||
|
|
|
@ -28,6 +28,7 @@ pub enum GameHandlerMessage {
|
||||||
id: String,
|
id: String,
|
||||||
},
|
},
|
||||||
MoveRequest(PlayerMoveRequest, SocketAddr),
|
MoveRequest(PlayerMoveRequest, SocketAddr),
|
||||||
|
JudgeDecision(JudgeDecisionRequest, SocketAddr),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles game stuff
|
/// Handles game stuff
|
||||||
|
@ -48,12 +49,14 @@ impl GameHandler {
|
||||||
NewGame { addr, new_game } => self.create_new_game(addr, new_game).await,
|
NewGame { addr, new_game } => self.create_new_game(addr, new_game).await,
|
||||||
JoinGame { addr, id } => self.join_game(addr, id).await,
|
JoinGame { addr, id } => self.join_game(addr, id).await,
|
||||||
MoveRequest(request, addr) => self.handle_player_move(request, addr).await,
|
MoveRequest(request, addr) => self.handle_player_move(request, addr).await,
|
||||||
|
JudgeDecision(request, addr) => self.handle_judging(request, addr).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process player move request
|
/// Process judging
|
||||||
async fn handle_player_move(&self, request: PlayerMoveRequest, addr: SocketAddr) {
|
async fn handle_judging(&self, request: JudgeDecisionRequest, addr: SocketAddr) {
|
||||||
let this_player_id = self
|
// Get pointers
|
||||||
|
let this_user_id = self
|
||||||
.state
|
.state
|
||||||
.online_users
|
.online_users
|
||||||
.read()
|
.read()
|
||||||
|
@ -74,6 +77,8 @@ impl GameHandler {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
|
// Check if this player is czar
|
||||||
|
// Check if player is currently Czar
|
||||||
if this_game
|
if this_game
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -82,19 +87,129 @@ impl GameHandler {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.uuid
|
.uuid
|
||||||
.to_string()
|
.to_string()
|
||||||
== this_player_id
|
== this_user_id
|
||||||
{
|
{
|
||||||
tracing::error!("No! User id is same as current czar");
|
// Find user who submitted the card
|
||||||
} else {
|
let winning_user_id = this_game
|
||||||
tracing::error!("Ok, but i have nothing to do");
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.judge_pool
|
||||||
|
.get(&request.winning_cards)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
tracing::debug!("{:#?} Won the round!", winning_user_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!(
|
/// Process player move request
|
||||||
"Player move received:\nCards: {:#?}\nGame: {}\nPlayer: {}",
|
async fn handle_player_move(&self, request: PlayerMoveRequest, addr: SocketAddr) {
|
||||||
request.card_ids,
|
// Get pointers
|
||||||
request.game_id,
|
let this_user_id = self
|
||||||
this_player_id,
|
.state
|
||||||
);
|
.online_users
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&addr)
|
||||||
|
.unwrap()
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.uuid
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let this_game = self
|
||||||
|
.state
|
||||||
|
.games
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&request.game_id)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
// Do the stuff
|
||||||
|
|
||||||
|
// Check if player is currently Czar
|
||||||
|
if this_game
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.czar
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.uuid
|
||||||
|
.to_string()
|
||||||
|
== this_user_id
|
||||||
|
{
|
||||||
|
// Tell player no
|
||||||
|
let _ = self
|
||||||
|
.state
|
||||||
|
.users_tx
|
||||||
|
.send(DmUserAddr {
|
||||||
|
addr,
|
||||||
|
message: SendChatMessage(ChatMessage {
|
||||||
|
text: "You can't submit cards to judge, you ARE the judge!".to_string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
// Ignore extra cards
|
||||||
|
let current_round_pick: usize = this_game.read().unwrap().current_black.pick.into();
|
||||||
|
// TODO: handle not enough cards submitted
|
||||||
|
let trimmed = &request.card_ids[..current_round_pick];
|
||||||
|
|
||||||
|
// Put cards into game judge pool
|
||||||
|
this_game
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.judge_pool
|
||||||
|
.insert(trimmed.to_vec(), this_user_id.clone());
|
||||||
|
|
||||||
|
// Check if this is the last player to submit
|
||||||
|
if this_game.read().unwrap().judge_pool.len()
|
||||||
|
== this_game.read().unwrap().players.len() - 1
|
||||||
|
{
|
||||||
|
let message = SendJudgeRound(JudgeRound {
|
||||||
|
cards_to_judge: this_game
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.judge_pool
|
||||||
|
.keys()
|
||||||
|
.into_iter()
|
||||||
|
.map(|group| {
|
||||||
|
group
|
||||||
|
.iter()
|
||||||
|
.map(|id| WhiteCardMeta {
|
||||||
|
uuid: self
|
||||||
|
.state
|
||||||
|
.white_cards_by_id
|
||||||
|
.get(id)
|
||||||
|
.unwrap()
|
||||||
|
.uuid
|
||||||
|
.to_string(),
|
||||||
|
text: self
|
||||||
|
.state
|
||||||
|
.white_cards_by_id
|
||||||
|
.get(id)
|
||||||
|
.unwrap()
|
||||||
|
.text
|
||||||
|
.clone(),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
});
|
||||||
|
|
||||||
|
tracing::debug!("send for judging");
|
||||||
|
let czar_id = this_game.read().unwrap().czar.read().unwrap().uuid.clone();
|
||||||
|
let _ = self
|
||||||
|
.state
|
||||||
|
.users_tx
|
||||||
|
.send(DmUserId {
|
||||||
|
id: czar_id,
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Puts a user in a game
|
/// Puts a user in a game
|
||||||
|
@ -304,11 +419,11 @@ struct CardBlackFromJSON {
|
||||||
|
|
||||||
/// A processed white card for use server-side
|
/// A processed white card for use server-side
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CardWhiteWithID {
|
pub struct CardWhiteWithID {
|
||||||
/// Unique identifier
|
/// Unique identifier
|
||||||
uuid: Uuid,
|
pub uuid: Uuid,
|
||||||
/// Card text
|
/// Card text
|
||||||
text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A processed black card for use server-side
|
/// A processed black card for use server-side
|
||||||
|
@ -366,16 +481,19 @@ pub struct Game {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// 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: Vec<Arc<CardWhiteWithID>>,
|
|
||||||
/// Current card czar
|
/// Current card czar
|
||||||
pub czar: Arc<RwLock<User>>,
|
pub czar: Arc<RwLock<User>>,
|
||||||
|
/// Packs selected for this game
|
||||||
|
pub packs: Vec<u8>,
|
||||||
|
/// White draw pile
|
||||||
|
white: Vec<Arc<CardWhiteWithID>>,
|
||||||
/// Black draw pile
|
/// Black draw pile
|
||||||
black: Vec<Arc<CardBlackWithID>>,
|
black: Vec<Arc<CardBlackWithID>>,
|
||||||
pub players: HashMap<Uuid, Player>,
|
pub players: HashMap<String, Player>,
|
||||||
/// Black card for the current round
|
/// Black card for the current round
|
||||||
current_black: Arc<CardBlackWithID>,
|
current_black: Arc<CardBlackWithID>,
|
||||||
pub packs: Vec<u8>,
|
/// Judge pool
|
||||||
|
judge_pool: HashMap<Vec<String>, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
|
@ -424,6 +542,7 @@ impl Game {
|
||||||
players: HashMap::new(),
|
players: HashMap::new(),
|
||||||
current_black,
|
current_black,
|
||||||
packs: request.packs.clone(),
|
packs: request.packs.clone(),
|
||||||
|
judge_pool: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +582,13 @@ impl Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse json for card data
|
/// Parse json for card data
|
||||||
pub fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> {
|
pub fn load_cards_from_json(
|
||||||
|
path: &str,
|
||||||
|
) -> Result<(
|
||||||
|
CardPacks,
|
||||||
|
CardPacksMeta,
|
||||||
|
HashMap<String, Arc<CardWhiteWithID>>,
|
||||||
|
)> {
|
||||||
// Load in json
|
// Load in json
|
||||||
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))?;
|
||||||
|
@ -475,6 +600,7 @@ pub fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> {
|
||||||
let mut unofficial: HashMap<u8, CardSet> = HashMap::new();
|
let mut unofficial: HashMap<u8, CardSet> = HashMap::new();
|
||||||
let mut official_meta: Vec<CardPackMeta> = vec![];
|
let mut official_meta: Vec<CardPackMeta> = vec![];
|
||||||
let mut unofficial_meta: Vec<CardPackMeta> = vec![];
|
let mut unofficial_meta: Vec<CardPackMeta> = vec![];
|
||||||
|
let mut white_cards_by_id = HashMap::<String, Arc<CardWhiteWithID>>::new();
|
||||||
|
|
||||||
// Unpack the json
|
// Unpack the json
|
||||||
for sets in jayson {
|
for sets in jayson {
|
||||||
|
@ -496,10 +622,15 @@ pub fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> {
|
||||||
pack = Some(white[0].pack);
|
pack = Some(white[0].pack);
|
||||||
let mut white_buf = vec![];
|
let mut white_buf = vec![];
|
||||||
for card in sets.white.unwrap() {
|
for card in sets.white.unwrap() {
|
||||||
white_buf.push(Arc::new(CardWhiteWithID {
|
let uuid = Uuid::now_v7();
|
||||||
uuid: Uuid::now_v7(),
|
|
||||||
|
let new_card = Arc::new(CardWhiteWithID {
|
||||||
|
uuid,
|
||||||
text: card.text,
|
text: card.text,
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
white_cards_by_id.insert(uuid.to_string(), new_card.clone());
|
||||||
|
white_buf.push(new_card);
|
||||||
}
|
}
|
||||||
newset.white = Some(white_buf);
|
newset.white = Some(white_buf);
|
||||||
}
|
}
|
||||||
|
@ -555,5 +686,5 @@ pub fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> {
|
||||||
unofficial_meta,
|
unofficial_meta,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((packs, packs_meta))
|
Ok((packs, packs_meta, white_cards_by_id))
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub mod websocket;
|
||||||
/// User
|
/// User
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub uuid: Uuid,
|
pub uuid: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub tx: Sender<String>,
|
pub tx: Sender<String>,
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ impl User {
|
||||||
/// Create a new user object from incoming data
|
/// Create a new user object from incoming data
|
||||||
pub fn new(name: String, tx: Sender<String>) -> User {
|
pub fn new(name: String, tx: Sender<String>) -> User {
|
||||||
User {
|
User {
|
||||||
uuid: Uuid::now_v7(),
|
uuid: Uuid::now_v7().to_string(),
|
||||||
name,
|
name,
|
||||||
tx,
|
tx,
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ pub fn load_names(path: &str) -> Result<Vec<String>> {
|
||||||
|
|
||||||
// Our shared state
|
// Our shared state
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
|
pub white_cards_by_id: HashMap<String, Arc<CardWhiteWithID>>,
|
||||||
pub broadcast_tx: broadcast::Sender<String>,
|
pub broadcast_tx: broadcast::Sender<String>,
|
||||||
pub users_tx: mpsc::Sender<UserHandlerMessage>,
|
pub users_tx: mpsc::Sender<UserHandlerMessage>,
|
||||||
pub messages_tx: mpsc::Sender<(SocketAddr, Message)>,
|
pub messages_tx: mpsc::Sender<(SocketAddr, Message)>,
|
||||||
|
@ -67,7 +68,7 @@ pub struct AppState {
|
||||||
pub first_names: Vec<String>,
|
pub first_names: Vec<String>,
|
||||||
pub last_names: Vec<String>,
|
pub last_names: Vec<String>,
|
||||||
pub reserved_names: RwLock<HashSet<String>>,
|
pub reserved_names: RwLock<HashSet<String>>,
|
||||||
pub users_by_id: RwLock<HashMap<Uuid, Arc<RwLock<User>>>>,
|
pub users_by_id: RwLock<HashMap<String, Arc<RwLock<User>>>>,
|
||||||
pub online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>,
|
pub online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>,
|
||||||
pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>,
|
pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>,
|
||||||
pub packs: CardPacks,
|
pub packs: CardPacks,
|
||||||
|
|
|
@ -14,7 +14,6 @@ use tower::ServiceBuilder;
|
||||||
use tower_http::{compression::CompressionLayer, services::ServeDir};
|
use tower_http::{compression::CompressionLayer, services::ServeDir};
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
use user_handler::UserHandler;
|
use user_handler::UserHandler;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
@ -35,13 +34,14 @@ async fn main() -> Result<()> {
|
||||||
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")?;
|
||||||
let reserved_names = RwLock::new(HashSet::<String>::new());
|
let reserved_names = RwLock::new(HashSet::<String>::new());
|
||||||
let users_by_id = RwLock::new(HashMap::<Uuid, Arc<RwLock<User>>>::new());
|
let users_by_id = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new());
|
||||||
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 (packs, packs_meta) = load_cards_from_json("data/cah-cards-full.json")?;
|
let (packs, packs_meta, white_cards_by_id) = load_cards_from_json("data/cah-cards-full.json")?;
|
||||||
let games = RwLock::new(HashMap::new());
|
let games = RwLock::new(HashMap::new());
|
||||||
|
|
||||||
let app_state = Arc::new(AppState {
|
let app_state = Arc::new(AppState {
|
||||||
|
white_cards_by_id,
|
||||||
broadcast_tx,
|
broadcast_tx,
|
||||||
users_tx,
|
users_tx,
|
||||||
messages_tx,
|
messages_tx,
|
||||||
|
@ -65,7 +65,8 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Make an outgoing message handler handler
|
// TODO: Restart handler threads if they crash
|
||||||
|
// TODO: Make an outgoing message handler handler?
|
||||||
|
|
||||||
// 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());
|
||||||
|
|
|
@ -83,6 +83,16 @@ impl MessageHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_judge_decision
|
||||||
|
if let Ok(judge_request) = from_str::<JudgeDecisionRequest>(&text) =>
|
||||||
|
{
|
||||||
|
self.state
|
||||||
|
.games_tx
|
||||||
|
.send(JudgeDecision(judge_request, addr))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
_ => tracing::debug!("Unhandled text from {}", addr),
|
_ => tracing::debug!("Unhandled text from {}", addr),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,18 @@ pub enum UserHandlerMessage {
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
message: SendUserMessage,
|
message: SendUserMessage,
|
||||||
},
|
},
|
||||||
|
DmUserId {
|
||||||
|
id: String,
|
||||||
|
message: SendUserMessage,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Types of messages that can be sent to a user as a DM
|
/// Types of messages that can be sent to a user as a DM
|
||||||
|
// TODO: try to eliminate this extra step
|
||||||
pub enum SendUserMessage {
|
pub enum SendUserMessage {
|
||||||
SendUserUpdate(UserUpdate),
|
SendUserUpdate(UserUpdate),
|
||||||
SendChatMessage(ChatMessage),
|
SendChatMessage(ChatMessage),
|
||||||
|
SendJudgeRound(JudgeRound),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserHandler {
|
impl UserHandler {
|
||||||
|
@ -50,9 +56,43 @@ impl UserHandler {
|
||||||
}
|
}
|
||||||
UserLogIn { username, addr } => self.login(username, addr).await,
|
UserLogIn { username, addr } => self.login(username, addr).await,
|
||||||
DmUserAddr { addr, message } => self.send_message_addr(addr, message).await,
|
DmUserAddr { addr, message } => self.send_message_addr(addr, message).await,
|
||||||
|
DmUserId { id, message } => self.send_message_id(id, message).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Send message direct to a single user via user id
|
||||||
|
async fn send_message_id(&self, id: String, message: SendUserMessage) {
|
||||||
|
let tx = self
|
||||||
|
.state
|
||||||
|
.users_by_id
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&id)
|
||||||
|
.unwrap()
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.tx
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
// TODO: this feels messy
|
||||||
|
match message {
|
||||||
|
SendUserUpdate(message) => {
|
||||||
|
let msg = to_string::<UserUpdate>(&message).unwrap();
|
||||||
|
tx.send(msg).await.unwrap()
|
||||||
|
}
|
||||||
|
SendChatMessage(message) => {
|
||||||
|
let msg = to_string::<ChatMessage>(&message).unwrap();
|
||||||
|
tx.send(msg).await.unwrap()
|
||||||
|
}
|
||||||
|
SendJudgeRound(message) => {
|
||||||
|
let msg = to_string::<JudgeRound>(&message).unwrap();
|
||||||
|
tx.send(msg).await.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Combine ^v these
|
||||||
|
|
||||||
/// Send message direct to a single user via addr
|
/// Send message direct to a single user via addr
|
||||||
async fn send_message_addr(&self, addr: SocketAddr, message: SendUserMessage) {
|
async fn send_message_addr(&self, addr: SocketAddr, message: SendUserMessage) {
|
||||||
let tx = self
|
let tx = self
|
||||||
|
@ -75,6 +115,10 @@ impl UserHandler {
|
||||||
let msg = to_string::<ChatMessage>(&message).unwrap();
|
let msg = to_string::<ChatMessage>(&message).unwrap();
|
||||||
tx.send(msg).await.unwrap()
|
tx.send(msg).await.unwrap()
|
||||||
}
|
}
|
||||||
|
SendJudgeRound(message) => {
|
||||||
|
let msg = to_string::<JudgeRound>(&message).unwrap();
|
||||||
|
tx.send(msg).await.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue