Refactor before it is too late
This commit is contained in:
parent
5404f4256a
commit
6b804724b4
13 changed files with 312 additions and 254 deletions
|
@ -33,7 +33,6 @@ use futures::future::FutureExt;
|
|||
use mozaic::graph;
|
||||
use mozaic::modules::*;
|
||||
|
||||
mod info;
|
||||
mod planetwars;
|
||||
mod routes;
|
||||
mod util;
|
||||
|
@ -45,6 +44,8 @@ use rocket_contrib::templates::tera::{self, Value};
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Calculate viewbox from array of points (used in map preview), added to Tera engine.
|
||||
/// So this function can be called in template.
|
||||
fn calc_viewbox(value: Value, _: HashMap<String, Value>) -> tera::Result<Value> {
|
||||
let mut min_x = std::f64::MAX;
|
||||
let mut min_y = std::f64::MAX;
|
||||
|
@ -62,10 +63,12 @@ fn calc_viewbox(value: Value, _: HashMap<String, Value>) -> tera::Result<Value>
|
|||
return Ok(Value::String(format!("{} {} {} {}", min_x - 3., min_y - 3., (max_x - min_x) + 6., (max_y - min_y) + 6.)));
|
||||
}
|
||||
|
||||
/// Get's the right colour for planets
|
||||
fn get_colour(value: Value, _: HashMap<String, Value>) -> tera::Result<Value> {
|
||||
return Ok(Value::String(COLOURS[value.as_u64().unwrap_or(0) as usize].to_string()));
|
||||
}
|
||||
|
||||
/// Async main function, starting logger, graph and rocket
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let fut = graph::set_default();
|
||||
|
@ -81,7 +84,6 @@ async fn main() {
|
|||
|
||||
let mut routes = Vec::new();
|
||||
routes::fuel(&mut routes);
|
||||
info::fuel(&mut routes);
|
||||
|
||||
let tera = Template::custom(|engines: &mut Engines| {
|
||||
engines.tera.register_filter("calc_viewbox", calc_viewbox);
|
||||
|
@ -98,6 +100,8 @@ async fn main() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
/// Creates the actual game_manager
|
||||
/// Opening tcp socket etc..
|
||||
async fn create_game_manager(tcp: &str, pool: ThreadPool) -> game::Manager {
|
||||
let addr = tcp.parse::<SocketAddr>().unwrap();
|
||||
let (gmb, handle) = game::Manager::builder(pool.clone());
|
||||
|
|
|
@ -7,22 +7,24 @@ use crate::util::*;
|
|||
|
||||
const MAX: usize = 6;
|
||||
|
||||
/// Redirects to the first info page
|
||||
#[get("/info")]
|
||||
fn help_base() -> Redirect {
|
||||
fn info_base() -> Redirect {
|
||||
Redirect::to("/info/1")
|
||||
}
|
||||
|
||||
/// Renders the <page> info page
|
||||
#[get("/info/<page>")]
|
||||
async fn help(page: usize) -> Template {
|
||||
async fn info(page: usize) -> Template {
|
||||
let context = Context::new_with("info", json!({
|
||||
"page": page,
|
||||
"next": if page + 1 <= MAX { Some(page + 1) } else { None },
|
||||
"prev": if page - 1 > 0 { Some(page - 1) } else { None }
|
||||
}));
|
||||
|
||||
Template::render(format!("help/help_{}", page), &context)
|
||||
Template::render(format!("info/info_{}", page), &context)
|
||||
}
|
||||
|
||||
pub fn fuel(routes: &mut Vec<Route>) {
|
||||
routes.extend(routes![help_base, help]);
|
||||
routes.extend(routes![info_base, info]);
|
||||
}
|
|
@ -1,102 +1,25 @@
|
|||
|
||||
use serde::{Deserialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use rocket::{Route, State};
|
||||
use rocket::response::NamedFile;
|
||||
|
||||
use rocket_contrib::templates::Template;
|
||||
use rocket_contrib::json::Json;
|
||||
|
||||
use async_std::prelude::*;
|
||||
use async_std::fs;
|
||||
use async_std::io::ReadExt;
|
||||
|
||||
use crate::planetwars;
|
||||
use crate::util::*;
|
||||
|
||||
use std::path::Path;
|
||||
use rocket::{State, Route};
|
||||
use rocket_contrib::json::Json;
|
||||
use rocket_contrib::templates::Template;
|
||||
|
||||
#[get("/<file..>", rank = 6)]
|
||||
async fn files(file: PathBuf) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new("static/").join(file)).ok()
|
||||
}
|
||||
use mozaic::modules::types::*;
|
||||
use mozaic::modules::{game, StepLock};
|
||||
use mozaic::util::request::Connect;
|
||||
|
||||
#[get("/")]
|
||||
async fn index() -> Template {
|
||||
// let context = context();
|
||||
let context = Context::new("Home");
|
||||
// context.insert("name".to_string(), "Arthur".to_string());
|
||||
Template::render("index", &context)
|
||||
}
|
||||
use async_std::fs;
|
||||
use async_std::prelude::StreamExt;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct MapReq {
|
||||
pub name: String,
|
||||
pub map: crate::planetwars::Map,
|
||||
}
|
||||
use futures::executor::ThreadPool;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[post("/maps", data="<map_req>")]
|
||||
async fn map_post(map_req: Json<MapReq>) -> Result<String, String> {
|
||||
let MapReq { name, map } = map_req.into_inner();
|
||||
|
||||
let path: PathBuf = PathBuf::from(format!("maps/{}.json", name));
|
||||
if path.exists() {
|
||||
return Err("File already exists!".into());
|
||||
}
|
||||
|
||||
let mut file = fs::File::create(path).await.map_err(|_| "IO error".to_string())?;
|
||||
file.write_all(&serde_json::to_vec_pretty(&map).unwrap()).await.map_err(|_| "IO error".to_string())?;
|
||||
|
||||
Ok("ok".into())
|
||||
}
|
||||
|
||||
#[get("/lobby")]
|
||||
async fn lobby_get(gm: State<'_, game::Manager>, state: State<'_, Games>) -> Result<Template, String> {
|
||||
let maps = get_maps().await?;
|
||||
let games = get_states(&state.get_games(), &gm).await?;
|
||||
let context = Context::new_with("Lobby", Lobby { games, maps });
|
||||
Ok(Template::render("lobby", &context))
|
||||
}
|
||||
|
||||
#[get("/mapbuilder")]
|
||||
async fn builder_get() -> Result<Template, String> {
|
||||
let context = Context::new("Map Builder");
|
||||
Ok(Template::render("mapbuilder", &context))
|
||||
}
|
||||
|
||||
#[get("/debug")]
|
||||
async fn debug_get() -> Result<Template, String> {
|
||||
let context = Context::new("Debug Station");
|
||||
Ok(Template::render("debug", &context))
|
||||
}
|
||||
|
||||
#[get("/visualizer")]
|
||||
async fn visualizer_get() -> Template {
|
||||
let game_options = get_games().await;
|
||||
let context = Context::new_with("Visualizer", json!({"games": game_options, "colours": COLOURS}));
|
||||
Template::render("visualizer", &context)
|
||||
}
|
||||
|
||||
#[get("/maps/<file>")]
|
||||
async fn map_get(file: String) -> Result<Template, String> {
|
||||
let mut content = String::new();
|
||||
let mut file = fs::File::open(Path::new("maps/").join(file)).await.map_err(|_| "IO ERROR".to_string())?;
|
||||
file.read_to_string(&mut content).await.map_err(|_| "IO ERROR".to_string())?;
|
||||
|
||||
Ok(Template::render("map_partial", &serde_json::from_str::<serde_json::Value>(&content).unwrap()))
|
||||
}
|
||||
|
||||
#[get("/partial/state")]
|
||||
async fn state_get(gm: State<'_, game::Manager>, state: State<'_, Games>) -> Result<Template, String> {
|
||||
let games = get_states(&state.get_games(), &gm).await?;
|
||||
let context = Context::new_with("Lobby", Lobby { games, maps: Vec::new() });
|
||||
|
||||
Ok(Template::render("state_partial", &context))
|
||||
}
|
||||
use serde_json::Value;
|
||||
|
||||
use rand::prelude::*;
|
||||
|
||||
/// The type required to build a game.
|
||||
/// (json in POST request).
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct GameReq {
|
||||
nop: u64,
|
||||
|
@ -105,15 +28,35 @@ struct GameReq {
|
|||
name: String,
|
||||
}
|
||||
|
||||
/// Response when building a game.
|
||||
#[derive(Serialize)]
|
||||
struct GameRes {
|
||||
players: Vec<u64>,
|
||||
state: Value,
|
||||
}
|
||||
|
||||
use mozaic::util::request::Connect;
|
||||
/// Standard get function for the lobby tab
|
||||
#[get("/lobby")]
|
||||
async fn get_lobby(gm: State<'_, game::Manager>, state: State<'_, Games>) -> Result<Template, String> {
|
||||
let maps = get_maps().await?;
|
||||
let games = get_states(&state.get_games(), &gm).await?;
|
||||
let context = Context::new_with("Lobby", Lobby { games, maps });
|
||||
Ok(Template::render("lobby", &context))
|
||||
}
|
||||
|
||||
/// The lobby get's this automatically on load and on refresh.
|
||||
#[get("/partial/state")]
|
||||
async fn state_get(gm: State<'_, game::Manager>, state: State<'_, Games>) -> Result<Template, String> {
|
||||
let games = get_states(&state.get_games(), &gm).await?;
|
||||
let context = Context::new_with("Lobby", Lobby { games, maps: Vec::new() });
|
||||
|
||||
Ok(Template::render("state_partial", &context))
|
||||
}
|
||||
|
||||
/// Post function to create a game.
|
||||
/// Returns the keys of the players in json.
|
||||
#[post("/lobby", data="<game_req>")]
|
||||
async fn game_post(game_req: Json<GameReq>, tp: State<'_, ThreadPool>, gm: State<'_, game::Manager>, state: State<'_, Games>) -> Result<Json<GameRes>, String> {
|
||||
async fn post_game(game_req: Json<GameReq>, tp: State<'_, ThreadPool>, gm: State<'_, game::Manager>, state: State<'_, Games>) -> Result<Json<GameRes>, String> {
|
||||
let game = build_builder(tp.clone(), game_req.nop, game_req.max_turns, &game_req.map, &game_req.name);
|
||||
let game_id = gm.start_game(game).await.unwrap();
|
||||
state.add_game(game_req.name.clone(), game_id);
|
||||
|
@ -136,17 +79,7 @@ async fn game_post(game_req: Json<GameReq>, tp: State<'_, ThreadPool>, gm: State
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fuel(routes: &mut Vec<Route>) {
|
||||
routes.extend(routes![files, index, map_post, map_get, lobby_get, builder_get, visualizer_get, game_post, state_get, debug_get]);
|
||||
}
|
||||
|
||||
|
||||
use crate::planetwars;
|
||||
use mozaic::modules::types::*;
|
||||
use mozaic::modules::{game, StepLock};
|
||||
use futures::executor::ThreadPool;
|
||||
|
||||
use rand::prelude::*;
|
||||
/// Generate random ID for the game, used as filename
|
||||
fn generate_string_id() -> String {
|
||||
rand::thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
|
@ -154,6 +87,8 @@ fn generate_string_id() -> String {
|
|||
.collect::<String>() + ".json"
|
||||
}
|
||||
|
||||
/// game::Manager spawns game::Builder to start games.
|
||||
/// This returns such a Builder for a planetwars game.
|
||||
fn build_builder(
|
||||
pool: ThreadPool,
|
||||
number_of_clients: u64,
|
||||
|
@ -176,3 +111,83 @@ fn build_builder(
|
|||
.with_timeout(std::time::Duration::from_secs(1)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Fuels the lobby routes
|
||||
pub fn fuel(routes: &mut Vec<Route>) {
|
||||
routes.extend(routes![post_game, get_lobby, state_get]);
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Lobby {
|
||||
pub games: Vec<GameState>,
|
||||
pub maps: Vec<Map>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Map {
|
||||
name: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
async fn get_maps() -> Result<Vec<Map>, 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 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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(maps)
|
||||
}
|
||||
|
||||
use crate::planetwars::FinishedState;
|
||||
|
||||
use futures::future::{join_all, FutureExt};
|
||||
pub async fn get_states(
|
||||
game_ids: &Vec<(String, u64)>,
|
||||
manager: &game::Manager,
|
||||
) -> Result<Vec<GameState>, 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;
|
||||
|
||||
for (gs, name) in gss {
|
||||
if let Some(state) = gs {
|
||||
match state {
|
||||
Ok((state, conns)) => {
|
||||
let players: Vec<PlayerStatus> =
|
||||
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(),
|
||||
state,
|
||||
});
|
||||
}
|
||||
Err(value) => {
|
||||
let state: FinishedState = serde_json::from_value(value).expect("Shit failed");
|
||||
states.push(state.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states.sort();
|
||||
Ok(states)
|
||||
}
|
50
backend/src/routes/maps.rs
Normal file
50
backend/src/routes/maps.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use serde::{Deserialize};
|
||||
|
||||
use rocket::{Route};
|
||||
use rocket_contrib::templates::Template;
|
||||
use rocket_contrib::json::Json;
|
||||
|
||||
use async_std::prelude::*;
|
||||
use async_std::fs;
|
||||
use async_std::io::ReadExt;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// The expected body to create a map.
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct MapReq {
|
||||
pub name: String,
|
||||
pub map: crate::planetwars::Map,
|
||||
}
|
||||
|
||||
/// Post route to create a map.
|
||||
#[post("/maps", data="<map_req>")]
|
||||
async fn map_post(map_req: Json<MapReq>) -> Result<String, String> {
|
||||
let MapReq { name, map } = map_req.into_inner();
|
||||
|
||||
let path: PathBuf = PathBuf::from(format!("maps/{}.json", name));
|
||||
if path.exists() {
|
||||
return Err("File already exists!".into());
|
||||
}
|
||||
|
||||
let mut file = fs::File::create(path).await.map_err(|_| "IO error".to_string())?;
|
||||
file.write_all(&serde_json::to_vec_pretty(&map).unwrap()).await.map_err(|_| "IO error".to_string())?;
|
||||
|
||||
Ok("ok".into())
|
||||
}
|
||||
|
||||
/// Map partial, rendering a map as svg and returning the svg element
|
||||
/// Used in the lobby page for the map previewer
|
||||
#[get("/maps/<file>")]
|
||||
async fn map_get(file: String) -> Result<Template, String> {
|
||||
let mut content = String::new();
|
||||
let mut file = fs::File::open(Path::new("maps/").join(file)).await.map_err(|_| "IO ERROR".to_string())?;
|
||||
file.read_to_string(&mut content).await.map_err(|_| "IO ERROR".to_string())?;
|
||||
|
||||
Ok(Template::render("map_partial", &serde_json::from_str::<serde_json::Value>(&content).unwrap()))
|
||||
}
|
||||
|
||||
|
||||
pub fn fuel(routes: &mut Vec<Route>) {
|
||||
routes.extend(routes![map_post, map_get]);
|
||||
}
|
82
backend/src/routes/mod.rs
Normal file
82
backend/src/routes/mod.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use crate::util::*;
|
||||
use crate::planetwars::FinishedState;
|
||||
|
||||
use rocket::{Route};
|
||||
use rocket::response::NamedFile;
|
||||
use rocket_contrib::templates::Template;
|
||||
|
||||
use async_std::prelude::*;
|
||||
use async_std::io::BufReader;
|
||||
use async_std::fs;
|
||||
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
mod lobby;
|
||||
mod maps;
|
||||
mod info;
|
||||
|
||||
/// Handles all files located in the static folder
|
||||
#[get("/<file..>", rank = 6)]
|
||||
async fn files(file: PathBuf) -> Option<NamedFile> {
|
||||
NamedFile::open(Path::new("static/").join(file)).ok()
|
||||
}
|
||||
|
||||
/// Routes the index page, rendering the index Template.
|
||||
#[get("/")]
|
||||
async fn index() -> Template {
|
||||
let context = Context::new("Home");
|
||||
Template::render("index", &context)
|
||||
}
|
||||
|
||||
/// Routes the mapbuilder page, rendering the mapbuilder Template.
|
||||
#[get("/mapbuilder")]
|
||||
async fn builder_get() -> Result<Template, String> {
|
||||
let context = Context::new("Map Builder");
|
||||
Ok(Template::render("mapbuilder", &context))
|
||||
}
|
||||
|
||||
/// Routes the debug page, rendering the debug Template.
|
||||
#[get("/debug")]
|
||||
async fn debug_get() -> Result<Template, String> {
|
||||
let context = Context::new("Debug Station");
|
||||
Ok(Template::render("debug", &context))
|
||||
}
|
||||
|
||||
/// Routes the visualizer page, rendering the visualizer Template.
|
||||
#[get("/visualizer")]
|
||||
async fn visualizer_get() -> Template {
|
||||
let game_options = get_played_games().await;
|
||||
let context = Context::new_with("Visualizer", json!({"games": game_options, "colours": COLOURS}));
|
||||
Template::render("visualizer", &context)
|
||||
}
|
||||
|
||||
/// Fuels all routes
|
||||
pub fn fuel(routes: &mut Vec<Route>) {
|
||||
routes.extend(routes![files, index, builder_get, visualizer_get, debug_get]);
|
||||
lobby::fuel(routes);
|
||||
maps::fuel(routes);
|
||||
info::fuel(routes);
|
||||
}
|
||||
|
||||
/// Reads games.ini
|
||||
/// File that represents all played games
|
||||
/// Ready to be visualized
|
||||
async fn get_played_games() -> Vec<GameState> {
|
||||
match fs::File::open("games.ini").await {
|
||||
Ok(file) => {
|
||||
let file = BufReader::new(file);
|
||||
file.lines()
|
||||
.filter_map(move |maybe| async {
|
||||
maybe
|
||||
.ok()
|
||||
.and_then(|line| serde_json::from_str::<FinishedState>(&line).ok())
|
||||
})
|
||||
.map(|state| state.into())
|
||||
.collect()
|
||||
.await
|
||||
}
|
||||
Err(_) => Vec::new(),
|
||||
}
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
use async_std::fs;
|
||||
use async_std::prelude::*;
|
||||
use crate::planetwars::FinishedState;
|
||||
use mozaic::util::request::Connect;
|
||||
use serde_json::Value;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
static NAV: [(&'static str, &'static str); 6] = [
|
||||
("/", "Home"),
|
||||
|
@ -14,18 +18,14 @@ pub static COLOURS: [&'static str; 9] = [
|
|||
"gray", "blue", "cyan", "green", "yellow", "orange", "red", "pink", "purple",
|
||||
];
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Map {
|
||||
name: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
/// The state of a player, in a running game.
|
||||
/// This represents actual players or connection keys.
|
||||
#[derive(Serialize, Eq, PartialEq)]
|
||||
pub struct PlayerStatus {
|
||||
waiting: bool,
|
||||
connected: bool,
|
||||
reconnecting: bool,
|
||||
value: String,
|
||||
pub waiting: bool,
|
||||
pub connected: bool,
|
||||
pub reconnecting: bool,
|
||||
pub value: String,
|
||||
}
|
||||
impl From<Connect> for PlayerStatus {
|
||||
fn from(value: Connect) -> Self {
|
||||
|
@ -53,8 +53,9 @@ impl From<Connect> for PlayerStatus {
|
|||
}
|
||||
}
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
/// 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)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum GameState {
|
||||
|
@ -75,8 +76,6 @@ pub enum GameState {
|
|||
},
|
||||
}
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
impl PartialOrd for GameState {
|
||||
fn partial_cmp(&self, other: &GameState) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
|
@ -116,6 +115,7 @@ impl From<FinishedState> for GameState {
|
|||
}
|
||||
}
|
||||
|
||||
/// Link struct, holding all necessary information
|
||||
#[derive(Serialize)]
|
||||
struct Link {
|
||||
name: String,
|
||||
|
@ -123,18 +123,21 @@ struct Link {
|
|||
active: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Lobby {
|
||||
pub games: Vec<GameState>,
|
||||
pub maps: Vec<Map>,
|
||||
impl Link {
|
||||
fn build_nav(active: &str) -> Vec<Link> {
|
||||
NAV.iter()
|
||||
.map(|(href, name)| Link {
|
||||
name: name.to_string(),
|
||||
href: href.to_string(),
|
||||
active: *name == active,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Serialize)]
|
||||
// #[serde(rename_all = "camelCase")]
|
||||
// pub enum ContextT {
|
||||
// Games(Vec<GameState>),
|
||||
// }
|
||||
|
||||
/// Context used as template context.
|
||||
/// This way you know to add nav bar support etc.
|
||||
/// This T can be anything that is serializable, like json!({}) macro.
|
||||
#[derive(Serialize)]
|
||||
pub struct Context<T> {
|
||||
pub name: String,
|
||||
|
@ -146,14 +149,7 @@ pub struct Context<T> {
|
|||
|
||||
impl<T> Context<T> {
|
||||
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 = Link::build_nav(active);
|
||||
|
||||
Context {
|
||||
nav,
|
||||
|
@ -165,14 +161,7 @@ impl<T> Context<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 = Link::build_nav(active);
|
||||
|
||||
Context {
|
||||
nav,
|
||||
|
@ -182,92 +171,8 @@ impl Context<()> {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn get_maps() -> Result<Vec<Map>, 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 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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(maps)
|
||||
}
|
||||
|
||||
use async_std::io::BufReader;
|
||||
use futures::stream::StreamExt;
|
||||
pub async fn get_games() -> Vec<GameState> {
|
||||
match fs::File::open("games.ini").await {
|
||||
Ok(file) => {
|
||||
let file = BufReader::new(file);
|
||||
file.lines()
|
||||
.filter_map(move |maybe| async {
|
||||
maybe
|
||||
.ok()
|
||||
.and_then(|line| serde_json::from_str::<FinishedState>(&line).ok())
|
||||
})
|
||||
.map(|state| state.into())
|
||||
.collect()
|
||||
.await
|
||||
}
|
||||
Err(_) => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
use crate::planetwars::FinishedState;
|
||||
|
||||
use futures::future::{join_all, FutureExt};
|
||||
use mozaic::modules::game;
|
||||
use mozaic::util::request::Connect;
|
||||
pub async fn get_states(
|
||||
game_ids: &Vec<(String, u64)>,
|
||||
manager: &game::Manager,
|
||||
) -> Result<Vec<GameState>, 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;
|
||||
|
||||
for (gs, name) in gss {
|
||||
if let Some(state) = gs {
|
||||
match state {
|
||||
Ok((state, conns)) => {
|
||||
let players: Vec<PlayerStatus> =
|
||||
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(),
|
||||
state,
|
||||
});
|
||||
}
|
||||
Err(value) => {
|
||||
let state: FinishedState = serde_json::from_value(value).expect("Shit failed");
|
||||
states.push(state.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states.sort();
|
||||
Ok(states)
|
||||
}
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Games is the game manager wrapper so Rocket can manage it
|
||||
pub struct Games {
|
||||
inner: Arc<Mutex<Vec<(String, u64)>>>,
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</div>
|
||||
<hr style="width:100%">
|
||||
<div class="help_content">
|
||||
{% block help %}{% endblock help %}
|
||||
{% block info %}{% endblock info %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
{% extends "help/base" %}
|
||||
{% extends "info/base" %}
|
||||
|
||||
{% block header %}
|
||||
|
||||
|
@ -7,7 +7,7 @@ Information
|
|||
{% endblock %}
|
||||
|
||||
|
||||
{% block help %}
|
||||
{% block info %}
|
||||
|
||||
<div class="help_content_2">
|
||||
<h2>Rules</h2>
|
|
@ -1,4 +1,4 @@
|
|||
{% extends "help/base" %}
|
||||
{% extends "info/base" %}
|
||||
|
||||
{% block header %}
|
||||
|
||||
|
@ -7,7 +7,7 @@ Information
|
|||
{% endblock %}
|
||||
|
||||
|
||||
{% block help %}
|
||||
{% block info %}
|
||||
|
||||
<div class="help_content_2">
|
||||
<h2>Combat resolution</h2>
|
|
@ -1,4 +1,4 @@
|
|||
{% extends "help/base" %}
|
||||
{% extends "info/base" %}
|
||||
|
||||
{% block header %}
|
||||
|
||||
|
@ -7,7 +7,7 @@ Interaction with the game
|
|||
{% endblock %}
|
||||
|
||||
|
||||
{% block help %}
|
||||
{% block info %}
|
||||
|
||||
<div class="help_content_1">
|
||||
<div class="boxed centering">
|
|
@ -1,4 +1,4 @@
|
|||
{% extends "help/base" %}
|
||||
{% extends "info/base" %}
|
||||
|
||||
{% block header %}
|
||||
|
||||
|
@ -7,7 +7,7 @@ Format: Game state
|
|||
{% endblock %}
|
||||
|
||||
|
||||
{% block help %}
|
||||
{% block info %}
|
||||
|
||||
<div class="help_content_2">
|
||||
<pre>
|
|
@ -1,4 +1,4 @@
|
|||
{% extends "help/base" %}
|
||||
{% extends "info/base" %}
|
||||
|
||||
{% block header %}
|
||||
|
||||
|
@ -7,7 +7,7 @@ Format: Player turn (actions)
|
|||
{% endblock %}
|
||||
|
||||
|
||||
{% block help %}
|
||||
{% block info %}
|
||||
|
||||
<div class="help_content_2">
|
||||
<pre>
|
|
@ -1,4 +1,4 @@
|
|||
{% extends "help/base" %}
|
||||
{% extends "info/base" %}
|
||||
|
||||
{% block header %}
|
||||
|
||||
|
@ -7,7 +7,7 @@ How to connect
|
|||
{% endblock %}
|
||||
|
||||
|
||||
{% block help %}
|
||||
{% block info %}
|
||||
|
||||
<div class="help_content_2">
|
||||
|
Loading…
Reference in a new issue