From d0bdbaa1ed76dfbf3211bb43eee48c49cc4fd448 Mon Sep 17 00:00:00 2001 From: Valentin Popov Date: Mon, 22 Jun 2026 13:12:27 +0400 Subject: feat: implement FParkan architecture foundation Add the modular fparkan workspace, domain crates, adapters, apps, xtask policy/CI, acceptance evidence, and licensed corpus gates for the macOS-focused roadmap foundation. --- apps/fparkan-headless/src/main.rs | 114 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 apps/fparkan-headless/src/main.rs (limited to 'apps/fparkan-headless/src') diff --git a/apps/fparkan-headless/src/main.rs b/apps/fparkan-headless/src/main.rs new file mode 100644 index 0000000..b78a7dc --- /dev/null +++ b/apps/fparkan-headless/src/main.rs @@ -0,0 +1,114 @@ +#![forbid(unsafe_code)] +#![allow(clippy::print_stderr, clippy::print_stdout)] +//! `FParkan` headless runtime entrypoint. + +use fparkan_runtime::{ + create, load_mission, step_headless, EngineConfig, EngineMode, EngineServices, MissionRequest, +}; +use fparkan_vfs::DirectoryVfs; +use fparkan_world::InputSnapshot; +use std::path::PathBuf; +use std::sync::Arc; + +fn main() { + if let Err(err) = run() { + eprintln!("{err}"); + std::process::exit(2); + } +} + +fn run() -> Result<(), String> { + let raw_args: Vec = std::env::args().skip(1).collect(); + let args = Args::parse(&raw_args)?; + let services = if let Some(root) = &args.root { + EngineServices::new(Arc::new(DirectoryVfs::new(root))) + } else { + EngineServices::default() + }; + let mut engine = create( + EngineConfig { + mode: EngineMode::Headless, + }, + services, + ) + .map_err(|err| format!("{err}"))?; + if let Some(mission) = args.mission { + let loaded = load_mission(&mut engine, MissionRequest { key: mission }) + .map_err(|err| format!("{err}"))?; + println!( + "mission objects={} areals={} surfaces={} graph_roots={} components={} wear={} material_slots={} textures={} lightmaps={} graph_failures={}", + loaded.object_count, + loaded.areal_count, + loaded.surface_count, + loaded.graph_root_count, + loaded.graph_unit_component_count, + loaded.graph_wear_resolved_count, + loaded.graph_material_resolved_count, + loaded.graph_texture_resolved_count, + loaded.graph_lightmap_resolved_count, + loaded.graph_failure_count + ); + } + let mut last = None; + for _ in 0..args.ticks { + last = Some(step_headless(&mut engine, InputSnapshot).map_err(|err| format!("{err}"))?); + } + if let Some(frame) = last { + println!( + "tick={} hash={:02x?}", + frame.snapshot.tick.0, frame.snapshot.hash.0 + ); + } + Ok(()) +} + +struct Args { + root: Option, + mission: Option, + ticks: u64, +} + +impl Args { + fn parse(args: &[String]) -> Result { + let mut parsed = Self { + root: None, + mission: None, + ticks: 1, + }; + let mut iter = args.iter(); + while let Some(arg) = iter.next() { + match arg.as_str() { + "--root" => { + parsed.root = Some( + iter.next() + .map(PathBuf::from) + .ok_or_else(|| "--root requires a path".to_string())?, + ); + } + "--mission" => { + parsed.mission = Some( + iter.next() + .cloned() + .ok_or_else(|| "--mission requires a path".to_string())?, + ); + } + "--ticks" => { + parsed.ticks = iter + .next() + .ok_or_else(|| "--ticks requires a value".to_string())? + .parse() + .map_err(|_| "--ticks must be an integer".to_string())?; + } + _ => return Err(usage()), + } + } + if parsed.mission.is_some() && parsed.root.is_none() { + return Err("--mission requires --root".to_string()); + } + Ok(parsed) + } +} + +fn usage() -> String { + "usage: fparkan-headless [--root --mission ] [--ticks ]".to_string() +} -- cgit v1.2.3