From fd3178060a11dde8c3d672cd5b13b40b231dd5d2 Mon Sep 17 00:00:00 2001 From: ajuvercr Date: Fri, 10 Apr 2020 13:50:52 +0200 Subject: [PATCH] Backend/order (#19) * let educe do everything * try some ordering --- backend/Cargo.toml | 4 +-- backend/src/main.rs | 3 +- backend/src/planetwars/mod.rs | 8 +++++ backend/src/routes/lobby.rs | 15 ++++---- backend/src/routes/mod.rs | 4 ++- backend/src/util.rs | 65 +++++++++++++++++------------------ 6 files changed, 55 insertions(+), 44 deletions(-) diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 1c50db5..9f20460 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "backend" -version = "0.1.0" +version = "0.1.1" authors = ["ajuvercr "] edition = "2018" @@ -22,4 +22,4 @@ tracing-subscriber = "0.1.5" rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" } rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", branch = "async", features = ["handlebars_templates", "tera_templates"] } -rust-ini = "0.15.2" +educe = { version = "0.4.2", features = ["Debug", "Default", "Hash", "Clone", "Copy"] } diff --git a/backend/src/main.rs b/backend/src/main.rs index 888164d..d4b2cc8 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -19,7 +19,8 @@ extern crate tracing_subscriber; extern crate rocket; extern crate rocket_contrib; -extern crate ini; +#[macro_use] +extern crate educe; use tracing_subscriber::{EnvFilter, FmtSubscriber}; diff --git a/backend/src/planetwars/mod.rs b/backend/src/planetwars/mod.rs index 58013c4..7da6dab 100644 --- a/backend/src/planetwars/mod.rs +++ b/backend/src/planetwars/mod.rs @@ -8,6 +8,7 @@ use std::convert::TryInto; use std::fs::{create_dir, File}; use std::io::Write; use std::path::PathBuf; +use std::time::SystemTime; mod pw_config; mod pw_protocol; @@ -213,6 +214,7 @@ impl game::Controller for PlanetWarsGame { "name": self.name, "map": self.map, "file": self.log_file_loc, + "time": SystemTime::now(), })) } else { None @@ -220,6 +222,10 @@ impl game::Controller for PlanetWarsGame { } } +fn get_epoch() -> SystemTime { + SystemTime::UNIX_EPOCH +} + #[derive(Debug, Serialize, Deserialize)] pub struct FinishedState { pub winners: Vec, @@ -227,5 +233,7 @@ pub struct FinishedState { pub name: String, pub file: String, pub map: String, + #[serde(default = "get_epoch")] + pub time: SystemTime, pub players: Vec<(u64, String)>, } diff --git a/backend/src/routes/lobby.rs b/backend/src/routes/lobby.rs index bf0ab02..e31646a 100644 --- a/backend/src/routes/lobby.rs +++ b/backend/src/routes/lobby.rs @@ -1,4 +1,4 @@ -use crate::planetwars; +use crate::planetwars::{self, FinishedState}; use crate::util::*; use rocket::{Route, State}; @@ -13,10 +13,12 @@ use async_std::fs; use async_std::prelude::StreamExt; use futures::executor::ThreadPool; +use futures::future::{join_all, FutureExt}; use serde_json::Value; use rand::prelude::*; +use std::time::SystemTime; /// The type required to build a game. /// (json in POST request). @@ -174,11 +176,8 @@ async fn get_maps() -> Result, String> { Ok(maps) } -use crate::planetwars::FinishedState; - -use futures::future::{join_all, FutureExt}; pub async fn get_states( - game_ids: &Vec<(String, u64)>, + game_ids: &Vec<(String, u64, SystemTime)>, manager: &game::Manager, ) -> Result, String> { let mut states = Vec::new(); @@ -186,17 +185,18 @@ pub async fn get_states( game_ids .iter() .cloned() - .map(|(name, id)| manager.get_state(id).map(move |f| (f, name))), + .map(|(name, id, time)| manager.get_state(id).map(move |f| (f, name, time))), ) .await; - for (gs, name) in gss { + for (gs, name, time) in gss { if let Some(state) = gs { match state { Ok((state, conns)) => { let players: Vec = conns.iter().cloned().map(|x| x.into()).collect(); let connected = players.iter().filter(|x| x.connected).count(); + states.push(GameState::Playing { name: name, total: players.len(), @@ -204,6 +204,7 @@ pub async fn get_states( connected, map: String::new(), state, + time, }); } Err(value) => { diff --git a/backend/src/routes/mod.rs b/backend/src/routes/mod.rs index a8b4f32..ec12ea2 100644 --- a/backend/src/routes/mod.rs +++ b/backend/src/routes/mod.rs @@ -47,7 +47,9 @@ async fn debug_get() -> Result { /// Routes the visualizer page, rendering the visualizer Template. #[get("/visualizer")] async fn visualizer_get() -> Template { - let game_options = get_played_games().await; + let mut game_options: Vec = get_played_games().await; + game_options.sort(); + let context = Context::new_with( "Visualizer", json!({"games": game_options, "colours": COLOURS}), diff --git a/backend/src/util.rs b/backend/src/util.rs index e097f8d..1b9bb65 100644 --- a/backend/src/util.rs +++ b/backend/src/util.rs @@ -4,6 +4,7 @@ use serde_json::Value; use std::cmp::Ordering; use std::sync::{Arc, Mutex}; +use std::time::SystemTime; static NAV: [(&'static str, &'static str); 6] = [ ("/", "Home"), @@ -20,7 +21,8 @@ pub static COLOURS: [&'static str; 9] = [ /// The state of a player, in a running game. /// This represents actual players or connection keys. -#[derive(Serialize, Eq, PartialEq)] +#[derive(Serialize, Educe)] +#[educe(PartialEq, Eq, PartialOrd, Ord)] pub struct PlayerStatus { pub waiting: bool, pub connected: bool, @@ -53,48 +55,41 @@ impl From for PlayerStatus { } } +fn partial_cmp(a: &SystemTime, b: &SystemTime) -> Option { + b.partial_cmp(a) +} + /// The GameState is the state of a game. /// Either Finished, so the game is done, not running, and there is a posible visualization. /// Or Playing, the game is still being managed by the mozaic framework. -#[derive(Serialize, Eq, PartialEq)] +#[derive(Serialize, Educe)] #[serde(tag = "type")] +#[educe(PartialEq, Eq, PartialOrd, Ord)] pub enum GameState { - Finished { - name: String, - map: String, - players: Vec<(String, bool)>, - turns: u64, - file: String, - }, + #[educe(PartialOrd(rank = 1))] Playing { + #[educe(PartialOrd(method = "partial_cmp"))] + time: SystemTime, + name: String, map: String, players: Vec, connected: usize, total: usize, + #[educe(Ord(ignore), PartialOrd(ignore))] state: Value, }, -} + #[educe(PartialOrd(rank = 2))] + Finished { + #[educe(PartialOrd(method = "partial_cmp"))] + time: SystemTime, + name: String, + map: String, -impl PartialOrd for GameState { - fn partial_cmp(&self, other: &GameState) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for GameState { - fn cmp(&self, other: &GameState) -> Ordering { - match self { - GameState::Finished { name, .. } => match other { - GameState::Finished { name: _name, .. } => name.cmp(_name), - _ => Ordering::Greater, - }, - GameState::Playing { name, .. } => match other { - GameState::Playing { name: _name, .. } => name.cmp(_name), - _ => Ordering::Less, - }, - } - } + players: Vec<(String, bool)>, + turns: u64, + file: String, + }, } impl From for GameState { @@ -111,6 +106,7 @@ impl From for GameState { name: state.name, turns: state.turns, file: state.file, + time: state.time, } } } @@ -171,9 +167,9 @@ impl Context<()> { } } -/// Games is the game manager wrapper so Rocket can manage it +/// State of current live games pub struct Games { - inner: Arc>>, + inner: Arc>>, } impl Games { @@ -184,10 +180,13 @@ impl Games { } pub fn add_game(&self, name: String, id: u64) { - self.inner.lock().unwrap().push((name, id)); + self.inner + .lock() + .unwrap() + .push((name, id, SystemTime::now())); } - pub fn get_games(&self) -> Vec<(String, u64)> { + pub fn get_games(&self) -> Vec<(String, u64, SystemTime)> { self.inner.lock().unwrap().clone() } }