From dbb224e11860ffbac8ab7226813e173148714779 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Thu, 19 Mar 2026 22:05:13 +0100 Subject: [PATCH] record-daemon: fix gamephase event handling --- record-daemon/src/lqp/events.rs | 19 +++++++++++++++++++ record-daemon/src/main.rs | 7 +++++++ record-daemon/src/state/machine.rs | 10 ++++++++++ record-daemon/src/timeline/mod.rs | 1 + 4 files changed, 37 insertions(+) diff --git a/record-daemon/src/lqp/events.rs b/record-daemon/src/lqp/events.rs index 9167c37..0bfc50d 100644 --- a/record-daemon/src/lqp/events.rs +++ b/record-daemon/src/lqp/events.rs @@ -33,11 +33,27 @@ pub enum GameEvent { #[serde(rename = "lcu-game-end")] GameEnd(GameEndInfo), + /// Gameflow phase changed. + #[serde(rename = "lcu-phase-change")] + PhaseChange(PhaseChangeInfo), + /// Unknown event type. #[serde(other)] Unknown, } +/// Phase change event data. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PhaseChangeInfo { + /// The new phase. + pub phase: String, + + /// Timestamp when phase changed. + #[serde(default = "Utc::now")] + pub timestamp: DateTime, +} + /// Match found event data. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -314,6 +330,9 @@ impl GameEvent { let result = if end.victory { "Victory" } else { "Defeat" }; format!("Game ended: {} ({:.1}s)", result, end.duration) } + GameEvent::PhaseChange(info) => { + format!("Phase changed to: {}", info.phase) + } GameEvent::Unknown => "Unknown event".to_string(), } } diff --git a/record-daemon/src/main.rs b/record-daemon/src/main.rs index 49c2d1b..e27f8ff 100644 --- a/record-daemon/src/main.rs +++ b/record-daemon/src/main.rs @@ -210,6 +210,13 @@ impl Daemon { // Handle recording start/stop match transition { StateTransition::GameStarted { game_id, champion } => { + // If already recording, stop the current recording first + if self.state_machine.is_recording() { + info!("Stopping previous recording before starting new one"); + if let Err(e) = self.stop_recording().await { + warn!("Failed to stop previous recording: {}", e); + } + } self.start_recording(game_id, champion.as_deref()).await?; } StateTransition::GameEnded => { diff --git a/record-daemon/src/state/machine.rs b/record-daemon/src/state/machine.rs index bd9ecca..f23d829 100644 --- a/record-daemon/src/state/machine.rs +++ b/record-daemon/src/state/machine.rs @@ -178,6 +178,8 @@ impl DaemonStateMachine { // From Recording (DaemonState::Recording, StateTransition::GameEnded) => Some(DaemonState::Monitoring), + // Allow GameStarted from Recording (handles case where GameEnded wasn't received) + (DaemonState::Recording, StateTransition::GameStarted { .. }) => Some(DaemonState::Recording), (DaemonState::Recording, StateTransition::ClientStopped) => Some(DaemonState::Idle), (DaemonState::Recording, StateTransition::Error(_)) => Some(DaemonState::Error), (DaemonState::Recording, StateTransition::Shutdown) => Some(DaemonState::ShuttingDown), @@ -212,6 +214,14 @@ impl DaemonStateMachine { champion: info.champion.clone(), }), GameEvent::GameEnd(_) => Some(StateTransition::GameEnded), + GameEvent::PhaseChange(info) => { + // When phase changes to None while recording, the player left the game + if info.phase == "None" && self.is_recording() { + Some(StateTransition::GameEnded) + } else { + None + } + } _ => None, } } diff --git a/record-daemon/src/timeline/mod.rs b/record-daemon/src/timeline/mod.rs index 0f18699..c2dffec 100644 --- a/record-daemon/src/timeline/mod.rs +++ b/record-daemon/src/timeline/mod.rs @@ -143,6 +143,7 @@ fn event_type_name(event: &GameEvent) -> String { GameEvent::Death(_) => "death", GameEvent::Objective(_) => "objective", GameEvent::GameEnd(_) => "game_end", + GameEvent::PhaseChange(_) => "phase_change", GameEvent::Unknown => "unknown", } .to_string()