tf_demo_parser/demo/parser/
state.rs1use fnv::FnvHashMap;
2use std::borrow::Cow;
3use std::collections::HashMap;
4
5use crate::demo::gamevent::GameEventDefinition;
6
7use crate::demo::message::packetentities::{
8 EntityId, PacketEntitiesMessage, PacketEntity, UpdateType,
9};
10use crate::demo::message::stringtable::StringTableMeta;
11use crate::demo::message::{Message, MessageType};
12use crate::demo::packet::datatable::{
13 ClassId, ParseSendTable, SendTable, SendTableName, ServerClass,
14};
15use crate::demo::packet::stringtable::StringTableEntry;
16
17use crate::demo::data::DemoTick;
18use crate::demo::sendprop::{SendProp, SendPropIdentifier};
19use crate::nullhasher::NullHasherBuilder;
20use crate::{Result, Stream};
21use serde::{Deserialize, Serialize};
22use std::cell::RefCell;
23#[cfg(feature = "trace")]
24use tracing::warn;
25
26#[derive(Default, Clone, Serialize, Deserialize)]
27pub struct DemoMeta {
28 pub version: u16,
29 pub game: String,
30 pub interval_per_tick: f32,
31}
32
33#[derive(Clone)]
34pub struct ParserState {
35 pub static_baselines: HashMap<ClassId, StaticBaseline, NullHasherBuilder>,
36 pub parsed_static_baselines: RefCell<HashMap<ClassId, Vec<SendProp>, NullHasherBuilder>>,
37 pub event_definitions: Vec<GameEventDefinition>,
38 pub string_tables: Vec<StringTableMeta>,
39 pub entity_classes: HashMap<EntityId, ClassId, NullHasherBuilder>,
40 pub send_tables: Vec<SendTable>,
42 pub server_classes: Vec<ServerClass>,
43 pub instance_baselines: [Baseline; 2],
44 pub demo_meta: DemoMeta,
45 analyser_handles: fn(message_type: MessageType) -> bool,
46 handle_entities: bool,
47 parse_all: bool,
48 pub protocol_version: u32,
49}
50
51#[derive(Clone)]
52pub struct StaticBaseline {
53 pub class_id: ClassId,
54 pub raw: Stream<'static>,
55}
56
57impl StaticBaseline {
58 fn new(class_id: ClassId, raw: Stream<'static>) -> Self {
59 StaticBaseline { class_id, raw }
60 }
61
62 pub fn parse(&self, send_table: &SendTable) -> Result<Vec<SendProp>> {
63 let mut props = Vec::with_capacity(8);
64 PacketEntitiesMessage::read_update(
65 &mut self.raw.clone(),
66 send_table,
67 &mut props,
68 0u32.into(),
69 )?;
70 Ok(props)
71 }
72}
73
74impl<'a> ParserState {
75 pub fn new(
76 protocol_version: u32,
77 analyser_handles: fn(message_type: MessageType) -> bool,
78 parse_all: bool,
79 ) -> Self {
80 ParserState {
81 static_baselines: HashMap::with_hasher(NullHasherBuilder),
82 parsed_static_baselines: RefCell::new(HashMap::with_hasher(NullHasherBuilder)),
83 event_definitions: Vec::new(),
84 string_tables: Vec::new(),
85 entity_classes: HashMap::with_hasher(NullHasherBuilder),
86 send_tables: Vec::new(),
87 server_classes: Vec::new(),
88 instance_baselines: [Baseline::default(), Baseline::default()],
89 demo_meta: DemoMeta::default(),
90 analyser_handles,
91 handle_entities: analyser_handles(MessageType::PacketEntities) || parse_all,
92 parse_all,
93 protocol_version,
94 }
95 }
96
97 pub fn get_static_baseline(
98 &self,
99 class_id: ClassId,
100 send_table: &SendTable,
101 ) -> Result<Vec<SendProp>> {
102 match self.static_baselines.get(&class_id) {
103 Some(static_baseline) => static_baseline.parse(send_table),
104 None => {
105 #[cfg(feature = "trace")]
106 warn!(
107 class_id = display(class_id),
108 "class without static baseline"
109 );
110 Ok(Vec::new())
111 }
112 }
113 }
132
133 pub fn get_baseline(
134 &self,
135 baseline_index: usize,
136 entity_index: EntityId,
137 class_id: ClassId,
138 send_table: &SendTable,
139 is_delta: bool,
140 ) -> Result<Cow<[SendProp]>> {
141 match self.instance_baselines[baseline_index].get(entity_index) {
142 Some(baseline) if baseline.server_class == class_id && is_delta => {
143 Ok(Cow::Borrowed(&baseline.props))
144 }
145 _ => match self.static_baselines.get(&class_id) {
146 Some(_static_baseline) => {
147 Ok(Cow::Owned(self.get_static_baseline(class_id, send_table)?))
148 }
149 None => {
150 #[cfg(feature = "trace")]
151 warn!(
152 class_id = display(class_id),
153 "class without static baseline"
154 );
155 Ok(Cow::Owned(Vec::new()))
156 }
157 },
158 }
159 }
160
161 pub fn handle_data_table(
162 &mut self,
163 parse_tables: &[ParseSendTable],
164 server_classes: Vec<ServerClass>,
165 ) -> Result<()> {
166 if self.handle_entities {
167 let mut send_tables: FnvHashMap<SendTableName, SendTable> = parse_tables
168 .iter()
169 .map(|parse_table| {
170 let flat = parse_table.flatten_props(parse_tables);
171 Ok((
172 parse_table.name.clone(),
173 SendTable {
174 name: parse_table.name.clone(),
175 needs_decoder: parse_table.needs_decoder,
176 flattened_props: flat?,
177 },
178 ))
179 })
180 .collect::<Result<_>>()?;
181
182 self.server_classes = server_classes;
183
184 self.send_tables.reserve(self.server_classes.len());
185
186 for class in self.server_classes.iter() {
187 if let Some(table) = send_tables.remove(&class.data_table) {
188 self.send_tables.push(table);
189 } else {
190 #[cfg(feature = "trace")]
191 warn!(class = debug(class), "class without table");
192 }
193 }
194 }
195
196 Ok(())
197 }
198
199 pub fn handle_string_table_meta(&mut self, table: StringTableMeta) {
200 self.string_tables.push(table);
201 }
202
203 pub fn should_parse_message(&self, message_type: MessageType) -> bool {
204 self.parse_all
205 || if message_type == MessageType::PacketEntities {
206 self.handle_entities
207 } else {
208 Self::does_handle(message_type) || (self.analyser_handles)(message_type)
209 }
210 }
211
212 pub fn does_handle(message_type: MessageType) -> bool {
213 matches!(
214 message_type,
215 MessageType::ServerInfo
216 | MessageType::NetTick
217 | MessageType::GameEventList
218 | MessageType::CreateStringTable
219 | MessageType::PacketEntities
220 | MessageType::UpdateStringTable
221 )
222 }
223
224 pub fn handle_message(&mut self, message: Message, _tick: DemoTick) {
225 match message {
226 Message::ServerInfo(message) => {
227 self.demo_meta.version = message.version;
228 self.demo_meta.game = message.game;
229 self.demo_meta.interval_per_tick = message.interval_per_tick;
230 }
231 Message::GameEventList(message) => {
232 self.event_definitions = message.event_list;
233 }
234 Message::PacketEntities(ent_message) => {
235 for removed in ent_message.removed_entities.iter() {
236 self.entity_classes.remove(removed);
237 }
238
239 for entity in ent_message.entities.iter() {
240 if entity.update_type == UpdateType::Delete {
241 self.entity_classes.remove(&entity.entity_index);
242 }
243 self.entity_classes
244 .insert(entity.entity_index, entity.server_class);
245 }
246
247 if ent_message.updated_base_line {
248 let old_index = ent_message.base_line as usize;
249 let new_index = 1 - old_index;
250 let [baseline1, baseline2] = &mut self.instance_baselines;
251 if old_index == 0 {
252 baseline2.copy_from(baseline1);
253 } else {
254 baseline1.copy_from(baseline2);
255 }
256
257 for entity in ent_message.entities {
258 if entity.update_type == UpdateType::Enter {
259 let updated_baseline =
260 match self.instance_baselines[old_index].get(entity.entity_index) {
261 Some(baseline_entity)
262 if baseline_entity.server_class == entity.server_class
263 && ent_message.delta.is_some() =>
264 {
265 let mut updated_baseline = baseline_entity.clone();
266 updated_baseline.apply_update(&entity.props);
267 updated_baseline
268 }
269 _ => entity.into(),
270 };
271 self.instance_baselines[new_index].set(updated_baseline);
272 }
273 }
274 }
275 }
276 _ => {}
277 }
278 }
279
280 pub fn handle_string_entry(
281 &mut self,
282 table: &str,
283 _index: usize,
284 entry: &StringTableEntry<'a>,
285 ) {
286 if table == "instancebaseline" {
287 if let (Some(extra), Ok(class_id)) = (&entry.extra_data, entry.text().parse()) {
288 let baseline = StaticBaseline::new(class_id, extra.data.to_owned());
289 self.static_baselines.insert(class_id, baseline);
290 self.parsed_static_baselines.borrow_mut().remove(&class_id);
291 }
292 }
293 }
294
295 pub fn index_for_prop(&self, class: ClassId, prop: SendPropIdentifier) -> Option<u32> {
296 let send_table = self.send_tables.get(usize::from(class))?;
297 send_table
298 .flattened_props
299 .iter()
300 .enumerate()
301 .find(|(_i, def)| def.identifier == prop)
302 .map(|(index, _)| index as u32)
303 }
304}
305
306#[derive(Clone)]
307pub struct Baseline {
308 instances: Vec<Option<BaselineEntity>>,
309}
310
311impl Default for Baseline {
312 fn default() -> Self {
313 Baseline {
314 instances: vec![None; 2048],
315 }
316 }
317}
318
319impl Baseline {
320 pub fn get(&self, index: EntityId) -> Option<&BaselineEntity> {
321 self.instances
322 .get(usize::from(index))
323 .and_then(|opt| opt.as_ref())
324 }
325
326 fn set(&mut self, entity: BaselineEntity) {
327 let index = entity.entity_id;
328 self.instances[usize::from(index)] = Some(entity);
329 }
330
331 pub fn keys(&self) -> impl Iterator<Item = EntityId> + '_ {
332 self.instances
333 .iter()
334 .filter_map(|entity| entity.as_ref().map(|entity| entity.entity_id))
335 }
336
337 pub fn into_values(self) -> impl Iterator<Item = PacketEntity> {
338 self.instances
339 .into_iter()
340 .filter_map(|entity| entity.map(|entity| entity.into()))
341 }
342
343 pub fn contains(&self, index: EntityId) -> bool {
344 self.get(index).is_some()
345 }
346
347 fn copy_from(&mut self, other: &Baseline) {
348 for (ent, other_ent) in self.instances.iter_mut().zip(other.instances.iter()) {
349 match (ent, other_ent) {
350 (ent, Some(other_ent)) => *ent = Some(other_ent.clone()),
351 (ent, None) => {
352 *ent = None;
353 }
354 }
355 }
356 }
357}
358
359#[derive(Clone)]
360pub struct BaselineEntity {
361 pub entity_id: EntityId,
362 pub server_class: ClassId,
363 pub props: Vec<SendProp>,
364 pub serial: u32,
365}
366
367impl BaselineEntity {
368 fn mut_prop_by_identifier(&mut self, index: &SendPropIdentifier) -> Option<&mut SendProp> {
369 self.props.iter_mut().find(|prop| prop.identifier == *index)
370 }
371
372 pub fn apply_update(&mut self, props: &[SendProp]) {
373 for prop in props {
374 match self.mut_prop_by_identifier(&prop.identifier) {
375 Some(existing_prop) => existing_prop.value = prop.value.clone(),
376 None => self.props.push(prop.clone()),
377 }
378 }
379 }
380}
381
382impl From<PacketEntity> for BaselineEntity {
383 fn from(entity: PacketEntity) -> Self {
384 BaselineEntity {
385 entity_id: entity.entity_index,
386 server_class: entity.server_class,
387 props: entity.props,
388 serial: entity.serial_number,
389 }
390 }
391}
392
393impl From<BaselineEntity> for PacketEntity {
394 fn from(baseline: BaselineEntity) -> Self {
395 PacketEntity {
396 server_class: baseline.server_class,
397 entity_index: baseline.entity_id,
398 props: baseline.props,
399 in_pvs: false,
400 update_type: UpdateType::Enter,
401 serial_number: baseline.serial,
402 delay: None,
403 delta: None,
404 baseline_index: 0,
405 }
406 }
407}