record-daemon: don't propagate puuid, record it and compute at end
All checks were successful
record-daemon / Build, check and test (push) Successful in 2m10s

This commit is contained in:
2026-03-26 13:19:44 +01:00
parent 2814d85b6b
commit b94101c4d2
3 changed files with 20 additions and 74 deletions

View File

@@ -191,10 +191,7 @@ impl LqpClient {
if text.is_empty() { if text.is_empty() {
continue; continue;
} }
// Get local_puuid from state for champion extraction if let Some(event) = parse_websocket_message(&text) {
let local_puuid = state.read().await.local_puuid.clone();
if let Some(event) = parse_websocket_message(&text, local_puuid.as_deref())
{
// Update state based on event // Update state based on event
Self::update_state_from_event(&state, &event).await; Self::update_state_from_event(&state, &event).await;
@@ -227,11 +224,7 @@ impl LqpClient {
// Try to parse as UTF-8 // Try to parse as UTF-8
if let Ok(text) = String::from_utf8(data) { if let Ok(text) = String::from_utf8(data) {
if !text.is_empty() { if !text.is_empty() {
// Get local_puuid from state for champion extraction if let Some(event) = parse_websocket_message(&text) {
let local_puuid = state.read().await.local_puuid.clone();
if let Some(event) =
parse_websocket_message(&text, local_puuid.as_deref())
{
// Update state based on event // Update state based on event
Self::update_state_from_event(&state, &event).await; Self::update_state_from_event(&state, &event).await;

View File

@@ -6,10 +6,10 @@
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
use super::events::{GameEvent, GameflowSession}; use super::events::{GameEvent, GameflowSession};
use super::mappings::{champion_id_to_name, map_id_to_name}; use super::mappings::map_id_to_name;
/// Parse a WebSocket message into a game event. /// Parse a WebSocket message into a game event.
pub fn parse_websocket_message(text: &str, local_puuid: Option<&str>) -> Option<GameEvent> { pub fn parse_websocket_message(text: &str) -> Option<GameEvent> {
// Parse the message array format: [type, callback, data] // Parse the message array format: [type, callback, data]
let value: serde_json::Value = match serde_json::from_str(text) { let value: serde_json::Value = match serde_json::from_str(text) {
Ok(v) => v, Ok(v) => v,
@@ -42,7 +42,6 @@ pub fn parse_websocket_message(text: &str, local_puuid: Option<&str>) -> Option<
&raw_event.uri, &raw_event.uri,
event_type, event_type,
&serde_json::to_value(raw_event.data).unwrap_or_default(), &serde_json::to_value(raw_event.data).unwrap_or_default(),
local_puuid,
); );
} }
@@ -54,7 +53,7 @@ pub fn parse_websocket_message(text: &str, local_puuid: Option<&str>) -> Option<
.and_then(|t| t.as_str()) .and_then(|t| t.as_str())
.unwrap_or("Update"); .unwrap_or("Update");
return parse_event_from_uri(uri, event_type, data, local_puuid); return parse_event_from_uri(uri, event_type, data);
} else { } else {
debug!("Unknown callback: {}", callback); debug!("Unknown callback: {}", callback);
} }
@@ -80,7 +79,6 @@ pub fn parse_event_from_uri(
uri: &str, uri: &str,
event_type: &str, event_type: &str,
data: &serde_json::Value, data: &serde_json::Value,
local_puuid: Option<&str>,
) -> Option<GameEvent> { ) -> Option<GameEvent> {
info!("Parsing event from URI: {} (type: {})", uri, event_type); info!("Parsing event from URI: {} (type: {})", uri, event_type);
@@ -91,7 +89,7 @@ pub fn parse_event_from_uri(
// Handle gameflow session updates // Handle gameflow session updates
if uri == "/lol-gameflow/v1/session" { if uri == "/lol-gameflow/v1/session" {
return parse_gameflow_session_event(data, local_puuid); return parse_gameflow_session_event(data);
} }
// Handle game events (kills, deaths, objectives) // Handle game events (kills, deaths, objectives)
@@ -158,16 +156,13 @@ fn parse_gameflow_phase_event(data: &serde_json::Value) -> Option<GameEvent> {
} }
/// Parse gameflow session event. /// Parse gameflow session event.
fn parse_gameflow_session_event( fn parse_gameflow_session_event(data: &serde_json::Value) -> Option<GameEvent> {
data: &serde_json::Value,
local_puuid: Option<&str>,
) -> Option<GameEvent> {
if let Some(phase) = data.get("phase").and_then(|p| p.as_str()) { if let Some(phase) = data.get("phase").and_then(|p| p.as_str()) {
info!("Gameflow session phase: {}", phase); info!("Gameflow session phase: {}", phase);
// Check for game start // Check for game start
if phase == "InProgress" { if phase == "InProgress" {
return parse_game_start_event(data, local_puuid); return parse_game_start_event(data);
} }
return Some( return Some(
@@ -182,10 +177,10 @@ fn parse_gameflow_session_event(
} }
/// Parse game start event from session data. /// Parse game start event from session data.
fn parse_game_start_event( ///
data: &serde_json::Value, /// The session data contains all player information. The reading software should
local_puuid: Option<&str>, /// use the puuid from RecordingMetadata to find the local player's data in the session.
) -> Option<GameEvent> { fn parse_game_start_event(data: &serde_json::Value) -> Option<GameEvent> {
info!("Game is now in progress!"); info!("Game is now in progress!");
// Try to parse the gameData into a GameflowSession struct // Try to parse the gameData into a GameflowSession struct
@@ -256,28 +251,10 @@ fn parse_game_start_event(
game_id, queue_type, queue_id, game_mode, map_name game_id, queue_type, queue_id, game_mode, map_name
); );
// Extract player-specific data using puuid // Note: Player-specific data (champion, team, summoner_name) is NOT extracted here.
let (champion, team, summoner_name) = if let Some(puuid) = local_puuid { // The full session is stored in the event, and the reading software should use
let champ_id = session.as_ref().and_then(|s| s.get_champion_id(puuid)); // the puuid from RecordingMetadata to find the local player's data in the session.
let team_id = session.as_ref().and_then(|s| s.get_team(puuid)); // This allows the same recording to be analyzed for any player by their puuid.
let summoner = session
.as_ref()
.and_then(|s| s.get_summoner_name(puuid))
.map(|s| s.to_string());
// Convert champion_id to champion name
let champ_name = champ_id.and_then(champion_id_to_name);
info!(
"Extracted player data via puuid: champion={:?}, team={:?}, summoner={:?}",
champ_name, team_id, summoner
);
(champ_name, team_id, summoner)
} else {
info!("No local_puuid available, cannot extract player-specific data");
(None, None, None)
};
Some( Some(
GameEvent::from_json(&serde_json::json!({ GameEvent::from_json(&serde_json::json!({
@@ -287,9 +264,6 @@ fn parse_game_start_event(
"queueId": queue_id, "queueId": queue_id,
"gameMode": game_mode, "gameMode": game_mode,
"map": map_name, "map": map_name,
"champion": champion,
"team": team,
"summonerName": summoner_name,
"session": session "session": session
})) }))
.unwrap_or(GameEvent::Unknown), .unwrap_or(GameEvent::Unknown),
@@ -470,13 +444,13 @@ mod tests {
#[test] #[test]
fn test_parse_websocket_message_invalid_json() { fn test_parse_websocket_message_invalid_json() {
let result = parse_websocket_message("not json", None); let result = parse_websocket_message("not json");
assert!(result.is_none()); assert!(result.is_none());
} }
#[test] #[test]
fn test_parse_websocket_message_empty_array() { fn test_parse_websocket_message_empty_array() {
let result = parse_websocket_message("[]", None); let result = parse_websocket_message("[]");
assert!(result.is_none()); assert!(result.is_none());
} }
} }

View File

@@ -233,27 +233,6 @@ impl DaemonStateMachine {
match event { match event {
GameEvent::GameStart(info) => { GameEvent::GameStart(info) => {
// Extract summoner spells from session data if available
let summoner_spells = info.session.as_ref().and_then(|session| {
session
.player_champion_selections
.first()
.map(|selection| SummonerSpells {
spell1_id: selection.spell1_id,
spell2_id: selection.spell2_id,
spell1_name: crate::lqp::spell_id_to_name(selection.spell1_id),
spell2_name: crate::lqp::spell_id_to_name(selection.spell2_id),
})
});
// Extract puuid from session data if available
let puuid = info.session.as_ref().and_then(|session| {
session
.player_champion_selections
.first()
.map(|selection| selection.puuid.clone())
});
Some(StateTransition::GameStarted { Some(StateTransition::GameStarted {
game_id: info.game_id, game_id: info.game_id,
champion: info.champion.clone(), champion: info.champion.clone(),
@@ -263,9 +242,9 @@ impl DaemonStateMachine {
map_name: info.map_name.clone(), map_name: info.map_name.clone(),
team: info.team, team: info.team,
summoner_name: info.summoner_name.clone(), summoner_name: info.summoner_name.clone(),
puuid, puuid: None, // Will be populated from client.fetch_player_game_metadata()
runes: None, // Will be populated from client.fetch_player_game_metadata() runes: None, // Will be populated from client.fetch_player_game_metadata()
summoner_spells, summoner_spells: None, // Will be populated from client.fetch_player_game_metadata()
}) })
} }
GameEvent::GameEnd(info) => Some(StateTransition::GameEnded { GameEvent::GameEnd(info) => Some(StateTransition::GameEnded {