more event parsers

This commit is contained in:
Robin Appelman 2021-08-24 23:01:07 +02:00
commit 58100e5f7b
8 changed files with 418 additions and 17 deletions

View file

@ -14,6 +14,7 @@ use thiserror::Error;
pub enum Team {
Red,
Blue,
Spectator,
}
impl Team {
@ -21,6 +22,7 @@ impl Team {
match self {
Team::Red => "Red",
Team::Blue => "Blue",
Team::Spectator => "Spectator",
}
}
}
@ -38,6 +40,7 @@ impl FromStr for Team {
match s {
"Blue" => Ok(Team::Blue),
"Red" => Ok(Team::Red),
"Spectator" => Ok(Team::Spectator),
_ => Err(()),
}
}

View file

@ -1,4 +1,6 @@
use crate::event::{param_parse, param_parse_with};
use crate::event::{param_parse, param_parse_with, parse_from_str, position, u_int, ParamIter};
use crate::raw_event::{subject_parser, RawSubject};
use nom::bytes::complete::{tag, take_while};
use nom::combinator::opt;
use nom::number::complete::float;
use nom::IResult;
@ -22,3 +24,145 @@ pub fn round_length_event_parser(input: &str) -> IResult<&str, RoundLengthEvent>
let (input, length) = opt(param_parse_with("against", float))(input)?;
Ok((input, RoundLengthEvent { length }))
}
#[derive(Debug)]
pub struct LogFileStartedEvent<'a> {
pub file: Option<&'a str>,
pub game: Option<&'a str>,
pub version: Option<&'a str>,
}
pub fn log_file_started_event_parser(input: &str) -> IResult<&str, LogFileStartedEvent> {
let (input, file) = opt(param_parse("file"))(input)?;
let (input, game) = opt(param_parse("game"))(input)?;
let (input, version) = opt(param_parse("version"))(input)?;
Ok((
input,
LogFileStartedEvent {
file,
game,
version,
},
))
}
#[derive(Debug)]
pub struct TournamentModeStartedEvent<'a> {
pub blue: &'a str,
pub red: &'a str,
}
pub fn tournament_mode_started_event_parser(
input: &str,
) -> IResult<&str, TournamentModeStartedEvent> {
let (input, _) = tag("\nBlue Team: ")(input)?;
let (input, blue) = take_while(|c| c != '\n')(input)?;
let (input, _) = tag("\nRed Team: ")(input)?;
let (input, red) = take_while(|c| c != '\n')(input)?;
Ok((input, TournamentModeStartedEvent { blue, red }))
}
#[derive(Debug)]
pub struct CaptureBlockedEvent<'a> {
pub cp: Option<u8>,
pub cp_name: Option<&'a str>,
pub position: Option<(i32, i32, i32)>,
}
pub fn capture_blocked_event_parser(input: &str) -> IResult<&str, CaptureBlockedEvent> {
let (input, cp) = opt(param_parse_with("cp", u_int))(input)?;
let (input, cp_name) = opt(param_parse("map"))(input)?;
let (input, position) = opt(param_parse_with("red", position))(input)?;
Ok((
input,
CaptureBlockedEvent {
cp: cp.map(|cp| cp as u8),
cp_name,
position,
},
))
}
#[derive(Debug)]
pub struct PointCapturedEvent<'a> {
pub cp: Option<u8>,
pub cp_name: Option<&'a str>,
pub num_cappers: Option<u8>,
pub players: Vec<(RawSubject<'a>, (i32, i32, i32))>,
}
pub fn point_captures_event_parser(input: &str) -> IResult<&str, PointCapturedEvent> {
let (input, cp) = opt(param_parse_with("cp", u_int))(input)?;
let (input, cp_name) = opt(param_parse("map"))(input)?;
let (input, num_cappers) = opt(param_parse_with("numcappers", u_int))(input)?;
let mut players = Vec::new();
let mut params = ParamIter::new(input);
match (params.next(), params.next()) {
(Some((subject_key, subject)), Some((position_key, position_str)))
if subject_key.starts_with("player") && position_key.starts_with("position") =>
{
let (_, subject) = subject_parser(subject)?;
let (_, position) = position(position_str)?;
players.push((subject, position));
}
_ => {}
}
Ok((
input,
PointCapturedEvent {
cp: cp.map(|cp| cp as u8),
num_cappers: num_cappers.map(|num| num as u8),
cp_name,
players,
},
))
}
#[derive(Debug)]
pub struct CurrentScoreEvent {
pub score: u8,
pub player: u8,
}
pub fn current_score_event_parser(input: &str) -> IResult<&str, CurrentScoreEvent> {
let (input, score) = param_parse_with("cp", u_int)(input)?;
let (input, player) = param_parse_with("with", u_int)(input)?;
Ok((
input,
CurrentScoreEvent {
score: score as u8,
player: player as u8,
},
))
}
#[derive(Debug)]
pub struct GameOverEvent<'a> {
pub reason: &'a str,
}
pub fn game_over_event_parser(input: &str) -> IResult<&str, GameOverEvent> {
let (input, reason) = param_parse("reason")(input)?;
Ok((input, GameOverEvent { reason }))
}
#[derive(Debug)]
pub struct FinalScoreEvent {
pub score: u8,
pub player: u8,
}
pub fn final_score_event_parser(input: &str) -> IResult<&str, FinalScoreEvent> {
let (input, score) = param_parse_with("cp", u_int)(input)?;
let (input, player) = param_parse_with("with", u_int)(input)?;
Ok((
input,
FinalScoreEvent {
score: score as u8,
player: player as u8,
},
))
}

View file

@ -76,3 +76,18 @@ pub fn medic_death_event_parser(input: &str) -> IResult<&str, MedicDeathEvent> {
}
Ok((input, MedicDeathEvent { charge }))
}
#[derive(Debug)]
pub struct MedicDeathExEvent {
pub charge_percentage: Option<u8>,
}
pub fn medic_death_ex_event_parser(input: &str) -> IResult<&str, MedicDeathExEvent> {
let (input, charge_percentage) = opt(param_parse_with("time", quoted(u_int)))(input)?;
Ok((
input,
MedicDeathExEvent {
charge_percentage: charge_percentage.map(|charge: u32| charge as u8),
},
))
}

View file

@ -9,9 +9,10 @@ pub use medic::*;
use nom::bytes::complete::{tag, take_while};
use nom::character::complete::{alpha1, digit1};
use nom::combinator::opt;
use nom::error::ErrorKind;
use nom::error::{ErrorKind, ParseError};
use nom::{Err, IResult};
pub use player::*;
use std::str::FromStr;
use thiserror::Error;
#[derive(Error, Debug)]
@ -68,12 +69,34 @@ pub enum GameEvent<'a> {
FirstHeal(FirstHealEvent),
ChargeReady,
MedicDeath(MedicDeathEvent),
MedicDeathEx(MedicDeathExEvent),
Spawned(SpawnEvent),
RoleChange(RoleChangeEvent),
RoundStart,
RoundWin(RoundWinEvent<'a>),
RoundLength(RoundLengthEvent),
RoundOverTime,
LogFileStarted(LogFileStartedEvent<'a>),
Connected(ConnectedEvent),
Disconnect(DisconnectEvent<'a>),
SteamIdValidated,
Entered,
Joined(JoinedTeamEvent),
Suicide(CommittedSuicideEvent<'a>),
PickedUp(PickedUpEvent<'a>),
Domination(DominationEvent<'a>),
EmptyUber,
Revenge(RevengeEvent<'a>),
TournamentModeStarted(TournamentModeStartedEvent<'a>),
CaptureBlocked(CaptureBlockedEvent<'a>),
PointCaptured(PointCapturedEvent<'a>),
CurrentScore(CurrentScoreEvent),
BuiltObject(BuiltObjectEvent<'a>),
KilledObject(KilledObjectEvent<'a>),
Extinguished(ExtinguishedEvent<'a>),
GameOver(GameOverEvent<'a>),
FinalScore(FinalScoreEvent),
LogFileClosed,
}
impl<'a> GameEvent<'a> {
@ -115,6 +138,9 @@ impl<'a> GameEvent<'a> {
RawEventType::MedicDeath => {
GameEvent::MedicDeath(medic_death_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::MedicDeathEx => {
GameEvent::MedicDeathEx(medic_death_ex_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Spawned => {
GameEvent::Spawned(spawn_event_parser(raw.params).with_type(raw.ty)?)
}
@ -129,6 +155,61 @@ impl<'a> GameEvent<'a> {
GameEvent::RoundWin(round_win_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::RoundOvertime => GameEvent::RoundOverTime,
RawEventType::LogFileStarted => GameEvent::LogFileStarted(
log_file_started_event_parser(raw.params).with_type(raw.ty)?,
),
RawEventType::Connected => {
GameEvent::Connected(connected_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Disconnected => {
GameEvent::Disconnect(disconnected_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::SteamIdValidated => GameEvent::SteamIdValidated,
RawEventType::Entered => GameEvent::Entered,
RawEventType::Joined => {
GameEvent::Joined(joined_team_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Suicide => {
GameEvent::Suicide(committed_suicide_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::PickedUp => {
GameEvent::PickedUp(picked_up_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::Domination => {
GameEvent::Domination(domination_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::EmptyUber => GameEvent::EmptyUber,
RawEventType::Revenge => {
GameEvent::Revenge(revenge_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::TournamentStart => GameEvent::TournamentModeStarted(
tournament_mode_started_event_parser(raw.params).with_type(raw.ty)?,
),
RawEventType::CaptureBlocked => GameEvent::CaptureBlocked(
capture_blocked_event_parser(raw.params).with_type(raw.ty)?,
),
RawEventType::PointCaptured => {
GameEvent::PointCaptured(point_captures_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::CurrentScore => {
GameEvent::CurrentScore(current_score_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::PlayerBuiltObject => {
GameEvent::BuiltObject(built_object_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::PlayerKilledObject => {
GameEvent::KilledObject(killed_object_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::PlayerExtinguished => {
GameEvent::Extinguished(extinguished_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::GameOver => {
GameEvent::GameOver(game_over_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::FinalScore => {
GameEvent::FinalScore(final_score_event_parser(raw.params).with_type(raw.ty)?)
}
RawEventType::LogFileClosed => GameEvent::LogFileClosed,
_ => {
todo!("{:?} not parsed yet", raw.ty);
}
@ -205,6 +286,12 @@ fn param_parse_with<'a, T, P: Fn(&'a str) -> IResult<&'a str, T>>(
}
}
fn parse_from_str<T: FromStr>(input: &str) -> IResult<&str, T> {
T::from_str(input)
.map(|res| ("", res))
.map_err(|_| nom::Err::Error(nom::error::Error::from_error_kind(input, ErrorKind::IsNot)))
}
fn int(input: &str) -> IResult<&str, i32> {
let (input, sign) = opt(tag("-"))(input)?;
let (input, raw) = digit1(input)?;

View file

@ -1,8 +1,9 @@
use crate::common::Class;
use crate::event::{param_parse, param_parse_with, position, u_int, ParamIter};
use crate::common::{Class, Team};
use crate::event::{param_parse, param_parse_with, parse_from_str, position, u_int, ParamIter};
use crate::raw_event::{subject_parser, RawSubject};
use nom::combinator::opt;
use nom::IResult;
use std::net::SocketAddr;
use std::num::NonZeroU32;
#[derive(Debug)]
@ -132,3 +133,141 @@ pub fn role_changed_event_parser(input: &str) -> IResult<&str, RoleChangeEvent>
},
))
}
#[derive(Debug)]
pub struct ConnectedEvent {
pub address: SocketAddr,
}
pub fn connected_event_parser(input: &str) -> IResult<&str, ConnectedEvent> {
let (input, address) = param_parse_with("to", parse_from_str)(input)?;
Ok((input, ConnectedEvent { address }))
}
#[derive(Debug)]
pub struct JoinedTeamEvent {
pub team: Team,
}
pub fn joined_team_event_parser(input: &str) -> IResult<&str, JoinedTeamEvent> {
let (input, team) = param_parse_with("team", parse_from_str)(input)?;
Ok((input, JoinedTeamEvent { team }))
}
#[derive(Debug)]
pub struct CommittedSuicideEvent<'a> {
pub weapon: &'a str,
pub attacker_position: Option<(i32, i32, i32)>,
}
pub fn committed_suicide_event_parser(input: &str) -> IResult<&str, CommittedSuicideEvent> {
let (input, weapon) = param_parse("with")(input)?;
let (input, attacker_position) = opt(param_parse_with("attacker_position", position))(input)?;
Ok((
input,
CommittedSuicideEvent {
weapon,
attacker_position,
},
))
}
#[derive(Debug)]
pub struct PickedUpEvent<'a> {
pub item: &'a str,
}
pub fn picked_up_event_parser(input: &str) -> IResult<&str, PickedUpEvent> {
let (input, item) = param_parse("item")(input)?;
Ok((input, PickedUpEvent { item }))
}
#[derive(Debug)]
pub struct DominationEvent<'a> {
pub against: RawSubject<'a>,
}
pub fn domination_event_parser(input: &str) -> IResult<&str, DominationEvent> {
let (input, against) = param_parse_with("against", subject_parser)(input)?;
Ok((input, DominationEvent { against }))
}
#[derive(Debug)]
pub struct RevengeEvent<'a> {
pub against: RawSubject<'a>,
}
pub fn revenge_event_parser(input: &str) -> IResult<&str, RevengeEvent> {
let (input, against) = param_parse_with("against", subject_parser)(input)?;
Ok((input, RevengeEvent { against }))
}
#[derive(Debug)]
pub struct DisconnectEvent<'a> {
pub reason: Option<&'a str>,
}
pub fn disconnected_event_parser(input: &str) -> IResult<&str, DisconnectEvent> {
let (input, reason) = opt(param_parse("reason"))(input)?;
Ok((input, DisconnectEvent { reason }))
}
#[derive(Debug)]
pub struct BuiltObjectEvent<'a> {
pub object: Option<&'a str>,
pub position: Option<(i32, i32, i32)>,
}
pub fn built_object_event_parser(input: &str) -> IResult<&str, BuiltObjectEvent> {
let (input, object) = opt(param_parse("object"))(input)?;
let (input, position) = opt(param_parse_with("position", position))(input)?;
Ok((input, BuiltObjectEvent { object, position }))
}
#[derive(Debug)]
pub struct KilledObjectEvent<'a> {
pub object: Option<&'a str>,
pub weapon: Option<&'a str>,
pub object_owner: Option<RawSubject<'a>>,
pub attacker_position: Option<(i32, i32, i32)>,
}
pub fn killed_object_event_parser(input: &str) -> IResult<&str, KilledObjectEvent> {
let (input, object) = opt(param_parse("object"))(input)?;
let (input, weapon) = opt(param_parse("weapon"))(input)?;
let (input, object_owner) = opt(param_parse_with("objectowner", subject_parser))(input)?;
let (input, attacker_position) = opt(param_parse_with("attacker_position", position))(input)?;
Ok((
input,
KilledObjectEvent {
object,
weapon,
object_owner,
attacker_position,
},
))
}
#[derive(Debug)]
pub struct ExtinguishedEvent<'a> {
pub against: RawSubject<'a>,
pub with: &'a str,
pub attacker_position: Option<(i32, i32, i32)>,
pub victim_position: Option<(i32, i32, i32)>,
}
pub fn extinguished_event_parser(input: &str) -> IResult<&str, ExtinguishedEvent> {
let (input, against) = param_parse_with("against", subject_parser)(input)?;
let (input, with) = param_parse("with")(input)?;
let (input, attacker_position) = opt(param_parse_with("attacker_position", position))(input)?;
let (input, victim_position) = opt(param_parse_with("victim_position", position))(input)?;
Ok((
input,
ExtinguishedEvent {
against,
with,
attacker_position,
victim_position,
},
))
}

View file

@ -61,8 +61,8 @@ pub fn parse_with_handler<Handler: EventHandler>(
Error,
> {
let events = log
.lines()
.filter(|line| line.starts_with("L "))
.split("L ")
.filter(|line| !line.is_empty())
.map(RawEvent::parse);
let mut handler = Handler::default();

View file

@ -27,7 +27,6 @@ impl<'a> RawEvent<'a> {
}
fn event_parser(input: &str) -> IResult<&str, RawEvent> {
let (input, _) = tag("L ")(input)?;
let (input, date) = date_parser(input)?;
let (input, _) = tag(": ")(input)?;
@ -190,7 +189,7 @@ pub fn subject_parser(input: &str) -> IResult<&str, RawSubject> {
#[derive(IntoEnumIterator, Copy, Clone, Debug, PartialEq)]
pub enum RawEventType {
JoinedTeam,
Joined,
ChangedRole,
ShotFired,
ShotHit,
@ -212,7 +211,7 @@ pub enum RawEventType {
PlayerKilledObject,
PlayerExtinguished,
ObjectDetonated,
PickedUpItem,
PickedUp,
MedicDeath,
MedicDeathEx,
ChargeEnd,
@ -253,7 +252,7 @@ pub enum RawEventType {
impl RawEventType {
pub fn tag(self) -> &'static str {
match self {
RawEventType::JoinedTeam => r#"joined team"#,
RawEventType::Joined => r#"joined"#,
RawEventType::ChangedRole => r#"changed role"#,
RawEventType::ShotFired => r#"triggered "shot_fired""#,
RawEventType::ShotHit => r#"triggered "shot_hit""#,
@ -275,7 +274,7 @@ impl RawEventType {
RawEventType::PlayerKilledObject => r#"triggered "killedobject""#,
RawEventType::ObjectDetonated => r#"triggered "object_detonated""#,
RawEventType::PlayerExtinguished => r#"triggered "player_extinguished""#,
RawEventType::PickedUpItem => r#"picked up item"#,
RawEventType::PickedUp => r#"picked up"#,
RawEventType::MedicDeath => r#"triggered "medic_death""#,
RawEventType::MedicDeathEx => r#"triggered "medic_death_ex""#,
RawEventType::ChargeEnd => r#"triggered "chargeended""#,
@ -295,8 +294,8 @@ impl RawEventType {
RawEventType::PointCaptured => r#"triggered "pointcaptured""#,
RawEventType::CaptureBlocked => r#"triggered "captureblocked""#,
RawEventType::GameOver => r#"triggered "Game_Over""#,
RawEventType::CurrentScore => r#"current score"#,
RawEventType::FinalScore => r#"final score"#,
RawEventType::CurrentScore => r#"current"#,
RawEventType::FinalScore => r#"final"#,
RawEventType::WinLimit => r#"triggered "Intermission_Win_Limit""#,
RawEventType::Paused => r#"triggered "Game_Paused""#,
RawEventType::UnPaused => r#"triggered "Game_Unpaused""#,