mod validation;
#[allow(unused_imports)]
pub use validation::{validate_godot_version, validate_symlink};
use crate::paths;
use crate::sim::EnvironmentCatalog;
use anyhow::{Context, Result};
use std::path::Path;
use std::process::Command;
pub fn run_simulation(
robot: &str,
env: &str,
project_path: &Path,
headless: bool,
godot_exe: &str,
validate_only: bool,
) -> Result<()> {
println!("🚀 Mecha10 Simulation Runner\n");
println!("Robot: {}", robot);
println!("Environment: {}", env);
println!("Project: {}", project_path.display());
println!("Headless: {}", headless);
if validate_only {
println!("Mode: Validation Only (dry-run)");
}
println!();
println!("🔍 Validating environment...");
let catalog_path = std::path::PathBuf::from(paths::framework::ROBOT_TASKS_CATALOG);
let catalog = EnvironmentCatalog::load(&catalog_path)?;
let env_entry = catalog.environments.iter().find(|e| e.id == env).ok_or_else(|| {
anyhow::anyhow!(
"Environment '{}' not found in catalog.\n\
Run 'mecha10 sim list-envs' to see available environments.",
env
)
})?;
println!(" ✓ Found: {} ({})", env_entry.name, env_entry.id);
let base_path = std::path::PathBuf::from(paths::framework::ROBOT_TASKS_DIR);
let env_path = base_path.join(&env_entry.path);
if !env_path.exists() {
anyhow::bail!(
"Environment '{}' is defined in catalog but not implemented.\n\
Expected path: {}\n\
Run 'mecha10 sim validate-catalog' to see missing environments.",
env,
env_path.display()
);
}
println!(" ✓ Implementation: {}", env_path.display());
println!();
println!("🤖 Checking robot scene...");
let robot_scene_path = project_path.join("robots").join(robot).join("robot.tscn");
if !robot_scene_path.exists() {
println!(" ⚠️ WARNING: Robot scene not found: {}", robot_scene_path.display());
println!(" Generate it with: mecha10 sim generate-robot");
println!(" Continuing anyway (environment may have default robot)...");
} else {
println!(" ✓ Robot scene: {}", robot_scene_path.display());
}
println!();
println!("📁 Validating Godot project...");
if !project_path.exists() {
anyhow::bail!(
"Godot project not found: {}\n\
Make sure you're in the monorepo root directory.",
project_path.display()
);
}
let project_file = project_path.join("project.godot");
if !project_file.exists() {
anyhow::bail!(
"project.godot not found: {}\n\
Expected path: {}",
project_path.display(),
project_file.display()
);
}
println!(" ✓ Project: {}", project_file.display());
println!("\n🔗 Validating simulation symlinks...");
validation::validate_symlink(
project_path,
"components",
"../godot-components",
"Godot components (sensors, actuators, etc.)",
)?;
validation::validate_symlink(
project_path,
"environments",
"../../simulation/environments/robot-tasks",
"Simulation environments",
)?;
let robot_symlink = project_path.join("robots").join(robot);
if robot_symlink.exists() {
if robot_symlink.is_symlink() {
match std::fs::read_link(&robot_symlink) {
Ok(target) => {
let target_path = if target.is_absolute() {
target.clone()
} else {
robot_symlink.parent().unwrap().join(&target)
};
if !target_path.exists() {
anyhow::bail!(
"Robot symlink is broken:\n\
Symlink: {}\n\
Points to: {} (does not exist)\n\
\n\
Fix this by recreating the symlink:\n\
ln -sf <robot-path> {}",
robot_symlink.display(),
target.display(),
robot_symlink.display()
);
}
println!(" ✓ Robot symlink: {} -> {}", robot, target.display());
}
Err(e) => {
anyhow::bail!(
"Failed to read robot symlink: {}\n\
Error: {}",
robot_symlink.display(),
e
);
}
}
} else {
println!(" ✓ Robot directory: {}", robot_symlink.display());
}
} else {
println!(
" ⚠️ Robot path not found: {}\n\
The environment may provide a default robot.",
robot_symlink.display()
);
}
println!();
println!("🔍 Checking Godot version...");
validation::validate_godot_version(godot_exe)?;
println!();
if validate_only {
println!("✅ Validation completed successfully!");
println!("\nAll checks passed:");
println!(" ✓ Environment '{}' exists in catalog", env);
println!(" ✓ Environment implementation found");
println!(" ✓ Godot project structure validated");
println!(" ✓ Required symlinks verified");
println!(" ✓ Godot version check passed");
println!("\nReady to run simulation with:");
println!(" mecha10 sim run --robot {} --env {}", robot, env);
return Ok(());
}
let mut cmd = Command::new(godot_exe);
if headless {
cmd.arg("--headless");
}
cmd.arg("--path");
cmd.arg(project_path);
cmd.arg(format!("--env={}", env));
cmd.arg(format!("--robot={}", robot));
println!("🎮 Launching Godot...\n");
println!("Command: {:?}\n", cmd);
let status = cmd
.status()
.context("Failed to execute Godot. Is Godot installed and in PATH?")?;
if status.success() {
println!("\n✅ Simulation completed successfully!");
} else {
anyhow::bail!("Godot exited with error code: {:?}", status.code());
}
Ok(())
}