mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
clippy fixes
This commit is contained in:
parent
44c560713a
commit
a561cbaf64
12 changed files with 345 additions and 398 deletions
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<()> {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)? {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue