From 1c37ce60294d0bd7d690be2bfcd70344b5601908 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Wed, 18 Jan 2023 07:09:44 +0100 Subject: [PATCH] Add leader board --- Cargo.toml | 1 + src/overlay/elements/leaderboard.rs | 223 ++++++++++++++++++ src/overlay/elements/mod.rs | 2 + src/overlay/elements/pedals.rs | 6 - .../elements/ui_files/leaderboard_entry.xml | 7 + .../elements/ui_files/leaderboard_grid.xml | 5 + src/overlay/elements/watermark.rs | 10 +- src/overlay/mod.rs | 10 + 8 files changed, 250 insertions(+), 14 deletions(-) create mode 100644 src/overlay/elements/leaderboard.rs create mode 100644 src/overlay/elements/ui_files/leaderboard_entry.xml create mode 100644 src/overlay/elements/ui_files/leaderboard_grid.xml diff --git a/Cargo.toml b/Cargo.toml index 0891f89..1b3f910 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ crate-type = ["cdylib"] [dependencies] vulkan-rs = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } assetpath = { git = "https://gavania.de/hodasemi/vulkan_lib.git" } +utilities = { git = "https://gavania.de/hodasemi/utilities.git" } rfactor_sm_reader = { git = "https://gavania.de/hodasemi/rfactor_sm_reader.git" } ui = { git = "https://gavania.de/hodasemi/ui.git" } diff --git a/src/overlay/elements/leaderboard.rs b/src/overlay/elements/leaderboard.rs new file mode 100644 index 0000000..754166e --- /dev/null +++ b/src/overlay/elements/leaderboard.rs @@ -0,0 +1,223 @@ +use std::{ + ffi::{c_char, CStr}, + sync::Arc, +}; + +use anyhow::Result; +use rfactor_sm_reader::{rF2VehicleTelemetry, VehicleScoringInfoV01}; +use ui::prelude::*; +use utilities::prelude::Color; + +use crate::overlay::{rfactor_data::DataReceiver, UiOverlay}; + +pub struct LeaderBoard { + gui_handler: Arc, + gui: Arc, + main_grid: Arc, + + entries: Vec, + + entry_backgrounds: [Color; 2], +} + +impl LeaderBoard { + const GRID: &str = include_str!("ui_files/leaderboard_grid.xml"); + const ENTRY: &str = include_str!("ui_files/leaderboard_entry.xml"); + + pub fn new(gui_handler: &Arc) -> Result { + let gui = GuiBuilder::from_str(gui_handler, Self::GRID)?; + + let main_grid = gui.element("main_grid")?; + + Ok(Self { + gui_handler: gui_handler.clone(), + gui, + main_grid, + + entries: Vec::new(), + + entry_backgrounds: [Color::try_from("#838383")?, Color::try_from("#545454")?], + }) + } +} + +impl UiOverlay for LeaderBoard {} + +impl DataReceiver for LeaderBoard { + fn scoring_update(&mut self, vehicle_scorings: &[VehicleScoringInfoV01]) -> Result<()> { + for vehicle_scoring in vehicle_scorings { + // check driver list + match self + .entries + .iter_mut() + .find(|entry| vehicle_scoring.mID == entry.id) + { + Some(entry) => { + entry.update_place(vehicle_scoring.mPlace)?; + entry.update_time_behind_leader(vehicle_scoring.mTimeBehindLeader)?; + entry.update_time_behind_next(vehicle_scoring.mTimeBehindNext)?; + } + None => { + let entry = LeaderBoardEntry::new( + &self.gui_handler, + vehicle_scoring.mID, + unsafe { CStr::from_ptr(&vehicle_scoring.mDriverName as *const c_char) } + .to_str() + .unwrap() + .to_string(), + vehicle_scoring.mPlace, + vehicle_scoring.mTimeBehindLeader, + vehicle_scoring.mTimeBehindNext, + )?; + + self.entries.push(entry); + } + } + } + + // check if entry count in grid is the same as the gathered entries + let force_update = if self + .main_grid + .child_at(0, self.entries.len() - 1)? + .is_none() + { + for i in 0..self.entries.len() { + self.main_grid.detach(0, i)?; + } + + true + } else { + false + }; + + // check if any entry needs resorting + if force_update || self.entries.iter().any(|entry| entry.needs_resorting()) { + self.entries + .sort_by(|lhs, rhs| lhs.place().cmp(&rhs.place())); + + for (i, entry) in self.entries.iter_mut().enumerate() { + entry.resorting_finished(); + entry.change_background_color(self.entry_backgrounds[i % 2])?; + + self.main_grid.attach(entry.snippet(), 0, i, 1, 1)?; + } + } + + if self.entries.is_empty() { + self.gui.disable()?; + } else { + self.gui.enable()?; + } + + Ok(()) + } + + fn telemetry_update( + &mut self, + _player_id: Option, + _telemetries: &[rF2VehicleTelemetry], + ) -> Result<()> { + Ok(()) + } +} + +struct LeaderBoardEntry { + id: i32, + + name: String, + place: u8, + time_behind_leader: f64, + time_behind_next: f64, + + snippet: Arc, + + grid: Arc, + name_label: Arc