aboutsummaryrefslogtreecommitdiff
path: root/src/spectate/server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/spectate/server.rs')
-rw-r--r--src/spectate/server.rs44
1 files changed, 40 insertions, 4 deletions
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 {