Skip to content

Commit

Permalink
Move the CS:GO code from csdemoparser into the csgo_demo crate.
Browse files Browse the repository at this point in the history
Also parse the header in both csgo_demo and cs2_demo.
  • Loading branch information
abenea committed Feb 9, 2024
1 parent 828e089 commit 73b7489
Show file tree
Hide file tree
Showing 25 changed files with 386 additions and 390 deletions.
9 changes: 0 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ members = [
"csgo-demo",
"cs2-demo",
"csdemoparser",
"demo-format",
"parsetest",
]

Expand Down
7 changes: 6 additions & 1 deletion cs2-demo/src/demo_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ pub struct DemoParser<'a> {
}

impl<'a> DemoParser<'a> {
pub fn try_new_after_demo_type(read: &'a mut dyn std::io::Read) -> Result<Self> {
pub fn try_new(read: &'a mut dyn std::io::Read) -> Result<Self> {
let mut demo_type = [0; 8];
read.read_exact(&mut demo_type)?;
if &demo_type != b"PBDEMS2\0" {
return Err(Error::InvalidDemoType(Box::new(demo_type)));
}
let mut reader = CodedInputStream::new(read);
reader.skip_raw_bytes(8)?;
Ok(Self { reader })
Expand Down
4 changes: 2 additions & 2 deletions cs2-demo/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ pub enum Error {
Io(#[from] std::io::Error),
#[error(transparent)]
Protobuf(#[from] protobuf::Error),
#[error("invalid demo type (expected: PBDEMS2, found: {found})")]
InvalidDemoType { found: String },
#[error("invalid demo type (expected: PBDEMS2, found: {0:?})")]
InvalidDemoType(Box<[u8]>),
#[error("unknown packet command found: {0}")]
UnknownPacketCommand(u32),
#[error(transparent)]
Expand Down
2 changes: 1 addition & 1 deletion cs2-demo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod visit;
pub use crate::error::{Error, Result};
pub use crate::game_event::GameEventDescriptors;
pub use crate::string_table::{PlayerInfo, UserInfo};
pub use crate::visit::{parse_after_demo_type, Visitor};
pub use crate::visit::{parse, Visitor};
pub type Tick = i32;

type BitReader<'a> = bitstream_io::BitReader<&'a [u8], bitstream_io::LittleEndian>;
Expand Down
4 changes: 2 additions & 2 deletions cs2-demo/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ pub trait Visitor {
}
}

pub fn parse_after_demo_type(read: &mut dyn Read, visitor: &mut dyn Visitor) -> Result<()> {
DemoVisit::new(DemoParser::try_new_after_demo_type(read)?, visitor).parse()
pub fn parse(read: &mut dyn Read, visitor: &mut dyn Visitor) -> Result<()> {
DemoVisit::new(DemoParser::try_new(read)?, visitor).parse()
}

struct DemoVisit<'a> {
Expand Down
1 change: 0 additions & 1 deletion csdemoparser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ bitstream-io.workspace = true
byteorder.workspace = true
csgo-demo = { path = "../csgo-demo" }
cs2-demo = { path = "../cs2-demo" }
demo-format = { path = "../demo-format" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
Expand Down
4 changes: 2 additions & 2 deletions csdemoparser/src/cs2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ use crate::demoinfo::{

use crate::game_event::GameEvent;
use crate::last_jump::LastJump;
use crate::Tick;
use crate::{DemoInfo, Slot, UserId};
use cs2_demo::entity::Entities;
use cs2_demo::proto::demo::CDemoFileHeader;
use cs2_demo::proto::gameevents::CMsgSource1LegacyGameEvent;
use cs2_demo::{GameEventDescriptors, UserInfo, Visitor};
use demo_format::Tick;
use std::collections::{hash_map, HashMap};
use tracing::{instrument, trace};

pub fn parse(read: &mut dyn std::io::Read) -> anyhow::Result<DemoInfo> {
let mut state = GameState::new();
cs2_demo::parse_after_demo_type(read, &mut state)?;
cs2_demo::parse(read, &mut state)?;
state.get_info()
}

Expand Down
30 changes: 12 additions & 18 deletions csdemoparser/src/csgo.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
mod game_event;

use crate::entity::{Entities, Entity, EntityId, PropValue, Scalar};
use crate::entity::{ServerClasses, TrackProp};
use csgo_demo::entity::{Entities, Entity, EntityId, PropValue, Scalar, ServerClasses, TrackProp};
use crate::geometry::{through_smoke, Point};
use crate::last_jump::LastJump;
use crate::string_table::{self, PlayerInfo, StringTables};
use crate::{account_id_to_xuid, guid_to_xuid, maybe_get_i32, maybe_get_u16, DemoInfo, TeamScore};
use anyhow::bail;
use csgo_demo::proto::netmessages::CSVCMsg_GameEvent;
use csgo_demo::Message;
use csgo_demo::PacketContent;
use csgo_demo::StringTable;
use demo_format::Tick;
use csgo_demo::{Message, PacketContent};
use csgo_demo::string_table::{parse_player_infos, PlayerInfo, StringTable, StringTables};
use crate::Tick;
use serde_json::json;
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
Expand All @@ -32,7 +29,7 @@ const GAME_RESTART: &str = "m_bGameRestart";
const TEAM_CLASS: &str = "CCSTeam";

pub fn parse(read: &mut dyn io::Read) -> anyhow::Result<DemoInfo> {
let mut parser = csgo_demo::DemoParser::try_new_after_demo_type(read)?;
let mut parser = csgo_demo::DemoParser::try_new(read)?;
let server_name = parser.header().server_name().to_string();
let mut server_classes = None;
let mut packets = vec![];
Expand Down Expand Up @@ -191,13 +188,8 @@ impl<'a> HeadshotBoxParser<'a> {
fn handle_string_tables(&mut self, st: Vec<StringTable>) -> anyhow::Result<()> {
// demoinfogo clears the players but I don't think this is correct
self.players.clear();
for st in st.iter().filter(|st| st.name() == "userinfo") {
for (entity_id, string) in st.strings().iter().enumerate() {
if let Some(data) = string.data() {
let player_info = string_table::parse_player_info(data, entity_id as i32)?;
Self::update_players(&mut self.players, &self.demoinfo, player_info);
}
}
for player_info in parse_player_infos(st)? {
Self::update_players(&mut self.players, &self.demoinfo, player_info);
}
Ok(())
}
Expand All @@ -211,17 +203,19 @@ impl<'a> HeadshotBoxParser<'a> {
}
Message::CreateStringTable(table) => {
let mut updates = self.string_tables.create_string_table(&table);
while let Some(player_info) = updates.next()? {
while let Some(player_info) = updates.next_player_info()? {
Self::update_players(&mut self.players, &self.demoinfo, player_info);
}
}
Message::UpdateStringTable(table) => {
let mut updates = self.string_tables.update_string_table(&table)?;
while let Some(player_info) = updates.next()? {
while let Some(player_info) = updates.next_player_info()? {
Self::update_players(&mut self.players, &self.demoinfo, player_info);
}
}
Message::GameEventList(gel) => self.game_event_descriptors = self::game_event::parse_game_event_list(gel),
Message::GameEventList(gel) => {
self.game_event_descriptors = self::game_event::parse_game_event_list(gel)
}
Message::GameEvent(event) => {
if let Some(descriptor) = self.game_event_descriptors.get(&event.eventid()) {
let attrs = self.event_map(event, descriptor, tick)?;
Expand Down
2 changes: 1 addition & 1 deletion csdemoparser/src/demoinfo.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use demo_format::Tick;
use crate::Tick;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

Expand Down
2 changes: 1 addition & 1 deletion csdemoparser/src/last_jump.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use demo_format::Tick;
use crate::Tick;
use std::collections::HashMap;

#[derive(Default)]
Expand Down
35 changes: 15 additions & 20 deletions csdemoparser/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
mod cs2;
mod csgo;
pub mod demoinfo;
mod entity;
mod game_event;
mod geometry;
mod last_jump;
mod string_table;

use crate::entity::{Entity, EntityId, PropValue, Scalar};
use demo_format::read::ReadExt;
use csgo_demo::entity::{Entity, EntityId, PropValue, Scalar};
use demoinfo::DemoInfo;
use std::io;
use std::{
fs::File,
io::{Read, Seek},
};

const SOURCE1_DEMO_TYPE: &str = "HL2DEMO";
const SOURCE2_DEMO_TYPE: &str = "PBDEMS2";
type Tick = i32;

pub fn parse(mut read: &mut dyn io::Read) -> anyhow::Result<DemoInfo> {
let demo_type = read.read_string_limited(8)?;
match demo_type.as_str() {
const SOURCE1_DEMO_TYPE: &[u8; 8] = b"HL2DEMO\0";
const SOURCE2_DEMO_TYPE: &[u8; 8] = b"PBDEMS2\0";

pub fn parse(read: &mut File) -> anyhow::Result<DemoInfo> {
let mut demo_type = [0; 8];
read.read_exact(&mut demo_type)?;
read.rewind()?;
match &demo_type {
SOURCE1_DEMO_TYPE => csgo::parse(read),
SOURCE2_DEMO_TYPE => {
if std::env::var("CS2_EXPERIMENTAL_PARSER").is_ok() {
Expand All @@ -26,7 +30,7 @@ pub fn parse(mut read: &mut dyn io::Read) -> anyhow::Result<DemoInfo> {
panic!("CS2 demo parser is not complete. You can test it by seting the CS2_EXPERIMENTAL_PARSER environment variable.")
}
}
_ => Err(cs2_demo::Error::InvalidDemoType { found: demo_type }.into()),
_ => Err(cs2_demo::Error::InvalidDemoType(Box::new(demo_type)).into()),
}
}

Expand Down Expand Up @@ -94,15 +98,6 @@ fn maybe_get_i32(v: Option<&serde_json::Value>) -> Option<i32> {
Some(v?.as_i64()? as i32)
}

// Number of bits needed to represent values in the 0..=n interval.
fn num_bits(n: u32) -> u32 {
if n == 0 {
1
} else {
u32::BITS - n.leading_zeros()
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading

0 comments on commit 73b7489

Please sign in to comment.