diff options
Diffstat (limited to 'planetwars-server/src/routes')
-rw-r--r-- | planetwars-server/src/routes/bots.rs | 32 | ||||
-rw-r--r-- | planetwars-server/src/routes/demo.rs | 44 | ||||
-rw-r--r-- | planetwars-server/src/routes/matches.rs | 113 | ||||
-rw-r--r-- | planetwars-server/src/routes/users.rs | 6 |
4 files changed, 59 insertions, 136 deletions
diff --git a/planetwars-server/src/routes/bots.rs b/planetwars-server/src/routes/bots.rs index 3bbaa1a..9ddb109 100644 --- a/planetwars-server/src/routes/bots.rs +++ b/planetwars-server/src/routes/bots.rs @@ -1,7 +1,7 @@ use axum::extract::{Multipart, Path}; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; -use axum::{body, Json}; +use axum::{body, Extension, Json}; use diesel::OptionalExtension; use rand::distributions::Alphanumeric; use rand::Rng; @@ -9,13 +9,14 @@ use serde::{Deserialize, Serialize}; use serde_json::{self, json, value::Value as JsonValue}; use std::io::Cursor; use std::path::PathBuf; +use std::sync::Arc; use thiserror; -use crate::db::bots::{self, CodeBundle}; -use crate::db::ratings::{RankedBot, self}; +use crate::db::bots::{self, BotVersion}; +use crate::db::ratings::{self, RankedBot}; use crate::db::users::User; -use crate::modules::bots::save_code_bundle; -use crate::{DatabaseConnection, BOTS_DIR}; +use crate::modules::bots::save_code_string; +use crate::{DatabaseConnection, GlobalConfig}; use bots::Bot; #[derive(Serialize, Deserialize, Debug)] @@ -96,6 +97,7 @@ pub async fn save_bot( Json(params): Json<SaveBotParams>, user: User, conn: DatabaseConnection, + Extension(config): Extension<Arc<GlobalConfig>>, ) -> Result<Json<Bot>, SaveBotError> { let res = bots::find_bot_by_name(¶ms.bot_name, &conn) .optional() @@ -119,8 +121,8 @@ pub async fn save_bot( bots::create_bot(&new_bot, &conn).expect("could not create bot") } }; - let _code_bundle = - save_code_bundle(¶ms.code, Some(bot.id), &conn).expect("failed to save code bundle"); + let _code_bundle = save_code_string(¶ms.code, Some(bot.id), &conn, &config) + .expect("failed to save code bundle"); Ok(Json(bot)) } @@ -148,8 +150,8 @@ pub async fn get_bot( Path(bot_id): Path<i32>, ) -> Result<Json<JsonValue>, StatusCode> { let bot = bots::find_bot(bot_id, &conn).map_err(|_| StatusCode::NOT_FOUND)?; - let bundles = bots::find_bot_code_bundles(bot.id, &conn) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let bundles = + bots::find_bot_versions(bot.id, &conn).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(Json(json!({ "bot": bot, "bundles": bundles, @@ -183,8 +185,9 @@ pub async fn upload_code_multipart( user: User, Path(bot_id): Path<i32>, mut multipart: Multipart, -) -> Result<Json<CodeBundle>, StatusCode> { - let bots_dir = PathBuf::from(BOTS_DIR); + Extension(config): Extension<Arc<GlobalConfig>>, +) -> Result<Json<BotVersion>, StatusCode> { + let bots_dir = PathBuf::from(&config.bots_directory); let bot = bots::find_bot(bot_id, &conn).map_err(|_| StatusCode::NOT_FOUND)?; @@ -213,12 +216,13 @@ pub async fn upload_code_multipart( .extract(bots_dir.join(&folder_name)) .map_err(|_| StatusCode::BAD_REQUEST)?; - let bundle = bots::NewCodeBundle { + let bot_version = bots::NewBotVersion { bot_id: Some(bot.id), - path: &folder_name, + code_bundle_path: Some(&folder_name), + container_digest: None, }; let code_bundle = - bots::create_code_bundle(&bundle, &conn).expect("Failed to create code bundle"); + bots::create_bot_version(&bot_version, &conn).expect("Failed to create code bundle"); Ok(Json(code_bundle)) } diff --git a/planetwars-server/src/routes/demo.rs b/planetwars-server/src/routes/demo.rs index 7f7ba71..69838f3 100644 --- a/planetwars-server/src/routes/demo.rs +++ b/planetwars-server/src/routes/demo.rs @@ -1,8 +1,11 @@ +use std::sync::Arc; + use crate::db; use crate::db::matches::{FullMatchData, FullMatchPlayerData}; -use crate::modules::bots::save_code_bundle; -use crate::modules::matches::RunMatch; +use crate::modules::bots::save_code_string; +use crate::modules::matches::{MatchPlayer, RunMatch}; use crate::ConnectionPool; +use crate::GlobalConfig; use axum::extract::Extension; use axum::Json; use hyper::StatusCode; @@ -30,6 +33,7 @@ pub struct SubmitBotResponse { pub async fn submit_bot( Json(params): Json<SubmitBotParams>, Extension(pool): Extension<ConnectionPool>, + Extension(config): Extension<Arc<GlobalConfig>>, ) -> Result<Json<SubmitBotResponse>, StatusCode> { let conn = pool.get().await.expect("could not get database connection"); @@ -37,20 +41,32 @@ pub async fn submit_bot( .opponent_name .unwrap_or_else(|| DEFAULT_OPPONENT_NAME.to_string()); - let opponent = + let opponent_bot = db::bots::find_bot_by_name(&opponent_name, &conn).map_err(|_| StatusCode::BAD_REQUEST)?; - let opponent_code_bundle = - db::bots::active_code_bundle(opponent.id, &conn).map_err(|_| StatusCode::BAD_REQUEST)?; + let opponent_bot_version = db::bots::active_bot_version(opponent_bot.id, &conn) + .map_err(|_| StatusCode::BAD_REQUEST)?; - let player_code_bundle = save_code_bundle(¶ms.code, None, &conn) + let player_bot_version = save_code_string(¶ms.code, None, &conn, &config) // TODO: can we recover from this? .expect("could not save bot code"); - let mut run_match = RunMatch::from_players(vec![&player_code_bundle, &opponent_code_bundle]); - let match_data = run_match - .store_in_database(&conn) - .expect("failed to save match"); - run_match.spawn(pool.clone()); + let run_match = RunMatch::from_players( + config, + vec![ + MatchPlayer::BotVersion { + bot: None, + version: player_bot_version.clone(), + }, + MatchPlayer::BotVersion { + bot: Some(opponent_bot.clone()), + version: opponent_bot_version.clone(), + }, + ], + ); + let (match_data, _) = run_match + .run(pool.clone()) + .await + .expect("failed to run match"); // TODO: avoid clones let full_match_data = FullMatchData { @@ -58,13 +74,13 @@ pub async fn submit_bot( match_players: vec![ FullMatchPlayerData { base: match_data.match_players[0].clone(), - code_bundle: player_code_bundle, + bot_version: Some(player_bot_version), bot: None, }, FullMatchPlayerData { base: match_data.match_players[1].clone(), - code_bundle: opponent_code_bundle, - bot: Some(opponent), + bot_version: Some(opponent_bot_version), + bot: Some(opponent_bot), }, ], }; diff --git a/planetwars-server/src/routes/matches.rs b/planetwars-server/src/routes/matches.rs index b61008d..a980daa 100644 --- a/planetwars-server/src/routes/matches.rs +++ b/planetwars-server/src/routes/matches.rs @@ -1,102 +1,13 @@ -use std::path::PathBuf; - -use axum::{ - extract::{Extension, Path}, - Json, -}; +use axum::{extract::Path, Extension, Json}; use hyper::StatusCode; -use planetwars_matchrunner::{docker_runner::DockerBotSpec, run_match, MatchConfig, MatchPlayer}; -use rand::{distributions::Alphanumeric, Rng}; use serde::{Deserialize, Serialize}; +use std::{path::PathBuf, sync::Arc}; use crate::{ - db::{ - bots, - matches::{self, MatchState}, - users::User, - }, - ConnectionPool, DatabaseConnection, BOTS_DIR, MAPS_DIR, MATCHES_DIR, + db::matches::{self, MatchState}, + DatabaseConnection, GlobalConfig, }; -#[derive(Serialize, Deserialize, Debug)] -pub struct MatchParams { - // Just bot ids for now - players: Vec<i32>, -} - -pub async fn play_match( - _user: User, - Extension(pool): Extension<ConnectionPool>, - Json(params): Json<MatchParams>, -) -> Result<(), StatusCode> { - let conn = pool.get().await.expect("could not get database connection"); - let map_path = PathBuf::from(MAPS_DIR).join("hex.json"); - - let slug: String = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(16) - .map(char::from) - .collect(); - let log_file_name = format!("{}.log", slug); - - let mut players = Vec::new(); - let mut bot_ids = Vec::new(); - for bot_name in params.players { - let bot = bots::find_bot(bot_name, &conn).map_err(|_| StatusCode::BAD_REQUEST)?; - let code_bundle = - bots::active_code_bundle(bot.id, &conn).map_err(|_| StatusCode::BAD_REQUEST)?; - - let bundle_path = PathBuf::from(BOTS_DIR).join(&code_bundle.path); - let bot_config: BotConfig = std::fs::read_to_string(bundle_path.join("botconfig.toml")) - .and_then(|config_str| toml::from_str(&config_str).map_err(|e| e.into())) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - - players.push(MatchPlayer { - bot_spec: Box::new(DockerBotSpec { - code_path: PathBuf::from(BOTS_DIR).join(code_bundle.path), - image: "python:3.10-slim-buster".to_string(), - argv: shlex::split(&bot_config.run_command) - .ok_or(StatusCode::INTERNAL_SERVER_ERROR)?, - }), - }); - - bot_ids.push(matches::MatchPlayerData { - code_bundle_id: code_bundle.id, - }); - } - - let match_config = MatchConfig { - map_name: "hex".to_string(), - map_path, - log_path: PathBuf::from(MATCHES_DIR).join(&log_file_name), - players, - }; - - tokio::spawn(run_match_task( - match_config, - log_file_name, - bot_ids, - pool.clone(), - )); - Ok(()) -} - -async fn run_match_task( - config: MatchConfig, - log_file_name: String, - match_players: Vec<matches::MatchPlayerData>, - pool: ConnectionPool, -) { - let match_data = matches::NewMatch { - state: MatchState::Finished, - log_path: &log_file_name, - }; - - run_match(config).await; - let conn = pool.get().await.expect("could not get database connection"); - matches::create_match(&match_data, &match_players, &conn).expect("could not create match"); -} - #[derive(Serialize, Deserialize)] pub struct ApiMatch { id: i32, @@ -107,7 +18,7 @@ pub struct ApiMatch { #[derive(Serialize, Deserialize)] pub struct ApiMatchPlayer { - code_bundle_id: i32, + bot_version_id: Option<i32>, bot_id: Option<i32>, bot_name: Option<String>, } @@ -127,7 +38,7 @@ pub fn match_data_to_api(data: matches::FullMatchData) -> ApiMatch { .match_players .iter() .map(|_p| ApiMatchPlayer { - code_bundle_id: _p.code_bundle.id, + bot_version_id: _p.bot_version.as_ref().map(|cb| cb.id), bot_id: _p.bot.as_ref().map(|b| b.id), bot_name: _p.bot.as_ref().map(|b| b.name.clone()), }) @@ -135,15 +46,6 @@ pub fn match_data_to_api(data: matches::FullMatchData) -> ApiMatch { } } -// TODO: this is duplicated from planetwars-cli -// clean this up and move to matchrunner crate -#[derive(Serialize, Deserialize)] -pub struct BotConfig { - pub name: String, - pub run_command: String, - pub build_command: Option<String>, -} - pub async fn get_match_data( Path(match_id): Path<i32>, conn: DatabaseConnection, @@ -157,10 +59,11 @@ pub async fn get_match_data( pub async fn get_match_log( Path(match_id): Path<i32>, conn: DatabaseConnection, + Extension(config): Extension<Arc<GlobalConfig>>, ) -> Result<Vec<u8>, StatusCode> { let match_base = matches::find_match_base(match_id, &conn).map_err(|_| StatusCode::NOT_FOUND)?; - let log_path = PathBuf::from(MATCHES_DIR).join(&match_base.log_path); + let log_path = PathBuf::from(&config.match_logs_directory).join(&match_base.log_path); let log_contents = std::fs::read(log_path).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(log_contents) } diff --git a/planetwars-server/src/routes/users.rs b/planetwars-server/src/routes/users.rs index 54ddd09..1989904 100644 --- a/planetwars-server/src/routes/users.rs +++ b/planetwars-server/src/routes/users.rs @@ -5,7 +5,7 @@ use axum::extract::{FromRequest, RequestParts, TypedHeader}; use axum::headers::authorization::Bearer; use axum::headers::Authorization; use axum::http::StatusCode; -use axum::response::{Headers, IntoResponse, Response}; +use axum::response::{IntoResponse, Response}; use axum::{async_trait, Json}; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -163,9 +163,9 @@ pub async fn login(conn: DatabaseConnection, params: Json<LoginParams>) -> Respo Some(user) => { let session = sessions::create_session(&user, &conn); let user_data: UserData = user.into(); - let headers = Headers(vec![("Token", &session.token)]); + let headers = [("Token", &session.token)]; - (headers, Json(user_data)).into_response() + (StatusCode::OK, headers, Json(user_data)).into_response() } } } |