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

track spawns

This commit is contained in:
Robin Appelman 2019-03-04 20:56:12 +01:00
commit 6a47c91636
6 changed files with 176 additions and 58 deletions

View file

@ -13,7 +13,7 @@ fn main() -> std::result::Result<(), Box<ParseError>> {
//dbg!(header);
//dbg!(state.deaths);
//std::thread::sleep(std::time::Duration::from_secs(5));
//let json = serde_json::to_string(&state.borrow().deaths).unwrap_or("err".to_string());
//println!("{}", json);
let json = serde_json::to_string(&state.borrow().user_spans).unwrap_or("err".to_string());
println!("{}", json);
Ok(())
}

View file

@ -1,12 +1,19 @@
use bitstream_reader::{BitRead, BitReadSized, LittleEndian};
use serde::Serialize;
use crate::demo::packet::datatable::ServerClass;
use crate::demo::sendprop::SendProp;
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct EntityId(u32);
impl EntityId {
pub fn new(num: u32) -> Self {
EntityId(num)
}
}
#[derive(BitRead, Clone, Copy, Debug)]
#[discriminant_bits = 3]
pub enum PVS {

View file

@ -240,10 +240,7 @@ fn read_table_entry(
} else {
None
}
.map(|stream| ExtraData {
len: stream.bit_len() as u16 / 8,
data: stream,
});
.map(ExtraData::new);
Ok(match existing_entry {
Some(existing_entry) => {

View file

@ -100,21 +100,21 @@ impl BitRead<LittleEndian> for UserMessage {
}
#[derive(Debug, Clone, Serialize)]
pub enum SayText2Kind {
pub enum ChatMessageKind {
ChatAll,
ChatTeam,
ChatAllDead,
NameChange,
}
impl BitRead<LittleEndian> for SayText2Kind {
impl BitRead<LittleEndian> for ChatMessageKind {
fn read(stream: &mut Stream) -> ReadResult<Self> {
let raw: String = stream.read()?;
Ok(match raw.as_str() {
"TF_Chat_Team" => SayText2Kind::ChatTeam,
"TF_Chat_AllDead" => SayText2Kind::ChatAllDead,
"#TF_Name_Change" => SayText2Kind::NameChange,
_ => SayText2Kind::ChatAll,
"TF_Chat_Team" => ChatMessageKind::ChatTeam,
"TF_Chat_AllDead" => ChatMessageKind::ChatAllDead,
"#TF_Name_Change" => ChatMessageKind::NameChange,
_ => ChatMessageKind::ChatAll,
})
}
}
@ -123,7 +123,7 @@ impl BitRead<LittleEndian> for SayText2Kind {
pub struct SayText2Message {
pub client: u8,
pub raw: u8,
pub kind: SayText2Kind,
pub kind: ChatMessageKind,
pub from: String,
pub text: String,
}
@ -132,7 +132,7 @@ impl BitRead<LittleEndian> for SayText2Message {
fn read(stream: &mut Stream) -> ReadResult<Self> {
let client = stream.read()?;
let raw = stream.read()?;
let (kind, from, text): (SayText2Kind, String, String) = if stream.read::<u8>()? == 1 {
let (kind, from, text): (ChatMessageKind, String, String) = if stream.read::<u8>()? == 1 {
let first = stream.read::<u8>()?;
if first == 7 {
let _color = stream.read_string(Some(6))?;
@ -149,10 +149,10 @@ impl BitRead<LittleEndian> for SayText2Message {
text.bytes().skip(start + 1).take(end - start - 1).collect(),
)?;
let text: String = String::from_utf8(text.bytes().skip(end + 5).collect())?;
let kind = SayText2Kind::ChatAllDead;
let kind = ChatMessageKind::ChatAllDead;
(kind, from, text)
} else {
(SayText2Kind::ChatAll, "".to_owned(), text)
(ChatMessageKind::ChatAll, "".to_owned(), text)
}
} else {
let _ = stream.set_pos(stream.pos() - 8)?;

View file

@ -2,9 +2,9 @@ use std::fmt;
use bitstream_reader::{BitRead, LittleEndian};
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
use crate::demo::message::stringtable::StringTableMeta;
use crate::demo::sendprop::SendPropFlag::Exclude;
use crate::{Parse, ParseError, ParserState, ReadResult, Result, Stream};
#[derive(BitRead, Clone, Copy, Debug)]
pub struct FixedUserdataSize {
@ -57,14 +57,24 @@ impl BitRead<LittleEndian> for StringTable {
}
}
#[derive(BitRead, Clone)]
#[derive(BitRead, Clone, Debug)]
#[endianness = "LittleEndian"]
pub struct ExtraData {
pub len: u16,
#[size = "len * 8"]
pub byte_len: u16,
#[size = "byte_len * 8"]
pub data: Stream,
}
impl ExtraData {
pub fn new(data: Stream) -> Self {
let byte_len = (data.bit_len() / 8) as u16;
ExtraData {
byte_len,
data,
}
}
}
#[derive(BitRead, Clone, Default)]
#[endianness = "LittleEndian"]
pub struct StringTableEntry {
@ -78,8 +88,8 @@ impl fmt::Debug for StringTableEntry {
None => write!(f, "StringTableEntry {{ text: \"{}\" }}", self.text),
Some(extra_data) => write!(
f,
"StringTableEntry{{ text: \"{}\" extra_data: {} bits }}",
self.text, extra_data.len
"StringTableEntry{{ text: \"{}\" extra_data: {} bytes }}",
self.text, extra_data.byte_len
),
}
}

View file

@ -1,43 +1,130 @@
use std::collections::HashMap;
use serde::Serialize;
use crate::demo::gameevent_gen::{GameEvent, PlayerDeathEvent};
use crate::{ReadResult, Stream};
use crate::demo::gameevent_gen::{GameEvent, PlayerDeathEvent, PlayerSpawnEvent};
use crate::demo::message::{Message, MessageType};
use crate::demo::message::usermessage::{SayText2Kind, UserMessage};
use crate::demo::message::packetentities::EntityId;
use crate::demo::message::usermessage::{ChatMessageKind, UserMessage};
use crate::demo::packet::stringtable::StringTableEntry;
use crate::demo::parser::dispatcher::{MessageHandler, StringTableEntryHandler};
use crate::demo::vector::Vector;
use crate::demo::packet::stringtable::StringTableEntry;
#[derive(Debug, Clone, Serialize)]
pub struct ChatMassage {
pub kind: SayText2Kind,
pub kind: ChatMessageKind,
pub from: String,
pub text: String,
pub tick: u32,
}
#[derive(Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash)]
pub enum Team {
Other = 0,
Spectator = 1,
Red = 2,
Blue = 3,
}
impl Team {
pub fn new(number: u16) -> Self {
match number {
1 => Team::Spectator,
2 => Team::Red,
3 => Team::Blue,
_ => Team::Other
}
}
}
#[derive(Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash)]
pub enum Class {
Other = 0,
Scout = 1,
Sniper = 2,
Solder = 3,
Demoman = 4,
Medic = 5,
Heavy = 6,
Pyro = 7,
Spy = 8,
Engineer = 9,
}
impl Class {
pub fn new(number: u16) -> Self {
match number {
1 => Class::Scout,
2 => Class::Sniper,
3 => Class::Solder,
4 => Class::Demoman,
5 => Class::Medic,
6 => Class::Heavy,
7 => Class::Pyro,
8 => Class::Spy,
9 => Class::Engineer,
_ => Class::Other
}
}
}
#[derive(Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash)]
pub struct UserId(u8);
#[derive(Debug, Clone, Serialize)]
pub struct UserEntityInfo {
pub name: String,
pub user_id: u8,
pub steam_id: String,
pub entity_id: u32,
pub struct Spawn {
pub user: UserId,
pub class: Class,
pub team: Team,
pub tick: u32,
}
impl Spawn {
pub fn from_event(event: PlayerSpawnEvent, tick: u32) -> Self {
Spawn {
user: UserId((event.user_id & 255) as u8),
class: Class::new(event.class),
team: Team::new(event.team),
tick,
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct UserInfo {
pub team: String,
pub entity_info: UserEntityInfo,
pub name: String,
pub user_id: UserId,
pub steam_id: String,
pub entity_id: EntityId,
}
#[derive(Debug, Clone, Serialize)]
pub struct Death {
pub weapon: String,
pub victim: u8,
pub assister: Option<u8>,
pub killer: u8,
pub victim: UserId,
pub assister: Option<UserId>,
pub killer: UserId,
pub tick: u32,
}
impl Death {
pub fn from_event(event: PlayerDeathEvent, tick: u32) -> Self {
let assister = if event.assister < (16 * 1024) {
Some(UserId((event.assister & 255) as u8))
} else {
None
};
Death {
assister,
tick,
killer: UserId((event.attacker & 255) as u8),
weapon: event.weapon,
victim: UserId((event.user_id & 255) as u8),
}
}
}
#[derive(Debug, Clone, Serialize)]
pub struct Round {
winner: String,
@ -54,7 +141,8 @@ pub struct World {
#[derive(Default, Debug, Serialize)]
pub struct Analyser {
pub chat: Vec<ChatMassage>,
pub users: Vec<UserInfo>,
pub users: HashMap<UserId, UserInfo>,
pub user_spans: Vec<Spawn>,
pub deaths: Vec<Death>,
pub rounds: Vec<Round>,
pub world: World,
@ -80,7 +168,19 @@ impl MessageHandler for Analyser {
impl StringTableEntryHandler for Analyser {
fn handle_string_entry(&mut self, table: &String, index: usize, entry: &StringTableEntry) {
match table.as_str() {
"userinfo" => {
match &entry.extra_data {
Some(data) => {
if data.byte_len > 32 {
let _ = self.parse_user_info(&entry.text, data.data.clone());
}
}
_ => {} //unreachable!("no userdata {}, {}", table, &entry.text)
}
}
_ => {}
}
}
}
@ -93,7 +193,7 @@ impl Analyser {
match message {
UserMessage::SayText2(message) => {
match message.kind {
SayText2Kind::NameChange => self.change_name(message.from, message.text),
ChatMessageKind::NameChange => self.change_name(message.from, message.text),
_ => self.chat.push(ChatMassage {
tick,
text: message.text,
@ -107,9 +207,9 @@ impl Analyser {
}
fn change_name(&mut self, from: String, to: String) {
for user in &mut self.users {
if user.entity_info.name == from {
user.entity_info.name = to;
for (id, user) in self.users.iter_mut() {
if user.name == from {
user.name = to;
return;
}
}
@ -117,24 +217,28 @@ impl Analyser {
fn handle_event(&mut self, event: GameEvent, tick: u32) {
match event {
GameEvent::PlayerDeath(event) => self.handle_death(event, tick),
GameEvent::PlayerDeath(event) => self.deaths.push(Death::from_event(event, tick)),
GameEvent::PlayerSpawn(event) => self.user_spans.push(Spawn::from_event(event, tick)),
_ => {}
}
}
fn handle_death(&mut self, death: PlayerDeathEvent, tick: u32) {
let assister = if death.assister < (16 * 1024) {
Some((death.assister & 255) as u8)
} else {
None
};
let death = Death {
assister,
tick,
killer: (death.attacker & 255) as u8,
weapon: death.weapon,
victim: (death.user_id & 255) as u8,
};
self.deaths.push(death);
fn parse_user_info(&mut self, text: &String, mut data: Stream) -> ReadResult<()> {
let name: String = data.read_sized(32)?;
let user_id = UserId((data.read::<u32>()? & 255) as u8);
let steam_id: String = data.read()?;
let entity_id: u32 = text.parse().unwrap_or_default();
if entity_id > 0 && steam_id.len() > 0 {
let user = UserInfo {
steam_id,
user_id,
name,
entity_id: EntityId::new(entity_id),
};
self.users.insert(user_id, user);
}
Ok(())
}
}