Skip to main content

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//! - [`StatsTimelineCollector`] accumulates graph-backed replay statistics
40//!   frame by frame and returns typed snapshots ([`ReplayStatsTimeline`]) for
41//!   the builtin analysis modules.
42//!
43//! ## Stats and exports
44//!
45//! The [`stats`] module houses analysis calculators, graph nodes, stat
46//! mechanics helpers, and the exported stat-field model built around
47//! [`ExportedStat`].
48//!
49//! ## Examples
50//!
51//! ### Collect structured replay data
52//!
53//! ```no_run
54//! use boxcars::ParserBuilder;
55//! use subtr_actor::ReplayDataCollector;
56//!
57//! let bytes = std::fs::read("replay.replay").unwrap();
58//! let replay = ParserBuilder::new(&bytes)
59//!     .must_parse_network_data()
60//!     .on_error_check_crc()
61//!     .parse()
62//!     .unwrap();
63//!
64//! let replay_data = ReplayDataCollector::new().get_replay_data(&replay).unwrap();
65//! println!("frames: {}", replay_data.frame_data.frame_count());
66//! println!("touches: {}", replay_data.touch_events.len());
67//! ```
68//!
69//! ### Build a sampled feature matrix
70//!
71//! ```no_run
72//! use boxcars::ParserBuilder;
73//! use subtr_actor::{Collector, FrameRateDecorator, NDArrayCollector};
74//!
75//! let bytes = std::fs::read("replay.replay").unwrap();
76//! let replay = ParserBuilder::new(&bytes)
77//!     .must_parse_network_data()
78//!     .on_error_check_crc()
79//!     .parse()
80//!     .unwrap();
81//!
82//! let mut collector = NDArrayCollector::<f32>::from_strings(
83//!     &["BallRigidBody", "CurrentTime"],
84//!     &["PlayerRigidBody", "PlayerBoost", "PlayerAnyJump"],
85//! )
86//! .unwrap();
87//!
88//! FrameRateDecorator::new_from_fps(30.0, &mut collector)
89//!     .process_replay(&replay)
90//!     .unwrap();
91//!
92//! let (meta, features) = collector.get_meta_and_ndarray().unwrap();
93//! println!("players: {}", meta.replay_meta.player_count());
94//! println!("shape: {:?}", features.raw_dim());
95//! ```
96//!
97//! ### Export typed stats timeline snapshots
98//!
99//! ```no_run
100//! use boxcars::ParserBuilder;
101//! use subtr_actor::StatsTimelineCollector;
102//!
103//! let bytes = std::fs::read("replay.replay").unwrap();
104//! let replay = ParserBuilder::new(&bytes)
105//!     .must_parse_network_data()
106//!     .on_error_check_crc()
107//!     .parse()
108//!     .unwrap();
109//!
110//! let timeline = StatsTimelineCollector::new().get_replay_data(&replay).unwrap();
111//!
112//! println!("timeline frames: {}", timeline.frames.len());
113//! println!("rush events: {}", timeline.events.rush.len());
114//! ```
115
116pub mod ballchasing;
117pub mod collector;
118pub mod constants;
119pub mod error;
120pub mod geometry;
121pub mod mechanics;
122pub mod processor;
123pub mod replay_meta;
124pub mod replay_plausibility;
125pub mod replay_types;
126pub mod search;
127pub mod stats;
128pub mod ts_bindings;
129pub mod vec_map;
130
131#[cfg(test)]
132mod shared_test;
133
134pub mod actor_state {
135    //! Compatibility re-export for processor actor-state types.
136    pub use crate::processor::actor_state::*;
137}
138
139pub use crate::actor_state::*;
140pub use crate::collector::*;
141pub use crate::constants::*;
142pub use crate::error::*;
143pub use crate::geometry::*;
144pub use crate::mechanics::*;
145pub use crate::processor::*;
146pub use crate::replay_meta::*;
147pub use crate::replay_plausibility::*;
148pub use crate::replay_types::*;
149pub use crate::search::*;
150pub use crate::stats::*;
151pub(crate) use crate::vec_map::*;