subtr_actor_spec/
lib.rs

1//! # subtr-actor
2//!
3//! [`subtr-actor`](crate) is a versatile library designed to facilitate the
4//! processes of working with and extracting data from Rocket League replays.
5//! Utilizing the powerful [`boxcars`] library for parsing, subtr-actor
6//! simplifies (or 'subtracts', as hinted by its name) the underlying
7//! actor-based structure of replay files, making them more accessible and
8//! easier to manipulate.
9//!
10//! ## Overview of Key Components
11//!
12//! - **[`ReplayProcessor`]**: This struct is at the heart of subtr-actor's
13//! replay processing capabilities. In its main entry point,
14//! [`ReplayProcessor::process`], it pushes network frames from the
15//! [`boxcars::Replay`] that it is initialized with though an
16//! [`ActorStateModeler`] instance, calling the [`Collector`] instance that is
17//! provided as an argument as it does so. The [`Collector`] is provided with a
18//! reference to the [`ReplayProcessor`] each time the it is invoked, which
19//! allows it to use the suite of helper methods which greatly assist in the
20//! navigation of the actor graph and the retrieval of information about the
21//! current game state.
22//!
23//! - **[`Collector`]**: This trait outlines the blueprint for data collection
24//! from replays. The [`Collector`] interfaces with a [`ReplayProcessor`],
25//! handling frame data and guiding the pace of replay progression with
26//! [`TimeAdvance`]. It is typically invoked repeatedly through the
27//! [`ReplayProcessor::process`] method as the replay is processed frame by
28//! frame.
29//!
30//! - **[`FrameRateDecorator`]**: This struct decorates a [`Collector`]
31//! implementation with a target frame duration, controlling the frame rate of
32//! the replay processing.
33//!
34//! ### Collector implementations
35//!
36//! [`subtr-actor`](crate) also includes implementations of the [`Collector`] trait:
37//!
38//! - **[`NDArrayCollector`]**: This [`Collector`] implementations translates
39//! frame-based replay data into a 2 dimensional array in the form of a
40//! [`::ndarray::Array2`] instance. The exact data that is recorded in each
41//! frame can be configured with the [`FeatureAdder`] and [`PlayerFeatureAdder`]
42//! instances that are provided to its constructor ([`NDArrayCollector::new`]).
43//! Extending the exact behavior of [`NDArrayCollector`] is thus possible with
44//! user defined [`FeatureAdder`] and [`PlayerFeatureAdder`], which is made easy
45//! with the [`build_global_feature_adder!`] and [`build_player_feature_adder!`]
46//! macros. The [`::ndarray::Array2`] produced by [`NDArrayCollector`] is ideal
47//! for use with machine learning libraries like pytorch and tensorflow.
48//!
49//! - **[`ReplayDataCollector`]**: This [`Collector`] implementation provides an
50//! easy way to get a serializable to e.g. json (though [`serde::Serialize`])
51//! representation of the replay. The representation differs from what you might
52//! get from e.g. raw [`boxcars`] in that it is not a complicated graph of actor
53//! objects, but instead something more natural where the data associated with
54//! each entity in the game is grouped together.
55//!
56//! ## Examples
57//!
58//! ### Getting JSON
59//!
60//! ```
61//! fn get_json(filepath: std::path::PathBuf) -> anyhow::Result<String> {
62//!     let data = std::fs::read(filepath.as_path())?;
63//!     let replay = boxcars::ParserBuilder::new(&data)
64//!         .must_parse_network_data()
65//!         .on_error_check_crc()
66//!         .parse()?;
67//!     Ok(subtr_actor_spec::ReplayDataCollector::new()
68//!         .get_replay_data(&replay)
69//!         .map_err(|e| e.variant)?
70//!         .as_json()?)
71//! }
72//! ```
73//!
74//! ### Getting a [`::ndarray::Array2`]
75//!
76//! In the following example, we demonstrate how to use [`boxcars`],
77//! [`NDArrayCollector`] and [`FrameRateDecorator`] to write a function that
78//! takes a replay filepath and collections of features adders and returns a
79//! [`ReplayMetaWithHeaders`] along with a [`::ndarray::Array2`] . The resulting
80//! [`::ndarray::Array2`] would be appropriate for use in a machine learning
81//! context. Note that [`ReplayProcessor`] is also used implicitly here in the
82//! [`Collector::process_replay`]
83//!
84//! ```
85//! use subtr_actor_spec::*;
86//!
87//! fn get_ndarray_with_info_from_replay_filepath(
88//!     filepath: std::path::PathBuf,
89//!     feature_adders: FeatureAdders<f32>,
90//!     player_feature_adders: PlayerFeatureAdders<f32>,
91//!     fps: Option<f32>,
92//! ) -> anyhow::Result<(ReplayMetaWithHeaders, ::ndarray::Array2<f32>)> {
93//!     let data = std::fs::read(filepath.as_path())?;
94//!     let replay = boxcars::ParserBuilder::new(&data)
95//!         .must_parse_network_data()
96//!         .on_error_check_crc()
97//!         .parse()?;
98//!
99//!     let mut collector = NDArrayCollector::new(feature_adders, player_feature_adders);
100//!
101//!     FrameRateDecorator::new_from_fps(fps.unwrap_or(10.0), &mut collector)
102//!         .process_replay(&replay)
103//!         .map_err(|e| e.variant)?;
104//!
105//!     Ok(collector.get_meta_and_ndarray().map_err(|e| e.variant)?)
106//! }
107//!
108//! fn get_ndarray_with_default_feature_adders(
109//!     filepath: std::path::PathBuf,
110//! ) -> anyhow::Result<(ReplayMetaWithHeaders, ::ndarray::Array2<f32>)> {
111//!     get_ndarray_with_info_from_replay_filepath(
112//!         filepath,
113//!         vec![
114//!             InterpolatedBallRigidBodyNoVelocities::arc_new(0.003),
115//!             CurrentTime::arc_new(),
116//!         ],
117//!         vec![
118//!             InterpolatedPlayerRigidBodyNoVelocities::arc_new(0.003),
119//!             PlayerBoost::arc_new(),
120//!             PlayerAnyJump::arc_new(),
121//!             PlayerDemolishedBy::arc_new(),
122//!         ],
123//!         Some(30.0),
124//!     )
125//! }
126//! ```
127//!
128//! ### Using [`NDArrayCollector::from_strings`]
129//!
130//! In the second function in the example above, we see the use of feature
131//! adders like [`InterpolatedPlayerRigidBodyNoVelocities`]. The feature adders
132//! that are included with [`subtr_actor`](crate) can all be found in the
133//! [`crate::collector::ndarray`] module. It is also possible to access these
134//! feature adders by name with strings, which can be useful when implementing
135//! bindings for other languages since those languages may not be able to access
136//! rust structs an instantiate them easily or at all.
137//!
138//! ```
139//! pub static DEFAULT_GLOBAL_FEATURE_ADDERS: [&str; 1] = ["BallRigidBody"];
140//!
141//! pub static DEFAULT_PLAYER_FEATURE_ADDERS: [&str; 3] =
142//!     ["PlayerRigidBody", "PlayerBoost", "PlayerAnyJump"];
143//!
144//! fn build_ndarray_collector(
145//!     global_feature_adders: Option<Vec<String>>,
146//!     player_feature_adders: Option<Vec<String>>,
147//! ) -> subtr_actor_spec::SubtrActorResult<subtr_actor_spec::NDArrayCollector<f32>> {
148//!     let global_feature_adders = global_feature_adders.unwrap_or_else(|| {
149//!         DEFAULT_GLOBAL_FEATURE_ADDERS
150//!             .iter()
151//!             .map(|i| i.to_string())
152//!             .collect()
153//!     });
154//!     let player_feature_adders = player_feature_adders.unwrap_or_else(|| {
155//!         DEFAULT_PLAYER_FEATURE_ADDERS
156//!             .iter()
157//!             .map(|i| i.to_string())
158//!             .collect()
159//!     });
160//!     let global_feature_adders: Vec<&str> = global_feature_adders.iter().map(|s| &s[..]).collect();
161//!     let player_feature_adders: Vec<&str> = player_feature_adders.iter().map(|s| &s[..]).collect();
162//!     subtr_actor_spec::NDArrayCollector::<f32>::from_strings(
163//!         &global_feature_adders,
164//!         &player_feature_adders,
165//!     )
166//! }
167//! ```
168
169pub mod actor_state;
170pub mod collector;
171pub mod constants;
172pub mod error;
173pub mod processor;
174pub mod util;
175
176#[cfg(test)]
177mod util_test;
178
179pub use crate::actor_state::*;
180pub use crate::collector::*;
181pub use crate::constants::*;
182pub use crate::error::*;
183pub use crate::processor::*;
184pub use crate::util::*;