Skip to main content

subtr_actor/
lib.rs

1#![allow(clippy::result_large_err)]
2
3//! `subtr-actor` turns raw [`boxcars`](https://docs.rs/boxcars) replay data into
4//! higher-level game state, derived replay events, structured frame payloads, and
5//! dense numeric features for analytics and ML workflows.
6//!
7//! - **Higher-level game state** modeled from the raw actor graph
8//! - **Frame-by-frame structured data** ready for JSON export and playback UIs
9//! - **Dense numeric feature matrices** for ML, built from a string-addressable
10//!   feature registry
11//! - **Derived events and cumulative stats** — touches, boost pickups, dodge
12//!   refreshes, goals, demolishes, and more
13//! - **One pipeline, three languages** — the same Rust core drives the Python and
14//!   JavaScript/WASM bindings
15//!
16//! ## Processing model
17//!
18//! - `ReplayProcessor` walks the replay's network frames, models actor state,
19//!   and tracks derived replay events such as touches, boost pad pickups,
20//!   dodge refreshes, goals, player stat events, and demolishes.
21//! - `Collector` is the core extension point. Collectors observe the replay
22//!   frame by frame and can either process every frame or control sampling via
23//!   `TimeAdvance`.
24//! - `ReplayProcessor::process_all` lets multiple collectors share a single
25//!   replay pass when you want to build several outputs at once.
26//! - `FrameRateDecorator` and `CallbackCollector` provide lightweight
27//!   utilities for downsampling a collector or attaching side-effectful hooks
28//!   such as progress reporting and debugging.
29//!
30//! ## Primary output layers
31//!
32//! - `ReplayDataCollector` builds a serde-friendly replay payload with frame
33//!   data, replay metadata, and derived event streams suitable for JSON export
34//!   and playback UIs.
35//! - `NDArrayCollector` emits a dense `ndarray::Array2` with replay
36//!   metadata and headers. It supports both explicit feature adders and the
37//!   string-based registry exposed through `NDArrayCollector::from_strings`
38//!   and `NDArrayCollector::from_strings_typed`.
39//! - `StatsCollector` accumulates graph-backed replay statistics as a
40//!   module-keyed dynamic payload suitable for builtin module selection and
41//!   JSON export.
42//! - `StatsTimelineEventCollector` accumulates graph-backed replay statistics
43//!   as event streams plus lightweight frame scaffolding. This is the preferred
44//!   timeline export when callers do not need to serialize full per-frame
45//!   partial sums.
46//! - `StatsTimelineCollector` preserves the legacy full snapshot timeline
47//!   form (`ReplayStatsTimeline`) for parity checks and compatibility.
48//!
49//! ## Stats and exports
50//!
51//! The `stats` module houses analysis calculators, graph nodes, stat
52//! event calculators, and the labeled stat-aggregation types
53//! (`LabeledCounts`, `LabeledFloatSums`) consumed by the stats collectors.
54//!
55//! ## Architecture / module map
56//!
57//! Read top-down — each module's own documentation expands on the summary
58//! here and links to the collections of implementations it contains.
59//!
60//! - [`processor`] — the replay-walking core. [`ReplayProcessor`] models actor
61//!   state from `boxcars` network frames and tracks derived events, applying a
62//!   sequence of per-frame state updaters.
63//! - [`collector`] — the output layer. The [`Collector`] trait is the extension
64//!   point; built-in collectors are [`ReplayDataCollector`] (structured frames),
65//!   [`NDArrayCollector`] ([numeric features][collector::ndarray]), and the
66//!   stats-timeline collectors ([`collector::stats`]).
67//! - [`stats`] — the analysis layer. A dependency graph of
68//!   [analysis nodes][stats::analysis_graph] wraps
69//!   [gameplay-event calculators][StatsEvent] that detect mechanics; results
70//!   land in accumulators and the [exported stat-field model][stats::export].
71//! - [`replay_model`] / [`replay_meta`] — the serde-friendly higher-level game
72//!   state and replay metadata produced for export and playback UIs.
73//! - [`interop`] — bindings-facing helpers shared by the Python and
74//!   JavaScript/WASM wrappers (e.g. the replay-player manifest).
75//! - [`util`] — geometry, search, and small data-structure helpers used
76//!   throughout the crate.
77//!
78//! ## Where to find collections of implementations
79//!
80//! Several parts of the crate are large families of similar types. Each has a
81//! catalog in its module documentation, and the shared trait's *Implementors*
82//! list is a second way to browse them:
83//!
84//! | Collection | Module | Shared trait / registry |
85//! |---|---|---|
86//! | Gameplay-event calculators | [`stats::analysis_graph`] | [`StatsEvent`] |
87//! | Analysis-graph nodes | [`stats::analysis_graph`] | [`AnalysisNode`](stats::analysis_graph::AnalysisNode) |
88//! | Stat accumulators | [`stats::accumulators`] | (plain accumulation structs) |
89//! | Exported stat fields | [`stats::export`] | [`StatFieldProvider`] |
90//! | NDArray feature adders | [`collector::ndarray`] | [`FeatureAdder`] family + string registry |
91//! | Processor state updaters | [`processor`] | (`impl ReplayProcessor` methods) |
92//!
93//! ## In-depth guides
94//!
95//! Longer prose guides are rendered into the API docs under [`guides`]:
96//!
97//! - [`guides::calculators_and_analysis_nodes`] — the stats runtime DAG layout.
98//! - [`guides::stat_confidence`] — how to read exported-stat confidence levels.
99//! - [`guides::replay_format_evolution`] — replay-format changes that matter
100//!   to parsing.
101//!
102//! ## Examples
103//!
104//! ### Collect structured replay data
105//!
106//! ```no_run
107//! use boxcars::ParserBuilder;
108//! use subtr_actor::ReplayDataCollector;
109//!
110//! let bytes = std::fs::read("replay.replay").unwrap();
111//! let replay = ParserBuilder::new(&bytes)
112//!     .must_parse_network_data()
113//!     .on_error_check_crc()
114//!     .parse()
115//!     .unwrap();
116//!
117//! let replay_data = ReplayDataCollector::new().get_replay_data(&replay).unwrap();
118//! println!("frames: {}", replay_data.frame_data.frame_count());
119//! println!("touches: {}", replay_data.touch_events.len());
120//! ```
121//!
122//! ### Build a sampled feature matrix
123//!
124//! ```no_run
125//! use boxcars::ParserBuilder;
126//! use subtr_actor::{Collector, FrameRateDecorator, NDArrayCollector};
127//!
128//! let bytes = std::fs::read("replay.replay").unwrap();
129//! let replay = ParserBuilder::new(&bytes)
130//!     .must_parse_network_data()
131//!     .on_error_check_crc()
132//!     .parse()
133//!     .unwrap();
134//!
135//! let mut collector = NDArrayCollector::<f32>::from_strings(
136//!     &["BallRigidBody", "CurrentTime"],
137//!     &["PlayerRigidBody", "PlayerBoost", "PlayerAnyJump"],
138//! )
139//! .unwrap();
140//!
141//! FrameRateDecorator::new_from_fps(30.0, &mut collector)
142//!     .process_replay(&replay)
143//!     .unwrap();
144//!
145//! let (meta, features) = collector.get_meta_and_ndarray().unwrap();
146//! println!("players: {}", meta.replay_meta.player_count());
147//! println!("shape: {:?}", features.raw_dim());
148//! ```
149//!
150//! ### Export compact event-backed stats timeline
151//!
152//! ```no_run
153//! use boxcars::ParserBuilder;
154//! use subtr_actor::StatsTimelineEventCollector;
155//!
156//! let bytes = std::fs::read("replay.replay").unwrap();
157//! let replay = ParserBuilder::new(&bytes)
158//!     .must_parse_network_data()
159//!     .on_error_check_crc()
160//!     .parse()
161//!     .unwrap();
162//!
163//! let timeline = StatsTimelineEventCollector::new()
164//!     .get_replay_stats_timeline_scaffold(&replay)
165//!     .unwrap();
166//!
167//! println!("timeline frames: {}", timeline.frames.len());
168//! let rush_events = timeline
169//!     .events
170//!     .events
171//!     .iter()
172//!     .filter(|event| event.meta.stream == "rush")
173//!     .count();
174//! println!("rush events: {rush_events}");
175//! ```
176
177#[path = "domain/boost_pad_locations.rs"]
178pub mod boost_pad_locations;
179#[path = "domain/boost_units.rs"]
180pub mod boost_units;
181pub mod clip;
182pub mod collector;
183#[path = "domain/error.rs"]
184pub mod error;
185pub mod interop;
186pub mod processor;
187#[path = "domain/replay_meta.rs"]
188pub mod replay_meta;
189#[path = "domain/replay_model.rs"]
190pub mod replay_model;
191pub mod stats;
192pub mod util;
193
194pub mod geometry {
195    //! Compatibility re-export for geometry helpers.
196    pub use crate::util::geometry::*;
197}
198
199pub mod search {
200    //! Compatibility re-export for search helpers.
201    pub use crate::util::search::*;
202}
203
204pub mod actor_state {
205    //! Compatibility re-export for processor actor-state types.
206    pub use crate::processor::actor_state::*;
207}
208
209/// In-depth prose guides, rendered from the repository's `docs/` directory.
210///
211/// These pages give background and design context that does not belong on any
212/// single type. They are documentation-only modules (no code).
213pub mod guides {
214    /// Map of the stats runtime: how calculators, analysis-graph nodes, and
215    /// accumulators fit together into the analysis DAG.
216    #[doc = include_str!("../docs/calculators-and-analysis-nodes.md")]
217    pub mod calculators_and_analysis_nodes {}
218
219    /// How to interpret the confidence levels attached to exported stats.
220    #[doc = include_str!("../docs/stat-confidence.md")]
221    pub mod stat_confidence {}
222
223    /// Working notes on Rocket League replay-format changes that affect parsing.
224    #[doc = include_str!("../docs/replay-format-evolution.md")]
225    pub mod replay_format_evolution {
226        // The guide uses markdown link-reference definitions, which rustdoc's
227        // bare-URL lint flags even though they render correctly.
228        #![allow(rustdoc::bare_urls)]
229    }
230}
231
232pub mod ballistics {
233    //! Free-flight Rocket League ball prediction helpers.
234    pub use crate::util::ballistics::*;
235}
236
237pub use crate::actor_state::*;
238pub use crate::ballistics::*;
239pub use crate::boost_pad_locations::*;
240pub use crate::boost_units::*;
241pub use crate::clip::*;
242pub use crate::collector::*;
243pub use crate::error::*;
244pub use crate::geometry::*;
245pub use crate::processor::*;
246pub use crate::replay_meta::*;
247pub use crate::replay_model::*;
248pub use crate::search::*;
249pub use crate::stats::*;
250pub(crate) use crate::util::vec_map::*;