Compare commits

..

No commits in common. "adc81b923223c56fa50487bc8b036b7c6b0b4a2d" and "144c7ab5f0e8009a9c83afe4487255e519e3046d" have entirely different histories.

11 changed files with 273 additions and 659 deletions

View file

@ -1,6 +0,0 @@
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?>
<root reference_width="2560" reference_height="1440">
<!-- max 5 entries, each entry gets 39 pixel (at 1440p) -->
<grid id="main_grid" x_dim="1" y_dim="5" x_offset="-410" y_offset="10" width="400" height="195"
vert_align="top" hori_align="right" margin="0" padding="0"> </grid>
</root>

View file

@ -1,211 +0,0 @@
use std::sync::Arc;
use anyhow::Result;
use ui::prelude::*;
use utilities::prelude::Color;
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum BehindLeader {
Time(f64),
Laps(i32),
}
pub struct LeaderBoardEntry {
id: i32,
name: String,
place: u8,
behind: BehindLeader,
time_behind_next: f64,
best_lap: f64,
snippet: Arc<GuiSnippet>,
name_label: Arc<Label>,
place_label: Arc<Label>,
time_label: Arc<Label>,
place_updated: bool,
}
impl LeaderBoardEntry {
const ENTRY: &str = include_str!("leaderboard_entry.xml");
pub fn empty(gui_handler: &Arc<GuiHandler>) -> Result<Self> {
Self::new(
gui_handler,
-1,
"".to_string(),
0,
BehindLeader::Laps(0),
0.0,
0.0,
)
}
pub fn new(
gui_handler: &Arc<GuiHandler>,
id: i32,
name: String,
place: u8,
behind: BehindLeader,
time_behind_next: f64,
best_lap: f64,
) -> Result<Self> {
let snippet = GuiSnippet::from_str(gui_handler, Self::ENTRY)?;
let name_label: Arc<Label> = snippet.element("name")?;
let place_label: Arc<Label> = snippet.element("place")?;
let time_label: Arc<Label> = snippet.element("time")?;
name_label.set_text(&name)?;
place_label.set_text(place)?;
time_label.set_text("---")?;
Ok(Self {
id,
name,
place,
behind,
time_behind_next,
best_lap,
snippet,
name_label,
place_label,
time_label,
place_updated: true,
})
}
pub fn change_id(&mut self, id: i32) {
self.id = id;
}
pub fn id(&self) -> i32 {
self.id
}
pub fn place(&self) -> u8 {
self.place
}
pub fn name(&self) -> &str {
&self.name
}
pub fn change_name(&mut self, name: String) -> Result<()> {
self.name = name;
self.name_label.set_text(&self.name)
}
pub fn snippet(&self) -> Arc<GuiSnippet> {
self.snippet.clone()
}
pub fn change_background_color(&self, color: Color) -> Result<()> {
self.time_label.set_background(color)?;
self.name_label.set_background(color)?;
self.place_label.set_background(color)?;
Ok(())
}
pub fn update_place(&mut self, place: u8) -> Result<()> {
if self.place != place {
self.place_updated = true;
}
self.place = place;
self.place_label.set_text(self.place)
}
pub fn update_time_behind_leader(&mut self, behind: BehindLeader) -> Result<()> {
if self.behind != behind {
self.behind = behind;
match self.behind {
BehindLeader::Time(time_behind) => {
// check if we are leader
if time_behind == 0.0 {
self.time_label.set_text("---")?;
} else {
let text = if time_behind > 60.0 {
let full_minutes = (self.best_lap / 60.0).floor();
let remainder = self.best_lap - (full_minutes * 60.0);
format!("+{:.0}:{:.0}", full_minutes, remainder)
} else {
format!("+{:.3}", time_behind)
};
self.time_label.set_text(text)?;
}
}
BehindLeader::Laps(laps_behind) => {
let text = if laps_behind == 1 {
format!("+{} Lap", laps_behind)
} else {
format!("+{} Laps", laps_behind)
};
self.time_label.set_text(text)?;
}
}
}
Ok(())
}
pub fn update_best_lap(&mut self, time: f64) -> Result<()> {
if self.best_lap != time {
self.best_lap = time;
if self.best_lap < 0.0 {
self.time_label.set_text("---")?;
} else {
let text = if self.best_lap > 60.0 {
let full_minutes = (self.best_lap / 60.0).floor();
let remainder = self.best_lap - (full_minutes * 60.0);
format!("{:.0}:{:.3}", full_minutes, remainder)
} else {
format!("{:.3}", self.best_lap)
};
self.time_label.set_text(text)?;
}
}
Ok(())
}
pub fn update_time_behind_next(&mut self, time: f64) -> Result<()> {
self.time_behind_next = time;
let text = if self.time_behind_next > 60.0 {
let full_minutes = (self.time_behind_next / 60.0).floor();
let remainder = self.time_behind_next - (full_minutes * 60.0);
format!("+{:.0}:{:.3}", full_minutes, remainder)
} else {
format!("+{:.3}", self.time_behind_next)
};
self.time_label.set_text(text)
}
pub fn needs_resorting(&self) -> bool {
self.place_updated
}
pub fn resorting_finished(&mut self) {
self.place_updated = false;
}
}

View file

@ -1,12 +1,12 @@
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?> <?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
<root> <root>
<grid x_dim="11" y_dim="1" padding="2" margin="2"> <grid id="grid" x_dim="9" y_dim="1">
<label id="place" x_slot="0" y_slot="0" text_color="black" text_alignment="right"></label> <label id="place" x_slot="0" y_slot="0" text_color="black" text_alignment="right"></label>
<label <label
id="name" id="name"
x_slot="1" y_slot="0" x_size="7" text_color="black"></label> x_slot="1" y_slot="0" x_size="6" text_color="black" text_alignment="left"></label>
<label <label
id="time" id="time_behind"
x_slot="8" y_slot="0" x_size="3" text_color="black" text_alignment="right"></label> x_slot="7" y_slot="0" x_size="2" text_color="black" text_alignment="right"></label>
</grid> </grid>
</root> </root>

View file

@ -1,6 +1,5 @@
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?> <?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
<root reference_width="2560" reference_height="1440"> <root reference_width="2560" reference_height="1440">
<!-- max 25 entries, each entry gets 39 pixel (at 1440p) --> <grid id="main_grid" x_dim="1" y_dim="25" x_offset="10" y_offset="10" width="350" height="875"
<grid id="main_grid" x_dim="1" y_dim="25" x_offset="10" y_offset="10" width="400" height="975"
vert_align="top" hori_align="left" margin="0" padding="0"> </grid> vert_align="top" hori_align="left" margin="0" padding="0"> </grid>
</root> </root>

View file

@ -1,7 +1,3 @@
mod leaderboard_entry;
use leaderboard_entry::*;
use std::{ use std::{
ffi::{c_char, CStr}, ffi::{c_char, CStr},
sync::Arc, sync::Arc,
@ -20,51 +16,30 @@ use crate::write_log;
pub struct LeaderBoard { pub struct LeaderBoard {
gui_handler: Arc<GuiHandler>, gui_handler: Arc<GuiHandler>,
leaderboard: Arc<GuiBuilder>, gui: Arc<GuiBuilder>,
deltaboard: Arc<GuiBuilder>, main_grid: Arc<Grid>,
leaderboard_grid: Arc<Grid>, entries: Vec<LeaderBoardEntry>,
deltaboard_grid: Arc<Grid>,
leaderboard_entries: Vec<LeaderBoardEntry>,
deltaboard_entries: [LeaderBoardEntry; 5],
leaderboard_redraw: bool,
last_player_id: i32,
entry_backgrounds: [Color; 2], entry_backgrounds: [Color; 2],
player_background: Color, player_background: Color,
} }
impl LeaderBoard { impl LeaderBoard {
const LEADERBOARD: &str = include_str!("leaderboard_grid.xml"); const GRID: &str = include_str!("leaderboard_grid.xml");
const DELTABOARD: &str = include_str!("deltaboard_grid.xml"); const ENTRY: &str = include_str!("leaderboard_entry.xml");
pub fn new(gui_handler: &Arc<GuiHandler>) -> Result<Self> { pub fn new(gui_handler: &Arc<GuiHandler>) -> Result<Self> {
let leaderboard = GuiBuilder::from_str(gui_handler, Self::LEADERBOARD)?; let gui = GuiBuilder::from_str(gui_handler, Self::GRID)?;
let deltaboard = GuiBuilder::from_str(gui_handler, Self::DELTABOARD)?;
let leaderboard_grid = leaderboard.element("main_grid")?; let main_grid = gui.element("main_grid")?;
let deltaboard_grid = deltaboard.element("main_grid")?;
Ok(Self { Ok(Self {
gui_handler: gui_handler.clone(), gui_handler: gui_handler.clone(),
leaderboard, gui,
deltaboard, main_grid,
leaderboard_grid,
deltaboard_grid,
leaderboard_entries: Vec::new(), entries: Vec::new(),
deltaboard_entries: [
LeaderBoardEntry::empty(gui_handler)?,
LeaderBoardEntry::empty(gui_handler)?,
LeaderBoardEntry::empty(gui_handler)?,
LeaderBoardEntry::empty(gui_handler)?,
LeaderBoardEntry::empty(gui_handler)?,
],
leaderboard_redraw: false,
last_player_id: -1,
entry_backgrounds: [Color::try_from("#838383")?, Color::try_from("#545454")?], entry_backgrounds: [Color::try_from("#838383")?, Color::try_from("#545454")?],
player_background: Color::try_from("#b4bf26")?, player_background: Color::try_from("#b4bf26")?,
@ -78,20 +53,13 @@ impl LeaderBoard {
.to_string() .to_string()
} }
fn update_leaderboard<F>( fn race_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
&mut self,
vehicle_scorings: &[VehicleScoringInfoV01],
f: F,
) -> Result<()>
where
F: Fn(&mut LeaderBoardEntry, &VehicleScoringInfoV01) -> Result<()>,
{
for vehicle_scoring in vehicle_scorings { for vehicle_scoring in vehicle_scorings {
let driver_name = Self::c_char_to_string(vehicle_scoring.mDriverName); let driver_name = Self::c_char_to_string(vehicle_scoring.mDriverName);
// check driver list // check driver list
match self match self
.leaderboard_entries .entries
.iter_mut() .iter_mut()
.find(|entry| vehicle_scoring.mID == entry.id()) .find(|entry| vehicle_scoring.mID == entry.id())
{ {
@ -101,8 +69,8 @@ impl LeaderBoard {
} }
entry.update_place(vehicle_scoring.mPlace)?; entry.update_place(vehicle_scoring.mPlace)?;
entry.update_time_behind_leader(vehicle_scoring.mTimeBehindLeader)?;
f(entry, vehicle_scoring)?; entry.update_time_behind_next(vehicle_scoring.mTimeBehindNext)?;
} }
None => { None => {
let entry = LeaderBoardEntry::new( let entry = LeaderBoardEntry::new(
@ -110,39 +78,26 @@ impl LeaderBoard {
vehicle_scoring.mID, vehicle_scoring.mID,
driver_name, driver_name,
vehicle_scoring.mPlace, vehicle_scoring.mPlace,
{ vehicle_scoring.mTimeBehindLeader,
let laps_behind = vehicle_scoring.mLapsBehindLeader;
if laps_behind != 0 {
BehindLeader::Laps(laps_behind)
} else {
BehindLeader::Time(vehicle_scoring.mTimeBehindLeader)
}
},
vehicle_scoring.mTimeBehindNext, vehicle_scoring.mTimeBehindNext,
vehicle_scoring.mBestLapTime,
)?; )?;
self.leaderboard_entries.push(entry); self.entries.push(entry);
} }
} }
} }
// TODO: when people disconnect there needs to be a remove function
// vehicle_scorings.len() != self.leaderboard_entries.len()
write_log!("create entries"); write_log!("create entries");
// check if entry count in grid is the same as the gathered entries // check if entry count in grid is the same as the gathered entries
let force_update = if !self.leaderboard_entries.is_empty() let force_update = if !self.entries.is_empty()
&& self && self
.leaderboard_grid .main_grid
.child_at(0, self.leaderboard_entries.len() - 1)? .child_at(0, self.entries.len() - 1)?
.is_none() .is_none()
{ {
for (i, entry) in self.leaderboard_entries.iter().enumerate() { for i in 0..self.entries.len() {
self.leaderboard_grid.detach(0, i)?; self.main_grid.detach(0, i)?;
entry.snippet().set_visibility(false)?;
} }
true true
@ -151,154 +106,33 @@ impl LeaderBoard {
}; };
// check if any entry needs resorting // check if any entry needs resorting
if force_update if force_update || self.entries.iter().any(|entry| entry.needs_resorting()) {
|| self
.leaderboard_entries
.iter()
.any(|entry| entry.needs_resorting())
{
write_log!("leader board update required"); write_log!("leader board update required");
self.leaderboard_entries self.entries
.sort_by(|lhs, rhs| lhs.place().cmp(&rhs.place())); .sort_by(|lhs, rhs| lhs.place().cmp(&rhs.place()));
for (i, entry) in self.leaderboard_entries.iter_mut().enumerate() { for (i, entry) in self.entries.iter_mut().enumerate() {
entry.resorting_finished(); entry.resorting_finished();
// don't break here, just skip adding to grid
// because resorting_finished should be called for every entry
if i < self.leaderboard_grid.dimensions().1 {
entry.change_background_color(self.entry_backgrounds[i % 2])?; entry.change_background_color(self.entry_backgrounds[i % 2])?;
self.leaderboard_grid.attach(entry.snippet(), 0, i, 1, 1)?; self.main_grid.attach(entry.snippet(), 0, i, 1, 1)?;
} }
} }
self.leaderboard_redraw = true; if self.entries.is_empty() {
} self.gui.disable()?;
// update delta board
if self.last_player_id != -1 {
write_log!("update delta board");
if let Some((index, _player_entry)) = self
.leaderboard_entries
.iter()
.enumerate()
.find(|(_index, entry)| entry.id() == self.last_player_id)
{
let mut start_index = if index >= 2 { index - 2 } else { 0 };
let max = self.leaderboard_entries.len().min(5);
write_log!(format!(
"Delta Board: start {} - count {}",
start_index, max
));
// clear old entries
for i in 0..5 {
if let Some(child) = self.deltaboard_grid.detach(0, i)? {
if let Some(visiblity) = child.visibility() {
visiblity.set_visibility(false)?;
}
}
}
// add new entries
for i in 0..max {
if let Some(leaderboard_entry) = self.leaderboard_entries.get(start_index) {
if let Some(entry) = self.deltaboard_entries.get_mut(i) {
entry.change_id(leaderboard_entry.id());
entry.update_place(leaderboard_entry.place())?;
if entry.id() == self.last_player_id {
entry.change_background_color(self.player_background)?;
} else { } else {
entry.change_background_color(self.entry_backgrounds[i % 2])?; self.gui.enable()?;
}
if entry.name() != leaderboard_entry.name() {
entry.change_name(leaderboard_entry.name().to_string())?;
}
if let Some(vehicle_scoring) = vehicle_scorings
.iter()
.find(|scoring| scoring.mID == entry.id())
{
entry.update_time_behind_next(vehicle_scoring.mTimeBehindNext)?;
}
self.deltaboard_grid.attach(entry.snippet(), 0, i, 1, 1)?;
}
start_index += 1;
}
}
}
} }
Ok(()) Ok(())
} }
fn race_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
self.update_leaderboard(vehicle_scorings, |entry, scoring| {
let laps_behind = scoring.mLapsBehindLeader;
if laps_behind != 0 {
entry.update_time_behind_leader(BehindLeader::Laps(laps_behind))
} else {
entry.update_time_behind_leader(BehindLeader::Time(scoring.mTimeBehindLeader))
}
})
}
fn quali_leaderboard(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> {
self.update_leaderboard(vehicle_scorings, |entry, scoring| {
entry.update_best_lap(scoring.mBestLapTime)
})
}
} }
impl UiOverlay for LeaderBoard {} impl UiOverlay for LeaderBoard {}
impl DataReceiver for LeaderBoard { impl DataReceiver for LeaderBoard {
fn game_phase_change(&mut self, phase: GamePhase) -> Result<()> {
match phase {
GamePhase::Practice | GamePhase::Qualifying => {
for entry in self.leaderboard_entries.iter_mut() {
entry.update_best_lap(-1.0)?;
}
self.leaderboard.enable()?;
self.deltaboard.enable()?;
}
GamePhase::Race => {
for entry in self.leaderboard_entries.iter_mut() {
entry.update_time_behind_leader(BehindLeader::Time(0.0))?;
}
self.leaderboard.enable()?;
self.deltaboard.enable()?;
}
_ => {
self.last_player_id = -1;
self.leaderboard.disable()?;
self.deltaboard.disable()?;
}
}
Ok(())
}
fn update_for_phase(&self, phase: GamePhase) -> bool {
match phase {
GamePhase::Practice | GamePhase::Qualifying | GamePhase::Race => true,
_ => false,
}
}
fn scoring_update( fn scoring_update(
&mut self, &mut self,
phase: GamePhase, phase: GamePhase,
@ -307,15 +141,13 @@ impl DataReceiver for LeaderBoard {
write_log!("=================== leader board: scoring update ==================="); write_log!("=================== leader board: scoring update ===================");
match phase { match phase {
GamePhase::Practice | GamePhase::Qualifying => { GamePhase::TestDay => self.gui.disable()?,
self.quali_leaderboard(vehicle_scorings)?; GamePhase::Practice => self.gui.disable()?,
} GamePhase::Qualifying => self.gui.disable()?,
GamePhase::Warmup => self.gui.disable()?,
GamePhase::Race => { GamePhase::Race => {
self.race_leaderboard(vehicle_scorings)?; self.race_leaderboard(vehicle_scorings)?;
} }
_ => (),
} }
write_log!("leader board update finished"); write_log!("leader board update finished");
@ -328,28 +160,132 @@ impl DataReceiver for LeaderBoard {
player_id: Option<i32>, player_id: Option<i32>,
_telemetries: &[rF2VehicleTelemetry], _telemetries: &[rF2VehicleTelemetry],
) -> Result<()> { ) -> Result<()> {
if self.leaderboard_redraw {
self.leaderboard_redraw = false;
if let Some(player_id) = player_id { if let Some(player_id) = player_id {
if let Some(entry) = self if let Some(entry) = self.entries.iter().find(|entry| entry.id() == player_id) {
.leaderboard_entries
.iter()
.find(|entry| entry.id() == player_id)
{
write_log!(format!(
"Update player entry background color: {:?}",
self.player_background
));
entry.change_background_color(self.player_background)?; entry.change_background_color(self.player_background)?;
} }
} }
}
if let Some(player_id) = player_id {
self.last_player_id = player_id;
}
Ok(()) Ok(())
} }
} }
struct LeaderBoardEntry {
id: i32,
name: String,
place: u8,
time_behind_leader: f64,
time_behind_next: f64,
snippet: Arc<GuiSnippet>,
grid: Arc<Grid>,
name_label: Arc<Label>,
place_label: Arc<Label>,
time_behind_label: Arc<Label>,
place_updated: bool,
}
impl LeaderBoardEntry {
pub fn new(
gui_handler: &Arc<GuiHandler>,
id: i32,
name: String,
place: u8,
time_behind_leader: f64,
time_behind_next: f64,
) -> Result<Self> {
let snippet = GuiSnippet::from_str(gui_handler, LeaderBoard::ENTRY)?;
let background = snippet.element("grid")?;
let name_label: Arc<Label> = snippet.element("name")?;
let place_label: Arc<Label> = snippet.element("place")?;
let time_behind_label: Arc<Label> = snippet.element("time_behind")?;
name_label.set_text(&name)?;
place_label.set_text(place)?;
time_behind_label.set_text(format!("{:.3}", time_behind_leader))?;
Ok(Self {
id,
name,
place,
time_behind_leader,
time_behind_next,
snippet,
grid: background,
name_label,
place_label,
time_behind_label,
place_updated: true,
})
}
pub fn id(&self) -> i32 {
self.id
}
pub fn place(&self) -> u8 {
self.place
}
pub fn name(&self) -> &str {
&self.name
}
pub fn change_name(&mut self, name: String) -> Result<()> {
self.name = name;
self.name_label.set_text(&self.name)
}
pub fn snippet(&self) -> Arc<GuiSnippet> {
self.snippet.clone()
}
pub fn change_background_color(&self, color: Color) -> Result<()> {
self.grid.set_background(color)
}
pub fn update_place(&mut self, place: u8) -> Result<()> {
if self.place != place {
self.place_updated = true;
}
self.place = place;
self.place_label.set_text(self.place)
}
pub fn update_time_behind_leader(&mut self, time: f64) -> Result<()> {
self.time_behind_leader = time;
// check if we are leader
if self.time_behind_leader == 0.0 {
self.time_behind_label.set_text("---")
} else {
self.time_behind_label
.set_text(format!("+{:.3}", self.time_behind_leader))
}
}
pub fn update_time_behind_next(&mut self, time: f64) -> Result<()> {
self.time_behind_next = time;
Ok(())
}
pub fn needs_resorting(&self) -> bool {
self.place_updated
}
pub fn resorting_finished(&mut self) {
self.place_updated = false;
}
}

View file

@ -26,8 +26,6 @@ pub struct Pedals {
throttle: Arc<ProgressBar>, throttle: Arc<ProgressBar>,
_history: Arc<Icon>, _history: Arc<Icon>,
enable: bool,
throttle_samples: HeapRb<f32>, throttle_samples: HeapRb<f32>,
brake_samples: HeapRb<f32>, brake_samples: HeapRb<f32>,
@ -140,8 +138,6 @@ impl Pedals {
throttle, throttle,
_history: history, _history: history,
enable: false,
throttle_samples, throttle_samples,
brake_samples, brake_samples,
@ -206,7 +202,7 @@ impl Pedals {
let command_buffer = let command_buffer =
CommandBuffer::new_primary().build(self.device.clone(), self.queue.clone())?; CommandBuffer::new_primary().build(self.device.clone(), self.queue.clone())?;
if self.enable { {
let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new(
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
| VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
@ -235,30 +231,6 @@ impl Pedals {
impl UiOverlay for Pedals {} impl UiOverlay for Pedals {}
impl DataReceiver for Pedals { impl DataReceiver for Pedals {
fn game_phase_change(&mut self, phase: GamePhase) -> Result<()> {
match phase {
GamePhase::None | GamePhase::TestDay => {
self.enable = false;
self.gui.disable()?;
}
_ => {
self.enable = true;
self.gui.enable()?;
}
}
Ok(())
}
fn update_for_phase(&self, phase: GamePhase) -> bool {
match phase {
GamePhase::Practice | GamePhase::Qualifying | GamePhase::Race | GamePhase::Warmup => {
true
}
_ => false,
}
}
fn scoring_update( fn scoring_update(
&mut self, &mut self,
_phase: GamePhase, _phase: GamePhase,
@ -272,7 +244,10 @@ impl DataReceiver for Pedals {
player_id: Option<i32>, player_id: Option<i32>,
telemetries: &[rF2VehicleTelemetry], telemetries: &[rF2VehicleTelemetry],
) -> Result<()> { ) -> Result<()> {
if let Some(id) = player_id { match player_id {
Some(id) => {
self.gui.enable()?;
if let Some(telemetry) = telemetries.iter().find(|telemetry| telemetry.id == id) { if let Some(telemetry) = telemetries.iter().find(|telemetry| telemetry.id == id) {
let brake = 1.0 - telemetry.unfiltered_brake as f32; let brake = 1.0 - telemetry.unfiltered_brake as f32;
let throttle = 1.0 - telemetry.unfiltered_throttle as f32; let throttle = 1.0 - telemetry.unfiltered_throttle as f32;
@ -286,6 +261,10 @@ impl DataReceiver for Pedals {
self.update_vertex_buffers()?; self.update_vertex_buffers()?;
} }
} }
None => {
self.gui.disable()?;
}
}
Ok(()) Ok(())
} }

View file

@ -1,4 +1,4 @@
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?> <?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
<root reference_width="2560" reference_height="1440"> <root reference_width="2560" reference_height="1440">
<grid x_dim="10" y_dim="2" x_offset="-850" y_offset="-190" width="200" height="160" <grid x_dim="10" y_dim="2" x_offset="-850" y_offset="-190" width="200" height="160"
vert_align="bottom" hori_align="right" margin="3" padding="3" background="#686868"> vert_align="bottom" hori_align="right" margin="3" padding="3" background="#686868">

View file

@ -73,8 +73,6 @@ pub struct Radar {
car_width: f32, car_width: f32,
car_height: f32, car_height: f32,
enable: bool,
device: Arc<Device>, device: Arc<Device>,
queue: Arc<Mutex<Queue>>, queue: Arc<Mutex<Queue>>,
@ -173,8 +171,6 @@ impl Radar {
car_width, car_width,
car_height, car_height,
enable: false,
device, device,
queue, queue,
@ -220,7 +216,7 @@ impl Radar {
let command_buffer = let command_buffer =
CommandBuffer::new_primary().build(self.device.clone(), self.queue.clone())?; CommandBuffer::new_primary().build(self.device.clone(), self.queue.clone())?;
if self.enable { {
let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( let mut recorder = command_buffer.begin(VkCommandBufferBeginInfo::new(
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
| VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, | VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
@ -272,24 +268,6 @@ impl Radar {
impl UiOverlay for Radar {} impl UiOverlay for Radar {}
impl DataReceiver for Radar { impl DataReceiver for Radar {
fn game_phase_change(&mut self, phase: GamePhase) -> Result<()> {
match phase {
GamePhase::None | GamePhase::TestDay => self.enable = false,
_ => self.enable = true,
}
Ok(())
}
fn update_for_phase(&self, phase: GamePhase) -> bool {
match phase {
GamePhase::Practice | GamePhase::Qualifying | GamePhase::Race | GamePhase::Warmup => {
true
}
_ => false,
}
}
fn scoring_update( fn scoring_update(
&mut self, &mut self,
_phase: GamePhase, _phase: GamePhase,
@ -308,7 +286,6 @@ impl DataReceiver for Radar {
self.cars.clear(); self.cars.clear();
if self.enable {
if let Some(player_id) = player_id { if let Some(player_id) = player_id {
// make sure there are enough cars in buffer // make sure there are enough cars in buffer
if self.car_handles.len() < telemetries.len() { if self.car_handles.len() < telemetries.len() {
@ -349,8 +326,7 @@ impl DataReceiver for Radar {
// check if car is close enough to the players car // check if car is close enough to the players car
if distance < self.config.radar_car_distance { if distance < self.config.radar_car_distance {
let offset = let offset = diff.xz() * (self.radar_extent / self.config.radar_car_distance);
diff.xz() * (self.radar_extent / self.config.radar_car_distance);
let buffered_car = self.car_handles[buffer_car_index].clone(); let buffered_car = self.car_handles[buffer_car_index].clone();
buffer_car_index += 1; buffer_car_index += 1;
@ -371,7 +347,6 @@ impl DataReceiver for Radar {
} }
write_log!(format!("other cars: {:?}", self.cars.len())); write_log!(format!("other cars: {:?}", self.cars.len()));
}
Ok(()) Ok(())
} }

View file

@ -26,7 +26,11 @@ impl Watermark {
impl UiOverlay for Watermark {} impl UiOverlay for Watermark {}
impl DataReceiver for Watermark { impl DataReceiver for Watermark {
fn game_phase_change(&mut self, phase: GamePhase) -> Result<()> { fn scoring_update(
&mut self,
phase: GamePhase,
_vehicle_scoring: &[VehicleScoringInfoV01],
) -> Result<()> {
match phase { match phase {
GamePhase::TestDay => self.gui.enable()?, GamePhase::TestDay => self.gui.enable()?,
_ => self.gui.disable()?, _ => self.gui.disable()?,
@ -35,20 +39,6 @@ impl DataReceiver for Watermark {
Ok(()) Ok(())
} }
fn update_for_phase(&self, phase: GamePhase) -> bool {
match phase {
_ => false,
}
}
fn scoring_update(
&mut self,
_phase: GamePhase,
_vehicle_scoring: &[VehicleScoringInfoV01],
) -> Result<()> {
Ok(())
}
fn telemetry_update( fn telemetry_update(
&mut self, &mut self,
_player_id: Option<i32>, _player_id: Option<i32>,

View file

@ -1,4 +1,4 @@
<?xml-model href="../gui.xsd" type="application/xml" schematypes="http://www.w3.org/2001/XMLSchema"?> <?xml-model href="../gui.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
<root reference_width="2560" reference_height="1440"> <root reference_width="2560" reference_height="1440">
<grid x_dim="1" y_dim="1" x_offset="10" y_offset="10" width="300" height="50" <grid x_dim="1" y_dim="1" x_offset="10" y_offset="10" width="300" height="50"
vert_align="top" hori_align="left" margin="2" padding="2" background="#c9c9c9"> vert_align="top" hori_align="left" margin="2" padding="2" background="#c9c9c9">

View file

@ -1,21 +1,13 @@
use anyhow::Result; use anyhow::Result;
use rfactor_sm_reader::*; use rfactor_sm_reader::*;
use std::{ use std::{cell::RefCell, rc::Rc, time::Instant};
cell::RefCell,
rc::Rc,
time::{Duration, Instant},
};
use crate::write_log; use crate::write_log;
use super::UiOverlay; use super::UiOverlay;
pub trait DataReceiver { pub trait DataReceiver {
fn game_phase_change(&mut self, phase: GamePhase) -> Result<()>;
fn update_for_phase(&self, phase: GamePhase) -> bool;
fn scoring_update( fn scoring_update(
&mut self, &mut self,
phase: GamePhase, phase: GamePhase,
@ -29,16 +21,13 @@ pub trait DataReceiver {
) -> Result<()>; ) -> Result<()>;
} }
const GAME_PHASE_TIME_OUT: Duration = Duration::from_secs(2); #[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum GamePhase { pub enum GamePhase {
TestDay, TestDay,
Practice, Practice,
Qualifying, Qualifying,
Warmup, Warmup,
Race, Race,
None,
} }
impl TryFrom<i32> for GamePhase { impl TryFrom<i32> for GamePhase {
@ -62,10 +51,8 @@ pub struct RFactorData {
telemetry_reader: TelemetryReader, telemetry_reader: TelemetryReader,
scoring_reader: ScoringReader, scoring_reader: ScoringReader,
last_scoring_read: Duration,
start_time: Instant, start_time: Instant,
player_id: Option<i32>, player_id: Option<i32>,
previous_game_phase: GamePhase,
receivers: Vec<Rc<RefCell<dyn UiOverlay>>>, receivers: Vec<Rc<RefCell<dyn UiOverlay>>>,
} }
@ -80,10 +67,8 @@ impl RFactorData {
telemetry_reader: TelemetryReader::new(start_time.elapsed().as_secs_f32())?, telemetry_reader: TelemetryReader::new(start_time.elapsed().as_secs_f32())?,
scoring_reader: ScoringReader::new(start_time.elapsed().as_secs_f32())?, scoring_reader: ScoringReader::new(start_time.elapsed().as_secs_f32())?,
last_scoring_read: start_time.elapsed(),
start_time, start_time,
player_id: None, player_id: None,
previous_game_phase: GamePhase::None,
receivers: Vec::new(), receivers: Vec::new(),
}) })
@ -101,10 +86,9 @@ impl RFactorData {
write_log!(" =================== update RFactorData ==================="); write_log!(" =================== update RFactorData ===================");
// get scoring info // get scoring info
match self.scoring_reader.vehicle_scoring(self.now()) { if let Some((scoring_info, vehicle_scorings)) =
Some((scoring_info, vehicle_scorings)) => { self.scoring_reader.vehicle_scoring(self.now())
self.last_scoring_read = self.start_time.elapsed(); {
write_log!(format!( write_log!(format!(
"new scoring info: vehicles: {}", "new scoring info: vehicles: {}",
scoring_info.mNumVehicles scoring_info.mNumVehicles
@ -123,44 +107,14 @@ impl RFactorData {
} }
} }
{
let phase = GamePhase::try_from(scoring_info.mSession)?; let phase = GamePhase::try_from(scoring_info.mSession)?;
if self.previous_game_phase != phase { write_log!(format!("GamePhase: {:?}", phase));
self.previous_game_phase = phase;
for receiver in self.receivers.iter() { for receiver in self.receivers.iter() {
receiver receiver
.borrow_mut() .borrow_mut()
.game_phase_change(self.previous_game_phase)?; .scoring_update(phase, &vehicle_scorings)?;
}
}
write_log!(format!("GamePhase: {:?}", self.previous_game_phase));
}
for receiver in self.receivers.iter() {
let mut rec_mut = receiver.borrow_mut();
if rec_mut.update_for_phase(self.previous_game_phase) {
rec_mut.scoring_update(self.previous_game_phase, &vehicle_scorings)?;
}
}
}
None => {
let now = self.start_time.elapsed();
if now > (self.last_scoring_read + GAME_PHASE_TIME_OUT) {
if self.previous_game_phase != GamePhase::None {
self.previous_game_phase = GamePhase::None;
for receiver in self.receivers.iter() {
receiver
.borrow_mut()
.game_phase_change(self.previous_game_phase)?;
}
}
}
} }
} }
@ -170,11 +124,9 @@ impl RFactorData {
write_log!("new telemetry update"); write_log!("new telemetry update");
for receiver in self.receivers.iter() { for receiver in self.receivers.iter() {
let mut rec_mut = receiver.borrow_mut(); receiver
.borrow_mut()
if rec_mut.update_for_phase(self.previous_game_phase) { .telemetry_update(self.player_id, &telemetries)?;
rec_mut.telemetry_update(self.player_id, &telemetries)?;
}
} }
} }