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:
parent
d8f01428c5
commit
198fe0b1ba
18 changed files with 459 additions and 421 deletions
|
|
@ -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
74
src/Data/Message.ts
Normal 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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue