add player submitted indicator
This commit is contained in:
parent
a78974bc2f
commit
fb691891c8
8 changed files with 149 additions and 62 deletions
|
@ -16,7 +16,7 @@ pub fn Game() -> impl IntoView {
|
|||
let connected = move || websocket.ready_state.get() == ConnectionReadyState::Open;
|
||||
|
||||
// Incoming
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameStateMeta>>>();
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameMeta>>>();
|
||||
let user_update = expect_context::<ReadSignal<Option<UserUpdate>>>();
|
||||
|
||||
// Signals //
|
||||
|
|
|
@ -3,7 +3,7 @@ use lib::*;
|
|||
|
||||
#[component]
|
||||
fn Meta() -> impl IntoView {
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameStateMeta>>>();
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameMeta>>>();
|
||||
|
||||
view! {
|
||||
<span>
|
||||
|
@ -17,7 +17,7 @@ fn Meta() -> impl IntoView {
|
|||
|
||||
#[component]
|
||||
fn Scoreboard() -> impl IntoView {
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameStateMeta>>>();
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameMeta>>>();
|
||||
|
||||
view! {
|
||||
<Show when=move || { game_meta().is_some() }>
|
||||
|
@ -28,6 +28,7 @@ fn Scoreboard() -> impl IntoView {
|
|||
<th></th>
|
||||
<th></th>
|
||||
<th>"Score"</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{game_meta()
|
||||
|
@ -52,6 +53,7 @@ fn Scoreboard() -> impl IntoView {
|
|||
}} {player.name}
|
||||
</td>
|
||||
<td class="text-center">{player.score}</td>
|
||||
<td>{if player.submitted { "🟢" } else { "⚫" }}</td>
|
||||
</tr>
|
||||
}
|
||||
})
|
||||
|
|
|
@ -8,7 +8,7 @@ use serde_json::to_string;
|
|||
pub fn JudgingView() -> impl IntoView {
|
||||
// Incoming
|
||||
let websocket = expect_context::<WebSocketContext>();
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameStateMeta>>>();
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameMeta>>>();
|
||||
let judge_round = expect_context::<ReadSignal<Option<JudgeRound>>>();
|
||||
let set_judge_round = expect_context::<WriteSignal<Option<JudgeRound>>>();
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ use std::collections::{HashMap, HashSet};
|
|||
pub fn PlayingView() -> impl IntoView {
|
||||
// Incoming
|
||||
let websocket = expect_context::<WebSocketContext>();
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameStateMeta>>>();
|
||||
let game_meta = expect_context::<ReadSignal<Option<GameMeta>>>();
|
||||
let game_state = expect_context::<ReadSignal<Option<GameStateMeta>>>();
|
||||
|
||||
// Signals
|
||||
let (selected_cards, set_selected_cards) = create_signal::<Vec<WhiteCardMeta>>(vec![]);
|
||||
|
@ -27,8 +28,8 @@ pub fn PlayingView() -> impl IntoView {
|
|||
// Put cards in a map for easier lookup
|
||||
// The server sends a vec to preserve ordering
|
||||
create_effect(move |_| {
|
||||
if game_meta().is_some() {
|
||||
for card in game_meta().unwrap().white {
|
||||
if game_state().is_some() {
|
||||
for card in game_state().unwrap().white {
|
||||
set_player_hand.update(|map| {
|
||||
map.insert(card.uuid.clone(), card.clone());
|
||||
});
|
||||
|
@ -48,7 +49,7 @@ pub fn PlayingView() -> impl IntoView {
|
|||
create_effect(move |_| {
|
||||
if card_clicked() != "".to_string()
|
||||
&& !submitted()
|
||||
&& submitted_cards().len() < game_meta().unwrap().black.1.into()
|
||||
&& submitted_cards().len() < game_state().unwrap().black.1.into()
|
||||
{
|
||||
if let Some(clicked_card) = player_hand
|
||||
.get_untracked()
|
||||
|
@ -86,7 +87,7 @@ pub fn PlayingView() -> impl IntoView {
|
|||
}
|
||||
});
|
||||
|
||||
if submitted_cards().len() < game_meta().unwrap().black.1.into() {
|
||||
if submitted_cards().len() < game_state().unwrap().black.1.into() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -129,28 +130,30 @@ pub fn PlayingView() -> impl IntoView {
|
|||
|
||||
// Player hand
|
||||
<div class="inline-flex flex-wrap justify-center">
|
||||
<For
|
||||
each=move || game_meta().unwrap().white
|
||||
key=move |card| card.uuid.clone()
|
||||
children=move |card| {
|
||||
view! {
|
||||
// Hide cards from hand view when they exist as selected or submitted
|
||||
<Show when={
|
||||
let id = card.uuid.clone();
|
||||
move || {
|
||||
if let Some(card) = player_hand().get(&id) {
|
||||
!selected_cards().contains(card)
|
||||
&& !submitted_cards().contains(card)
|
||||
} else {
|
||||
true
|
||||
<Show when=move || game_state().is_some()>
|
||||
<For
|
||||
each=move || game_state().unwrap().white
|
||||
key=move |card| card.uuid.clone()
|
||||
children=move |card| {
|
||||
view! {
|
||||
// Hide cards from hand view when they exist as selected or submitted
|
||||
<Show when={
|
||||
let id = card.uuid.clone();
|
||||
move || {
|
||||
if let Some(card) = player_hand().get(&id) {
|
||||
!selected_cards().contains(card)
|
||||
&& !submitted_cards().contains(card)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}>
|
||||
<WhiteCard card_data=card.clone() />
|
||||
</Show>
|
||||
}>
|
||||
<WhiteCard card_data=card.clone() />
|
||||
</Show>
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
/>
|
||||
</Show>
|
||||
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ pub fn Websocket() -> impl IntoView {
|
|||
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 (current_game, set_current_game) = create_signal::<Option<GameStateMeta>>(Option::None);
|
||||
let (game_meta, set_game_meta) = create_signal::<Option<GameMeta>>(Option::None);
|
||||
let (game_state, set_game_state) = create_signal::<Option<GameStateMeta>>(Option::None);
|
||||
let (card_packs_meta, set_card_packs_meta) = create_signal::<CardPacksMeta>(CardPacksMeta {
|
||||
official_meta: vec![],
|
||||
unofficial_meta: vec![],
|
||||
|
@ -87,7 +88,8 @@ pub fn Websocket() -> impl IntoView {
|
|||
set_chat_update.set_untracked(None);
|
||||
set_judge_round.set_untracked(None);
|
||||
set_chat_message.set_untracked(None);
|
||||
set_current_game.set_untracked(None);
|
||||
set_game_meta.set_untracked(None);
|
||||
set_game_state.set_untracked(None);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -98,7 +100,8 @@ pub fn Websocket() -> impl IntoView {
|
|||
provide_context::<WriteSignal<Option<JudgeRound>>>(set_judge_round);
|
||||
provide_context::<ReadSignal<Option<ChatMessage>>>(chat_message);
|
||||
provide_context::<ReadSignal<Vec<GameBrowserMeta>>>(active_games);
|
||||
provide_context::<ReadSignal<Option<GameStateMeta>>>(current_game);
|
||||
provide_context::<ReadSignal<Option<GameMeta>>>(game_meta);
|
||||
provide_context::<ReadSignal<Option<GameStateMeta>>>(game_state);
|
||||
provide_context::<ReadSignal<Option<ServerStateSummary>>>(state_summary);
|
||||
|
||||
// Message handler
|
||||
|
@ -115,8 +118,10 @@ pub fn Websocket() -> impl IntoView {
|
|||
set_chat_update(Some(chat_update));
|
||||
} else if let Ok(games_update) = from_str::<GamesUpdate>(message) {
|
||||
set_active_games(games_update.games);
|
||||
} else if let Ok(game_meta) = from_str::<GameMeta>(message) {
|
||||
set_game_meta(Some(game_meta));
|
||||
} else if let Ok(game_update) = from_str::<GameStateMeta>(message) {
|
||||
set_current_game(Some(game_update));
|
||||
set_game_state(Some(game_update));
|
||||
} else if let Ok(packs_meta_update) = from_str::<CardPacksMeta>(message) {
|
||||
set_card_packs_meta(packs_meta_update);
|
||||
} else if let Ok(judge_update) = from_str::<JudgeRound>(message) {
|
||||
|
|
|
@ -44,24 +44,30 @@ pub struct WhiteCardMeta {
|
|||
pub struct GamePlayerMeta {
|
||||
pub name: String,
|
||||
pub score: usize,
|
||||
pub submitted: bool,
|
||||
}
|
||||
|
||||
/// Game meta
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct GameStateMeta {
|
||||
pub struct GameMeta {
|
||||
pub uuid: String,
|
||||
pub name: String,
|
||||
pub host: String,
|
||||
pub players: Vec<GamePlayerMeta>,
|
||||
pub czar: String,
|
||||
pub black: (String, u8),
|
||||
pub white: Vec<WhiteCardMeta>,
|
||||
pub packs: Vec<u8>,
|
||||
pub white_count: usize,
|
||||
pub black_count: usize,
|
||||
pub white_discard_count: usize,
|
||||
}
|
||||
|
||||
/// Game state meta
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct GameStateMeta {
|
||||
pub black: (String, u8),
|
||||
pub white: Vec<WhiteCardMeta>,
|
||||
}
|
||||
|
||||
/// Game browser meta
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct GameBrowserMeta {
|
||||
|
|
|
@ -19,7 +19,8 @@ pub enum GameHandlerMessage {
|
|||
MoveRequest(PlayerMoveRequest, SocketAddr),
|
||||
JudgeDecision(JudgeDecisionRequest, SocketAddr),
|
||||
DeleteGame(GameDeleteRequest),
|
||||
SendGameUpdate(Vec<String>),
|
||||
SendGameStateUpdate(Vec<String>),
|
||||
SendGameMetaUpdate(Vec<String>),
|
||||
}
|
||||
|
||||
/// Handles game stuff
|
||||
|
@ -42,7 +43,8 @@ impl GameHandler {
|
|||
MoveRequest(request, addr) => self.handle_player_move(request, addr).await,
|
||||
JudgeDecision(request, addr) => self.handle_judging(request, addr).await,
|
||||
DeleteGame(request) => self.delete_game(request).await,
|
||||
SendGameUpdate(game_ids) => self.send_game_state_update(game_ids),
|
||||
SendGameStateUpdate(game_ids) => self.send_game_state_update_all(game_ids),
|
||||
SendGameMetaUpdate(game_ids) => self.send_game_meta_update(game_ids),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,15 +80,20 @@ impl GameHandler {
|
|||
this_game
|
||||
.write()
|
||||
.unwrap()
|
||||
.judge_round(request, player_user_id);
|
||||
.judge_round(request.clone(), player_user_id);
|
||||
|
||||
self.send_game_state_update(vec![game_id]);
|
||||
self.send_game_state_update_all(vec![game_id.clone()]);
|
||||
self.send_game_meta_update(vec![game_id]);
|
||||
} else {
|
||||
tracing::error!("Received judge request for nonexistent judge player!");
|
||||
}
|
||||
} else {
|
||||
tracing::error!("Received judge request for nonexistent game!");
|
||||
}
|
||||
|
||||
// Send updates for all players
|
||||
self.send_game_state_update_all(vec![request.game_id.clone()]);
|
||||
self.send_game_meta_update(vec![request.game_id]);
|
||||
}
|
||||
|
||||
/// Process player move request
|
||||
|
@ -99,7 +106,7 @@ impl GameHandler {
|
|||
match this_game
|
||||
.write()
|
||||
.unwrap()
|
||||
.player_move(request, player_user_id)
|
||||
.player_move(request.clone(), player_user_id)
|
||||
{
|
||||
Err(err) => {
|
||||
let message = ChatMessage { text: err };
|
||||
|
@ -125,6 +132,7 @@ impl GameHandler {
|
|||
} else {
|
||||
tracing::error!("Player tried to submit move for nonexistent game!");
|
||||
}
|
||||
self.send_game_meta_update(vec![request.game_id]);
|
||||
}
|
||||
|
||||
// Ties game ids to user for easier lookup
|
||||
|
@ -159,16 +167,19 @@ impl GameHandler {
|
|||
}
|
||||
|
||||
/// Puts a user in a game
|
||||
async fn join_game(&self, id: String, addr: SocketAddr) {
|
||||
if let Some(this_game) = self.state.games.read().unwrap().get(&id) {
|
||||
async fn join_game(&self, game_id: String, addr: SocketAddr) {
|
||||
if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) {
|
||||
if let Some(this_user) = self.state.online_users.read().unwrap().get(&addr) {
|
||||
let this_user_id = this_user.read().unwrap().uuid.clone();
|
||||
|
||||
// Register game to user
|
||||
self.register_user_in_game(id.clone(), this_user_id);
|
||||
self.register_user_in_game(game_id.clone(), this_user_id.clone());
|
||||
|
||||
// Create player
|
||||
this_game.write().unwrap().create_player(this_user.clone());
|
||||
|
||||
// Send cards
|
||||
self.send_game_state_update_single(this_user_id, game_id.clone())
|
||||
} else {
|
||||
tracing::error!("Tried to add a nonexistent user to game!");
|
||||
return;
|
||||
|
@ -180,7 +191,7 @@ impl GameHandler {
|
|||
tracing::debug!("{:#?}", self.state.games_by_user.read().unwrap());
|
||||
|
||||
// Send updates for all players
|
||||
self.send_game_state_update(vec![id]);
|
||||
self.send_game_meta_update(vec![game_id.clone()]);
|
||||
|
||||
// Broadcast game browser update
|
||||
self.state
|
||||
|
@ -195,8 +206,8 @@ impl GameHandler {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
/// Send game state update for all players of a game
|
||||
fn send_game_state_update(&self, game_ids: Vec<String>) {
|
||||
/// Send game meta update for all players of a game
|
||||
fn send_game_meta_update(&self, game_ids: Vec<String>) {
|
||||
for game_id in game_ids {
|
||||
if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) {
|
||||
let players = this_game
|
||||
|
@ -207,29 +218,24 @@ impl GameHandler {
|
|||
.map(|player| GamePlayerMeta {
|
||||
name: player.user.read().unwrap().name.clone(),
|
||||
score: player.black.len(),
|
||||
submitted: this_game
|
||||
.read()
|
||||
.unwrap()
|
||||
.judge_pile_meta
|
||||
.values()
|
||||
.collect::<Vec<&String>>()
|
||||
.contains(&&player.user.read().unwrap().uuid),
|
||||
})
|
||||
.collect::<Vec<GamePlayerMeta>>();
|
||||
|
||||
for player in this_game.read().unwrap().players.values() {
|
||||
// Create update for user's game view
|
||||
let meta = GameStateMeta {
|
||||
let meta = GameMeta {
|
||||
uuid: game_id.clone(),
|
||||
name: this_game.read().unwrap().name.clone(),
|
||||
host: this_game.read().unwrap().host.read().unwrap().name.clone(),
|
||||
players: players.clone(),
|
||||
czar: this_game.read().unwrap().czar.read().unwrap().name.clone(),
|
||||
black: (
|
||||
this_game.read().unwrap().current_black.text.clone(),
|
||||
this_game.read().unwrap().current_black.pick,
|
||||
),
|
||||
white: player
|
||||
.white
|
||||
.iter()
|
||||
.map(|card| WhiteCardMeta {
|
||||
uuid: card.uuid.to_string(),
|
||||
text: card.text.clone(),
|
||||
})
|
||||
.collect(),
|
||||
packs: this_game.read().unwrap().packs.clone(),
|
||||
white_count: this_game.read().unwrap().white.len(),
|
||||
black_count: this_game.read().unwrap().black.len(),
|
||||
|
@ -242,11 +248,75 @@ impl GameHandler {
|
|||
tokio::spawn(async move { user_tx.send(msg).await });
|
||||
}
|
||||
} else {
|
||||
tracing::error!("Attempted to create game meta update for nonexistent game!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send game state update for all players of a game
|
||||
fn send_game_state_update_all(&self, game_ids: Vec<String>) {
|
||||
for game_id in game_ids {
|
||||
if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) {
|
||||
for player in this_game.read().unwrap().players.values() {
|
||||
// Create update for user's game view
|
||||
let meta = GameStateMeta {
|
||||
black: (
|
||||
this_game.read().unwrap().current_black.text.clone(),
|
||||
this_game.read().unwrap().current_black.pick,
|
||||
),
|
||||
white: player
|
||||
.white
|
||||
.iter()
|
||||
.map(|card| WhiteCardMeta {
|
||||
uuid: card.uuid.to_string(),
|
||||
text: card.text.clone(),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
// 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 });
|
||||
}
|
||||
} else {
|
||||
tracing::error!("Attempted to create game state update for nonexistent game!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send game state update for a single user
|
||||
fn send_game_state_update_single(&self, user_id: String, game_id: String) {
|
||||
if let Some(this_game) = self.state.games.read().unwrap().get(&game_id) {
|
||||
if let Some(player) = this_game.read().unwrap().players.get(&user_id) {
|
||||
// Create update for user's game view
|
||||
let meta = GameStateMeta {
|
||||
black: (
|
||||
this_game.read().unwrap().current_black.text.clone(),
|
||||
this_game.read().unwrap().current_black.pick,
|
||||
),
|
||||
white: player
|
||||
.white
|
||||
.iter()
|
||||
.map(|card| WhiteCardMeta {
|
||||
uuid: card.uuid.to_string(),
|
||||
text: card.text.clone(),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
// 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 });
|
||||
} else {
|
||||
tracing::error!("Attempted to create game state update for nonexistent player!");
|
||||
}
|
||||
} else {
|
||||
tracing::error!("Attempted to create game state update for nonexistent game!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new game
|
||||
async fn create_new_game(&self, new_game: NewGameRequest, addr: SocketAddr) {
|
||||
if new_game.packs.is_empty() {
|
||||
|
@ -282,7 +352,8 @@ impl GameHandler {
|
|||
// Register game to user
|
||||
self.register_user_in_game(game_id.clone(), host.read().unwrap().uuid.clone());
|
||||
|
||||
self.send_game_state_update(vec![game_id]);
|
||||
self.send_game_state_update_all(vec![game_id.clone()]);
|
||||
self.send_game_meta_update(vec![game_id]);
|
||||
|
||||
// Broadcast game browser update
|
||||
self.state
|
||||
|
|
|
@ -256,7 +256,7 @@ impl UserHandler {
|
|||
.unwrap()
|
||||
.contains_key(&user_id)
|
||||
{
|
||||
let msg = GameHandlerMessage::SendGameUpdate(
|
||||
let msg = GameHandlerMessage::SendGameMetaUpdate(
|
||||
self.state
|
||||
.games_by_user
|
||||
.read()
|
||||
|
|
Loading…
Add table
Reference in a new issue