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!(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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue