mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
more typescript conversions
This commit is contained in:
parent
06860cc3fe
commit
94383f447f
48 changed files with 1204 additions and 1051 deletions
35
src/Data/GameEvent.ts
Normal file
35
src/Data/GameEvent.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
export interface GameEventDefinition {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
entries: GameEventEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GameEvent {
|
||||||
|
name: string;
|
||||||
|
values: GameEventValueMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GameEventEntry {
|
||||||
|
name: string;
|
||||||
|
type: GameEventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum GameEventType {
|
||||||
|
STRING = 1,
|
||||||
|
FLOAT = 2,
|
||||||
|
LONG = 3,
|
||||||
|
SHORT = 4,
|
||||||
|
BYTE = 5,
|
||||||
|
BOOLEAN = 6,
|
||||||
|
LOCAL = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GameEventValue = string|number|boolean;
|
||||||
|
|
||||||
|
export type GameEventValueMap = {
|
||||||
|
[name: string]: GameEventValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GameEventDefinitionMap = {
|
||||||
|
[id: number]: GameEventDefinition;
|
||||||
|
}
|
||||||
168
src/Data/Match.ts
Normal file
168
src/Data/Match.ts
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
import {Entity} from "./Entity";
|
||||||
|
import {ServerClass} from "./ServerClass";
|
||||||
|
import {SendTable} from "./SendTable";
|
||||||
|
import {StringTable} from "./StringTable";
|
||||||
|
export class Match {
|
||||||
|
tick: number;
|
||||||
|
chat: any[];
|
||||||
|
users: any;
|
||||||
|
deaths: any[];
|
||||||
|
rounds: any[];
|
||||||
|
startTick: number;
|
||||||
|
intervalPerTick: number;
|
||||||
|
entities: Entity[];
|
||||||
|
stringTables: StringTable[];
|
||||||
|
serverClasses: ServerClass[];
|
||||||
|
sendTables: SendTable[];
|
||||||
|
instanceBaselines: any[][];
|
||||||
|
staticBaseLines: any[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.tick = 0;
|
||||||
|
this.chat = [];
|
||||||
|
this.users = {};
|
||||||
|
this.deaths = [];
|
||||||
|
this.rounds = [];
|
||||||
|
this.startTick = 0;
|
||||||
|
this.intervalPerTick = 0;
|
||||||
|
this.entities = [];
|
||||||
|
this.stringTables = [];
|
||||||
|
this.sendTables = [];
|
||||||
|
this.serverClasses = [];
|
||||||
|
this.entities = [];
|
||||||
|
this.instanceBaselines = [[], []];
|
||||||
|
this.staticBaseLines = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getSendTable(name) {
|
||||||
|
for (var i = 0; i < this.sendTables.length; i++) {
|
||||||
|
if (this.sendTables[i].name === name) {
|
||||||
|
return this.sendTables[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStringTable(name) {
|
||||||
|
for (var i = 0; i < this.stringTables.length; i++) {
|
||||||
|
if (this.stringTables[i].name === name) {
|
||||||
|
return this.stringTables[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getState() {
|
||||||
|
return {
|
||||||
|
'chat': this.chat,
|
||||||
|
'users': this.users,
|
||||||
|
'deaths': this.deaths,
|
||||||
|
'rounds': this.rounds,
|
||||||
|
'startTick': this.startTick,
|
||||||
|
'intervalPerTick': this.intervalPerTick
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePacket(packet) {
|
||||||
|
var userState;
|
||||||
|
switch (packet.packetType) {
|
||||||
|
case 'netTick':
|
||||||
|
if (this.startTick === 0) {
|
||||||
|
this.startTick = packet.tick;
|
||||||
|
}
|
||||||
|
this.tick = packet.tick;
|
||||||
|
break;
|
||||||
|
case 'serverInfo':
|
||||||
|
this.intervalPerTick = packet.intervalPerTick;
|
||||||
|
break;
|
||||||
|
case 'sayText2':
|
||||||
|
this.chat.push({
|
||||||
|
kind: packet.kind,
|
||||||
|
from: packet.from,
|
||||||
|
text: packet.text,
|
||||||
|
tick: this.tick
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'stringTable':
|
||||||
|
if (packet.tables.userinfo) {
|
||||||
|
for (var j = 0; j < packet.tables.userinfo.length; j++) {
|
||||||
|
if (packet.tables.userinfo[j].extraData) {
|
||||||
|
var name = packet.tables.userinfo[j].extraData[0];
|
||||||
|
var steamId = packet.tables.userinfo[j].extraData[2];
|
||||||
|
var userId = packet.tables.userinfo[j].extraData[1].charCodeAt(0);
|
||||||
|
userState = this.getUserState(userId);
|
||||||
|
userState.name = name;
|
||||||
|
userState.steamId = steamId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'gameEvent':
|
||||||
|
switch (packet.event.name) {
|
||||||
|
case 'player_death':
|
||||||
|
while (packet.event.values.assister > 256 && packet.event.values.assister < (1024 * 16)) {
|
||||||
|
packet.event.values.assister -= 256;
|
||||||
|
}
|
||||||
|
var assister = packet.event.values.assister < 256 ? packet.event.values.assister : null;
|
||||||
|
// todo get player names, not same id as the name string table
|
||||||
|
while (packet.event.values.attacker > 256) {
|
||||||
|
packet.event.values.attacker -= 256;
|
||||||
|
}
|
||||||
|
while (packet.event.values.userid > 256) {
|
||||||
|
packet.event.values.userid -= 256;
|
||||||
|
}
|
||||||
|
this.deaths.push({
|
||||||
|
killer: packet.event.values.attacker,
|
||||||
|
assister: assister,
|
||||||
|
victim: packet.event.values.userid,
|
||||||
|
weapon: packet.event.values.weapon,
|
||||||
|
tick: this.tick
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'teamplay_round_win':
|
||||||
|
if (packet.event.values.winreason !== 6) {// 6 = timelimit
|
||||||
|
this.rounds.push({
|
||||||
|
winner: packet.event.values.team === 2 ? 'red' : 'blue',
|
||||||
|
length: packet.event.values.round_time,
|
||||||
|
end_tick: this.tick
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'player_spawn':
|
||||||
|
userId = packet.event.values.userid;
|
||||||
|
userState = this.getUserState(userId);
|
||||||
|
if (!userState.team) { //only register first spawn
|
||||||
|
userState.team = packet.event.values.team === 2 ? 'red' : 'blue'
|
||||||
|
}
|
||||||
|
var classId = packet.event.values.class;
|
||||||
|
if (!userState.classes[classId]) {
|
||||||
|
userState.classes[classId] = 0;
|
||||||
|
}
|
||||||
|
userState.classes[classId]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getUserState(userId) {
|
||||||
|
// no clue why it does this
|
||||||
|
// only seems to be the case with per user ready
|
||||||
|
while (userId > 256) {
|
||||||
|
userId -= 256;
|
||||||
|
}
|
||||||
|
if (!this.users[userId]) {
|
||||||
|
this.users[userId] = {
|
||||||
|
name: null,
|
||||||
|
userId: userId,
|
||||||
|
steamId: null,
|
||||||
|
classes: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.users[userId];
|
||||||
|
}
|
||||||
|
|
||||||
|
get classBits() {
|
||||||
|
return Math.ceil(Math.log(this.serverClasses.length) * Math.LOG2E)
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/Data/Packet.ts
Normal file
4
src/Data/Packet.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface Packet {
|
||||||
|
packetType: string;
|
||||||
|
[name: string]: any;
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import * as clone from 'clone';
|
import * as clone from 'clone';
|
||||||
import {SendPropDefinition} from "./SendPropDefinition";
|
import {SendPropDefinition} from "./SendPropDefinition";
|
||||||
|
import {Vector} from "./Vector";
|
||||||
|
|
||||||
export class SendProp {
|
export class SendProp {
|
||||||
definition: SendPropDefinition;
|
definition: SendPropDefinition;
|
||||||
value: any;
|
value: SendPropValue|null;
|
||||||
|
|
||||||
constructor(definition: SendPropDefinition) {
|
constructor(definition: SendPropDefinition) {
|
||||||
this.definition = definition;
|
this.definition = definition;
|
||||||
|
|
@ -17,3 +18,5 @@ export class SendProp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SendPropArrayValue = Vector | number | string;
|
||||||
|
export type SendPropValue = Vector | number | string | SendPropArrayValue[];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,16 @@
|
||||||
|
import {SendTable} from "./SendTable";
|
||||||
export class SendPropDefinition {
|
export class SendPropDefinition {
|
||||||
|
type: SendPropType;
|
||||||
|
name: string;
|
||||||
|
flags: number;
|
||||||
|
excludeDTName: string|null;
|
||||||
|
lowValue: number;
|
||||||
|
highValue: number;
|
||||||
|
bitCount: number;
|
||||||
|
table: SendTable|null;
|
||||||
|
numElements: number|null;
|
||||||
|
arrayProperty: SendPropDefinition|null;
|
||||||
|
|
||||||
constructor(type, name, flags) {
|
constructor(type, name, flags) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
@ -12,79 +24,70 @@ export class SendPropDefinition {
|
||||||
this.arrayProperty = null;
|
this.arrayProperty = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasFlag(flag) {
|
hasFlag(flag: SendPropFlag) {
|
||||||
return (this.flags & flag) != 0;
|
return (this.flags & flag) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
isExcludeProp() {
|
isExcludeProp() {
|
||||||
return this.hasFlag(SendPropDefinition.flags.SPROP_EXCLUDE);
|
return this.hasFlag(SendPropFlag.SPROP_EXCLUDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
inspect() {
|
inspect() {
|
||||||
return {
|
return {
|
||||||
name : this.name,
|
name: this.name,
|
||||||
type : SendPropDefinition.formatType(this.type),
|
type: SendPropType[this.type],
|
||||||
flags: SendPropDefinition.formatFlags(this.flags)
|
flags: SendPropDefinition.formatFlags(this.flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static formatFlags(flags) {
|
static formatFlags(flags: number) {
|
||||||
let names = [];
|
let names: string[] = [];
|
||||||
for (const name in SendPropDefinition.flags) {
|
for (const name in SendPropFlag) {
|
||||||
if (SendPropDefinition.flags.hasOwnProperty(name)) {
|
const flagValue = <SendPropFlag|string>SendPropFlag[name];
|
||||||
if (flags & SendPropDefinition.flags[name]) {
|
if (typeof flagValue === 'number') {
|
||||||
|
if (flags & flagValue) {
|
||||||
names.push(name);
|
names.push(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
static formatType(type) {
|
|
||||||
for (let name in SendPropDefinition.types) {
|
|
||||||
if (SendPropDefinition.types.hasOwnProperty(name)) {
|
|
||||||
if (SendPropDefinition.types[name] === type) {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'unknown';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SendPropDefinition.types = {
|
export enum SendPropType {
|
||||||
DPT_Int : 0,
|
DPT_Int,
|
||||||
DPT_Float : 1,
|
DPT_Float,
|
||||||
DPT_Vector : 2,
|
DPT_Vector,
|
||||||
DPT_VectorXY : 3,// Only encodes the XY of a vector, ignores Z
|
DPT_VectorXY,
|
||||||
DPT_String : 4,
|
DPT_String,
|
||||||
DPT_Array : 5,
|
DPT_Array,
|
||||||
DPT_DataTable : 6,
|
DPT_DataTable,
|
||||||
DPT_NUMSendPropTypes: 7
|
DPT_NUMSendPropTypes
|
||||||
};
|
}
|
||||||
|
|
||||||
SendPropDefinition.flags = {
|
|
||||||
SPROP_UNSIGNED : (1 << 0),// Unsigned integer data.
|
export enum SendPropFlag {
|
||||||
SPROP_COORD : (1 << 1),// If this is set, the float/vector is treated like a world coordinate.
|
SPROP_UNSIGNED = (1 << 0),// Unsigned integer data.
|
||||||
|
SPROP_COORD = (1 << 1),// If this is set, the float/vector is treated like a world coordinate.
|
||||||
// Note that the bit count is ignored in this case.
|
// Note that the bit count is ignored in this case.
|
||||||
SPROP_NOSCALE : (1 << 2),// For floating point, don't scale into range, just take value as is.
|
SPROP_NOSCALE = (1 << 2),// For floating point, don't scale into range, just take value as is.
|
||||||
SPROP_ROUNDDOWN : (1 << 3),// For floating point, limit high value to range minus one bit unit
|
SPROP_ROUNDDOWN = (1 << 3),// For floating point, limit high value to range minus one bit unit
|
||||||
SPROP_ROUNDUP : (1 << 4),// For floating point, limit low value to range minus one bit unit
|
SPROP_ROUNDUP = (1 << 4),// For floating point, limit low value to range minus one bit unit
|
||||||
SPROP_NORMAL : (1 << 5),// If this is set, the vector is treated like a normal (only valid for vectors)
|
SPROP_NORMAL = (1 << 5),// If this is set, the vector is treated like a normal (only valid for vectors)
|
||||||
SPROP_EXCLUDE : (1 << 6),// This is an exclude prop (not excludED, but it points at another prop to be excluded).
|
SPROP_EXCLUDE = (1 << 6),// This is an exclude prop (not excludED, but it points at another prop to be excluded).
|
||||||
SPROP_XYZE : (1 << 7),// Use XYZ/Exponent encoding for vectors.
|
SPROP_XYZE = (1 << 7),// Use XYZ/Exponent encoding for vectors.
|
||||||
SPROP_INSIDEARRAY : (1 << 8),// This tells us that the property is inside an array, so it shouldn't be put into the
|
SPROP_INSIDEARRAY = (1 << 8),// This tells us that the property is inside an array, so it shouldn't be put into the
|
||||||
// flattened property list. Its array will point at it when it needs to.
|
// flattened property list. Its array will point at it when it needs to.
|
||||||
SPROP_PROXY_ALWAYS_YES : (1 << 9),// Set for datatable props using one of the default datatable proxies like
|
SPROP_PROXY_ALWAYS_YES = (1 << 9),// Set for datatable props using one of the default datatable proxies like
|
||||||
// SendProxy_DataTableToDataTable that always send the data to all clients.
|
// SendProxy_DataTableToDataTable that always send the data to all clients.
|
||||||
SPROP_CHANGES_OFTEN : (1 << 10),// this is an often changed field, moved to head of sendtable so it gets a small index
|
SPROP_CHANGES_OFTEN = (1 << 10),// this is an often changed field, moved to head of sendtable so it gets a small index
|
||||||
SPROP_IS_A_VECTOR_ELEM : (1 << 11),// Set automatically if SPROP_VECTORELEM is used.
|
SPROP_IS_A_VECTOR_ELEM = (1 << 11),// Set automatically if SPROP_VECTORELEM is used.
|
||||||
SPROP_COLLAPSIBLE : (1 << 12),// Set automatically if it's a datatable with an offset of 0 that doesn't change the pointer
|
SPROP_COLLAPSIBLE = (1 << 12),// Set automatically if it's a datatable with an offset of 0 that doesn't change the pointer
|
||||||
// (ie: for all automatically-chained base classes).
|
// (ie: for all automatically-chained base classes).
|
||||||
// In this case, it can get rid of this SendPropDataTable altogether and spare the
|
// In this case, it can get rid of this SendPropDataTable altogether and spare the
|
||||||
// trouble of walking the hierarchy more than necessary.
|
// trouble of walking the hierarchy more than necessary.
|
||||||
SPROP_COORD_MP : (1 << 13),// Like SPROP_COORD, but special handling for multiplayer games
|
SPROP_COORD_MP = (1 << 13),// Like SPROP_COORD, but special handling for multiplayer games
|
||||||
SPROP_COORD_MP_LOWPRECISION: (1 << 14),// Like SPROP_COORD, but special handling for multiplayer games where the fractional component only gets a 3 bits instead of 5
|
SPROP_COORD_MP_LOWPRECISION = (1 << 14),// Like SPROP_COORD, but special handling for multiplayer games where the fractional component only gets a 3 bits instead of 5
|
||||||
SPROP_COORD_MP_INTEGRAL : (1 << 15),// SPROP_COORD_MP, but coordinates are rounded to integral boundaries
|
SPROP_COORD_MP_INTEGRAL = (1 << 15),// SPROP_COORD_MP, but coordinates are rounded to integral boundaries
|
||||||
SPROP_VARINT : (1 << 5) //reuse normal
|
SPROP_VARINT = (1 << 5)
|
||||||
};
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
import {SendPropDefinition} from './SendPropDefinition';
|
import {SendPropDefinition, SendPropType, SendPropFlag} from './SendPropDefinition';
|
||||||
|
|
||||||
export class SendTable {
|
export class SendTable {
|
||||||
|
name: string;
|
||||||
|
props: SendPropDefinition[];
|
||||||
|
private _flattenedProps: SendPropDefinition[];
|
||||||
|
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.props = [];
|
this.props = [];
|
||||||
|
|
@ -13,13 +17,13 @@ export class SendTable {
|
||||||
|
|
||||||
flatten() {
|
flatten() {
|
||||||
let excludes = [];
|
let excludes = [];
|
||||||
let props = [];
|
let props: SendPropDefinition[] = [];
|
||||||
this.getAllProps(excludes, props);
|
this.getAllProps(excludes, props);
|
||||||
|
|
||||||
// sort often changed props before the others
|
// sort often changed props before the others
|
||||||
let start = 0;
|
let start = 0;
|
||||||
for (let i = 0; i < props.length; i++) {
|
for (let i = 0; i < props.length; i++) {
|
||||||
if (props[i].hasFlag(SendPropDefinition.flags.SPROP_CHANGES_OFTEN)) {
|
if (props[i].hasFlag(SendPropFlag.SPROP_CHANGES_OFTEN)) {
|
||||||
if (i != start) {
|
if (i != start) {
|
||||||
const temp = props[i];
|
const temp = props[i];
|
||||||
props[i] = props[start];
|
props[i] = props[start];
|
||||||
|
|
@ -31,7 +35,7 @@ export class SendTable {
|
||||||
this._flattenedProps = props;
|
this._flattenedProps = props;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllProps(excludes, props) {
|
getAllProps(excludes: SendTable[], props: SendPropDefinition[]) {
|
||||||
let localProps = [];
|
let localProps = [];
|
||||||
this.getAllPropsIteratorProps(excludes, localProps, props);
|
this.getAllPropsIteratorProps(excludes, localProps, props);
|
||||||
for (let i = 0; i < localProps.length; i++) {
|
for (let i = 0; i < localProps.length; i++) {
|
||||||
|
|
@ -39,26 +43,26 @@ export class SendTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllPropsIteratorProps(excludes, props, childProps) {
|
getAllPropsIteratorProps(excludes: SendTable[], props: SendPropDefinition[], childProps: SendPropDefinition[]) {
|
||||||
for (let i = 0; i < this.props.length; i++) {
|
for (let i = 0; i < this.props.length; i++) {
|
||||||
const prop = this.props[i];
|
const prop = this.props[i];
|
||||||
if (prop.type === SendPropDefinition.types.DPT_DataTable) {
|
if (prop.type === SendPropType.DPT_DataTable && prop.table) {
|
||||||
if (prop.hasFlag(SendPropDefinition.flags.SPROP_EXCLUDE)) {
|
if (prop.hasFlag(SendPropFlag.SPROP_EXCLUDE)) {
|
||||||
excludes.push(prop.table);
|
excludes.push(prop.table);
|
||||||
} else if (excludes.indexOf(this) === -1) {
|
} else if (excludes.indexOf(this) === -1) {
|
||||||
if (prop.hasFlag(SendPropDefinition.flags.SPROP_COLLAPSIBLE)) {
|
if (prop.hasFlag(SendPropFlag.SPROP_COLLAPSIBLE)) {
|
||||||
prop.table.getAllPropsIteratorProps(excludes, props, childProps);
|
prop.table.getAllPropsIteratorProps(excludes, props, childProps);
|
||||||
} else {
|
} else {
|
||||||
prop.table.getAllProps(excludes, childProps);
|
prop.table.getAllProps(excludes, childProps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!prop.hasFlag(SendPropDefinition.flags.SPROP_EXCLUDE)) {
|
} else if (!prop.hasFlag(SendPropFlag.SPROP_EXCLUDE)) {
|
||||||
props.push(prop);
|
props.push(prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get flattenedProps(){
|
get flattenedProps() {
|
||||||
if (this._flattenedProps.length === 0) {
|
if (this._flattenedProps.length === 0) {
|
||||||
this.flatten();
|
this.flatten();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
src/Data/StringTable.ts
Normal file
9
src/Data/StringTable.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export interface StringTable {
|
||||||
|
name: string;
|
||||||
|
entries: StringTableEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StringTableEntry {
|
||||||
|
text: string;
|
||||||
|
extraData: string[];
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
export class Vector {
|
export class Vector {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
z: number;
|
||||||
|
|
||||||
constructor(x, y, z) {
|
constructor(x, y, z) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
|
|
|
||||||
7
src/Parser/Message/ConsoleCmd.ts
Normal file
7
src/Parser/Message/ConsoleCmd.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import {Parser} from './Parser';
|
||||||
|
|
||||||
|
export class ConsoleCmd extends Parser {
|
||||||
|
parse() {
|
||||||
|
return this.stream.readUTF8String();
|
||||||
|
}
|
||||||
|
}
|
||||||
101
src/Parser/Message/DataTable.ts
Normal file
101
src/Parser/Message/DataTable.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import {SendTable} from '../../Data/SendTable';
|
||||||
|
import {SendPropDefinition, SendPropFlag, SendPropType} from '../../Data/SendPropDefinition';
|
||||||
|
import {ServerClass} from '../../Data/ServerClass';
|
||||||
|
import {Parser} from './Parser';
|
||||||
|
|
||||||
|
export class DataTable extends Parser {
|
||||||
|
parse() {
|
||||||
|
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_common_eng.cpp#L356
|
||||||
|
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_recv_eng.cpp#L310
|
||||||
|
// https://github.com/PazerOP/DemoLib/blob/master/DemoLib/Commands/DemoDataTablesCommand.cs
|
||||||
|
var tables:SendTable[] = [];
|
||||||
|
var i, j;
|
||||||
|
while (this.stream.readBoolean()) {
|
||||||
|
var needsDecoder = this.stream.readBoolean();
|
||||||
|
var tableName = this.stream.readASCIIString();
|
||||||
|
var numProps = this.stream.readBits(10);
|
||||||
|
var table = new SendTable(tableName);
|
||||||
|
|
||||||
|
// get props metadata
|
||||||
|
var arrayElementProp;
|
||||||
|
for (i = 0; i < numProps; i++) {
|
||||||
|
var propType = this.stream.readBits(5);
|
||||||
|
var propName = this.stream.readASCIIString();
|
||||||
|
var nFlagsBits = 16; // might be 11 (old?), 13 (new?), 16(networked) or 17(??)
|
||||||
|
var flags = this.stream.readBits(nFlagsBits);
|
||||||
|
var prop = new SendPropDefinition(propType, propName, flags);
|
||||||
|
if (propType === SendPropType.DPT_DataTable) {
|
||||||
|
prop.excludeDTName = this.stream.readASCIIString();
|
||||||
|
} else {
|
||||||
|
if (prop.isExcludeProp()) {
|
||||||
|
prop.excludeDTName = this.stream.readASCIIString();
|
||||||
|
} else if (prop.type === SendPropType.DPT_Array) {
|
||||||
|
prop.numElements = this.stream.readBits(10);
|
||||||
|
} else {
|
||||||
|
prop.lowValue = this.stream.readFloat32();
|
||||||
|
prop.highValue = this.stream.readFloat32();
|
||||||
|
prop.bitCount = this.stream.readBits(7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.hasFlag(SendPropFlag.SPROP_NOSCALE)) {
|
||||||
|
if (prop.type === SendPropType.DPT_Float) {
|
||||||
|
prop.bitCount = 32;
|
||||||
|
} else if (prop.type === SendPropType.DPT_Vector) {
|
||||||
|
if (!prop.hasFlag(SendPropFlag.SPROP_NORMAL)) {
|
||||||
|
prop.bitCount = 32 * 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arrayElementProp) {
|
||||||
|
if (prop.type !== SendPropType.DPT_Array) {
|
||||||
|
throw "expected prop of type array";
|
||||||
|
}
|
||||||
|
prop.arrayProperty = arrayElementProp;
|
||||||
|
arrayElementProp = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop.hasFlag(SendPropFlag.SPROP_INSIDEARRAY)) {
|
||||||
|
arrayElementProp = prop;
|
||||||
|
} else {
|
||||||
|
table.addProp(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tables.push(table);
|
||||||
|
}
|
||||||
|
this.match.sendTables = tables;
|
||||||
|
|
||||||
|
// link referenced tables
|
||||||
|
for (i = 0; i < tables.length; i++) {
|
||||||
|
for (j = 0; j < tables[i].props.length; j++) {
|
||||||
|
if (tables[i].props[j].type === SendPropType.DPT_DataTable) {
|
||||||
|
tables[i].props[j].table = this.match.getSendTable(tables[i].props[j].excludeDTName);
|
||||||
|
tables[i].props[j].excludeDTName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverClasses = this.stream.readUint16(); // short
|
||||||
|
if (serverClasses <= 0) {
|
||||||
|
throw "expected one or more serverclasses";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < serverClasses; i++) {
|
||||||
|
var classId = this.stream.readUint16();
|
||||||
|
if (classId > serverClasses) {
|
||||||
|
throw "invalid class id";
|
||||||
|
}
|
||||||
|
var className = this.stream.readASCIIString();
|
||||||
|
var dataTable = this.stream.readASCIIString();
|
||||||
|
this.match.serverClasses.push(new ServerClass(classId, className, dataTable));
|
||||||
|
}
|
||||||
|
|
||||||
|
var bitsLeft = (this.length * 8) - this.stream._index;
|
||||||
|
if (bitsLeft > 7) {
|
||||||
|
throw "unexpected remaining data in datatable (" + bitsLeft + " bits)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return tables;
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/Parser/Message/Packet.ts
Normal file
87
src/Parser/Message/Packet.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import * as ParserGenerator from '../Packet/ParserGenerator';
|
||||||
|
|
||||||
|
import {Parser} from './Parser';
|
||||||
|
import {BSPDecal} from '../Packet/BSPDecal';
|
||||||
|
import {ClassInfo} from '../Packet/ClassInfo';
|
||||||
|
import {CreateStringTable} from '../Packet/CreateStringTable';
|
||||||
|
import {EntityMessage} from '../Packet/EntityMessage';
|
||||||
|
import {GameEvent} from '../Packet/GameEvent';
|
||||||
|
import {GameEventList} from '../Packet/GameEventList';
|
||||||
|
import {PacketEntities} from '../Packet/PacketEntities';
|
||||||
|
import {ParseSounds} from '../Packet/ParseSounds';
|
||||||
|
import {SetConVar} from '../Packet/SetConVar';
|
||||||
|
import {UpdateStringTable} from '../Packet/UpdateStringTable';
|
||||||
|
import {UserMessage} from '../Packet/UserMessage';
|
||||||
|
import {PacketParserMap} from '../Packet/Parser'
|
||||||
|
import {GameEventDefinitionMap} from "../../Data/GameEvent";
|
||||||
|
|
||||||
|
import {Packet as IPacket} from '../../Data/Packet';
|
||||||
|
|
||||||
|
// https://code.google.com/p/coldemoplayer/source/browse/branches/2.0/compLexity+Demo+Player/CDP.Source/Messages/?r=219
|
||||||
|
// https://github.com/TimePath/hl2-toolkit/tree/master/src/main/java/com/timepath/hl2/io/demo
|
||||||
|
// https://github.com/stgn/netdecode/blob/master/Packet.cs
|
||||||
|
// https://github.com/LestaD/SourceEngine2007/blob/master/src_main/common/netmessages.cpp
|
||||||
|
|
||||||
|
export class Packet extends Parser {
|
||||||
|
viewOrigin: any;
|
||||||
|
|
||||||
|
parse() {
|
||||||
|
//var table = new PacketStringTable(this.stream);
|
||||||
|
//table.searchIds();
|
||||||
|
//return [];
|
||||||
|
|
||||||
|
let packets: IPacket[] = [];
|
||||||
|
let entities = [];
|
||||||
|
while (this.bitsLeft > 6) { // last 6 bits for NOOP
|
||||||
|
const type = this.stream.readBits(6);
|
||||||
|
if (type !== 0) {
|
||||||
|
if (Packet.parsers[type]) {
|
||||||
|
let packet = Packet.parsers[type].call(this, this.stream, Packet.gameEventMap, entities, this.match);
|
||||||
|
packets.push(packet);
|
||||||
|
} else {
|
||||||
|
throw 'Unknown packet type ' + type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bitsLeft() {
|
||||||
|
return (this.length * 8) - this.stream._index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static parsers: PacketParserMap = {
|
||||||
|
2: ParserGenerator.make('file', 'transferId{32}fileName{s}requested{b}'),
|
||||||
|
3: ParserGenerator.make('netTick', 'tick{32}frameTime{16}stdDev{16}'),
|
||||||
|
4: ParserGenerator.make('stringCmd', 'command{s}'),
|
||||||
|
5: SetConVar,
|
||||||
|
6: ParserGenerator.make('sigOnState', 'state{8}count{32}'),
|
||||||
|
7: ParserGenerator.make('print', 'value{s}'),
|
||||||
|
8: ParserGenerator.make('serverInfo',
|
||||||
|
'version{16}serverCount{32}stv{b}dedicated{b}maxCrc{32}maxClasses{16}' +
|
||||||
|
'mapHash{128}playerCount{8}maxPlayerCount{8}intervalPerTick{f32}platform{s1}' +
|
||||||
|
'game{s}map{s}skybox{s}serverName{s}replay{b}'),
|
||||||
|
10: ClassInfo,
|
||||||
|
11: ParserGenerator.make('setPause', 'paused{b}'),
|
||||||
|
12: CreateStringTable,
|
||||||
|
13: UpdateStringTable,
|
||||||
|
14: ParserGenerator.make('voiceInit', 'codec{s}quality{8}'),
|
||||||
|
15: ParserGenerator.make('voiceData', 'client{8}proximity{8}length{16}_{$length}'),
|
||||||
|
17: ParseSounds,
|
||||||
|
18: ParserGenerator.make('setView', 'index{11}'),
|
||||||
|
19: ParserGenerator.make('fixAngle', 'relative{b}x{16}y{16}z{16}'),
|
||||||
|
21: BSPDecal,
|
||||||
|
23: UserMessage,
|
||||||
|
24: EntityMessage,
|
||||||
|
25: GameEvent,
|
||||||
|
26: PacketEntities,
|
||||||
|
27: ParserGenerator.make('tempEntities', 'count{8}length{17}_{$length}'),
|
||||||
|
28: ParserGenerator.make('preFetch', 'index{14}'),
|
||||||
|
29: ParserGenerator.make('menu', 'type{16}length{16}_{$length}_{$length}_{$length}_{$length}_{$length}_{$length}_{$length}'),//length*8
|
||||||
|
30: GameEventList,
|
||||||
|
31: ParserGenerator.make('getCvarValue', 'cookie{32}value{s}'),
|
||||||
|
32: ParserGenerator.make('cmdKeyValues', 'length{32}data{$length}')
|
||||||
|
};
|
||||||
|
|
||||||
|
static gameEventMap: GameEventDefinitionMap = {};
|
||||||
|
}
|
||||||
20
src/Parser/Message/Parser.ts
Normal file
20
src/Parser/Message/Parser.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {Match} from '../../Data/Match';
|
||||||
|
|
||||||
|
export abstract class Parser {
|
||||||
|
type: any;
|
||||||
|
tick: number;
|
||||||
|
stream: BitStream;
|
||||||
|
length: number;
|
||||||
|
match: Match;
|
||||||
|
|
||||||
|
constructor(type, tick, stream, length, match) {
|
||||||
|
this.type = type;
|
||||||
|
this.tick = tick;
|
||||||
|
this.stream = stream;
|
||||||
|
this.length = length;//length in bytes
|
||||||
|
this.match = match;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract parse();
|
||||||
|
}
|
||||||
75
src/Parser/Message/StringTable.ts
Normal file
75
src/Parser/Message/StringTable.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import {Parser} from './Parser';
|
||||||
|
import {StringTableEntry} from "../../Data/StringTable";
|
||||||
|
|
||||||
|
export class StringTable extends Parser {
|
||||||
|
parse() {
|
||||||
|
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/ST/StringTableParser.cs
|
||||||
|
const tableCount = this.stream.readUint8();
|
||||||
|
let tables = {};
|
||||||
|
let extraDataLength;
|
||||||
|
for (let i = 0; i < tableCount; i++) {
|
||||||
|
let entries:StringTableEntry[] = [];
|
||||||
|
const tableName = this.stream.readASCIIString();
|
||||||
|
const entryCount = this.stream.readUint16();
|
||||||
|
for (let j = 0; j < entryCount; j++) {
|
||||||
|
let entry;
|
||||||
|
try {
|
||||||
|
entry = {
|
||||||
|
text: this.stream.readUTF8String()
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return [{
|
||||||
|
packetType: 'stringTable',
|
||||||
|
tables: tables
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
if (this.stream.readBits(1)) {
|
||||||
|
extraDataLength = this.stream.readUint16();
|
||||||
|
if (tableName === 'instancebaseline') {
|
||||||
|
this.match.staticBaseLines[parseInt(entry.text, 10)] = this.stream.readBitStream(8 * extraDataLength);
|
||||||
|
} else {
|
||||||
|
entry.extraData = this.readExtraData(extraDataLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries.push(entry);
|
||||||
|
}
|
||||||
|
tables[tableName] = entries;
|
||||||
|
this.match.stringTables.push({
|
||||||
|
name: tableName,
|
||||||
|
entries: entries
|
||||||
|
});
|
||||||
|
if (this.stream.readBits(1)) {
|
||||||
|
this.stream.readASCIIString();
|
||||||
|
if (this.stream.readBits(1)) {
|
||||||
|
//throw 'more extra data not implemented';
|
||||||
|
extraDataLength = this.stream.readBits(16);
|
||||||
|
this.stream.readBits(extraDataLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log(tables);
|
||||||
|
return [{
|
||||||
|
packetType: 'stringTable',
|
||||||
|
tables: tables
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
readExtraData(length):string[] {
|
||||||
|
const end = this.stream._index + (length * 8);
|
||||||
|
let data:string[] = [];
|
||||||
|
//console.log(this.stream.readUTF8String());
|
||||||
|
data.push(this.stream.readUTF8String());
|
||||||
|
while (this.stream._index < end) {
|
||||||
|
try {
|
||||||
|
let string = this.stream.readUTF8String();
|
||||||
|
if (string) {
|
||||||
|
data.push(string);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.stream._index = end;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/Parser/Message/UserCmd.ts
Normal file
7
src/Parser/Message/UserCmd.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import {Parser} from './Parser';
|
||||||
|
|
||||||
|
export class UserCmd extends Parser {
|
||||||
|
parse() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/Parser/Packet/BSPDecal.ts
Normal file
51
src/Parser/Packet/BSPDecal.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
const getCoord = function (stream) {
|
||||||
|
const hasInt = !!stream.readBits(1);
|
||||||
|
const hasFract = !!stream.readBits(1);
|
||||||
|
let value = 0;
|
||||||
|
if (hasInt || hasFract) {
|
||||||
|
const sign = !!stream.readBits(1);
|
||||||
|
if (hasInt) {
|
||||||
|
value += stream.readBits(14) + 1;
|
||||||
|
}
|
||||||
|
if (hasFract) {
|
||||||
|
value += stream.readBits(5) * (1 / 32);
|
||||||
|
}
|
||||||
|
if (sign) {
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVecCoord = function (stream) {
|
||||||
|
const hasX = !!stream.readBits(1);
|
||||||
|
const hasY = !!stream.readBits(1);
|
||||||
|
const hasZ = !!stream.readBits(1);
|
||||||
|
return {
|
||||||
|
x: hasX ? getCoord(stream) : 0,
|
||||||
|
y: hasY ? getCoord(stream) : 0,
|
||||||
|
z: hasZ ? getCoord(stream) : 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function BSPDecal(stream: BitStream): Packet { // 21: BSPDecal
|
||||||
|
let modelIndex, entIndex;
|
||||||
|
const position = getVecCoord(stream);
|
||||||
|
const textureIndex = stream.readBits(9);
|
||||||
|
if (stream.readBits(1)) {
|
||||||
|
entIndex = stream.readBits(11);
|
||||||
|
modelIndex = stream.readBits(12);
|
||||||
|
}
|
||||||
|
const lowPriority = !!stream.readBits(1);
|
||||||
|
return {
|
||||||
|
packetType: 'BSPDecal',
|
||||||
|
position: position,
|
||||||
|
textureIndex: textureIndex,
|
||||||
|
entIndex: entIndex,
|
||||||
|
modelIndex: modelIndex,
|
||||||
|
lowPriority: lowPriority
|
||||||
|
}
|
||||||
|
}
|
||||||
33
src/Parser/Packet/ClassInfo.ts
Normal file
33
src/Parser/Packet/ClassInfo.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
function logBase2(num: number): number {
|
||||||
|
let result = 0;
|
||||||
|
while ((num >>= 1) != 0) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ClassInfo(stream: BitStream): Packet { // 10: classInfo
|
||||||
|
const number = stream.readBits(16);
|
||||||
|
const create = !!stream.readBits(1);
|
||||||
|
let entries: any[] = [];
|
||||||
|
if (!create) {
|
||||||
|
const bits = logBase2(number) + 1;
|
||||||
|
for (let i = 0; i < number; i++) {
|
||||||
|
const entry = {
|
||||||
|
'classId': stream.readBits(bits),
|
||||||
|
'className': stream.readASCIIString(),
|
||||||
|
'dataTableName': stream.readASCIIString()
|
||||||
|
};
|
||||||
|
entries.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'packetType': 'classInfo',
|
||||||
|
number: number,
|
||||||
|
create: create,
|
||||||
|
entries: entries
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Parser/Packet/CreateStringTable.ts
Normal file
13
src/Parser/Packet/CreateStringTable.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import {PacketStringTable} from '../../packetstringtable';
|
||||||
|
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
export function CreateStringTable(stream: BitStream): Packet { // 12: createStringTable
|
||||||
|
const stringTable = new PacketStringTable(stream);
|
||||||
|
const tables = stringTable.parse();
|
||||||
|
return {
|
||||||
|
packetType: 'createStringTable',
|
||||||
|
table: tables
|
||||||
|
};
|
||||||
|
}
|
||||||
10
src/Parser/Packet/EntityMessage.ts
Normal file
10
src/Parser/Packet/EntityMessage.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import {make} from './ParserGenerator';
|
||||||
|
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
const baseParser = make('entityMessage', 'index{11}classId{9}length{11}data{$length}');
|
||||||
|
|
||||||
|
export function EntityMessage(stream:BitStream):Packet { // 24: entityMessage
|
||||||
|
return baseParser(stream); //todo parse data further?
|
||||||
|
};
|
||||||
59
src/Parser/Packet/GameEvent.ts
Normal file
59
src/Parser/Packet/GameEvent.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {
|
||||||
|
GameEventType, GameEventValue, GameEventEntry, GameEventDefinition, GameEvent as IGameEvent,
|
||||||
|
GameEventValueMap, GameEventDefinitionMap
|
||||||
|
} from "../../Data/GameEvent";
|
||||||
|
|
||||||
|
const parseGameEvent = function (eventId: number, stream: BitStream, events: GameEventDefinitionMap): IGameEvent|null {
|
||||||
|
if (!events[eventId]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const eventDescription: GameEventDefinition = events[eventId];
|
||||||
|
const values: GameEventValueMap = {};
|
||||||
|
for (let i = 0; i < eventDescription.entries.length; i++) {
|
||||||
|
const entry: GameEventEntry = eventDescription.entries[i];
|
||||||
|
const value = getGameEventValue(stream, entry);
|
||||||
|
if (value) {
|
||||||
|
values[entry.name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: eventDescription.name,
|
||||||
|
values: values
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getGameEventValue = function (stream: BitStream, entry: GameEventEntry): GameEventValue|null {
|
||||||
|
switch (entry.type) {
|
||||||
|
case GameEventType.STRING:
|
||||||
|
return stream.readUTF8String();
|
||||||
|
case GameEventType.FLOAT:
|
||||||
|
return stream.readFloat32();
|
||||||
|
case GameEventType.LONG:
|
||||||
|
return stream.readUint32();
|
||||||
|
case GameEventType.SHORT:
|
||||||
|
return stream.readUint16();
|
||||||
|
case GameEventType.BYTE:
|
||||||
|
return stream.readUint8();
|
||||||
|
case GameEventType.BOOLEAN:
|
||||||
|
return stream.readBoolean();
|
||||||
|
case GameEventType.LOCAL:
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw new Error('invalid game event type');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export function GameEvent(stream: BitStream, events: GameEventDefinitionMap): Packet { // 25: game event
|
||||||
|
const length = stream.readBits(11);
|
||||||
|
const end = stream._index + length;
|
||||||
|
const eventId = stream.readBits(9);
|
||||||
|
const event = parseGameEvent(eventId, stream, events);
|
||||||
|
stream._index = end;
|
||||||
|
return {
|
||||||
|
packetType: 'gameEvent',
|
||||||
|
event: event
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/Parser/Packet/GameEventList.ts
Normal file
31
src/Parser/Packet/GameEventList.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {GameEventEntry, GameEventDefinitionMap} from "../../Data/GameEvent";
|
||||||
|
|
||||||
|
export function GameEventList(stream: BitStream, events: GameEventDefinitionMap): Packet { // 30: gameEventList
|
||||||
|
// list of game events and parameters
|
||||||
|
const numEvents = stream.readBits(9);
|
||||||
|
const length = stream.readBits(20);
|
||||||
|
for (let i = 0; i < numEvents; i++) {
|
||||||
|
const id = stream.readBits(9);
|
||||||
|
const name = stream.readASCIIString();
|
||||||
|
let type = stream.readBits(3);
|
||||||
|
const entries: GameEventEntry[] = [];
|
||||||
|
while (type !== 0) {
|
||||||
|
entries.push({
|
||||||
|
type: type,
|
||||||
|
name: stream.readASCIIString()
|
||||||
|
});
|
||||||
|
type = stream.readBits(3);
|
||||||
|
}
|
||||||
|
events[id] = {
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
entries: entries
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
packetType: 'gameEventList',
|
||||||
|
events: events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
import {SendPropParser} from '../../Parser/SendPropParser';
|
import {SendPropParser} from '../../Parser/SendPropParser';
|
||||||
import {Entity} from '../../Data/Entity';
|
import {Entity} from '../../Data/Entity';
|
||||||
import {SendProp} from '../../Data/SendProp';
|
import {SendProp} from '../../Data/SendProp';
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {GameEventDefinition} from "../../Data/GameEvent";
|
||||||
|
import {Match} from "../../Data/Match";
|
||||||
|
|
||||||
var PVS = {
|
var PVS = {
|
||||||
PRESERVE: 0,
|
PRESERVE: 0,
|
||||||
ENTER : 1,
|
ENTER: 1,
|
||||||
LEAVE : 2,
|
LEAVE: 2,
|
||||||
DELETE : 4
|
DELETE: 4
|
||||||
};
|
};
|
||||||
|
|
||||||
function readPVSType(stream) {
|
function readPVSType(stream) {
|
||||||
|
|
@ -60,7 +64,7 @@ function readLeavePVS(match, entityId, shouldDelete) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function (stream, events, entities, match) { //26: packetEntities
|
export function PacketEntities(stream: BitStream, events: GameEventDefinition[], entities: Entity[], 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
|
||||||
|
|
@ -80,6 +84,12 @@ module.exports = function (stream, events, entities, match) { //26: packetEntiti
|
||||||
var end = stream._index + length;
|
var end = stream._index + length;
|
||||||
var entityId = -1;
|
var entityId = -1;
|
||||||
|
|
||||||
|
stream._index = end;
|
||||||
|
return {
|
||||||
|
packetType: 'packetEntities',
|
||||||
|
entities: entities
|
||||||
|
};
|
||||||
|
|
||||||
if (updatedBaseLine) {
|
if (updatedBaseLine) {
|
||||||
if (baseLine === 0) {
|
if (baseLine === 0) {
|
||||||
match.instanceBaselines[1] = match.instanceBaselines[0];
|
match.instanceBaselines[1] = match.instanceBaselines[0];
|
||||||
|
|
@ -128,22 +138,9 @@ module.exports = function (stream, events, entities, match) { //26: packetEntiti
|
||||||
}
|
}
|
||||||
|
|
||||||
stream._index = end;
|
stream._index = end;
|
||||||
//var ent = {
|
|
||||||
// packetType : 'packetEntities',
|
|
||||||
// maxEntries : maxEntries,
|
|
||||||
// isDelta : isDelta,
|
|
||||||
// delta : delta,
|
|
||||||
// baseLine : baseLine,
|
|
||||||
// updatedEntries : updatedEntries,
|
|
||||||
// length : length,
|
|
||||||
// updatedBaseLine: updatedBaseLine
|
|
||||||
//};
|
|
||||||
//console.log(ent);
|
|
||||||
//console.log(entities);
|
|
||||||
//process.exit();
|
|
||||||
return {
|
return {
|
||||||
packetType: 'packetEntities',
|
packetType: 'packetEntities',
|
||||||
entities : entities
|
entities: entities
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
15
src/Parser/Packet/ParseSounds.ts
Normal file
15
src/Parser/Packet/ParseSounds.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
export function ParseSounds(stream: BitStream): Packet { // 17: parseSounds
|
||||||
|
const reliable = stream.readBoolean();
|
||||||
|
const num = (reliable) ? 1 : stream.readUint8();
|
||||||
|
const length = (reliable) ? stream.readUint8() : stream.readUint16();
|
||||||
|
stream._index += length;
|
||||||
|
return {
|
||||||
|
packetType: 'parseSounds',
|
||||||
|
reliable: reliable,
|
||||||
|
num: num,
|
||||||
|
length: length
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/Parser/Packet/Parser.ts
Normal file
8
src/Parser/Packet/Parser.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {GameEventDefinitionMap} from "../../Data/GameEvent";
|
||||||
|
import {Match} from "../../Data/Match";
|
||||||
|
import {Entity} from "../../Data/Entity";
|
||||||
|
|
||||||
|
export type Parser = (stream: BitStream, gameEventMap?: GameEventDefinitionMap, entities?: Entity[], match?: Match) => Packet;
|
||||||
|
export type PacketParserMap = {[id: number]: Parser};
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
var Generator = {};
|
import {Parser} from './Parser';
|
||||||
|
|
||||||
Generator.make = function (name, string) {
|
export function make(name: string, definition: string): Parser {
|
||||||
var parts = string.substr(0, string.length - 1).split('}');//remove leading } to prevent empty part
|
var parts = definition.substr(0, definition.length - 1).split('}');//remove leading } to prevent empty part
|
||||||
var items = parts.map(function (part) {
|
var items = parts.map(function (part) {
|
||||||
return part.split('{');
|
return part.split('{');
|
||||||
});
|
});
|
||||||
|
|
@ -11,19 +11,19 @@ Generator.make = function (name, string) {
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
for (var i = 0; i < items.length; i++) {
|
for (var i = 0; i < items.length; i++) {
|
||||||
var value = Generator.readItem(stream, items[i][1], result);
|
var value = readItem(stream, items[i][1], result);
|
||||||
if (items[i][0] !== '_') {
|
if (items[i][0] !== '_') {
|
||||||
result[items[i][0]] = value;
|
result[items[i][0]] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw 'Failed reading pattern ' + string + '. ' + e;
|
throw 'Failed reading pattern ' + definition + '. ' + e;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Generator.readItem = function (stream, description, data) {
|
const readItem = function (stream, description, data) {
|
||||||
var length;
|
var length;
|
||||||
if (description[0] === 'b') {
|
if (description[0] === 'b') {
|
||||||
return !!stream.readBits(1);
|
return !!stream.readBits(1);
|
||||||
|
|
@ -46,5 +46,3 @@ Generator.readItem = function (stream, description, data) {
|
||||||
return stream.readBits(parseInt(description, 10), true);
|
return stream.readBits(parseInt(description, 10), true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Generator;
|
|
||||||
14
src/Parser/Packet/SetConVar.ts
Normal file
14
src/Parser/Packet/SetConVar.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
export function SetConVar(stream: BitStream): Packet { // 5: setconvar
|
||||||
|
const count = stream.readBits(8);
|
||||||
|
let vars = {};
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
vars[stream.readUTF8String()] = stream.readUTF8String();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
packetType: 'setConVar',
|
||||||
|
vars: vars
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/Parser/Packet/UpdateStringTable.ts
Normal file
12
src/Parser/Packet/UpdateStringTable.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
import {PacketStringTable} from '../../packetstringtable';
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
export function UpdateStringTable(stream: BitStream): Packet { // 12: updateStringTable
|
||||||
|
const stringTable = new PacketStringTable(stream);
|
||||||
|
const tables = stringTable.parse();
|
||||||
|
return {
|
||||||
|
packetType: 'updateStringTable',
|
||||||
|
table: tables
|
||||||
|
};
|
||||||
|
}
|
||||||
86
src/Parser/Packet/UserMessage.ts
Normal file
86
src/Parser/Packet/UserMessage.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {make} from './ParserGenerator';
|
||||||
|
|
||||||
|
const userMessageParsers = {
|
||||||
|
4: require('../../handlers/userMessage/SayText2'),
|
||||||
|
5: make('textMsg', 'destType{8}text{s}')
|
||||||
|
};
|
||||||
|
|
||||||
|
export function UserMessage(stream: BitStream): Packet { // 23: user message
|
||||||
|
const type = stream.readBits(8);
|
||||||
|
const length = stream.readBits(11);
|
||||||
|
const pos = stream._index;
|
||||||
|
let result;
|
||||||
|
if (userMessageParsers[type]) {
|
||||||
|
result = userMessageParsers[type](stream);
|
||||||
|
} else {
|
||||||
|
result = {
|
||||||
|
packetType: 'unknownUserMessage',
|
||||||
|
type: type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream._index = pos + length;
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import {SendPropDefinition} from '../Data/SendPropDefinition';
|
import {SendPropDefinition, SendPropType, SendPropFlag} from '../Data/SendPropDefinition';
|
||||||
import {Vector} from "../Data/Vector";
|
import {Vector} from "../Data/Vector";
|
||||||
|
import {BitStream} from "bit-buffer";
|
||||||
|
import {SendPropValue, SendPropArrayValue} from "../Data/SendProp";
|
||||||
|
|
||||||
|
|
||||||
const readBitVar = function (stream, signed) {
|
const readBitVar = function (stream: BitStream, signed: boolean): number {
|
||||||
switch (stream.readBits(2)) {
|
switch (stream.readBits(2)) {
|
||||||
case 0:
|
case 0:
|
||||||
return stream.readBits(4, signed);
|
return stream.readBits(4, signed);
|
||||||
|
|
@ -13,78 +15,86 @@ const readBitVar = function (stream, signed) {
|
||||||
case 3:
|
case 3:
|
||||||
return stream.readBits(32, signed);
|
return stream.readBits(32, signed);
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SendPropParser {
|
export class SendPropParser {
|
||||||
static decode(propDefinition, stream) {
|
static decode(propDefinition: SendPropDefinition, stream: BitStream): SendPropValue {
|
||||||
switch (propDefinition.type) {
|
switch (propDefinition.type) {
|
||||||
case SendPropDefinition.types.DPT_Int:
|
case SendPropType.DPT_Int:
|
||||||
return SendPropParser.readInt(propDefinition, stream);
|
return SendPropParser.readInt(propDefinition, stream);
|
||||||
case SendPropDefinition.types.DPT_Vector:
|
case SendPropType.DPT_Vector:
|
||||||
return SendPropParser.readVector(propDefinition, stream);
|
return SendPropParser.readVector(propDefinition, stream);
|
||||||
case SendPropDefinition.types.DPT_VectorXY:
|
case SendPropType.DPT_VectorXY:
|
||||||
return SendPropParser.readVectorXY(propDefinition, stream);
|
return SendPropParser.readVectorXY(propDefinition, stream);
|
||||||
case SendPropDefinition.types.DPT_Float:
|
case SendPropType.DPT_Float:
|
||||||
return SendPropParser.readFloat(propDefinition, stream);
|
return SendPropParser.readFloat(propDefinition, stream);
|
||||||
case SendPropDefinition.types.DPT_String:
|
case SendPropType.DPT_String:
|
||||||
return SendPropParser.readString(stream);
|
return SendPropParser.readString(stream);
|
||||||
case SendPropDefinition.types.DPT_Array:
|
case SendPropType.DPT_Array:
|
||||||
return SendPropParser.readArray(propDefinition, stream);
|
return SendPropParser.readArray(propDefinition, stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static readInt(propDefinition, stream) {
|
static readInt(propDefinition: SendPropDefinition, stream: BitStream) {
|
||||||
if (propDefinition.hasFlag(SendPropDefinition.flags.SPROP_VARINT)) {
|
if (propDefinition.hasFlag(SendPropFlag.SPROP_VARINT)) {
|
||||||
return readBitVar(stream, !propDefinition.hasFlag(SendPropDefinition.flags.SPROP_UNSIGNED));
|
return readBitVar(stream, !propDefinition.hasFlag(SendPropFlag.SPROP_UNSIGNED));
|
||||||
} else {
|
} else {
|
||||||
return stream.readBits(propDefinition.bitCount, !propDefinition.hasFlag(SendPropDefinition.flags.SPROP_UNSIGNED));
|
return stream.readBits(propDefinition.bitCount, !propDefinition.hasFlag(SendPropFlag.SPROP_UNSIGNED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static readArray(propDefinition, stream) {
|
static readArray(propDefinition: SendPropDefinition, stream: BitStream): SendPropArrayValue[] {
|
||||||
let maxElements = propDefinition.numElements;
|
let maxElements = propDefinition.numElements;
|
||||||
let numBits = 1;
|
let numBits = 1;
|
||||||
while ((maxElements >>= 1) != 0)
|
while ((maxElements >>= 1) != 0)
|
||||||
numBits++;
|
numBits++;
|
||||||
|
|
||||||
const count = stream.readBits(numBits);
|
const count = stream.readBits(numBits);
|
||||||
const values = [];
|
const values: SendPropArrayValue[] = [];
|
||||||
|
if (!propDefinition.arrayProperty) {
|
||||||
|
throw new Error('Array of undefniend type');
|
||||||
|
}
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
values.push(SendPropParser.decode(propDefinition.arrayProperty, stream));
|
const value = SendPropParser.decode(propDefinition.arrayProperty, stream);
|
||||||
|
if (value instanceof Array) {
|
||||||
|
throw new Error('Nested arrays not supported');
|
||||||
|
}
|
||||||
|
values.push();
|
||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
static readString(stream) {
|
static readString(stream: BitStream): string {
|
||||||
const length = stream.readBits(9);
|
const length = stream.readBits(9);
|
||||||
return stream.readASCIIString(length);
|
return stream.readASCIIString(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
static readVector(propDefinition, stream) {
|
static readVector(propDefinition: SendPropDefinition, stream: BitStream): Vector {
|
||||||
const x = SendPropParser.readFloat(propDefinition, stream);
|
const x = SendPropParser.readFloat(propDefinition, stream);
|
||||||
const y = SendPropParser.readFloat(propDefinition, stream);
|
const y = SendPropParser.readFloat(propDefinition, stream);
|
||||||
const z = (propDefinition.hasFlag(SendPropDefinition.flags.SPROP_NORMAL)) ? SendPropParser.readFloat(propDefinition, stream) : 0;
|
const z = (propDefinition.hasFlag(SendPropFlag.SPROP_NORMAL)) ? SendPropParser.readFloat(propDefinition, stream) : 0;
|
||||||
return new Vector(x, y, z);
|
return new Vector(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
static readVectorXY(propDefinition, stream) {
|
static readVectorXY(propDefinition: SendPropDefinition, stream: BitStream): Vector {
|
||||||
const x = SendPropParser.readFloat(propDefinition, stream);
|
const x = SendPropParser.readFloat(propDefinition, stream);
|
||||||
const y = SendPropParser.readFloat(propDefinition, stream);
|
const y = SendPropParser.readFloat(propDefinition, stream);
|
||||||
return new Vector(x, y, 0);
|
return new Vector(x, y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static readFloat(propDefinition, stream) {
|
static readFloat(propDefinition: SendPropDefinition, stream: BitStream): number {
|
||||||
if (propDefinition.hasFlag(SendPropDefinition.flags.SPROP_COORD)) {
|
if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD)) {
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
} else if (propDefinition.hasFlag(SendPropDefinition.flags.SPROP_COORD_MP)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP)) {
|
||||||
return SendPropParser.readBitCoord(stream, false, false);
|
return SendPropParser.readBitCoord(stream, false, false);
|
||||||
} else if (propDefinition.hasFlag(SendPropDefinition.flags.SPROP_COORD_MP_LOWPRECISION)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP_LOWPRECISION)) {
|
||||||
return SendPropParser.readBitCoord(stream, false, true);
|
return SendPropParser.readBitCoord(stream, false, true);
|
||||||
} else if (propDefinition.hasFlag(SendPropDefinition.flags.SPROP_COORD_MP_INTEGRAL)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP_INTEGRAL)) {
|
||||||
return SendPropParser.readBitCoord(stream, true, false);
|
return SendPropParser.readBitCoord(stream, true, false);
|
||||||
} else if (propDefinition.hasFlag(SendPropDefinition.flags.SPROP_NOSCALE)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_NOSCALE)) {
|
||||||
return stream.readFloat32();
|
return stream.readFloat32();
|
||||||
} else if (propDefinition.hasFlag(SendPropDefinition.flags.SPROP_NORMAL)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_NORMAL)) {
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
} else {
|
} else {
|
||||||
const raw = stream.readBits(propDefinition.bitCount);
|
const raw = stream.readBits(propDefinition.bitCount);
|
||||||
|
|
@ -93,7 +103,7 @@ export class SendPropParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static readBitCoord(stream, isIntegral, isLowPrecision) {
|
static readBitCoord(stream: BitStream, isIntegral: boolean, isLowPrecision: boolean): number {
|
||||||
let value = 0;
|
let value = 0;
|
||||||
let isNegative = false;
|
let isNegative = false;
|
||||||
const inBounds = stream.readBoolean();
|
const inBounds = stream.readBoolean();
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
var ConsoleCmd = function (type, tick, stream, length, match) {
|
|
||||||
this.type = type;
|
|
||||||
this.tick = tick;
|
|
||||||
this.stream = stream;
|
|
||||||
this.length = length;//length in bytes
|
|
||||||
this.match = match;
|
|
||||||
};
|
|
||||||
|
|
||||||
ConsoleCmd.prototype.parse = function () {
|
|
||||||
var cmd = this.stream.readUTF8String();
|
|
||||||
//console.log("cmd " + cmd);
|
|
||||||
return cmd;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = ConsoleCmd;
|
|
||||||
108
src/datatable.ts
108
src/datatable.ts
|
|
@ -1,108 +0,0 @@
|
||||||
import {SendTable} from './Data/SendTable';
|
|
||||||
import {SendPropDefinition} from './Data/SendPropDefinition';
|
|
||||||
import {ServerClass} from './Data/ServerClass';
|
|
||||||
|
|
||||||
var DataTableParser = function (type, tick, stream, length, match) {
|
|
||||||
this.type = type;
|
|
||||||
this.tick = tick;
|
|
||||||
this.stream = stream;
|
|
||||||
this.length = length;//length in bytes
|
|
||||||
this.match = match;
|
|
||||||
};
|
|
||||||
|
|
||||||
DataTableParser.prototype.parse = function () {
|
|
||||||
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_common_eng.cpp#L356
|
|
||||||
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_recv_eng.cpp#L310
|
|
||||||
// https://github.com/PazerOP/DemoLib/blob/master/DemoLib/Commands/DemoDataTablesCommand.cs
|
|
||||||
var tables = [];
|
|
||||||
var i, j;
|
|
||||||
while (this.stream.readBoolean()) {
|
|
||||||
var needsDecoder = this.stream.readBoolean();
|
|
||||||
var tableName = this.stream.readASCIIString();
|
|
||||||
var numProps = this.stream.readBits(10);
|
|
||||||
var table = new SendTable(tableName);
|
|
||||||
|
|
||||||
// get props metadata
|
|
||||||
var arrayElementProp;
|
|
||||||
for (i = 0; i < numProps; i++) {
|
|
||||||
var propType = this.stream.readBits(5);
|
|
||||||
var propName = this.stream.readASCIIString();
|
|
||||||
var nFlagsBits = 16; // might be 11 (old?), 13 (new?), 16(networked) or 17(??)
|
|
||||||
var flags = this.stream.readBits(nFlagsBits);
|
|
||||||
var prop = new SendPropDefinition(propType, propName, flags);
|
|
||||||
if (propType === SendPropDefinition.types.DPT_DataTable) {
|
|
||||||
prop.excludeDTName = this.stream.readASCIIString();
|
|
||||||
} else {
|
|
||||||
if (prop.isExcludeProp()) {
|
|
||||||
prop.excludeDTName = this.stream.readASCIIString();
|
|
||||||
} else if (prop.type === SendPropDefinition.types.DPT_Array) {
|
|
||||||
prop.numElements = this.stream.readBits(10);
|
|
||||||
} else {
|
|
||||||
prop.lowValue = this.stream.readFloat32();
|
|
||||||
prop.highValue = this.stream.readFloat32();
|
|
||||||
prop.bitCount = this.stream.readBits(7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prop.hasFlag(SendPropDefinition.flags.SPROP_NOSCALE)) {
|
|
||||||
if (prop.type === SendPropDefinition.types.DPT_Float) {
|
|
||||||
prop.bitCount = 32;
|
|
||||||
} else if (prop.type === SendPropDefinition.types.DPT_Vector) {
|
|
||||||
if (!prop.hasFlag(SendPropDefinition.flags.SPROP_NORMAL)) {
|
|
||||||
prop.bitCount = 32 * 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arrayElementProp) {
|
|
||||||
if (!prop.type === SendPropDefinition.types.DPT_Array) {
|
|
||||||
throw "expected prop of type array";
|
|
||||||
}
|
|
||||||
prop.arrayProperty = arrayElementProp;
|
|
||||||
arrayElementProp = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prop.hasFlag(SendPropDefinition.flags.SPROP_INSIDEARRAY)) {
|
|
||||||
arrayElementProp = prop;
|
|
||||||
} else {
|
|
||||||
table.addProp(prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tables.push(table);
|
|
||||||
}
|
|
||||||
this.match.sendTables = tables;
|
|
||||||
|
|
||||||
// link referenced tables
|
|
||||||
for (i = 0; i < tables.length; i++) {
|
|
||||||
for (j = 0; j < tables[i].props.length; j++) {
|
|
||||||
if (tables[i].props[j].type === SendPropDefinition.types.DPT_DataTable) {
|
|
||||||
tables[i].props[j].table = this.match.getSendTable(tables[i].props[j].excludeDTName);
|
|
||||||
tables[i].props[j].excludeDTName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverClasses = this.stream.readUint16(); // short
|
|
||||||
if (serverClasses <= 0) {
|
|
||||||
throw "expected one or more serverclasses";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < serverClasses; i++) {
|
|
||||||
var classId = this.stream.readUint16();
|
|
||||||
if (classId > serverClasses) {
|
|
||||||
throw "invalid class id";
|
|
||||||
}
|
|
||||||
var className = this.stream.readASCIIString();
|
|
||||||
var dataTable = this.stream.readASCIIString();
|
|
||||||
this.match.serverClasses.push(new ServerClass(classId, className, dataTable));
|
|
||||||
}
|
|
||||||
|
|
||||||
var bitsLeft = (this.length * 8) - this.stream._index;
|
|
||||||
if (bitsLeft > 7) {
|
|
||||||
throw "unexpected remaining data in datatable (" + bitsLeft + " bits)";
|
|
||||||
}
|
|
||||||
|
|
||||||
return tables;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = DataTableParser;
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
var getCoord = function (stream) {
|
|
||||||
var hasInt = !!stream.readBits(1);
|
|
||||||
var hasFract = !!stream.readBits(1);
|
|
||||||
var value = 0;
|
|
||||||
if (hasInt || hasFract) {
|
|
||||||
var sign = !!stream.readBits(1);
|
|
||||||
if (hasInt) {
|
|
||||||
value += stream.readBits(14) + 1;
|
|
||||||
}
|
|
||||||
if (hasFract) {
|
|
||||||
value += stream.readBits(5) * (1 / 32);
|
|
||||||
}
|
|
||||||
if (sign) {
|
|
||||||
value = -value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
var getVecCoord = function (stream) {
|
|
||||||
var hasX = !!stream.readBits(1);
|
|
||||||
var hasY = !!stream.readBits(1);
|
|
||||||
var hasZ = !!stream.readBits(1);
|
|
||||||
return {
|
|
||||||
x: hasX ? getCoord(stream) : 0,
|
|
||||||
y: hasY ? getCoord(stream) : 0,
|
|
||||||
z: hasZ ? getCoord(stream) : 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function (stream) { // 21: BSPDecal
|
|
||||||
var position = getVecCoord(stream);
|
|
||||||
var textureIndex = stream.readBits(9);
|
|
||||||
if (stream.readBits(1)) {
|
|
||||||
var entIndex = stream.readBits(11);
|
|
||||||
var modelIndex = stream.readBits(12);
|
|
||||||
}
|
|
||||||
var lowPriority = !!stream.readBits(1);
|
|
||||||
return {
|
|
||||||
packetType : 'BSPDecal',
|
|
||||||
position : position,
|
|
||||||
textureIndex: textureIndex,
|
|
||||||
entIndex : entIndex,
|
|
||||||
modelIndex : modelIndex,
|
|
||||||
lowPriority : lowPriority
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
function logBase2(num) {
|
|
||||||
var result = 0;
|
|
||||||
while ((num >>= 1) != 0) {
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function (stream) { // 10: classInfo
|
|
||||||
var number = stream.readBits(16);
|
|
||||||
var create = !!stream.readBits(1);
|
|
||||||
var entries = [];
|
|
||||||
if (!create) {
|
|
||||||
var bits = logBase2(number) + 1;
|
|
||||||
for (var i = 0; i < number; i++) {
|
|
||||||
var entry = {
|
|
||||||
'classId' : stream.readBits(bits),
|
|
||||||
'className' : stream.readASCIIString(),
|
|
||||||
'dataTableName': stream.readASCIIString()
|
|
||||||
};
|
|
||||||
entries.push(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
'packetType': 'classInfo',
|
|
||||||
number : number,
|
|
||||||
create : create,
|
|
||||||
entries : entries
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
var PacketStringTable = require('../../packetstringtable');
|
|
||||||
|
|
||||||
module.exports = function (stream) { // 12: createStringTable
|
|
||||||
var stringTable = new PacketStringTable(stream);
|
|
||||||
var tables = stringTable.parse();
|
|
||||||
return {
|
|
||||||
packetType: 'createStringTable',
|
|
||||||
table : tables
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
var ParserGenerator = require('../../parsergenerator');
|
|
||||||
|
|
||||||
var baseParser = ParserGenerator.make('entityMessage', 'index{11}classId{9}length{11}data{$length}');
|
|
||||||
|
|
||||||
module.exports = function (stream) { // 24: entityMessage
|
|
||||||
var data = baseParser(stream); //todo parse data further?
|
|
||||||
// console.log(data.index);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
var parseGameEvent = function (eventId, stream, events) {
|
|
||||||
if (!events[eventId]) {
|
|
||||||
return 'unknown';
|
|
||||||
}
|
|
||||||
var eventDescription = events[eventId];
|
|
||||||
var values = {};
|
|
||||||
for (var i = 0; i < eventDescription.entries.length; i++) {
|
|
||||||
var entry = eventDescription.entries[i];
|
|
||||||
values[entry.name] = getGameEventValue(stream, entry);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
name : eventDescription.name,
|
|
||||||
type : eventDescription.type,
|
|
||||||
values: values
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var getGameEventValue = function (stream, entry) {
|
|
||||||
switch (entry.type) {
|
|
||||||
case 1:
|
|
||||||
return stream.readUTF8String();
|
|
||||||
case 2:
|
|
||||||
return stream.readFloat32();
|
|
||||||
case 3:
|
|
||||||
return stream.readInt32();
|
|
||||||
case 4:
|
|
||||||
return stream.readBits(16);
|
|
||||||
case 5:
|
|
||||||
return stream.readBits(8);
|
|
||||||
case 6:
|
|
||||||
return !!stream.readBits(1);
|
|
||||||
case 7:
|
|
||||||
return 'local value';
|
|
||||||
default:
|
|
||||||
throw 'invalid game event type';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = function (stream, events) { // 25: game event
|
|
||||||
var length = stream.readBits(11);
|
|
||||||
var end = stream._index + length;
|
|
||||||
var eventId = stream.readBits(9);
|
|
||||||
var event = parseGameEvent(eventId, stream, events);
|
|
||||||
stream._index = end;
|
|
||||||
return {
|
|
||||||
packetType: 'gameEvent',
|
|
||||||
event : event
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
module.exports = function (stream, events) { // 30: gameEventList
|
|
||||||
// list of game events and parameters
|
|
||||||
var numEvents = stream.readBits(9);
|
|
||||||
var length = stream.readBits(20);
|
|
||||||
for (var i = 0; i < numEvents; i++) {
|
|
||||||
var id = stream.readBits(9);
|
|
||||||
var name = stream.readASCIIString();
|
|
||||||
var type = stream.readBits(3);
|
|
||||||
var entries = [];
|
|
||||||
while (type !== 0) {
|
|
||||||
var entryName = stream.readASCIIString();
|
|
||||||
entries.push({
|
|
||||||
type: type,
|
|
||||||
name: entryName
|
|
||||||
});
|
|
||||||
type = stream.readBits(3);
|
|
||||||
}
|
|
||||||
events[id] = {
|
|
||||||
id : id,
|
|
||||||
name : name,
|
|
||||||
type : type,
|
|
||||||
entries: entries
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
packetType: 'gameEventList',
|
|
||||||
events : events
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
module.exports = function (stream) { // 17: parseSounds
|
|
||||||
var reliable = !!stream.readBits(1);
|
|
||||||
var num = (reliable) ? 1 : stream.readBits(8);
|
|
||||||
var length = (reliable) ? stream.readBits(8) : stream.readBits(16);
|
|
||||||
stream._index += length;
|
|
||||||
return {
|
|
||||||
packetType: 'parseSounds',
|
|
||||||
reliable : reliable,
|
|
||||||
num : num,
|
|
||||||
length : length
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
module.exports= function (stream) { // 5: setconvar
|
|
||||||
var count = stream.readBits(8);
|
|
||||||
var vars = {};
|
|
||||||
for (var i = 0; i < count; i++) {
|
|
||||||
vars[stream.readUTF8String()] = stream.readUTF8String();
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
packetType: 'setConVar',
|
|
||||||
vars : vars
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
var PacketStringTable = require('../../packetstringtable');
|
|
||||||
|
|
||||||
module.exports = function (stream) { // 12: updateStringTable
|
|
||||||
var stringTable = new PacketStringTable(stream);
|
|
||||||
var tables = stringTable.parse();
|
|
||||||
return {
|
|
||||||
packetType: 'updateStringTable',
|
|
||||||
table : tables
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
var ParserGenerator = require('../../parsergenerator');
|
|
||||||
|
|
||||||
var userMessageParsers = {
|
|
||||||
4: require('../userMessage/SayText2'),
|
|
||||||
5: ParserGenerator.make('textMsg', 'destType{8}text{s}')
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function (stream) { // 23: user message
|
|
||||||
var type = stream.readBits(8);
|
|
||||||
var length = stream.readBits(11);
|
|
||||||
var pos = stream._index;
|
|
||||||
if (userMessageParsers[type]) {
|
|
||||||
var result = userMessageParsers[type](stream);
|
|
||||||
} else {
|
|
||||||
result = {
|
|
||||||
packetType: 'unknownUserMessage',
|
|
||||||
type : type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stream._index = pos + length;
|
|
||||||
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
|
|
||||||
};
|
|
||||||
152
src/match.ts
152
src/match.ts
|
|
@ -1,152 +0,0 @@
|
||||||
var Match = function () {
|
|
||||||
this.tick = 0;
|
|
||||||
this.chat = [];
|
|
||||||
this.users = {};
|
|
||||||
this.deaths = [];
|
|
||||||
this.rounds = [];
|
|
||||||
this.startTick = 0;
|
|
||||||
this.intervalPerTick = 0;
|
|
||||||
this.entities = [];
|
|
||||||
this.stringTables = [];
|
|
||||||
this.sendTables = [];
|
|
||||||
this.serverClasses = [];
|
|
||||||
this.entities = [];
|
|
||||||
this.instanceBaselines = [[],[]];
|
|
||||||
this.staticBaseLines = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
Match.prototype.getSendTable = function (name) {
|
|
||||||
for (var i = 0; i < this.sendTables.length; i++) {
|
|
||||||
if (this.sendTables[i].name === name) {
|
|
||||||
return this.sendTables[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
Match.prototype.getStringTable = function (name) {
|
|
||||||
for (var i = 0; i < this.stringTables.length; i++) {
|
|
||||||
if (this.stringTables[i].name === name) {
|
|
||||||
return this.stringTables[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
Match.prototype.getState = function () {
|
|
||||||
return {
|
|
||||||
'chat' : this.chat,
|
|
||||||
'users' : this.users,
|
|
||||||
'deaths' : this.deaths,
|
|
||||||
'rounds' : this.rounds,
|
|
||||||
'startTick' : this.startTick,
|
|
||||||
'intervalPerTick': this.intervalPerTick
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Match.prototype.handlePacket = function (packet) {
|
|
||||||
var userState;
|
|
||||||
switch (packet.packetType) {
|
|
||||||
case 'netTick':
|
|
||||||
if (this.startTick === 0) {
|
|
||||||
this.startTick = packet.tick;
|
|
||||||
}
|
|
||||||
this.tick = packet.tick;
|
|
||||||
break;
|
|
||||||
case 'serverInfo':
|
|
||||||
this.intervalPerTick = packet.intervalPerTick;
|
|
||||||
break;
|
|
||||||
case 'sayText2':
|
|
||||||
this.chat.push({
|
|
||||||
kind: packet.kind,
|
|
||||||
from: packet.from,
|
|
||||||
text: packet.text,
|
|
||||||
tick: this.tick
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'stringTable':
|
|
||||||
if (packet.tables.userinfo) {
|
|
||||||
for (var j = 0; j < packet.tables.userinfo.length; j++) {
|
|
||||||
if (packet.tables.userinfo[j].extraData) {
|
|
||||||
var name = packet.tables.userinfo[j].extraData[0];
|
|
||||||
var steamId = packet.tables.userinfo[j].extraData[2];
|
|
||||||
var userId = packet.tables.userinfo[j].extraData[1].charCodeAt(0);
|
|
||||||
userState = this.getUserState(userId);
|
|
||||||
userState.name = name;
|
|
||||||
userState.steamId = steamId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'gameEvent':
|
|
||||||
switch (packet.event.name) {
|
|
||||||
case 'player_death':
|
|
||||||
while (packet.event.values.assister > 256 && packet.event.values.assister < (1024 * 16)) {
|
|
||||||
packet.event.values.assister -= 256;
|
|
||||||
}
|
|
||||||
var assister = packet.event.values.assister < 256 ? packet.event.values.assister : null;
|
|
||||||
// todo get player names, not same id as the name string table
|
|
||||||
while (packet.event.values.attacker > 256) {
|
|
||||||
packet.event.values.attacker -= 256;
|
|
||||||
}
|
|
||||||
while (packet.event.values.userid > 256) {
|
|
||||||
packet.event.values.userid -= 256;
|
|
||||||
}
|
|
||||||
this.deaths.push({
|
|
||||||
killer : packet.event.values.attacker,
|
|
||||||
assister: assister,
|
|
||||||
victim : packet.event.values.userid,
|
|
||||||
weapon : packet.event.values.weapon,
|
|
||||||
tick : this.tick
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'teamplay_round_win':
|
|
||||||
if (packet.event.values.winreason !== 6) {// 6 = timelimit
|
|
||||||
this.rounds.push({
|
|
||||||
winner : packet.event.values.team === 2 ? 'red' : 'blue',
|
|
||||||
length : packet.event.values.round_time,
|
|
||||||
end_tick: this.tick
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'player_spawn':
|
|
||||||
userId = packet.event.values.userid;
|
|
||||||
userState = this.getUserState(userId);
|
|
||||||
if (!userState.team) { //only register first spawn
|
|
||||||
userState.team = packet.event.values.team === 2 ? 'red' : 'blue'
|
|
||||||
}
|
|
||||||
var classId = packet.event.values.class;
|
|
||||||
if (!userState.classes[classId]) {
|
|
||||||
userState.classes[classId] = 0;
|
|
||||||
}
|
|
||||||
userState.classes[classId]++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Match.prototype.getUserState = function (userId) {
|
|
||||||
// no clue why it does this
|
|
||||||
// only seems to be the case with per user ready
|
|
||||||
while (userId > 256) {
|
|
||||||
userId -= 256;
|
|
||||||
}
|
|
||||||
if (!this.users[userId]) {
|
|
||||||
this.users[userId] = {
|
|
||||||
name : null,
|
|
||||||
userId : userId,
|
|
||||||
steamId: null,
|
|
||||||
classes: {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.users[userId];
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperty(Match.prototype, 'classBits', {
|
|
||||||
get: function () {
|
|
||||||
return Math.ceil(Math.log(this.serverClasses.length) * Math.LOG2E)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = Match;
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
var ParserGenerator = require('./parsergenerator');
|
|
||||||
|
|
||||||
// https://code.google.com/p/coldemoplayer/source/browse/branches/2.0/compLexity+Demo+Player/CDP.Source/Messages/?r=219
|
|
||||||
// https://github.com/TimePath/hl2-toolkit/tree/master/src/main/java/com/timepath/hl2/io/demo
|
|
||||||
// https://github.com/stgn/netdecode/blob/master/Packet.cs
|
|
||||||
// https://github.com/LestaD/SourceEngine2007/blob/master/src_main/common/netmessages.cpp
|
|
||||||
|
|
||||||
var Packet = function (type, tick, stream, length, viewOrigin, match) {
|
|
||||||
this.type = type;
|
|
||||||
this.tick = tick;
|
|
||||||
this.stream = stream;
|
|
||||||
this.length = length;//length in bytes
|
|
||||||
this.viewOrigin = viewOrigin;
|
|
||||||
this.match = match;
|
|
||||||
};
|
|
||||||
|
|
||||||
Packet.gameEventMap = {};
|
|
||||||
|
|
||||||
Object.defineProperty(Packet.prototype, 'bitsLeft', {
|
|
||||||
get: function () {
|
|
||||||
return (this.length * 8) - this.stream._index;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Packet.prototype.parse = function () {
|
|
||||||
//var table = new PacketStringTable(this.stream);
|
|
||||||
//table.searchIds();
|
|
||||||
//return [];
|
|
||||||
|
|
||||||
var packets = [];
|
|
||||||
var entities = [];
|
|
||||||
while (this.bitsLeft > 6) { // last 6 bits for NOOP
|
|
||||||
var type = this.stream.readBits(6);
|
|
||||||
if (Packet.parsers[type]) {
|
|
||||||
var packet = Packet.parsers[type].call(this, this.stream, Packet.gameEventMap, entities, this.match);
|
|
||||||
if (packet) {
|
|
||||||
packet.viewOrigin = this.viewOrigin;
|
|
||||||
}
|
|
||||||
//console.log(packet);
|
|
||||||
packets.push(packet);
|
|
||||||
} else {
|
|
||||||
throw 'Unknown packet type ' + type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return packets;
|
|
||||||
};
|
|
||||||
|
|
||||||
Packet.parsers = {
|
|
||||||
0 : function () {//NOOP
|
|
||||||
},
|
|
||||||
2 : ParserGenerator.make('file', 'transferId{32}fileName{s}requested{b}'),
|
|
||||||
3 : ParserGenerator.make('netTick', 'tick{32}frameTime{16}stdDev{16}'),
|
|
||||||
4 : ParserGenerator.make('stringCmd', 'command{s}'),
|
|
||||||
5 : require('./handlers/packet/setConVar'),
|
|
||||||
6 : ParserGenerator.make('sigOnState', 'state{8}count{32}'),
|
|
||||||
7 : ParserGenerator.make('print', 'value{s}'),
|
|
||||||
8 : ParserGenerator.make('serverInfo',
|
|
||||||
'version{16}serverCount{32}stv{b}dedicated{b}maxCrc{32}maxClasses{16}' +
|
|
||||||
'mapHash{128}playerCount{8}maxPlayerCount{8}intervalPerTick{f32}platform{s1}' +
|
|
||||||
'game{s}map{s}skybox{s}serverName{s}replay{b}'),
|
|
||||||
10: require('./handlers/packet/classInfo'),
|
|
||||||
11: ParserGenerator.make('setPause', 'paused{b}'),
|
|
||||||
12: require('./handlers/packet/createStringTable'),
|
|
||||||
13: require('./handlers/packet/updateStringTable'),
|
|
||||||
14: ParserGenerator.make('voiceInit', 'codec{s}quality{8}'),
|
|
||||||
15: ParserGenerator.make('voiceData', 'client{8}proximity{8}length{16}_{$length}'),
|
|
||||||
17: require('./handlers/packet/parseSounds'),
|
|
||||||
18: ParserGenerator.make('setView', 'index{11}'),
|
|
||||||
19: ParserGenerator.make('fixAngle', 'relative{b}x{16}y{16}z{16}'),
|
|
||||||
21: require('./handlers/packet/bspDecal'),
|
|
||||||
23: require('./handlers/packet/userMessage'),
|
|
||||||
24: require('./handlers/packet/entityMessage'),
|
|
||||||
25: require('./handlers/packet/gameEvent'),
|
|
||||||
26: require('./handlers/packet/packetEntities'),
|
|
||||||
27: ParserGenerator.make('tempEntities', 'count{8}length{17}_{$length}'),
|
|
||||||
28: ParserGenerator.make('preFetch', 'index{14}'),
|
|
||||||
29: ParserGenerator.make('menu', 'type{16}length{16}_{$length}_{$length}_{$length}_{$length}_{$length}_{$length}_{$length}'),//length*8
|
|
||||||
30: require('./handlers/packet/gameEventList'),
|
|
||||||
31: ParserGenerator.make('getCvarValue', 'cookie{32}value{s}'),
|
|
||||||
32: ParserGenerator.make('cmdKeyValues', 'length{32}data{$length}')
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = Packet;
|
|
||||||
|
|
@ -1,21 +1,14 @@
|
||||||
function logBase2(num) {
|
export class PacketStringTable {
|
||||||
var result = 0;
|
constructor(stream) {
|
||||||
while ((num >>= 1) != 0) {
|
|
||||||
result++;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
var PacketStringTable = function (stream) {
|
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.id = PacketStringTable.tables.length;
|
this.id = PacketStringTable.tables.length;
|
||||||
this.strings = [];
|
this.strings = [];
|
||||||
this.numEntries = 0;
|
this.numEntries = 0;
|
||||||
this.name = '';
|
this.name = '';
|
||||||
PacketStringTable.tables.push(this);
|
PacketStringTable.tables.push(this);
|
||||||
};
|
}
|
||||||
|
|
||||||
PacketStringTable.prototype.parse = function () {
|
parse() {
|
||||||
//todo
|
//todo
|
||||||
// https://coldemoplayer.googlecode.com/svn/branches/2.0/code/plugins/CDP.Source/Messages/SvcCreateStringTable.cs
|
// https://coldemoplayer.googlecode.com/svn/branches/2.0/code/plugins/CDP.Source/Messages/SvcCreateStringTable.cs
|
||||||
this.stream._index = this.stream._view._view.length * 8;
|
this.stream._index = this.stream._view._view.length * 8;
|
||||||
|
|
@ -23,14 +16,14 @@ PacketStringTable.prototype.parse = function () {
|
||||||
packetType: 'stringTableTODO'
|
packetType: 'stringTableTODO'
|
||||||
};
|
};
|
||||||
//return this.searchIds();
|
//return this.searchIds();
|
||||||
};
|
}
|
||||||
|
|
||||||
PacketStringTable.prototype.parsePlayerInfo = function () {
|
parsePlayerInfo() {
|
||||||
console.log('name: ' + this.stream.readUTF8String());
|
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
|
// "fuckit" parsing, look for anything that looks like a steam id, user id is the 32 bit before that
|
||||||
PacketStringTable.prototype.searchIds = function () {
|
searchIds() {
|
||||||
var validChar = function (charCode) {
|
var validChar = function (charCode) {
|
||||||
return charCode === 91 || charCode === 93 || charCode === 58 || (charCode > 47 && charCode < 58); // [ ] : 0-9
|
return charCode === 91 || charCode === 93 || charCode === 58 || (charCode > 47 && charCode < 58); // [ ] : 0-9
|
||||||
};
|
};
|
||||||
|
|
@ -71,8 +64,7 @@ PacketStringTable.prototype.searchIds = function () {
|
||||||
users[userId] = steamId;
|
users[userId] = steamId;
|
||||||
numFound++;
|
numFound++;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PacketStringTable.tables = [];
|
PacketStringTable.tables = [];
|
||||||
|
|
||||||
module.exports = PacketStringTable;
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
var toBuffer = require('typedarray-to-buffer')
|
import * as toBuffer from 'typedarray-to-buffer'
|
||||||
var util = require('util');
|
import {Packet} from './Parser/Message/Packet';
|
||||||
var Packet = require('./packet');
|
import {ConsoleCmd} from './Parser/Message/ConsoleCmd';
|
||||||
var ConsoleCmd = require('./consolecmd');
|
import {StringTable} from './Parser/Message/StringTable';
|
||||||
var StringTable = require('./stringtable');
|
import {DataTable} from './Parser/Message/DataTable';
|
||||||
var DataTable = require('./datatable');
|
import {UserCmd} from './Parser/Message/UserCmd';
|
||||||
var UserCmd = require('./usercmd');
|
import {BitStream} from 'bit-buffer';
|
||||||
var BitStream = require('bit-buffer').BitStream;
|
import {EventEmitter} from 'events';
|
||||||
var EventEmitter = require('events').EventEmitter;
|
import {Match} from './Data/Match';
|
||||||
var Match = require('./match');
|
|
||||||
|
|
||||||
var Parser = function (stream) {
|
class Parser extends EventEmitter {
|
||||||
|
constructor(stream) {
|
||||||
|
super();
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.packets = [];
|
this.packets = [];
|
||||||
this.match = new Match();
|
this.match = new Match();
|
||||||
|
|
@ -17,26 +18,13 @@ var Parser = function (stream) {
|
||||||
this.on('packet', function (packet) {
|
this.on('packet', function (packet) {
|
||||||
this.packets.push(packet);
|
this.packets.push(packet);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
util.inherits(Parser, EventEmitter);
|
readHeader() {
|
||||||
|
|
||||||
Parser.MessageType = {
|
|
||||||
Sigon : 1,
|
|
||||||
Packet : 2,
|
|
||||||
SyncTick : 3,
|
|
||||||
ConsoleCmd : 4,
|
|
||||||
UserCmd : 5,
|
|
||||||
DataTables : 6,
|
|
||||||
Stop : 7,
|
|
||||||
StringTables: 8
|
|
||||||
};
|
|
||||||
|
|
||||||
Parser.prototype.readHeader = function () {
|
|
||||||
return this.parseHeader(this.stream);
|
return this.parseHeader(this.stream);
|
||||||
};
|
}
|
||||||
|
|
||||||
Parser.prototype.parseHeader = function (stream) {
|
parseHeader(stream) {
|
||||||
return {
|
return {
|
||||||
'type' : stream.readASCIIString(8),
|
'type' : stream.readASCIIString(8),
|
||||||
'version' : stream.readInt32(),
|
'version' : stream.readInt32(),
|
||||||
|
|
@ -50,18 +38,18 @@ Parser.prototype.parseHeader = function (stream) {
|
||||||
'frames' : stream.readInt32(),
|
'frames' : stream.readInt32(),
|
||||||
'sigon' : stream.readInt32()
|
'sigon' : stream.readInt32()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Parser.prototype.parseBody = function () {
|
parseBody() {
|
||||||
var message;
|
var message;
|
||||||
while (message = this.readMessage(this.stream, this.match)) {
|
while (message = this.readMessage(this.stream, this.match)) {
|
||||||
this.handleMessage(message);
|
this.handleMessage(message);
|
||||||
}
|
}
|
||||||
this.emit('done', this.match);
|
this.emit('done', this.match);
|
||||||
return this.match;
|
return this.match;
|
||||||
};
|
}
|
||||||
|
|
||||||
Parser.prototype.parseMessage = function (buffer, type, tick, length, viewOrigin, match) {
|
parseMessage(buffer, type, tick, length, viewOrigin, match) {
|
||||||
var data = new BitStream(buffer);
|
var data = new BitStream(buffer);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
@ -79,9 +67,9 @@ Parser.prototype.parseMessage = function (buffer, type, tick, length, viewOrigin
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Parser.prototype.handleMessage = function (message) {
|
handleMessage(message) {
|
||||||
if (message.parse) {
|
if (message.parse) {
|
||||||
var packets = message.parse();
|
var packets = message.parse();
|
||||||
for (var i = 0; i < packets.length; i++) {
|
for (var i = 0; i < packets.length; i++) {
|
||||||
|
|
@ -91,9 +79,9 @@ Parser.prototype.handleMessage = function (message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Parser.prototype.readMessage = function (stream, match) {
|
readMessage(stream, match) {
|
||||||
var type = stream.readBits(8);
|
var type = stream.readBits(8);
|
||||||
if (type === Parser.MessageType.Stop) {
|
if (type === Parser.MessageType.Stop) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -138,6 +126,18 @@ Parser.prototype.readMessage = function (stream, match) {
|
||||||
buffer = toBuffer(stream._view._view.slice(start, start + length));
|
buffer = toBuffer(stream._view._view.slice(start, start + length));
|
||||||
stream.byteIndex += length;
|
stream.byteIndex += length;
|
||||||
return this.parseMessage(buffer, type, tick, length, viewOrigin, match);
|
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;
|
module.exports = Parser;
|
||||||
|
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
var BitStream = require('bit-buffer').BitStream;
|
|
||||||
|
|
||||||
var StringTable = function (type, tick, stream, length, match) {
|
|
||||||
this.type = type;
|
|
||||||
this.tick = tick;
|
|
||||||
this.stream = stream;
|
|
||||||
this.length = length;//length in bytes
|
|
||||||
this.match = match;
|
|
||||||
};
|
|
||||||
|
|
||||||
StringTable.prototype.parse = function () {
|
|
||||||
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/ST/StringTableParser.cs
|
|
||||||
var tableCount = this.stream.readUint8();
|
|
||||||
var tables = {};
|
|
||||||
var extraDataLength;
|
|
||||||
for (var i = 0; i < tableCount; i++) {
|
|
||||||
var entries = [];
|
|
||||||
var tableName = this.stream.readASCIIString();
|
|
||||||
var entryCount = this.stream.readUint16();
|
|
||||||
for (var j = 0; j < entryCount; j++) {
|
|
||||||
try {
|
|
||||||
var entry = {
|
|
||||||
text: this.stream.readUTF8String()
|
|
||||||
};
|
|
||||||
} catch (e){
|
|
||||||
return [{
|
|
||||||
packetType: 'stringTable',
|
|
||||||
tables : tables
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
if (this.stream.readBits(1)) {
|
|
||||||
extraDataLength = this.stream.readUint16();
|
|
||||||
if (tableName === 'instancebaseline') {
|
|
||||||
this.match.staticBaseLines[parseInt(entry.text, 10)] = this.stream.readBitStream(8 * extraDataLength);
|
|
||||||
} else {
|
|
||||||
entry.extraData = this.readExtraData(extraDataLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entries.push(entry);
|
|
||||||
}
|
|
||||||
tables[tableName] = entries;
|
|
||||||
this.match.stringTables.push({
|
|
||||||
name : tableName,
|
|
||||||
entries: entries
|
|
||||||
});
|
|
||||||
if (this.stream.readBits(1)) {
|
|
||||||
this.stream.readASCIIString();
|
|
||||||
if (this.stream.readBits(1)) {
|
|
||||||
//throw 'more extra data not implemented';
|
|
||||||
extraDataLength = this.stream.readBits(16);
|
|
||||||
this.stream.readBits(extraDataLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//console.log(tables);
|
|
||||||
return [{
|
|
||||||
packetType: 'stringTable',
|
|
||||||
tables : tables
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
|
|
||||||
StringTable.prototype.readExtraData = function (length) {
|
|
||||||
var end = this.stream._index + (length * 8);
|
|
||||||
var data = [];
|
|
||||||
//console.log(this.stream.readUTF8String());
|
|
||||||
data.push(this.stream.readUTF8String());
|
|
||||||
while (this.stream._index < end) {
|
|
||||||
try {
|
|
||||||
var string = this.stream.readUTF8String();
|
|
||||||
} catch (e) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
if (string) {
|
|
||||||
data.push(string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.stream._index = end;
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = StringTable;
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
var UserCMD = function (type, tick, stream, length, match) {
|
|
||||||
this.type = type;
|
|
||||||
this.tick = tick;
|
|
||||||
this.stream = stream;
|
|
||||||
this.length = length;//length in bytes
|
|
||||||
this.match = match;
|
|
||||||
};
|
|
||||||
|
|
||||||
UserCMD.prototype.parse = function () {
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = UserCMD;
|
|
||||||
|
|
@ -5,10 +5,10 @@
|
||||||
"es2015.promise",
|
"es2015.promise",
|
||||||
"es5"
|
"es5"
|
||||||
],
|
],
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "ES5",
|
"target": "ES5",
|
||||||
"outDir": "build",
|
"outDir": "build",
|
||||||
"rootDir": "src"
|
"rootDir": "src",
|
||||||
|
"strictNullChecks": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
40
typings/modules/bit-buffer/index.d.ts
vendored
Normal file
40
typings/modules/bit-buffer/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue