use std::collections::HashMap;
use std::io::Cursor;
use anyhow::{anyhow as e, Result};
use crate::qw::frame;
use crate::qw::message::message_type::ReadMessageType;
use crate::qw::message::update_ping::ReadUpdatePing;
use crate::qw::prot::MessageType;
pub fn ping_per_player_number(data: &[u8]) -> Result<HashMap<u8, u32>> {
let max_samples: usize = 8;
let mut sample_count: usize = 0;
let mut index = 0;
let mut total_pings: HashMap<u8, Vec<u16>> = HashMap::new();
while let Ok(info) = frame::Info::from_data_and_index(data, index) {
if info.body_size > 0 {
let mut body = Cursor::new(&data[info.body_range.clone()]);
let mut did_ping_update = false;
while body
.read_message_type()
.is_ok_and(|t| t == MessageType::UpdatePing)
{
if let Ok(u) = body.read_update_ping() {
let pings = total_pings.entry(u.player_number).or_default();
pings.push(u.ping);
body.set_position(body.position() + 2); did_ping_update = true;
}
}
if did_ping_update {
sample_count += 1;
}
}
if sample_count >= max_samples {
break;
}
index += info.size;
}
if total_pings.is_empty() {
return Err(e!("Unable to read pings"));
}
let mut average_ping: HashMap<u8, u32> = HashMap::new();
for (pnum, pings) in total_pings.iter() {
let pings_sum = pings.iter().sum::<u16>();
let avg_ping = (pings_sum as f32 / pings.len() as f32) as u32;
average_ping.insert(*pnum, avg_ping);
}
Ok(average_ping)
}
#[cfg(test)]
mod tests {
use std::fs::read;
use anyhow::Result;
use pretty_assertions::assert_eq;
use super::*;
#[test]
fn test_ping_per_player_number() -> Result<()> {
{
let demo_data: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let err = ping_per_player_number(&demo_data).unwrap_err();
assert_eq!(err.to_string(), "Unable to read pings".to_string());
}
{
let demo_data = read("tests/files/duel_equ_vs_kaboom[povdmm4]20240422-1038.mvd")?;
let pings = ping_per_player_number(&demo_data)?;
assert_eq!(pings.get(&0), Some(&25));
assert_eq!(pings.get(&1), Some(&620));
assert_eq!(pings.get(&2), Some(&29));
}
{
let demo_data = read("tests/files/4on4_oeks_vs_tsq[dm2]20240426-1716.mvd");
let pings = ping_per_player_number(&demo_data?)?;
assert_eq!(pings.get(&0), Some(&26));
assert_eq!(pings.get(&1), Some(&26));
assert_eq!(pings.get(&2), Some(&666));
assert_eq!(pings.get(&3), Some(&12));
assert_eq!(pings.get(&4), Some(&28));
assert_eq!(pings.get(&5), Some(&12));
assert_eq!(pings.get(&6), Some(&12));
assert_eq!(pings.get(&7), Some(&12));
assert_eq!(pings.get(&8), Some(&12));
}
{
let demo_data = read("tests/files/ctf_blue_vs_red[ctf5]20240520-1925.mvd");
let pings = ping_per_player_number(&demo_data?)?;
assert_eq!(pings.get(&0), Some(&19));
assert_eq!(pings.get(&1), Some(&12));
assert_eq!(pings.get(&2), Some(&30));
assert_eq!(pings.get(&3), Some(&12));
assert_eq!(pings.get(&4), Some(&13));
assert_eq!(pings.get(&5), Some(&50));
assert_eq!(pings.get(&6), Some(&39));
assert_eq!(pings.get(&7), Some(&12));
}
{
let demo_data = read("tests/files/wipeout_red_vs_blue[q3dm6qw]20240406-2028.mvd");
let pings = ping_per_player_number(&demo_data?)?;
assert_eq!(pings.get(&0), Some(&14));
assert_eq!(pings.get(&1), Some(&52));
assert_eq!(pings.get(&2), Some(&38));
assert_eq!(pings.get(&3), Some(&12));
assert_eq!(pings.get(&4), Some(&666));
assert_eq!(pings.get(&5), Some(&25));
}
{
let demo_data = read("tests/files/ffa_5[dm4]20240501-1229.mvd")?;
let pings = ping_per_player_number(&demo_data)?;
assert_eq!(pings.get(&0), Some(&557));
assert_eq!(pings.get(&1), Some(&12));
assert_eq!(pings.get(&2), Some(&12));
assert_eq!(pings.get(&3), Some(&10));
assert_eq!(pings.get(&4), Some(&50));
assert_eq!(pings.get(&5), Some(&10));
assert_eq!(pings.get(&6), Some(&10));
assert_eq!(pings.get(&7), Some(&10));
}
Ok(())
}
}