aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/event.rs67
-rw-r--r--src/statistics/trackers.rs86
2 files changed, 153 insertions, 0 deletions
diff --git a/src/event.rs b/src/event.rs
index 02f4686..f87688e 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -104,6 +104,73 @@ pub enum EventKind {
},
}
+impl EventKind {
+ /// Returns the source agent address for this event, if one exists.
+ pub fn source_agent_addr(&self) -> Option<u64> {
+ use EventKind::*;
+ match *self {
+ EnterCombat { agent_addr, .. } => return Some(agent_addr),
+ ExitCombat { agent_addr } => return Some(agent_addr),
+ ChangeUp { agent_addr } => return Some(agent_addr),
+ ChangeDown { agent_addr } => return Some(agent_addr),
+ ChangeDead { agent_addr } => return Some(agent_addr),
+ Spawn { agent_addr } => return Some(agent_addr),
+ Despawn { agent_addr } => return Some(agent_addr),
+ HealthUpdate { agent_addr, .. } => return Some(agent_addr),
+ WeaponSwap { agent_addr, .. } => return Some(agent_addr),
+ MaxHealthUpdate { agent_addr, .. } => return Some(agent_addr),
+ PointOfView { agent_addr } => return Some(agent_addr),
+ SkillUse {
+ source_agent_addr, ..
+ } => return Some(source_agent_addr),
+ ConditionTick {
+ source_agent_addr, ..
+ } => return Some(source_agent_addr),
+ InvulnTick {
+ source_agent_addr, ..
+ } => return Some(source_agent_addr),
+ Physical {
+ source_agent_addr, ..
+ } => return Some(source_agent_addr),
+ BuffApplication {
+ source_agent_addr, ..
+ } => return Some(source_agent_addr),
+ BuffRemove {
+ source_agent_addr, ..
+ } => return Some(source_agent_addr),
+ _ => return None,
+ }
+ }
+
+ /// Returns the destination agent address for this event, if one exists.
+ pub fn destination_agent_addr(&self) -> Option<u64> {
+ use EventKind::*;
+ match *self {
+ ConditionTick {
+ destination_agent_addr,
+ ..
+ } => return Some(destination_agent_addr),
+ InvulnTick {
+ destination_agent_addr,
+ ..
+ } => return Some(destination_agent_addr),
+ Physical {
+ destination_agent_addr,
+ ..
+ } => return Some(destination_agent_addr),
+ BuffApplication {
+ destination_agent_addr,
+ ..
+ } => return Some(destination_agent_addr),
+ BuffRemove {
+ destination_agent_addr,
+ ..
+ } => return Some(destination_agent_addr),
+ _ => return None,
+ }
+ }
+}
+
/// A higher-level representation of a combat event.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Event {
diff --git a/src/statistics/trackers.rs b/src/statistics/trackers.rs
index bcf5e90..8381a43 100644
--- a/src/statistics/trackers.rs
+++ b/src/statistics/trackers.rs
@@ -13,6 +13,7 @@
//! trackers on the same log.
use std::collections::HashMap;
use std::error::Error;
+use std::hash::Hash;
use super::super::{Event, EventKind, Log};
use super::boon::BoonQueue;
@@ -76,6 +77,91 @@ impl<S, E: Error + 'static, T: Tracker<Stat = S, Error = E>> RunnableTracker for
}
}
+/// A trait that allows a tracker to be multiplexed.
+///
+/// Basically, this is a factory that allows new trackers to be created. Each
+/// tracker can be given a key that it should listen on, which is expressed by
+/// the `K` type parameter and the `key` parameter to `create`.
+pub trait Multiplexable<K> {
+ /// The type of tracker that this multiplexable/factory creates.
+ type T: Tracker;
+
+ /// Create a new tracker, listening for the given key.
+ fn create(&mut self, key: K) -> Self::T;
+}
+
+/// A helper that wraps (decorates) another tracker and separates the results
+/// based on the given criteria.
+///
+/// Instead of outputting a single statistic, it outputs a `HashMap`, mapping
+/// the criteria to its own tracker.
+///
+/// This can be used for example to count damage per player: The damage tracker
+/// itself only counts damage for a single player, and together with a
+/// multiplexer, it will count the damage per player.
+///
+/// Type parameters:
+/// * `K` Key that is used to distinguish criteria. For example, `u64` for a
+/// multiplexer that separates based on agents.
+/// * `F` Factory that creates new trackers for each key.
+/// * `T` Inner tracker type. Usually determined by the factory.
+/// * `S` Selection function type. Takes an event and outputs a key.
+pub struct Multiplexer<K, F, T, S> {
+ factory: F,
+ trackers: HashMap<K, T>,
+ selector: S,
+}
+
+impl<K, F, T, S> Multiplexer<K, F, T, S> {
+ /// Create a new multiplexer that multiplexes on the source agent.
+ pub fn multiplex_on_source<Factory: Multiplexable<u64>>(
+ factory: Factory,
+ ) -> Multiplexer<u64, Factory, Factory::T, impl Fn(&Event) -> Option<u64>> {
+ Multiplexer {
+ factory,
+ trackers: HashMap::new(),
+ selector: |event: &Event| event.kind.source_agent_addr(),
+ }
+ }
+
+ /// Create a new multiplexer that multiplexes on the destination agent.
+ pub fn multiplex_on_destination<Factory: Multiplexable<u64>>(
+ factory: Factory,
+ ) -> Multiplexer<u64, Factory, Factory::T, impl Fn(&Event) -> Option<u64>> {
+ Multiplexer {
+ factory,
+ trackers: HashMap::new(),
+ selector: |event: &Event| event.kind.destination_agent_addr(),
+ }
+ }
+}
+
+impl<K: Hash + Eq + Clone, F: Multiplexable<K>, S: FnMut(&Event) -> Option<K>> Tracker
+ for Multiplexer<K, F, F::T, S>
+{
+ type Stat = HashMap<K, <F::T as Tracker>::Stat>;
+ type Error = <F::T as Tracker>::Error;
+
+ fn feed(&mut self, event: &Event) -> Result<(), Self::Error> {
+ if let Some(key) = (self.selector)(event) {
+ let factory = &mut self.factory;
+ let entry = self
+ .trackers
+ .entry(key.clone())
+ .or_insert_with(|| factory.create(key));
+ entry.feed(event)?;
+ }
+ Ok(())
+ }
+
+ fn finalize(self) -> Result<Self::Stat, Self::Error> {
+ self.trackers
+ .into_iter()
+ .map(|(k, v)| v.finalize().map(|inner| (k, inner)))
+ .collect()
+ }
+}
+
/// A tracker that tracks per-target damage of all agents.
pub struct DamageTracker<'l> {
log: &'l Log,