1
0
Fork 0
mirror of https://github.com/demostf/demo.js synced 2026-06-04 09:04:13 +02:00

more typescript conversions

This commit is contained in:
Robin Appelman 2016-12-18 17:45:16 +01:00
commit 94383f447f
48 changed files with 1204 additions and 1051 deletions

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

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

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

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

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

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

View file

@ -0,0 +1,192 @@
import {SendPropParser} from '../../Parser/SendPropParser';
import {Entity} from '../../Data/Entity';
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 = {
PRESERVE: 0,
ENTER: 1,
LEAVE: 2,
DELETE: 4
};
function readPVSType(stream) {
// https://github.com/skadistats/smoke/blob/a2954fbe2fa3936d64aee5b5567be294fef228e6/smoke/io/stream/entity.pyx#L24
var pvs;
var hi = stream.readBoolean();
var low = stream.readBoolean();
if (low && !hi) {
pvs = PVS.ENTER;
} else if (!(hi || low)) {
pvs = PVS.PRESERVE;
} else if (hi) {
pvs = (low) ? (PVS.LEAVE | PVS.DELETE) : PVS.LEAVE;
} else {
pvs = -1;
}
return pvs;
}
function readEnterPVS(stream, entityId, match, baseLine) {
// https://github.com/PazerOP/DemoLib/blob/5f9467650f942a4a70f9ec689eadcd3e0a051956/TF2Net/NetMessages/NetPacketEntitiesMessage.cs#L198
var serverClass = match.serverClasses[stream.readBits(match.classBits)];
console.log(serverClass);
var sendTable = match.getSendTable(serverClass.dataTable);
var serialNumber = stream.readBits(10);
var entity = (match.entities[entityId]) ? match.entities[entityId] : new Entity(serverClass, sendTable, entityId, serialNumber);
var decodedBaseLine = match.instanceBaselines[baseLine][entityId];
if (decodedBaseLine) {
for (var i = 0; i < decodedBaseLine.length; i++) {
var newProp = decodedBaseLine[i];
if (!entity.getPropByDefinition(newProp.definition)) {
entity.props.push(newProp.clone(entity));
}
}
} else {
var staticBaseLine = match.staticBaseLines[serverClass.id];
if (staticBaseLine) {
var streamStart = staticBaseLine._index;
applyEntityUpdate(entity, staticBaseLine);
staticBaseLine._index = streamStart;
}
}
return entity;
}
function readLeavePVS(match, entityId, shouldDelete) {
if (shouldDelete) {
match.entities[entityId] = null;
}
}
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/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/DP/Handler/PacketEntitesHandler.cs
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/DP/Entity.cs
// https://github.com/PazerOP/DemoLib/blob/5f9467650f942a4a70f9ec689eadcd3e0a051956/TF2Net/NetMessages/NetPacketEntitiesMessage.cs
// todo
var maxEntries = stream.readBits(11);
var isDelta = !!stream.readBits(1);
if (isDelta) {
var delta = stream.readInt32();
} else {
delta = null;
}
var baseLine = stream.readBits(1);
var updatedEntries = stream.readBits(11);
var length = stream.readBits(20);
var updatedBaseLine = stream.readBoolean();
var end = stream._index + length;
var entityId = -1;
stream._index = end;
return {
packetType: 'packetEntities',
entities: entities
};
if (updatedBaseLine) {
if (baseLine === 0) {
match.instanceBaselines[1] = match.instanceBaselines[0];
match.instanceBaselines[0] = new Array((1 << 11)); // array of SendPropDefinition with size MAX_EDICTS
} else {
match.instanceBaselines[0] = match.instanceBaselines[1];
match.instanceBaselines[1] = new Array((1 << 11)); // array of SendPropDefinition with size MAX_EDICTS
}
}
for (var i = 0; i < updatedEntries; i++) {
var diff = readUBitVar(stream);
console.log(diff);
entityId += 1 + diff;
var pvs = readPVSType(stream);
if (pvs === PVS.ENTER) {
var entity = readEnterPVS(stream, entityId, match, baseLine);
applyEntityUpdate(entity, stream);
match.entities[entityId] = entity;
if (updatedBaseLine) {
match.instanceBaselines[baseLine][entityId] = [].concat(entity.props);
}
entity.inPVS = true;
} else if (pvs === PVS.PRESERVE) {
entity = match.entities[entityId];
if (!entity) {
console.log(entityId, match.entities.length);
throw new Error("unknown entity");
}
applyEntityUpdate(entity, stream);
} else {
entity = match.entities[entityId];
if (entity) {
entity.inPVS = false;
}
readLeavePVS(match, entityId, pvs === PVS.DELETE);
}
}
if (isDelta) {
while (stream.readBoolean()) {
var ent = stream.readBits(11);
match.entities[ent] = null;
}
}
stream._index = end;
return {
packetType: 'packetEntities',
entities: entities
};
};
var readFieldIndex = function (stream, lastIndex) {
if (!stream.readBoolean()) {
return -1;
}
var diff = readUBitVar(stream);
return lastIndex + diff + 1;
};
var applyEntityUpdate = function (entity, stream) {
var index = -1;
var allProps = entity.sendTable.flattenedProps;
while ((index = readFieldIndex(stream, index)) != -1) {
if (index > 4096) {
throw new Error('prop index out of bounds');
}
console.log(index);
var propDefinition = allProps[index];
var existingProp = entity.getPropByDefinition(propDefinition);
var prop;
if (existingProp) {
prop = existingProp;
} else {
prop = new SendProp(propDefinition);
}
prop.value = SendPropParser.decode(propDefinition, stream);
console.log(prop);
if (!existingProp) {
entity.props.push(prop);
}
}
return entity;
};
var readUBitVar = function (stream) {
switch (stream.readBits(2)) {
case 0:
return stream.readBits(4);
case 1:
return stream.readBits(8);
case 2:
return stream.readBits(12);
case 3:
return stream.readBits(32);
}
};

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

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

View file

@ -0,0 +1,48 @@
import {Parser} from './Parser';
export function make(name: string, definition: string): Parser {
var parts = definition.substr(0, definition.length - 1).split('}');//remove leading } to prevent empty part
var items = parts.map(function (part) {
return part.split('{');
});
return function (stream) {
var result = {
'packetType': name
};
try {
for (var i = 0; i < items.length; i++) {
var value = readItem(stream, items[i][1], result);
if (items[i][0] !== '_') {
result[items[i][0]] = value;
}
}
} catch (e) {
throw 'Failed reading pattern ' + definition + '. ' + e;
}
return result;
}
}
const readItem = function (stream, description, data) {
var length;
if (description[0] === 'b') {
return !!stream.readBits(1);
} else if (description[0] === 's') {
if (description.length === 1) {
return stream.readUTF8String();
} else {
length = parseInt(description.substr(1), 10);
return stream.readASCIIString(length);
}
} else if (description === 'f32') {
return stream.readFloat32();
} else if (description[0] === 'u') {
length = parseInt(description.substr(1), 10);
return stream.readBits(length);
} else if (description[0] === '$') {
var variable = description.substr(1);
return stream.readBits(variable);
} else {
return stream.readBits(parseInt(description, 10), true);
}
};

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

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

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