mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 18:24:09 +02:00
subject postprocessing
This commit is contained in:
parent
ca61f4ea6a
commit
747ee7d1a9
10 changed files with 2970 additions and 22 deletions
|
|
@ -18,7 +18,7 @@ fn main() -> Result<(), MainError> {
|
|||
}
|
||||
|
||||
for message in chat {
|
||||
println!("{}: {}", message.time, message.message);
|
||||
println!("{}: {}", message.name, message.message);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,31 @@
|
|||
use crate::raw_event::RawSubject;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
use steamid_ng::SteamID;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)]
|
||||
pub enum Team {
|
||||
Red,
|
||||
Blue,
|
||||
}
|
||||
|
||||
impl Team {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Team::Red => "Red",
|
||||
Team::Blue => "Blue",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Team {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Team {
|
||||
type Err = ();
|
||||
|
||||
|
|
@ -20,7 +39,7 @@ impl FromStr for Team {
|
|||
}
|
||||
|
||||
/// Optimized subject id
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd)]
|
||||
pub enum SubjectId {
|
||||
Player(u8),
|
||||
Team(Team),
|
||||
|
|
@ -40,3 +59,52 @@ impl From<&RawSubject<'_>> for SubjectId {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SubjectData {
|
||||
Player {
|
||||
name: String,
|
||||
user_id: u8,
|
||||
steam_id: SteamID,
|
||||
team: Team,
|
||||
},
|
||||
Team(Team),
|
||||
System(String),
|
||||
Console,
|
||||
World,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SubjectError {
|
||||
#[error("Invalid user id")]
|
||||
InvalidUserId,
|
||||
#[error("Invalid steam id")]
|
||||
InvalidSteamId,
|
||||
#[error("Invalid team name")]
|
||||
InvalidTeam,
|
||||
}
|
||||
|
||||
impl TryFrom<&RawSubject<'_>> for SubjectData {
|
||||
type Error = SubjectError;
|
||||
|
||||
fn try_from(raw: &RawSubject<'_>) -> Result<Self, Self::Error> {
|
||||
Ok(match raw {
|
||||
RawSubject::Player {
|
||||
name,
|
||||
user_id,
|
||||
steam_id,
|
||||
team,
|
||||
} => SubjectData::Player {
|
||||
name: name.to_string(),
|
||||
user_id: user_id.parse().map_err(|_| SubjectError::InvalidUserId)?,
|
||||
steam_id: SteamID::from_steam3(steam_id)
|
||||
.map_err(|_| SubjectError::InvalidSteamId)?,
|
||||
team: team.parse().map_err(|_| SubjectError::InvalidTeam)?,
|
||||
},
|
||||
RawSubject::Team(team) => SubjectData::Team(team.parse().unwrap()),
|
||||
RawSubject::System(name) => SubjectData::System(name.to_string()),
|
||||
RawSubject::Console => SubjectData::Console,
|
||||
RawSubject::World => SubjectData::World,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
39
src/lib.rs
39
src/lib.rs
|
|
@ -1,8 +1,11 @@
|
|||
use crate::common::{SubjectData, SubjectError, SubjectId};
|
||||
use crate::module::EventHandler;
|
||||
use crate::raw_event::RawEvent;
|
||||
use crate::raw_event::{RawEvent, RawSubject};
|
||||
use chrono::{DateTime, Utc};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::ops::Index;
|
||||
use thiserror::Error;
|
||||
|
||||
mod common;
|
||||
|
|
@ -26,12 +29,41 @@ impl<Handler: EventHandler> Debug for Error<Handler> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Handler: EventHandler> From<SubjectError> for Error<Handler> {
|
||||
fn from(e: SubjectError) -> Self {
|
||||
Error::Malformed(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Handler: EventHandler> From<nom::error::Error<&'_ str>> for Error<Handler> {
|
||||
fn from(e: nom::error::Error<&str>) -> Self {
|
||||
Error::Malformed(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SubjectMap(BTreeMap<SubjectId, SubjectData>);
|
||||
|
||||
impl Index<SubjectId> for SubjectMap {
|
||||
type Output = SubjectData;
|
||||
|
||||
fn index(&self, index: SubjectId) -> &Self::Output {
|
||||
self.0
|
||||
.get(&index)
|
||||
.expect("subject id created without matching subject data")
|
||||
}
|
||||
}
|
||||
|
||||
impl SubjectMap {
|
||||
pub fn insert(&mut self, raw: &RawSubject) -> Result<SubjectId, SubjectError> {
|
||||
let id = raw.into();
|
||||
if !self.0.contains_key(&id) {
|
||||
self.0.insert(id, raw.try_into()?);
|
||||
}
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_with_handler<Handler: EventHandler>(
|
||||
log: &str,
|
||||
) -> Result<Handler::Output, Error<Handler>> {
|
||||
|
|
@ -43,6 +75,7 @@ pub fn parse_with_handler<Handler: EventHandler>(
|
|||
let mut handler = Handler::default();
|
||||
|
||||
let mut start_time: Option<DateTime<Utc>> = None;
|
||||
let mut subjects = SubjectMap::default();
|
||||
|
||||
for event_res in events {
|
||||
let event = event_res?;
|
||||
|
|
@ -56,10 +89,10 @@ pub fn parse_with_handler<Handler: EventHandler>(
|
|||
}
|
||||
};
|
||||
handler
|
||||
.handle(match_time, (&event.subject).into(), &event)
|
||||
.handle(match_time, subjects.insert(&event.subject)?, &event)
|
||||
.map_err(Error::HandlerError)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(handler.finish())
|
||||
Ok(handler.finish(&subjects))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,50 @@
|
|||
use crate::common::SubjectId;
|
||||
use crate::common::{SubjectData, SubjectId};
|
||||
use crate::module::EventHandler;
|
||||
use crate::raw_event::{RawEvent, RawEventType};
|
||||
use crate::SubjectMap;
|
||||
use std::convert::Infallible;
|
||||
use steamid_ng::SteamID;
|
||||
|
||||
pub struct ChatMessage {
|
||||
struct BareChatMessage {
|
||||
pub time: u32,
|
||||
pub subject: SubjectId,
|
||||
pub message: String,
|
||||
pub chat_type: ChatType,
|
||||
}
|
||||
|
||||
pub struct ChatMessage {
|
||||
pub time: u32,
|
||||
pub name: String,
|
||||
pub steam_id: SteamID,
|
||||
pub message: String,
|
||||
pub chat_type: ChatType,
|
||||
}
|
||||
|
||||
impl ChatMessage {
|
||||
fn from_bare(bare: BareChatMessage, subjects: &SubjectMap) -> Self {
|
||||
let (name, steam_id) = match &subjects[bare.subject] {
|
||||
SubjectData::Player { name, steam_id, .. } => (name.clone(), steam_id.clone()),
|
||||
_ => {
|
||||
unreachable!("only player messages are added");
|
||||
}
|
||||
};
|
||||
ChatMessage {
|
||||
time: bare.time,
|
||||
name,
|
||||
steam_id,
|
||||
message: bare.message,
|
||||
chat_type: bare.chat_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ChatType {
|
||||
All,
|
||||
Team,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ChatHandler(Vec<ChatMessage>);
|
||||
pub struct ChatHandler(Vec<BareChatMessage>);
|
||||
|
||||
impl EventHandler for ChatHandler {
|
||||
type Output = Vec<ChatMessage>;
|
||||
|
|
@ -36,13 +64,13 @@ impl EventHandler for ChatHandler {
|
|||
return Ok(());
|
||||
}
|
||||
match event.ty {
|
||||
RawEventType::SayTeam => self.0.push(ChatMessage {
|
||||
RawEventType::SayTeam => self.0.push(BareChatMessage {
|
||||
time,
|
||||
subject,
|
||||
message: event.params.trim_matches('"').into(),
|
||||
chat_type: ChatType::Team,
|
||||
}),
|
||||
RawEventType::Say => self.0.push(ChatMessage {
|
||||
RawEventType::Say => self.0.push(BareChatMessage {
|
||||
time,
|
||||
subject,
|
||||
message: event.params.trim_matches('"').into(),
|
||||
|
|
@ -53,7 +81,10 @@ impl EventHandler for ChatHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(self) -> Self::Output {
|
||||
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||
self.0
|
||||
.into_iter()
|
||||
.map(|bare| ChatMessage::from_bare(bare, subjects))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::common::SubjectId;
|
||||
use crate::module::EventHandler;
|
||||
use crate::raw_event::{RawEvent, RawEventType};
|
||||
use crate::SubjectMap;
|
||||
use chrono::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc};
|
||||
use std::str::{FromStr, ParseBoolError};
|
||||
use steamid_ng::SteamID;
|
||||
|
|
@ -182,7 +183,7 @@ impl EventHandler for LobbySettingsHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(self) -> Self::Output {
|
||||
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
||||
if self.0.id > 0 {
|
||||
Some(self.0)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::common::SubjectId;
|
||||
use crate::raw_event::{RawEvent, RawEventType};
|
||||
use crate::SubjectMap;
|
||||
pub use chat::{ChatHandler, ChatMessage, ChatType};
|
||||
pub use lobbysettings::{
|
||||
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
||||
|
|
@ -25,7 +26,7 @@ pub trait EventHandler: Default {
|
|||
event: &RawEvent,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
fn finish(self) -> Self::Output;
|
||||
fn finish(self, subjects: &SubjectMap) -> Self::Output;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
@ -57,8 +58,8 @@ impl<Head: EventHandler, Tail: EventHandler> EventHandler for HandlerStack<Head,
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(self) -> Self::Output {
|
||||
(self.head.finish(), self.tail.finish())
|
||||
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||
(self.head.finish(subjects), self.tail.finish(subjects))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,9 +113,9 @@ impl<Handler: EventHandler> EventHandler for OptionalHandler<Handler> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(self) -> Self::Output {
|
||||
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||
match self {
|
||||
OptionalHandler::Active(handler) => Ok(handler.finish()),
|
||||
OptionalHandler::Active(handler) => Ok(handler.finish(subjects)),
|
||||
OptionalHandler::Failed(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +1,211 @@
|
|||
{"game":{"rounds":[],"toatlLength":0},"chat":[{"timeInSeconds":588,"steamid":"Console","name":"Console","team":"Console","message":"hello, bear! just donated to serveme.tf - 1 year! 31 percent of our monthly server bills are now taken care of"}],"healspread":{},"killstreaks":[{"steamid":"[U:1:101449025]","streak":3,"time":29},{"steamid":"[U:1:101608870]","streak":4,"time":44},{"steamid":"[U:1:101449025]","streak":3,"time":187},{"steamid":"[U:1:82123920]","streak":3,"time":207},{"steamid":"[U:1:88677982]","streak":4,"time":234},{"steamid":"[U:1:101608870]","streak":3,"time":310},{"steamid":"[U:1:82123920]","streak":4,"time":371},{"steamid":"[U:1:101608870]","streak":3,"time":463}],"playerClasses":{},"players":{"[U:1:82123920]":{"team":"Red","kills":31,"assists":6,"deaths":32,"damage":6461,"suicides":3,"damageTaken":7399,"charges":0,"chargesByType":{},"drops":0,"airshots":4,"sentriesBuilt":0,"sentriesDestroyed":0,"headshots":0,"headshotKills":0,"healing":0,"healingReceived":0,"medkits":0,"medkitsHp":0,"backstabs":0,"capturesPoint":0,"capturesIntel":1,"longestKillStreak":3,"currentKillStreak":0,"medicstats":null},"[U:1:101608870]":{"team":"Blue","kills":31,"assists":10,"deaths":29,"damage":8262,"suicides":1,"damageTaken":6274,"charges":0,"chargesByType":{},"drops":0,"airshots":4,"sentriesBuilt":0,"sentriesDestroyed":0,"headshots":0,"headshotKills":0,"healing":0,"healingReceived":0,"medkits":0,"medkitsHp":0,"backstabs":0,"capturesPoint":0,"capturesIntel":10,"longestKillStreak":6,"currentKillStreak":0,"medicstats":null},"[U:1:88677982]":{"team":"Blue","kills":30,"assists":8,"deaths":29,"damage":5764,"suicides":1,"damageTaken":5226,"charges":0,"chargesByType":{},"drops":0,"airshots":6,"sentriesBuilt":0,"sentriesDestroyed":0,"headshots":0,"headshotKills":0,"healing":0,"healingReceived":0,"medkits":0,"medkitsHp":0,"backstabs":0,"capturesPoint":0,"capturesIntel":15,"longestKillStreak":5,"currentKillStreak":2,"medicstats":null},"[U:1:101449025]":{"team":"Red","kills":25,"assists":9,"deaths":32,"damage":5039,"suicides":0,"damageTaken":6627,"charges":0,"chargesByType":{},"drops":0,"airshots":5,"sentriesBuilt":0,"sentriesDestroyed":0,"headshots":0,"headshotKills":0,"healing":0,"healingReceived":0,"medkits":0,"medkitsHp":0,"backstabs":0,"capturesPoint":0,"capturesIntel":15,"longestKillStreak":3,"currentKillStreak":0,"medicstats":null}},"PvC":{},"PvP":{},"realDamage":{"[U:1:82123920]":{"DamageTaken":7399,"DamageDealt":6461},"[U:1:101608870]":{"DamageTaken":6274,"DamageDealt":8160},"[U:1:88677982]":{"DamageTaken":5226,"DamageDealt":5764},"[U:1:101449025]":{"DamageTaken":6525,"DamageDealt":5039}},"teams":{"Red":{"score":0,"kills":56,"deaths":61,"damage":11500,"charges":0,"drops":0,"captures":0,"midfights":0},"Blue":{"score":1,"kills":61,"deaths":56,"damage":14026,"charges":0,"drops":0,"captures":0,"midfights":0}}}
|
||||
{
|
||||
"game": {
|
||||
"rounds": [],
|
||||
"toatlLength": 0
|
||||
},
|
||||
"chat": [
|
||||
{
|
||||
"timeInSeconds": 588,
|
||||
"steamid": "Console",
|
||||
"name": "Console",
|
||||
"team": "Console",
|
||||
"message": "hello, bear! just donated to serveme.tf - 1 year! 31 percent of our monthly server bills are now taken care of"
|
||||
}
|
||||
],
|
||||
"healspread": {},
|
||||
"killstreaks": [
|
||||
{
|
||||
"steamid": "[U:1:101449025]",
|
||||
"streak": 3,
|
||||
"time": 29
|
||||
},
|
||||
{
|
||||
"steamid": "[U:1:101608870]",
|
||||
"streak": 4,
|
||||
"time": 44
|
||||
},
|
||||
{
|
||||
"steamid": "[U:1:101449025]",
|
||||
"streak": 3,
|
||||
"time": 187
|
||||
},
|
||||
{
|
||||
"steamid": "[U:1:82123920]",
|
||||
"streak": 3,
|
||||
"time": 207
|
||||
},
|
||||
{
|
||||
"steamid": "[U:1:88677982]",
|
||||
"streak": 4,
|
||||
"time": 234
|
||||
},
|
||||
{
|
||||
"steamid": "[U:1:101608870]",
|
||||
"streak": 3,
|
||||
"time": 310
|
||||
},
|
||||
{
|
||||
"steamid": "[U:1:82123920]",
|
||||
"streak": 4,
|
||||
"time": 371
|
||||
},
|
||||
{
|
||||
"steamid": "[U:1:101608870]",
|
||||
"streak": 3,
|
||||
"time": 463
|
||||
}
|
||||
],
|
||||
"playerClasses": {},
|
||||
"players": {
|
||||
"[U:1:82123920]": {
|
||||
"team": "Red",
|
||||
"kills": 31,
|
||||
"assists": 6,
|
||||
"deaths": 32,
|
||||
"damage": 6461,
|
||||
"suicides": 3,
|
||||
"damageTaken": 7399,
|
||||
"charges": 0,
|
||||
"chargesByType": {},
|
||||
"drops": 0,
|
||||
"airshots": 4,
|
||||
"sentriesBuilt": 0,
|
||||
"sentriesDestroyed": 0,
|
||||
"headshots": 0,
|
||||
"headshotKills": 0,
|
||||
"healing": 0,
|
||||
"healingReceived": 0,
|
||||
"medkits": 0,
|
||||
"medkitsHp": 0,
|
||||
"backstabs": 0,
|
||||
"capturesPoint": 0,
|
||||
"capturesIntel": 1,
|
||||
"longestKillStreak": 3,
|
||||
"currentKillStreak": 0,
|
||||
"medicstats": null
|
||||
},
|
||||
"[U:1:101608870]": {
|
||||
"team": "Blue",
|
||||
"kills": 31,
|
||||
"assists": 10,
|
||||
"deaths": 29,
|
||||
"damage": 8262,
|
||||
"suicides": 1,
|
||||
"damageTaken": 6274,
|
||||
"charges": 0,
|
||||
"chargesByType": {},
|
||||
"drops": 0,
|
||||
"airshots": 4,
|
||||
"sentriesBuilt": 0,
|
||||
"sentriesDestroyed": 0,
|
||||
"headshots": 0,
|
||||
"headshotKills": 0,
|
||||
"healing": 0,
|
||||
"healingReceived": 0,
|
||||
"medkits": 0,
|
||||
"medkitsHp": 0,
|
||||
"backstabs": 0,
|
||||
"capturesPoint": 0,
|
||||
"capturesIntel": 10,
|
||||
"longestKillStreak": 6,
|
||||
"currentKillStreak": 0,
|
||||
"medicstats": null
|
||||
},
|
||||
"[U:1:88677982]": {
|
||||
"team": "Blue",
|
||||
"kills": 30,
|
||||
"assists": 8,
|
||||
"deaths": 29,
|
||||
"damage": 5764,
|
||||
"suicides": 1,
|
||||
"damageTaken": 5226,
|
||||
"charges": 0,
|
||||
"chargesByType": {},
|
||||
"drops": 0,
|
||||
"airshots": 6,
|
||||
"sentriesBuilt": 0,
|
||||
"sentriesDestroyed": 0,
|
||||
"headshots": 0,
|
||||
"headshotKills": 0,
|
||||
"healing": 0,
|
||||
"healingReceived": 0,
|
||||
"medkits": 0,
|
||||
"medkitsHp": 0,
|
||||
"backstabs": 0,
|
||||
"capturesPoint": 0,
|
||||
"capturesIntel": 15,
|
||||
"longestKillStreak": 5,
|
||||
"currentKillStreak": 2,
|
||||
"medicstats": null
|
||||
},
|
||||
"[U:1:101449025]": {
|
||||
"team": "Red",
|
||||
"kills": 25,
|
||||
"assists": 9,
|
||||
"deaths": 32,
|
||||
"damage": 5039,
|
||||
"suicides": 0,
|
||||
"damageTaken": 6627,
|
||||
"charges": 0,
|
||||
"chargesByType": {},
|
||||
"drops": 0,
|
||||
"airshots": 5,
|
||||
"sentriesBuilt": 0,
|
||||
"sentriesDestroyed": 0,
|
||||
"headshots": 0,
|
||||
"headshotKills": 0,
|
||||
"healing": 0,
|
||||
"healingReceived": 0,
|
||||
"medkits": 0,
|
||||
"medkitsHp": 0,
|
||||
"backstabs": 0,
|
||||
"capturesPoint": 0,
|
||||
"capturesIntel": 15,
|
||||
"longestKillStreak": 3,
|
||||
"currentKillStreak": 0,
|
||||
"medicstats": null
|
||||
}
|
||||
},
|
||||
"PvC": {},
|
||||
"PvP": {},
|
||||
"realDamage": {
|
||||
"[U:1:82123920]": {
|
||||
"DamageTaken": 7399,
|
||||
"DamageDealt": 6461
|
||||
},
|
||||
"[U:1:101608870]": {
|
||||
"DamageTaken": 6274,
|
||||
"DamageDealt": 8160
|
||||
},
|
||||
"[U:1:88677982]": {
|
||||
"DamageTaken": 5226,
|
||||
"DamageDealt": 5764
|
||||
},
|
||||
"[U:1:101449025]": {
|
||||
"DamageTaken": 6525,
|
||||
"DamageDealt": 5039
|
||||
}
|
||||
},
|
||||
"teams": {
|
||||
"Red": {
|
||||
"score": 0,
|
||||
"kills": 56,
|
||||
"deaths": 61,
|
||||
"damage": 11500,
|
||||
"charges": 0,
|
||||
"drops": 0,
|
||||
"captures": 0,
|
||||
"midfights": 0
|
||||
},
|
||||
"Blue": {
|
||||
"score": 1,
|
||||
"kills": 61,
|
||||
"deaths": 56,
|
||||
"damage": 14026,
|
||||
"charges": 0,
|
||||
"drops": 0,
|
||||
"captures": 0,
|
||||
"midfights": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue