mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 10:14:10 +02:00
derive GameEvent parsing
This commit is contained in:
parent
ab5a061eb0
commit
f5957059d8
10 changed files with 116 additions and 121 deletions
68
derive/src/events.rs
Normal file
68
derive/src/events.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,10 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
mod event;
|
||||
mod events;
|
||||
|
||||
use crate::event::Event;
|
||||
use crate::events::Events;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
|
@ -12,12 +14,20 @@ use syn::{parse_macro_input, DeriveInput, Error, Result};
|
|||
|
||||
/// Derive the `Event` trait for a struct
|
||||
#[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));
|
||||
|
||||
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
|
||||
fn derive_trait<Trait: Derivable>(input: DeriveInput) -> TokenStream {
|
||||
derive_trait_inner::<Trait>(input).unwrap_or_else(|err| err.into_compile_error())
|
||||
|
|
|
|||
105
src/event/mod.rs
105
src/event/mod.rs
|
|
@ -5,7 +5,7 @@ mod player;
|
|||
use crate::event::game::{RoundLengthEvent, RoundWinEvent};
|
||||
use crate::parsing::{skip, skip_matches, split_once};
|
||||
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 medic::*;
|
||||
pub use player::*;
|
||||
|
|
@ -54,15 +54,15 @@ pub struct EventMeta {
|
|||
pub subject: SubjectId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Events)]
|
||||
pub enum GameEvent<'a> {
|
||||
ShotFired(ShotFiredEvent<'a>),
|
||||
ShotHit(ShotHitEvent<'a>),
|
||||
Damage(DamageEvent<'a>),
|
||||
Kill(KillEvent<'a>),
|
||||
Killed(KillEvent<'a>),
|
||||
KillAssist(KillAssistEvent<'a>),
|
||||
Say(&'a str),
|
||||
SayTeam(&'a str),
|
||||
Say(SayEvent<'a>),
|
||||
SayTeam(SayTeamEvent<'a>),
|
||||
Healed(HealedEvent<'a>),
|
||||
ChargeDeployed(ChargeDeployedEvent<'a>),
|
||||
ChargeEnded(ChargeEndedEvent),
|
||||
|
|
@ -101,101 +101,6 @@ pub enum GameEvent<'a> {
|
|||
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> {
|
||||
input: &'a str,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,3 +123,15 @@ pub struct ExtinguishedEvent<'a> {
|
|||
pub attacker_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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub use raw_event::{RawEvent, RawEventType};
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
use std::num::ParseIntError;
|
||||
pub use tf_log_parser_derive::Event;
|
||||
pub(crate) use tf_log_parser_derive::{Event, Events};
|
||||
use thiserror::Error;
|
||||
|
||||
mod common;
|
||||
|
|
|
|||
|
|
@ -65,13 +65,13 @@ impl GlobalData for ChatMessages {
|
|||
GameEvent::SayTeam(message) => self.0.push(BareChatMessage {
|
||||
time,
|
||||
subject,
|
||||
message: message.to_string(),
|
||||
message: message.text.to_string(),
|
||||
chat_type: ChatType::Team,
|
||||
}),
|
||||
GameEvent::Say(message) => self.0.push(BareChatMessage {
|
||||
time,
|
||||
subject,
|
||||
message: message.to_string(),
|
||||
message: message.text.to_string(),
|
||||
chat_type: ChatType::All,
|
||||
}),
|
||||
_ => {}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ impl EventHandler for ClassStatsHandler {
|
|||
| RawEventType::KillAssist
|
||||
| RawEventType::Damage
|
||||
| RawEventType::Spawned
|
||||
| RawEventType::ChangedRole
|
||||
| RawEventType::RoleChange
|
||||
| RawEventType::RoundWin
|
||||
| RawEventType::RoundStart
|
||||
)
|
||||
|
|
@ -76,7 +76,7 @@ impl EventHandler for ClassStatsHandler {
|
|||
GameEvent::RoundWin(_) => {
|
||||
self.active = false;
|
||||
}
|
||||
GameEvent::Kill(kill) if self.active => {
|
||||
GameEvent::Killed(kill) if self.active => {
|
||||
if let Ok(target) = kill.target.id() {
|
||||
let subject_class = self.data.get(&subject).map(|data| data.class);
|
||||
let target_data = self.data_mut(target);
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ impl GlobalData for LobbySettingsHandler {
|
|||
return;
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,11 +65,11 @@ impl PlayerSpecificData for MedicStatsBuilder {
|
|||
matches!(
|
||||
ty,
|
||||
RawEventType::ChargeDeployed
|
||||
| RawEventType::ChargeEnd
|
||||
| RawEventType::ChargeEnded
|
||||
| RawEventType::ChargeReady
|
||||
| RawEventType::UberAdvantageLost
|
||||
| RawEventType::AdvantageLost
|
||||
| RawEventType::MedicDeath
|
||||
| RawEventType::FirstHealAfterSpawn
|
||||
| RawEventType::FirstHeal
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ pub enum RawEventType {
|
|||
#[token(r#"joined"#)]
|
||||
Joined,
|
||||
#[token(r#"changed role"#)]
|
||||
ChangedRole,
|
||||
RoleChange,
|
||||
#[token(r#"triggered "shot_fired""#)]
|
||||
ShotFired,
|
||||
#[token(r#"triggered "shot_hit""#)]
|
||||
|
|
@ -158,7 +158,7 @@ pub enum RawEventType {
|
|||
#[token(r#"triggered "healed""#)]
|
||||
Healed,
|
||||
#[token(r#"triggered "first_heal_after_spawn""#)]
|
||||
FirstHealAfterSpawn,
|
||||
FirstHeal,
|
||||
#[token(r#"killed"#)]
|
||||
Killed,
|
||||
#[token(r#"triggered "kill assist""#)]
|
||||
|
|
@ -178,15 +178,15 @@ pub enum RawEventType {
|
|||
#[token(r#"triggered "empty_uber""#)]
|
||||
EmptyUber,
|
||||
#[token(r#"triggered "player_builtobject""#)]
|
||||
PlayerBuiltObject,
|
||||
BuiltObject,
|
||||
#[token(r#"triggered "player_dropobject""#)]
|
||||
PlayerCarryObject,
|
||||
#[token(r#"triggered "player_carryobject""#)]
|
||||
PlayerDropObject,
|
||||
#[token(r#"triggered "killedobject""#)]
|
||||
PlayerKilledObject,
|
||||
KilledObject,
|
||||
#[token(r#"triggered "object_detonated""#)]
|
||||
PlayerExtinguished,
|
||||
Extinguished,
|
||||
#[token(r#"triggered "player_extinguished""#)]
|
||||
ObjectDetonated,
|
||||
#[token(r#"picked up"#)]
|
||||
|
|
@ -196,13 +196,13 @@ pub enum RawEventType {
|
|||
#[token(r#"triggered "medic_death_ex""#)]
|
||||
MedicDeathEx,
|
||||
#[token(r#"triggered "chargeended""#)]
|
||||
ChargeEnd,
|
||||
ChargeEnded,
|
||||
#[token(r#"triggered "chargeready""#)]
|
||||
ChargeReady,
|
||||
#[token(r#"triggered "chargedeployed""#)]
|
||||
ChargeDeployed,
|
||||
#[token(r#"triggered "lost_uber_advantage""#)]
|
||||
UberAdvantageLost,
|
||||
AdvantageLost,
|
||||
#[token(r#"triggered "Round_Start""#)]
|
||||
RoundStart,
|
||||
#[token(r#"triggered "Round_Setup_Begin""#)]
|
||||
|
|
@ -222,7 +222,7 @@ pub enum RawEventType {
|
|||
#[token(r#"triggered "Mini_Round_Length""#)]
|
||||
MiniRoundLength,
|
||||
#[token(r#"triggered "Round_Overtime""#)]
|
||||
RoundOvertime,
|
||||
RoundOverTime,
|
||||
#[token(r#"triggered "pointcaptured""#)]
|
||||
PointCaptured,
|
||||
#[token(r#"triggered "captureblocked""#)]
|
||||
|
|
@ -246,7 +246,7 @@ pub enum RawEventType {
|
|||
#[token(r#"connected,"#)]
|
||||
Connected,
|
||||
#[token(r#"disconnected"#)]
|
||||
Disconnected,
|
||||
Disconnect,
|
||||
#[token(r#"STEAM USERID validated"#)]
|
||||
SteamIdValidated,
|
||||
#[token(r#"entered the game"#)]
|
||||
|
|
@ -258,7 +258,7 @@ pub enum RawEventType {
|
|||
#[token(r#"The log might have not been uploaded."#)]
|
||||
NotUploaded,
|
||||
#[token(r#"mode started"#)]
|
||||
TournamentStart,
|
||||
TournamentModeStarted,
|
||||
#[token(r#"triggered "flagevent""#)]
|
||||
FlagEvent,
|
||||
#[error]
|
||||
|
|
@ -280,7 +280,7 @@ fn test_parse_raw() {
|
|||
RawEvent {
|
||||
date: RawDate("08/06/2018 - 21:13:57"),
|
||||
subject: RawSubject::Player("makxbi<27><[U:1:40364391]><Red>"),
|
||||
ty: RawEventType::ChangedRole,
|
||||
ty: RawEventType::RoleChange,
|
||||
params: r#"to "sniper""#,
|
||||
},
|
||||
raw
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue