diff options
author | metamuffin <metamuffin@disroot.org> | 2024-06-04 22:19:03 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-06-04 22:19:03 +0200 |
commit | f077fd79246e0ddced696d8b3f9c12a648577abf (patch) | |
tree | 02acd35ec4ef819455e4e260547fce6c7185b1e9 /src | |
parent | d50cbd513c7208cb2a5c13571a964544b9470017 (diff) | |
download | gpn-tron-rust-f077fd79246e0ddced696d8b3f9c12a648577abf.tar gpn-tron-rust-f077fd79246e0ddced696d8b3f9c12a648577abf.tar.bz2 gpn-tron-rust-f077fd79246e0ddced696d8b3f9c12a648577abf.tar.zst |
scoreboard
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 3 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/spectate/index.html | 1 | ||||
-rw-r--r-- | src/spectate/main.ts | 15 | ||||
-rw-r--r-- | src/spectate/mod.rs | 20 | ||||
-rw-r--r-- | src/spectate/server.rs | 44 |
6 files changed, 58 insertions, 26 deletions
@@ -2,7 +2,7 @@ #![feature(iterator_try_collect)] use game::Game; use redb::Database; -use std::collections::{HashMap, VecDeque}; +use std::collections::HashMap; use tokio::sync::{broadcast, RwLock}; pub mod bot; @@ -15,7 +15,6 @@ pub struct State { pub tick: broadcast::Sender<Option<Option<u32>>>, pub game: RwLock<Game>, pub players: RwLock<HashMap<u32, String>>, - pub win_history: RwLock<VecDeque<String>>, pub chat: broadcast::Sender<(String, String)>, pub db: Database, } diff --git a/src/main.rs b/src/main.rs index e7a60de..740beb3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,6 @@ async fn main() -> anyhow::Result<()> { tick: broadcast::channel(512).0, game: Game::new(vec![]).into(), players: Default::default(), - win_history: Default::default(), chat: broadcast::channel(512).0, }); spawn(spectate_server(config.spectate, state.clone())); diff --git a/src/spectate/index.html b/src/spectate/index.html index 0c86aec..318102a 100644 --- a/src/spectate/index.html +++ b/src/spectate/index.html @@ -41,6 +41,7 @@ </div> <div id="scoreboard"> <h2>Scoreboard</h2> + <ol id="scoreboard_inner"></ol> </div> <div id="chat"> <h2>Chat</h2> diff --git a/src/spectate/main.ts b/src/spectate/main.ts index 3fcf414..2f6de83 100644 --- a/src/spectate/main.ts +++ b/src/spectate/main.ts @@ -133,6 +133,7 @@ ws.onmessage = message => { d.forEach(k => snakes.delete(k)) } else if ("game" in p) { snakes.clear() + update_stats() size = p.game.width } else if ("win" in p) { const winner = snakes.get(p.win[0])?.name; @@ -165,3 +166,17 @@ function chat(message: string, clas = "chat") { chat_history.splice(0, 1).forEach(e => e.remove()) } } + +async function update_stats() { + const res = await fetch("/stats"); + const scoreboard = await res.json() as [string, number][] + + const sce = document.getElementById("scoreboard_inner")! + sce.innerHTML = "" + + for (const [name, score] of scoreboard) { + const e = document.createElement("li") + e.textContent = `${name}: ${score}` + sce.append(e) + } +} diff --git a/src/spectate/mod.rs b/src/spectate/mod.rs index 3da4a0d..f80551e 100644 --- a/src/spectate/mod.rs +++ b/src/spectate/mod.rs @@ -1,6 +1,5 @@ -use crate::State; use serde::Deserialize; -use std::{net::SocketAddr, sync::Arc}; +use std::net::SocketAddr; pub mod server; @@ -8,20 +7,3 @@ 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 0f51c2a..ec6fbe5 100644 --- a/src/spectate/server.rs +++ b/src/spectate/server.rs @@ -1,5 +1,5 @@ use super::Config; -use crate::{game::protocol::Packet, spectate::stats_loop, State}; +use crate::{game::protocol::Packet, State}; use anyhow::{anyhow, Result}; use axum::{ extract::{ @@ -10,11 +10,17 @@ use axum::{ http::HeaderMap, response::{Html, IntoResponse}, routing::get, - Router, + Json, Router, }; use headers::ContentType; use log::{info, warn}; -use std::{net::SocketAddr, str::FromStr, sync::Arc}; +use std::{ + cmp::Reverse, + collections::{HashMap, VecDeque}, + net::SocketAddr, + str::FromStr, + sync::Arc, +}; use tokio::{ spawn, sync::{broadcast, RwLock}, @@ -22,13 +28,14 @@ use tokio::{ struct SpectateState { past_events: RwLock<Vec<Packet>>, + win_history: RwLock<VecDeque<Option<String>>>, events: broadcast::Sender<Packet>, } 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(), + win_history: Default::default(), events: broadcast::channel(512).0, }); spawn(broadcaster(sstate.clone(), state)); @@ -37,6 +44,7 @@ pub async fn spectate_server(config: Config, state: Arc<State>) -> Result<()> { .route("/main.js", get(javascript)) .route("/style.css", get(css)) .route("/events", get(ws_handler)) + .route("/stats", get(stats)) .with_state(sstate); let listener = tokio::net::TcpListener::bind(config.bind).await.unwrap(); info!("listening on {}", listener.local_addr()?); @@ -108,6 +116,21 @@ async fn css() -> (HeaderMap, &'static str) { (hm, include_str!("style.css")) } +async fn stats( + extract::State(state): extract::State<Arc<SpectateState>>, +) -> Json<Vec<(String, usize)>> { + let stats = state.win_history.read().await.to_owned(); + let mut scoreboard = HashMap::new(); + for w in stats.clone() { + if let Some(w) = w { + *scoreboard.entry(w).or_default() += 1 + } + } + let mut scoreboard = scoreboard.into_iter().collect::<Vec<_>>(); + scoreboard.sort_by_key(|(_, s)| Reverse(*s)); + Json(scoreboard) +} + async fn broadcaster(sstate: Arc<SpectateState>, state: Arc<State>) { let mut ticks = state.tick.subscribe(); while let Ok(new_game) = ticks.recv().await { @@ -117,6 +140,19 @@ async fn broadcaster(sstate: Arc<SpectateState>, state: Arc<State>) { let g = state.game.read().await; events.push(Packet::Tick); if let Some(winner) = new_game { + if let Some(winner) = winner { + let winner = + if let Some(winner) = state.players.write().await.get(&winner).cloned() { + Some(winner) + } else { + None + }; + let mut h = sstate.win_history.write().await; + h.push_front(winner); + while h.len() > 64 { + h.pop_back(); + } + } 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 { |