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::*;