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..3c3472bc 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,7 @@ 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()? { + match tag { 18 => { ::protobuf::rt::read_singular_message_into_field(is, &mut self.view_angles)?; 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/e2e_test.rs b/src/parser/src/e2e_test.rs index d955082b..dcd34656 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()]; @@ -367,6 +381,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 { @@ -670,6 +698,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(); @@ -722,6 +764,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) { @@ -1141,12 +1198,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); } @@ -1315,10 +1367,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() { @@ -1375,13 +1424,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); } @@ -1495,10 +1538,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); @@ -1664,13 +1704,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); } @@ -1888,13 +1922,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); } @@ -2115,10 +2143,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); @@ -2920,13 +2945,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] @@ -3250,13 +3269,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] @@ -5009,13 +5022,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] @@ -5709,13 +5716,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); } @@ -6197,13 +6198,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); } @@ -8169,13 +8164,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); } @@ -8395,10 +8384,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); @@ -9836,23 +9822,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); } @@ -10311,12 +10288,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()], @@ -10357,11 +10329,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(), @@ -11738,13 +11706,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); } @@ -12492,13 +12454,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); } @@ -13352,13 +13308,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); } @@ -14369,13 +14319,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] @@ -15014,13 +14958,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/first_pass/prop_controller.rs b/src/parser/src/first_pass/prop_controller.rs index 96d0807f..9b5f55ef 100644 --- a/src/parser/src/first_pass/prop_controller.rs +++ b/src/parser/src/first_pass/prop_controller.rs @@ -50,6 +50,38 @@ 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; + +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, @@ -211,7 +243,6 @@ impl PropController { is_player_prop: false, }); } - self.prop_infos.push(PropInfo { id: TICK_ID, prop_type: PropType::Tick, @@ -307,8 +338,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") @@ -316,9 +346,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. @@ -422,16 +451,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); @@ -453,11 +474,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 fa2ea434..3f263891 100644 --- a/src/parser/src/maps.rs +++ b/src/parser/src/maps.rs @@ -1,26 +1,4 @@ -use crate::first_pass::prop_controller::AGENT_SKIN_ID; -use crate::first_pass::prop_controller::ENTITY_ID_ID; -use crate::first_pass::prop_controller::INVENTORY_AS_IDS_ID; -use crate::first_pass::prop_controller::INVENTORY_ID; -use crate::first_pass::prop_controller::IS_AIRBORNE_ID; -use crate::first_pass::prop_controller::IS_ALIVE_ID; -use crate::first_pass::prop_controller::PITCH_ID; -use crate::first_pass::prop_controller::PLAYER_X_ID; -use crate::first_pass::prop_controller::PLAYER_Y_ID; -use crate::first_pass::prop_controller::PLAYER_Z_ID; -use crate::first_pass::prop_controller::USERID_ID; -use crate::first_pass::prop_controller::VELOCITY_ID; -use crate::first_pass::prop_controller::VELOCITY_X_ID; -use crate::first_pass::prop_controller::VELOCITY_Y_ID; -use crate::first_pass::prop_controller::VELOCITY_Z_ID; -use crate::first_pass::prop_controller::WEAPON_FLOAT; -use crate::first_pass::prop_controller::WEAPON_NAME_ID; -use crate::first_pass::prop_controller::WEAPON_ORIGINGAL_OWNER_ID; -use crate::first_pass::prop_controller::WEAPON_PAINT_SEED; -use crate::first_pass::prop_controller::WEAPON_SKIN_ID; -use crate::first_pass::prop_controller::WEAPON_SKIN_NAME; -use crate::first_pass::prop_controller::WEAPON_STICKERS_ID; -use crate::first_pass::prop_controller::YAW_ID; +use crate::first_pass::prop_controller::*; use crate::first_pass::read_bits::DemoParserError; use crate::second_pass::collect_data::PropType; use crate::second_pass::decoder::Decoder; @@ -295,6 +273,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, @@ -612,6 +591,7 @@ pub enum NetmessageType { UM_DllStatusResponse, UM_RequestInventory, UM_InventoryResponse, + svc_UserCmds, } pub fn demo_cmd_type_from_int(value: i32) -> Result { match value { @@ -1978,6 +1958,22 @@ pub static CUSTOM_PLAYER_PROP_IDS: phf::Map<&'static str, u32> = phf_map! { "weapon_paint_seed" => WEAPON_PAINT_SEED, "weapon_float" => WEAPON_FLOAT, "weapon_stickers" => WEAPON_STICKERS_ID, + + "usercmd_viewangle_x" => USERCMD_VIEWANGLE_X, + "usercmd_viewangle_y" => USERCMD_VIEWANGLE_Y, + "usercmd_viewangle_z" => USERCMD_VIEWANGLE_Z, + "usercmd_buttonstate_1" => USERCMD_BUTTONSTATE_1, + "usercmd_buttonstate_2" => USERCMD_BUTTONSTATE_2, + "usercmd_buttonstate_3" => USERCMD_BUTTONSTATE_3, + "usercmd_consumed_server_angle_changes" => USERCMD_CONSUMED_SERVER_ANGLE_CHANGES, + "usercmd_forward_move" => USERCMD_FORWARDMOVE, + "usercmd_left_move" => USERCMD_LEFTMOVE, + "usercmd_impulse" => USERCMD_IMPULSE, + "usercmd_mouse_dx" => USERCMD_MOUSE_DX, + "usercmd_mouse_dy" => USERCMD_MOUSE_DY, + "usercmd_left_hand_desired" => USERCMD_SUBTICK_LEFT_HAND_DESIRED, + "usercmd_weapon_select" => USERCMD_WEAPON_SELECT, + "usercmd_input_history" => USERCMD_INPUT_HISTORY_BASEID, }; pub static TYPEHM: phf::Map<&'static str, PropType> = phf_map! { @@ -1994,6 +1990,23 @@ 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_consumed_server_angle_changes" => PropType::Player, + "usercmd_input_history" => PropType::Custom, + + "CCSPlayerPawn.CCSPlayer_MovementServices.m_nButtonDownMaskPrev" => PropType::Player, // TEAM "CCSTeam.m_iTeamNum" => PropType::Team, @@ -2576,6 +2589,22 @@ 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_consumed_server_angle_changes" => "usercmd_consumed_server_angle_changes", + "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 539d9b97..32aef23a 100644 --- a/src/parser/src/second_pass/collect_data.rs +++ b/src/parser/src/second_pass/collect_data.rs @@ -100,33 +100,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); } } } @@ -134,12 +122,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), @@ -430,13 +413,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), @@ -463,6 +440,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), } } @@ -534,21 +512,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(), @@ -933,9 +902,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), }, } @@ -1064,10 +1031,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 ee0c2221..a45f2cb5 100644 --- a/src/parser/src/second_pass/entities.rs +++ b/src/parser/src/second_pass/entities.rs @@ -110,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)?; } @@ -237,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( @@ -277,7 +270,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); } } @@ -306,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 1e8172f5..effdc4d2 100644 --- a/src/parser/src/second_pass/parser.rs +++ b/src/parser/src/second_pass/parser.rs @@ -2,6 +2,7 @@ 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; @@ -15,8 +16,10 @@ 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; @@ -25,6 +28,8 @@ 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; @@ -68,6 +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 => Ok(()), DEM_FullPacket => { if self.parse_full_packet_and_break_if_needed(&bytes, &mut buf2, started_at)? { break; @@ -80,12 +86,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); @@ -134,12 +135,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))?; @@ -214,6 +210,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 +220,60 @@ impl<'a> SecondPassParser<'a> { } Ok(()) } + 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. + + // 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) { + 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_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())); + ent.props.insert( + USERCMD_CONSUMED_SERVER_ANGLE_CHANGES, + Variant::U32(user_cmd.base.consumed_server_angle_changes()), + ); + } + } + 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); @@ -249,12 +300,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 c7f390d0..84970d93 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 { @@ -158,14 +159,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, @@ -178,10 +181,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, @@ -341,3 +341,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 0d916cec..6b409ff9 100644 --- a/src/python/src/lib.rs +++ b/src/python/src/lib.rs @@ -698,6 +698,39 @@ 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)); + } _ => {} } }