mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 10:14:06 +02:00
more state handling
This commit is contained in:
parent
713ab0de42
commit
8376353542
8 changed files with 152 additions and 80 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
|
@ -129,6 +129,16 @@ dependencies = [
|
|||
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snap"
|
||||
version = "0.2.5"
|
||||
|
|
@ -178,6 +188,7 @@ dependencies = [
|
|||
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_repr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"snap 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
|
@ -208,6 +219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560"
|
||||
"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c"
|
||||
"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d"
|
||||
"checksum serde_repr 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "27eea49bc616621e68d4de38110ff9e18ab8bd4c61dae48c477e85587571afc1"
|
||||
"checksum snap 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "95d697d63d44ad8b78b8d235bf85b34022a78af292c8918527c5f0cffdde7f43"
|
||||
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||
"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
|
||||
|
|
|
|||
|
|
@ -21,3 +21,4 @@ arraydeque = "0.4"
|
|||
snap = "0.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_repr = "0.1"
|
||||
|
|
|
|||
|
|
@ -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().rounds).unwrap_or("err".to_string());
|
||||
let json = serde_json::to_string(&state).unwrap_or("err".to_string());
|
||||
println!("{}", json);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,9 +101,14 @@ impl BitRead<LittleEndian> for UserMessage {
|
|||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub enum ChatMessageKind {
|
||||
#[serde(rename = "TF_Chat_All")]
|
||||
ChatAll,
|
||||
#[serde(rename = "TF_Chat_Team")]
|
||||
ChatTeam,
|
||||
#[serde(rename = "TF_Chat_AllDead")]
|
||||
ChatAllDead,
|
||||
#[serde(rename = "TF_Chat_Team_Dead")]
|
||||
ChatTeamDead,
|
||||
NameChange,
|
||||
}
|
||||
|
||||
|
|
@ -113,8 +118,12 @@ impl BitRead<LittleEndian> for ChatMessageKind {
|
|||
Ok(match raw.as_str() {
|
||||
"TF_Chat_Team" => ChatMessageKind::ChatTeam,
|
||||
"TF_Chat_AllDead" => ChatMessageKind::ChatAllDead,
|
||||
"TF_Chat_Team_Dead" => ChatMessageKind::ChatTeamDead,
|
||||
"#TF_Name_Change" => ChatMessageKind::NameChange,
|
||||
_ => ChatMessageKind::ChatAll,
|
||||
"TF_Chat_All" => ChatMessageKind::ChatAll,
|
||||
_ => {
|
||||
unreachable!("unknown chat kind")
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_repr::Serialize_repr;
|
||||
|
||||
use crate::{ReadResult, Stream, ParserState};
|
||||
use crate::demo::gameevent_gen::{
|
||||
GameEvent, PlayerDeathEvent, PlayerSpawnEvent, TeamPlayRoundWinEvent,
|
||||
};
|
||||
use crate::demo::message::packetentities::EntityId;
|
||||
use crate::demo::message::usermessage::{ChatMessageKind, UserMessage};
|
||||
use crate::demo::message::{Message, MessageType};
|
||||
use crate::demo::message::packetentities::EntityId;
|
||||
use crate::demo::message::usermessage::{ChatMessageKind, SayText2Message, UserMessage};
|
||||
use crate::demo::packet::stringtable::StringTableEntry;
|
||||
use crate::demo::parser::dispatcher::{MessageHandler, StringTableEntryHandler};
|
||||
use crate::demo::parser::handler::{MessageHandler, StringTableEntryHandler};
|
||||
use crate::demo::vector::Vector;
|
||||
use crate::{ReadResult, Stream};
|
||||
use std::thread::spawn;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct ChatMassage {
|
||||
|
|
@ -21,11 +23,24 @@ pub struct ChatMassage {
|
|||
pub tick: u32,
|
||||
}
|
||||
|
||||
impl ChatMassage {
|
||||
pub fn from_message(message: SayText2Message, tick: u32) -> Self {
|
||||
ChatMassage {
|
||||
kind: message.kind,
|
||||
from: message.from,
|
||||
text: message.text,
|
||||
tick,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Team {
|
||||
Other = 0,
|
||||
Spectator = 1,
|
||||
#[serde(rename = "red")]
|
||||
Red = 2,
|
||||
#[serde(rename = "blue")]
|
||||
Blue = 3,
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +55,8 @@ impl Team {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Serialize_repr, Copy, PartialEq, Eq, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum Class {
|
||||
Other = 0,
|
||||
Scout = 1,
|
||||
|
|
@ -154,10 +170,9 @@ pub struct World {
|
|||
pub struct Analyser {
|
||||
pub chat: Vec<ChatMassage>,
|
||||
pub users: HashMap<UserId, UserInfo>,
|
||||
pub user_spans: Vec<Spawn>,
|
||||
pub user_spawns: Vec<Spawn>,
|
||||
pub deaths: Vec<Death>,
|
||||
pub rounds: Vec<Round>,
|
||||
pub world: World,
|
||||
}
|
||||
|
||||
impl MessageHandler for Analyser {
|
||||
|
|
@ -204,12 +219,7 @@ impl Analyser {
|
|||
match message {
|
||||
UserMessage::SayText2(message) => match message.kind {
|
||||
ChatMessageKind::NameChange => self.change_name(message.from, message.text),
|
||||
_ => self.chat.push(ChatMassage {
|
||||
tick,
|
||||
text: message.text,
|
||||
from: message.from,
|
||||
kind: message.kind,
|
||||
}),
|
||||
_ => self.chat.push(ChatMassage::from_message(message, tick)),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -227,7 +237,7 @@ impl Analyser {
|
|||
fn handle_event(&mut self, event: GameEvent, tick: u32) {
|
||||
match event {
|
||||
GameEvent::PlayerDeath(event) => self.deaths.push(Death::from_event(event, tick)),
|
||||
GameEvent::PlayerSpawn(event) => self.user_spans.push(Spawn::from_event(event, tick)),
|
||||
GameEvent::PlayerSpawn(event) => self.user_spawns.push(Spawn::from_event(event, tick)),
|
||||
GameEvent::TeamPlayRoundWin(event) => {
|
||||
// 6 = time limit
|
||||
if event.win_reason != 6 {
|
||||
|
|
@ -256,4 +266,62 @@ impl Analyser {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_match_state(self, state: ParserState) -> MatchState {
|
||||
let start_tick = self.user_spawns.first().map(|spawn| spawn.tick).unwrap_or(0);
|
||||
MatchState {
|
||||
start_tick,
|
||||
interval_per_tick: state.interval_per_tick,
|
||||
chat: self.chat,
|
||||
deaths: self.deaths,
|
||||
rounds: self.rounds,
|
||||
users: UserState::from_users_and_spawn(self.users, self.user_spawns),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct UserState {
|
||||
pub classes: HashMap<Class, u8>,
|
||||
pub name: String,
|
||||
#[serde(rename = "userId")]
|
||||
pub user_id: UserId,
|
||||
#[serde(rename = "steamId")]
|
||||
pub steam_id: String,
|
||||
pub team: Team,
|
||||
}
|
||||
|
||||
impl UserState {
|
||||
pub fn from_users_and_spawn(users: HashMap<UserId, UserInfo>, spawns: Vec<Spawn>) -> HashMap<UserId, Self> {
|
||||
let mut teams: HashMap<UserId, Team> = HashMap::with_capacity(users.len());
|
||||
let mut classes: HashMap<UserId, HashMap<Class, u8>> = HashMap::with_capacity(9);
|
||||
for spawn in spawns {
|
||||
teams.insert(spawn.user, spawn.team);
|
||||
let user_classes = classes.entry(spawn.user).or_default();
|
||||
let mut class_spawns = user_classes.entry(spawn.class).or_default();
|
||||
*class_spawns += 1;
|
||||
}
|
||||
|
||||
users.into_iter().map(|(user_id, user)| {
|
||||
(user_id, UserState {
|
||||
classes: classes.remove(&user.user_id).unwrap_or(HashMap::new()),
|
||||
team: teams.remove(&user.user_id).unwrap_or(Team::Other),
|
||||
name: user.name,
|
||||
user_id: user.user_id,
|
||||
steam_id: user.steam_id,
|
||||
})
|
||||
}).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct MatchState {
|
||||
pub chat: Vec<ChatMassage>,
|
||||
pub users: HashMap<UserId, UserState>,
|
||||
pub deaths: Vec<Death>,
|
||||
pub rounds: Vec<Round>,
|
||||
#[serde(rename = "startTick")]
|
||||
pub start_tick: u32,
|
||||
#[serde(rename = "intervalPerTick")]
|
||||
pub interval_per_tick: f32,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ use std::rc::Rc;
|
|||
|
||||
use crate::demo::message::{Message, MessageType};
|
||||
use crate::demo::packet::datatable::{SendTable, ServerClass};
|
||||
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||
use crate::demo::packet::Packet;
|
||||
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||
use crate::demo::parser::analyser::{Analyser, MatchState};
|
||||
use crate::ParserState;
|
||||
|
||||
pub trait MessageHandler {
|
||||
|
|
@ -19,28 +20,21 @@ pub trait StringTableEntryHandler {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Dispatcher {
|
||||
pub struct DemoHandler {
|
||||
tick: u32,
|
||||
string_table_names: Vec<String>,
|
||||
on_message: Vec<Rc<RefCell<MessageHandler>>>,
|
||||
on_string_table_entry: Vec<Rc<RefCell<StringTableEntryHandler>>>,
|
||||
state_handler: Option<Rc<RefCell<ParserState>>>,
|
||||
analyser: Analyser,
|
||||
state_handler: ParserState,
|
||||
}
|
||||
|
||||
impl Dispatcher {
|
||||
pub fn register_message_handler(&mut self, handler: Rc<RefCell<MessageHandler>>) {
|
||||
self.on_message.push(handler);
|
||||
impl DemoHandler {
|
||||
pub fn new() -> Self {
|
||||
DemoHandler {
|
||||
tick: 0,
|
||||
string_table_names: Vec::new(),
|
||||
analyser: Analyser::new(),
|
||||
state_handler: ParserState::new(),
|
||||
}
|
||||
|
||||
pub fn set_state_handler(&mut self, handler: Rc<RefCell<ParserState>>) {
|
||||
self.state_handler = Some(handler);
|
||||
}
|
||||
|
||||
pub fn register_string_table_entry_handler(
|
||||
&mut self,
|
||||
handler: Rc<RefCell<StringTableEntryHandler>>,
|
||||
) {
|
||||
self.on_string_table_entry.push(handler);
|
||||
}
|
||||
|
||||
pub fn handle_packet(&mut self, packet: Packet) {
|
||||
|
|
@ -78,9 +72,7 @@ impl Dispatcher {
|
|||
self.handle_string_entry(&table.name, entry_index, entry);
|
||||
}
|
||||
|
||||
if let Some(handler) = &self.state_handler {
|
||||
handler.borrow_mut().handle_string_table(table);
|
||||
}
|
||||
self.state_handler.handle_string_table(table);
|
||||
}
|
||||
|
||||
fn handle_table_update(&mut self, table_id: u8, entries: HashMap<u16, StringTableEntry>) {
|
||||
|
|
@ -97,29 +89,31 @@ impl Dispatcher {
|
|||
}
|
||||
|
||||
fn handle_data_table(&mut self, send_tables: Vec<SendTable>, server_classes: Vec<ServerClass>) {
|
||||
if let Some(handler) = &self.state_handler {
|
||||
handler
|
||||
.borrow_mut()
|
||||
.handle_data_table(send_tables, server_classes);
|
||||
}
|
||||
self.state_handler.handle_data_table(send_tables, server_classes);
|
||||
}
|
||||
|
||||
fn handle_string_entry(&mut self, table: &String, index: usize, entries: &StringTableEntry) {
|
||||
for handler in self.on_string_table_entry.iter() {
|
||||
handler
|
||||
.borrow_mut()
|
||||
.handle_string_entry(table, index, entries);
|
||||
}
|
||||
self.state_handler.handle_string_entry(table, index, entries);
|
||||
self.analyser.handle_string_entry(table, index, entries);
|
||||
}
|
||||
|
||||
fn handle_message(&mut self, message: Message) {
|
||||
let message_type = message.get_message_type();
|
||||
for handler in self.on_message.iter() {
|
||||
let mut handler = handler.borrow_mut();
|
||||
if handler.does_handle(message_type) {
|
||||
handler.handle_message(message, self.tick);
|
||||
if self.state_handler.does_handle(message_type) {
|
||||
self.state_handler.handle_message(message, self.tick);
|
||||
return;
|
||||
}
|
||||
if self.analyser.does_handle(message_type) {
|
||||
self.analyser.handle_message(message, self.tick);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_match_state(mut self) -> MatchState {
|
||||
self.analyser.get_match_state(self.state_handler)
|
||||
}
|
||||
|
||||
pub fn get_parser_state(&self) -> &ParserState {
|
||||
&self.state_handler
|
||||
}
|
||||
}
|
||||
|
|
@ -8,15 +8,15 @@ use crate::demo::header::Header;
|
|||
use crate::demo::message::{Message, MessageType};
|
||||
use crate::demo::packet::stringtable::StringTableEntry;
|
||||
use crate::demo::packet::Packet;
|
||||
use crate::demo::parser::analyser::Analyser;
|
||||
use crate::demo::parser::dispatcher::{Dispatcher, MessageHandler, StringTableEntryHandler};
|
||||
use crate::demo::parser::analyser::{Analyser, MatchState};
|
||||
use crate::demo::parser::handler::{DemoHandler, MessageHandler, StringTableEntryHandler};
|
||||
pub use crate::demo::parser::state::ParserState;
|
||||
use crate::Stream;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
|
||||
mod analyser;
|
||||
mod dispatcher;
|
||||
mod handler;
|
||||
mod state;
|
||||
|
||||
/// Errors that can occur during parsing
|
||||
|
|
@ -79,46 +79,32 @@ impl<T: BitRead<LittleEndian>> Parse for T {
|
|||
|
||||
pub struct DemoParser {
|
||||
stream: Stream,
|
||||
state: Rc<RefCell<ParserState>>,
|
||||
analyser: Rc<RefCell<Analyser>>,
|
||||
dispatcher: Dispatcher,
|
||||
handler: DemoHandler,
|
||||
}
|
||||
|
||||
impl DemoParser {
|
||||
pub fn new(stream: Stream) -> Self {
|
||||
let state = Rc::new(RefCell::new(ParserState::new()));
|
||||
let analyser = Rc::new(RefCell::new(Analyser::new()));
|
||||
let mut dispatcher = Dispatcher::default();
|
||||
|
||||
dispatcher.register_message_handler(Rc::clone(&state) as Rc<RefCell<MessageHandler>>);
|
||||
dispatcher.register_message_handler(Rc::clone(&analyser) as Rc<RefCell<MessageHandler>>);
|
||||
dispatcher.register_string_table_entry_handler(
|
||||
Rc::clone(&analyser) as Rc<RefCell<StringTableEntryHandler>>
|
||||
);
|
||||
dispatcher.set_state_handler(Rc::clone(&state));
|
||||
|
||||
DemoParser {
|
||||
state,
|
||||
stream,
|
||||
analyser,
|
||||
dispatcher,
|
||||
handler: DemoHandler::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read<T: Parse>(&mut self) -> Result<T> {
|
||||
T::parse(&mut self.stream, self.state.borrow().deref())
|
||||
T::parse(&mut self.stream, self.handler.get_parser_state())
|
||||
}
|
||||
|
||||
pub fn parse_demo(mut self) -> Result<(Header, Rc<RefCell<Analyser>>)> {
|
||||
pub fn parse_demo(mut self) -> Result<(Header, MatchState)> {
|
||||
let header = self.read::<Header>()?;
|
||||
loop {
|
||||
let packet = self.read::<Packet>()?;
|
||||
match packet {
|
||||
Packet::Stop(_) => break,
|
||||
packet => self.dispatcher.handle_packet(packet),
|
||||
packet => self.handler.handle_packet(packet),
|
||||
};
|
||||
}
|
||||
Ok((header, self.analyser.clone()))
|
||||
Ok((header, self.handler.get_match_state()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::demo::message::{Message, MessageType};
|
|||
use crate::demo::packet::datatable::{SendTable, ServerClass};
|
||||
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||
use crate::demo::packet::Packet;
|
||||
use crate::demo::parser::dispatcher::{MessageHandler, StringTableEntryHandler};
|
||||
use crate::demo::parser::handler::{MessageHandler, StringTableEntryHandler};
|
||||
use crate::demo::sendprop::SendProp;
|
||||
use crate::Stream;
|
||||
use std::mem::replace;
|
||||
|
|
@ -26,6 +26,7 @@ pub struct ParserState {
|
|||
pub server_classes: Vec<ServerClass>,
|
||||
pub instance_baselines: [HashMap<EntityId, Vec<SendProp>>; 2],
|
||||
pub game: String,
|
||||
pub interval_per_tick: f32,
|
||||
}
|
||||
|
||||
pub struct StaticBaseline {
|
||||
|
|
@ -102,6 +103,7 @@ impl MessageHandler for ParserState {
|
|||
Message::ServerInfo(message) => {
|
||||
self.version = message.version;
|
||||
self.game = message.game;
|
||||
self.interval_per_tick = message.interval_per_tick
|
||||
}
|
||||
Message::GameEventList(message) => {
|
||||
self.event_definitions = message.event_list;
|
||||
|
|
@ -124,7 +126,7 @@ impl StringTableEntryHandler for ParserState {
|
|||
},
|
||||
_ => unreachable!("missing baseline"),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue