aboutsummaryrefslogtreecommitdiff
path: root/apps/fparkan-headless
diff options
context:
space:
mode:
Diffstat (limited to 'apps/fparkan-headless')
-rw-r--r--apps/fparkan-headless/Cargo.toml14
-rw-r--r--apps/fparkan-headless/src/main.rs267
2 files changed, 0 insertions, 281 deletions
diff --git a/apps/fparkan-headless/Cargo.toml b/apps/fparkan-headless/Cargo.toml
deleted file mode 100644
index 483438b..0000000
--- a/apps/fparkan-headless/Cargo.toml
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-name = "fparkan-headless"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-repository.workspace = true
-
-[dependencies]
-fparkan-runtime = { path = "../../crates/fparkan-runtime", version = "0.1.0" }
-fparkan-vfs = { path = "../../crates/fparkan-vfs", version = "0.1.0" }
-fparkan-world = { path = "../../crates/fparkan-world", version = "0.1.0" }
-
-[lints]
-workspace = true
diff --git a/apps/fparkan-headless/src/main.rs b/apps/fparkan-headless/src/main.rs
deleted file mode 100644
index 9c5ab52..0000000
--- a/apps/fparkan-headless/src/main.rs
+++ /dev/null
@@ -1,267 +0,0 @@
-#![forbid(unsafe_code)]
-#![cfg_attr(
- test,
- allow(
- clippy::cast_possible_truncation,
- clippy::cast_possible_wrap,
- clippy::cast_precision_loss,
- clippy::expect_used,
- clippy::float_cmp,
- clippy::identity_op,
- clippy::too_many_lines,
- clippy::uninlined_format_args,
- clippy::map_unwrap_or,
- clippy::needless_raw_string_hashes,
- clippy::semicolon_if_nothing_returned,
- clippy::type_complexity,
- clippy::panic,
- clippy::unwrap_used
- )
-)]
-#![allow(clippy::print_stderr, clippy::print_stdout)]
-//! `FParkan` headless runtime entrypoint.
-
-use fparkan_runtime::{
- advance_reference_movement, create, load_mission, step_headless, EngineConfig, EngineMode,
- EngineServices, MissionRequest,
-};
-use fparkan_vfs::DirectoryVfs;
-use fparkan_world::{InputSnapshot, OriginalObjectId};
-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<String> = 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={} scripts={} script_events={} script_varset_declarations={} script_init_states={} script_varset_states={} 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.script_bundle_count,
- loaded.script_event_count,
- loaded.script_varset_declaration_count,
- loaded.script_init_state_count,
- loaded.script_varset_state_count,
- loaded.graph_failure_count
- );
- }
- if let Some(movement) = args.reference_movement {
- let reached = advance_reference_movement(
- &mut engine,
- OriginalObjectId(movement.original_id),
- movement.target_xy,
- movement.max_step,
- )
- .map_err(|err| format!("{err}"))?;
- println!(
- "reference_movement original_id={} target_xy=[{},{}] max_step={} reached={reached}",
- movement.original_id, movement.target_xy[0], movement.target_xy[1], movement.max_step,
- );
- }
- 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(())
-}
-
-#[derive(Debug)]
-struct Args {
- root: Option<PathBuf>,
- mission: Option<String>,
- ticks: u64,
- reference_movement: Option<ReferenceMovement>,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq)]
-struct ReferenceMovement {
- original_id: u32,
- target_xy: [f32; 2],
- max_step: f32,
-}
-
-impl Args {
- fn parse(args: &[String]) -> Result<Self, String> {
- let mut parsed = Self {
- root: None,
- mission: None,
- ticks: 1,
- reference_movement: None,
- };
- 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())?;
- }
- "--move-object" => {
- if parsed.reference_movement.is_some() {
- return Err("--move-object may be specified once".to_string());
- }
- let original_id = iter
- .next()
- .ok_or_else(|| "--move-object requires an original object id".to_string())?
- .parse()
- .map_err(|_| {
- "--move-object object id must be an unsigned integer".to_string()
- })?;
- let x = parse_finite_argument(
- iter.next(),
- "--move-object requires a finite X target",
- )?;
- let y = parse_finite_argument(
- iter.next(),
- "--move-object requires a finite Y target",
- )?;
- let max_step = parse_finite_argument(
- iter.next(),
- "--move-object requires a finite positive maximum step",
- )?;
- if max_step <= 0.0 {
- return Err("--move-object maximum step must be positive".to_string());
- }
- parsed.reference_movement = Some(ReferenceMovement {
- original_id,
- target_xy: [x, y],
- max_step,
- });
- }
- _ => return Err(usage()),
- }
- }
- if parsed.mission.is_some() && parsed.root.is_none() {
- return Err("--mission requires --root".to_string());
- }
- if parsed.reference_movement.is_some() && parsed.mission.is_none() {
- return Err("--move-object requires --mission".to_string());
- }
- Ok(parsed)
- }
-}
-
-fn parse_finite_argument(value: Option<&String>, error: &str) -> Result<f32, String> {
- let value: f32 = value
- .ok_or_else(|| error.to_string())?
- .parse()
- .map_err(|_| error.to_string())?;
- value
- .is_finite()
- .then_some(value)
- .ok_or_else(|| error.to_string())
-}
-
-fn usage() -> String {
- "usage: fparkan-headless [--root <path> --mission <path>] [--move-object <original-id> <x> <y> <max-step>] [--ticks <n>]".to_string()
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- fn args(values: &[&str]) -> Vec<String> {
- values.iter().map(ToString::to_string).collect()
- }
-
- #[test]
- fn move_object_parses_a_single_finite_reference_command() {
- let parsed = Args::parse(&args(&[
- "--root",
- "C:/game",
- "--mission",
- "MISSIONS/Autodemo.00/data.tma",
- "--move-object",
- "7",
- "12.5",
- "-3",
- "0.25",
- "--ticks",
- "2",
- ]))
- .expect("args");
- assert_eq!(parsed.ticks, 2);
- assert_eq!(
- parsed.reference_movement,
- Some(ReferenceMovement {
- original_id: 7,
- target_xy: [12.5, -3.0],
- max_step: 0.25,
- })
- );
- }
-
- #[test]
- fn move_object_requires_a_loaded_mission_and_valid_step() {
- assert_eq!(
- Args::parse(&args(&["--move-object", "7", "1", "2", "1"])).expect_err("mission"),
- "--move-object requires --mission"
- );
- assert_eq!(
- Args::parse(&args(&[
- "--root",
- "C:/game",
- "--mission",
- "M/data.tma",
- "--move-object",
- "7",
- "1",
- "2",
- "0",
- ]))
- .expect_err("step"),
- "--move-object maximum step must be positive"
- );
- }
-}