mirror of
https://codeberg.org/demostf/parser.git
synced 2026-06-03 18:24:05 +02:00
some state rework
This commit is contained in:
parent
5886debbfb
commit
eb62b5960e
5 changed files with 203 additions and 78 deletions
|
|
@ -12,8 +12,8 @@ fn main() -> std::result::Result<(), Box<ParseError>> {
|
||||||
let (header, state) = parser.parse_demo()?;
|
let (header, state) = parser.parse_demo()?;
|
||||||
//dbg!(header);
|
//dbg!(header);
|
||||||
//dbg!(state.deaths);
|
//dbg!(state.deaths);
|
||||||
//let json = serde_json::to_string(&state.deaths).unwrap_or("err".to_string());
|
|
||||||
//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());
|
||||||
//println!("{}", json);
|
//println!("{}", json);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ use serde::Serialize;
|
||||||
use crate::demo::gameevent_gen::{GameEvent, PlayerDeathEvent};
|
use crate::demo::gameevent_gen::{GameEvent, PlayerDeathEvent};
|
||||||
use crate::demo::message::{Message, MessageType};
|
use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::message::usermessage::{SayText2Kind, UserMessage};
|
use crate::demo::message::usermessage::{SayText2Kind, UserMessage};
|
||||||
use crate::demo::parser::MessageHandler;
|
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 {
|
||||||
|
|
@ -77,6 +78,12 @@ impl MessageHandler for Analyser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StringTableEntryHandler for Analyser {
|
||||||
|
fn handle_string_entry(&mut self, table: &String, index: usize, entry: &StringTableEntry) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Analyser {
|
impl Analyser {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
|
|
|
||||||
116
src/demo/parser/dispatcher.rs
Normal file
116
src/demo/parser/dispatcher.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::demo::message::{Message, MessageType};
|
||||||
|
use crate::demo::packet::datatable::{SendTable, ServerClass};
|
||||||
|
use crate::demo::packet::Packet;
|
||||||
|
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||||
|
use crate::ParserState;
|
||||||
|
|
||||||
|
pub trait MessageHandler {
|
||||||
|
fn does_handle(&self, message_type: MessageType) -> bool;
|
||||||
|
|
||||||
|
fn handle_message(&mut self, message: Message, tick: u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait StringTableEntryHandler {
|
||||||
|
fn handle_string_entry(&mut self, table: &String, index: usize, entries: &StringTableEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Dispatcher {
|
||||||
|
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>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatcher {
|
||||||
|
pub fn register_message_handler(&mut self, handler: Rc<RefCell<MessageHandler>>) {
|
||||||
|
self.on_message.push(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
match packet {
|
||||||
|
Packet::DataTables(packet) => {
|
||||||
|
self.handle_data_table(packet.tables, packet.server_classes);
|
||||||
|
}
|
||||||
|
Packet::StringTables(packet) => {
|
||||||
|
for table in packet.tables.into_iter() {
|
||||||
|
self.handle_string_table(table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Packet::Message(packet) | Packet::Sigon(packet) => {
|
||||||
|
for message in packet.messages {
|
||||||
|
match message {
|
||||||
|
Message::NetTick(message) => {
|
||||||
|
self.tick = message.tick
|
||||||
|
}
|
||||||
|
Message::CreateStringTable(message) => self.handle_string_table(message.table),
|
||||||
|
Message::UpdateStringTable(message) => self.handle_table_update(message.table_id, message.entries),
|
||||||
|
_ => self.handle_message(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_string_table(&mut self, table: StringTable) {
|
||||||
|
self.string_table_names.push(table.name.clone());
|
||||||
|
|
||||||
|
for (entry_index, entry) in table.entries.iter().enumerate() {
|
||||||
|
self.handle_string_entry(&table.name, entry_index, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(handler) = &self.state_handler {
|
||||||
|
handler.borrow_mut().handle_string_table(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_table_update(&mut self, table_id: u8, entries: HashMap<u16, StringTableEntry>) {
|
||||||
|
let table_name = self.string_table_names.get(table_id as usize);
|
||||||
|
match table_name {
|
||||||
|
Some(table_name) => {
|
||||||
|
let table_name = table_name.clone();
|
||||||
|
for (index, entry) in &entries {
|
||||||
|
self.handle_string_entry(&table_name, *index as usize, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("trying to update non existing table"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use bitstream_reader::{BitRead, LittleEndian, ReadError};
|
use bitstream_reader::{BitRead, LittleEndian, ReadError};
|
||||||
|
|
||||||
|
|
@ -6,12 +7,17 @@ use crate::demo::gamevent::GameEventValue;
|
||||||
use crate::demo::header::Header;
|
use crate::demo::header::Header;
|
||||||
use crate::demo::message::{Message, MessageType};
|
use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::packet::Packet;
|
use crate::demo::packet::Packet;
|
||||||
|
use crate::demo::packet::stringtable::StringTableEntry;
|
||||||
use crate::demo::parser::analyser::Analyser;
|
use crate::demo::parser::analyser::Analyser;
|
||||||
|
use crate::demo::parser::dispatcher::{Dispatcher, MessageHandler, StringTableEntryHandler};
|
||||||
pub use crate::demo::parser::state::ParserState;
|
pub use crate::demo::parser::state::ParserState;
|
||||||
use crate::Stream;
|
use crate::Stream;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
mod state;
|
mod state;
|
||||||
mod analyser;
|
mod analyser;
|
||||||
|
mod dispatcher;
|
||||||
|
|
||||||
/// Errors that can occur during parsing
|
/// Errors that can occur during parsing
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -71,65 +77,46 @@ impl<T: BitRead<LittleEndian>> Parse for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MessageHandler {
|
|
||||||
fn does_handle(&self, message_type: MessageType) -> bool;
|
|
||||||
|
|
||||||
fn handle_message(&mut self, message: Message, tick: u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DemoParser {
|
pub struct DemoParser {
|
||||||
stream: Stream,
|
stream: Stream,
|
||||||
state: ParserState,
|
state: Rc<RefCell<ParserState>>,
|
||||||
analyser: Analyser,
|
analyser: Rc<RefCell<Analyser>>,
|
||||||
|
dispatcher: Dispatcher,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DemoParser {
|
impl DemoParser {
|
||||||
pub fn new(stream: Stream) -> Self {
|
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 {
|
DemoParser {
|
||||||
state: ParserState::new(),
|
state,
|
||||||
stream,
|
stream,
|
||||||
analyser: Analyser::new(),
|
analyser,
|
||||||
|
dispatcher,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn read<T: Parse>(&mut self) -> Result<T> {
|
pub fn read<T: Parse>(&mut self) -> Result<T> {
|
||||||
T::parse(&mut self.stream, &self.state)
|
T::parse(&mut self.stream, self.state.borrow().deref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stream_pos(&self) -> usize {
|
pub fn parse_demo(mut self) -> Result<(Header, Rc<RefCell<Analyser>>)> {
|
||||||
self.stream.pos()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_stream_pos(&mut self, pos: usize) -> Result<()> {
|
|
||||||
self.stream.set_pos(pos)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispatch_messages(&mut self, messages: Vec<Message>) {
|
|
||||||
let tick = self.state.tick;
|
|
||||||
for message in messages {
|
|
||||||
let message_type = message.get_message_type();
|
|
||||||
if self.state.does_handle(message_type) {
|
|
||||||
self.state.handle_message(message, tick);
|
|
||||||
} else if self.analyser.does_handle(message_type) {
|
|
||||||
self.analyser.handle_message(message, tick);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_demo(mut self) -> Result<(Header, Analyser)> {
|
|
||||||
let header = self.read::<Header>()?;
|
let header = self.read::<Header>()?;
|
||||||
loop {
|
loop {
|
||||||
let packet = self.read::<Packet>()?;
|
let packet = self.read::<Packet>()?;
|
||||||
match packet {
|
match packet {
|
||||||
Packet::Stop(_) => break,
|
Packet::Stop(_) => break,
|
||||||
Packet::Message(packet) | Packet::Sigon(packet) => {
|
packet => self.dispatcher.handle_packet(packet),
|
||||||
self.dispatch_messages(packet.messages);
|
|
||||||
}
|
|
||||||
packet => self.state.handle_packet(packet),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok((header, self.analyser))
|
Ok((header, self.analyser.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,18 @@ use crate::demo::gameevent_gen::GameEventType;
|
||||||
use crate::demo::gamevent::GameEventDefinition;
|
use crate::demo::gamevent::GameEventDefinition;
|
||||||
use crate::demo::message::{Message, MessageType};
|
use crate::demo::message::{Message, MessageType};
|
||||||
use crate::demo::message::packetentities::EntityId;
|
use crate::demo::message::packetentities::EntityId;
|
||||||
|
use crate::demo::message::stringtable::StringTableMeta;
|
||||||
use crate::demo::packet::datatable::{SendTable, ServerClass};
|
use crate::demo::packet::datatable::{SendTable, ServerClass};
|
||||||
use crate::demo::packet::Packet;
|
use crate::demo::packet::Packet;
|
||||||
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
use crate::demo::packet::stringtable::{StringTable, StringTableEntry};
|
||||||
use crate::demo::parser::MessageHandler;
|
use crate::demo::parser::dispatcher::{MessageHandler, StringTableEntryHandler};
|
||||||
use crate::demo::sendprop::SendProp;
|
use crate::demo::sendprop::SendProp;
|
||||||
use crate::Stream;
|
use crate::Stream;
|
||||||
|
use std::mem::replace;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
pub type StringEntryHandler = Box<FnMut(&String, &StringTableEntry) -> ()>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct ParserState {
|
pub struct ParserState {
|
||||||
pub version: u16,
|
pub version: u16,
|
||||||
pub static_baselines: HashMap<u32, StaticBaseline>,
|
pub static_baselines: HashMap<u32, StaticBaseline>,
|
||||||
|
|
@ -21,58 +25,55 @@ pub struct ParserState {
|
||||||
pub send_tables: HashMap<String, SendTable>,
|
pub send_tables: HashMap<String, SendTable>,
|
||||||
pub server_classes: Vec<ServerClass>,
|
pub server_classes: Vec<ServerClass>,
|
||||||
pub instance_baselines: [HashMap<EntityId, Vec<SendProp>>; 2],
|
pub instance_baselines: [HashMap<EntityId, Vec<SendProp>>; 2],
|
||||||
pub tick: u32,
|
|
||||||
pub game: String,
|
pub game: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct StaticBaseline {
|
pub struct StaticBaseline {
|
||||||
class_id: u32,
|
class_id: u32,
|
||||||
raw: Stream,
|
raw: Stream,
|
||||||
parsed: Option<Vec<SendProp>>,
|
parsed: Option<Vec<SendProp>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StaticBaseline {
|
||||||
|
fn new(class_id: u32, raw: Stream) -> Self {
|
||||||
|
StaticBaseline {
|
||||||
|
class_id,
|
||||||
|
raw,
|
||||||
|
parsed: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ParserState {
|
impl ParserState {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
ParserState::default()
|
ParserState::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_packet(&mut self, packet: Packet) {
|
pub fn handle_data_table(&mut self, send_tables: Vec<SendTable>, server_classes: Vec<ServerClass>) {
|
||||||
match packet {
|
for table in send_tables {
|
||||||
Packet::DataTables(packet) => {
|
|
||||||
if self.send_tables.len() > 0 {
|
|
||||||
unreachable!("overwriting tables");
|
|
||||||
}
|
|
||||||
for table in packet.tables {
|
|
||||||
self.send_tables.insert(table.name.clone(), table);
|
self.send_tables.insert(table.name.clone(), table);
|
||||||
}
|
}
|
||||||
self.server_classes = packet.server_classes;
|
self.server_classes = server_classes
|
||||||
}
|
|
||||||
Packet::StringTables(packet) => {
|
|
||||||
for table in packet.tables {
|
|
||||||
self.handle_table(table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_table(&mut self, table: StringTable) {
|
pub fn handle_string_table(&mut self, table: StringTable) {
|
||||||
self.string_tables.push(table);
|
self.string_tables.push(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_table_update(&mut self, table_id: u8, entries: HashMap<u16, StringTableEntry>) {
|
pub fn handle_string_table_update(&mut self, table_id: u8, entries: HashMap<u16, StringTableEntry>) {
|
||||||
let mut table = self.string_tables.get_mut(table_id as usize);
|
match self.string_tables.get_mut(table_id as usize) {
|
||||||
match table {
|
|
||||||
Some(table) => {
|
Some(table) => {
|
||||||
for (index, entry) in entries {
|
for (index, entry) in entries {
|
||||||
if index as usize > table.entries.len() {
|
let index = index as usize;
|
||||||
table.entries.resize(index as usize, StringTableEntry::default());
|
if index > table.entries.len() {
|
||||||
|
table.entries.resize(index, StringTableEntry::default())
|
||||||
}
|
}
|
||||||
table.entries.insert(index as usize, entry);
|
unsafe {
|
||||||
|
replace(table.entries.get_unchecked_mut(index), entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!("trying to update non existing table"),
|
},
|
||||||
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +81,6 @@ impl ParserState {
|
||||||
impl MessageHandler for ParserState {
|
impl MessageHandler for ParserState {
|
||||||
fn does_handle(&self, message_type: MessageType) -> bool {
|
fn does_handle(&self, message_type: MessageType) -> bool {
|
||||||
match message_type {
|
match message_type {
|
||||||
MessageType::NetTick |
|
|
||||||
MessageType::ServerInfo |
|
MessageType::ServerInfo |
|
||||||
MessageType::GameEventList |
|
MessageType::GameEventList |
|
||||||
MessageType::CreateStringTable |
|
MessageType::CreateStringTable |
|
||||||
|
|
@ -91,7 +91,6 @@ impl MessageHandler for ParserState {
|
||||||
|
|
||||||
fn handle_message(&mut self, message: Message, _tick: u32) {
|
fn handle_message(&mut self, message: Message, _tick: u32) {
|
||||||
match message {
|
match message {
|
||||||
Message::NetTick(message) => self.tick = message.tick,
|
|
||||||
Message::ServerInfo(message) => {
|
Message::ServerInfo(message) => {
|
||||||
self.version = message.version;
|
self.version = message.version;
|
||||||
self.game = message.game;
|
self.game = message.game;
|
||||||
|
|
@ -99,13 +98,29 @@ impl MessageHandler for ParserState {
|
||||||
Message::GameEventList(message) => {
|
Message::GameEventList(message) => {
|
||||||
self.event_definitions = message.event_list;
|
self.event_definitions = message.event_list;
|
||||||
}
|
}
|
||||||
Message::CreateStringTable(message) => {
|
|
||||||
self.handle_table(message.table);
|
|
||||||
}
|
|
||||||
Message::UpdateStringTable(message) => {
|
|
||||||
self.handle_table_update(message.table_id, message.entries);
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StringTableEntryHandler for ParserState {
|
||||||
|
fn handle_string_entry(&mut self, table: &String, index: usize, entry: &StringTableEntry) {
|
||||||
|
match table.as_str() {
|
||||||
|
"instancebaseline" => {
|
||||||
|
match &entry.extra_data {
|
||||||
|
Some(extra) => {
|
||||||
|
match entry.text.parse::<u32>() {
|
||||||
|
Ok(class_id) => {
|
||||||
|
let baseline = StaticBaseline::new(class_id, extra.data.clone());
|
||||||
|
self.static_baselines.insert(class_id, baseline);
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!("missing baseline")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue