From 5cb9094f76a5f9e1fc6668402ab479ba0b9dd976 Mon Sep 17 00:00:00 2001
From: Lol3rrr
Date: Tue, 15 Oct 2024 22:02:19 +0200
Subject: [PATCH] Implement Team display in perround overview
Now tracks the scores of the teams and displays them on the homepage.
Also now displays the team numbers next to the overview of rounds won to know which team won which round
---
Cargo.lock | 38 +++++++-------
analysis/src/endofgame.rs | 90 +++++++++++++++------------------
analysis/tests/endofgame.rs | 19 +------
backend/src/analysis.rs | 30 -----------
backend/src/analysis/base.rs | 81 +++++++++++++++++++++--------
backend/src/api/demos.rs | 35 +++++++------
backend/src/models.rs | 20 ++++----
common/src/demo_analysis.rs | 73 ++++++++++++++++++++++++++
common/src/lib.rs | 65 +-----------------------
frontend/src/demo/perround.rs | 46 +++++++++--------
frontend/src/demo/scoreboard.rs | 19 +++++--
11 files changed, 263 insertions(+), 253 deletions(-)
create mode 100644 common/src/demo_analysis.rs
diff --git a/Cargo.lock b/Cargo.lock
index 4e2e84a..c3b0abd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -224,7 +224,7 @@ dependencies = [
"http 1.1.0",
"http-body 1.0.1",
"http-body-util",
- "hyper 1.4.1",
+ "hyper 1.5.0",
"hyper-util",
"itoa",
"matchit",
@@ -663,7 +663,7 @@ dependencies = [
[[package]]
name = "csdemo"
version = "0.1.0"
-source = "git+https://github.com/Lol3rrr/csdemo.git#7dfa4fa57ac25e17c3abe5ad7d7621e003786eca"
+source = "git+https://github.com/Lol3rrr/csdemo.git#195d2fbc15e7cf3795995519b696239233f5f9b8"
dependencies = [
"bitter",
"phf",
@@ -1328,9 +1328,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
-version = "0.14.30"
+version = "0.14.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
+checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85"
dependencies = [
"bytes",
"futures-channel",
@@ -1352,9 +1352,9 @@ dependencies = [
[[package]]
name = "hyper"
-version = "1.4.1"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
+checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
dependencies = [
"bytes",
"futures-channel",
@@ -1378,7 +1378,7 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
"futures-util",
"http 1.1.0",
- "hyper 1.4.1",
+ "hyper 1.5.0",
"hyper-util",
"rustls",
"rustls-pki-types",
@@ -1395,7 +1395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
- "hyper 0.14.30",
+ "hyper 0.14.31",
"native-tls",
"tokio",
"tokio-native-tls",
@@ -1412,7 +1412,7 @@ dependencies = [
"futures-util",
"http 1.1.0",
"http-body 1.0.1",
- "hyper 1.4.1",
+ "hyper 1.5.0",
"pin-project-lite",
"socket2",
"tokio",
@@ -2459,18 +2459,18 @@ dependencies = [
[[package]]
name = "profiling"
-version = "1.0.15"
+version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
+checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
-version = "1.0.15"
+version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
+checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30"
dependencies = [
"quote",
"syn",
@@ -2798,7 +2798,7 @@ dependencies = [
"h2",
"http 0.2.12",
"http-body 0.4.6",
- "hyper 0.14.30",
+ "hyper 0.14.31",
"hyper-tls",
"ipnet",
"js-sys",
@@ -2838,7 +2838,7 @@ dependencies = [
"http 1.1.0",
"http-body 1.0.1",
"http-body-util",
- "hyper 1.4.1",
+ "hyper 1.5.0",
"hyper-rustls",
"hyper-util",
"ipnet",
@@ -2970,9 +2970,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
+checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
[[package]]
name = "rustls-webpki"
@@ -2987,9 +2987,9 @@ dependencies = [
[[package]]
name = "rustversion"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
[[package]]
name = "ryu"
diff --git a/analysis/src/endofgame.rs b/analysis/src/endofgame.rs
index fb5f70f..3088ff6 100644
--- a/analysis/src/endofgame.rs
+++ b/analysis/src/endofgame.rs
@@ -1,16 +1,14 @@
-use std::collections::HashMap;
-
#[derive(Debug, PartialEq)]
pub struct EndOfGame {
pub map: String,
pub players: Vec<(PlayerInfo, PlayerStats)>,
- pub teams: HashMap,
+ pub teams: std::collections::HashMap,
}
#[derive(Debug, PartialEq)]
pub struct TeamInfo {
- pub name: String,
- pub score: usize,
+ pub end_score: usize,
+ pub start_side: String,
}
#[derive(Debug, PartialEq)]
@@ -44,6 +42,7 @@ pub fn parse(buf: &[u8]) -> Result {
let header = &output.header;
let mut player_stats = std::collections::HashMap::<_, PlayerStats>::new();
+ let mut pawn_to_player = std::collections::HashMap::::new();
let mut track = false;
let mut player_life = std::collections::HashMap::<_, u8>::new();
@@ -59,7 +58,13 @@ pub fn parse(buf: &[u8]) -> Result {
track = true;
}
csdemo::game_event::GameEvent::PlayerSpawn(pspawn) => {
- player_life.insert(pspawn.userid.unwrap(), 100);
+ let userid = pspawn.userid.unwrap();
+
+ player_life.insert(userid.clone(), 100);
+
+ if let Some(pawn) = pspawn.userid_pawn.as_ref().map(|p| match p { csdemo::RawValue::I32(v) => Some(csdemo::structured::pawnid::PawnID::from(*v)), _ => None }).flatten() {
+ pawn_to_player.insert(pawn, userid);
+ }
}
csdemo::game_event::GameEvent::WinPanelMatch(_) => {
track = false;
@@ -85,55 +90,42 @@ pub fn parse(buf: &[u8]) -> Result {
};
}
- let mut teams = HashMap::new();
- let mut entity_to_team = HashMap::new();
- let mut entity_to_name = HashMap::<_, String>::new();
+ let mut teams = std::collections::HashMap::::new();
+
+ let mut entity_to_team = std::collections::HashMap::new();
for tick_state in output.entity_states.ticks {
for state in tick_state.states {
- if state.class.as_ref() != "CCSTeam" {
+ let team = match csdemo::structured::ccsteam::CCSTeam::try_from(&state) {
+ Ok(t) => t,
+ Err(_) => continue,
+ };
+
+ let pawns = team.player_pawns();
+ let player_ids = pawns.into_iter().filter_map(|pawn| pawn_to_player.get(&pawn)).collect::>();
+ if player_ids.is_empty() {
+ if let Some(team_number) = entity_to_team.get(&team.entity_id()) {
+ if let Some(score) = team.score() {
+ if let Some(team_entry) = teams.get_mut(team_number) {
+ team_entry.end_score = score as usize;
+ }
+ }
+ }
+
continue;
}
- let team = match state
- .get_prop("CCSTeam.m_iTeamNum")
- .map(|p| p.value.as_u32())
- .flatten()
- {
- Some(team) => {
- entity_to_team.insert(state.id, team.clone());
- team
+ let team_number = player_ids.iter().filter_map(|p| output.player_info.get(*p).map(|p| p.team)).next().unwrap();
+
+ entity_to_team.insert(team.entity_id(), team_number);
+
+ let team_entry = teams.entry(team_number).or_insert_with(|| {
+ TeamInfo {
+ end_score: 0,
+ start_side: team.team_name().map(|t| t.to_owned()).unwrap_or(String::new()),
}
- None => match entity_to_team.get(&state.id) {
- Some(t) => t.clone(),
- None => continue,
- },
- };
-
- let name = match entity_to_name.get(&state.id) {
- Some(n) => n.to_owned(),
- None => match state
- .get_prop("CCSTeam.m_szTeamname")
- .map(|p| match &p.value {
- csdemo::parser::Variant::String(v) => Some(v.to_owned()),
- _ => None,
- })
- .flatten()
- {
- Some(n) => {
- entity_to_name.insert(state.id, n.clone());
- n
- }
- None => continue,
- },
- };
-
- if let Some(score) = state
- .get_prop("CCSTeam.m_iScore")
- .map(|p| p.value.as_i32())
- .flatten()
- .map(|v| v as usize)
- {
- teams.insert(team, TeamInfo { name, score });
+ });
+ if let Some(score) = team.score() {
+ team_entry.end_score = score as usize;
}
}
}
diff --git a/analysis/tests/endofgame.rs b/analysis/tests/endofgame.rs
index b68e4dc..32e19e5 100644
--- a/analysis/tests/endofgame.rs
+++ b/analysis/tests/endofgame.rs
@@ -11,24 +11,7 @@ fn endofgame_nuke() {
let expected = endofgame::EndOfGame {
map: "de_nuke".to_owned(),
- teams: [
- (
- 3,
- endofgame::TeamInfo {
- name: "CT".to_owned(),
- score: 8,
- },
- ),
- (
- 2,
- endofgame::TeamInfo {
- name: "TERRORIST".to_owned(),
- score: 13,
- },
- ),
- ]
- .into_iter()
- .collect(),
+ teams: [].into_iter().collect(),
players: vec![
(
endofgame::PlayerInfo {
diff --git a/backend/src/analysis.rs b/backend/src/analysis.rs
index e00dff4..4c38120 100644
--- a/backend/src/analysis.rs
+++ b/backend/src/analysis.rs
@@ -102,33 +102,3 @@ pub struct AnalysisInput {
pub demoid: String,
pub path: PathBuf,
}
-
-#[derive(Debug)]
-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)]
-pub struct BasePlayerInfo {
- pub name: String,
- pub steam_id: String,
- pub team: i32,
- pub color: i32,
- pub ingame_id: i32,
-}
-
-#[derive(Debug)]
-pub struct BasePlayerStats {
- pub kills: usize,
- pub deaths: usize,
- pub damage: usize,
- pub assists: usize,
-}
diff --git a/backend/src/analysis/base.rs b/backend/src/analysis/base.rs
index 34b8842..e71396c 100644
--- a/backend/src/analysis/base.rs
+++ b/backend/src/analysis/base.rs
@@ -1,5 +1,35 @@
use super::*;
+#[derive(Debug)]
+struct BaseInfo {
+ pub map: String,
+ pub players: Vec<(BasePlayerInfo, BasePlayerStats)>,
+ pub teams: std::collections::HashMap,
+}
+
+#[derive(Debug)]
+struct BaseTeamInfo {
+ pub start_side: String,
+ pub end_score: usize,
+}
+
+#[derive(Debug)]
+struct BasePlayerInfo {
+ pub name: String,
+ pub steam_id: String,
+ pub team: i32,
+ pub color: i32,
+ pub ingame_id: i32,
+}
+
+#[derive(Debug)]
+struct BasePlayerStats {
+ pub kills: usize,
+ pub deaths: usize,
+ pub damage: usize,
+ pub assists: usize,
+}
+
pub struct BaseAnalysis {}
impl BaseAnalysis {
@@ -36,6 +66,12 @@ impl Analysis for BaseAnalysis {
let base_result = BaseInfo {
map: result.map,
+ teams: result.teams.into_iter().map(|(numb, team)| {
+ (numb, BaseTeamInfo {
+ end_score: team.end_score,
+ start_side: team.start_side,
+ })
+ }).collect(),
players: result
.players
.into_iter()
@@ -57,12 +93,6 @@ impl Analysis for BaseAnalysis {
)
})
.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
@@ -89,20 +119,20 @@ impl Analysis for BaseAnalysis {
})
.unzip();
+ let teams = base_result.teams.into_iter().map(|(numb, team)| {
+ crate::models::DemoTeam {
+ demo_id: input.demoid.clone(),
+ team: numb as i16,
+ end_score: team.end_score as i16,
+ start_name: team.start_side,
+ }
+ }).collect::>();
+
let demo_info = crate::models::DemoInfo {
demo_id: input.demoid.clone(),
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)
@@ -143,19 +173,26 @@ impl Analysis for BaseAnalysis {
)),
));
- 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)),
+ let store_teams = diesel::dsl::insert_into(crate::schema::demo_teams::dsl::demo_teams)
+ .values(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?;
+ store_teams.execute(connection).await?;
Ok(())
})
diff --git a/backend/src/api/demos.rs b/backend/src/api/demos.rs
index 2e8dcbd..134ff03 100644
--- a/backend/src/api/demos.rs
+++ b/backend/src/api/demos.rs
@@ -264,18 +264,11 @@ async fn scoreboard(
return Err(axum::http::StatusCode::INTERNAL_SERVER_ERROR);
}
- let team1_number: i16 = response.last().map(|(p, _)| p.team).unwrap();
-
- let mut team1 = Vec::new();
- let mut team2 = Vec::new();
+ let mut teams = std::collections::BTreeMap::new();
for (player, stats) in response {
- let team_vec = if player.team == team1_number {
- &mut team1
- } else {
- &mut team2
- };
+ let team = teams.entry(player.team as u32).or_insert(Vec::new());
- team_vec.push(common::demo_analysis::ScoreBoardPlayer {
+ team.push(common::demo_analysis::ScoreBoardPlayer {
name: player.name,
kills: stats.kills as usize,
deaths: stats.deaths as usize,
@@ -285,8 +278,7 @@ async fn scoreboard(
}
Ok(axum::Json(common::demo_analysis::ScoreBoard {
- team1,
- team2,
+ teams: teams.into_iter().collect::>()
}))
}
@@ -373,17 +365,20 @@ async fn heatmap(
async fn perround(
session: UserSession,
Path(demo_id): Path,
-) -> Result>, axum::http::StatusCode> {
+) -> 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));
+ .filter(crate::schema::demo_players::dsl::demo_id.eq(demo_id.clone()));
+ let demo_teams = crate::schema::demo_teams::dsl::demo_teams
+ .filter(crate::schema::demo_teams::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 raw_teams: Vec = demo_teams.load(&mut db_con).await.unwrap();
let mut result = Vec::with_capacity(raw_rounds.len());
for raw_round in raw_rounds.into_iter() {
@@ -450,7 +445,17 @@ async fn perround(
result.push(common::demo_analysis::DemoRound { reason, events });
}
- Ok(axum::Json(result))
+ let teams = raw_teams.into_iter().map(|dteam| {
+ common::demo_analysis::PerRoundTeam {
+ name: dteam.start_name,
+ number: dteam.team as u32,
+ }
+ }).collect();
+
+ Ok(axum::Json(common::demo_analysis::PerRoundResult {
+ rounds: result,
+ teams,
+ }))
}
// The corresponding values for each map can be found using the Source2 Viewer and opening the
diff --git a/backend/src/models.rs b/backend/src/models.rs
index 940ec6b..6d074a6 100644
--- a/backend/src/models.rs
+++ b/backend/src/models.rs
@@ -42,6 +42,16 @@ pub struct DemoInfo {
pub map: String,
}
+#[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::demo_players)]
#[diesel(check_for_backend(diesel::pg::Pg))]
@@ -65,16 +75,6 @@ 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/common/src/demo_analysis.rs b/common/src/demo_analysis.rs
new file mode 100644
index 0000000..b5e01b8
--- /dev/null
+++ b/common/src/demo_analysis.rs
@@ -0,0 +1,73 @@
+#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+ pub struct ScoreBoard {
+ pub teams: Vec<(u32, Vec)>
+ }
+
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+ pub struct ScoreBoardPlayer {
+ pub name: String,
+ pub kills: usize,
+ pub deaths: usize,
+ pub damage: usize,
+ pub assists: usize,
+ }
+
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+ pub struct PlayerHeatmap {
+ pub name: String,
+ pub team: String,
+ pub png_data: String,
+ }
+
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+ pub struct PerRoundResult {
+ pub teams: Vec,
+ pub rounds: Vec,
+ }
+
+#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+pub struct PerRoundTeam {
+ pub name: String,
+ pub number: u32,
+}
+
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+ pub struct DemoRound {
+ pub reason: RoundWinReason,
+ pub events: Vec
+ }
+
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+ pub enum RoundWinReason {
+ StillInProgress,
+ BombExploded,
+ VipEscaped,
+ VipKilled,
+ TSaved,
+ CtStoppedEscape,
+ RoundEndReasonTerroristsStopped,
+ BombDefused,
+ TKilled,
+ CTKilled,
+ Draw,
+ HostageRescued,
+ TimeRanOut,
+ RoundEndReasonHostagesNotRescued,
+ TerroristsNotEscaped,
+ VipNotEscaped,
+ GameStart,
+ TSurrender,
+ CTSurrender,
+ TPlanted,
+ CTReachedHostage,
+ }
+
+ #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+ pub enum RoundEvent {
+ BombPlanted,
+ BombDefused,
+ Killed {
+ attacker: String,
+ died: String,
+ },
+ }
diff --git a/common/src/lib.rs b/common/src/lib.rs
index cab97d3..90729ac 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -18,67 +18,4 @@ pub struct DemoInfo {
pub map: String,
}
-pub mod demo_analysis {
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
- pub struct ScoreBoard {
- pub team1: Vec,
- pub team2: Vec,
- }
-
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
- pub struct ScoreBoardPlayer {
- pub name: String,
- pub kills: usize,
- pub deaths: usize,
- pub damage: usize,
- pub assists: usize,
- }
-
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
- pub struct PlayerHeatmap {
- pub name: String,
- pub team: String,
- pub png_data: String,
- }
-
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
- pub struct DemoRound {
- pub reason: RoundWinReason,
- pub events: Vec
- }
-
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
- pub enum RoundWinReason {
- StillInProgress,
- BombExploded,
- VipEscaped,
- VipKilled,
- TSaved,
- CtStoppedEscape,
- RoundEndReasonTerroristsStopped,
- BombDefused,
- TKilled,
- CTKilled,
- Draw,
- HostageRescued,
- TimeRanOut,
- RoundEndReasonHostagesNotRescued,
- TerroristsNotEscaped,
- VipNotEscaped,
- GameStart,
- TSurrender,
- CTSurrender,
- TPlanted,
- CTReachedHostage,
- }
-
- #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
- pub enum RoundEvent {
- BombPlanted,
- BombDefused,
- Killed {
- attacker: String,
- died: String,
- },
- }
-}
+pub mod demo_analysis;
diff --git a/frontend/src/demo/perround.rs b/frontend/src/demo/perround.rs
index 6a7e0c5..2cca48f 100644
--- a/frontend/src/demo/perround.rs
+++ b/frontend/src/demo/perround.rs
@@ -1,26 +1,14 @@
use leptos::*;
-fn to_roman(mut number: u32) -> char {
- if number < 12 {
- char::from_u32(8544 + number).unwrap()
- } else if number < 24 {
- char::from_u32(8544 + (number - 12)).unwrap()
- } else if number < 27 {
- char::from_u32(8544 + (number - 24)).unwrap()
- } else {
- char::from_u32(8544 + (number - 27)).unwrap()
- }
-}
-
fn to_coloumn(idx: usize) -> usize {
if idx < 12 {
- 1 + idx
+ 2 + idx
} else if idx < 24 {
- 1 + idx + 1
+ 2 + idx + 1
} else if idx < 27 {
- 1 + idx + 2
+ 2 + idx + 2
} else {
- 1 + idx + 3
+ 2 + idx + 3
}
}
@@ -35,18 +23,18 @@ pub fn per_round() -> impl leptos::IntoView {
.send()
.await
.unwrap();
- res.json::>()
+ res.json::()
.await
.unwrap()
});
-
+
let style = stylers::style! {
"PerRound",
.round_overview {
display: inline-grid;
width: 90vw;
- grid-template-columns: repeat(12, 1fr) 5px repeat(12, 1fr) 5px repeat(3, 1fr) 5px repeat(3, 1fr);
+ grid-template-columns: auto repeat(12, 1fr) 5px repeat(12, 1fr) 5px repeat(3, 1fr) 5px repeat(3, 1fr);
grid-template-rows: repeat(3, auto);
}
@@ -84,7 +72,7 @@ pub fn per_round() -> impl leptos::IntoView {
let events_list = move || {
let round_index = round();
- let current_round = perround_resource.get().map(|rs| rs.get(round_index).cloned()).flatten();
+ let current_round = perround_resource.get().map(|rs| rs.rounds.get(round_index).cloned()).flatten();
match current_round {
Some(round) => {
@@ -106,7 +94,7 @@ pub fn per_round() -> impl leptos::IntoView {
set_round.set(r);
};
- let round = perround_resource.get().map(|rs| rs.get(r).cloned()).flatten();
+ let round = perround_resource.get().map(|rs| rs.rounds.get(r).cloned()).flatten();
let reason = round.map(|r| r.reason);
let (ct_won, t_won) = match &reason {
@@ -159,11 +147,27 @@ pub fn per_round() -> impl leptos::IntoView {
}).collect::>()
};
+ let team_names = move || {
+ let perround_teams = match perround_resource.get().map(|p| p.teams) {
+ Some(t) => t,
+ None => return view! {}.into_view(),
+ };
+
+ let upper = perround_teams.iter().find(|t| t.name == "CT").map(|t| t.number).unwrap_or(0);
+ let lower = perround_teams.iter().find(|t| t.name == "TERRORIST").map(|t| t.number).unwrap_or(0);
+
+ view! {
+ Team { upper }
+ Team { lower }
+ }.into_view()
+ };
+
view! {
class=style,
Per Round
+ { team_names }
{ round_overview }
diff --git a/frontend/src/demo/scoreboard.rs b/frontend/src/demo/scoreboard.rs
index f40b472..0143711 100644
--- a/frontend/src/demo/scoreboard.rs
+++ b/frontend/src/demo/scoreboard.rs
@@ -20,14 +20,24 @@ pub fn scoreboard() -> impl leptos::IntoView {
let (ordering, set_ordering) = create_signal::(orderings::DAMAGE);
+ let scoreboards = move || {
+ scoreboard_resource
+ .get()
+ .into_iter()
+ .flat_map(|v| v.teams.into_iter())
+ .map(|(team, players)| view! {
+
+ })
+ .collect::>()
+ };
+
view! {
Scoreboard
Loading Scoreboard data
}
>
-
-
+ { scoreboards }
}
}
@@ -79,7 +89,7 @@ mod orderings {
}
#[leptos::component]
-fn team_scoreboard(info: Resource, team_name: String, part: fn(common::demo_analysis::ScoreBoard) -> Vec) -> impl IntoView {
+fn team_scoreboard(value: Vec, team_name: String) -> impl IntoView {
let (ordering, set_ordering) = create_signal::(orderings::DAMAGE);
let style = stylers::style! {
@@ -119,8 +129,7 @@ fn team_scoreboard(info: Resource
{
move || {
- let value = info.get().map(|v| part(v));
- let mut players: Vec<_> = value.into_iter().flat_map(|v| v).collect();
+ let mut players: Vec<_> = value.clone().into_iter().collect();
let sorting = ordering.get();
players.sort_unstable_by(|p1, p2| (sorting.sort_fn)(p1, p2));