record-daemon: add live client events, fix video_file
record-daemon / Build, check and test (push) Successful in 2m12s
record-daemon / Build, check and test (push) Successful in 2m12s
This commit is contained in:
@@ -14,7 +14,7 @@ use super::endpoints;
|
||||
use super::events::GameEvent;
|
||||
use super::state::{ClientState, GameflowPhase};
|
||||
use super::tls::create_insecure_tls_config;
|
||||
use super::websocket::{parse_websocket_message, ParsedEvent};
|
||||
use super::websocket::{parse_live_client_event, parse_websocket_message, ParsedEvent};
|
||||
use crate::error::{LqpError, Result};
|
||||
|
||||
/// LQP Client for League Client communication.
|
||||
@@ -409,12 +409,151 @@ impl LqpClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Get live client event data (kills, deaths, objectives) from port 2999.
|
||||
/// This endpoint is available during games and provides real-time events.
|
||||
pub async fn get_live_client_events(&self) -> Result<serde_json::Value> {
|
||||
// Live client data runs on port 2999, separate from the LQP client port
|
||||
let url = format!(
|
||||
"{}{}",
|
||||
endpoints::LIVE_CLIENT_DATA_BASE_URL,
|
||||
endpoints::LIVE_CLIENT_DATA_EVENTS
|
||||
);
|
||||
|
||||
let response = self
|
||||
.http_client
|
||||
.get(&url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| LqpError::ConnectionFailed(e.to_string()))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(LqpError::ConnectionFailed(format!(
|
||||
"Live client events request failed: {}",
|
||||
response.status()
|
||||
))
|
||||
.into());
|
||||
}
|
||||
|
||||
let json = response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|e| LqpError::EventParseError(e.to_string()))?;
|
||||
|
||||
Ok(json)
|
||||
}
|
||||
|
||||
/// Get local player selection from champion select.
|
||||
pub async fn get_local_player_selection(&self) -> Result<serde_json::Value> {
|
||||
self.request("GET", endpoints::CHAMPION_SELECT_LOCAL_PLAYER)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Start polling for live client events during a game.
|
||||
/// This polls the /liveclientdata/eventdata endpoint and broadcasts events.
|
||||
pub async fn start_live_client_event_poller(&self) {
|
||||
let event_sender = self.event_sender.clone();
|
||||
let state = self.state.clone();
|
||||
let shutdown = self.shutdown.clone();
|
||||
let http_client = self.http_client.clone();
|
||||
|
||||
info!("Starting live client event poller");
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut last_event_id: Option<u64> = None;
|
||||
let mut poll_count = 0u32;
|
||||
|
||||
loop {
|
||||
if *shutdown.read().await {
|
||||
info!("Live client event poller shutting down");
|
||||
break;
|
||||
}
|
||||
|
||||
// Only poll when in game
|
||||
let current_phase = state.read().await.phase;
|
||||
if current_phase != GameflowPhase::InProgress {
|
||||
// Reset event tracking when not in game
|
||||
last_event_id = None;
|
||||
poll_count = 0;
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
continue;
|
||||
}
|
||||
|
||||
poll_count += 1;
|
||||
if poll_count % 20 == 1 {
|
||||
// Log every 10 seconds (20 * 500ms)
|
||||
info!("Live client event poller active, polling for events...");
|
||||
}
|
||||
|
||||
// Poll for events
|
||||
let url = format!(
|
||||
"{}{}",
|
||||
endpoints::LIVE_CLIENT_DATA_BASE_URL,
|
||||
endpoints::LIVE_CLIENT_DATA_EVENTS
|
||||
);
|
||||
|
||||
match http_client.get(&url).send().await {
|
||||
Ok(response) if response.status().is_success() => {
|
||||
match response.json::<serde_json::Value>().await {
|
||||
Ok(events) => {
|
||||
// The response has an "Events" key containing the array
|
||||
let events_array = events.get("Events").and_then(|e| e.as_array());
|
||||
if let Some(events_array) = events_array {
|
||||
let event_count = events_array.len();
|
||||
if event_count > 0 {
|
||||
info!(
|
||||
"Received {} events from live client API",
|
||||
event_count
|
||||
);
|
||||
}
|
||||
for event in events_array {
|
||||
// Check if this is a new event
|
||||
let event_id =
|
||||
event.get("EventID").and_then(|id| id.as_u64());
|
||||
|
||||
// Skip events we've already processed
|
||||
if let Some(id) = event_id {
|
||||
if let Some(last_id) = last_event_id {
|
||||
if id <= last_id {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
last_event_id = Some(id);
|
||||
}
|
||||
|
||||
// Parse and broadcast the event
|
||||
if let Some(parsed) = parse_live_client_event(event) {
|
||||
info!("Parsed live client event: {:?}", parsed.event);
|
||||
// Update state based on event
|
||||
Self::update_state_from_event(&state, &parsed.event)
|
||||
.await;
|
||||
|
||||
// Broadcast event
|
||||
if event_sender.send(parsed).is_err() {
|
||||
trace!("No event subscribers");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Failed to parse live client events: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(response) => {
|
||||
info!("Live client events request failed: {}", response.status());
|
||||
}
|
||||
Err(e) => {
|
||||
info!("Failed to fetch live client events: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Poll every 500ms during games
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Metadata Fetching Methods
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user