mirror of
https://codeberg.org/icewind/tf-log-parser.git
synced 2026-06-03 10:14:10 +02:00
wip
This commit is contained in:
parent
9da2d2230a
commit
8a88e452ea
13 changed files with 291 additions and 284 deletions
|
|
@ -5,7 +5,7 @@ use tf_log_parser::{parse, RawEvent};
|
||||||
|
|
||||||
pub fn parse_benchmark(c: &mut Criterion) {
|
pub fn parse_benchmark(c: &mut Criterion) {
|
||||||
let input = read_to_string("test_data/log_2892242.log").unwrap();
|
let input = read_to_string("test_data/log_2892242.log").unwrap();
|
||||||
c.bench_function("parse 2892242", |b| b.iter(|| parse(black_box(&input))));
|
c.bench_function("parse log 2892242", |b| b.iter(|| parse(black_box(&input))));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_raw(c: &mut Criterion) {
|
pub fn parse_raw(c: &mut Criterion) {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
use main_error::MainError;
|
use main_error::MainError;
|
||||||
use std::env::args;
|
use std::env::args;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use tf_log_parser::module::{ChatHandler, LobbySettingsHandler};
|
use tf_log_parser::module::{ChatMessages, LobbySettingsHandler};
|
||||||
use tf_log_parser::{handler, parse_with_handler};
|
use tf_log_parser::{handler, parse_with_handler};
|
||||||
|
|
||||||
handler!(Handler {
|
handler!(Handler {
|
||||||
chat: ChatHandler,
|
chat: ChatMessages,
|
||||||
lobby_settings: LobbySettingsHandler
|
lobby_settings: LobbySettingsHandler
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -15,12 +15,15 @@ fn main() -> Result<(), MainError> {
|
||||||
let path = args().skip(1).next().expect("No path provided");
|
let path = args().skip(1).next().expect("No path provided");
|
||||||
let content = fs::read_to_string(path)?;
|
let content = fs::read_to_string(path)?;
|
||||||
|
|
||||||
let HandlerOutput {
|
let (
|
||||||
|
HandlerGlobalOutput {
|
||||||
chat,
|
chat,
|
||||||
lobby_settings,
|
lobby_settings,
|
||||||
} = parse_with_handler::<Handler>(&content)?;
|
},
|
||||||
|
_,
|
||||||
|
) = parse_with_handler::<Handler>(&content)?;
|
||||||
|
|
||||||
if let Ok(Some(settings)) = lobby_settings {
|
if let Some(Ok(settings)) = lobby_settings {
|
||||||
println!("Lobby settings: {:#?}", settings);
|
println!("Lobby settings: {:#?}", settings);
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ use main_error::MainError;
|
||||||
use std::env::args;
|
use std::env::args;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use tf_log_parser::event::DamageEvent;
|
use tf_log_parser::event::DamageEvent;
|
||||||
|
use tf_log_parser::module::GlobalData;
|
||||||
use tf_log_parser::{
|
use tf_log_parser::{
|
||||||
parse_with_handler, EventHandler, GameEvent, RawEventType, SubjectData, SubjectId, SubjectMap,
|
parse_with_handler, EventMeta, GameEvent, RawEventType, SubjectData, SubjectId, SubjectMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HighestDamage {
|
struct HighestDamage {
|
||||||
|
|
@ -18,14 +19,14 @@ struct HighestDamageHandler {
|
||||||
current: Option<(SubjectId, u32)>,
|
current: Option<(SubjectId, u32)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventHandler for HighestDamageHandler {
|
impl GlobalData for HighestDamageHandler {
|
||||||
type GlobalOutput = Option<HighestDamage>;
|
type Output = Option<HighestDamage>;
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
matches!(ty, RawEventType::Damage)
|
matches!(ty, RawEventType::Damage)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(&mut self, _time: u32, subject: SubjectId, event: &GameEvent) {
|
fn handle_event(&mut self, _meta: &EventMeta, subject: SubjectId, event: &GameEvent) {
|
||||||
if let GameEvent::Damage(DamageEvent {
|
if let GameEvent::Damage(DamageEvent {
|
||||||
damage: Some(damage),
|
damage: Some(damage),
|
||||||
..
|
..
|
||||||
|
|
@ -42,9 +43,9 @@ impl EventHandler for HighestDamageHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_global(self, subjects: &SubjectMap) -> Self::GlobalOutput {
|
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||||
self.current.map(|(subject, damage)| {
|
self.current.map(|(subject, damage)| {
|
||||||
let user = match &subjects[subject] {
|
let user = match subjects.subject(subject) {
|
||||||
SubjectData::Player { name, .. } => name.clone(),
|
SubjectData::Player { name, .. } => name.clone(),
|
||||||
_ => {
|
_ => {
|
||||||
panic!("A non player did the most damage?")
|
panic!("A non player did the most damage?")
|
||||||
|
|
@ -59,8 +60,9 @@ fn main() -> Result<(), MainError> {
|
||||||
let path = args().skip(1).next().expect("No path provided");
|
let path = args().skip(1).next().expect("No path provided");
|
||||||
let content = fs::read_to_string(path)?;
|
let content = fs::read_to_string(path)?;
|
||||||
|
|
||||||
let HighestDamage { user, damage } =
|
let HighestDamage { user, damage } = parse_with_handler::<HighestDamageHandler>(&content)?
|
||||||
parse_with_handler::<HighestDamageHandler>(&content)?.expect("nobody did any damage?");
|
.0
|
||||||
|
.expect("nobody did any damage?");
|
||||||
|
|
||||||
println!("highest damage was {} done by {}", user, damage);
|
println!("highest damage was {} done by {}", user, damage);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use serde::ser::SerializeMap;
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use steamid_ng::{AccountType, Instance, SteamID, Universe};
|
use steamid_ng::{AccountType, Instance, SteamID, Universe};
|
||||||
|
|
@ -27,7 +27,7 @@ impl Team {
|
||||||
|
|
||||||
impl Display for Team {
|
impl Display for Team {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
self.as_str().fmt(f)
|
<str as Debug>::fmt(self.as_str(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,6 +153,18 @@ impl<T: Default> Default for ClassMap<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> Debug for ClassMap<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
<[T; 9] as Debug>::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq for ClassMap<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0.eq(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Optimized subject id
|
/// Optimized subject id
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd, Hash)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Ord, PartialOrd, Hash)]
|
||||||
pub enum SubjectId {
|
pub enum SubjectId {
|
||||||
|
|
@ -222,6 +234,18 @@ pub enum SubjectData {
|
||||||
World,
|
World,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SubjectData {
|
||||||
|
pub fn id(&self) -> SubjectId {
|
||||||
|
match self {
|
||||||
|
SubjectData::Player { steam_id, .. } => SubjectId::Player(steam_id.account_id()),
|
||||||
|
SubjectData::Team(team) => SubjectId::Team(*team),
|
||||||
|
SubjectData::System(_) => SubjectId::System,
|
||||||
|
SubjectData::Console => SubjectId::Console,
|
||||||
|
SubjectData::World => SubjectId::World,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum SubjectError {
|
pub enum SubjectError {
|
||||||
#[error("Invalid user id")]
|
#[error("Invalid user id")]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ mod medic;
|
||||||
mod player;
|
mod player;
|
||||||
|
|
||||||
use crate::event::game::{RoundLengthEvent, RoundWinEvent};
|
use crate::event::game::{RoundLengthEvent, RoundWinEvent};
|
||||||
use crate::{RawEvent, RawEventType};
|
use crate::{RawEvent, RawEventType, SubjectId};
|
||||||
pub use game::*;
|
pub use game::*;
|
||||||
pub use medic::*;
|
pub use medic::*;
|
||||||
use nom::bytes::complete::{tag, take_while};
|
use nom::bytes::complete::{tag, take_while};
|
||||||
|
|
@ -46,6 +46,12 @@ impl<'a, T> GameEventErrTrait<T> for IResult<&str, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EventMeta {
|
||||||
|
pub time: u32,
|
||||||
|
pub subject: SubjectId,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GameEvent<'a> {
|
pub enum GameEvent<'a> {
|
||||||
ShotFired(ShotFiredEvent<'a>),
|
ShotFired(ShotFiredEvent<'a>),
|
||||||
|
|
|
||||||
20
src/lib.rs
20
src/lib.rs
|
|
@ -1,10 +1,10 @@
|
||||||
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
|
pub use crate::common::{SteamId3, SubjectData, SubjectError, SubjectId};
|
||||||
use crate::event::GameEventError;
|
use crate::event::GameEventError;
|
||||||
pub use crate::module::EventHandler;
|
pub use crate::module::EventHandler;
|
||||||
use crate::module::{ChatHandler, ClassStatsHandler, HealSpreadHandler, MedicStatsHandler};
|
use crate::module::{ChatMessages, ClassStatsHandler, HealSpread, PlayerHandler};
|
||||||
use crate::subjectmap::SubjectMap;
|
pub use crate::subjectmap::SubjectMap;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
pub use event::GameEvent;
|
pub use event::{EventMeta, GameEvent};
|
||||||
pub use raw_event::{RawEvent, RawEventType};
|
pub use raw_event::{RawEvent, RawEventType};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
@ -72,7 +72,7 @@ pub fn parse_with_handler<Handler: EventHandler>(
|
||||||
|
|
||||||
for event_res in events {
|
for event_res in events {
|
||||||
let raw_event = event_res?;
|
let raw_event = event_res?;
|
||||||
let should_handle = handler.does_handle(raw_event.ty);
|
let should_handle = Handler::does_handle(raw_event.ty);
|
||||||
if should_handle || start_time.is_none() {
|
if should_handle || start_time.is_none() {
|
||||||
let event_time: DateTime<Utc> = (&raw_event.date).try_into().unwrap();
|
let event_time: DateTime<Utc> = (&raw_event.date).try_into().unwrap();
|
||||||
let match_time = match start_time {
|
let match_time = match start_time {
|
||||||
|
|
@ -85,7 +85,11 @@ pub fn parse_with_handler<Handler: EventHandler>(
|
||||||
if should_handle {
|
if should_handle {
|
||||||
let event = GameEvent::parse(&raw_event)?;
|
let event = GameEvent::parse(&raw_event)?;
|
||||||
let (subject, data) = subjects.insert(&raw_event.subject)?;
|
let (subject, data) = subjects.insert(&raw_event.subject)?;
|
||||||
handler.handle(match_time, subject, data, &event);
|
let meta = EventMeta {
|
||||||
|
time: match_time,
|
||||||
|
subject,
|
||||||
|
};
|
||||||
|
handler.handle(&meta, subject, data, &event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -107,8 +111,8 @@ pub fn parse_with_handler<Handler: EventHandler>(
|
||||||
}
|
}
|
||||||
|
|
||||||
handler!(LogHandler {
|
handler!(LogHandler {
|
||||||
chat: ChatHandler,
|
chat: ChatMessages,
|
||||||
heal_spread: HealSpreadHandler,
|
heal_spread: PlayerHandler::<HealSpread>,
|
||||||
medic_stats: MedicStatsHandler,
|
// medic_stats: MedicStatsHandler,
|
||||||
class_stats: ClassStatsHandler,
|
class_stats: ClassStatsHandler,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::common::{SubjectData, SubjectId};
|
use crate::common::{SubjectData, SubjectId};
|
||||||
use crate::event::GameEvent;
|
use crate::event::GameEvent;
|
||||||
use crate::module::EventHandler;
|
use crate::module::GlobalData;
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::SubjectMap;
|
use crate::{EventMeta, SubjectMap};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use steamid_ng::SteamID;
|
use steamid_ng::SteamID;
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ struct BareChatMessage {
|
||||||
pub chat_type: ChatType,
|
pub chat_type: ChatType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, PartialEq)]
|
||||||
pub struct ChatMessage {
|
pub struct ChatMessage {
|
||||||
pub time: u32,
|
pub time: u32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
@ -40,31 +40,24 @@ impl ChatMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize, PartialEq)]
|
||||||
pub enum ChatType {
|
pub enum ChatType {
|
||||||
All,
|
All,
|
||||||
Team,
|
Team,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ChatHandler(Vec<BareChatMessage>);
|
pub struct ChatMessages(Vec<BareChatMessage>);
|
||||||
|
|
||||||
impl EventHandler for ChatHandler {
|
impl GlobalData for ChatMessages {
|
||||||
type GlobalOutput = Vec<ChatMessage>;
|
type Output = Vec<ChatMessage>;
|
||||||
type PerSubjectData = ();
|
|
||||||
type PerSubjectOutput = ();
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
matches!(ty, RawEventType::SayTeam | RawEventType::Say)
|
matches!(ty, RawEventType::SayTeam | RawEventType::Say)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle_event(&mut self, meta: &EventMeta, subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
let time = meta.time;
|
||||||
time: u32,
|
|
||||||
subject: SubjectId,
|
|
||||||
_subject_data: &mut Self::PerSubjectData,
|
|
||||||
event: &GameEvent,
|
|
||||||
) {
|
|
||||||
if !matches!(subject, SubjectId::Player(_)) {
|
if !matches!(subject, SubjectId::Player(_)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -85,18 +78,10 @@ impl EventHandler for ChatHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_global(self, subjects: &SubjectMap) -> Self::GlobalOutput {
|
fn finish(self, subjects: &SubjectMap) -> Self::Output {
|
||||||
self.0
|
self.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|bare| ChatMessage::from_bare(bare, subjects))
|
.map(|bare| ChatMessage::from_bare(bare, subjects))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_per_subject(
|
|
||||||
&self,
|
|
||||||
_subject: &SubjectData,
|
|
||||||
data: Self::PerSubjectData,
|
|
||||||
) -> Self::PerSubjectOutput {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,42 @@
|
||||||
use crate::common::{Class, ClassMap, SteamId3, SubjectId};
|
use crate::common::{Class, ClassMap, SubjectId};
|
||||||
use crate::event::{DamageEvent, GameEvent, RoleChangeEvent, SpawnEvent};
|
use crate::event::{DamageEvent, GameEvent, RoleChangeEvent, SpawnEvent};
|
||||||
use crate::module::EventHandler;
|
use crate::module::EventHandler;
|
||||||
use crate::raw_event::{RawEventType, RawSubject};
|
use crate::raw_event::{RawEventType, RawSubject};
|
||||||
use crate::{SubjectData, SubjectMap};
|
use crate::{EventMeta, SubjectData, SubjectMap};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::ops::{Add, AddAssign};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Default, PartialEq)]
|
#[derive(Debug, Serialize, Default, PartialEq)]
|
||||||
pub struct ClassStat {
|
pub struct ClassStats {
|
||||||
kills: u8,
|
kills: ClassMap<u8>,
|
||||||
deaths: u8,
|
deaths: ClassMap<u8>,
|
||||||
assists: u8,
|
assists: ClassMap<u8>,
|
||||||
damage: u16,
|
damage: ClassMap<u16>,
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for ClassStat {
|
|
||||||
type Output = ClassStat;
|
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
|
||||||
ClassStat {
|
|
||||||
kills: self.kills + rhs.kills,
|
|
||||||
deaths: self.deaths + rhs.deaths,
|
|
||||||
assists: self.assists + rhs.assists,
|
|
||||||
damage: self.damage + rhs.damage,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign for ClassStat {
|
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
|
||||||
self.kills += rhs.kills;
|
|
||||||
self.deaths += rhs.deaths;
|
|
||||||
self.assists += rhs.assists;
|
|
||||||
self.damage += rhs.damage;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ClassStatsHandler {
|
pub struct ClassStatsHandler {
|
||||||
active: bool,
|
active: bool,
|
||||||
classes: BTreeMap<SubjectId, Class>,
|
classes: BTreeMap<SubjectId, Class>,
|
||||||
stats: BTreeMap<SteamId3, ClassMap<ClassStat>>,
|
deaths: BTreeMap<SubjectId, ClassMap<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClassStatsHandler {
|
impl ClassStatsHandler {
|
||||||
fn handle_stats(&mut self, subject: SubjectId, target: &RawSubject, stats: ClassStat) {
|
fn get_class(&self, subject: &RawSubject) -> Option<Class> {
|
||||||
if let Ok(target) = target.id() {
|
subject
|
||||||
self.handle_stats_id(subject, target, stats)
|
.id()
|
||||||
}
|
.ok()
|
||||||
}
|
.and_then(|id| self.classes.get(&id))
|
||||||
|
.copied()
|
||||||
fn handle_stats_id(&mut self, subject: SubjectId, target: SubjectId, stats: ClassStat) {
|
|
||||||
let subject = if let Some(steam_id) = subject.steam_id() {
|
|
||||||
steam_id
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(target_class) = self.classes.get(&target) {
|
|
||||||
self.stats.entry(SteamId3(subject)).or_default()[*target_class] += stats;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventHandler for ClassStatsHandler {
|
impl EventHandler for ClassStatsHandler {
|
||||||
type GlobalOutput = ();
|
type GlobalOutput = ();
|
||||||
type PerSubjectData = ClassMap<ClassStat>;
|
type PerSubjectData = ClassStats;
|
||||||
type PerSubjectOutput = ClassMap<ClassStat>;
|
type PerSubjectOutput = ClassStats;
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
ty,
|
ty,
|
||||||
RawEventType::Killed
|
RawEventType::Killed
|
||||||
|
|
@ -84,9 +51,9 @@ impl EventHandler for ClassStatsHandler {
|
||||||
|
|
||||||
fn handle(
|
fn handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
_time: u32,
|
_meta: &EventMeta,
|
||||||
subject: SubjectId,
|
subject: SubjectId,
|
||||||
_subject_data: &mut Self::PerSubjectData,
|
subject_data: &mut Self::PerSubjectData,
|
||||||
event: &GameEvent,
|
event: &GameEvent,
|
||||||
) {
|
) {
|
||||||
match event {
|
match event {
|
||||||
|
|
@ -101,48 +68,28 @@ impl EventHandler for ClassStatsHandler {
|
||||||
self.active = false;
|
self.active = false;
|
||||||
}
|
}
|
||||||
GameEvent::Kill(kill) if self.active => {
|
GameEvent::Kill(kill) if self.active => {
|
||||||
self.handle_stats(
|
if let Some(target_class) = self.get_class(&kill.target) {
|
||||||
subject,
|
subject_data.kills[target_class] += 1;
|
||||||
&kill.target,
|
}
|
||||||
ClassStat {
|
|
||||||
kills: 1,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if let Ok(target) = kill.target.id() {
|
if let Ok(target) = kill.target.id() {
|
||||||
self.handle_stats_id(
|
if let Some(subject_class) = self.classes.get(&subject) {
|
||||||
target,
|
self.deaths.entry(target).or_default()[*subject_class] += 1;
|
||||||
subject,
|
|
||||||
ClassStat {
|
|
||||||
deaths: 1,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameEvent::KillAssist(kill) if self.active => {
|
}
|
||||||
self.handle_stats(
|
GameEvent::KillAssist(assist) if self.active => {
|
||||||
subject,
|
if let Some(target_class) = self.get_class(&assist.target) {
|
||||||
&kill.target,
|
subject_data.assists[target_class] += 1;
|
||||||
ClassStat {
|
}
|
||||||
assists: 1,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
GameEvent::Damage(DamageEvent {
|
GameEvent::Damage(DamageEvent {
|
||||||
damage: Some(damage),
|
damage: Some(damage),
|
||||||
target,
|
target,
|
||||||
..
|
..
|
||||||
}) if self.active => {
|
}) if self.active => {
|
||||||
self.handle_stats(
|
if let Some(target_class) = self.get_class(target) {
|
||||||
subject,
|
subject_data.damage[target_class] += damage.get() as u16;
|
||||||
target,
|
}
|
||||||
ClassStat {
|
|
||||||
damage: damage.get() as u16,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -153,10 +100,11 @@ impl EventHandler for ClassStatsHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_per_subject(
|
fn finish_per_subject(
|
||||||
&self,
|
&mut self,
|
||||||
_subject: &SubjectData,
|
subject: &SubjectData,
|
||||||
data: Self::PerSubjectData,
|
mut data: Self::PerSubjectData,
|
||||||
) -> Self::PerSubjectOutput {
|
) -> Self::PerSubjectOutput {
|
||||||
|
data.deaths = self.deaths.remove(&subject.id()).unwrap_or_default();
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,34 @@
|
||||||
use crate::common::{SteamId3, SubjectId};
|
use crate::common::{SteamId3, SubjectId};
|
||||||
use crate::event::GameEvent;
|
use crate::event::GameEvent;
|
||||||
use crate::module::EventHandler;
|
use crate::module::PlayerSpecificData;
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::{SubjectData, SubjectMap};
|
use crate::EventMeta;
|
||||||
|
use serde::Serialize;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Serialize, PartialEq)]
|
||||||
pub struct HealSpreadHandler;
|
pub struct HealSpread(BTreeMap<SteamId3, u32>);
|
||||||
|
|
||||||
impl EventHandler for HealSpreadHandler {
|
impl PlayerSpecificData for HealSpread {
|
||||||
type GlobalOutput = ();
|
type Output = HealSpread;
|
||||||
type PerSubjectData = BTreeMap<SteamId3, u32>;
|
|
||||||
type PerSubjectOutput = BTreeMap<SteamId3, u32>;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
matches!(ty, RawEventType::Healed)
|
matches!(ty, RawEventType::Healed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle_event(&mut self, _meta: &EventMeta, _subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
|
||||||
_time: u32,
|
|
||||||
_subject: SubjectId,
|
|
||||||
subject_data: &mut Self::PerSubjectData,
|
|
||||||
event: &GameEvent,
|
|
||||||
) {
|
|
||||||
if let GameEvent::Healed(heal_event) = event {
|
if let GameEvent::Healed(heal_event) = event {
|
||||||
if let Ok(target_subject) = SubjectId::try_from(&heal_event.target) {
|
if let Ok(target_subject) = SubjectId::try_from(&heal_event.target) {
|
||||||
if let Some(target_steam_id) = target_subject.steam_id() {
|
if let Some(target_steam_id) = target_subject.steam_id() {
|
||||||
let healed = subject_data.entry(SteamId3(target_steam_id)).or_default();
|
let healed = self.0.entry(SteamId3(target_steam_id)).or_default();
|
||||||
*healed += heal_event.amount
|
*healed += heal_event.amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_global(self, _subjects: &SubjectMap) -> Self::GlobalOutput {
|
fn finish(self) -> Self::Output {
|
||||||
()
|
self
|
||||||
}
|
|
||||||
|
|
||||||
fn finish_per_subject(
|
|
||||||
&self,
|
|
||||||
_subject: &SubjectData,
|
|
||||||
data: Self::PerSubjectData,
|
|
||||||
) -> Self::PerSubjectOutput {
|
|
||||||
data
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::common::SubjectId;
|
use crate::common::SubjectId;
|
||||||
use crate::event::GameEvent;
|
use crate::event::GameEvent;
|
||||||
use crate::module::EventHandler;
|
use crate::module::GlobalData;
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::{SubjectData, SubjectMap};
|
use crate::{EventMeta, SubjectMap};
|
||||||
use chrono::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc};
|
use chrono::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc};
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
@ -10,7 +10,7 @@ use std::str::{FromStr, ParseBoolError};
|
||||||
use steamid_ng::SteamID;
|
use steamid_ng::SteamID;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, PartialEq)]
|
||||||
pub enum GameType {
|
pub enum GameType {
|
||||||
Sixes,
|
Sixes,
|
||||||
Highlander,
|
Highlander,
|
||||||
|
|
@ -28,7 +28,7 @@ impl FromStr for GameType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, PartialEq)]
|
||||||
pub enum Location {
|
pub enum Location {
|
||||||
Europe,
|
Europe,
|
||||||
NorthAmerica,
|
NorthAmerica,
|
||||||
|
|
@ -46,7 +46,7 @@ impl FromStr for Location {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Serialize)]
|
#[derive(Debug, Default, Serialize, PartialEq)]
|
||||||
pub struct LobbyLeader {
|
pub struct LobbyLeader {
|
||||||
name: String,
|
name: String,
|
||||||
steam_id: SteamID,
|
steam_id: SteamID,
|
||||||
|
|
@ -71,7 +71,7 @@ impl FromStr for LobbyLeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, PartialEq)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
id: u32,
|
id: u32,
|
||||||
leader: LobbyLeader,
|
leader: LobbyLeader,
|
||||||
|
|
@ -108,7 +108,7 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error, PartialEq)]
|
||||||
pub enum LobbySettingsError {
|
pub enum LobbySettingsError {
|
||||||
#[error("Malformed lobby id: {0}")]
|
#[error("Malformed lobby id: {0}")]
|
||||||
InvalidLobbyId(String),
|
InvalidLobbyId(String),
|
||||||
|
|
@ -196,22 +196,14 @@ impl LobbySettingsHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventHandler for LobbySettingsHandler {
|
impl GlobalData for LobbySettingsHandler {
|
||||||
type GlobalOutput = Result<Option<Settings>, LobbySettingsError>;
|
type Output = Option<Result<Settings, LobbySettingsError>>;
|
||||||
type PerSubjectData = ();
|
|
||||||
type PerSubjectOutput = ();
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
matches!(ty, RawEventType::Say)
|
matches!(ty, RawEventType::Say)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle_event(&mut self, _meta: &EventMeta, subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
|
||||||
_time: u32,
|
|
||||||
subject: SubjectId,
|
|
||||||
_subject_data: &mut Self::PerSubjectData,
|
|
||||||
event: &GameEvent,
|
|
||||||
) {
|
|
||||||
if !matches!(subject, SubjectId::Console) {
|
if !matches!(subject, SubjectId::Console) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -222,21 +214,13 @@ impl EventHandler for LobbySettingsHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_global(self, _subjects: &SubjectMap) -> Self::GlobalOutput {
|
fn finish(self, _subjects: &SubjectMap) -> Self::Output {
|
||||||
match self {
|
match self {
|
||||||
LobbySettingsHandler::NotAvailable => Ok(None),
|
LobbySettingsHandler::NotAvailable => None,
|
||||||
LobbySettingsHandler::Active(settings) => Ok(Some(settings)),
|
LobbySettingsHandler::Active(settings) => Some(Ok(settings)),
|
||||||
LobbySettingsHandler::Err(e) => Err(e),
|
LobbySettingsHandler::Err(e) => Some(Err(e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_per_subject(
|
|
||||||
&self,
|
|
||||||
_subject: &SubjectData,
|
|
||||||
data: Self::PerSubjectData,
|
|
||||||
) -> Self::PerSubjectOutput {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_timezone(date: &str) -> Result<FixedOffset, LobbySettingsError> {
|
fn get_timezone(date: &str) -> Result<FixedOffset, LobbySettingsError> {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use crate::common::SubjectId;
|
use crate::common::SubjectId;
|
||||||
use crate::event::GameEvent;
|
use crate::event::GameEvent;
|
||||||
use crate::module::EventHandler;
|
use crate::module::PlayerSpecificData;
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::{SubjectData, SubjectMap};
|
use crate::EventMeta;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MedicStatsBuilder {
|
pub struct MedicStatsBuilder {
|
||||||
|
|
@ -24,7 +23,7 @@ pub struct MedicStatsBuilder {
|
||||||
drops: u32,
|
drops: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize, Default, PartialEq)]
|
||||||
pub struct MedicStats {
|
pub struct MedicStats {
|
||||||
advantages_lost: u32,
|
advantages_lost: u32,
|
||||||
biggest_advantage_lost: f32,
|
biggest_advantage_lost: f32,
|
||||||
|
|
@ -40,6 +39,9 @@ pub struct MedicStats {
|
||||||
|
|
||||||
impl From<MedicStatsBuilder> for MedicStats {
|
impl From<MedicStatsBuilder> for MedicStats {
|
||||||
fn from(builder: MedicStatsBuilder) -> Self {
|
fn from(builder: MedicStatsBuilder) -> Self {
|
||||||
|
if builder.start_healing_count == 0 {
|
||||||
|
return Self::default();
|
||||||
|
}
|
||||||
MedicStats {
|
MedicStats {
|
||||||
advantages_lost: builder.advantages_lost,
|
advantages_lost: builder.advantages_lost,
|
||||||
biggest_advantage_lost: builder.biggest_advantage_lost,
|
biggest_advantage_lost: builder.biggest_advantage_lost,
|
||||||
|
|
@ -56,19 +58,10 @@ impl From<MedicStatsBuilder> for MedicStats {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
impl PlayerSpecificData for MedicStatsBuilder {
|
||||||
#[error("Invalid charge event: {0}")]
|
type Output = MedicStats;
|
||||||
pub struct InvalidMedicEvent(String);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
pub struct MedicStatsHandler;
|
|
||||||
|
|
||||||
impl EventHandler for MedicStatsHandler {
|
|
||||||
type GlobalOutput = ();
|
|
||||||
type PerSubjectData = MedicStatsBuilder;
|
|
||||||
type PerSubjectOutput = MedicStats;
|
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
|
||||||
matches!(
|
matches!(
|
||||||
ty,
|
ty,
|
||||||
RawEventType::ChargeDeployed
|
RawEventType::ChargeDeployed
|
||||||
|
|
@ -80,65 +73,51 @@ impl EventHandler for MedicStatsHandler {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle_event(&mut self, meta: &EventMeta, _subject: SubjectId, event: &GameEvent) {
|
||||||
&mut self,
|
|
||||||
time: u32,
|
|
||||||
_subject: SubjectId,
|
|
||||||
subject_data: &mut Self::PerSubjectData,
|
|
||||||
event: &GameEvent,
|
|
||||||
) {
|
|
||||||
match event {
|
match event {
|
||||||
GameEvent::ChargeEnded(end) => {
|
GameEvent::ChargeEnded(end) => {
|
||||||
subject_data.total_uber_length += end.duration.unwrap_or_default();
|
self.total_uber_length += end.duration.unwrap_or_default();
|
||||||
subject_data.last_uber_end = time;
|
self.last_uber_end = meta.time;
|
||||||
}
|
}
|
||||||
GameEvent::ChargeDeployed(_deployed) => {
|
GameEvent::ChargeDeployed(_deployed) => {
|
||||||
subject_data.charge_count += 1;
|
self.charge_count += 1;
|
||||||
}
|
}
|
||||||
GameEvent::AdvantageLost(lost) => {
|
GameEvent::AdvantageLost(lost) => {
|
||||||
subject_data.advantages_lost += 1;
|
self.advantages_lost += 1;
|
||||||
let time = lost.time.unwrap_or_default();
|
let time = lost.time.unwrap_or_default();
|
||||||
if time > subject_data.biggest_advantage_lost {
|
if time > self.biggest_advantage_lost {
|
||||||
subject_data.biggest_advantage_lost = time;
|
self.biggest_advantage_lost = time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameEvent::FirstHeal(first) => {
|
GameEvent::FirstHeal(first) => {
|
||||||
subject_data.total_time_before_healing += first.time.unwrap_or_default();
|
self.total_time_before_healing += first.time.unwrap_or_default();
|
||||||
subject_data.start_healing_count += 1;
|
self.start_healing_count += 1;
|
||||||
subject_data.last_build_start = time;
|
self.last_build_start = meta.time;
|
||||||
}
|
}
|
||||||
GameEvent::ChargeReady => {
|
GameEvent::ChargeReady => {
|
||||||
if subject_data.last_build_start > 0 {
|
if self.last_build_start > 0 {
|
||||||
let build_time = time - subject_data.last_build_start;
|
let build_time = meta.time - self.last_build_start;
|
||||||
subject_data.last_build_start = 0;
|
self.last_build_start = 0;
|
||||||
subject_data.total_time_to_build += build_time;
|
self.total_time_to_build += build_time;
|
||||||
subject_data.uber_build_count += 1;
|
self.uber_build_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameEvent::MedicDeath(death) => {
|
GameEvent::MedicDeath(death) => {
|
||||||
let charge = death.charge.unwrap_or_default();
|
let charge = death.charge.unwrap_or_default();
|
||||||
if charge >= 95 && charge < 100 {
|
if charge >= 95 && charge < 100 {
|
||||||
subject_data.near_full_charge_death += 1;
|
self.near_full_charge_death += 1;
|
||||||
} else if charge >= 100 {
|
} else if charge >= 100 {
|
||||||
subject_data.drops += 1;
|
self.drops += 1;
|
||||||
}
|
}
|
||||||
if time - subject_data.last_uber_end <= 10 {
|
if meta.time - self.last_uber_end <= 10 {
|
||||||
subject_data.deaths_after_uber += 1;
|
self.deaths_after_uber += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_global(self, _subjects: &SubjectMap) -> Self::GlobalOutput {
|
fn finish(self) -> Self::Output {
|
||||||
()
|
self.into()
|
||||||
}
|
|
||||||
|
|
||||||
fn finish_per_subject(
|
|
||||||
&self,
|
|
||||||
_subject: &SubjectData,
|
|
||||||
data: Self::PerSubjectData,
|
|
||||||
) -> Self::PerSubjectOutput {
|
|
||||||
data.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
use crate::common::SubjectId;
|
use crate::common::SubjectId;
|
||||||
use crate::event::GameEvent;
|
use crate::event::{EventMeta, GameEvent};
|
||||||
use crate::raw_event::RawEventType;
|
use crate::raw_event::RawEventType;
|
||||||
use crate::{SubjectData, SubjectMap};
|
use crate::{SubjectData, SubjectMap};
|
||||||
pub use chat::{ChatHandler, ChatMessage, ChatType};
|
pub use chat::{ChatMessage, ChatMessages, ChatType};
|
||||||
pub use classstats::{ClassStat, ClassStatsHandler};
|
pub use classstats::{ClassStats, ClassStatsHandler};
|
||||||
pub use healspread::HealSpreadHandler;
|
pub use healspread::HealSpread;
|
||||||
pub use lobbysettings::{
|
pub use lobbysettings::{
|
||||||
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
LobbySettingsError, LobbySettingsHandler, Location, Settings as LobbySettings,
|
||||||
};
|
};
|
||||||
pub use medicstats::{MedicStats, MedicStatsHandler};
|
pub use medicstats::MedicStats;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
mod chat;
|
mod chat;
|
||||||
mod classstats;
|
mod classstats;
|
||||||
|
|
@ -21,11 +23,11 @@ pub trait EventHandler: Default {
|
||||||
type PerSubjectData: Default;
|
type PerSubjectData: Default;
|
||||||
type PerSubjectOutput;
|
type PerSubjectOutput;
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool;
|
fn does_handle(ty: RawEventType) -> bool;
|
||||||
|
|
||||||
fn handle(
|
fn handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
time: u32,
|
meta: &EventMeta,
|
||||||
subject: SubjectId,
|
subject: SubjectId,
|
||||||
subject_data: &mut Self::PerSubjectData,
|
subject_data: &mut Self::PerSubjectData,
|
||||||
event: &GameEvent,
|
event: &GameEvent,
|
||||||
|
|
@ -34,7 +36,7 @@ pub trait EventHandler: Default {
|
||||||
fn finish_global(self, subjects: &SubjectMap) -> Self::GlobalOutput;
|
fn finish_global(self, subjects: &SubjectMap) -> Self::GlobalOutput;
|
||||||
|
|
||||||
fn finish_per_subject(
|
fn finish_per_subject(
|
||||||
&self,
|
&mut self,
|
||||||
subject: &SubjectData,
|
subject: &SubjectData,
|
||||||
data: Self::PerSubjectData,
|
data: Self::PerSubjectData,
|
||||||
) -> Self::PerSubjectOutput;
|
) -> Self::PerSubjectOutput;
|
||||||
|
|
@ -51,19 +53,19 @@ impl<Head: EventHandler, Tail: EventHandler> EventHandler for HandlerStack<Head,
|
||||||
type PerSubjectData = (Head::PerSubjectData, Tail::PerSubjectData);
|
type PerSubjectData = (Head::PerSubjectData, Tail::PerSubjectData);
|
||||||
type PerSubjectOutput = (Head::PerSubjectOutput, Tail::PerSubjectOutput);
|
type PerSubjectOutput = (Head::PerSubjectOutput, Tail::PerSubjectOutput);
|
||||||
|
|
||||||
fn does_handle(&self, ty: RawEventType) -> bool {
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
self.head.does_handle(ty) || self.tail.does_handle(ty)
|
Head::does_handle(ty) || Tail::does_handle(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(
|
fn handle(
|
||||||
&mut self,
|
&mut self,
|
||||||
time: u32,
|
meta: &EventMeta,
|
||||||
subject: SubjectId,
|
subject: SubjectId,
|
||||||
subject_data: &mut Self::PerSubjectData,
|
subject_data: &mut Self::PerSubjectData,
|
||||||
event: &GameEvent,
|
event: &GameEvent,
|
||||||
) {
|
) {
|
||||||
self.head.handle(time, subject, &mut subject_data.0, event);
|
self.head.handle(meta, subject, &mut subject_data.0, event);
|
||||||
self.tail.handle(time, subject, &mut subject_data.1, event);
|
self.tail.handle(meta, subject, &mut subject_data.1, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_global(self, subjects: &SubjectMap) -> Self::GlobalOutput {
|
fn finish_global(self, subjects: &SubjectMap) -> Self::GlobalOutput {
|
||||||
|
|
@ -74,7 +76,7 @@ impl<Head: EventHandler, Tail: EventHandler> EventHandler for HandlerStack<Head,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_per_subject(
|
fn finish_per_subject(
|
||||||
&self,
|
&mut self,
|
||||||
subject: &SubjectData,
|
subject: &SubjectData,
|
||||||
data: Self::PerSubjectData,
|
data: Self::PerSubjectData,
|
||||||
) -> Self::PerSubjectOutput {
|
) -> Self::PerSubjectOutput {
|
||||||
|
|
@ -85,16 +87,6 @@ impl<Head: EventHandler, Tail: EventHandler> EventHandler for HandlerStack<Head,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! replace_expr {
|
|
||||||
($_t:tt $sub:expr) => {
|
|
||||||
$sub
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! count_tts {
|
|
||||||
($($tts:tt)*) => {0usize $(+ replace_expr!($tts 1usize))*};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! handler {
|
macro_rules! handler {
|
||||||
($name:ident {$($child:ident: $ty:path),*}) => {
|
($name:ident {$($child:ident: $ty:path),*}) => {
|
||||||
|
|
@ -117,8 +109,12 @@ macro_rules! handler {
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
use serde::ser::SerializeStruct;
|
use serde::ser::SerializeStruct;
|
||||||
let mut state = serializer.serialize_struct(concat!(stringify!($name), "output"), count_tts!($($child)*))?;
|
let mut state = serializer.serialize_struct(concat!(stringify!($name), "output"), 0)?;
|
||||||
$(state.serialize_field(stringify!($child), &self.$child)?;)*
|
$(
|
||||||
|
if self.$child != <<$ty as $crate::EventHandler>::GlobalOutput>::default() {
|
||||||
|
state.serialize_field(stringify!($child), &self.$child)?;
|
||||||
|
}
|
||||||
|
)*
|
||||||
state.end()
|
state.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -145,8 +141,12 @@ macro_rules! handler {
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
use serde::ser::SerializeStruct;
|
use serde::ser::SerializeStruct;
|
||||||
let mut state = serializer.serialize_struct(concat!(stringify!($name), "output"), count_tts!($($child)*))?;
|
let mut state = serializer.serialize_struct(concat!(stringify!($name), "output"), 0)?;
|
||||||
$(state.serialize_field(stringify!($child), &self.$child)?;)*
|
$(
|
||||||
|
if self.$child != <<$ty as $crate::EventHandler>::PerSubjectOutput>::default() {
|
||||||
|
state.serialize_field(stringify!($child), &self.$child)?;
|
||||||
|
}
|
||||||
|
)*
|
||||||
state.end()
|
state.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -157,16 +157,16 @@ macro_rules! handler {
|
||||||
type PerSubjectData = [<$name PerSubjectData>];
|
type PerSubjectData = [<$name PerSubjectData>];
|
||||||
type PerSubjectOutput = [<$name PerSubjectOutput>];
|
type PerSubjectOutput = [<$name PerSubjectOutput>];
|
||||||
|
|
||||||
fn does_handle(&self, ty: $crate::RawEventType) -> bool {
|
fn does_handle(ty: $crate::RawEventType) -> bool {
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use $crate::EventHandler;
|
use $crate::EventHandler;
|
||||||
$(self.$child.does_handle(ty))||*
|
$($ty::does_handle(ty))||*
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(&mut self, time: u32, subject: $crate::SubjectId,subject_data:&mut Self::PerSubjectData, event: &$crate::GameEvent) {
|
fn handle(&mut self, meta: &$crate::EventMeta, subject: $crate::SubjectId,subject_data:&mut Self::PerSubjectData, event: &$crate::GameEvent) {
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use $crate::EventHandler;
|
use $crate::EventHandler;
|
||||||
$(self.$child.handle(time, subject, &mut subject_data.$child, event);)*
|
$(self.$child.handle(meta, subject, &mut subject_data.$child, event);)*
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_global(self, subjects: &$crate::SubjectMap) -> Self::GlobalOutput {
|
fn finish_global(self, subjects: &$crate::SubjectMap) -> Self::GlobalOutput {
|
||||||
|
|
@ -177,7 +177,7 @@ macro_rules! handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_per_subject(&self, subject: &SubjectData, data: Self::PerSubjectData) -> Self::PerSubjectOutput {
|
fn finish_per_subject(&mut self, subject: &$crate::SubjectData, data: Self::PerSubjectData) -> Self::PerSubjectOutput {
|
||||||
Self::PerSubjectOutput {
|
Self::PerSubjectOutput {
|
||||||
$($child: self.$child.finish_per_subject(subject, data.$child),)*
|
$($child: self.$child.finish_per_subject(subject, data.$child),)*
|
||||||
}
|
}
|
||||||
|
|
@ -186,3 +186,86 @@ macro_rules! handler {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait GlobalData: Default {
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn does_handle(ty: RawEventType) -> bool;
|
||||||
|
fn handle_event(&mut self, meta: &EventMeta, subject: SubjectId, event: &GameEvent);
|
||||||
|
fn finish(self, subjects: &SubjectMap) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: GlobalData> EventHandler for T {
|
||||||
|
type GlobalOutput = T::Output;
|
||||||
|
type PerSubjectData = ();
|
||||||
|
type PerSubjectOutput = ();
|
||||||
|
|
||||||
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
|
T::does_handle(ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(
|
||||||
|
&mut self,
|
||||||
|
meta: &EventMeta,
|
||||||
|
subject: SubjectId,
|
||||||
|
_subject_data: &mut Self::PerSubjectData,
|
||||||
|
event: &GameEvent,
|
||||||
|
) {
|
||||||
|
self.handle_event(meta, subject, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_global(self, subjects: &SubjectMap) -> Self::GlobalOutput {
|
||||||
|
self.finish(subjects)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_per_subject(
|
||||||
|
&mut self,
|
||||||
|
_subject: &SubjectData,
|
||||||
|
_data: Self::PerSubjectData,
|
||||||
|
) -> Self::PerSubjectOutput {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PlayerSpecificData: Default {
|
||||||
|
type Output: Serialize;
|
||||||
|
|
||||||
|
fn does_handle(ty: RawEventType) -> bool;
|
||||||
|
fn handle_event(&mut self, meta: &EventMeta, subject: SubjectId, event: &GameEvent);
|
||||||
|
fn finish(self) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PlayerHandler<T: PlayerSpecificData>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: PlayerSpecificData + Default> EventHandler for PlayerHandler<T> {
|
||||||
|
type GlobalOutput = ();
|
||||||
|
type PerSubjectData = T;
|
||||||
|
type PerSubjectOutput = T::Output;
|
||||||
|
|
||||||
|
fn does_handle(ty: RawEventType) -> bool {
|
||||||
|
T::does_handle(ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle(
|
||||||
|
&mut self,
|
||||||
|
meta: &EventMeta,
|
||||||
|
subject: SubjectId,
|
||||||
|
subject_data: &mut Self::PerSubjectData,
|
||||||
|
event: &GameEvent,
|
||||||
|
) {
|
||||||
|
subject_data.handle_event(meta, subject, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_global(self, _subjects: &SubjectMap) -> Self::GlobalOutput {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_per_subject(
|
||||||
|
&mut self,
|
||||||
|
_subject: &SubjectData,
|
||||||
|
data: Self::PerSubjectData,
|
||||||
|
) -> Self::PerSubjectOutput {
|
||||||
|
data.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ impl<T: Default> SubjectMap<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SubjectMap<T> {
|
impl<T> SubjectMap<T> {
|
||||||
|
pub fn subject(&self, id: SubjectId) -> &SubjectData {
|
||||||
|
&self[id].0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_just_subjects(&self) -> SubjectMap<()> {
|
pub fn to_just_subjects(&self) -> SubjectMap<()> {
|
||||||
SubjectMap(
|
SubjectMap(
|
||||||
self.0
|
self.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue