mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
stricter packet handler types
This commit is contained in:
parent
70bdf0257e
commit
df1aac6575
9 changed files with 262 additions and 145 deletions
|
|
@ -1,12 +1,11 @@
|
|||
import {make} from '../Packet/ParserGenerator';
|
||||
|
||||
import {EncodeBSPDecal, ParseBSPDecal} from '../Packet/BSPDecal';
|
||||
import {EncodeClassInfo, ParseClassInfo} from '../Packet/ClassInfo';
|
||||
import {EncodeCreateStringTable, ParseCreateStringTable} from '../Packet/CreateStringTable';
|
||||
import {ParseGameEvent} from '../Packet/GameEvent';
|
||||
import {EncodeGameEventList, ParseGameEventList} from '../Packet/GameEventList';
|
||||
import {ParsePacketEntities} from '../Packet/PacketEntities';
|
||||
import {PacketParserMap, voidEncoder} from '../Packet/Parser';
|
||||
import {PacketHandler, voidEncoder} from '../Packet/Parser';
|
||||
import {EncodeParseSounds, ParseParseSounds} from '../Packet/ParseSounds';
|
||||
import {EncodeSetConVar, ParseSetConVar} from '../Packet/SetConVar';
|
||||
import {ParseTempEntities} from '../Packet/TempEntities';
|
||||
|
|
@ -16,60 +15,82 @@ import {EncodeVoiceData, ParseVoiceData} from '../Packet/VoiceData';
|
|||
import {EncodeVoiceInit, ParseVoiceInit} from '../Packet/VoiceInit';
|
||||
import {Parser} from './Parser';
|
||||
|
||||
import {Packet as IPacket} from '../../Data/Packet';
|
||||
|
||||
// https://code.google.com/p/coldemoplayer/source/browse/branches/2.0/compLexity+Demo+Player/CDP.Source/Messages/?r=219
|
||||
// https://github.com/TimePath/hl2-toolkit/tree/master/src/main/java/com/timepath/hl2/io/demo
|
||||
// https://github.com/stgn/netdecode/blob/master/Packet.cs
|
||||
// https://github.com/LestaD/SourceEngine2007/blob/master/src_main/common/netmessages.cpp
|
||||
|
||||
import {Packet as IPacket, PacketType} from '../../Data/Packet';
|
||||
|
||||
export class Packet extends Parser {
|
||||
private static parsers: PacketParserMap = {
|
||||
2: make('file', 'transferId{32}fileName{s}requested{b}'),
|
||||
3: make('netTick', 'tick{32}frameTime{16}stdDev{16}'),
|
||||
4: make('stringCmd', 'command{s}'),
|
||||
5: {parser: ParseSetConVar, encoder: EncodeSetConVar},
|
||||
6: make('sigOnState', 'state{8}count{32}'),
|
||||
7: make('print', 'value{s}'),
|
||||
8: make('serverInfo',
|
||||
'version{16}serverCount{32}stv{b}dedicated{b}maxCrc{32}maxClasses{16}' +
|
||||
'mapHash{128}playerCount{8}maxPlayerCount{8}intervalPerTick{f32}platform{s1}' +
|
||||
'game{s}map{s}skybox{s}serverName{s}replay{b}'),
|
||||
10: {parser: ParseClassInfo, encoder: EncodeClassInfo},
|
||||
11: make('setPause', 'paused{b}'),
|
||||
12: {parser: ParseCreateStringTable, encoder: EncodeCreateStringTable},
|
||||
13: {parser: ParseUpdateStringTable, encoder: EncodeUpdateStringTable},
|
||||
14: {parser: ParseVoiceInit, encoder: EncodeVoiceInit},
|
||||
15: {parser: ParseVoiceData, encoder: EncodeVoiceData},
|
||||
17: {parser: ParseParseSounds, encoder: EncodeParseSounds},
|
||||
18: make('setView', 'index{11}'),
|
||||
19: make('fixAngle', 'relative{b}x{16}y{16}z{16}'),
|
||||
21: {parser: ParseBSPDecal, encoder: EncodeBSPDecal},
|
||||
23: {parser: ParseUserMessage, encoder: voidEncoder},
|
||||
24: make('entityMessage', 'index{11}classId{9}length{11}data{$length}'),
|
||||
25: {parser: ParseGameEvent, encoder: voidEncoder},
|
||||
26: {parser: ParsePacketEntities, encoder: voidEncoder},
|
||||
27: {parser: ParseTempEntities, encoder: voidEncoder},
|
||||
28: make('preFetch', 'index{14}'),
|
||||
29: make('menu', 'type{u16}length{u16}data{$length*8}'),
|
||||
30: {parser: ParseGameEventList, encoder: EncodeGameEventList},
|
||||
31: make('getCvarValue', 'cookie{32}value{s}'),
|
||||
32: make('cmdKeyValues', 'length{32}data{$length}'),
|
||||
};
|
||||
private static parsers: Map<PacketType, PacketHandler<IPacket>> = new Map([
|
||||
[PacketType.file,
|
||||
make('file', 'transferId{32}fileName{s}requested{b}')],
|
||||
[PacketType.netTick,
|
||||
make('netTick', 'tick{32}frameTime{16}stdDev{16}')],
|
||||
[PacketType.stringCmd,
|
||||
make('stringCmd', 'command{s}')],
|
||||
[PacketType.setConVar,
|
||||
{parser: ParseSetConVar, encoder: EncodeSetConVar}],
|
||||
[PacketType.sigOnState,
|
||||
make('sigOnState', 'state{8}count{32}')],
|
||||
[PacketType.print,
|
||||
make('print', 'value{s}')],
|
||||
[PacketType.serverInfo,
|
||||
make('serverInfo',
|
||||
'version{16}serverCount{32}stv{b}dedicated{b}maxCrc{32}maxClasses{16}' +
|
||||
'mapHash{128}playerCount{8}maxPlayerCount{8}intervalPerTick{f32}platform{s1}' +
|
||||
'game{s}map{s}skybox{s}serverName{s}replay{b}')],
|
||||
[PacketType.classInfo,
|
||||
{parser: ParseClassInfo, encoder: EncodeClassInfo}],
|
||||
[PacketType.setPause,
|
||||
make('setPause', 'paused{b}')],
|
||||
[PacketType.createStringTable,
|
||||
{parser: ParseCreateStringTable, encoder: EncodeCreateStringTable}],
|
||||
[PacketType.updateStringTable,
|
||||
{parser: ParseUpdateStringTable, encoder: EncodeUpdateStringTable}],
|
||||
[PacketType.voiceInit,
|
||||
{parser: ParseVoiceInit, encoder: EncodeVoiceInit}],
|
||||
[PacketType.voiceData,
|
||||
{parser: ParseVoiceData, encoder: EncodeVoiceData}],
|
||||
[PacketType.parseSounds,
|
||||
{parser: ParseParseSounds, encoder: EncodeParseSounds}],
|
||||
[PacketType.setView,
|
||||
make('setView', 'index{11}')],
|
||||
[PacketType.fixAngle,
|
||||
make('fixAngle', 'relative{b}x{16}y{16}z{16}')],
|
||||
[PacketType.bspDecal,
|
||||
{parser: ParseBSPDecal, encoder: EncodeBSPDecal}],
|
||||
[PacketType.userMessage,
|
||||
{parser: ParseUserMessage, encoder: voidEncoder}],
|
||||
[PacketType.entityMessage,
|
||||
make('entityMessage', 'index{11}classId{9}length{11}data{$length}')],
|
||||
[PacketType.gameEvent,
|
||||
{parser: ParseGameEvent, encoder: voidEncoder}],
|
||||
[PacketType.packetEntities,
|
||||
{parser: ParsePacketEntities, encoder: voidEncoder}],
|
||||
[PacketType.tempEntities,
|
||||
{parser: ParseTempEntities, encoder: voidEncoder}],
|
||||
[PacketType.preFetch,
|
||||
make('preFetch', 'index{14}')],
|
||||
[PacketType.menu,
|
||||
make('menu', 'type{u16}length{u16}data{$length*8}')],
|
||||
[PacketType.gameEventList,
|
||||
{parser: ParseGameEventList, encoder: EncodeGameEventList}],
|
||||
[PacketType.getCvarValue,
|
||||
make('getCvarValue', 'cookie{32}value{s}')],
|
||||
[PacketType.cmdKeyValues,
|
||||
make('cmdKeyValues', 'length{32}data{$length}')],
|
||||
]);
|
||||
|
||||
public parse() {
|
||||
const packets: IPacket[] = [];
|
||||
let lastPacketType = 0;
|
||||
while (this.bitsLeft > 6) { // last 6 bits for NOOP
|
||||
const type = this.stream.readBits(6);
|
||||
const type = this.stream.readBits(6) as PacketType;
|
||||
if (type !== 0) {
|
||||
if (Packet.parsers[type]) {
|
||||
const parser = Packet.parsers.get(type);
|
||||
if (parser) {
|
||||
const skip = this.skippedPackets.indexOf(type) !== -1;
|
||||
const packet = Packet.parsers[type].parser.call(this, this.stream, this.match, skip);
|
||||
const packet = parser.parser(this.stream, this.match, skip);
|
||||
packets.push(packet);
|
||||
} else {
|
||||
throw new Error('Unknown packet type ' + type + ' just parsed a ' + PacketType[lastPacketType]);
|
||||
throw new Error(`Unknown packet type ${type} just parsed a ${PacketType[lastPacketType]}`);
|
||||
}
|
||||
lastPacketType = type;
|
||||
}
|
||||
|
|
@ -81,33 +102,3 @@ export class Packet extends Parser {
|
|||
return (this.length * 8) - this.stream.index;
|
||||
}
|
||||
}
|
||||
|
||||
export enum PacketType {
|
||||
file = 2,
|
||||
netTick = 3,
|
||||
stringCmd = 4,
|
||||
setConVar = 5,
|
||||
sigOnState = 6,
|
||||
print = 7,
|
||||
serverInfo = 8,
|
||||
classInfo = 10,
|
||||
setPause = 11,
|
||||
createStringTable = 12,
|
||||
updateStringTable = 13,
|
||||
voiceInit = 14,
|
||||
voiceData = 15,
|
||||
parseSounds = 17,
|
||||
setView = 18,
|
||||
fixAngle = 19,
|
||||
bspDecal = 21,
|
||||
userMessage = 23,
|
||||
entityMessage = 24,
|
||||
gameEvent = 25,
|
||||
packetEntities = 26,
|
||||
tempEntities = 27,
|
||||
preFetch = 28,
|
||||
menu = 29,
|
||||
gameEventList = 30,
|
||||
getCvarValue = 30,
|
||||
cmdKeyValues = 32,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import {BitStream} from 'bit-buffer';
|
||||
import {Match} from '../../Data/Match';
|
||||
import {Packet} from '../../Data/Packet';
|
||||
import {Packet, PacketType} from '../../Data/Packet';
|
||||
import {MessageType} from '../../Parser';
|
||||
import {PacketType} from './Packet';
|
||||
|
||||
export abstract class Parser {
|
||||
protected type: any;
|
||||
|
|
@ -13,11 +12,11 @@ export abstract class Parser {
|
|||
protected skippedPackets: PacketType[];
|
||||
|
||||
constructor(type: MessageType, tick: number, stream: BitStream, length: number, match: Match, skippedPacket: PacketType[] = []) {
|
||||
this.type = type;
|
||||
this.tick = tick;
|
||||
this.stream = stream;
|
||||
this.length = length; // length in bytes
|
||||
this.match = match;
|
||||
this.type = type;
|
||||
this.tick = tick;
|
||||
this.stream = stream;
|
||||
this.length = length; // length in bytes
|
||||
this.match = match;
|
||||
this.skippedPackets = skippedPacket;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
import {BitStream} from 'bit-buffer';
|
||||
import {Match} from '../../Data/Match';
|
||||
import {Packet} from '../../Data/Packet';
|
||||
import {Packet, PacketType, VoidPacket} from '../../Data/Packet';
|
||||
|
||||
export type Parser = (stream: BitStream, match?: Match, skip?: boolean) => Packet;
|
||||
export type Encoder = (packet: Packet, stream: BitStream, match?: Match) => void;
|
||||
export type Parser<P extends Packet> = (stream: BitStream, match?: Match, skip?: boolean) => P;
|
||||
export type Encoder<P extends Packet> = (packet: P, stream: BitStream, match?: Match) => void;
|
||||
|
||||
export interface PacketHandler {
|
||||
parser: Parser,
|
||||
encoder: Encoder
|
||||
export interface PacketHandler<P extends Packet> {
|
||||
parser: Parser<P>,
|
||||
encoder: Encoder<P>
|
||||
}
|
||||
|
||||
|
||||
export const voidEncoder: Encoder = () => {
|
||||
export const voidEncoder: Encoder<VoidPacket> = () => {
|
||||
return {
|
||||
type: 'void'
|
||||
};
|
||||
};
|
||||
|
||||
export interface PacketParserMap {
|
||||
[id: number]: PacketHandler;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,34 @@
|
|||
import {Packet} from '../../Data/Packet';
|
||||
import {PacketHandler} from './Parser';
|
||||
import {Encoder, PacketHandler, Parser} from './Parser';
|
||||
import {BitStream} from 'bit-buffer';
|
||||
|
||||
export function make(name: string, definition: string): PacketHandler {
|
||||
export function make<P extends Packet>(name: P['packetType'], definition: string): PacketHandler<P> {
|
||||
const parts = definition.split('}');
|
||||
const items = parts.map((part) => {
|
||||
return part.split('{');
|
||||
}).filter(part => part[0]);
|
||||
return {
|
||||
parser: (stream) => {
|
||||
const result = {
|
||||
packetType: name,
|
||||
};
|
||||
try {
|
||||
for (const group of items) {
|
||||
const value = readItem(stream, group[1], result);
|
||||
if (group[0] !== '_') {
|
||||
result[group[0]] = value;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error('Failed reading pattern ' + definition + '. ' + e);
|
||||
}
|
||||
return result as Packet;
|
||||
},
|
||||
encoder: (packet, stream) => {
|
||||
const parser: Parser<P> = (stream: BitStream) => {
|
||||
const result = {
|
||||
packetType: name,
|
||||
};
|
||||
try {
|
||||
for (const group of items) {
|
||||
writeItem(stream, group[1], packet, packet[group[0]]);
|
||||
const value = readItem(stream, group[1], result);
|
||||
if (group[0] !== '_') {
|
||||
result[group[0]] = value;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error('Failed reading pattern ' + definition + '. ' + e);
|
||||
}
|
||||
return result as P;
|
||||
};
|
||||
const encoder: Encoder<P> = (packet: P, stream: BitStream) => {
|
||||
for (const group of items) {
|
||||
writeItem(stream, group[1], packet, packet[group[0]]);
|
||||
}
|
||||
};
|
||||
return {parser, encoder};
|
||||
}
|
||||
|
||||
function readItem(stream: BitStream, description: string, data) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue