mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 18:24:09 +02:00
healspread
This commit is contained in:
parent
747ee7d1a9
commit
d1b3525b2a
10 changed files with 209 additions and 16 deletions
|
|
@ -1,11 +1,12 @@
|
|||
use crate::raw_event::RawSubject;
|
||||
use serde::Serialize;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
use steamid_ng::SteamID;
|
||||
use steamid_ng::{AccountType, Instance, SteamID, Universe};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)]
|
||||
#[derive(Serialize, Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)]
|
||||
pub enum Team {
|
||||
Red,
|
||||
Blue,
|
||||
|
|
@ -41,22 +42,44 @@ impl FromStr for Team {
|
|||
/// Optimized subject id
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)]
|
||||
pub enum SubjectId {
|
||||
Player(u8),
|
||||
Player(u32),
|
||||
Team(Team),
|
||||
System,
|
||||
World,
|
||||
Console,
|
||||
}
|
||||
|
||||
impl From<&RawSubject<'_>> for SubjectId {
|
||||
fn from(raw: &RawSubject) -> Self {
|
||||
match raw {
|
||||
RawSubject::Player { user_id, .. } => SubjectId::Player(user_id.parse().unwrap()),
|
||||
RawSubject::Team(team) => SubjectId::Team(team.parse().unwrap()),
|
||||
impl SubjectId {
|
||||
pub fn steam_id(&self) -> Option<SteamID> {
|
||||
match self {
|
||||
SubjectId::Player(account_id) => Some(SteamID::new(
|
||||
*account_id,
|
||||
Instance::All,
|
||||
AccountType::Individual,
|
||||
Universe::Public,
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&RawSubject<'_>> for SubjectId {
|
||||
type Error = SubjectError;
|
||||
|
||||
fn try_from(raw: &RawSubject) -> Result<Self, Self::Error> {
|
||||
Ok(match raw {
|
||||
RawSubject::Player { steam_id, .. } => SubjectId::Player(
|
||||
SteamID::from_steam3(steam_id)
|
||||
.map_err(|_| SubjectError::InvalidSteamId)?
|
||||
.account_id(),
|
||||
),
|
||||
RawSubject::Team(team) => {
|
||||
SubjectId::Team(team.parse().map_err(|_| SubjectError::InvalidTeam)?)
|
||||
}
|
||||
RawSubject::System(_) => SubjectId::System,
|
||||
RawSubject::Console => SubjectId::Console,
|
||||
RawSubject::World => SubjectId::World,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,3 +131,7 @@ impl TryFrom<&RawSubject<'_>> for SubjectData {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Steam id formatted as steamid3 when serialized
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
pub struct SteamId3(pub SteamID);
|
||||
|
|
|
|||
22
src/event/medic.rs
Normal file
22
src/event/medic.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
use crate::raw_event::{subject_parser, RawSubject};
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::digit1;
|
||||
use nom::error::ErrorKind;
|
||||
use nom::IResult;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HealedEvent<'a> {
|
||||
pub subject: RawSubject<'a>,
|
||||
pub amount: u32,
|
||||
}
|
||||
|
||||
pub fn healed_event_parser(input: &str) -> IResult<&str, HealedEvent> {
|
||||
let (input, _) = tag("against ")(input)?;
|
||||
let (input, subject) = subject_parser(input)?;
|
||||
let (input, _) = tag(r#" (healing ""#)(input)?;
|
||||
let (input, raw_amount) = digit1(input)?;
|
||||
let amount = raw_amount
|
||||
.parse()
|
||||
.map_err(|_| nom::Err::Error(nom::error::Error::new(raw_amount, ErrorKind::Digit)))?;
|
||||
Ok((input, HealedEvent { subject, amount }))
|
||||
}
|
||||
3
src/event/mod.rs
Normal file
3
src/event/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
mod medic;
|
||||
|
||||
pub use medic::{healed_event_parser, HealedEvent};
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::common::{SubjectData, SubjectError, SubjectId};
|
||||
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
|
||||
use crate::module::EventHandler;
|
||||
use crate::raw_event::{RawEvent, RawSubject};
|
||||
use crate::raw_event::RawSubject;
|
||||
use chrono::{DateTime, Utc};
|
||||
pub use raw_event::{RawEvent, RawEventType};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
|
@ -9,6 +10,7 @@ use std::ops::Index;
|
|||
use thiserror::Error;
|
||||
|
||||
mod common;
|
||||
mod event;
|
||||
pub mod module;
|
||||
mod raw_event;
|
||||
|
||||
|
|
@ -56,7 +58,7 @@ impl Index<SubjectId> for SubjectMap {
|
|||
|
||||
impl SubjectMap {
|
||||
pub fn insert(&mut self, raw: &RawSubject) -> Result<SubjectId, SubjectError> {
|
||||
let id = raw.into();
|
||||
let id = raw.try_into()?;
|
||||
if !self.0.contains_key(&id) {
|
||||
self.0.insert(id, raw.try_into()?);
|
||||
}
|
||||
|
|
|
|||
67
src/main.rs
Normal file
67
src/main.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use main_error::MainError;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::env::args;
|
||||
use std::fs;
|
||||
use std::io::stdout;
|
||||
use tf_log_parser::module::{
|
||||
ChatHandler, ChatMessage, EventHandler, HealSpreadHandler, InvalidHealEvent,
|
||||
};
|
||||
use tf_log_parser::{parse_with_handler, RawEvent, RawEventType, SteamId3, SubjectId, SubjectMap};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Default)]
|
||||
struct LogHandler {
|
||||
chat: ChatHandler,
|
||||
heal_spread: HealSpreadHandler,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize)]
|
||||
struct LogOutput {
|
||||
chat: Vec<ChatMessage>,
|
||||
heal_spread: HashMap<SteamId3, HashMap<SteamId3, u32>>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum LogError {
|
||||
#[error("{0}")]
|
||||
HealSpread(#[from] InvalidHealEvent),
|
||||
}
|
||||
|
||||
impl EventHandler for LogHandler {
|
||||
type Output = LogOutput;
|
||||
type Error = LogError;
|
||||
|
||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||
self.chat.does_handle(ty) || self.heal_spread.does_handle(ty)
|
||||
}
|
||||
|
||||
fn handle(
|
||||
&mut self,
|
||||
time: u32,
|
||||
subject: SubjectId,
|
||||
event: &RawEvent,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.chat.handle(time, subject, event).unwrap();
|
||||
self.heal_spread.handle(time, subject, event)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||
LogOutput {
|
||||
chat: self.chat.finish(subjects),
|
||||
heal_spread: self.heal_spread.finish(subjects),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), MainError> {
|
||||
let path = args().skip(1).next().expect("No path provided");
|
||||
let content = fs::read_to_string(path)?;
|
||||
|
||||
let log = parse_with_handler::<LogHandler>(&content)?;
|
||||
|
||||
serde_json::to_writer_pretty(stdout().lock(), &log).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ use crate::common::{SubjectData, SubjectId};
|
|||
use crate::module::EventHandler;
|
||||
use crate::raw_event::{RawEvent, RawEventType};
|
||||
use crate::SubjectMap;
|
||||
use serde::Serialize;
|
||||
use std::convert::Infallible;
|
||||
use steamid_ng::SteamID;
|
||||
|
||||
|
|
@ -12,6 +13,7 @@ struct BareChatMessage {
|
|||
pub chat_type: ChatType,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ChatMessage {
|
||||
pub time: u32,
|
||||
pub name: String,
|
||||
|
|
@ -38,6 +40,7 @@ impl ChatMessage {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub enum ChatType {
|
||||
All,
|
||||
Team,
|
||||
|
|
|
|||
67
src/module/healspread.rs
Normal file
67
src/module/healspread.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use crate::common::{SteamId3, SubjectId};
|
||||
use crate::event::healed_event_parser;
|
||||
use crate::module::EventHandler;
|
||||
use crate::raw_event::{RawEvent, RawEventType};
|
||||
use crate::SubjectMap;
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use thiserror::Error;
|
||||
|
||||
impl Serialize for SteamId3 {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.steam3().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HealSpreadHandler(HashMap<SteamId3, HashMap<SteamId3, u32>>);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("Invalid healed event: {0}")]
|
||||
pub struct InvalidHealEvent(String);
|
||||
|
||||
impl EventHandler for HealSpreadHandler {
|
||||
type Output = HashMap<SteamId3, HashMap<SteamId3, u32>>;
|
||||
type Error = InvalidHealEvent;
|
||||
|
||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
||||
matches!(ty, RawEventType::Healed)
|
||||
}
|
||||
|
||||
fn handle(
|
||||
&mut self,
|
||||
_time: u32,
|
||||
subject: SubjectId,
|
||||
event: &RawEvent,
|
||||
) -> Result<(), Self::Error> {
|
||||
let healer_steam_id = if let Some(steam_id) = subject.steam_id() {
|
||||
steam_id
|
||||
} else {
|
||||
return Ok(());
|
||||
};
|
||||
if matches!(event.ty, RawEventType::Healed) {
|
||||
let (_, heal_event) = healed_event_parser(event.params)
|
||||
.map_err(|_| InvalidHealEvent(event.params.into()))?;
|
||||
if let Ok(target_subject) = SubjectId::try_from(&heal_event.subject) {
|
||||
if let Some(target_steam_id) = target_subject.steam_id() {
|
||||
let healed = self
|
||||
.0
|
||||
.entry(SteamId3(healer_steam_id))
|
||||
.or_default()
|
||||
.entry(SteamId3(target_steam_id))
|
||||
.or_default();
|
||||
*healed += heal_event.amount
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ use crate::common::SubjectId;
|
|||
use crate::raw_event::{RawEvent, RawEventType};
|
||||
use crate::SubjectMap;
|
||||
pub use chat::{ChatHandler, ChatMessage, ChatType};
|
||||
pub use healspread::{HealSpreadHandler, InvalidHealEvent};
|
||||
pub use lobbysettings::{
|
||||
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
||||
};
|
||||
|
|
@ -11,6 +12,7 @@ use std::fmt::Debug;
|
|||
use thiserror::Error;
|
||||
|
||||
mod chat;
|
||||
mod healspread;
|
||||
mod lobbysettings;
|
||||
|
||||
pub trait EventHandler: Default {
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ fn subject_parser_player(input: &str) -> IResult<&str, RawSubject> {
|
|||
))
|
||||
}
|
||||
|
||||
fn subject_parser(input: &str) -> IResult<&str, RawSubject> {
|
||||
pub fn subject_parser(input: &str) -> IResult<&str, RawSubject> {
|
||||
alt((
|
||||
subject_parser_console,
|
||||
subject_parser_world,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue