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