1
0
Fork 0
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:
Robin Appelman 2016-12-19 00:45:22 +01:00
commit e83ca71f6d
10 changed files with 106 additions and 78 deletions

View file

@ -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) {

View file

@ -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);

View file

@ -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;

View 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;
};

View file

@ -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 {

View file

@ -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
} }
} }

View file

@ -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
} }
} }

View file

@ -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;
};

View file

@ -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};

View file

@ -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'
} }