record-daemon: refactor client.rs, dropping metadata
All checks were successful
record-daemon / Build, check and test (push) Successful in 2m17s

This commit is contained in:
2026-03-26 11:03:24 +01:00
parent c576ac2b34
commit 2814d85b6b
6 changed files with 192 additions and 265 deletions

View File

@@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
// ============================================================================= // =============================================================================
/// Response from `/lol-summoner/v1/current-summoner` /// Response from `/lol-summoner/v1/current-summoner`
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct SummonerResponse { pub struct SummonerResponse {
/// Summoner ID. /// Summoner ID.
@@ -51,7 +51,7 @@ pub struct SummonerResponse {
// ============================================================================= // =============================================================================
/// Response from `/lol-gameflow/v1/session` /// Response from `/lol-gameflow/v1/session`
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct GameflowSessionResponse { pub struct GameflowSessionResponse {
/// Current gameflow phase. /// Current gameflow phase.
@@ -552,6 +552,80 @@ impl ChampionSelectResponse {
} }
} }
// =============================================================================
// Aggregated Data Containers
// =============================================================================
/// Container for pre-game data collected from multiple API calls.
/// Stores raw API responses directly for maximum flexibility.
#[derive(Debug, Clone, Default)]
pub struct PreGameData {
/// Session data (map, game mode, queue info).
pub session: Option<GameflowSessionResponse>,
/// Summoner data (puuid, name).
pub summoner: Option<SummonerResponse>,
/// Champion select data (champion, skin, team).
pub champion_select: Option<ChampionSelectResponse>,
/// Rune page data.
pub rune_page: Option<RunePageResponse>,
}
impl PreGameData {
/// Get the summoner name from any available source.
pub fn summoner_name(&self) -> Option<&str> {
self.summoner
.as_ref()
.and_then(|s| s.display_name.as_deref())
.or_else(|| self.summoner.as_ref().and_then(|s| s.name.as_deref()))
}
/// Get the PUUID.
pub fn puuid(&self) -> Option<&str> {
self.summoner.as_ref()?.puuid.as_deref()
}
/// Get the champion ID from champion select.
pub fn champion_id(&self) -> Option<u64> {
let cs = self.champion_select.as_ref()?;
let player = cs.get_local_player_selection()?;
player.champion_id
}
/// Get the team ID from champion select.
pub fn team(&self) -> Option<i64> {
let cs = self.champion_select.as_ref()?;
let player = cs.get_local_player_selection()?;
player.team
}
/// Get the skin ID from champion select.
pub fn skin_id(&self) -> Option<u64> {
let cs = self.champion_select.as_ref()?;
let player = cs.get_local_player_selection()?;
player.skin_id
}
/// Get the map name from session.
pub fn map_name(&self) -> Option<&str> {
self.session.as_ref()?.map.as_deref()
}
/// Get the game mode from session.
pub fn game_mode(&self) -> Option<&str> {
self.session.as_ref()?.game_mode.as_deref()
}
/// Get the queue ID from session.
pub fn queue_id(&self) -> Option<u64> {
self.session.as_ref()?.queue_id
}
/// Get the rune page name.
pub fn rune_page_name(&self) -> Option<&str> {
self.rune_page.as_ref()?.name.as_deref()
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@@ -12,14 +12,13 @@ use tracing::{debug, error, info, trace, warn};
use super::api_types::{ use super::api_types::{
ActivePlayerResponse, ChampionSelectResponse, EndOfGameStatsResponse, GameflowSessionResponse, ActivePlayerResponse, ChampionSelectResponse, EndOfGameStatsResponse, GameflowSessionResponse,
PlayerListResponse, RunePageResponse, SummonerResponse, PlayerListResponse, PreGameData, RunePageResponse, SummonerResponse,
}; };
use super::auth::LockfileCredentials; use super::auth::LockfileCredentials;
use super::endpoints; use super::endpoints;
use super::events::{GameEvent, ItemBuild}; use super::events::{GameEvent, ItemBuild};
use super::items::{parse_items_from_game_stats, parse_items_from_live_client}; use super::items::{parse_items_from_game_stats, parse_items_from_live_client};
use super::mappings::{champion_id_to_name, spell_id_to_name}; use super::mappings::{champion_id_to_name, spell_id_to_name};
use super::metadata::{GameEndMetadata, PreGameMetadata};
use super::state::{ClientState, GameflowPhase}; use super::state::{ClientState, GameflowPhase};
use super::tls::create_insecure_tls_config; use super::tls::create_insecure_tls_config;
use super::websocket::parse_websocket_message; use super::websocket::parse_websocket_message;
@@ -478,69 +477,47 @@ impl LqpClient {
// Metadata Fetching Methods // Metadata Fetching Methods
// ========================================================================= // =========================================================================
/// Fetch pre-game metadata (champion, skin, runes, queue info). /// Fetch pre-game data (stores raw API responses directly).
pub async fn fetch_pregame_metadata(&self) -> Result<PreGameMetadata> { pub async fn fetch_pregame_data(&self) -> Result<PreGameData> {
let mut metadata = PreGameMetadata::default(); let mut data = PreGameData::default();
// Get session info for queue type and game mode // Fetch all API responses in parallel where possible
if let Ok(session) = self.get_session_typed().await { let (session, summoner, champ_select, rune_page) = tokio::join!(
metadata.map_name = session.map; self.get_session_typed(),
metadata.game_mode = session.game_mode; self.get_summoner_typed(),
metadata.queue_id = session.queue_id.map(|id| id as u32); self.get_champion_select_typed(),
self.get_rune_page_typed()
);
// Store session data
if let Ok(session) = session {
data.session = Some(session);
} }
// Get summoner info (including puuid) // Store summoner data and update state
if let Ok(summoner) = self.get_summoner_typed().await { if let Ok(summoner) = summoner {
metadata.summoner_name = summoner.display_name; if let Some(ref puuid) = summoner.puuid {
if let Some(puuid) = &summoner.puuid {
self.state.write().await.local_puuid = Some(puuid.clone()); self.state.write().await.local_puuid = Some(puuid.clone());
} }
metadata.local_puuid = summoner.puuid; data.summoner = Some(summoner);
} }
// Get champion select info // Store champion select data
if let Ok(champ_select) = self.get_champion_select_typed().await { if let Ok(champ_select) = champ_select {
if let Some(player) = champ_select.get_local_player_selection() { data.champion_select = Some(champ_select);
metadata.champion_id = player.champion_id.map(|id| id as u32);
metadata.team = player.team.map(|id| id as u32);
metadata.skin_id = player.skin_id.map(|id| id as u32);
}
} }
// Get rune page // Store rune page data
if let Ok(rune_page) = self.get_rune_page_typed().await { if let Ok(rune_page) = rune_page {
metadata.rune_page_name = rune_page.name; data.rune_page = Some(rune_page);
} }
Ok(metadata) Ok(data)
} }
/// Fetch end-of-game stats. /// Fetch end-of-game stats (returns raw API response).
pub async fn fetch_game_end_stats(&self) -> Result<GameEndMetadata> { pub async fn fetch_game_end_stats(&self) -> Result<EndOfGameStatsResponse> {
let mut metadata = GameEndMetadata::default(); self.get_game_stats_typed().await
if let Ok(stats) = self.get_game_stats_typed().await {
metadata.victory = Some(stats.is_victory());
metadata.game_duration = stats.game_length.unwrap_or(0.0);
metadata.match_id = stats.match_id.map(|id| id.to_string());
// Extract player stats
if let Some(player) = stats.get_local_player() {
if let Some(player_stats) = &player.stats {
metadata.kills = player_stats.champions_killed.unwrap_or(0) as u32;
metadata.deaths = player_stats.num_deaths.unwrap_or(0) as u32;
metadata.assists = player_stats.assists.unwrap_or(0) as u32;
metadata.creep_score = player_stats.minions_killed.unwrap_or(0) as u32;
metadata.gold_earned = player_stats.gold_earned.unwrap_or(0) as u32;
metadata.damage_dealt =
player_stats.total_damage_dealt_to_champions.unwrap_or(0);
metadata.damage_taken = player_stats.total_damage_taken.unwrap_or(0);
metadata.vision_score = player_stats.vision_score.unwrap_or(0.0);
}
}
}
Ok(metadata)
} }
/// Fetch complete player game metadata including runes, summoner spells, and items. /// Fetch complete player game metadata including runes, summoner spells, and items.

View File

@@ -1,77 +0,0 @@
//! Metadata structures for pre-game and end-of-game data.
//!
//! Contains structures for capturing game information before and after matches.
/// Pre-game metadata fetched before the game starts.
#[derive(Debug, Clone, Default)]
pub struct PreGameMetadata {
/// Champion ID selected.
pub champion_id: Option<u32>,
/// Skin ID selected.
pub skin_id: Option<u32>,
/// Name of the rune page.
pub rune_page_name: Option<String>,
/// Summoner name.
pub summoner_name: Option<String>,
/// Queue type (e.g., "RANKED_SOLO_5x5").
pub queue_type: Option<String>,
/// Queue ID.
pub queue_id: Option<u32>,
/// Game mode (e.g., "CLASSIC", "ARAM").
pub game_mode: Option<String>,
/// Map name.
pub map_name: Option<String>,
/// Team ID (100 or 200).
pub team: Option<u32>,
/// Local player's PUUID.
pub local_puuid: Option<String>,
}
/// End-of-game metadata fetched after the game ends.
#[derive(Debug, Clone, Default)]
pub struct GameEndMetadata {
/// Whether the player won.
pub victory: Option<bool>,
/// Match ID.
pub match_id: Option<String>,
/// Number of kills.
pub kills: u32,
/// Number of deaths.
pub deaths: u32,
/// Number of assists.
pub assists: u32,
/// Creep score (minions killed).
pub creep_score: u32,
/// Gold earned.
pub gold_earned: u32,
/// Damage dealt to champions.
pub damage_dealt: u64,
/// Damage taken.
pub damage_taken: u64,
/// Vision score.
pub vision_score: f64,
/// Game duration in seconds.
pub game_duration: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pre_game_metadata_default() {
let metadata = PreGameMetadata::default();
assert!(metadata.champion_id.is_none());
assert!(metadata.skin_id.is_none());
assert!(metadata.summoner_name.is_none());
}
#[test]
fn test_game_end_metadata_default() {
let metadata = GameEndMetadata::default();
assert!(metadata.victory.is_none());
assert_eq!(metadata.kills, 0);
assert_eq!(metadata.deaths, 0);
assert_eq!(metadata.assists, 0);
}
}

View File

@@ -10,7 +10,6 @@ mod endpoints;
mod events; mod events;
mod items; mod items;
mod mappings; mod mappings;
mod metadata;
mod state; mod state;
mod tls; mod tls;
mod websocket; mod websocket;
@@ -18,7 +17,7 @@ mod websocket;
pub use api_types::{ pub use api_types::{
ActivePlayerResponse, ChampionSelectPlayer, ChampionSelectResponse, EndOfGamePlayer, ActivePlayerResponse, ChampionSelectPlayer, ChampionSelectResponse, EndOfGamePlayer,
EndOfGameStatsResponse, EndOfGameTeam, GameData, GameflowSessionResponse, LiveClientItem, EndOfGameStatsResponse, EndOfGameTeam, GameData, GameflowSessionResponse, LiveClientItem,
LiveClientPlayer, PlayerListResponse, PlayerStats, QueueData, RunePageResponse, LiveClientPlayer, PlayerListResponse, PlayerStats, PreGameData, QueueData, RunePageResponse,
SummonerResponse, SummonerSpellsData, TeamPlayer, Timers, SummonerResponse, SummonerSpellsData, TeamPlayer, Timers,
}; };
pub use auth::{LockfileCredentials, LockfileWatcher}; pub use auth::{LockfileCredentials, LockfileWatcher};
@@ -37,6 +36,5 @@ pub use events::{
}; };
pub use items::{parse_items_from_game_stats, parse_items_from_live_client}; pub use items::{parse_items_from_game_stats, parse_items_from_live_client};
pub use mappings::{champion_id_to_name, map_id_to_name, spell_id_to_name}; pub use mappings::{champion_id_to_name, map_id_to_name, spell_id_to_name};
pub use metadata::{GameEndMetadata, PreGameMetadata};
pub use state::{ClientState, GameflowPhase}; pub use state::{ClientState, GameflowPhase};
pub use websocket::{parse_event_from_uri, parse_websocket_message}; pub use websocket::{parse_event_from_uri, parse_websocket_message};

View File

@@ -56,8 +56,8 @@ struct Daemon {
event_mapper: Arc<RwLock<EventMapper>>, event_mapper: Arc<RwLock<EventMapper>>,
/// Current recording ID (if recording). /// Current recording ID (if recording).
current_recording_id: Arc<RwLock<Option<uuid::Uuid>>>, current_recording_id: Arc<RwLock<Option<uuid::Uuid>>>,
/// Pre-game metadata (collected before game starts). /// Pre-game data (collected before game starts).
pregame_metadata: Arc<RwLock<Option<record_daemon::lqp::PreGameMetadata>>>, pregame_data: Arc<RwLock<Option<record_daemon::lqp::PreGameData>>>,
/// IPC server. /// IPC server.
ipc_server: Option<IpcServer>, ipc_server: Option<IpcServer>,
/// Shutdown signal. /// Shutdown signal.
@@ -77,7 +77,7 @@ impl Daemon {
timeline_store: Arc::new(RwLock::new(TimelineStore::new())), timeline_store: Arc::new(RwLock::new(TimelineStore::new())),
event_mapper: Arc::new(RwLock::new(EventMapper::new())), event_mapper: Arc::new(RwLock::new(EventMapper::new())),
current_recording_id: Arc::new(RwLock::new(None)), current_recording_id: Arc::new(RwLock::new(None)),
pregame_metadata: Arc::new(RwLock::new(None)), pregame_data: Arc::new(RwLock::new(None)),
ipc_server: None, ipc_server: None,
shutdown_tx, shutdown_tx,
} }
@@ -229,17 +229,17 @@ impl Daemon {
async fn handle_game_event(&self, event: GameEvent) -> Result<()> { async fn handle_game_event(&self, event: GameEvent) -> Result<()> {
info!("[EVENT_HANDLER] Game event received: {:?}", event); info!("[EVENT_HANDLER] Game event received: {:?}", event);
// Handle pre-game metadata collection // Handle pre-game data collection
match &event { match &event {
GameEvent::PhaseChange(info) if info.phase == "ChampSelect" => { GameEvent::PhaseChange(info) if info.phase == "ChampSelect" => {
info!("[EVENT_HANDLER] Champion select started, fetching pre-game metadata"); info!("[EVENT_HANDLER] Champion select started, fetching pre-game data");
match self.lqp_client.fetch_pregame_metadata().await { match self.lqp_client.fetch_pregame_data().await {
Ok(metadata) => { Ok(data) => {
info!("[EVENT_HANDLER] Pre-game metadata fetched: {:?}", metadata); info!("[EVENT_HANDLER] Pre-game data fetched: {:?}", data);
*self.pregame_metadata.write() = Some(metadata); *self.pregame_data.write() = Some(data);
} }
Err(e) => { Err(e) => {
warn!("[EVENT_HANDLER] Failed to fetch pre-game metadata: {}", e); warn!("[EVENT_HANDLER] Failed to fetch pre-game data: {}", e);
} }
} }
} }
@@ -248,13 +248,8 @@ impl Daemon {
"[EVENT_HANDLER] Local player picked champion: {}", "[EVENT_HANDLER] Local player picked champion: {}",
pick.champion_name pick.champion_name
); );
let mut metadata = self.pregame_metadata.write(); // Champion pick info is now stored in champion_select response
if let Some(ref mut meta) = *metadata { // No need to manually update - refetch if needed
meta.champion_id = Some(pick.champion_id);
if let Some(_skin_name) = &pick.skin_name {
// Store skin name for later use
}
}
} }
GameEvent::GameStart(info) => { GameEvent::GameStart(info) => {
info!( info!(
@@ -262,13 +257,13 @@ impl Daemon {
info.queue_type, info.game_mode, info.map_name info.queue_type, info.game_mode, info.map_name
); );
// Update pre-game metadata with game start info // Update pre-game data with game start info if needed
let mut pregame = self.pregame_metadata.write(); let mut pregame = self.pregame_data.write();
// Extract player-specific data from session using puuid // Extract player-specific data from session using puuid
let (champion_id, team, summoner_name) = if let Some(ref session) = info.session { let (_champion_id, _team, summoner_name) = if let Some(ref session) = info.session {
// Get puuid from pregame metadata (fetched earlier) // Get puuid from pregame data (fetched earlier)
let puuid = pregame.as_ref().and_then(|m| m.local_puuid.as_ref()); let puuid = pregame.as_ref().and_then(|d| d.puuid());
if let Some(puuid) = puuid { if let Some(puuid) = puuid {
// Use the puuid to find the correct player's data // Use the puuid to find the correct player's data
@@ -289,45 +284,24 @@ impl Daemon {
(None, None, None) (None, None, None)
}; };
if let Some(ref mut meta) = *pregame { // If we don't have pre-game data, create minimal from game start info
// Fill in champion_id from session if not already set if pregame.is_none() {
if meta.champion_id.is_none() { use record_daemon::lqp::{GameflowSessionResponse, PreGameData};
meta.champion_id = champion_id; *pregame = Some(PreGameData {
} session: Some(GameflowSessionResponse {
// Fill in team from session if not already set map: info.map_name.clone(),
if meta.team.is_none() {
meta.team = team;
}
// Fill in summoner_name from session if not already set
if meta.summoner_name.is_none() {
meta.summoner_name = summoner_name;
}
// Fill in queue info
if meta.queue_type.is_none() {
meta.queue_type = info.queue_type.clone();
}
if meta.queue_id.is_none() {
meta.queue_id = info.queue_id;
}
if meta.game_mode.is_none() {
meta.game_mode = info.game_mode.clone();
}
if meta.map_name.is_none() {
meta.map_name = info.map_name.clone();
}
} else {
// Create pre-game metadata from game start info
*pregame = Some(record_daemon::lqp::PreGameMetadata {
summoner_name,
champion_id,
skin_id: None,
rune_page_name: None,
queue_type: info.queue_type.clone(),
queue_id: info.queue_id,
game_mode: info.game_mode.clone(), game_mode: info.game_mode.clone(),
map_name: info.map_name.clone(), queue_id: info.queue_id.map(|id| id as u64),
team, ..Default::default()
local_puuid: None, }),
summoner: summoner_name.map(|name| {
use record_daemon::lqp::SummonerResponse;
SummonerResponse {
display_name: Some(name),
..Default::default()
}
}),
..Default::default()
}); });
} }
} }
@@ -434,7 +408,7 @@ impl Daemon {
} }
// Get pre-game metadata to pass to recording // Get pre-game metadata to pass to recording
let pregame = self.pregame_metadata.read().clone(); let pregame = self.pregame_data.read().clone();
let champion_name = champion.or_else(|| { let champion_name = champion.or_else(|| {
pregame.as_ref().and({ pregame.as_ref().and({
// Try to get champion name from metadata if not provided // Try to get champion name from metadata if not provided
@@ -519,49 +493,16 @@ impl Daemon {
let fetched_final_items = let fetched_final_items =
self.lqp_client.fetch_final_items().await.ok().flatten(); self.lqp_client.fetch_final_items().await.ok().flatten();
// Convert GameEndInfo to GameEndMetadata if available // Fetch end-of-game stats from API
let game_end_metadata = let game_end_stats = self.lqp_client.fetch_game_end_stats().await.ok();
game_end_info.map(|info| record_daemon::lqp::GameEndMetadata {
victory: Some(info.victory),
match_id: None,
kills: info.stats.as_ref().map(|s| s.kills).unwrap_or(0),
deaths: info.stats.as_ref().map(|s| s.deaths).unwrap_or(0),
assists: info.stats.as_ref().map(|s| s.assists).unwrap_or(0),
creep_score: info
.stats
.as_ref()
.map(|s| s.minions_killed)
.unwrap_or(0),
gold_earned: info
.stats
.as_ref()
.map(|s| s.gold_earned)
.unwrap_or(0),
damage_dealt: info
.stats
.as_ref()
.map(|s| s.damage_dealt)
.unwrap_or(0),
damage_taken: info
.stats
.as_ref()
.map(|s| s.damage_taken)
.unwrap_or(0),
vision_score: info
.stats
.as_ref()
.map(|s| s.vision_score)
.unwrap_or(0.0),
game_duration: info.duration,
});
info!( info!(
"[EVENT_HANDLER] Game end metadata from event: {:?}", "[EVENT_HANDLER] Game end stats from API: {:?}",
game_end_metadata game_end_stats
); );
if let Err(e) = self if let Err(e) = self
.stop_recording_with_metadata(game_end_metadata, fetched_final_items) .stop_recording_with_metadata(game_end_stats, fetched_final_items)
.await .await
{ {
error!("[EVENT_HANDLER] Failed to stop recording: {}", e); error!("[EVENT_HANDLER] Failed to stop recording: {}", e);
@@ -569,8 +510,8 @@ impl Daemon {
// Don't propagate error - keep daemon running // Don't propagate error - keep daemon running
} }
// Clear pre-game metadata // Clear pre-game data
*self.pregame_metadata.write() = None; *self.pregame_data.write() = None;
} }
_ => {} _ => {}
} }
@@ -656,10 +597,10 @@ impl Daemon {
self.stop_recording_with_metadata(None, None).await self.stop_recording_with_metadata(None, None).await
} }
/// Stop recording with optional game end metadata. /// Stop recording with optional game end stats.
async fn stop_recording_with_metadata( async fn stop_recording_with_metadata(
&self, &self,
game_end_metadata: Option<record_daemon::lqp::GameEndMetadata>, game_end_stats: Option<record_daemon::lqp::EndOfGameStatsResponse>,
final_items: Option<record_daemon::lqp::ItemBuild>, final_items: Option<record_daemon::lqp::ItemBuild>,
) -> Result<()> { ) -> Result<()> {
info!("Stopping recording"); info!("Stopping recording");
@@ -671,7 +612,7 @@ impl Daemon {
let recording_engine = self.recording_engine.clone(); let recording_engine = self.recording_engine.clone();
let event_mapper = self.event_mapper.clone(); let event_mapper = self.event_mapper.clone();
let timeline_store = self.timeline_store.clone(); let timeline_store = self.timeline_store.clone();
let pregame_metadata = self.pregame_metadata.read().clone(); let pregame_data = self.pregame_data.read().clone();
// Use spawn_blocking to avoid blocking the async runtime // Use spawn_blocking to avoid blocking the async runtime
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
@@ -698,36 +639,44 @@ impl Daemon {
// Update metadata if we have it // Update metadata if we have it
let mut update = record_daemon::timeline::MetadataUpdate::default(); let mut update = record_daemon::timeline::MetadataUpdate::default();
// Add pre-game metadata // Add pre-game data
if let Some(pregame) = pregame_metadata { if let Some(pregame) = pregame_data {
// Convert champion_id to name if available // Convert champion_id to name if available
if let Some(champion_id) = pregame.champion_id { if let Some(champion_id) = pregame.champion_id() {
update.champion = record_daemon::lqp::champion_id_to_name(champion_id); update.champion =
record_daemon::lqp::champion_id_to_name(champion_id as u32);
} }
update.summoner_name = pregame.summoner_name; update.summoner_name = pregame.summoner_name().map(|s| s.to_string());
update.queue_type = pregame.queue_type; update.queue_id = pregame.queue_id().map(|id| id as u32);
update.queue_id = pregame.queue_id; update.game_mode = pregame.game_mode().map(|s| s.to_string());
update.game_mode = pregame.game_mode; update.map_name = pregame.map_name().map(|s| s.to_string());
update.map_name = pregame.map_name; update.team = pregame.team().map(|id| id as u32);
update.team = pregame.team;
} }
// Add game end metadata // Add game end stats from API response
if let Some(end_meta) = game_end_metadata { if let Some(stats) = game_end_stats {
update.match_id = end_meta.match_id; update.victory = Some(stats.is_victory());
update.victory = end_meta.victory; update.match_id = stats.match_id.map(|id| id.to_string());
// Get local player stats
if let Some(player) = stats.get_local_player() {
if let Some(player_stats) = &player.stats {
update.final_stats = Some(record_daemon::timeline::GameFinalStats { update.final_stats = Some(record_daemon::timeline::GameFinalStats {
kills: end_meta.kills, kills: player_stats.champions_killed.unwrap_or(0) as u32,
deaths: end_meta.deaths, deaths: player_stats.num_deaths.unwrap_or(0) as u32,
assists: end_meta.assists, assists: player_stats.assists.unwrap_or(0) as u32,
creep_score: end_meta.creep_score, creep_score: player_stats.minions_killed.unwrap_or(0) as u32,
gold_earned: end_meta.gold_earned, gold_earned: player_stats.gold_earned.unwrap_or(0) as u32,
damage_dealt: end_meta.damage_dealt, damage_dealt: player_stats
damage_taken: end_meta.damage_taken, .total_damage_dealt_to_champions
vision_score: end_meta.vision_score, .unwrap_or(0),
game_duration: end_meta.game_duration, damage_taken: player_stats.total_damage_taken.unwrap_or(0),
vision_score: player_stats.vision_score.unwrap_or(0.0),
game_duration: stats.game_length.unwrap_or(0.0),
}); });
} }
}
}
// Add final items // Add final items
update.final_items = final_items; update.final_items = final_items;

View File

@@ -299,10 +299,16 @@ pub fn detect_hardware_encoders() -> Vec<EncoderCapability> {
info!("[ENCODER_DETECT] Starting hardware encoder detection..."); info!("[ENCODER_DETECT] Starting hardware encoder detection...");
// Software encoding is always available= // Software encoding is always available=
let mut capabilities = vec![EncoderCapability::Software]; let capabilities = vec![EncoderCapability::Software];
#[cfg(target_os = "linux")]
{
let _capabilities = [EncoderCapability::Software];
}
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
let mut capabilities = vec![EncoderCapability::Software];
let gpu_vendors = detect_gpu_vendors(); let gpu_vendors = detect_gpu_vendors();
if gpu_vendors.contains(&GpuVendor::Nvidia) { if gpu_vendors.contains(&GpuVendor::Nvidia) {