MVP BOY
This commit is contained in:
parent
b650f8d95a
commit
70aabd39e9
9 changed files with 150 additions and 123 deletions
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
target/
|
target/
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
games/
|
||||||
|
|
|
@ -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
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
|
@ -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) => {
|
||||||
|
if (OPTIONS) {
|
||||||
parse_ini(response);
|
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) {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue