This commit is contained in:
ajuvercr 2020-03-27 18:35:56 +01:00
parent b650f8d95a
commit 70aabd39e9
9 changed files with 150 additions and 123 deletions

1
backend/.gitignore vendored
View file

@ -1,2 +1,3 @@
target/ target/
Cargo.lock Cargo.lock
games/

View file

@ -23,10 +23,8 @@ extern crate ini;
use tracing_subscriber::{EnvFilter, FmtSubscriber}; use tracing_subscriber::{EnvFilter, FmtSubscriber};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::{env, time};
use mozaic::modules::types::*; use mozaic::modules::{game};
use mozaic::modules::{game, StepLock};
use futures::executor::ThreadPool; use futures::executor::ThreadPool;
use futures::future::FutureExt; use futures::future::FutureExt;
@ -66,7 +64,20 @@ 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())); 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(); let mut routes = Vec::new();
routes::fuel(&mut routes); routes::fuel(&mut routes);
@ -75,106 +86,22 @@ fn main() {
engines.tera.register_filter("get_colour", get_colour); engines.tera.register_filter("get_colour", get_colour);
}); });
rocket::ignite() rocket::ignite()
.manage(gm)
.manage(pool)
.attach(tera) .attach(tera)
.mount("/", routes) .mount("/", routes)
.launch() .launch()
.unwrap(); .unwrap();
} }
// // Load the config and start the game. fn create_game_manager(tcp: &str, pool: ThreadPool) -> game::Manager {
// #[async_std::main] let addr = tcp.parse::<SocketAddr>().unwrap();
// async fn main() {
// let args: Vec<String> = 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<planetwars::PlanetWarsGame> {
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<PlayerId> = (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<String>) -> 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::<SocketAddr>().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(|_| ()));
let (gmb, handle) = game::Manager::builder(pool.clone()); let (gmb, handle) = game::Manager::builder(pool.clone());
pool.spawn_ok(handle.map(|_| ()));
let ep = TcpEndpoint::new(addr, pool.clone()); let ep = TcpEndpoint::new(addr, pool.clone());
let gmb = gmb.add_endpoint(ep, "TCP endpoint"); let gmb = gmb.add_endpoint(ep, "TCP endpoint");
let mut gm = gmb.build(); 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(())
} }
// fn print_info(name: &str) {
// println!(
// "Usage: {} map_location [number_of_clients [output [max_turns]]]",
// name
// );
// }

View file

@ -19,22 +19,28 @@ use pw_rules::Dispatch;
pub struct PlanetWarsGame { pub struct PlanetWarsGame {
state: pw_rules::PlanetWars, state: pw_rules::PlanetWars,
planet_map: HashMap<String, usize>, planet_map: HashMap<String, usize>,
log_file_loc: String,
log_file: File, log_file: File,
turns: u64,
name: String,
} }
impl PlanetWarsGame { 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 let planet_map = state
.planets .planets
.iter() .iter()
.map(|p| (p.name.clone(), p.id)) .map(|p| (p.name.clone(), p.id))
.collect(); .collect();
let file = File::create(location).unwrap(); let file = File::create(format!("static/games/{}", location)).unwrap();
Self { Self {
state, state,
planet_map, planet_map,
log_file_loc: location.to_string(),
log_file: file, 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 { impl game::Controller for PlanetWarsGame {
fn start(&mut self) -> Vec<HostMsg> { fn start(&mut self) -> Vec<HostMsg> {
let mut updates = Vec::new(); let mut updates = Vec::new();
@ -167,6 +175,8 @@ impl game::Controller for PlanetWarsGame {
} }
fn step(&mut self, turns: Vec<PlayerMsg>) -> Vec<HostMsg> { fn step(&mut self, turns: Vec<PlayerMsg>) -> Vec<HostMsg> {
self.turns += 1;
let mut updates = Vec::new(); let mut updates = Vec::new();
let alive = self.state.living_players(); let alive = self.state.living_players();
@ -181,6 +191,20 @@ impl game::Controller for PlanetWarsGame {
} }
fn is_done(&mut self) -> bool { 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
}
} }
} }

View file

@ -1,7 +1,7 @@
use serde::{Deserialize}; use serde::{Deserialize};
use rocket::Route; use rocket::{Route, State};
use rocket::response::NamedFile; use rocket::response::NamedFile;
use rocket_contrib::templates::Template; use rocket_contrib::templates::Template;
@ -80,6 +80,58 @@ async fn map_get(file: String) -> Result<Template, String> {
Ok(Template::render("map_partial", &serde_json::from_str::<serde_json::Value>(&content).unwrap())) Ok(Template::render("map_partial", &serde_json::from_str::<serde_json::Value>(&content).unwrap()))
} }
pub fn fuel(routes: &mut Vec<Route>) { #[derive(Deserialize, Debug)]
routes.extend(routes![files, index, map_post, map_get, maps_get, builder_get, visualizer_get]); struct GameReq {
nop: u64,
max_turns: u64,
map: String,
name: String,
}
#[post("/lobby", data="<game_req>")]
async fn game_post(game_req: Json<GameReq>, tp: State<'_, ThreadPool>, gm: State<'_, game::Manager>) -> Result<String, 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;
Ok(format!("{:?}", gm.get_state(game_id.unwrap()).await))
}
pub fn fuel(routes: &mut Vec<Route>) {
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::<String>() + ".json"
}
fn build_builder(
pool: ThreadPool,
number_of_clients: u64,
max_turns: u64,
map: &str,
name: &str,
) -> game::Builder<planetwars::PlanetWarsGame> {
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<PlayerId> = (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)),
)
} }

View file

@ -2,6 +2,7 @@ const ids = {};
["map_holder", "name", "turns", "nop"].forEach(id => ids[id] = document.getElementById(id)); ["map_holder", "name", "turns", "nop"].forEach(id => ids[id] = document.getElementById(id));
var last_map; var last_map;
var last_url;
async function handle_map_click(url, event) { async function handle_map_click(url, event) {
if (last_map) { if (last_map) {
@ -9,10 +10,30 @@ async function handle_map_click(url, event) {
} }
last_map = event.target; last_map = event.target;
event.target.classList.add("selected"); event.target.classList.add("selected");
last_url = url;
const c = await fetch(url); const c = await fetch(url);
ids["map_holder"].innerHTML = await c.text(); ids["map_holder"].innerHTML = await c.text();
} }
async function start_game() { async function start_game() {
const obj = {
"nop": parseInt(ids["nop"].value),
"name": ids["name"].value,
"map": last_url,
"max_turns": parseInt(ids["turns"].value),
};
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));
} }

View file

@ -10,7 +10,11 @@ const game_location = LOCATION + "static/games/mod.ini";
fetch(game_location) fetch(game_location)
.then((r) => r.text()) .then((r) => r.text())
.then((response) => { .then((response) => {
parse_ini(response); if (OPTIONS) {
parse_ini(response);
} else {
console.log("Options is not defined, tera?");
}
}).catch(console.error); }).catch(console.error);
export function handle(location, name: string) { 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"); const div = document.createElement("div");
div.className = "option"; div.className = "option";
div.onclick = (_) => handle(location, name); div.onclick = (_) => handle(location, name);
console.log("hello there");
console.log(`"${location}, "${name}"`);
let ps = ""; let ps = "";
if (players) { if (players) {

View file

@ -28,7 +28,7 @@
</div> </div>
</div> </div>
<div id=options> <div class=".options" id=options>
<div class="option"> <div class="option">
Option one Option one
</div> </div>
@ -88,5 +88,15 @@
<noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript> <noscript>This page contains webassembly and javascript content, please enable javascript in your browser.</noscript>
<script src="bootstrap.js"></script> <script src="bootstrap.js"></script>
<script>
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";
const name = "Chandra Garrett";
window.setTimeout(
() => visualizer.handle(game_location, name), 1000
)
</script>
</body> </body>
</html> </html>

View file

@ -1,15 +1,2 @@
import { set_game_name, set_instance } from './index.ts' import { } from './index.ts'
export { handle } export { handle } from './games.ts' // IMPORT GAMES PLEASE, thank you webpack <3
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);

View file

@ -39,7 +39,7 @@ p {
width: 96%; width: 96%;
} }
#options { .options {
background-color: #444; background-color: #444;
overflow-y: hidden; overflow-y: hidden;
overflow-x: scroll; overflow-x: scroll;