mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
strict game event types
This commit is contained in:
parent
b3e69f1af4
commit
a7911f2d3d
11 changed files with 3925 additions and 96 deletions
4
Makefile
4
Makefile
|
|
@ -25,3 +25,7 @@ unit: node_modules
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: node_modules
|
lint: node_modules
|
||||||
node_modules/.bin/tslint -p tsconfig.json
|
node_modules/.bin/tslint -p tsconfig.json
|
||||||
|
|
||||||
|
.PHONY: src/Data/GameEventTypes.ts
|
||||||
|
src/Data/GameEventTypes.ts:
|
||||||
|
node bin/analyse.js --create-event-definitions src/tests/data/celt.dem > src/Data/GameEventTypes.ts
|
||||||
|
|
|
||||||
147
bin/analyse.js
147
bin/analyse.js
|
|
@ -1,30 +1,40 @@
|
||||||
require('source-map-support').install();
|
require('source-map-support').install();
|
||||||
|
|
||||||
var Demo = require('../index');
|
const Demo = require('../index');
|
||||||
var fs = require('fs');
|
const fs = require('fs');
|
||||||
var argv = require('minimist')(process.argv.slice(2), {boolean: true});
|
const argv = require('minimist')(process.argv.slice(2), {boolean: true});
|
||||||
|
|
||||||
if (argv._.length !== 1) {
|
if (argv._.length !== 1) {
|
||||||
console.log('Usage: "node analyse [--strings] [--dump] [--head] FILE"');
|
console.log('Usage: "node analyse [--strings] [--dump] [--head] [--event-list] [--create-event-definitions] FILE"');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var echo = function (data) {
|
const echo = function (data) {
|
||||||
var string = JSON.stringify(data, null, 2);
|
const string = JSON.stringify(data, null, 2);
|
||||||
console.log(string);
|
console.log(string);
|
||||||
};
|
};
|
||||||
|
|
||||||
fs.readFile(argv._[0], function (err, data) {
|
fs.readFile(argv._[0], function (err, data) {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
var demo = Demo.fromNodeBuffer(data);
|
const demo = Demo.fromNodeBuffer(data);
|
||||||
var parser = demo.getParser(true);
|
const parser = demo.getParser(true);
|
||||||
var head = parser.readHeader();
|
const head = parser.readHeader();
|
||||||
if (argv.head) {
|
if (argv.head) {
|
||||||
echo(head);
|
echo(head);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var match = parser.parseBody();
|
const match = parser.parseBody();
|
||||||
if (argv.dump) {
|
if (argv['create-event-definitions']) {
|
||||||
|
const definitions = Array.from(parser.match.eventDefinitions.values());
|
||||||
|
const definition = definitions
|
||||||
|
.map(createEventDefinition)
|
||||||
|
.join('\n\n')
|
||||||
|
+ '\n\n' + createEventDefinitionUnion(definitions) + '\n\n'
|
||||||
|
+ createEventTpeMap(definitions) + '\n';
|
||||||
|
console.log(definition);
|
||||||
|
} else if (argv['event-list']) {
|
||||||
|
echo(Array.from(parser.match.eventDefinitions.values()));
|
||||||
|
} else if (argv.dump) {
|
||||||
echo(parser.match.packets);
|
echo(parser.match.packets);
|
||||||
} else if (argv.strings) {
|
} else if (argv.strings) {
|
||||||
echo(parser.match.strings);
|
echo(parser.match.strings);
|
||||||
|
|
@ -32,3 +42,118 @@ fs.readFile(argv._[0], function (err, data) {
|
||||||
echo(match.getState());
|
echo(match.getState());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function getEventTypeName(s) {
|
||||||
|
const name = s.replace(/(\_\w)/g, function (m) {
|
||||||
|
return m[1].toUpperCase();
|
||||||
|
}).replace(/\b[a-z]/g, function (letter) {
|
||||||
|
return letter.toUpperCase();
|
||||||
|
});
|
||||||
|
if (EventNameReplace.has(name)) {
|
||||||
|
return EventNameReplace.get(name);
|
||||||
|
} else {
|
||||||
|
return name
|
||||||
|
.replace('Teamplay', 'TeamPlay')
|
||||||
|
.replace('death', 'Death')
|
||||||
|
.replace('panel', 'Panel')
|
||||||
|
.replace('object', 'Object')
|
||||||
|
.replace('update', 'Update')
|
||||||
|
.replace('ready', 'Ready')
|
||||||
|
.replace('Gameui', 'GameUI')
|
||||||
|
.replace('onhit', 'OnHit')
|
||||||
|
.replace('bymedic', 'ByMedic')
|
||||||
|
.replace('Controlpoint', 'ControlPoint')
|
||||||
|
.replace('Pipebomb', 'PipeBomb')
|
||||||
|
.replace('Scorestats', 'ScoreStats')
|
||||||
|
.replace('Creditbonus', 'CreditBonus')
|
||||||
|
.replace('Sentrybuster', 'SentryBuster')
|
||||||
|
.replace('Questlog', 'QuestLog')
|
||||||
|
.replace('Localplayer', 'LocalPlayer')
|
||||||
|
.replace('Minigame', 'MiniGame')
|
||||||
|
.replace('Winlimit', 'WinLimit')
|
||||||
|
.replace('Hltv', 'HLTV');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEntryTypeDefinition(typeId) {
|
||||||
|
switch (typeId) {
|
||||||
|
case 1:
|
||||||
|
return 'string';
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
return 'number';
|
||||||
|
case 6:
|
||||||
|
return 'boolean';
|
||||||
|
case 7:
|
||||||
|
return 'null';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEventDefinition(definition) {
|
||||||
|
return `
|
||||||
|
export interface ${getEventTypeName(definition.name)}Event {
|
||||||
|
name: '${definition.name}';
|
||||||
|
values: {
|
||||||
|
${definition.entries.map(entry => ` ${entry.name}: ${getEntryTypeDefinition(entry.type)};`).join('\n')}
|
||||||
|
};
|
||||||
|
}`.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEventDefinitionUnion(definitions) {
|
||||||
|
return `export type GameEvent = ` +
|
||||||
|
definitions.map(definition => '\t' + getEventTypeName(definition.name) + 'Event')
|
||||||
|
.join(' |\n').trim()
|
||||||
|
+ ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEventTpeMap(definitions) {
|
||||||
|
return `export type GameEventTypeMap = {
|
||||||
|
${definitions.map(definition => ` ${definition.name}: ${getEventTypeName(definition.name)}Event;`).join('\n')}
|
||||||
|
};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EventNameReplace = new Map([
|
||||||
|
['ReplayReplaysavailable', 'ReplayReplaysAvailable'],
|
||||||
|
['ServerAddban', 'ServerAddBan'],
|
||||||
|
['ServerRemoveban', 'ServerRemoveBan'],
|
||||||
|
['ClientBeginconnect', 'ClientBeginConnect'],
|
||||||
|
['ClientFullconnect', 'ClientFullConnect'],
|
||||||
|
['PlayerChangename', 'PlayerChangeName'],
|
||||||
|
['PlayerHintmessage', 'PlayerHintMessage'],
|
||||||
|
['GameNewmap', 'GameNewMap'],
|
||||||
|
['IntroNextcamera', 'IntroNextCamera'],
|
||||||
|
['PlayerChangeclass', 'PlayerChangeClass'],
|
||||||
|
['ControlpointInitialized', 'ControlPointInitialized'],
|
||||||
|
['ControlpointUpdateimages', 'ControlPointUpdateImages'],
|
||||||
|
['ControlpointUpdatelayout', 'ControlPointUpdateLayout'],
|
||||||
|
['ControlpointUpdatecapping', 'ControlPointUpdateCapping'],
|
||||||
|
['ControlpointUpdateowner', 'ControlPointUpdateOwner'],
|
||||||
|
['ControlpointStarttouch', 'ControlPointStartTouch'],
|
||||||
|
['ControlpointEndtouch', 'ControlPointEndTouch'],
|
||||||
|
['ControlpointPulseElement', 'ControlPointPulseElement'],
|
||||||
|
['ControlpointFakeCapture', 'ControlPointFakeCapture'],
|
||||||
|
['ControlpointFakeCaptureMult', 'ControlPointFakeCaptureMult'],
|
||||||
|
['TeamplayWaitingAbouttoend', 'TeamPlayWaitingAboutToEnd'],
|
||||||
|
['TeamplayPointStartcapture', 'TeamPlayPointStartCapture'],
|
||||||
|
['FreezecamStarted', 'FreezeCamStarted'],
|
||||||
|
['LocalplayerChangeteam', 'LocalPlayerChangeTeam'],
|
||||||
|
['LocalplayerChangeclass', 'LocalPlayerChangeClass'],
|
||||||
|
['LocalplayerChangedisguise', 'LocalPlayerChangeDisguise'],
|
||||||
|
['FlagstatusUpdate', 'FlagStatusUpdate'],
|
||||||
|
['TournamentEnablecountdown', 'TournamentEnableCountdown'],
|
||||||
|
['PlayerCalledformedic', 'PlayerCalledForMedic'],
|
||||||
|
['PlayerAskedforball', 'PlayerAskedForBall'],
|
||||||
|
['LocalplayerBecameobserver', 'LocalPlayerBecameObserver'],
|
||||||
|
['PlayerHealedmediccall', 'PlayerHealedMedicCall'],
|
||||||
|
['ArenaMatchMaxstreak', 'ArenaMatchMaxStreak'],
|
||||||
|
['StatsResetround', 'StatsResetRound'],
|
||||||
|
['FishNotice_arm', 'FishNoticeArm'],
|
||||||
|
['PlayerBonuspoints', 'PlayerBonusPoints'],
|
||||||
|
['PlayerUsedPowerupBottle', 'PlayerUsedPowerUpBottle'],
|
||||||
|
['ReplayStartrecord', 'ReplayStartRecord'],
|
||||||
|
['ReplaySessioninfo', 'ReplaySessionInfo'],
|
||||||
|
['ReplayEndrecord', 'ReplayEndRecord'],
|
||||||
|
['ReplayServererror', 'ReplayServerError']
|
||||||
|
]);
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,17 @@
|
||||||
export interface GameEventDefinition {
|
import {GameEvent} from './GameEventTypes';
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
entries: GameEventEntry[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GameEvent {
|
export interface GameEventDefinition<T extends GameEvent['name']> {
|
||||||
name: string;
|
id: number;
|
||||||
values: GameEventValues;
|
name: T;
|
||||||
|
entries: GameEventEntry[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameEventEntry {
|
export interface GameEventEntry {
|
||||||
name: string;
|
name: string;
|
||||||
type: GameEventType;
|
type: GameEventValueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum GameEventType {
|
export enum GameEventValueType {
|
||||||
STRING = 1,
|
STRING = 1,
|
||||||
FLOAT = 2,
|
FLOAT = 2,
|
||||||
LONG = 3,
|
LONG = 3,
|
||||||
|
|
@ -24,42 +21,8 @@ export enum GameEventType {
|
||||||
LOCAL = 7,
|
LOCAL = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeathEventValues {
|
|
||||||
attacker: number;
|
|
||||||
userid: number;
|
|
||||||
assister: number;
|
|
||||||
weapon: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RoundWinEventValues {
|
|
||||||
winreason: number;
|
|
||||||
team: number;
|
|
||||||
round_time: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PlayerSpawnEventValues {
|
|
||||||
userid: number;
|
|
||||||
team: number;
|
|
||||||
'class': number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ObjectDestroyedValues {
|
|
||||||
userid: number;
|
|
||||||
attacker: number;
|
|
||||||
weapon: string;
|
|
||||||
weapinid: number;
|
|
||||||
objecttype: number;
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GameEventValue = string | number | boolean;
|
export type GameEventValue = string | number | boolean;
|
||||||
|
|
||||||
export interface GameEventValueMap {
|
export interface GameEventValues {
|
||||||
[name: string]: GameEventValue;
|
[name: string]: GameEventValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GameEventValues = GameEventValueMap |
|
|
||||||
DeathEventValues |
|
|
||||||
RoundWinEventValues |
|
|
||||||
PlayerSpawnEventValues |
|
|
||||||
ObjectDestroyedValues;
|
|
||||||
|
|
|
||||||
3730
src/Data/GameEventTypes.ts
Normal file
3730
src/Data/GameEventTypes.ts
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -20,6 +20,7 @@ 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';
|
||||||
|
|
||||||
export class Match {
|
export class Match {
|
||||||
public tick: number = 0;
|
public tick: number = 0;
|
||||||
|
|
@ -30,7 +31,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> = new Map();
|
public eventDefinitions: Map<number, GameEventDefinition<GameEvent['name']>> = 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},
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
import {GameEvent, GameEventDefinition} from './GameEvent';
|
import {GameEventDefinition} from './GameEvent';
|
||||||
import {PacketEntity} from './PacketEntity';
|
import {PacketEntity} from './PacketEntity';
|
||||||
import {SendTable} from './SendTable';
|
import {SendTable} from './SendTable';
|
||||||
import {ServerClass} from './ServerClass';
|
import {ServerClass} from './ServerClass';
|
||||||
import {StringTable, StringTableEntry} from './StringTable';
|
import {StringTable, StringTableEntry} from './StringTable';
|
||||||
import {Vector} from './Vector';
|
import {Vector} from './Vector';
|
||||||
|
import {GameEvent, GameEventType} from './GameEventTypes';
|
||||||
|
|
||||||
export interface BasePacket {
|
export interface BasePacket {
|
||||||
}
|
}
|
||||||
|
|
@ -70,7 +71,7 @@ export interface GameEventPacket extends BasePacket {
|
||||||
|
|
||||||
export interface GameEventListPacket extends BasePacket {
|
export interface GameEventListPacket extends BasePacket {
|
||||||
packetType: 'gameEventList';
|
packetType: 'gameEventList';
|
||||||
eventList: Map<number, GameEventDefinition>;
|
eventList: Map<number, GameEventDefinition<GameEvent['name']>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PacketEntitiesPacket extends BasePacket {
|
export interface PacketEntitiesPacket extends BasePacket {
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,31 @@
|
||||||
import {DeathEventValues, ObjectDestroyedValues, PlayerSpawnEventValues, RoundWinEventValues} from '../Data/GameEvent';
|
|
||||||
import {Match} from '../Data/Match';
|
import {Match} from '../Data/Match';
|
||||||
import {GameEventPacket} from '../Data/Packet';
|
import {GameEventPacket} from '../Data/Packet';
|
||||||
|
import {
|
||||||
|
ObjectDestroyedEvent, PlayerDeathEvent, PlayerSpawnEvent, TeamPlayRoundStartEvent, TeamPlayRoundWinEvent
|
||||||
|
} from '../Data/GameEventTypes';
|
||||||
|
|
||||||
export function handleGameEvent(packet: GameEventPacket, match: Match) {
|
export function handleGameEvent(packet: GameEventPacket, match: Match) {
|
||||||
switch (packet.event.name) {
|
switch (packet.event.name) {
|
||||||
case 'player_death':
|
case 'player_death':
|
||||||
handlePlayerDeath(packet, match);
|
handlePlayerDeath(packet.event, match);
|
||||||
break;
|
break;
|
||||||
case 'teamplay_round_win':
|
case 'teamplay_round_win':
|
||||||
handleRoundWin(packet, match);
|
handleRoundWin(packet.event, match);
|
||||||
break;
|
break;
|
||||||
case 'player_spawn':
|
case 'player_spawn':
|
||||||
handlePlayerSpawn(packet, match);
|
handlePlayerSpawn(packet.event, match);
|
||||||
break;
|
break;
|
||||||
case 'object_destroyed':
|
case 'object_destroyed':
|
||||||
handleObjectDestroyed(packet, match);
|
handleObjectDestroyed(packet.event, match);
|
||||||
break;
|
break;
|
||||||
case 'teamplay_round_start':
|
case 'teamplay_round_start':
|
||||||
handleRoundStart(packet, match);
|
handleRoundStart(packet.event, match);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePlayerDeath(packet: GameEventPacket, match: Match) {
|
function handlePlayerDeath(event: PlayerDeathEvent, match: Match) {
|
||||||
const values = packet.event.values as DeathEventValues;
|
const values = event.values;
|
||||||
while (values.assister > 256 && values.assister < (1024 * 16)) {
|
while (values.assister > 256 && values.assister < (1024 * 16)) {
|
||||||
values.assister -= 256;
|
values.assister -= 256;
|
||||||
}
|
}
|
||||||
|
|
@ -44,8 +46,8 @@ function handlePlayerDeath(packet: GameEventPacket, match: Match) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRoundWin(packet: GameEventPacket, match: Match) {
|
function handleRoundWin(event: TeamPlayRoundWinEvent, match: Match) {
|
||||||
const values = packet.event.values as RoundWinEventValues;
|
const values = event.values;
|
||||||
if (values.winreason !== 6) {// 6 = timelimit
|
if (values.winreason !== 6) {// 6 = timelimit
|
||||||
match.rounds.push({
|
match.rounds.push({
|
||||||
winner: values.team === 2 ? 'red' : 'blue',
|
winner: values.team === 2 ? 'red' : 'blue',
|
||||||
|
|
@ -55,8 +57,8 @@ function handleRoundWin(packet: GameEventPacket, match: Match) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePlayerSpawn(packet: GameEventPacket, match: Match) {
|
function handlePlayerSpawn(event: PlayerSpawnEvent, match: Match) {
|
||||||
const values = packet.event.values as PlayerSpawnEventValues;
|
const values = event.values;
|
||||||
const userId = values.userid;
|
const userId = values.userid;
|
||||||
const userState = match.getUserInfo(userId);
|
const userState = match.getUserInfo(userId);
|
||||||
const player = match.playerEntityMap.get(userState.entityId);
|
const player = match.playerEntityMap.get(userState.entityId);
|
||||||
|
|
@ -72,11 +74,11 @@ function handlePlayerSpawn(packet: GameEventPacket, match: Match) {
|
||||||
userState.classes[classId]++;
|
userState.classes[classId]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleObjectDestroyed(packet: GameEventPacket, match: Match) {
|
function handleObjectDestroyed(event: ObjectDestroyedEvent, match: Match) {
|
||||||
const values = packet.event.values as ObjectDestroyedValues;
|
const values = event.values;
|
||||||
match.buildings.delete(values.index);
|
match.buildings.delete(values.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRoundStart(packet: GameEventPacket, match: Match) {
|
function handleRoundStart(event: TeamPlayRoundStartEvent, match: Match) {
|
||||||
match.buildings.clear();
|
match.buildings.clear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,48 @@
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
import {
|
import {
|
||||||
GameEvent as IGameEvent, GameEventDefinition, GameEventEntry, GameEventType,
|
GameEventDefinition, GameEventEntry,
|
||||||
GameEventValue, GameEventValueMap,
|
GameEventValue, GameEventValueType,
|
||||||
} from '../../Data/GameEvent';
|
} from '../../Data/GameEvent';
|
||||||
|
import {GameEvent, GameEventType} 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>): IGameEvent {
|
function parseGameEvent(eventId: number, stream: BitStream, events: Map<number, GameEventDefinition<GameEventType>>) {
|
||||||
const eventDescription = events.get(eventId);
|
const eventDescription = events.get(eventId);
|
||||||
if (!eventDescription) {
|
if (!eventDescription) {
|
||||||
throw new Error('unknown event type');
|
throw new Error('unknown event type');
|
||||||
}
|
}
|
||||||
const values: GameEventValueMap = {};
|
const values: GameEvent['values'] = {};
|
||||||
for (const entry of eventDescription.entries) {
|
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;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: eventDescription.name,
|
name,
|
||||||
values,
|
values,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getGameEventValue(stream: BitStream, entry: GameEventEntry): GameEventValue | null {
|
function getGameEventValue(stream: BitStream, entry: GameEventEntry): GameEventValue | null {
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
case GameEventType.STRING:
|
case GameEventValueType.STRING:
|
||||||
return stream.readUTF8String();
|
return stream.readUTF8String();
|
||||||
case GameEventType.FLOAT:
|
case GameEventValueType.FLOAT:
|
||||||
return stream.readFloat32();
|
return stream.readFloat32();
|
||||||
case GameEventType.LONG:
|
case GameEventValueType.LONG:
|
||||||
return stream.readUint32();
|
return stream.readUint32();
|
||||||
case GameEventType.SHORT:
|
case GameEventValueType.SHORT:
|
||||||
return stream.readUint16();
|
return stream.readUint16();
|
||||||
case GameEventType.BYTE:
|
case GameEventValueType.BYTE:
|
||||||
return stream.readUint8();
|
return stream.readUint8();
|
||||||
case GameEventType.BOOLEAN:
|
case GameEventValueType.BOOLEAN:
|
||||||
return stream.readBoolean();
|
return stream.readBoolean();
|
||||||
case GameEventType.LOCAL:
|
case GameEventValueType.LOCAL:
|
||||||
return null;
|
return null;
|
||||||
default:
|
|
||||||
throw new Error('invalid game event type');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +54,6 @@ export function ParseGameEvent(stream: BitStream, match: Match): GameEventPacket
|
||||||
stream.index = end;
|
stream.index = end;
|
||||||
return {
|
return {
|
||||||
packetType: 'gameEvent',
|
packetType: 'gameEvent',
|
||||||
event,
|
event: event as GameEvent,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +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';
|
||||||
|
|
||||||
export function ParseGameEventList(stream: BitStream): GameEventListPacket { // 30: gameEventList
|
export function ParseGameEventList(stream: BitStream): GameEventListPacket { // 30: gameEventList
|
||||||
const s = stream.index;
|
const s = stream.index;
|
||||||
|
|
@ -8,10 +9,10 @@ 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> = new Map();
|
const eventList: Map<number, GameEventDefinition<GameEvent['name']>> = 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();
|
const name = stream.readASCIIString() as GameEvent['name'];
|
||||||
let type = stream.readBits(3);
|
let type = stream.readBits(3);
|
||||||
const entries: GameEventEntry[] = [];
|
const entries: GameEventEntry[] = [];
|
||||||
while (type !== 0) {
|
while (type !== 0) {
|
||||||
|
|
@ -57,8 +58,8 @@ export function EncodeGameEventList(packet: GameEventListPacket, stream: BitStre
|
||||||
stream.writeBitStream(eventListStream);
|
stream.writeBitStream(eventListStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEventListLength(eventList: GameEventDefinition[]) {
|
function getEventListLength(eventList: GameEventDefinition<GameEvent['name']>[]) {
|
||||||
return eventList.reduce((length: number, entry: GameEventDefinition) => {
|
return eventList.reduce((length: number, entry: GameEventDefinition<GameEvent['name']>) => {
|
||||||
return length +
|
return length +
|
||||||
9 +
|
9 +
|
||||||
(entry.name.length + 1) * 8 +
|
(entry.name.length + 1) * 8 +
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ export {StreamParser} from './StreamParser';
|
||||||
export {Match} from './Data/Match';
|
export {Match} from './Data/Match';
|
||||||
export {Player} from './Data/Player';
|
export {Player} from './Data/Player';
|
||||||
export {PlayerCondition} from './Data/PlayerCondition';
|
export {PlayerCondition} from './Data/PlayerCondition';
|
||||||
export {GameEvent} from './Data/GameEvent';
|
export {GameEvent} from './Data/GameEventTypes';
|
||||||
export {PacketEntity} from './Data/PacketEntity';
|
export {PacketEntity} from './Data/PacketEntity';
|
||||||
export {SendPropDefinition, SendPropFlag, SendPropType} from './Data/SendPropDefinition';
|
export {SendPropDefinition, SendPropFlag, SendPropType} from './Data/SendPropDefinition';
|
||||||
export {SendProp} from './Data/SendProp';
|
export {SendProp} from './Data/SendProp';
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {assertEncoder, assertParser, getStream} from './PacketTest';
|
||||||
import {readFileSync} from 'fs';
|
import {readFileSync} from 'fs';
|
||||||
import {EncodeGameEventList, ParseGameEventList} from '../../../../Parser/Packet/GameEventList';
|
import {EncodeGameEventList, ParseGameEventList} from '../../../../Parser/Packet/GameEventList';
|
||||||
import {GameEventListPacket} from '../../../../Data/Packet';
|
import {GameEventListPacket} from '../../../../Data/Packet';
|
||||||
|
import {GameEvent} from '../../../../Data/GameEventTypes';
|
||||||
|
|
||||||
const data = JSON.parse(readFileSync(__dirname + '/../../../data/gameEventListData.json', 'utf8'));
|
const data = JSON.parse(readFileSync(__dirname + '/../../../data/gameEventListData.json', 'utf8'));
|
||||||
const expectedSource = JSON.parse(readFileSync(__dirname + '/../../../data/gameEventList.json', 'utf8'));
|
const expectedSource = JSON.parse(readFileSync(__dirname + '/../../../data/gameEventList.json', 'utf8'));
|
||||||
|
|
@ -17,7 +18,7 @@ const eventList: GameEventListPacket = {
|
||||||
'eventList': new Map([
|
'eventList': new Map([
|
||||||
[0, {
|
[0, {
|
||||||
'id': 0,
|
'id': 0,
|
||||||
'name': 'server_spawn',
|
'name': 'server_spawn' as GameEvent['name'],
|
||||||
'entries': [
|
'entries': [
|
||||||
{
|
{
|
||||||
'type': 1,
|
'type': 1,
|
||||||
|
|
@ -63,7 +64,7 @@ const eventList: GameEventListPacket = {
|
||||||
}],
|
}],
|
||||||
[1, {
|
[1, {
|
||||||
'id': 1,
|
'id': 1,
|
||||||
'name': 'server_changelevel_failed',
|
'name': 'server_changelevel_failed' as GameEvent['name'],
|
||||||
'entries': [
|
'entries': [
|
||||||
{
|
{
|
||||||
'type': 1,
|
'type': 1,
|
||||||
|
|
@ -73,7 +74,7 @@ const eventList: GameEventListPacket = {
|
||||||
}],
|
}],
|
||||||
[2, {
|
[2, {
|
||||||
'id': 2,
|
'id': 2,
|
||||||
'name': 'server_shutdown',
|
'name': 'server_shutdown' as GameEvent['name'],
|
||||||
'entries': [
|
'entries': [
|
||||||
{
|
{
|
||||||
'type': 1,
|
'type': 1,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue