mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
final typescript conversion
This commit is contained in:
parent
94383f447f
commit
3b805be013
19 changed files with 440 additions and 518 deletions
2
index.js
2
index.js
|
|
@ -1 +1 @@
|
||||||
module.exports = require('./build/demo');
|
module.exports = require('./build/Demo').Demo;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import {Entity} from "./Entity";
|
||||||
import {ServerClass} from "./ServerClass";
|
import {ServerClass} from "./ServerClass";
|
||||||
import {SendTable} from "./SendTable";
|
import {SendTable} from "./SendTable";
|
||||||
import {StringTable} from "./StringTable";
|
import {StringTable} from "./StringTable";
|
||||||
|
import {SendProp} from "./SendProp";
|
||||||
export class Match {
|
export class Match {
|
||||||
tick: number;
|
tick: number;
|
||||||
chat: any[];
|
chat: any[];
|
||||||
|
|
@ -10,11 +11,11 @@ export class Match {
|
||||||
rounds: any[];
|
rounds: any[];
|
||||||
startTick: number;
|
startTick: number;
|
||||||
intervalPerTick: number;
|
intervalPerTick: number;
|
||||||
entities: Entity[];
|
entities: (Entity|null)[];
|
||||||
stringTables: StringTable[];
|
stringTables: StringTable[];
|
||||||
serverClasses: ServerClass[];
|
serverClasses: ServerClass[];
|
||||||
sendTables: SendTable[];
|
sendTables: SendTable[];
|
||||||
instanceBaselines: any[][];
|
instanceBaselines: SendProp[][][];
|
||||||
staticBaseLines: any[];
|
staticBaseLines: any[];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export class SendProp {
|
||||||
this.value = null;
|
this.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone():SendProp {
|
||||||
const prop = new SendProp(this.definition);
|
const prop = new SendProp(this.definition);
|
||||||
prop.value = clone(this.value);
|
prop.value = clone(this.value);
|
||||||
return prop;
|
return prop;
|
||||||
|
|
|
||||||
41
src/Demo.ts
Normal file
41
src/Demo.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import {Stream} from "stream";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {Parser} from './Parser';
|
||||||
|
import {StreamParser} from './StreamParser';
|
||||||
|
|
||||||
|
export class Demo {
|
||||||
|
stream: BitStream;
|
||||||
|
|
||||||
|
constructor(arrayBuffer: ArrayBuffer) {
|
||||||
|
this.stream = new BitStream(arrayBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
getParser() {
|
||||||
|
return new Parser(this.stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromNodeBuffer(nodeBuffer) {
|
||||||
|
const arrayBuffer = new ArrayBuffer(nodeBuffer.length);
|
||||||
|
const view = new Uint8Array(arrayBuffer);
|
||||||
|
for (let i = 0; i < nodeBuffer.length; ++i) {
|
||||||
|
view[i] = nodeBuffer[i];
|
||||||
|
}
|
||||||
|
return new Demo(arrayBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromNodeStream(nodeStream) {
|
||||||
|
return new StreamDemo(nodeStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StreamDemo {
|
||||||
|
stream: Stream;
|
||||||
|
|
||||||
|
constructor(nodeStream: Stream) {
|
||||||
|
this.stream = nodeStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
getParser() {
|
||||||
|
return new StreamParser(this.stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
147
src/Parser.ts
Normal file
147
src/Parser.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
import toBuffer = require('typedarray-to-buffer');
|
||||||
|
import {Packet} from './Parser/Message/Packet';
|
||||||
|
import {ConsoleCmd} from './Parser/Message/ConsoleCmd';
|
||||||
|
import {StringTable} from './Parser/Message/StringTable';
|
||||||
|
import {DataTable} from './Parser/Message/DataTable';
|
||||||
|
import {UserCmd} from './Parser/Message/UserCmd';
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {EventEmitter} from 'events';
|
||||||
|
import {Match} from './Data/Match';
|
||||||
|
import {Packet as IPacket} from "./Data/Packet";
|
||||||
|
import {Parser as MessageParser} from './Parser/Message/Parser';
|
||||||
|
|
||||||
|
export class Parser extends EventEmitter {
|
||||||
|
stream: BitStream;
|
||||||
|
packets: IPacket[];
|
||||||
|
match: Match;
|
||||||
|
|
||||||
|
constructor(stream: BitStream) {
|
||||||
|
super();
|
||||||
|
this.stream = stream;
|
||||||
|
this.packets = [];
|
||||||
|
this.match = new Match();
|
||||||
|
this.on('packet', this.match.handlePacket.bind(this.match));
|
||||||
|
this.on('packet', function (packet) {
|
||||||
|
this.packets.push(packet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
readHeader() {
|
||||||
|
return this.parseHeader(this.stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
parseHeader(stream) {
|
||||||
|
return {
|
||||||
|
'type': stream.readASCIIString(8),
|
||||||
|
'version': stream.readInt32(),
|
||||||
|
'protocol': stream.readInt32(),
|
||||||
|
'server': stream.readASCIIString(260),
|
||||||
|
'nick': stream.readASCIIString(260),
|
||||||
|
'map': stream.readASCIIString(260),
|
||||||
|
'game': stream.readASCIIString(260),
|
||||||
|
'duration': stream.readFloat32(),
|
||||||
|
'ticks': stream.readInt32(),
|
||||||
|
'frames': stream.readInt32(),
|
||||||
|
'sigon': stream.readInt32()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseBody() {
|
||||||
|
let message;
|
||||||
|
while (message = this.readMessage(this.stream, this.match)) {
|
||||||
|
if (message instanceof MessageParser) {
|
||||||
|
this.handleMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.emit('done', this.match);
|
||||||
|
return this.match;
|
||||||
|
}
|
||||||
|
|
||||||
|
parseMessage(buffer: ArrayBuffer, type: MessageType, tick: number, length: number, match: Match): MessageParser {
|
||||||
|
const data = new BitStream(buffer);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case MessageType.Sigon:
|
||||||
|
case MessageType.Packet:
|
||||||
|
return new Packet(type, tick, data, length, match);
|
||||||
|
case MessageType.ConsoleCmd:
|
||||||
|
return new ConsoleCmd(type, tick, data, length, match);
|
||||||
|
case MessageType.UserCmd:
|
||||||
|
return new UserCmd(type, tick, data, length, match);
|
||||||
|
case MessageType.DataTables:
|
||||||
|
return new DataTable(type, tick, data, length, match);
|
||||||
|
case MessageType.StringTables:
|
||||||
|
return new StringTable(type, tick, data, length, match);
|
||||||
|
default:
|
||||||
|
throw new Error("unknown message type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMessage(message: MessageParser) {
|
||||||
|
if (message.parse) {
|
||||||
|
const packets = message.parse();
|
||||||
|
for (let i = 0; i < packets.length; i++) {
|
||||||
|
const packet = packets[i];
|
||||||
|
if (packet) {
|
||||||
|
this.emit('packet', packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readMessage(stream, match): MessageParser|boolean {
|
||||||
|
const type: MessageType = stream.readBits(8);
|
||||||
|
if (type === MessageType.Stop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const tick = stream.readInt32();
|
||||||
|
let start, length, buffer;
|
||||||
|
|
||||||
|
let viewOrigin: number[][] = [];
|
||||||
|
let viewAngles: number[][] = [];
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case MessageType.Sigon:
|
||||||
|
case MessageType.Packet:
|
||||||
|
this.stream.readInt32(); // flags
|
||||||
|
for (let j = 0; j < 2; j++) {
|
||||||
|
viewOrigin[j] = [];
|
||||||
|
viewAngles[j] = [];
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
viewOrigin[j][i] = this.stream.readInt32();
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
viewAngles[j][i] = this.stream.readInt32();
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
this.stream.readInt32(); // local viewAngles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.stream.readInt32(); // sequence in
|
||||||
|
this.stream.readInt32(); // sequence out
|
||||||
|
break;
|
||||||
|
case MessageType.UserCmd:
|
||||||
|
stream.byteIndex += 0x04; // unknown / outgoing sequence
|
||||||
|
break;
|
||||||
|
case MessageType.SyncTick:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = stream.readInt32();
|
||||||
|
start = stream.byteIndex;
|
||||||
|
buffer = toBuffer(stream._view._view.slice(start, start + length));
|
||||||
|
stream.byteIndex += length;
|
||||||
|
return this.parseMessage(buffer, type, tick, length, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MessageType {
|
||||||
|
Sigon = 1,
|
||||||
|
Packet = 2,
|
||||||
|
SyncTick = 3,
|
||||||
|
ConsoleCmd = 4,
|
||||||
|
UserCmd = 5,
|
||||||
|
DataTables = 6,
|
||||||
|
Stop = 7,
|
||||||
|
StringTables = 8
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
import {Match} from '../../Data/Match';
|
import {Match} from '../../Data/Match';
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {SendTable} from "../../Data/SendTable";
|
||||||
|
|
||||||
export abstract class Parser {
|
export abstract class Parser {
|
||||||
type: any;
|
type: any;
|
||||||
|
|
@ -16,5 +18,5 @@ export abstract class Parser {
|
||||||
this.match = match;
|
this.match = match;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract parse();
|
abstract parse():Packet[]|string|SendTable[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import {PacketStringTable} from '../../packetstringtable';
|
import {PacketStringTable} from './PacketStringTable';
|
||||||
|
|
||||||
import {Packet} from "../../Data/Packet";
|
import {Packet} from "../../Data/Packet";
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
export function CreateStringTable(stream: BitStream): Packet { // 12: createStringTable
|
export function CreateStringTable(stream: BitStream): Packet { // 12: createStringTable
|
||||||
const stringTable = new PacketStringTable(stream);
|
const tables = PacketStringTable(stream);
|
||||||
const tables = stringTable.parse();
|
|
||||||
return {
|
return {
|
||||||
packetType: 'createStringTable',
|
packetType: 'createStringTable',
|
||||||
table: tables
|
table: tables
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,18 @@ 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";
|
||||||
|
|
||||||
var PVS = {
|
enum PVS {
|
||||||
PRESERVE: 0,
|
PRESERVE = 0,
|
||||||
ENTER: 1,
|
ENTER = 1,
|
||||||
LEAVE: 2,
|
LEAVE = 2,
|
||||||
DELETE: 4
|
DELETE = 4
|
||||||
};
|
}
|
||||||
|
|
||||||
function readPVSType(stream) {
|
function readPVSType(stream: BitStream): number {
|
||||||
// https://github.com/skadistats/smoke/blob/a2954fbe2fa3936d64aee5b5567be294fef228e6/smoke/io/stream/entity.pyx#L24
|
// https://github.com/skadistats/smoke/blob/a2954fbe2fa3936d64aee5b5567be294fef228e6/smoke/io/stream/entity.pyx#L24
|
||||||
var pvs;
|
let pvs;
|
||||||
var hi = stream.readBoolean();
|
const hi = stream.readBoolean();
|
||||||
var low = stream.readBoolean();
|
const low = stream.readBoolean();
|
||||||
if (low && !hi) {
|
if (low && !hi) {
|
||||||
pvs = PVS.ENTER;
|
pvs = PVS.ENTER;
|
||||||
} else if (!(hi || low)) {
|
} else if (!(hi || low)) {
|
||||||
|
|
@ -30,27 +30,33 @@ function readPVSType(stream) {
|
||||||
return pvs;
|
return pvs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readEnterPVS(stream, entityId, match, baseLine) {
|
function readEnterPVS(stream: BitStream, entityId: number, match: Match, baseLine: number):Entity {
|
||||||
// https://github.com/PazerOP/DemoLib/blob/5f9467650f942a4a70f9ec689eadcd3e0a051956/TF2Net/NetMessages/NetPacketEntitiesMessage.cs#L198
|
// https://github.com/PazerOP/DemoLib/blob/5f9467650f942a4a70f9ec689eadcd3e0a051956/TF2Net/NetMessages/NetPacketEntitiesMessage.cs#L198
|
||||||
var serverClass = match.serverClasses[stream.readBits(match.classBits)];
|
const serverClass = match.serverClasses[stream.readBits(match.classBits)];
|
||||||
console.log(serverClass);
|
console.log(serverClass);
|
||||||
var sendTable = match.getSendTable(serverClass.dataTable);
|
const sendTable = match.getSendTable(serverClass.dataTable);
|
||||||
var serialNumber = stream.readBits(10);
|
const serialNumber = stream.readBits(10);
|
||||||
|
if (!sendTable) {
|
||||||
|
throw new Error('Unknown SendTable for serverclass');
|
||||||
|
}
|
||||||
|
|
||||||
var entity = (match.entities[entityId]) ? match.entities[entityId] : new Entity(serverClass, sendTable, entityId, serialNumber);
|
let entity = match.entities[entityId];
|
||||||
|
if (!entity) {
|
||||||
|
entity = new Entity(serverClass, sendTable, entityId, serialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
var decodedBaseLine = match.instanceBaselines[baseLine][entityId];
|
const decodedBaseLine = match.instanceBaselines[baseLine][entityId];
|
||||||
if (decodedBaseLine) {
|
if (decodedBaseLine) {
|
||||||
for (var i = 0; i < decodedBaseLine.length; i++) {
|
for (let i = 0; i < decodedBaseLine.length; i++) {
|
||||||
var newProp = decodedBaseLine[i];
|
const newProp = decodedBaseLine[i];
|
||||||
if (!entity.getPropByDefinition(newProp.definition)) {
|
if (!entity.getPropByDefinition(newProp.definition)) {
|
||||||
entity.props.push(newProp.clone(entity));
|
entity.props.push(newProp.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var staticBaseLine = match.staticBaseLines[serverClass.id];
|
const staticBaseLine = match.staticBaseLines[serverClass.id];
|
||||||
if (staticBaseLine) {
|
if (staticBaseLine) {
|
||||||
var streamStart = staticBaseLine._index;
|
const streamStart = staticBaseLine._index;
|
||||||
applyEntityUpdate(entity, staticBaseLine);
|
applyEntityUpdate(entity, staticBaseLine);
|
||||||
staticBaseLine._index = streamStart;
|
staticBaseLine._index = streamStart;
|
||||||
}
|
}
|
||||||
|
|
@ -70,24 +76,20 @@ export function PacketEntities(stream: BitStream, events: GameEventDefinition[],
|
||||||
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/DP/Entity.cs
|
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/DP/Entity.cs
|
||||||
// https://github.com/PazerOP/DemoLib/blob/5f9467650f942a4a70f9ec689eadcd3e0a051956/TF2Net/NetMessages/NetPacketEntitiesMessage.cs
|
// https://github.com/PazerOP/DemoLib/blob/5f9467650f942a4a70f9ec689eadcd3e0a051956/TF2Net/NetMessages/NetPacketEntitiesMessage.cs
|
||||||
// todo
|
// todo
|
||||||
var maxEntries = stream.readBits(11);
|
const maxEntries = stream.readBits(11);
|
||||||
var isDelta = !!stream.readBits(1);
|
const isDelta = !!stream.readBits(1);
|
||||||
if (isDelta) {
|
const delta = (isDelta) ? stream.readInt32() : null;
|
||||||
var delta = stream.readInt32();
|
const baseLine = stream.readBits(1);
|
||||||
} else {
|
const updatedEntries = stream.readBits(11);
|
||||||
delta = null;
|
const length = stream.readBits(20);
|
||||||
}
|
const updatedBaseLine = stream.readBoolean();
|
||||||
var baseLine = stream.readBits(1);
|
const end = stream._index + length;
|
||||||
var updatedEntries = stream.readBits(11);
|
let entityId = -1
|
||||||
var length = stream.readBits(20);
|
|
||||||
var updatedBaseLine = stream.readBoolean();
|
|
||||||
var end = stream._index + length;
|
|
||||||
var entityId = -1;
|
|
||||||
|
|
||||||
stream._index = end;
|
stream._index = end;
|
||||||
return {
|
return {
|
||||||
packetType: 'packetEntities',
|
packetType: 'packetEntities',
|
||||||
entities: entities
|
entities: entities
|
||||||
};
|
};
|
||||||
|
|
||||||
if (updatedBaseLine) {
|
if (updatedBaseLine) {
|
||||||
|
|
@ -100,29 +102,31 @@ export function PacketEntities(stream: BitStream, events: GameEventDefinition[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < updatedEntries; i++) {
|
for (let i = 0; i < updatedEntries; i++) {
|
||||||
var diff = readUBitVar(stream);
|
const diff = readUBitVar(stream);
|
||||||
console.log(diff);
|
|
||||||
entityId += 1 + diff;
|
entityId += 1 + diff;
|
||||||
var pvs = readPVSType(stream);
|
const pvs = readPVSType(stream);
|
||||||
if (pvs === PVS.ENTER) {
|
if (pvs === PVS.ENTER) {
|
||||||
var entity = readEnterPVS(stream, entityId, match, baseLine);
|
const entity = readEnterPVS(stream, entityId, match, baseLine);
|
||||||
applyEntityUpdate(entity, stream);
|
applyEntityUpdate(entity, stream);
|
||||||
match.entities[entityId] = entity;
|
match.entities[entityId] = entity;
|
||||||
|
|
||||||
if (updatedBaseLine) {
|
if (updatedBaseLine) {
|
||||||
match.instanceBaselines[baseLine][entityId] = [].concat(entity.props);
|
const newBaseLine:SendProp[] = [];
|
||||||
|
newBaseLine.concat(entity.props);
|
||||||
|
match.instanceBaselines[baseLine][entityId] = newBaseLine;
|
||||||
}
|
}
|
||||||
entity.inPVS = true;
|
entity.inPVS = true;
|
||||||
} else if (pvs === PVS.PRESERVE) {
|
} else if (pvs === PVS.PRESERVE) {
|
||||||
entity = match.entities[entityId];
|
const entity = match.entities[entityId];
|
||||||
if (!entity) {
|
if (entity) {
|
||||||
|
applyEntityUpdate(entity, stream);
|
||||||
|
} else {
|
||||||
console.log(entityId, match.entities.length);
|
console.log(entityId, match.entities.length);
|
||||||
throw new Error("unknown entity");
|
throw new Error("unknown entity");
|
||||||
}
|
}
|
||||||
applyEntityUpdate(entity, stream);
|
|
||||||
} else {
|
} else {
|
||||||
entity = match.entities[entityId];
|
const entity = match.entities[entityId];
|
||||||
if (entity) {
|
if (entity) {
|
||||||
entity.inPVS = false;
|
entity.inPVS = false;
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +136,7 @@ export function PacketEntities(stream: BitStream, events: GameEventDefinition[],
|
||||||
|
|
||||||
if (isDelta) {
|
if (isDelta) {
|
||||||
while (stream.readBoolean()) {
|
while (stream.readBoolean()) {
|
||||||
var ent = stream.readBits(11);
|
const ent = stream.readBits(11);
|
||||||
match.entities[ent] = null;
|
match.entities[ent] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,29 +144,29 @@ export function PacketEntities(stream: BitStream, events: GameEventDefinition[],
|
||||||
stream._index = end;
|
stream._index = end;
|
||||||
return {
|
return {
|
||||||
packetType: 'packetEntities',
|
packetType: 'packetEntities',
|
||||||
entities: entities
|
entities: entities
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
var readFieldIndex = function (stream, lastIndex) {
|
const readFieldIndex = function (stream: BitStream, lastIndex: number): number {
|
||||||
if (!stream.readBoolean()) {
|
if (!stream.readBoolean()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
var diff = readUBitVar(stream);
|
const diff = readUBitVar(stream);
|
||||||
return lastIndex + diff + 1;
|
return lastIndex + diff + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
var applyEntityUpdate = function (entity, stream) {
|
const applyEntityUpdate = function (entity: Entity, stream: BitStream): Entity {
|
||||||
var index = -1;
|
let index = -1;
|
||||||
var allProps = entity.sendTable.flattenedProps;
|
const allProps = entity.sendTable.flattenedProps;
|
||||||
while ((index = readFieldIndex(stream, index)) != -1) {
|
while ((index = readFieldIndex(stream, index)) != -1) {
|
||||||
if (index > 4096) {
|
if (index > 4096) {
|
||||||
throw new Error('prop index out of bounds');
|
throw new Error('prop index out of bounds');
|
||||||
}
|
}
|
||||||
console.log(index);
|
console.log(index);
|
||||||
var propDefinition = allProps[index];
|
const propDefinition = allProps[index];
|
||||||
var existingProp = entity.getPropByDefinition(propDefinition);
|
const existingProp = entity.getPropByDefinition(propDefinition);
|
||||||
var prop;
|
let prop;
|
||||||
if (existingProp) {
|
if (existingProp) {
|
||||||
prop = existingProp;
|
prop = existingProp;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -178,7 +182,7 @@ var applyEntityUpdate = function (entity, stream) {
|
||||||
return entity;
|
return entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
var readUBitVar = function (stream) {
|
const readUBitVar = function (stream: BitStream): number {
|
||||||
switch (stream.readBits(2)) {
|
switch (stream.readBits(2)) {
|
||||||
case 0:
|
case 0:
|
||||||
return stream.readBits(4);
|
return stream.readBits(4);
|
||||||
|
|
@ -189,4 +193,5 @@ var readUBitVar = function (stream) {
|
||||||
case 3:
|
case 3:
|
||||||
return stream.readBits(32);
|
return stream.readBits(32);
|
||||||
}
|
}
|
||||||
|
throw new Error('Invalid var bit');
|
||||||
};
|
};
|
||||||
|
|
|
||||||
11
src/Parser/Packet/PacketStringTable.ts
Normal file
11
src/Parser/Packet/PacketStringTable.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import {BitStream} from "bit-buffer";
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
|
||||||
|
export function PacketStringTable(stream: BitStream):Packet {
|
||||||
|
//todo
|
||||||
|
// https://coldemoplayer.googlecode.com/svn/branches/2.0/code/plugins/CDP.Source/Messages/SvcCreateStringTable.cs
|
||||||
|
stream._index = stream._view._view.byteLength * 8;
|
||||||
|
return {
|
||||||
|
packetType: 'stringTableTODO'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
import {PacketStringTable} from '../../packetstringtable';
|
import {PacketStringTable} from './PacketStringTable';
|
||||||
import {Packet} from "../../Data/Packet";
|
import {Packet} from "../../Data/Packet";
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
export function UpdateStringTable(stream: BitStream): Packet { // 12: updateStringTable
|
export function UpdateStringTable(stream: BitStream): Packet { // 12: updateStringTable
|
||||||
const stringTable = new PacketStringTable(stream);
|
const tables = PacketStringTable(stream);
|
||||||
const tables = stringTable.parse();
|
|
||||||
return {
|
return {
|
||||||
packetType: 'updateStringTable',
|
packetType: 'updateStringTable',
|
||||||
table: tables
|
table: tables
|
||||||
|
|
|
||||||
|
|
@ -1,86 +1,87 @@
|
||||||
import {Packet} from "../../Data/Packet";
|
import {Packet} from "../../Data/Packet";
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
import {make} from './ParserGenerator';
|
import {make} from './ParserGenerator';
|
||||||
|
import {SayText2} from '../UserMessage/SayText2';
|
||||||
|
|
||||||
|
enum UserMessageType{
|
||||||
|
Geiger = 0,
|
||||||
|
Train = 1,
|
||||||
|
HudText = 2,
|
||||||
|
SayText = 3,
|
||||||
|
SayText2 = 4,
|
||||||
|
TextMsg = 5,
|
||||||
|
ResetHUD = 6,
|
||||||
|
GameTitle = 7,
|
||||||
|
ItemPickup = 8,
|
||||||
|
ShowMenu = 9,
|
||||||
|
Shake = 10,
|
||||||
|
Fade = 11,
|
||||||
|
VGUIMenu = 12,
|
||||||
|
Rumble = 13,
|
||||||
|
CloseCaption = 14,
|
||||||
|
SendAudio = 15,
|
||||||
|
VoiceMask = 16,
|
||||||
|
RequestState = 17,
|
||||||
|
Damage = 18,
|
||||||
|
HintText = 19,
|
||||||
|
KeyHintText = 20,
|
||||||
|
HudMsg = 21,
|
||||||
|
AmmoDenied = 22,
|
||||||
|
AchievementEvent = 23,
|
||||||
|
UpdateRadar = 24,
|
||||||
|
VoiceSubtitle = 25,
|
||||||
|
HudNotify = 26,
|
||||||
|
HudNotifyCustom = 27,
|
||||||
|
PlayerStatsUpdate = 28,
|
||||||
|
PlayerIgnited = 29,
|
||||||
|
PlayerIgnitedInv = 30,
|
||||||
|
HudArenaNotify = 31,
|
||||||
|
UpdateAchievement = 32,
|
||||||
|
TrainingMsg = 33,
|
||||||
|
TrainingObjective = 34,
|
||||||
|
DamageDodged = 35,
|
||||||
|
PlayerJarated = 36,
|
||||||
|
PlayerExtinguished = 37,
|
||||||
|
PlayerJaratedFade = 38,
|
||||||
|
PlayerShieldBlocked = 39,
|
||||||
|
BreakModel = 40,
|
||||||
|
CheapBreakModel = 41,
|
||||||
|
BreakModel_Pumpkin = 42,
|
||||||
|
BreakModelRocketDud = 43,
|
||||||
|
CallVoteFailed = 44,
|
||||||
|
VoteStart = 45,
|
||||||
|
VotePass = 46,
|
||||||
|
VoteFailed = 47,
|
||||||
|
VoteSetup = 48,
|
||||||
|
PlayerBonusPoints = 49,
|
||||||
|
SpawnFlyingBird = 50,
|
||||||
|
PlayerGodRayEffect = 51,
|
||||||
|
SPHapWeapEvent = 52,
|
||||||
|
HapDmg = 53,
|
||||||
|
HapPunch = 54,
|
||||||
|
HapSetDrag = 55,
|
||||||
|
HapSet = 56,
|
||||||
|
HapMeleeContact = 57
|
||||||
|
}
|
||||||
|
|
||||||
const userMessageParsers = {
|
const userMessageParsers = {
|
||||||
4: require('../../handlers/userMessage/SayText2'),
|
4: SayText2,
|
||||||
5: make('textMsg', 'destType{8}text{s}')
|
5: make('textMsg', 'destType{8}text{s}')
|
||||||
};
|
};
|
||||||
|
|
||||||
export function UserMessage(stream: BitStream): Packet { // 23: user message
|
export function UserMessage(stream: BitStream): Packet { // 23: user message
|
||||||
const type = stream.readBits(8);
|
const type = stream.readBits(8);
|
||||||
const length = stream.readBits(11);
|
const length = stream.readBits(11);
|
||||||
const pos = stream._index;
|
const pos = stream._index;
|
||||||
let result;
|
let result;
|
||||||
if (userMessageParsers[type]) {
|
if (userMessageParsers[type]) {
|
||||||
result = userMessageParsers[type](stream);
|
result = userMessageParsers[type](stream);
|
||||||
} else {
|
} else {
|
||||||
result = {
|
result = {
|
||||||
packetType: 'unknownUserMessage',
|
packetType: 'unknownUserMessage',
|
||||||
type: type
|
type: type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream._index = pos + length;
|
stream._index = pos + length;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var UserMessageType = {
|
|
||||||
Geiger: 0,
|
|
||||||
Train: 1,
|
|
||||||
HudText: 2,
|
|
||||||
SayText: 3,
|
|
||||||
SayText2: 4,
|
|
||||||
TextMsg: 5,
|
|
||||||
ResetHUD: 6,
|
|
||||||
GameTitle: 7,
|
|
||||||
ItemPickup: 8,
|
|
||||||
ShowMenu: 9,
|
|
||||||
Shake: 10,
|
|
||||||
Fade: 11,
|
|
||||||
VGUIMenu: 12,
|
|
||||||
Rumble: 13,
|
|
||||||
CloseCaption: 14,
|
|
||||||
SendAudio: 15,
|
|
||||||
VoiceMask: 16,
|
|
||||||
RequestState: 17,
|
|
||||||
Damage: 18,
|
|
||||||
HintText: 19,
|
|
||||||
KeyHintText: 20,
|
|
||||||
HudMsg: 21,
|
|
||||||
AmmoDenied: 22,
|
|
||||||
AchievementEvent: 23,
|
|
||||||
UpdateRadar: 24,
|
|
||||||
VoiceSubtitle: 25,
|
|
||||||
HudNotify: 26,
|
|
||||||
HudNotifyCustom: 27,
|
|
||||||
PlayerStatsUpdate: 28,
|
|
||||||
PlayerIgnited: 29,
|
|
||||||
PlayerIgnitedInv: 30,
|
|
||||||
HudArenaNotify: 31,
|
|
||||||
UpdateAchievement: 32,
|
|
||||||
TrainingMsg: 33,
|
|
||||||
TrainingObjective: 34,
|
|
||||||
DamageDodged: 35,
|
|
||||||
PlayerJarated: 36,
|
|
||||||
PlayerExtinguished: 37,
|
|
||||||
PlayerJaratedFade: 38,
|
|
||||||
PlayerShieldBlocked: 39,
|
|
||||||
BreakModel: 40,
|
|
||||||
CheapBreakModel: 41,
|
|
||||||
BreakModel_Pumpkin: 42,
|
|
||||||
BreakModelRocketDud: 43,
|
|
||||||
CallVoteFailed: 44,
|
|
||||||
VoteStart: 45,
|
|
||||||
VotePass: 46,
|
|
||||||
VoteFailed: 47,
|
|
||||||
VoteSetup: 48,
|
|
||||||
PlayerBonusPoints: 49,
|
|
||||||
SpawnFlyingBird: 50,
|
|
||||||
PlayerGodRayEffect: 51,
|
|
||||||
SPHapWeapEvent: 52,
|
|
||||||
HapDmg: 53,
|
|
||||||
HapPunch: 54,
|
|
||||||
HapSetDrag: 55,
|
|
||||||
HapSet: 56,
|
|
||||||
HapMeleeContact: 57
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ export class SendPropParser {
|
||||||
case SendPropType.DPT_Array:
|
case SendPropType.DPT_Array:
|
||||||
return SendPropParser.readArray(propDefinition, stream);
|
return SendPropParser.readArray(propDefinition, stream);
|
||||||
}
|
}
|
||||||
|
throw new Error('Unknown property type');
|
||||||
}
|
}
|
||||||
|
|
||||||
static readInt(propDefinition: SendPropDefinition, stream: BitStream) {
|
static readInt(propDefinition: SendPropDefinition, stream: BitStream) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
module.exports = function (stream) { // 4: SayText2
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
export function SayText2(stream: BitStream): Packet { // 4: SayText2
|
||||||
var client = stream.readBits(8);
|
var client = stream.readBits(8);
|
||||||
var raw = stream.readBits(8);
|
var raw = stream.readBits(8);
|
||||||
var pos = stream._index;
|
var pos = stream._index;
|
||||||
var from, text, kind, arg1, arg2;
|
var from, text, kind, arg1, arg2;
|
||||||
if (stream.readBits(8) === 1) {
|
if (stream.readBits(8) === 1) {
|
||||||
var first = stream.readBits(8);
|
var first = stream.readBits(8);
|
||||||
|
|
@ -14,16 +17,16 @@ module.exports = function (stream) { // 4: SayText2
|
||||||
if (text.substr(0, 6) === '*DEAD*') {
|
if (text.substr(0, 6) === '*DEAD*') {
|
||||||
// grave talk is in the format '*DEAD* \u0003$from\u0001: $text'
|
// grave talk is in the format '*DEAD* \u0003$from\u0001: $text'
|
||||||
var start = text.indexOf('\u0003');
|
var start = text.indexOf('\u0003');
|
||||||
var end = text.indexOf('\u0001');
|
var end = text.indexOf('\u0001');
|
||||||
from = text.substr(start + 1, end - start - 1);
|
from = text.substr(start + 1, end - start - 1);
|
||||||
text = text.substr(end + 5);
|
text = text.substr(end + 5);
|
||||||
kind = 'TF_Chat_AllDead';
|
kind = 'TF_Chat_AllDead';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stream._index = pos;
|
stream._index = pos;
|
||||||
kind = stream.readUTF8String();
|
kind = stream.readUTF8String();
|
||||||
from = stream.readUTF8String();
|
from = stream.readUTF8String();
|
||||||
text = stream.readUTF8String();
|
text = stream.readUTF8String();
|
||||||
stream.readASCIIString();
|
stream.readASCIIString();
|
||||||
stream.readASCIIString();
|
stream.readASCIIString();
|
||||||
}
|
}
|
||||||
|
|
@ -35,10 +38,10 @@ module.exports = function (stream) { // 4: SayText2
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
packetType: 'sayText2',
|
packetType: 'sayText2',
|
||||||
client : client,
|
client: client,
|
||||||
raw : raw,
|
raw: raw,
|
||||||
kind : kind,
|
kind: kind,
|
||||||
from : from,
|
from: from,
|
||||||
text : text
|
text: text
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,16 +1,88 @@
|
||||||
var util = require('util');
|
import {Match} from "./Data/Match";
|
||||||
var Parser = require('./parser');
|
import {BitStream} from 'bit-buffer';
|
||||||
var BitStream = require('bit-buffer').BitStream;
|
import {Parser, MessageType} from './Parser';
|
||||||
|
import {Stream} from "stream";
|
||||||
|
|
||||||
var StreamParser = function (stream) {
|
export class StreamParser extends Parser {
|
||||||
this.stream = stream;
|
buffer: Buffer;
|
||||||
this.match = new Match();
|
header: any;
|
||||||
this.on('packet', this, match.handlePacket.bind(this.match));
|
sourceStream: Stream;
|
||||||
this.header = null;
|
|
||||||
this.buffer = new Buffer(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
util.inherits(StreamParser, Parser);
|
constructor(stream) {
|
||||||
|
super(new BitStream(new ArrayBuffer(0)));
|
||||||
|
this.sourceStream = stream;
|
||||||
|
this.on('packet', this.match.handlePacket.bind(this.match));
|
||||||
|
this.header = null;
|
||||||
|
this.buffer = new Buffer(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
eatBuffer(length) {
|
||||||
|
this.buffer = shrinkBuffer(this.buffer, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.sourceStream.on('data', this.handleData.bind(this));
|
||||||
|
this.sourceStream.on('end', function () {
|
||||||
|
this.emit('done', this.match);
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleData(data) {
|
||||||
|
this.buffer = Buffer.concat([this.buffer, data]);
|
||||||
|
if (this.header === null) {
|
||||||
|
if (this.buffer.length > 1072) {
|
||||||
|
this.header = this.parseHeader(new BitStream(this.buffer));
|
||||||
|
this.eatBuffer(1072);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.readStreamMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readStreamMessage() {
|
||||||
|
if (this.buffer.length < 9) { // 9 byte minimum message header (type, tick, length)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const stream = new BitStream(this.buffer);
|
||||||
|
const type = stream.readBits(8);
|
||||||
|
if (type === MessageType.Stop) {
|
||||||
|
console.log('stop');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tick = stream.readInt32();
|
||||||
|
|
||||||
|
let headerSize = 5;
|
||||||
|
let extraHeader = 0;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case MessageType.Sigon:
|
||||||
|
case MessageType.Packet:
|
||||||
|
extraHeader += 0x54; // command/sequence info
|
||||||
|
break;
|
||||||
|
case MessageType.UserCmd:
|
||||||
|
extraHeader += 0x04; // unknown / outgoing sequence
|
||||||
|
break;
|
||||||
|
case MessageType.Stop:
|
||||||
|
case MessageType.SyncTick:
|
||||||
|
this.eatBuffer(headerSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stream.byteIndex += extraHeader;
|
||||||
|
const length = stream.readInt32();
|
||||||
|
headerSize += extraHeader + 4;
|
||||||
|
|
||||||
|
if (this.buffer.length < (headerSize + length)) {
|
||||||
|
console.log('wants ' + length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('got message ' + tick);
|
||||||
|
const messageBuffer = this.buffer.slice(headerSize, headerSize + length);
|
||||||
|
this.eatBuffer(headerSize + length);
|
||||||
|
const message = this.parseMessage(messageBuffer, type, tick, length, this.match);
|
||||||
|
this.handleMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function shrinkBuffer(buffer, length) {
|
function shrinkBuffer(buffer, length) {
|
||||||
if (length < 0) {
|
if (length < 0) {
|
||||||
|
|
@ -18,72 +90,3 @@ function shrinkBuffer(buffer, length) {
|
||||||
}
|
}
|
||||||
return buffer.slice(length, buffer.length);
|
return buffer.slice(length, buffer.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamParser.prototype.eatBuffer = function (length) {
|
|
||||||
this.buffer = shrinkBuffer(this.buffer, length);
|
|
||||||
};
|
|
||||||
|
|
||||||
StreamParser.prototype.start = function () {
|
|
||||||
this.stream.on('data', this.handleData.bind(this));
|
|
||||||
this.stream.on('end', function () {
|
|
||||||
this.emit('done', this.match);
|
|
||||||
}.bind(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
StreamParser.prototype.handleData = function (data) {
|
|
||||||
this.buffer = Buffer.concat([this.buffer, data]);
|
|
||||||
if (this.header === null) {
|
|
||||||
if (this.buffer.length > 1072) {
|
|
||||||
this.header = this.parseHeader(new BitStream(this.buffer));
|
|
||||||
this.eatBuffer(1072);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.readMessage();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
StreamParser.prototype.readMessage = function () {
|
|
||||||
if (this.buffer.length < 9) { // 9 byte minimum message header (type, tick, length)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var stream = new BitStream(this.buffer);
|
|
||||||
var type = stream.readBits(8);
|
|
||||||
if (type === Parser.MessageType.Stop) {
|
|
||||||
console.log('stop');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var tick = stream.readInt32();
|
|
||||||
|
|
||||||
var headerSize = 5;
|
|
||||||
var extraHeader = 0;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Parser.MessageType.Sigon:
|
|
||||||
case Parser.MessageType.Packet:
|
|
||||||
extraHeader += 0x54; // command/sequence info
|
|
||||||
break;
|
|
||||||
case Parser.MessageType.UserCmd:
|
|
||||||
extraHeader += 0x04; // unknown / outgoing sequence
|
|
||||||
break;
|
|
||||||
case Parser.MessageType.Stop:
|
|
||||||
case Parser.MessageType.SyncTick:
|
|
||||||
this.eatBuffer(headerSize);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stream.byteIndex += extraHeader;
|
|
||||||
var length = stream.readInt32();
|
|
||||||
headerSize += extraHeader + 4;
|
|
||||||
|
|
||||||
if (this.buffer.length < (headerSize + length)) {
|
|
||||||
console.log('wants ' + length);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('got message ' + tick);
|
|
||||||
var messageBuffer = this.buffer.slice(headerSize, headerSize + length);
|
|
||||||
this.eatBuffer(headerSize + length);
|
|
||||||
var message = this.parseMessage(messageBuffer, type, tick, length);
|
|
||||||
this.handleMessage(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = StreamParser;
|
|
||||||
|
|
|
||||||
43
src/demo.ts
43
src/demo.ts
|
|
@ -1,43 +0,0 @@
|
||||||
var BitStream = require('bit-buffer').BitStream;
|
|
||||||
var Parser = require('./parser');
|
|
||||||
var StreamParser = require('./StreamParser');
|
|
||||||
|
|
||||||
var Demo = function (arrayBuffer) {
|
|
||||||
this.stream = new BitStream(arrayBuffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
Demo.prototype.getParser = function () {
|
|
||||||
return new Parser(this.stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
var StreamDemo = function (nodeStream) {
|
|
||||||
this.stream = nodeStream;
|
|
||||||
};
|
|
||||||
|
|
||||||
StreamDemo.prototype.getParser = function () {
|
|
||||||
return new StreamParser(this.stream);
|
|
||||||
};
|
|
||||||
|
|
||||||
Demo.fromNodeBuffer = function (nodeBuffer) {
|
|
||||||
var arrayBuffer = new ArrayBuffer(nodeBuffer.length);
|
|
||||||
var view = new Uint8Array(arrayBuffer);
|
|
||||||
for (var i = 0; i < nodeBuffer.length; ++i) {
|
|
||||||
view[i] = nodeBuffer[i];
|
|
||||||
}
|
|
||||||
return new Demo(arrayBuffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
Demo.fromNodeStream = function (nodeStream) {
|
|
||||||
return new StreamDemo(nodeStream);
|
|
||||||
};
|
|
||||||
|
|
||||||
Demo.fromPath = function (path) {
|
|
||||||
var arrayBuffer = new ArrayBuffer(nodeBuffer.length);
|
|
||||||
var view = new Uint8Array(arrayBuffer);
|
|
||||||
for (var i = 0; i < nodeBuffer.length; ++i) {
|
|
||||||
view[i] = nodeBuffer[i];
|
|
||||||
}
|
|
||||||
return new Demo(arrayBuffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Demo;
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
export class PacketStringTable {
|
|
||||||
constructor(stream) {
|
|
||||||
this.stream = stream;
|
|
||||||
this.id = PacketStringTable.tables.length;
|
|
||||||
this.strings = [];
|
|
||||||
this.numEntries = 0;
|
|
||||||
this.name = '';
|
|
||||||
PacketStringTable.tables.push(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
parse() {
|
|
||||||
//todo
|
|
||||||
// https://coldemoplayer.googlecode.com/svn/branches/2.0/code/plugins/CDP.Source/Messages/SvcCreateStringTable.cs
|
|
||||||
this.stream._index = this.stream._view._view.length * 8;
|
|
||||||
return {
|
|
||||||
packetType: 'stringTableTODO'
|
|
||||||
};
|
|
||||||
//return this.searchIds();
|
|
||||||
}
|
|
||||||
|
|
||||||
parsePlayerInfo() {
|
|
||||||
console.log('name: ' + this.stream.readUTF8String());
|
|
||||||
}
|
|
||||||
|
|
||||||
// "fuckit" parsing, look for anything that looks like a steam id, user id is the 32 bit before that
|
|
||||||
searchIds() {
|
|
||||||
var validChar = function (charCode) {
|
|
||||||
return charCode === 91 || charCode === 93 || charCode === 58 || (charCode > 47 && charCode < 58); // [ ] : 0-9
|
|
||||||
};
|
|
||||||
var users = {};
|
|
||||||
var numFound = 0;
|
|
||||||
while (true) {
|
|
||||||
var found = false;
|
|
||||||
while (this.stream._index < ((this.stream._view._view.length - 1) * 8)) {
|
|
||||||
var startPos = this.stream._index;
|
|
||||||
try {
|
|
||||||
if (this.stream.readASCIIString(3) === '[U:') {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.stream._index = startPos + 1;
|
|
||||||
} catch (e) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
if (numFound) {
|
|
||||||
//console.log(users);
|
|
||||||
}
|
|
||||||
this.stream._index = this.stream._view._view.length * 8;
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
while (validChar(this.stream.readBits(8))) {
|
|
||||||
// seek
|
|
||||||
}
|
|
||||||
var endPos = this.stream._index - 8;
|
|
||||||
var length = (endPos / 8) - (startPos / 8);
|
|
||||||
this.stream._index = startPos - 32;
|
|
||||||
var userId = this.stream.readBits(32);
|
|
||||||
var steamId = this.stream.readASCIIString(length);
|
|
||||||
if (steamId[steamId.length - 1] !== ']') {
|
|
||||||
steamId += ']';
|
|
||||||
}
|
|
||||||
users[userId] = steamId;
|
|
||||||
numFound++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PacketStringTable.tables = [];
|
|
||||||
143
src/parser.ts
143
src/parser.ts
|
|
@ -1,143 +0,0 @@
|
||||||
import * as toBuffer from 'typedarray-to-buffer'
|
|
||||||
import {Packet} from './Parser/Message/Packet';
|
|
||||||
import {ConsoleCmd} from './Parser/Message/ConsoleCmd';
|
|
||||||
import {StringTable} from './Parser/Message/StringTable';
|
|
||||||
import {DataTable} from './Parser/Message/DataTable';
|
|
||||||
import {UserCmd} from './Parser/Message/UserCmd';
|
|
||||||
import {BitStream} from 'bit-buffer';
|
|
||||||
import {EventEmitter} from 'events';
|
|
||||||
import {Match} from './Data/Match';
|
|
||||||
|
|
||||||
class Parser extends EventEmitter {
|
|
||||||
constructor(stream) {
|
|
||||||
super();
|
|
||||||
this.stream = stream;
|
|
||||||
this.packets = [];
|
|
||||||
this.match = new Match();
|
|
||||||
this.on('packet', this.match.handlePacket.bind(this.match));
|
|
||||||
this.on('packet', function (packet) {
|
|
||||||
this.packets.push(packet);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
readHeader() {
|
|
||||||
return this.parseHeader(this.stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
parseHeader(stream) {
|
|
||||||
return {
|
|
||||||
'type' : stream.readASCIIString(8),
|
|
||||||
'version' : stream.readInt32(),
|
|
||||||
'protocol': stream.readInt32(),
|
|
||||||
'server' : stream.readASCIIString(260),
|
|
||||||
'nick' : stream.readASCIIString(260),
|
|
||||||
'map' : stream.readASCIIString(260),
|
|
||||||
'game' : stream.readASCIIString(260),
|
|
||||||
'duration': stream.readFloat32(),
|
|
||||||
'ticks' : stream.readInt32(),
|
|
||||||
'frames' : stream.readInt32(),
|
|
||||||
'sigon' : stream.readInt32()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parseBody() {
|
|
||||||
var message;
|
|
||||||
while (message = this.readMessage(this.stream, this.match)) {
|
|
||||||
this.handleMessage(message);
|
|
||||||
}
|
|
||||||
this.emit('done', this.match);
|
|
||||||
return this.match;
|
|
||||||
}
|
|
||||||
|
|
||||||
parseMessage(buffer, type, tick, length, viewOrigin, match) {
|
|
||||||
var data = new BitStream(buffer);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Parser.MessageType.Sigon:
|
|
||||||
case Parser.MessageType.Packet:
|
|
||||||
return new Packet(type, tick, data, length, viewOrigin, match);
|
|
||||||
case Parser.MessageType.ConsoleCmd:
|
|
||||||
return new ConsoleCmd(type, tick, data, length, match);
|
|
||||||
case Parser.MessageType.UserCmd:
|
|
||||||
return new UserCmd(type, tick, data, length, match);
|
|
||||||
case Parser.MessageType.DataTables:
|
|
||||||
return new DataTable(type, tick, data, length, match);
|
|
||||||
case Parser.MessageType.StringTables:
|
|
||||||
return new StringTable(type, tick, data, length, match);
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleMessage(message) {
|
|
||||||
if (message.parse) {
|
|
||||||
var packets = message.parse();
|
|
||||||
for (var i = 0; i < packets.length; i++) {
|
|
||||||
var packet = packets[i];
|
|
||||||
if (packet) {
|
|
||||||
this.emit('packet', packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readMessage(stream, match) {
|
|
||||||
var type = stream.readBits(8);
|
|
||||||
if (type === Parser.MessageType.Stop) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var tick = stream.readInt32();
|
|
||||||
var start, length, buffer;
|
|
||||||
|
|
||||||
var viewOrigin = [];
|
|
||||||
var viewAngles = [];
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case Parser.MessageType.Sigon:
|
|
||||||
case Parser.MessageType.Packet:
|
|
||||||
this.stream.readInt32(); // flags
|
|
||||||
for (var j = 0; j < 2; j++) {
|
|
||||||
viewOrigin[j] = [];
|
|
||||||
viewAngles[j] = [];
|
|
||||||
for (var i = 0; i < 3; i++) {
|
|
||||||
viewOrigin[j][i] = this.stream.readInt32();
|
|
||||||
}
|
|
||||||
for (i = 0; i < 3; i++) {
|
|
||||||
viewAngles[j][i] = this.stream.readInt32();
|
|
||||||
}
|
|
||||||
for (i = 0; i < 3; i++) {
|
|
||||||
this.stream.readInt32(); // local viewAngles
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.stream.readInt32(); // sequence in
|
|
||||||
this.stream.readInt32(); // sequence out
|
|
||||||
break;
|
|
||||||
case Parser.MessageType.UserCmd:
|
|
||||||
stream.byteIndex += 0x04; // unknown / outgoing sequence
|
|
||||||
break;
|
|
||||||
case Parser.MessageType.Stop:
|
|
||||||
return false;
|
|
||||||
case Parser.MessageType.SyncTick:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
length = stream.readInt32();
|
|
||||||
start = stream.byteIndex;
|
|
||||||
buffer = toBuffer(stream._view._view.slice(start, start + length));
|
|
||||||
stream.byteIndex += length;
|
|
||||||
return this.parseMessage(buffer, type, tick, length, viewOrigin, match);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Parser.MessageType = {
|
|
||||||
Sigon : 1,
|
|
||||||
Packet : 2,
|
|
||||||
SyncTick : 3,
|
|
||||||
ConsoleCmd : 4,
|
|
||||||
UserCmd : 5,
|
|
||||||
DataTables : 6,
|
|
||||||
Stop : 7,
|
|
||||||
StringTables: 8
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Parser;
|
|
||||||
40
typings/modules/bit-buffer/index.d.ts
vendored
40
typings/modules/bit-buffer/index.d.ts
vendored
|
|
@ -1,40 +0,0 @@
|
||||||
export interface BitView {
|
|
||||||
length: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BitStream {
|
|
||||||
byteIndex: number;
|
|
||||||
buffer: Buffer;
|
|
||||||
_view: BitView;
|
|
||||||
_index: number;
|
|
||||||
readBits(bits: number, signed?: boolean): number;
|
|
||||||
readWrite(value: number, bits: number);
|
|
||||||
|
|
||||||
readBoolean(): boolean;
|
|
||||||
readInt8(): number;
|
|
||||||
readUint8(): number;
|
|
||||||
readInt16(): number;
|
|
||||||
readUint16(): number;
|
|
||||||
readInt32(): number;
|
|
||||||
readUint32(): number;
|
|
||||||
readFloat32(): number;
|
|
||||||
readFloat64(): number;
|
|
||||||
|
|
||||||
writeBoolean(value: number);
|
|
||||||
writeInt8(value: number);
|
|
||||||
writeUint8(value: number);
|
|
||||||
writeInt16(value: number);
|
|
||||||
writeUint16(value: number);
|
|
||||||
writeInt32(value: number);
|
|
||||||
writeUint32(value: number);
|
|
||||||
writeFloat32(value: number);
|
|
||||||
writeFloat64(value: number);
|
|
||||||
|
|
||||||
readASCIIString(length?: number): string;
|
|
||||||
readUTF8String(length?: number): string;
|
|
||||||
|
|
||||||
writeASCIIString(data: string, length?: number);
|
|
||||||
writeUTF8String(data: string, length?: number);
|
|
||||||
|
|
||||||
readBitStream(length: number): BitStream;
|
|
||||||
}
|
|
||||||
5
typings/modules/typedarray-to-buffer/index.d.ts
vendored
Normal file
5
typings/modules/typedarray-to-buffer/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
declare module 'typedarray-to-buffer' {
|
||||||
|
function toBuffer(typedArray:Uint8Array):ArrayBuffer;
|
||||||
|
|
||||||
|
export = toBuffer
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue