1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-06-03 10:14:06 +02:00

clippy fixes

This commit is contained in:
Robin Appelman 2023-06-03 18:30:30 +02:00
commit a561cbaf64
12 changed files with 345 additions and 398 deletions

View file

@ -2,9 +2,8 @@ use std::env;
use std::fs;
use main_error::MainError;
pub use tf_demo_parser::{Demo, DemoParser, Parse, ParseError, ParserState, Stream};
use tf_demo_parser::demo::parser::player_summary_analyzer::PlayerSummaryAnalyzer;
pub use tf_demo_parser::{Demo, DemoParser, Parse, ParseError, ParserState, Stream};
#[cfg(feature = "jemallocator")]
#[global_allocator]
@ -50,47 +49,41 @@ fn main() -> Result<(), MainError> {
for (user_id, user_data) in state.users {
let player_name = user_data.name;
let summary = state.player_summaries.get(&user_id);
match summary {
Some(s) => {
let (color_code_start, color_code_end) = if player_name == header.nick {
// Give the line for the player a green background with white text
// ANSI color codes are in hex, since rust doesn't support octal literals in strings
// See: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
("\x1b[1;42;37m", "\x1b[0m")
} else {
("", "")
};
if let Some(s) = state.player_summaries.get(&user_id) {
let (color_code_start, color_code_end) = if player_name == header.nick {
// Give the line for the player a green background with white text
// ANSI color codes are in hex, since rust doesn't support octal literals in strings
// See: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
("\x1b[1;42;37m", "\x1b[0m")
} else {
("", "")
};
println!(
"{}{:32} | {:10} | {:10} | {:10} | {:10} | {:11} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:12}{}",
color_code_start,
println!(
"{}{:32} | {:10} | {:10} | {:10} | {:10} | {:11} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:10} | {:12}{}",
color_code_start,
player_name,
s.points,
s.kills,
s.deaths,
s.assists,
s.buildings_destroyed,
s.captures,
s.defenses,
s.dominations,
s.revenges,
s.ubercharges,
s.headshots,
s.teleports,
s.healing,
s.backstabs,
s.bonus_points,
s.support,
s.damage_dealt,
player_name,
s.points,
s.kills,
s.deaths,
s.assists,
s.buildings_destroyed,
s.captures,
s.defenses,
s.dominations,
s.revenges,
s.ubercharges,
s.headshots,
s.teleports,
s.healing,
s.backstabs,
s.bonus_points,
s.support,
s.damage_dealt,
color_code_end,
);
},
None => {
// No summary for this player - they likely joined at the end of the match, or left before they did anything noteworthy
}
color_code_end,
);
}
}
}

View file

@ -92,9 +92,9 @@ impl<E: Endianness> BitWrite<E> for MaybeUtf8String {
}
}
impl Into<String> for MaybeUtf8String {
fn into(self) -> String {
match self {
impl From<MaybeUtf8String> for String {
fn from(str: MaybeUtf8String) -> String {
match str {
MaybeUtf8String::Valid(s) => s,
MaybeUtf8String::Invalid(_) => "-- Malformed utf8 --".into(),
}
@ -150,7 +150,7 @@ pub struct ServerTick(u32);
impl ServerTick {
pub fn range_inclusive(&self, till: Self) -> impl Iterator<Item = Self> {
(self.0..=till.0).into_iter().map(Self::from)
(self.0..=till.0).map(Self::from)
}
}
@ -242,7 +242,7 @@ pub struct DemoTick(u32);
impl DemoTick {
pub fn range_inclusive(&self, till: Self) -> impl Iterator<Item = Self> {
(self.0..=till.0).into_iter().map(Self::from)
(self.0..=till.0).map(Self::from)
}
}

View file

@ -161,6 +161,7 @@ impl PacketEntity {
if self.update_type == UpdateType::Enter {
let mut found_props = HashSet::<SendPropIdentifier>::new();
let props = self.props.iter().cloned();
#[allow(clippy::unnecessary_to_owned)]
let baseline_props = self
.get_baseline_props(parser_state)
.into_owned()

View file

@ -428,8 +428,8 @@ pub fn parse_string_table_update<'a>(
Ok(entries.into_entries())
}
pub fn write_string_table_update<'a>(
entries: &[(u16, StringTableEntry<'a>)],
pub fn write_string_table_update(
entries: &[(u16, StringTableEntry)],
stream: &mut BitWriteStream<LittleEndian>,
table_meta: &StringTableMeta,
) -> ReadResult<()> {

View file

@ -41,10 +41,13 @@ impl ChatMessage {
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
#[derive(
Debug, Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Hash, TryFromPrimitive, Default,
)]
#[serde(rename_all = "lowercase")]
#[repr(u8)]
pub enum Team {
#[default]
Other = 0,
Spectator = 1,
Red = 2,
@ -64,19 +67,14 @@ impl Team {
}
}
impl Default for Team {
fn default() -> Self {
Team::Other
}
}
#[derive(
Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash, TryFromPrimitive, Display, FromStr,
Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash, TryFromPrimitive, Display, FromStr, Default,
)]
#[display(style = "lowercase")]
#[serde(rename_all = "lowercase")]
#[repr(u8)]
pub enum Class {
#[default]
Other = 0,
Scout = 1,
Sniper = 2,
@ -129,12 +127,6 @@ impl Class {
}
}
impl Default for Class {
fn default() -> Self {
Class::Other
}
}
#[derive(Default, Debug, Eq, PartialEq, Deserialize, Clone)]
#[serde(from = "HashMap<Class, u8>")]
pub struct ClassList([u8; 10]);
@ -476,7 +468,7 @@ impl Analyser {
{
self.state
.users
.entry(user_info.player_info.user_id.into())
.entry(user_info.player_info.user_id)
.and_modify(|info| {
info.entity_id = user_info.entity_id;
})

View file

@ -21,8 +21,9 @@ use std::str::FromStr;
pub struct CachedEntities {}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default)]
pub enum PlayerState {
#[default]
Alive = 0,
Dying = 1,
Death = 2,
@ -40,12 +41,6 @@ impl PlayerState {
}
}
impl Default for PlayerState {
fn default() -> Self {
PlayerState::Alive
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct Player {
entity: EntityId,
@ -554,31 +549,26 @@ impl GameStateAnalyser {
.state
.get_or_create_building(entity.entity_index, BuildingClass::Sentry);
match building {
Building::Sentry(sentry) => {
for prop in entity.props(parser_state) {
match prop.identifier {
ANGLE => sentry.angle = f32::try_from(&prop.value).unwrap_or_default(),
MINI => sentry.is_mini = i64::try_from(&prop.value).unwrap_or_default() > 0,
CONTROLLED => {
sentry.player_controlled =
i64::try_from(&prop.value).unwrap_or_default() > 0
}
TARGET => {
sentry.auto_aim_target =
UserId::from(i64::try_from(&prop.value).unwrap_or_default() as u16)
}
SHELLS => {
sentry.shells = i64::try_from(&prop.value).unwrap_or_default() as u16
}
ROCKETS => {
sentry.rockets = i64::try_from(&prop.value).unwrap_or_default() as u16
}
_ => {}
if let Building::Sentry(sentry) = building {
for prop in entity.props(parser_state) {
match prop.identifier {
ANGLE => sentry.angle = f32::try_from(&prop.value).unwrap_or_default(),
MINI => sentry.is_mini = i64::try_from(&prop.value).unwrap_or_default() > 0,
CONTROLLED => {
sentry.player_controlled =
i64::try_from(&prop.value).unwrap_or_default() > 0
}
TARGET => {
sentry.auto_aim_target =
UserId::from(i64::try_from(&prop.value).unwrap_or_default() as u16)
}
SHELLS => sentry.shells = i64::try_from(&prop.value).unwrap_or_default() as u16,
ROCKETS => {
sentry.rockets = i64::try_from(&prop.value).unwrap_or_default() as u16
}
_ => {}
}
}
_ => {}
}
}
@ -607,39 +597,33 @@ impl GameStateAnalyser {
.state
.get_or_create_building(entity.entity_index, BuildingClass::Teleporter);
match building {
Building::Teleporter(teleporter) => {
for prop in entity.props(parser_state) {
match prop.identifier {
RECHARGE_TIME => {
teleporter.recharge_time =
f32::try_from(&prop.value).unwrap_or_default()
}
RECHARGE_DURATION => {
teleporter.recharge_duration =
f32::try_from(&prop.value).unwrap_or_default()
}
TIMES_USED => {
teleporter.times_used =
i64::try_from(&prop.value).unwrap_or_default() as u16
}
OTHER_END => {
teleporter.other_end = EntityId::from(
i64::try_from(&prop.value).unwrap_or_default() as u32,
)
}
YAW_TO_EXIT => {
teleporter.yaw_to_exit = f32::try_from(&prop.value).unwrap_or_default()
}
IS_ENTRANCE => {
teleporter.is_entrance =
i64::try_from(&prop.value).unwrap_or_default() == 0
}
_ => {}
if let Building::Teleporter(teleporter) = building {
for prop in entity.props(parser_state) {
match prop.identifier {
RECHARGE_TIME => {
teleporter.recharge_time = f32::try_from(&prop.value).unwrap_or_default()
}
RECHARGE_DURATION => {
teleporter.recharge_duration =
f32::try_from(&prop.value).unwrap_or_default()
}
TIMES_USED => {
teleporter.times_used =
i64::try_from(&prop.value).unwrap_or_default() as u16
}
OTHER_END => {
teleporter.other_end =
EntityId::from(i64::try_from(&prop.value).unwrap_or_default() as u32)
}
YAW_TO_EXIT => {
teleporter.yaw_to_exit = f32::try_from(&prop.value).unwrap_or_default()
}
IS_ENTRANCE => {
teleporter.is_entrance = i64::try_from(&prop.value).unwrap_or_default() == 0
}
_ => {}
}
}
_ => {}
}
}
@ -660,31 +644,24 @@ impl GameStateAnalyser {
.state
.get_or_create_building(entity.entity_index, BuildingClass::Dispenser);
match building {
Building::Dispenser(dispenser) => {
for prop in entity.props(parser_state) {
match prop.identifier {
AMMO => {
dispenser.metal = i64::try_from(&prop.value).unwrap_or_default() as u16
}
HEALING => {
let values = match &prop.value {
SendPropValue::Array(vec) => vec.as_slice(),
_ => Default::default(),
};
if let Building::Dispenser(dispenser) = building {
for prop in entity.props(parser_state) {
match prop.identifier {
AMMO => dispenser.metal = i64::try_from(&prop.value).unwrap_or_default() as u16,
HEALING => {
let values = match &prop.value {
SendPropValue::Array(vec) => vec.as_slice(),
_ => Default::default(),
};
dispenser.healing = values
.iter()
.map(|val| {
UserId::from(i64::try_from(val).unwrap_or_default() as u16)
})
.collect()
}
_ => {}
dispenser.healing = values
.iter()
.map(|val| UserId::from(i64::try_from(val).unwrap_or_default() as u16))
.collect()
}
_ => {}
}
}
_ => {}
}
}

View file

@ -189,6 +189,7 @@ impl<'a, A: MessageHandler + BorrowMessageHandler> DemoTicker<'a, A> {
}
/// Process the next packet
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<Tick<A::Output>>> {
Ok(
if let Some(packet) = self.packets.next(&self.handler.state_handler)? {

View file

@ -7,6 +7,7 @@ use crate::demo::packet::stringtable::StringTableEntry;
use crate::demo::parser::analyser::UserInfo;
use crate::demo::parser::gamestateanalyser::UserId;
use crate::demo::parser::handler::{BorrowMessageHandler, MessageHandler};
use crate::demo::sendprop::SendProp;
use crate::{ParserState, ReadResult, Stream};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
@ -58,13 +59,10 @@ impl MessageHandler for PlayerSummaryAnalyzer {
}
fn handle_message(&mut self, message: &Message, _tick: DemoTick, parser_state: &ParserState) {
match message {
Message::PacketEntities(message) => {
for entity in message.entities.iter() {
self.handle_packet_entity(&entity, parser_state);
}
if let Message::PacketEntities(message) = message {
for entity in message.entities.iter() {
self.handle_packet_entity(entity, parser_state);
}
_ => {}
}
}
@ -111,14 +109,12 @@ fn parse_integer_prop<F>(
{
use crate::demo::sendprop::SendPropValue;
match packet.get_prop_by_name(table, name, parser_state) {
Some(prop) => {
match prop.value {
SendPropValue::Integer(val) => handler(val as u32),
_ => {} // not an integer, ignore
}
}
None => {} // the packet doesn't have this property
if let Some(SendProp {
value: SendPropValue::Integer(val),
..
}) = packet.get_prop_by_name(table, name, parser_state)
{
handler(val as u32);
}
}
@ -139,229 +135,223 @@ impl PlayerSummaryAnalyzer {
// println!("Got a {} data packet: {:?}", class.name, packet);
match class.name.as_str() {
"CTFPlayer" => {
match self.user_id_map.get(&packet.entity_index) {
Some(user_id) => {
let summaries = &mut self.state.player_summaries;
let player_summary = summaries.entry(*user_id).or_default();
if let Some(user_id) = self.user_id_map.get(&packet.entity_index) {
let summaries = &mut self.state.player_summaries;
let player_summary = summaries.entry(*user_id).or_default();
// Extract scoreboard information, if present, and update the player's summary accordingly
// NOTE: Multiple DT_TFPlayerScoringDataExclusive structures may be present - one for the entire match,
// and one for just the current round. Since we're only interested in the overall match scores,
// we need to ignore the round-specific values. Fortunately, this is easy - just ignore the
// lesser value (if multiple values are present), since none of these scores are able to decrement.
// Extract scoreboard information, if present, and update the player's summary accordingly
// NOTE: Multiple DT_TFPlayerScoringDataExclusive structures may be present - one for the entire match,
// and one for just the current round. Since we're only interested in the overall match scores,
// we need to ignore the round-specific values. Fortunately, this is easy - just ignore the
// lesser value (if multiple values are present), since none of these scores are able to decrement.
/*
* Member: m_iCaptures (offset 4) (type integer) (bits 10) (Unsigned)
* Member: m_iDefenses (offset 8) (type integer) (bits 10) (Unsigned)
* Member: m_iKills (offset 12) (type integer) (bits 10) (Unsigned)
* Member: m_iDeaths (offset 16) (type integer) (bits 10) (Unsigned)
* Member: m_iSuicides (offset 20) (type integer) (bits 10) (Unsigned)
* Member: m_iDominations (offset 24) (type integer) (bits 10) (Unsigned)
* Member: m_iRevenge (offset 28) (type integer) (bits 10) (Unsigned)
* Member: m_iBuildingsBuilt (offset 32) (type integer) (bits 10) (Unsigned)
* Member: m_iBuildingsDestroyed (offset 36) (type integer) (bits 10) (Unsigned)
* Member: m_iHeadshots (offset 40) (type integer) (bits 10) (Unsigned)
* Member: m_iBackstabs (offset 44) (type integer) (bits 10) (Unsigned)
* Member: m_iHealPoints (offset 48) (type integer) (bits 20) (Unsigned)
* Member: m_iInvulns (offset 52) (type integer) (bits 10) (Unsigned)
* Member: m_iTeleports (offset 56) (type integer) (bits 10) (Unsigned)
* Member: m_iDamageDone (offset 60) (type integer) (bits 20) (Unsigned)
* Member: m_iCrits (offset 64) (type integer) (bits 10) (Unsigned)
* Member: m_iResupplyPoints (offset 68) (type integer) (bits 10) (Unsigned)
* Member: m_iKillAssists (offset 72) (type integer) (bits 12) (Unsigned)
* Member: m_iBonusPoints (offset 76) (type integer) (bits 10) (Unsigned)
* Member: m_iPoints (offset 80) (type integer) (bits 10) (Unsigned)
*
* NOTE: support points aren't included here, but is equal to the sum of m_iHealingAssist and m_iDamageAssist
* TODO: pull data for support points
*/
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iCaptures",
parser_state,
|captures| {
if captures > player_summary.captures {
player_summary.captures = captures;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iDefenses",
parser_state,
|defenses| {
if defenses > player_summary.defenses {
player_summary.defenses = defenses;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iKills",
parser_state,
|kills| {
if kills > player_summary.kills {
// TODO: This might not be accruate. Tested with a demo file with 89 kills (88 on the scoreboard),
// but only a 83 were reported in the scoring data.
player_summary.kills = kills;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iDeaths",
parser_state,
|deaths| {
if deaths > player_summary.deaths {
player_summary.deaths = deaths;
}
},
);
// ignore m_iSuicides
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iDominations",
parser_state,
|dominations| {
if dominations > player_summary.dominations {
player_summary.dominations = dominations;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iRevenge",
parser_state,
|revenges| {
if revenges > player_summary.revenges {
player_summary.revenges = revenges;
}
},
);
// ignore m_iBuildingsBuilt
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iBuildingsDestroyed",
parser_state,
|buildings_destroyed| {
if buildings_destroyed > player_summary.buildings_destroyed {
player_summary.buildings_destroyed = buildings_destroyed;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iHeadshots",
parser_state,
|headshots| {
if headshots > player_summary.headshots {
player_summary.headshots = headshots;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iBackstabs",
parser_state,
|backstabs| {
if backstabs > player_summary.backstabs {
player_summary.backstabs = backstabs;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iHealPoints",
parser_state,
|healing| {
if healing > player_summary.healing {
player_summary.healing = healing;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iInvulns",
parser_state,
|ubercharges| {
if ubercharges > player_summary.ubercharges {
player_summary.ubercharges = ubercharges;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iTeleports",
parser_state,
|teleports| {
if teleports > player_summary.teleports {
player_summary.teleports = teleports;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iDamageDone",
parser_state,
|damage_dealt| {
if damage_dealt > player_summary.damage_dealt {
player_summary.damage_dealt = damage_dealt;
}
},
);
// ignore m_iCrits
// ignore m_iResupplyPoints
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iKillAssists",
parser_state,
|assists| {
if assists > player_summary.assists {
player_summary.assists = assists;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iBonusPoints",
parser_state,
|bonus_points| {
if bonus_points > player_summary.bonus_points {
player_summary.bonus_points = bonus_points;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iPoints",
parser_state,
|points| {
if points > player_summary.points {
player_summary.points = points;
}
},
);
}
None => {
// Player entity likely spawned before the player was assigned to it?
// This can rarely happen, but doesn't seem to affect the end result
}
/*
* Member: m_iCaptures (offset 4) (type integer) (bits 10) (Unsigned)
* Member: m_iDefenses (offset 8) (type integer) (bits 10) (Unsigned)
* Member: m_iKills (offset 12) (type integer) (bits 10) (Unsigned)
* Member: m_iDeaths (offset 16) (type integer) (bits 10) (Unsigned)
* Member: m_iSuicides (offset 20) (type integer) (bits 10) (Unsigned)
* Member: m_iDominations (offset 24) (type integer) (bits 10) (Unsigned)
* Member: m_iRevenge (offset 28) (type integer) (bits 10) (Unsigned)
* Member: m_iBuildingsBuilt (offset 32) (type integer) (bits 10) (Unsigned)
* Member: m_iBuildingsDestroyed (offset 36) (type integer) (bits 10) (Unsigned)
* Member: m_iHeadshots (offset 40) (type integer) (bits 10) (Unsigned)
* Member: m_iBackstabs (offset 44) (type integer) (bits 10) (Unsigned)
* Member: m_iHealPoints (offset 48) (type integer) (bits 20) (Unsigned)
* Member: m_iInvulns (offset 52) (type integer) (bits 10) (Unsigned)
* Member: m_iTeleports (offset 56) (type integer) (bits 10) (Unsigned)
* Member: m_iDamageDone (offset 60) (type integer) (bits 20) (Unsigned)
* Member: m_iCrits (offset 64) (type integer) (bits 10) (Unsigned)
* Member: m_iResupplyPoints (offset 68) (type integer) (bits 10) (Unsigned)
* Member: m_iKillAssists (offset 72) (type integer) (bits 12) (Unsigned)
* Member: m_iBonusPoints (offset 76) (type integer) (bits 10) (Unsigned)
* Member: m_iPoints (offset 80) (type integer) (bits 10) (Unsigned)
*
* NOTE: support points aren't included here, but is equal to the sum of m_iHealingAssist and m_iDamageAssist
* TODO: pull data for support points
*/
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iCaptures",
parser_state,
|captures| {
if captures > player_summary.captures {
player_summary.captures = captures;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iDefenses",
parser_state,
|defenses| {
if defenses > player_summary.defenses {
player_summary.defenses = defenses;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iKills",
parser_state,
|kills| {
if kills > player_summary.kills {
// TODO: This might not be accruate. Tested with a demo file with 89 kills (88 on the scoreboard),
// but only a 83 were reported in the scoring data.
player_summary.kills = kills;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iDeaths",
parser_state,
|deaths| {
if deaths > player_summary.deaths {
player_summary.deaths = deaths;
}
},
);
// ignore m_iSuicides
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iDominations",
parser_state,
|dominations| {
if dominations > player_summary.dominations {
player_summary.dominations = dominations;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iRevenge",
parser_state,
|revenges| {
if revenges > player_summary.revenges {
player_summary.revenges = revenges;
}
},
);
// ignore m_iBuildingsBuilt
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iBuildingsDestroyed",
parser_state,
|buildings_destroyed| {
if buildings_destroyed > player_summary.buildings_destroyed {
player_summary.buildings_destroyed = buildings_destroyed;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iHeadshots",
parser_state,
|headshots| {
if headshots > player_summary.headshots {
player_summary.headshots = headshots;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iBackstabs",
parser_state,
|backstabs| {
if backstabs > player_summary.backstabs {
player_summary.backstabs = backstabs;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iHealPoints",
parser_state,
|healing| {
if healing > player_summary.healing {
player_summary.healing = healing;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iInvulns",
parser_state,
|ubercharges| {
if ubercharges > player_summary.ubercharges {
player_summary.ubercharges = ubercharges;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iTeleports",
parser_state,
|teleports| {
if teleports > player_summary.teleports {
player_summary.teleports = teleports;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iDamageDone",
parser_state,
|damage_dealt| {
if damage_dealt > player_summary.damage_dealt {
player_summary.damage_dealt = damage_dealt;
}
},
);
// ignore m_iCrits
// ignore m_iResupplyPoints
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iKillAssists",
parser_state,
|assists| {
if assists > player_summary.assists {
player_summary.assists = assists;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iBonusPoints",
parser_state,
|bonus_points| {
if bonus_points > player_summary.bonus_points {
player_summary.bonus_points = bonus_points;
}
},
);
parse_integer_prop(
packet,
"DT_TFPlayerScoringDataExclusive",
"m_iPoints",
parser_state,
|points| {
if points > player_summary.points {
player_summary.points = points;
}
},
);
}
}
"CTFPlayerResource" => {
@ -370,24 +360,17 @@ impl PlayerSummaryAnalyzer {
// for example, `m_iUserID.024 = 2523` means entity 24 is user 2523
for i in 0..33 {
// 0 to 32, inclusive (1..33 might also work, not sure if there's a user 0 or not). Not exhaustive and doesn't work for servers with > 32 players
match packet.get_prop_by_name(
if let Some(SendProp {
value: SendPropValue::Integer(x),
..
}) = packet.get_prop_by_name(
"m_iUserID",
format!("{:0>3}", i).as_str(),
parser_state,
) {
Some(prop) => {
match prop.value {
SendPropValue::Integer(x) => {
let entity_id = EntityId::from(i as u32);
let user_id = UserId::from(x as u32);
self.user_id_map.insert(entity_id, user_id);
}
_ => {
// These properties should all be integers...
}
}
}
None => {} // ignore, no property for this entity was included
let entity_id = EntityId::from(i as u32);
let user_id = UserId::from(x as u32);
self.user_id_map.insert(entity_id, user_id);
}
}
}
@ -409,7 +392,7 @@ impl PlayerSummaryAnalyzer {
{
self.state
.users
.entry(user_info.player_info.user_id.into())
.entry(user_info.player_info.user_id)
.and_modify(|info| {
info.entity_id = user_info.entity_id;
})

View file

@ -329,7 +329,7 @@ impl Baseline {
self.instances[usize::from(index)] = Some(entity);
}
pub fn keys<'a>(&'a self) -> impl Iterator<Item = EntityId> + 'a {
pub fn keys(&self) -> impl Iterator<Item = EntityId> + '_ {
self.instances
.iter()
.filter_map(|entity| entity.as_ref().map(|entity| entity.entity_id))

View file

@ -1185,7 +1185,7 @@ impl<'de> Deserialize<'de> for SendPropIdentifier {
Ok(match raw {
Options::Num(num) => SendPropIdentifier(num),
Options::Str(s) => {
let num: u64 = s.parse().map_err(|e| D::Error::custom(e))?;
let num: u64 = s.parse().map_err(D::Error::custom)?;
SendPropIdentifier(num)
}
})