aboutsummaryrefslogtreecommitdiff
path: root/apps/fparkan-headless/src/main.rs
blob: b78a7dc9aea9294f710f41dc6605b7654bb49335 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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<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={} 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<PathBuf>,
    mission: Option<String>,
    ticks: u64,
}

impl Args {
    fn parse(args: &[String]) -> Result<Self, String> {
        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 <path> --mission <path>] [--ticks <n>]".to_string()
}