diff --git a/analysis/src/heatmap.rs b/analysis/src/heatmap.rs index ad11a5a..dd7ec34 100644 --- a/analysis/src/heatmap.rs +++ b/analysis/src/heatmap.rs @@ -52,7 +52,7 @@ impl HeatMap { #[derive(Debug)] pub struct HeatMapOutput { - pub player_heatmaps: std::collections::HashMap, + pub player_heatmaps: std::collections::HashMap<(csdemo::UserId, String), HeatMap>, pub player_info: std::collections::HashMap, } @@ -96,8 +96,7 @@ pub fn parse(config: &Config, buf: &[u8]) -> Result { Some(csdemo::RawValue::I32(v)) => { Some((PawnID::from(*v), pspawn.userid.unwrap())) } - other => { - // tracing::info!("Unknown Pawn-ID: {:?}", other); + _ => { None }, }, @@ -116,6 +115,7 @@ pub fn parse(config: &Config, buf: &[u8]) -> Result { tmp }; + let mut teams = std::collections::HashMap::new(); let mut player_lifestate = std::collections::HashMap::::new(); let mut player_position = std::collections::HashMap::::new(); let mut player_cells = std::collections::HashMap::new(); @@ -128,6 +128,7 @@ pub fn parse(config: &Config, buf: &[u8]) -> Result { config, tick_state, &pawn_ids, + &mut teams, &mut player_lifestate, &mut player_position, &mut player_cells, @@ -149,22 +150,41 @@ fn process_tick( config: &Config, tick_state: &csdemo::parser::EntityTickStates, pawn_ids: &std::collections::HashMap, + teams: &mut std::collections::HashMap, player_lifestate: &mut std::collections::HashMap, player_position: &mut std::collections::HashMap, player_cells: &mut std::collections::HashMap, - heatmaps: &mut std::collections::HashMap, + heatmaps: &mut std::collections::HashMap<(csdemo::UserId, String), HeatMap>, ) { for entity_state in tick_state .states .iter() - .filter(|s| s.class.as_ref() == "CCSPlayerPawn") + .filter(|s| matches!(s.class.as_ref(), "CCSPlayerPawn" | "CCSTeam")) { + if entity_state.class.as_ref() == "CCSTeam" { + let raw_team_name = match entity_state.get_prop("CCSTeam.m_szTeamname").map(|p| match &p.value { + csdemo::parser::Variant::String(v) => Some(v), + _ => None, + }).flatten() { + Some(n) => n, + None => continue, + }; + + for prop in entity_state.props.iter().filter(|p| p.prop_info.prop_name.as_ref() == "CCSTeam.m_aPawns").filter_map(|p| p.value.as_u32().map(|v| PawnID::from(v))) { + teams.insert(prop, raw_team_name.clone()); + } + + continue; + } + let pawn_id = PawnID::from(entity_state.id); let user_id = match pawn_ids.get(&pawn_id).cloned() { Some(id) => id, - None => { - continue - } + None => continue, + }; + let team = match teams.get(&pawn_id).cloned() { + Some(t) => t, + None => continue, }; let _inner_guard = @@ -245,7 +265,7 @@ fn process_tick( // tracing::trace!("Coord (X, Y, Z): {:?} -> {:?}", (x_coord, y_coord, z_coord), (x_cell, y_cell)); - let heatmap = heatmaps.entry(user_id.clone()).or_insert(HeatMap::new(config.cell_size)); + let heatmap = heatmaps.entry((user_id.clone(), team)).or_insert(HeatMap::new(config.cell_size)); heatmap.increment(x_cell, y_cell); } } diff --git a/analysis/tests/heatmap.rs b/analysis/tests/heatmap.rs index c530cd9..ccad5d4 100644 --- a/analysis/tests/heatmap.rs +++ b/analysis/tests/heatmap.rs @@ -11,7 +11,7 @@ fn heatmap_nuke() { let config = heatmap::Config { cell_size: 5.0 }; let result = heatmap::parse(&config, &input_bytes).unwrap(); - assert_eq!(result.player_heatmaps.len(), 10); + assert_eq!(result.player_heatmaps.len(), 20); } #[test] @@ -24,7 +24,7 @@ fn heatmap_inferno() { let config = heatmap::Config { cell_size: 5.0 }; let result = heatmap::parse(&config, &input_bytes).unwrap(); - assert_eq!(result.player_heatmaps.len(), 10); + assert_eq!(result.player_heatmaps.len(), 20); } #[test] @@ -37,5 +37,5 @@ fn heatmap_dust2() { let config = heatmap::Config { cell_size: 5.0 }; let result = heatmap::parse(&config, &input_bytes).unwrap(); - assert_eq!(result.player_heatmaps.len(), 10); + assert_eq!(result.player_heatmaps.len(), 20); } diff --git a/backend/src/analysis/heatmap.rs b/backend/src/analysis/heatmap.rs index 1176c55..68a1692 100644 --- a/backend/src/analysis/heatmap.rs +++ b/backend/src/analysis/heatmap.rs @@ -22,7 +22,7 @@ impl Analysis for HeatmapAnalysis { let result = analysis::heatmap::parse(&config, &mmap).unwrap(); tracing::info!("Got {} Entity-Heatmaps", result.player_heatmaps.len()); - let heatmap_result: Vec<_> = result.player_heatmaps.into_iter().filter_map(|(userid, heatmap)| { + let heatmap_result: Vec<_> = result.player_heatmaps.into_iter().filter_map(|((userid, team), heatmap)| { let player = match result.player_info.get(&userid) { Some(p) => p, None => { @@ -31,15 +31,16 @@ impl Analysis for HeatmapAnalysis { } }; - Some((player.xuid.to_string(), heatmap)) + Some(((player.xuid.to_string(), team), heatmap)) }).collect(); - let player_heatmaps: Vec<_> = heatmap_result.into_iter().map(|(player, heatmap)| { - tracing::trace!("HeatMap for Player: {:?}", player); + let player_heatmaps: Vec<_> = heatmap_result.into_iter().map(|((player, team), heatmap)| { + tracing::trace!("HeatMap for Player: {:?} in Team {:?}", player, team); crate::models::DemoPlayerHeatmap { demo_id: input.demoid.clone(), steam_id: player, + team, data: serde_json::to_string(&heatmap).unwrap(), } }).collect(); @@ -47,7 +48,7 @@ impl Analysis for HeatmapAnalysis { Ok(Box::new(move |connection| { let store_demo_player_heatmaps_query = diesel::dsl::insert_into(crate::schema::demo_heatmaps::dsl::demo_heatmaps) .values(player_heatmaps) - .on_conflict((crate::schema::demo_heatmaps::dsl::demo_id, crate::schema::demo_heatmaps::dsl::steam_id)) + .on_conflict((crate::schema::demo_heatmaps::dsl::demo_id, crate::schema::demo_heatmaps::dsl::steam_id, crate::schema::demo_heatmaps::dsl::team)) .do_update() .set(crate::schema::demo_heatmaps::dsl::data.eq(diesel::upsert::excluded(crate::schema::demo_heatmaps::dsl::data))); diff --git a/backend/src/api/demos.rs b/backend/src/api/demos.rs index 4eb8a90..40d7550 100644 --- a/backend/src/api/demos.rs +++ b/backend/src/api/demos.rs @@ -305,6 +305,7 @@ async fn heatmap( }; let data: Vec = result.into_iter().map(|(player, heatmap)| { + let team = heatmap.team.clone(); let mut heatmap: analysis::heatmap::HeatMap = serde_json::from_str(&heatmap.data).unwrap(); heatmap.fit(minimap_coords.x_coord(0.0)..minimap_coords.x_coord(1024.0), minimap_coords.y_coord(1024.0)..minimap_coords.y_coord(0.0)); let h_image = heatmap.as_image(); @@ -314,6 +315,7 @@ async fn heatmap( common::demo_analysis::PlayerHeatmap { name: player.name, + team, png_data: base64::prelude::BASE64_STANDARD.encode(buffer.into_inner()), } }).collect(); diff --git a/backend/src/models.rs b/backend/src/models.rs index 753d36a..c1bf795 100644 --- a/backend/src/models.rs +++ b/backend/src/models.rs @@ -95,6 +95,7 @@ pub struct AnalysisTask { pub struct DemoPlayerHeatmap { pub demo_id: String, pub steam_id: String, + pub team: String, pub data: String, } diff --git a/backend/src/schema.rs b/backend/src/schema.rs index cf75bf6..e153d31 100644 --- a/backend/src/schema.rs +++ b/backend/src/schema.rs @@ -9,9 +9,10 @@ diesel::table! { } diesel::table! { - demo_heatmaps (demo_id, steam_id) { + demo_heatmaps (demo_id, steam_id, team) { demo_id -> Text, steam_id -> Text, + team -> Text, data -> Text, } } diff --git a/common/src/lib.rs b/common/src/lib.rs index 8cefcb0..da2d9f0 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -35,6 +35,7 @@ pub mod demo_analysis { #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct PlayerHeatmap { pub name: String, + pub team: String, pub png_data: String, } diff --git a/frontend/src/demo/heatmap.rs b/frontend/src/demo/heatmap.rs index 1c0a1d5..c8fa3f2 100644 --- a/frontend/src/demo/heatmap.rs +++ b/frontend/src/demo/heatmap.rs @@ -90,7 +90,7 @@ fn heatmap_view(heatmaps: Vec) -> impl lep { (move |heatmaps: Vec| { heatmaps.iter().enumerate().map(|(idx, heatmap)| { view! { - + } }).collect::>() })(h1.clone())} diff --git a/migrations/2024-09-28-132839_heatmap/up.sql b/migrations/2024-09-28-132839_heatmap/up.sql index 63670c5..1407da0 100644 --- a/migrations/2024-09-28-132839_heatmap/up.sql +++ b/migrations/2024-09-28-132839_heatmap/up.sql @@ -2,6 +2,7 @@ CREATE TABLE IF NOT EXISTS demo_heatmaps ( demo_id TEXT NOT NULL, steam_id TEXT NOT NULL, + team TEXT NOT NULL, data TEXT NOT NULL, - PRIMARY KEY (demo_id, steam_id) + PRIMARY KEY (demo_id, steam_id, team) );