diff options
-rw-r--r-- | src/statistics/mod.rs | 3 | ||||
-rw-r--r-- | src/statistics/trackers.rs | 95 |
2 files changed, 54 insertions, 44 deletions
diff --git a/src/statistics/mod.rs b/src/statistics/mod.rs index 7f465ae..1dd48f7 100644 --- a/src/statistics/mod.rs +++ b/src/statistics/mod.rs @@ -103,7 +103,8 @@ pub fn calculate(log: &Log) -> StatResult<Statistics> { let mut damage_tracker = trackers::DamageTracker::new(log); let mut log_start_tracker = trackers::LogStartTracker::new(); let mut combat_time_tracker = trackers::CombatTimeTracker::new(); - let mut boon_tracker = trackers::BoonTracker::new(); + let mut boon_tracker = + trackers::Multiplexer::multiplex_on_destination(|dest| trackers::BoonTracker::new(dest)); run_trackers( log, diff --git a/src/statistics/trackers.rs b/src/statistics/trackers.rs index 8381a43..44b2f97 100644 --- a/src/statistics/trackers.rs +++ b/src/statistics/trackers.rs @@ -82,6 +82,9 @@ impl<S, E: Error + 'static, T: Tracker<Stat = S, Error = E>> RunnableTracker for /// 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`. +/// +/// A blanket implementation for closures is provided, so you can use any +/// `FnMut(K) -> T`, where `K` is the key and `T` is the tracker. pub trait Multiplexable<K> { /// The type of tracker that this multiplexable/factory creates. type T: Tracker; @@ -90,6 +93,20 @@ pub trait Multiplexable<K> { fn create(&mut self, key: K) -> Self::T; } +// This implementation allows a closure to be used as a multiplexable/tracker +// factory. +impl<T, K, O> Multiplexable<K> for T +where + T: FnMut(K) -> O, + O: Tracker, +{ + type T = O; + + fn create(&mut self, key: K) -> Self::T { + self(key) + } +} + /// A helper that wraps (decorates) another tracker and separates the results /// based on the given criteria. /// @@ -106,13 +123,20 @@ pub trait Multiplexable<K> { /// * `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. +/// +/// # Example +/// +/// ``` +/// # use evtclib::statistics::trackers::*; +/// let boons = Multiplexer::multiplex_on_destination(|agent| BoonTracker::new(agent)); +/// ``` 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> { +impl Multiplexer<(), (), (), ()> { /// Create a new multiplexer that multiplexes on the source agent. pub fn multiplex_on_source<Factory: Multiplexable<u64>>( factory: Factory, @@ -318,18 +342,24 @@ impl Tracker for CombatTimeTracker { /// /// This tracker only tracks the boons that are known to evtclib, that is the /// boons defined in `evtclib::statistics::gamedata::BOONS`. -#[derive(Default)] pub struct BoonTracker { - boon_areas: HashMap<u64, HashMap<u16, u64>>, - boon_queues: HashMap<u64, HashMap<u16, BoonQueue>>, + agent_addr: u64, + boon_areas: HashMap<u16, u64>, + boon_queues: HashMap<u16, BoonQueue>, last_time: u64, next_update: u64, } impl BoonTracker { - /// Creates a new boon tracker. - pub fn new() -> BoonTracker { - Default::default() + /// Creates a new boon tracker for the given agent. + pub fn new(agent_addr: u64) -> BoonTracker { + BoonTracker { + agent_addr, + boon_areas: Default::default(), + boon_queues: Default::default(), + last_time: 0, + next_update: 0, + } } /// Updates the internal boon queues by the given amount of milliseconds. @@ -338,14 +368,9 @@ impl BoonTracker { fn update_queues(&mut self, delta_t: u64) { self.boon_queues .values_mut() - .flat_map(HashMap::values_mut) .for_each(|queue| queue.simulate(delta_t)); - // Throw away empty boon queues or agents without any boon queue to - // improve performance - self.boon_queues - .values_mut() - .for_each(|q| q.retain(|_, v| !v.is_empty())); + // Throw away empty boon queues or to improve performance self.boon_queues.retain(|_, q| !q.is_empty()); } @@ -355,17 +380,10 @@ impl BoonTracker { /// /// * `delta_t` - Amount of milliseconds that passed. fn update_areas(&mut self, delta_t: u64) { - for (agent, queues) in &self.boon_queues { - for (buff_id, queue) in queues { - let current_stacks = queue.current_stacks(); - let area = self - .boon_areas - .entry(*agent) - .or_insert_with(Default::default) - .entry(*buff_id) - .or_insert(0); - *area += current_stacks as u64 * delta_t; - } + for (buff_id, queue) in &self.boon_queues { + let current_stacks = queue.current_stacks(); + let area = self.boon_areas.entry(*buff_id).or_insert(0); + *area += current_stacks as u64 * delta_t; } } @@ -373,7 +391,6 @@ impl BoonTracker { let next_update = self .boon_queues .values() - .flat_map(HashMap::values) .map(BoonQueue::next_update) .filter(|v| *v != 0) .min() @@ -381,19 +398,14 @@ impl BoonTracker { self.next_update = next_update; } - /// Get the boon queue for the given agent and buff_id. + /// Get the boon queue for the given buff_id. /// /// If the queue does not yet exist, create it. /// - /// * `agent` - The agent. /// * `buff_id` - The buff (or condition) id. - fn get_queue(&mut self, agent: u64, buff_id: u16) -> Option<&mut BoonQueue> { + fn get_queue(&mut self, buff_id: u16) -> Option<&mut BoonQueue> { use std::collections::hash_map::Entry; - let entry = self - .boon_queues - .entry(agent) - .or_insert_with(Default::default) - .entry(buff_id); + let entry = self.boon_queues.entry(buff_id); match entry { // Queue already exists Entry::Occupied(e) => Some(e.into_mut()), @@ -411,10 +423,14 @@ impl BoonTracker { } impl Tracker for BoonTracker { - type Stat = HashMap<u64, HashMap<u16, u64>>; + type Stat = HashMap<u16, u64>; type Error = !; fn feed(&mut self, event: &Event) -> Result<(), Self::Error> { + if event.kind.destination_agent_addr() != Some(self.agent_addr) { + return Ok(()); + } + let delta_t = event.time - self.last_time; if self.next_update != 0 && delta_t > self.next_update { self.update_queues(delta_t); @@ -425,12 +441,11 @@ impl Tracker for BoonTracker { match event.kind { EventKind::BuffApplication { - destination_agent_addr, buff_id, duration, .. } => { - if let Some(queue) = self.get_queue(destination_agent_addr, buff_id) { + if let Some(queue) = self.get_queue(buff_id) { queue.add_stack(duration as u64); } self.update_next_update(); @@ -438,11 +453,10 @@ impl Tracker for BoonTracker { // XXX: Properly handle SINGLE and MANUAL removal types EventKind::BuffRemove { - destination_agent_addr, buff_id, .. } => { - if let Some(queue) = self.get_queue(destination_agent_addr, buff_id) { + if let Some(queue) = self.get_queue(buff_id) { queue.clear(); } self.update_next_update(); @@ -455,11 +469,6 @@ impl Tracker for BoonTracker { } fn finalize(self) -> Result<Self::Stat, Self::Error> { - println!("Number of agents: {}", self.boon_queues.len()); - println!( - "Number of boon queues: {}", - self.boon_queues.values().flat_map(|qs| qs.values()).count() - ); Ok(self.boon_areas) } } |