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:
parent
b3e69f1af4
commit
a7911f2d3d
11 changed files with 3925 additions and 96 deletions
|
|
@ -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
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 {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},
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 +
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue