From 5b2d873571eddbc5821f17c7a8a431897abf5a0f Mon Sep 17 00:00:00 2001 From: ajuvercr Date: Wed, 25 Mar 2020 22:14:26 +0100 Subject: [PATCH] fancy frontend for the backend --- backend/src/main.rs | 111 +++++++++++++++++------- backend/src/planetwars/mod.rs | 2 +- backend/src/routes.rs | 81 +++++++++++++++++ backend/src/util.rs | 27 ++++++ backend/static/script/script.js | 8 ++ backend/static/style/style.css | 3 + backend/templates/base.html.tera | 16 ++++ backend/templates/index.html.tera | 11 +++ backend/templates/map_partial.html.tera | 8 ++ backend/templates/maps.html.tera | 3 + 10 files changed, 240 insertions(+), 30 deletions(-) create mode 100644 backend/src/routes.rs create mode 100644 backend/src/util.rs create mode 100644 backend/static/script/script.js create mode 100644 backend/static/style/style.css create mode 100644 backend/templates/base.html.tera create mode 100644 backend/templates/index.html.tera create mode 100644 backend/templates/map_partial.html.tera create mode 100644 backend/templates/maps.html.tera diff --git a/backend/src/main.rs b/backend/src/main.rs index 14fec91..c2b61bb 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,3 +1,5 @@ +#![feature(proc_macro_hygiene)] + extern crate serde; #[macro_use] extern crate serde_derive; @@ -12,6 +14,10 @@ extern crate tracing; extern crate tracing_futures; extern crate tracing_subscriber; +#[macro_use] +extern crate rocket; +extern crate rocket_contrib; + use tracing_subscriber::{EnvFilter, FmtSubscriber}; use std::net::SocketAddr; @@ -27,18 +33,65 @@ use mozaic::graph; use mozaic::modules::*; mod planetwars; +mod routes; +mod util; -// Load the config and start the game. -#[async_std::main] -async fn main() { - let args: Vec = env::args().collect(); - let name = args[0].clone(); - match run(args).await { - None => print_info(&name), - _ => {} - }; +use rocket_contrib::templates::{Template, Engines}; +use rocket_contrib::templates::tera::{self, Value}; + +use std::collections::HashMap; +use std::cmp::Ordering::Equal; + +const COLOURS: [&'static str; 9] = ["grey", "blue", "cyan", "green", "yellow", "orange", "red", "pink", "purple"]; + +fn calc_viewbox(value: Value, _: HashMap) -> tera::Result { + let mut min_x = std::f64::MAX; + let mut min_y = std::f64::MAX; + let mut max_x = std::f64::MIN; + let mut max_y = std::f64::MIN; + for v in value.as_array().unwrap() { + let x = v.get("x").and_then(|v| v.as_f64()).unwrap(); + let y = v.get("y").and_then(|v| v.as_f64()).unwrap(); + if x < min_x { min_x = x; } + if x > max_x { max_x = x; } + if y < min_y { min_y = y; } + if y > max_y { max_y = y; } + } + + return Ok(Value::String(format!("{} {} {} {}", min_x - 3., min_y - 3., (max_x - min_x) + 6., (max_y - min_y) + 6.))); } +fn get_colour(value: Value, _: HashMap) -> tera::Result { + return Ok(Value::String(COLOURS[value.as_u64().unwrap_or(0) as usize].to_string())); +} + +fn main() { + let mut routes = Vec::new(); + routes::fuel(&mut routes); + + let tera = Template::custom(|engines: &mut Engines| { + engines.tera.register_filter("calc_viewbox", calc_viewbox); + engines.tera.register_filter("get_colour", get_colour); + }); + + rocket::ignite() + .attach(tera) + .mount("/", routes) + .launch() + .unwrap(); +} + +// // Load the config and start the game. +// #[async_std::main] +// async fn main() { +// let args: Vec = env::args().collect(); +// let name = args[0].clone(); +// match run(args).await { +// None => print_info(&name), +// _ => {} +// }; +// } + fn build_builder( pool: ThreadPool, number_of_clients: u64, @@ -96,20 +149,20 @@ async fn run(args: Vec) -> Option<()> { let mut current_game = gm.start_game(game_builder).await.unwrap(); - loop { - match gm.get_state(current_game).await { - None => { - println!("Game finished, let's play a new one"); - let game_builder = - build_builder(pool.clone(), number_of_clients, max_turns, map, location); - current_game = gm.start_game(game_builder).await.unwrap(); - } - Some(state) => { - println!("{:?}", state); - } - } - std::thread::sleep(time::Duration::from_millis(3000)); - } + // loop { + // match gm.get_state(current_game).await { + // None => { + // println!("Game finished, let's play a new one"); + // let game_builder = + // build_builder(pool.clone(), number_of_clients, max_turns, map, location); + // current_game = gm.start_game(game_builder).await.unwrap(); + // } + // Some(state) => { + // println!("{:?}", state); + // } + // } + // std::thread::sleep(time::Duration::from_millis(3000)); + // } handle.await; @@ -118,9 +171,9 @@ async fn run(args: Vec) -> Option<()> { Some(()) } -fn print_info(name: &str) { - println!( - "Usage: {} map_location [number_of_clients [output [max_turns]]]", - name - ); -} +// fn print_info(name: &str) { +// println!( +// "Usage: {} map_location [number_of_clients [output [max_turns]]]", +// name +// ); +// } diff --git a/backend/src/planetwars/mod.rs b/backend/src/planetwars/mod.rs index a7dd27a..6d172d3 100644 --- a/backend/src/planetwars/mod.rs +++ b/backend/src/planetwars/mod.rs @@ -12,7 +12,7 @@ mod pw_config; mod pw_protocol; mod pw_rules; mod pw_serializer; -pub use pw_config::Config; +pub use pw_config::{Config, Map}; use pw_protocol::{self as proto, CommandError}; use pw_rules::Dispatch; diff --git a/backend/src/routes.rs b/backend/src/routes.rs new file mode 100644 index 0000000..17cb0ff --- /dev/null +++ b/backend/src/routes.rs @@ -0,0 +1,81 @@ + +use serde::{Deserialize}; + +use rocket::Route; +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::util::*; + +use std::path::Path; + +#[get("/", rank = 6)] +async fn files(file: PathBuf) -> Option { + NamedFile::open(Path::new("static/").join(file)).ok() +} + +#[get("/")] +async fn index() -> Template { + // let context = context(); + let context = Context { name: "Arthur".into(), maps: None }; + // context.insert("name".to_string(), "Arthur".to_string()); + Template::render("index", &context) +} + +#[get("/status")] +async fn status() -> Template { + // let context = context(); + let context = Context { name: "Arthur".into(), maps: None }; + // context.insert("name".to_string(), "Arthur".to_string()); + Template::render("index", &context) +} + +#[derive(Deserialize, Debug)] +struct MapReq { + pub name: String, + pub map: crate::planetwars::Map, +} + +use std::path::PathBuf; + +#[post("/maps", data="")] +async fn map_post(map_req: Json) -> Result { + 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("/maps")] +async fn maps_get() -> Result { + let maps = get_maps().await?; + + let context = Context { name: "Arthur".into(), maps: Some(maps) }; + Ok(Template::render("index", &context)) +} + +#[get("/maps/")] +async fn map_get(file: String) -> Result { + 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::(&content).unwrap())) +} + +pub fn fuel(routes: &mut Vec) { + routes.extend(routes![files, status, index, map_post, map_get, maps_get]); +} diff --git a/backend/src/util.rs b/backend/src/util.rs new file mode 100644 index 0000000..305ecc5 --- /dev/null +++ b/backend/src/util.rs @@ -0,0 +1,27 @@ +use async_std::prelude::*; +use async_std::fs; + +#[derive(Serialize)] +pub struct Map { + name: String, + url: String, +} + +#[derive(Serialize)] +pub struct Context { + pub name: String, + pub maps: Option>, +} + +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 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) +} diff --git a/backend/static/script/script.js b/backend/static/script/script.js new file mode 100644 index 0000000..2bdfc89 --- /dev/null +++ b/backend/static/script/script.js @@ -0,0 +1,8 @@ +const ids = {}; +["map_holder"].forEach(id => ids[id] = document.getElementById(id)); + +async function handle_map_click(url) { + console.log(url); + const c = await fetch(url); + ids["map_holder"].innerHTML = await c.text(); +} diff --git a/backend/static/style/style.css b/backend/static/style/style.css new file mode 100644 index 0000000..0c58e47 --- /dev/null +++ b/backend/static/style/style.css @@ -0,0 +1,3 @@ +.small { + font-size: 2px; + } diff --git a/backend/templates/base.html.tera b/backend/templates/base.html.tera new file mode 100644 index 0000000..bdd4d0c --- /dev/null +++ b/backend/templates/base.html.tera @@ -0,0 +1,16 @@ + + + + + Tera Demo + + + + {% block content %}{% endblock content %} + +
+
+ + + + diff --git a/backend/templates/index.html.tera b/backend/templates/index.html.tera new file mode 100644 index 0000000..903b3b6 --- /dev/null +++ b/backend/templates/index.html.tera @@ -0,0 +1,11 @@ +{% extends "base" %} + +{% block content %} + +

Hello {{ name }}!

+ +{% if maps %} + {% include "maps" %} +{% endif %} + +{% endblock %} diff --git a/backend/templates/map_partial.html.tera b/backend/templates/map_partial.html.tera new file mode 100644 index 0000000..61a7be4 --- /dev/null +++ b/backend/templates/map_partial.html.tera @@ -0,0 +1,8 @@ + +

Map rendering

+ +{% for planet in planets %} + +{{planet.name}} +{% endfor %} + diff --git a/backend/templates/maps.html.tera b/backend/templates/maps.html.tera new file mode 100644 index 0000000..61818be --- /dev/null +++ b/backend/templates/maps.html.tera @@ -0,0 +1,3 @@ +{% for m in maps %} +

{{ m.name }}

+{% endfor %}