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

move message specific bits to their respective handlers

This commit is contained in:
Robin Appelman 2017-09-24 15:27:02 +02:00
commit d54f639633
8 changed files with 131 additions and 98 deletions

View file

@ -4,6 +4,7 @@ import {ServerClass} from './ServerClass';
import {SendTable} from './SendTable'; import {SendTable} from './SendTable';
import {StringTable} from './StringTable'; import {StringTable} from './StringTable';
import {ParserState} from './ParserState'; import {ParserState} from './ParserState';
import {Vector} from './Vector';
export enum MessageType { export enum MessageType {
Sigon = 1, Sigon = 1,
@ -21,14 +22,18 @@ export interface BaseMessage {
rawData: BitStream; rawData: BitStream;
} }
export interface SigonMessage extends BaseMessage {
type: MessageType.Sigon;
packets: Packet[];
}
export interface PacketMessage extends BaseMessage { export interface PacketMessage extends BaseMessage {
type: MessageType.Packet; type: MessageType.Packet;
packets: Packet[]; packets: Packet[];
viewOrigin: [Vector, Vector];
viewAngles: [Vector, Vector];
localViewAngles: [Vector, Vector];
sequenceIn: number;
sequenceOut: number;
flags: number;
}
export interface SigonMessage extends PacketMessage {
} }
export interface SyncTickMessage extends BaseMessage { export interface SyncTickMessage extends BaseMessage {
@ -42,6 +47,7 @@ export interface ConsoleCmdMessage extends BaseMessage {
export interface UserCmdMessage extends BaseMessage { export interface UserCmdMessage extends BaseMessage {
type: MessageType.UserCmd; type: MessageType.UserCmd;
sequenceOut: number;
} }
export interface DataTablesMessage extends BaseMessage { export interface DataTablesMessage extends BaseMessage {
@ -69,6 +75,6 @@ export type Message = SigonMessage |
StringTablesMessage; StringTablesMessage;
export interface MessageHandler<M extends Message> { export interface MessageHandler<M extends Message> {
parseMessage: (stream: BitStream, tick: number, state: ParserState) => M; parseMessage: (stream: BitStream, state: ParserState) => M;
encodeMessage: (message: M, stream: BitStream, state: ParserState) => void; encodeMessage: (message: M, stream: BitStream, state: ParserState) => void;
} }

View file

@ -8,6 +8,7 @@ import {UserCmdHandler} from './Parser/Message/UserCmd';
import {Packet, PacketTypeId} from './Data/Packet'; import {Packet, PacketTypeId} from './Data/Packet';
import {Message, MessageHandler, MessageType, PacketMessage} from './Data/Message'; import {Message, MessageHandler, MessageType, PacketMessage} from './Data/Message';
import {ParserState} from './Data/ParserState'; import {ParserState} from './Data/ParserState';
import {SyncTickHandler} from './Parser/Message/SyncTick';
const messageHandlers: Map<MessageType, MessageHandler<Message>> = new Map<MessageType, MessageHandler<Message>>([ const messageHandlers: Map<MessageType, MessageHandler<Message>> = new Map<MessageType, MessageHandler<Message>>([
[MessageType.Sigon, PacketMessageHandler], [MessageType.Sigon, PacketMessageHandler],
@ -16,6 +17,7 @@ const messageHandlers: Map<MessageType, MessageHandler<Message>> = new Map<Messa
[MessageType.UserCmd, UserCmdHandler], [MessageType.UserCmd, UserCmdHandler],
[MessageType.DataTables, DataTableHandler], [MessageType.DataTables, DataTableHandler],
[MessageType.StringTables, StringTableHandler], [MessageType.StringTables, StringTableHandler],
[MessageType.SyncTick, SyncTickHandler]
]); ]);
export class Parser { export class Parser {
@ -23,9 +25,6 @@ export class Parser {
public readonly parserState: ParserState; public readonly parserState: ParserState;
private header: Header | null = null; private header: Header | null = null;
public viewOrigin: number[][] = [[], []];
public viewAngles: number[][] = [[], []];
constructor(stream: BitStream, skipPackets: PacketTypeId[] = []) { constructor(stream: BitStream, skipPackets: PacketTypeId[] = []) {
this.stream = stream; this.stream = stream;
this.parserState = new ParserState(); this.parserState = new ParserState();
@ -60,12 +59,12 @@ export class Parser {
} }
} }
protected parseMessage(data: BitStream, type: MessageType, tick: number, state: ParserState): Message { protected parseMessage(data: BitStream, type: MessageType, state: ParserState): Message {
const handler = messageHandlers.get(type); const handler = messageHandlers.get(type);
if (!handler) { if (!handler) {
throw new Error(`No handler for message of type ${MessageType[type]}`); throw new Error(`No handler for message of type ${MessageType[type]}`);
} }
return handler.parseMessage(data, tick, state); return handler.parseMessage(data, state);
} }
protected parseHeader(stream): Header { protected parseHeader(stream): Header {
@ -98,43 +97,11 @@ export class Parser {
if (stream.bitsLeft < 8) { if (stream.bitsLeft < 8) {
return false; return false;
} }
const type: MessageType = stream.readBits(8); const type: MessageType = stream.readUint8();
if (type === MessageType.Stop) { if (type === MessageType.Stop) {
return false; return false;
} }
const tick = stream.readInt32();
switch (type) { return this.parseMessage(stream, type, state);
case MessageType.Sigon:
case MessageType.Packet:
this.stream.readInt32(); // flags
for (let j = 0; j < 2; j++) {
for (let i = 0; i < 3; i++) {
this.viewOrigin[j][i] = this.stream.readFloat32();
}
for (let i = 0; i < 3; i++) {
this.viewAngles[j][i] = this.stream.readFloat32();
}
for (let i = 0; i < 3; i++) {
this.stream.readInt32(); // local viewAngles
}
}
this.stream.readInt32(); // sequence in
this.stream.readInt32(); // sequence out
break;
case MessageType.UserCmd:
stream.byteIndex += 0x04; // unknown / outgoing sequence
break;
case MessageType.SyncTick:
return {
type: MessageType.SyncTick,
tick,
rawData: stream.readBitStream(0)
};
}
const length = stream.readInt32();
const buffer = stream.readBitStream(length * 8);
return this.parseMessage(buffer, type, tick, state);
} }
} }

View file

@ -13,12 +13,17 @@ export class ConsoleCmd extends Parser {
} }
export const ConsoleCmdHandler: MessageHandler<ConsoleCmdMessage> = { export const ConsoleCmdHandler: MessageHandler<ConsoleCmdMessage> = {
parseMessage: (stream: BitStream, tick: number) => { parseMessage: (stream: BitStream) => {
const tick = stream.readInt32();
const length = stream.readInt32();
const messageStream = stream.readBitStream(length * 8);
return { return {
type: MessageType.ConsoleCmd, type: MessageType.ConsoleCmd,
tick, tick,
rawData: stream, rawData: messageStream,
command: stream.readUTF8String() command: messageStream.readUTF8String()
}; };
}, },
encodeMessage: (message: ConsoleCmdMessage, stream: BitStream) => { encodeMessage: (message: ConsoleCmdMessage, stream: BitStream) => {

View file

@ -5,37 +5,42 @@ import {DataTablesMessage, MessageHandler, MessageType} from '../../Data/Message
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
export const DataTableHandler: MessageHandler<DataTablesMessage> = { export const DataTableHandler: MessageHandler<DataTablesMessage> = {
parseMessage: (stream: BitStream, tick: number) => { parseMessage: (stream: BitStream) => {
const tick = stream.readInt32();
const length = stream.readInt32();
const messageStream = stream.readBitStream(length * 8);
// 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_common_eng.cpp#L356
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_recv_eng.cpp#L310 // 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 // https://github.com/PazerOP/DemoLib/blob/master/DemoLib/Commands/DemoDataTablesCommand.cs
const tables: SendTable[] = []; const tables: SendTable[] = [];
const tableMap: {[key: string]: SendTable} = {}; const tableMap: {[key: string]: SendTable} = {};
while (stream.readBoolean()) { while (messageStream.readBoolean()) {
const needsDecoder = stream.readBoolean(); const needsDecoder = messageStream.readBoolean();
const tableName = stream.readASCIIString(); const tableName = messageStream.readASCIIString();
const numProps = stream.readBits(10); const numProps = messageStream.readBits(10);
const table = new SendTable(tableName); const table = new SendTable(tableName);
// get props metadata // get props metadata
let arrayElementProp; let arrayElementProp;
for (let i = 0; i < numProps; i++) { for (let i = 0; i < numProps; i++) {
const propType = stream.readBits(5); const propType = messageStream.readBits(5);
const propName = stream.readASCIIString(); const propName = messageStream.readASCIIString();
const nFlagsBits = 16; // might be 11 (old?), 13 (new?), 16(networked) or 17(??) const nFlagsBits = 16; // might be 11 (old?), 13 (new?), 16(networked) or 17(??)
const flags = stream.readBits(nFlagsBits); const flags = messageStream.readBits(nFlagsBits);
const prop = new SendPropDefinition(propType, propName, flags, tableName); const prop = new SendPropDefinition(propType, propName, flags, tableName);
if (propType === SendPropType.DPT_DataTable) { if (propType === SendPropType.DPT_DataTable) {
prop.excludeDTName = stream.readASCIIString(); prop.excludeDTName = messageStream.readASCIIString();
} else { } else {
if (prop.isExcludeProp()) { if (prop.isExcludeProp()) {
prop.excludeDTName = stream.readASCIIString(); prop.excludeDTName = messageStream.readASCIIString();
} else if (prop.type === SendPropType.DPT_Array) { } else if (prop.type === SendPropType.DPT_Array) {
prop.numElements = stream.readBits(10); prop.numElements = messageStream.readBits(10);
} else { } else {
prop.lowValue = stream.readFloat32(); prop.lowValue = messageStream.readFloat32();
prop.highValue = stream.readFloat32(); prop.highValue = messageStream.readFloat32();
prop.bitCount = stream.readBits(7); prop.bitCount = messageStream.readBits(7);
} }
} }
@ -85,23 +90,23 @@ export const DataTableHandler: MessageHandler<DataTablesMessage> = {
} }
} }
const numServerClasses = stream.readUint16(); // short const numServerClasses = messageStream.readUint16(); // short
const serverClasses: ServerClass[] = []; const serverClasses: ServerClass[] = [];
if (numServerClasses <= 0) { if (numServerClasses <= 0) {
throw new Error('expected one or more serverclasses'); throw new Error('expected one or more serverclasses');
} }
for (let i = 0; i < numServerClasses; i++) { for (let i = 0; i < numServerClasses; i++) {
const classId = stream.readUint16(); const classId = messageStream.readUint16();
if (classId > numServerClasses) { if (classId > numServerClasses) {
throw new Error('invalid class id'); throw new Error('invalid class id');
} }
const className = stream.readASCIIString(); const className = messageStream.readASCIIString();
const dataTable = stream.readASCIIString(); const dataTable = messageStream.readASCIIString();
serverClasses.push(new ServerClass(classId, className, dataTable)); serverClasses.push(new ServerClass(classId, className, dataTable));
} }
const bitsLeft = (this.length * 8) - stream.index; const bitsLeft = (this.length * 8) - messageStream.index;
if (bitsLeft > 7 || bitsLeft < 0) { if (bitsLeft > 7 || bitsLeft < 0) {
throw new Error('unexpected remaining data in datatable (' + bitsLeft + ' bits)'); throw new Error('unexpected remaining data in datatable (' + bitsLeft + ' bits)');
} }
@ -109,7 +114,7 @@ export const DataTableHandler: MessageHandler<DataTablesMessage> = {
return { return {
type: MessageType.DataTables, type: MessageType.DataTables,
tick, tick,
rawData: stream, rawData: messageStream,
tables, tables,
serverClasses, serverClasses,
}; };

View file

@ -18,6 +18,7 @@ import {Packet as IPacket, PacketTypeId} from '../../Data/Packet';
import {MessageHandler, MessageType, PacketMessage} from '../../Data/Message'; import {MessageHandler, MessageType, PacketMessage} from '../../Data/Message';
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
import {ParserState} from '../../Data/ParserState'; import {ParserState} from '../../Data/ParserState';
import {Vector} from '../../Data/Vector';
type PacketHandlerMap = Map<PacketTypeId, PacketHandler<IPacket>>; type PacketHandlerMap = Map<PacketTypeId, PacketHandler<IPacket>>;
@ -82,16 +83,34 @@ const handlers: PacketHandlerMap = new Map<PacketTypeId, PacketHandler<IPacket>>
]); ]);
export const PacketMessageHandler: MessageHandler<PacketMessage> = { export const PacketMessageHandler: MessageHandler<PacketMessage> = {
parseMessage: (stream: BitStream, tick: number, state: ParserState) => { parseMessage: (stream: BitStream, state: ParserState) => {
const tick = stream.readInt32();
const flags = stream.readInt32();
const viewOrigin: [Vector, Vector] = [new Vector(0, 0, 0), new Vector(0, 0, 0)];
const viewAngles: [Vector, Vector] = [new Vector(0, 0, 0), new Vector(0, 0, 0)];
const localViewAngles: [Vector, Vector] = [new Vector(0, 0, 0), new Vector(0, 0, 0)];
for (let j = 0; j < 2; j++) {
viewOrigin[j] = new Vector(stream.readFloat32(), stream.readFloat32(), stream.readFloat32());
viewAngles[j] = new Vector(stream.readFloat32(), stream.readFloat32(), stream.readFloat32());
localViewAngles[j] = new Vector(stream.readFloat32(), stream.readFloat32(), stream.readFloat32());
}
const sequenceIn = stream.readInt32();
const sequenceOut = stream.readInt32();
const length = stream.readInt32();
const messageStream = stream.readBitStream(length * 8);
const packets: IPacket[] = []; const packets: IPacket[] = [];
let lastPacketType = 0; let lastPacketType = 0;
while (stream.bitsLeft > 6) { // last 6 bits for NOOP while (messageStream.bitsLeft > 6) { // last 6 bits for NOOP
const type = stream.readBits(6) as PacketTypeId; const type = messageStream.readBits(6) as PacketTypeId;
if (type !== 0) { if (type !== 0) {
const parser = handlers.get(type); const parser = handlers.get(type);
if (parser) { if (parser) {
const skip = state.skippedPackets.indexOf(type) !== -1; const skip = state.skippedPackets.indexOf(type) !== -1;
const packet = parser.parser(stream, state, skip); const packet = parser.parser(messageStream, state, skip);
packets.push(packet); packets.push(packet);
} else { } else {
throw new Error(`Unknown packet type ${type} just parsed a ${PacketTypeId[lastPacketType]}`); throw new Error(`Unknown packet type ${type} just parsed a ${PacketTypeId[lastPacketType]}`);
@ -102,8 +121,14 @@ export const PacketMessageHandler: MessageHandler<PacketMessage> = {
return { return {
type: MessageType.Packet, type: MessageType.Packet,
tick, tick,
rawData: stream, rawData: messageStream,
packets packets,
flags,
viewOrigin,
viewAngles,
localViewAngles,
sequenceIn,
sequenceOut
}; };
}, },
encodeMessage: (message, stream) => { encodeMessage: (message, stream) => {

View file

@ -3,47 +3,47 @@ import {MessageHandler, MessageType, StringTablesMessage} from '../../Data/Messa
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
export const StringTableHandler: MessageHandler<StringTablesMessage> = { export const StringTableHandler: MessageHandler<StringTablesMessage> = {
parseMessage: (stream: BitStream, tick: number) => { parseMessage: (stream: BitStream) => {
// we get the tables from the packets const tick = stream.readInt32();
// return [{
// packetType: 'stringTable', const length = stream.readInt32();
// tables: [] const messageStream = stream.readBitStream(length * 8);
// }];
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/ST/StringTableParser.cs // https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/ST/StringTableParser.cs
const tableCount = stream.readUint8(); const tableCount = messageStream.readUint8();
const tables: StringTableObject[] = []; const tables: StringTableObject[] = [];
let extraDataLength; let extraDataLength;
for (let i = 0; i < tableCount; i++) { for (let i = 0; i < tableCount; i++) {
const entries: StringTableEntry[] = []; const entries: StringTableEntry[] = [];
const tableName = stream.readASCIIString(); const tableName = messageStream.readASCIIString();
const entryCount = stream.readUint16(); const entryCount = messageStream.readUint16();
for (let j = 0; j < entryCount; j++) { for (let j = 0; j < entryCount; j++) {
let entry: StringTableEntry; let entry: StringTableEntry;
try { try {
entry = { entry = {
text: stream.readUTF8String(), text: messageStream.readUTF8String(),
}; };
} catch (e) { } catch (e) {
return { return {
type: MessageType.StringTables, type: MessageType.StringTables,
tick, tick,
rawData: stream, rawData: messageStream,
tables, tables,
}; };
} }
if (stream.readBoolean()) { if (messageStream.readBoolean()) {
extraDataLength = stream.readUint16(); extraDataLength = messageStream.readUint16();
if ((extraDataLength * 8) > stream.bitsLeft) { if ((extraDataLength * 8) > messageStream.bitsLeft) {
// extradata to long, can't continue parsing the tables // extradata to long, can't continue parsing the tables
// seems to happen in POV demos after the MyM update // seems to happen in POV demos after the MyM update
return { return {
type: MessageType.StringTables, type: MessageType.StringTables,
tick, tick,
rawData: stream, rawData: messageStream,
tables, tables,
}; };
} }
entry.extraData = stream.readBitStream(extraDataLength * 8); entry.extraData = messageStream.readBitStream(extraDataLength * 8);
} }
entries.push(entry); entries.push(entry);
} }
@ -53,19 +53,19 @@ export const StringTableHandler: MessageHandler<StringTablesMessage> = {
maxEntries: entryCount, maxEntries: entryCount,
}; };
tables.push(table); tables.push(table);
if (stream.readBits(1)) { if (messageStream.readBits(1)) {
stream.readASCIIString(); messageStream.readASCIIString();
if (stream.readBits(1)) { if (messageStream.readBits(1)) {
// throw 'more extra data not implemented'; // throw 'more extra data not implemented';
extraDataLength = stream.readBits(16); extraDataLength = messageStream.readBits(16);
stream.readBits(extraDataLength); messageStream.readBits(extraDataLength);
} }
} }
} }
return { return {
type: MessageType.StringTables, type: MessageType.StringTables,
tick, tick,
rawData: stream, rawData: messageStream,
tables, tables,
}; };
}, },

View file

@ -0,0 +1,17 @@
import {MessageHandler, MessageType, SyncTickMessage} from '../../Data/Message';
import {BitStream} from 'bit-buffer';
export const SyncTickHandler: MessageHandler<SyncTickMessage> = {
parseMessage: (stream: BitStream) => {
const tick = stream.readInt32();
return {
type: MessageType.SyncTick,
tick,
rawData: stream.readBitStream(0)
};
},
encodeMessage: (message, stream) => {
throw new Error('not implemented');
}
};

View file

@ -2,11 +2,19 @@ import {MessageHandler, MessageType, UserCmdMessage} from '../../Data/Message';
import {BitStream} from 'bit-buffer'; import {BitStream} from 'bit-buffer';
export const UserCmdHandler: MessageHandler<UserCmdMessage> = { export const UserCmdHandler: MessageHandler<UserCmdMessage> = {
parseMessage: (stream: BitStream, tick: number) => { parseMessage: (stream: BitStream) => {
const tick = stream.readInt32();
const sequenceOut = stream.readInt32();
const length = stream.readInt32();
const messageStream = stream.readBitStream(length * 8);
return { return {
type: MessageType.UserCmd, type: MessageType.UserCmd,
tick, tick,
rawData: stream rawData: messageStream,
sequenceOut
}; };
}, },
encodeMessage: (message, stream) => { encodeMessage: (message, stream) => {