1
0
Fork 0
mirror of https://codeberg.org/demostf/parser.git synced 2026-10-01 00:34:06 +02:00
Parsing of tf2 demo files
  • Rust 99.8%
  • Nix 0.2%
Find a file
Peter 56f0856441 Write protocol dependent lengths when encoding
CreateStringTable and TempEntities both read their payload length as a fixed
width field on protocol 23 and older and as a var int after that, but the
encoders always write the var int. Reencoding an older demo therefore hands
the game a length it can't read: string tables come back as "bogus substring
index", and any packet carrying temp entities is malformed from there on,
taking whatever messages follow it in that packet with it.

The threshold and both widths are taken from the read paths a few lines up in
these same two files rather than worked out separately, so the two sides can't
drift: > 23, with 20 bits for CreateStringTable and 17 for TempEntities. The
engine agrees, for what it's worth - netmessages.cpp reads both of these under
> PROTOCOL_VERSION_23 at NET_MAX_PAYLOAD_BITS_V23 + 3 and
NET_MAX_PAYLOAD_BITS_V23 respectively, and that constant is 17.

serverinfo and prefetch already branch on the protocol on both sides, these
two were the only ones that didn't.

On a protocol 15 demo that was 28909 of 92518 reencoded packets failing to
parse back; on test_data/protocol23.dem, 13220 of 43327. Both are zero now,
so protocol23.dem can join the reencode round trip test. Protocol 24 output
is unchanged, byte for byte.
2026-08-31 11:58:21 +01:00
.forgejo/workflows workflow updates 2025-05-25 22:37:37 +02:00
benches move benchmarks to iai-callgrind 2025-07-13 23:40:28 +02:00
examples clippy fixes 2025-07-13 23:40:28 +02:00
fuzz bumb dependencies 2023-01-07 13:51:48 +01:00
nix nix fmt 2024-05-06 18:39:49 +02:00
smoker smoker fixes 2020-01-29 22:53:04 +01:00
src Write protocol dependent lengths when encoding 2026-08-31 11:58:21 +01:00
test_data Fix saturating overflow in ExtraData parsing 2025-06-02 21:35:42 +01:00
tests Write protocol dependent lengths when encoding 2026-08-31 11:58:21 +01:00
.envrc flake 2022-08-05 23:36:56 +02:00
.gitignore feature gate write support, use hash to compare game event definitions 2025-07-15 16:06:23 +02:00
Cargo.lock update schemars to 1.0 2025-09-17 00:14:27 +02:00
Cargo.toml update schemars to 1.0 2025-09-17 00:14:27 +02:00
flake.lock flake update 2025-08-07 22:46:05 +02:00
flake.nix flake update 2025-06-03 20:41:13 +02:00
pgo_build.sh first prop flattening version 2019-08-10 19:38:42 +02:00
README.md formatting 2025-07-13 23:41:22 +02:00
rustig.toml check for panicing code in ci 2019-08-11 13:57:20 +02:00
schema.json update schemars to 1.0 2025-09-17 00:14:27 +02:00

TF Demo Parser

Build Status

Parsing of tf2 demo files

Building

This project is build using rust and requires cargo and friends, see the rust website for how to get started.

Once rust is setup building is as simple as

cargo build --release

which will place the binary at target/release/parse_demo

Usage

Basic usage is as simple as parse_demo demofile.dem which will output a "summary" of the demo file in JSON format.

Passing the detailed_summary argument to the end of parse_demo will output a table with scoreboard information for all players who were ever on the server while the demo was being recorded. The player who created the demo will be highlighted in the output.

Advanced usage

Loop through every packet

use bitbuffer::BitRead;
use main_error::MainError;
use std::fs;
use tf_demo_parser::demo::header::Header;
use tf_demo_parser::demo::parser::{DemoHandler, RawPacketStream};
use tf_demo_parser::Demo;

fn main() -> Result<(), MainError> {
    let file = fs::read("demofile.dem")?;

    let demo = Demo::new(&file);
    let mut handler = DemoHandler::default();

    let mut stream = demo.get_stream();
    let header = Header::read(&mut stream)?;
    handler.handle_header(&header);

    let mut packets = RawPacketStream::new(stream);

    while let Some(packet) = packets.next(&handler.state_handler)? {
        handler.handle_packet(packet).unwrap();
    }
    assert_eq!(false, packets.incomplete);

    Ok(())
}

Handle demo data with a custom analyser

Sometimes it's easier to create a custom Analyser to handle the demo data as it comes along.

See src/demo/parser/analyser.rs for an example.
Once you have a custom analyser you can use it with:

DemoParser::new_all_with_analyser(demo.get_stream(), CustomAnalyser::new());
let (header, state) = parser.parse()?;