mirror of
https://codeberg.org/icewind/log-normalizer.git
synced 2026-06-03 13:54:11 +02:00
normalized database
This commit is contained in:
parent
05be5b1e0a
commit
d6c622fcda
8 changed files with 677 additions and 591 deletions
249
src/data.rs
249
src/data.rs
|
|
@ -1,12 +1,17 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type, Deserialize, Eq, PartialEq)]
|
||||
#[sqlx(rename = "team")]
|
||||
#[sqlx(rename_all = "lowercase")]
|
||||
pub enum TeamId {
|
||||
Blue,
|
||||
Red,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[sqlx(rename_all = "lowercase")]
|
||||
#[sqlx(rename = "class_type")]
|
||||
pub enum Class {
|
||||
Scout,
|
||||
Soldier,
|
||||
|
|
@ -19,13 +24,20 @@ pub enum Class {
|
|||
Spy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type, Deserialize, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type, Eq, PartialEq)]
|
||||
#[sqlx(rename = "game_mode")]
|
||||
pub enum GameMode {
|
||||
#[sqlx(rename = "ultiduo")]
|
||||
UltiDuo,
|
||||
#[sqlx(rename = "4v4")]
|
||||
Fours,
|
||||
#[sqlx(rename = "6v6")]
|
||||
Sixes,
|
||||
#[sqlx(rename = "7v7")]
|
||||
Sevens,
|
||||
#[sqlx(rename = "9v9")]
|
||||
Highlander,
|
||||
#[sqlx(rename = "other")]
|
||||
Other,
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +51,7 @@ pub enum EventType {
|
|||
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type, Deserialize, Hash, Eq, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
#[sqlx(rename_all = "lowercase")]
|
||||
pub enum Medigun {
|
||||
Medigun,
|
||||
KritzKrieg,
|
||||
|
|
@ -52,221 +65,21 @@ impl Default for Medigun {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type, Deserialize, Hash, Eq, PartialEq)]
|
||||
// #[sqlx(rename_all = "snake_case")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Weapon {
|
||||
Sniperrifle,
|
||||
TauntSniper,
|
||||
SydneySleeper,
|
||||
TheWinger,
|
||||
HotHand,
|
||||
DeflectRocket,
|
||||
ScoutSword,
|
||||
VoodooPin,
|
||||
Degreaser,
|
||||
Shortstop,
|
||||
RobotArm,
|
||||
TfPumpkinBomb,
|
||||
Kunai,
|
||||
Wrench,
|
||||
NecroSmasher,
|
||||
Headtaker,
|
||||
ProtoSyringe,
|
||||
EternalReward,
|
||||
EurekaEffect,
|
||||
DragonsFuryBonus,
|
||||
GrapplingHook,
|
||||
PepPistol,
|
||||
Guillotine,
|
||||
LavaAxe,
|
||||
WranglerKill,
|
||||
PanicAttack,
|
||||
LooseCannon,
|
||||
Thirddegree,
|
||||
TauntPyro,
|
||||
IronBomber,
|
||||
PersianPersuader,
|
||||
Amputator,
|
||||
TfProjectilePipe,
|
||||
AwperHand,
|
||||
Demokatana,
|
||||
LooseCannonReflect,
|
||||
ObjMinisentry,
|
||||
BackScratcher,
|
||||
Pomson,
|
||||
TheClassic,
|
||||
Sandman,
|
||||
Axtinguisher,
|
||||
ObjSentrygun2,
|
||||
Jar,
|
||||
Samrevolver,
|
||||
DeflectFlare,
|
||||
LochNLoad,
|
||||
TfProjectileRocket,
|
||||
Annihilator,
|
||||
RocketpackStomp,
|
||||
UllapoolCaberExplosion,
|
||||
Minigun,
|
||||
DeflectPromode,
|
||||
LibertyLauncher,
|
||||
Nessieclub,
|
||||
QuickiebombLauncher,
|
||||
CowMangler,
|
||||
WrapAssassin,
|
||||
Blackbox,
|
||||
CandyCane,
|
||||
RocketlauncherDirecthit,
|
||||
AiFlamethrower,
|
||||
SodaPopper,
|
||||
FrontierJustice,
|
||||
RescueRangerReflect,
|
||||
WarriorSpirit,
|
||||
Widowmaker,
|
||||
QuakeRl,
|
||||
TfProjectileArrow,
|
||||
Sledgehammer,
|
||||
DeflectArrow,
|
||||
MarketGardener,
|
||||
UniquePickaxeEscape,
|
||||
PistolScout,
|
||||
BostonBasher,
|
||||
SpellbookBats,
|
||||
Holymackerel,
|
||||
Club,
|
||||
Ball,
|
||||
Fireaxe,
|
||||
Backburner,
|
||||
EvictionNotice,
|
||||
HolidayPunch,
|
||||
TauntSoldier,
|
||||
ShotgunPrimary,
|
||||
NonnonviolentProtest,
|
||||
ForceANature,
|
||||
Paintrain,
|
||||
FreedomStaff,
|
||||
ShotgunHwg,
|
||||
LongHeatmaker,
|
||||
ShotgunSoldier,
|
||||
Knife,
|
||||
Batsaber,
|
||||
Mailbox,
|
||||
CompoundBow,
|
||||
UllapoolCaber,
|
||||
Ambassador,
|
||||
PlayerPenetration,
|
||||
Powerjack,
|
||||
CrusadersCrossbow,
|
||||
ScotlandShard,
|
||||
WrenchJag,
|
||||
Scattergun,
|
||||
Unknown,
|
||||
Shahanshah,
|
||||
Pistol,
|
||||
SpellbookBoss,
|
||||
Smg,
|
||||
DragonsFury,
|
||||
Revolver,
|
||||
Player,
|
||||
TheMaul,
|
||||
Skullbat,
|
||||
HamShank,
|
||||
SolemnVow,
|
||||
IronCurtain,
|
||||
Bonesaw,
|
||||
DumpsterDevice,
|
||||
Bushwacka,
|
||||
Builder,
|
||||
BreadBite,
|
||||
SouthernHospitality,
|
||||
Tribalkukri,
|
||||
TheCapper,
|
||||
Fists,
|
||||
DisciplinaryAction,
|
||||
TfProjectileFlare,
|
||||
BleedKill,
|
||||
BlackRose,
|
||||
Letranger,
|
||||
Tomislav,
|
||||
Atomizer,
|
||||
BackScatter,
|
||||
PepBrawlerblaster,
|
||||
TfProjectilePipeRemote,
|
||||
Battleaxe,
|
||||
DeflectFlareDetonator,
|
||||
TauntMedic,
|
||||
Telefrag,
|
||||
StickybombDefender,
|
||||
SplendidScreen,
|
||||
Claidheamohmor,
|
||||
Airstrike,
|
||||
RighteousBison,
|
||||
GlovesRunningUrgently,
|
||||
Sword,
|
||||
Mantreads,
|
||||
DeflectSticky,
|
||||
Enforcer,
|
||||
ScorchShot,
|
||||
ProRifle,
|
||||
SpyCicle,
|
||||
Bat,
|
||||
SharpDresser,
|
||||
SpellbookLightning,
|
||||
TideTurner,
|
||||
ShotgunPyro,
|
||||
LavaBat,
|
||||
TauntHeavy,
|
||||
Bottle,
|
||||
UniquePickaxe,
|
||||
Phlogistinator,
|
||||
CrossingGuard,
|
||||
GigerCounter,
|
||||
ChargedSmg,
|
||||
SyringegunMedic,
|
||||
Gloves,
|
||||
BazaarBargain,
|
||||
SpellbookMirv,
|
||||
BigEarner,
|
||||
Battleneedle,
|
||||
Warfan,
|
||||
ObjSentrygun,
|
||||
Manmelter,
|
||||
FamilyBusiness,
|
||||
ReserveShooter,
|
||||
ShortCircuit,
|
||||
Flaregun,
|
||||
SpellbookFireball,
|
||||
World,
|
||||
JarMilk,
|
||||
Flamethrower,
|
||||
ShootingStar,
|
||||
TriggerHurt,
|
||||
Blutsauger,
|
||||
TauntSpy,
|
||||
TfProjectileEnergyBall,
|
||||
JarGas,
|
||||
TfProjectileMechanicalarmorb,
|
||||
ObjSentrygun3,
|
||||
Diamondback,
|
||||
Shovel,
|
||||
BrassBeast,
|
||||
LooseCannonImpact,
|
||||
Demoshield,
|
||||
PrinnyMachete,
|
||||
Machina,
|
||||
RocketlauncherFireball,
|
||||
StickyResistance,
|
||||
Detonator,
|
||||
TfProjectileSentryrocket,
|
||||
UnarmedCombat,
|
||||
SpellbookSkeleton,
|
||||
Ubersaw,
|
||||
Maxgun,
|
||||
RobotArmComboKill,
|
||||
RescueRanger,
|
||||
Apocofists,
|
||||
Natascha,
|
||||
ProSmg,
|
||||
SteelFists,
|
||||
Fryingpan,
|
||||
#[derive(Debug, Clone, Copy, sqlx::Type, Eq, PartialEq)]
|
||||
#[sqlx(rename_all = "lowercase")]
|
||||
#[sqlx(rename = "map_type")]
|
||||
pub enum MapType {
|
||||
Stopwatch,
|
||||
Cp,
|
||||
KOTH,
|
||||
CTF,
|
||||
UltiDuo,
|
||||
BBall,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl Default for MapType {
|
||||
fn default() -> Self {
|
||||
MapType::Other
|
||||
}
|
||||
}
|
||||
|
|
|
|||
257
src/database.rs
Normal file
257
src/database.rs
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
use crate::data::{Class, GameMode, MapType, Medigun, TeamId};
|
||||
use crate::normalized::NormalizedLog;
|
||||
use crate::raw::Event;
|
||||
use chrono::{DateTime, Utc};
|
||||
use sqlx::PgPool;
|
||||
use std::collections::HashMap;
|
||||
use steamid_ng::SteamID;
|
||||
|
||||
pub async fn store_log(pool: &PgPool, id: u32, log: &NormalizedLog) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO logs(id, red_score, blue_score, length, game_mode, map, type, date)\
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
id as i32,
|
||||
log.teams.red.score as i32,
|
||||
log.teams.blue.score as i32,
|
||||
log.info.total_length as i32,
|
||||
log.game_mode() as GameMode,
|
||||
log.info.map,
|
||||
log.info.map_type() as MapType,
|
||||
log.info.date() as DateTime<Utc>
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
for (num, round) in log.rounds.iter().enumerate() {
|
||||
let round_id: i32 = sqlx::query!(
|
||||
r#"INSERT INTO rounds(
|
||||
round, log_id, length, winner, first_cap, red_score, blue_score,
|
||||
red_kills, blue_kills, red_dmg, blue_dmg, red_ubers, blue_ubers
|
||||
)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||
RETURNING id"#,
|
||||
num as i32,
|
||||
id as i32,
|
||||
round.length as i32,
|
||||
round.winner as TeamId,
|
||||
round.first_cap as TeamId,
|
||||
round.team.red.score as i32,
|
||||
round.team.blue.score as i32,
|
||||
round.team.red.kills as i32,
|
||||
round.team.blue.kills as i32,
|
||||
round.team.red.dmg as i32,
|
||||
round.team.blue.dmg as i32,
|
||||
round.team.red.charges as i32,
|
||||
round.team.blue.charges as i32,
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.id;
|
||||
|
||||
for event in &round.events {
|
||||
match event {
|
||||
Event::PointCap { time, team, point } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO events_point_cap(round_id, time, team, point)\
|
||||
VALUES($1, $2, $3, $4)",
|
||||
round_id,
|
||||
*time as i32,
|
||||
*team as TeamId,
|
||||
*point as i32,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
Event::RoundWin { time, team } => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO events_round_win(round_id, time, team)\
|
||||
VALUES($1, $2, $3)",
|
||||
round_id,
|
||||
*time as i32,
|
||||
*team as TeamId,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
Event::MedicDeath {
|
||||
time,
|
||||
team,
|
||||
steamid,
|
||||
killer,
|
||||
} => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO events_medic_death(round_id, time, team, steam_id, killer)\
|
||||
VALUES($1, $2, $3, $4, $5)",
|
||||
round_id,
|
||||
*time as i32,
|
||||
*team as TeamId,
|
||||
u64::from(*steamid) as i64,
|
||||
u64::from(*killer) as i64,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
Event::Drop {
|
||||
time,
|
||||
steamid,
|
||||
team,
|
||||
} => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO events_drop(round_id, time, team, steam_id)\
|
||||
VALUES($1, $2, $3, $4)",
|
||||
round_id,
|
||||
*time as i32,
|
||||
*team as TeamId,
|
||||
u64::from(*steamid) as i64,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
Event::Charge {
|
||||
medigun,
|
||||
time,
|
||||
steamid,
|
||||
team,
|
||||
} => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO events_charge(round_id, time, team, medigun, steam_id)\
|
||||
VALUES($1, $2, $3, $4, $5)",
|
||||
round_id,
|
||||
*time as i32,
|
||||
*team as TeamId,
|
||||
*medigun as Medigun,
|
||||
u64::from(*steamid) as i64,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut heals_received: HashMap<SteamID, u32> = HashMap::new();
|
||||
for heal_map in log.heal_spread.values() {
|
||||
for (steam_id, heals) in heal_map {
|
||||
heals_received
|
||||
.entry(*steam_id)
|
||||
.and_modify(|received| *received += heals)
|
||||
.or_insert(*heals);
|
||||
}
|
||||
}
|
||||
|
||||
for (steam_id, player) in &log.players {
|
||||
let kills = log.class_kills.get(steam_id).cloned().unwrap_or_default();
|
||||
let player_id: i32 = sqlx::query!(
|
||||
"INSERT INTO players (\
|
||||
log_id, steam_id, name, kills, deaths, assists,\
|
||||
suicides, dmg, damage_taken, ubers, medigun_ubers,\
|
||||
kritzkrieg_ubers, quickfix_ubers, vacinator_ubers,\
|
||||
drops, medkits, medkits_hp, backstabs, headshots,\
|
||||
heal, heals_received,\
|
||||
scout_kills, soldier_kills, pyro_kills, demoman_kills,\
|
||||
heavy_kills, engineer_kills, medic_kills, sniper_kills, spy_kills
|
||||
)\
|
||||
VALUES(\
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10,\
|
||||
$11, $12, $13, $14, $15, $16, $17, $18, $19, $20,\
|
||||
$21, $22, $23, $24, $25, $26, $27, $28, $29, $30\
|
||||
)\
|
||||
RETURNING id",
|
||||
id as i32,
|
||||
u64::from(*steam_id) as i64,
|
||||
log.names.get(steam_id).cloned().unwrap_or_default(),
|
||||
player.kills as i32,
|
||||
player.deaths as i32,
|
||||
player.assists as i32,
|
||||
player.suicides as i32,
|
||||
player.dmg as i32,
|
||||
player.dt_real as i32,
|
||||
player.ubers as i32,
|
||||
player
|
||||
.ubertypes
|
||||
.get(&Medigun::Medigun)
|
||||
.copied()
|
||||
.unwrap_or_default() as i32,
|
||||
player
|
||||
.ubertypes
|
||||
.get(&Medigun::KritzKrieg)
|
||||
.copied()
|
||||
.unwrap_or_default() as i32,
|
||||
player
|
||||
.ubertypes
|
||||
.get(&Medigun::QuickFix)
|
||||
.copied()
|
||||
.unwrap_or_default() as i32,
|
||||
player
|
||||
.ubertypes
|
||||
.get(&Medigun::Vacinator)
|
||||
.copied()
|
||||
.unwrap_or_default() as i32,
|
||||
player.drops as i32,
|
||||
player.medkits as i32,
|
||||
player.medkits_hp as i32,
|
||||
player.backstabs as i32,
|
||||
player.headshots as i32,
|
||||
player.heal as i32,
|
||||
heals_received.get(steam_id).copied().unwrap_or_default() as i32,
|
||||
kills.scout as i32,
|
||||
kills.soldier as i32,
|
||||
kills.pyro as i32,
|
||||
kills.demoman as i32,
|
||||
kills.heavyweapons as i32,
|
||||
kills.engineer as i32,
|
||||
kills.medic as i32,
|
||||
kills.sniper as i32,
|
||||
kills.spy as i32
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.id;
|
||||
|
||||
for class in &player.class_stats {
|
||||
let class_stat_id: i32 = sqlx::query!(
|
||||
"INSERT INTO class_stats(player_id, type, time, kills, deaths, assists, dmg)\
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7)\
|
||||
RETURNING id",
|
||||
player_id,
|
||||
class.class as Class,
|
||||
class.total_time as i32,
|
||||
class.kills as i32,
|
||||
class.deaths as i32,
|
||||
class.assists as i32,
|
||||
class.dmg as i32,
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?
|
||||
.id;
|
||||
|
||||
for (weapon, stats) in &class.weapon {
|
||||
sqlx::query!(
|
||||
"INSERT INTO player_weapon_stats(class_stat_id, weapon, kills, shots, hits, dmg)\
|
||||
VALUES($1, $2, $3, $4, $5, $6)",
|
||||
class_stat_id as i32,
|
||||
*weapon,
|
||||
stats.kills as i32,
|
||||
stats.shots as i32,
|
||||
stats.hits as i32,
|
||||
stats.dmg as i32,
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// macro_rules! insert_fields {
|
||||
// ($table:ident, {
|
||||
// $($($field:ident => $value:expr),)+
|
||||
// }) => {
|
||||
// sqlx::query!(
|
||||
// concat!("INSERT INTO ", stringify!($table), "(", stringify!$(field)) ") VALUES ()"\
|
||||
// VALUES($1, $2, $3, $4, $5)",
|
||||
// )
|
||||
// };
|
||||
// }
|
||||
22
src/main.rs
22
src/main.rs
|
|
@ -1,7 +1,23 @@
|
|||
mod data;
|
||||
mod raw;
|
||||
mod database;
|
||||
mod normalized;
|
||||
mod raw;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use crate::database::store_log;
|
||||
use crate::normalized::NormalizedLog;
|
||||
use main_error::MainError;
|
||||
use sqlx::PgPool;
|
||||
use std::fs;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), MainError> {
|
||||
let database_url = dotenv::var("DATABASE_URL")?;
|
||||
|
||||
let content = fs::read_to_string("tests/data/2522305.json").unwrap();
|
||||
let parsed: NormalizedLog = serde_json::from_str(&content).unwrap();
|
||||
|
||||
let pool = PgPool::builder().max_size(2).build(&database_url).await?;
|
||||
dbg!(store_log(&pool, 2522305, &parsed).await)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
pub use crate::data::TeamId;
|
||||
use crate::data::{GameMode, MapType};
|
||||
use crate::raw::RawLog;
|
||||
pub use crate::raw::{
|
||||
ChatMessage, ClassNumbers, Event, Player, RoundPlayer, Team, Teams, Uploader,
|
||||
};
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use steamid_ng::SteamID;
|
||||
|
|
@ -24,6 +26,22 @@ pub struct NormalizedLog {
|
|||
pub info: Info,
|
||||
}
|
||||
|
||||
impl NormalizedLog {
|
||||
pub fn game_mode(&self) -> GameMode {
|
||||
if self.info.map_type() == MapType::UltiDuo {
|
||||
return GameMode::UltiDuo;
|
||||
}
|
||||
|
||||
match self.players.len() {
|
||||
7..=9 => GameMode::Fours,
|
||||
11..=13 => GameMode::Sixes,
|
||||
14 => GameMode::Sevens,
|
||||
17..=19 => GameMode::Highlander,
|
||||
_ => GameMode::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Info {
|
||||
pub map: String,
|
||||
|
|
@ -49,6 +67,30 @@ pub struct Info {
|
|||
pub uploader: Uploader,
|
||||
}
|
||||
|
||||
impl Info {
|
||||
pub fn map_type(&self) -> MapType {
|
||||
if map_is_stopwatch(&self.map) {
|
||||
MapType::Stopwatch
|
||||
} else if self.map.starts_with("cp") {
|
||||
MapType::Cp
|
||||
} else if self.map.starts_with("koth") {
|
||||
MapType::KOTH
|
||||
} else if self.map.starts_with("ctf") {
|
||||
MapType::CTF
|
||||
} else if self.map.starts_with("ultiduo") {
|
||||
MapType::UltiDuo
|
||||
} else if self.map.starts_with("bball") {
|
||||
MapType::BBall
|
||||
} else {
|
||||
MapType::Other
|
||||
}
|
||||
}
|
||||
|
||||
pub fn date(&self) -> DateTime<Utc> {
|
||||
DateTime::from_utc(NaiveDateTime::from_timestamp(self.date as i64, 0), Utc)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Round {
|
||||
pub start_time: u64,
|
||||
|
|
|
|||
53
src/raw.rs
53
src/raw.rs
|
|
@ -1,4 +1,4 @@
|
|||
use crate::data::{Class, Medigun, TeamId, Weapon};
|
||||
use crate::data::{Class, Medigun, TeamId};
|
||||
use serde::export::TryFrom;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -54,6 +54,7 @@ pub struct Team {
|
|||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Player {
|
||||
pub class_stats: Vec<ClassStat>,
|
||||
pub team: TeamId,
|
||||
pub kills: u16,
|
||||
pub deaths: u16,
|
||||
|
|
@ -106,20 +107,64 @@ pub struct ClassStat {
|
|||
#[serde(rename = "type")]
|
||||
pub class: Class,
|
||||
pub kills: u16,
|
||||
pub assists: u16,
|
||||
pub deaths: u16,
|
||||
pub dmg: u32,
|
||||
pub total_time: u32,
|
||||
pub weapon: HashMap<Weapon, WeaponStat>,
|
||||
#[serde(default)]
|
||||
pub weapon: HashMap<String, WeaponStat>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum RawWeaponStats {
|
||||
Kills(u32),
|
||||
Stats {
|
||||
kills: u32,
|
||||
dmg: u32,
|
||||
avg_dmg: f32,
|
||||
shots: u32,
|
||||
hits: u32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(from = "RawWeaponStats")]
|
||||
pub struct WeaponStat {
|
||||
pub kills: u32,
|
||||
pub dmg: u32,
|
||||
pub avg_dmg: u32,
|
||||
pub avg_dmg: f32,
|
||||
pub shots: u32,
|
||||
pub hits: u32,
|
||||
}
|
||||
|
||||
impl From<RawWeaponStats> for WeaponStat {
|
||||
fn from(raw: RawWeaponStats) -> Self {
|
||||
match raw {
|
||||
RawWeaponStats::Kills(kills) => WeaponStat {
|
||||
kills,
|
||||
dmg: 0,
|
||||
avg_dmg: 0.0,
|
||||
shots: 0,
|
||||
hits: 0,
|
||||
},
|
||||
RawWeaponStats::Stats {
|
||||
kills,
|
||||
dmg,
|
||||
avg_dmg,
|
||||
shots,
|
||||
hits,
|
||||
} => WeaponStat {
|
||||
kills,
|
||||
dmg,
|
||||
avg_dmg,
|
||||
shots,
|
||||
hits,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Round {
|
||||
#[serde(default)]
|
||||
|
|
@ -187,7 +232,7 @@ impl Event {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone, Deserialize, Default)]
|
||||
pub struct ClassNumbers {
|
||||
#[serde(default)]
|
||||
pub scout: u8,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue