fmt + CL_PreserveExistingEntity wip fix

This commit is contained in:
Robin Appelman 2025-05-02 19:25:18 +02:00
commit e95a9e4a2c
11 changed files with 139 additions and 49 deletions

View file

@ -1,6 +1,7 @@
use tf_demo_parser::demo::message::Message; use crate::missing_preserve::RemoveInvalidPreserveEntity;
use tf_demo_parser::demo::message::usermessage::UserMessageType;
use crate::mutate::MutatorList; use crate::mutate::MutatorList;
use tf_demo_parser::demo::message::usermessage::UserMessageType;
use tf_demo_parser::demo::message::Message;
/// General cleanup we always want to do /// General cleanup we always want to do
pub fn clean_demo(mutators: &mut MutatorList) { pub fn clean_demo(mutators: &mut MutatorList) {
@ -11,4 +12,5 @@ pub fn clean_demo(mutators: &mut MutatorList) {
true true
} }
}); });
mutators.push_message_mutator(RemoveInvalidPreserveEntity::new());
} }

View file

@ -1,9 +1,9 @@
use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::message::packetentities::{EntityId, PacketEntity};
use tf_demo_parser::demo::sendprop::{SendPropIdentifier, SendPropValue};
use tf_demo_parser::ParserState;
use crate::mutate::MessageMutator; use crate::mutate::MessageMutator;
use crate::MutatorList; use crate::MutatorList;
use tf_demo_parser::demo::message::packetentities::{EntityId, PacketEntity};
use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::sendprop::{SendPropIdentifier, SendPropValue};
use tf_demo_parser::ParserState;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]

View file

@ -1,13 +1,11 @@
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use tf_demo_parser::demo::data::{DemoTick, ServerTick}; use tf_demo_parser::demo::data::{DemoTick, ServerTick};
use tf_demo_parser::demo::message::packetentities::{ use tf_demo_parser::demo::message::packetentities::{
EntityId, PacketEntitiesMessage, PacketEntity, UpdateType, EntityId, PacketEntitiesMessage, PacketEntity, UpdateType,
}; };
use tf_demo_parser::demo::packet::datatable::ClassId; use tf_demo_parser::demo::packet::datatable::ClassId;
use tf_demo_parser::demo::sendprop::{SendPropIdentifier}; use tf_demo_parser::demo::sendprop::SendPropIdentifier;
use tf_demo_parser::ParserState; use tf_demo_parser::ParserState;
#[derive(Default)] #[derive(Default)]
@ -20,7 +18,12 @@ pub struct ActiveEntities {
} }
impl ActiveEntities { impl ActiveEntities {
pub fn handle_message(&mut self, msg: &PacketEntitiesMessage, state: &ParserState, tick: DemoTick) { pub fn handle_message(
&mut self,
msg: &PacketEntitiesMessage,
state: &ParserState,
tick: DemoTick,
) {
self.max_entities = self.max_entities.max(msg.max_entries); self.max_entities = self.max_entities.max(msg.max_entries);
for entity in &msg.entities { for entity in &msg.entities {
self.removed_entities.remove(&entity.entity_index); self.removed_entities.remove(&entity.entity_index);
@ -160,7 +163,7 @@ impl ActiveEntities {
}, },
serial_number: 0, serial_number: 0,
delay: None, delay: None,
delta: None delta: None,
})) }))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
self.max_entities, self.max_entities,

View file

@ -2,7 +2,7 @@ mod entity;
mod string_tables; mod string_tables;
use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian}; use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
use std::cmp::{min}; use std::cmp::min;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::convert::TryInto; use std::convert::TryInto;
use std::iter::once; use std::iter::once;
@ -10,19 +10,18 @@ use std::mem::take;
use tf_demo_parser::demo::header::Header; use tf_demo_parser::demo::header::Header;
use tf_demo_parser::demo::message::packetentities::{EntityId, PacketEntitiesMessage, UpdateType}; use tf_demo_parser::demo::message::packetentities::{EntityId, PacketEntitiesMessage, UpdateType};
use tf_demo_parser::demo::data::{DemoTick, ServerTick};
use tf_demo_parser::demo::message::{Message, NetTickMessage}; use tf_demo_parser::demo::message::{Message, NetTickMessage};
use tf_demo_parser::demo::packet::message::{MessagePacket}; use tf_demo_parser::demo::packet::message::MessagePacket;
use tf_demo_parser::demo::packet::stop::StopPacket; use tf_demo_parser::demo::packet::stop::StopPacket;
use tf_demo_parser::demo::packet::{Packet, PacketType}; use tf_demo_parser::demo::packet::{Packet, PacketType};
use tf_demo_parser::demo::parser::{DemoHandler, Encode, NullHandler, RawPacketStream}; use tf_demo_parser::demo::parser::{DemoHandler, Encode, NullHandler, RawPacketStream};
use tf_demo_parser::{Demo, ParserState}; use tf_demo_parser::{Demo, ParserState};
use tf_demo_parser::demo::data::{DemoTick, ServerTick};
use crate::cut::entity::ActiveEntities; use crate::cut::entity::ActiveEntities;
use crate::cut::string_tables::StringTablesUpdates; use crate::cut::string_tables::StringTablesUpdates;
use crate::mutate::MessageMutator; use crate::mutate::MessageMutator;
use crate::{EditOptions, find_stv, PacketMutator}; use crate::{find_stv, EditOptions, PacketMutator};
const PRESERVE_PACKETS: &[PacketType] = &[ const PRESERVE_PACKETS: &[PacketType] = &[
PacketType::Signon, PacketType::Signon,
@ -78,10 +77,12 @@ pub fn cut(input: &[u8], options: EditOptions) -> Vec<u8> {
.encode() .encode()
.into_iter() .into_iter()
.map(|msg| Message::UpdateStringTable(msg)); .map(|msg| Message::UpdateStringTable(msg));
let (baseline_updates, entity_update, removed_update) = let (baseline_updates, entity_update, removed_update) = start_state.entities.encode(
start_state &start_handler.state_handler,
.entities delta_tick - 2,
.encode(&start_handler.state_handler, delta_tick - 2, start_tick, &start_handler.state_handler); start_tick,
&start_handler.state_handler,
);
let baseline_updates = baseline_updates.into_iter().map(Message::PacketEntities); let baseline_updates = baseline_updates.into_iter().map(Message::PacketEntities);
let start_packets = string_table_updates let start_packets = string_table_updates
.chain(baseline_updates) .chain(baseline_updates)
@ -105,7 +106,8 @@ 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).range_inclusive(start_state.server_tick) let fill_ticks = (delta_tick + 1)
.range_inclusive(start_state.server_tick)
.into_iter() .into_iter()
.map(|tick| net_tick(tick)); .map(|tick| net_tick(tick));
let fill_packets = fill_ticks.map(|msg| { let fill_packets = fill_ticks.map(|msg| {

View file

@ -1,5 +1,5 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use tf_demo_parser::demo::message::stringtable::{UpdateStringTableMessage}; use tf_demo_parser::demo::message::stringtable::UpdateStringTableMessage;
use tf_demo_parser::demo::message::Message; use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::packet::stringtable::StringTableEntry; use tf_demo_parser::demo::packet::stringtable::StringTableEntry;

View file

@ -20,7 +20,10 @@ impl Args {
EditOptions { EditOptions {
unlock_pov: self.unlock_pov, unlock_pov: self.unlock_pov,
cut: if let (Some(from), Some(to)) = (self.from, self.to) { cut: if let (Some(from), Some(to)) = (self.from, self.to) {
Some(TickRange { from: from.into(), to: to.into() }) Some(TickRange {
from: from.into(),
to: to.into(),
})
} else { } else {
None None
}, },
@ -30,6 +33,7 @@ impl Args {
} }
fn main() { fn main() {
env_logger::init();
let args: Args = Args::parse(); let args: Args = Args::parse();
let options = args.get_options(); let options = args.get_options();
let file = fs::read(&args.path).unwrap(); let file = fs::read(&args.path).unwrap();

View file

@ -1,17 +1,18 @@
mod mutate;
mod pov;
mod clean; mod clean;
mod cond; mod cond;
mod cut; mod cut;
pub mod missing_preserve;
mod mutate;
mod options; mod options;
mod pov;
use wasm_bindgen::prelude::*;
use tf_demo_parser::{Demo, DemoParser};
use tf_demo_parser::demo::header::Header;
use tf_demo_parser::demo::parser::{RawPacketStream, DemoHandler, Encode};
use tf_demo_parser::demo::packet::PacketType;
use bitbuffer::{BitRead, BitWriteStream, LittleEndian}; use bitbuffer::{BitRead, BitWriteStream, LittleEndian};
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::packet::PacketType;
use tf_demo_parser::demo::parser::{DemoHandler, Encode, RawPacketStream};
use tf_demo_parser::{Demo, DemoParser};
use wasm_bindgen::prelude::*;
use bitbuffer::BitWrite; use bitbuffer::BitWrite;
use tf_demo_parser::demo::data::DemoTick; use tf_demo_parser::demo::data::DemoTick;
@ -20,7 +21,7 @@ use crate::clean::clean_demo;
use crate::cond::strip_cond; use crate::cond::strip_cond;
use crate::cut::cut; use crate::cut::cut;
use crate::mutate::{MutatorList, PacketMutator}; use crate::mutate::{MutatorList, PacketMutator};
pub use crate::options::{EditOptions, TickRange, CondOptions}; pub use crate::options::{CondOptions, EditOptions, TickRange};
use crate::pov::unlock_pov; use crate::pov::unlock_pov;
extern crate web_sys; extern crate web_sys;
@ -92,7 +93,9 @@ fn no_cut(input: &[u8], options: EditOptions) -> Vec<u8> {
while let Some(mut packet) = packets.next(&handler.state_handler).unwrap() { while let Some(mut packet) = packets.next(&handler.state_handler).unwrap() {
mutators.mutate_packet(&mut packet, &handler.state_handler); mutators.mutate_packet(&mut packet, &handler.state_handler);
if packet.packet_type() != PacketType::ConsoleCmd && packet.packet_type() != PacketType::UserCmd { if packet.packet_type() != PacketType::ConsoleCmd
&& packet.packet_type() != PacketType::UserCmd
{
packet packet
.encode(&mut out_stream, &handler.state_handler) .encode(&mut out_stream, &handler.state_handler)
.unwrap(); .unwrap();
@ -106,6 +109,8 @@ fn no_cut(input: &[u8], options: EditOptions) -> Vec<u8> {
fn find_stv(demo: &Demo) -> Option<EntityId> { fn find_stv(demo: &Demo) -> Option<EntityId> {
let parser = DemoParser::new(demo.get_stream()); let parser = DemoParser::new(demo.get_stream());
let (_, data) = parser.parse().expect("failed to parse demo"); let (_, data) = parser.parse().expect("failed to parse demo");
data.users.values().find(|user| user.steam_id == "BOT") data.users
.values()
.find(|user| user.steam_id == "BOT")
.map(|user| user.entity_id) .map(|user| user.entity_id)
} }

View file

@ -0,0 +1,71 @@
use crate::mutate::MessageMutator;
use log::{info, warn};
use std::cell::RefCell;
use std::collections::{BTreeMap, BTreeSet};
use tf_demo_parser::demo::message::packetentities::{EntityId, PacketEntity, UpdateType};
use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::packet::datatable::{ClassId, ServerClass};
use tf_demo_parser::ParserState;
#[derive(Default)]
pub struct RemoveInvalidPreserveEntity {
known_entities: RefCell<BTreeSet<EntityId>>,
deferred_delete: RefCell<Vec<EntityId>>,
}
impl RemoveInvalidPreserveEntity {
pub fn new() -> RemoveInvalidPreserveEntity {
RemoveInvalidPreserveEntity::default()
}
}
impl MessageMutator for RemoveInvalidPreserveEntity {
fn mutate_message(&self, message: &mut Message, _state: &ParserState) {
if let Message::PacketEntities(ent_message) = message {
let deferred_deletes = self.deferred_delete.take();
for entity in ent_message.entities.iter() {
match entity.update_type {
UpdateType::Enter => {
self.known_entities.borrow_mut().insert(entity.entity_index);
}
UpdateType::Preserve => {
if !self.known_entities.borrow().contains(&entity.entity_index) {
warn!("preserving missing entity {}", entity.entity_index);
}
}
UpdateType::Delete => {
self.known_entities.borrow_mut().remove(&entity.entity_index);
},
_ => {}
};
}
ent_message.removed_entities.retain(|id| {
if self.known_entities.borrow().contains(&id) {
// just not deleting makes the demo play, but with some ERROR entities
// having a Delete or Leave makes it crash further in the demo
// warn!("inserting delete for {}", id);
// only entity_index and update_type is used
// ent_message.entities.push(PacketEntity {
// entity_index: *id,
// server_class: 0.into(),
// props: vec![],
// in_pvs: false,
// update_type: UpdateType::Leave,
// serial_number: 0,
// delay: None,
// delta: None,
// baseline_index: 0,
// });
// self.deferred_delete.borrow_mut().push(*id);
false
} else {
true
}
});
ent_message.entities.sort_by(|a, b| a.entity_index.cmp(&b.entity_index));
ent_message.removed_entities.extend(deferred_deletes);
ent_message.removed_entities.sort();
}
}
}

View file

@ -27,7 +27,7 @@ impl<T: MessageMutator> PacketMutator for PacketMessageMutator<T> {
.messages .messages
.iter_mut() .iter_mut()
.for_each(|msg| self.mutator.mutate_message(msg, state)); .for_each(|msg| self.mutator.mutate_message(msg, state));
}, }
_ => {} _ => {}
} }
} }
@ -114,4 +114,3 @@ impl PacketMutator for MutatorList {
} }
} }
} }

View file

@ -1,7 +1,7 @@
use crate::{clean_demo, strip_cond, unlock_pov, MutatorList};
use serde::{Deserialize, Serialize};
use tf_demo_parser::demo::data::DemoTick; use tf_demo_parser::demo::data::DemoTick;
use tf_demo_parser::demo::message::packetentities::EntityId; use tf_demo_parser::demo::message::packetentities::EntityId;
use crate::{clean_demo, MutatorList, strip_cond, unlock_pov};
use serde::{Serialize, Deserialize};
use tf_demo_parser::demo::message::Message; use tf_demo_parser::demo::message::Message;
#[derive(Debug, Serialize, Deserialize, Default)] #[derive(Debug, Serialize, Deserialize, Default)]

View file

@ -1,10 +1,10 @@
use crate::mutate::{MessageMutator, MutatorList};
use std::cell::Cell; use std::cell::Cell;
use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::message::packetentities::{EntityId, PacketEntity, UpdateType}; use tf_demo_parser::demo::message::packetentities::{EntityId, PacketEntity, UpdateType};
use tf_demo_parser::demo::message::usermessage::{UserMessage}; use tf_demo_parser::demo::message::usermessage::UserMessage;
use tf_demo_parser::demo::message::Message;
use tf_demo_parser::demo::packet::Packet; use tf_demo_parser::demo::packet::Packet;
use tf_demo_parser::ParserState; use tf_demo_parser::ParserState;
use crate::mutate::{MessageMutator, MutatorList};
struct AddStvEntity { struct AddStvEntity {
added: Cell<bool>, added: Cell<bool>,
@ -25,7 +25,11 @@ impl MessageMutator for AddStvEntity {
if !self.added.get() { if !self.added.get() {
if let Message::PacketEntities(ent_message) = message { if let Message::PacketEntities(ent_message) = message {
if ent_message.base_line == 0 { if ent_message.base_line == 0 {
let player_entity = ent_message.entities.iter().find(|ent| ent.entity_index >= 1 && ent.entity_index < 255).expect("Failed to find a player entity"); let player_entity = ent_message
.entities
.iter()
.find(|ent| ent.entity_index >= 1 && ent.entity_index < 255)
.expect("Failed to find a player entity");
if player_entity.entity_index == self.entity_index { if player_entity.entity_index == self.entity_index {
// already stv? // already stv?
self.added.set(true); self.added.set(true);
@ -42,9 +46,11 @@ impl MessageMutator for AddStvEntity {
serial_number: 1234567, serial_number: 1234567,
delay: None, delay: None,
delta: None, delta: None,
baseline_index: 0 baseline_index: 0,
}); });
ent_message.entities.sort_by(|a, b| a.entity_index.cmp(&b.entity_index)); ent_message
.entities
.sort_by(|a, b| a.entity_index.cmp(&b.entity_index));
self.added.set(true); self.added.set(true);
} }
} }
@ -58,9 +64,7 @@ pub fn unlock_pov(mutators: &mut MutatorList, spectator_id: EntityId) {
info.player_slot = u32::from(spectator_id) as u8 - 1; info.player_slot = u32::from(spectator_id) as u8 - 1;
} }
}); });
mutators.push_message_filter(|message: &Message| { mutators.push_message_filter(|message: &Message| !matches!(message, Message::SetView(_)));
!matches!(message, Message::SetView(_))
});
mutators.push_message_filter(|message: &Message| { mutators.push_message_filter(|message: &Message| {
!matches!(message, Message::UserMessage(UserMessage::VGuiMenu(_))) !matches!(message, Message::UserMessage(UserMessage::VGuiMenu(_)))
}); });