mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
cleanup packet parser signature
This commit is contained in:
parent
1fbb61f252
commit
e83ca71f6d
10 changed files with 106 additions and 78 deletions
|
|
@ -3,6 +3,7 @@ import {ServerClass} from "./ServerClass";
|
||||||
import {SendTable} from "./SendTable";
|
import {SendTable} from "./SendTable";
|
||||||
import {StringTable} from "./StringTable";
|
import {StringTable} from "./StringTable";
|
||||||
import {SendProp} from "./SendProp";
|
import {SendProp} from "./SendProp";
|
||||||
|
import {GameEventDefinitionMap} from "./GameEvent";
|
||||||
export class Match {
|
export class Match {
|
||||||
tick: number;
|
tick: number;
|
||||||
chat: any[];
|
chat: any[];
|
||||||
|
|
@ -17,7 +18,7 @@ export class Match {
|
||||||
sendTables: SendTable[];
|
sendTables: SendTable[];
|
||||||
instanceBaselines: SendProp[][][];
|
instanceBaselines: SendProp[][][];
|
||||||
staticBaseLines: any[];
|
staticBaseLines: any[];
|
||||||
_classBits: number = 0
|
eventDefinitions: GameEventDefinitionMap;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tick = 0;
|
this.tick = 0;
|
||||||
|
|
@ -34,6 +35,7 @@ export class Match {
|
||||||
this.entities = [];
|
this.entities = [];
|
||||||
this.instanceBaselines = [[], []];
|
this.instanceBaselines = [[], []];
|
||||||
this.staticBaseLines = [];
|
this.staticBaseLines = [];
|
||||||
|
this.eventDefinitions = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
getSendTable(name) {
|
getSendTable(name) {
|
||||||
|
|
@ -42,7 +44,7 @@ export class Match {
|
||||||
return this.sendTables[i];
|
return this.sendTables[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
throw new Error("unknown SendTable " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStringTable(name) {
|
getStringTable(name) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export class SendTable {
|
||||||
this.props.push(prop);
|
this.props.push(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
flatten() {
|
private flatten() {
|
||||||
let excludes: SendPropDefinition[] = [];
|
let excludes: SendPropDefinition[] = [];
|
||||||
let props: SendPropDefinition[] = [];
|
let props: SendPropDefinition[] = [];
|
||||||
this.getAllProps(excludes, props);
|
this.getAllProps(excludes, props);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import {SendTable} from "./SendTable";
|
|
||||||
|
|
||||||
export class ServerClass {
|
export class ServerClass {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
dataTable: SendTable;
|
dataTable: string;
|
||||||
|
|
||||||
constructor(id, name, dataTable) {
|
constructor(id:number, name:string, dataTable:string) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.dataTable = dataTable;
|
this.dataTable = dataTable;
|
||||||
|
|
|
||||||
44
src/Parser/EntityDecoder.ts
Normal file
44
src/Parser/EntityDecoder.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import {Entity} from "../Data/Entity";
|
||||||
|
import {BitStream} from "bit-buffer";
|
||||||
|
import {SendProp} from "../Data/SendProp";
|
||||||
|
import {SendPropParser} from "./SendPropParser";
|
||||||
|
import {readUBitVar} from "./readBitVar";
|
||||||
|
export function applyEntityUpdate(entity: Entity, stream: BitStream): Entity {
|
||||||
|
let index = -1;
|
||||||
|
const allProps = entity.sendTable.flattenedProps;
|
||||||
|
let changedProps: SendProp[] = [];
|
||||||
|
while ((index = readFieldIndex(stream, index)) != -1) {
|
||||||
|
if (index > 4096) {
|
||||||
|
throw new Error('prop index out of bounds while applying update for ' + entity.sendTable.name+ ' got ' + index);
|
||||||
|
}
|
||||||
|
const propDefinition = allProps[index];
|
||||||
|
const existingProp = entity.getPropByDefinition(propDefinition);
|
||||||
|
let prop;
|
||||||
|
if (existingProp) {
|
||||||
|
prop = existingProp;
|
||||||
|
} else {
|
||||||
|
prop = new SendProp(propDefinition);
|
||||||
|
}
|
||||||
|
// prop.value = SendPropParser.decode(propDefinition, stream);
|
||||||
|
// console.log(prop);
|
||||||
|
changedProps.push(prop);
|
||||||
|
|
||||||
|
if (!existingProp) {
|
||||||
|
entity.props.push(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < changedProps.length; i++) {
|
||||||
|
const prop = changedProps[i];
|
||||||
|
prop.value = SendPropParser.decode(prop.definition, stream);
|
||||||
|
console.log(prop);
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
};
|
||||||
|
|
||||||
|
const readFieldIndex = function (stream: BitStream, lastIndex: number): number {
|
||||||
|
if (!stream.readBoolean()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const diff = readUBitVar(stream);
|
||||||
|
return lastIndex + diff + 1;
|
||||||
|
};
|
||||||
|
|
@ -28,13 +28,12 @@ import {Packet as IPacket} from '../../Data/Packet';
|
||||||
export class Packet extends Parser {
|
export class Packet extends Parser {
|
||||||
parse() {
|
parse() {
|
||||||
let packets: IPacket[] = [];
|
let packets: IPacket[] = [];
|
||||||
let entities = [];
|
|
||||||
let lastPacketType = 0;
|
let lastPacketType = 0;
|
||||||
while (this.bitsLeft > 6) { // last 6 bits for NOOP
|
while (this.bitsLeft > 6) { // last 6 bits for NOOP
|
||||||
const type = this.stream.readBits(6);
|
const type = this.stream.readBits(6);
|
||||||
if (type !== 0) {
|
if (type !== 0) {
|
||||||
if (Packet.parsers[type]) {
|
if (Packet.parsers[type]) {
|
||||||
let packet = Packet.parsers[type].call(this, this.stream, Packet.gameEventMap, entities, this.match);
|
let packet = Packet.parsers[type].call(this, this.stream, this.match);
|
||||||
packets.push(packet);
|
packets.push(packet);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown packet type ' + type + " just parsed a " + PacketType[lastPacketType]);
|
throw new Error('Unknown packet type ' + type + " just parsed a " + PacketType[lastPacketType]);
|
||||||
|
|
@ -81,8 +80,6 @@ export class Packet extends Parser {
|
||||||
31: ParserGenerator.make('getCvarValue', 'cookie{32}value{s}'),
|
31: ParserGenerator.make('getCvarValue', 'cookie{32}value{s}'),
|
||||||
32: ParserGenerator.make('cmdKeyValues', 'length{32}data{$length}')
|
32: ParserGenerator.make('cmdKeyValues', 'length{32}data{$length}')
|
||||||
};
|
};
|
||||||
|
|
||||||
static gameEventMap: GameEventDefinitionMap = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PacketType {
|
enum PacketType {
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,23 @@ import {
|
||||||
GameEventType, GameEventValue, GameEventEntry, GameEventDefinition, GameEvent as IGameEvent,
|
GameEventType, GameEventValue, GameEventEntry, GameEventDefinition, GameEvent as IGameEvent,
|
||||||
GameEventValueMap, GameEventDefinitionMap
|
GameEventValueMap, GameEventDefinitionMap
|
||||||
} from "../../Data/GameEvent";
|
} from "../../Data/GameEvent";
|
||||||
|
import {Match} from "../../Data/Match";
|
||||||
|
|
||||||
const parseGameEvent = function (eventId: number, stream: BitStream, events: GameEventDefinitionMap): IGameEvent|null {
|
const parseGameEvent = function (eventId: number, stream: BitStream, events: GameEventDefinitionMap): IGameEvent|null {
|
||||||
if (!events[eventId]) {
|
if (!events[eventId]) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const eventDescription: GameEventDefinition = events[eventId];
|
const eventDescription: GameEventDefinition = events[eventId];
|
||||||
const values: GameEventValueMap = {};
|
const values: GameEventValueMap = {};
|
||||||
for (let i = 0; i < eventDescription.entries.length; i++) {
|
for (let i = 0; i < eventDescription.entries.length; i++) {
|
||||||
const entry: GameEventEntry = eventDescription.entries[i];
|
const entry: GameEventEntry = eventDescription.entries[i];
|
||||||
const value = getGameEventValue(stream, entry);
|
const value = getGameEventValue(stream, entry);
|
||||||
if (value) {
|
if (value) {
|
||||||
values[entry.name] = value;
|
values[entry.name] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: eventDescription.name,
|
name: eventDescription.name,
|
||||||
values: values
|
values: values
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -46,14 +47,14 @@ const getGameEventValue = function (stream: BitStream, entry: GameEventEntry): G
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export function GameEvent(stream: BitStream, events: GameEventDefinitionMap): Packet { // 25: game event
|
export function GameEvent(stream: BitStream, match: Match): Packet { // 25: game event
|
||||||
const length = stream.readBits(11);
|
const length = stream.readBits(11);
|
||||||
const end = stream._index + length;
|
const end = stream._index + length;
|
||||||
const eventId = stream.readBits(9);
|
const eventId = stream.readBits(9);
|
||||||
const event = parseGameEvent(eventId, stream, events);
|
const event = parseGameEvent(eventId, stream, match.eventDefinitions);
|
||||||
stream._index = end;
|
stream._index = end;
|
||||||
return {
|
return {
|
||||||
packetType: 'gameEvent',
|
packetType: 'gameEvent',
|
||||||
event: event
|
event: event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
import {Packet} from "../../Data/Packet";
|
import {Packet} from "../../Data/Packet";
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
import {GameEventEntry, GameEventDefinitionMap} from "../../Data/GameEvent";
|
import {GameEventEntry, GameEventDefinitionMap} from "../../Data/GameEvent";
|
||||||
|
import {Match} from "../../Data/Match";
|
||||||
|
|
||||||
export function GameEventList(stream: BitStream, events: GameEventDefinitionMap): Packet { // 30: gameEventList
|
export function GameEventList(stream: BitStream, match: Match): Packet { // 30: gameEventList
|
||||||
// list of game events and parameters
|
// list of game events and parameters
|
||||||
const numEvents = stream.readBits(9);
|
const numEvents = stream.readBits(9);
|
||||||
const length = stream.readBits(20);
|
const length = stream.readBits(20);
|
||||||
for (let i = 0; i < numEvents; i++) {
|
for (let i = 0; i < numEvents; i++) {
|
||||||
const id = stream.readBits(9);
|
const id = stream.readBits(9);
|
||||||
const name = stream.readASCIIString();
|
const name = stream.readASCIIString();
|
||||||
let type = stream.readBits(3);
|
let type = stream.readBits(3);
|
||||||
const entries: GameEventEntry[] = [];
|
const entries: GameEventEntry[] = [];
|
||||||
while (type !== 0) {
|
while (type !== 0) {
|
||||||
entries.push({
|
entries.push({
|
||||||
|
|
@ -18,14 +19,13 @@ export function GameEventList(stream: BitStream, events: GameEventDefinitionMap)
|
||||||
});
|
});
|
||||||
type = stream.readBits(3);
|
type = stream.readBits(3);
|
||||||
}
|
}
|
||||||
events[id] = {
|
match.eventDefinitions[id] = {
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
entries: entries
|
entries: entries
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
packetType: 'gameEventList',
|
packetType: 'gameEventList'
|
||||||
events: events
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {BitStream} from 'bit-buffer';
|
||||||
import {GameEventDefinition} from "../../Data/GameEvent";
|
import {GameEventDefinition} from "../../Data/GameEvent";
|
||||||
import {Match} from "../../Data/Match";
|
import {Match} from "../../Data/Match";
|
||||||
import {readUBitVar} from "../readBitVar";
|
import {readUBitVar} from "../readBitVar";
|
||||||
|
import {applyEntityUpdate} from "../EntityDecoder";
|
||||||
|
|
||||||
enum PVS {
|
enum PVS {
|
||||||
PRESERVE = 0,
|
PRESERVE = 0,
|
||||||
|
|
@ -71,7 +72,7 @@ function readLeavePVS(match, entityId, shouldDelete) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PacketEntities(stream: BitStream, events: GameEventDefinition[], entities: Entity[], match: Match): Packet { //26: packetEntities
|
export function PacketEntities(stream: BitStream, match: Match): Packet { //26: packetEntities
|
||||||
// https://github.com/skadistats/smoke/blob/master/smoke/replay/handler/svc_packetentities.pyx
|
// https://github.com/skadistats/smoke/blob/master/smoke/replay/handler/svc_packetentities.pyx
|
||||||
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/DP/Handler/PacketEntitesHandler.cs
|
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/DP/Handler/PacketEntitesHandler.cs
|
||||||
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/DP/Entity.cs
|
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/DP/Entity.cs
|
||||||
|
|
@ -89,8 +90,7 @@ export function PacketEntities(stream: BitStream, events: GameEventDefinition[],
|
||||||
|
|
||||||
stream._index = end;
|
stream._index = end;
|
||||||
return {
|
return {
|
||||||
packetType: 'packetEntities',
|
packetType: 'packetEntities'
|
||||||
entities: entities
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (updatedBaseLine) {
|
if (updatedBaseLine) {
|
||||||
|
|
@ -146,47 +146,6 @@ export function PacketEntities(stream: BitStream, events: GameEventDefinition[],
|
||||||
|
|
||||||
stream._index = end;
|
stream._index = end;
|
||||||
return {
|
return {
|
||||||
packetType: 'packetEntities',
|
packetType: 'packetEntities'
|
||||||
entities: entities
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const readFieldIndex = function (stream: BitStream, lastIndex: number): number {
|
|
||||||
if (!stream.readBoolean()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
const diff = readUBitVar(stream);
|
|
||||||
return lastIndex + diff + 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const applyEntityUpdate = function (entity: Entity, stream: BitStream): Entity {
|
|
||||||
let index = -1;
|
|
||||||
const allProps = entity.sendTable.flattenedProps;
|
|
||||||
let changedProps: SendProp[] = [];
|
|
||||||
while ((index = readFieldIndex(stream, index)) != -1) {
|
|
||||||
if (index > 4096) {
|
|
||||||
throw new Error('prop index out of bounds');
|
|
||||||
}
|
|
||||||
const propDefinition = allProps[index];
|
|
||||||
const existingProp = entity.getPropByDefinition(propDefinition);
|
|
||||||
let prop;
|
|
||||||
if (existingProp) {
|
|
||||||
prop = existingProp;
|
|
||||||
} else {
|
|
||||||
prop = new SendProp(propDefinition);
|
|
||||||
}
|
|
||||||
// prop.value = SendPropParser.decode(propDefinition, stream);
|
|
||||||
// console.log(prop);
|
|
||||||
changedProps.push(prop);
|
|
||||||
|
|
||||||
if (!existingProp) {
|
|
||||||
entity.props.push(prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < changedProps.length; i++) {
|
|
||||||
const prop = changedProps[i];
|
|
||||||
prop.value = SendPropParser.decode(prop.definition, stream);
|
|
||||||
console.log(prop);
|
|
||||||
}
|
|
||||||
return entity;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@ import {GameEventDefinitionMap} from "../../Data/GameEvent";
|
||||||
import {Match} from "../../Data/Match";
|
import {Match} from "../../Data/Match";
|
||||||
import {Entity} from "../../Data/Entity";
|
import {Entity} from "../../Data/Entity";
|
||||||
|
|
||||||
export type Parser = (stream: BitStream, gameEventMap?: GameEventDefinitionMap, entities?: Entity[], match?: Match) => Packet;
|
export type Parser = (stream: BitStream, match?: Match) => Packet;
|
||||||
export type PacketParserMap = {[id: number]: Parser};
|
export type PacketParserMap = {[id: number]: Parser};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,39 @@
|
||||||
import {Packet} from "../../Data/Packet";
|
import {Packet} from "../../Data/Packet";
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {Match} from "../../Data/Match";
|
||||||
|
import {Entity} from "../../Data/Entity";
|
||||||
|
import {applyEntityUpdate} from "../EntityDecoder";
|
||||||
|
|
||||||
export function TempEntities(stream: BitStream): Packet { // 10: classInfo
|
export function TempEntities(stream: BitStream, match: Match): Packet { // 10: classInfo
|
||||||
const entityCount = stream.readBits(8);
|
const entityCount = stream.readBits(8);
|
||||||
const length = readVarInt(stream);
|
const length = readVarInt(stream);
|
||||||
console.log(length);
|
const end = stream._index + length;
|
||||||
stream._index += length;
|
|
||||||
|
|
||||||
|
// let entity: Entity|null = null;
|
||||||
|
// let entities: Entity[] = [];
|
||||||
|
// for (let i = 0; i < entityCount; i++) {
|
||||||
|
// const delay = (stream.readBoolean()) ? stream.readUint8() / 100 : 0; //unused it seems
|
||||||
|
// if (stream.readBoolean()) {
|
||||||
|
// const classId = stream.readBits(match.classBits);
|
||||||
|
// const serverClass = match.serverClasses[classId];
|
||||||
|
// const sendTable = match.getSendTable(serverClass.dataTable);
|
||||||
|
// entity = new Entity(serverClass, sendTable, 0, 0);
|
||||||
|
// applyEntityUpdate(entity, stream);
|
||||||
|
// entities.push(entity);
|
||||||
|
// } else {
|
||||||
|
// if (entity) {
|
||||||
|
// applyEntityUpdate(entity, stream);
|
||||||
|
// } else {
|
||||||
|
// throw new Error("no entity set to update");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// console.log(entity);
|
||||||
|
// }
|
||||||
|
// if (end - stream._index > 8) {
|
||||||
|
// throw new Error("unexpected content after TempEntities");
|
||||||
|
// }
|
||||||
|
|
||||||
|
stream._index = end;
|
||||||
return {
|
return {
|
||||||
'packetType': 'tempEntities'
|
'packetType': 'tempEntities'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue