mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
make userid field private
This commit is contained in:
parent
f57ed1ca1c
commit
d82555e58a
2 changed files with 211 additions and 109 deletions
|
|
@ -216,7 +216,7 @@ impl From<HashMap<Class, u8>> for ClassList {
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Default,
|
Debug, Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Default,
|
||||||
)]
|
)]
|
||||||
pub struct UserId(pub u16);
|
pub struct UserId(u16);
|
||||||
|
|
||||||
impl<E: Endianness> BitWrite<E> for UserId {
|
impl<E: Endianness> BitWrite<E> for UserId {
|
||||||
fn write(&self, stream: &mut BitWriteStream<E>) -> ReadResult<()> {
|
fn write(&self, stream: &mut BitWriteStream<E>) -> ReadResult<()> {
|
||||||
|
|
@ -236,12 +236,6 @@ impl From<u16> for UserId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u8> for UserId {
|
|
||||||
fn from(int: u8) -> Self {
|
|
||||||
UserId(int as u16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<UserId> for u16 {
|
impl From<UserId> for u16 {
|
||||||
fn from(id: UserId) -> Self {
|
fn from(id: UserId) -> Self {
|
||||||
id.0
|
id.0
|
||||||
|
|
|
||||||
|
|
@ -54,19 +54,16 @@ impl MessageHandler for PlayerSummaryAnalyzer {
|
||||||
type Output = PlayerSummaryState;
|
type Output = PlayerSummaryState;
|
||||||
|
|
||||||
fn does_handle(message_type: MessageType) -> bool {
|
fn does_handle(message_type: MessageType) -> bool {
|
||||||
matches!(
|
matches!(message_type, MessageType::PacketEntities)
|
||||||
message_type,
|
|
||||||
MessageType::PacketEntities
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
match message {
|
||||||
Message::PacketEntities(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);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -103,8 +100,15 @@ impl BorrowMessageHandler for PlayerSummaryAnalyzer {
|
||||||
*
|
*
|
||||||
* parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iPoints", |points| { println!("Scored {} points", points) });
|
* parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iPoints", |points| { println!("Scored {} points", points) });
|
||||||
*/
|
*/
|
||||||
fn parse_integer_prop<F>(packet: &PacketEntity, table: &str, name: &str, parser_state: &ParserState, handler: F)
|
fn parse_integer_prop<F>(
|
||||||
where F: FnOnce(u32) {
|
packet: &PacketEntity,
|
||||||
|
table: &str,
|
||||||
|
name: &str,
|
||||||
|
parser_state: &ParserState,
|
||||||
|
handler: F,
|
||||||
|
) where
|
||||||
|
F: FnOnce(u32),
|
||||||
|
{
|
||||||
use crate::demo::sendprop::SendPropValue;
|
use crate::demo::sendprop::SendPropValue;
|
||||||
|
|
||||||
match packet.get_prop_by_name(table, name, parser_state) {
|
match packet.get_prop_by_name(table, name, parser_state) {
|
||||||
|
|
@ -113,7 +117,7 @@ fn parse_integer_prop<F>(packet: &PacketEntity, table: &str, name: &str, parser_
|
||||||
SendPropValue::Integer(val) => handler(val as u32),
|
SendPropValue::Integer(val) => handler(val as u32),
|
||||||
_ => {} // not an integer, ignore
|
_ => {} // not an integer, ignore
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
None => {} // the packet doesn't have this property
|
None => {} // the packet doesn't have this property
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -128,7 +132,10 @@ impl PlayerSummaryAnalyzer {
|
||||||
|
|
||||||
// println!("Known server classes: {:?}", parser_state.server_classes);
|
// println!("Known server classes: {:?}", parser_state.server_classes);
|
||||||
|
|
||||||
if let Some(class) = parser_state.server_classes.get(<ClassId as Into<usize>>::into(packet.server_class)) {
|
if let Some(class) = parser_state
|
||||||
|
.server_classes
|
||||||
|
.get(<ClassId as Into<usize>>::into(packet.server_class))
|
||||||
|
{
|
||||||
// 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" => {
|
||||||
|
|
@ -168,124 +175,225 @@ impl PlayerSummaryAnalyzer {
|
||||||
* 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(packet, "DT_TFPlayerScoringDataExclusive", "m_iCaptures", parser_state, |captures| {
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iCaptures",
|
||||||
|
parser_state,
|
||||||
|
|captures| {
|
||||||
if captures > player_summary.captures {
|
if captures > player_summary.captures {
|
||||||
player_summary.captures = captures;
|
player_summary.captures = captures;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iDefenses", parser_state, |defenses| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iDefenses",
|
||||||
|
parser_state,
|
||||||
|
|defenses| {
|
||||||
if defenses > player_summary.defenses {
|
if defenses > player_summary.defenses {
|
||||||
player_summary.defenses = defenses;
|
player_summary.defenses = defenses;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iKills", parser_state, |kills| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iKills",
|
||||||
|
parser_state,
|
||||||
|
|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(packet, "DT_TFPlayerScoringDataExclusive", "m_iDeaths", parser_state, |deaths| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iDeaths",
|
||||||
|
parser_state,
|
||||||
|
|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(packet, "DT_TFPlayerScoringDataExclusive", "m_iDominations", parser_state, |dominations| {
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iDominations",
|
||||||
|
parser_state,
|
||||||
|
|dominations| {
|
||||||
if dominations > player_summary.dominations {
|
if dominations > player_summary.dominations {
|
||||||
player_summary.dominations = dominations;
|
player_summary.dominations = dominations;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iRevenge", parser_state, |revenges| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iRevenge",
|
||||||
|
parser_state,
|
||||||
|
|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(packet, "DT_TFPlayerScoringDataExclusive", "m_iBuildingsDestroyed", parser_state, |buildings_destroyed| {
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iBuildingsDestroyed",
|
||||||
|
parser_state,
|
||||||
|
|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(packet, "DT_TFPlayerScoringDataExclusive", "m_iHeadshots", parser_state, |headshots| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iHeadshots",
|
||||||
|
parser_state,
|
||||||
|
|headshots| {
|
||||||
if headshots > player_summary.headshots {
|
if headshots > player_summary.headshots {
|
||||||
player_summary.headshots = headshots;
|
player_summary.headshots = headshots;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iBackstabs", parser_state, |backstabs| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iBackstabs",
|
||||||
|
parser_state,
|
||||||
|
|backstabs| {
|
||||||
if backstabs > player_summary.backstabs {
|
if backstabs > player_summary.backstabs {
|
||||||
player_summary.backstabs = backstabs;
|
player_summary.backstabs = backstabs;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iHealPoints", parser_state, |healing| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iHealPoints",
|
||||||
|
parser_state,
|
||||||
|
|healing| {
|
||||||
if healing > player_summary.healing {
|
if healing > player_summary.healing {
|
||||||
player_summary.healing = healing;
|
player_summary.healing = healing;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iInvulns", parser_state, |ubercharges| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iInvulns",
|
||||||
|
parser_state,
|
||||||
|
|ubercharges| {
|
||||||
if ubercharges > player_summary.ubercharges {
|
if ubercharges > player_summary.ubercharges {
|
||||||
player_summary.ubercharges = ubercharges;
|
player_summary.ubercharges = ubercharges;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iTeleports", parser_state, |teleports| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iTeleports",
|
||||||
|
parser_state,
|
||||||
|
|teleports| {
|
||||||
if teleports > player_summary.teleports {
|
if teleports > player_summary.teleports {
|
||||||
player_summary.teleports = teleports;
|
player_summary.teleports = teleports;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iDamageDone", parser_state, |damage_dealt| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iDamageDone",
|
||||||
|
parser_state,
|
||||||
|
|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(packet, "DT_TFPlayerScoringDataExclusive", "m_iKillAssists", parser_state, |assists| {
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iKillAssists",
|
||||||
|
parser_state,
|
||||||
|
|assists| {
|
||||||
if assists > player_summary.assists {
|
if assists > player_summary.assists {
|
||||||
player_summary.assists = assists;
|
player_summary.assists = assists;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
parse_integer_prop(packet, "DT_TFPlayerScoringDataExclusive", "m_iBonusPoints", parser_state, |bonus_points| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iBonusPoints",
|
||||||
|
parser_state,
|
||||||
|
|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(packet, "DT_TFPlayerScoringDataExclusive", "m_iPoints", parser_state, |points| {
|
);
|
||||||
|
parse_integer_prop(
|
||||||
|
packet,
|
||||||
|
"DT_TFPlayerScoringDataExclusive",
|
||||||
|
"m_iPoints",
|
||||||
|
parser_state,
|
||||||
|
|points| {
|
||||||
if points > player_summary.points {
|
if points > player_summary.points {
|
||||||
player_summary.points = points;
|
player_summary.points = points;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
// Player entity likely spawned before the player was assigned to it?
|
// Player entity likely spawned before the player was assigned to it?
|
||||||
// This can rarely happen, but doesn't seem to affect the end result
|
// This can rarely happen, but doesn't seem to affect the end result
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
"CTFPlayerResource" => {
|
"CTFPlayerResource" => {
|
||||||
// Player summaries - including entity IDs!
|
// Player summaries - including entity IDs!
|
||||||
// look for props like m_iUserID.<entity_id> = <user_id>
|
// look for props like m_iUserID.<entity_id> = <user_id>
|
||||||
// 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 { // 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
|
for i in 0..33 {
|
||||||
match packet.get_prop_by_name("m_iUserID", format!("{:0>3}", i).as_str(), parser_state) {
|
// 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(
|
||||||
|
"m_iUserID",
|
||||||
|
format!("{:0>3}", i).as_str(),
|
||||||
|
parser_state,
|
||||||
|
) {
|
||||||
Some(prop) => {
|
Some(prop) => {
|
||||||
match prop.value {
|
match prop.value {
|
||||||
SendPropValue::Integer(x) => {
|
SendPropValue::Integer(x) => {
|
||||||
let entity_id = EntityId::from(i as u32);
|
let entity_id = EntityId::from(i as u32);
|
||||||
let user_id = UserId::from(x as u32);
|
let user_id = UserId::from(x as u32);
|
||||||
self.user_id_map.insert(entity_id, user_id);
|
self.user_id_map.insert(entity_id, user_id);
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// These properties should all be integers...
|
// These properties should all be integers...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
None => {} // ignore, no property for this entity was included
|
None => {} // ignore, no property for this entity was included
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_other => {
|
_other => {
|
||||||
// Don't care
|
// Don't care
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue