aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-06-04 22:19:03 +0200
committermetamuffin <metamuffin@disroot.org>2024-06-04 22:19:03 +0200
commitf077fd79246e0ddced696d8b3f9c12a648577abf (patch)
tree02acd35ec4ef819455e4e260547fce6c7185b1e9
parentd50cbd513c7208cb2a5c13571a964544b9470017 (diff)
downloadgpn-tron-rust-f077fd79246e0ddced696d8b3f9c12a648577abf.tar
gpn-tron-rust-f077fd79246e0ddced696d8b3f9c12a648577abf.tar.bz2
gpn-tron-rust-f077fd79246e0ddced696d8b3f9c12a648577abf.tar.zst
scoreboard
-rw-r--r--src/lib.rs3
-rw-r--r--src/main.rs1
-rw-r--r--src/spectate/index.html1
-rw-r--r--src/spectate/main.ts15
-rw-r--r--src/spectate/mod.rs20
-rw-r--r--src/spectate/server.rs44
6 files changed, 58 insertions, 26 deletions
diff --git a/src/lib.rs b/src/lib.rs
index e216643..a4d422f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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 {