Skip to content

Commit 3f90d45

Browse files
authored
Add rank update custom event (#25)
* rank update custom event * add rank update test * 0.6.0 * ci
1 parent fddd9d4 commit 3f90d45

File tree

7 files changed

+85
-15
lines changed

7 files changed

+85
-15
lines changed

.github/workflows/ci.yml

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ name: Python application
33
on:
44
push:
55
branches: ["main"]
6-
pull_request:
7-
branches: ["main"]
86

97
permissions:
108
contents: read

src/parser/src/entities.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ impl ParserThread {
105105
for (field_info, debug) in self.field_infos[..n_updates].iter().zip(&self.debug_fields) {
106106
let result = bitreader.decode(&field_info.decoder, &self.qf_mapper)?;
107107
// self.game_events_counter.insert(debug.field.full_name.clone());
108-
if debug.field.full_name.contains("Time") {
109-
println!("{:?} {:?} {:?}", debug.path, debug.field.full_name, result);
108+
if debug.field.full_name.contains("Controller") && self.tick < 200 {
109+
println!("{:?} {:?} {:?} {:?}", debug.path, debug.field.full_name, result, self.tick);
110110
}
111111
}
112112
} else {

src/parser/src/game_events.rs

+74-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::stringtables::UserInfo;
77
use crate::variants::*;
88
use ahash::AHashMap;
99
use ahash::RandomState;
10+
use csgoproto::cstrike15_usermessages::CCSUsrMsg_ServerRankUpdate;
1011
use csgoproto::netmessages::csvcmsg_game_event_list::Descriptor_t;
1112
use csgoproto::netmessages::CSVCMsg_GameEventList;
1213
use csgoproto::networkbasetypes::csvcmsg_game_event::Key_t;
@@ -24,6 +25,8 @@ static INTERNALEVENTFIELDS: &'static [&str] = &[
2425
"assister_pawn",
2526
];
2627
const ENTITYIDNONE: i32 = 2047;
28+
// https://developer.valvesoftware.com/wiki/SteamID
29+
const STEAMID64INDIVIDUALIDENTIFIER: u64 = 0x0110000100000000;
2730

2831
impl Parser {
2932
// Message that should come before first game event
@@ -81,15 +84,15 @@ impl ParserThread {
8184
});
8285
Ok(())
8386
}
84-
fn find_user_by_userid(&self, userid: i32) -> Option<&UserInfo> {
87+
pub fn find_user_by_userid(&self, userid: i32) -> Option<&UserInfo> {
8588
for player in self.stringtable_players.values() {
8689
if player.userid == userid {
8790
return Some(player);
8891
}
8992
}
9093
return None;
9194
}
92-
fn entity_id_from_userid(&self, userid: i32) -> Option<i32> {
95+
pub fn entity_id_from_userid(&self, userid: i32) -> Option<i32> {
9396
if let Some(userinfo) = self.find_user_by_userid(userid) {
9497
for player in self.players.values() {
9598
if player.steamid == Some(userinfo.steamid) {
@@ -99,7 +102,7 @@ impl ParserThread {
99102
}
100103
return None;
101104
}
102-
fn find_extra(&self, fields: &Vec<EventField>) -> Result<Vec<EventField>, DemoParserError> {
105+
pub fn find_extra(&self, fields: &Vec<EventField>) -> Result<Vec<EventField>, DemoParserError> {
103106
let mut extra_fields = vec![];
104107
// Always add tick to event
105108
extra_fields.push(EventField {
@@ -132,7 +135,7 @@ impl ParserThread {
132135
extra_fields.extend(self.find_non_player_props());
133136
Ok(extra_fields)
134137
}
135-
fn generate_empty_fields(&self, prefix: &str) -> Vec<EventField> {
138+
pub fn generate_empty_fields(&self, prefix: &str) -> Vec<EventField> {
136139
let mut extra_fields = vec![];
137140
// when pointer fails for some reason we need to add None to output
138141
for prop_info in &self.prop_controller.prop_infos {
@@ -159,7 +162,7 @@ impl ParserThread {
159162
extra_fields
160163
}
161164

162-
fn find_non_player_props(&self) -> Vec<EventField> {
165+
pub fn find_non_player_props(&self) -> Vec<EventField> {
163166
let mut extra_fields = vec![];
164167
for prop_info in &self.prop_controller.prop_infos {
165168
let fields = match prop_info.prop_type {
@@ -172,7 +175,7 @@ impl ParserThread {
172175
extra_fields
173176
}
174177

175-
fn find_other_rules_props(&self, prop_info: &PropInfo) -> Vec<EventField> {
178+
pub fn find_other_rules_props(&self, prop_info: &PropInfo) -> Vec<EventField> {
176179
let mut extra_fields = vec![];
177180
let prop = match self.rules_entity_id {
178181
Some(entid) => match self.get_prop_from_ent(&prop_info.id, &entid) {
@@ -187,7 +190,7 @@ impl ParserThread {
187190
});
188191
extra_fields
189192
}
190-
fn find_other_team_props(&self, prop_info: &PropInfo) -> Vec<EventField> {
193+
pub fn find_other_team_props(&self, prop_info: &PropInfo) -> Vec<EventField> {
191194
let mut extra_fields = vec![];
192195
let t = self.teams.team2_entid;
193196
let ct = self.teams.team3_entid;
@@ -259,7 +262,7 @@ impl ParserThread {
259262
}
260263
extra_pairs
261264
}
262-
fn create_player_name_field(&self, entity_id: i32, prefix: &str) -> EventField {
265+
pub fn create_player_name_field(&self, entity_id: i32, prefix: &str) -> EventField {
263266
if entity_id == ENTITYIDNONE {
264267
return EventField {
265268
name: prefix.to_owned() + "_name",
@@ -278,7 +281,7 @@ impl ParserThread {
278281
data: data,
279282
}
280283
}
281-
fn create_player_steamid_field(&self, entity_id: i32, prefix: &str) -> EventField {
284+
pub fn create_player_steamid_field(&self, entity_id: i32, prefix: &str) -> EventField {
282285
if entity_id == ENTITYIDNONE {
283286
return EventField {
284287
name: prefix.to_owned() + "_steamid",
@@ -297,6 +300,68 @@ impl ParserThread {
297300
data: data,
298301
}
299302
}
303+
pub fn player_from_steamid32(&self, steamid32: i32) -> Option<i32> {
304+
for (_entid, player) in &self.players {
305+
if let Some(steamid) = player.steamid {
306+
if steamid - STEAMID64INDIVIDUALIDENTIFIER == steamid32 as u64 {
307+
return Some(player.player_entity_id.unwrap());
308+
}
309+
}
310+
}
311+
None
312+
}
313+
pub fn create_custom_event_rank_update(&mut self, msg_bytes: &[u8]) -> Result<(), DemoParserError> {
314+
if !self.wanted_events.contains(&"rank_update".to_string()) {
315+
return Ok(());
316+
}
317+
let update_msg: CCSUsrMsg_ServerRankUpdate = match Message::parse_from_bytes(&msg_bytes) {
318+
Ok(m) => m,
319+
Err(_e) => return Err(DemoParserError::MalformedMessage),
320+
};
321+
322+
for update in update_msg.rank_update {
323+
let mut fields = vec![];
324+
325+
let entity_id = match self.player_from_steamid32(update.account_id.unwrap()) {
326+
Some(eid) => eid,
327+
None => continue,
328+
};
329+
330+
fields.push(self.create_player_name_field(entity_id, "user"));
331+
fields.push(self.create_player_steamid_field(entity_id, "user"));
332+
fields.extend(self.find_extra_props_events(entity_id, "user"));
333+
334+
fields.push(EventField {
335+
data: Some(Variant::I32(update.num_wins())),
336+
name: "num_wins".to_string(),
337+
});
338+
fields.push(EventField {
339+
data: Some(Variant::I32(update.rank_old())),
340+
name: "rank_old".to_string(),
341+
});
342+
fields.push(EventField {
343+
data: Some(Variant::I32(update.rank_new())),
344+
name: "rank_new".to_string(),
345+
});
346+
fields.push(EventField {
347+
data: Some(Variant::F32(update.rank_change())),
348+
name: "rank_change".to_string(),
349+
});
350+
fields.push(EventField {
351+
data: Some(Variant::I32(update.rank_type_id())),
352+
name: "rank_type_id".to_string(),
353+
});
354+
let ge = GameEvent {
355+
name: "rank_update".to_string(),
356+
fields: fields,
357+
tick: self.tick,
358+
};
359+
self.game_events.push(ge);
360+
self.game_events_counter.insert("rank_update".to_string());
361+
}
362+
363+
Ok(())
364+
}
300365
}
301366
// what is this shit
302367
fn parse_key(key: &Key_t) -> Option<Variant> {

src/parser/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ fn main() {
5858
let mut ds = Parser::new(settings);
5959
let d = ds.parse_demo().unwrap();
6060
println!("TOTAL {:?}", before.elapsed());
61-
println!("{:?}", d.game_events_counter);
61+
println!("{:?}", d.game_events);
6262
}
6363
println!("TOTAL {:?}", before.elapsed());
6464
}

src/parser/src/parser_threads.rs

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ impl ParserThread {
9393
UM_SayText2 => self.parse_chat_messages(&msg_bytes),
9494
net_SetConVar => self.parse_convars(&msg_bytes),
9595
CS_UM_PlayerStatsUpdate => self.parse_player_stats_update(&msg_bytes),
96+
CS_UM_ServerRankUpdate => self.create_custom_event_rank_update(&msg_bytes),
9697
_ => Ok(()),
9798
};
9899
ok?;

src/python/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "demoparser2"
3-
version = "0.5.1"
3+
version = "0.6.0"
44
edition = "2021"
55

66

src/python/tests/e2e_test.py

+6
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,11 @@ def test_parse_grenades(self):
4747
df = convert_same_dtypes(parser.parse_grenades(), df_correct)
4848
assert_frame_equal(df, df_correct)
4949

50+
def test_custom_even_rank_update(self):
51+
parser = DemoParser("tests/data/test.dem")
52+
df = parser.parse_event("rank_update")
53+
df_correct = convert_same_dtypes(df, pd.read_csv("tests/data/python/rank_update.csv"))
54+
assert_frame_equal(df, df_correct)
55+
5056
if __name__ == '__main__':
5157
unittest.main()

0 commit comments

Comments
 (0)