1use crate::*;
2
3use super::{
4 BallFrameState, BallSample, DemoEventSample, FrameEventsState, FrameInfo, GameplayState,
5 LivePlayState, PlayerFrameState, PlayerSample,
6};
7
8#[derive(Debug, Clone, Default)]
10pub struct ReplayFrameInputBuilder {
11 aggregate_events: FrameEventsSampler,
12}
13
14#[derive(Debug, Clone, Default)]
15struct FrameEventsSampler {
16 cursors: ProcessorEventCursors,
17}
18
19impl ReplayFrameInputBuilder {
20 pub fn timeline(
21 &mut self,
22 processor: &dyn ProcessorView,
23 frame_number: usize,
24 current_time: f32,
25 dt: f32,
26 ) -> FrameInput {
27 FrameInput::timeline(processor, frame_number, current_time, dt)
28 }
29
30 pub fn aggregate(
31 &mut self,
32 processor: &dyn ProcessorView,
33 frame_number: usize,
34 current_time: f32,
35 dt: f32,
36 ) -> FrameInput {
37 let frame_events_state = self.aggregate_events.events_since_last_sample(processor);
38 FrameInput::from_processor_and_events(
39 processor,
40 frame_number,
41 current_time,
42 dt,
43 frame_events_state,
44 )
45 }
46}
47
48impl FrameEventsSampler {
49 fn events_since_last_sample(&mut self, processor: &dyn ProcessorView) -> FrameEventsState {
50 let events = FrameInput::build_events_since_last_sample(processor, &self.cursors);
51 self.cursors = ProcessorEventCursors::from_processor(processor);
52 events
53 }
54}
55
56#[derive(Debug, Clone, Default)]
57struct ProcessorEventCursors {
58 demolish_count: usize,
59 boost_pad_event_count: usize,
60 touch_event_count: usize,
61 dodge_refreshed_event_count: usize,
62 player_stat_event_count: usize,
63 goal_event_count: usize,
64}
65
66impl ProcessorEventCursors {
67 fn from_processor(processor: &dyn ProcessorView) -> Self {
68 Self {
69 demolish_count: processor.demolishes().len(),
70 boost_pad_event_count: processor.boost_pad_events().len(),
71 touch_event_count: processor.touch_events().len(),
72 dodge_refreshed_event_count: processor.dodge_refreshed_events().len(),
73 player_stat_event_count: processor.player_stat_events().len(),
74 goal_event_count: processor.goal_events().len(),
75 }
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct FrameInput {
82 frame_info: FrameInfo,
83 gameplay_state: GameplayState,
84 ball_frame_state: BallFrameState,
85 player_frame_state: PlayerFrameState,
86 frame_events_state: FrameEventsState,
87 live_play_state: Option<LivePlayState>,
88}
89
90impl FrameInput {
91 pub fn from_parts(
97 frame_info: FrameInfo,
98 gameplay_state: GameplayState,
99 ball_frame_state: BallFrameState,
100 player_frame_state: PlayerFrameState,
101 frame_events_state: FrameEventsState,
102 ) -> Self {
103 Self {
104 frame_info,
105 gameplay_state,
106 ball_frame_state,
107 player_frame_state,
108 frame_events_state,
109 live_play_state: None,
110 }
111 }
112
113 pub fn from_parts_with_live_play_state(
119 frame_info: FrameInfo,
120 gameplay_state: GameplayState,
121 ball_frame_state: BallFrameState,
122 player_frame_state: PlayerFrameState,
123 frame_events_state: FrameEventsState,
124 live_play_state: LivePlayState,
125 ) -> Self {
126 Self {
127 frame_info,
128 gameplay_state,
129 ball_frame_state,
130 player_frame_state,
131 frame_events_state,
132 live_play_state: Some(live_play_state),
133 }
134 }
135
136 pub fn timeline(
137 processor: &dyn ProcessorView,
138 frame_number: usize,
139 current_time: f32,
140 dt: f32,
141 ) -> Self {
142 let frame_events_state = Self::build_current_frame_events_state(processor);
143 Self::from_processor_and_events(
144 processor,
145 frame_number,
146 current_time,
147 dt,
148 frame_events_state,
149 )
150 }
151
152 fn from_processor_and_events(
153 processor: &dyn ProcessorView,
154 frame_number: usize,
155 current_time: f32,
156 dt: f32,
157 frame_events_state: FrameEventsState,
158 ) -> Self {
159 Self {
160 frame_info: Self::build_frame_info(processor, frame_number, current_time, dt),
161 gameplay_state: Self::build_gameplay_state(processor),
162 ball_frame_state: Self::build_ball_frame_state(processor, current_time),
163 player_frame_state: Self::build_player_frame_state(processor, current_time),
164 frame_events_state,
165 live_play_state: None,
166 }
167 }
168
169 pub fn timeline_with_live_play_state(
170 processor: &dyn ProcessorView,
171 frame_number: usize,
172 current_time: f32,
173 dt: f32,
174 live_play_state: LivePlayState,
175 ) -> Self {
176 let mut input = Self::timeline(processor, frame_number, current_time, dt);
177 input.live_play_state = Some(live_play_state);
178 input
179 }
180
181 fn build_frame_info(
182 processor: &dyn ProcessorView,
183 frame_number: usize,
184 current_time: f32,
185 dt: f32,
186 ) -> FrameInfo {
187 FrameInfo {
188 frame_number,
189 time: current_time,
190 dt,
191 seconds_remaining: processor.get_seconds_remaining().ok(),
192 }
193 }
194
195 fn build_gameplay_state(processor: &dyn ProcessorView) -> GameplayState {
196 let team_scores = processor.get_team_scores().ok();
197 let possession_team_is_team_0 =
198 processor
199 .get_ball_hit_team_num()
200 .ok()
201 .and_then(|team_num| match team_num {
202 0 => Some(true),
203 1 => Some(false),
204 _ => None,
205 });
206 let scored_on_team_is_team_0 =
207 processor
208 .get_scored_on_team_num()
209 .ok()
210 .and_then(|team_num| match team_num {
211 0 => Some(true),
212 1 => Some(false),
213 _ => None,
214 });
215 GameplayState {
216 game_state: processor.get_replicated_state_name().ok(),
217 ball_has_been_hit: processor.get_ball_has_been_hit().ok(),
218 kickoff_countdown_time: processor.get_replicated_game_state_time_remaining().ok(),
219 team_zero_score: team_scores.map(|scores| scores.0),
220 team_one_score: team_scores.map(|scores| scores.1),
221 possession_team_is_team_0,
222 scored_on_team_is_team_0,
223 current_in_game_team_player_counts: processor.current_in_game_team_player_counts(),
224 }
225 }
226
227 fn build_ball_frame_state(processor: &dyn ProcessorView, current_time: f32) -> BallFrameState {
228 processor
229 .get_interpolated_ball_rigid_body(current_time, 0.0)
230 .ok()
231 .map(|rigid_body| BallSample { rigid_body })
232 .into()
233 }
234
235 fn build_player_frame_state(
236 processor: &dyn ProcessorView,
237 current_time: f32,
238 ) -> PlayerFrameState {
239 let mut players = Vec::new();
240 for player_id in processor.iter_player_ids_in_order() {
241 let Ok(is_team_0) = processor.get_player_is_team_0(player_id) else {
242 continue;
243 };
244 players.push(PlayerSample {
245 player_id: player_id.clone(),
246 is_team_0,
247 hitbox: processor.get_player_car_hitbox(player_id),
248 rigid_body: processor
249 .get_interpolated_player_rigid_body(player_id, current_time, 0.0)
250 .ok()
251 .filter(|rigid_body| !rigid_body.sleeping),
252 boost_amount: processor.get_player_boost_level(player_id).ok(),
253 last_boost_amount: processor.get_player_last_boost_level(player_id).ok(),
254 boost_active: processor.get_boost_active(player_id).unwrap_or(0) % 2 == 1,
255 dodge_active: processor.get_dodge_active(player_id).unwrap_or(0) % 2 == 1,
256 dodge_torque: processor
257 .get_dodge_torque(player_id)
258 .ok()
259 .map(|(x, y, z)| glam::Vec3::new(x, y, z)),
260 powerslide_active: processor.get_powerslide_active(player_id).unwrap_or(false),
261 match_goals: processor.get_player_match_goals(player_id).ok(),
262 match_assists: processor.get_player_match_assists(player_id).ok(),
263 match_saves: processor.get_player_match_saves(player_id).ok(),
264 match_shots: processor.get_player_match_shots(player_id).ok(),
265 match_score: processor.get_player_match_score(player_id).ok(),
266 });
267 }
268 PlayerFrameState { players }
269 }
270
271 fn build_active_demo_events(processor: &dyn ProcessorView) -> Vec<DemoEventSample> {
272 let active_demo_events = processor.current_frame_active_demo_events();
273 if !active_demo_events.is_empty() {
274 active_demo_events.to_vec()
275 } else {
276 match processor.get_active_demos() {
277 Ok(demos) => demos
278 .into_iter()
279 .filter_map(|demo| {
280 let attacker = processor
281 .get_player_id_from_car_id(&demo.attacker_actor_id())
282 .ok()?;
283 let victim = processor
284 .get_player_id_from_car_id(&demo.victim_actor_id())
285 .ok()?;
286 Some(DemoEventSample { attacker, victim })
287 })
288 .collect(),
289 _ => Vec::new(),
290 }
291 }
292 }
293
294 fn build_current_frame_events_state(processor: &dyn ProcessorView) -> FrameEventsState {
295 FrameEventsState {
296 active_demos: Self::build_active_demo_events(processor),
297 demo_events: processor.current_frame_demolish_events().to_vec(),
298 boost_pad_events: processor.current_frame_boost_pad_events().to_vec(),
299 touch_events: processor.current_frame_touch_events().to_vec(),
300 dodge_refreshed_counter_available: processor.dodge_refreshed_counter_available(),
301 dodge_refreshed_events: processor.current_frame_dodge_refreshed_events().to_vec(),
302 player_stat_events: processor.current_frame_player_stat_events().to_vec(),
303 goal_events: processor.current_frame_goal_events().to_vec(),
304 }
305 }
306
307 fn build_events_since_last_sample(
308 processor: &dyn ProcessorView,
309 event_cursors: &ProcessorEventCursors,
310 ) -> FrameEventsState {
311 FrameEventsState {
312 active_demos: Self::build_active_demo_events(processor),
313 demo_events: processor.demolishes()[event_cursors.demolish_count..].to_vec(),
314 boost_pad_events: processor.boost_pad_events()[event_cursors.boost_pad_event_count..]
315 .to_vec(),
316 touch_events: processor.touch_events()[event_cursors.touch_event_count..].to_vec(),
317 dodge_refreshed_counter_available: processor.dodge_refreshed_counter_available(),
318 dodge_refreshed_events: processor.dodge_refreshed_events()
319 [event_cursors.dodge_refreshed_event_count..]
320 .to_vec(),
321 player_stat_events: processor.player_stat_events()
322 [event_cursors.player_stat_event_count..]
323 .to_vec(),
324 goal_events: processor.goal_events()[event_cursors.goal_event_count..].to_vec(),
325 }
326 }
327
328 pub fn frame_info(&self) -> FrameInfo {
329 self.frame_info.clone()
330 }
331
332 pub fn gameplay_state(&self) -> GameplayState {
333 self.gameplay_state.clone()
334 }
335
336 pub fn ball_frame_state(&self) -> BallFrameState {
337 self.ball_frame_state.clone()
338 }
339
340 pub fn player_frame_state(&self) -> PlayerFrameState {
341 self.player_frame_state.clone()
342 }
343
344 pub fn frame_events_state(&self) -> FrameEventsState {
345 self.frame_events_state.clone()
346 }
347
348 pub fn live_play_state(&self) -> Option<LivePlayState> {
349 self.live_play_state.clone()
350 }
351}