From affbd3f82f82a0434afe3974622b0d811484b9fb Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Thu, 22 Jan 2026 22:23:21 -0700 Subject: [PATCH] mistakes were made --- protossbot/src/bot.rs | 6 +++ protossbot/src/main.rs | 8 ++++ protossbot/src/web_server.rs | 93 ++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 protossbot/src/web_server.rs diff --git a/protossbot/src/bot.rs b/protossbot/src/bot.rs index ab76704..bbb4c8c 100644 --- a/protossbot/src/bot.rs +++ b/protossbot/src/bot.rs @@ -38,6 +38,12 @@ impl AiModule for ProtosBot { return; }; + // Apply desired game speed + unsafe { + let game_ptr = game as *const Game as *mut Game; + (*game_ptr).set_local_speed(locked_state.desired_game_speed); + } + build_manager::on_frame(game, &player, &mut locked_state); worker_management::assign_idle_workers_to_minerals(game, &player, &mut locked_state); diff --git a/protossbot/src/main.rs b/protossbot/src/main.rs index e3990e1..d040897 100644 --- a/protossbot/src/main.rs +++ b/protossbot/src/main.rs @@ -1,6 +1,7 @@ mod bot; mod state; mod utils; +mod web_server; use bot::ProtosBot; use state::game_state::GameState; @@ -11,5 +12,12 @@ fn main() { let game_state = Arc::new(Mutex::new(GameState::default())); + // Start web server in a separate thread + let game_state_clone = game_state.clone(); + std::thread::spawn(move || { + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(web_server::start_web_server(game_state_clone)); + }); + rsbwapi::start(move |_game| ProtosBot::new(game_state.clone())); } diff --git a/protossbot/src/web_server.rs b/protossbot/src/web_server.rs new file mode 100644 index 0000000..f641385 --- /dev/null +++ b/protossbot/src/web_server.rs @@ -0,0 +1,93 @@ +use crate::state::game_state::GameState; +use axum::{ + extract::State, + http::StatusCode, + response::IntoResponse, + routing::{get, post}, + Json, Router, +}; +use serde::{Deserialize, Serialize}; +use std::sync::{Arc, Mutex}; +use tower_http::{cors::CorsLayer, services::ServeDir}; + +#[derive(Clone)] +pub struct AppState { + pub game_state: Arc>, +} + +#[derive(Serialize, Deserialize)] +pub struct GameSpeedResponse { + pub speed: i32, +} + +#[derive(Serialize, Deserialize)] +pub struct GameSpeedRequest { + pub speed: i32, +} + +// GET /api/speed - Get current game speed +async fn get_game_speed(State(state): State) -> impl IntoResponse { + let game_state = match state.game_state.lock() { + Ok(gs) => gs, + Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, Json(GameSpeedResponse { speed: 0 })), + }; + + ( + StatusCode::OK, + Json(GameSpeedResponse { + speed: game_state.desired_game_speed, + }), + ) +} + +// POST /api/speed - Set game speed +async fn set_game_speed( + State(state): State, + Json(payload): Json, +) -> impl IntoResponse { + // Validate speed is within reasonable range (0-1000) + if payload.speed < 0 || payload.speed > 1000 { + return ( + StatusCode::BAD_REQUEST, + Json(GameSpeedResponse { speed: 0 }), + ); + } + + let mut game_state = match state.game_state.lock() { + Ok(gs) => gs, + Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, Json(GameSpeedResponse { speed: 0 })), + }; + + game_state.desired_game_speed = payload.speed; + + ( + StatusCode::OK, + Json(GameSpeedResponse { + speed: game_state.desired_game_speed, + }), + ) +} + +pub async fn start_web_server(game_state: Arc>) { + let app_state = AppState { game_state }; + + // Serve static files from the "static" directory + let static_dir = std::env::current_dir() + .unwrap() + .join("static"); + + let app = Router::new() + .route("/api/speed", get(get_game_speed)) + .route("/api/speed", post(set_game_speed)) + .fallback_service(ServeDir::new(static_dir)) + .layer(CorsLayer::permissive()) + .with_state(app_state); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") + .await + .unwrap(); + + println!("Web server running at http://127.0.0.1:3000"); + + axum::serve(listener, app).await.unwrap(); +}