add submission and judging

This commit is contained in:
Adam 2024-08-20 22:25:39 -04:00
parent 9418451af1
commit 5d1ca4a96d
8 changed files with 314 additions and 54 deletions

View file

@ -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">

View file

@ -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);
} }

View file

@ -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

View file

@ -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))
} }

View file

@ -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,

View file

@ -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());

View file

@ -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),
}, },

View file

@ -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()
}
} }
} }