diff --git a/backend/src/analysis.rs b/backend/src/analysis.rs index 569402a..e00dff4 100644 --- a/backend/src/analysis.rs +++ b/backend/src/analysis.rs @@ -107,6 +107,13 @@ pub struct AnalysisInput { pub struct BaseInfo { pub map: String, pub players: Vec<(BasePlayerInfo, BasePlayerStats)>, + pub teams: Vec<(u32, BaseTeamInfo)> +} + +#[derive(Debug)] +pub struct BaseTeamInfo { + pub score: usize, + pub name: String, } #[derive(Debug)] diff --git a/backend/src/analysis/base.rs b/backend/src/analysis/base.rs index 6702e2b..34b8842 100644 --- a/backend/src/analysis/base.rs +++ b/backend/src/analysis/base.rs @@ -10,8 +10,25 @@ impl BaseAnalysis { impl Analysis for BaseAnalysis { #[tracing::instrument(name = "Base", skip(self, input))] - fn analyse(&self, input: AnalysisInput) -> Result core::pin::Pin> + Send + '_)>> + Send>, ()> { tracing::info!("Performing Base analysis"); - + fn analyse( + &self, + input: AnalysisInput, + ) -> Result< + Box< + dyn FnOnce( + &mut diesel_async::pg::AsyncPgConnection, + ) -> core::pin::Pin< + Box< + (dyn core::future::Future> + + Send + + '_), + >, + > + Send, + >, + (), + > { + tracing::info!("Performing Base analysis"); + let file = std::fs::File::open(&input.path).unwrap(); let mmap = unsafe { memmap2::MmapOptions::new().map(&file).unwrap() }; @@ -19,20 +36,33 @@ impl Analysis for BaseAnalysis { let base_result = BaseInfo { map: result.map, - players: result.players.into_iter().map(|(info, stats)| { - (BasePlayerInfo { - name: info.name, - steam_id: info.steam_id, - team: info.team, - ingame_id: info.ingame_id, - color: info.color, - }, BasePlayerStats { - kills: stats.kills, - assists: stats.assists, - damage: stats.damage, - deaths: stats.deaths, + players: result + .players + .into_iter() + .map(|(info, stats)| { + ( + BasePlayerInfo { + name: info.name, + steam_id: info.steam_id, + team: info.team, + ingame_id: info.ingame_id, + color: info.color, + }, + BasePlayerStats { + kills: stats.kills, + assists: stats.assists, + damage: stats.damage, + deaths: stats.deaths, + }, + ) }) - }).collect() + .collect(), + teams: result.teams.into_iter().map(|(numb, val)| { + (numb, BaseTeamInfo { + name: val.name, + score: val.score, + }) + }).collect(), }; let (player_info, player_stats): (Vec<_>, Vec<_>) = base_result @@ -64,48 +94,68 @@ impl Analysis for BaseAnalysis { map: base_result.map, }; + let demo_teams: Vec = base_result.teams.into_iter().map(|(numb, info)| { + crate::models::DemoTeam { + demo_id: input.demoid.clone(), + team: numb as i16, + end_score: info.score as i16, + start_name: info.name, + } + }).collect(); + Ok(Box::new(move |connection| { let store_demo_info_query = - diesel::dsl::insert_into(crate::schema::demo_info::dsl::demo_info) - .values(demo_info) - .on_conflict(crate::schema::demo_info::dsl::demo_id) - .do_update() - .set( - crate::schema::demo_info::dsl::map - .eq(diesel::upsert::excluded(crate::schema::demo_info::dsl::map)), - ); - let store_demo_players_query = - diesel::dsl::insert_into(crate::schema::demo_players::dsl::demo_players) - .values(player_info) - .on_conflict_do_nothing(); + diesel::dsl::insert_into(crate::schema::demo_info::dsl::demo_info) + .values(demo_info) + .on_conflict(crate::schema::demo_info::dsl::demo_id) + .do_update() + .set( + crate::schema::demo_info::dsl::map + .eq(diesel::upsert::excluded(crate::schema::demo_info::dsl::map)), + ); + let store_demo_players_query = + diesel::dsl::insert_into(crate::schema::demo_players::dsl::demo_players) + .values(player_info) + .on_conflict_do_nothing(); - let store_demo_player_stats_query = - diesel::dsl::insert_into(crate::schema::demo_player_stats::dsl::demo_player_stats) - .values(player_stats) - .on_conflict(( - crate::schema::demo_player_stats::dsl::demo_id, - crate::schema::demo_player_stats::dsl::steam_id, - )) - .do_update() - .set(( - crate::schema::demo_player_stats::dsl::deaths.eq(diesel::upsert::excluded( - crate::schema::demo_player_stats::dsl::deaths, - )), - crate::schema::demo_player_stats::dsl::kills.eq(diesel::upsert::excluded( - crate::schema::demo_player_stats::dsl::kills, - )), - crate::schema::demo_player_stats::dsl::assists.eq(diesel::upsert::excluded( - crate::schema::demo_player_stats::dsl::assists, - )), - crate::schema::demo_player_stats::dsl::damage.eq(diesel::upsert::excluded( - crate::schema::demo_player_stats::dsl::damage, - )), + let store_demo_player_stats_query = + diesel::dsl::insert_into(crate::schema::demo_player_stats::dsl::demo_player_stats) + .values(player_stats) + .on_conflict(( + crate::schema::demo_player_stats::dsl::demo_id, + crate::schema::demo_player_stats::dsl::steam_id, + )) + .do_update() + .set(( + crate::schema::demo_player_stats::dsl::deaths.eq(diesel::upsert::excluded( + crate::schema::demo_player_stats::dsl::deaths, + )), + crate::schema::demo_player_stats::dsl::kills.eq(diesel::upsert::excluded( + crate::schema::demo_player_stats::dsl::kills, + )), + crate::schema::demo_player_stats::dsl::assists.eq( + diesel::upsert::excluded( + crate::schema::demo_player_stats::dsl::assists, + ), + ), + crate::schema::demo_player_stats::dsl::damage.eq(diesel::upsert::excluded( + crate::schema::demo_player_stats::dsl::damage, + )), + )); + + let store_demo_teams = diesel::dsl::insert_into(crate::schema::demo_teams::dsl::demo_teams) + .values(demo_teams).on_conflict((crate::schema::demo_teams::dsl::demo_id, crate::schema::demo_teams::dsl::team)) + .do_update() + .set(( + crate::schema::demo_teams::dsl::start_name.eq(diesel::upsert::excluded(crate::schema::demo_teams::dsl::start_name)), + crate::schema::demo_teams::dsl::end_score.eq(diesel::upsert::excluded(crate::schema::demo_teams::dsl::end_score)), )); Box::pin(async move { store_demo_info_query.execute(connection).await?; store_demo_players_query.execute(connection).await?; store_demo_player_stats_query.execute(connection).await?; + store_demo_teams.execute(connection).await?; Ok(()) }) diff --git a/backend/src/analysis/heatmap.rs b/backend/src/analysis/heatmap.rs index 68a1692..3d3677e 100644 --- a/backend/src/analysis/heatmap.rs +++ b/backend/src/analysis/heatmap.rs @@ -10,47 +10,77 @@ impl HeatmapAnalysis { impl Analysis for HeatmapAnalysis { #[tracing::instrument(name = "Heatmap", skip(self, input))] - fn analyse(&self, input: AnalysisInput) -> Result core::pin::Pin> + Send + '_)>> + Send>, ()> { + fn analyse( + &self, + input: AnalysisInput, + ) -> Result< + Box< + dyn FnOnce( + &mut diesel_async::pg::AsyncPgConnection, + ) -> core::pin::Pin< + Box< + (dyn core::future::Future> + + Send + + '_), + >, + > + Send, + >, + (), + > { tracing::info!("Generating HEATMAPs"); - let file = std::fs::File::open(&input.path).unwrap(); - let mmap = unsafe { memmap2::MmapOptions::new().map(&file).unwrap() }; + let file = std::fs::File::open(&input.path).unwrap(); + let mmap = unsafe { memmap2::MmapOptions::new().map(&file).unwrap() }; - let config = analysis::heatmap::Config { - cell_size: 5.0, - }; - let result = analysis::heatmap::parse(&config, &mmap).unwrap(); + let config = analysis::heatmap::Config { cell_size: 5.0 }; + 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, team), heatmap)| { - let player = match result.player_info.get(&userid) { - Some(p) => p, - None => { - tracing::warn!("Could not find player: {:?}", userid); - return None; - } - }; - - Some(((player.xuid.to_string(), team), heatmap)) - }).collect(); + tracing::info!("Got {} Entity-Heatmaps", result.player_heatmaps.len()); + 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 => { + tracing::warn!("Could not find player: {:?}", userid); + return None; + } + }; - let player_heatmaps: Vec<_> = heatmap_result.into_iter().map(|((player, team), heatmap)| { - tracing::trace!("HeatMap for Player: {:?} in Team {:?}", player, team); + Some(((player.xuid.to_string(), team), heatmap)) + }) + .collect(); - crate::models::DemoPlayerHeatmap { - demo_id: input.demoid.clone(), - steam_id: player, - team, - data: serde_json::to_string(&heatmap).unwrap(), - } - }).collect(); + 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(); 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, 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))); + 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, + 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, + )), + ); Box::pin(async move { store_demo_player_heatmaps_query.execute(connection).await?; diff --git a/backend/src/analysis/perround.rs b/backend/src/analysis/perround.rs index 749613a..755ce3e 100644 --- a/backend/src/analysis/perround.rs +++ b/backend/src/analysis/perround.rs @@ -10,22 +10,41 @@ impl PerRoundAnalysis { impl Analysis for PerRoundAnalysis { #[tracing::instrument(name = "PerRoundAnalysis", skip(self, input))] - fn analyse(&self, input: AnalysisInput) -> Result core::pin::Pin> + Send + '_)>> + Send>, ()> { + fn analyse( + &self, + input: AnalysisInput, + ) -> Result< + Box< + dyn FnOnce( + &mut diesel_async::pg::AsyncPgConnection, + ) -> core::pin::Pin< + Box< + (dyn core::future::Future> + + Send + + '_), + >, + > + Send, + >, + (), + > { let file = std::fs::File::open(&input.path).unwrap(); let mmap = unsafe { memmap2::MmapOptions::new().map(&file).unwrap() }; let result = analysis::perround::parse(&mmap).unwrap(); - let values: Vec = result.rounds.into_iter().enumerate().map(|(i, r)| { - crate::models::DemoRound { + let values: Vec = result + .rounds + .into_iter() + .enumerate() + .map(|(i, r)| crate::models::DemoRound { demo_id: input.demoid.clone(), round_number: i as i16, start_tick: r.start as i64, end_tick: r.end as i64, win_reason: serde_json::to_string(&r.winreason).unwrap(), events: serde_json::to_value(&r.events).unwrap(), - } - }).collect(); + }) + .collect(); Ok(Box::new(move |connection| { Box::pin(async move { diff --git a/backend/src/api/demos.rs b/backend/src/api/demos.rs index 40d7550..2e8dcbd 100644 --- a/backend/src/api/demos.rs +++ b/backend/src/api/demos.rs @@ -41,28 +41,46 @@ async fn list( tracing::info!("SteamID: {:?}", steam_id); let query = crate::schema::demos::dsl::demos - .inner_join(crate::schema::demo_info::table.on( - crate::schema::demos::dsl::demo_id.eq(crate::schema::demo_info::dsl::demo_id), - )) + .inner_join( + crate::schema::demo_info::table + .on(crate::schema::demos::dsl::demo_id.eq(crate::schema::demo_info::dsl::demo_id)), + ) + .inner_join( + crate::schema::demo_teams::table + .on(crate::schema::demos::dsl::demo_id.eq(crate::schema::demo_teams::dsl::demo_id)), + ) .select(( crate::models::Demo::as_select(), crate::models::DemoInfo::as_select(), + crate::models::DemoTeam::as_select(), )) .filter(crate::schema::demos::dsl::steam_id.eq(steam_id.to_string())); - let results: Vec<(crate::models::Demo, crate::models::DemoInfo)> = + let results: Vec<(crate::models::Demo, crate::models::DemoInfo, crate::models::DemoTeam)> = query.load(&mut crate::db_connection().await).await.unwrap(); + + let mut demos = std::collections::HashMap::new(); + for (demo, info, team) in results.into_iter() { + let entry = demos.entry(demo.demo_id.clone()).or_insert(common::BaseDemoInfo { + id: demo.demo_id, + map: info.map, + team2_score: 0, + team3_score: 0, + }); - Ok(axum::response::Json( - results - .into_iter() - .map(|(demo, info)| { - common::BaseDemoInfo { - id: demo.demo_id, - map: info.map, - } - }) - .collect::>(), - )) + if team.team == 2 { + entry.team2_score = team.end_score; + } else if team.team == 3 { + entry.team3_score = team.end_score; + } else { + tracing::warn!("Unknown Team: {:?}", team); + } + } + + // TODO + // Sort this + let mut output = demos.into_values().collect::>(); + + Ok(axum::response::Json(output)) } #[tracing::instrument(skip(state, session, form))] @@ -82,7 +100,10 @@ async fn upload( Some(c) => c, None => { tracing::error!("Getting File content from request"); - return Err((axum::http::StatusCode::BAD_REQUEST, "Failed to get file-content from upload")); + return Err(( + axum::http::StatusCode::BAD_REQUEST, + "Failed to get file-content from upload", + )); } }; @@ -90,7 +111,7 @@ async fn upload( let demo_id = raw_demo_id.to_string(); tracing::debug!(?demo_id, "Upload Size: {:?}", file_content.len()); - + let user_folder = std::path::Path::new(&state.upload_folder).join(format!("{}/", steam_id)); if !tokio::fs::try_exists(&user_folder).await.unwrap_or(false) { tokio::fs::create_dir_all(&user_folder).await.unwrap(); @@ -106,30 +127,36 @@ async fn upload( // Turn all of this into a single transaction - let db_trans_result = db_con.build_transaction().run(|c| { - Box::pin(async move { - let query = - diesel::dsl::insert_into(crate::schema::demos::dsl::demos).values(crate::models::NewDemo { - demo_id: demo_id.clone(), - steam_id: steam_id.to_string(), - }); - query.execute(c).await?; + let db_trans_result = db_con + .build_transaction() + .run(|c| { + Box::pin(async move { + let query = diesel::dsl::insert_into(crate::schema::demos::dsl::demos).values( + crate::models::NewDemo { + demo_id: demo_id.clone(), + steam_id: steam_id.to_string(), + }, + ); + query.execute(c).await?; - let queue_query = diesel::dsl::insert_into(crate::schema::analysis_queue::dsl::analysis_queue) - .values(crate::models::AddAnalysisTask { - demo_id: demo_id.clone(), - steam_id: steam_id.to_string(), - }); - queue_query.execute(c).await?; + let queue_query = + diesel::dsl::insert_into(crate::schema::analysis_queue::dsl::analysis_queue) + .values(crate::models::AddAnalysisTask { + demo_id: demo_id.clone(), + steam_id: steam_id.to_string(), + }); + queue_query.execute(c).await?; - let processing_query = - diesel::dsl::insert_into(crate::schema::processing_status::dsl::processing_status) - .values(crate::models::ProcessingStatus { demo_id, info: 0 }); - processing_query.execute(c).await?; + let processing_query = diesel::dsl::insert_into( + crate::schema::processing_status::dsl::processing_status, + ) + .values(crate::models::ProcessingStatus { demo_id, info: 0 }); + processing_query.execute(c).await?; - Ok::<(), diesel::result::Error>(()) + Ok::<(), diesel::result::Error>(()) + }) }) - }).await; + .await; if let Err(e) = db_trans_result { tracing::error!("Inserting data into db: {:?}", e); @@ -267,12 +294,14 @@ async fn scoreboard( async fn heatmap( session: UserSession, Path(demo_id): Path, -) -> Result>, axum::http::StatusCode> { +) -> Result>, axum::http::StatusCode> +{ use base64::prelude::Engine; let mut db_con = crate::db_connection().await; - let demo_info_query = crate::schema::demo_info::dsl::demo_info.filter(crate::schema::demo_info::dsl::demo_id.eq(demo_id.clone())); + let demo_info_query = crate::schema::demo_info::dsl::demo_info + .filter(crate::schema::demo_info::dsl::demo_id.eq(demo_id.clone())); let demo_info: crate::models::DemoInfo = match demo_info_query.first(&mut db_con).await { Ok(i) => i, Err(e) => { @@ -282,18 +311,26 @@ async fn heatmap( }; let query = crate::schema::demo_players::dsl::demo_players - .inner_join(crate::schema::demo_heatmaps::dsl::demo_heatmaps.on( - crate::schema::demo_players::dsl::steam_id.eq(crate::schema::demo_heatmaps::dsl::steam_id) - .and(crate::schema::demo_players::dsl::demo_id.eq(crate::schema::demo_heatmaps::dsl::demo_id)) - )).filter(crate::schema::demo_players::dsl::demo_id.eq(demo_id)); + .inner_join( + crate::schema::demo_heatmaps::dsl::demo_heatmaps.on( + crate::schema::demo_players::dsl::steam_id + .eq(crate::schema::demo_heatmaps::dsl::steam_id) + .and( + crate::schema::demo_players::dsl::demo_id + .eq(crate::schema::demo_heatmaps::dsl::demo_id), + ), + ), + ) + .filter(crate::schema::demo_players::dsl::demo_id.eq(demo_id)); - let result: Vec<(crate::models::DemoPlayer, crate::models::DemoPlayerHeatmap)> = match query.load(&mut db_con).await { - Ok(d) => d, - Err(e) => { - tracing::error!("Querying DB: {:?}", e); - return Err(axum::http::StatusCode::INTERNAL_SERVER_ERROR); - } - }; + let result: Vec<(crate::models::DemoPlayer, crate::models::DemoPlayerHeatmap)> = + match query.load(&mut db_con).await { + Ok(d) => d, + Err(e) => { + tracing::error!("Querying DB: {:?}", e); + return Err(axum::http::StatusCode::INTERNAL_SERVER_ERROR); + } + }; let demo_map = &demo_info.map; let minimap_coords = match MINIMAP_COORDINATES.get(demo_map) { @@ -304,21 +341,30 @@ 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(); + 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(); - let mut buffer = std::io::Cursor::new(Vec::new()); - h_image.write_to(&mut buffer, image::ImageFormat::Png).unwrap(); + let mut buffer = std::io::Cursor::new(Vec::new()); + h_image + .write_to(&mut buffer, image::ImageFormat::Png) + .unwrap(); - common::demo_analysis::PlayerHeatmap { - name: player.name, - team, - png_data: base64::prelude::BASE64_STANDARD.encode(buffer.into_inner()), - } - }).collect(); + common::demo_analysis::PlayerHeatmap { + name: player.name, + team, + png_data: base64::prelude::BASE64_STANDARD.encode(buffer.into_inner()), + } + }) + .collect(); Ok(axum::Json(data)) } @@ -328,23 +374,38 @@ async fn perround( session: UserSession, Path(demo_id): Path, ) -> Result>, axum::http::StatusCode> { - let rounds_query = crate::schema::demo_round::dsl::demo_round.filter(crate::schema::demo_round::dsl::demo_id.eq(demo_id.clone())); - let round_players_query = crate::schema::demo_players::dsl::demo_players.filter(crate::schema::demo_players::dsl::demo_id.eq(demo_id)); + let rounds_query = crate::schema::demo_round::dsl::demo_round + .filter(crate::schema::demo_round::dsl::demo_id.eq(demo_id.clone())); + let round_players_query = crate::schema::demo_players::dsl::demo_players + .filter(crate::schema::demo_players::dsl::demo_id.eq(demo_id)); let mut db_con = crate::db_connection().await; - + let raw_rounds: Vec = rounds_query.load(&mut db_con).await.unwrap(); - let players: Vec = round_players_query.load(&mut db_con).await.unwrap(); + let players: Vec = + round_players_query.load(&mut db_con).await.unwrap(); let mut result = Vec::with_capacity(raw_rounds.len()); for raw_round in raw_rounds.into_iter() { let reason = match serde_json::from_str(&raw_round.win_reason) { - Ok(analysis::perround::WinReason::StillInProgress) => common::demo_analysis::RoundWinReason::StillInProgress, - Ok(analysis::perround::WinReason::TKilled) => common::demo_analysis::RoundWinReason::TKilled, - Ok(analysis::perround::WinReason::CTKilled) => common::demo_analysis::RoundWinReason::CTKilled, - Ok(analysis::perround::WinReason::BombDefused) => common::demo_analysis::RoundWinReason::BombDefused, - Ok(analysis::perround::WinReason::BombExploded) => common::demo_analysis::RoundWinReason::BombExploded, - Ok(analysis::perround::WinReason::TimeRanOut) => common::demo_analysis::RoundWinReason::TimeRanOut, + Ok(analysis::perround::WinReason::StillInProgress) => { + common::demo_analysis::RoundWinReason::StillInProgress + } + Ok(analysis::perround::WinReason::TKilled) => { + common::demo_analysis::RoundWinReason::TKilled + } + Ok(analysis::perround::WinReason::CTKilled) => { + common::demo_analysis::RoundWinReason::CTKilled + } + Ok(analysis::perround::WinReason::BombDefused) => { + common::demo_analysis::RoundWinReason::BombDefused + } + Ok(analysis::perround::WinReason::BombExploded) => { + common::demo_analysis::RoundWinReason::BombExploded + } + Ok(analysis::perround::WinReason::TimeRanOut) => { + common::demo_analysis::RoundWinReason::TimeRanOut + } Ok(other) => { tracing::error!("Unknown Mapping {:?}", other); return Err(axum::http::StatusCode::INTERNAL_SERVER_ERROR); @@ -355,27 +416,38 @@ async fn perround( } }; - let parsed_events: Vec = serde_json::from_value(raw_round.events).unwrap(); - let events: Vec<_> = parsed_events.into_iter().map(|event| { - match event { - analysis::perround::RoundEvent::BombPlanted => common::demo_analysis::RoundEvent::BombPlanted, - analysis::perround::RoundEvent::BombDefused => common::demo_analysis::RoundEvent::BombDefused, + let parsed_events: Vec = + serde_json::from_value(raw_round.events).unwrap(); + let events: Vec<_> = parsed_events + .into_iter() + .map(|event| match event { + analysis::perround::RoundEvent::BombPlanted => { + common::demo_analysis::RoundEvent::BombPlanted + } + analysis::perround::RoundEvent::BombDefused => { + common::demo_analysis::RoundEvent::BombDefused + } analysis::perround::RoundEvent::Kill { attacker, died } => { - let attacker_name = players.iter().find(|p| p.steam_id == attacker.to_string()).map(|p| p.name.clone()).unwrap(); - let died_name = players.iter().find(|p| p.steam_id == died.to_string()).map(|p| p.name.clone()).unwrap(); + let attacker_name = players + .iter() + .find(|p| p.steam_id == attacker.to_string()) + .map(|p| p.name.clone()) + .unwrap(); + let died_name = players + .iter() + .find(|p| p.steam_id == died.to_string()) + .map(|p| p.name.clone()) + .unwrap(); common::demo_analysis::RoundEvent::Killed { attacker: attacker_name, died: died_name, } } - } - }).collect(); + }) + .collect(); - result.push(common::demo_analysis::DemoRound { - reason, - events, - }); + result.push(common::demo_analysis::DemoRound { reason, events }); } Ok(axum::Json(result)) diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 06d3244..cce5673 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -76,12 +76,12 @@ pub async fn run_api( }), ) .layer(session_layer) - .nest_service( - "/", - tower_http::services::ServeDir::new(serve_dir), - ); + .nest_service("/", tower_http::services::ServeDir::new(serve_dir)); - let listen_addr = std::net::SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)), 3000); + let listen_addr = std::net::SocketAddr::new( + std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)), + 3000, + ); tracing::info!("Listening on Addr: {:?}", listen_addr); let listener = tokio::net::TcpListener::bind(listen_addr).await.unwrap(); @@ -110,17 +110,18 @@ pub async fn run_analysis(upload_folder: impl Into) { let mut store_result_fns = Vec::new(); for analysis in analysis::ANALYSIS_METHODS.iter().map(|a| a.clone()) { let input = input.clone(); - let store_result = match tokio::task::spawn_blocking(move || analysis.analyse(input)).await { - Ok(Ok(r)) => r, - Ok(Err(e)) => { - tracing::error!("Analysis failed: {:?}", e); - continue; - } - Err(e) => { - tracing::error!("Joining Task: {:?}", e); - continue; - } - }; + let store_result = + match tokio::task::spawn_blocking(move || analysis.analyse(input)).await { + Ok(Ok(r)) => r, + Ok(Err(e)) => { + tracing::error!("Analysis failed: {:?}", e); + continue; + } + Err(e) => { + tracing::error!("Joining Task: {:?}", e); + continue; + } + }; store_result_fns.push(store_result); } diff --git a/backend/src/main.rs b/backend/src/main.rs index 9f1754c..3e55f59 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -26,8 +26,7 @@ async fn main() { let registry = tracing_subscriber::Registry::default() .with(tracing_subscriber::fmt::layer()) .with(tracing_subscriber::filter::filter_fn(|meta| { - meta.target().contains("backend") - || meta.target().contains("analysis") + meta.target().contains("backend") || meta.target().contains("analysis") })); tracing::subscriber::set_global_default(registry).unwrap(); diff --git a/backend/src/models.rs b/backend/src/models.rs index c1bf795..940ec6b 100644 --- a/backend/src/models.rs +++ b/backend/src/models.rs @@ -23,7 +23,7 @@ pub struct NewDemo { pub struct Demo { pub steam_id: String, pub demo_id: String, - pub uploaded_at: diesel::data_types::PgTimestamp + pub uploaded_at: diesel::data_types::PgTimestamp, } #[derive(Queryable, Selectable, Insertable, Debug)] @@ -65,6 +65,16 @@ pub struct DemoPlayerStats { pub assists: i16, } +#[derive(Queryable, Selectable, Insertable, Debug)] +#[diesel(table_name = crate::schema::demo_teams)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct DemoTeam { + pub demo_id: String, + pub team: i16, + pub end_score: i16, + pub start_name: String, +} + #[derive(Queryable, Selectable, Insertable, Debug)] #[diesel(table_name = crate::schema::processing_status)] #[diesel(check_for_backend(diesel::pg::Pg))] diff --git a/backend/src/schema.rs b/backend/src/schema.rs index e153d31..e0e6c35 100644 --- a/backend/src/schema.rs +++ b/backend/src/schema.rs @@ -56,6 +56,15 @@ diesel::table! { } } +diesel::table! { + demo_teams (demo_id, team) { + demo_id -> Text, + team -> Int2, + end_score -> Int2, + start_name -> Text, + } +} + diesel::table! { demos (steam_id, demo_id) { steam_id -> Text, @@ -79,13 +88,6 @@ diesel::table! { } } -diesel::table! { - user_demos (steam_id, demo_id) { - steam_id -> Text, - demo_id -> Text, - } -} - diesel::table! { users (steamid) { steamid -> Text, @@ -100,9 +102,9 @@ diesel::allow_tables_to_appear_in_same_query!( demo_player_stats, demo_players, demo_round, + demo_teams, demos, processing_status, sessions, - user_demos, users, ); diff --git a/common/src/lib.rs b/common/src/lib.rs index da2d9f0..cab97d3 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -2,6 +2,8 @@ pub struct BaseDemoInfo { pub id: String, pub map: String, + pub team2_score: i16, + pub team3_score: i16, } #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs index 33cade3..b35af6c 100644 --- a/frontend/src/lib.rs +++ b/frontend/src/lib.rs @@ -18,7 +18,7 @@ pub fn demo_list_entry(demo: common::BaseDemoInfo) -> impl leptos::IntoView { view! {
  • - {demo.map} - {demo.id} + {demo.map} - {demo.id} - {demo.team2_score}:{demo.team3_score}
  • } diff --git a/migrations/2024-09-11-200628_demo_info/down.sql b/migrations/2024-09-11-200628_demo_info/down.sql index 16986aa..5184bd3 100644 --- a/migrations/2024-09-11-200628_demo_info/down.sql +++ b/migrations/2024-09-11-200628_demo_info/down.sql @@ -2,4 +2,5 @@ DROP TABLE processing_status; DROP TABLE demo_player_stats; DROP TABLE demo_players; +DROP TABLE demo_teams; DROP TABLE demo_info; diff --git a/migrations/2024-09-11-200628_demo_info/up.sql b/migrations/2024-09-11-200628_demo_info/up.sql index d2473fc..baa644a 100644 --- a/migrations/2024-09-11-200628_demo_info/up.sql +++ b/migrations/2024-09-11-200628_demo_info/up.sql @@ -27,3 +27,11 @@ CREATE TABLE IF NOT EXISTS demo_player_stats ( assists int2 NOT NULL, PRIMARY KEY (demo_id, steam_id) ); + +CREATE TABLE IF NOT EXISTS demo_teams ( + demo_id TEXT NOT NULL, + team int2 NOT NULL, + end_score int2 NOT NULL, + start_name TEXT NOT NULL, + PRIMARY KEY (demo_id, team) +);