Move analysis into own crate and some more improvements all around
This commit is contained in:
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
testfiles/* filter=lfs diff=lfs merge=lfs -text
|
||||||
113
Cargo.lock
generated
113
Cargo.lock
generated
@@ -26,6 +26,14 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "analysis"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"csdemo",
|
||||||
|
"pretty_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.15"
|
version = "0.6.15"
|
||||||
@@ -147,9 +155,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.7.5"
|
version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
@@ -174,7 +182,7 @@ dependencies = [
|
|||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"sync_wrapper 1.0.1",
|
"sync_wrapper 1.0.1",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower 0.5.1",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -182,9 +190,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
|
checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -195,7 +203,7 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
"sync_wrapper 0.1.2",
|
"sync_wrapper 1.0.1",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -205,6 +213,7 @@ dependencies = [
|
|||||||
name = "backend"
|
name = "backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"analysis",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -295,9 +304,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.7.1"
|
version = "1.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "camino"
|
name = "camino"
|
||||||
@@ -307,9 +316,9 @@ checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.20"
|
version = "1.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "45bcde016d64c21da4be18b655631e5ab6d3107607e71a73a9f53eb48aae23fb"
|
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@@ -349,9 +358,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.17"
|
version = "4.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -359,9 +368,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.17"
|
version = "4.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -371,9 +380,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.13"
|
version = "4.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -504,13 +513,14 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "csdemo"
|
name = "csdemo"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/Lol3rrr/csdemo.git#c5237af33bc892437cf7b8658de34d7dad8947a0"
|
source = "git+https://github.com/Lol3rrr/csdemo.git#a2b3ee18452145e42da2667321bf33752ad31ba2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitter",
|
"bitter",
|
||||||
"phf",
|
"phf",
|
||||||
"prost",
|
"prost",
|
||||||
"prost-build",
|
"prost-build",
|
||||||
"prost-types",
|
"prost-types",
|
||||||
|
"regex",
|
||||||
"snap",
|
"snap",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -655,6 +665,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diff"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -1225,7 +1241,7 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower 0.4.13",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
@@ -1965,6 +1981,16 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pretty_assertions"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||||
|
dependencies = [
|
||||||
|
"diff",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.2.22"
|
version = "0.2.22"
|
||||||
@@ -2065,9 +2091,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost"
|
name = "prost"
|
||||||
version = "0.13.2"
|
version = "0.13.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995"
|
checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"prost-derive",
|
"prost-derive",
|
||||||
@@ -2075,9 +2101,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-build"
|
name = "prost-build"
|
||||||
version = "0.13.2"
|
version = "0.13.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302"
|
checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"heck",
|
"heck",
|
||||||
@@ -2096,9 +2122,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-derive"
|
name = "prost-derive"
|
||||||
version = "0.13.2"
|
version = "0.13.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac"
|
checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
@@ -2109,9 +2135,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-types"
|
name = "prost-types"
|
||||||
version = "0.13.2"
|
version = "0.13.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519"
|
checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prost",
|
"prost",
|
||||||
]
|
]
|
||||||
@@ -2471,9 +2497,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework-sys"
|
name = "security-framework-sys"
|
||||||
version = "2.11.1"
|
version = "2.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
|
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -2875,18 +2901,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.63"
|
version = "1.0.64"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -3083,6 +3109,21 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"pin-project-lite",
|
||||||
|
"sync_wrapper 0.1.2",
|
||||||
|
"tokio",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3304,9 +3345,9 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.23"
|
version = "0.1.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
|
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
@@ -3325,9 +3366,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["backend", "common", "frontend"]
|
members = [ "analysis","backend", "common", "frontend"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|||||||
10
analysis/Cargo.toml
Normal file
10
analysis/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "analysis"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
csdemo = { package = "csdemo", git = "https://github.com/Lol3rrr/csdemo.git", ref = "main" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pretty_assertions = { version = "1.4" }
|
||||||
179
analysis/src/endofgame.rs
Normal file
179
analysis/src/endofgame.rs
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct EndOfGame {
|
||||||
|
pub map: String,
|
||||||
|
pub players: Vec<(PlayerInfo, PlayerStats)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct PlayerInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub steam_id: String,
|
||||||
|
pub team: i32,
|
||||||
|
pub color: i32,
|
||||||
|
pub ingame_id: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct PlayerStats {
|
||||||
|
pub kills: usize,
|
||||||
|
pub deaths: usize,
|
||||||
|
pub damage: usize,
|
||||||
|
pub team_damage: usize,
|
||||||
|
pub self_damage: usize,
|
||||||
|
pub assists: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(buf: &[u8]) -> Result<EndOfGame, ()> {
|
||||||
|
let tmp = csdemo::Container::parse(buf).map_err(|e| ())?;
|
||||||
|
let output = csdemo::parser::parse(
|
||||||
|
csdemo::FrameIterator::parse(tmp.inner),
|
||||||
|
csdemo::parser::EntityFilter::all(),
|
||||||
|
)
|
||||||
|
.map_err(|e| ())?;
|
||||||
|
|
||||||
|
let header = &output.header;
|
||||||
|
|
||||||
|
let mut player_stats = std::collections::HashMap::<_, PlayerStats>::new();
|
||||||
|
|
||||||
|
let mut track = false;
|
||||||
|
let mut player_life = std::collections::HashMap::<_, u8>::new();
|
||||||
|
for event in output.events.iter() {
|
||||||
|
match event {
|
||||||
|
csdemo::DemoEvent::GameEvent(gevent) => {
|
||||||
|
match gevent.as_ref() {
|
||||||
|
csdemo::game_event::GameEvent::RoundAnnounceMatchStart(_) => {
|
||||||
|
player_stats.clear();
|
||||||
|
track = true;
|
||||||
|
}
|
||||||
|
csdemo::game_event::GameEvent::RoundPreStart(_) => {
|
||||||
|
track = true;
|
||||||
|
}
|
||||||
|
csdemo::game_event::GameEvent::PlayerSpawn(pspawn) => {
|
||||||
|
player_life.insert(pspawn.userid.unwrap(), 100);
|
||||||
|
}
|
||||||
|
csdemo::game_event::GameEvent::WinPanelMatch(_) => {
|
||||||
|
track = false;
|
||||||
|
}
|
||||||
|
csdemo::game_event::GameEvent::RoundOfficiallyEnded(_) => {
|
||||||
|
track = false;
|
||||||
|
}
|
||||||
|
csdemo::game_event::GameEvent::PlayerDeath(pdeath) if track => {
|
||||||
|
player_death(pdeath, &output.player_info, &mut player_stats);
|
||||||
|
}
|
||||||
|
csdemo::game_event::GameEvent::PlayerHurt(phurt) if track => {
|
||||||
|
player_hurt(
|
||||||
|
phurt,
|
||||||
|
&output.player_info,
|
||||||
|
&mut player_stats,
|
||||||
|
&mut player_life,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
other => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut players: Vec<_> = player_stats
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(id, stats)| {
|
||||||
|
let player = output.player_info.get(&id)?;
|
||||||
|
|
||||||
|
Some((
|
||||||
|
PlayerInfo {
|
||||||
|
name: player.name.clone(),
|
||||||
|
steam_id: player.xuid.to_string(),
|
||||||
|
team: player.team,
|
||||||
|
color: player.color,
|
||||||
|
ingame_id: id.0,
|
||||||
|
},
|
||||||
|
stats,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
players.sort_unstable_by_key(|(p, _)| p.ingame_id);
|
||||||
|
|
||||||
|
let map = header.map_name().to_owned();
|
||||||
|
|
||||||
|
Ok(EndOfGame { map, players })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn player_death(
|
||||||
|
death: &csdemo::game_event::PlayerDeath,
|
||||||
|
player_info: &std::collections::HashMap<csdemo::UserId, csdemo::parser::Player>,
|
||||||
|
player_stats: &mut std::collections::HashMap<csdemo::UserId, PlayerStats>,
|
||||||
|
) {
|
||||||
|
let player_died_id = death.userid.unwrap();
|
||||||
|
|
||||||
|
let player_died_player = player_info.get(&player_died_id).unwrap();
|
||||||
|
let player_died = player_stats.entry(player_died_id).or_default();
|
||||||
|
player_died.deaths += 1;
|
||||||
|
|
||||||
|
if let Some(attacker_id) = death.attacker.filter(|p| p.0 < 10) {
|
||||||
|
let attacker_player = player_info
|
||||||
|
.get(&attacker_id)
|
||||||
|
.expect(&format!("Attacker-ID: {:?}", attacker_id));
|
||||||
|
if attacker_player.team == player_died_player.team {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
let attacker = player_stats.entry(attacker_id).or_default();
|
||||||
|
attacker.kills += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(assist_id) = death.assister.filter(|p| p.0 < 10) {
|
||||||
|
let assister_player = player_info
|
||||||
|
.get(&assist_id)
|
||||||
|
.expect(&format!("Assister-ID: {:?}", assist_id));
|
||||||
|
|
||||||
|
if assister_player.team == player_died_player.team {
|
||||||
|
} else {
|
||||||
|
let assister = player_stats.entry(assist_id).or_default();
|
||||||
|
assister.assists += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn player_hurt(
|
||||||
|
hurt: &csdemo::game_event::PlayerHurt,
|
||||||
|
player_info: &std::collections::HashMap<csdemo::UserId, csdemo::parser::Player>,
|
||||||
|
player_stats: &mut std::collections::HashMap<csdemo::UserId, PlayerStats>,
|
||||||
|
player_life: &mut std::collections::HashMap<csdemo::UserId, u8>,
|
||||||
|
) {
|
||||||
|
let attacked_player = match player_info.get(hurt.userid.as_ref().unwrap()) {
|
||||||
|
Some(a) => a,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let attacker_id = match hurt.attacker {
|
||||||
|
Some(aid) => aid,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let attacking_player = match player_info.get(&attacker_id) {
|
||||||
|
Some(a) => a,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let attacker = player_stats.entry(attacker_id).or_default();
|
||||||
|
|
||||||
|
let n_health = match hurt.health {
|
||||||
|
Some(csdemo::RawValue::F32(v)) => v as u8,
|
||||||
|
Some(csdemo::RawValue::I32(v)) => v as u8,
|
||||||
|
Some(csdemo::RawValue::U64(v)) => v as u8,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
let dmg_dealt = player_life
|
||||||
|
.get(hurt.userid.as_ref().unwrap())
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(100)
|
||||||
|
- n_health;
|
||||||
|
|
||||||
|
player_life.insert(hurt.userid.unwrap(), n_health);
|
||||||
|
|
||||||
|
if attacking_player.team == attacked_player.team {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attacker.damage += dmg_dealt as usize;
|
||||||
|
}
|
||||||
1
analysis/src/lib.rs
Normal file
1
analysis/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod endofgame;
|
||||||
190
analysis/tests/endofgame.rs
Normal file
190
analysis/tests/endofgame.rs
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
use analysis::endofgame;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ancient() {
|
||||||
|
let input_bytes = include_bytes!("../../testfiles/nuke.dem");
|
||||||
|
|
||||||
|
let result = endofgame::parse(input_bytes).unwrap();
|
||||||
|
|
||||||
|
let expected = endofgame::EndOfGame {
|
||||||
|
map: "de_nuke".to_owned(),
|
||||||
|
players: vec![
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "Excel".to_owned(),
|
||||||
|
steam_id: "76561198236134832".to_owned(),
|
||||||
|
team: 2,
|
||||||
|
color: 0,
|
||||||
|
ingame_id: 0,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 28,
|
||||||
|
deaths: 11,
|
||||||
|
damage: 2504,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 4,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "Der Porzellan König".to_owned(),
|
||||||
|
steam_id: "76561198301388087".to_owned(),
|
||||||
|
team: 2,
|
||||||
|
color: 2,
|
||||||
|
ingame_id: 1,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 15,
|
||||||
|
deaths: 12,
|
||||||
|
damage: 1827,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 6,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "Crippled Hentai addict".to_owned(),
|
||||||
|
steam_id: "76561198386810758".to_owned(),
|
||||||
|
team: 2,
|
||||||
|
color: 3,
|
||||||
|
ingame_id: 2,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 11,
|
||||||
|
deaths: 16,
|
||||||
|
damage: 1394,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 5,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "Skalla_xD".to_owned(),
|
||||||
|
steam_id: "76561199014043225".to_owned(),
|
||||||
|
team: 2,
|
||||||
|
color: 1,
|
||||||
|
ingame_id: 3,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 11,
|
||||||
|
deaths: 15,
|
||||||
|
damage: 1331,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 3,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "xTee".to_owned(),
|
||||||
|
steam_id: "76561199132258707".to_owned(),
|
||||||
|
team: 2,
|
||||||
|
color: 4,
|
||||||
|
ingame_id: 4,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 9,
|
||||||
|
deaths: 17,
|
||||||
|
damage: 1148,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 2,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "cute".to_owned(),
|
||||||
|
steam_id: "76561197966517722".to_owned(),
|
||||||
|
team: 3,
|
||||||
|
color: 3,
|
||||||
|
ingame_id: 5,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 17,
|
||||||
|
deaths: 16,
|
||||||
|
damage: 2143,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 7,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "zodiac".to_owned(),
|
||||||
|
steam_id: "76561198872143644".to_owned(),
|
||||||
|
team: 3,
|
||||||
|
color: 4,
|
||||||
|
ingame_id: 6,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 7,
|
||||||
|
deaths: 15,
|
||||||
|
damage: 844,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 4,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "IReLaX exe".to_owned(),
|
||||||
|
steam_id: "76561199077629121".to_owned(),
|
||||||
|
team: 3,
|
||||||
|
color: 2,
|
||||||
|
ingame_id: 7,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 13,
|
||||||
|
deaths: 17,
|
||||||
|
damage: 1423,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 6,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "Haze".to_owned(),
|
||||||
|
steam_id: "76561198375555469".to_owned(),
|
||||||
|
team: 3,
|
||||||
|
color: 0,
|
||||||
|
ingame_id: 8,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 19,
|
||||||
|
deaths: 15,
|
||||||
|
damage: 1512,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 3,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
endofgame::PlayerInfo {
|
||||||
|
name: "Know_Name".to_owned(),
|
||||||
|
steam_id: "76561198119236104".to_owned(),
|
||||||
|
team: 3,
|
||||||
|
color: 1,
|
||||||
|
ingame_id: 9,
|
||||||
|
},
|
||||||
|
endofgame::PlayerStats {
|
||||||
|
kills: 14,
|
||||||
|
deaths: 16,
|
||||||
|
damage: 1431,
|
||||||
|
team_damage: 0,
|
||||||
|
self_damage: 0,
|
||||||
|
assists: 4,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Add stats for rest of players
|
||||||
|
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ diesel_async_migrations = { version = "0.15" }
|
|||||||
reqwest = { version = "0.12", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
|
|
||||||
common = { path = "../common/" }
|
common = { path = "../common/" }
|
||||||
|
analysis = { path = "../analysis/" }
|
||||||
|
|
||||||
csdemo = { package = "csdemo", git = "https://github.com/Lol3rrr/csdemo.git", ref = "main" }
|
csdemo = { package = "csdemo", git = "https://github.com/Lol3rrr/csdemo.git", ref = "main" }
|
||||||
memmap2 = { version = "0.9" }
|
memmap2 = { version = "0.9" }
|
||||||
|
|||||||
@@ -3,27 +3,51 @@ use std::path::PathBuf;
|
|||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
pub async fn poll_next_task(upload_folder: &std::path::Path, db_con: &mut diesel_async::pg::AsyncPgConnection) -> Result<AnalysisInput, ()> {
|
pub async fn poll_next_task(
|
||||||
let query = crate::schema::analysis_queue::dsl::analysis_queue.order(crate::schema::analysis_queue::dsl::created_at.asc()).limit(1).select(crate::models::AnalysisTask::as_select()).for_update().skip_locked();
|
upload_folder: &std::path::Path,
|
||||||
|
db_con: &mut diesel_async::pg::AsyncPgConnection,
|
||||||
|
) -> Result<AnalysisInput, ()> {
|
||||||
|
let query = crate::schema::analysis_queue::dsl::analysis_queue
|
||||||
|
.order(crate::schema::analysis_queue::dsl::created_at.asc())
|
||||||
|
.limit(1)
|
||||||
|
.select(crate::models::AnalysisTask::as_select())
|
||||||
|
.for_update()
|
||||||
|
.skip_locked();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let result = db_con.build_transaction().run::<'_, _, diesel::result::Error, _>(|conn| Box::pin(async move {
|
let result = db_con
|
||||||
|
.build_transaction()
|
||||||
|
.run::<'_, _, diesel::result::Error, _>(|conn| {
|
||||||
|
Box::pin(async move {
|
||||||
let mut results: Vec<crate::models::AnalysisTask> = query.load(conn).await?;
|
let mut results: Vec<crate::models::AnalysisTask> = query.load(conn).await?;
|
||||||
let final_result = match results.pop() {
|
let final_result = match results.pop() {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let delete_query = diesel::dsl::delete(crate::schema::analysis_queue::dsl::analysis_queue).filter(crate::schema::analysis_queue::dsl::demo_id.eq(final_result.demo_id)).filter(crate::schema::analysis_queue::dsl::steam_id.eq(final_result.steam_id.clone()));
|
let delete_query =
|
||||||
|
diesel::dsl::delete(crate::schema::analysis_queue::dsl::analysis_queue)
|
||||||
|
.filter(
|
||||||
|
crate::schema::analysis_queue::dsl::demo_id
|
||||||
|
.eq(final_result.demo_id),
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
crate::schema::analysis_queue::dsl::steam_id
|
||||||
|
.eq(final_result.steam_id.clone()),
|
||||||
|
);
|
||||||
delete_query.execute(conn).await?;
|
delete_query.execute(conn).await?;
|
||||||
|
|
||||||
Ok(Some(final_result))
|
Ok(Some(final_result))
|
||||||
})).await;
|
})
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(Some(r)) => {
|
Ok(Some(r)) => {
|
||||||
return Ok(AnalysisInput {
|
return Ok(AnalysisInput {
|
||||||
path: upload_folder.join(&r.steam_id).join(format!("{}.dem", r.demo_id)),
|
path: upload_folder
|
||||||
|
.join(&r.steam_id)
|
||||||
|
.join(format!("{}.dem", r.demo_id)),
|
||||||
steamid: r.steam_id,
|
steamid: r.steam_id,
|
||||||
demoid: r.demo_id,
|
demoid: r.demo_id,
|
||||||
});
|
});
|
||||||
@@ -65,6 +89,8 @@ pub struct BasePlayerInfo {
|
|||||||
pub struct BasePlayerStats {
|
pub struct BasePlayerStats {
|
||||||
pub kills: usize,
|
pub kills: usize,
|
||||||
pub deaths: usize,
|
pub deaths: usize,
|
||||||
|
pub damage: usize,
|
||||||
|
pub assists: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(input))]
|
#[tracing::instrument(skip(input))]
|
||||||
@@ -74,75 +100,23 @@ pub fn analyse_base(input: AnalysisInput) -> BaseInfo {
|
|||||||
let file = std::fs::File::open(&input.path).unwrap();
|
let file = std::fs::File::open(&input.path).unwrap();
|
||||||
let mmap = unsafe { memmap2::MmapOptions::new().map(&file).unwrap() };
|
let mmap = unsafe { memmap2::MmapOptions::new().map(&file).unwrap() };
|
||||||
|
|
||||||
let tmp = csdemo::Container::parse(&mmap).unwrap();
|
let result = analysis::endofgame::parse(&mmap).unwrap();
|
||||||
let output = csdemo::parser::parse(csdemo::FrameIterator::parse(tmp.inner)).unwrap();
|
|
||||||
|
|
||||||
let header = &output.header;
|
|
||||||
|
|
||||||
tracing::info!("Header: {:?}", header);
|
|
||||||
|
|
||||||
let mut player_stats = std::collections::HashMap::with_capacity(output.player_info.len());
|
|
||||||
|
|
||||||
for event in output.events.iter() {
|
|
||||||
match event {
|
|
||||||
csdemo::DemoEvent::Tick(tick) => {}
|
|
||||||
csdemo::DemoEvent::ServerInfo(info) => {}
|
|
||||||
csdemo::DemoEvent::RankUpdate(update) => {}
|
|
||||||
csdemo::DemoEvent::RankReveal(reveal) => {}
|
|
||||||
csdemo::DemoEvent::GameEvent(gevent) => {
|
|
||||||
match gevent {
|
|
||||||
csdemo::game_event::GameEvent::BeginNewMatch(_) => {
|
|
||||||
player_stats.clear();
|
|
||||||
}
|
|
||||||
csdemo::game_event::GameEvent::PlayerTeam(pteam) => {
|
|
||||||
// tracing::info!("{:?}", pteam);
|
|
||||||
}
|
|
||||||
csdemo::game_event::GameEvent::RoundOfficiallyEnded(r_end) => {
|
|
||||||
// tracing::info!("{:?}", r_end);
|
|
||||||
}
|
|
||||||
csdemo::game_event::GameEvent::PlayerDeath(pdeath) => {
|
|
||||||
// tracing::info!("{:?}", pdeath);
|
|
||||||
|
|
||||||
let player_died_id = pdeath.userid.unwrap();
|
|
||||||
|
|
||||||
let player_died = player_stats.entry(player_died_id).or_insert(BasePlayerStats {
|
|
||||||
kills: 0,
|
|
||||||
deaths: 0,
|
|
||||||
});
|
|
||||||
player_died.deaths += 1;
|
|
||||||
|
|
||||||
if let Some(attacker_id) = pdeath.attacker {
|
|
||||||
let attacker = player_stats.entry(attacker_id).or_insert(BasePlayerStats {
|
|
||||||
kills: 0,
|
|
||||||
deaths: 0,
|
|
||||||
});
|
|
||||||
attacker.kills += 1;
|
|
||||||
|
|
||||||
// tracing::trace!("{:?} killed {:?}", attacker_id, player_died_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
other => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let players: Vec<_> = player_stats.into_iter().filter_map(|(id, stats)| {
|
|
||||||
let player = output.player_info.get(&id)?;
|
|
||||||
|
|
||||||
Some((BasePlayerInfo {
|
|
||||||
name: player.name.clone(),
|
|
||||||
steam_id: player.xuid.to_string(),
|
|
||||||
team: player.team,
|
|
||||||
color: player.color,
|
|
||||||
ingame_id: id.0,
|
|
||||||
}, stats))
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
let map = header.map_name().to_owned();
|
|
||||||
|
|
||||||
BaseInfo {
|
BaseInfo {
|
||||||
map,
|
map: result.map,
|
||||||
players,
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,9 +154,7 @@ pub struct RouterConfig {
|
|||||||
pub upload_dir: std::path::PathBuf,
|
pub upload_dir: std::path::PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn router(
|
pub fn router(config: RouterConfig) -> axum::Router {
|
||||||
config: RouterConfig,
|
|
||||||
) -> axum::Router {
|
|
||||||
axum::Router::new()
|
axum::Router::new()
|
||||||
.nest(
|
.nest(
|
||||||
"/steam/",
|
"/steam/",
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ struct DemoState {
|
|||||||
upload_folder: std::path::PathBuf,
|
upload_folder: std::path::PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn router<P>(
|
pub fn router<P>(upload_folder: P) -> axum::Router
|
||||||
upload_folder: P,
|
|
||||||
) -> axum::Router
|
|
||||||
where
|
where
|
||||||
P: Into<std::path::PathBuf>,
|
P: Into<std::path::PathBuf>,
|
||||||
{
|
{
|
||||||
@@ -102,7 +100,8 @@ async fn upload(
|
|||||||
});
|
});
|
||||||
query.execute(&mut db_con).await.unwrap();
|
query.execute(&mut db_con).await.unwrap();
|
||||||
|
|
||||||
let queue_query = diesel::dsl::insert_into(crate::schema::analysis_queue::dsl::analysis_queue).values(crate::models::AddAnalysisTask {
|
let queue_query = diesel::dsl::insert_into(crate::schema::analysis_queue::dsl::analysis_queue)
|
||||||
|
.values(crate::models::AddAnalysisTask {
|
||||||
demo_id,
|
demo_id,
|
||||||
steam_id: steam_id.to_string(),
|
steam_id: steam_id.to_string(),
|
||||||
});
|
});
|
||||||
@@ -145,7 +144,8 @@ async fn analyise(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let queue_query = diesel::dsl::insert_into(crate::schema::analysis_queue::dsl::analysis_queue).values(crate::models::AddAnalysisTask {
|
let queue_query = diesel::dsl::insert_into(crate::schema::analysis_queue::dsl::analysis_queue)
|
||||||
|
.values(crate::models::AddAnalysisTask {
|
||||||
demo_id,
|
demo_id,
|
||||||
steam_id: steam_id.to_string(),
|
steam_id: steam_id.to_string(),
|
||||||
});
|
});
|
||||||
@@ -181,14 +181,27 @@ async fn info(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(session))]
|
#[tracing::instrument(skip(session))]
|
||||||
async fn scoreboard(session: UserSession, Path(demo_id): Path<i64>) -> Result<axum::response::Json<common::demo_analysis::ScoreBoard>, axum::http::StatusCode> {
|
async fn scoreboard(
|
||||||
|
session: UserSession,
|
||||||
|
Path(demo_id): Path<i64>,
|
||||||
|
) -> Result<axum::response::Json<common::demo_analysis::ScoreBoard>, axum::http::StatusCode> {
|
||||||
let query = crate::schema::demo_players::dsl::demo_players
|
let query = crate::schema::demo_players::dsl::demo_players
|
||||||
.inner_join(crate::schema::demo_player_stats::dsl::demo_player_stats.on(crate::schema::demo_players::dsl::demo_id.eq(crate::schema::demo_player_stats::dsl::demo_id).and(crate::schema::demo_players::dsl::steam_id.eq(crate::schema::demo_player_stats::dsl::steam_id))))
|
.inner_join(
|
||||||
|
crate::schema::demo_player_stats::dsl::demo_player_stats.on(
|
||||||
|
crate::schema::demo_players::dsl::demo_id
|
||||||
|
.eq(crate::schema::demo_player_stats::dsl::demo_id)
|
||||||
|
.and(
|
||||||
|
crate::schema::demo_players::dsl::steam_id
|
||||||
|
.eq(crate::schema::demo_player_stats::dsl::steam_id),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
.filter(crate::schema::demo_players::dsl::demo_id.eq(demo_id));
|
.filter(crate::schema::demo_players::dsl::demo_id.eq(demo_id));
|
||||||
|
|
||||||
let mut db_con = crate::db_connection().await;
|
let mut db_con = crate::db_connection().await;
|
||||||
|
|
||||||
let response: Vec<(crate::models::DemoPlayer, crate::models::DemoPlayerStats)> = match query.load(&mut db_con).await {
|
let response: Vec<(crate::models::DemoPlayer, crate::models::DemoPlayerStats)> =
|
||||||
|
match query.load(&mut db_con).await {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Querying DB: {:?}", e);
|
tracing::error!("Querying DB: {:?}", e);
|
||||||
@@ -206,12 +219,18 @@ async fn scoreboard(session: UserSession, Path(demo_id): Path<i64>) -> Result<ax
|
|||||||
let mut team1 = Vec::new();
|
let mut team1 = Vec::new();
|
||||||
let mut team2 = Vec::new();
|
let mut team2 = Vec::new();
|
||||||
for (player, stats) in response {
|
for (player, stats) in response {
|
||||||
let team_vec = if player.team == team1_number { &mut team1 } else { &mut team2 };
|
let team_vec = if player.team == team1_number {
|
||||||
|
&mut team1
|
||||||
|
} else {
|
||||||
|
&mut team2
|
||||||
|
};
|
||||||
|
|
||||||
team_vec.push(common::demo_analysis::ScoreBoardPlayer {
|
team_vec.push(common::demo_analysis::ScoreBoardPlayer {
|
||||||
name: player.name,
|
name: player.name,
|
||||||
kills: stats.kills as usize,
|
kills: stats.kills as usize,
|
||||||
deaths: stats.deaths as usize,
|
deaths: stats.deaths as usize,
|
||||||
|
damage: stats.damage as usize,
|
||||||
|
assists: stats.assists as usize,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,15 +64,13 @@ pub async fn run_api(
|
|||||||
let router = axum::Router::new()
|
let router = axum::Router::new()
|
||||||
.nest(
|
.nest(
|
||||||
"/api/",
|
"/api/",
|
||||||
crate::api::router(
|
crate::api::router(crate::api::RouterConfig {
|
||||||
crate::api::RouterConfig {
|
|
||||||
steam_api_key: steam_api_key.into(),
|
steam_api_key: steam_api_key.into(),
|
||||||
// steam_callback_base_url: "http://192.168.0.156:3000".into(),
|
steam_callback_base_url: "http://192.168.0.156:3000".into(),
|
||||||
steam_callback_base_url: "http://localhost:3000".into(),
|
// steam_callback_base_url: "http://localhost:3000".into(),
|
||||||
steam_callback_path: "/api/steam/callback".into(),
|
steam_callback_path: "/api/steam/callback".into(),
|
||||||
upload_dir: upload_folder.clone(),
|
upload_dir: upload_folder.clone(),
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.layer(session_layer)
|
.layer(session_layer)
|
||||||
.nest_service(
|
.nest_service(
|
||||||
@@ -85,15 +83,12 @@ pub async fn run_api(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(upload_folder))]
|
#[tracing::instrument(skip(upload_folder))]
|
||||||
pub async fn run_analysis(
|
pub async fn run_analysis(upload_folder: impl Into<std::path::PathBuf>) {
|
||||||
upload_folder: impl Into<std::path::PathBuf>
|
|
||||||
) {
|
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
use diesel_async::{AsyncConnection, RunQueryDsl};
|
use diesel_async::{AsyncConnection, RunQueryDsl};
|
||||||
|
|
||||||
let upload_folder: std::path::PathBuf = upload_folder.into();
|
let upload_folder: std::path::PathBuf = upload_folder.into();
|
||||||
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut db_con = db_connection().await;
|
let mut db_con = db_connection().await;
|
||||||
let input = match crate::analysis::poll_next_task(&upload_folder, &mut db_con).await {
|
let input = match crate::analysis::poll_next_task(&upload_folder, &mut db_con).await {
|
||||||
@@ -114,37 +109,69 @@ pub async fn run_analysis(
|
|||||||
|
|
||||||
let mut db_con = crate::db_connection().await;
|
let mut db_con = crate::db_connection().await;
|
||||||
|
|
||||||
let (player_info, player_stats): (Vec<_>, Vec<_>) = result.players.into_iter().map(|(info, stats)| {
|
let (player_info, player_stats): (Vec<_>, Vec<_>) = result
|
||||||
(crate::models::DemoPlayer {
|
.players
|
||||||
|
.into_iter()
|
||||||
|
.map(|(info, stats)| {
|
||||||
|
(
|
||||||
|
crate::models::DemoPlayer {
|
||||||
demo_id,
|
demo_id,
|
||||||
name: info.name,
|
name: info.name,
|
||||||
steam_id: info.steam_id.clone(),
|
steam_id: info.steam_id.clone(),
|
||||||
team: info.team as i16,
|
team: info.team as i16,
|
||||||
color: info.color as i16,
|
color: info.color as i16,
|
||||||
}, crate::models::DemoPlayerStats {
|
},
|
||||||
|
crate::models::DemoPlayerStats {
|
||||||
demo_id,
|
demo_id,
|
||||||
steam_id: info.steam_id,
|
steam_id: info.steam_id,
|
||||||
deaths: stats.deaths as i16,
|
deaths: stats.deaths as i16,
|
||||||
kills: stats.kills as i16,
|
kills: stats.kills as i16,
|
||||||
|
damage: stats.damage as i16,
|
||||||
|
assists: stats.assists as i16,
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}).unzip();
|
.unzip();
|
||||||
|
|
||||||
let demo_info = crate::models::DemoInfo {
|
let demo_info = crate::models::DemoInfo {
|
||||||
demo_id,
|
demo_id,
|
||||||
map: result.map,
|
map: result.map,
|
||||||
};
|
};
|
||||||
|
|
||||||
let store_demo_info_query = diesel::dsl::insert_into(crate::schema::demo_info::dsl::demo_info)
|
let store_demo_info_query =
|
||||||
.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)));
|
diesel::dsl::insert_into(crate::schema::demo_info::dsl::demo_info)
|
||||||
let store_demo_players_query = diesel::dsl::insert_into(crate::schema::demo_players::dsl::demo_players).values(player_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();
|
.on_conflict_do_nothing();
|
||||||
let store_demo_player_stats_query = diesel::dsl::insert_into(crate::schema::demo_player_stats::dsl::demo_player_stats)
|
let store_demo_player_stats_query =
|
||||||
|
diesel::dsl::insert_into(crate::schema::demo_player_stats::dsl::demo_player_stats)
|
||||||
.values(player_stats)
|
.values(player_stats)
|
||||||
.on_conflict((crate::schema::demo_player_stats::dsl::demo_id, crate::schema::demo_player_stats::dsl::steam_id))
|
.on_conflict((
|
||||||
|
crate::schema::demo_player_stats::dsl::demo_id,
|
||||||
|
crate::schema::demo_player_stats::dsl::steam_id,
|
||||||
|
))
|
||||||
.do_update()
|
.do_update()
|
||||||
.set((
|
.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::deaths.eq(diesel::upsert::excluded(
|
||||||
crate::schema::demo_player_stats::dsl::kills.eq(diesel::upsert::excluded(crate::schema::demo_player_stats::dsl::kills)),
|
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 update_process_info =
|
let update_process_info =
|
||||||
diesel::dsl::update(crate::schema::processing_status::dsl::processing_status)
|
diesel::dsl::update(crate::schema::processing_status::dsl::processing_status)
|
||||||
|
|||||||
@@ -49,10 +49,7 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
component_set.spawn(backend::run_api(
|
component_set.spawn(backend::run_api(args.upload_folder.clone(), steam_api_key));
|
||||||
args.upload_folder.clone(),
|
|
||||||
steam_api_key,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if args.analysis {
|
if args.analysis {
|
||||||
component_set.spawn(backend::run_analysis(args.upload_folder.clone()));
|
component_set.spawn(backend::run_analysis(args.upload_folder.clone()));
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ pub struct DemoPlayerStats {
|
|||||||
pub steam_id: String,
|
pub steam_id: String,
|
||||||
pub kills: i16,
|
pub kills: i16,
|
||||||
pub deaths: i16,
|
pub deaths: i16,
|
||||||
|
pub damage: i16,
|
||||||
|
pub assists: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Selectable, Insertable, Debug)]
|
#[derive(Queryable, Selectable, Insertable, Debug)]
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ diesel::table! {
|
|||||||
steam_id -> Text,
|
steam_id -> Text,
|
||||||
kills -> Int2,
|
kills -> Int2,
|
||||||
deaths -> Int2,
|
deaths -> Int2,
|
||||||
|
damage -> Int2,
|
||||||
|
assists -> Int2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,5 +28,7 @@ pub mod demo_analysis {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
pub kills: usize,
|
pub kills: usize,
|
||||||
pub deaths: usize,
|
pub deaths: usize,
|
||||||
|
pub damage: usize,
|
||||||
|
pub assists: usize,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ pub fn demo() -> impl leptos::IntoView {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let rerun_analysis = create_action(move |_: &()| {
|
||||||
|
async move {
|
||||||
|
let _ = reqwasm::http::Request::get(&format!("/api/demos/{}/reanalyse", id())).send().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let map = move || match demo_info.get() {
|
let map = move || match demo_info.get() {
|
||||||
Some(v) => v.map.clone(),
|
Some(v) => v.map.clone(),
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
@@ -58,6 +64,7 @@ pub fn demo() -> impl leptos::IntoView {
|
|||||||
view! {class = style,
|
view! {class = style,
|
||||||
<h2>Demo - { id } - { map }</h2>
|
<h2>Demo - { id } - { map }</h2>
|
||||||
|
|
||||||
|
<button on:click=move |_| rerun_analysis.dispatch(())>Rerun analysis</button>
|
||||||
<div class="analysis_bar">
|
<div class="analysis_bar">
|
||||||
<div class="analysis_selector" class:current=move || selected_tab() == "scoreboard"><A href="scoreboard"><span>Scoreboard</span></A></div>
|
<div class="analysis_selector" class:current=move || selected_tab() == "scoreboard"><A href="scoreboard"><span>Scoreboard</span></A></div>
|
||||||
<div class="analysis_selector" class:current=move || selected_tab() == "perround"><A href="perround"><span>Per Round</span></A></div>
|
<div class="analysis_selector" class:current=move || selected_tab() == "perround"><A href="perround"><span>Per Round</span></A></div>
|
||||||
@@ -72,26 +79,34 @@ pub fn demo() -> impl leptos::IntoView {
|
|||||||
pub fn scoreboard() -> impl leptos::IntoView {
|
pub fn scoreboard() -> impl leptos::IntoView {
|
||||||
use leptos::Suspense;
|
use leptos::Suspense;
|
||||||
|
|
||||||
let scoreboard_resource = create_resource(leptos_router::use_params_map(), |params| async move {
|
let scoreboard_resource =
|
||||||
|
create_resource(leptos_router::use_params_map(), |params| async move {
|
||||||
let id = params.get("id").unwrap();
|
let id = params.get("id").unwrap();
|
||||||
|
|
||||||
let res = reqwasm::http::Request::get(&format!("/api/demos/{}/analysis/scoreboard", id))
|
let res =
|
||||||
|
reqwasm::http::Request::get(&format!("/api/demos/{}/analysis/scoreboard", id))
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
res.json::<common::demo_analysis::ScoreBoard>().await.unwrap()
|
res.json::<common::demo_analysis::ScoreBoard>()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
|
||||||
let team_display_func = |team: &[common::demo_analysis::ScoreBoardPlayer]| {
|
let team_display_func = |team: &[common::demo_analysis::ScoreBoardPlayer]| {
|
||||||
team.iter().map(|player| {
|
team.iter()
|
||||||
|
.map(|player| {
|
||||||
view! {
|
view! {
|
||||||
<tr>
|
<tr>
|
||||||
<td> { player.name.clone() } </td>
|
<td> { player.name.clone() } </td>
|
||||||
<td> { player.kills } </td>
|
<td> { player.kills } </td>
|
||||||
|
<td> { player.assists } </td>
|
||||||
<td> { player.deaths } </td>
|
<td> { player.deaths } </td>
|
||||||
|
<td> { player.damage } </td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
}).collect::<Vec<_>>()
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
@@ -106,7 +121,9 @@ pub fn scoreboard() -> impl leptos::IntoView {
|
|||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Kills</th>
|
<th>Kills</th>
|
||||||
|
<th>Assists</th>
|
||||||
<th>Deaths</th>
|
<th>Deaths</th>
|
||||||
|
<th>Damage</th>
|
||||||
</tr>
|
</tr>
|
||||||
{
|
{
|
||||||
move || {
|
move || {
|
||||||
@@ -124,6 +141,10 @@ pub fn scoreboard() -> impl leptos::IntoView {
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
<th>Kills</th>
|
||||||
|
<th>Assists</th>
|
||||||
|
<th>Deaths</th>
|
||||||
|
<th>Damage</th>
|
||||||
</tr>
|
</tr>
|
||||||
{
|
{
|
||||||
move || {
|
move || {
|
||||||
|
|||||||
@@ -23,5 +23,7 @@ CREATE TABLE IF NOT EXISTS demo_player_stats (
|
|||||||
steam_id TEXT NOT NULL,
|
steam_id TEXT NOT NULL,
|
||||||
kills int2 NOT NULL,
|
kills int2 NOT NULL,
|
||||||
deaths int2 NOT NULL,
|
deaths int2 NOT NULL,
|
||||||
|
damage int2 NOT NULL,
|
||||||
|
assists int2 NOT NULL,
|
||||||
PRIMARY KEY (demo_id, steam_id)
|
PRIMARY KEY (demo_id, steam_id)
|
||||||
);
|
);
|
||||||
|
|||||||
BIN
testfiles/nuke.dem
LFS
Normal file
BIN
testfiles/nuke.dem
LFS
Normal file
Binary file not shown.
Reference in New Issue
Block a user