mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 18:24:09 +02:00
medic stats
This commit is contained in:
parent
b2d169601c
commit
a5040ec355
5 changed files with 147 additions and 20 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::event::{param_parse, param_parse_with, quoted, u_int};
|
use crate::event::{param_parse, param_parse_with, quoted, u_int, ParamIter};
|
||||||
use crate::raw_event::{subject_parser, RawSubject};
|
use crate::raw_event::{subject_parser, RawSubject};
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::number::complete::float;
|
use nom::number::complete::float;
|
||||||
|
|
@ -41,3 +41,39 @@ pub fn charge_ended_event_parser(input: &str) -> IResult<&str, ChargeEndedEvent>
|
||||||
let (input, duration) = opt(param_parse_with("duration", quoted(float)))(input)?;
|
let (input, duration) = opt(param_parse_with("duration", quoted(float)))(input)?;
|
||||||
Ok((input, ChargeEndedEvent { duration }))
|
Ok((input, ChargeEndedEvent { duration }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AdvantageLostEvent {
|
||||||
|
pub time: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn advantage_lost_event_parser(input: &str) -> IResult<&str, AdvantageLostEvent> {
|
||||||
|
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
|
||||||
|
Ok((input, AdvantageLostEvent { time }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FirstHealEvent {
|
||||||
|
pub time: Option<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn first_heal_event_parser(input: &str) -> IResult<&str, FirstHealEvent> {
|
||||||
|
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
|
||||||
|
Ok((input, FirstHealEvent { time }))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MedicDeathEvent {
|
||||||
|
pub charge: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn medic_death_event_parser(input: &str) -> IResult<&str, MedicDeathEvent> {
|
||||||
|
let mut charge = None;
|
||||||
|
for (key, value) in ParamIter::new(input) {
|
||||||
|
if key == "ubercharge" {
|
||||||
|
charge = Some(quoted(u_int)(value)?.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (input, time) = opt(param_parse_with("time", quoted(float)))(input)?;
|
||||||
|
Ok((input, MedicDeathEvent { charge }))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ impl<'a, T> GameEventErrTrait<T> for IResult<&str, T> {
|
||||||
|
|
||||||
Err::Incomplete(_) => GameEventError::Incomplete(ty),
|
Err::Incomplete(_) => GameEventError::Incomplete(ty),
|
||||||
})
|
})
|
||||||
.map(|(rest, t)| t)
|
.map(|(_rest, t)| t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,6 +51,12 @@ pub enum GameEvent<'a> {
|
||||||
Say(&'a str),
|
Say(&'a str),
|
||||||
SayTeam(&'a str),
|
SayTeam(&'a str),
|
||||||
Healed(HealedEvent<'a>),
|
Healed(HealedEvent<'a>),
|
||||||
|
ChargeDeployed(ChargeDeployedEvent<'a>),
|
||||||
|
ChargeEnded(ChargeEndedEvent),
|
||||||
|
AdvantageLost(AdvantageLostEvent),
|
||||||
|
FirstHeal(FirstHealEvent),
|
||||||
|
ChargeReady,
|
||||||
|
MedicDeath(MedicDeathEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GameEvent<'a> {
|
impl<'a> GameEvent<'a> {
|
||||||
|
|
@ -64,6 +70,22 @@ impl<'a> GameEvent<'a> {
|
||||||
RawEventType::Healed => {
|
RawEventType::Healed => {
|
||||||
GameEvent::Healed(healed_event_parser(raw.params).with_type(raw.ty)?)
|
GameEvent::Healed(healed_event_parser(raw.params).with_type(raw.ty)?)
|
||||||
}
|
}
|
||||||
|
RawEventType::ChargeDeployed => GameEvent::ChargeDeployed(
|
||||||
|
charge_deployed_event_parser(raw.params).with_type(raw.ty)?,
|
||||||
|
),
|
||||||
|
RawEventType::ChargeEnd => {
|
||||||
|
GameEvent::ChargeEnded(charge_ended_event_parser(raw.params).with_type(raw.ty)?)
|
||||||
|
}
|
||||||
|
RawEventType::UberAdvantageLost => {
|
||||||
|
GameEvent::AdvantageLost(advantage_lost_event_parser(raw.params).with_type(raw.ty)?)
|
||||||
|
}
|
||||||
|
RawEventType::FirstHealAfterSpawn => {
|
||||||
|
GameEvent::FirstHeal(first_heal_event_parser(raw.params).with_type(raw.ty)?)
|
||||||
|
}
|
||||||
|
RawEventType::ChargeReady => GameEvent::ChargeReady,
|
||||||
|
RawEventType::MedicDeath => {
|
||||||
|
GameEvent::MedicDeath(medic_death_event_parser(raw.params).with_type(raw.ty)?)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
todo!("{:?} not parsed yet", raw.ty);
|
todo!("{:?} not parsed yet", raw.ty);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/lib.rs
12
src/lib.rs
|
|
@ -1,6 +1,8 @@
|
||||||
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
|
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
|
||||||
use crate::event::{GameEvent, GameEventError};
|
use crate::event::{GameEvent, GameEventError};
|
||||||
use crate::module::{ChatHandler, ChatMessage, EventHandler, HealSpreadHandler};
|
use crate::module::{
|
||||||
|
ChatHandler, ChatMessage, EventHandler, HealSpreadHandler, MedicStats, MedicStatsHandler,
|
||||||
|
};
|
||||||
use crate::raw_event::RawSubject;
|
use crate::raw_event::RawSubject;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
pub use raw_event::{RawEvent, RawEventType};
|
pub use raw_event::{RawEvent, RawEventType};
|
||||||
|
|
@ -116,12 +118,14 @@ pub fn parse_with_handler<Handler: EventHandler>(
|
||||||
pub struct LogHandler {
|
pub struct LogHandler {
|
||||||
chat: ChatHandler,
|
chat: ChatHandler,
|
||||||
heal_spread: HealSpreadHandler,
|
heal_spread: HealSpreadHandler,
|
||||||
|
medic_stats: MedicStatsHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Serialize)]
|
#[derive(Default, Serialize)]
|
||||||
pub struct LogOutput {
|
pub struct LogOutput {
|
||||||
chat: Vec<ChatMessage>,
|
chat: Vec<ChatMessage>,
|
||||||
heal_spread: HashMap<SteamId3, HashMap<SteamId3, u32>>,
|
heal_spread: HashMap<SteamId3, HashMap<SteamId3, u32>>,
|
||||||
|
medic_stats: HashMap<SteamId3, MedicStats>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
|
@ -135,7 +139,9 @@ impl EventHandler for LogHandler {
|
||||||
type Error = LogError;
|
type Error = LogError;
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||||
self.chat.does_handle(ty) || self.heal_spread.does_handle(ty)
|
self.chat.does_handle(ty)
|
||||||
|
|| self.heal_spread.does_handle(ty)
|
||||||
|
|| self.medic_stats.does_handle(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(
|
||||||
|
|
@ -146,6 +152,7 @@ impl EventHandler for LogHandler {
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.chat.handle(time, subject, event).unwrap();
|
self.chat.handle(time, subject, event).unwrap();
|
||||||
self.heal_spread.handle(time, subject, event).unwrap();
|
self.heal_spread.handle(time, subject, event).unwrap();
|
||||||
|
self.medic_stats.handle(time, subject, event).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,6 +160,7 @@ impl EventHandler for LogHandler {
|
||||||
LogOutput {
|
LogOutput {
|
||||||
chat: self.chat.finish(subjects),
|
chat: self.chat.finish(subjects),
|
||||||
heal_spread: self.heal_spread.finish(subjects),
|
heal_spread: self.heal_spread.finish(subjects),
|
||||||
|
medic_stats: self.medic_stats.finish(subjects),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,34 +3,40 @@ use crate::event::GameEvent;
|
||||||
use crate::module::EventHandler;
|
use crate::module::EventHandler;
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::SubjectMap;
|
use crate::SubjectMap;
|
||||||
|
use serde::Serialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MedicStatsBuilder {
|
struct MedicStatsBuilder {
|
||||||
advantages_lost: u32,
|
advantages_lost: u32,
|
||||||
biggest_advantage_lost: u32,
|
biggest_advantage_lost: f32,
|
||||||
near_full_charge_death: u32,
|
near_full_charge_death: u32,
|
||||||
deaths_after_uber: u32,
|
deaths_after_uber: u32,
|
||||||
total_time_before_healing: u32,
|
total_time_before_healing: f32,
|
||||||
start_healing_count: u32,
|
start_healing_count: u32,
|
||||||
total_time_to_build: u32,
|
total_time_to_build: u32,
|
||||||
uber_build_count: u32,
|
uber_build_count: u32,
|
||||||
total_time_to_use: u32,
|
total_time_to_use: f32,
|
||||||
total_uber_length: u32,
|
total_uber_length: f32,
|
||||||
charge_count: u32,
|
charge_count: u32,
|
||||||
|
last_build_start: u32,
|
||||||
|
last_uber_end: u32,
|
||||||
|
drops: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
pub struct MedicStats {
|
pub struct MedicStats {
|
||||||
advantages_lost: u32,
|
advantages_lost: u32,
|
||||||
biggest_advantage_lost: u32,
|
biggest_advantage_lost: f32,
|
||||||
near_full_charge_death: u32,
|
near_full_charge_death: u32,
|
||||||
deaths_after_uber: u32,
|
deaths_after_uber: u32,
|
||||||
avg_time_before_healing: u32,
|
avg_time_before_healing: f32,
|
||||||
avg_time_to_build: u32,
|
avg_time_to_build: f32,
|
||||||
avg_time_to_use: u32,
|
avg_time_to_use: f32,
|
||||||
avg_uber_length: u32,
|
avg_uber_length: f32,
|
||||||
charge_count: u32,
|
charge_count: u32,
|
||||||
|
drops: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<MedicStatsBuilder> for MedicStats {
|
impl From<MedicStatsBuilder> for MedicStats {
|
||||||
|
|
@ -41,11 +47,12 @@ impl From<MedicStatsBuilder> for MedicStats {
|
||||||
near_full_charge_death: builder.near_full_charge_death,
|
near_full_charge_death: builder.near_full_charge_death,
|
||||||
deaths_after_uber: builder.deaths_after_uber,
|
deaths_after_uber: builder.deaths_after_uber,
|
||||||
avg_time_before_healing: builder.total_time_before_healing
|
avg_time_before_healing: builder.total_time_before_healing
|
||||||
/ builder.start_healing_count,
|
/ builder.start_healing_count as f32,
|
||||||
avg_time_to_build: builder.total_time_to_build / builder.uber_build_count,
|
avg_time_to_build: builder.total_time_to_build as f32 / builder.uber_build_count as f32,
|
||||||
avg_time_to_use: builder.total_time_to_use / builder.charge_count,
|
avg_time_to_use: builder.total_time_to_use / builder.charge_count as f32,
|
||||||
avg_uber_length: builder.total_uber_length / builder.charge_count,
|
avg_uber_length: builder.total_uber_length / builder.charge_count as f32,
|
||||||
charge_count: builder.charge_count,
|
charge_count: builder.charge_count,
|
||||||
|
drops: builder.drops,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64,13 +71,18 @@ impl EventHandler for MedicStatsHandler {
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
ty,
|
ty,
|
||||||
RawEventType::ChargeDeployed | RawEventType::ChargeEnd | RawEventType::ChargeReady
|
RawEventType::ChargeDeployed
|
||||||
|
| RawEventType::ChargeEnd
|
||||||
|
| RawEventType::ChargeReady
|
||||||
|
| RawEventType::UberAdvantageLost
|
||||||
|
| RawEventType::MedicDeath
|
||||||
|
| RawEventType::FirstHealAfterSpawn
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
_time: u32,
|
time: u32,
|
||||||
subject: SubjectId,
|
subject: SubjectId,
|
||||||
event: &GameEvent,
|
event: &GameEvent,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
|
|
@ -79,12 +91,60 @@ impl EventHandler for MedicStatsHandler {
|
||||||
} else {
|
} else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
match event {
|
||||||
|
GameEvent::ChargeEnded(end) => {
|
||||||
|
let builder = self.0.entry(SteamId3(healer_steam_id)).or_default();
|
||||||
|
builder.total_uber_length += end.duration.unwrap_or_default();
|
||||||
|
builder.last_uber_end = time;
|
||||||
|
}
|
||||||
|
GameEvent::ChargeDeployed(_deployed) => {
|
||||||
|
let builder = self.0.entry(SteamId3(healer_steam_id)).or_default();
|
||||||
|
builder.charge_count += 1;
|
||||||
|
}
|
||||||
|
GameEvent::AdvantageLost(lost) => {
|
||||||
|
let builder = self.0.entry(SteamId3(healer_steam_id)).or_default();
|
||||||
|
builder.advantages_lost += 1;
|
||||||
|
let time = lost.time.unwrap_or_default();
|
||||||
|
if time > builder.biggest_advantage_lost {
|
||||||
|
builder.biggest_advantage_lost = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameEvent::FirstHeal(first) => {
|
||||||
|
let builder = self.0.entry(SteamId3(healer_steam_id)).or_default();
|
||||||
|
builder.total_time_before_healing += first.time.unwrap_or_default();
|
||||||
|
builder.start_healing_count += 1;
|
||||||
|
builder.last_build_start = time;
|
||||||
|
}
|
||||||
|
GameEvent::ChargeReady => {
|
||||||
|
let builder = self.0.entry(SteamId3(healer_steam_id)).or_default();
|
||||||
|
if builder.last_build_start > 0 {
|
||||||
|
let build_time = time - builder.last_build_start;
|
||||||
|
builder.last_build_start = 0;
|
||||||
|
builder.total_time_to_build += build_time;
|
||||||
|
builder.uber_build_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameEvent::MedicDeath(death) => {
|
||||||
|
let builder = self.0.entry(SteamId3(healer_steam_id)).or_default();
|
||||||
|
let charge = death.charge.unwrap_or_default();
|
||||||
|
if charge > 95 && charge < 100 {
|
||||||
|
builder.near_full_charge_death += 1;
|
||||||
|
} else if charge >= 100 {
|
||||||
|
builder.drops += 1;
|
||||||
|
}
|
||||||
|
if time - builder.last_uber_end <= 20 {
|
||||||
|
builder.deaths_after_uber += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
||||||
self.0
|
self.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.filter(|(_, builder)| builder.start_healing_count > 0)
|
||||||
.map(|(steam_id, builder)| (steam_id, builder.into()))
|
.map(|(steam_id, builder)| (steam_id, builder.into()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ pub use healspread::HealSpreadHandler;
|
||||||
pub use lobbysettings::{
|
pub use lobbysettings::{
|
||||||
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
||||||
};
|
};
|
||||||
|
pub use medicstats::{MedicStats, MedicStatsHandler};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue