From de9e01544d6d656a71a7007be2466545e9fc861a Mon Sep 17 00:00:00 2001 From: Laiho Date: Sun, 4 Aug 2024 20:08:36 +0300 Subject: [PATCH] refactors to second pass --- src/parser/src/first_pass/sendtables.rs | 103 +++++++ src/parser/src/second_pass/collect_data.rs | 98 ++++++- src/parser/src/second_pass/entities.rs | 319 ++------------------- src/parser/src/second_pass/game_events.rs | 91 +++++- src/parser/src/second_pass/parser.rs | 235 ++++++++------- src/parser/src/second_pass/path_ops.rs | 8 + src/parser/src/second_pass/variants2.rs | 3 + 7 files changed, 451 insertions(+), 406 deletions(-) create mode 100644 src/parser/src/second_pass/variants2.rs diff --git a/src/parser/src/first_pass/sendtables.rs b/src/parser/src/first_pass/sendtables.rs index c4dadc4c..0132b51f 100644 --- a/src/parser/src/first_pass/sendtables.rs +++ b/src/parser/src/first_pass/sendtables.rs @@ -3,11 +3,20 @@ use super::read_bits::DemoParserError; use crate::first_pass::parser_settings::needs_velocity; use crate::first_pass::parser_settings::FirstPassParser; use crate::first_pass::prop_controller::PropController; +use crate::first_pass::prop_controller::FLATTENED_VEC_MAX_LEN; +use crate::first_pass::prop_controller::ITEM_PURCHASE_COST; +use crate::first_pass::prop_controller::ITEM_PURCHASE_COUNT; +use crate::first_pass::prop_controller::ITEM_PURCHASE_DEF_IDX; +use crate::first_pass::prop_controller::ITEM_PURCHASE_HANDLE; +use crate::first_pass::prop_controller::ITEM_PURCHASE_NEW_DEF_IDX; +use crate::first_pass::prop_controller::MY_WEAPONS_OFFSET; +use crate::first_pass::prop_controller::WEAPON_SKIN_ID; use crate::maps::BASETYPE_DECODERS; use crate::second_pass::decoder::Decoder; use crate::second_pass::decoder::Decoder::*; use crate::second_pass::decoder::QfMapper; use crate::second_pass::decoder::QuantalizedFloat; +use crate::second_pass::path_ops::FieldPath; use crate::second_pass::variants::Variant; use ahash::AHashMap; use csgoproto::netmessages::ProtoFlattenedSerializer_t; @@ -28,6 +37,13 @@ pub struct Serializer { pub name: String, pub fields: Vec, } +#[derive(Debug, Clone, Copy)] +pub struct FieldInfo { + pub decoder: Decoder, + pub should_parse: bool, + pub prop_id: u32, +} + #[derive(Debug, Clone, PartialEq)] pub enum FieldCategory { Pointer, @@ -402,6 +418,93 @@ pub fn field_from_msg( }; Ok(f) } +#[inline(always)] +pub fn find_field<'b>(fp: &FieldPath, ser: &'b Serializer) -> Result<&'b Field, DemoParserError> { + let f = match ser.fields.get(fp.path[0] as usize) { + Some(entry) => entry, + None => return Err(DemoParserError::IllegalPathOp), + }; + match fp.last { + 0 => Ok(f), + 1 => Ok(f.get_inner(fp.path[1] as usize)?), + 2 => Ok(f.get_inner(fp.path[1] as usize)?.get_inner(fp.path[2] as usize)?), + 3 => Ok(f + .get_inner(fp.path[1] as usize)? + .get_inner(fp.path[2] as usize)? + .get_inner(fp.path[3] as usize)?), + 4 => Ok(f + .get_inner(fp.path[1] as usize)? + .get_inner(fp.path[2] as usize)? + .get_inner(fp.path[3] as usize)? + .get_inner(fp.path[4] as usize)?), + 5 => Ok(f + .get_inner(fp.path[1] as usize)? + .get_inner(fp.path[2] as usize)? + .get_inner(fp.path[3] as usize)? + .get_inner(fp.path[4] as usize)? + .get_inner(fp.path[5] as usize)?), + _ => return Err(DemoParserError::IllegalPathOp), + } +} +pub fn get_decoder_from_field(field: &Field) -> Result { + let decoder = match field { + Field::Value(inner) => inner.decoder, + Field::Vector(_) => UnsignedDecoder, + Field::Pointer(inner) => inner.decoder, + _ => return Err(DemoParserError::FieldNoDecoder), + }; + Ok(decoder) +} + +pub fn get_propinfo(field: &Field, path: &FieldPath) -> Option { + let info = match field { + Field::Value(v) => Some(FieldInfo { + decoder: v.decoder, + should_parse: v.should_parse, + prop_id: v.prop_id, + }), + Field::Vector(v) => match field.get_inner(0) { + Ok(Field::Value(inner)) => Some(FieldInfo { + decoder: v.decoder, + should_parse: inner.should_parse, + prop_id: inner.prop_id, + }), + _ => None, + }, + _ => None, + }; + // Flatten vector props + if let Some(mut fi) = info { + if fi.prop_id == MY_WEAPONS_OFFSET { + if path.last == 1 { + } else { + fi.prop_id = MY_WEAPONS_OFFSET + path.path[2] as u32 + 1; + } + } + if fi.prop_id == WEAPON_SKIN_ID { + fi.prop_id = WEAPON_SKIN_ID + path.path[1] as u32; + } + if path.path[1] != 1 { + if fi.prop_id >= ITEM_PURCHASE_COUNT && fi.prop_id < ITEM_PURCHASE_COUNT + FLATTENED_VEC_MAX_LEN { + fi.prop_id = ITEM_PURCHASE_COUNT + path.path[2] as u32; + } + if fi.prop_id >= ITEM_PURCHASE_DEF_IDX && fi.prop_id < ITEM_PURCHASE_DEF_IDX + FLATTENED_VEC_MAX_LEN { + fi.prop_id = ITEM_PURCHASE_DEF_IDX + path.path[2] as u32; + } + if fi.prop_id >= ITEM_PURCHASE_COST && fi.prop_id < ITEM_PURCHASE_COST + FLATTENED_VEC_MAX_LEN { + fi.prop_id = ITEM_PURCHASE_COST + path.path[2] as u32; + } + if fi.prop_id >= ITEM_PURCHASE_HANDLE && fi.prop_id < ITEM_PURCHASE_HANDLE + FLATTENED_VEC_MAX_LEN { + fi.prop_id = ITEM_PURCHASE_HANDLE + path.path[2] as u32; + } + if fi.prop_id >= ITEM_PURCHASE_NEW_DEF_IDX && fi.prop_id < ITEM_PURCHASE_NEW_DEF_IDX + FLATTENED_VEC_MAX_LEN { + fi.prop_id = ITEM_PURCHASE_NEW_DEF_IDX + path.path[2] as u32; + } + } + return Some(fi); + } + return None; +} fn create_field( _sid: &String, diff --git a/src/parser/src/second_pass/collect_data.rs b/src/parser/src/second_pass/collect_data.rs index 01150dc3..abcd0fb0 100644 --- a/src/parser/src/second_pass/collect_data.rs +++ b/src/parser/src/second_pass/collect_data.rs @@ -1,18 +1,19 @@ -use ahash::AHashMap; - use super::entities::PlayerMetaData; use super::variants::Sticker; use super::variants::Variant; use crate::first_pass::prop_controller::*; +use crate::first_pass::read_bits::DemoParserError; use crate::maps::AGENTSMAP; use crate::maps::BUTTONMAP; use crate::maps::GRENADE_FRIENDLY_NAMES; use crate::maps::PAINTKITS; use crate::maps::STICKER_ID_TO_NAME; use crate::maps::WEAPINDICIES; +use crate::second_pass::entities::EntityType; use crate::second_pass::parser_settings::SecondPassParser; use crate::second_pass::variants::PropColumn; use crate::second_pass::variants::VarVec; +use ahash::AHashMap; use std::fmt; #[derive(Debug, Clone, Copy, PartialEq)] @@ -939,6 +940,99 @@ impl<'a> SecondPassParser<'a> { } } } + pub fn gather_extra_info(&mut self, entity_id: &i32, is_baseline: bool) -> Result<(), DemoParserError> { + // Boring stuff.. function does some bookkeeping + let entity = match self.entities.get(*entity_id as usize) { + Some(Some(entity)) => entity, + _ => return Err(DemoParserError::EntityNotFound), + }; + if !(entity.entity_type == EntityType::PlayerController || entity.entity_type == EntityType::Team) { + return Ok(()); + } + if entity.entity_type == EntityType::Team && !is_baseline { + if let Some(team_num_id) = self.prop_controller.special_ids.team_team_num { + if let Ok(Variant::U32(t)) = self.get_prop_from_ent(&team_num_id, entity_id) { + match t { + 1 => self.teams.team1_entid = Some(*entity_id), + 2 => self.teams.team2_entid = Some(*entity_id), + 3 => self.teams.team3_entid = Some(*entity_id), + _ => {} + } + } + } + } + + let team_num = match self.prop_controller.special_ids.teamnum { + Some(team_num_id) => match self.get_prop_from_ent(&team_num_id, entity_id) { + Ok(team_num) => match team_num { + Variant::U32(team_num) => Some(team_num), + _ => return Err(DemoParserError::IncorrectMetaDataProp), + }, + Err(_) => None, + }, + _ => None, + }; + + let name = match self.prop_controller.special_ids.player_name { + Some(id) => match self.get_prop_from_ent(&id, entity_id) { + Ok(team_num) => match team_num { + Variant::String(team_num) => Some(team_num), + _ => return Err(DemoParserError::IncorrectMetaDataProp), + }, + Err(_) => None, + }, + _ => None, + }; + let steamid = match self.prop_controller.special_ids.steamid { + Some(id) => match self.get_prop_from_ent(&id, entity_id) { + Ok(team_num) => match team_num { + Variant::U64(team_num) => Some(team_num), + _ => return Err(DemoParserError::IncorrectMetaDataProp), + }, + Err(_) => None, + }, + _ => None, + }; + let player_entid = match self.prop_controller.special_ids.player_pawn { + Some(id) => match self.get_prop_from_ent(&id, entity_id) { + Ok(player_entid) => match player_entid { + Variant::U32(handle) => Some((handle & 0x7FF) as i32), + _ => return Err(DemoParserError::IncorrectMetaDataProp), + }, + Err(_) => None, + }, + _ => None, + }; + if let Some(e) = player_entid { + if e != PLAYER_ENTITY_HANDLE_MISSING && steamid != Some(0) && team_num != Some(SPECTATOR_TEAM_NUM) { + match self.should_remove(steamid) { + Some(eid) => { + self.players.remove(&eid); + } + None => {} + } + self.players.insert( + e, + PlayerMetaData { + name: name, + team_num: team_num, + player_entity_id: player_entid, + steamid: steamid, + controller_entid: Some(*entity_id), + }, + ); + } + } + Ok(()) + } + fn should_remove(&self, steamid: Option) -> Option { + for (entid, player) in &self.players { + if player.steamid == steamid { + return Some(*entid); + } + } + None + } } fn coord_from_cell( diff --git a/src/parser/src/second_pass/entities.rs b/src/parser/src/second_pass/entities.rs index ce122bb6..61672f2c 100644 --- a/src/parser/src/second_pass/entities.rs +++ b/src/parser/src/second_pass/entities.rs @@ -1,20 +1,11 @@ -use crate::first_pass::prop_controller::PropController; -use crate::first_pass::prop_controller::FLATTENED_VEC_MAX_LEN; -use crate::first_pass::prop_controller::ITEM_PURCHASE_COST; -use crate::first_pass::prop_controller::ITEM_PURCHASE_COUNT; -use crate::first_pass::prop_controller::ITEM_PURCHASE_DEF_IDX; -use crate::first_pass::prop_controller::ITEM_PURCHASE_HANDLE; -use crate::first_pass::prop_controller::ITEM_PURCHASE_NEW_DEF_IDX; -use crate::first_pass::prop_controller::MY_WEAPONS_OFFSET; -use crate::first_pass::prop_controller::PLAYER_ENTITY_HANDLE_MISSING; -use crate::first_pass::prop_controller::SPECTATOR_TEAM_NUM; -use crate::first_pass::prop_controller::WEAPON_SKIN_ID; use crate::first_pass::read_bits::Bitreader; use crate::first_pass::read_bits::DemoParserError; +use crate::first_pass::sendtables::find_field; +use crate::first_pass::sendtables::get_decoder_from_field; +use crate::first_pass::sendtables::get_propinfo; use crate::first_pass::sendtables::Field; -use crate::first_pass::sendtables::Serializer; -use crate::second_pass::decoder::Decoder; -use crate::second_pass::decoder::Decoder::UnsignedDecoder; +use crate::first_pass::sendtables::FieldInfo; +use crate::second_pass::game_events::GameEventInfo; use crate::second_pass::other_netmessages::Class; use crate::second_pass::parser_settings::SecondPassParser; use crate::second_pass::path_ops::*; @@ -34,12 +25,6 @@ pub struct Entity { pub props: AHashMap, pub entity_type: EntityType, } -#[derive(Debug, Clone, Copy)] -pub struct FieldInfo { - pub decoder: Decoder, - pub should_parse: bool, - pub prop_id: u32, -} #[derive(Debug, Clone, PartialEq)] pub struct PlayerMetaData { @@ -63,26 +48,6 @@ enum EntityCmd { CreateAndUpdate, Update, } -#[derive(Debug, Clone)] -pub struct RoundEnd { - pub old_value: Option, - pub new_value: Option, -} -#[derive(Debug, Clone)] -pub struct RoundWinReason { - pub reason: i32, -} -#[derive(Debug, Clone)] -pub enum GameEventInfo { - RoundEnd(RoundEnd), - RoundWinReason(RoundWinReason), - FreezePeriodStart(bool), - MatchEnd(), - WeaponCreateHitem((Variant, i32)), - WeaponCreateNCost((Variant, i32)), - WeaponCreateDefIdx((Variant, i32, u32)), - WeaponPurchaseCount((Variant, i32, u32)), -} impl<'a> SecondPassParser<'a> { pub fn parse_packet_ents(&mut self, bytes: &[u8], is_fullpacket: bool) -> Result<(), DemoParserError> { @@ -267,9 +232,9 @@ impl<'a> SecondPassParser<'a> { }; for path in self.paths.iter().take(n_updates) { - let field = SecondPassParser::find_field(&path, &class.serializer)?; - let field_info = SecondPassParser::get_propinfo(&field, path); - let decoder = SecondPassParser::get_decoder_from_field(field)?; + let field = find_field(&path, &class.serializer)?; + let field_info = get_propinfo(&field, path); + let decoder = get_decoder_from_field(field)?; let result = bitreader.decode(&decoder, self.qf_mapper)?; if !is_fullpacket && !is_baseline { @@ -318,15 +283,7 @@ impl<'a> SecondPassParser<'a> { } } } - fn get_decoder_from_field(field: &Field) -> Result { - let decoder = match field { - Field::Value(inner) => inner.decoder, - Field::Vector(_) => UnsignedDecoder, - Field::Pointer(inner) => inner.decoder, - _ => return Err(DemoParserError::FieldNoDecoder), - }; - Ok(decoder) - } + pub fn insert_field(entity: &mut Entity, result: Variant, field_info: Option) { if let Some(fi) = field_info { if fi.should_parse { @@ -334,117 +291,7 @@ impl<'a> SecondPassParser<'a> { } } } - pub fn get_propinfo(field: &Field, path: &FieldPath) -> Option { - let info = match field { - Field::Value(v) => Some(FieldInfo { - decoder: v.decoder, - should_parse: v.should_parse, - prop_id: v.prop_id, - }), - Field::Vector(v) => match field.get_inner(0) { - Ok(Field::Value(inner)) => Some(FieldInfo { - decoder: v.decoder, - should_parse: inner.should_parse, - prop_id: inner.prop_id, - }), - _ => None, - }, - _ => None, - }; - // Flatten vector props - if let Some(mut fi) = info { - if fi.prop_id == MY_WEAPONS_OFFSET { - if path.last == 1 { - } else { - fi.prop_id = MY_WEAPONS_OFFSET + path.path[2] as u32 + 1; - } - } - if fi.prop_id == WEAPON_SKIN_ID { - fi.prop_id = WEAPON_SKIN_ID + path.path[1] as u32; - } - if path.path[1] != 1 { - if fi.prop_id >= ITEM_PURCHASE_COUNT && fi.prop_id < ITEM_PURCHASE_COUNT + FLATTENED_VEC_MAX_LEN { - fi.prop_id = ITEM_PURCHASE_COUNT + path.path[2] as u32; - } - if fi.prop_id >= ITEM_PURCHASE_DEF_IDX && fi.prop_id < ITEM_PURCHASE_DEF_IDX + FLATTENED_VEC_MAX_LEN { - fi.prop_id = ITEM_PURCHASE_DEF_IDX + path.path[2] as u32; - } - if fi.prop_id >= ITEM_PURCHASE_COST && fi.prop_id < ITEM_PURCHASE_COST + FLATTENED_VEC_MAX_LEN { - fi.prop_id = ITEM_PURCHASE_COST + path.path[2] as u32; - } - if fi.prop_id >= ITEM_PURCHASE_HANDLE && fi.prop_id < ITEM_PURCHASE_HANDLE + FLATTENED_VEC_MAX_LEN { - fi.prop_id = ITEM_PURCHASE_HANDLE + path.path[2] as u32; - } - if fi.prop_id >= ITEM_PURCHASE_NEW_DEF_IDX && fi.prop_id < ITEM_PURCHASE_NEW_DEF_IDX + FLATTENED_VEC_MAX_LEN { - fi.prop_id = ITEM_PURCHASE_NEW_DEF_IDX + path.path[2] as u32; - } - } - return Some(fi); - } - return None; - } - fn listen_for_events( - entity: &mut Entity, - result: &Variant, - _field: &Field, - field_info: Option, - prop_controller: &PropController, - ) -> Vec { - // Might want to start splitting this function - let mut events = vec![]; - if let Some(fi) = field_info { - // round end - if let Some(id) = prop_controller.special_ids.round_end_count { - if fi.prop_id == id { - events.push(GameEventInfo::RoundEnd(RoundEnd { - old_value: entity.props.get(&id).cloned(), - new_value: Some(result.clone()), - })); - } - } - // Round win reason - if let Some(id) = prop_controller.special_ids.round_win_reason { - if fi.prop_id == id { - if let Variant::I32(reason) = result { - events.push(GameEventInfo::RoundWinReason(RoundWinReason { reason: *reason })); - } - } - } - // freeze period start - if let Some(id) = prop_controller.special_ids.round_start_count { - if fi.prop_id == id { - events.push(GameEventInfo::FreezePeriodStart(true)); - } - } - if let Some(id) = prop_controller.special_ids.match_end_count { - if fi.prop_id == id { - events.push(GameEventInfo::MatchEnd()); - } - } - if fi.prop_id >= ITEM_PURCHASE_COST && fi.prop_id < ITEM_PURCHASE_COST + FLATTENED_VEC_MAX_LEN { - events.push(GameEventInfo::WeaponCreateNCost((result.clone(), entity.entity_id))); - } - if fi.prop_id >= ITEM_PURCHASE_HANDLE && fi.prop_id < ITEM_PURCHASE_HANDLE + FLATTENED_VEC_MAX_LEN { - events.push(GameEventInfo::WeaponCreateHitem((result.clone(), entity.entity_id))); - } - if fi.prop_id >= ITEM_PURCHASE_COUNT && fi.prop_id < ITEM_PURCHASE_COUNT + FLATTENED_VEC_MAX_LEN { - events.push(GameEventInfo::WeaponPurchaseCount(( - result.clone(), - entity.entity_id, - fi.prop_id, - ))); - } - if fi.prop_id >= ITEM_PURCHASE_DEF_IDX && fi.prop_id < ITEM_PURCHASE_DEF_IDX + FLATTENED_VEC_MAX_LEN { - events.push(GameEventInfo::WeaponCreateDefIdx(( - result.clone(), - entity.entity_id, - fi.prop_id, - ))); - } - } - events - } - + #[inline] fn write_fp(&mut self, fp_src: &mut FieldPath, idx: usize) -> Result<(), DemoParserError> { match self.paths.get_mut(idx) { Some(entry) => *entry = *fp_src, @@ -459,129 +306,6 @@ impl<'a> SecondPassParser<'a> { } Ok(()) } - #[inline(always)] - fn find_field<'b>(fp: &FieldPath, ser: &'b Serializer) -> Result<&'b Field, DemoParserError> { - let f = match ser.fields.get(fp.path[0] as usize) { - Some(entry) => entry, - None => return Err(DemoParserError::IllegalPathOp), - }; - match fp.last { - 0 => Ok(f), - 1 => Ok(f.get_inner(fp.path[1] as usize)?), - 2 => Ok(f.get_inner(fp.path[1] as usize)?.get_inner(fp.path[2] as usize)?), - 3 => Ok(f - .get_inner(fp.path[1] as usize)? - .get_inner(fp.path[2] as usize)? - .get_inner(fp.path[3] as usize)?), - 4 => Ok(f - .get_inner(fp.path[1] as usize)? - .get_inner(fp.path[2] as usize)? - .get_inner(fp.path[3] as usize)? - .get_inner(fp.path[4] as usize)?), - 5 => Ok(f - .get_inner(fp.path[1] as usize)? - .get_inner(fp.path[2] as usize)? - .get_inner(fp.path[3] as usize)? - .get_inner(fp.path[4] as usize)? - .get_inner(fp.path[5] as usize)?), - _ => return Err(DemoParserError::IllegalPathOp), - } - } - - pub fn gather_extra_info(&mut self, entity_id: &i32, is_baseline: bool) -> Result<(), DemoParserError> { - // Boring stuff.. function does some bookkeeping - let entity = match self.entities.get(*entity_id as usize) { - Some(Some(entity)) => entity, - _ => return Err(DemoParserError::EntityNotFound), - }; - if !(entity.entity_type == EntityType::PlayerController || entity.entity_type == EntityType::Team) { - return Ok(()); - } - if entity.entity_type == EntityType::Team && !is_baseline { - if let Some(team_num_id) = self.prop_controller.special_ids.team_team_num { - if let Ok(Variant::U32(t)) = self.get_prop_from_ent(&team_num_id, entity_id) { - match t { - 1 => self.teams.team1_entid = Some(*entity_id), - 2 => self.teams.team2_entid = Some(*entity_id), - 3 => self.teams.team3_entid = Some(*entity_id), - _ => {} - } - } - } - } - - let team_num = match self.prop_controller.special_ids.teamnum { - Some(team_num_id) => match self.get_prop_from_ent(&team_num_id, entity_id) { - Ok(team_num) => match team_num { - Variant::U32(team_num) => Some(team_num), - _ => return Err(DemoParserError::IncorrectMetaDataProp), - }, - Err(_) => None, - }, - _ => None, - }; - - let name = match self.prop_controller.special_ids.player_name { - Some(id) => match self.get_prop_from_ent(&id, entity_id) { - Ok(team_num) => match team_num { - Variant::String(team_num) => Some(team_num), - _ => return Err(DemoParserError::IncorrectMetaDataProp), - }, - Err(_) => None, - }, - _ => None, - }; - let steamid = match self.prop_controller.special_ids.steamid { - Some(id) => match self.get_prop_from_ent(&id, entity_id) { - Ok(team_num) => match team_num { - Variant::U64(team_num) => Some(team_num), - _ => return Err(DemoParserError::IncorrectMetaDataProp), - }, - Err(_) => None, - }, - _ => None, - }; - let player_entid = match self.prop_controller.special_ids.player_pawn { - Some(id) => match self.get_prop_from_ent(&id, entity_id) { - Ok(player_entid) => match player_entid { - Variant::U32(handle) => Some((handle & 0x7FF) as i32), - _ => return Err(DemoParserError::IncorrectMetaDataProp), - }, - Err(_) => None, - }, - _ => None, - }; - if let Some(e) = player_entid { - if e != PLAYER_ENTITY_HANDLE_MISSING && steamid != Some(0) && team_num != Some(SPECTATOR_TEAM_NUM) { - match self.should_remove(steamid) { - Some(eid) => { - self.players.remove(&eid); - } - None => {} - } - self.players.insert( - e, - PlayerMetaData { - name: name, - team_num: team_num, - player_entity_id: player_entid, - steamid: steamid, - controller_entid: Some(*entity_id), - }, - ); - } - } - Ok(()) - } - fn should_remove(&self, steamid: Option) -> Option { - for (entid, player) in &self.players { - if player.steamid == steamid { - return Some(*entid); - } - } - None - } - fn create_new_entity( &mut self, bitreader: &mut Bitreader, @@ -601,7 +325,12 @@ impl<'a> SecondPassParser<'a> { EntityType::C4 => self.c4_entity_id = Some(*entity_id), _ => {} }; - let entity = SecondPassParser::make_ent(entity_id, cls_id, entity_type); + let entity = Entity { + entity_id: *entity_id, + cls_id: cls_id, + props: AHashMap::with_capacity(0), + entity_type: entity_type, + }; if self.entities.len() as i32 <= *entity_id { // if corrupt, this can cause oom allocations if *entity_id > 100000 { @@ -621,14 +350,7 @@ impl<'a> SecondPassParser<'a> { } Ok(()) } - fn make_ent(entity_id: &i32, cls_id: u32, entity_type: EntityType) -> Entity { - Entity { - entity_id: *entity_id, - cls_id: cls_id, - props: AHashMap::with_capacity(0), - entity_type: entity_type, - } - } + pub fn check_entity_type(&self, cls_id: &u32) -> Result { let class = match self.cls_by_id.get(*cls_id as usize) { Some(cls) => cls, @@ -649,10 +371,3 @@ impl<'a> SecondPassParser<'a> { return Ok(EntityType::Normal); } } -#[inline(always)] -fn generate_fp() -> FieldPath { - FieldPath { - path: [-1, 0, 0, 0, 0, 0, 0], - last: 0, - } -} diff --git a/src/parser/src/second_pass/game_events.rs b/src/parser/src/second_pass/game_events.rs index 81e4a3f4..d11d7742 100644 --- a/src/parser/src/second_pass/game_events.rs +++ b/src/parser/src/second_pass/game_events.rs @@ -1,4 +1,5 @@ -use super::entities::RoundEnd; +//use super::entities::RoundEnd; +use crate::first_pass::prop_controller::PropController; use crate::first_pass::prop_controller::PropInfo; use crate::first_pass::prop_controller::ITEM_PURCHASE_COST; use crate::first_pass::prop_controller::ITEM_PURCHASE_COUNT; @@ -7,12 +8,15 @@ use crate::first_pass::prop_controller::ITEM_PURCHASE_NEW_DEF_IDX; use crate::first_pass::prop_controller::WEAPON_FLOAT; use crate::first_pass::prop_controller::WEAPON_PAINT_SEED; use crate::first_pass::read_bits::DemoParserError; +use crate::first_pass::sendtables::Field; +use crate::first_pass::sendtables::FieldInfo; use crate::first_pass::stringtables::UserInfo; use crate::maps::ROUND_WIN_REASON; use crate::maps::ROUND_WIN_REASON_TO_WINNER; use crate::maps::WEAPINDICIES; use crate::second_pass::collect_data::PropType; -use crate::second_pass::entities::GameEventInfo; +use crate::second_pass::entities::Entity; +//use crate::second_pass::entities::GameEventInfo; use crate::second_pass::entities::PlayerMetaData; use crate::second_pass::parser_settings::SecondPassParser; use crate::second_pass::variants::*; @@ -35,6 +39,26 @@ static INTERNALEVENTFIELDS: &'static [&str] = &[ "attacker_pawn", "assister_pawn", ]; +#[derive(Debug, Clone)] +pub struct RoundEnd { + pub old_value: Option, + pub new_value: Option, +} +#[derive(Debug, Clone)] +pub struct RoundWinReason { + pub reason: i32, +} +#[derive(Debug, Clone)] +pub enum GameEventInfo { + RoundEnd(RoundEnd), + RoundWinReason(RoundWinReason), + FreezePeriodStart(bool), + MatchEnd(), + WeaponCreateHitem((Variant, i32)), + WeaponCreateNCost((Variant, i32)), + WeaponCreateDefIdx((Variant, i32, u32)), + WeaponPurchaseCount((Variant, i32, u32)), +} static ENTITIES_FIRST_EVENTS: &'static [&str] = &["inferno_startburn", "decoy_started", "inferno_expire"]; static REMOVEDEVENTS: &'static [&str] = &["server_cvar"]; @@ -989,6 +1013,69 @@ impl<'a> SecondPassParser<'a> { } Ok(()) } + pub fn listen_for_events( + entity: &mut Entity, + result: &Variant, + _field: &Field, + field_info: Option, + prop_controller: &PropController, + ) -> Vec { + // Might want to start splitting this function + let mut events = vec![]; + if let Some(fi) = field_info { + // round end + if let Some(id) = prop_controller.special_ids.round_end_count { + if fi.prop_id == id { + events.push(GameEventInfo::RoundEnd(RoundEnd { + old_value: entity.props.get(&id).cloned(), + new_value: Some(result.clone()), + })); + } + } + // Round win reason + if let Some(id) = prop_controller.special_ids.round_win_reason { + if fi.prop_id == id { + if let Variant::I32(reason) = result { + events.push(GameEventInfo::RoundWinReason(RoundWinReason { reason: *reason })); + } + } + } + // freeze period start + if let Some(id) = prop_controller.special_ids.round_start_count { + if fi.prop_id == id { + events.push(GameEventInfo::FreezePeriodStart(true)); + } + } + if let Some(id) = prop_controller.special_ids.match_end_count { + if fi.prop_id == id { + events.push(GameEventInfo::MatchEnd()); + } + } + use crate::first_pass::prop_controller::FLATTENED_VEC_MAX_LEN; + use crate::first_pass::prop_controller::ITEM_PURCHASE_HANDLE; + if fi.prop_id >= ITEM_PURCHASE_COST && fi.prop_id < ITEM_PURCHASE_COST + FLATTENED_VEC_MAX_LEN { + events.push(GameEventInfo::WeaponCreateNCost((result.clone(), entity.entity_id))); + } + if fi.prop_id >= ITEM_PURCHASE_HANDLE && fi.prop_id < ITEM_PURCHASE_HANDLE + FLATTENED_VEC_MAX_LEN { + events.push(GameEventInfo::WeaponCreateHitem((result.clone(), entity.entity_id))); + } + if fi.prop_id >= ITEM_PURCHASE_COUNT && fi.prop_id < ITEM_PURCHASE_COUNT + FLATTENED_VEC_MAX_LEN { + events.push(GameEventInfo::WeaponPurchaseCount(( + result.clone(), + entity.entity_id, + fi.prop_id, + ))); + } + if fi.prop_id >= ITEM_PURCHASE_DEF_IDX && fi.prop_id < ITEM_PURCHASE_DEF_IDX + FLATTENED_VEC_MAX_LEN { + events.push(GameEventInfo::WeaponCreateDefIdx(( + result.clone(), + entity.entity_id, + fi.prop_id, + ))); + } + } + events + } } // what is this shit fn parse_key(key: &Key_t) -> Option { diff --git a/src/parser/src/second_pass/parser.rs b/src/parser/src/second_pass/parser.rs index 2a9eb2a2..9d7dd83c 100644 --- a/src/parser/src/second_pass/parser.rs +++ b/src/parser/src/second_pass/parser.rs @@ -1,3 +1,4 @@ +use crate::first_pass::parser::Frame; use crate::first_pass::parser::HEADER_ENDS_AT_BYTE; use crate::first_pass::parser_settings::FirstPassParser; use crate::first_pass::prop_controller::PropController; @@ -44,68 +45,109 @@ pub struct SecondPassOutput { pub df_per_player: AHashMap>, } impl<'a> SecondPassParser<'a> { - pub fn start(&mut self, demo_bytes: &[u8]) -> Result<(), DemoParserError> { + pub fn start(&mut self, demo_bytes: &'a [u8]) -> Result<(), DemoParserError> { let started_at = self.ptr; // re-use these to avoid allocation let mut buf = vec![0_u8; INNER_BUF_DEFAULT_LEN]; let mut buf2 = vec![0_u8; OUTER_BUF_DEFAULT_LEN]; loop { - let cmd = read_varint(demo_bytes, &mut self.ptr)?; - let tick = read_varint(demo_bytes, &mut self.ptr)?; - let size = read_varint(demo_bytes, &mut self.ptr)?; - self.tick = tick as i32; - // Safety check - if self.ptr + size as usize >= demo_bytes.len() { - break; - } - - let msg_type = cmd & !64; - let is_compressed = (cmd & 64) == 64; - let demo_cmd = demo_cmd_type_from_int(msg_type as i32)?; - - if demo_cmd == DEM_AnimationData || demo_cmd == DEM_SendTables || demo_cmd == DEM_StringTables { - self.ptr += size as usize; + let frame = self.read_frame(demo_bytes)?; + if frame.demo_cmd == DEM_AnimationData || frame.demo_cmd == DEM_SendTables || frame.demo_cmd == DEM_StringTables { + self.ptr += frame.size as usize; continue; } - let input = &demo_bytes[self.ptr..self.ptr + size as usize]; - self.ptr += size as usize; - let bytes = match is_compressed { - true => { - FirstPassParser::resize_if_needed(&mut buf2, decompress_len(input))?; - match SnapDecoder::new().decompress(input, &mut buf2) { - Ok(idx) => &buf2[..idx], - Err(e) => return Err(DemoParserError::DecompressionFailure(format!("{}", e))), - } - } - false => input, - }; - - let ok = match demo_cmd { - DEM_SignonPacket => self.parse_packet(&bytes, &mut buf), - DEM_Packet => self.parse_packet(&bytes, &mut buf), + let bytes = self.slice_packet_bytes(demo_bytes, frame.size)?; + let bytes = self.decompress_if_needed(&mut buf, bytes, &frame)?; + self.ptr += frame.size; + let ok = match frame.demo_cmd { + DEM_SignonPacket => self.parse_packet(&bytes, &mut buf2), + DEM_Packet => self.parse_packet(&bytes, &mut buf2), + DEM_Stop => break, DEM_FullPacket => { - match self.parse_all_packets { - true => { - self.parse_full_packet(&bytes, false)?; - } - false => { - if self.fullpackets_parsed == 0 && started_at != HEADER_ENDS_AT_BYTE { - self.parse_full_packet(&bytes, true)?; - self.fullpackets_parsed += 1; - } else { - break; - } - } + if self.parse_full_packet_and_break_if_needed(&bytes, &mut buf2, started_at)? { + break; } Ok(()) } - DEM_Stop => break, _ => Ok(()), }; ok?; } Ok(()) } + fn parse_full_packet_and_break_if_needed( + &mut self, + bytes: &[u8], + buf: &mut Vec, + started_at: usize, + ) -> Result { + match self.parse_all_packets { + true => { + self.parse_full_packet(&bytes, false, buf)?; + } + false => { + if self.fullpackets_parsed == 0 && started_at != HEADER_ENDS_AT_BYTE { + self.parse_full_packet(&bytes, true, buf)?; + self.fullpackets_parsed += 1; + } else { + return Ok(true); + } + } + } + return Ok(false); + } + fn read_frame(&mut self, demo_bytes: &[u8]) -> Result { + let frame_starts_at = self.ptr; + let cmd = read_varint(demo_bytes, &mut self.ptr)?; + let tick = read_varint(demo_bytes, &mut self.ptr)?; + let size = read_varint(demo_bytes, &mut self.ptr)?; + self.tick = tick as i32; + + let msg_type = cmd & !64; + let is_compressed = (cmd & 64) == 64; + let demo_cmd = demo_cmd_type_from_int(msg_type as i32)?; + + Ok(Frame { + size: size as usize, + frame_starts_at: frame_starts_at, + is_compressed: is_compressed, + demo_cmd: demo_cmd, + }) + } + fn slice_packet_bytes(&mut self, demo_bytes: &'a [u8], frame_size: usize) -> Result<&'a [u8], DemoParserError> { + if self.ptr + frame_size as usize >= demo_bytes.len() { + return Err(DemoParserError::MalformedMessage); + } + Ok(&demo_bytes[self.ptr..self.ptr + frame_size]) + } + fn decompress_if_needed<'b>( + &mut self, + buf: &'b mut Vec, + possibly_uncompressed_bytes: &'b [u8], + frame: &Frame, + ) -> Result<&'b [u8], DemoParserError> { + match frame.is_compressed { + true => { + FirstPassParser::resize_if_needed(buf, decompress_len(possibly_uncompressed_bytes))?; + match SnapDecoder::new().decompress(possibly_uncompressed_bytes, buf) { + Ok(idx) => Ok(&buf[..idx]), + Err(e) => return Err(DemoParserError::DecompressionFailure(format!("{}", e))), + } + } + false => Ok(possibly_uncompressed_bytes), + } + } + pub fn resize_if_needed(buf: &mut Vec, needed_len: Result) -> Result<(), DemoParserError> { + match needed_len { + Ok(len) => { + if buf.len() < len { + buf.resize(len, 0) + } + } + Err(e) => return Err(DemoParserError::DecompressionFailure(e.to_string())), + }; + Ok(()) + } pub fn parse_packet(&mut self, bytes: &[u8], buf: &mut Vec) -> Result<(), DemoParserError> { let msg: CDemoPacket = match Message::parse_from_bytes(bytes) { @@ -113,6 +155,17 @@ impl<'a> SecondPassParser<'a> { Ok(msg) => msg, }; let mut bitreader = Bitreader::new(msg.data()); + self.parse_packet_from_bitreader(&mut bitreader, buf, true, false)?; + Ok(()) + } + + pub fn parse_packet_from_bitreader( + &mut self, + bitreader: &mut Bitreader, + buf: &mut Vec, + should_parse_entities: bool, + is_fullpacket: bool, + ) -> Result<(), DemoParserError> { let mut wrong_order_events = vec![]; while bitreader.bits_remaining().unwrap_or(0) > 8 { @@ -124,7 +177,12 @@ impl<'a> SecondPassParser<'a> { bitreader.read_n_bytes_mut(size as usize, buf)?; let msg_bytes = &buf[..size as usize]; let ok = match netmessage_type_from_int(msg_type as i32) { - svc_PacketEntities => self.parse_packet_ents(msg_bytes, false), + svc_PacketEntities => { + if should_parse_entities { + self.parse_packet_ents(&msg_bytes, is_fullpacket)?; + } + Ok(()) + } svc_CreateStringTable => self.parse_create_stringtable(msg_bytes), svc_UpdateStringTable => self.update_string_table(msg_bytes), svc_ServerInfo => self.parse_server_info(msg_bytes), @@ -137,20 +195,8 @@ impl<'a> SecondPassParser<'a> { CS_UM_ServerRankUpdate => self.create_custom_event_rank_update(msg_bytes), net_Tick => self.parse_net_tick(msg_bytes), svc_ClearAllStringTables => self.clear_stringtables(), - svc_VoiceData => { - if let Ok(m) = Message::parse_from_bytes(msg_bytes) { - self.voice_data.push(m); - } - Ok(()) - } - GE_Source1LegacyGameEvent => match self.parse_event(msg_bytes) { - Ok(Some(event)) => { - wrong_order_events.push(event); - Ok(()) - } - Ok(None) => Ok(()), - Err(e) => return Err(e), - }, + svc_VoiceData => self.parse_voice_data(msg_bytes), + GE_Source1LegacyGameEvent => self.parse_game_event(msg_bytes, &mut wrong_order_events), _ => Ok(()), }; ok? @@ -161,6 +207,23 @@ impl<'a> SecondPassParser<'a> { self.collect_entities(); Ok(()) } + pub fn parse_voice_data(&mut self, bytes: &[u8]) -> Result<(), DemoParserError> { + if let Ok(m) = Message::parse_from_bytes(bytes) { + self.voice_data.push(m); + } + Ok(()) + } + pub fn parse_game_event(&mut self, bytes: &[u8], wrong_order_events: &mut Vec) -> Result<(), DemoParserError> { + match self.parse_event(bytes) { + Ok(Some(event)) => { + wrong_order_events.push(event); + Ok(()) + } + Ok(None) => Ok(()), + Err(e) => return Err(e), + } + } + pub fn parse_net_tick(&mut self, bytes: &[u8]) -> Result<(), DemoParserError> { let message: CNETMsg_Tick = match Message::parse_from_bytes(&bytes) { Ok(message) => message, @@ -169,14 +232,24 @@ impl<'a> SecondPassParser<'a> { self.net_tick = message.tick(); Ok(()) } - pub fn parse_full_packet(&mut self, bytes: &[u8], should_parse_entities: bool) -> Result<(), DemoParserError> { - self.string_tables = vec![]; + pub fn parse_full_packet( + &mut self, + bytes: &[u8], + should_parse_entities: bool, + buf: &mut Vec, + ) -> Result<(), DemoParserError> { + self.string_tables = vec![]; let full_packet: CDemoFullPacket = match Message::parse_from_bytes(bytes) { Err(_e) => return Err(DemoParserError::MalformedMessage), Ok(p) => p, }; + self.parse_full_packet_stringtables(&full_packet); + let mut bitreader = Bitreader::new(full_packet.packet.data()); + self.parse_packet_from_bitreader(&mut bitreader, buf, should_parse_entities, true) + } + pub fn parse_full_packet_stringtables(&mut self, full_packet: &CDemoFullPacket) { for item in &full_packet.string_table.tables { if item.table_name == Some("instancebaseline".to_string()) { for i in &item.items { @@ -194,44 +267,6 @@ impl<'a> SecondPassParser<'a> { } } } - let packet = match full_packet.packet.0 { - Some(packet) => packet, - None => return Err(DemoParserError::MalformedMessage), - }; - let mut bitreader = Bitreader::new(packet.data()); - let mut buf = vec![0; 5_00_000]; - // Inner loop - while bitreader.bits_remaining().unwrap_or(0) > 8 { - let msg_type = bitreader.read_u_bit_var()?; - let size = bitreader.read_varint()?; - if buf.len() < size as usize { - buf.resize(size as usize, 0) - } - bitreader.read_n_bytes_mut(size as usize, &mut buf)?; - let msg_bytes = &buf[..size as usize]; - let ok = match netmessage_type_from_int(msg_type as i32) { - svc_PacketEntities => { - if should_parse_entities { - self.parse_packet_ents(&msg_bytes, true)?; - } - Ok(()) - } - svc_CreateStringTable => self.parse_create_stringtable(&msg_bytes), - svc_UpdateStringTable => self.update_string_table(&msg_bytes), - CS_UM_SendPlayerItemDrops => self.parse_item_drops(&msg_bytes), - CS_UM_EndOfMatchAllPlayersData => self.parse_player_end_msg(&msg_bytes), - UM_SayText2 => self.create_custom_event_chat_message(&msg_bytes), - UM_SayText => self.create_custom_event_server_message(bytes), - net_SetConVar => self.create_custom_event_parse_convars(&msg_bytes), - CS_UM_PlayerStatsUpdate => self.parse_player_stats_update(&msg_bytes), - svc_ServerInfo => self.parse_server_info(&msg_bytes), - net_Tick => self.parse_net_tick(&msg_bytes), - svc_ClearAllStringTables => self.clear_stringtables(), - _ => Ok(()), - }; - ok? - } - Ok(()) } fn clear_stringtables(&mut self) -> Result<(), DemoParserError> { self.string_tables = vec![]; diff --git a/src/parser/src/second_pass/path_ops.rs b/src/parser/src/second_pass/path_ops.rs index 58f40598..b5f22f9f 100644 --- a/src/parser/src/second_pass/path_ops.rs +++ b/src/parser/src/second_pass/path_ops.rs @@ -1,5 +1,13 @@ use crate::first_pass::read_bits::{Bitreader, DemoParserError}; +#[inline(always)] +pub fn generate_fp() -> FieldPath { + FieldPath { + path: [-1, 0, 0, 0, 0, 0, 0], + last: 0, + } +} + #[inline(always)] pub fn do_op(opcode: u8, bitreader: &mut Bitreader, field_path: &mut FieldPath) -> Result<(), DemoParserError> { // taken directly from here: https://github.com/dotabuff/manta/blob/master/field_path.go diff --git a/src/parser/src/second_pass/variants2.rs b/src/parser/src/second_pass/variants2.rs new file mode 100644 index 00000000..790a014a --- /dev/null +++ b/src/parser/src/second_pass/variants2.rs @@ -0,0 +1,3 @@ +use crate::second_pass::variants::Variant; + +pub struct VV {}