From 70aabd39e9de74005b26b98f7a4f51003084e64f Mon Sep 17 00:00:00 2001 From: ajuvercr Date: Fri, 27 Mar 2020 18:35:56 +0100 Subject: [PATCH] MVP BOY --- backend/.gitignore | 1 + backend/src/main.rs | 119 ++++++------------------------ backend/src/planetwars/mod.rs | 30 +++++++- backend/src/routes.rs | 58 ++++++++++++++- backend/static/script/maps.js | 23 +++++- frontend/www/games.ts | 9 ++- frontend/www/index.html | 12 ++- frontend/www/index.js | 17 +---- frontend/www/static/res/style.css | 4 +- 9 files changed, 150 insertions(+), 123 deletions(-) diff --git a/backend/.gitignore b/backend/.gitignore index 2c96eb1..f889b68 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,2 +1,3 @@ target/ Cargo.lock +games/ diff --git a/backend/src/main.rs b/backend/src/main.rs index f26caa5..4d85271 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -23,10 +23,8 @@ extern crate ini; use tracing_subscriber::{EnvFilter, FmtSubscriber}; use std::net::SocketAddr; -use std::{env, time}; -use mozaic::modules::types::*; -use mozaic::modules::{game, StepLock}; +use mozaic::modules::{game}; use futures::executor::ThreadPool; use futures::future::FutureExt; @@ -66,7 +64,20 @@ 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() { +#[async_std::main] +async fn main() { + let fut = graph::set_default(); + + let sub = FmtSubscriber::builder() + .with_env_filter(EnvFilter::from_default_env()) + .finish(); + tracing::subscriber::set_global_default(sub).unwrap(); + + let pool = ThreadPool::new().unwrap(); + pool.spawn_ok(fut.map(|_| ())); + let gm = create_game_manager("0.0.0.0:9142", pool.clone()); + + let mut routes = Vec::new(); routes::fuel(&mut routes); @@ -75,106 +86,22 @@ fn main() { engines.tera.register_filter("get_colour", get_colour); }); + rocket::ignite() + .manage(gm) + .manage(pool) .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, - max_turns: u64, - map: &str, - location: &str, -) -> game::Builder { - let config = planetwars::Config { - map_file: map.to_string(), - max_turns: max_turns, - }; - - let game = - planetwars::PlanetWarsGame::new(config.create_game(number_of_clients as usize), location); - - let players: Vec = (0..number_of_clients).collect(); - - game::Builder::new(players.clone(), game).with_step_lock( - StepLock::new(players.clone(), pool.clone()) - .with_timeout(std::time::Duration::from_secs(1)), - ) -} - -async fn run(args: Vec) -> Option<()> { - let fut = graph::set_default(); - - let sub = FmtSubscriber::builder() - .with_env_filter(EnvFilter::from_default_env()) - .finish(); - tracing::subscriber::set_global_default(sub).unwrap(); - - let addr = "127.0.0.1:9142".parse::().unwrap(); - - let map = args.get(1)?; - let number_of_clients = args - .get(2) - .map(|x| x.parse().expect("Client number should be a number")) - .unwrap_or(1); - let location = args.get(3).map(|x| x.as_str()).unwrap_or("game.json"); - let max_turns: u64 = args - .get(4) - .map(|x| x.parse().expect("Max turns should be a number")) - .unwrap_or(500); - - let pool = ThreadPool::new().ok()?; - pool.spawn_ok(fut.map(|_| ())); - +fn create_game_manager(tcp: &str, pool: ThreadPool) -> game::Manager { + let addr = tcp.parse::().unwrap(); let (gmb, handle) = game::Manager::builder(pool.clone()); + pool.spawn_ok(handle.map(|_| ())); let ep = TcpEndpoint::new(addr, pool.clone()); + let gmb = gmb.add_endpoint(ep, "TCP endpoint"); - let mut gm = gmb.build(); - - let game_builder = build_builder(pool.clone(), number_of_clients, max_turns, map, location); - std::thread::sleep(time::Duration::from_millis(3000)); - - 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)); - // } - - handle.await; - - std::thread::sleep(time::Duration::from_millis(100)); - - Some(()) + gmb.build() } - -// 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 6d172d3..f76666c 100644 --- a/backend/src/planetwars/mod.rs +++ b/backend/src/planetwars/mod.rs @@ -19,22 +19,28 @@ use pw_rules::Dispatch; pub struct PlanetWarsGame { state: pw_rules::PlanetWars, planet_map: HashMap, + log_file_loc: String, log_file: File, + turns: u64, + name: String, } impl PlanetWarsGame { - pub fn new(state: pw_rules::PlanetWars, location: &str) -> Self { + pub fn new(state: pw_rules::PlanetWars, location: &str, name: &str) -> Self { let planet_map = state .planets .iter() .map(|p| (p.name.clone(), p.id)) .collect(); - let file = File::create(location).unwrap(); + let file = File::create(format!("static/games/{}", location)).unwrap(); Self { state, planet_map, + log_file_loc: location.to_string(), log_file: file, + turns: 0, + name: name.to_string(), } } @@ -159,6 +165,8 @@ impl PlanetWarsGame { } } +use std::fs::OpenOptions; +use ini::Ini; impl game::Controller for PlanetWarsGame { fn start(&mut self) -> Vec { let mut updates = Vec::new(); @@ -167,6 +175,8 @@ impl game::Controller for PlanetWarsGame { } fn step(&mut self, turns: Vec) -> Vec { + self.turns += 1; + let mut updates = Vec::new(); let alive = self.state.living_players(); @@ -181,6 +191,20 @@ impl game::Controller for PlanetWarsGame { } fn is_done(&mut self) -> bool { - self.state.is_finished() + if self.state.is_finished() { + + let mut f = match OpenOptions::new().append(true).open("games.ini") { Err(_) => return true, Ok(f) => f }; + + let mut conf = Ini::new(); + conf.with_section(Some(self.log_file_loc.clone())) + .set("name", &self.name) + .set("turns", format!("{}", self.turns)); + + conf.write_to(&mut f).unwrap(); + + true + } else { + false + } } } diff --git a/backend/src/routes.rs b/backend/src/routes.rs index 95d6876..0a4be80 100644 --- a/backend/src/routes.rs +++ b/backend/src/routes.rs @@ -1,7 +1,7 @@ use serde::{Deserialize}; -use rocket::Route; +use rocket::{Route, State}; use rocket::response::NamedFile; use rocket_contrib::templates::Template; @@ -80,6 +80,58 @@ async fn map_get(file: String) -> Result { Ok(Template::render("map_partial", &serde_json::from_str::(&content).unwrap())) } -pub fn fuel(routes: &mut Vec) { - routes.extend(routes![files, index, map_post, map_get, maps_get, builder_get, visualizer_get]); +#[derive(Deserialize, Debug)] +struct GameReq { + nop: u64, + max_turns: u64, + map: String, + name: String, +} + +#[post("/lobby", data="")] +async fn game_post(game_req: Json, tp: State<'_, ThreadPool>, gm: State<'_, game::Manager>) -> Result { + 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; + Ok(format!("{:?}", gm.get_state(game_id.unwrap()).await)) +} + +pub fn fuel(routes: &mut Vec) { + routes.extend(routes![files, index, map_post, map_get, maps_get, builder_get, visualizer_get, game_post]); +} + + +use crate::planetwars; +use mozaic::modules::types::*; +use mozaic::modules::{game, StepLock}; +use futures::executor::ThreadPool; + +use rand::prelude::*; +fn generate_string_id() -> String { + rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .take(15) + .collect::() + ".json" +} + +fn build_builder( + pool: ThreadPool, + number_of_clients: u64, + max_turns: u64, + map: &str, + name: &str, +) -> game::Builder { + let config = planetwars::Config { + map_file: map.to_string(), + max_turns: max_turns, + }; + + let game = + planetwars::PlanetWarsGame::new(config.create_game(number_of_clients as usize), &generate_string_id(), name); + + let players: Vec = (0..number_of_clients).collect(); + + game::Builder::new(players.clone(), game).with_step_lock( + StepLock::new(players.clone(), pool.clone()) + .with_timeout(std::time::Duration::from_secs(1)), + ) } diff --git a/backend/static/script/maps.js b/backend/static/script/maps.js index b9bb0d7..1ab0459 100644 --- a/backend/static/script/maps.js +++ b/backend/static/script/maps.js @@ -2,6 +2,7 @@ const ids = {}; ["map_holder", "name", "turns", "nop"].forEach(id => ids[id] = document.getElementById(id)); var last_map; +var last_url; async function handle_map_click(url, event) { if (last_map) { @@ -9,10 +10,30 @@ async function handle_map_click(url, event) { } last_map = event.target; event.target.classList.add("selected"); + last_url = url; const c = await fetch(url); ids["map_holder"].innerHTML = await c.text(); } async function start_game() { + const obj = { + "nop": parseInt(ids["nop"].value), + "name": ids["name"].value, + "map": last_url, + "max_turns": parseInt(ids["turns"].value), + }; -} \ No newline at end of file + console.log(obj); + + const xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = async function() { + console.log(this); + console.log(await this.text()); + + // TODO: make response visible + }; + + xhr.open("POST", "/lobby"); + xhr.send(JSON.stringify(obj)); +} diff --git a/frontend/www/games.ts b/frontend/www/games.ts index eb5e647..8e0ae40 100644 --- a/frontend/www/games.ts +++ b/frontend/www/games.ts @@ -10,7 +10,11 @@ const game_location = LOCATION + "static/games/mod.ini"; fetch(game_location) .then((r) => r.text()) .then((response) => { - parse_ini(response); + if (OPTIONS) { + parse_ini(response); + } else { + console.log("Options is not defined, tera?"); + } }).catch(console.error); export function handle(location, name: string) { @@ -29,7 +33,8 @@ function create_option(location: string, name: string, turns: string, players: s const div = document.createElement("div"); div.className = "option"; div.onclick = (_) => handle(location, name); - + console.log("hello there"); + console.log(`"${location}, "${name}"`); let ps = ""; if (players) { diff --git a/frontend/www/index.html b/frontend/www/index.html index d9e0aa5..c2b2c33 100644 --- a/frontend/www/index.html +++ b/frontend/www/index.html @@ -28,7 +28,7 @@ -
+
Option one
@@ -88,5 +88,15 @@ + diff --git a/frontend/www/index.js b/frontend/www/index.js index ec215a4..d5d61f4 100644 --- a/frontend/www/index.js +++ b/frontend/www/index.js @@ -1,15 +1,2 @@ -import { set_game_name, set_instance } from './index.ts' -export { handle } -from './games.ts' // IMPORT GAMES PLEASE, thank you webpack <3 - -const URL = window.location.origin + window.location.pathname; -const LOCATION = URL.substring(0, URL.lastIndexOf("/") + 1); - -const game_location = LOCATION + "static/games/Chandra Garrett.json"; - -fetch(game_location) - .then((r) => r.text()) - .then((response) => { - set_instance(response); - set_game_name("Chandra Garrett"); - }).catch(console.error); \ No newline at end of file +import { } from './index.ts' +export { handle } from './games.ts' // IMPORT GAMES PLEASE, thank you webpack <3 diff --git a/frontend/www/static/res/style.css b/frontend/www/static/res/style.css index 02b1c2a..aadf372 100644 --- a/frontend/www/static/res/style.css +++ b/frontend/www/static/res/style.css @@ -39,7 +39,7 @@ p { width: 96%; } -#options { +.options { background-color: #444; overflow-y: hidden; overflow-x: scroll; @@ -226,4 +226,4 @@ p { border-radius: 10px; background-color: #F90; background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .2) 50%, rgba(255, 255, 255, .2) 75%, transparent 75%, transparent) -} \ No newline at end of file +}