diff --git a/backend/src/routes.rs b/backend/src/routes.rs index 57822fa..443fe55 100644 --- a/backend/src/routes.rs +++ b/backend/src/routes.rs @@ -72,10 +72,10 @@ async fn debug_get() -> Result { } #[get("/visualizer")] -async fn visualizer_get() -> Result { - let game_options = get_games().await?; +async fn visualizer_get() -> Template { + let game_options = get_games().await; let context = Context::new_with("Visualizer", ContextT::Games(game_options)); - Ok(Template::render("visualizer", &context)) + Template::render("visualizer", &context) } #[get("/maps/")] diff --git a/backend/src/util.rs b/backend/src/util.rs index c0d0fbb..66b8c40 100644 --- a/backend/src/util.rs +++ b/backend/src/util.rs @@ -1,7 +1,13 @@ -use async_std::prelude::*; use async_std::fs; +use async_std::prelude::*; -static NAV: [(&'static str, &'static str); 5] = [("/", "Home"), ("/lobby", "Lobby"), ("/mapbuilder", "Map Builder"), ("/visualizer", "Visualizer"), ("/debug", "Debug Station")]; +static NAV: [(&'static str, &'static str); 5] = [ + ("/", "Home"), + ("/lobby", "Lobby"), + ("/mapbuilder", "Map Builder"), + ("/visualizer", "Visualizer"), + ("/debug", "Debug Station"), +]; #[derive(Serialize)] pub struct Map { @@ -19,9 +25,24 @@ pub struct PlayerStatus { impl From for PlayerStatus { fn from(value: Connect) -> Self { match value { - Connect::Connected(_, name) => PlayerStatus { waiting: false, connected: true, reconnecting: false, value: name }, - Connect::Reconnecting(_, name) => PlayerStatus { waiting: false, connected: true, reconnecting: true, value: name }, - Connect::Waiting(_, key) => PlayerStatus { waiting: true, connected: false, reconnecting: false, value: format!("Key: {}", key) }, + Connect::Connected(_, name) => PlayerStatus { + waiting: false, + connected: true, + reconnecting: false, + value: name, + }, + Connect::Reconnecting(_, name) => PlayerStatus { + waiting: false, + connected: true, + reconnecting: true, + value: name, + }, + Connect::Waiting(_, key) => PlayerStatus { + waiting: true, + connected: false, + reconnecting: false, + value: format!("Key: {}", key), + }, _ => panic!("No playerstatus possible from Connect::Request"), } } @@ -35,6 +56,7 @@ pub enum GameState { map: String, players: Vec<(String, bool)>, turns: u64, + file: String, }, Playing { name: String, @@ -42,7 +64,7 @@ pub enum GameState { players: Vec, connected: usize, total: usize, - } + }, } use std::cmp::Ordering; @@ -56,18 +78,30 @@ impl PartialOrd for GameState { 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::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, - } - } + GameState::Playing { name, .. } => match other { + GameState::Playing { name: _name, .. } => name.cmp(_name), + _ => Ordering::Less, + }, + } + } +} + +impl From for GameState { + fn from(state: FinishedState) -> Self { + GameState::Finished { + map: String::new(), + players: state + .players + .iter() + .map(|(id, name)| (name.clone(), state.winners.contains(&id))) + .collect(), + name: state.name, + turns: state.turns, + file: state.file, } } } @@ -84,7 +118,7 @@ impl GameOption { Self { name: name.to_string(), location: location.to_string(), - turns + turns, } } } @@ -105,7 +139,7 @@ pub struct Lobby { #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub enum ContextT { - Games(Vec), + Games(Vec), } #[derive(Serialize)] @@ -114,106 +148,123 @@ pub struct Context { nav: Vec, #[serde(flatten)] - pub t: Option + pub t: Option, } impl Context { pub fn new_with(active: &str, t: T) -> Self { - let nav = NAV.iter().map(|(href, name)| Link { name: name.to_string(), href: href.to_string(), active: *name == active }).collect(); + let nav = NAV + .iter() + .map(|(href, name)| Link { + name: name.to_string(), + href: href.to_string(), + active: *name == active, + }) + .collect(); Context { - nav, name: String::from(""), t: Some(t) + nav, + name: String::from(""), + t: Some(t), } } } impl Context<()> { pub fn new(active: &str) -> Self { - let nav = NAV.iter().map(|(href, name)| Link { name: name.to_string(), href: href.to_string(), active: *name == active }).collect(); + let nav = NAV + .iter() + .map(|(href, name)| Link { + name: name.to_string(), + href: href.to_string(), + active: *name == active, + }) + .collect(); Context { - nav, name: String::from(""), t: None, + nav, + name: String::from(""), + t: None, } } } pub async fn get_maps() -> Result, String> { let mut maps = Vec::new(); - let mut entries = fs::read_dir("maps").await.map_err(|_| "IO error".to_string())?; - while let Some(file) = entries.next().await { + let mut entries = fs::read_dir("maps") + .await + .map_err(|_| "IO error".to_string())?; + while let Some(file) = entries.next().await { let file = file.map_err(|_| "IO error".to_string())?.path(); if let Some(stem) = file.file_stem().and_then(|x| x.to_str()) { - maps.push(Map { name: stem.to_string(), url: file.to_str().unwrap().to_string() }); + maps.push(Map { + name: stem.to_string(), + url: file.to_str().unwrap().to_string(), + }); } } Ok(maps) } -use ini::Ini; -pub async fn get_games() -> Result, String> { - let mut games = Vec::new(); - - let content = match fs::read_to_string("games.ini").await { - Ok(v) => v, - Err(_) => { - fs::File::create("games.ini").await.map_err(|_| "IO Error".to_string())?; - String::new() - } - }; - - let i = Ini::load_from_str(&content).map_err(|_| "Corrupt ini file".to_string())?; - - for (sec, prop) in i.iter() { - if let Some(sec) = sec { - let name = match prop.get("name") { None => continue, Some(v) => v}; - let turns = match prop.get("turns").and_then(|v| v.parse().ok()) { None => continue, Some(v) => v}; - games.push(GameOption::new(name, sec, turns)); +use async_std::io::BufReader; +use futures::stream::StreamExt; +pub async fn get_games() -> Vec { + match fs::File::open("games.ini").await { + Ok(file) => { + let mut file = BufReader::new(file); + file.lines().filter_map(move |maybe| async { + maybe + .ok() + .and_then(|line| serde_json::from_str::(&line).ok()) + }).map(|state| state.into()).collect().await } + Err(_) => Vec::new(), } - - Ok(games) } use crate::planetwars::FinishedState; +use futures::future::{join_all, FutureExt}; use mozaic::modules::game; use mozaic::util::request::Connect; -use futures::future::{join_all, FutureExt}; -pub async fn get_states(game_ids: &Vec<(String, u64)>, manager: &game::Manager) -> Result, String> { +pub async fn get_states( + game_ids: &Vec<(String, u64)>, + manager: &game::Manager, +) -> Result, String> { let mut states = Vec::new(); - let gss = join_all(game_ids.iter().cloned().map(|(name, id)| manager.get_state(id).map(move |f| (f, name)))).await; + let gss = join_all( + game_ids + .iter() + .cloned() + .map(|(name, id)| manager.get_state(id).map(move |f| (f, name))), + ) + .await; for (gs, name) in gss { if let Some(state) = gs { match state { Ok(conns) => { - let players: Vec = conns.iter().cloned().map(|x| x.into()).collect(); + 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(), players, connected, map: String::new(), } - ); - }, + states.push(GameState::Playing { + name: name, + total: players.len(), + players, + connected, + map: String::new(), + }); + } Err(value) => { let state: FinishedState = serde_json::from_value(value).expect("Shit failed"); - states.push( - GameState::Finished { - map: String::new(), - players: state.players.iter().map(|(id, name)| (name.clone(), state.winners.contains(&id))).collect(), - name: state.name, - turns: state.turns, - } - ); + states.push(state.into()); } } } } states.sort(); - println!( - "{}", serde_json::to_string_pretty(&states).unwrap(), - ); - Ok(states) } @@ -226,7 +277,7 @@ pub struct Games { impl Games { pub fn new() -> Self { Self { - inner: Arc::new(Mutex::new(Vec::new())) + inner: Arc::new(Mutex::new(Vec::new())), } } diff --git a/backend/templates/visualizer.html.tera b/backend/templates/visualizer.html.tera index e393e6e..d559696 100644 --- a/backend/templates/visualizer.html.tera +++ b/backend/templates/visualizer.html.tera @@ -2,6 +2,7 @@ {% block content %} +
@@ -26,10 +27,16 @@
- {% for game in games %} -
-

{{game.name}}

-

Turns: {{game.turns}}

+ {% for state in games %} +
+

{{state.name}} ({{ state.map }}) {{state.turns}} turns

+
+
+ {% for player in state.players %} +

{{ player[0] }}

+ {% endfor %} +
+
{% endfor %}