1
0
Fork 0
mirror of https://github.com/demostf/demo.js synced 2026-06-04 09:04:13 +02:00

strict game event types

This commit is contained in:
Robin Appelman 2017-09-02 22:44:10 +02:00
commit a7911f2d3d
11 changed files with 3925 additions and 96 deletions

View file

@ -1,20 +1,17 @@
export interface GameEventDefinition {
id: number;
name: string;
entries: GameEventEntry[];
}
import {GameEvent} from './GameEventTypes';
export interface GameEvent {
name: string;
values: GameEventValues;
export interface GameEventDefinition<T extends GameEvent['name']> {
id: number;
name: T;
entries: GameEventEntry[];
}
export interface GameEventEntry {
name: string;
type: GameEventType;
type: GameEventValueType;
}
export enum GameEventType {
export enum GameEventValueType {
STRING = 1,
FLOAT = 2,
LONG = 3,
@ -24,42 +21,8 @@ export enum GameEventType {
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 interface GameEventValueMap {
export interface GameEventValues {
[name: string]: GameEventValue;
}
export type GameEventValues = GameEventValueMap |
DeathEventValues |
RoundWinEventValues |
PlayerSpawnEventValues |
ObjectDestroyedValues;

3730
src/Data/GameEventTypes.ts Normal file

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@ import {Weapon} from './Weapon';
import {World} from './World';
import {Round} from './Round';
import {Chat} from './Chat';
import {GameEvent} from './GameEventTypes';
export class Match {
public tick: number = 0;
@ -30,7 +31,7 @@ export class Match {
public startTick: number = 0;
public intervalPerTick: number = 0;
public staticBaseLines: BitStream[] = [];
public eventDefinitions: Map<number, GameEventDefinition> = new Map();
public eventDefinitions: Map<number, GameEventDefinition<GameEvent['name']>> = new Map();
public world: World = {
boundaryMin: {x: 0, y: 0, z: 0},
boundaryMax: {x: 0, y: 0, z: 0},

View file

@ -1,10 +1,11 @@
import {BitStream} from 'bit-buffer';
import {GameEvent, GameEventDefinition} from './GameEvent';
import {GameEventDefinition} from './GameEvent';
import {PacketEntity} from './PacketEntity';
import {SendTable} from './SendTable';
import {ServerClass} from './ServerClass';
import {StringTable, StringTableEntry} from './StringTable';
import {Vector} from './Vector';
import {GameEvent, GameEventType} from './GameEventTypes';
export interface BasePacket {
}
@ -70,7 +71,7 @@ export interface GameEventPacket extends BasePacket {
export interface GameEventListPacket extends BasePacket {
packetType: 'gameEventList';
eventList: Map<number, GameEventDefinition>;
eventList: Map<number, GameEventDefinition<GameEvent['name']>>;
}
export interface PacketEntitiesPacket extends BasePacket {

View file

@ -1,29 +1,31 @@
import {DeathEventValues, ObjectDestroyedValues, PlayerSpawnEventValues, RoundWinEventValues} from '../Data/GameEvent';
import {Match} from '../Data/Match';
import {GameEventPacket} from '../Data/Packet';
import {
ObjectDestroyedEvent, PlayerDeathEvent, PlayerSpawnEvent, TeamPlayRoundStartEvent, TeamPlayRoundWinEvent
} from '../Data/GameEventTypes';
export function handleGameEvent(packet: GameEventPacket, match: Match) {
switch (packet.event.name) {
case 'player_death':
handlePlayerDeath(packet, match);
handlePlayerDeath(packet.event, match);
break;
case 'teamplay_round_win':
handleRoundWin(packet, match);
handleRoundWin(packet.event, match);
break;
case 'player_spawn':
handlePlayerSpawn(packet, match);
handlePlayerSpawn(packet.event, match);
break;
case 'object_destroyed':
handleObjectDestroyed(packet, match);
handleObjectDestroyed(packet.event, match);
break;
case 'teamplay_round_start':
handleRoundStart(packet, match);
handleRoundStart(packet.event, match);
break;
}
}
function handlePlayerDeath(packet: GameEventPacket, match: Match) {
const values = packet.event.values as DeathEventValues;
function handlePlayerDeath(event: PlayerDeathEvent, match: Match) {
const values = event.values;
while (values.assister > 256 && values.assister < (1024 * 16)) {
values.assister -= 256;
}
@ -44,8 +46,8 @@ function handlePlayerDeath(packet: GameEventPacket, match: Match) {
});
}
function handleRoundWin(packet: GameEventPacket, match: Match) {
const values = packet.event.values as RoundWinEventValues;
function handleRoundWin(event: TeamPlayRoundWinEvent, match: Match) {
const values = event.values;
if (values.winreason !== 6) {// 6 = timelimit
match.rounds.push({
winner: values.team === 2 ? 'red' : 'blue',
@ -55,8 +57,8 @@ function handleRoundWin(packet: GameEventPacket, match: Match) {
}
}
function handlePlayerSpawn(packet: GameEventPacket, match: Match) {
const values = packet.event.values as PlayerSpawnEventValues;
function handlePlayerSpawn(event: PlayerSpawnEvent, match: Match) {
const values = event.values;
const userId = values.userid;
const userState = match.getUserInfo(userId);
const player = match.playerEntityMap.get(userState.entityId);
@ -72,11 +74,11 @@ function handlePlayerSpawn(packet: GameEventPacket, match: Match) {
userState.classes[classId]++;
}
function handleObjectDestroyed(packet: GameEventPacket, match: Match) {
const values = packet.event.values as ObjectDestroyedValues;
function handleObjectDestroyed(event: ObjectDestroyedEvent, match: Match) {
const values = event.values;
match.buildings.delete(values.index);
}
function handleRoundStart(packet: GameEventPacket, match: Match) {
function handleRoundStart(event: TeamPlayRoundStartEvent, match: Match) {
match.buildings.clear();
}

View file

@ -1,47 +1,48 @@
import {BitStream} from 'bit-buffer';
import {
GameEvent as IGameEvent, GameEventDefinition, GameEventEntry, GameEventType,
GameEventValue, GameEventValueMap,
GameEventDefinition, GameEventEntry,
GameEventValue, GameEventValueType,
} from '../../Data/GameEvent';
import {GameEvent, GameEventType} from '../../Data/GameEventTypes';
import {Match} from '../../Data/Match';
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);
if (!eventDescription) {
throw new Error('unknown event type');
}
const values: GameEventValueMap = {};
const values: GameEvent['values'] = {};
for (const entry of eventDescription.entries) {
const value = getGameEventValue(stream, entry);
if (value) {
values[entry.name] = value;
}
}
const name = eventDescription.name;
return {
name: eventDescription.name,
name,
values,
};
}
function getGameEventValue(stream: BitStream, entry: GameEventEntry): GameEventValue | null {
switch (entry.type) {
case GameEventType.STRING:
case GameEventValueType.STRING:
return stream.readUTF8String();
case GameEventType.FLOAT:
case GameEventValueType.FLOAT:
return stream.readFloat32();
case GameEventType.LONG:
case GameEventValueType.LONG:
return stream.readUint32();
case GameEventType.SHORT:
case GameEventValueType.SHORT:
return stream.readUint16();
case GameEventType.BYTE:
case GameEventValueType.BYTE:
return stream.readUint8();
case GameEventType.BOOLEAN:
case GameEventValueType.BOOLEAN:
return stream.readBoolean();
case GameEventType.LOCAL:
case GameEventValueType.LOCAL:
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;
return {
packetType: 'gameEvent',
event,
event: event as GameEvent,
};
}

View file

@ -1,6 +1,7 @@
import {BitStream} from 'bit-buffer';
import {GameEventDefinition, GameEventEntry} from '../../Data/GameEvent';
import {GameEventListPacket} from '../../Data/Packet';
import {GameEvent} from '../../Data/GameEventTypes';
export function ParseGameEventList(stream: BitStream): GameEventListPacket { // 30: gameEventList
const s = stream.index;
@ -8,10 +9,10 @@ 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> = new Map();
const eventList: Map<number, GameEventDefinition<GameEvent['name']>> = new Map();
for (let i = 0; i < numEvents; i++) {
const id = stream.readBits(9);
const name = stream.readASCIIString();
const name = stream.readASCIIString() as GameEvent['name'];
let type = stream.readBits(3);
const entries: GameEventEntry[] = [];
while (type !== 0) {
@ -57,8 +58,8 @@ export function EncodeGameEventList(packet: GameEventListPacket, stream: BitStre
stream.writeBitStream(eventListStream);
}
function getEventListLength(eventList: GameEventDefinition[]) {
return eventList.reduce((length: number, entry: GameEventDefinition) => {
function getEventListLength(eventList: GameEventDefinition<GameEvent['name']>[]) {
return eventList.reduce((length: number, entry: GameEventDefinition<GameEvent['name']>) => {
return length +
9 +
(entry.name.length + 1) * 8 +

View file

@ -4,7 +4,7 @@ export {StreamParser} from './StreamParser';
export {Match} from './Data/Match';
export {Player} from './Data/Player';
export {PlayerCondition} from './Data/PlayerCondition';
export {GameEvent} from './Data/GameEvent';
export {GameEvent} from './Data/GameEventTypes';
export {PacketEntity} from './Data/PacketEntity';
export {SendPropDefinition, SendPropFlag, SendPropType} from './Data/SendPropDefinition';
export {SendProp} from './Data/SendProp';

View file

@ -3,6 +3,7 @@ import {assertEncoder, assertParser, getStream} from './PacketTest';
import {readFileSync} from 'fs';
import {EncodeGameEventList, ParseGameEventList} from '../../../../Parser/Packet/GameEventList';
import {GameEventListPacket} from '../../../../Data/Packet';
import {GameEvent} from '../../../../Data/GameEventTypes';
const data = JSON.parse(readFileSync(__dirname + '/../../../data/gameEventListData.json', 'utf8'));
const expectedSource = JSON.parse(readFileSync(__dirname + '/../../../data/gameEventList.json', 'utf8'));
@ -17,7 +18,7 @@ const eventList: GameEventListPacket = {
'eventList': new Map([
[0, {
'id': 0,
'name': 'server_spawn',
'name': 'server_spawn' as GameEvent['name'],
'entries': [
{
'type': 1,
@ -63,7 +64,7 @@ const eventList: GameEventListPacket = {
}],
[1, {
'id': 1,
'name': 'server_changelevel_failed',
'name': 'server_changelevel_failed' as GameEvent['name'],
'entries': [
{
'type': 1,
@ -73,7 +74,7 @@ const eventList: GameEventListPacket = {
}],
[2, {
'id': 2,
'name': 'server_shutdown',
'name': 'server_shutdown' as GameEvent['name'],
'entries': [
{
'type': 1,