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

parse deaths and strictly type packet types

This commit is contained in:
Robin Appelman 2017-02-12 15:09:03 +01:00
commit cb02fdee9b
18 changed files with 217 additions and 119 deletions

7
src/Data/Death.ts Normal file
View file

@ -0,0 +1,7 @@
export interface Death {
weapon: string;
victim: number;
assister: number;
killer: number;
tick: number;
}

View file

@ -9,11 +9,13 @@ import {UserInfo} from "./UserInfo";
import {World} from "./World"; import {World} from "./World";
import {Vector} from "./Vector"; import {Vector} from "./Vector";
import {Player} from "./Player"; import {Player} from "./Player";
import {Death} from "./Death";
import {HandlerMap} from "../PacketHandler/Handler";
export class Match { export class Match {
tick: number; tick: number;
chat: any[]; chat: any[];
users: UserInfo[]; users: UserInfo[];
deaths: any[]; deaths: Death[];
rounds: any[]; rounds: any[];
startTick: number; startTick: number;
intervalPerTick: number; intervalPerTick: number;
@ -53,18 +55,18 @@ export class Match {
} }
getSendTable(name) { getSendTable(name) {
for (var i = 0; i < this.sendTables.length; i++) { for (const table of this.sendTables) {
if (this.sendTables[i].name === name) { if (table.name === name) {
return this.sendTables[i]; return table;
} }
} }
throw new Error("unknown SendTable " + name); throw new Error("unknown SendTable " + name);
} }
getStringTable(name) { getStringTable(name) {
for (var i = 0; i < this.stringTables.length; i++) { for (const table of this.stringTables) {
if (this.stringTables[i].name === name) { if (table.name === name) {
return this.stringTables[i]; return table;
} }
} }
return null; return null;
@ -112,7 +114,7 @@ export class Match {
if (table.name === 'userinfo') { if (table.name === 'userinfo') {
for (const userData of table.entries) { for (const userData of table.entries) {
if (userData.extraData) { if (userData.extraData) {
if (userData.extraData.bitsLeft > (32*8)) { if (userData.extraData.bitsLeft > (32 * 8)) {
const name = userData.extraData.readUTF8String(32); const name = userData.extraData.readUTF8String(32);
const userId = userData.extraData.readUint32(); const userId = userData.extraData.readUint32();
const steamId = userData.extraData.readUTF8String(); const steamId = userData.extraData.readUTF8String();
@ -164,12 +166,11 @@ export class Match {
const userId = packet.event.values.userid; const userId = packet.event.values.userid;
const userState = this.getUserInfo(userId); const userState = this.getUserInfo(userId);
const player = this.playerMap[userState.entityId]; const player = this.playerMap[userState.entityId];
if (!userState.team) { //only register first spawn userState.team = packet.event.values.team === 2 ? 'red' : 'blue';
userState.team = packet.event.values.team === 2 ? 'red' : 'blue'
}
const classId = packet.event.values.class; const classId = packet.event.values.class;
if (player) { if (player) {
player.classId = classId; player.classId = classId;
player.team = packet.event.values.team;
} }
if (!userState.classes[classId]) { if (!userState.classes[classId]) {
userState.classes[classId] = 0; userState.classes[classId] = 0;
@ -209,6 +210,15 @@ export class Match {
throw new Error('User not found for entity ' + entity.entityIndex); throw new Error('User not found for entity ' + entity.entityIndex);
} }
getPlayerByUserId(userId: number): Player {
for (const player of this.players) {
if (player.user.userId === userId) {
return player;
}
}
throw new Error('player not found for user id');
}
get classBits() { get classBits() {
return Math.ceil(Math.log(this.serverClasses.length) * Math.LOG2E) return Math.ceil(Math.log(this.serverClasses.length) * Math.LOG2E)
} }
@ -220,11 +230,14 @@ export class Match {
this.world.boundaryMax = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMaxs').value; this.world.boundaryMax = <Vector>entity.getProperty('DT_WORLD', 'm_WorldMaxs').value;
break; break;
case 'CTFPlayer': case 'CTFPlayer':
try {
const player: Player = (this.playerMap[entity.entityIndex]) ? this.playerMap[entity.entityIndex] : { const player: Player = (this.playerMap[entity.entityIndex]) ? this.playerMap[entity.entityIndex] : {
user: this.getUserInfoForEntity(entity), user: this.getUserInfoForEntity(entity),
position: new Vector(0, 0, 0), position: new Vector(0, 0, 0),
maxHealth: 0, maxHealth: 0,
health: 0, health: 0,
classId: 0,
team: 0
}; };
if (!this.playerMap[entity.entityIndex]) { if (!this.playerMap[entity.entityIndex]) {
this.playerMap[entity.entityIndex] = player; this.playerMap[entity.entityIndex] = player;
@ -257,6 +270,9 @@ export class Match {
break; break;
} }
} }
} catch (e) {
}
} }
entity.updatedProps = []; entity.updatedProps = [];

View file

@ -1,4 +1,93 @@
export interface Packet { import {StringTable} from "./StringTable";
packetType: string; import {Vector} from "./Vector";
[name: string]: any; import {GameEvent} from "./GameEvent";
export interface StringTablePacket {
packetType: 'stringTable';
tables: StringTable[];
} }
export interface BSPDecalPacket {
packetType: 'bspDecal';
position: Vector;
textureIndex: number;
entIndex: number;
modelIndex: number;
lowPriority: boolean;
}
export interface ClassInfoPacket {
packetType: 'classInfo';
number: number;
create: boolean;
entries: {
classId: number;
className: string;
dataTableName: string;
}[]
}
export interface EntityMessagePacket {
packetType: 'entityMessage';
classId: number;
length: number;
data: string;
}
export interface GameEventPacket {
packetType: 'gameEvent';
event: GameEvent;
}
export interface GameEventListPacket {
packetType: 'gameEventList';
}
export interface PacketEntitiesPacket {
packetType: 'packetEntities';
}
export interface ParseSoundsPacket {
packetType: 'parseSounds';
reliable: boolean;
num: number;
length: number;
}
export interface SetConVarPacket {
packetType: 'setConVar';
vars: {[key: string]: string};
}
export interface SayText2Packet {
packetType: 'sayText2';
client: number;
raw: number;
kind: string;
from: string;
text: string;
}
export interface TextMessagePacket {
packetType: 'textMsg';
destType: number;
text: string;
}
export interface UnknownUserMessagePacket {
packetType: 'unknownUserMessage';
type: number;
}
export type UserMessagePacket = SayText2Packet | TextMessagePacket | UnknownUserMessagePacket;
export type Packet = BSPDecalPacket |
StringTablePacket |
ClassInfoPacket |
EntityMessagePacket |
GameEventPacket |
GameEventListPacket |
PacketEntitiesPacket |
ParseSoundsPacket |
SetConVarPacket |
UserMessagePacket;

View file

@ -6,4 +6,5 @@ export interface Player {
health: number; health: number;
maxHealth: number; maxHealth: number;
classId: number; classId: number;
team: number;
} }

View file

@ -85,7 +85,7 @@ export class Packet extends Parser {
enum PacketType { enum PacketType {
file = 2, file = 2,
netTick = 3, netTick = 3,
stringSmd = 4, stringCmd = 4,
setConVar = 5, setConVar = 5,
sigOnState = 6, sigOnState = 6,
print = 7, print = 7,

View file

@ -1,5 +1,6 @@
import {Packet} from "../../Data/Packet"; import {BSPDecalPacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import {Vector} from "../../Data/Vector";
const getCoord = function (stream) { const getCoord = function (stream) {
const hasInt = !!stream.readBits(1); const hasInt = !!stream.readBits(1);
@ -20,7 +21,7 @@ const getCoord = function (stream) {
return value; return value;
}; };
const getVecCoord = function (stream) { const getVecCoord = function (stream): Vector {
const hasX = !!stream.readBits(1); const hasX = !!stream.readBits(1);
const hasY = !!stream.readBits(1); const hasY = !!stream.readBits(1);
const hasZ = !!stream.readBits(1); const hasZ = !!stream.readBits(1);
@ -31,7 +32,7 @@ const getVecCoord = function (stream) {
} }
}; };
export function BSPDecal(stream: BitStream): Packet { // 21: BSPDecal export function BSPDecal(stream: BitStream): BSPDecalPacket { // 21: BSPDecal
let modelIndex, entIndex; let modelIndex, entIndex;
const position = getVecCoord(stream); const position = getVecCoord(stream);
const textureIndex = stream.readBits(9); const textureIndex = stream.readBits(9);
@ -39,7 +40,7 @@ export function BSPDecal(stream: BitStream): Packet { // 21: BSPDecal
entIndex = stream.readBits(11); entIndex = stream.readBits(11);
modelIndex = stream.readBits(12); modelIndex = stream.readBits(12);
} }
const lowPriority = !!stream.readBits(1); const lowPriority = stream.readBoolean();
return { return {
packetType: 'bspDecal', packetType: 'bspDecal',
position: position, position: position,

View file

@ -1,10 +1,10 @@
import {Packet} from "../../Data/Packet"; import {ClassInfoPacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import {logBase2} from '../../Math'; import {logBase2} from '../../Math';
export function ClassInfo(stream: BitStream): Packet { // 10: classInfo export function ClassInfo(stream: BitStream): ClassInfoPacket { // 10: classInfo
const number = stream.readBits(16); const number = stream.readBits(16);
const create = !!stream.readBits(1); const create = stream.readBoolean();
let entries: any[] = []; let entries: any[] = [];
if (!create) { if (!create) {
const bits = logBase2(number) + 1; const bits = logBase2(number) + 1;

View file

@ -1,16 +1,14 @@
import {PacketStringTable} from './PacketStringTable'; import {StringTablePacket} from "../../Data/Packet";
import {Packet} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import {logBase2} from "../../Math"; import {logBase2} from "../../Math";
import {readBitVar, readVarInt} from "../readBitVar"; import {readVarInt} from "../readBitVar";
import {uncompress} from "snappyjs"; import {uncompress} from "snappyjs";
import {StringTable} from "../../Data/StringTable"; import {StringTable} from "../../Data/StringTable";
import {parseStringTable} from "../StringTableParser"; import {parseStringTable} from "../StringTableParser";
import {Match} from "../../Data/Match"; import {Match} from "../../Data/Match";
export function CreateStringTable(stream: BitStream, match: Match): Packet { // 12: createStringTable export function CreateStringTable(stream: BitStream, match: Match): StringTablePacket { // 12: createStringTable
const tableName = stream.readASCIIString(); const tableName = stream.readASCIIString();
const maxEntries = stream.readUint16(); const maxEntries = stream.readUint16();
const encodeBits = logBase2(maxEntries); const encodeBits = logBase2(maxEntries);

View file

@ -1,10 +1,10 @@
import {make} from './ParserGenerator'; import {make} from './ParserGenerator';
import {Packet} from "../../Data/Packet"; import {EntityMessagePacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
const baseParser = make('entityMessage', 'index{11}classId{9}length{11}data{$length}'); const baseParser = make('entityMessage', 'index{11}classId{9}length{11}data{$length}');
export function EntityMessage(stream:BitStream):Packet { // 24: entityMessage export function EntityMessage(stream: BitStream): EntityMessagePacket { // 24: entityMessage
return baseParser(stream); //todo parse data further? return <EntityMessagePacket>baseParser(stream); //todo parse data further?
}; };

View file

@ -1,4 +1,4 @@
import {Packet} from "../../Data/Packet"; import {GameEventPacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import { import {
GameEventType, GameEventValue, GameEventEntry, GameEventDefinition, GameEvent as IGameEvent, GameEventType, GameEventValue, GameEventEntry, GameEventDefinition, GameEvent as IGameEvent,
@ -6,9 +6,9 @@ import {
} from "../../Data/GameEvent"; } from "../../Data/GameEvent";
import {Match} from "../../Data/Match"; import {Match} from "../../Data/Match";
const parseGameEvent = function (eventId: number, stream: BitStream, events: GameEventDefinitionMap): IGameEvent|null { const parseGameEvent = function (eventId: number, stream: BitStream, events: GameEventDefinitionMap): IGameEvent {
if (!events[eventId]) { if (!events[eventId]) {
return null; throw new Error('unknown event type')
} }
const eventDescription: GameEventDefinition = events[eventId]; const eventDescription: GameEventDefinition = events[eventId];
const values: GameEventValueMap = {}; const values: GameEventValueMap = {};
@ -47,7 +47,7 @@ const getGameEventValue = function (stream: BitStream, entry: GameEventEntry): G
}; };
export function GameEvent(stream: BitStream, match: Match): Packet { // 25: game event export function GameEvent(stream: BitStream, match: Match): GameEventPacket { // 25: game event
const length = stream.readBits(11); const length = stream.readBits(11);
const end = stream.index + length; const end = stream.index + length;
const eventId = stream.readBits(9); const eventId = stream.readBits(9);

View file

@ -1,9 +1,9 @@
import {Packet} from "../../Data/Packet"; import {GameEventListPacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import {GameEventEntry, GameEventDefinitionMap} from "../../Data/GameEvent"; import {GameEventEntry, GameEventDefinitionMap} from "../../Data/GameEvent";
import {Match} from "../../Data/Match"; import {Match} from "../../Data/Match";
export function GameEventList(stream: BitStream, match: Match): Packet { // 30: gameEventList export function GameEventList(stream: BitStream, match: Match): GameEventListPacket { // 30: gameEventList
// list of game events and parameters // list of game events and parameters
const numEvents = stream.readBits(9); const numEvents = stream.readBits(9);
const length = stream.readBits(20); const length = stream.readBits(20);

View file

@ -1,9 +1,7 @@
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 {PacketEntitiesPacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import {GameEventDefinition} from "../../Data/GameEvent";
import {Match} from "../../Data/Match"; import {Match} from "../../Data/Match";
import {readUBitVar} from "../readBitVar"; import {readUBitVar} from "../readBitVar";
import {applyEntityUpdate} from "../EntityDecoder"; import {applyEntityUpdate} from "../EntityDecoder";
@ -74,7 +72,7 @@ function readLeavePVS(match, entityId, shouldDelete) {
} }
} }
export function PacketEntities(stream: BitStream, match: Match): Packet { //26: packetEntities export function PacketEntities(stream: BitStream, match: Match): PacketEntitiesPacket { //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

View file

@ -1,12 +0,0 @@
import {BitStream} from "bit-buffer";
import {Packet} from "../../Data/Packet";
export function PacketStringTable(stream: BitStream):Packet {
//todo
// https://coldemoplayer.googlecode.com/svn/branches/2.0/code/plugins/CDP.Source/Messages/SvcCreateStringTable.cs
// throw new Error('packetstringtable not implemented');
stream.index = stream.length; // no idea, skip to the end of the
return {
packetType: 'stringTableTODO'
};
}

View file

@ -1,7 +1,7 @@
import {Packet} from "../../Data/Packet"; import {ParseSoundsPacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
export function ParseSounds(stream: BitStream): Packet { // 17: parseSounds export function ParseSounds(stream: BitStream): ParseSoundsPacket { // 17: parseSounds
const reliable = stream.readBoolean(); const reliable = stream.readBoolean();
const num = (reliable) ? 1 : stream.readUint8(); const num = (reliable) ? 1 : stream.readUint8();
const length = (reliable) ? stream.readUint8() : stream.readUint16(); const length = (reliable) ? stream.readUint8() : stream.readUint16();

View file

@ -1,9 +1,9 @@
import {Packet} from "../../Data/Packet"; import {SetConVarPacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
export function SetConVar(stream: BitStream): Packet { // 5: setconvar export function SetConVar(stream: BitStream): SetConVarPacket { // 5: setconvar
const count = stream.readBits(8); const count = stream.readBits(8);
let vars = {}; let vars: {[key: string]: string} = {};
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
vars[stream.readUTF8String()] = stream.readUTF8String(); vars[stream.readUTF8String()] = stream.readUTF8String();
} }

View file

@ -1,10 +1,10 @@
import {PacketStringTable} from './PacketStringTable'; import {PacketStringTable} from './PacketStringTable';
import {Packet} from "../../Data/Packet"; import {StringTablePacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import {Match} from "../../Data/Match"; import {Match} from "../../Data/Match";
import {parseStringTable} from "../StringTableParser"; import {parseStringTable} from "../StringTableParser";
export function UpdateStringTable(stream: BitStream, match: Match): Packet { // 12: updateStringTable export function UpdateStringTable(stream: BitStream, match: Match): StringTablePacket { // 12: updateStringTable
const tableId = stream.readBits(5); const tableId = stream.readBits(5);
const multipleChanged = stream.readBoolean(); const multipleChanged = stream.readBoolean();

View file

@ -1,4 +1,4 @@
import {Packet} from "../../Data/Packet"; import {UserMessagePacket} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import {make} from './ParserGenerator'; import {make} from './ParserGenerator';
import {SayText2} from '../UserMessage/SayText2'; import {SayText2} from '../UserMessage/SayText2';
@ -69,7 +69,7 @@ const userMessageParsers = {
5: make('textMsg', 'destType{8}text{s}') 5: make('textMsg', 'destType{8}text{s}')
}; };
export function UserMessage(stream: BitStream): Packet { // 23: user message export function UserMessage(stream: BitStream): UserMessagePacket { // 23: user message
const type = stream.readBits(8); const type = stream.readBits(8);
const length = stream.readBits(11); const length = stream.readBits(11);
const pos = stream.index; const pos = stream.index;

View file

@ -1,7 +1,7 @@
import {Packet} from "../../Data/Packet"; import {SayText2Packet} from "../../Data/Packet";
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
export function SayText2(stream: BitStream): Packet { // 4: SayText2 export function SayText2(stream: BitStream): SayText2Packet { // 4: SayText2
var client = stream.readBits(8); var client = stream.readBits(8);
var raw = stream.readBits(8); var raw = stream.readBits(8);
var pos = stream.index; var pos = stream.index;