Download crates/urt-gameplay/src/method_stats.rs from nmarshall/urt-rs: direct link, hf CLI and curl.
- Browser
- Download file 5 kB
-
https://huggingface.co/nmarshall/urt-rs/resolve/main/crates/urt-gameplay/src/method_stats.rs
- Command line
-
hf download hf://nmarshall/urt-rs/crates/urt-gameplay/src/method_stats.rs
-
curl -L -o method_stats.rs https://huggingface.co/nmarshall/urt-rs/resolve/main/crates/urt-gameplay/src/method_stats.rs
5 kB
| //! Numeric278733/278834 counters; aggregation/reporting remains caller work. | |
| use std::collections::BTreeMap; | |
| pub enum Side { | |
| Victim, | |
| Actor, | |
| } | |
| pub struct Plan { | |
| pub increments: Vec<(Side, u32)>, | |
| pub aggregate_pair: bool, | |
| pub report: bool, | |
| } | |
| pub enum StatsError { | |
| MissingCounter, | |
| } | |
| pub fn slot(method: i32) -> Option<u8> { | |
| Some(match method { | |
| 14 => 9, | |
| 15 => 8, | |
| 16 => 11, | |
| 17 => 5, | |
| 18 => 4, | |
| 19 => 2, | |
| 20 => 0, | |
| 21 => 7, | |
| 28 => 6, | |
| 30 => 1, | |
| 36 => 10, | |
| 38 => 3, | |
| 39 => 12, | |
| 40 => 13, | |
| 41 => 14, | |
| 42 => 15, | |
| 43 => 16, | |
| 44 => 17, | |
| 45 => 18, | |
| _ => return None, | |
| }) | |
| } | |
| /// All participants are existing clients with valid source IDs. Self aliases | |
| /// resolve to the actor's storage; an absent actor exits before any requests. | |
| pub fn plan(method: i32, self_hit: bool, actor_absent: bool) -> Plan { | |
| use Side::{Actor, Victim}; | |
| let mut result = Plan { | |
| increments: vec![], | |
| aggregate_pair: false, | |
| report: !actor_absent, | |
| }; | |
| if actor_absent { | |
| return result; | |
| } | |
| let victim = if self_hit { Actor } else { Victim }; | |
| if let Some(slot) = slot(method) { | |
| result.increments = vec![ | |
| (Actor, 916 + 76 * u32::from(slot)), | |
| (victim, 920 + 76 * u32::from(slot)), | |
| ]; | |
| result.aggregate_pair = true; | |
| return result; | |
| } | |
| let single = match method { | |
| 0..=5 | 8 | 9 => Some(2512), | |
| 6 => Some(2488), | |
| 7 => Some(2492), | |
| 31 => Some(2504), | |
| 32 | 33 | 35 => Some(2508), | |
| 47 => Some(2496), | |
| 22 if self_hit => Some(2460), | |
| 25 if self_hit => Some(2452), | |
| _ => None, | |
| }; | |
| if let Some(offset) = single { | |
| result.increments.push((victim, offset)); | |
| return result; | |
| } | |
| let pair = match method { | |
| 12 => Some((2396, 2468)), | |
| 13 => Some((2400, 2472)), | |
| 22 => Some((2384, 2456)), | |
| 23 => Some((2408, 2480)), | |
| 24 => Some((2404, 2476)), | |
| 25 => Some((2376, 2448)), | |
| 34 => Some((2428, 2500)), | |
| 37 => Some((2392, 2464)), | |
| 48 => Some((2412, 2484)), | |
| _ => None, | |
| }; | |
| if let Some((a, v)) = pair { | |
| result.increments = vec![(Actor, a), (victim, v)]; | |
| result.aggregate_pair = method != 34; | |
| } | |
| result | |
| } | |
| /// Validate storage before mutation, then apply source wrapping increments. | |
| /// The returned plan describes reporting intent without executing callbacks. | |
| pub fn apply( | |
| method: i32, | |
| self_hit: bool, | |
| actor_absent: bool, | |
| actor: &mut BTreeMap<u32, u32>, | |
| victim: &mut BTreeMap<u32, u32>, | |
| ) -> Result<Plan, StatsError> { | |
| let p = plan(method, self_hit, actor_absent); | |
| for (side, offset) in &p.increments { | |
| if !(if *side == Side::Actor { | |
| &*actor | |
| } else { | |
| &*victim | |
| }) | |
| .contains_key(offset) | |
| { | |
| return Err(StatsError::MissingCounter); | |
| } | |
| } | |
| for (side, offset) in &p.increments { | |
| let counters = if *side == Side::Actor { | |
| &mut *actor | |
| } else { | |
| &mut *victim | |
| }; | |
| let v = counters.get_mut(offset).unwrap(); | |
| *v = v.wrapping_add(1); | |
| } | |
| Ok(p) | |
| } | |
| mod tests { | |
| use super::*; | |
| fn all_slots_and_invalid_signed_extremes() { | |
| let slots: std::collections::BTreeSet<_> = (0..50).filter_map(slot).collect(); | |
| assert_eq!(slots, (0..19).collect()); | |
| assert_eq!(slot(i32::MIN), None); | |
| assert_eq!(slot(i32::MAX), None); | |
| } | |
| fn self_alias_and_fallback_exceptions() { | |
| assert_eq!(plan(22, true, false).increments, vec![(Side::Actor, 2460)]); | |
| assert_eq!(plan(25, true, false).increments, vec![(Side::Actor, 2452)]); | |
| assert!(!plan(34, false, false).aggregate_pair); | |
| assert!(plan(20, false, false).aggregate_pair); | |
| } | |
| fn apply_wraps_and_keeps_other_counters() { | |
| let mut actor = BTreeMap::from([(916, u32::MAX), (999, 17)]); | |
| let mut victim = BTreeMap::from([(920, 3)]); | |
| apply(20, false, false, &mut actor, &mut victim).unwrap(); | |
| assert_eq!(actor, BTreeMap::from([(916, 0), (999, 17)])); | |
| assert_eq!(victim[&920], 4); | |
| } | |
| fn absent_actor_and_missing_storage_do_not_partially_mutate() { | |
| let mut a = BTreeMap::from([(916, 4)]); | |
| let mut v = BTreeMap::new(); | |
| assert_eq!( | |
| apply(20, false, false, &mut a, &mut v), | |
| Err(StatsError::MissingCounter) | |
| ); | |
| assert_eq!(a[&916], 4); | |
| let p = apply(20, false, true, &mut a, &mut v).unwrap(); | |
| assert!(!p.report); | |
| assert!(p.increments.is_empty()); | |
| assert_eq!(a[&916], 4); | |
| } | |
| } | |