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

start splitting parserstate from analysing

This commit is contained in:
Robin Appelman 2017-09-23 17:41:52 +02:00
commit 198fe0b1ba
18 changed files with 459 additions and 421 deletions

View file

@ -1,19 +1,12 @@
import {BitStream} from 'bit-buffer';
import {handleDataTable} from '../PacketHandler/DataTable';
import {handleGameEvent} from '../PacketHandler/GameEvent';
import {handleGameEventList} from '../PacketHandler/GameEventList';
import {handlePacketEntities} from '../PacketHandler/PacketEntities';
import {handleSayText2} from '../PacketHandler/SayText2';
import {handleStringTable, handleStringTables, handleStringTableUpdate} from '../PacketHandler/StringTable';
import {Building} from './Building';
import {Death} from './Death';
import {GameEventDefinition} from './GameEvent';
import {EntityId, PacketEntity} from './PacketEntity';
import {Player} from './Player';
import {PlayerResource} from './PlayerResource';
import {SendTable, SendTableName} from './SendTable';
import {ServerClass, ServerClassId} from './ServerClass';
import {StringTable} from './StringTable';
import {Team, TeamNumber} from './Team';
import {UserInfo} from './UserInfo';
import {Weapon} from './Weapon';
@ -21,11 +14,10 @@ import {World} from './World';
import {Round} from './Round';
import {Chat} from './Chat';
import {Packet} from './Packet';
import {GameEventType} from './GameEventTypes';
import {ParserState} from './ParserState';
import {SendProp} from './SendProp';
import {StringTableEntry} from './StringTable';
export class Match implements ParserState {
export class Match {
public tick: number = 0;
public chat: Chat[] = [];
public users: Map<number, UserInfo> = new Map();
@ -33,26 +25,18 @@ export class Match implements ParserState {
public rounds: Round[] = [];
public startTick: number = 0;
public intervalPerTick: number = 0;
public staticBaseLines: Map<ServerClassId, BitStream> = new Map();
public staticBaselineCache: Map<ServerClassId, SendProp[]> = new Map();
public eventDefinitions: Map<number, GameEventDefinition<GameEventType>> = new Map();
public world: World = {
boundaryMin: {x: 0, y: 0, z: 0},
boundaryMax: {x: 0, y: 0, z: 0},
};
public playerEntityMap: Map<EntityId, Player> = new Map();
public entityClasses: Map<EntityId, ServerClass> = new Map();
public sendTables: Map<SendTableName, SendTable> = new Map();
public instanceBaselines: [Map<EntityId, SendProp[]>, Map<EntityId, SendProp[]>] = [new Map(), new Map()];
public weaponMap: Map<EntityId, Weapon> = new Map();
public outerMap: Map<number, EntityId> = new Map();
public teams: Map<TeamNumber, Team> = new Map();
public teamEntityMap: Map<EntityId, Team> = new Map();
public version: number = 0;
public buildings: Map<EntityId, Building> = new Map();
public playerResources: PlayerResource[] = [];
public stringTables: StringTable[] = [];
public serverClasses: ServerClass[] = [];
public readonly parserState: ParserState = new ParserState();
public getState() {
const users = {};
@ -91,32 +75,55 @@ export class Match implements ParserState {
break;
case 'serverInfo':
this.intervalPerTick = packet.intervalPerTick;
this.version = packet.version;
break;
case 'createStringTable':
if (packet.table.name === 'userinfo') {
this.calculateUserInfo();
}
break;
case 'sayText2':
handleSayText2(packet, this);
break;
case 'dataTable':
handleDataTable(packet, this);
break;
case 'stringTable':
handleStringTables(packet, this);
break;
case 'createStringTable':
handleStringTable(packet, this);
break;
case 'updateStringTable':
handleStringTableUpdate(packet, this);
break;
case 'gameEventList':
handleGameEventList(packet, this);
break;
case 'gameEvent':
handleGameEvent(packet, this);
break;
}
}
private calculateUserInfo() {
for (const [text, extraData] of this.parserState.userInfoEntries.entries()) {
this.calculateUserInfoFromEntry(text, extraData);
}
}
private calculateUserInfoByEntityId(entityId: number) {
const text = `${entityId - 1}`;
const extraData = this.parserState.userInfoEntries.get(text);
if (!extraData) {
throw new Error(`No user info in stringable for entity id ${entityId}`);
}
return this.calculateUserInfoFromEntry(text, extraData);
}
private calculateUserInfoFromEntry(text: string, extraData: BitStream): UserInfo {
if (extraData.bitsLeft > (32 * 8)) {
const name = extraData.readUTF8String(32);
const userId = extraData.readUint32();
const steamId = extraData.readUTF8String();
if (steamId) {
const userState = this.getUserInfo(userId);
userState.name = name;
userState.steamId = steamId;
userState.entityId = parseInt(text, 10) + 1;
return userState;
} else {
throw new Error(`No steamid for user info ${text}`);
}
} else {
throw new Error();
}
}
public getUserInfo(userId: number): UserInfo {
// no clue why it does this
// only seems to be the case with per user ready
@ -125,6 +132,7 @@ export class Match implements ParserState {
}
const user = this.users.get(userId);
if (!user) {
const newUser = {
name: '',
userId,
@ -139,12 +147,12 @@ export class Match implements ParserState {
return user;
}
public getUserInfoForEntity(entity: PacketEntity): UserInfo {
public getUserInfoForEntity(entity: PacketEntity): UserInfo | null {
for (const user of this.users.values()) {
if (user && user.entityId === entity.entityIndex) {
return user;
}
}
throw new Error('User not found for entity ' + entity.entityIndex);
return this.calculateUserInfoByEntityId(entity.entityIndex);
}
}

74
src/Data/Message.ts Normal file
View file

@ -0,0 +1,74 @@
import {Packet} from './Packet';
import {BitStream} from 'bit-buffer';
import {ServerClass} from './ServerClass';
import {SendTable} from './SendTable';
import {StringTable} from './StringTable';
import {ParserState} from './ParserState';
export enum MessageType {
Sigon = 1,
Packet = 2,
SyncTick = 3,
ConsoleCmd = 4,
UserCmd = 5,
DataTables = 6,
Stop = 7,
StringTables = 8,
}
export interface BaseMessage {
tick: number;
rawData: BitStream;
}
export interface SigonMessage extends BaseMessage {
type: MessageType.Sigon;
packets: Packet[];
}
export interface PacketMessage extends BaseMessage {
type: MessageType.Packet;
packets: Packet[];
}
export interface SyncTickMessage extends BaseMessage {
type: MessageType.SyncTick;
}
export interface ConsoleCmdMessage extends BaseMessage {
type: MessageType.ConsoleCmd;
command: string;
}
export interface UserCmdMessage extends BaseMessage {
type: MessageType.UserCmd;
}
export interface DataTablesMessage extends BaseMessage {
type: MessageType.DataTables;
tables: SendTable[];
serverClasses: ServerClass[]
}
export interface StopMessage extends BaseMessage {
type: MessageType.Stop;
}
export interface StringTablesMessage extends BaseMessage {
type: MessageType.StringTables;
tables: StringTable[]
}
export type Message = SigonMessage |
PacketMessage |
SyncTickMessage |
ConsoleCmdMessage |
UserCmdMessage |
DataTablesMessage |
StopMessage |
StringTablesMessage;
export interface MessageHandler<M extends Message> {
parseMessage: (stream: BitStream, tick: number, state: ParserState) => M;
encodeMessage: (message: M, stream: BitStream, state: ParserState) => void;
}

View file

@ -1,22 +1,83 @@
import {BitStream} from 'bit-buffer';
import {GameEventDefinition} from './GameEvent';
import {EntityId, PacketEntity} from './PacketEntity';
import {EntityId} from './PacketEntity';
import {SendTable, SendTableName} from './SendTable';
import {ServerClass, ServerClassId} from './ServerClass';
import {StringTable} from './StringTable';
import {GameEventType} from './GameEventTypes';
import {SendProp} from './SendProp';
import {Packet, PacketTypeId} from './Packet';
import {
handleStringTable, handleStringTables, handleStringTableUpdate,
handleTable
} from '../PacketHandler/StringTable';
import {handleGameEventList} from '../PacketHandler/GameEventList';
import {DataTablesMessage, Message, MessageType, StringTablesMessage} from './Message';
export interface ParserState {
staticBaseLines: Map<ServerClassId, BitStream>;
staticBaselineCache: Map<ServerClassId, SendProp[]>;
eventDefinitions: Map<number, GameEventDefinition<GameEventType>>;
entityClasses: Map<EntityId, ServerClass>;
sendTables: Map<SendTableName, SendTable>;
version: number;
stringTables: StringTable[];
serverClasses: ServerClass[];
instanceBaselines: [Map<EntityId, SendProp[]>, Map<EntityId, SendProp[]>];
export class ParserState {
public version: number = 0;
public staticBaseLines: Map<ServerClassId, BitStream> = new Map();
public staticBaselineCache: Map<ServerClassId, SendProp[]> = new Map();
public eventDefinitions: Map<number, GameEventDefinition<GameEventType>> = new Map();
public entityClasses: Map<EntityId, ServerClass> = new Map();
public sendTables: Map<SendTableName, SendTable> = new Map();
public stringTables: StringTable[] = [];
public serverClasses: ServerClass[] = [];
public instanceBaselines: [Map<EntityId, SendProp[]>, Map<EntityId, SendProp[]>] = [new Map(), new Map()];
public skippedPackets: PacketTypeId[] = [];
public userInfoEntries: Map<string, BitStream> = new Map();
public handlePacket(packet: Packet) {
switch (packet.packetType) {
case 'serverInfo':
this.version = packet.version;
break;
case 'stringTable':
handleStringTables(packet, this);
break;
case 'createStringTable':
handleStringTable(packet, this);
break;
case 'updateStringTable':
handleStringTableUpdate(packet, this);
break;
case 'gameEventList':
handleGameEventList(packet, this);
break;
}
}
public handleMessage(message: Message) {
switch (message.type) {
case MessageType.DataTables:
this.handleDataTableMessage(message);
break;
case MessageType.StringTables:
this.handleStringTableMessage(message);
break;
}
}
private handleDataTableMessage(message: DataTablesMessage) {
for (const table of message.tables) {
this.sendTables.set(table.name, table);
}
this.serverClasses = message.serverClasses;
}
private handleStringTableMessage(message: StringTablesMessage) {
for (const table of message.tables) {
handleTable(table, this);
}
}
public getStringTable(name: string): StringTable | null {
const table = this.stringTables.find(table => table.name === name);
if (!table) {
return null;
}
return table;
}
}
export function getClassBits(state: ParserState) {
@ -32,24 +93,5 @@ export function getSendTable(state: ParserState, dataTable: string): SendTable {
}
export function createParserState(): ParserState {
return {
staticBaseLines: new Map(),
staticBaselineCache: new Map(),
eventDefinitions: new Map(),
entityClasses: new Map(),
sendTables: new Map(),
version: 0,
stringTables: [],
serverClasses: [],
instanceBaselines: [new Map(), new Map()]
};
}
export function getStringTable(state: ParserState, name: string) {
for (const table of state.stringTables) {
if (table.name === name) {
return table;
}
}
return null;
return new ParserState();
}