subtr_actor/lib.rs
1#![allow(clippy::result_large_err)]
2
3//! # subtr-actor
4//!
5//! `subtr-actor` turns raw [`boxcars`] replay data into higher-level game
6//! state, derived replay events, structured frame payloads, and dense numeric
7//! features for analytics and ML workflows.
8//!
9//! The Rust crate is the source of truth for the replay-processing pipeline.
10//! The Python and JavaScript bindings build on the same collector APIs and
11//! string-addressable feature registry exposed here.
12//!
13//! ## Processing model
14//!
15//! - [`ReplayProcessor`] walks the replay's network frames, models actor state,
16//! and tracks derived replay events such as touches, boost pad pickups,
17//! dodge refreshes, goals, player stat events, and demolishes.
18//! - [`Collector`] is the core extension point. Collectors observe the replay
19//! frame by frame and can either process every frame or control sampling via
20//! [`TimeAdvance`].
21//! - [`ReplayProcessor::process_all`] lets multiple collectors share a single
22//! replay pass when you want to build several outputs at once.
23//! - [`FrameRateDecorator`] and [`CallbackCollector`] provide lightweight
24//! utilities for downsampling a collector or attaching side-effectful hooks
25//! such as progress reporting and debugging.
26//!
27//! ## Primary output layers
28//!
29//! - [`ReplayDataCollector`] builds a serde-friendly replay payload with frame
30//! data, replay metadata, and derived event streams suitable for JSON export
31//! and playback UIs.
32//! - [`NDArrayCollector`] emits a dense [`::ndarray::Array2`] with replay
33//! metadata and headers. It supports both explicit feature adders and the
34//! string-based registry exposed through [`NDArrayCollector::from_strings`]
35//! and [`NDArrayCollector::from_strings_typed`].
36//! - [`StatsCollector`] accumulates graph-backed replay statistics as a
37//! module-keyed dynamic payload suitable for builtin module selection and
38//! JSON export.
39//! - [`StatsTimelineEventCollector`] accumulates graph-backed replay statistics
40//! as event streams plus lightweight frame scaffolding. This is the preferred
41//! timeline export when callers do not need to serialize full per-frame
42//! partial sums.
43//! - [`StatsTimelineCollector`] preserves the legacy full snapshot timeline
44//! form ([`ReplayStatsTimeline`]) for parity checks and compatibility.
45//!
46//! ## Stats and exports
47//!
48//! The [`stats`] module houses analysis calculators, graph nodes, stat
49//! mechanics helpers, and the exported stat-field model built around
50//! [`ExportedStat`].
51//!
52//! ## Examples
53//!
54//! ### Collect structured replay data
55//!
56//! ```no_run
57//! use boxcars::ParserBuilder;
58//! use subtr_actor::ReplayDataCollector;
59//!
60//! let bytes = std::fs::read("replay.replay").unwrap();
61//! let replay = ParserBuilder::new(&bytes)
62//! .must_parse_network_data()
63//! .on_error_check_crc()
64//! .parse()
65//! .unwrap();
66//!
67//! let replay_data = ReplayDataCollector::new().get_replay_data(&replay).unwrap();
68//! println!("frames: {}", replay_data.frame_data.frame_count());
69//! println!("touches: {}", replay_data.touch_events.len());
70//! ```
71//!
72//! ### Build a sampled feature matrix
73//!
74//! ```no_run
75//! use boxcars::ParserBuilder;
76//! use subtr_actor::{Collector, FrameRateDecorator, NDArrayCollector};
77//!
78//! let bytes = std::fs::read("replay.replay").unwrap();
79//! let replay = ParserBuilder::new(&bytes)
80//! .must_parse_network_data()
81//! .on_error_check_crc()
82//! .parse()
83//! .unwrap();
84//!
85//! let mut collector = NDArrayCollector::<f32>::from_strings(
86//! &["BallRigidBody", "CurrentTime"],
87//! &["PlayerRigidBody", "PlayerBoost", "PlayerAnyJump"],
88//! )
89//! .unwrap();
90//!
91//! FrameRateDecorator::new_from_fps(30.0, &mut collector)
92//! .process_replay(&replay)
93//! .unwrap();
94//!
95//! let (meta, features) = collector.get_meta_and_ndarray().unwrap();
96//! println!("players: {}", meta.replay_meta.player_count());
97//! println!("shape: {:?}", features.raw_dim());
98//! ```
99//!
100//! ### Export compact event-backed stats timeline
101//!
102//! ```no_run
103//! use boxcars::ParserBuilder;
104//! use subtr_actor::StatsTimelineEventCollector;
105//!
106//! let bytes = std::fs::read("replay.replay").unwrap();
107//! let replay = ParserBuilder::new(&bytes)
108//! .must_parse_network_data()
109//! .on_error_check_crc()
110//! .parse()
111//! .unwrap();
112//!
113//! let timeline = StatsTimelineEventCollector::new()
114//! .get_replay_stats_timeline_scaffold(&replay)
115//! .unwrap();
116//!
117//! println!("timeline frames: {}", timeline.frames.len());
118//! println!("rush events: {}", timeline.events.rush.len());
119//! ```
120
121pub mod collector;
122pub mod constants;
123pub mod error;
124pub mod geometry;
125pub mod mechanics;
126pub mod playlist_generation;
127pub mod processor;
128pub mod replay_meta;
129pub mod replay_plausibility;
130pub mod replay_types;
131pub mod search;
132pub mod stats;
133pub mod ts_bindings;
134pub mod vec_map;
135
136#[cfg(test)]
137mod shared_test;
138
139pub mod actor_state {
140 //! Compatibility re-export for processor actor-state types.
141 pub use crate::processor::actor_state::*;
142}
143
144pub use crate::actor_state::*;
145pub use crate::collector::*;
146pub use crate::constants::*;
147pub use crate::error::*;
148pub use crate::geometry::*;
149pub use crate::mechanics::*;
150pub use crate::playlist_generation::*;
151pub use crate::processor::*;
152pub use crate::replay_meta::*;
153pub use crate::replay_plausibility::*;
154pub use crate::replay_types::*;
155pub use crate::search::*;
156pub use crate::stats::*;
157pub(crate) use crate::vec_map::*;