mirror of
https://codeberg.org/demostf/edit.git
synced 2026-06-03 20:04:06 +02:00
start importing cut
This commit is contained in:
parent
96a8d88394
commit
9a103fa13b
6 changed files with 547 additions and 5 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
name = "edit"
|
name = "edit"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
@ -16,8 +16,8 @@ default = ["console_error_panic_hook"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitbuffer = "0.10.5"
|
bitbuffer = "0.10.5"
|
||||||
tf-demo-parser = { version = "0.4", git = "https://github.com/demostf/parser" }
|
#tf-demo-parser = { version = "0.4", git = "https://github.com/demostf/parser" }
|
||||||
#tf-demo-parser = { version = "0.4", path = "../tf-demo-parser" }
|
tf-demo-parser = { version = "0.4", path = "../tf-demo-parser" }
|
||||||
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
wasm-bindgen = { version = "0.2", features = ["serde-serialize"] }
|
||||||
web-sys = { version = "0.3", features = ["console"] }
|
web-sys = { version = "0.3", features = ["console"] }
|
||||||
|
|
||||||
|
|
|
||||||
21
src/cut/cut.rs
Normal file
21
src/cut/cut.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
use clap::Parser;
|
||||||
|
use democutter::cut;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[clap(author, version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// Path to the source demo
|
||||||
|
path: String,
|
||||||
|
/// Start tick
|
||||||
|
start: u32,
|
||||||
|
/// End tick
|
||||||
|
end: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
let file = fs::read(&args.path).unwrap();
|
||||||
|
let output = cut(&file, args.start, args.end.unwrap_or(u32::MAX));
|
||||||
|
fs::write("out.dem", output).unwrap();
|
||||||
|
}
|
||||||
203
src/cut/entity.rs
Normal file
203
src/cut/entity.rs
Normal file
|
|
@ -0,0 +1,203 @@
|
||||||
|
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||||
|
use std::iter::once;
|
||||||
|
use std::mem::{replace, take};
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
use tf_demo_parser::demo::data::{DemoTick, ServerTick};
|
||||||
|
use tf_demo_parser::demo::message::packetentities::{
|
||||||
|
EntityId, PacketEntitiesMessage, PacketEntity, UpdateType,
|
||||||
|
};
|
||||||
|
use tf_demo_parser::demo::packet::datatable::ClassId;
|
||||||
|
use tf_demo_parser::demo::sendprop::{SendPropIdentifier, SendPropValue};
|
||||||
|
use tf_demo_parser::ParserState;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ActiveEntities {
|
||||||
|
entities: BTreeMap<EntityId, PacketEntity>,
|
||||||
|
simulation_updates: BTreeMap<EntityId, DemoTick>,
|
||||||
|
max_entities: u16,
|
||||||
|
deleted_entities: BTreeSet<EntityId>,
|
||||||
|
removed_entities: BTreeSet<EntityId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveEntities {
|
||||||
|
pub fn handle_message(&mut self, msg: &PacketEntitiesMessage, state: &ParserState, tick: DemoTick) {
|
||||||
|
self.max_entities = self.max_entities.max(msg.max_entries);
|
||||||
|
for entity in &msg.entities {
|
||||||
|
self.removed_entities.remove(&entity.entity_index);
|
||||||
|
|
||||||
|
if entity.update_type == UpdateType::Delete {
|
||||||
|
self.deleted_entities.insert(entity.entity_index);
|
||||||
|
} else {
|
||||||
|
self.deleted_entities.remove(&entity.entity_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if entity.update_type == UpdateType::Delete || entity.update_type == UpdateType::Leave {
|
||||||
|
self.remove_entity(entity.entity_index);
|
||||||
|
} else {
|
||||||
|
self.handle_entity(entity, state, tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for deleted in msg.removed_entities.iter() {
|
||||||
|
self.remove_entity(*deleted);
|
||||||
|
self.removed_entities.insert(*deleted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_entity(&mut self, entity_index: EntityId) {
|
||||||
|
self.entities.remove(&entity_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_entity(&mut self, entity: &PacketEntity, state: &ParserState, tick: DemoTick) {
|
||||||
|
self.entities
|
||||||
|
.entry(entity.entity_index)
|
||||||
|
.and_modify(|existing| {
|
||||||
|
if existing.serial_number != entity.serial_number
|
||||||
|
&& existing.server_class != entity.server_class
|
||||||
|
{
|
||||||
|
*existing = entity.clone();
|
||||||
|
existing.apply_update(&entity.props);
|
||||||
|
} else {
|
||||||
|
debug_assert_eq!(
|
||||||
|
state.server_classes[usize::from(existing.server_class)],
|
||||||
|
state.server_classes[usize::from(entity.server_class)]
|
||||||
|
);
|
||||||
|
if existing.serial_number != entity.serial_number {
|
||||||
|
existing.serial_number = entity.serial_number;
|
||||||
|
existing.update_type = UpdateType::Enter;
|
||||||
|
}
|
||||||
|
existing.apply_update(&entity.props);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| entity.clone());
|
||||||
|
|
||||||
|
for prop in &entity.props {
|
||||||
|
if prop.identifier == SendPropIdentifier::new("DT_BaseEntity", "m_flSimulationTime") {
|
||||||
|
self.simulation_updates.insert(entity.entity_index, tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entity_ids(&self) -> BTreeSet<EntityId> {
|
||||||
|
self.entities.keys().copied().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn baseline_ids(&self, state: &ParserState) -> BTreeSet<EntityId> {
|
||||||
|
state.instance_baselines[0]
|
||||||
|
.keys()
|
||||||
|
.chain(state.instance_baselines[1].keys())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(
|
||||||
|
mut self,
|
||||||
|
state: &ParserState,
|
||||||
|
delta: ServerTick,
|
||||||
|
tick: u32,
|
||||||
|
parser_state: &ParserState,
|
||||||
|
) -> (
|
||||||
|
impl IntoIterator<Item = PacketEntitiesMessage>,
|
||||||
|
PacketEntitiesMessage,
|
||||||
|
PacketEntitiesMessage,
|
||||||
|
) {
|
||||||
|
// baselines in reverse order
|
||||||
|
let mut baselines = [
|
||||||
|
encode_entities(
|
||||||
|
state.instance_baselines[1]
|
||||||
|
.clone()
|
||||||
|
.into_values()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
self.max_entities,
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
),
|
||||||
|
encode_entities(
|
||||||
|
state.instance_baselines[0]
|
||||||
|
.clone()
|
||||||
|
.into_values()
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
self.max_entities,
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
for entity in self.entities.values_mut() {
|
||||||
|
match state.instance_baselines[0].get(entity.entity_index) {
|
||||||
|
Some(baseline_entity) if baseline_entity.server_class == entity.server_class => {
|
||||||
|
entity.update_type = UpdateType::Preserve;
|
||||||
|
}
|
||||||
|
Some(_baseline_entity) => {
|
||||||
|
// encode the baseline if the baseline server class differs
|
||||||
|
entity.props = entity.props(parser_state).collect();
|
||||||
|
entity.update_type = UpdateType::Enter;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
entity.update_type = UpdateType::Enter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create deletes for all entities that have an updated baseline but are since removed
|
||||||
|
let removed_entities = self
|
||||||
|
.baseline_ids(state)
|
||||||
|
.into_iter()
|
||||||
|
.filter(|id| !self.entities.contains_key(id))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter();
|
||||||
|
|
||||||
|
let entities = encode_entities(
|
||||||
|
self.entities
|
||||||
|
.into_values()
|
||||||
|
.chain(removed_entities.map(|removed| PacketEntity {
|
||||||
|
server_class: ClassId::from(0),
|
||||||
|
entity_index: removed,
|
||||||
|
props: vec![],
|
||||||
|
in_pvs: false,
|
||||||
|
baseline_index: 0,
|
||||||
|
update_type: if self.deleted_entities.contains(&removed) {
|
||||||
|
UpdateType::Delete
|
||||||
|
} else {
|
||||||
|
UpdateType::Leave
|
||||||
|
},
|
||||||
|
serial_number: 0,
|
||||||
|
delay: None,
|
||||||
|
delta: None
|
||||||
|
}))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
self.max_entities,
|
||||||
|
Some(delta),
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
baselines[0].updated_base_line = true;
|
||||||
|
baselines[1].updated_base_line = true;
|
||||||
|
baselines[0].base_line = 1;
|
||||||
|
|
||||||
|
(
|
||||||
|
baselines.into_iter(),
|
||||||
|
entities,
|
||||||
|
encode_entities(
|
||||||
|
Vec::new(),
|
||||||
|
self.max_entities,
|
||||||
|
Some(delta + 1),
|
||||||
|
self.removed_entities.into_iter().collect(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_entities(
|
||||||
|
mut entities: Vec<PacketEntity>,
|
||||||
|
max_entries: u16,
|
||||||
|
delta: Option<ServerTick>,
|
||||||
|
removed_entities: Vec<EntityId>,
|
||||||
|
) -> PacketEntitiesMessage {
|
||||||
|
entities.sort_by(|a, b| a.entity_index.cmp(&b.entity_index));
|
||||||
|
PacketEntitiesMessage {
|
||||||
|
entities,
|
||||||
|
removed_entities,
|
||||||
|
max_entries,
|
||||||
|
delta,
|
||||||
|
base_line: 0,
|
||||||
|
updated_base_line: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
268
src/cut/mod.rs
Normal file
268
src/cut/mod.rs
Normal file
|
|
@ -0,0 +1,268 @@
|
||||||
|
mod entity;
|
||||||
|
mod string_tables;
|
||||||
|
|
||||||
|
use bitbuffer::{BitRead, BitWrite, BitWriteStream, LittleEndian};
|
||||||
|
use std::cmp::{max, min};
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::iter::once;
|
||||||
|
use std::mem::take;
|
||||||
|
use tf_demo_parser::demo::header::Header;
|
||||||
|
use tf_demo_parser::demo::message::packetentities::{EntityId, PacketEntitiesMessage, UpdateType};
|
||||||
|
use tf_demo_parser::demo::message::usermessage::{UserMessage, UserMessageType};
|
||||||
|
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, DemoParser, MessageType, ParserState};
|
||||||
|
use tf_demo_parser::demo::data::ServerTick;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use web_sys::console;
|
||||||
|
use crate::cut::entity::ActiveEntities;
|
||||||
|
use crate::cut::string_tables::StringTablesUpdates;
|
||||||
|
use crate::mutate::MessageMutator;
|
||||||
|
use crate::{MutatorList, PacketMutator};
|
||||||
|
|
||||||
|
const PRESERVE_PACKETS: &[PacketType] = &[
|
||||||
|
PacketType::Signon,
|
||||||
|
PacketType::DataTables,
|
||||||
|
PacketType::StringTables,
|
||||||
|
PacketType::SyncTick,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn cut(input: &[u8], start_tick: u32, end_tick: u32) -> Vec<u8> {
|
||||||
|
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 start_state = skip_start(&mut start_handler, &mut packets, start_tick);
|
||||||
|
|
||||||
|
for packet in start_state.start_packets {
|
||||||
|
packet
|
||||||
|
.encode(&mut out_stream, &handler.state_handler)
|
||||||
|
.unwrap();
|
||||||
|
handler.handle_packet(packet).unwrap();
|
||||||
|
}
|
||||||
|
let delta_tick = start_state.last_delta;
|
||||||
|
|
||||||
|
let start_entities = start_state.entities.entity_ids();
|
||||||
|
|
||||||
|
let string_table_updates = start_state
|
||||||
|
.table_updates
|
||||||
|
.encode()
|
||||||
|
.into_iter()
|
||||||
|
.map(|msg| Message::UpdateStringTable(msg));
|
||||||
|
let (baseline_updates, entity_update, removed_update) =
|
||||||
|
start_state
|
||||||
|
.entities
|
||||||
|
.encode(&start_handler.state_handler, delta_tick - 2, start_tick, &start_handler.state_handler);
|
||||||
|
let baseline_updates = baseline_updates.into_iter().map(Message::PacketEntities);
|
||||||
|
let start_packets = string_table_updates
|
||||||
|
.chain(baseline_updates)
|
||||||
|
.map(|msg| msg_packet(vec![net_tick(delta_tick - 2), msg]))
|
||||||
|
.chain(once(msg_packet(vec![
|
||||||
|
net_tick(delta_tick - 1),
|
||||||
|
Message::PacketEntities(entity_update),
|
||||||
|
])))
|
||||||
|
.chain(once(Packet::Message(MessagePacket {
|
||||||
|
messages: vec![
|
||||||
|
net_tick(delta_tick),
|
||||||
|
Message::PacketEntities(removed_update),
|
||||||
|
],
|
||||||
|
..MessagePacket::default()
|
||||||
|
})));
|
||||||
|
for packet in start_packets {
|
||||||
|
packet
|
||||||
|
.encode(&mut out_stream, &handler.state_handler)
|
||||||
|
.unwrap();
|
||||||
|
handler.handle_packet(packet).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the net ticks needed for later deltas
|
||||||
|
let fill_ticks = (delta_tick + 1).range_inclusive(start_state.server_tick)
|
||||||
|
.into_iter()
|
||||||
|
.map(|tick| net_tick(tick));
|
||||||
|
let fill_packets = fill_ticks.map(|msg| {
|
||||||
|
Packet::Message(MessagePacket {
|
||||||
|
messages: vec![
|
||||||
|
msg,
|
||||||
|
Message::PacketEntities(PacketEntitiesMessage {
|
||||||
|
max_entries: start_state.entity_max,
|
||||||
|
delta: Some((delta_tick - 1).try_into().unwrap()),
|
||||||
|
..PacketEntitiesMessage::default()
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
..MessagePacket::default()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
for packet in fill_packets {
|
||||||
|
packet
|
||||||
|
.encode(&mut out_stream, &handler.state_handler)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mutators = MutatorList::new();
|
||||||
|
mutators.push_message_mutator(DeleteFilter::new(start_entities, start_state.server_tick));
|
||||||
|
mutators.push_packet_mutator(move |packet: &mut Packet| {
|
||||||
|
packet.set_tick(packet.tick() - start_tick)
|
||||||
|
});
|
||||||
|
|
||||||
|
while let Some(mut packet) = packets.next(&handler.state_handler).unwrap() {
|
||||||
|
let original_tick = packet.tick();
|
||||||
|
|
||||||
|
mutators.mutate_packet(&mut packet, &handler.state_handler);
|
||||||
|
|
||||||
|
if packet.packet_type() != 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 - start_tick).into(),
|
||||||
|
}
|
||||||
|
.encode(&mut out_stream, &handler.state_handler)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
out_buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StartState<'a> {
|
||||||
|
entities: ActiveEntities,
|
||||||
|
table_updates: StringTablesUpdates,
|
||||||
|
start_packets: Vec<Packet<'a>>,
|
||||||
|
server_tick: ServerTick,
|
||||||
|
entity_max: u16,
|
||||||
|
last_delta: ServerTick,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip_start<'a>(
|
||||||
|
handler: &mut DemoHandler<'a, NullHandler>,
|
||||||
|
packets: &mut RawPacketStream<'a>,
|
||||||
|
start_tick: u32,
|
||||||
|
) -> StartState<'a> {
|
||||||
|
let mut entities = ActiveEntities::default();
|
||||||
|
let mut table_updates = StringTablesUpdates::default();
|
||||||
|
let mut start_packets = Vec::with_capacity(6);
|
||||||
|
let mut server_tick = ServerTick::default();
|
||||||
|
let mut entity_max = 0;
|
||||||
|
let mut last_delta = ServerTick::default();
|
||||||
|
|
||||||
|
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 packet.packet_type() != PacketType::ConsoleCmd {
|
||||||
|
if let Packet::Message(message_packet) = &packet {
|
||||||
|
for msg in &message_packet.messages {
|
||||||
|
table_updates.handle_message(&msg);
|
||||||
|
match msg {
|
||||||
|
Message::PacketEntities(msg) => {
|
||||||
|
if let Some(delta) = msg.delta {
|
||||||
|
last_delta = delta;
|
||||||
|
}
|
||||||
|
entity_max = msg.max_entries;
|
||||||
|
entities.handle_message(msg, &handler.state_handler, packet.tick());
|
||||||
|
}
|
||||||
|
Message::NetTick(NetTickMessage { tick, .. }) => {
|
||||||
|
server_tick = *tick;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let tick = packet.tick();
|
||||||
|
handler.handle_packet(packet).unwrap();
|
||||||
|
|
||||||
|
if tick >= start_tick {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StartState {
|
||||||
|
entities,
|
||||||
|
table_updates,
|
||||||
|
start_packets,
|
||||||
|
server_tick,
|
||||||
|
entity_max,
|
||||||
|
last_delta,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DeleteFilter {
|
||||||
|
current_entities: BTreeSet<EntityId>,
|
||||||
|
till_delta: ServerTick,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeleteFilter {
|
||||||
|
pub fn new(current_entities: BTreeSet<EntityId>, till_delta: ServerTick) -> Self {
|
||||||
|
DeleteFilter {
|
||||||
|
current_entities,
|
||||||
|
till_delta,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageMutator for DeleteFilter {
|
||||||
|
fn mutate_message(&self, message: &mut Message, _state: &ParserState) {
|
||||||
|
if let Message::PacketEntities(message) = message {
|
||||||
|
if let Some(delta) = message.delta {
|
||||||
|
if delta < self.till_delta {
|
||||||
|
let packet_entities = take(&mut message.entities);
|
||||||
|
message.entities = packet_entities
|
||||||
|
.into_iter()
|
||||||
|
.filter(|ent| match ent.update_type {
|
||||||
|
UpdateType::Delete | UpdateType::Leave => {
|
||||||
|
self.current_entities.contains(&ent.entity_index)
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn msg_packet(messages: Vec<Message>) -> Packet {
|
||||||
|
Packet::Message(MessagePacket {
|
||||||
|
messages,
|
||||||
|
..MessagePacket::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn net_tick(tick: ServerTick) -> Message<'static> {
|
||||||
|
Message::NetTick(NetTickMessage {
|
||||||
|
tick,
|
||||||
|
frame_time: 1881,
|
||||||
|
std_dev: 263,
|
||||||
|
})
|
||||||
|
}
|
||||||
37
src/cut/string_tables.rs
Normal file
37
src/cut/string_tables.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use tf_demo_parser::demo::message::stringtable::{StringTableMeta, UpdateStringTableMessage};
|
||||||
|
use tf_demo_parser::demo::message::Message;
|
||||||
|
use tf_demo_parser::demo::packet::stringtable::StringTableEntry;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct StringTable {
|
||||||
|
entries: BTreeMap<u16, StringTableEntry<'static>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct StringTablesUpdates {
|
||||||
|
pub tables: BTreeMap<u8, StringTable>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringTablesUpdates {
|
||||||
|
pub fn handle_message(&mut self, message: &Message) {
|
||||||
|
match message {
|
||||||
|
Message::UpdateStringTable(msg) => {
|
||||||
|
let table = self.tables.entry(msg.table_id).or_default();
|
||||||
|
for (id, entry) in &msg.entries {
|
||||||
|
table.entries.insert(*id, entry.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(self) -> impl IntoIterator<Item = UpdateStringTableMessage<'static>> {
|
||||||
|
self.tables
|
||||||
|
.into_iter()
|
||||||
|
.map(|(table_id, table)| UpdateStringTableMessage {
|
||||||
|
entries: table.entries.into_iter().collect(),
|
||||||
|
table_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/lib.rs
17
src/lib.rs
|
|
@ -2,6 +2,7 @@ mod mutate;
|
||||||
mod pov;
|
mod pov;
|
||||||
mod clean;
|
mod clean;
|
||||||
mod cond;
|
mod cond;
|
||||||
|
mod cut;
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use tf_demo_parser::{Demo, DemoParser};
|
use tf_demo_parser::{Demo, DemoParser};
|
||||||
|
|
@ -12,6 +13,7 @@ use bitbuffer::{BitRead, BitWriteStream, LittleEndian};
|
||||||
use tf_demo_parser::demo::message::packetentities::EntityId;
|
use tf_demo_parser::demo::message::packetentities::EntityId;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use bitbuffer::BitWrite;
|
use bitbuffer::BitWrite;
|
||||||
|
use tf_demo_parser::demo::data::DemoTick;
|
||||||
use crate::clean::clean_demo;
|
use crate::clean::clean_demo;
|
||||||
use crate::cond::strip_cond;
|
use crate::cond::strip_cond;
|
||||||
use crate::mutate::{MutatorList, PacketMutator};
|
use crate::mutate::{MutatorList, PacketMutator};
|
||||||
|
|
@ -40,14 +42,22 @@ fn set_panic_hook() {
|
||||||
pub struct EditOptions {
|
pub struct EditOptions {
|
||||||
pub unlock_pov: bool,
|
pub unlock_pov: bool,
|
||||||
pub remove_conditions: Vec<CondOptions>,
|
pub remove_conditions: Vec<CondOptions>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub cut: Option<TickRange>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct CondOptions {
|
pub struct CondOptions {
|
||||||
entity: u32,
|
entity: EntityId,
|
||||||
mask: u32,
|
mask: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
|
pub struct TickRange {
|
||||||
|
from: DemoTick,
|
||||||
|
to: DemoTick,
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn edit(input: &[u8], options: JsValue) -> Vec<u8> {
|
pub fn edit(input: &[u8], options: JsValue) -> Vec<u8> {
|
||||||
set_panic_hook();
|
set_panic_hook();
|
||||||
|
|
@ -62,7 +72,6 @@ pub fn rust_edit(input: &[u8], options: EditOptions) -> Vec<u8> {
|
||||||
|
|
||||||
let demo = Demo::new(&input);
|
let demo = Demo::new(&input);
|
||||||
let spectator_id = find_stv(&demo).expect("no stv bot found");
|
let spectator_id = find_stv(&demo).expect("no stv bot found");
|
||||||
dbg!(spectator_id);
|
|
||||||
|
|
||||||
let mut stream = demo.get_stream();
|
let mut stream = demo.get_stream();
|
||||||
let header = Header::read(&mut stream).unwrap();
|
let header = Header::read(&mut stream).unwrap();
|
||||||
|
|
@ -88,6 +97,10 @@ pub fn rust_edit(input: &[u8], options: EditOptions) -> Vec<u8> {
|
||||||
unlock_pov(&mut mutators, spectator_id);
|
unlock_pov(&mut mutators, spectator_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(cut) = options.cut {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue