rewrite instance baselines after the array size fixup

This commit is contained in:
Robin Appelman 2026-09-09 23:17:55 +02:00
commit 46e955b411
6 changed files with 302 additions and 187 deletions

157
wasm/Cargo.lock generated
View file

@ -89,7 +89,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta 0.3.0", "structmeta 0.3.0",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -151,7 +151,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -227,7 +227,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -301,9 +301,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.14.0" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
dependencies = [ dependencies = [
"either", "either",
] ]
@ -335,7 +335,7 @@ checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -356,9 +356,9 @@ checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.28" version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]] [[package]]
name = "main_error" name = "main_error"
@ -397,7 +397,7 @@ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -430,11 +430,11 @@ dependencies = [
[[package]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.7.4" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
dependencies = [ dependencies = [
"num_enum_derive 0.7.4", "num_enum_derive 0.7.6",
"rustversion", "rustversion",
] ]
@ -452,14 +452,14 @@ dependencies = [
[[package]] [[package]]
name = "num_enum_derive" name = "num_enum_derive"
version = "0.7.4" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
dependencies = [ dependencies = [
"proc-macro-crate 3.4.0", "proc-macro-crate 3.4.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -487,13 +487,13 @@ dependencies = [
[[package]] [[package]]
name = "parse-display" name = "parse-display"
version = "0.9.1" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" checksum = "e78deb158fb1d73b29efb4b7e9b9860b78059c670de06bd28df8d0b458ded0eb"
dependencies = [ dependencies = [
"parse-display-derive 0.9.1", "parse-display-derive 0.11.0",
"regex", "regex",
"regex-syntax 0.8.6", "regex-syntax 0.8.11",
] ]
[[package]] [[package]]
@ -513,16 +513,16 @@ dependencies = [
[[package]] [[package]]
name = "parse-display-derive" name = "parse-display-derive"
version = "0.9.1" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" checksum = "8e95a50d1084dab562913062c4c34bb204b68fc6ec38a1395909ff5aaaf4f10a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex", "regex",
"regex-syntax 0.8.6", "regex-syntax 0.8.11",
"structmeta 0.3.0", "structmeta 0.3.0",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -578,48 +578,48 @@ dependencies = [
"proc-macro-error-attr2", "proc-macro-error-attr2",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.101" version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.41" version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.3" version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata", "regex-automata",
"regex-syntax 0.8.6", "regex-syntax 0.8.11",
] ]
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.11" version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-syntax 0.8.6", "regex-syntax 0.8.11",
] ]
[[package]] [[package]]
@ -630,9 +630,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.6" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]] [[package]]
name = "rustversion" name = "rustversion"
@ -640,12 +640,6 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -657,9 +651,9 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [ dependencies = [
"serde_core", "serde_core",
"serde_derive", "serde_derive",
@ -678,46 +672,46 @@ dependencies = [
[[package]] [[package]]
name = "serde_core" name = "serde_core"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.228" version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 3.0.5",
] ]
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.145" version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
"ryu",
"serde", "serde",
"serde_core", "serde_core",
"zmij",
] ]
[[package]] [[package]]
name = "serde_repr" name = "serde_repr"
version = "0.1.20" version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -728,9 +722,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "snap" name = "snap"
version = "1.1.1" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" checksum = "199905e6153d6405f9728fe44daace35f8f837bbf830bb6e85fbd5828709a886"
[[package]] [[package]]
name = "strsim" name = "strsim"
@ -759,7 +753,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"structmeta-derive 0.3.0", "structmeta-derive 0.3.0",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -781,7 +775,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -797,9 +791,20 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.106" version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -808,7 +813,7 @@ dependencies = [
[[package]] [[package]]
name = "tf-demo-parser" name = "tf-demo-parser"
version = "0.7.0" version = "0.7.1"
dependencies = [ dependencies = [
"bitbuffer", "bitbuffer",
"const-fnv1a-hash", "const-fnv1a-hash",
@ -818,8 +823,8 @@ dependencies = [
"log", "log",
"main_error", "main_error",
"num-traits", "num-traits",
"num_enum 0.7.4", "num_enum 0.7.6",
"parse-display 0.9.1", "parse-display 0.11.0",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
@ -829,22 +834,22 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.17" version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "2.0.17" version = "2.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 3.0.5",
] ]
[[package]] [[package]]
@ -911,7 +916,7 @@ checksum = "1ecb9ecf7799210407c14a8cfdfe0173365780968dc57973ed082211958e0b18"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -959,7 +964,7 @@ dependencies = [
"log", "log",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -994,7 +999,7 @@ checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -1029,7 +1034,7 @@ checksum = "b673bca3298fe582aeef8352330ecbad91849f85090805582400850f8270a2e8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.106", "syn 2.0.119",
] ]
[[package]] [[package]]
@ -1191,3 +1196,9 @@ checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"

View file

@ -39,7 +39,9 @@ pub fn cut(input: &[u8], options: EditOptions) -> Vec<u8> {
let mut stream = demo.get_stream(); let mut stream = demo.get_stream();
let mut header = Header::read(&mut stream).unwrap(); let mut header = Header::read(&mut stream).unwrap();
let mut mutators = options.as_mutator(spectator_id); let mut mutators = options
.as_mutator(spectator_id, &header, &stream)
.expect("invalid demo");
let start_tick = options.cut.unwrap().from; let start_tick = options.cut.unwrap().from;
let end_tick = options.cut.unwrap().to; let end_tick = options.cut.unwrap().to;
@ -105,9 +107,9 @@ pub fn cut(input: &[u8], options: EditOptions) -> Vec<u8> {
} }
// create the net ticks needed for later deltas // create the net ticks needed for later deltas
let fill_ticks = (delta_tick + 1) let next_tick = delta_tick + 1;
.range_inclusive(start_state.server_tick) let tick_range = next_tick.range_inclusive(start_state.server_tick);
.map(net_tick); let fill_ticks = tick_range.map(net_tick);
let fill_packets = fill_ticks.map(|msg| { let fill_packets = fill_ticks.map(|msg| {
Packet::Message(MessagePacket { Packet::Message(MessagePacket {
messages: vec![ messages: vec![

View file

@ -4,8 +4,8 @@ mod cut;
pub mod missing_preserve; pub mod missing_preserve;
mod mutate; mod mutate;
mod options; mod options;
mod pov;
mod player_count; mod player_count;
mod pov;
use bitbuffer::{BitRead, BitWriteStream, LittleEndian}; use bitbuffer::{BitRead, BitWriteStream, LittleEndian};
use tf_demo_parser::demo::header::Header; use tf_demo_parser::demo::header::Header;
@ -92,12 +92,15 @@ fn no_cut(input: &[u8], options: EditOptions) -> Vec<u8> {
let mut output_handler = DemoHandler::default(); let mut output_handler = DemoHandler::default();
output_handler.handle_header(&header); output_handler.handle_header(&header);
let mutators = options.as_mutator(spectator_id); let mut mutators = options
.as_mutator(spectator_id, &header, &stream)
.expect("invalid demo");
while let Some(mut packet) = packets.next(&handler.state_handler).unwrap() { while let Some(mut packet) = packets.next(&handler.state_handler).unwrap() {
handler.handle_packet(packet.clone()).expect("failed to handle source packet"); handler
.handle_packet(packet.clone())
.expect("failed to handle source packet");
mutators.mutate_packet(&mut packet, &handler.state_handler); mutators.mutate_packet(&mut packet, &handler.state_handler);
output_handler.handle_packet(packet.clone()).expect("failed to handle mutated packet");
if packet.packet_type() != PacketType::ConsoleCmd if packet.packet_type() != PacketType::ConsoleCmd
&& packet.packet_type() != PacketType::UserCmd && packet.packet_type() != PacketType::UserCmd
@ -106,6 +109,10 @@ fn no_cut(input: &[u8], options: EditOptions) -> Vec<u8> {
.encode(&mut out_stream, &output_handler.state_handler) .encode(&mut out_stream, &output_handler.state_handler)
.unwrap(); .unwrap();
} }
output_handler
.handle_packet(packet)
.expect("failed to handle mutated packet");
} }
} }
out_buffer out_buffer

View file

@ -4,7 +4,7 @@ use tf_demo_parser::demo::packet::Packet;
use tf_demo_parser::ParserState; use tf_demo_parser::ParserState;
pub trait PacketMutator { pub trait PacketMutator {
fn mutate_packet(&self, packet: &mut Packet, state: &ParserState); fn mutate_packet(&mut self, packet: &mut Packet, state: &ParserState);
} }
pub trait MessageMutator { pub trait MessageMutator {
@ -20,7 +20,7 @@ pub struct PacketMessageMutator<T: MessageMutator> {
} }
impl<T: MessageMutator> PacketMutator for PacketMessageMutator<T> { impl<T: MessageMutator> PacketMutator for PacketMessageMutator<T> {
fn mutate_packet(&self, packet: &mut Packet, state: &ParserState) { fn mutate_packet(&mut self, packet: &mut Packet, state: &ParserState) {
match packet { match packet {
Packet::Message(msg_packet) | Packet::Signon(msg_packet) => { Packet::Message(msg_packet) | Packet::Signon(msg_packet) => {
msg_packet msg_packet
@ -34,7 +34,7 @@ impl<T: MessageMutator> PacketMutator for PacketMessageMutator<T> {
} }
impl<F: Fn(&mut Packet)> PacketMutator for F { impl<F: Fn(&mut Packet)> PacketMutator for F {
fn mutate_packet(&self, packet: &mut Packet, _state: &ParserState) { fn mutate_packet(&mut self, packet: &mut Packet, _state: &ParserState) {
self(packet) self(packet)
} }
} }
@ -56,7 +56,7 @@ pub struct PacketMessageFilter<T: MessageFilter> {
} }
impl<T: MessageFilter> PacketMutator for PacketMessageFilter<T> { impl<T: MessageFilter> PacketMutator for PacketMessageFilter<T> {
fn mutate_packet(&self, packet: &mut Packet, _state: &ParserState) { fn mutate_packet(&mut self, packet: &mut Packet, _state: &ParserState) {
match packet { match packet {
Packet::Message(msg_packet) | Packet::Signon(msg_packet) => { Packet::Message(msg_packet) | Packet::Signon(msg_packet) => {
let messages = take(&mut msg_packet.messages); let messages = take(&mut msg_packet.messages);
@ -108,8 +108,8 @@ impl MutatorList {
} }
impl PacketMutator for MutatorList { impl PacketMutator for MutatorList {
fn mutate_packet(&self, packet: &mut Packet, state: &ParserState) { fn mutate_packet(&mut self, packet: &mut Packet, state: &ParserState) {
for mutator in self.mutators.iter() { for mutator in self.mutators.iter_mut() {
mutator.mutate_packet(packet, state); mutator.mutate_packet(packet, state);
} }
} }

View file

@ -1,10 +1,12 @@
use crate::missing_preserve::removed_invalid_deletes; use crate::missing_preserve::removed_invalid_deletes;
use crate::player_count::fix_player_count;
use crate::{clean_demo, strip_cond, unlock_pov, MutatorList}; use crate::{clean_demo, strip_cond, unlock_pov, MutatorList};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tf_demo_parser::demo::data::DemoTick; use tf_demo_parser::demo::data::DemoTick;
use tf_demo_parser::demo::header::Header;
use tf_demo_parser::demo::message::packetentities::EntityId; use tf_demo_parser::demo::message::packetentities::EntityId;
use tf_demo_parser::demo::message::Message; use tf_demo_parser::demo::message::Message;
use crate::player_count::fix_player_count; use tf_demo_parser::{ParseError, Stream};
#[derive(Debug, Serialize, Deserialize, Default)] #[derive(Debug, Serialize, Deserialize, Default)]
#[non_exhaustive] #[non_exhaustive]
@ -27,7 +29,12 @@ pub struct EditOptions {
} }
impl EditOptions { impl EditOptions {
pub fn as_mutator(&self, spectator_id: EntityId) -> MutatorList { pub fn as_mutator(
&self,
spectator_id: EntityId,
header: &Header,
stream: &Stream,
) -> Result<MutatorList, ParseError> {
let mut mutators = MutatorList::new(); let mut mutators = MutatorList::new();
mutators.push_message_mutator(|message: &mut Message| { mutators.push_message_mutator(|message: &mut Message| {
@ -56,10 +63,10 @@ impl EditOptions {
} }
if self.fix_player_count { if self.fix_player_count {
fix_player_count(&mut mutators); fix_player_count(&mut mutators, header, stream)?;
} }
mutators Ok(mutators)
} }
} }

View file

@ -1,57 +1,43 @@
use std::collections::HashSet;
use crate::mutate::{MutatorList, PacketMutator}; use crate::mutate::{MutatorList, PacketMutator};
use tf_demo_parser::demo::header::Header;
use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::packet::datatable::{DataTablePacket, SendTable};
use tf_demo_parser::demo::packet::stringtable::{ExtraData, StringTable, StringTableEntry};
use tf_demo_parser::demo::packet::Packet; use tf_demo_parser::demo::packet::Packet;
use tf_demo_parser::ParserState; use tf_demo_parser::demo::parser::state::StaticBaseline;
use tf_demo_parser::demo::parser::{DemoHandler, RawPacketStream};
use tf_demo_parser::{ParseError, ParserState, Stream};
#[derive(Default)] pub struct PlayerCount {
pub struct PlayerCount {} baselines: Option<Baselines>,
string_table_names: Vec<String>,
}
/// property arrays that got renamed and new items fn find_data_tables(header: &Header, stream: Stream) -> Result<DataTablePacket, ParseError> {
const PLAYER_ARRAY_PROPS_RENAMED: &[(&str, u16, u16)] = &[ let mut packets = RawPacketStream::new(stream);
("m_aBonusPlayerRoll_33", 33, 101), let mut handler = DemoHandler::default();
("m_ConditionData_128", 128, 131), handler.handle_header(header);
]; while let Some(packet) = packets.next(&handler.state_handler)? {
if let Packet::DataTables(tables) = &packet {
return Ok(tables.clone());
}
handler.handle_packet(packet)?;
}
Err(ParseError::InvalidDemo("missing data tables"))
}
/// property arrays that just got new items impl PlayerCount {
const PLAYER_ARRAY_PROPS: &[(&str, u16)] = &[ pub fn new(header: &Header, stream: Stream) -> Result<PlayerCount, ParseError> {
("m_iPing", 101), let data_tables = find_data_tables(header, stream)?;
("m_iScore", 101), let baselines = Baselines::new(&header, &data_tables)?;
("m_iDeaths", 101), Ok(PlayerCount {
("m_bConnected", 101), baselines,
("m_iTeam", 101), string_table_names: Vec::new(),
("m_bAlive", 101), })
("m_iHealth", 101), }
("m_iAccountID", 101), }
("m_bValid", 101),
("m_iUserID", 101),
("m_bPlayerReady", 101),
("m_ePlayerWantsRematch", 101),
("m_bPlayerIsPackMember", 101),
("m_bPlayerDominated", 101),
("m_bPlayerDominatingMe", 101),
("m_iTotalScore", 101),
("m_iMaxHealth", 101),
("m_iMaxBuffedHealth", 101),
("m_iPlayerClass", 101),
("m_bArenaSpectator", 101),
("m_iActiveDominations", 101),
("m_flNextRespawnTime", 101),
("m_iChargeLevel", 101),
("m_iDamage", 101),
("m_iDamageAssist", 101),
("m_iDamageBoss", 101),
("m_iHealing", 101),
("m_iHealingAssist", 101),
("m_iDamageBlocked", 101),
("m_iCurrencyCollected", 101),
("m_iBonusPoints", 101),
("m_iPlayerLevel", 101),
("m_iUpgradeRefundCredits", 101),
("m_iBuybackCredits", 101),
("m_iPlayerClassWhenKilled", 101),
("m_iConnectionState", 101),
("m_flConnectTime", 101),
("m_iStreaks", 407),
];
// property values that got changed // property values that got changed
const PLAYER_PROPS_VALUES: &[(&str, &str, u16, u16)] = &[ const PLAYER_PROPS_VALUES: &[(&str, &str, u16, u16)] = &[
@ -60,61 +46,163 @@ const PLAYER_PROPS_VALUES: &[(&str, &str, u16, u16)] = &[
("DT_TFTeam", r#""team_object_array""#, 198, 606), ("DT_TFTeam", r#""team_object_array""#, 198, 606),
]; ];
fn new_prop_size(table: &str, prop: &str) -> Option<u16> {
for (changed_table, changed_prop, _, new_size) in PLAYER_PROPS_VALUES.iter().copied() {
if changed_table == table && changed_prop == prop {
return Some(new_size);
}
}
None
}
impl PacketMutator for PlayerCount { impl PacketMutator for PlayerCount {
fn mutate_packet(&self, packet: &mut Packet, _state: &ParserState) { fn mutate_packet(&mut self, packet: &mut Packet, _state: &ParserState) {
if let Packet::DataTables(data_tables) = packet { match packet {
for table in &mut data_tables.tables { Packet::DataTables(data_tables) => {
let table_name = table.name.as_str(); fixup_tables(data_tables);
if let Some(new_length) = PLAYER_ARRAY_PROPS.iter().copied().find_map(|(name, new_length)| (name == table_name).then_some(new_length)) {
let base = table.props.last().unwrap().clone();
let old_length = table.props.len() as u16;
for i in old_length ..= new_length {
let mut prop = base.clone();
prop.name = format!("{i:03}").into();
table.props.push(prop);
}
} else if let Some((old_length, new_length)) = PLAYER_ARRAY_PROPS_RENAMED.iter().copied().find_map(|(name, old_length, new_length)| table_name.ends_with(name).then_some((old_length, new_length))) {
if table_name.starts_with("_ST_") {
let base = table.props.last().unwrap().clone();
for i in old_length.. new_length { // non-inclusive range, because for some reasons these arrays are one short
let mut prop = base.clone();
prop.name = format!("{i:03}").into();
table.props.push(prop);
}
} else if table_name.starts_with("_LPT_") {
table.props[0].name = format!("lengthprop{new_length}").into();
}
let name_prefix = table_name.rsplit_once('_').unwrap().0;
table.name = format!("{name_prefix}_{}", new_length).into();
} else if let Some((prop_name, _, new_length)) = PLAYER_PROPS_VALUES.iter().copied().find_map(|(name, prop_name, old_length, new_length)| table_name.eq(name).then_some((prop_name, old_length, new_length))) {
for prop in &mut table.props {
if prop.name == prop_name {
prop.element_count = new_length.into();
}
}
}
for prop in &mut table.props {
if let Some(target_name) = &mut prop.table_name {
if let Some((_, new_length)) = PLAYER_ARRAY_PROPS_RENAMED.iter().copied().find_map(|(name, old_length, new_length)| target_name.ends_with(name).then_some((old_length, new_length))) {
let name_prefix = target_name.rsplit_once('_').unwrap().0;
*target_name = format!("{name_prefix}_{}", new_length).into();
}
}
}
// let table_name = table.name.as_str();
// for prop in &mut table.props {
// println!(
// "{}.{}: {}",
// table_name,
// prop.name,
// prop.element_count.unwrap_or_default()
// );
// }
} }
Packet::StringTables(tables_packet) => {
if let Some(baselines) = self.baselines.as_mut() {
for table in tables_packet.tables.iter_mut() {
self.string_table_names.push(table.name.as_ref().into());
baselines.rewrite_table(table);
}
}
}
Packet::Signon(message_packet) | Packet::Message(message_packet) => {
for msg in message_packet.messages.iter_mut() {
if let Some(baselines) = self.baselines.as_mut() {
baselines.rewrite_message(msg, &self.string_table_names);
}
}
}
_ => {}
} }
} }
} }
pub fn fix_player_count(mutators: &mut MutatorList) { pub fn fix_player_count(
mutators.push_packet_mutator(PlayerCount::default()) mutators: &mut MutatorList,
header: &Header,
stream: &Stream,
) -> Result<(), ParseError> {
PlayerCount::new(header, stream.clone())
.map(|player_count| mutators.push_packet_mutator(player_count))
}
/// Raise the MAX_PLAYERS arrays from the pre-100-player sizes, which a current
/// client rejects with "RecvProp type doesn't match server type".
///
/// Also changes the encoding: the length field is log2(element_count) + 1 bits.
/// See `Baselines`.
fn fixup_tables(tables: &mut DataTablePacket) -> bool {
let mut changed = false;
for table in tables.tables.iter_mut() {
for prop in table.props.iter_mut() {
if let Some(size) = new_prop_size(table.name.as_str(), prop.name.as_str()) {
if prop.element_count != Some(size) {
prop.element_count = Some(size);
changed = true;
}
}
}
}
changed
}
/// Re-encodes instance baselines to match the tables `fixup_tables` rewrote.
///
/// Left stale, SourceTV demos decode team data wrongly and POV demos overflow
/// the baseline merge buffer.
struct Baselines {
old: Vec<SendTable>,
new: Vec<SendTable>,
/// Classes whose props encode differently. The rest are left untouched.
affected: HashSet<u32>,
}
impl Baselines {
fn new(header: &Header, tables: &DataTablePacket) -> Result<Option<Self>, ParseError> {
let mut fixed = tables.clone();
if !fixup_tables(&mut fixed) {
return Ok(None);
}
let mut before = DemoHandler::default();
before.handle_header(header);
before.handle_packet(Packet::DataTables(tables.clone()))?;
let mut after = DemoHandler::default();
after.handle_header(header);
after.handle_packet(Packet::DataTables(fixed))?;
let old = before.state_handler.send_tables.clone();
let new = after.state_handler.send_tables.clone();
let affected = old
.iter()
.zip(new.iter())
.enumerate()
.filter(|(_, (o, n))| o.flattened_props != n.flattened_props)
.map(|(id, _)| id as u32)
.collect();
Ok(Some(Baselines { old, new, affected }))
}
/// `table_names` is the handler's `string_table_names`, which maps the id
/// an UpdateStringTable carries back to the table it belongs to.
fn rewrite_message(&mut self, message: &mut Message, string_tables: &[String]) {
match message {
Message::CreateStringTable(create) => self.rewrite_table(&mut create.table),
Message::UpdateStringTable(update) => {
let name = string_tables
.get(update.table_id as usize)
.map(String::as_str);
if name == Some("instancebaseline") {
for (_, entry) in update.entries.iter_mut() {
self.rewrite_entry(entry);
}
}
}
_ => {}
}
}
fn rewrite_table(&mut self, table: &mut StringTable) {
if table.name.as_ref() != "instancebaseline" {
return;
}
for (_, entry) in table.entries.iter_mut() {
self.rewrite_entry(entry);
}
}
fn rewrite_entry(&mut self, entry: &mut StringTableEntry) {
let Some(class_id) = entry
.text
.as_ref()
.and_then(|t| t.trim().parse::<u32>().ok())
else {
return;
};
if !self.affected.contains(&class_id) {
return;
}
let (Some(from), Some(to), Some(extra)) = (
self.old.get(class_id as usize),
self.new.get(class_id as usize),
entry.extra_data.as_ref(),
) else {
return;
};
let baseline = StaticBaseline {
class_id: (class_id as u16).into(),
raw: extra.data.to_owned(),
};
// A blob we cannot read is better left alone than half written.
if let Ok(raw) = baseline.reencode(from, to) {
entry.extra_data = Some(ExtraData::new(raw));
}
}
} }