replace items record with full end-of-game stats record
All checks were successful
record-daemon / Build, check and test (push) Successful in 2m5s
All checks were successful
record-daemon / Build, check and test (push) Successful in 2m5s
- record-daemon: remove items parsing module - tauri-app: add items parsing from recorded end-of-game stats
This commit is contained in:
@@ -16,8 +16,7 @@ use super::api_types::{
|
||||
};
|
||||
use super::auth::LockfileCredentials;
|
||||
use super::endpoints;
|
||||
use super::events::{GameEvent, ItemBuild};
|
||||
use super::items::{parse_items_from_game_stats, parse_items_from_live_client};
|
||||
use super::events::GameEvent;
|
||||
use super::mappings::{champion_id_to_name, spell_id_to_name};
|
||||
use super::state::{ClientState, GameflowPhase};
|
||||
use super::tls::create_insecure_tls_config;
|
||||
@@ -725,122 +724,6 @@ impl LqpClient {
|
||||
|
||||
Ok(players)
|
||||
}
|
||||
|
||||
/// Fetch final items from end-of-game stats or live client data.
|
||||
pub async fn fetch_final_items(&self) -> Result<Option<ItemBuild>> {
|
||||
info!("[ITEMS] Fetching final items...");
|
||||
|
||||
// First try live client data (typed)
|
||||
match self.get_live_client_player_list_typed().await {
|
||||
Ok(player_list) => {
|
||||
info!(
|
||||
"[ITEMS] Live client player list response received with {} players",
|
||||
player_list.0.len()
|
||||
);
|
||||
for player in &player_list.0 {
|
||||
if player.is_local_player == Some(true) {
|
||||
info!("[ITEMS] Found local player in live client data");
|
||||
if let Some(ref items) = player.items {
|
||||
info!("[ITEMS] Items array has {} items", items.len());
|
||||
// Convert LiveClientItem to serde_json::Value for parsing
|
||||
let items_json: Vec<serde_json::Value> = items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
item.item_id.map(|id| {
|
||||
serde_json::json!({"itemId": id, "displayName": item.display_name})
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let item_build = parse_items_from_live_client(&items_json);
|
||||
if item_build.is_some() {
|
||||
info!("[ITEMS] Successfully parsed items from live client data");
|
||||
return Ok(item_build);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
info!("[ITEMS] Failed to get live client player list: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try end-of-game stats (typed)
|
||||
match self.get_game_stats_typed().await {
|
||||
Ok(stats) => {
|
||||
info!("[ITEMS] Game stats response received");
|
||||
|
||||
// Try local player first
|
||||
if let Some(local_player) = stats.get_local_player() {
|
||||
info!("[ITEMS] Found localPlayer in game stats");
|
||||
if let Some(ref items) = local_player.items {
|
||||
info!("[ITEMS] localPlayer.items array has {} items", items.len());
|
||||
// Convert item IDs to serde_json::Value for parsing (as raw numbers)
|
||||
let items_json: Vec<serde_json::Value> =
|
||||
items.iter().map(|id| serde_json::json!(*id)).collect();
|
||||
let item_build = parse_items_from_game_stats(&items_json);
|
||||
if item_build.is_some() {
|
||||
info!("[ITEMS] Successfully parsed items from localPlayer");
|
||||
return Ok(item_build);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try teams
|
||||
if let Some(ref teams) = stats.teams {
|
||||
info!("[ITEMS] Found {} teams in game stats", teams.len());
|
||||
for team in teams {
|
||||
if let Some(ref players) = team.players {
|
||||
for player in players {
|
||||
if player.is_local_player == Some(true) {
|
||||
info!("[ITEMS] Found local player in teams[].players[]");
|
||||
if let Some(ref items) = player.items {
|
||||
info!(
|
||||
"[ITEMS] Player items array has {} items",
|
||||
items.len()
|
||||
);
|
||||
let items_json: Vec<serde_json::Value> =
|
||||
items.iter().map(|id| serde_json::json!(*id)).collect();
|
||||
let item_build = parse_items_from_game_stats(&items_json);
|
||||
if item_build.is_some() {
|
||||
info!("[ITEMS] Successfully parsed items from teams[].players[]");
|
||||
return Ok(item_build);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try legacy players array
|
||||
if let Some(ref players) = stats.players {
|
||||
info!(
|
||||
"[ITEMS] Found {} players in game stats (legacy)",
|
||||
players.len()
|
||||
);
|
||||
if let Some(player) = players.first() {
|
||||
if let Some(ref items) = player.items {
|
||||
let items_json: Vec<serde_json::Value> =
|
||||
items.iter().map(|id| serde_json::json!(*id)).collect();
|
||||
let item_build = parse_items_from_game_stats(&items_json);
|
||||
if item_build.is_some() {
|
||||
return Ok(item_build);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("[ITEMS] Could not find items in game stats structure");
|
||||
}
|
||||
Err(e) => {
|
||||
info!("[ITEMS] Failed to get game stats: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
info!("[ITEMS] Could not fetch final items from any source");
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LqpClient {
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
//! Item parsing utilities for LQP client.
|
||||
//!
|
||||
//! Provides functions for parsing item builds from different data formats.
|
||||
|
||||
use super::events::{ItemBuild, ItemInfo};
|
||||
|
||||
/// Parse items from live client data format.
|
||||
///
|
||||
/// Live client data items are objects with `itemID` and `displayName` fields.
|
||||
pub fn parse_items_from_live_client(items: &[serde_json::Value]) -> Option<ItemBuild> {
|
||||
let mut item_list = Vec::new();
|
||||
let mut trinket = None;
|
||||
|
||||
for (slot, item) in items.iter().enumerate() {
|
||||
if let Some(item_id) = item.get("itemID").and_then(|id| id.as_u64()) {
|
||||
if item_id > 0 {
|
||||
let item_info = ItemInfo {
|
||||
item_id: item_id as u32,
|
||||
name: item
|
||||
.get("displayName")
|
||||
.and_then(|n| n.as_str())
|
||||
.map(|s| s.to_string()),
|
||||
slot: slot as u32,
|
||||
};
|
||||
|
||||
// Slot 6 is typically the trinket
|
||||
if slot == 6 {
|
||||
trinket = Some(item_info);
|
||||
} else {
|
||||
item_list.push(item_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !item_list.is_empty() || trinket.is_some() {
|
||||
Some(ItemBuild {
|
||||
items: item_list,
|
||||
trinket,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse items from game stats format.
|
||||
///
|
||||
/// Game stats items are just numbers (item IDs) in an array.
|
||||
pub fn parse_items_from_game_stats(items: &[serde_json::Value]) -> Option<ItemBuild> {
|
||||
let mut item_list = Vec::new();
|
||||
let mut trinket = None;
|
||||
|
||||
for (slot, item) in items.iter().enumerate() {
|
||||
// Items in game stats are just numbers (item IDs), not objects
|
||||
if let Some(item_id) = item.as_u64() {
|
||||
if item_id > 0 {
|
||||
let item_info = ItemInfo {
|
||||
item_id: item_id as u32,
|
||||
name: None, // Item names would need a separate mapping
|
||||
slot: slot as u32,
|
||||
};
|
||||
|
||||
// Slot 6 is typically the trinket
|
||||
if slot == 6 {
|
||||
trinket = Some(item_info);
|
||||
} else {
|
||||
item_list.push(item_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !item_list.is_empty() || trinket.is_some() {
|
||||
Some(ItemBuild {
|
||||
items: item_list,
|
||||
trinket,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_items_from_live_client_empty() {
|
||||
let items = vec![];
|
||||
let result = parse_items_from_live_client(&items);
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_items_from_live_client_with_items() {
|
||||
let items = vec![
|
||||
serde_json::json!({"itemID": 1001, "displayName": "Boots"}),
|
||||
serde_json::json!({"itemID": 0, "displayName": ""}),
|
||||
];
|
||||
let result = parse_items_from_live_client(&items);
|
||||
assert!(result.is_some());
|
||||
let build = result.unwrap();
|
||||
assert_eq!(build.items.len(), 1);
|
||||
assert_eq!(build.items[0].item_id, 1001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_items_from_game_stats_empty() {
|
||||
let items = vec![];
|
||||
let result = parse_items_from_game_stats(&items);
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_items_from_game_stats_with_items() {
|
||||
let items = vec![
|
||||
serde_json::json!(1001),
|
||||
serde_json::json!(0),
|
||||
serde_json::json!(3020),
|
||||
];
|
||||
let result = parse_items_from_game_stats(&items);
|
||||
assert!(result.is_some());
|
||||
let build = result.unwrap();
|
||||
assert_eq!(build.items.len(), 2);
|
||||
assert_eq!(build.items[0].item_id, 1001);
|
||||
assert_eq!(build.items[1].item_id, 3020);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_items_with_trinket() {
|
||||
// Slot 6 is trinket
|
||||
let items = vec![
|
||||
serde_json::json!(1001),
|
||||
serde_json::json!(0),
|
||||
serde_json::json!(0),
|
||||
serde_json::json!(0),
|
||||
serde_json::json!(0),
|
||||
serde_json::json!(0),
|
||||
serde_json::json!(3340), // Trinket in slot 6
|
||||
];
|
||||
let result = parse_items_from_game_stats(&items);
|
||||
assert!(result.is_some());
|
||||
let build = result.unwrap();
|
||||
assert_eq!(build.items.len(), 1);
|
||||
assert!(build.trinket.is_some());
|
||||
assert_eq!(build.trinket.unwrap().item_id, 3340);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ mod auth;
|
||||
mod client;
|
||||
mod endpoints;
|
||||
mod events;
|
||||
mod items;
|
||||
mod mappings;
|
||||
mod state;
|
||||
mod tls;
|
||||
@@ -34,7 +33,6 @@ pub use events::{
|
||||
ObjectiveEvent, ObjectiveType, PlayerChampionSelection, PlayerGameMetadata, PlayerIdentity,
|
||||
QueueInfo, RunePage, RuneSlot, SummonerSpells, TeamMember,
|
||||
};
|
||||
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 state::{ClientState, GameflowPhase};
|
||||
pub use websocket::{parse_event_from_uri, parse_websocket_message};
|
||||
|
||||
Reference in New Issue
Block a user