1mod client;
6mod data;
7mod shared_memory_data;
8
9use crate::{Moment, RacingFlags, Simetry};
10pub use client::{Client, Config};
11pub use data::{Extended, ForceFeedback, MultiRules, PitInfo, Rules, Scoring, Telemetry, Weather};
12use std::borrow::Cow;
13use std::sync::Arc;
14use uom::si::angular_velocity::revolution_per_minute;
15use uom::si::f64::{AngularVelocity, Velocity};
16use uom::si::velocity::meter_per_second;
17
18#[derive(Clone, Debug)]
19pub struct SimState {
20 pub telemetry: Arc<Telemetry>,
21 pub scoring: Arc<Scoring>,
22 pub rules: Arc<Rules>,
23 pub multi_rules: Arc<MultiRules>,
24 pub force_feedback: Arc<ForceFeedback>,
25 pub pit_info: Arc<PitInfo>,
26 pub weather: Arc<Weather>,
27 pub extended: Arc<Extended>,
28}
29
30#[async_trait::async_trait]
31impl Simetry for Client {
32 fn name(&self) -> &str {
33 "rFactor2"
34 }
35
36 async fn next_moment(&mut self) -> Option<Box<dyn Moment + Send + Sync + 'static>> {
37 Some(Box::new(self.next_sim_state().await?))
38 }
39}
40
41impl Moment for SimState {
42 fn vehicle_gear(&self) -> Option<i8> {
43 let player_scoring = self.scoring.vehicles.iter().find(|v| v.is_player != 0)?;
44 let player_id = player_scoring.id;
45 let player_telemetry = self.telemetry.vehicles.iter().find(|v| v.id == player_id)?;
46 Some(player_telemetry.gear as i8)
47 }
48
49 fn vehicle_velocity(&self) -> Option<Velocity> {
50 let player_scoring = self.scoring.vehicles.iter().find(|v| v.is_player != 0)?;
51 let player_id = player_scoring.id;
52 let player_telemetry = self.telemetry.vehicles.iter().find(|v| v.id == player_id)?;
53 let speed_vec_ms = &player_telemetry.local_vel;
54 let speed_ms = (speed_vec_ms.x * speed_vec_ms.x
55 + speed_vec_ms.y * speed_vec_ms.y
56 + speed_vec_ms.z * speed_vec_ms.z)
57 .sqrt();
58 Some(Velocity::new::<meter_per_second>(speed_ms))
59 }
60
61 fn vehicle_engine_rotation_speed(&self) -> Option<AngularVelocity> {
62 let player_scoring = self.scoring.vehicles.iter().find(|v| v.is_player != 0)?;
63 let player_id = player_scoring.id;
64 let player_telemetry = self.telemetry.vehicles.iter().find(|v| v.id == player_id)?;
65 Some(AngularVelocity::new::<revolution_per_minute>(
66 player_telemetry.engine_rpm,
67 ))
68 }
69
70 fn vehicle_max_engine_rotation_speed(&self) -> Option<AngularVelocity> {
71 let player_scoring = self.scoring.vehicles.iter().find(|v| v.is_player != 0)?;
72 let player_id = player_scoring.id;
73 let player_telemetry = self.telemetry.vehicles.iter().find(|v| v.id == player_id)?;
74 Some(AngularVelocity::new::<revolution_per_minute>(
75 player_telemetry.engine_max_rpm,
76 ))
77 }
78
79 fn is_pit_limiter_engaged(&self) -> Option<bool> {
80 let player_scoring = self.scoring.vehicles.iter().find(|v| v.is_player != 0)?;
81 let player_id = player_scoring.id;
82 let player_telemetry = self.telemetry.vehicles.iter().find(|v| v.id == player_id)?;
83 Some(player_telemetry.speed_limiter != 0)
84 }
85
86 fn is_vehicle_in_pit_lane(&self) -> Option<bool> {
87 let player_scoring = self.scoring.vehicles.iter().find(|v| v.is_player != 0)?;
88 Some(player_scoring.in_pits != 0)
89 }
90
91 fn flags(&self) -> Option<RacingFlags> {
92 let player_scoring = self.scoring.vehicles.iter().find(|v| v.is_player != 0)?;
93 Some(RacingFlags {
94 green: player_scoring.flag == 0,
95 yellow: player_scoring.individual_phase == 10,
96 blue: player_scoring.flag == 6,
97 white: false,
98 red: false,
99 black: player_scoring.num_penalties > 0,
100 checkered: player_scoring.finish_status == 1,
101 meatball: false,
102 black_and_white: false,
103 start_ready: false,
104 start_set: false,
105 start_go: false,
106 })
107 }
108
109 fn vehicle_unique_id(&self) -> Option<Cow<str>> {
110 let player_scoring = self.scoring.vehicles.iter().find(|v| v.is_player != 0)?;
111 Some(
112 player_scoring
113 .vehicle_name
114 .split_once('#')
115 .unwrap_or(("?", ""))
116 .0
117 .trim()
118 .into(),
119 )
120 }
121}