aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--planetwars-cli/Cargo.toml1
-rw-r--r--planetwars-cli/assets/pw_workspace.toml12
-rw-r--r--planetwars-cli/assets/simplebot/botconfig.toml2
-rw-r--r--planetwars-cli/assets/simplebot/simplebot.py (renamed from planetwars-cli/assets/simplebot.py)0
-rw-r--r--planetwars-cli/src/commands/init.rs4
-rw-r--r--planetwars-cli/src/commands/run_match.rs42
-rw-r--r--planetwars-cli/src/commands/serve.rs7
-rw-r--r--planetwars-cli/src/lib.rs27
-rw-r--r--planetwars-cli/src/match_runner/bot_runner.rs3
-rw-r--r--planetwars-cli/src/match_runner/mod.rs14
-rw-r--r--planetwars-cli/src/web/mod.rs18
-rw-r--r--planetwars-cli/src/workspace/bot.rs40
-rw-r--r--planetwars-cli/src/workspace/mod.rs77
13 files changed, 162 insertions, 85 deletions
diff --git a/planetwars-cli/Cargo.toml b/planetwars-cli/Cargo.toml
index 25b6e74..e1f0a8e 100644
--- a/planetwars-cli/Cargo.toml
+++ b/planetwars-cli/Cargo.toml
@@ -18,6 +18,7 @@ toml = "0.5"
planetwars-rules = { path = "../planetwars-rules" }
clap = { version = "3.0.0-rc.8", features = ["derive"] }
chrono = { version = "0.4", features = ["serde"] }
+shlex = "1.1"
rust-embed = "6.3.0"
axum = { version = "0.4", features = ["ws"] }
diff --git a/planetwars-cli/assets/pw_workspace.toml b/planetwars-cli/assets/pw_workspace.toml
index 85a4ab6..d82840f 100644
--- a/planetwars-cli/assets/pw_workspace.toml
+++ b/planetwars-cli/assets/pw_workspace.toml
@@ -1,10 +1,6 @@
-[bots]
+[paths]
+maps_dir = "maps"
+matches_dir = "matches"
-# define a bot called simplebot
[bots.simplebot]
-
-# The working directory for the bot.
-path = "./bots/simplebot"
-
-# What command to use for running the bot
-argv = ["python", "simplebot.py"] \ No newline at end of file
+path = "bots/simplebot"
diff --git a/planetwars-cli/assets/simplebot/botconfig.toml b/planetwars-cli/assets/simplebot/botconfig.toml
new file mode 100644
index 0000000..b3a4163
--- /dev/null
+++ b/planetwars-cli/assets/simplebot/botconfig.toml
@@ -0,0 +1,2 @@
+name = "simplebot"
+run_command = "python3 simplebot.py" \ No newline at end of file
diff --git a/planetwars-cli/assets/simplebot.py b/planetwars-cli/assets/simplebot/simplebot.py
index b2a6b8f..b2a6b8f 100644
--- a/planetwars-cli/assets/simplebot.py
+++ b/planetwars-cli/assets/simplebot/simplebot.py
diff --git a/planetwars-cli/src/commands/init.rs b/planetwars-cli/src/commands/init.rs
index 3c9cf08..c95626b 100644
--- a/planetwars-cli/src/commands/init.rs
+++ b/planetwars-cli/src/commands/init.rs
@@ -31,8 +31,8 @@ impl InitCommand {
// create files
copy_asset!(path, "pw_workspace.toml");
copy_asset!(path.join("maps"), "hex.json");
- copy_asset!(path.join("bots/simplebot"), "simplebot.py");
-
+ copy_asset!(path.join("bots/"), "simplebot/botconfig.toml");
+ copy_asset!(path.join("bots/"), "simplebot/simplebot.py");
Ok(())
}
}
diff --git a/planetwars-cli/src/commands/run_match.rs b/planetwars-cli/src/commands/run_match.rs
index 039d89a..5b7a3ec 100644
--- a/planetwars-cli/src/commands/run_match.rs
+++ b/planetwars-cli/src/commands/run_match.rs
@@ -1,14 +1,10 @@
-use std::env;
use std::io;
use clap::Parser;
-use crate::match_runner;
-use crate::match_runner::MatchBot;
use crate::match_runner::MatchConfig;
-use crate::resolve_bot_config;
-use crate::WorkspaceConfig;
-
+use crate::match_runner::{self, MatchPlayer};
+use crate::workspace::Workspace;
#[derive(Parser)]
pub struct RunMatchCommand {
/// map name
@@ -19,30 +15,20 @@ pub struct RunMatchCommand {
impl RunMatchCommand {
pub async fn run(self) -> io::Result<()> {
- let workspace_root = env::current_dir().unwrap();
-
- let config_path = workspace_root.join("pw_workspace.toml");
-
- let map_path = workspace_root.join(format!("maps/{}.json", self.map));
+ let workspace = Workspace::open_current_dir()?;
+ let map_path = workspace.maps_dir().join(format!("{}.json", &self.map));
let timestamp = chrono::Local::now().format("%Y-%m-%d-%H-%M-%S");
- let log_path = workspace_root.join(format!("matches/{}.log", timestamp));
-
- let config_str = std::fs::read_to_string(config_path).unwrap();
- let workspace_config: WorkspaceConfig = toml::from_str(&config_str).unwrap();
-
- let players = self
- .bots
- .into_iter()
- .map(|bot_name| {
- let bot_config = workspace_config.bots.get(&bot_name).unwrap().clone();
- let resolved_config = resolve_bot_config(&workspace_root, bot_config);
- MatchBot {
- name: bot_name,
- bot_config: resolved_config,
- }
- })
- .collect();
+ let log_path = workspace.match_path(&format!("{}-{}", &self.map, &timestamp));
+
+ let mut players = Vec::new();
+ for bot_name in &self.bots {
+ let bot = workspace.get_bot(&bot_name)?;
+ players.push(MatchPlayer {
+ name: bot_name.clone(),
+ bot,
+ });
+ }
let match_config = MatchConfig {
map_name: self.map,
diff --git a/planetwars-cli/src/commands/serve.rs b/planetwars-cli/src/commands/serve.rs
index a078c83..aa8d149 100644
--- a/planetwars-cli/src/commands/serve.rs
+++ b/planetwars-cli/src/commands/serve.rs
@@ -1,18 +1,17 @@
-use std::env;
use std::io;
use clap::Parser;
use crate::web;
+use crate::workspace::Workspace;
#[derive(Parser)]
pub struct ServeCommand;
impl ServeCommand {
pub async fn run(self) -> io::Result<()> {
- let workspace_root = env::current_dir().unwrap();
-
- web::run(workspace_root).await;
+ let workspace = Workspace::open_current_dir()?;
+ web::run(workspace).await;
Ok(())
}
}
diff --git a/planetwars-cli/src/lib.rs b/planetwars-cli/src/lib.rs
index b71164f..e5566b0 100644
--- a/planetwars-cli/src/lib.rs
+++ b/planetwars-cli/src/lib.rs
@@ -1,23 +1,7 @@
-use serde::Deserialize;
-
mod commands;
mod match_runner;
mod web;
-
-use serde::Serialize;
-use std::collections::HashMap;
-use std::path::{Path, PathBuf};
-
-#[derive(Serialize, Deserialize, Debug)]
-struct WorkspaceConfig {
- bots: HashMap<String, BotConfig>,
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct BotConfig {
- path: String,
- argv: Vec<String>,
-}
+mod workspace;
pub async fn run() {
let res = commands::Cli::run().await;
@@ -26,12 +10,3 @@ pub async fn run() {
std::process::exit(1);
}
}
-
-fn resolve_bot_config(workspace_dir: &Path, config: BotConfig) -> BotConfig {
- let mut path = PathBuf::from(workspace_dir);
- path.push(&config.path);
- BotConfig {
- path: path.to_str().unwrap().to_string(),
- argv: config.argv,
- }
-}
diff --git a/planetwars-cli/src/match_runner/bot_runner.rs b/planetwars-cli/src/match_runner/bot_runner.rs
index 290df07..70fc060 100644
--- a/planetwars-cli/src/match_runner/bot_runner.rs
+++ b/planetwars-cli/src/match_runner/bot_runner.rs
@@ -1,4 +1,5 @@
use std::io;
+use std::path::PathBuf;
use std::process::Stdio;
use std::sync::Arc;
use std::sync::Mutex;
@@ -75,7 +76,7 @@ impl LocalBotRunner {
#[derive(Debug, Clone)]
pub struct Bot {
- pub working_dir: String,
+ pub working_dir: PathBuf,
pub argv: Vec<String>,
}
diff --git a/planetwars-cli/src/match_runner/mod.rs b/planetwars-cli/src/match_runner/mod.rs
index 50b7a3b..fdd02d5 100644
--- a/planetwars-cli/src/match_runner/mod.rs
+++ b/planetwars-cli/src/match_runner/mod.rs
@@ -12,7 +12,7 @@ use match_context::MatchCtx;
use planetwars_rules::PwConfig;
use serde::{Deserialize, Serialize};
-use crate::BotConfig;
+use crate::workspace::bot::WorkspaceBot;
use self::match_context::{EventBus, PlayerHandle};
@@ -20,7 +20,7 @@ pub struct MatchConfig {
pub map_name: String,
pub map_path: PathBuf,
pub log_path: PathBuf,
- pub players: Vec<MatchBot>,
+ pub players: Vec<MatchPlayer>,
}
#[derive(Serialize, Deserialize)]
@@ -35,9 +35,9 @@ pub struct PlayerInfo {
pub name: String,
}
-pub struct MatchBot {
+pub struct MatchPlayer {
pub name: String,
- pub bot_config: BotConfig,
+ pub bot: WorkspaceBot,
}
pub async fn run_match(config: MatchConfig) {
@@ -53,11 +53,11 @@ pub async fn run_match(config: MatchConfig) {
.players
.iter()
.enumerate()
- .map(|(player_id, bot)| {
+ .map(|(player_id, player)| {
let player_id = (player_id + 1) as u32;
let bot = bot_runner::Bot {
- working_dir: bot.bot_config.path.clone(),
- argv: bot.bot_config.argv.clone(),
+ working_dir: player.bot.path.clone(),
+ argv: player.bot.config.get_run_argv(),
};
let handle = bot_runner::run_local_bot(player_id, event_bus.clone(), bot);
(player_id, Box::new(handle) as Box<dyn PlayerHandle>)
diff --git a/planetwars-cli/src/web/mod.rs b/planetwars-cli/src/web/mod.rs
index 8394897..676d7af 100644
--- a/planetwars-cli/src/web/mod.rs
+++ b/planetwars-cli/src/web/mod.rs
@@ -18,20 +18,20 @@ use std::{
sync::Arc,
};
-use crate::match_runner::MatchMeta;
+use crate::{match_runner::MatchMeta, workspace::Workspace};
struct State {
- workspace_root: PathBuf,
+ workspace: Workspace,
}
impl State {
- fn new(workspace_root: PathBuf) -> Self {
- Self { workspace_root }
+ fn new(workspace: Workspace) -> Self {
+ Self { workspace }
}
}
-pub async fn run(workspace_root: PathBuf) {
- let shared_state = Arc::new(State::new(workspace_root));
+pub async fn run(workspace: Workspace) {
+ let shared_state = Arc::new(State::new(workspace));
// build our application with a route
let app = Router::new()
@@ -80,8 +80,8 @@ struct MatchData {
async fn list_matches(Extension(state): Extension<Arc<State>>) -> Json<Vec<MatchData>> {
let matches = state
- .workspace_root
- .join("matches")
+ .workspace
+ .matches_dir()
.read_dir()
.unwrap()
.filter_map(|entry| {
@@ -125,7 +125,7 @@ fn read_match_meta(path: &path::Path) -> io::Result<MatchMeta> {
}
async fn get_match(Extension(state): Extension<Arc<State>>, Path(id): Path<String>) -> String {
- let mut match_path = state.workspace_root.join("matches").join(id);
+ let mut match_path = state.workspace.matches_dir().join(id);
match_path.set_extension("log");
fs::read_to_string(match_path).unwrap()
}
diff --git a/planetwars-cli/src/workspace/bot.rs b/planetwars-cli/src/workspace/bot.rs
new file mode 100644
index 0000000..3cd4f87
--- /dev/null
+++ b/planetwars-cli/src/workspace/bot.rs
@@ -0,0 +1,40 @@
+use shlex;
+use std::fs;
+use std::io;
+use std::path::{Path, PathBuf};
+
+use serde::{Deserialize, Serialize};
+
+const BOT_CONFIG_FILENAME: &str = "botconfig.toml";
+
+pub struct WorkspaceBot {
+ pub path: PathBuf,
+ pub config: BotConfig,
+}
+
+impl WorkspaceBot {
+ pub fn open(path: &Path) -> io::Result<Self> {
+ let config_path = path.join(BOT_CONFIG_FILENAME);
+ let config_str = fs::read_to_string(config_path)?;
+ let bot_config: BotConfig = toml::from_str(&config_str)?;
+
+ Ok(WorkspaceBot {
+ path: path.to_path_buf(),
+ config: bot_config,
+ })
+ }
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct BotConfig {
+ pub name: String,
+ pub run_command: String,
+}
+
+impl BotConfig {
+ pub fn get_run_argv(&self) -> Vec<String> {
+ // TODO: proper error handling
+ shlex::split(&self.run_command)
+ .expect("Failed to parse bot run command. Check for unterminated quotes.")
+ }
+}
diff --git a/planetwars-cli/src/workspace/mod.rs b/planetwars-cli/src/workspace/mod.rs
new file mode 100644
index 0000000..64777c2
--- /dev/null
+++ b/planetwars-cli/src/workspace/mod.rs
@@ -0,0 +1,77 @@
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+use std::env;
+use std::fs;
+use std::io;
+use std::path::{Path, PathBuf};
+
+use self::bot::WorkspaceBot;
+
+const WORKSPACE_CONFIG_FILENAME: &str = "pw_workspace.toml";
+
+pub mod bot;
+
+pub struct Workspace {
+ root_path: PathBuf,
+ config: WorkspaceConfig,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WorkspaceConfig {
+ paths: WorkspacePaths,
+ bots: HashMap<String, BotEntry>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WorkspacePaths {
+ maps_dir: PathBuf,
+ matches_dir: PathBuf,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct BotEntry {
+ path: PathBuf,
+}
+
+impl Workspace {
+ pub fn open(root_path: &Path) -> io::Result<Workspace> {
+ let config_path = root_path.join(WORKSPACE_CONFIG_FILENAME);
+ let config_str = fs::read_to_string(config_path)?;
+ let workspace_config: WorkspaceConfig = toml::from_str(&config_str)?;
+
+ Ok(Workspace {
+ root_path: root_path.to_path_buf(),
+ config: workspace_config,
+ })
+ }
+
+ pub fn open_current_dir() -> io::Result<Workspace> {
+ Workspace::open(&env::current_dir()?)
+ }
+
+ pub fn maps_dir(&self) -> PathBuf {
+ self.root_path.join(&self.config.paths.maps_dir)
+ }
+
+ pub fn map_path(&self, map_name: &str) -> PathBuf {
+ self.maps_dir().join(format!("{}.json", map_name))
+ }
+
+ pub fn matches_dir(&self) -> PathBuf {
+ self.root_path.join(&self.config.paths.matches_dir)
+ }
+
+ pub fn match_path(&self, match_name: &str) -> PathBuf {
+ self.matches_dir().join(format!("{}.log", match_name))
+ }
+
+ pub fn get_bot(&self, bot_name: &str) -> io::Result<WorkspaceBot> {
+ let bot_entry = self.config.bots.get(bot_name).ok_or_else(|| {
+ io::Error::new(
+ io::ErrorKind::NotFound,
+ format!("no such bot: {}", bot_name),
+ )
+ })?;
+ WorkspaceBot::open(&self.root_path.join(&bot_entry.path))
+ }
+}