mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
track spawns
This commit is contained in:
parent
f335dc2163
commit
6a47c91636
6 changed files with 176 additions and 58 deletions
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
|
|
@ -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)?;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
if entity_id > 0 && steam_id.len() > 0 {
|
||||||
|
let user = UserInfo {
|
||||||
|
steam_id,
|
||||||
|
user_id,
|
||||||
|
name,
|
||||||
|
entity_id: EntityId::new(entity_id),
|
||||||
};
|
};
|
||||||
let death = Death {
|
|
||||||
assister,
|
self.users.insert(user_id, user);
|
||||||
tick,
|
}
|
||||||
killer: (death.attacker & 255) as u8,
|
Ok(())
|
||||||
weapon: death.weapon,
|
|
||||||
victim: (death.user_id & 255) as u8,
|
|
||||||
};
|
|
||||||
self.deaths.push(death);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue