derive GameEvent parsing

This commit is contained in:
Robin Appelman 2023-03-11 22:39:31 +01:00
commit f5957059d8
10 changed files with 116 additions and 121 deletions

68
derive/src/events.rs Normal file
View file

@ -0,0 +1,68 @@
use crate::{err, Derivable, DeriveParams};
use proc_macro2::{Ident, TokenStream};
use quote::quote_spanned;
use syn::{Data, DeriveInput, Generics, Result};
pub struct Events;
impl Derivable for Events {
type Params = EventsParam;
fn derive(params: EventsParam) -> Result<TokenStream> {
let enum_ident = params.name;
let span = enum_ident.span();
let variants = params.variants.iter().map(|(variant_name, empty)| {
let span = variant_name.span();
if *empty {
quote_spanned!(span => RawEventType::#variant_name => Self::#variant_name,)
} else {
quote_spanned!(span => RawEventType::#variant_name => Self::#variant_name(parse_event(raw.params).with_raw(raw)?),)
}
});
let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl();
Ok(
quote_spanned!(span => impl #impl_generics #enum_ident #ty_generics #where_clause {
pub fn parse(raw: &RawEvent<'a>) -> Result<Self, GameEventError> {
Ok(match raw.ty {
#(#variants)*
_ => {
todo!("{:?} not parsed yet", raw.ty);
}
})
}
}),
)
}
}
#[derive(Debug)]
pub struct EventsParam {
name: Ident,
generics: Generics,
variants: Vec<(Ident, bool)>,
}
impl DeriveParams for EventsParam {
fn parse(input: &DeriveInput) -> Result<EventsParam> {
let Data::Enum(data) = &input.data else {
return err("only supported on enums", input);
};
let name = input.ident.clone();
let generics = input.generics.clone();
let variants = data
.variants
.iter()
.map(|variant| (variant.ident.clone(), variant.fields.is_empty()))
.collect();
Ok(EventsParam {
name,
generics,
variants,
})
}
}

View file

@ -3,8 +3,10 @@
extern crate proc_macro; extern crate proc_macro;
mod event; mod event;
mod events;
use crate::event::Event; use crate::event::Event;
use crate::events::Events;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::ToTokens; use quote::ToTokens;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
@ -12,12 +14,20 @@ use syn::{parse_macro_input, DeriveInput, Error, Result};
/// Derive the `Event` trait for a struct /// Derive the `Event` trait for a struct
#[proc_macro_derive(Event, attributes(event))] #[proc_macro_derive(Event, attributes(event))]
pub fn derive_pod(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn derive_event(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let expanded = derive_trait::<Event>(parse_macro_input!(input as DeriveInput)); let expanded = derive_trait::<Event>(parse_macro_input!(input as DeriveInput));
proc_macro::TokenStream::from(expanded) proc_macro::TokenStream::from(expanded)
} }
/// Derive the `Events` trait for a struct
#[proc_macro_derive(Events, attributes())]
pub fn derive_events(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let expanded = derive_trait::<Events>(parse_macro_input!(input as DeriveInput));
proc_macro::TokenStream::from(expanded)
}
/// Basic wrapper for error handling /// Basic wrapper for error handling
fn derive_trait<Trait: Derivable>(input: DeriveInput) -> TokenStream { fn derive_trait<Trait: Derivable>(input: DeriveInput) -> TokenStream {
derive_trait_inner::<Trait>(input).unwrap_or_else(|err| err.into_compile_error()) derive_trait_inner::<Trait>(input).unwrap_or_else(|err| err.into_compile_error())

View file

@ -5,7 +5,7 @@ mod player;
use crate::event::game::{RoundLengthEvent, RoundWinEvent}; use crate::event::game::{RoundLengthEvent, RoundWinEvent};
use crate::parsing::{skip, skip_matches, split_once}; use crate::parsing::{skip, skip_matches, split_once};
use crate::raw_event::{against_subject_parser, RawSubject}; use crate::raw_event::{against_subject_parser, RawSubject};
use crate::{Error, IResult, RawEvent, RawEventType, Result, SubjectId}; use crate::{Error, Events, IResult, RawEvent, RawEventType, Result, SubjectId};
pub use game::*; pub use game::*;
pub use medic::*; pub use medic::*;
pub use player::*; pub use player::*;
@ -54,15 +54,15 @@ pub struct EventMeta {
pub subject: SubjectId, pub subject: SubjectId,
} }
#[derive(Debug)] #[derive(Debug, Events)]
pub enum GameEvent<'a> { pub enum GameEvent<'a> {
ShotFired(ShotFiredEvent<'a>), ShotFired(ShotFiredEvent<'a>),
ShotHit(ShotHitEvent<'a>), ShotHit(ShotHitEvent<'a>),
Damage(DamageEvent<'a>), Damage(DamageEvent<'a>),
Kill(KillEvent<'a>), Killed(KillEvent<'a>),
KillAssist(KillAssistEvent<'a>), KillAssist(KillAssistEvent<'a>),
Say(&'a str), Say(SayEvent<'a>),
SayTeam(&'a str), SayTeam(SayTeamEvent<'a>),
Healed(HealedEvent<'a>), Healed(HealedEvent<'a>),
ChargeDeployed(ChargeDeployedEvent<'a>), ChargeDeployed(ChargeDeployedEvent<'a>),
ChargeEnded(ChargeEndedEvent), ChargeEnded(ChargeEndedEvent),
@ -101,101 +101,6 @@ pub enum GameEvent<'a> {
LogFileClosed, LogFileClosed,
} }
impl<'a> GameEvent<'a> {
pub fn parse(raw: &RawEvent<'a>) -> Result<GameEvent<'a>, GameEventError> {
Ok(match raw.ty {
RawEventType::ShotFired => GameEvent::ShotFired(parse_event(raw.params).with_raw(raw)?),
RawEventType::ShotHit => GameEvent::ShotHit(parse_event(raw.params).with_raw(raw)?),
RawEventType::Damage => GameEvent::Damage(parse_event(raw.params).with_raw(raw)?),
RawEventType::Killed => GameEvent::Kill(parse_event(raw.params).with_raw(raw)?),
RawEventType::KillAssist => {
GameEvent::KillAssist(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::SayTeam => GameEvent::SayTeam(raw.params.trim_matches('"')),
RawEventType::Say => GameEvent::Say(raw.params.trim_matches('"')),
RawEventType::Healed => GameEvent::Healed(parse_event(raw.params).with_raw(raw)?),
RawEventType::ChargeDeployed => {
GameEvent::ChargeDeployed(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::ChargeEnd => {
GameEvent::ChargeEnded(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::UberAdvantageLost => {
GameEvent::AdvantageLost(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::FirstHealAfterSpawn => {
GameEvent::FirstHeal(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::ChargeReady => GameEvent::ChargeReady,
RawEventType::MedicDeath => {
GameEvent::MedicDeath(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::MedicDeathEx => {
GameEvent::MedicDeathEx(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::Spawned => GameEvent::Spawned(parse_event(raw.params).with_raw(raw)?),
RawEventType::ChangedRole => {
GameEvent::RoleChange(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::RoundStart => GameEvent::RoundStart,
RawEventType::RoundLength => {
GameEvent::RoundLength(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::RoundWin => GameEvent::RoundWin(parse_event(raw.params).with_raw(raw)?),
RawEventType::RoundOvertime => GameEvent::RoundOverTime,
RawEventType::LogFileStarted => {
GameEvent::LogFileStarted(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::Connected => GameEvent::Connected(parse_event(raw.params).with_raw(raw)?),
RawEventType::Disconnected => {
GameEvent::Disconnect(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::SteamIdValidated => GameEvent::SteamIdValidated,
RawEventType::Entered => GameEvent::Entered,
RawEventType::Joined => GameEvent::Joined(parse_event(raw.params).with_raw(raw)?),
RawEventType::Suicide => GameEvent::Suicide(parse_event(raw.params).with_raw(raw)?),
RawEventType::PickedUp => GameEvent::PickedUp(parse_event(raw.params).with_raw(raw)?),
RawEventType::Domination => {
GameEvent::Domination(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::EmptyUber => GameEvent::EmptyUber,
RawEventType::Revenge => GameEvent::Revenge(parse_event(raw.params).with_raw(raw)?),
RawEventType::TournamentStart => {
GameEvent::TournamentModeStarted(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::CaptureBlocked => {
GameEvent::CaptureBlocked(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::PointCaptured => {
GameEvent::PointCaptured(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::CurrentScore => {
GameEvent::CurrentScore(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::PlayerBuiltObject => {
GameEvent::BuiltObject(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::PlayerKilledObject => {
GameEvent::KilledObject(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::PlayerExtinguished => {
GameEvent::Extinguished(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::GameOver => GameEvent::GameOver(parse_event(raw.params).with_raw(raw)?),
RawEventType::FinalScore => {
GameEvent::FinalScore(parse_event(raw.params).with_raw(raw)?)
}
RawEventType::LogFileClosed => GameEvent::LogFileClosed,
RawEventType::ObjectDetonated => {
GameEvent::ObjectDetonated(parse_event(raw.params).with_raw(raw)?)
}
_ => {
todo!("{:?} not parsed yet", raw.ty);
}
})
}
}
pub struct ParamIter<'a> { pub struct ParamIter<'a> {
input: &'a str, input: &'a str,
} }

View file

@ -123,3 +123,15 @@ pub struct ExtinguishedEvent<'a> {
pub attacker_position: Option<(i32, i32, i32)>, pub attacker_position: Option<(i32, i32, i32)>,
pub victim_position: Option<(i32, i32, i32)>, pub victim_position: Option<(i32, i32, i32)>,
} }
#[derive(Debug, Event)]
pub struct SayEvent<'a> {
#[event(unnamed)]
pub text: &'a str,
}
#[derive(Debug, Event)]
pub struct SayTeamEvent<'a> {
#[event(unnamed)]
pub text: &'a str,
}

View file

@ -12,7 +12,7 @@ pub use raw_event::{RawEvent, RawEventType};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::num::ParseIntError; use std::num::ParseIntError;
pub use tf_log_parser_derive::Event; pub(crate) use tf_log_parser_derive::{Event, Events};
use thiserror::Error; use thiserror::Error;
mod common; mod common;

View file

@ -65,13 +65,13 @@ impl GlobalData for ChatMessages {
GameEvent::SayTeam(message) => self.0.push(BareChatMessage { GameEvent::SayTeam(message) => self.0.push(BareChatMessage {
time, time,
subject, subject,
message: message.to_string(), message: message.text.to_string(),
chat_type: ChatType::Team, chat_type: ChatType::Team,
}), }),
GameEvent::Say(message) => self.0.push(BareChatMessage { GameEvent::Say(message) => self.0.push(BareChatMessage {
time, time,
subject, subject,
message: message.to_string(), message: message.text.to_string(),
chat_type: ChatType::All, chat_type: ChatType::All,
}), }),
_ => {} _ => {}

View file

@ -52,7 +52,7 @@ impl EventHandler for ClassStatsHandler {
| RawEventType::KillAssist | RawEventType::KillAssist
| RawEventType::Damage | RawEventType::Damage
| RawEventType::Spawned | RawEventType::Spawned
| RawEventType::ChangedRole | RawEventType::RoleChange
| RawEventType::RoundWin | RawEventType::RoundWin
| RawEventType::RoundStart | RawEventType::RoundStart
) )
@ -76,7 +76,7 @@ impl EventHandler for ClassStatsHandler {
GameEvent::RoundWin(_) => { GameEvent::RoundWin(_) => {
self.active = false; self.active = false;
} }
GameEvent::Kill(kill) if self.active => { GameEvent::Killed(kill) if self.active => {
if let Ok(target) = kill.target.id() { if let Ok(target) = kill.target.id() {
let subject_class = self.data.get(&subject).map(|data| data.class); let subject_class = self.data.get(&subject).map(|data| data.class);
let target_data = self.data_mut(target); let target_data = self.data_mut(target);

View file

@ -213,7 +213,7 @@ impl GlobalData for LobbySettingsHandler {
return; return;
} }
if let GameEvent::Say(msg) = event { if let GameEvent::Say(msg) = event {
if let Err(e) = self.try_handle(msg) { if let Err(e) = self.try_handle(msg.text) {
*self = LobbySettingsHandler::Err(e) *self = LobbySettingsHandler::Err(e)
} }
} }

View file

@ -65,11 +65,11 @@ impl PlayerSpecificData for MedicStatsBuilder {
matches!( matches!(
ty, ty,
RawEventType::ChargeDeployed RawEventType::ChargeDeployed
| RawEventType::ChargeEnd | RawEventType::ChargeEnded
| RawEventType::ChargeReady | RawEventType::ChargeReady
| RawEventType::UberAdvantageLost | RawEventType::AdvantageLost
| RawEventType::MedicDeath | RawEventType::MedicDeath
| RawEventType::FirstHealAfterSpawn | RawEventType::FirstHeal
) )
} }

View file

@ -148,7 +148,7 @@ pub enum RawEventType {
#[token(r#"joined"#)] #[token(r#"joined"#)]
Joined, Joined,
#[token(r#"changed role"#)] #[token(r#"changed role"#)]
ChangedRole, RoleChange,
#[token(r#"triggered "shot_fired""#)] #[token(r#"triggered "shot_fired""#)]
ShotFired, ShotFired,
#[token(r#"triggered "shot_hit""#)] #[token(r#"triggered "shot_hit""#)]
@ -158,7 +158,7 @@ pub enum RawEventType {
#[token(r#"triggered "healed""#)] #[token(r#"triggered "healed""#)]
Healed, Healed,
#[token(r#"triggered "first_heal_after_spawn""#)] #[token(r#"triggered "first_heal_after_spawn""#)]
FirstHealAfterSpawn, FirstHeal,
#[token(r#"killed"#)] #[token(r#"killed"#)]
Killed, Killed,
#[token(r#"triggered "kill assist""#)] #[token(r#"triggered "kill assist""#)]
@ -178,15 +178,15 @@ pub enum RawEventType {
#[token(r#"triggered "empty_uber""#)] #[token(r#"triggered "empty_uber""#)]
EmptyUber, EmptyUber,
#[token(r#"triggered "player_builtobject""#)] #[token(r#"triggered "player_builtobject""#)]
PlayerBuiltObject, BuiltObject,
#[token(r#"triggered "player_dropobject""#)] #[token(r#"triggered "player_dropobject""#)]
PlayerCarryObject, PlayerCarryObject,
#[token(r#"triggered "player_carryobject""#)] #[token(r#"triggered "player_carryobject""#)]
PlayerDropObject, PlayerDropObject,
#[token(r#"triggered "killedobject""#)] #[token(r#"triggered "killedobject""#)]
PlayerKilledObject, KilledObject,
#[token(r#"triggered "object_detonated""#)] #[token(r#"triggered "object_detonated""#)]
PlayerExtinguished, Extinguished,
#[token(r#"triggered "player_extinguished""#)] #[token(r#"triggered "player_extinguished""#)]
ObjectDetonated, ObjectDetonated,
#[token(r#"picked up"#)] #[token(r#"picked up"#)]
@ -196,13 +196,13 @@ pub enum RawEventType {
#[token(r#"triggered "medic_death_ex""#)] #[token(r#"triggered "medic_death_ex""#)]
MedicDeathEx, MedicDeathEx,
#[token(r#"triggered "chargeended""#)] #[token(r#"triggered "chargeended""#)]
ChargeEnd, ChargeEnded,
#[token(r#"triggered "chargeready""#)] #[token(r#"triggered "chargeready""#)]
ChargeReady, ChargeReady,
#[token(r#"triggered "chargedeployed""#)] #[token(r#"triggered "chargedeployed""#)]
ChargeDeployed, ChargeDeployed,
#[token(r#"triggered "lost_uber_advantage""#)] #[token(r#"triggered "lost_uber_advantage""#)]
UberAdvantageLost, AdvantageLost,
#[token(r#"triggered "Round_Start""#)] #[token(r#"triggered "Round_Start""#)]
RoundStart, RoundStart,
#[token(r#"triggered "Round_Setup_Begin""#)] #[token(r#"triggered "Round_Setup_Begin""#)]
@ -222,7 +222,7 @@ pub enum RawEventType {
#[token(r#"triggered "Mini_Round_Length""#)] #[token(r#"triggered "Mini_Round_Length""#)]
MiniRoundLength, MiniRoundLength,
#[token(r#"triggered "Round_Overtime""#)] #[token(r#"triggered "Round_Overtime""#)]
RoundOvertime, RoundOverTime,
#[token(r#"triggered "pointcaptured""#)] #[token(r#"triggered "pointcaptured""#)]
PointCaptured, PointCaptured,
#[token(r#"triggered "captureblocked""#)] #[token(r#"triggered "captureblocked""#)]
@ -246,7 +246,7 @@ pub enum RawEventType {
#[token(r#"connected,"#)] #[token(r#"connected,"#)]
Connected, Connected,
#[token(r#"disconnected"#)] #[token(r#"disconnected"#)]
Disconnected, Disconnect,
#[token(r#"STEAM USERID validated"#)] #[token(r#"STEAM USERID validated"#)]
SteamIdValidated, SteamIdValidated,
#[token(r#"entered the game"#)] #[token(r#"entered the game"#)]
@ -258,7 +258,7 @@ pub enum RawEventType {
#[token(r#"The log might have not been uploaded."#)] #[token(r#"The log might have not been uploaded."#)]
NotUploaded, NotUploaded,
#[token(r#"mode started"#)] #[token(r#"mode started"#)]
TournamentStart, TournamentModeStarted,
#[token(r#"triggered "flagevent""#)] #[token(r#"triggered "flagevent""#)]
FlagEvent, FlagEvent,
#[error] #[error]
@ -280,7 +280,7 @@ fn test_parse_raw() {
RawEvent { RawEvent {
date: RawDate("08/06/2018 - 21:13:57"), date: RawDate("08/06/2018 - 21:13:57"),
subject: RawSubject::Player("makxbi<27><[U:1:40364391]><Red>"), subject: RawSubject::Player("makxbi<27><[U:1:40364391]><Red>"),
ty: RawEventType::ChangedRole, ty: RawEventType::RoleChange,
params: r#"to "sniper""#, params: r#"to "sniper""#,
}, },
raw raw