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//! - [`StatsTimelineCollector`] accumulates reducer-based replay statistics
37//!   frame by frame and can return either typed snapshots
38//!   ([`ReplayStatsTimeline`]) or a dynamic stat-field representation
39//!   ([`DynamicReplayStatsTimeline`]).
40//!
41//! ## Stats and exports
42//!
43//! The [`stats`] module houses reducer implementations, stat mechanics helpers,
44//! and the exported stat-field model built around [`ExportedStat`]. The same
45//! export layer is re-exported from [`crate::stats_export`] for compatibility
46//! with older import paths.
47//!
48//! ## Examples
49//!
50//! ### Collect structured replay data
51//!
52//! ```no_run
53//! use boxcars::ParserBuilder;
54//! use subtr_actor::ReplayDataCollector;
55//!
56//! let bytes = std::fs::read("replay.replay").unwrap();
57//! let replay = ParserBuilder::new(&bytes)
58//!     .must_parse_network_data()
59//!     .on_error_check_crc()
60//!     .parse()
61//!     .unwrap();
62//!
63//! let replay_data = ReplayDataCollector::new().get_replay_data(&replay).unwrap();
64//! println!("frames: {}", replay_data.frame_data.frame_count());
65//! println!("touches: {}", replay_data.touch_events.len());
66//! ```
67//!
68//! ### Build a sampled feature matrix
69//!
70//! ```no_run
71//! use boxcars::ParserBuilder;
72//! use subtr_actor::{Collector, FrameRateDecorator, NDArrayCollector};
73//!
74//! let bytes = std::fs::read("replay.replay").unwrap();
75//! let replay = ParserBuilder::new(&bytes)
76//!     .must_parse_network_data()
77//!     .on_error_check_crc()
78//!     .parse()
79//!     .unwrap();
80//!
81//! let mut collector = NDArrayCollector::<f32>::from_strings(
82//!     &["BallRigidBody", "CurrentTime"],
83//!     &["PlayerRigidBody", "PlayerBoost", "PlayerAnyJump"],
84//! )
85//! .unwrap();
86//!
87//! FrameRateDecorator::new_from_fps(30.0, &mut collector)
88//!     .process_replay(&replay)
89//!     .unwrap();
90//!
91//! let (meta, features) = collector.get_meta_and_ndarray().unwrap();
92//! println!("players: {}", meta.replay_meta.player_count());
93//! println!("shape: {:?}", features.raw_dim());
94//! ```
95//!
96//! ### Export dynamic stats timeline snapshots
97//!
98//! ```no_run
99//! use boxcars::ParserBuilder;
100//! use subtr_actor::StatsTimelineCollector;
101//!
102//! let bytes = std::fs::read("replay.replay").unwrap();
103//! let replay = ParserBuilder::new(&bytes)
104//!     .must_parse_network_data()
105//!     .on_error_check_crc()
106//!     .parse()
107//!     .unwrap();
108//!
109//! let timeline = StatsTimelineCollector::new()
110//!     .get_dynamic_replay_data(&replay)
111//!     .unwrap();
112//!
113//! println!("timeline frames: {}", timeline.frames.len());
114//! println!("rush events: {}", timeline.rush_events.len());
115//! ```
116
117pub mod actor_state;
118pub mod ballchasing;
119pub mod collector;
120pub mod constants;
121pub mod error;
122pub mod mechanics;
123pub mod processor;
124pub mod stats;
125pub mod stats_export;
126pub mod util;
127
128#[cfg(test)]
129mod util_test;
130
131pub use crate::actor_state::*;
132pub use crate::collector::*;
133pub use crate::constants::*;
134pub use crate::error::*;
135pub use crate::mechanics::*;
136pub use crate::processor::*;
137pub use crate::stats::*;
138pub use crate::util::*;