From 9abcfaf0f0451aa41352a23622053d064ac08719 Mon Sep 17 00:00:00 2001 From: Laiho Date: Sun, 25 Aug 2024 00:51:20 +0300 Subject: [PATCH 1/9] temp --- src/parser/src/maps.rs | 2 + src/parser/src/second_pass/parser.rs | 66 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/parser/src/maps.rs b/src/parser/src/maps.rs index 24bceca4..baee56ba 100644 --- a/src/parser/src/maps.rs +++ b/src/parser/src/maps.rs @@ -272,6 +272,7 @@ pub fn netmessage_type_from_int(msg_type: i32) -> NetmessageType { 72 => svc_UserMessage, 73 => svc_HltvReplay, 74 => svc_Broadcast_Command, + 76 => svc_UserCmds, 101 => UM_AchievementEvent, 102 => UM_CloseCaption, 103 => UM_CloseCaptionDirect, @@ -589,6 +590,7 @@ pub enum NetmessageType { UM_DllStatusResponse, UM_RequestInventory, UM_InventoryResponse, + svc_UserCmds, } pub fn demo_cmd_type_from_int(value: i32) -> Result { match value { diff --git a/src/parser/src/second_pass/parser.rs b/src/parser/src/second_pass/parser.rs index 1e8172f5..76587195 100644 --- a/src/parser/src/second_pass/parser.rs +++ b/src/parser/src/second_pass/parser.rs @@ -11,12 +11,14 @@ use crate::maps::netmessage_type_from_int; use crate::maps::NetmessageType::*; use crate::second_pass::collect_data::ProjectileRecord; use crate::second_pass::entities::Entity; +use crate::second_pass::game_events::EventField; use crate::second_pass::game_events::GameEvent; use crate::second_pass::parser_settings::SecondPassParser; use crate::second_pass::parser_settings::*; use crate::second_pass::variants::PropColumn; use ahash::AHashMap; use ahash::AHashSet; +use csgoproto::cs_usercmd::CSGOUserCmdPB; use csgoproto::demo::*; use csgoproto::netmessages::*; use csgoproto::networkbasetypes::CNETMsg_Tick; @@ -214,6 +216,7 @@ impl<'a> SecondPassParser<'a> { svc_ClearAllStringTables => self.clear_stringtables(), svc_VoiceData => self.parse_voice_data(msg_bytes), GE_Source1LegacyGameEvent => self.parse_game_event(msg_bytes, &mut wrong_order_events), + svc_UserCmds => self.parse_user_cmd(msg_bytes), _ => Ok(()), }; ok? @@ -223,6 +226,69 @@ impl<'a> SecondPassParser<'a> { } Ok(()) } + pub fn parse_user_cmd(&mut self, bytes: &[u8]) -> Result<(), DemoParserError> { + let m: CSVCMsg_UserCommands = Message::parse_from_bytes(bytes).unwrap(); + + for cmd in m.commands { + let user_cmd = CSGOUserCmdPB::parse_from_bytes(cmd.data()).unwrap(); + let mut fields = vec![]; + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::F32(user_cmd.base.viewangles.x())), + name: "X".to_string(), + }); + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::F32(user_cmd.base.viewangles.y())), + name: "Y".to_string(), + }); + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::F32(user_cmd.base.viewangles.z())), + name: "Z".to_string(), + }); + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::F32(user_cmd.base.forwardmove())), + name: "forward_move".to_string(), + }); + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::I32(user_cmd.base.impulse())), + name: "impulse".to_string(), + }); + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::I32(user_cmd.base.mousedx())), + name: "mouse_x".to_string(), + }); + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::I32(user_cmd.base.mousedy())), + name: "mouse_y".to_string(), + }); + + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::U64( + user_cmd.base.buttons_pb.buttonstate1(), + )), + name: "button_state_1".to_string(), + }); + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::U64( + user_cmd.base.buttons_pb.buttonstate2(), + )), + name: "button_state_2".to_string(), + }); + fields.push(EventField { + data: Some(crate::second_pass::variants::Variant::U64( + user_cmd.base.buttons_pb.buttonstate3(), + )), + name: "button_state_3".to_string(), + }); + + self.game_events.push(GameEvent { + name: "user_cmd".to_string(), + fields: fields, + tick: self.tick, + }) + } + 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); From 93352bd9e2caf8a5f1b2cb893f16999dc07b2c5b Mon Sep 17 00:00:00 2001 From: Laiho Date: Thu, 12 Sep 2024 01:05:23 +0300 Subject: [PATCH 2/9] before flattening usercmds --- src/parser/src/first_pass/prop_controller.rs | 135 +++++++++++++++++++ src/parser/src/second_pass/entities.rs | 6 +- src/parser/src/second_pass/parser.rs | 90 +++++-------- 3 files changed, 176 insertions(+), 55 deletions(-) diff --git a/src/parser/src/first_pass/prop_controller.rs b/src/parser/src/first_pass/prop_controller.rs index c75d5046..60db9ce4 100644 --- a/src/parser/src/first_pass/prop_controller.rs +++ b/src/parser/src/first_pass/prop_controller.rs @@ -48,6 +48,29 @@ pub const ITEM_PURCHASE_HANDLE: u32 = 500000000; pub const ITEM_PURCHASE_NEW_DEF_IDX: u32 = 600000000; pub const FLATTENED_VEC_MAX_LEN: u32 = 100000; +pub const USERCMD_VIEWANGLE_X: u32 = 100000022; +pub const USERCMD_VIEWANGLE_Y: u32 = 100000023; +pub const USERCMD_VIEWANGLE_Z: u32 = 100000024; +pub const USERCMD_FORWARDMOVE: u32 = 100000025; +pub const USERCMD_IMPULSE: u32 = 100000026; +pub const USERCMD_MOUSE_DX: u32 = 100000027; +pub const USERCMD_MOUSE_DY: u32 = 100000028; +pub const USERCMD_BUTTONSTATE_1: u32 = 100000029; +pub const USERCMD_BUTTONSTATE_2: u32 = 100000030; +pub const USERCMD_BUTTONSTATE_3: u32 = 100000031; +pub const USERCMD_CONSUMED_SERVER_ANGLE_CHANGES: u32 = 100000032; +pub const USERCMD_LEFTMOVE: u32 = 100000033; +pub const USERCMD_WEAPON_SELECT: u32 = 100000034; +pub const USERCMD_SUBTICK_MOVE_ANALOG_FORWARD_DELTA: u32 = 100000035; +pub const USERCMD_SUBTICK_MOVE_ANALOG_LEFT_DELTA: u32 = 100000036; +pub const USERCMD_SUBTICK_MOVE_BUTTON: u32 = 100000037; +pub const USERCMD_SUBTICK_MOVE_WHEN: u32 = 100000038; +pub const USERCMD_SUBTICK_LEFT_HAND_DESIRED: u32 = 100000039; + +pub const USERCMD_ATTACK_START_HISTORY_INDEX_1: u32 = 100000040; +pub const USERCMD_ATTACK_START_HISTORY_INDEX_2: u32 = 100000041; +pub const USERCMD_ATTACK_START_HISTORY_INDEX_3: u32 = 100000042; + #[derive(Clone, Debug)] pub struct PropController { pub id: u32, @@ -340,6 +363,118 @@ impl PropController { is_player_prop: true, }); } + if self.wanted_player_props.contains(&("usercmd_viewangle_x".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_VIEWANGLE_X, + prop_type: PropType::Player, + prop_name: "usercmd_viewangle_x".to_string(), + prop_friendly_name: "usercmd_viewangle_x".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_viewangle_y".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_VIEWANGLE_Y, + prop_type: PropType::Player, + prop_name: "usercmd_viewangle_y".to_string(), + prop_friendly_name: "usercmd_viewangle_y".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_viewangle_z".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_VIEWANGLE_Z, + prop_type: PropType::Player, + prop_name: "usercmd_viewangle_z".to_string(), + prop_friendly_name: "usercmd_viewangle_z".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_buttonstate_1".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_BUTTONSTATE_1, + prop_type: PropType::Player, + prop_name: "usercmd_buttonstate_1".to_string(), + prop_friendly_name: "usercmd_buttonstate_1".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_buttonstate_2".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_BUTTONSTATE_2, + prop_type: PropType::Player, + prop_name: "usercmd_buttonstate_2".to_string(), + prop_friendly_name: "usercmd_buttonstate_2".to_string(), + is_player_prop: true, + }); + } + if self + .wanted_player_props + .contains(&("usercmd_consumed_server_angle_changes".to_string())) + { + self.prop_infos.push(PropInfo { + id: USERCMD_CONSUMED_SERVER_ANGLE_CHANGES, + prop_type: PropType::Player, + prop_name: "usercmd_consumed_server_angle_changes".to_string(), + prop_friendly_name: "usercmd_consumed_server_angle_changes".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_forward_move".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_FORWARDMOVE, + prop_type: PropType::Player, + prop_name: "usercmd_forward_move".to_string(), + prop_friendly_name: "usercmd_forward_move".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_impulse".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_IMPULSE, + prop_type: PropType::Player, + prop_name: "usercmd_impulse".to_string(), + prop_friendly_name: "usercmd_impulse".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_mouse_dx".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_MOUSE_DX, + prop_type: PropType::Player, + prop_name: "usercmd_mouse_dx".to_string(), + prop_friendly_name: "usercmd_mouse_dx".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_mouse_dy".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_MOUSE_DY, + prop_type: PropType::Player, + prop_name: "usercmd_mouse_dy".to_string(), + prop_friendly_name: "usercmd_mouse_dy".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_left_hand_desired".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_SUBTICK_LEFT_HAND_DESIRED, + prop_type: PropType::Player, + prop_name: "usercmd_left_hand_desired".to_string(), + prop_friendly_name: "usercmd_left_hand_desired".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("usercmd_weapon_select".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_WEAPON_SELECT, + prop_type: PropType::Player, + prop_name: "usercmd_weapon_select".to_string(), + prop_friendly_name: "usercmd_weapon_select".to_string(), + is_player_prop: true, + }); + } + if self.wanted_player_props.contains(&("is_airborne".to_string())) { self.prop_infos.push(PropInfo { id: IS_AIRBORNE_ID, diff --git a/src/parser/src/second_pass/entities.rs b/src/parser/src/second_pass/entities.rs index ee0c2221..098fd9f5 100644 --- a/src/parser/src/second_pass/entities.rs +++ b/src/parser/src/second_pass/entities.rs @@ -1,3 +1,5 @@ +use std::fs; + use crate::first_pass::read_bits::Bitreader; use crate::first_pass::read_bits::DemoParserError; use crate::first_pass::sendtables::find_field; @@ -58,6 +60,8 @@ impl<'a> SecondPassParser<'a> { Err(_) => return Err(DemoParserError::MalformedMessage), Ok(msg) => msg, }; + // println!("{:?}", msg); + return Ok(()); let mut bitreader = Bitreader::new(msg.entity_data()); let mut entity_id: i32 = -1; @@ -277,7 +281,7 @@ impl<'a> SecondPassParser<'a> { _entity_id: &i32, ) { if let Field::Value(_v) = field { - if _v.full_name.contains("Services") { + if _v.full_name.contains("Services") { println!("{:?} {:?} {:?} {:?}", _path, field_info, _v.full_name, _result); } } diff --git a/src/parser/src/second_pass/parser.rs b/src/parser/src/second_pass/parser.rs index 76587195..8968c30a 100644 --- a/src/parser/src/second_pass/parser.rs +++ b/src/parser/src/second_pass/parser.rs @@ -1,7 +1,10 @@ +use std::fs; + 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; +use crate::first_pass::prop_controller::*; use crate::first_pass::read_bits::read_varint; use crate::first_pass::read_bits::Bitreader; use crate::first_pass::read_bits::DemoParserError; @@ -16,13 +19,16 @@ use crate::second_pass::game_events::GameEvent; use crate::second_pass::parser_settings::SecondPassParser; use crate::second_pass::parser_settings::*; use crate::second_pass::variants::PropColumn; +use crate::second_pass::variants::Variant; use ahash::AHashMap; use ahash::AHashSet; use csgoproto::cs_usercmd::CSGOUserCmdPB; use csgoproto::demo::*; use csgoproto::netmessages::*; use csgoproto::networkbasetypes::CNETMsg_Tick; +use protobuf::text_format::print_to_string_pretty; use protobuf::Message; +use protobuf_json_mapping::print_to_string; use snap::raw::decompress_len; use snap::raw::Decoder as SnapDecoder; use EDemoCommands::*; @@ -70,6 +76,10 @@ impl<'a> SecondPassParser<'a> { DEM_SignonPacket => self.parse_packet(&bytes, &mut buf2), DEM_Packet => self.parse_packet(&bytes, &mut buf2), DEM_Stop => break, + DEM_UserCmd => { + println!("{}", self.tick); + Ok(()) + } DEM_FullPacket => { if self.parse_full_packet_and_break_if_needed(&bytes, &mut buf2, started_at)? { break; @@ -216,7 +226,7 @@ impl<'a> SecondPassParser<'a> { svc_ClearAllStringTables => self.clear_stringtables(), svc_VoiceData => self.parse_voice_data(msg_bytes), GE_Source1LegacyGameEvent => self.parse_game_event(msg_bytes, &mut wrong_order_events), - svc_UserCmds => self.parse_user_cmd(msg_bytes), + // svc_UserCmds => self.parse_user_cmd(msg_bytes), _ => Ok(()), }; ok? @@ -227,64 +237,36 @@ impl<'a> SecondPassParser<'a> { Ok(()) } pub fn parse_user_cmd(&mut self, bytes: &[u8]) -> Result<(), DemoParserError> { - let m: CSVCMsg_UserCommands = Message::parse_from_bytes(bytes).unwrap(); + // We simply inject the values into the entities as if they came from packet_ents like any other val. + let m: CSVCMsg_UserCommands = Message::parse_from_bytes(bytes).unwrap(); for cmd in m.commands { let user_cmd = CSGOUserCmdPB::parse_from_bytes(cmd.data()).unwrap(); - let mut fields = vec![]; - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::F32(user_cmd.base.viewangles.x())), - name: "X".to_string(), - }); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::F32(user_cmd.base.viewangles.y())), - name: "Y".to_string(), - }); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::F32(user_cmd.base.viewangles.z())), - name: "Z".to_string(), - }); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::F32(user_cmd.base.forwardmove())), - name: "forward_move".to_string(), - }); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::I32(user_cmd.base.impulse())), - name: "impulse".to_string(), - }); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::I32(user_cmd.base.mousedx())), - name: "mouse_x".to_string(), - }); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::I32(user_cmd.base.mousedy())), - name: "mouse_y".to_string(), - }); + let s = print_to_string(&user_cmd); + //fs::write("omg.json", s); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::U64( - user_cmd.base.buttons_pb.buttonstate1(), - )), - name: "button_state_1".to_string(), - }); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::U64( - user_cmd.base.buttons_pb.buttonstate2(), - )), - name: "button_state_2".to_string(), - }); - fields.push(EventField { - data: Some(crate::second_pass::variants::Variant::U64( - user_cmd.base.buttons_pb.buttonstate3(), - )), - name: "button_state_3".to_string(), - }); + let entity_id = user_cmd.base.pawn_entity_handle() & 0x7FF; - self.game_events.push(GameEvent { - name: "user_cmd".to_string(), - fields: fields, - tick: self.tick, - }) + if let Some(Some(ent)) = self.entities.get_mut(entity_id as usize) { + ent.props + .insert(USERCMD_VIEWANGLE_X, Variant::F32(user_cmd.base.viewangles.x())); + ent.props + .insert(USERCMD_VIEWANGLE_Y, Variant::F32(user_cmd.base.viewangles.y())); + ent.props + .insert(USERCMD_VIEWANGLE_Z, Variant::F32(user_cmd.base.viewangles.z())); + ent.props + .insert(USERCMD_FORWARDMOVE, Variant::F32(user_cmd.base.forwardmove())); + ent.props.insert(USERCMD_LEFTMOVE, Variant::F32(user_cmd.base.leftmove())); + ent.props.insert(USERCMD_IMPULSE, Variant::I32(user_cmd.base.impulse())); + ent.props.insert(USERCMD_MOUSE_DX, Variant::I32(user_cmd.base.mousedx())); + ent.props.insert(USERCMD_MOUSE_DY, Variant::I32(user_cmd.base.mousedy())); + ent.props + .insert(USERCMD_BUTTONSTATE_1, Variant::U64(user_cmd.base.buttons_pb.buttonstate1())); + ent.props + .insert(USERCMD_BUTTONSTATE_2, Variant::U64(user_cmd.base.buttons_pb.buttonstate2())); + ent.props + .insert(USERCMD_BUTTONSTATE_3, Variant::U64(user_cmd.base.buttons_pb.buttonstate3())); + } } Ok(()) } From 1e94f09568b0019658b80bed925ec513ec42cec1 Mon Sep 17 00:00:00 2001 From: Laiho Date: Thu, 19 Sep 2024 14:15:00 +0300 Subject: [PATCH 3/9] temp --- src/csgoproto/Cargo.lock | 36 ++++----- src/csgoproto/Cargo.toml | 6 +- src/csgoproto/src/cs_usercmd.rs | 11 ++- src/csgoproto/src/cstrike15_gcmessages.rs | 2 +- src/csgoproto/src/cstrike15_usermessages.rs | 2 +- src/csgoproto/src/demo.rs | 2 +- src/csgoproto/src/engine_gcmessages.rs | 2 +- src/csgoproto/src/gcsdk_gcmessages.rs | 2 +- src/csgoproto/src/netmessages.rs | 2 +- src/csgoproto/src/network_connection.rs | 2 +- src/csgoproto/src/networkbasetypes.rs | 2 +- src/csgoproto/src/steammessages.rs | 2 +- src/csgoproto/src/usercmd.rs | 2 +- src/csgoproto/src/usermessages.rs | 2 +- src/parser/rustfmt.toml | 2 +- src/parser/src/first_pass/prop_controller.rs | 18 +++++ src/parser/src/maps.rs | 33 ++++++++ src/parser/src/second_pass/collect_data.rs | 3 + src/parser/src/second_pass/entities.rs | 2 - src/parser/src/second_pass/parser.rs | 80 +++++++++---------- src/parser/src/second_pass/parser_settings.rs | 22 ++--- src/parser/src/second_pass/variants.rs | 55 +++++++++++++ src/python/src/lib.rs | 34 ++++++++ 23 files changed, 238 insertions(+), 86 deletions(-) diff --git a/src/csgoproto/Cargo.lock b/src/csgoproto/Cargo.lock index 74d5f8b6..9e26d07a 100644 --- a/src/csgoproto/Cargo.lock +++ b/src/csgoproto/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "csgoproto" @@ -32,18 +32,18 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "protobuf" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df67496db1a89596beaced1579212e9b7c53c22dca1d9745de00ead76573d514" +checksum = "0bcc343da15609eaecd65f8aa76df8dc4209d325131d8219358c0aaaebab0bf6" dependencies = [ "bytes", "once_cell", @@ -53,27 +53,27 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e2d30ab1878b2e72d1e2fc23ff5517799c9929e2cf81a8516f9f4dcf2b9cf3" +checksum = "f0766e3675a627c327e4b3964582594b0e8741305d628a98a5de75a1d15f99b9" dependencies = [ "thiserror", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "syn" -version = "2.0.53" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -82,18 +82,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -102,6 +102,6 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/src/csgoproto/Cargo.toml b/src/csgoproto/Cargo.toml index 7609d69c..f4099066 100644 --- a/src/csgoproto/Cargo.toml +++ b/src/csgoproto/Cargo.toml @@ -7,7 +7,7 @@ authors = ["Laiho"] edition = "2021" [dependencies] bytes = "1.5.0" -glob = "0.3.1" +glob = "0.3.0" proc-macro2 = "1.0.78" -protobuf = { version = "=3.5.0", features = ["bytes"] } -# ENABLE ME TO BUILD protobuf-codegen = "=3.5.0" +protobuf = { version = "3.5.1", features = ["bytes"] } +# protobuf-codegen = "3.5.0" diff --git a/src/csgoproto/src/cs_usercmd.rs b/src/csgoproto/src/cs_usercmd.rs index 1c6a305e..992e265c 100644 --- a/src/csgoproto/src/cs_usercmd.rs +++ b/src/csgoproto/src/cs_usercmd.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CSGOInterpolationInfoPB) #[derive(PartialEq,Clone,Default,Debug)] @@ -499,6 +499,11 @@ impl ::protobuf::Message for CSGOInputHistoryEntryPB { fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { while let Some(tag) = is.read_raw_tag_or_eof()? { + + if tag != 18 && tag != 32 && tag != 45 && tag != 48 && tag != 61 && tag != 106 && tag != 114 && tag != 98 && tag != 122{ + println!("TAG: {:?}", tag); + } + match tag { 18 => { ::protobuf::rt::read_singular_message_into_field(is, &mut self.view_angles)?; @@ -900,7 +905,9 @@ impl ::protobuf::Message for CSGOUserCmdPB { ::protobuf::rt::read_singular_message_into_field(is, &mut self.base)?; }, 18 => { - self.input_history.push(is.read_message()?); + let m = is.read_message()?; + // println!("{:?}", m); + self.input_history.push(m); }, 48 => { self.attack1_start_history_index = ::std::option::Option::Some(is.read_int32()?); diff --git a/src/csgoproto/src/cstrike15_gcmessages.rs b/src/csgoproto/src/cstrike15_gcmessages.rs index 6fbbab01..1174baf2 100644 --- a/src/csgoproto/src/cstrike15_gcmessages.rs +++ b/src/csgoproto/src/cstrike15_gcmessages.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:GameServerPing) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/cstrike15_usermessages.rs b/src/csgoproto/src/cstrike15_usermessages.rs index 58e27b3a..dd6ec737 100644 --- a/src/csgoproto/src/cstrike15_usermessages.rs +++ b/src/csgoproto/src/cstrike15_usermessages.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CCSUsrMsg_VGUIMenu) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/demo.rs b/src/csgoproto/src/demo.rs index fab110aa..d0bef182 100644 --- a/src/csgoproto/src/demo.rs +++ b/src/csgoproto/src/demo.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CDemoFileHeader) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/engine_gcmessages.rs b/src/csgoproto/src/engine_gcmessages.rs index 1aec4483..fe08a5c0 100644 --- a/src/csgoproto/src/engine_gcmessages.rs +++ b/src/csgoproto/src/engine_gcmessages.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CEngineGotvSyncPacket) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/gcsdk_gcmessages.rs b/src/csgoproto/src/gcsdk_gcmessages.rs index d98414d0..4afad746 100644 --- a/src/csgoproto/src/gcsdk_gcmessages.rs +++ b/src/csgoproto/src/gcsdk_gcmessages.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CMsgSOIDOwner) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/netmessages.rs b/src/csgoproto/src/netmessages.rs index cb58d0c8..a01cd83f 100644 --- a/src/csgoproto/src/netmessages.rs +++ b/src/csgoproto/src/netmessages.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CCLCMsg_ClientInfo) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/network_connection.rs b/src/csgoproto/src/network_connection.rs index 580b8655..8bf8f289 100644 --- a/src/csgoproto/src/network_connection.rs +++ b/src/csgoproto/src/network_connection.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; #[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)] // @@protoc_insertion_point(enum:ENetworkDisconnectionReason) diff --git a/src/csgoproto/src/networkbasetypes.rs b/src/csgoproto/src/networkbasetypes.rs index c756e91e..ce8abd01 100644 --- a/src/csgoproto/src/networkbasetypes.rs +++ b/src/csgoproto/src/networkbasetypes.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CMsgVector) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/steammessages.rs b/src/csgoproto/src/steammessages.rs index ffbeb091..01886ea8 100644 --- a/src/csgoproto/src/steammessages.rs +++ b/src/csgoproto/src/steammessages.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CMsgProtoBufHeader) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/usercmd.rs b/src/csgoproto/src/usercmd.rs index 71db2651..d794c7d8 100644 --- a/src/csgoproto/src/usercmd.rs +++ b/src/csgoproto/src/usercmd.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CInButtonStatePB) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/csgoproto/src/usermessages.rs b/src/csgoproto/src/usermessages.rs index 0caf7822..0c83a556 100644 --- a/src/csgoproto/src/usermessages.rs +++ b/src/csgoproto/src/usermessages.rs @@ -23,7 +23,7 @@ /// Generated files are compatible only with the same version /// of protobuf runtime. -const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_0; +const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_5_1; // @@protoc_insertion_point(message:CUserMessageAchievementEvent) #[derive(PartialEq,Clone,Default,Debug)] diff --git a/src/parser/rustfmt.toml b/src/parser/rustfmt.toml index 979a3146..1cbd3296 100644 --- a/src/parser/rustfmt.toml +++ b/src/parser/rustfmt.toml @@ -1 +1 @@ -max_width = 130 \ No newline at end of file +max_width = 160 \ No newline at end of file diff --git a/src/parser/src/first_pass/prop_controller.rs b/src/parser/src/first_pass/prop_controller.rs index 60db9ce4..e80f0793 100644 --- a/src/parser/src/first_pass/prop_controller.rs +++ b/src/parser/src/first_pass/prop_controller.rs @@ -71,6 +71,15 @@ pub const USERCMD_ATTACK_START_HISTORY_INDEX_1: u32 = 100000040; pub const USERCMD_ATTACK_START_HISTORY_INDEX_2: u32 = 100000041; pub const USERCMD_ATTACK_START_HISTORY_INDEX_3: u32 = 100000042; +pub const USERCMD_INPUT_HISTORY_BASEID: u32 = 100001000; +pub const INPUT_HISTORY_X_OFFSET: u32 = 0; +pub const INPUT_HISTORY_Y_OFFSET: u32 = 1; +pub const INPUT_HISTORY_Z_OFFSET: u32 = 2; +pub const INPUT_HISTORY_RENDER_TICK_COUNT_OFFSET: u32 = 3; +pub const INPUT_HISTORY_RENDER_TICK_FRACTION_OFFSET: u32 = 4; +pub const INPUT_HISTORY_PLAYER_TICK_COUNT_OFFSET: u32 = 5; +pub const INPUT_HISTORY_PLAYER_TICK_FRACTION_OFFSET: u32 = 6; + #[derive(Clone, Debug)] pub struct PropController { pub id: u32, @@ -164,6 +173,15 @@ impl PropController { is_player_prop: true, }); } + if self.wanted_player_props.contains(&("usercmd_input_history".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_INPUT_HISTORY_BASEID, + prop_type: PropType::Custom, + prop_name: "usercmd_input_history".to_string(), + prop_friendly_name: "usercmd_input_history".to_string(), + is_player_prop: true, + }); + } if self.wanted_player_props.contains(&("inventory_as_ids".to_string())) { self.prop_infos.push(PropInfo { id: INVENTORY_AS_IDS_ID, diff --git a/src/parser/src/maps.rs b/src/parser/src/maps.rs index baee56ba..722eeb93 100644 --- a/src/parser/src/maps.rs +++ b/src/parser/src/maps.rs @@ -1947,6 +1947,22 @@ pub static TYPEHM: phf::Map<&'static str, PropType> = phf_map! { "SCOREBOARD" =>PropType::Button, "WALK" => PropType::Button, + "usercmd_viewangle_x" => PropType::Player, + "usercmd_viewangle_y" => PropType::Player, + "usercmd_viewangle_z" => PropType::Player, + "usercmd_forward_move" => PropType::Player, + "usercmd_left_move" => PropType::Player, + "usercmd_impulse" => PropType::Player, + "usercmd_mouse_dx" => PropType::Player, + "usercmd_mouse_dy" => PropType::Player, + "usercmd_buttonstate_1" => PropType::Player, + "usercmd_buttonstate_2" => PropType::Player, + "usercmd_buttonstate_3" => PropType::Player, + "usercmd_weapon_select" => PropType::Player, + "usercmd_left_hand_desired" => PropType::Player, + "usercmd_input_history" => PropType::Custom, + + "CCSPlayerPawn.CCSPlayer_MovementServices.m_nButtonDownMaskPrev" => PropType::Player, // TEAM "CCSTeam.m_iTeamNum" => PropType::Team, @@ -2529,6 +2545,23 @@ pub static TYPEHM: phf::Map<&'static str, PropType> = phf_map! { }; pub static FRIENDLY_NAMES_MAPPING: phf::Map<&'static str, &'static str> = phf_map! { + "usercmd_viewangle_x" => "usercmd_viewangle_x", + "usercmd_viewangle_y" => "usercmd_viewangle_y", + "usercmd_viewangle_z" => "usercmd_viewangle_z", + "usercmd_forward_move" => "usercmd_forward_move", + "usercmd_left_move" => "usercmd_left_move", + "usercmd_impulse" => "usercmd_impulse", + "usercmd_mouse_dx" => "usercmd_mouse_dx", + "usercmd_mouse_dy" => "usercmd_mouse_dy", + "usercmd_buttonstate_1" => "usercmd_buttonstate_1", + "usercmd_buttonstate_2" => "usercmd_buttonstate_2", + "usercmd_buttonstate_3" => "usercmd_buttonstate_3", + "usercmd_weapon_select" => "usercmd_weapon_select", + "usercmd_left_hand_desired" => "usercmd_left_hand_desired", + "usercmd_input_history" => "usercmd_input_history", + + + "active_weapon_skin" => "weapon_skin", "weapon_skin_id" => "weapon_skin_id", "FORWARD" => "FORWARD", diff --git a/src/parser/src/second_pass/collect_data.rs b/src/parser/src/second_pass/collect_data.rs index d9e33e5b..d0d7ee04 100644 --- a/src/parser/src/second_pass/collect_data.rs +++ b/src/parser/src/second_pass/collect_data.rs @@ -71,6 +71,7 @@ impl<'a> SecondPassParser<'a> { // if either one is missing then push None to output for (entity_id, player) in &self.players { for prop_info in &self.prop_controller.prop_infos { + // println!("{:?}", prop_info); let player_steamid = match player.steamid { Some(steamid) => steamid, None => 0, @@ -139,6 +140,7 @@ impl<'a> SecondPassParser<'a> { PropType::Controller => return self.get_controller_prop(&prop_info.id, player), PropType::Rules => return self.get_rules_prop(prop_info), PropType::GameTime => return Ok(Variant::F32(self.net_tick as f32 / 64.0)), + // PropType::InputHistory => return self.get_input_history_prop(&prop_info.id, entity_id), } } pub fn get_prop_from_ent(&self, prop_id: &u32, entity_id: &i32) -> Result { @@ -450,6 +452,7 @@ impl<'a> SecondPassParser<'a> { "is_airborne" => self.find_is_airborne(player), "agent_skin" => return self.find_agent_skin(player), "CCSPlayerController.m_iCompTeammateColor" => return self.find_player_color(player, prop_info), + "usercmd_input_history" => self.get_prop_from_ent(&USERCMD_INPUT_HISTORY_BASEID, entity_id), _ => Err(PropCollectionError::UnknownCustomPropName), } } diff --git a/src/parser/src/second_pass/entities.rs b/src/parser/src/second_pass/entities.rs index 098fd9f5..cb10dc83 100644 --- a/src/parser/src/second_pass/entities.rs +++ b/src/parser/src/second_pass/entities.rs @@ -60,8 +60,6 @@ impl<'a> SecondPassParser<'a> { Err(_) => return Err(DemoParserError::MalformedMessage), Ok(msg) => msg, }; - // println!("{:?}", msg); - return Ok(()); let mut bitreader = Bitreader::new(msg.entity_data()); let mut entity_id: i32 = -1; diff --git a/src/parser/src/second_pass/parser.rs b/src/parser/src/second_pass/parser.rs index 8968c30a..22052d5e 100644 --- a/src/parser/src/second_pass/parser.rs +++ b/src/parser/src/second_pass/parser.rs @@ -28,11 +28,12 @@ use csgoproto::netmessages::*; use csgoproto::networkbasetypes::CNETMsg_Tick; use protobuf::text_format::print_to_string_pretty; use protobuf::Message; -use protobuf_json_mapping::print_to_string; use snap::raw::decompress_len; use snap::raw::Decoder as SnapDecoder; use EDemoCommands::*; +use super::variants::InputHistory; + const OUTER_BUF_DEFAULT_LEN: usize = 400_000; const INNER_BUF_DEFAULT_LEN: usize = 8192 * 15; @@ -92,12 +93,7 @@ impl<'a> SecondPassParser<'a> { } Ok(()) } - fn parse_full_packet_and_break_if_needed( - &mut self, - bytes: &[u8], - buf: &mut Vec, - started_at: usize, - ) -> Result { + fn parse_full_packet_and_break_if_needed(&mut self, bytes: &[u8], buf: &mut Vec, started_at: usize) -> Result { if let Some(start_end_offset) = self.start_end_offset { if self.ptr > start_end_offset.end { return Ok(true); @@ -146,12 +142,7 @@ impl<'a> SecondPassParser<'a> { } 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> { + 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))?; @@ -226,7 +217,7 @@ impl<'a> SecondPassParser<'a> { svc_ClearAllStringTables => self.clear_stringtables(), svc_VoiceData => self.parse_voice_data(msg_bytes), GE_Source1LegacyGameEvent => self.parse_game_event(msg_bytes, &mut wrong_order_events), - // svc_UserCmds => self.parse_user_cmd(msg_bytes), + svc_UserCmds => self.parse_user_cmd(msg_bytes), _ => Ok(()), }; ok? @@ -239,33 +230,47 @@ impl<'a> SecondPassParser<'a> { pub fn parse_user_cmd(&mut self, bytes: &[u8]) -> Result<(), DemoParserError> { // We simply inject the values into the entities as if they came from packet_ents like any other val. - let m: CSVCMsg_UserCommands = Message::parse_from_bytes(bytes).unwrap(); - for cmd in m.commands { - let user_cmd = CSGOUserCmdPB::parse_from_bytes(cmd.data()).unwrap(); - let s = print_to_string(&user_cmd); - //fs::write("omg.json", s); + // This method is quite expensive so early exit it if not needed. + if !self.parse_usercmd { + return Ok(()); + } + let msg: CSVCMsg_UserCommands = match CSVCMsg_UserCommands::parse_from_bytes(bytes) { + Ok(m) => m, + _ => return Ok(()), + }; + for cmd in msg.commands { + let user_cmd: CSGOUserCmdPB = match CSGOUserCmdPB::parse_from_bytes(cmd.data()) { + Ok(m) => m, + _ => return Ok(()), + }; let entity_id = user_cmd.base.pawn_entity_handle() & 0x7FF; - if let Some(Some(ent)) = self.entities.get_mut(entity_id as usize) { - ent.props - .insert(USERCMD_VIEWANGLE_X, Variant::F32(user_cmd.base.viewangles.x())); - ent.props - .insert(USERCMD_VIEWANGLE_Y, Variant::F32(user_cmd.base.viewangles.y())); - ent.props - .insert(USERCMD_VIEWANGLE_Z, Variant::F32(user_cmd.base.viewangles.z())); - ent.props - .insert(USERCMD_FORWARDMOVE, Variant::F32(user_cmd.base.forwardmove())); + let mut history = vec![]; + for input in user_cmd.input_history { + let ih = InputHistory { + player_tick_count: input.player_tick_count(), + player_tick_fraction: input.player_tick_fraction(), + render_tick_count: input.render_tick_count(), + render_tick_fraction: input.render_tick_fraction(), + x: input.view_angles.x(), + y: input.view_angles.y(), + z: input.view_angles.z(), + }; + history.push(ih); + } + ent.props.insert(USERCMD_INPUT_HISTORY_BASEID, Variant::InputHistory(history)); ent.props.insert(USERCMD_LEFTMOVE, Variant::F32(user_cmd.base.leftmove())); ent.props.insert(USERCMD_IMPULSE, Variant::I32(user_cmd.base.impulse())); ent.props.insert(USERCMD_MOUSE_DX, Variant::I32(user_cmd.base.mousedx())); ent.props.insert(USERCMD_MOUSE_DY, Variant::I32(user_cmd.base.mousedy())); - ent.props - .insert(USERCMD_BUTTONSTATE_1, Variant::U64(user_cmd.base.buttons_pb.buttonstate1())); - ent.props - .insert(USERCMD_BUTTONSTATE_2, Variant::U64(user_cmd.base.buttons_pb.buttonstate2())); - ent.props - .insert(USERCMD_BUTTONSTATE_3, Variant::U64(user_cmd.base.buttons_pb.buttonstate3())); + ent.props.insert(USERCMD_VIEWANGLE_X, Variant::F32(user_cmd.base.viewangles.x())); + ent.props.insert(USERCMD_VIEWANGLE_Y, Variant::F32(user_cmd.base.viewangles.y())); + ent.props.insert(USERCMD_VIEWANGLE_Z, Variant::F32(user_cmd.base.viewangles.z())); + ent.props.insert(USERCMD_FORWARDMOVE, Variant::F32(user_cmd.base.forwardmove())); + ent.props.insert(USERCMD_BUTTONSTATE_1, Variant::U64(user_cmd.base.buttons_pb.buttonstate1())); + ent.props.insert(USERCMD_BUTTONSTATE_2, Variant::U64(user_cmd.base.buttons_pb.buttonstate2())); + ent.props.insert(USERCMD_BUTTONSTATE_3, Variant::U64(user_cmd.base.buttons_pb.buttonstate3())); } } Ok(()) @@ -297,12 +302,7 @@ impl<'a> SecondPassParser<'a> { Ok(()) } - pub fn parse_full_packet( - &mut self, - bytes: &[u8], - should_parse_entities: bool, - buf: &mut Vec, - ) -> Result<(), DemoParserError> { + 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), diff --git a/src/parser/src/second_pass/parser_settings.rs b/src/parser/src/second_pass/parser_settings.rs index 8554853c..90154116 100644 --- a/src/parser/src/second_pass/parser_settings.rs +++ b/src/parser/src/second_pass/parser_settings.rs @@ -73,6 +73,7 @@ pub struct SecondPassParser<'a> { pub df_per_player: AHashMap>, pub order_by_steamid: bool, pub last_tick: i32, + pub parse_usercmd: bool, } #[derive(Debug, Clone)] pub struct Teams { @@ -151,14 +152,16 @@ impl<'a> SecondPassParser<'a> { parse_all_packets: bool, start_end_offset: Option, ) -> Result { - first_pass_output.settings.wanted_player_props.clone().extend(vec![ - "tick".to_owned(), - "steamid".to_owned(), - "name".to_owned(), - ]); + first_pass_output + .settings + .wanted_player_props + .clone() + .extend(vec!["tick".to_owned(), "steamid".to_owned(), "name".to_owned()]); let args: Vec = env::args().collect(); let debug = if args.len() > 2 { args[2] == "true" } else { false }; + Ok(SecondPassParser { + parse_usercmd: contains_usercmd_prop(&first_pass_output.settings.wanted_player_props), last_tick: 0, start_end_offset: start_end_offset, order_by_steamid: first_pass_output.order_by_steamid, @@ -171,10 +174,7 @@ impl<'a> SecondPassParser<'a> { }; 8192 ], - parse_inventory: first_pass_output - .prop_controller - .wanted_player_props - .contains(&"inventory".to_string()), + parse_inventory: first_pass_output.prop_controller.wanted_player_props.contains(&"inventory".to_string()), net_tick: 0, c4_entity_id: None, stringtable_players: first_pass_output.stringtable_players, @@ -334,3 +334,7 @@ pub fn create_huffman_lookup_table() -> Vec<(u8, u8)> { } return huf2; } + +fn contains_usercmd_prop(names: &[String]) -> bool { + names.iter().any(|name| name.contains("usercmd")) +} diff --git a/src/parser/src/second_pass/variants.rs b/src/parser/src/second_pass/variants.rs index 9e3945a3..321967a4 100644 --- a/src/parser/src/second_pass/variants.rs +++ b/src/parser/src/second_pass/variants.rs @@ -24,6 +24,7 @@ pub enum Variant { U32Vec(Vec), U64Vec(Vec), Stickers(Vec), + InputHistory(Vec), } #[derive(Debug, Clone, PartialEq, Serialize)] pub struct Sticker { @@ -33,6 +34,16 @@ pub struct Sticker { pub x: f32, pub y: f32, } +#[derive(Debug, Clone, PartialEq, Serialize)] +pub struct InputHistory { + pub x: f32, + pub y: f32, + pub z: f32, + pub render_tick_count: i32, + pub render_tick_fraction: f32, + pub player_tick_count: i32, + pub player_tick_fraction: f32, +} #[derive(Debug, Clone, PartialEq)] pub enum VarVec { @@ -48,6 +59,7 @@ pub enum VarVec { XYVec(Vec>), XYZVec(Vec>), Stickers(Vec>), + InputHistory(Vec>), } impl VarVec { @@ -67,6 +79,7 @@ impl VarVec { Variant::Stickers(_) => VarVec::Stickers(vec![]), Variant::I16(_) => VarVec::I32(vec![]), Variant::U8(_) => VarVec::I32(vec![]), + Variant::InputHistory(_) => VarVec::InputHistory(vec![]), } } } @@ -98,6 +111,7 @@ impl PropColumn { Some(VarVec::XYVec(b)) => VarVec::XYVec(indicies.iter().map(|x| b[*x]).collect_vec()), Some(VarVec::XYZVec(b)) => VarVec::XYZVec(indicies.iter().map(|x| b[*x]).collect_vec()), Some(VarVec::Stickers(b)) => VarVec::Stickers(indicies.iter().map(|x| b[*x].to_owned()).collect_vec()), + Some(VarVec::InputHistory(b)) => VarVec::InputHistory(indicies.iter().map(|x| b[*x].to_owned()).collect_vec()), None => { return Some(PropColumn { data: None, @@ -124,6 +138,7 @@ impl PropColumn { Some(VarVec::XYVec(b)) => b.len(), Some(VarVec::XYZVec(b)) => b.len(), Some(VarVec::Stickers(b)) => b.len(), + Some(VarVec::InputHistory(b)) => b.len(), None => self.num_nones, } } @@ -250,6 +265,17 @@ impl PropColumn { } _ => {} }, + Some(VarVec::InputHistory(v)) => match &other.data { + Some(VarVec::InputHistory(v_other)) => { + v.extend_from_slice(&v_other); + } + None => { + for _ in 0..other.num_nones { + v.push(vec![]); + } + } + _ => {} + }, Some(VarVec::U32Vec(v)) => match &other.data { Some(VarVec::U32Vec(v_other)) => { v.extend_from_slice(&v_other); @@ -310,6 +336,10 @@ impl PropColumn { self.resolve_vec_type(PropColumn::get_type(&other.data)); self.extend_from(other); } + Some(VarVec::InputHistory(_inner)) => { + self.resolve_vec_type(PropColumn::get_type(&other.data)); + self.extend_from(other); + } None => { self.num_nones += other.num_nones; } @@ -331,6 +361,8 @@ impl PropColumn { Some(VarVec::XYZVec(_)) => Some(9), Some(VarVec::Stickers(_)) => Some(10), Some(VarVec::U32Vec(_)) => Some(11), + Some(VarVec::InputHistory(_)) => Some(12), + None => None, } } @@ -347,7 +379,11 @@ impl PropColumn { Some(5) => self.data = Some(VarVec::U64(vec![])), Some(6) => self.data = Some(VarVec::StringVec(vec![])), Some(7) => self.data = Some(VarVec::U64Vec(vec![])), + Some(8) => self.data = Some(VarVec::XYVec(vec![])), + Some(9) => self.data = Some(VarVec::XYZVec(vec![])), + Some(10) => self.data = Some(VarVec::Stickers(vec![])), Some(11) => self.data = Some(VarVec::U32Vec(vec![])), + Some(12) => self.data = Some(VarVec::InputHistory(vec![])), _ => {} } for _ in 0..self.num_nones { @@ -428,6 +464,10 @@ impl VarVec { VarVec::Stickers(f) => f.push(p), _ => {} }, + Some(Variant::InputHistory(p)) => match self { + VarVec::InputHistory(f) => f.push(p), + _ => {} + }, None => self.push_none(), _ => {} } @@ -446,6 +486,7 @@ impl VarVec { VarVec::XYZVec(f) => f.push(None), VarVec::U32Vec(f) => f.push(vec![]), VarVec::Stickers(f) => f.push(vec![]), + VarVec::InputHistory(f) => f.push(vec![]), } } } @@ -510,6 +551,13 @@ impl Serialize for Variant { } s.end() } + Variant::InputHistory(v) => { + let mut s = serializer.serialize_seq(Some(v.len()))?; + for item in v { + s.serialize_element(&item)?; + } + s.end() + } } } } @@ -674,6 +722,10 @@ pub fn soa_to_aos(soa: OutputSerdeHelperStruct) -> Vec hm.insert(prop_info.prop_friendly_name.clone(), Some(Variant::Stickers(f.clone()))), _ => hm.insert(prop_info.prop_friendly_name.clone(), None), }, + Some(VarVec::InputHistory(val)) => match val.get(idx) { + Some(f) => hm.insert(prop_info.prop_friendly_name.clone(), Some(Variant::InputHistory(f.clone()))), + _ => hm.insert(prop_info.prop_friendly_name.clone(), None), + }, }; } } @@ -733,6 +785,9 @@ impl Serialize for OutputSerdeHelperStruct { Some(VarVec::Stickers(val)) => { map.serialize_entry(&prop_info.prop_friendly_name, val)?; } + Some(VarVec::InputHistory(val)) => { + map.serialize_entry(&prop_info.prop_friendly_name, val)?; + } Some(VarVec::U64Vec(val)) => { let string_sid = val .iter() diff --git a/src/python/src/lib.rs b/src/python/src/lib.rs index a28d6de0..65ce245b 100644 --- a/src/python/src/lib.rs +++ b/src/python/src/lib.rs @@ -633,10 +633,44 @@ impl DemoParser { df_column_names_py.push(prop_info.prop_friendly_name); all_pyobjects.push(dicts.to_object(py)); } + + Some(VarVec::InputHistory(data)) => { + let mut dicts = vec![]; + for input in data { + let mut v = vec![]; + for sticker in input { + let dict = PyDict::new_bound(py); + dict.set_item("x", sticker.x.to_object(py))?; + dict.set_item("y", sticker.y.to_object(py))?; + dict.set_item("z", sticker.z.to_object(py))?; + dict.set_item( + "render_tick_count", + sticker.render_tick_count.to_object(py), + )?; + dict.set_item( + "render_tick_fraction", + sticker.render_tick_fraction.to_object(py), + )?; + dict.set_item( + "player_tick_count", + sticker.player_tick_count.to_object(py), + )?; + dict.set_item( + "player_tick_fraction", + sticker.player_tick_fraction.to_object(py), + )?; + v.push(dict); + } + dicts.push(v); + } + df_column_names_py.push(prop_info.prop_friendly_name); + all_pyobjects.push(dicts.to_object(py)); + } _ => {} } } } + println!("{:?} {:?}", df_column_names_arrow, df_column_names_py); Python::with_gil(|py| { let polars = py.import_bound("polars")?; let all_series_py = all_series.to_object(py); From 1b0f5c8d6e400aacc74a161616138bfe43a9eb11 Mon Sep 17 00:00:00 2001 From: Laiho Date: Fri, 20 Sep 2024 10:45:28 +0300 Subject: [PATCH 4/9] first working usercmd --- src/parser/src/e2e_test.rs | 222 ++++++++------------- src/parser/src/maps.rs | 3 - src/parser/src/second_pass/collect_data.rs | 58 +----- src/parser/src/second_pass/entities.rs | 20 +- src/parser/src/second_pass/parser.rs | 9 +- 5 files changed, 94 insertions(+), 218 deletions(-) diff --git a/src/parser/src/e2e_test.rs b/src/parser/src/e2e_test.rs index f69d5075..04f54500 100644 --- a/src/parser/src/e2e_test.rs +++ b/src/parser/src/e2e_test.rs @@ -294,6 +294,20 @@ pub fn _create_ge_tests() { "user_id".to_string(), "agent_skin".to_string(), "is_airborne".to_string(), + "usercmd_viewangle_x".to_string(), + "usercmd_viewangle_y".to_string(), + "usercmd_viewangle_z".to_string(), + "usercmd_forward_move".to_string(), + "usercmd_left_move".to_string(), + "usercmd_impulse".to_string(), + "usercmd_mouse_dx".to_string(), + "usercmd_mouse_dy".to_string(), + "usercmd_buttonstate_1".to_string(), + "usercmd_buttonstate_2".to_string(), + "usercmd_buttonstate_3".to_string(), + "usercmd_weapon_select".to_string(), + "usercmd_left_hand_desired".to_string(), + "usercmd_input_history".to_string(), ]; let wanted_events = vec!["all".to_string()]; @@ -366,6 +380,20 @@ pub fn _create_ge_tests() { "round_mvp", "round_announce_match_start", "hegrenade_detonate", + "usercmd_viewangle_x", + "usercmd_viewangle_y", + "usercmd_viewangle_z", + "usercmd_forward_move", + "usercmd_left_move", + "usercmd_impulse", + "usercmd_mouse_dx", + "usercmd_mouse_dy", + "usercmd_buttonstate_1", + "usercmd_buttonstate_2", + "usercmd_buttonstate_3", + "usercmd_weapon_select", + "usercmd_left_hand_desired", + "usercmd_input_history", ]; for name in events { @@ -669,6 +697,20 @@ pub fn _create_tests() { "user_id".to_string(), "agent_skin".to_string(), "is_airborne".to_string(), + "usercmd_viewangle_x".to_string(), + "usercmd_viewangle_y".to_string(), + "usercmd_viewangle_z".to_string(), + "usercmd_forward_move".to_string(), + "usercmd_left_move".to_string(), + "usercmd_impulse".to_string(), + "usercmd_mouse_dx".to_string(), + "usercmd_mouse_dy".to_string(), + "usercmd_buttonstate_1".to_string(), + "usercmd_buttonstate_2".to_string(), + "usercmd_buttonstate_3".to_string(), + "usercmd_weapon_select".to_string(), + "usercmd_left_hand_desired".to_string(), + "usercmd_input_history".to_string(), ]; let huf = create_huffman_lookup_table(); @@ -720,6 +762,21 @@ pub fn _create_tests() { custom.insert(NAME_ID, "name"); custom.insert(WEAPON_STICKERS_ID, "weapon_stickers"); custom.insert(IS_AIRBORNE_ID, "is_airborne"); + custom.insert(USERCMD_INPUT_HISTORY_BASEID, "usercmd_input_history"); + // Currently test demo is too old to contain these :/ + custom.insert(USERCMD_VIEWANGLE_X, "usercmd_viewangle_x"); + custom.insert(USERCMD_VIEWANGLE_Y, "usercmd_viewangle_y"); + custom.insert(USERCMD_VIEWANGLE_Z, "usercmd_viewangle_z"); + custom.insert(USERCMD_FORWARDMOVE, "usercmd_forward_move"); + custom.insert(USERCMD_LEFTMOVE, "usercmd_left_move"); + custom.insert(USERCMD_IMPULSE, "usercmd_impulsee"); + custom.insert(USERCMD_MOUSE_DX, "usercmd_mouse_dx"); + custom.insert(USERCMD_MOUSE_DY, "usercmd_mouse_dy"); + custom.insert(USERCMD_BUTTONSTATE_1, "usercmd_buttonstate_1"); + custom.insert(USERCMD_BUTTONSTATE_2, "usercmd_buttonstate_2"); + custom.insert(USERCMD_BUTTONSTATE_3, "usercmd_buttonstate_3"); + custom.insert(USERCMD_WEAPON_SELECT, "usercmd_weapon_select"); + custom.insert(USERCMD_SUBTICK_LEFT_HAND_DESIRED, "usercmd_left_hand_desired"); for (k, v) in d.df { if let Some(real_name) = d.prop_controller.id_to_name.get(&k) { @@ -1137,12 +1194,7 @@ fn create_data() -> (DemoOutput, PropController, BTreeMap let mut hm = BTreeMap::default(); for name in events { - let mut v = out2 - .game_events - .iter() - .map(|x| x.clone()) - .filter(|x| x.name == name) - .collect_vec(); + let mut v = out2.game_events.iter().map(|x| x.clone()).filter(|x| x.name == name).collect_vec(); v.truncate(2); hm.insert(name.to_string(), v); } @@ -1206,10 +1258,7 @@ mod tests { let steamids = output.df.get(&STEAMID_ID).unwrap(); - assert_eq!( - steamids.data, - Some(VarVec::U64(vec![Some(76561198244754626), Some(76561198244754626)])) - ); + assert_eq!(steamids.data, Some(VarVec::U64(vec![Some(76561198244754626), Some(76561198244754626)]))); } #[test] fn CBodyComponentBaseAnimGraph_m_nNewSequenceParity() { @@ -1266,13 +1315,7 @@ mod tests { } #[test] fn CCSPlayerController_m_nDisconnectionTick() { - let prop = ( - "CCSPlayerController.m_nDisconnectionTick", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_nDisconnectionTick", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -1386,10 +1429,7 @@ mod tests { fn CCSPlayerController_CCSPlayerController_ActionTrackingServices_m_iMoneySaved() { let prop = ( "CCSPlayerController.CCSPlayerController_ActionTrackingServices.m_iMoneySaved", - PropColumn { - data: None, - num_nones: 40, - }, + PropColumn { data: None, num_nones: 40 }, ); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); @@ -1555,13 +1595,7 @@ mod tests { } #[test] fn CEconItemAttribute_m_iRawValue32() { - let prop = ( - "CEconItemAttribute.m_iRawValue32", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CEconItemAttribute.m_iRawValue32", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -1779,13 +1813,7 @@ mod tests { } #[test] fn CCSPlayerController_m_nQuestProgressReason() { - let prop = ( - "CCSPlayerController.m_nQuestProgressReason", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_nQuestProgressReason", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -2006,10 +2034,7 @@ mod tests { fn CCSPlayerController_CCSPlayerController_ActionTrackingServices_m_iCashEarned() { let prop = ( "CCSPlayerController.CCSPlayerController_ActionTrackingServices.m_iCashEarned", - PropColumn { - data: None, - num_nones: 40, - }, + PropColumn { data: None, num_nones: 40 }, ); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); @@ -2811,13 +2836,7 @@ mod tests { } #[test] fn velocity_Y() { - let prop = ( - "velocity_Y", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("velocity_Y", PropColumn { data: None, num_nones: 40 }); assert_eq!(out.0.df[&VELOCITY_Y_ID], prop.1); } #[test] @@ -3141,13 +3160,7 @@ mod tests { } #[test] fn velocity() { - let prop = ( - "velocity", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("velocity", PropColumn { data: None, num_nones: 40 }); assert_eq!(out.0.df[&VELOCITY_ID], prop.1); } #[test] @@ -4900,13 +4913,7 @@ mod tests { } #[test] fn velocity_X() { - let prop = ( - "velocity_X", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("velocity_X", PropColumn { data: None, num_nones: 40 }); assert_eq!(out.0.df[&VELOCITY_X_ID], prop.1); } #[test] @@ -5600,13 +5607,7 @@ mod tests { } #[test] fn CCSPlayerController_m_iPawnArmor() { - let prop = ( - "CCSPlayerController.m_iPawnArmor", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_iPawnArmor", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -6088,13 +6089,7 @@ mod tests { } #[test] fn CCSPlayerController_m_bPawnHasHelmet() { - let prop = ( - "CCSPlayerController.m_bPawnHasHelmet", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_bPawnHasHelmet", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -8060,13 +8055,7 @@ mod tests { } #[test] fn CCSPlayerController_m_nTickBase() { - let prop = ( - "CCSPlayerController.m_nTickBase", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_nTickBase", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -8286,10 +8275,7 @@ mod tests { fn CCSPlayerController_CCSPlayerController_ActionTrackingServices_m_iKillReward() { let prop = ( "CCSPlayerController.CCSPlayerController_ActionTrackingServices.m_iKillReward", - PropColumn { - data: None, - num_nones: 40, - }, + PropColumn { data: None, num_nones: 40 }, ); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); @@ -9727,23 +9713,14 @@ mod tests { fn CCSPlayerController_CCSPlayerController_ActionTrackingServices_m_iEquipmentValue() { let prop = ( "CCSPlayerController.CCSPlayerController_ActionTrackingServices.m_iEquipmentValue", - PropColumn { - data: None, - num_nones: 40, - }, + PropColumn { data: None, num_nones: 40 }, ); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } #[test] fn CCSPlayerController_m_bPawnHasDefuser() { - let prop = ( - "CCSPlayerController.m_bPawnHasDefuser", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_bPawnHasDefuser", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -10202,12 +10179,7 @@ mod tests { ], vec!["knife".to_string(), "USP-S".to_string()], vec!["knife_t".to_string()], - vec![ - "knife_t".to_string(), - "Glock-18".to_string(), - "AK-47".to_string(), - "Flashbang".to_string(), - ], + vec!["knife_t".to_string(), "Glock-18".to_string(), "AK-47".to_string(), "Flashbang".to_string()], vec!["knife_survival_bowie".to_string(), "USP-S".to_string(), "M4A4".to_string()], vec!["knife_t".to_string(), "Glock-18".to_string(), "AK-47".to_string()], vec!["knife_t".to_string(), "Glock-18".to_string(), "MAC-10".to_string()], @@ -10248,11 +10220,7 @@ mod tests { ], vec![], vec!["knife".to_string()], - vec![ - "knife_tactical".to_string(), - "Smoke Grenade".to_string(), - "Flashbang".to_string(), - ], + vec!["knife_tactical".to_string(), "Smoke Grenade".to_string(), "Flashbang".to_string()], vec![ "knife_t".to_string(), "Glock-18".to_string(), @@ -11629,13 +11597,7 @@ mod tests { } #[test] fn CCSPlayerController_m_unPlayerTvControlFlags() { - let prop = ( - "CCSPlayerController.m_unPlayerTvControlFlags", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_unPlayerTvControlFlags", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -12383,13 +12345,7 @@ mod tests { } #[test] fn CCSPlayerController_m_iPawnHealth() { - let prop = ( - "CCSPlayerController.m_iPawnHealth", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_iPawnHealth", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -13243,13 +13199,7 @@ mod tests { } #[test] fn CCSPlayerController_m_bCanControlObservedBot() { - let prop = ( - "CCSPlayerController.m_bCanControlObservedBot", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_bCanControlObservedBot", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } @@ -14260,13 +14210,7 @@ mod tests { } #[test] fn velocity_Z() { - let prop = ( - "velocity_Z", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("velocity_Z", PropColumn { data: None, num_nones: 40 }); assert_eq!(out.0.df[&VELOCITY_Z_ID], prop.1); } #[test] @@ -14905,13 +14849,7 @@ mod tests { } #[test] fn CCSPlayerController_m_unActiveQuestId() { - let prop = ( - "CCSPlayerController.m_unActiveQuestId", - PropColumn { - data: None, - num_nones: 40, - }, - ); + let prop = ("CCSPlayerController.m_unActiveQuestId", PropColumn { data: None, num_nones: 40 }); let prop_id = out.1.name_to_id[prop.0]; assert_eq!(out.0.df[&prop_id], prop.1); } diff --git a/src/parser/src/maps.rs b/src/parser/src/maps.rs index 722eeb93..5689b664 100644 --- a/src/parser/src/maps.rs +++ b/src/parser/src/maps.rs @@ -1962,7 +1962,6 @@ pub static TYPEHM: phf::Map<&'static str, PropType> = phf_map! { "usercmd_left_hand_desired" => PropType::Player, "usercmd_input_history" => PropType::Custom, - "CCSPlayerPawn.CCSPlayer_MovementServices.m_nButtonDownMaskPrev" => PropType::Player, // TEAM "CCSTeam.m_iTeamNum" => PropType::Team, @@ -2560,8 +2559,6 @@ pub static FRIENDLY_NAMES_MAPPING: phf::Map<&'static str, &'static str> = phf_ma "usercmd_left_hand_desired" => "usercmd_left_hand_desired", "usercmd_input_history" => "usercmd_input_history", - - "active_weapon_skin" => "weapon_skin", "weapon_skin_id" => "weapon_skin_id", "FORWARD" => "FORWARD", diff --git a/src/parser/src/second_pass/collect_data.rs b/src/parser/src/second_pass/collect_data.rs index d0d7ee04..eaa21f6c 100644 --- a/src/parser/src/second_pass/collect_data.rs +++ b/src/parser/src/second_pass/collect_data.rs @@ -88,33 +88,21 @@ impl<'a> SecondPassParser<'a> { match self.find_prop(prop_info, entity_id, player) { Ok(prop) => { let df_this_player = self.df_per_player.get_mut(&player.steamid.unwrap_or(0)).unwrap(); - df_this_player - .entry(prop_info.id) - .or_insert_with(|| PropColumn::new()) - .push(Some(prop.clone())); + df_this_player.entry(prop_info.id).or_insert_with(|| PropColumn::new()).push(Some(prop.clone())); } Err(_e) => { let df_this_player = self.df_per_player.get_mut(&player.steamid.unwrap_or(0)).unwrap(); - df_this_player - .entry(prop_info.id) - .or_insert_with(|| PropColumn::new()) - .push(None); + df_this_player.entry(prop_info.id).or_insert_with(|| PropColumn::new()).push(None); } } } else { match self.find_prop(prop_info, entity_id, player) { Ok(prop) => { - self.output - .entry(prop_info.id) - .or_insert_with(|| PropColumn::new()) - .push(Some(prop)); + self.output.entry(prop_info.id).or_insert_with(|| PropColumn::new()).push(Some(prop)); } Err(_e) => { // Ultimate debugger is to print this error - self.output - .entry(prop_info.id) - .or_insert_with(|| PropColumn::new()) - .push(None); + self.output.entry(prop_info.id).or_insert_with(|| PropColumn::new()).push(None); } } } @@ -122,12 +110,7 @@ impl<'a> SecondPassParser<'a> { } } - pub fn find_prop( - &self, - prop_info: &PropInfo, - entity_id: &i32, - player: &PlayerMetaData, - ) -> Result { + pub fn find_prop(&self, prop_info: &PropInfo, entity_id: &i32, player: &PlayerMetaData) -> Result { match prop_info.prop_type { PropType::Tick => return self.create_tick(), PropType::Name => return self.create_name(player), @@ -140,7 +123,6 @@ impl<'a> SecondPassParser<'a> { PropType::Controller => return self.get_controller_prop(&prop_info.id, player), PropType::Rules => return self.get_rules_prop(prop_info), PropType::GameTime => return Ok(Variant::F32(self.net_tick as f32 / 64.0)), - // PropType::InputHistory => return self.get_input_history_prop(&prop_info.id, entity_id), } } pub fn get_prop_from_ent(&self, prop_id: &u32, entity_id: &i32) -> Result { @@ -419,13 +401,7 @@ impl<'a> SecondPassParser<'a> { None => Err(PropCollectionError::SpecialidsEyeAnglesNotSet), } } - pub fn create_custom_prop( - &self, - prop_name: &str, - entity_id: &i32, - prop_info: &PropInfo, - player: &PlayerMetaData, - ) -> Result { + pub fn create_custom_prop(&self, prop_name: &str, entity_id: &i32, prop_info: &PropInfo, player: &PlayerMetaData) -> Result { match prop_name { "X" => self.collect_cell_coordinate_player(CoordinateAxis::X, entity_id), "Y" => self.collect_cell_coordinate_player(CoordinateAxis::Y, entity_id), @@ -524,21 +500,12 @@ impl<'a> SecondPassParser<'a> { } return Ok(Variant::Stickers(stickers)); } - fn find_sticker( - &self, - entity_id: &i32, - sticker_id_id: u32, - sticker_wear_id: u32, - sticker_x: u32, - sticker_y: u32, - ) -> Option { + fn find_sticker(&self, entity_id: &i32, sticker_id_id: u32, sticker_wear_id: u32, sticker_x: u32, sticker_y: u32) -> Option { let id = self.get_prop_from_ent(&sticker_id_id, entity_id); let wear = self.get_prop_from_ent(&sticker_wear_id, entity_id); let sticker_x = self.get_prop_from_ent(&sticker_x, entity_id); let sticker_y = self.get_prop_from_ent(&sticker_y, entity_id); - if let (Ok(Variant::F32(id)), Ok(Variant::F32(wear)), Ok(Variant::F32(sticker_x)), Ok(Variant::F32(sticker_y))) = - (id, wear, sticker_x, sticker_y) - { + if let (Ok(Variant::F32(id)), Ok(Variant::F32(wear)), Ok(Variant::F32(sticker_x)), Ok(Variant::F32(sticker_y))) = (id, wear, sticker_x, sticker_y) { return Some(Sticker { id: id.to_bits(), name: STICKER_ID_TO_NAME.get(&id.to_bits()).unwrap_or(&"unknown").to_string(), @@ -923,9 +890,7 @@ impl<'a> SecondPassParser<'a> { Ok(p) => Ok(p), Err(e) => match e { PropCollectionError::GetPropFromEntEntityNotFound => Err(PropCollectionError::WeaponEntityNotFound), - PropCollectionError::GetPropFromEntPropNotFound => { - Err(PropCollectionError::WeaponEntityWantedPropNotFound) - } + PropCollectionError::GetPropFromEntPropNotFound => Err(PropCollectionError::WeaponEntityWantedPropNotFound), _ => Err(e), }, } @@ -1054,10 +1019,7 @@ impl<'a> SecondPassParser<'a> { } } -fn coord_from_cell( - cell: Result, - offset: Result, -) -> Result { +fn coord_from_cell(cell: Result, offset: Result) -> Result { // Both cell and offset are needed for calculation match (offset, cell) { (Ok(Variant::F32(offset)), Ok(Variant::U32(cell))) => { diff --git a/src/parser/src/second_pass/entities.rs b/src/parser/src/second_pass/entities.rs index cb10dc83..a45f2cb5 100644 --- a/src/parser/src/second_pass/entities.rs +++ b/src/parser/src/second_pass/entities.rs @@ -1,5 +1,3 @@ -use std::fs; - use crate::first_pass::read_bits::Bitreader; use crate::first_pass::read_bits::DemoParserError; use crate::first_pass::sendtables::find_field; @@ -112,8 +110,7 @@ impl<'a> SecondPassParser<'a> { is_fullpacket: bool, ) -> Result<(), DemoParserError> { let n_updates = self.parse_paths(bitreader)?; - let n_updated_values = - self.decode_entity_update(bitreader, entity_id, n_updates, is_fullpacket, is_baseline, events_to_emit)?; + let n_updated_values = self.decode_entity_update(bitreader, entity_id, n_updates, is_fullpacket, is_baseline, events_to_emit)?; if n_updated_values > 0 { self.gather_extra_info(&entity_id, is_baseline)?; } @@ -239,13 +236,7 @@ impl<'a> SecondPassParser<'a> { let result = bitreader.decode(&decoder, self.qf_mapper)?; if !is_fullpacket && !is_baseline { - events_to_emit.extend(SecondPassParser::listen_for_events( - entity, - &result, - field, - field_info, - &self.prop_controller, - )); + events_to_emit.extend(SecondPassParser::listen_for_events(entity, &result, field, field_info, &self.prop_controller)); } if self.is_debug_mode { SecondPassParser::debug_inspect( @@ -308,12 +299,7 @@ impl<'a> SecondPassParser<'a> { } Ok(()) } - fn create_new_entity( - &mut self, - bitreader: &mut Bitreader, - entity_id: &i32, - _events_to_emit: &mut Vec, - ) -> Result<(), DemoParserError> { + fn create_new_entity(&mut self, bitreader: &mut Bitreader, entity_id: &i32, _events_to_emit: &mut Vec) -> Result<(), DemoParserError> { let cls_id: u32 = bitreader.read_nbits(8)?; // Both of these are not used. Don't think they are interesting for the parser let _serial = bitreader.read_nbits(NSERIALBITS)?; diff --git a/src/parser/src/second_pass/parser.rs b/src/parser/src/second_pass/parser.rs index 22052d5e..98aaa0b0 100644 --- a/src/parser/src/second_pass/parser.rs +++ b/src/parser/src/second_pass/parser.rs @@ -1,5 +1,3 @@ -use std::fs; - use crate::first_pass::parser::Frame; use crate::first_pass::parser::HEADER_ENDS_AT_BYTE; use crate::first_pass::parser_settings::FirstPassParser; @@ -14,7 +12,6 @@ use crate::maps::netmessage_type_from_int; use crate::maps::NetmessageType::*; use crate::second_pass::collect_data::ProjectileRecord; use crate::second_pass::entities::Entity; -use crate::second_pass::game_events::EventField; use crate::second_pass::game_events::GameEvent; use crate::second_pass::parser_settings::SecondPassParser; use crate::second_pass::parser_settings::*; @@ -26,7 +23,6 @@ use csgoproto::cs_usercmd::CSGOUserCmdPB; use csgoproto::demo::*; use csgoproto::netmessages::*; use csgoproto::networkbasetypes::CNETMsg_Tick; -use protobuf::text_format::print_to_string_pretty; use protobuf::Message; use snap::raw::decompress_len; use snap::raw::Decoder as SnapDecoder; @@ -77,10 +73,7 @@ impl<'a> SecondPassParser<'a> { DEM_SignonPacket => self.parse_packet(&bytes, &mut buf2), DEM_Packet => self.parse_packet(&bytes, &mut buf2), DEM_Stop => break, - DEM_UserCmd => { - println!("{}", self.tick); - Ok(()) - } + DEM_UserCmd => Ok(()), DEM_FullPacket => { if self.parse_full_packet_and_break_if_needed(&bytes, &mut buf2, started_at)? { break; From b4f029064b10e6579dbcc283c32803c5e2cefe2c Mon Sep 17 00:00:00 2001 From: Laiho Date: Fri, 20 Sep 2024 11:05:21 +0300 Subject: [PATCH 5/9] add consumed_server_angles --- src/parser/src/first_pass/prop_controller.rs | 47 ++++++++------------ src/parser/src/maps.rs | 3 ++ src/parser/src/second_pass/parser.rs | 5 +++ 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/parser/src/first_pass/prop_controller.rs b/src/parser/src/first_pass/prop_controller.rs index e80f0793..92261d24 100644 --- a/src/parser/src/first_pass/prop_controller.rs +++ b/src/parser/src/first_pass/prop_controller.rs @@ -152,10 +152,7 @@ impl PropController { someid += 1; } } - if self - .wanted_player_props - .contains(&("active_weapon_original_owner".to_string())) - { + if self.wanted_player_props.contains(&("active_weapon_original_owner".to_string())) { self.prop_infos.push(PropInfo { id: WEAPON_ORIGINGAL_OWNER_ID, prop_type: PropType::Custom, @@ -182,6 +179,7 @@ impl PropController { is_player_prop: true, }); } + if self.wanted_player_props.contains(&("inventory_as_ids".to_string())) { self.prop_infos.push(PropInfo { id: INVENTORY_AS_IDS_ID, @@ -426,10 +424,7 @@ impl PropController { is_player_prop: true, }); } - if self - .wanted_player_props - .contains(&("usercmd_consumed_server_angle_changes".to_string())) - { + if self.wanted_player_props.contains(&("usercmd_consumed_server_angle_changes".to_string())) { self.prop_infos.push(PropInfo { id: USERCMD_CONSUMED_SERVER_ANGLE_CHANGES, prop_type: PropType::Player, @@ -492,7 +487,15 @@ impl PropController { is_player_prop: true, }); } - + if self.wanted_player_props.contains(&("usercmd_consumed_server_angle_changes".to_string())) { + self.prop_infos.push(PropInfo { + id: USERCMD_CONSUMED_SERVER_ANGLE_CHANGES, + prop_type: PropType::Custom, + prop_name: "usercmd_consumed_server_angle_changes".to_string(), + prop_friendly_name: "usercmd_consumed_server_angle_changes".to_string(), + is_player_prop: true, + }); + } if self.wanted_player_props.contains(&("is_airborne".to_string())) { self.prop_infos.push(PropInfo { id: IS_AIRBORNE_ID, @@ -582,8 +585,7 @@ impl PropController { f.full_name = full_name.to_string(); // CAK47.m_iClip1 => ["CAK47", "m_iClip1"] let split_at_dot: Vec<&str> = full_name.split(".").collect(); - let is_weapon_prop = (split_at_dot[0].contains("Weapon") || split_at_dot[0].contains("AK")) - && !split_at_dot[0].contains("Player") + let is_weapon_prop = (split_at_dot[0].contains("Weapon") || split_at_dot[0].contains("AK")) && !split_at_dot[0].contains("Player") || split_at_dot[0].contains("Knife") || split_at_dot[0].contains("CDEagle") || split_at_dot[0].contains("C4") @@ -591,9 +593,8 @@ impl PropController { || split_at_dot[0].contains("Inc") || split_at_dot[0].contains("Infer"); - let is_projectile_prop = - (split_at_dot[0].contains("Projectile") || split_at_dot[0].contains("Grenade") || split_at_dot[0].contains("Flash")) - && !split_at_dot[0].contains("Player"); + let is_projectile_prop = (split_at_dot[0].contains("Projectile") || split_at_dot[0].contains("Grenade") || split_at_dot[0].contains("Flash")) + && !split_at_dot[0].contains("Player"); let is_grenade_or_weapon = is_weapon_prop || is_projectile_prop; // Strip first part of name from grenades and weapons. @@ -697,16 +698,8 @@ impl PropController { let full_name = ser_name.clone() + "." + &x.name; self.handle_prop(&full_name, x, path); } - Field::Serializer(ser) => self.traverse_fields( - &mut ser.serializer.fields, - ser_name.clone() + "." + &ser.serializer.name, - path.clone(), - ), - Field::Pointer(ser) => self.traverse_fields( - &mut ser.serializer.fields, - ser_name.clone() + "." + &ser.serializer.name, - path.clone(), - ), + Field::Serializer(ser) => self.traverse_fields(&mut ser.serializer.fields, ser_name.clone() + "." + &ser.serializer.name, path.clone()), + Field::Pointer(ser) => self.traverse_fields(&mut ser.serializer.fields, ser_name.clone() + "." + &ser.serializer.name, path.clone()), Field::Array(ser) => match &mut ser.field_enum.as_mut() { Field::Value(v) => { self.handle_prop(&(ser_name.clone() + "." + &v.name), v, path); @@ -728,11 +721,7 @@ impl PropController { _ => {} } } - self.traverse_fields( - &mut s.serializer.fields, - ser_name.clone() + "." + &s.serializer.name, - path_og.clone(), - ) + self.traverse_fields(&mut s.serializer.fields, ser_name.clone() + "." + &s.serializer.name, path_og.clone()) } Field::Value(x) => { self.handle_prop(&(ser_name.clone() + "." + &x.name), x, path.clone()); diff --git a/src/parser/src/maps.rs b/src/parser/src/maps.rs index 5689b664..807531ba 100644 --- a/src/parser/src/maps.rs +++ b/src/parser/src/maps.rs @@ -1960,8 +1960,10 @@ pub static TYPEHM: phf::Map<&'static str, PropType> = phf_map! { "usercmd_buttonstate_3" => PropType::Player, "usercmd_weapon_select" => PropType::Player, "usercmd_left_hand_desired" => PropType::Player, + "usercmd_consumed_server_angle_changes" => PropType::Player, "usercmd_input_history" => PropType::Custom, + "CCSPlayerPawn.CCSPlayer_MovementServices.m_nButtonDownMaskPrev" => PropType::Player, // TEAM "CCSTeam.m_iTeamNum" => PropType::Team, @@ -2557,6 +2559,7 @@ pub static FRIENDLY_NAMES_MAPPING: phf::Map<&'static str, &'static str> = phf_ma "usercmd_buttonstate_3" => "usercmd_buttonstate_3", "usercmd_weapon_select" => "usercmd_weapon_select", "usercmd_left_hand_desired" => "usercmd_left_hand_desired", + "usercmd_consumed_server_angle_changes" => "usercmd_consumed_server_angle_changes", "usercmd_input_history" => "usercmd_input_history", "active_weapon_skin" => "weapon_skin", diff --git a/src/parser/src/second_pass/parser.rs b/src/parser/src/second_pass/parser.rs index 98aaa0b0..effdc4d2 100644 --- a/src/parser/src/second_pass/parser.rs +++ b/src/parser/src/second_pass/parser.rs @@ -237,6 +237,7 @@ impl<'a> SecondPassParser<'a> { Ok(m) => m, _ => return Ok(()), }; + let entity_id = user_cmd.base.pawn_entity_handle() & 0x7FF; if let Some(Some(ent)) = self.entities.get_mut(entity_id as usize) { let mut history = vec![]; @@ -264,6 +265,10 @@ impl<'a> SecondPassParser<'a> { ent.props.insert(USERCMD_BUTTONSTATE_1, Variant::U64(user_cmd.base.buttons_pb.buttonstate1())); ent.props.insert(USERCMD_BUTTONSTATE_2, Variant::U64(user_cmd.base.buttons_pb.buttonstate2())); ent.props.insert(USERCMD_BUTTONSTATE_3, Variant::U64(user_cmd.base.buttons_pb.buttonstate3())); + ent.props.insert( + USERCMD_CONSUMED_SERVER_ANGLE_CHANGES, + Variant::U32(user_cmd.base.consumed_server_angle_changes()), + ); } } Ok(()) From 78994c6657ca65805ac9bf9be69582a83accc544 Mon Sep 17 00:00:00 2001 From: Laiho Date: Fri, 20 Sep 2024 11:08:23 +0300 Subject: [PATCH 6/9] rm duplicate custom --- src/parser/src/first_pass/prop_controller.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/parser/src/first_pass/prop_controller.rs b/src/parser/src/first_pass/prop_controller.rs index 92261d24..87db7853 100644 --- a/src/parser/src/first_pass/prop_controller.rs +++ b/src/parser/src/first_pass/prop_controller.rs @@ -487,15 +487,6 @@ impl PropController { is_player_prop: true, }); } - if self.wanted_player_props.contains(&("usercmd_consumed_server_angle_changes".to_string())) { - self.prop_infos.push(PropInfo { - id: USERCMD_CONSUMED_SERVER_ANGLE_CHANGES, - prop_type: PropType::Custom, - prop_name: "usercmd_consumed_server_angle_changes".to_string(), - prop_friendly_name: "usercmd_consumed_server_angle_changes".to_string(), - is_player_prop: true, - }); - } if self.wanted_player_props.contains(&("is_airborne".to_string())) { self.prop_infos.push(PropInfo { id: IS_AIRBORNE_ID, From 158d294fdc485b255dcaf13bfc69c678a604e138 Mon Sep 17 00:00:00 2001 From: Laiho Date: Fri, 20 Sep 2024 14:00:48 +0300 Subject: [PATCH 7/9] rm junk --- src/csgoproto/src/cs_usercmd.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/csgoproto/src/cs_usercmd.rs b/src/csgoproto/src/cs_usercmd.rs index 992e265c..3c3472bc 100644 --- a/src/csgoproto/src/cs_usercmd.rs +++ b/src/csgoproto/src/cs_usercmd.rs @@ -500,10 +500,6 @@ impl ::protobuf::Message for CSGOInputHistoryEntryPB { fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> { while let Some(tag) = is.read_raw_tag_or_eof()? { - if tag != 18 && tag != 32 && tag != 45 && tag != 48 && tag != 61 && tag != 106 && tag != 114 && tag != 98 && tag != 122{ - println!("TAG: {:?}", tag); - } - match tag { 18 => { ::protobuf::rt::read_singular_message_into_field(is, &mut self.view_angles)?; @@ -905,9 +901,7 @@ impl ::protobuf::Message for CSGOUserCmdPB { ::protobuf::rt::read_singular_message_into_field(is, &mut self.base)?; }, 18 => { - let m = is.read_message()?; - // println!("{:?}", m); - self.input_history.push(m); + self.input_history.push(is.read_message()?); }, 48 => { self.attack1_start_history_index = ::std::option::Option::Some(is.read_int32()?); From ced9688cea41b2ea0e41dd0f9810f692104dfa84 Mon Sep 17 00:00:00 2001 From: Laiho Date: Fri, 20 Sep 2024 14:03:59 +0300 Subject: [PATCH 8/9] rm junk --- src/python/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/python/src/lib.rs b/src/python/src/lib.rs index 2d22cb44..6b409ff9 100644 --- a/src/python/src/lib.rs +++ b/src/python/src/lib.rs @@ -735,7 +735,6 @@ impl DemoParser { } } } - println!("{:?} {:?}", df_column_names_arrow, df_column_names_py); Python::with_gil(|py| { let polars = py.import_bound("polars")?; let all_series_py = all_series.to_object(py); From a62c3daf94b2aec2a4a611907bc2b82ad8044ec2 Mon Sep 17 00:00:00 2001 From: Laiho Date: Fri, 20 Sep 2024 14:04:38 +0300 Subject: [PATCH 9/9] rm junk --- src/parser/src/second_pass/collect_data.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/parser/src/second_pass/collect_data.rs b/src/parser/src/second_pass/collect_data.rs index 3383c96d..32aef23a 100644 --- a/src/parser/src/second_pass/collect_data.rs +++ b/src/parser/src/second_pass/collect_data.rs @@ -84,7 +84,6 @@ impl<'a> SecondPassParser<'a> { } for prop_info in &self.prop_controller.prop_infos { - // println!("{:?}", prop_info); let player_steamid = match player.steamid { Some(steamid) => steamid, None => 0,