Skip to content

Commit

Permalink
Merge branch 'main' of github.com:LaihoE/demoparser
Browse files Browse the repository at this point in the history
  • Loading branch information
LaihoE committed Sep 20, 2023
2 parents c1cf5af + 49ac21f commit 642eb9f
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 93 deletions.
48 changes: 48 additions & 0 deletions src/parser/src/collect_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::maps::PAINTKITS;
use crate::maps::WEAPINDICIES;
use crate::parser_thread_settings::ParserThread;
use crate::prop_controller::PropInfo;
use crate::prop_controller::MY_WEAPONS_OFFSET;
use crate::prop_controller::WEAPON_SKIN_ID;
use crate::variants::PropColumn;
use std::fmt;
Expand Down Expand Up @@ -87,6 +88,7 @@ pub enum PropCollectionError {
// DONT KNOW IF THESE ARE CORRECT. SEEMS TO GIVE CORRECT VALUES
const CELL_BITS: i32 = 9;
const MAX_COORD: f32 = (1 << 14) as f32;
const MAX_INVENTORY_IDX: u32 = 16;

impl std::error::Error for PropCollectionError {}
impl fmt::Display for PropCollectionError {
Expand Down Expand Up @@ -444,9 +446,55 @@ impl ParserThread {
"weapon_name" => self.find_weapon_name(entity_id),
"weapon_skin" => self.find_weapon_skin(entity_id),
"active_weapon_original_owner" => self.find_weapon_original_owner(entity_id),
"inventory" => self.find_my_inventory(entity_id),
_ => Err(PropCollectionError::UnknownCustomPropName),
}
}
pub fn find_my_inventory(&self, entity_id: &i32) -> Result<Variant, PropCollectionError> {
let mut names = vec![];
let mut unique_eids = vec![];

for i in 0..MAX_INVENTORY_IDX {
let prop_id = MY_WEAPONS_OFFSET + i;
match self.get_prop_from_ent(&(prop_id as u32), entity_id) {
Err(_e) => {}
Ok(Variant::U32(x)) => {
let eid = (x & ((1 << 14) - 1)) as i32;
// Sometimes multiple references to same eid?
if unique_eids.contains(&eid) {
continue;
}
unique_eids.push(eid);
let res = match self.get_prop_from_ent(&self.prop_controller.special_ids.item_def.unwrap(), &eid) {
Err(_e) => continue,
Ok(def) => def,
};
self.insert_equipment_name(&mut names, res, entity_id);
}
_ => {}
}
}
Ok(Variant::StringVec(names))
}
fn insert_equipment_name(&self, names: &mut Vec<String>, res: Variant, player_entid: &i32) {
if let Variant::U32(def_idx) = res {
match WEAPINDICIES.get(&def_idx) {
None => return,
Some(weap_name) => {
match weap_name {
// Check how many flashbangs player has (only prop that works like this)
&"flashbang" => {
if let Ok(Variant::U32(2)) = self.get_prop_from_ent(&987654, player_entid) {
names.push(weap_name.to_string());
}
}
_ => {}
}
names.push(weap_name.to_string());
}
};
}
}

pub fn find_weapon_original_owner(&self, entity_id: &i32) -> Result<Variant, PropCollectionError> {
let low_id = match self.prop_controller.special_ids.orig_own_low {
Expand Down
9 changes: 4 additions & 5 deletions src/parser/src/entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,10 @@ impl ParserThread {
if self.is_debug_mode {
for (field_info, debug) in self.field_infos[..n_updates].iter().zip(&self.debug_fields) {
let result = bitreader.decode(&field_info.decoder, &self.qf_mapper)?;
// self.game_events_counter.insert(debug.field.full_name.clone());
if debug.field.full_name.contains("Freeze") {
if debug.field.full_name.contains("CWeaponMAC10") {
println!(
"{:?} {:?} {:?} {:?} {:?}",
debug.path, debug.field.full_name, result, self.tick, self.net_tick
"{:?} {:?} {:?} {:?} {:?} {:?}",
debug.path, debug.field.full_name, result, self.tick, self.net_tick, field_info.prop_id
);
}
}
Expand Down Expand Up @@ -226,7 +225,7 @@ impl ParserThread {
}
// We reuse one big vector for holding paths. Purely for performance.
// Alternatively we could create a new vector in this function and return it.
self.field_infos[idx] = class.serializer.find_decoder(&fp, 0);
self.field_infos[idx] = class.serializer.find_decoder(&fp, 0, self.parse_inventory);
idx += 1;
}
Ok(idx)
Expand Down
7 changes: 2 additions & 5 deletions src/parser/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::sync::Arc;
use std::time::Instant;

fn main() {
let wanted_props = vec!["active_weapon_original_owner".to_string()];
let wanted_props = vec!["X".to_string()];
let before = Instant::now();
let dir = fs::read_dir("/home/laiho/Documents/demos/cs2/test3/").unwrap();
let mut c = 0;
Expand All @@ -21,7 +21,7 @@ fn main() {

let before = Instant::now();

if c > 100 {
if c > 1000 {
break;
}

Expand Down Expand Up @@ -58,9 +58,6 @@ fn main() {
let mut ds = Parser::new(settings);
let d = ds.parse_demo().unwrap();
println!("TOTAL {:?}", before.elapsed());
for x in d.game_events_counter {
println!("{:?}", x);
}
}
println!("TOTAL {:?}", before.elapsed());
}
2 changes: 2 additions & 0 deletions src/parser/src/maps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,7 @@ pub static TYPEHM: phf::Map<&'static str, PropType> = phf_map! {
"weapon_name" => PropType::Custom,
"active_weapon_original_owner" => PropType::Custom,
"game_time" => PropType::GameTime,
"inventory" => PropType::Custom,
// Weapon
"m_flAnimTime" => PropType::Weapon,
"m_flSimulationTime"=> PropType::Weapon,
Expand Down Expand Up @@ -1767,6 +1768,7 @@ pub static FRIENDLY_NAMES_MAPPING: phf::Map<&'static str, &'static str> = phf_ma
"yaw" => "yaw",

"game_time" => "game_time",
"inventory" => "inventory",

"rank" => "CCSPlayerController.m_iCompetitiveRanking",
"rank_if_win" => "CCSPlayerController.m_iCompetitiveRankingPredicted_Win",
Expand Down
3 changes: 2 additions & 1 deletion src/parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ impl Parser {
DEM_SignonPacket => self.parse_packet(&bytes),
DEM_Stop => break,
DEM_FullPacket => {
self.parse_full_packet(&bytes).unwrap();
self.fullpacket_offsets.push(frame_starts_at);
Ok(())
}
_ => Ok(()),
};
ok?;
}

let outputs: Vec<Result<DemoOutput, DemoParserError>> = self
.fullpacket_offsets
.par_iter()
Expand All @@ -126,6 +126,7 @@ impl Parser {
}
Ok(self.combine_thread_outputs(&mut ok))
}
// fn parse_stringtables_cmd(bytes: &[u8]) -> Result<(), DemoParserError> {}
pub fn create_parser_thread_input(&self, offset: usize, parse_all: bool) -> ParserThreadInput {
let cls_by_id = match &self.cls_by_id {
Some(cls_by_id) => cls_by_id.clone(),
Expand Down
2 changes: 2 additions & 0 deletions src/parser/src/parser_thread_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct ParserThread {
pub cls_by_id: Arc<AHashMap<u32, Class>>,
pub stringtable_players: AHashMap<u64, UserInfo>,
pub net_tick: u32,
pub parse_inventory: bool,

pub ptr: usize,
pub bytes: Arc<BytesVariant>,
Expand Down Expand Up @@ -165,6 +166,7 @@ impl ParserThread {
false => 0,
};
Ok(ParserThread {
parse_inventory: input.prop_controller.wanted_player_props.contains(&"inventory".to_string()),
net_tick: 0,
debug_fields: vec![
DebugFieldAndPath {
Expand Down
21 changes: 18 additions & 3 deletions src/parser/src/prop_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const NORMAL_PROP_BASEID: u32 = 1000;

pub const WEAPON_SKIN_ID: u32 = 420420420;
pub const WEAPON_ORIGINGAL_OWNER_ID: u32 = 6942000;
pub const MY_WEAPONS_OFFSET: u32 = 500000;
pub const GRENADE_AMMO_ID: u32 = 1111111;

#[derive(Clone, Debug)]
pub struct PropController {
Expand Down Expand Up @@ -96,6 +98,15 @@ impl PropController {
is_player_prop: true,
});
}
if self.wanted_player_props.contains(&("inventory".to_string())) {
self.prop_infos.push(PropInfo {
id: 555555575,
prop_type: PropType::Custom,
prop_name: "inventory".to_string(),
prop_friendly_name: "inventory".to_string(),
is_player_prop: true,
});
}
if self.wanted_player_props.contains(&("game_time".to_string())) {
self.prop_infos.push(PropInfo {
id: 123456879,
Expand Down Expand Up @@ -264,10 +275,12 @@ impl PropController {
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("CDEagle")
|| split_at_dot[0].contains("C4");

let is_projectile_prop = (split_at_dot[0].contains("Projectile") || split_at_dot[0].contains("Grenade"))
&& !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.
Expand Down Expand Up @@ -329,7 +342,9 @@ impl PropController {
|| name.contains("CCSPlayerController.m_hPlayerPawn")
|| name.contains("CCSPlayerController.m_bPawnIsAlive")
|| name.contains("m_hActiveWeapon")
|| name.contains("Weapons")
|| name.contains("OriginalOwnerXuid")
|| name.contains("Flash")
{
return true;
}
Expand Down
41 changes: 36 additions & 5 deletions src/parser/src/sendtables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ pub static BASETYPE_DECODERS: phf::Map<&'static str, Decoder> = phf_map! {
const WEAPON_SKIN_PATH: [i32; 7] = [87, 0, 1, 0, 0, 0, 0];

impl Field {
pub fn decoder_from_path(&self, path: &FieldPath, pos: usize) -> FieldInfo {
pub fn decoder_from_path(&self, path: &FieldPath, pos: usize, parse_inventory: bool) -> FieldInfo {
match self.model {
FieldModelSimple => {
// EHHH IDK WILL HAVE TO DO FOR NOW
Expand Down Expand Up @@ -218,7 +218,7 @@ impl Field {
} else {
match &self.serializer {
Some(ser) => {
return ser.find_decoder(path, pos);
return ser.find_decoder(path, pos, parse_inventory);
}
None => panic!("no serializer for path"),
}
Expand All @@ -245,7 +245,7 @@ impl Field {
if path.last >= pos + 1 {
match &self.serializer {
Some(ser) => {
return ser.find_decoder(path, pos + 1);
return ser.find_decoder(path, pos + 1, parse_inventory);
}
None => panic!("no serializer for path"),
}
Expand Down Expand Up @@ -498,16 +498,44 @@ pub struct Serializer {
pub name: String,
pub fields: Vec<Field>,
}
const FLASH_AMMO_PATH: [i32; 7] = [86, 2, 14, 0, 0, 0, 0];
use crate::prop_controller::GRENADE_AMMO_ID;
use crate::prop_controller::MY_WEAPONS_OFFSET;

impl Serializer {
pub fn find_decoder(&self, path: &FieldPath, pos: usize) -> FieldInfo {
self.fields[path.path[pos] as usize].decoder_from_path(path, pos + 1)
pub fn find_decoder(&self, path: &FieldPath, pos: usize, parse_inventory: bool) -> FieldInfo {
// Edge case for now...
if parse_inventory {
if let Some(info) = self.find_inventory_info(path) {
return info;
}
}
self.fields[path.path[pos] as usize].decoder_from_path(path, pos + 1, parse_inventory)
}
pub fn debug_find_decoder(&self, path: &FieldPath, pos: usize, prop_name: String) -> DebugField {
let idx = path.path[pos];
let f = &self.fields[idx as usize];
f.debug_decoder_from_path(path, pos + 1, prop_name)
}
fn find_inventory_info(&self, path: &FieldPath) -> Option<FieldInfo> {
if path.path == FLASH_AMMO_PATH && self.name == "CCSPlayerPawn" {
return Some(FieldInfo {
controller_prop: None,
decoder: UnsignedDecoder,
should_parse: true,
prop_id: GRENADE_AMMO_ID,
});
}
if path.path[0] == 86 && path.path[1] == 0 && self.name == "CCSPlayerPawn" && path.last == 2 {
return Some(FieldInfo {
controller_prop: None,
decoder: UnsignedDecoder,
should_parse: true,
prop_id: MY_WEAPONS_OFFSET + path.path[2] as u32,
});
}
None
}
}

const POINTER_TYPES: &'static [&'static str] = &[
Expand Down Expand Up @@ -621,6 +649,9 @@ impl Parser {
|| my_serializer.name.contains("Knife")
|| my_serializer.name.contains("CDEagle")
|| my_serializer.name.contains("Rules")
|| my_serializer.name.contains("C4")
|| my_serializer.name.contains("Grenade")
|| my_serializer.name.contains("Flash")
{
prop_controller.find_prop_name_paths(&mut my_serializer);
}
Expand Down
13 changes: 10 additions & 3 deletions src/parser/src/stringtables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ impl Parser {

pub fn parse_create_stringtable(&mut self, bytes: &[u8]) -> Result<(), DemoParserError> {
let table: CSVCMsg_CreateStringTable = Message::parse_from_bytes(&bytes).unwrap();

if !(table.name() == "instancebaseline" || table.name() == "userinfo") {
return Ok(());
}

let bytes = match table.data_compressed() {
true => snap::raw::Decoder::new().decompress_vec(table.string_data()).unwrap(),
false => table.string_data().to_vec(),
Expand Down Expand Up @@ -148,8 +150,9 @@ impl Parser {
};
}
if name == "userinfo" {
let player = parse_userinfo(&value)?;
self.stringtable_players.insert(player.steamid, player);
if let Ok(player) = parse_userinfo(&value) {
self.stringtable_players.insert(player.steamid, player);
}
}
if name == "instancebaseline" {
match key.parse::<u32>() {
Expand Down Expand Up @@ -301,8 +304,12 @@ impl ParserThread {
value
};
}
if name == "userinfo" {
if let Ok(player) = parse_userinfo(&value) {
self.stringtable_players.insert(player.steamid, player);
}
}
if name == "instancebaseline" {
// Watch out for keys like 42:15 <-- seem to be props that are not used atm
match key.parse::<u32>() {
Ok(cls_id) => self.baselines.insert(cls_id, value.clone()),
Err(_e) => None,
Expand Down
Loading

0 comments on commit 642eb9f

Please sign in to comment.