mirror of
https://codeberg.org/demostf/edit.git
synced 2026-10-01 07:34:05 +02:00
player count fix wip
This commit is contained in:
parent
405c286b42
commit
04790ad6f4
9 changed files with 403 additions and 335 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -8,4 +8,5 @@ out.dem
|
||||||
.direnv
|
.direnv
|
||||||
result
|
result
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
|
*.txt
|
||||||
595
wasm/Cargo.lock
generated
595
wasm/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -17,9 +17,9 @@ default = ["console_error_panic_hook"]
|
||||||
cli = ["clap", "env_logger", "typed-builder"]
|
cli = ["clap", "env_logger", "typed-builder"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitbuffer = "0.11.1"
|
bitbuffer = "0.11.3"
|
||||||
tf-demo-parser = { version = "0.6" }
|
#tf-demo-parser = { version = "0.6" }
|
||||||
#tf-demo-parser = { version = "0.4", path = "../tf-demo-parser" }
|
tf-demo-parser = { version = "0.7", path = "../../tf-demo-parser", features = ["write"] }
|
||||||
wasm-bindgen = { version = "0.2.45" }
|
wasm-bindgen = { version = "0.2.45" }
|
||||||
serde-wasm-bindgen = "0.6.5"
|
serde-wasm-bindgen = "0.6.5"
|
||||||
web-sys = { version = "0.3", features = ["console"] }
|
web-sys = { version = "0.3", features = ["console"] }
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ impl ActiveEntities {
|
||||||
for entity in self.entities.values_mut() {
|
for entity in self.entities.values_mut() {
|
||||||
match state.instance_baselines[0].get(entity.entity_index) {
|
match state.instance_baselines[0].get(entity.entity_index) {
|
||||||
Some(baseline_entity) if baseline_entity.server_class == entity.server_class => {
|
Some(baseline_entity) if baseline_entity.server_class == entity.server_class => {
|
||||||
entity.update_type = UpdateType::Preserve;
|
entity.update_type = UpdateType::Delta;
|
||||||
}
|
}
|
||||||
Some(_baseline_entity) => {
|
Some(_baseline_entity) => {
|
||||||
// encode the baseline if the baseline server class differs
|
// encode the baseline if the baseline server class differs
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,16 @@ struct Args {
|
||||||
to: Option<u32>,
|
to: Option<u32>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
remove_invalid_deletes: bool,
|
remove_invalid_deletes: bool,
|
||||||
|
#[arg(long)]
|
||||||
|
fix_player_count: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
fn get_options(&self) -> EditOptions {
|
fn get_options(&self) -> EditOptions {
|
||||||
let builder = EditOptions::builder()
|
let builder = EditOptions::builder()
|
||||||
.unlock_pov(self.unlock_pov)
|
.unlock_pov(self.unlock_pov)
|
||||||
.remove_invalid_deletes(self.remove_invalid_deletes);
|
.remove_invalid_deletes(self.remove_invalid_deletes)
|
||||||
|
.fix_player_count(self.fix_player_count);
|
||||||
if let (Some(from), Some(to)) = (self.from, self.to) {
|
if let (Some(from), Some(to)) = (self.from, self.to) {
|
||||||
builder.cut(TickRange {
|
builder.cut(TickRange {
|
||||||
from: from.into(),
|
from: from.into(),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ pub mod missing_preserve;
|
||||||
mod mutate;
|
mod mutate;
|
||||||
mod options;
|
mod options;
|
||||||
mod pov;
|
mod pov;
|
||||||
|
mod player_count;
|
||||||
|
|
||||||
use bitbuffer::{BitRead, BitWriteStream, LittleEndian};
|
use bitbuffer::{BitRead, BitWriteStream, LittleEndian};
|
||||||
use tf_demo_parser::demo::header::Header;
|
use tf_demo_parser::demo::header::Header;
|
||||||
|
|
@ -88,19 +89,23 @@ fn no_cut(input: &[u8], options: EditOptions) -> Vec<u8> {
|
||||||
let mut handler = DemoHandler::default();
|
let mut handler = DemoHandler::default();
|
||||||
handler.handle_header(&header);
|
handler.handle_header(&header);
|
||||||
|
|
||||||
|
let mut output_handler = DemoHandler::default();
|
||||||
|
output_handler.handle_header(&header);
|
||||||
|
|
||||||
let mutators = options.as_mutator(spectator_id);
|
let mutators = options.as_mutator(spectator_id);
|
||||||
|
|
||||||
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");
|
||||||
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
|
||||||
{
|
{
|
||||||
packet
|
packet
|
||||||
.encode(&mut out_stream, &handler.state_handler)
|
.encode(&mut out_stream, &output_handler.state_handler)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
handler.handle_packet(packet).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out_buffer
|
out_buffer
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ impl MessageMutator for RemoveInvalidPreserveEntity {
|
||||||
UpdateType::Enter => {
|
UpdateType::Enter => {
|
||||||
self.known_entities.borrow_mut().insert(entity.entity_index);
|
self.known_entities.borrow_mut().insert(entity.entity_index);
|
||||||
}
|
}
|
||||||
UpdateType::Preserve => {
|
UpdateType::Delta => {
|
||||||
if !self.known_entities.borrow().contains(&entity.entity_index) {
|
if !self.known_entities.borrow().contains(&entity.entity_index) {
|
||||||
warn!("preserving missing entity {}", entity.entity_index);
|
warn!("preserving missing entity {}", entity.entity_index);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ 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 tf_demo_parser::demo::message::Message;
|
use tf_demo_parser::demo::message::Message;
|
||||||
|
use crate::player_count::fix_player_count;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
|
@ -20,6 +21,9 @@ pub struct EditOptions {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[cfg_attr(feature = "cli", builder(default))]
|
#[cfg_attr(feature = "cli", builder(default))]
|
||||||
pub remove_invalid_deletes: bool,
|
pub remove_invalid_deletes: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
#[cfg_attr(feature = "cli", builder(default))]
|
||||||
|
pub fix_player_count: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditOptions {
|
impl EditOptions {
|
||||||
|
|
@ -51,6 +55,10 @@ impl EditOptions {
|
||||||
removed_invalid_deletes(&mut mutators);
|
removed_invalid_deletes(&mut mutators);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.fix_player_count {
|
||||||
|
fix_player_count(&mut mutators);
|
||||||
|
}
|
||||||
|
|
||||||
mutators
|
mutators
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
120
wasm/src/player_count.rs
Normal file
120
wasm/src/player_count.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
use crate::mutate::{MutatorList, PacketMutator};
|
||||||
|
use tf_demo_parser::demo::packet::Packet;
|
||||||
|
use tf_demo_parser::ParserState;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PlayerCount {}
|
||||||
|
|
||||||
|
/// property arrays that got renamed and new items
|
||||||
|
const PLAYER_ARRAY_PROPS_RENAMED: &[(&str, u16, u16)] = &[
|
||||||
|
("m_aBonusPlayerRoll_33", 33, 101),
|
||||||
|
("m_ConditionData_128", 128, 131),
|
||||||
|
];
|
||||||
|
|
||||||
|
/// property arrays that just got new items
|
||||||
|
const PLAYER_ARRAY_PROPS: &[(&str, u16)] = &[
|
||||||
|
("m_iPing", 101),
|
||||||
|
("m_iScore", 101),
|
||||||
|
("m_iDeaths", 101),
|
||||||
|
("m_bConnected", 101),
|
||||||
|
("m_iTeam", 101),
|
||||||
|
("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
|
||||||
|
const PLAYER_PROPS_VALUES: &[(&str, &str, u16, u16)] = &[
|
||||||
|
("DT_ObjectDispenser", r#""healing_array""#, 33, 101),
|
||||||
|
("DT_Team", r#""player_array""#, 33, 101),
|
||||||
|
("DT_TFTeam", r#""team_object_array""#, 198, 606),
|
||||||
|
];
|
||||||
|
|
||||||
|
impl PacketMutator for PlayerCount {
|
||||||
|
fn mutate_packet(&self, packet: &mut Packet, _state: &ParserState) {
|
||||||
|
if let Packet::DataTables(data_tables) = packet {
|
||||||
|
for table in &mut data_tables.tables {
|
||||||
|
let table_name = table.name.as_str();
|
||||||
|
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()
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fix_player_count(mutators: &mut MutatorList) {
|
||||||
|
mutators.push_packet_mutator(PlayerCount::default())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue