This commit is contained in:
Robin Appelman 2021-07-28 22:49:00 +02:00
commit 1be42764ad
9 changed files with 7252 additions and 44 deletions

15
src/cut.rs Normal file
View file

@ -0,0 +1,15 @@
use democutter::cut;
use std::env;
use std::fs;
fn main() {
let args: Vec<_> = env::args().collect();
if args.len() < 2 {
println!("1 argument required");
return;
}
let path = args[1].clone();
let file = fs::read(path).unwrap();
let output = cut(&file, 30000, 50000);
fs::write("out.dem", output).unwrap();
}

70
src/entity.rs Normal file
View file

@ -0,0 +1,70 @@
use std::collections::HashMap;
use std::mem::replace;
use tf_demo_parser::demo::message::packetentities::{
EntityId, PacketEntitiesMessage, PacketEntity, PVS,
};
use tf_demo_parser::ParserState;
#[derive(Default)]
pub struct ActiveEntities {
entities: HashMap<EntityId, PacketEntity>,
}
impl ActiveEntities {
pub fn handle_message(&mut self, msg: &PacketEntitiesMessage, state: &ParserState) {
for entity in &msg.entities {
if entity.pvs == PVS::Delete || entity.pvs == PVS::Leave {
self.entities.remove(&entity.entity_index);
} else {
self.handle_entity(entity, state);
}
}
for deleted in msg.removed_entities.iter() {
self.entities.remove(deleted);
}
}
fn handle_entity(&mut self, entity: &PacketEntity, state: &ParserState) {
if entity.pvs == PVS::Enter {
self.entities.insert(entity.entity_index, entity.clone());
} else {
self.entities
.entry(entity.entity_index)
.and_modify(|existing| update_entity(existing, entity, state))
.or_insert_with(|| entity.clone());
}
}
pub fn encode(self) -> PacketEntitiesMessage {
let max_entries = self.entities.len() as u16;
let mut entities = self
.entities
.into_iter()
.map(|(_k, v)| v)
.collect::<Vec<_>>();
entities.sort_by(|a, b| a.entity_index.cmp(&b.entity_index));
PacketEntitiesMessage {
entities,
removed_entities: vec![],
max_entries,
delta: None,
base_line: 0,
updated_base_line: false,
}
}
}
fn update_entity(old: &mut PacketEntity, new: &PacketEntity, _state: &ParserState) {
for prop in &new.props {
match old
.props
.iter_mut()
.find(|existing| existing.index == prop.index)
{
Some(existing) => existing.value = prop.value.clone(),
None => old.props.push(prop.clone()),
}
}
}

View file

@ -1,19 +1,150 @@
#![allow(unused_imports)]
mod entity;
mod utils;
use crate::entity::ActiveEntities;
use crate::utils::set_panic_hook;
use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
use std::cmp::{max, min};
use tf_demo_parser::demo::header::Header;
use tf_demo_parser::demo::message::{Message, NetTickMessage};
use tf_demo_parser::demo::packet::message::{MessagePacket, MessagePacketMeta};
use tf_demo_parser::demo::packet::stop::StopPacket;
use tf_demo_parser::demo::packet::{Packet, PacketType};
use tf_demo_parser::demo::parser::{DemoHandler, Encode, NullHandler, RawPacketStream};
use tf_demo_parser::Demo;
use wasm_bindgen::prelude::*;
use web_sys::console;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
const PRESERVE_PACKETS: &[PacketType] = &[
PacketType::Sigon,
PacketType::DataTables,
PacketType::StringTables,
PacketType::SyncTick,
];
#[wasm_bindgen]
pub fn greet() {
alert("Hello, democutter!");
pub fn cut(input: &[u8], start_tick: u32, end_tick: u32) -> Vec<u8> {
set_panic_hook();
let mut out_buffer = Vec::with_capacity(input.len());
{
let mut out_stream = BitWriteStream::new(&mut out_buffer, LittleEndian);
let demo = Demo::new(&input);
let mut stream = demo.get_stream();
let mut header = Header::read(&mut stream).unwrap();
let start_tick = min(header.ticks - 10, start_tick);
let end_tick = min(header.ticks, end_tick);
let duration_per_tick = header.ticks as f32 / header.duration;
header.ticks = end_tick - start_tick;
header.duration = (end_tick - start_tick) as f32 * duration_per_tick;
header.write(&mut out_stream).unwrap();
let mut packets = RawPacketStream::new(stream.clone());
let mut start_handler = DemoHandler::default();
start_handler.handle_header(&header);
let mut handler = DemoHandler::default();
handler.handle_header(&header);
let (entities, start_packets, last_server_tick) =
skip_start(&mut start_handler, &mut packets, start_tick);
for packet in start_packets {
packet
.encode(&mut out_stream, &handler.state_handler)
.unwrap();
handler.handle_packet(packet).unwrap();
}
let msg = entities.encode();
let packet = Packet::Message(MessagePacket {
tick: 0,
messages: vec![
Message::NetTick(NetTickMessage {
tick: last_server_tick,
frame_time: 1881,
std_dev: 263,
}),
Message::PacketEntities(msg),
],
meta: MessagePacketMeta {
flags: 0,
view_angles: Default::default(),
sequence_in: 0,
sequence_out: 0,
},
});
packet
.encode(&mut out_stream, &handler.state_handler)
.unwrap();
handler.handle_packet(packet).unwrap();
while let Some(mut packet) = packets.next(&handler.state_handler).unwrap() {
let ty = packet.packet_type();
let original_tick = packet.tick();
packet.set_tick(max(original_tick, start_tick) - start_tick);
if ty != PacketType::ConsoleCmd {
packet
.encode(&mut out_stream, &handler.state_handler)
.unwrap();
}
handler.handle_packet(packet).unwrap();
if original_tick >= end_tick {
break;
}
}
PacketType::Stop.write(&mut out_stream).unwrap();
StopPacket { tick: end_tick }
.encode(&mut out_stream, &handler.state_handler)
.unwrap();
}
out_buffer
}
fn skip_start<'a>(
handler: &mut DemoHandler<'a, NullHandler>,
packets: &mut RawPacketStream<'a>,
start_tick: u32,
) -> (ActiveEntities, Vec<Packet<'a>>, u32) {
let mut entities = ActiveEntities::default();
let mut start_packets = Vec::with_capacity(6);
let mut server_tick = 0;
while let Some(packet) = packets.next(&handler.state_handler).unwrap() {
if PRESERVE_PACKETS.contains(&packet.packet_type()) {
start_packets.push(packet.clone());
handler.handle_packet(packet).unwrap();
} else {
if let Packet::Message(message_packet) = &packet {
for msg in &message_packet.messages {
match msg {
Message::PacketEntities(msg) => {
entities.handle_message(msg, &handler.state_handler);
}
Message::NetTick(NetTickMessage { tick, .. }) => {
server_tick = *tick;
}
_ => {}
}
}
}
let tick = packet.tick();
handler.handle_packet(packet).unwrap();
if tick >= start_tick {
break;
}
}
}
(entities, start_packets, server_tick)
}