mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
add encoder for gameEvent
This commit is contained in:
parent
55abaaff7a
commit
2e43f5bb7f
9 changed files with 802 additions and 118 deletions
|
|
@ -30,7 +30,9 @@ fs.readFile(argv._[0], function (err, data) {
|
||||||
.map(createEventDefinition)
|
.map(createEventDefinition)
|
||||||
.join('\n\n')
|
.join('\n\n')
|
||||||
+ '\n\n' + createEventDefinitionUnion(definitions) + '\n\n'
|
+ '\n\n' + createEventDefinitionUnion(definitions) + '\n\n'
|
||||||
+ createEventTpeMap(definitions) + '\n';
|
+ 'export type GameEventType = GameEvent[\'name\'];\n\n'
|
||||||
|
+ createEventTypeMap(definitions) + '\n\n'
|
||||||
|
+ createEventTypeIdMap(parser.match.eventDefinitions) + '\n';
|
||||||
console.log(definition);
|
console.log(definition);
|
||||||
} else if (argv['event-list']) {
|
} else if (argv['event-list']) {
|
||||||
echo(Array.from(parser.match.eventDefinitions.values()));
|
echo(Array.from(parser.match.eventDefinitions.values()));
|
||||||
|
|
@ -108,12 +110,21 @@ function createEventDefinitionUnion(definitions) {
|
||||||
+ ';';
|
+ ';';
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEventTpeMap(definitions) {
|
function createEventTypeMap(definitions) {
|
||||||
return `export type GameEventTypeMap = {
|
return `export type GameEventTypeMap = {
|
||||||
${definitions.map(definition => ` ${definition.name}: ${getEventTypeName(definition.name)}Event;`).join('\n')}
|
${definitions.map(definition => ` ${definition.name}: ${getEventTypeName(definition.name)}Event;`).join('\n')}
|
||||||
};`;
|
};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createEventTypeIdMap(definitionMap) {
|
||||||
|
const definitionEntries = Array.from(definitionMap.entries());
|
||||||
|
return `export type GameEventTypeId = number;
|
||||||
|
|
||||||
|
export const GameEventTypeIdMap: Map<GameEventType, GameEventTypeId> = new Map<GameEventType, GameEventTypeId>([
|
||||||
|
${definitionEntries.map(([typeId, definition]) => ` ['${definition.name}', ${typeId}],`).join('\n')}
|
||||||
|
]);`;
|
||||||
|
}
|
||||||
|
|
||||||
const EventNameReplace = new Map([
|
const EventNameReplace = new Map([
|
||||||
['ReplayReplaysavailable', 'ReplayReplaysAvailable'],
|
['ReplayReplaysavailable', 'ReplayReplaysAvailable'],
|
||||||
['ServerAddban', 'ServerAddBan'],
|
['ServerAddban', 'ServerAddBan'],
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {GameEvent} from './GameEventTypes';
|
import {GameEventType} from './GameEventTypes';
|
||||||
|
|
||||||
export interface GameEventDefinition<T extends GameEvent['name']> {
|
export interface GameEventDefinition<T extends GameEventType> {
|
||||||
id: number;
|
id: number;
|
||||||
name: T;
|
name: T;
|
||||||
entries: GameEventEntry[];
|
entries: GameEventEntry[];
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -20,8 +20,8 @@ import {Weapon} from './Weapon';
|
||||||
import {World} from './World';
|
import {World} from './World';
|
||||||
import {Round} from './Round';
|
import {Round} from './Round';
|
||||||
import {Chat} from './Chat';
|
import {Chat} from './Chat';
|
||||||
import {GameEvent} from './GameEventTypes';
|
|
||||||
import {Packet} from './Packet';
|
import {Packet} from './Packet';
|
||||||
|
import {GameEventType} from './GameEventTypes';
|
||||||
|
|
||||||
export class Match {
|
export class Match {
|
||||||
public tick: number = 0;
|
public tick: number = 0;
|
||||||
|
|
@ -32,7 +32,7 @@ export class Match {
|
||||||
public startTick: number = 0;
|
public startTick: number = 0;
|
||||||
public intervalPerTick: number = 0;
|
public intervalPerTick: number = 0;
|
||||||
public staticBaseLines: BitStream[] = [];
|
public staticBaseLines: BitStream[] = [];
|
||||||
public eventDefinitions: Map<number, GameEventDefinition<GameEvent['name']>> = new Map();
|
public eventDefinitions: Map<number, GameEventDefinition<GameEventType>> = new Map();
|
||||||
public world: World = {
|
public world: World = {
|
||||||
boundaryMin: {x: 0, y: 0, z: 0},
|
boundaryMin: {x: 0, y: 0, z: 0},
|
||||||
boundaryMax: {x: 0, y: 0, z: 0},
|
boundaryMax: {x: 0, y: 0, z: 0},
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import {make} from '../Packet/ParserGenerator';
|
||||||
import {EncodeBSPDecal, ParseBSPDecal} from '../Packet/BSPDecal';
|
import {EncodeBSPDecal, ParseBSPDecal} from '../Packet/BSPDecal';
|
||||||
import {EncodeClassInfo, ParseClassInfo} from '../Packet/ClassInfo';
|
import {EncodeClassInfo, ParseClassInfo} from '../Packet/ClassInfo';
|
||||||
import {EncodeCreateStringTable, ParseCreateStringTable} from '../Packet/CreateStringTable';
|
import {EncodeCreateStringTable, ParseCreateStringTable} from '../Packet/CreateStringTable';
|
||||||
import {ParseGameEvent} from '../Packet/GameEvent';
|
import {EncodeGameEvent, ParseGameEvent} from '../Packet/GameEvent';
|
||||||
import {EncodeGameEventList, ParseGameEventList} from '../Packet/GameEventList';
|
import {EncodeGameEventList, ParseGameEventList} from '../Packet/GameEventList';
|
||||||
import {ParsePacketEntities} from '../Packet/PacketEntities';
|
import {ParsePacketEntities} from '../Packet/PacketEntities';
|
||||||
import {PacketHandler, voidEncoder} from '../Packet/Parser';
|
import {PacketHandler, voidEncoder} from '../Packet/Parser';
|
||||||
|
|
@ -63,7 +63,7 @@ export class Packet extends Parser {
|
||||||
[PacketTypeId.entityMessage,
|
[PacketTypeId.entityMessage,
|
||||||
make('entityMessage', 'index{11}classId{9}length{11}data{$length}')],
|
make('entityMessage', 'index{11}classId{9}length{11}data{$length}')],
|
||||||
[PacketTypeId.gameEvent,
|
[PacketTypeId.gameEvent,
|
||||||
{parser: ParseGameEvent, encoder: voidEncoder}],
|
{parser: ParseGameEvent, encoder: EncodeGameEvent}],
|
||||||
[PacketTypeId.packetEntities,
|
[PacketTypeId.packetEntities,
|
||||||
{parser: ParsePacketEntities, encoder: voidEncoder}],
|
{parser: ParsePacketEntities, encoder: voidEncoder}],
|
||||||
[PacketTypeId.tempEntities,
|
[PacketTypeId.tempEntities,
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,19 @@ import {
|
||||||
GameEventDefinition, GameEventEntry,
|
GameEventDefinition, GameEventEntry,
|
||||||
GameEventValue, GameEventValueType,
|
GameEventValue, GameEventValueType,
|
||||||
} from '../../Data/GameEvent';
|
} from '../../Data/GameEvent';
|
||||||
import {GameEvent, GameEventType} from '../../Data/GameEventTypes';
|
import {GameEvent, GameEventType, GameEventTypeIdMap, GameEventTypeMap} from '../../Data/GameEventTypes';
|
||||||
import {Match} from '../../Data/Match';
|
import {Match} from '../../Data/Match';
|
||||||
import {GameEventPacket} from '../../Data/Packet';
|
import {GameEventPacket} from '../../Data/Packet';
|
||||||
|
|
||||||
function parseGameEvent(eventId: number, stream: BitStream, events: Map<number, GameEventDefinition<GameEventType>>) {
|
function parseGameEvent<T extends GameEventType>(definition: GameEventDefinition<T>, stream: BitStream) {
|
||||||
const eventDescription = events.get(eventId);
|
const values: GameEventTypeMap[T]['values'] = {};
|
||||||
if (!eventDescription) {
|
for (const entry of definition.entries) {
|
||||||
throw new Error('unknown event type');
|
|
||||||
}
|
|
||||||
const values: GameEvent['values'] = {};
|
|
||||||
for (const entry of eventDescription.entries) {
|
|
||||||
const value = getGameEventValue(stream, entry);
|
const value = getGameEventValue(stream, entry);
|
||||||
if (value) {
|
if (value) {
|
||||||
values[entry.name] = value;
|
values[entry.name] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const name = eventDescription.name;
|
const name = definition.name as T;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
|
|
@ -27,6 +23,15 @@ function parseGameEvent(eventId: number, stream: BitStream, events: Map<number,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encodeGameEvent<T extends GameEventType>(event: GameEventTypeMap[T], definition: GameEventDefinition<T>, stream: BitStream) {
|
||||||
|
for (const entry of definition.entries) {
|
||||||
|
const value = event.values[entry.name];
|
||||||
|
if (value !== null) {
|
||||||
|
encodeGameEventValue(value, stream, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getGameEventValue(stream: BitStream, entry: GameEventEntry): GameEventValue | null {
|
function getGameEventValue(stream: BitStream, entry: GameEventEntry): GameEventValue | null {
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
case GameEventValueType.STRING:
|
case GameEventValueType.STRING:
|
||||||
|
|
@ -46,14 +51,77 @@ function getGameEventValue(stream: BitStream, entry: GameEventEntry): GameEventV
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function encodeGameEventValue(value: GameEventValue | null, stream: BitStream, entry: GameEventEntry) {
|
||||||
|
switch (entry.type) {
|
||||||
|
case GameEventValueType.STRING:
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
throw new Error('Invalid value for game event');
|
||||||
|
}
|
||||||
|
return stream.writeASCIIString(value);
|
||||||
|
case GameEventValueType.FLOAT:
|
||||||
|
if (typeof value !== 'number') {
|
||||||
|
throw new Error('Invalid value for game event');
|
||||||
|
}
|
||||||
|
return stream.writeFloat32(value);
|
||||||
|
case GameEventValueType.LONG:
|
||||||
|
if (typeof value !== 'number') {
|
||||||
|
throw new Error('Invalid value for game event');
|
||||||
|
}
|
||||||
|
return stream.writeUint32(value);
|
||||||
|
case GameEventValueType.SHORT:
|
||||||
|
if (typeof value !== 'number') {
|
||||||
|
throw new Error('Invalid value for game event');
|
||||||
|
}
|
||||||
|
return stream.writeUint16(value);
|
||||||
|
case GameEventValueType.BYTE:
|
||||||
|
if (typeof value !== 'number') {
|
||||||
|
throw new Error('Invalid value for game event');
|
||||||
|
}
|
||||||
|
return stream.writeUint8(value);
|
||||||
|
case GameEventValueType.BOOLEAN:
|
||||||
|
if (typeof value !== 'boolean') {
|
||||||
|
throw new Error('Invalid value for game event');
|
||||||
|
}
|
||||||
|
return stream.writeBoolean(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function ParseGameEvent(stream: BitStream, match: Match): GameEventPacket { // 25: game event
|
export function ParseGameEvent(stream: BitStream, match: Match): GameEventPacket { // 25: game event
|
||||||
const length = stream.readBits(11);
|
const length = stream.readBits(11);
|
||||||
const end = stream.index + length;
|
const eventData = stream.readBitStream(length);
|
||||||
const eventId = stream.readBits(9);
|
const eventType = eventData.readBits(9);
|
||||||
const event = parseGameEvent(eventId, stream, match.eventDefinitions);
|
const definition = match.eventDefinitions.get(eventType);
|
||||||
stream.index = end;
|
if (!definition) {
|
||||||
|
throw new Error(`Unknown game event type ${eventType}`);
|
||||||
|
}
|
||||||
|
const event = parseGameEvent(definition, eventData);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
packetType: 'gameEvent',
|
packetType: 'gameEvent',
|
||||||
event: event as GameEvent,
|
event: event as GameEvent,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function EncodeGameEvent(packet: GameEventPacket, stream: BitStream, match: Match) {
|
||||||
|
const lengthStart = stream.index;
|
||||||
|
stream.index += 11;
|
||||||
|
const eventId = GameEventTypeIdMap.get(packet.event.name);
|
||||||
|
if (typeof eventId === 'undefined') {
|
||||||
|
throw new Error(`Unknown game event type ${packet.event.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventDataStart = stream.index;
|
||||||
|
stream.writeBits(eventId, 9);
|
||||||
|
|
||||||
|
const definition = match.eventDefinitions.get(eventId);
|
||||||
|
if (typeof definition === 'undefined') {
|
||||||
|
throw new Error(`Unknown game event type ${packet.event.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeGameEvent(packet.event, definition, stream);
|
||||||
|
const eventDataEnd = stream.index;
|
||||||
|
|
||||||
|
stream.index = lengthStart;
|
||||||
|
stream.writeBits(eventDataEnd - eventDataStart, 11);
|
||||||
|
stream.index = eventDataEnd;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
import {GameEventDefinition, GameEventEntry} from '../../Data/GameEvent';
|
import {GameEventDefinition, GameEventEntry} from '../../Data/GameEvent';
|
||||||
import {GameEventListPacket} from '../../Data/Packet';
|
import {GameEventListPacket} from '../../Data/Packet';
|
||||||
import {GameEvent} from '../../Data/GameEventTypes';
|
import {GameEvent, GameEventType} from '../../Data/GameEventTypes';
|
||||||
|
|
||||||
export function ParseGameEventList(stream: BitStream): GameEventListPacket { // 30: gameEventList
|
export function ParseGameEventList(stream: BitStream): GameEventListPacket { // 30: gameEventList
|
||||||
const s = stream.index;
|
const s = stream.index;
|
||||||
|
|
@ -9,7 +9,7 @@ export function ParseGameEventList(stream: BitStream): GameEventListPacket { //
|
||||||
// list of game events and parameters
|
// list of game events and parameters
|
||||||
const numEvents = stream.readBits(9);
|
const numEvents = stream.readBits(9);
|
||||||
const length = stream.readBits(20);
|
const length = stream.readBits(20);
|
||||||
const eventList: Map<number, GameEventDefinition<GameEvent['name']>> = new Map();
|
const eventList: Map<number, GameEventDefinition<GameEventType>> = new Map();
|
||||||
for (let i = 0; i < numEvents; i++) {
|
for (let i = 0; i < numEvents; i++) {
|
||||||
const id = stream.readBits(9);
|
const id = stream.readBits(9);
|
||||||
const name = stream.readASCIIString() as GameEvent['name'];
|
const name = stream.readASCIIString() as GameEvent['name'];
|
||||||
|
|
@ -58,8 +58,8 @@ export function EncodeGameEventList(packet: GameEventListPacket, stream: BitStre
|
||||||
stream.writeBitStream(eventListStream);
|
stream.writeBitStream(eventListStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEventListLength(eventList: GameEventDefinition<GameEvent['name']>[]) {
|
function getEventListLength(eventList: GameEventDefinition<GameEventType>[]) {
|
||||||
return eventList.reduce((length: number, entry: GameEventDefinition<GameEvent['name']>) => {
|
return eventList.reduce((length: number, entry: GameEventDefinition<GameEventType>) => {
|
||||||
return length +
|
return length +
|
||||||
9 +
|
9 +
|
||||||
(entry.name.length + 1) * 8 +
|
(entry.name.length + 1) * 8 +
|
||||||
|
|
|
||||||
45
src/tests/unit/Parser/Packet/GameEventTest.ts
Normal file
45
src/tests/unit/Parser/Packet/GameEventTest.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
import {assertEncoder, assertParser, getStream} from './PacketTest';
|
||||||
|
import {EncodeClassInfo, ParseClassInfo} from '../../../../Parser/Packet/ClassInfo';
|
||||||
|
import {EncodeGameEvent, ParseGameEvent} from '../../../../Parser/Packet/GameEvent';
|
||||||
|
import {GameEventPacket} from '../../../../Data/Packet';
|
||||||
|
import {Match} from '../../../../Data/Match';
|
||||||
|
import {GameEventTypeIdMap} from '../../../../Data/GameEventTypes';
|
||||||
|
import {GameEventValueType} from '../../../../Data/GameEvent';
|
||||||
|
|
||||||
|
const data = [25, 240, 149, 0, 0];
|
||||||
|
const expected = {
|
||||||
|
packetType: 'gameEvent',
|
||||||
|
event: {
|
||||||
|
name: 'post_inventory_application',
|
||||||
|
values: {userid: 9}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const match = new Match();
|
||||||
|
match.eventDefinitions.set(190, {
|
||||||
|
id: 190,
|
||||||
|
name: 'post_inventory_application',
|
||||||
|
entries: [{
|
||||||
|
name: 'userid',
|
||||||
|
type: GameEventValueType.SHORT
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
const parseEvent = (stream: BitStream) => {
|
||||||
|
return ParseGameEvent(stream, match);
|
||||||
|
};
|
||||||
|
|
||||||
|
const encodeEvent = (packet: GameEventPacket, stream: BitStream) => {
|
||||||
|
EncodeGameEvent(packet, stream, match);
|
||||||
|
};
|
||||||
|
|
||||||
|
suite('GameEvent', () => {
|
||||||
|
test('Parse gameEvent', () => {
|
||||||
|
assertParser(parseEvent, getStream(data), expected, 36);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Encode gameEvent', () => {
|
||||||
|
assertEncoder(parseEvent, encodeEvent, expected, 36);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -43,12 +43,12 @@ const expected = {
|
||||||
substitute4: '',
|
substitute4: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
suite('SayText2', () => {
|
suite('UserMessage', () => {
|
||||||
test('Parse sayText2', () => {
|
test('Parse userMessage', () => {
|
||||||
assertParser(ParseUserMessage, getStream(data), expected, 219);
|
assertParser(ParseUserMessage, getStream(data), expected, 219);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Encode sayText2', () => {
|
test('Encode userMessage', () => {
|
||||||
assertEncoder(ParseUserMessage, EncodeUserMessage, expected, 219);
|
assertEncoder(ParseUserMessage, EncodeUserMessage, expected, 219);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue