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!(header);
//dbg!(state.deaths); //dbg!(state.deaths);
//std::thread::sleep(std::time::Duration::from_secs(5)); //std::thread::sleep(std::time::Duration::from_secs(5));
//let json = serde_json::to_string(&state.borrow().deaths).unwrap_or("err".to_string()); let json = serde_json::to_string(&state.borrow().user_spans).unwrap_or("err".to_string());
//println!("{}", json); println!("{}", json);
Ok(()) Ok(())
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -1,43 +1,130 @@
use std::collections::HashMap;
use serde::Serialize; 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::{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::parser::dispatcher::{MessageHandler, StringTableEntryHandler};
use crate::demo::vector::Vector; use crate::demo::vector::Vector;
use crate::demo::packet::stringtable::StringTableEntry;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct ChatMassage { pub struct ChatMassage {
pub kind: SayText2Kind, pub kind: ChatMessageKind,
pub from: String, pub from: String,
pub text: String, pub text: String,
pub tick: u32, 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)] #[derive(Debug, Clone, Serialize)]
pub struct UserEntityInfo { pub struct Spawn {
pub name: String, pub user: UserId,
pub user_id: u8, pub class: Class,
pub steam_id: String, pub team: Team,
pub entity_id: u32, 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)] #[derive(Debug, Clone, Serialize)]
pub struct UserInfo { pub struct UserInfo {
pub team: String, pub name: String,
pub entity_info: UserEntityInfo, pub user_id: UserId,
pub steam_id: String,
pub entity_id: EntityId,
} }
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct Death { pub struct Death {
pub weapon: String, pub weapon: String,
pub victim: u8, pub victim: UserId,
pub assister: Option<u8>, pub assister: Option<UserId>,
pub killer: u8, pub killer: UserId,
pub tick: u32, 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)] #[derive(Debug, Clone, Serialize)]
pub struct Round { pub struct Round {
winner: String, winner: String,
@ -54,7 +141,8 @@ pub struct World {
#[derive(Default, Debug, Serialize)] #[derive(Default, Debug, Serialize)]
pub struct Analyser { pub struct Analyser {
pub chat: Vec<ChatMassage>, pub chat: Vec<ChatMassage>,
pub users: Vec<UserInfo>, pub users: HashMap<UserId, UserInfo>,
pub user_spans: Vec<Spawn>,
pub deaths: Vec<Death>, pub deaths: Vec<Death>,
pub rounds: Vec<Round>, pub rounds: Vec<Round>,
pub world: World, pub world: World,
@ -80,7 +168,19 @@ impl MessageHandler for Analyser {
impl StringTableEntryHandler for Analyser { impl StringTableEntryHandler for Analyser {
fn handle_string_entry(&mut self, table: &String, index: usize, entry: &StringTableEntry) { 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 { match message {
UserMessage::SayText2(message) => { UserMessage::SayText2(message) => {
match message.kind { 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 { _ => self.chat.push(ChatMassage {
tick, tick,
text: message.text, text: message.text,
@ -107,9 +207,9 @@ impl Analyser {
} }
fn change_name(&mut self, from: String, to: String) { fn change_name(&mut self, from: String, to: String) {
for user in &mut self.users { for (id, user) in self.users.iter_mut() {
if user.entity_info.name == from { if user.name == from {
user.entity_info.name = to; user.name = to;
return; return;
} }
} }
@ -117,24 +217,28 @@ impl Analyser {
fn handle_event(&mut self, event: GameEvent, tick: u32) { fn handle_event(&mut self, event: GameEvent, tick: u32) {
match event { 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) { fn parse_user_info(&mut self, text: &String, mut data: Stream) -> ReadResult<()> {
let assister = if death.assister < (16 * 1024) { let name: String = data.read_sized(32)?;
Some((death.assister & 255) as u8) let user_id = UserId((data.read::<u32>()? & 255) as u8);
} else { let steam_id: String = data.read()?;
None let entity_id: u32 = text.parse().unwrap_or_default();
};
let death = Death { if entity_id > 0 && steam_id.len() > 0 {
assister, let user = UserInfo {
tick, steam_id,
killer: (death.attacker & 255) as u8, user_id,
weapon: death.weapon, name,
victim: (death.user_id & 255) as u8, entity_id: EntityId::new(entity_id),
}; };
self.deaths.push(death);
self.users.insert(user_id, user);
}
Ok(())
} }
} }