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 {StringTable} from './StringTable';
import {ParserState} from './ParserState';
import {Vector} from './Vector';
export enum MessageType {
Sigon = 1,
@ -21,14 +22,18 @@ export interface BaseMessage {
rawData: BitStream;
}
export interface SigonMessage extends BaseMessage {
type: MessageType.Sigon;
packets: Packet[];
}
export interface PacketMessage extends BaseMessage {
type: MessageType.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 {
@ -42,6 +47,7 @@ export interface ConsoleCmdMessage extends BaseMessage {
export interface UserCmdMessage extends BaseMessage {
type: MessageType.UserCmd;
sequenceOut: number;
}
export interface DataTablesMessage extends BaseMessage {
@ -69,6 +75,6 @@ export type Message = SigonMessage |
StringTablesMessage;
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;
}

View file

@ -8,6 +8,7 @@ import {UserCmdHandler} from './Parser/Message/UserCmd';
import {Packet, PacketTypeId} from './Data/Packet';
import {Message, MessageHandler, MessageType, PacketMessage} from './Data/Message';
import {ParserState} from './Data/ParserState';
import {SyncTickHandler} from './Parser/Message/SyncTick';
const messageHandlers: Map<MessageType, MessageHandler<Message>> = new Map<MessageType, MessageHandler<Message>>([
[MessageType.Sigon, PacketMessageHandler],
@ -16,6 +17,7 @@ const messageHandlers: Map<MessageType, MessageHandler<Message>> = new Map<Messa
[MessageType.UserCmd, UserCmdHandler],
[MessageType.DataTables, DataTableHandler],
[MessageType.StringTables, StringTableHandler],
[MessageType.SyncTick, SyncTickHandler]
]);
export class Parser {
@ -23,9 +25,6 @@ export class Parser {
public readonly parserState: ParserState;
private header: Header | null = null;
public viewOrigin: number[][] = [[], []];
public viewAngles: number[][] = [[], []];
constructor(stream: BitStream, skipPackets: PacketTypeId[] = []) {
this.stream = stream;
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);
if (!handler) {
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 {
@ -98,43 +97,11 @@ export class Parser {
if (stream.bitsLeft < 8) {
return false;
}
const type: MessageType = stream.readBits(8);
const type: MessageType = stream.readUint8();
if (type === MessageType.Stop) {
return false;
}
const tick = stream.readInt32();
switch (type) {
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);
return this.parseMessage(stream, type, state);
}
}

View file

@ -13,12 +13,17 @@ export class ConsoleCmd extends Parser {
}
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 {
type: MessageType.ConsoleCmd,
tick,
rawData: stream,
command: stream.readUTF8String()
rawData: messageStream,
command: messageStream.readUTF8String()
};
},
encodeMessage: (message: ConsoleCmdMessage, stream: BitStream) => {

View file

@ -5,37 +5,42 @@ import {DataTablesMessage, MessageHandler, MessageType} from '../../Data/Message
import {BitStream} from 'bit-buffer';
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_recv_eng.cpp#L310
// https://github.com/PazerOP/DemoLib/blob/master/DemoLib/Commands/DemoDataTablesCommand.cs
const tables: SendTable[] = [];
const tableMap: {[key: string]: SendTable} = {};
while (stream.readBoolean()) {
const needsDecoder = stream.readBoolean();
const tableName = stream.readASCIIString();
const numProps = stream.readBits(10);
while (messageStream.readBoolean()) {
const needsDecoder = messageStream.readBoolean();
const tableName = messageStream.readASCIIString();
const numProps = messageStream.readBits(10);
const table = new SendTable(tableName);
// get props metadata
let arrayElementProp;
for (let i = 0; i < numProps; i++) {
const propType = stream.readBits(5);
const propName = stream.readASCIIString();
const propType = messageStream.readBits(5);
const propName = messageStream.readASCIIString();
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);
if (propType === SendPropType.DPT_DataTable) {
prop.excludeDTName = stream.readASCIIString();
prop.excludeDTName = messageStream.readASCIIString();
} else {
if (prop.isExcludeProp()) {
prop.excludeDTName = stream.readASCIIString();
prop.excludeDTName = messageStream.readASCIIString();
} else if (prop.type === SendPropType.DPT_Array) {
prop.numElements = stream.readBits(10);
prop.numElements = messageStream.readBits(10);
} else {
prop.lowValue = stream.readFloat32();
prop.highValue = stream.readFloat32();
prop.bitCount = stream.readBits(7);
prop.lowValue = messageStream.readFloat32();
prop.highValue = messageStream.readFloat32();
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[] = [];
if (numServerClasses <= 0) {
throw new Error('expected one or more serverclasses');
}
for (let i = 0; i < numServerClasses; i++) {
const classId = stream.readUint16();
const classId = messageStream.readUint16();
if (classId > numServerClasses) {
throw new Error('invalid class id');
}
const className = stream.readASCIIString();
const dataTable = stream.readASCIIString();
const className = messageStream.readASCIIString();
const dataTable = messageStream.readASCIIString();
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) {
throw new Error('unexpected remaining data in datatable (' + bitsLeft + ' bits)');
}
@ -109,7 +114,7 @@ export const DataTableHandler: MessageHandler<DataTablesMessage> = {
return {
type: MessageType.DataTables,
tick,
rawData: stream,
rawData: messageStream,
tables,
serverClasses,
};

View file

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

View file

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