This commit is contained in:
Lauri Räsänen 2020-09-26 13:29:30 +03:00
parent 28cf51a6b6
commit 270fce7f9e
9 changed files with 385 additions and 356 deletions

1
.prettierignore Normal file
View file

@ -0,0 +1 @@
build/

20
.prettierrc Normal file
View file

@ -0,0 +1,20 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"cursorOffset": -1,
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxBracketSameLine": false,
"jsxSingleQuote": false,
"printWidth": 140,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false
}

View file

@ -1,9 +1,11 @@
## demo-interp ## demo-interp
An experiemental project for interpolating TF2 SourceTV demo files. An experiemental project for interpolating TF2 SourceTV demo files.
Normally a SourceTV demo has a Packet every 4 ticks, this fills out the ticks inbetween by interpolating between the Packets. Normally a SourceTV demo has a Packet every 4 ticks, this fills out the ticks inbetween by interpolating between the Packets.
This may be pointless but there is a visible difference when using `cl_interp` values lower than ~60ms (4 / 66 ticks/s). This may be pointless but there is a visible difference when using `cl_interp` values lower than ~60ms (4 / 66 ticks/s).
## usage ## usage
`yarn install` `yarn install`
`yarn build` `yarn build`
`cd test` `cd test`

View file

@ -12,7 +12,7 @@ function lerp(start, end, amount) {
} }
function circleLerp(start, end, amount) { function circleLerp(start, end, amount) {
var shortestAngle = ((((end - start) % 360) + 540) % 360) - 180; var shortestAngle = ((((end - start) % 360) + 540) % 360) - 180;
return start + (shortestAngle * amount) % 360; return start + ((shortestAngle * amount) % 360);
} }
function interpNumber(start, end, amount, lowValue, highValue) { function interpNumber(start, end, amount, lowValue, highValue) {
if (lowValue === 0 && highValue === 360) { if (lowValue === 0 && highValue === 360) {
@ -127,10 +127,7 @@ function interpEntity(start, end, amount) {
return start; return start;
} }
function incrementEntityTicks(entity, amount) { function incrementEntityTicks(entity, amount) {
var names = [ var names = ["m_nTickBase", "m_flSimulationTime"];
"m_nTickBase",
"m_flSimulationTime"
];
for (let prop of entity.props) { for (let prop of entity.props) {
if (names.includes(prop.definition.name)) { if (names.includes(prop.definition.name)) {
prop.value = prop.value + amount; prop.value = prop.value + amount;
@ -194,7 +191,7 @@ class InterpTransformer extends Parser_1.Parser {
for (let e of p.entities) { for (let e of p.entities) {
if (p.removedEntities.includes(e.entityIndex)) { if (p.removedEntities.includes(e.entityIndex)) {
//console.log("packet includes removed ent"); //console.log("packet includes removed ent");
delete (lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`]); delete lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`];
} }
// Store from entity // Store from entity
for (const prop of e.props) { for (const prop of e.props) {
@ -233,7 +230,7 @@ class InterpTransformer extends Parser_1.Parser {
for (const index of newPacket.removedEntities) { for (const index of newPacket.removedEntities) {
for (let propIndex = Object.keys(lastKnownProps).length - 1; propIndex > 0; propIndex--) { for (let propIndex = Object.keys(lastKnownProps).length - 1; propIndex > 0; propIndex--) {
if (Object.keys(lastKnownProps)[propIndex].startsWith(index.toString() + ":")) { if (Object.keys(lastKnownProps)[propIndex].startsWith(index.toString() + ":")) {
delete (lastKnownProps[Object.keys(lastKnownProps)[propIndex]]); delete lastKnownProps[Object.keys(lastKnownProps)[propIndex]];
} }
} }
} }
@ -318,7 +315,7 @@ class InterpTransformer extends Parser_1.Parser {
// 0 0.25 1.0 0.25 // 0 0.25 1.0 0.25
// 0.25 0.5 1.0 1/3 // 0.25 0.5 1.0 1/3
// 0.5 0.75 1.0 0.5 // 0.5 0.75 1.0 0.5
var interp = 0.25 * (1 / (1 - (0.25 * i))); var interp = 0.25 * (1 / (1 - 0.25 * i));
entity = interpEntity(entity, newEntity, interp); entity = interpEntity(entity, newEntity, interp);
} }
} }

File diff suppressed because one or more lines are too long

View file

@ -11,6 +11,7 @@
}, },
"scripts": { "scripts": {
"postinstall": "patch-package", "postinstall": "patch-package",
"build": "tsc" "build": "tsc",
"format": "prettier --write ."
} }
} }

View file

@ -1,398 +1,406 @@
import { BitStream } from 'bit-buffer'; import { BitStream } from "bit-buffer";
import { readFileSync, writeFileSync } from 'fs'; import { readFileSync, writeFileSync } from "fs";
import { DynamicBitStream } from '@demostf/demo.js/build/DynamicBitStream'; import { DynamicBitStream } from "@demostf/demo.js/build/DynamicBitStream";
import { Message, MessageType, PacketMessage } from '@demostf/demo.js/build/Data/Message'; import { Message, MessageType, PacketMessage } from "@demostf/demo.js/build/Data/Message";
import { Parser } from '@demostf/demo.js/build/Parser'; import { Parser } from "@demostf/demo.js/build/Parser";
import { Encoder } from '@demostf/demo.js/build/Encoder'; import { Encoder } from "@demostf/demo.js/build/Encoder";
import { PacketEntity } from '@demostf/demo.js/build/Data/PacketEntity'; import { PacketEntity } from "@demostf/demo.js/build/Data/PacketEntity";
function lerp(start: number, end: number, amount: number) { function lerp(start: number, end: number, amount: number) {
return start + (end - start) * amount; return start + (end - start) * amount;
} }
function circleLerp(start: number, end: number, amount: number) { function circleLerp(start: number, end: number, amount: number) {
var shortestAngle = ((((end - start) % 360) + 540) % 360) - 180; var shortestAngle = ((((end - start) % 360) + 540) % 360) - 180;
return start + (shortestAngle * amount) % 360; return start + ((shortestAngle * amount) % 360);
} }
function interpNumber(start: number, end: number, amount: number, lowValue: number, highValue: number) { function interpNumber(start: number, end: number, amount: number, lowValue: number, highValue: number) {
if (lowValue === 0 && highValue === 360) { if (lowValue === 0 && highValue === 360) {
// angles clamped to 0-360 // angles clamped to 0-360
start = circleLerp(start, end, amount); start = circleLerp(start, end, amount);
} else { } else {
start = lerp(start, end, amount); start = lerp(start, end, amount);
} }
return start; return start;
} }
// Props to interpolate, // Props to interpolate,
// most of these are probably not needed // most of these are probably not needed
const propNames = [ const propNames = [
// These are in a bunch of tables // These are in a bunch of tables
"m_vecOrigin", "m_vecOrigin",
// DT_BaseEntity // DT_BaseEntity
"m_angRotation", "m_angRotation",
"m_flElasticity", "m_flElasticity",
"m_flShadowCastDistance", "m_flShadowCastDistance",
// DT_BaseAnimating // DT_BaseAnimating
"m_vecForce", "m_vecForce",
"m_flModelScale", "m_flModelScale",
"m_flFadeScale", "m_flFadeScale",
// DT_BasePlayer // DT_BasePlayer
"m_flMaxspeed", "m_flMaxspeed",
"m_flFOVTime", "m_flFOVTime",
// DT_TFLocalPlayerExclusive // DT_TFLocalPlayerExclusive
// DT_TFNonLocalPlayerExclusive // DT_TFNonLocalPlayerExclusive
"m_vecOrigin[2]", "m_vecOrigin[2]",
"m_angEyeAngles[0]", "m_angEyeAngles[0]",
"m_angEyeAngles[1]", "m_angEyeAngles[1]",
// DT_LocalPlayerExclusive // DT_LocalPlayerExclusive
"m_vecViewOffset[0]", "m_vecViewOffset[0]",
"m_vecViewOffset[1]", "m_vecViewOffset[1]",
"m_vecViewOffset[2]", "m_vecViewOffset[2]",
"m_vecBaseVelocity", "m_vecBaseVelocity",
"m_vecVelocity[0]", "m_vecVelocity[0]",
"m_vecVelocity[1]", "m_vecVelocity[1]",
"m_vecVelocity[2]", "m_vecVelocity[2]",
// DT_LOCAL // DT_LOCAL
"m_flDucktime", "m_flDucktime",
"m_flJumpTime", "m_flJumpTime",
"m_flDuckJumpTime", "m_flDuckJumpTime",
"m_flFallVelocity", "m_flFallVelocity",
"m_flNextAttack", "m_flNextAttack",
"m_vecPunchAngle", "m_vecPunchAngle",
"m_vecPunchAngleVel", "m_vecPunchAngleVel",
// DT_TFPlayerShared // DT_TFPlayerShared
"m_flDuckTimer", "m_flDuckTimer",
"m_flMovementStunTime", "m_flMovementStunTime",
"m_flFirstPrimaryAttack", "m_flFirstPrimaryAttack",
"m_flEnergyDrinkMeter", "m_flEnergyDrinkMeter",
"m_flHypeMeter", "m_flHypeMeter",
"m_flChargeMeter", "m_flChargeMeter",
"m_flInvisChangeCompleteTime", "m_flInvisChangeCompleteTime",
"m_flCloakMeter", "m_flCloakMeter",
"m_flSpyTranqBuffDuration", "m_flSpyTranqBuffDuration",
"m_flRuneCharge", "m_flRuneCharge",
"m_flHolsterAnimTime", "m_flHolsterAnimTime",
// DT_CollisionProperty // DT_CollisionProperty
"m_vecMins", "m_vecMins",
"m_vecMaxs", "m_vecMaxs",
"m_vecMinsPreScaled", "m_vecMinsPreScaled",
"m_vecMaxsPreScaled", "m_vecMaxsPreScaled",
"m_vecSpecifiedSurroundingMins", "m_vecSpecifiedSurroundingMins",
"m_vecSpecifiedSurroundingMaxs", "m_vecSpecifiedSurroundingMaxs",
"m_vecSpecifiedSurroundingMinsPreScaled", "m_vecSpecifiedSurroundingMinsPreScaled",
"m_vecSpecifiedSurroundingMaxsPreScaled", "m_vecSpecifiedSurroundingMaxsPreScaled",
]; ];
const vecKeys = ["x", "y", "z"]; const vecKeys = ["x", "y", "z"];
const EF_NOINTERP = 8 const EF_NOINTERP = 8;
function interpEntity(start: PacketEntity, end: PacketEntity, amount: number): PacketEntity { function interpEntity(start: PacketEntity, end: PacketEntity, amount: number): PacketEntity {
for (let prop1 of start.props) { for (let prop1 of start.props) {
if (!propNames.includes(prop1.definition.name)) continue; if (!propNames.includes(prop1.definition.name)) continue;
for (let prop2 of end.props) { for (let prop2 of end.props) {
if (!propNames.includes(prop2.definition.name)) continue; if (!propNames.includes(prop2.definition.name)) continue;
if (prop1.definition.fullName !== prop2.definition.fullName) continue; if (prop1.definition.fullName !== prop2.definition.fullName) continue;
if (typeof prop1.value === "number") { if (typeof prop1.value === "number") {
prop1.value = interpNumber(prop1.value as number, prop2.value as number, amount, prop1.definition.lowValue, prop1.definition.highValue); prop1.value = interpNumber(
break; prop1.value as number,
} prop2.value as number,
amount,
prop1.definition.lowValue,
prop1.definition.highValue
);
break;
}
// Interp vectors // Interp vectors
if (typeof prop1.value === "object") { if (typeof prop1.value === "object") {
if (Object.keys(prop1.value).length !== 3) continue; if (Object.keys(prop1.value).length !== 3) continue;
if (Object.keys(prop2.value).length !== 3) continue; if (Object.keys(prop2.value).length !== 3) continue;
// Check x,y,z keys // Check x,y,z keys
var cont = false; var cont = false;
for (const key of vecKeys) { for (const key of vecKeys) {
if ( if (
!Object.keys(prop1.value).includes(key) || !Object.keys(prop1.value).includes(key) ||
!Object.keys(prop2.value).includes(key) || !Object.keys(prop2.value).includes(key) ||
typeof prop1.value[key] !== "number" || typeof prop1.value[key] !== "number" ||
typeof prop2.value[key] !== "number" typeof prop2.value[key] !== "number"
) { ) {
cont = true; cont = true;
break; break;
} }
}
if (cont) continue;
for (const key of vecKeys) {
prop1.value[key] = interpNumber(prop1.value[key] as number, prop2.value[key] as number, amount, prop1.definition.lowValue, prop1.definition.highValue);
}
break;
}
} }
} if (cont) continue;
return start; for (const key of vecKeys) {
prop1.value[key] = interpNumber(
prop1.value[key] as number,
prop2.value[key] as number,
amount,
prop1.definition.lowValue,
prop1.definition.highValue
);
}
break;
}
}
}
return start;
} }
function incrementEntityTicks(entity: PacketEntity, amount: number): PacketEntity { function incrementEntityTicks(entity: PacketEntity, amount: number): PacketEntity {
var names = [ var names = ["m_nTickBase", "m_flSimulationTime"];
"m_nTickBase", for (let prop of entity.props) {
"m_flSimulationTime" if (names.includes(prop.definition.name)) {
]; prop.value = (prop.value as number) + amount;
for (let prop of entity.props) {
if (names.includes(prop.definition.name)) {
prop.value = (prop.value as number) + amount;
}
} }
return entity; }
return entity;
} }
class InterpTransformer extends Parser { class InterpTransformer extends Parser {
private readonly encoder: Encoder; private readonly encoder: Encoder;
private readonly maxVel: number; private readonly maxVel: number;
constructor(sourceStream: BitStream, targetStream: BitStream, maxVel: number = 3500) { constructor(sourceStream: BitStream, targetStream: BitStream, maxVel: number = 3500) {
super(sourceStream); super(sourceStream);
this.encoder = new Encoder(targetStream); this.encoder = new Encoder(targetStream);
this.maxVel = maxVel; this.maxVel = maxVel;
}
writeMessage(message: Message) {
this.parserState.handleMessage(message);
if (message.type === MessageType.Packet) {
for (const packet of message.packets) {
this.parserState.handlePacket(packet);
}
} }
writeMessage(message: Message) { this.encoder.writeMessage(message);
this.parserState.handleMessage(message); }
if (message.type === MessageType.Packet) {
for (const packet of message.packets) {
this.parserState.handlePacket(packet);
}
}
this.encoder.writeMessage(message); public transform() {
} const header = this.getHeader();
header.frames *= 4;
this.encoder.encodeHeader(header);
public transform() { var prevProgressPrint = 0;
const header = this.getHeader(); var prevPacketMessage = null;
header.frames *= 4; var synced = false;
this.encoder.encodeHeader(header); var skippedFirst = false;
var lastKnownProps = {};
var tickInterval = 0.015;
var prevProgressPrint = 0; for (let message of this.iterateMessages()) {
var prevPacketMessage = null; if (message.type === MessageType.SyncTick) {
var synced = false; // <Header>
var skippedFirst = false; // <Packet>
var lastKnownProps = {}; // <DataTables>
var tickInterval = 0.015; // <StringTables>
// <Packet>
// <Packet>
// <SyncTick>
// <Packet> | tick 4, the fat network packet - probably contains initial states of everything
// <Packet> | tick 8, delta packets start here?; i think we only want to manipulate these ones
// ...
// <Stop>
synced = true;
}
for (let message of this.iterateMessages()) { if (synced && skippedFirst && message.type === MessageType.Packet) {
if (message.type === MessageType.SyncTick) { if (prevPacketMessage) {
// <Header> message.sequenceIn = prevPacketMessage.sequenceIn + 4;
// <Packet> message.sequenceOut = prevPacketMessage.sequenceOut + 4;
// <DataTables>
// <StringTables> prevPacketMessage.packets = prevPacketMessage.packets.filter((value, index, arr) => {
// <Packet> return ["netTick", "packetEntities"].includes(value.packetType);
// <Packet> });
// <SyncTick>
// <Packet> | tick 4, the fat network packet - probably contains initial states of everything // Packets only contain props if they change during that tick,
// <Packet> | tick 8, delta packets start here?; i think we only want to manipulate these ones // store last known props and restore previously stored ones.
// ... for (let p of prevPacketMessage.packets) {
// <Stop> if (p.packetType !== "packetEntities") {
synced = true; continue;
} }
if (synced && skippedFirst && message.type === MessageType.Packet) { for (let e of p.entities as PacketEntity[]) {
if (prevPacketMessage) { if (p.removedEntities.includes(e.entityIndex)) {
message.sequenceIn = prevPacketMessage.sequenceIn + 4; //console.log("packet includes removed ent");
message.sequenceOut = prevPacketMessage.sequenceOut + 4; delete lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`];
}
prevPacketMessage.packets = prevPacketMessage.packets.filter((value, index, arr) => { // Store from entity
return ["netTick", "packetEntities"].includes(value.packetType); for (const prop of e.props) {
}); if (!propNames.includes(prop.definition.name)) continue;
// Packets only contain props if they change during that tick, // Using just entityIndex will sometimes fail
// store last known props and restore previously stored ones. // for some reason, maybe they are recycled.
for (let p of prevPacketMessage.packets) { // This happens even with the newPacket.removedEntities check
if (p.packetType !== "packetEntities") { // later on...
continue; if (lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`] == null) {
} lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`] = {};
}
lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`][prop.definition.fullName] = prop;
}
for (let e of p.entities as PacketEntity[]) { // Restore props later on after checking what props the future entities have
if (p.removedEntities.includes(e.entityIndex)) { }
//console.log("packet includes removed ent"); }
delete (lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`]);
}
// Store from entity // Add 3 intepolated ticks
for (const prop of e.props) { for (let i = 0; i < 3; i++) {
if (!propNames.includes(prop.definition.name)) continue; prevPacketMessage.tick++;
prevPacketMessage.sequenceIn++;
prevPacketMessage.sequenceOut++;
// Using just entityIndex will sometimes fail for (let packet of prevPacketMessage.packets) {
// for some reason, maybe they are recycled. if (packet.packetType === "netTick") {
// This happens even with the newPacket.removedEntities check packet.tick++;
// later on... tickInterval = packet.frameTime / 100000;
if (lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`] == null) { continue;
lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`] = {}; }
}
lastKnownProps[`${e.entityIndex}:${e.serverClass.id}`][prop.definition.fullName] = prop;
}
// Restore props later on after checking what props the future entities have // packetEntities
} for (let entity of packet.entities as PacketEntity[]) {
// Loop through new message and interp
for (let newPacket of message.packets) {
if (newPacket.packetType !== "packetEntities") {
continue;
}
for (const index of newPacket.removedEntities) {
for (let propIndex = Object.keys(lastKnownProps).length - 1; propIndex > 0; propIndex--) {
if (Object.keys(lastKnownProps)[propIndex].startsWith(index.toString() + ":")) {
delete lastKnownProps[Object.keys(lastKnownProps)[propIndex]];
}
}
}
if (newPacket.removedEntities.includes(entity.entityIndex)) {
continue;
}
for (let newEntity of newPacket.entities) {
if (newEntity.entityIndex !== entity.entityIndex) {
continue;
} }
// Add 3 intepolated ticks if (newEntity.serverClass.id !== entity.serverClass.id) {
for (let i = 0; i < 3; i++) { continue;
prevPacketMessage.tick++; }
prevPacketMessage.sequenceIn++;
prevPacketMessage.sequenceOut++;
for (let packet of prevPacketMessage.packets) { entity = incrementEntityTicks(entity, 1);
if (packet.packetType === "netTick") { newEntity = incrementEntityTicks(newEntity, 1);
packet.tick++;
tickInterval = packet.frameTime / 100000;
continue;
}
// packetEntities // Don't interp if new entity has EF_NOINTERP flag
for (let entity of packet.entities as PacketEntity[]) { var m_fEffects = newEntity.getPropValue("DT_BaseEntity.m_fEffects");
if (m_fEffects !== null) {
if (((m_fEffects as number) & EF_NOINTERP) === EF_NOINTERP) {
break;
}
}
// Loop through new message and interp // Check for maxVel
for (let newPacket of message.packets) { if (lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`] != null) {
if (newPacket.packetType !== "packetEntities") { var lastOrigin = null;
continue; var lastOriginZ = null;
} var newOrigin = null;
var newOriginZ = null;
for (const index of newPacket.removedEntities) { for (const p of newEntity.props) {
for (let propIndex = Object.keys(lastKnownProps).length - 1; propIndex > 0; propIndex--) { if (p.definition.name === "m_vecOrigin") {
if (Object.keys(lastKnownProps)[propIndex].startsWith(index.toString() + ":")) { newOrigin = p.value;
delete (lastKnownProps[Object.keys(lastKnownProps)[propIndex]]); if (lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][p.definition.fullName] != null) {
} lastOrigin = lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][p.definition.fullName].value;
} }
} } else if (p.definition.name === "m_vecOrigin[2]") {
newOriginZ = p.value;
if (lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][p.definition.fullName] != null) {
lastOriginZ = lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][p.definition.fullName].value;
}
}
}
if (newPacket.removedEntities.includes(entity.entityIndex)) { if (lastOrigin != null && newOrigin != null) {
continue; if (lastOriginZ) {
} lastOrigin.z = lastOriginZ;
}
for (let newEntity of newPacket.entities) { if (newOriginZ) {
if (newEntity.entityIndex !== entity.entityIndex) { newOrigin.z = newOriginZ;
continue;
}
if (newEntity.serverClass.id !== entity.serverClass.id) {
continue;
}
entity = incrementEntityTicks(entity, 1);
newEntity = incrementEntityTicks(newEntity, 1);
// Don't interp if new entity has EF_NOINTERP flag
var m_fEffects = newEntity.getPropValue("DT_BaseEntity.m_fEffects");
if (m_fEffects !== null) {
if (((m_fEffects as number) & EF_NOINTERP) === EF_NOINTERP) {
break;
}
}
// Check for maxVel
if (lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`] != null) {
var lastOrigin = null;
var lastOriginZ = null;
var newOrigin = null;
var newOriginZ = null;
for (const p of newEntity.props) {
if (p.definition.name === "m_vecOrigin") {
newOrigin = p.value;
if (lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][p.definition.fullName] != null) {
lastOrigin = lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][p.definition.fullName].value;
}
} else if (p.definition.name === "m_vecOrigin[2]") {
newOriginZ = p.value;
if (lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][p.definition.fullName] != null) {
lastOriginZ = lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][p.definition.fullName].value;
}
}
}
if (lastOrigin != null && newOrigin != null) {
if (lastOriginZ) {
lastOrigin.z = lastOriginZ;
}
if (newOriginZ) {
newOrigin.z = newOriginZ;
}
for (let axis of vecKeys) {
var dist = newOrigin[axis] - lastOrigin[axis];
if (dist > this.maxVel * tickInterval * 4) {
//console.log(`Entity ${newEntity.serverClass.name} (${newEntity.entityIndex}) moved over maxvel (${dist} > ${this.maxVel * tickInterval * 4})`);
break;
}
}
}
}
// Restore from previously stored props only the props
// that the future entity has, so we can interp between them.
// Only need to do this the first time,
// since we reuse the message.
if (i < 0) {
if (lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`] != null) {
var propArr = [];
for (const propKey of Object.keys(lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`])) {
// Check if new entity has this property,
// if not, no need to add it to the old entity for interpolation.
if (newEntity.hasProperty(propKey.split(".")[0], propKey.split(".")[1])) {
propArr.push(lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][propKey]);
}
}
if (propArr.length > 0) {
//console.log(`Applying ${propArr.length} props`);
entity.applyPropUpdate(propArr);
}
}
}
// We interpolate same entity multiple times,
// edit interp amount appropriately.
// min target max interp
// 0 0.25 1.0 0.25
// 0.25 0.5 1.0 1/3
// 0.5 0.75 1.0 0.5
var interp = 0.25 * (1 / (1 - (0.25 * i)));
entity = interpEntity(entity, newEntity, interp);
}
}
}
} }
this.writeMessage(prevPacketMessage); for (let axis of vecKeys) {
var dist = newOrigin[axis] - lastOrigin[axis];
if (dist > this.maxVel * tickInterval * 4) {
//console.log(`Entity ${newEntity.serverClass.name} (${newEntity.entityIndex}) moved over maxvel (${dist} > ${this.maxVel * tickInterval * 4})`);
break;
}
}
}
} }
}
prevPacketMessage = message; // Restore from previously stored props only the props
// that the future entity has, so we can interp between them.
// Only need to do this the first time,
// since we reuse the message.
if (i < 0) {
if (lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`] != null) {
var propArr = [];
if (message.tick > prevProgressPrint + 10000) { for (const propKey of Object.keys(lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`])) {
prevProgressPrint += 10000; // Check if new entity has this property,
console.log(`Progress: ${message.tick} / ${header.ticks}`); // if not, no need to add it to the old entity for interpolation.
if (newEntity.hasProperty(propKey.split(".")[0], propKey.split(".")[1])) {
propArr.push(lastKnownProps[`${entity.entityIndex}:${entity.serverClass.id}`][propKey]);
}
}
if (propArr.length > 0) {
//console.log(`Applying ${propArr.length} props`);
entity.applyPropUpdate(propArr);
}
}
}
// We interpolate same entity multiple times,
// edit interp amount appropriately.
// min target max interp
// 0 0.25 1.0 0.25
// 0.25 0.5 1.0 1/3
// 0.5 0.75 1.0 0.5
var interp = 0.25 * (1 / (1 - 0.25 * i));
entity = interpEntity(entity, newEntity, interp);
}
} }
}
} }
if (synced && !skippedFirst && message.type === MessageType.Packet) { this.writeMessage(prevPacketMessage);
// Skip first packet }
skippedFirst = true;
}
// Write current message
this.writeMessage(message);
} }
prevPacketMessage = message;
if (message.tick > prevProgressPrint + 10000) {
prevProgressPrint += 10000;
console.log(`Progress: ${message.tick} / ${header.ticks}`);
}
}
if (synced && !skippedFirst && message.type === MessageType.Packet) {
// Skip first packet
skippedFirst = true;
}
// Write current message
this.writeMessage(message);
} }
}
} }
/** /**
@ -401,16 +409,16 @@ class InterpTransformer extends Parser {
* @param output - Output demofile name * @param output - Output demofile name
*/ */
function interp(input: string, output: string, maxVel: number = 3500) { function interp(input: string, output: string, maxVel: number = 3500) {
const decodeStream = new BitStream(readFileSync(input).buffer as ArrayBuffer); const decodeStream = new BitStream(readFileSync(input).buffer as ArrayBuffer);
const encodeStream = new DynamicBitStream(32 * 1024 * 1024); const encodeStream = new DynamicBitStream(32 * 1024 * 1024);
const transformer = new InterpTransformer(decodeStream, encodeStream, maxVel); const transformer = new InterpTransformer(decodeStream, encodeStream, maxVel);
transformer.transform(); transformer.transform();
const encodedLength = encodeStream.index; const encodedLength = encodeStream.index;
encodeStream.index = 0; encodeStream.index = 0;
writeFileSync(output, encodeStream.readArrayBuffer(Math.ceil(encodedLength / 8))); writeFileSync(output, encodeStream.readArrayBuffer(Math.ceil(encodedLength / 8)));
} }
export { interp }; export { interp };

View file

@ -1,3 +1,3 @@
var interp = require('../build/index.js').interp; var interp = require("../build/index.js").interp;
interp('test.dem', 'test_out.dem'); interp("test.dem", "test_out.dem");

View file

@ -1,9 +1,9 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es6", "target": "es6",
"module": "commonjs", "module": "commonjs",
"sourceMap": true, "sourceMap": true,
"outDir": "build", "outDir": "build",
"rootDir": "src" "rootDir": "src"
}
} }
}