diff options
author | metamuffin <metamuffin@disroot.org> | 2024-06-04 21:53:00 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-06-04 21:53:00 +0200 |
commit | d50cbd513c7208cb2a5c13571a964544b9470017 (patch) | |
tree | 1d6e19928713d592b01bf1652285c49789f32051 | |
parent | 5063701929569fe37ca4b8659bd52981f8e7b77c (diff) | |
download | gpn-tron-rust-d50cbd513c7208cb2a5c13571a964544b9470017.tar gpn-tron-rust-d50cbd513c7208cb2a5c13571a964544b9470017.tar.bz2 gpn-tron-rust-d50cbd513c7208cb2a5c13571a964544b9470017.tar.zst |
show chat and winners
-rw-r--r-- | src/game/server.rs | 26 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/spectate/index.html | 1 | ||||
-rw-r--r-- | src/spectate/main.ts | 21 | ||||
-rw-r--r-- | src/spectate/mod.rs | 20 | ||||
-rw-r--r-- | src/spectate/server.rs | 6 | ||||
-rw-r--r-- | src/spectate/style.css | 16 |
7 files changed, 73 insertions, 19 deletions
diff --git a/src/game/server.rs b/src/game/server.rs index 2cc5d43..65a5cc9 100644 --- a/src/game/server.rs +++ b/src/game/server.rs @@ -37,25 +37,16 @@ async fn game_loop(config: Config, state: Arc<State>) { let res = g.tick(); match res { ControlFlow::Continue(()) => { - let _ = state.tick.send(false); + let _ = state.tick.send(None); speed += config.tickrate_speedup; speed = speed.min(config.tickrate_max) } ControlFlow::Break(winner) => { info!("winner: {winner:?}"); - if let Some(winner) = winner { - if let Some(winner) = state.players.write().await.get(&winner).cloned() { - let mut h = state.win_history.write().await; - h.push_front(winner); - while h.len() > 64 { - h.pop_back(); - } - } - } let p = state.players.read().await; *g = Game::new(p.clone().into_iter().collect()); speed = config.tickrate; - let _ = state.tick.send(true); + let _ = state.tick.send(Some(winner)); } } drop(g); @@ -126,12 +117,12 @@ async fn handle_tick( mut tx: impl AsyncWrite + Unpin, cstate: &mut ClientState, state: &Arc<State>, - new_game: bool, + new_game: Option<Option<u32>>, ) -> anyhow::Result<()> { let Some(pid) = cstate.pid else { return Ok(()) }; let mut events = Vec::new(); - if new_game { - if cstate.alive { + if let Some(winner) = new_game { + if winner == cstate.pid { tx.send_packet(Packet::Win(0, 0)).await?; } cstate.alive = true; @@ -141,7 +132,7 @@ async fn handle_tick( } { let g = state.game.read().await; - if new_game { + if new_game.is_some() { events.push(Packet::Game { my_id: pid, width: g.map.size.x as usize, @@ -235,6 +226,11 @@ async fn handle_packet( } } Packet::Chat(message) => { + if message.len() > 128 { + tx.send_packet(Packet::Error("chat message too long (> 128)".to_string())) + .await?; + return Ok(()); + } let _ = state.chat.send(("".to_string(), message)); } _ => { @@ -12,7 +12,7 @@ pub mod game; pub mod spectate; pub struct State { - pub tick: broadcast::Sender<bool>, // true for new game + pub tick: broadcast::Sender<Option<Option<u32>>>, pub game: RwLock<Game>, pub players: RwLock<HashMap<u32, String>>, pub win_history: RwLock<VecDeque<String>>, diff --git a/src/spectate/index.html b/src/spectate/index.html index 5b9dc5a..0c86aec 100644 --- a/src/spectate/index.html +++ b/src/spectate/index.html @@ -44,6 +44,7 @@ </div> <div id="chat"> <h2>Chat</h2> + <div id="chat_messages"></div> </div> </div> <div id="board"></div> diff --git a/src/spectate/main.ts b/src/spectate/main.ts index 508ca30..3fcf414 100644 --- a/src/spectate/main.ts +++ b/src/spectate/main.ts @@ -7,6 +7,8 @@ type Packet = "tick" | { game: { width: number, height: number } } | { player: { id: number, name: string } } | { die: number[] } + | { win: [number, number] } + | { chat: string } class Snake { parts: { x: number, y: number, dx: number, dy: number }[] = [] @@ -132,15 +134,34 @@ ws.onmessage = message => { } else if ("game" in p) { snakes.clear() size = p.game.width + } else if ("win" in p) { + const winner = snakes.get(p.win[0])?.name; + if (winner) chat(`${winner} won this round.`, "win") + else chat(`round ended in a tie.`, "tie") } else if ("player" in p) { snakes.set(p.player.id, new Snake(p.player.name)) } else if ("pos" in p) { snakes.get(p.pos.id)?.add_part(p.pos.x, p.pos.y) } else if ("die" in p) { + chat(`${p.die.map(e => snakes.get(e)?.name).join(", ")} died.`, "die") for (const d of p.die) { const s = snakes.get(d) if (s) s.dead = true } + } else if ("chat" in p) { + chat(p.chat) } } +const chat_history: HTMLElement[] = []; +function chat(message: string, clas = "chat") { + const e = document.createElement("p") + e.textContent = message; + e.classList.add("message") + e.classList.add(clas) + document.getElementById("chat_messages")!.prepend(e) + chat_history.push(e); + while (chat_history.length > 64) { + chat_history.splice(0, 1).forEach(e => e.remove()) + } +} diff --git a/src/spectate/mod.rs b/src/spectate/mod.rs index f80551e..3da4a0d 100644 --- a/src/spectate/mod.rs +++ b/src/spectate/mod.rs @@ -1,5 +1,6 @@ +use crate::State; use serde::Deserialize; -use std::net::SocketAddr; +use std::{net::SocketAddr, sync::Arc}; pub mod server; @@ -7,3 +8,20 @@ pub mod server; pub struct Config { bind: SocketAddr, } + +async fn stats_loop(state: Arc<State>) { + let mut ticks = state.tick.subscribe(); + while let Ok(end) = ticks.recv().await { + if let Some(winner) = end { + if let Some(winner) = winner { + if let Some(winner) = state.players.write().await.get(&winner).cloned() { + let mut h = state.win_history.write().await; + h.push_front(winner); + while h.len() > 64 { + h.pop_back(); + } + } + } + } + } +} diff --git a/src/spectate/server.rs b/src/spectate/server.rs index 11c4d5a..0f51c2a 100644 --- a/src/spectate/server.rs +++ b/src/spectate/server.rs @@ -1,5 +1,5 @@ use super::Config; -use crate::{game::protocol::Packet, State}; +use crate::{game::protocol::Packet, spectate::stats_loop, State}; use anyhow::{anyhow, Result}; use axum::{ extract::{ @@ -26,6 +26,7 @@ struct SpectateState { } pub async fn spectate_server(config: Config, state: Arc<State>) -> Result<()> { + spawn(stats_loop(state.clone())); let sstate = Arc::new(SpectateState { past_events: Default::default(), events: broadcast::channel(512).0, @@ -115,7 +116,8 @@ async fn broadcaster(sstate: Arc<SpectateState>, state: Arc<State>) { { let g = state.game.read().await; events.push(Packet::Tick); - if new_game { + if let Some(winner) = new_game { + events.push(Packet::Win(winner.unwrap_or(u32::MAX) as usize, 0)); // TODO packet misuse sstate.past_events.write().await.clear(); events.push(Packet::Game { my_id: 0, diff --git a/src/spectate/style.css b/src/spectate/style.css index c391020..5d15e9a 100644 --- a/src/spectate/style.css +++ b/src/spectate/style.css @@ -41,3 +41,19 @@ canvas { height: 40%; overflow-y: scroll; } + +.message { + margin: 2px; +} +.message.chat { + color: rgb(187, 187, 187); +} +.message.die { + color: rgb(255, 136, 136); +} +.message.win { + color: rgb(138, 255, 128); +} +.message.tie { + color: rgb(255, 221, 128); +} |