1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-06-03 18:24:05 +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

4
Cargo.lock generated
View file

@ -90,9 +90,9 @@ dependencies = [
[[package]] [[package]]
name = "bitbuffer_derive" name = "bitbuffer_derive"
version = "0.10.0" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4090254bfbc71442ff4a426ddba663346e26fd14b55b259281f763e350d7f621" checksum = "052a5a614540ae9bb7de25c2c86a94b6de7374cb7e3230f3128955bdaea62c3f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

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

View file

@ -92,9 +92,9 @@ impl<E: Endianness> BitWrite<E> for MaybeUtf8String {
} }
} }
impl Into<String> for MaybeUtf8String { impl From<MaybeUtf8String> for String {
fn into(self) -> String { fn from(str: MaybeUtf8String) -> String {
match self { match str {
MaybeUtf8String::Valid(s) => s, MaybeUtf8String::Valid(s) => s,
MaybeUtf8String::Invalid(_) => "-- Malformed utf8 --".into(), MaybeUtf8String::Invalid(_) => "-- Malformed utf8 --".into(),
} }
@ -150,7 +150,7 @@ pub struct ServerTick(u32);
impl ServerTick { impl ServerTick {
pub fn range_inclusive(&self, till: Self) -> impl Iterator<Item = Self> { 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 { impl DemoTick {
pub fn range_inclusive(&self, till: Self) -> impl Iterator<Item = Self> { 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 { if self.update_type == UpdateType::Enter {
let mut found_props = HashSet::<SendPropIdentifier>::new(); let mut found_props = HashSet::<SendPropIdentifier>::new();
let props = self.props.iter().cloned(); let props = self.props.iter().cloned();
#[allow(clippy::unnecessary_to_owned)]
let baseline_props = self let baseline_props = self
.get_baseline_props(parser_state) .get_baseline_props(parser_state)
.into_owned() .into_owned()

View file

@ -428,8 +428,8 @@ pub fn parse_string_table_update<'a>(
Ok(entries.into_entries()) Ok(entries.into_entries())
} }
pub fn write_string_table_update<'a>( pub fn write_string_table_update(
entries: &[(u16, StringTableEntry<'a>)], entries: &[(u16, StringTableEntry)],
stream: &mut BitWriteStream<LittleEndian>, stream: &mut BitWriteStream<LittleEndian>,
table_meta: &StringTableMeta, table_meta: &StringTableMeta,
) -> ReadResult<()> { ) -> 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")] #[serde(rename_all = "lowercase")]
#[repr(u8)] #[repr(u8)]
pub enum Team { pub enum Team {
#[default]
Other = 0, Other = 0,
Spectator = 1, Spectator = 1,
Red = 2, Red = 2,
@ -64,19 +67,14 @@ impl Team {
} }
} }
impl Default for Team {
fn default() -> Self {
Team::Other
}
}
#[derive( #[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")] #[display(style = "lowercase")]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
#[repr(u8)] #[repr(u8)]
pub enum Class { pub enum Class {
#[default]
Other = 0, Other = 0,
Scout = 1, Scout = 1,
Sniper = 2, Sniper = 2,
@ -129,12 +127,6 @@ impl Class {
} }
} }
impl Default for Class {
fn default() -> Self {
Class::Other
}
}
#[derive(Default, Debug, Eq, PartialEq, Deserialize, Clone)] #[derive(Default, Debug, Eq, PartialEq, Deserialize, Clone)]
#[serde(from = "HashMap<Class, u8>")] #[serde(from = "HashMap<Class, u8>")]
pub struct ClassList([u8; 10]); pub struct ClassList([u8; 10]);
@ -476,7 +468,7 @@ impl Analyser {
{ {
self.state self.state
.users .users
.entry(user_info.player_info.user_id.into()) .entry(user_info.player_info.user_id)
.and_modify(|info| { .and_modify(|info| {
info.entity_id = user_info.entity_id; info.entity_id = user_info.entity_id;
}) })

View file

@ -21,8 +21,9 @@ use std::str::FromStr;
pub struct CachedEntities {} pub struct CachedEntities {}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Default)]
pub enum PlayerState { pub enum PlayerState {
#[default]
Alive = 0, Alive = 0,
Dying = 1, Dying = 1,
Death = 2, Death = 2,
@ -40,12 +41,6 @@ impl PlayerState {
} }
} }
impl Default for PlayerState {
fn default() -> Self {
PlayerState::Alive
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct Player { pub struct Player {
entity: EntityId, entity: EntityId,
@ -554,31 +549,26 @@ impl GameStateAnalyser {
.state .state
.get_or_create_building(entity.entity_index, BuildingClass::Sentry); .get_or_create_building(entity.entity_index, BuildingClass::Sentry);
match building { if let Building::Sentry(sentry) = building {
Building::Sentry(sentry) => { for prop in entity.props(parser_state) {
for prop in entity.props(parser_state) { match prop.identifier {
match prop.identifier { ANGLE => sentry.angle = f32::try_from(&prop.value).unwrap_or_default(),
ANGLE => sentry.angle = f32::try_from(&prop.value).unwrap_or_default(), MINI => sentry.is_mini = i64::try_from(&prop.value).unwrap_or_default() > 0,
MINI => sentry.is_mini = i64::try_from(&prop.value).unwrap_or_default() > 0, CONTROLLED => {
CONTROLLED => { sentry.player_controlled =
sentry.player_controlled = i64::try_from(&prop.value).unwrap_or_default() > 0
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
}
_ => {}
} }
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 .state
.get_or_create_building(entity.entity_index, BuildingClass::Teleporter); .get_or_create_building(entity.entity_index, BuildingClass::Teleporter);
match building { if let Building::Teleporter(teleporter) = building {
Building::Teleporter(teleporter) => { for prop in entity.props(parser_state) {
for prop in entity.props(parser_state) { match prop.identifier {
match prop.identifier { RECHARGE_TIME => {
RECHARGE_TIME => { teleporter.recharge_time = f32::try_from(&prop.value).unwrap_or_default()
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
}
_ => {}
} }
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 .state
.get_or_create_building(entity.entity_index, BuildingClass::Dispenser); .get_or_create_building(entity.entity_index, BuildingClass::Dispenser);
match building { if let Building::Dispenser(dispenser) = building {
Building::Dispenser(dispenser) => { for prop in entity.props(parser_state) {
for prop in entity.props(parser_state) { match prop.identifier {
match prop.identifier { AMMO => dispenser.metal = i64::try_from(&prop.value).unwrap_or_default() as u16,
AMMO => { HEALING => {
dispenser.metal = i64::try_from(&prop.value).unwrap_or_default() as u16 let values = match &prop.value {
} SendPropValue::Array(vec) => vec.as_slice(),
HEALING => { _ => Default::default(),
let values = match &prop.value { };
SendPropValue::Array(vec) => vec.as_slice(),
_ => Default::default(),
};
dispenser.healing = values dispenser.healing = values
.iter() .iter()
.map(|val| { .map(|val| UserId::from(i64::try_from(val).unwrap_or_default() as u16))
UserId::from(i64::try_from(val).unwrap_or_default() as u16) .collect()
})
.collect()
}
_ => {}
} }
_ => {}
} }
} }
_ => {}
} }
} }

View file

@ -189,6 +189,7 @@ impl<'a, A: MessageHandler + BorrowMessageHandler> DemoTicker<'a, A> {
} }
/// Process the next packet /// Process the next packet
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<Tick<A::Output>>> { pub fn next(&mut self) -> Result<Option<Tick<A::Output>>> {
Ok( Ok(
if let Some(packet) = self.packets.next(&self.handler.state_handler)? { 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::analyser::UserInfo;
use crate::demo::parser::gamestateanalyser::UserId; use crate::demo::parser::gamestateanalyser::UserId;
use crate::demo::parser::handler::{BorrowMessageHandler, MessageHandler}; use crate::demo::parser::handler::{BorrowMessageHandler, MessageHandler};
use crate::demo::sendprop::SendProp;
use crate::{ParserState, ReadResult, Stream}; use crate::{ParserState, ReadResult, Stream};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap}; 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) { fn handle_message(&mut self, message: &Message, _tick: DemoTick, parser_state: &ParserState) {
match message { if let Message::PacketEntities(message) = message {
Message::PacketEntities(message) => { for entity in message.entities.iter() {
for entity in message.entities.iter() { self.handle_packet_entity(entity, parser_state);
self.handle_packet_entity(&entity, parser_state);
}
} }
_ => {}
} }
} }
@ -111,14 +109,12 @@ fn parse_integer_prop<F>(
{ {
use crate::demo::sendprop::SendPropValue; use crate::demo::sendprop::SendPropValue;
match packet.get_prop_by_name(table, name, parser_state) { if let Some(SendProp {
Some(prop) => { value: SendPropValue::Integer(val),
match prop.value { ..
SendPropValue::Integer(val) => handler(val as u32), }) = packet.get_prop_by_name(table, name, parser_state)
_ => {} // not an integer, ignore {
} handler(val as u32);
}
None => {} // the packet doesn't have this property
} }
} }
@ -139,229 +135,223 @@ impl PlayerSummaryAnalyzer {
// println!("Got a {} data packet: {:?}", class.name, packet); // println!("Got a {} data packet: {:?}", class.name, packet);
match class.name.as_str() { match class.name.as_str() {
"CTFPlayer" => { "CTFPlayer" => {
match self.user_id_map.get(&packet.entity_index) { if let Some(user_id) = self.user_id_map.get(&packet.entity_index) {
Some(user_id) => { let summaries = &mut self.state.player_summaries;
let summaries = &mut self.state.player_summaries; let player_summary = summaries.entry(*user_id).or_default();
let player_summary = summaries.entry(*user_id).or_default();
// Extract scoreboard information, if present, and update the player's summary accordingly // 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, // 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, // 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 // 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. // 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_iCaptures (offset 4) (type integer) (bits 10) (Unsigned)
* Member: m_iDefenses (offset 8) (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_iKills (offset 12) (type integer) (bits 10) (Unsigned)
* Member: m_iDeaths (offset 16) (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_iSuicides (offset 20) (type integer) (bits 10) (Unsigned)
* Member: m_iDominations (offset 24) (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_iRevenge (offset 28) (type integer) (bits 10) (Unsigned)
* Member: m_iBuildingsBuilt (offset 32) (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_iBuildingsDestroyed (offset 36) (type integer) (bits 10) (Unsigned)
* Member: m_iHeadshots (offset 40) (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_iBackstabs (offset 44) (type integer) (bits 10) (Unsigned)
* Member: m_iHealPoints (offset 48) (type integer) (bits 20) (Unsigned) * Member: m_iHealPoints (offset 48) (type integer) (bits 20) (Unsigned)
* Member: m_iInvulns (offset 52) (type integer) (bits 10) (Unsigned) * Member: m_iInvulns (offset 52) (type integer) (bits 10) (Unsigned)
* Member: m_iTeleports (offset 56) (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_iDamageDone (offset 60) (type integer) (bits 20) (Unsigned)
* Member: m_iCrits (offset 64) (type integer) (bits 10) (Unsigned) * Member: m_iCrits (offset 64) (type integer) (bits 10) (Unsigned)
* Member: m_iResupplyPoints (offset 68) (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_iKillAssists (offset 72) (type integer) (bits 12) (Unsigned)
* Member: m_iBonusPoints (offset 76) (type integer) (bits 10) (Unsigned) * Member: m_iBonusPoints (offset 76) (type integer) (bits 10) (Unsigned)
* Member: m_iPoints (offset 80) (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 * 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 * TODO: pull data for support points
*/ */
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iCaptures", "m_iCaptures",
parser_state, parser_state,
|captures| { |captures| {
if captures > player_summary.captures { if captures > player_summary.captures {
player_summary.captures = captures; player_summary.captures = captures;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iDefenses", "m_iDefenses",
parser_state, parser_state,
|defenses| { |defenses| {
if defenses > player_summary.defenses { if defenses > player_summary.defenses {
player_summary.defenses = defenses; player_summary.defenses = defenses;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iKills", "m_iKills",
parser_state, parser_state,
|kills| { |kills| {
if kills > player_summary.kills { if kills > player_summary.kills {
// TODO: This might not be accruate. Tested with a demo file with 89 kills (88 on the scoreboard), // 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. // but only a 83 were reported in the scoring data.
player_summary.kills = kills; player_summary.kills = kills;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iDeaths", "m_iDeaths",
parser_state, parser_state,
|deaths| { |deaths| {
if deaths > player_summary.deaths { if deaths > player_summary.deaths {
player_summary.deaths = deaths; player_summary.deaths = deaths;
} }
}, },
); );
// ignore m_iSuicides // ignore m_iSuicides
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iDominations", "m_iDominations",
parser_state, parser_state,
|dominations| { |dominations| {
if dominations > player_summary.dominations { if dominations > player_summary.dominations {
player_summary.dominations = dominations; player_summary.dominations = dominations;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iRevenge", "m_iRevenge",
parser_state, parser_state,
|revenges| { |revenges| {
if revenges > player_summary.revenges { if revenges > player_summary.revenges {
player_summary.revenges = revenges; player_summary.revenges = revenges;
} }
}, },
); );
// ignore m_iBuildingsBuilt // ignore m_iBuildingsBuilt
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iBuildingsDestroyed", "m_iBuildingsDestroyed",
parser_state, parser_state,
|buildings_destroyed| { |buildings_destroyed| {
if buildings_destroyed > player_summary.buildings_destroyed { if buildings_destroyed > player_summary.buildings_destroyed {
player_summary.buildings_destroyed = buildings_destroyed; player_summary.buildings_destroyed = buildings_destroyed;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iHeadshots", "m_iHeadshots",
parser_state, parser_state,
|headshots| { |headshots| {
if headshots > player_summary.headshots { if headshots > player_summary.headshots {
player_summary.headshots = headshots; player_summary.headshots = headshots;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iBackstabs", "m_iBackstabs",
parser_state, parser_state,
|backstabs| { |backstabs| {
if backstabs > player_summary.backstabs { if backstabs > player_summary.backstabs {
player_summary.backstabs = backstabs; player_summary.backstabs = backstabs;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iHealPoints", "m_iHealPoints",
parser_state, parser_state,
|healing| { |healing| {
if healing > player_summary.healing { if healing > player_summary.healing {
player_summary.healing = healing; player_summary.healing = healing;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iInvulns", "m_iInvulns",
parser_state, parser_state,
|ubercharges| { |ubercharges| {
if ubercharges > player_summary.ubercharges { if ubercharges > player_summary.ubercharges {
player_summary.ubercharges = ubercharges; player_summary.ubercharges = ubercharges;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iTeleports", "m_iTeleports",
parser_state, parser_state,
|teleports| { |teleports| {
if teleports > player_summary.teleports { if teleports > player_summary.teleports {
player_summary.teleports = teleports; player_summary.teleports = teleports;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iDamageDone", "m_iDamageDone",
parser_state, parser_state,
|damage_dealt| { |damage_dealt| {
if damage_dealt > player_summary.damage_dealt { if damage_dealt > player_summary.damage_dealt {
player_summary.damage_dealt = damage_dealt; player_summary.damage_dealt = damage_dealt;
} }
}, },
); );
// ignore m_iCrits // ignore m_iCrits
// ignore m_iResupplyPoints // ignore m_iResupplyPoints
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iKillAssists", "m_iKillAssists",
parser_state, parser_state,
|assists| { |assists| {
if assists > player_summary.assists { if assists > player_summary.assists {
player_summary.assists = assists; player_summary.assists = assists;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iBonusPoints", "m_iBonusPoints",
parser_state, parser_state,
|bonus_points| { |bonus_points| {
if bonus_points > player_summary.bonus_points { if bonus_points > player_summary.bonus_points {
player_summary.bonus_points = bonus_points; player_summary.bonus_points = bonus_points;
} }
}, },
); );
parse_integer_prop( parse_integer_prop(
packet, packet,
"DT_TFPlayerScoringDataExclusive", "DT_TFPlayerScoringDataExclusive",
"m_iPoints", "m_iPoints",
parser_state, parser_state,
|points| { |points| {
if points > player_summary.points { if points > player_summary.points {
player_summary.points = 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
}
} }
} }
"CTFPlayerResource" => { "CTFPlayerResource" => {
@ -370,24 +360,17 @@ impl PlayerSummaryAnalyzer {
// for example, `m_iUserID.024 = 2523` means entity 24 is user 2523 // for example, `m_iUserID.024 = 2523` means entity 24 is user 2523
for i in 0..33 { 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 // 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", "m_iUserID",
format!("{:0>3}", i).as_str(), format!("{:0>3}", i).as_str(),
parser_state, parser_state,
) { ) {
Some(prop) => { let entity_id = EntityId::from(i as u32);
match prop.value { let user_id = UserId::from(x as u32);
SendPropValue::Integer(x) => { self.user_id_map.insert(entity_id, user_id);
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
} }
} }
} }
@ -409,7 +392,7 @@ impl PlayerSummaryAnalyzer {
{ {
self.state self.state
.users .users
.entry(user_info.player_info.user_id.into()) .entry(user_info.player_info.user_id)
.and_modify(|info| { .and_modify(|info| {
info.entity_id = user_info.entity_id; info.entity_id = user_info.entity_id;
}) })

View file

@ -329,7 +329,7 @@ impl Baseline {
self.instances[usize::from(index)] = Some(entity); 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 self.instances
.iter() .iter()
.filter_map(|entity| entity.as_ref().map(|entity| entity.entity_id)) .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 { Ok(match raw {
Options::Num(num) => SendPropIdentifier(num), Options::Num(num) => SendPropIdentifier(num),
Options::Str(s) => { 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) SendPropIdentifier(num)
} }
}) })

View file

@ -65,7 +65,7 @@ fn re_encode_test(input_file: &str) {
} }
let mut re_read = BitReadStream::new(BitReadBuffer::new(&out_buffer, LittleEndian)); let mut re_read = BitReadStream::new(BitReadBuffer::new(&out_buffer, LittleEndian));
let re_decoded = Packet::parse(&mut re_read, &handler.state_handler) let re_decoded = Packet::parse(&mut re_read, &handler.state_handler)
.expect(&format!("while parsing {:?}", packet.packet_type())); .unwrap_or_else(|_| panic!("while parsing {:?}", packet.packet_type()));
assert_eq!(packet.packet_type(), re_decoded.packet_type()); assert_eq!(packet.packet_type(), re_decoded.packet_type());
match (&packet, &re_decoded) { match (&packet, &re_decoded) {
( (