subtr_actor_spec/collector/
replay_data.rs1use boxcars;
2use serde::Serialize;
3
4use crate::*;
5
6#[derive(Debug, Clone, PartialEq, Serialize)]
7pub enum BallFrame {
8 Empty,
9 Data { rigid_body: boxcars::RigidBody },
10}
11
12impl BallFrame {
13 fn new_from_processor(processor: &ReplayProcessor, current_time: f32) -> Self {
14 if processor.get_ignore_ball_syncing().unwrap_or(false) {
15 Self::Empty
16 } else if let Ok(rigid_body) = processor.get_interpolated_ball_rigid_body(current_time, 0.0)
17 {
18 Self::new_from_rigid_body(rigid_body)
19 } else {
20 Self::Empty
21 }
22 }
23
24 fn new_from_rigid_body(rigid_body: boxcars::RigidBody) -> Self {
25 if rigid_body.sleeping {
26 Self::Empty
27 } else {
28 Self::Data {
29 rigid_body: rigid_body.clone(),
30 }
31 }
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Serialize)]
36pub enum PlayerFrame {
37 Empty,
38 Data {
39 rigid_body: boxcars::RigidBody,
40 boost_amount: f32,
41 boost_active: bool,
42 jump_active: bool,
43 double_jump_active: bool,
44 dodge_active: bool,
45 },
46}
47
48impl PlayerFrame {
49 fn new_from_processor(
50 processor: &ReplayProcessor,
51 player_id: &PlayerId,
52 current_time: f32,
53 ) -> SubtrActorResult<Self> {
54 let rigid_body =
55 processor.get_interpolated_player_rigid_body(player_id, current_time, 0.0)?;
56
57 if rigid_body.sleeping {
58 return Ok(PlayerFrame::Empty);
59 }
60
61 let boost_amount = processor.get_player_boost_level(player_id)?;
62 let boost_active = processor.get_boost_active(player_id).unwrap_or(0) % 2 == 1;
63 let jump_active = processor.get_jump_active(player_id).unwrap_or(0) % 2 == 1;
64 let double_jump_active = processor.get_double_jump_active(player_id).unwrap_or(0) % 2 == 1;
65 let dodge_active = processor.get_dodge_active(player_id).unwrap_or(0) % 2 == 1;
66
67 Ok(Self::from_data(
68 rigid_body,
69 boost_amount,
70 boost_active,
71 jump_active,
72 double_jump_active,
73 dodge_active,
74 ))
75 }
76
77 fn from_data(
78 rigid_body: boxcars::RigidBody,
79 boost_amount: f32,
80 boost_active: bool,
81 jump_active: bool,
82 double_jump_active: bool,
83 dodge_active: bool,
84 ) -> Self {
85 if rigid_body.sleeping {
86 Self::Empty
87 } else {
88 Self::Data {
89 rigid_body,
90 boost_amount,
91 boost_active,
92 jump_active,
93 double_jump_active,
94 dodge_active,
95 }
96 }
97 }
98}
99
100#[derive(Debug, Clone, PartialEq, Serialize)]
101pub struct PlayerData {
102 frames: Vec<PlayerFrame>,
103}
104
105impl PlayerData {
106 fn new() -> Self {
107 Self { frames: Vec::new() }
108 }
109
110 fn add_frame(&mut self, frame_index: usize, frame: PlayerFrame) {
111 let empty_frames_to_add = frame_index - self.frames.len();
112 if empty_frames_to_add > 0 {
113 for _ in 0..empty_frames_to_add {
114 self.frames.push(PlayerFrame::Empty)
115 }
116 }
117 self.frames.push(frame)
118 }
119}
120
121#[derive(Debug, Clone, PartialEq, Serialize)]
122pub struct BallData {
123 frames: Vec<BallFrame>,
124}
125
126impl BallData {
127 fn add_frame(&mut self, frame_index: usize, frame: BallFrame) {
128 let empty_frames_to_add = frame_index - self.frames.len();
129 if empty_frames_to_add > 0 {
130 for _ in 0..empty_frames_to_add {
131 self.frames.push(BallFrame::Empty)
132 }
133 }
134 self.frames.push(frame)
135 }
136}
137
138#[derive(Debug, Clone, PartialEq, Serialize)]
139pub struct MetadataFrame {
140 pub time: f32,
141 pub seconds_remaining: i32,
142}
143
144impl MetadataFrame {
145 fn new_from_processor(processor: &ReplayProcessor, time: f32) -> SubtrActorResult<Self> {
146 Ok(Self::new(time, processor.get_seconds_remaining()?))
147 }
148
149 fn new(time: f32, seconds_remaining: i32) -> Self {
150 MetadataFrame {
151 time,
152 seconds_remaining,
153 }
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Serialize)]
158pub struct FrameData {
159 pub ball_data: BallData,
160 pub players: Vec<(PlayerId, PlayerData)>,
161 pub metadata_frames: Vec<MetadataFrame>,
162}
163
164#[derive(Debug, Clone, PartialEq, Serialize)]
165pub struct ReplayData {
166 pub frame_data: FrameData,
167 pub meta: ReplayMeta,
168 pub demolish_infos: Vec<DemolishInfo>,
169}
170
171impl ReplayData {
172 pub fn as_json(&self) -> Result<String, serde_json::Error> {
173 serde_json::to_string(self)
174 }
175}
176
177impl FrameData {
178 fn new() -> Self {
179 FrameData {
180 ball_data: BallData { frames: Vec::new() },
181 players: Vec::new(),
182 metadata_frames: Vec::new(),
183 }
184 }
185
186 fn add_frame(
187 &mut self,
188 frame_metadata: MetadataFrame,
189 ball_frame: BallFrame,
190 player_frames: Vec<(PlayerId, PlayerFrame)>,
191 ) -> SubtrActorResult<()> {
192 let frame_index = self.metadata_frames.len();
193 self.metadata_frames.push(frame_metadata);
194 self.ball_data.add_frame(frame_index, ball_frame);
195 for (player_id, frame) in player_frames {
196 self.players
197 .get_entry(player_id)
198 .or_insert_with(|| PlayerData::new())
199 .add_frame(frame_index, frame)
200 }
201 Ok(())
202 }
203}
204
205pub struct ReplayDataCollector {
206 frame_data: FrameData,
207}
208
209impl ReplayDataCollector {
210 pub fn new() -> Self {
211 ReplayDataCollector {
212 frame_data: FrameData::new(),
213 }
214 }
215
216 pub fn get_frame_data(self) -> FrameData {
217 self.frame_data
218 }
219
220 pub fn get_replay_data(mut self, replay: &boxcars::Replay) -> SubtrActorResult<ReplayData> {
221 let mut processor = ReplayProcessor::new(replay)?;
222 processor.process(&mut self)?;
223 let meta = processor.get_replay_meta()?;
224 Ok(ReplayData {
225 meta,
226 demolish_infos: processor.demolishes,
227 frame_data: self.get_frame_data(),
228 })
229 }
230
231 fn get_player_frames(
232 &self,
233 processor: &ReplayProcessor,
234 current_time: f32,
235 ) -> SubtrActorResult<Vec<(PlayerId, PlayerFrame)>> {
236 Ok(processor
237 .iter_player_ids_in_order()
238 .map(|player_id| {
239 (
240 player_id.clone(),
241 PlayerFrame::new_from_processor(processor, player_id, current_time)
242 .unwrap_or_else(|_err| PlayerFrame::Empty),
243 )
244 })
245 .collect())
246 }
247}
248
249impl Collector for ReplayDataCollector {
250 fn process_frame(
251 &mut self,
252 processor: &ReplayProcessor,
253 _frame: &boxcars::Frame,
254 _frame_number: usize,
255 current_time: f32,
256 ) -> SubtrActorResult<TimeAdvance> {
257 let metadata_frame = MetadataFrame::new_from_processor(processor, current_time)?;
258 let ball_frame = BallFrame::new_from_processor(processor, current_time);
259 let player_frames = self.get_player_frames(processor, current_time)?;
260 self.frame_data
261 .add_frame(metadata_frame, ball_frame, player_frames)?;
262 Ok(TimeAdvance::NextFrame)
263 }
264}