Skip to main content

tf_demo_parser/demo/packet/
mod.rs

1use bitbuffer::BitRead;
2#[cfg(feature = "write")]
3use bitbuffer::{BitWrite, BitWriteStream, LittleEndian};
4
5use crate::{Parse, ParserState, Result, Stream};
6
7use self::consolecmd::ConsoleCmdPacket;
8use self::datatable::DataTablePacket;
9use self::message::MessagePacket;
10use self::stop::StopPacket;
11use self::stringtable::StringTablePacket;
12use self::synctick::SyncTickPacket;
13use self::usercmd::UserCmdPacket;
14use crate::demo::data::DemoTick;
15#[cfg(feature = "write")]
16use crate::demo::parser::Encode;
17use serde::{Deserialize, Serialize};
18#[cfg(feature = "trace")]
19use tracing::{Level, event, span};
20
21pub mod consolecmd;
22pub mod datatable;
23pub mod message;
24pub mod stop;
25pub mod stringtable;
26pub mod synctick;
27pub mod usercmd;
28
29#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
30#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
31#[serde(bound(deserialize = "'a: 'static"))]
32#[serde(tag = "type")]
33pub enum Packet<'a> {
34    Signon(MessagePacket<'a>),
35    Message(MessagePacket<'a>),
36    SyncTick(SyncTickPacket),
37    ConsoleCmd(ConsoleCmdPacket),
38    UserCmd(UserCmdPacket),
39    DataTables(DataTablePacket),
40    Stop(StopPacket),
41    StringTables(StringTablePacket<'a>),
42}
43
44impl Packet<'_> {
45    pub fn tick(&self) -> DemoTick {
46        match self {
47            Packet::Signon(msg) => msg.tick,
48            Packet::Message(msg) => msg.tick,
49            Packet::SyncTick(msg) => msg.tick,
50            Packet::ConsoleCmd(msg) => msg.tick,
51            Packet::UserCmd(msg) => msg.tick,
52            Packet::DataTables(msg) => msg.tick,
53            Packet::Stop(msg) => msg.tick,
54            Packet::StringTables(msg) => msg.tick,
55        }
56    }
57
58    pub fn set_tick(&mut self, tick: DemoTick) {
59        match self {
60            Packet::Signon(msg) => msg.tick = tick,
61            Packet::Message(msg) => msg.tick = tick,
62            Packet::SyncTick(msg) => msg.tick = tick,
63            Packet::ConsoleCmd(msg) => msg.tick = tick,
64            Packet::UserCmd(msg) => msg.tick = tick,
65            Packet::DataTables(msg) => msg.tick = tick,
66            Packet::Stop(msg) => msg.tick = tick,
67            Packet::StringTables(msg) => msg.tick = tick,
68        }
69    }
70}
71
72#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
73#[derive(BitRead, Debug, Clone, Copy, Eq, PartialEq)]
74#[cfg_attr(feature = "write", derive(BitWrite))]
75#[discriminant_bits = 8]
76#[repr(u8)]
77pub enum PacketType {
78    Signon = 1,
79    Message = 2,
80    SyncTick = 3,
81    ConsoleCmd = 4,
82    UserCmd = 5,
83    DataTables = 6,
84    Stop = 7,
85    StringTables = 8,
86}
87
88impl Packet<'_> {
89    pub fn packet_type(&self) -> PacketType {
90        match self {
91            Packet::Signon(_) => PacketType::Signon,
92            Packet::Message(_) => PacketType::Message,
93            Packet::SyncTick(_) => PacketType::SyncTick,
94            Packet::ConsoleCmd(_) => PacketType::ConsoleCmd,
95            Packet::UserCmd(_) => PacketType::UserCmd,
96            Packet::DataTables(_) => PacketType::DataTables,
97            Packet::Stop(_) => PacketType::Stop,
98            Packet::StringTables(_) => PacketType::StringTables,
99        }
100    }
101}
102
103impl<'a> Parse<'a> for Packet<'a> {
104    fn parse(stream: &mut Stream<'a>, state: &ParserState) -> Result<Self> {
105        let packet_type = PacketType::read(stream)?;
106        #[cfg(feature = "trace")]
107        {
108            let tick: u32 = if packet_type == PacketType::Stop {
109                0
110            } else {
111                let tick = stream.read()?;
112                stream.set_pos(stream.pos() - 32)?;
113                tick
114            };
115            let _span =
116                span!(Level::INFO, "reading packet", packet_type = ?packet_type, tick = tick)
117                    .entered();
118            event!(Level::DEBUG, "parsing packet");
119        }
120        Ok(match packet_type {
121            PacketType::Signon => Packet::Signon(MessagePacket::parse(stream, state)?),
122            PacketType::Message => Packet::Message(MessagePacket::parse(stream, state)?),
123            PacketType::SyncTick => Packet::SyncTick(SyncTickPacket::parse(stream, state)?),
124            PacketType::ConsoleCmd => Packet::ConsoleCmd(ConsoleCmdPacket::parse(stream, state)?),
125            PacketType::UserCmd => Packet::UserCmd(UserCmdPacket::parse(stream, state)?),
126            PacketType::DataTables => Packet::DataTables(DataTablePacket::parse(stream, state)?),
127            PacketType::Stop => Packet::Stop(StopPacket::parse(stream, state)?),
128            PacketType::StringTables => {
129                Packet::StringTables(StringTablePacket::parse(stream, state)?)
130            }
131        })
132    }
133}
134
135#[cfg(feature = "write")]
136impl Encode for Packet<'_> {
137    fn encode(&self, stream: &mut BitWriteStream<LittleEndian>, state: &ParserState) -> Result<()> {
138        #[cfg(feature = "trace")]
139        let _span = span!(Level::INFO, "encoding packet", packet_type = ?self.packet_type(), tick = ?self.tick()).entered();
140        self.packet_type().write(stream)?;
141        match self {
142            Packet::Signon(inner) => inner.encode(stream, state),
143            Packet::Message(inner) => inner.encode(stream, state),
144            Packet::SyncTick(inner) => inner.encode(stream, state),
145            Packet::ConsoleCmd(inner) => inner.encode(stream, state),
146            Packet::UserCmd(inner) => inner.encode(stream, state),
147            Packet::DataTables(inner) => inner.encode(stream, state),
148            Packet::Stop(inner) => inner.encode(stream, state),
149            Packet::StringTables(inner) => inner.encode(stream, state),
150        }
151    }
152}
153
154impl PacketType {
155    pub fn as_str(&self) -> &'static str {
156        match self {
157            PacketType::Signon => "Signon",
158            PacketType::Message => "Message",
159            PacketType::SyncTick => "SyncTick",
160            PacketType::ConsoleCmd => "ConsoleCmd",
161            PacketType::UserCmd => "UserCmd",
162            PacketType::DataTables => "DataTables",
163            PacketType::Stop => "Stop",
164            PacketType::StringTables => "StringTables",
165        }
166    }
167
168    pub fn as_lowercase_str(&self) -> &'static str {
169        match self {
170            PacketType::Signon => "signon",
171            PacketType::Message => "message",
172            PacketType::SyncTick => "synctick",
173            PacketType::ConsoleCmd => "consolecmd",
174            PacketType::UserCmd => "usercmd",
175            PacketType::DataTables => "datatables",
176            PacketType::Stop => "stop",
177            PacketType::StringTables => "stringtables",
178        }
179    }
180}