Skip to main content

Parser

Struct Parser 

Source
pub struct Parser<'a, R = SliceReader<'a>>
where R: BitsReader + MessageReader,
{ /* private fields */ }
Expand description

Main parser for Source 2 demo files.

The parser maintains the replay state and processes demo commands sequentially. It supports multiple observers that can react to different types of events.

§Examples

§Basic usage with chat messages

use source2_demo::prelude::*;

#[derive(Default)]
struct ChatLogger;

#[observer]
impl ChatLogger {
    #[on_message]
    fn on_chat(&mut self, ctx: &Context, msg: CDotaUserMsgChatMessage) -> ObserverResult {
        println!("{}", msg.message_text());
        Ok(())
    }
}

fn main() -> anyhow::Result<()> {
    let replay = std::fs::File::open("replay.dem")?;

    let mut parser = Parser::from_reader(&replay)?;
    parser.register_observer::<ChatLogger>();
    parser.run_to_end()?;

    Ok(())
}

§Processing entities

use source2_demo::prelude::*;

#[derive(Default)]
struct HeroTracker;

impl Observer for HeroTracker {
    fn interests(&self) -> Interests {
        Interests::ENABLE_ENTITY | Interests::TRACK_ENTITY
    }

    fn on_entity(&mut self, ctx: &Context, event: EntityEvents, entity: &Entity) -> ObserverResult {
        if entity.class().name().starts_with("CDOTA_Unit_Hero_") {
            let health: i32 = property!(entity, "m_iHealth");
            println!("Hero {} health: {}", entity.class().name(), health);
        }
        Ok(())
    }
}

Implementations§

Source§

impl<'a> Parser<'a, SliceReader<'a>>

Source

pub fn new(replay: &'a [u8]) -> Result<Self, ParserError>

Creates a new parser instance from replay bytes.

This method validates the replay file format and reads the file header. The replay data should remain valid for the lifetime of the parser.

§Arguments
  • replay - Byte slice containing the demo file data (typically memory-mapped)
§Errors

Returns ParserError::WrongMagic if the file is not a valid Source 2 demo file. Returns ParserError::ReplayEncodingError if the file header is corrupted.

§Examples
use source2_demo::prelude::*;
use std::fs::File;

// Using memory-mapped file (recommended for large files)
let file = File::open("replay.dem")?;
let replay = unsafe { memmap2::Mmap::map(&file)? };
let parser = Parser::new(&replay)?;

// Or read into memory (for small files)
let replay = std::fs::read("replay.dem")?;
let parser = Parser::new(&replay)?;
Source

pub fn from_slice(replay: &'a [u8]) -> Result<Self, ParserError>

Creates a new parser from replay bytes (same as new).

This is an alias for Parser::new that makes it explicit if you’re using a slice.

§Arguments
  • replay - Byte slice containing the demo file data
§Errors

Returns ParserError::WrongMagic if the file is not a valid Source 2 demo file.

Source§

impl<S> Parser<'static, SeekableReader<S>>
where S: Read + Seek,

Source

pub fn from_reader(reader: S) -> Result<Self, ParserError>

Creates a new parser from a reader.

Uses SeekableReader for reading data from the reader, but internally uses SliceReader for parsing message buffers for maximum performance.

§Arguments
  • reader - Any type implementing Read + Seek (e.g., File, Cursor, BufReader)
§Errors

Returns an error if reading from the reader fails or data is invalid.

§Examples
use source2_demo::prelude::*;
use std::fs::File;

let file = File::open("replay.dem")?;
let mut parser = Parser::from_reader(file)?;
parser.run_to_end()?;
Source§

impl<'a, R> Parser<'a, R>
where R: BitsReader + MessageReader,

Source

pub fn context(&self) -> &Context

Returns a reference to the current parser context.

The context contains the current state of the replay, including

  • Entities and their properties
  • String tables
  • Game events
  • Current tick and game build
§Examples
use source2_demo::prelude::*;

let parser = Parser::from_reader(&replay)?;
let ctx = parser.context();
println!("Current tick: {}", ctx.tick());
println!("Game build: {}", ctx.game_build());
Source

pub fn replay_info(&self) -> &CDemoFileInfo

Returns replay file information. Contains metadata about the replay including:

  • Playback duration
  • Server information
  • Game-specific details
§Examples
use source2_demo::prelude::*;

let parser = Parser::from_reader(&replay)?;
let info = parser.replay_info();
println!("Playback ticks: {}", info.playback_ticks());
Source

pub fn register_observer<T>(&mut self) -> Rc<RefCell<T>>
where T: Observer + Default + 'a,

Registers an observer and returns a reference-counted handle to it.

Observers must implement the Observer trait and Default. Use the #[observer] attribute macro to automatically implement the trait.

The returned Rc<RefCell<T>> allows you to access the observer’s state after parsing completes.

§Type Parameters
§Examples
use source2_demo::prelude::*;
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Default)]
struct Stats {
    message_count: usize,
}

#[observer]
impl Stats {
    #[on_message]
    fn on_chat(&mut self, ctx: &Context, msg: CDotaUserMsgChatMessage) -> ObserverResult {
        self.message_count += 1;
        Ok(())
    }
}

let mut parser = Parser::from_reader(&replay)?;
let stats = parser.register_observer::<Stats>();
parser.run_to_end()?;

println!("Total messages: {}", stats.borrow().message_count);

Trait Implementations§

Source§

impl<'a, R> DemoRunner for Parser<'a, R>
where R: BitsReader + MessageReader,

Source§

fn run_to_end(&mut self) -> Result<(), ParserError>

Processes the entire replay from start to finish. Read more
Source§

fn run_to_tick(&mut self, target_tick: u32) -> Result<(), ParserError>

Processes the replay up to a specific tick. Read more
Source§

fn jump_to_tick(&mut self, target_tick: u32) -> Result<(), ParserError>

Jumps to a specific tick without full processing. Read more

Auto Trait Implementations§

§

impl<'a, R> Freeze for Parser<'a, R>
where R: Freeze,

§

impl<'a, R = SliceReader<'a>> !RefUnwindSafe for Parser<'a, R>

§

impl<'a, R = SliceReader<'a>> !Send for Parser<'a, R>

§

impl<'a, R = SliceReader<'a>> !Sync for Parser<'a, R>

§

impl<'a, R> Unpin for Parser<'a, R>
where R: Unpin,

§

impl<'a, R> UnsafeUnpin for Parser<'a, R>
where R: UnsafeUnpin,

§

impl<'a, R = SliceReader<'a>> !UnwindSafe for Parser<'a, R>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.