mirror of
https://github.com/demostf/demo.js
synced 2026-06-03 16:44:12 +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)
|
||||
.join('\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);
|
||||
} else if (argv['event-list']) {
|
||||
echo(Array.from(parser.match.eventDefinitions.values()));
|
||||
|
|
@ -108,12 +110,21 @@ function createEventDefinitionUnion(definitions) {
|
|||
+ ';';
|
||||
}
|
||||
|
||||
function createEventTpeMap(definitions) {
|
||||
function createEventTypeMap(definitions) {
|
||||
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([
|
||||
['ReplayReplaysavailable', 'ReplayReplaysAvailable'],
|
||||
['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;
|
||||
name: T;
|
||||
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 {Round} from './Round';
|
||||
import {Chat} from './Chat';
|
||||
import {GameEvent} from './GameEventTypes';
|
||||
import {Packet} from './Packet';
|
||||
import {GameEventType} from './GameEventTypes';
|
||||
|
||||
export class Match {
|
||||
public tick: number = 0;
|
||||
|
|
@ -32,7 +32,7 @@ export class Match {
|
|||
public startTick: number = 0;
|
||||
public intervalPerTick: number = 0;
|
||||
public staticBaseLines: BitStream[] = [];
|
||||
public eventDefinitions: Map<number, GameEventDefinition<GameEvent['name']>> = 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},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ 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 {EncodeGameEvent, ParseGameEvent} from '../Packet/GameEvent';
|
||||
import {EncodeGameEventList, ParseGameEventList} from '../Packet/GameEventList';
|
||||
import {ParsePacketEntities} from '../Packet/PacketEntities';
|
||||
import {PacketHandler, voidEncoder} from '../Packet/Parser';
|
||||
|
|
@ -63,7 +63,7 @@ export class Packet extends Parser {
|
|||
[PacketTypeId.entityMessage,
|
||||
make('entityMessage', 'index{11}classId{9}length{11}data{$length}')],
|
||||
[PacketTypeId.gameEvent,
|
||||
{parser: ParseGameEvent, encoder: voidEncoder}],
|
||||
{parser: ParseGameEvent, encoder: EncodeGameEvent}],
|
||||
[PacketTypeId.packetEntities,
|
||||
{parser: ParsePacketEntities, encoder: voidEncoder}],
|
||||
[PacketTypeId.tempEntities,
|
||||
|
|
|
|||
|
|
@ -3,23 +3,19 @@ import {
|
|||
GameEventDefinition, GameEventEntry,
|
||||
GameEventValue, GameEventValueType,
|
||||
} from '../../Data/GameEvent';
|
||||
import {GameEvent, GameEventType} from '../../Data/GameEventTypes';
|
||||
import {GameEvent, GameEventType, GameEventTypeIdMap, GameEventTypeMap} from '../../Data/GameEventTypes';
|
||||
import {Match} from '../../Data/Match';
|
||||
import {GameEventPacket} from '../../Data/Packet';
|
||||
|
||||
function parseGameEvent(eventId: number, stream: BitStream, events: Map<number, GameEventDefinition<GameEventType>>) {
|
||||
const eventDescription = events.get(eventId);
|
||||
if (!eventDescription) {
|
||||
throw new Error('unknown event type');
|
||||
}
|
||||
const values: GameEvent['values'] = {};
|
||||
for (const entry of eventDescription.entries) {
|
||||
function parseGameEvent<T extends GameEventType>(definition: GameEventDefinition<T>, stream: BitStream) {
|
||||
const values: GameEventTypeMap[T]['values'] = {};
|
||||
for (const entry of definition.entries) {
|
||||
const value = getGameEventValue(stream, entry);
|
||||
if (value) {
|
||||
values[entry.name] = value;
|
||||
}
|
||||
}
|
||||
const name = eventDescription.name;
|
||||
const name = definition.name as T;
|
||||
|
||||
return {
|
||||
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 {
|
||||
switch (entry.type) {
|
||||
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
|
||||
const length = stream.readBits(11);
|
||||
const end = stream.index + length;
|
||||
const eventId = stream.readBits(9);
|
||||
const event = parseGameEvent(eventId, stream, match.eventDefinitions);
|
||||
stream.index = end;
|
||||
const eventData = stream.readBitStream(length);
|
||||
const eventType = eventData.readBits(9);
|
||||
const definition = match.eventDefinitions.get(eventType);
|
||||
if (!definition) {
|
||||
throw new Error(`Unknown game event type ${eventType}`);
|
||||
}
|
||||
const event = parseGameEvent(definition, eventData);
|
||||
|
||||
return {
|
||||
packetType: '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 {GameEventDefinition, GameEventEntry} from '../../Data/GameEvent';
|
||||
import {GameEventListPacket} from '../../Data/Packet';
|
||||
import {GameEvent} from '../../Data/GameEventTypes';
|
||||
import {GameEvent, GameEventType} from '../../Data/GameEventTypes';
|
||||
|
||||
export function ParseGameEventList(stream: BitStream): GameEventListPacket { // 30: gameEventList
|
||||
const s = stream.index;
|
||||
|
|
@ -9,7 +9,7 @@ export function ParseGameEventList(stream: BitStream): GameEventListPacket { //
|
|||
// list of game events and parameters
|
||||
const numEvents = stream.readBits(9);
|
||||
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++) {
|
||||
const id = stream.readBits(9);
|
||||
const name = stream.readASCIIString() as GameEvent['name'];
|
||||
|
|
@ -58,8 +58,8 @@ export function EncodeGameEventList(packet: GameEventListPacket, stream: BitStre
|
|||
stream.writeBitStream(eventListStream);
|
||||
}
|
||||
|
||||
function getEventListLength(eventList: GameEventDefinition<GameEvent['name']>[]) {
|
||||
return eventList.reduce((length: number, entry: GameEventDefinition<GameEvent['name']>) => {
|
||||
function getEventListLength(eventList: GameEventDefinition<GameEventType>[]) {
|
||||
return eventList.reduce((length: number, entry: GameEventDefinition<GameEventType>) => {
|
||||
return length +
|
||||
9 +
|
||||
(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: '',
|
||||
};
|
||||
|
||||
suite('SayText2', () => {
|
||||
test('Parse sayText2', () => {
|
||||
suite('UserMessage', () => {
|
||||
test('Parse userMessage', () => {
|
||||
assertParser(ParseUserMessage, getStream(data), expected, 219);
|
||||
});
|
||||
|
||||
test('Encode sayText2', () => {
|
||||
test('Encode userMessage', () => {
|
||||
assertEncoder(ParseUserMessage, EncodeUserMessage, expected, 219);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue