From d08c0c0f5c0a7f452e7b950fc4ebf3164335c322 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Fri, 13 Jun 2025 19:02:30 -0600 Subject: [PATCH] handle ending games --- backend/src/app/clients/host_client.gleam | 4 ++ backend/src/app/clients/player_client.gleam | 2 + backend/src/app/router.gleam | 2 - backend/src/app/web.gleam | 6 +++ backend/src/session_manager.gleam | 16 ++++++- frontend/src/host_screen.rs | 46 ++++++++++++--------- frontend/src/main.rs | 22 ++-------- frontend/src/player_screen.rs | 6 +++ frontend/src/util.rs | 27 ++++++++++++ 9 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 frontend/src/util.rs diff --git a/backend/src/app/clients/host_client.gleam b/backend/src/app/clients/host_client.gleam index 4cd004c..4c45329 100644 --- a/backend/src/app/clients/host_client.gleam +++ b/backend/src/app/clients/host_client.gleam @@ -12,6 +12,7 @@ pub type HostClientMessages { ResetAllBuzers ResetPlayerBuzzer(user_id: Int) UpdateScore(user_id: Int, diff: Int) + EndGame } pub fn host_client_messages_decoder() -> decode.Decoder(HostClientMessages) { @@ -27,6 +28,7 @@ pub fn host_client_messages_decoder() -> decode.Decoder(HostClientMessages) { use diff <- decode.field("diff", decode.int) decode.success(UpdateScore(user_id:, diff:)) } + "EndGame" -> decode.success(EndGame) _ -> decode.failure(ResetAllBuzers, "HostClientMessages") } } @@ -34,6 +36,7 @@ pub fn host_client_messages_decoder() -> decode.Decoder(HostClientMessages) { pub type HostServerMessages { Ack UpdatePlayerStates(players: dict.Dict(Int, player.Player)) + ExitGame } pub fn encode_host_server_messages( @@ -53,5 +56,6 @@ pub fn encode_host_server_messages( ), ), ]) + ExitGame -> json.object([#("type", json.string("ExitGame"))]) } } diff --git a/backend/src/app/clients/player_client.gleam b/backend/src/app/clients/player_client.gleam index 1b4a3ad..323ea88 100644 --- a/backend/src/app/clients/player_client.gleam +++ b/backend/src/app/clients/player_client.gleam @@ -18,6 +18,7 @@ pub type PlayerServerMessages { Ack UpdatePointTotal(score: Int) ResetBuzzer + ExitGame } pub fn encode_player_server_messages( @@ -31,5 +32,6 @@ pub fn encode_player_server_messages( #("score", json.int(player_server_messages.score)), ]) ResetBuzzer -> json.object([#("type", json.string("ResetBuzzer"))]) + ExitGame -> json.object([#("type", json.string("ExitGame"))]) } } diff --git a/backend/src/app/router.gleam b/backend/src/app/router.gleam index 4410c6c..f973b92 100644 --- a/backend/src/app/router.gleam +++ b/backend/src/app/router.gleam @@ -173,14 +173,12 @@ fn handle_ws_message( conn, message: mist.WebsocketMessage(String), ) { - io.println("Got WS message") case message { mist.Text(msg) -> { echo msg let #(state, resp) = web.handle_client_msg(state, msg) - echo resp let assert Ok(_) = mist.send_text_frame(conn, resp) actor.continue(state) diff --git a/backend/src/app/web.gleam b/backend/src/app/web.gleam index 6571cfa..25bd8c9 100644 --- a/backend/src/app/web.gleam +++ b/backend/src/app/web.gleam @@ -174,6 +174,12 @@ fn handle_host( #(socket_state, host_client.Ack) } + host_client.EndGame -> { + let assert Ok(_) = + session_manager.end_game(socket_state.ctx.sessions, host_state.game_id) + + #(socket_state, host_client.Ack) + } } let resp = json.to_string(host_client.encode_host_server_messages(resp)) diff --git a/backend/src/session_manager.gleam b/backend/src/session_manager.gleam index 0837cd6..818b448 100644 --- a/backend/src/session_manager.gleam +++ b/backend/src/session_manager.gleam @@ -289,7 +289,21 @@ fn end_game_internal( state: State, game_id: String, ) -> Result(actor.Next(Request, State), SessionError) { - use _ <- result.try(get_game(state, game_id)) + use game <- result.try(get_game(state, game_id)) + + broadcast_msg_to_players(state, game, player_client.ExitGame) + send_message_to_host(state, game.host_user_id, host_client.ExitGame) + + game.players + |> dict.each(fn(key, _) { + let _ = player_manager.remove_player(state.internal.player_manager, key) + }) + + let _ = + player_manager.remove_player( + state.internal.player_manager, + game.host_user_id, + ) let games = dict.delete(state.internal.games, game_id) diff --git a/frontend/src/host_screen.rs b/frontend/src/host_screen.rs index 48d2418..0599887 100644 --- a/frontend/src/host_screen.rs +++ b/frontend/src/host_screen.rs @@ -6,7 +6,10 @@ use macroquad::{ }; use serde::{Deserialize, Serialize}; -use crate::{font::FontSize, model::player::Player, screen::Screen, ui_scaling::window_width}; +use crate::{ + font::FontSize, model::player::Player, screen::Screen, ui_scaling::window_width, + util::refresh_page, +}; #[allow(dead_code)] #[derive(Serialize)] @@ -15,6 +18,7 @@ enum HostClientMessages { ResetAllBuzers, ResetPlayerBuzzer { user_id: u64 }, UpdateScore { user_id: u64, diff: i32 }, + EndGame, } #[derive(Deserialize)] @@ -22,6 +26,7 @@ enum HostClientMessages { enum HostServerMessages { Ack, UpdatePlayerStates { players: HashMap }, + ExitGame, } #[derive(Default)] @@ -125,27 +130,24 @@ impl Screen for HostScreen { for user_id in &players { self.draw_user(ctx, user_id, ui, width); } + + ui.group( + hash!("Host Control"), + Vec2::new(width, window_size.y * 0.25), + |ui| { + if ui.button(Vec2::new(width * 0.05, 0.0), "Clear All") { + let msg = HostClientMessages::ResetAllBuzers; + ctx.send_msg(&msg); + } + + if ui.button(Vec2::new(width * 0.55, 0.0), "End Game") { + let msg = HostClientMessages::EndGame; + ctx.send_msg(&msg); + } + }, + ); }); - let button_size = screen_width() * 0.025; - let button_location = Vec2::new(screen_width() * 0.50, screen_height() * 0.90); - - draw_circle( - button_location.x, - button_location.y, - button_size * 1.1, - DARKBROWN, - ); - draw_circle(button_location.x, button_location.y, button_size, RED); - if is_mouse_button_pressed(MouseButton::Left) { - let loc = Vec2::from(mouse_position()); - let normalize = loc - button_location; - - if normalize.length() < button_size { - ctx.send_msg(&HostClientMessages::ResetAllBuzers); - } - } - root_ui().pop_skin(); } @@ -162,6 +164,10 @@ impl Screen for HostScreen { info!("Got new player state: {:?}", players); self.players = players; } + HostServerMessages::ExitGame => { + info!("Exiting Game"); + refresh_page(); + } }; } diff --git a/frontend/src/main.rs b/frontend/src/main.rs index 03ccd28..de087c5 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -10,6 +10,8 @@ use macroquad::{ use player_screen::PlayerScreen; use screen::Screen; +use crate::util::get_ws_url; + mod cfg; mod context; mod font; @@ -19,6 +21,7 @@ mod model; mod player_screen; mod screen; mod ui_scaling; +mod util; pub enum State { Init, @@ -98,25 +101,6 @@ pub fn default_skin() -> Skin { } } -fn get_ws_url() -> String { - let location = web_sys::window() - .unwrap() - .document() - .unwrap() - .location() - .unwrap(); - - let protocol = if location.protocol().unwrap() == "https" { - "wss" - } else { - "ws" - }; - - format!("{}://{}/ws", protocol, location.host().unwrap()) -} - -mod modname {} - #[macroquad::main("Play of the Game")] async fn main() { let cfg = Cfg::load_from_cookies().unwrap(); diff --git a/frontend/src/player_screen.rs b/frontend/src/player_screen.rs index 8b34eaa..7ba8c00 100644 --- a/frontend/src/player_screen.rs +++ b/frontend/src/player_screen.rs @@ -5,6 +5,7 @@ use crate::StateTransition; use crate::context::Context; use crate::font::FontSize; use crate::screen::Screen; +use crate::util::refresh_page; use macroquad::prelude::*; #[derive(Serialize, Debug)] @@ -19,6 +20,7 @@ pub enum PlayerServerMessages { Ack, UpdatePointTotal { score: i32 }, ResetBuzzer, + ExitGame, } #[derive(Default)] @@ -45,6 +47,10 @@ impl Screen for PlayerScreen { info!("Score updated to: {}", score); self.score = score; } + PlayerServerMessages::ExitGame => { + info!("Exiting Game"); + refresh_page(); + } } } diff --git a/frontend/src/util.rs b/frontend/src/util.rs new file mode 100644 index 0000000..b66f9f4 --- /dev/null +++ b/frontend/src/util.rs @@ -0,0 +1,27 @@ +pub fn refresh_page() { + let location = web_sys::window() + .unwrap() + .document() + .unwrap() + .location() + .unwrap(); + + location.reload().unwrap(); +} + +pub fn get_ws_url() -> String { + let location = web_sys::window() + .unwrap() + .document() + .unwrap() + .location() + .unwrap(); + + let protocol = if location.protocol().unwrap() == "https" { + "wss" + } else { + "ws" + }; + + format!("{}://{}/ws", protocol, location.host().unwrap()) +}