boon-deadlock 0.1.0

Boon is a Deadlock demo / replay file parser
Documentation

boon-deadlock

crates.io crates.io Downloads docs.rs License: MIT

A fast Deadlock demo file (.dem) parser for Rust.

Part of the Boon project.

Features

  • Memory-mapped, zero-copy parsing for maximum throughput
  • Match metadata (map, players, duration, build number)
  • Full entity state at any tick via snapshot seeking
  • Game event extraction with protobuf decoding
  • Filtered tick streaming for efficient per-entity-class analysis
  • Ability and modifier name lookups

Installation

Add to your Cargo.toml:

[dependencies]
boon-deadlock = "0.1"

Requires Rust 1.88+ (edition 2024).

Quick Start

use std::path::Path;
use boon::Parser;

let parser = Parser::from_file(Path::new("match.dem")).unwrap();
parser.verify().unwrap();

// File header
let header = parser.file_header().unwrap();
println!("Map: {:?}", header.map_name);
println!("Build: {:?}", header.build_num);

// File info (playback time, players)
let info = parser.file_info().unwrap();
println!("Duration: {:?}s", info.playback_time);

API Overview

Parser

The main entry point. Owns the demo file data (memory-mapped or in-memory).

Method Description
Parser::from_file(path) Open and memory-map a .dem file
Parser::from_bytes(bytes) Parse from an in-memory buffer
verify() Check magic bytes
file_header() Decode CDemoFileHeader (map, server, build)
file_info() Decode CDemoFileInfo (duration, players)
messages() List all command headers in the file
events(max_tick) Extract game events (legacy + Citadel user messages)
parse_to_tick(tick) Parse to a specific tick, returning full entity state
run_to_end(callback) Stream every tick with a callback
run_to_end_filtered(filter, callback) Stream with an entity class filter (much faster)

Context

Returned by parse_init, parse_to_tick, and passed to tick callbacks. Contains:

  • entities — all active entities (EntityContainer)
  • serializers — field definitions per class
  • class_info — class ID to name mappings
  • string_tables — key-value tables (models, baselines, etc.)
  • tick — current tick
  • tick_interval — seconds per tick

Entity

A single networked entity with class name and decoded field values.

// Look up fields by dotted path
let health = entity.get_by_name("m_iHealth", serializer);
let x = entity.get_by_name(
    "CBodyComponent.m_skeletonInstance.m_vecOrigin.m_vecX",
    serializer,
);

Helper Functions

  • ability_name(id) — resolve an ability hash to its name
  • modifier_name(id) — resolve a modifier hash to its name
  • decode_event_payload(msg_type, data) — decode a game event's protobuf payload

Examples

Runnable examples are in examples/. Each accepts a demo file path as a CLI argument.

# Match metadata
cargo run -p boon-deadlock --example info -- match.dem

# Game events (optionally filtered by name)
cargo run -p boon-deadlock --example events -- match.dem Damage

# Entity snapshot at a specific tick
cargo run -p boon-deadlock --example entities -- match.dem 5000

# Stream all ticks with a class filter
cargo run -p boon-deadlock --example player_ticks -- match.dem
Example What it shows
info file_header(), file_info(), match metadata and player list
events events(), event filtering, decode_event_payload()
entities parse_to_tick(), entity iteration, get_by_name(), ability_name()
player_ticks run_to_end_filtered(), resolve_field_key(), per-tick streaming

Performance

For best throughput when you only need specific entity types, use run_to_end_filtered with a class filter. This skips field decoding for entities outside the filter set.

use std::collections::HashSet;

let filter: HashSet<&str> = ["CCitadelPlayerPawn"].into_iter().collect();
parser.run_to_end_filtered(&filter, |ctx| {
    for (_, entity) in ctx.entities.iter() {
        // Only CCitadelPlayerPawn entities are tracked
    }
}).unwrap();

License

MIT — see LICENSE for details.